Merge branch 'color-representation-implementation' into 'main'

libweston: Implement color-representation protocol

See merge request wayland/weston!1682
This commit is contained in:
Robert Mader 2025-12-19 17:11:00 +01:00
commit a673e25f79
26 changed files with 2107 additions and 90 deletions

View file

@ -43,7 +43,7 @@
variables:
FDO_UPSTREAM_REPO: wayland/weston
FDO_REPO_SUFFIX: "$BUILD_OS-$FDO_DISTRIBUTION_VERSION/$BUILD_ARCH"
FDO_DISTRIBUTION_TAG: '2025-12-16-bump-to-trixie'
FDO_DISTRIBUTION_TAG: '2025-12-16-wayland-protocols-1.46'
include:

View file

@ -117,7 +117,7 @@ rm -rf wayland
# Keep this version in sync with our dependency in meson.build. If you wish to
# raise a MR against custom protocol, please change this reference to clone
# your relevant tree, and make sure you bump $FDO_DISTRIBUTION_TAG.
git clone --branch 1.44 --depth=1 https://gitlab.freedesktop.org/wayland/wayland-protocols
git clone --branch 1.46 --depth=1 https://gitlab.freedesktop.org/wayland/wayland-protocols
cd wayland-protocols
git show -s HEAD
meson setup build --wrap-mode=nofallback -Dtests=false

View file

@ -886,6 +886,7 @@ static const struct {
{ WESTON_CAP_VIEW_CLIP_MASK, "view mask clipping" },
{ WESTON_CAP_EXPLICIT_SYNC, "explicit sync" },
{ WESTON_CAP_COLOR_OPS, "color operations" },
{ WESTON_CAP_COLOR_REP, "color representation" }
};
static void

View file

@ -187,6 +187,36 @@ enum weston_transfer_function {
WESTON_TF_POWER,
};
enum weston_alpha_mode {
WESTON_ALPHA_MODE_PREMULTIPLIED_ELECTRICAL = 0,
WESTON_ALPHA_MODE_PREMULTIPLIED_OPTICAL,
WESTON_ALPHA_MODE_STRAIGHT,
};
enum weston_color_matrix_coef {
WESTON_COLOR_MATRIX_COEF_UNSET = 0,
WESTON_COLOR_MATRIX_COEF_IDENTITY,
WESTON_COLOR_MATRIX_COEF_BT601,
WESTON_COLOR_MATRIX_COEF_BT709,
WESTON_COLOR_MATRIX_COEF_BT2020,
};
enum weston_color_quant_range {
WESTON_COLOR_QUANT_RANGE_UNSET = 0,
WESTON_COLOR_QUANT_RANGE_FULL,
WESTON_COLOR_QUANT_RANGE_LIMITED,
};
enum weston_ycbcr_chroma_location {
WESTON_YCBCR_CHROMA_LOCATION_UNSET = 0,
WESTON_YCBCR_CHROMA_LOCATION_TYPE_0,
WESTON_YCBCR_CHROMA_LOCATION_TYPE_1,
WESTON_YCBCR_CHROMA_LOCATION_TYPE_2,
WESTON_YCBCR_CHROMA_LOCATION_TYPE_3,
WESTON_YCBCR_CHROMA_LOCATION_TYPE_4,
WESTON_YCBCR_CHROMA_LOCATION_TYPE_5,
};
/** Error codes that the color profile parameters functions may return. */
enum weston_color_profile_param_builder_error {
WESTON_COLOR_PROFILE_PARAM_BUILDER_ERROR_INVALID_TF = 0,

View file

@ -921,6 +921,18 @@ struct weston_tablet {
const char *path;
};
struct weston_color_representation {
enum weston_alpha_mode alpha_mode;
enum weston_color_matrix_coef matrix_coefficients;
enum weston_color_quant_range quant_range;
enum weston_ycbcr_chroma_location chroma_location;
};
struct weston_color_representation_matrix {
struct weston_mat3f matrix;
struct weston_vec3f offset;
};
struct weston_coord_global
weston_pointer_motion_to_abs(struct weston_pointer *pointer,
struct weston_pointer_motion_event *event);
@ -1278,6 +1290,9 @@ enum weston_capability {
/* renderer supports color management operations */
WESTON_CAP_COLOR_OPS = 0x0040,
/* renderer supports color representation operations */
WESTON_CAP_COLOR_REP = 0x0080,
};
/* Configuration struct for a backend.
@ -1826,6 +1841,11 @@ struct weston_surface_state {
struct weston_color_profile *color_profile;
const struct weston_render_intent_info *render_intent;
/* wp_color_representation_surface_v1.set_alpha_mode */
/* wp_color_representation_surface_v1.set_coefficients_and_range */
/* wp_color_representation_surface_v1.set_chroma_location */
struct weston_color_representation color_representation;
/* wp_fifo_v1 */
bool fifo_barrier;
bool fifo_wait;
@ -1990,6 +2010,9 @@ struct weston_surface {
struct wl_list cm_surface_feedback_resource_list;
struct wl_resource *cm_surface;
struct wl_resource *color_representation_resource;
struct weston_color_representation color_representation;
uint64_t damage_track_id;
uint64_t flow_id;

View file

@ -39,6 +39,7 @@
#include "drm-internal.h"
#include "color.h"
#include "color-representation.h"
#include "linux-dmabuf.h"
#include "presentation-time-server-protocol.h"
#include "linux-dmabuf-unstable-v1-server-protocol.h"
@ -139,23 +140,33 @@ drm_output_try_paint_node_on_plane(struct drm_plane *plane,
state->in_fence_fd = ev->surface->acquire_fence_fd;
if (fb->format && fb->format->color_model == COLOR_MODEL_YUV) {
enum wdrm_plane_color_encoding color_encoding;
enum wdrm_plane_color_range color_range;
struct weston_color_representation color_rep;
const struct weston_color_matrix_coef_info *matrix_coef_info;
const struct weston_color_quant_range_info *quant_range_info;
color_rep =
weston_fill_color_representation(&surface->color_representation,
fb->format);
matrix_coef_info =
weston_color_matrix_coef_info_get(color_rep.matrix_coefficients);
assert(matrix_coef_info);
assert(matrix_coef_info->wdrm != WDRM_PLANE_COLOR_ENCODING__COUNT);
quant_range_info =
weston_color_quant_range_info_get(color_rep.quant_range);
assert(quant_range_info);
assert(quant_range_info->wdrm != WDRM_PLANE_COLOR_RANGE__COUNT);
/* These values will become dynamic once we implement the
* color-representation protocol. */
color_encoding = WDRM_PLANE_COLOR_ENCODING_DEFAULT;
color_range = WDRM_PLANE_COLOR_RANGE_DEFAULT;
if (plane->props[WDRM_PLANE_COLOR_ENCODING].prop_id == 0) {
if (color_encoding != WDRM_PLANE_COLOR_ENCODING_DEFAULT) {
if (matrix_coef_info->wdrm != WDRM_PLANE_COLOR_ENCODING_DEFAULT) {
drm_debug(b, "\t\t\t[view] not placing view %p on plane %lu: "
"non-default color encoding not supported\n",
ev, (unsigned long) plane->plane_id);
goto out;
}
} else if (!drm_plane_supports_color_encoding(plane,
color_encoding)) {
matrix_coef_info->wdrm)) {
drm_debug(b, "\t\t\t[view] not placing view %p on plane %lu: "
"color encoding not supported\n", ev,
(unsigned long) plane->plane_id);
@ -163,22 +174,22 @@ drm_output_try_paint_node_on_plane(struct drm_plane *plane,
}
if (plane->props[WDRM_PLANE_COLOR_RANGE].prop_id == 0) {
if (color_range != WDRM_PLANE_COLOR_RANGE_DEFAULT) {
if (quant_range_info->wdrm != WDRM_PLANE_COLOR_RANGE_DEFAULT) {
drm_debug(b, "\t\t\t[view] not placing view %p on plane %lu: "
"non-default color range not supported\n",
ev, (unsigned long) plane->plane_id);
goto out;
}
} else if (!drm_plane_supports_color_range(plane,
color_range)) {
quant_range_info->wdrm)) {
drm_debug(b, "\t\t\t[view] not placing view %p on plane %lu: "
"color range not supported\n", ev,
(unsigned long) plane->plane_id);
goto out;
}
state->color_encoding = color_encoding;
state->color_range = color_range;
state->color_encoding = matrix_coef_info->wdrm;
state->color_range = quant_range_info->wdrm;
}
/* In planes-only mode, we don't have an incremental state to

View file

@ -0,0 +1,554 @@
/*
* Copyright 2025 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "config.h"
#include "color-representation.h"
#include "libweston-internal.h"
#include "pixel-formats.h"
#include "shared/string-helpers.h"
#include "shared/weston-assert.h"
#include "shared/xalloc.h"
#include "color-representation-v1-server-protocol.h"
static const enum wp_color_representation_surface_v1_alpha_mode supported_alpha_modes[] = {
WP_COLOR_REPRESENTATION_SURFACE_V1_ALPHA_MODE_PREMULTIPLIED_ELECTRICAL,
};
struct coeffs_and_range {
enum wp_color_representation_surface_v1_coefficients coefficients;
enum wp_color_representation_surface_v1_range range;
};
#define CRS(x) WP_COLOR_REPRESENTATION_SURFACE_V1_ ##x
static const struct coeffs_and_range supported_coeffs_and_ranges[] = {
{ CRS(COEFFICIENTS_IDENTITY), CRS(RANGE_FULL) },
{ CRS(COEFFICIENTS_BT601), CRS(RANGE_LIMITED) },
{ CRS(COEFFICIENTS_BT601), CRS(RANGE_FULL) },
{ CRS(COEFFICIENTS_BT709), CRS(RANGE_LIMITED) },
{ CRS(COEFFICIENTS_BT709), CRS(RANGE_FULL) },
{ CRS(COEFFICIENTS_BT2020), CRS(RANGE_LIMITED) },
{ CRS(COEFFICIENTS_BT2020), CRS(RANGE_FULL) },
};
#undef CRS
WL_EXPORT void
weston_reset_color_representation(struct weston_color_representation *color_rep)
{
color_rep->alpha_mode = WESTON_ALPHA_MODE_PREMULTIPLIED_ELECTRICAL;
color_rep->matrix_coefficients = WESTON_COLOR_MATRIX_COEF_UNSET;
color_rep->quant_range = WESTON_COLOR_QUANT_RANGE_UNSET;
color_rep->chroma_location = WESTON_YCBCR_CHROMA_LOCATION_UNSET;
}
WL_EXPORT struct weston_color_representation
weston_fill_color_representation(const struct weston_color_representation *color_rep_in,
const struct pixel_format_info *info)
{
struct weston_color_representation color_rep;
color_rep = *color_rep_in;
if (color_rep.matrix_coefficients == WESTON_COLOR_MATRIX_COEF_UNSET) {
if (info->color_model == COLOR_MODEL_YUV)
color_rep.matrix_coefficients =
WESTON_COLOR_MATRIX_COEF_BT709;
else
color_rep.matrix_coefficients =
WESTON_COLOR_MATRIX_COEF_IDENTITY;
}
if (color_rep.quant_range == WESTON_COLOR_QUANT_RANGE_UNSET) {
if (info->color_model == COLOR_MODEL_YUV)
color_rep.quant_range = WESTON_COLOR_QUANT_RANGE_LIMITED;
else
color_rep.quant_range = WESTON_COLOR_QUANT_RANGE_FULL;
}
return color_rep;
}
WL_EXPORT bool
weston_color_representation_equal(struct weston_color_representation *color_rep_A,
struct weston_color_representation *color_rep_B,
enum weston_cr_comparison_flag flags)
{
if (!(flags & WESTON_CR_COMPARISON_FLAG_IGNORE_ALPHA) &&
color_rep_A->alpha_mode != color_rep_B->alpha_mode)
return false;
if (!(flags & WESTON_CR_COMPARISON_FLAG_IGNORE_CHROMA_LOCATION) &&
color_rep_A->chroma_location != color_rep_B->chroma_location)
return false;
return (color_rep_A->matrix_coefficients == color_rep_B->matrix_coefficients &&
color_rep_A->quant_range == color_rep_B->quant_range);
}
WL_EXPORT void
weston_get_color_representation_matrix(struct weston_compositor *compositor,
enum weston_color_matrix_coef coefficients,
enum weston_color_quant_range range,
struct weston_color_representation_matrix *cr_matrix)
{
/* The values in this function are copied from Mesa and may not be
* optimal or correct in all cases. */
if (range == WESTON_COLOR_QUANT_RANGE_FULL) {
cr_matrix->offset =
WESTON_VEC3F(0.0, 128.0 / 255.0, 128.0 / 255.0);
switch(coefficients) {
case WESTON_COLOR_MATRIX_COEF_BT601:
cr_matrix->matrix =
WESTON_MAT3F(1.0, 0.0, 1.402,
1.0, -0.34413629, -0.71413629,
1.0, 1.772, 0.0);
return;
case WESTON_COLOR_MATRIX_COEF_BT709:
cr_matrix->matrix =
WESTON_MAT3F(1.0, 0.0, 1.5748,
1.0, -0.18732427, -0.46812427,
1.0, 1.8556, 0.0);
return;
case WESTON_COLOR_MATRIX_COEF_BT2020:
cr_matrix->matrix =
WESTON_MAT3F(1.0, 0.0, 1.4746,
1.0, -0.16455313, -0.57139187,
1.0, 1.8814, 0.0);
return;
default:
break;
}
} else if (range == WESTON_COLOR_QUANT_RANGE_LIMITED) {
cr_matrix->offset =
WESTON_VEC3F(16.0 / 255.0, 128.0 / 255.0, 128.0 / 255.0);
switch(coefficients) {
case WESTON_COLOR_MATRIX_COEF_BT601:
cr_matrix->matrix =
WESTON_MAT3F(255.0 / 219.0, 0.0, 1.59602678,
255.0 / 219.0, -0.39176229, -0.81296764,
255.0 / 219.0, 2.01723214, 0.0);
return;
case WESTON_COLOR_MATRIX_COEF_BT709:
cr_matrix->matrix =
WESTON_MAT3F(255.0 / 219.0, 0.0, 1.79274107,
255.0 / 219.0, -0.21324861, -0.53290933,
255.0 / 219.0, 2.11240179, 0.0);
return;
case WESTON_COLOR_MATRIX_COEF_BT2020:
cr_matrix->matrix =
WESTON_MAT3F(255.0 / 219.0, 0.0, 1.67878795,
255.0 / 219.0, -0.18732610, -0.65046843,
255.0 / 219.0, 2.14177232, 0.0);
return;
default:
break;
}
}
weston_assert_not_reached(compositor,
"unknown coefficients or range value");
}
bool
weston_surface_check_pending_color_representation_valid(
const struct weston_surface *surface)
{
const struct weston_surface_state *pend = &surface->pending;
const struct weston_color_representation *cr =
&pend->color_representation;
struct weston_buffer *buffer = NULL;
bool format_is_yuv;
if (!surface->color_representation_resource)
return true;
if (cr->matrix_coefficients == WESTON_COLOR_MATRIX_COEF_UNSET &&
cr->quant_range == WESTON_COLOR_QUANT_RANGE_UNSET)
return true;
assert(cr->matrix_coefficients != WESTON_COLOR_MATRIX_COEF_UNSET &&
cr->quant_range != WESTON_COLOR_QUANT_RANGE_UNSET);
if (pend->status & WESTON_SURFACE_DIRTY_BUFFER) {
buffer = pend->buffer_ref.buffer;
} else if (surface->buffer_ref.buffer) {
buffer = surface->buffer_ref.buffer;
}
if (!buffer)
return true;
format_is_yuv = buffer->pixel_format->color_model == COLOR_MODEL_YUV;
if ((format_is_yuv &&
cr->matrix_coefficients == WESTON_COLOR_MATRIX_COEF_IDENTITY) ||
(!format_is_yuv &&
cr->matrix_coefficients != WESTON_COLOR_MATRIX_COEF_IDENTITY)) {
wl_resource_post_error(surface->color_representation_resource,
WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_PIXEL_FORMAT,
"wp_color_representation_surface_v1@%"PRIu32" "
"Buffer format %s not compatible "
"with matrix coefficients %u",
wl_resource_get_id(surface->resource),
buffer->pixel_format->drm_format_name,
cr->matrix_coefficients);
return false;
}
return true;
}
static void
destroy_color_representation(struct wl_resource *resource)
{
struct weston_surface *surface =
wl_resource_get_user_data(resource);
if (!surface)
return;
surface->color_representation_resource = NULL;
weston_reset_color_representation(&surface->pending.color_representation);
}
static void
cr_destroy(struct wl_client *client,
struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static void
cr_set_alpha_mode(struct wl_client *client,
struct wl_resource *resource,
uint32_t alpha_mode)
{
struct weston_surface *surface =
wl_resource_get_user_data(resource);
bool supported = false;
if (!surface) {
wl_resource_post_error(resource,
WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_INERT,
"wp_color_representation_surface_v1@%"PRIu32" "
"The object is inert.",
wl_resource_get_id(resource));
return;
}
weston_assert_ptr_eq(surface->compositor,
surface->color_representation_resource, resource);
for (unsigned int i = 0; i < ARRAY_LENGTH(supported_alpha_modes); i++) {
if (supported_alpha_modes[i] == alpha_mode) {
supported = true;
break;
}
}
if (!supported) {
wl_resource_post_error(resource,
WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_ALPHA_MODE,
"wp_color_representation_surface_v1@%"PRIu32" "
"Invalid alpha mode (%u)",
wl_resource_get_id(resource), alpha_mode);
return;
}
surface->pending.color_representation.alpha_mode = alpha_mode;
}
static void
cr_set_coefficients_and_range(struct wl_client *client,
struct wl_resource *resource,
uint32_t coefficients,
uint32_t range)
{
struct weston_surface *surface =
wl_resource_get_user_data(resource);
struct weston_color_representation *color_representation;
bool supported = false;
if (!surface) {
wl_resource_post_error(resource,
WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_INERT,
"wp_color_representation_surface_v1@%"PRIu32" "
"The object is inert.",
wl_resource_get_id(resource));
return;
}
weston_assert_ptr_eq(surface->compositor,
surface->color_representation_resource, resource);
color_representation = &surface->pending.color_representation;
for (unsigned int i = 0; i < ARRAY_LENGTH(supported_coeffs_and_ranges); i++) {
if (supported_coeffs_and_ranges[i].coefficients == coefficients &&
supported_coeffs_and_ranges[i].range == range) {
supported = true;
break;
}
}
if (!supported) {
wl_resource_post_error(resource,
WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_COEFFICIENTS,
"wp_color_representation_surface_v1@%"PRIu32" "
"Invalid coefficients (%u) or range (%u).",
wl_resource_get_id(resource), coefficients, range);
return;
}
switch (coefficients) {
case WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_IDENTITY:
color_representation->matrix_coefficients = WESTON_COLOR_MATRIX_COEF_IDENTITY;
break;
case WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_BT709:
color_representation->matrix_coefficients = WESTON_COLOR_MATRIX_COEF_BT709;
break;
case WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_BT601:
color_representation->matrix_coefficients = WESTON_COLOR_MATRIX_COEF_BT601;
break;
case WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_BT2020:
color_representation->matrix_coefficients = WESTON_COLOR_MATRIX_COEF_BT2020;
break;
default:
weston_assert_not_reached(surface->compositor, "unsupported coefficients");
}
switch (range) {
case WP_COLOR_REPRESENTATION_SURFACE_V1_RANGE_FULL:
color_representation->quant_range = WESTON_COLOR_QUANT_RANGE_FULL;
break;
case WP_COLOR_REPRESENTATION_SURFACE_V1_RANGE_LIMITED:
color_representation->quant_range = WESTON_COLOR_QUANT_RANGE_LIMITED;
break;
default:
weston_assert_not_reached(surface->compositor, "unsupported range");
}
}
static void
cr_set_chroma_location(struct wl_client *client,
struct wl_resource *resource,
uint32_t chroma_location)
{
struct weston_surface *surface =
wl_resource_get_user_data(resource);
struct weston_color_representation *color_representation;
if (!surface) {
wl_resource_post_error(resource,
WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_INERT,
"wp_color_representation_surface_v1@%"PRIu32" "
"The object is inert.",
wl_resource_get_id(resource));
return;
}
weston_assert_ptr_eq(surface->compositor,
surface->color_representation_resource, resource);
color_representation = &surface->pending.color_representation;
switch (chroma_location) {
case WP_COLOR_REPRESENTATION_SURFACE_V1_CHROMA_LOCATION_TYPE_0:
color_representation->chroma_location = WESTON_YCBCR_CHROMA_LOCATION_TYPE_0;
break;
case WP_COLOR_REPRESENTATION_SURFACE_V1_CHROMA_LOCATION_TYPE_1:
color_representation->chroma_location = WESTON_YCBCR_CHROMA_LOCATION_TYPE_1;
break;
case WP_COLOR_REPRESENTATION_SURFACE_V1_CHROMA_LOCATION_TYPE_2:
color_representation->chroma_location = WESTON_YCBCR_CHROMA_LOCATION_TYPE_2;
break;
case WP_COLOR_REPRESENTATION_SURFACE_V1_CHROMA_LOCATION_TYPE_3:
color_representation->chroma_location = WESTON_YCBCR_CHROMA_LOCATION_TYPE_3;
break;
case WP_COLOR_REPRESENTATION_SURFACE_V1_CHROMA_LOCATION_TYPE_4:
color_representation->chroma_location = WESTON_YCBCR_CHROMA_LOCATION_TYPE_4;
break;
case WP_COLOR_REPRESENTATION_SURFACE_V1_CHROMA_LOCATION_TYPE_5:
color_representation->chroma_location = WESTON_YCBCR_CHROMA_LOCATION_TYPE_5;
break;
default:
wl_resource_post_error(resource,
WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_CHROMA_LOCATION,
"wp_color_representation_surface_v1@%"PRIu32" "
"Invalid chroma location (%u).",
wl_resource_get_id(resource),
chroma_location);
return;
}
}
static const struct wp_color_representation_surface_v1_interface cr_implementation = {
.destroy = cr_destroy,
.set_alpha_mode = cr_set_alpha_mode,
.set_coefficients_and_range = cr_set_coefficients_and_range,
.set_chroma_location = cr_set_chroma_location,
};
static void
cr_manager_destroy(struct wl_client *client,
struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static void
cr_manager_get_surface(struct wl_client *client,
struct wl_resource *resource,
uint32_t id,
struct wl_resource *surface_resource)
{
struct weston_surface *surface =
wl_resource_get_user_data(surface_resource);
struct wl_resource *color_representation_resource;
if (surface->color_representation_resource) {
wl_resource_post_error(resource,
WP_COLOR_REPRESENTATION_MANAGER_V1_ERROR_SURFACE_EXISTS,
"a color representation surface for that surface already exists");
return;
}
color_representation_resource = wl_resource_create(client,
&wp_color_representation_surface_v1_interface,
wl_resource_get_version(resource), id);
if (color_representation_resource == NULL) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(color_representation_resource,
&cr_implementation, surface, destroy_color_representation);
surface->color_representation_resource = color_representation_resource;
}
static const struct wp_color_representation_manager_v1_interface
cr_manager_implementation = {
.destroy = cr_manager_destroy,
.get_surface = cr_manager_get_surface,
};
static void
bind_color_representation(struct wl_client *client, void *data, uint32_t version,
uint32_t id)
{
struct wl_resource *resource;
struct weston_compositor *compositor = data;
resource = wl_resource_create(client,
&wp_color_representation_manager_v1_interface, version, id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(resource, &cr_manager_implementation,
compositor, NULL);
for (unsigned int i = 0; i < ARRAY_LENGTH(supported_alpha_modes); i++) {
wp_color_representation_manager_v1_send_supported_alpha_mode(
resource,
supported_alpha_modes[i]);
}
for (unsigned int i = 0; i < ARRAY_LENGTH(supported_coeffs_and_ranges); i++) {
wp_color_representation_manager_v1_send_supported_coefficients_and_ranges(
resource,
supported_coeffs_and_ranges[i].coefficients,
supported_coeffs_and_ranges[i].range);
}
wp_color_representation_manager_v1_send_done(resource);
}
/** Advertise color-representation support
*
* Calling this initializes the color-representation protocol support, so that
* wp_color_representation_manager_v1_interface will be advertised to clients.
* Essentially it creates a global. Do not call this function multiple times in
* the compositor's lifetime. There is no way to deinit explicitly, globals will
* be reaped when the wl_display gets destroyed.
*
* \param compositor The compositor to init for.
* \return Zero on success, -1 on failure.
*/
int
weston_compositor_enable_color_representation_protocol(struct weston_compositor *compositor)
{
uint32_t version = 1;
if (!(compositor->capabilities & WESTON_CAP_COLOR_REP)) {
weston_log("Color representation not supported by renderer\n");
return 0;
}
if (!wl_global_create(compositor->wl_display,
&wp_color_representation_manager_v1_interface,
version, compositor, bind_color_representation))
return -1;
return 0;
}
static const struct weston_color_matrix_coef_info color_matrix_coef_info_map[] = {
{ WESTON_COLOR_MATRIX_COEF_UNSET, "unset", WDRM_PLANE_COLOR_ENCODING__COUNT },
{ WESTON_COLOR_MATRIX_COEF_IDENTITY, "default", WDRM_PLANE_COLOR_ENCODING__COUNT },
{ WESTON_COLOR_MATRIX_COEF_BT601, "BT.601", WDRM_PLANE_COLOR_ENCODING_BT601 },
{ WESTON_COLOR_MATRIX_COEF_BT709, "BT.709", WDRM_PLANE_COLOR_ENCODING_BT709 },
{ WESTON_COLOR_MATRIX_COEF_BT2020, "BT.2020", WDRM_PLANE_COLOR_ENCODING_BT2020 },
};
WL_EXPORT const struct weston_color_matrix_coef_info *
weston_color_matrix_coef_info_get(enum weston_color_matrix_coef coefficients)
{
for (unsigned i = 0; i < ARRAY_LENGTH(color_matrix_coef_info_map); i++)
if (color_matrix_coef_info_map[i].coefficients == coefficients)
return &color_matrix_coef_info_map[i];
return NULL;
}
static const struct weston_color_quant_range_info color_quant_range_info_map[] = {
{ WESTON_COLOR_QUANT_RANGE_UNSET, "unset", WDRM_PLANE_COLOR_RANGE__COUNT },
{ WESTON_COLOR_QUANT_RANGE_FULL, "full", WDRM_PLANE_COLOR_RANGE_FULL },
{ WESTON_COLOR_QUANT_RANGE_LIMITED, "limited", WDRM_PLANE_COLOR_RANGE_LIMITED },
};
WL_EXPORT const struct weston_color_quant_range_info *
weston_color_quant_range_info_get(enum weston_color_quant_range range)
{
for (unsigned i = 0; i < ARRAY_LENGTH(color_quant_range_info_map); i++)
if (color_quant_range_info_map[i].range == range)
return &color_quant_range_info_map[i];
return NULL;
}

View file

@ -0,0 +1,78 @@
/*
* Copyright 2025 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include <libweston/libweston.h>
#include "backend-drm/drm-kms-enums.h"
void
weston_reset_color_representation(struct weston_color_representation *color_rep);
struct weston_color_representation
weston_fill_color_representation(const struct weston_color_representation *color_rep_in,
const struct pixel_format_info *info);
enum weston_cr_comparison_flag {
WESTON_CR_COMPARISON_FLAG_NONE = 0,
WESTON_CR_COMPARISON_FLAG_IGNORE_ALPHA = 1,
WESTON_CR_COMPARISON_FLAG_IGNORE_CHROMA_LOCATION = 2,
};
bool
weston_color_representation_equal(struct weston_color_representation *color_rep_A,
struct weston_color_representation *color_rep_B,
enum weston_cr_comparison_flag flags);
void
weston_get_color_representation_matrix(struct weston_compositor *compositor,
enum weston_color_matrix_coef coefficients,
enum weston_color_quant_range range,
struct weston_color_representation_matrix *cr_matrix);
bool
weston_surface_check_pending_color_representation_valid(const struct weston_surface *surface);
int
weston_compositor_enable_color_representation_protocol(struct weston_compositor *compositor);
struct weston_color_quant_range_info {
enum weston_color_quant_range range;
const char *name;
enum wdrm_plane_color_range wdrm;
};
const struct weston_color_quant_range_info *
weston_color_quant_range_info_get(enum weston_color_quant_range range);
struct weston_color_matrix_coef_info {
enum weston_color_matrix_coef coefficients;
const char *name;
enum wdrm_plane_color_encoding wdrm;
};
const struct weston_color_matrix_coef_info *
weston_color_matrix_coef_info_get(enum weston_color_matrix_coef coefficients);

View file

@ -84,6 +84,7 @@
#include "libweston-internal.h"
#include "color.h"
#include "color-management.h"
#include "color-representation.h"
#include "id-number-allocator.h"
#include "output-capture.h"
#include "pixman-renderer.h"
@ -1014,6 +1015,8 @@ weston_surface_create(struct weston_compositor *compositor)
/* Also part of the CM&HDR protocol extension implementation. */
weston_surface_update_preferred_color_profile(surface);
weston_reset_color_representation(&surface->color_representation);
wl_list_init(&surface->fifo_barrier_link);
return surface;
@ -2757,6 +2760,11 @@ destroy_surface(struct wl_resource *resource)
NULL);
}
if (surface->color_representation_resource) {
wl_resource_set_user_data(surface->color_representation_resource,
NULL);
}
weston_surface_unref(surface);
}
@ -5063,6 +5071,9 @@ surface_commit(struct wl_client *client, struct wl_resource *resource)
return;
}
if (!weston_surface_check_pending_color_representation_valid(surface))
return;
weston_surface_commit(surface);
}
@ -10136,6 +10147,9 @@ weston_compositor_backends_loaded(struct weston_compositor *compositor)
return -1;
}
if (weston_compositor_enable_color_representation_protocol(compositor) < 0)
return -1;
return 0;
}

View file

@ -18,6 +18,7 @@ srcs_libweston = [
'color.c',
'color-properties.c',
'color-management.c',
'color-representation.c',
'color-noop.c',
'color-operations.c',
'color-profile-param-builder.c',
@ -49,6 +50,8 @@ srcs_libweston = [
'weston-direct-display.c',
color_management_v1_protocol_c,
color_management_v1_server_protocol_h,
color_representation_v1_protocol_c,
color_representation_v1_server_protocol_h,
commit_timing_v1_protocol_c,
commit_timing_v1_server_protocol_h,
fifo_v1_protocol_c,

View file

@ -95,29 +95,16 @@ compile_const bool c_need_color_pipeline =
compile_const bool c_need_straight_alpha =
c_need_color_pipeline || c_color_effect != SHADER_COLOR_EFFECT_NONE;
uniform HIGHPRECISION mat3 yuv_coefficients;
uniform HIGHPRECISION vec3 yuv_offsets;
vec4
yuva2rgba(vec4 yuva)
{
vec4 color_out;
float Y, su, sv;
/* ITU-R BT.601 & BT.709 quantization (limited range) */
Y = 255.0/219.0 * (yuva.x - 16.0/255.0);
/* Remove offset 128/255, but the 255/224 multiplier comes later */
su = yuva.y - 128.0/255.0;
sv = yuva.z - 128.0/255.0;
/*
* ITU-R BT.709 encoding coefficients (inverse), with the
* 255/224 limited range multiplier already included in the
* factors for su (Cb) and sv (Cr).
*/
color_out.r = Y + 1.79274107 * sv;
color_out.g = Y - 0.21324861 * su - 0.53290933 * sv;
color_out.b = Y + 2.11240179 * su;
color_out.rgb = yuv_coefficients * (yuva.xyz - yuv_offsets);
color_out.rgb *= yuva.w;
color_out.a = yuva.w;
return color_out;

View file

@ -390,6 +390,9 @@ struct gl_shader_config {
union gl_shader_config_color_curve color_pre_curve;
union gl_shader_config_color_mapping color_mapping;
union gl_shader_config_color_curve color_post_curve;
enum weston_color_matrix_coef yuv_coefficients;
enum weston_color_quant_range yuv_range;
};
struct gl_renderer {

View file

@ -49,6 +49,7 @@
#include "timeline.h"
#include "color.h"
#include "color-representation.h"
#include "gl-renderer.h"
#include "gl-renderer-internal.h"
#include "vertex-clipping.h"
@ -63,6 +64,7 @@
#include "shared/platform.h"
#include "shared/string-helpers.h"
#include "shared/timespec-util.h"
#include "shared/weston-assert.h"
#include "shared/weston-drm-fourcc.h"
#include "shared/weston-egl-ext.h"
#include "shared/xalloc.h"
@ -251,6 +253,11 @@ struct yuv_format_descriptor {
struct yuv_plane_descriptor plane[3];
};
struct gl_color_egl_image {
EGLImageKHR image;
struct weston_color_representation import_color_rep;
};
struct gl_buffer_state {
struct gl_renderer *gr;
@ -265,13 +272,24 @@ struct gl_buffer_state {
int num_images;
enum gl_shader_texture_variant shader_variant;
struct weston_color_representation egl_image_import_color_rep;
/* For non-default color representations we need to re-import
* EGLImageKHR with different attributes for
* EGL_YUV_COLOR_SPACE_HINT_EXT, EGL_SAMPLE_RANGE_HINT_EXT and, in the
* future, EGL_YUV_CHROMA_HORIZONTAL_SITING_HINT_EXT and
* EGL_YUV_CHROMA_VERTICAL_SITING_HINT_EXT */
struct wl_array reimported_egl_images;
struct gl_color_egl_image *active_reimported_egl_image;
/* These values can refer to the EGLImageKHR's in either images[3] or
* active_reimported_egl_image->image. Textures will get destroyed and
* recreated when switching. */
struct gl_format_info texture_format[3];
struct gl_texture_parameters parameters[3];
GLuint textures[3];
int num_textures;
bool specified;
struct wl_listener destroy_listener;
};
@ -888,7 +906,9 @@ gl_renderer_create_renderbuffer(struct weston_output *output,
}
static EGLImageKHR
import_simple_dmabuf(struct gl_renderer *, const struct dmabuf_attributes *);
import_simple_dmabuf(struct gl_renderer *,
const struct dmabuf_attributes *,
const struct weston_color_representation *);
static weston_renderbuffer_t
gl_renderer_create_renderbuffer_dmabuf(struct weston_output *output,
@ -899,10 +919,14 @@ gl_renderer_create_renderbuffer_dmabuf(struct weston_output *output,
struct gl_renderer *gr = get_renderer(output->compositor);
struct dmabuf_attributes *attributes = dmabuf->attributes;
struct gl_renderbuffer *renderbuffer;
const struct pixel_format_info *info;
EGLImageKHR image;
GLuint fb, rb;
image = import_simple_dmabuf(gr, attributes);
info = pixel_format_get_info(attributes->format);
assert(info->color_model != COLOR_MODEL_YUV);
image = import_simple_dmabuf(gr, attributes, NULL);
if (image == EGL_NO_IMAGE_KHR) {
weston_log("Failed to import dmabuf\n");
return NULL;
@ -1442,6 +1466,142 @@ prepare_solid_draw(struct gl_shader_config *sconf,
return true;
}
static void
recreate_and_specify_textures(struct gl_buffer_state *gb,
EGLImageKHR *images)
{
struct gl_renderer *gr = gb->gr;
GLenum target;
if (gb->num_textures > 0)
glDeleteTextures(gb->num_textures, gb->textures);
glGenTextures(gb->num_images, gb->textures);
gb->num_textures = gb->num_images;
target = gl_shader_texture_variant_get_target(gb->shader_variant);
for (int i = 0; i < gb->num_images; i++) {
gl_texture_parameters_init(gb->gr, &gb->parameters[i], target,
NULL, NULL, gb->texture_format[i].swizzles.array, false);
}
for (int i = 0; i < gb->num_images; ++i) {
glBindTexture(gb->parameters[i].target, gb->textures[i]);
if (gl_extensions_has(gr, EXTENSION_EXT_EGL_IMAGE_STORAGE)) {
gr->image_target_tex_storage(gb->parameters[i].target,
images[i], NULL);
} else {
gr->image_target_texture_2d(gb->parameters[i].target,
images[i]);
}
}
}
static struct gl_color_egl_image *
ensure_color_egl_image(struct gl_surface_state *gs,
struct weston_color_representation *color_rep)
{
struct weston_buffer *buffer = gs->buffer_ref.buffer;
struct gl_buffer_state *gb = gs->buffer;
struct gl_color_egl_image *color_egl_image = NULL;
struct gl_color_egl_image *color_egl_image_cand;
wl_array_for_each(color_egl_image_cand, &gb->reimported_egl_images) {
if (!weston_color_representation_equal(&color_egl_image_cand->import_color_rep,
color_rep,
WESTON_CR_COMPARISON_FLAG_IGNORE_ALPHA |
WESTON_CR_COMPARISON_FLAG_IGNORE_CHROMA_LOCATION))
continue;
color_egl_image = color_egl_image_cand;
break;
}
if (!color_egl_image) {
struct linux_dmabuf_buffer *dmabuf;
EGLImageKHR image;
assert(buffer->dmabuf);
dmabuf = buffer->dmabuf;
image = import_simple_dmabuf(gb->gr,
&dmabuf->attributes,
color_rep);
if (image == EGL_NO_IMAGE_KHR)
return NULL;
color_egl_image = wl_array_add(&gb->reimported_egl_images,
sizeof *color_egl_image);
assert(color_egl_image);
color_egl_image->image = image;
color_egl_image->import_color_rep = *color_rep;
}
return color_egl_image;
}
static void
ensure_images_and_textures(struct gl_surface_state *gs)
{
struct weston_surface *surface = gs->surface;
struct weston_compositor *compositor = surface->compositor;
struct weston_buffer *buffer = gs->buffer_ref.buffer;
const struct pixel_format_info *info = buffer->pixel_format;
struct gl_buffer_state *gb = gs->buffer;
struct weston_color_representation color_rep;
if (buffer->type != WESTON_BUFFER_DMABUF ||
(gb->shader_variant != SHADER_VARIANT_RGBA &&
gb->shader_variant != SHADER_VARIANT_EXTERNAL)) {
if (gb->num_textures == 0)
recreate_and_specify_textures(gb, gb->images);
return;
}
color_rep =
weston_fill_color_representation(&surface->color_representation,
info);
/* Check if we need to re-import the EGLImage with non-default YCbCr
* attributes. */
if (!weston_color_representation_equal(&gb->egl_image_import_color_rep,
&color_rep,
WESTON_CR_COMPARISON_FLAG_IGNORE_ALPHA |
WESTON_CR_COMPARISON_FLAG_IGNORE_CHROMA_LOCATION)) {
struct gl_color_egl_image *color_egl_image = NULL;
color_egl_image = ensure_color_egl_image(gs, &color_rep);
if (!color_egl_image) {
weston_log("GL-renderer: failed to re-import EGLImageKHR\n");
goto out;
}
if (gb->active_reimported_egl_image == color_egl_image) {
weston_assert_int_gt(compositor, gb->num_textures, 0);
return;
}
weston_assert_int_eq(compositor, gb->num_images, 1);
recreate_and_specify_textures(gb, &color_egl_image->image);
gb->active_reimported_egl_image = color_egl_image;
return;
}
/* We switched from an EGLImage with non-default YCbCr attributes to
* default values. Recreate and bind textures. */
if (gb->active_reimported_egl_image != NULL) {
weston_assert_int_eq(compositor, gb->num_images, 1);
recreate_and_specify_textures(gb, gb->images);
gb->active_reimported_egl_image = NULL;
return;
}
out:
/* Ensure we create and bind textures. */
if (gb->num_textures == 0)
recreate_and_specify_textures(gb, gb->images);
}
static void
gl_shader_config_set_input_textures(struct gl_shader_config *sconf,
struct gl_buffer_state *gb)
@ -1464,9 +1624,12 @@ prepare_textured_draw(struct gl_shader_config *sconf,
struct gl_buffer_state *gb = gs->buffer;
struct gl_output_state *go = get_output_state(pnode->output);
struct weston_buffer *buffer = gs->buffer_ref.buffer;
struct weston_color_representation color_rep;
GLint filter;
int i;
ensure_images_and_textures(gs);
*sconf = (struct gl_shader_config) {
.req.texcoord_input = SHADER_TEXCOORD_INPUT_SURFACE,
.projection = pnode->view->transform.matrix,
@ -1508,6 +1671,12 @@ prepare_textured_draw(struct gl_shader_config *sconf,
return false;
}
color_rep =
weston_fill_color_representation(&pnode->surface->color_representation,
buffer->pixel_format);
sconf->yuv_coefficients = color_rep.matrix_coefficients;
sconf->yuv_range = color_rep.quant_range;
return true;
}
@ -2873,6 +3042,7 @@ done:
static void
destroy_buffer_state(struct gl_buffer_state *gb)
{
struct gl_color_egl_image *color_egl_image;
int i;
glDeleteTextures(gb->num_textures, gb->textures);
@ -2880,6 +3050,10 @@ destroy_buffer_state(struct gl_buffer_state *gb)
for (i = 0; i < gb->num_images; i++)
gb->gr->destroy_image(gb->gr->egl_display, gb->images[i]);
wl_array_for_each(color_egl_image, &gb->reimported_egl_images)
gb->gr->destroy_image(gb->gr->egl_display, color_egl_image->image);
wl_array_release(&gb->reimported_egl_images);
pixman_region32_fini(&gb->texture_damage);
wl_list_remove(&gb->destroy_listener.link);
@ -2899,23 +3073,6 @@ handle_buffer_destroy(struct wl_listener *listener, void *data)
destroy_buffer_state(gb);
}
static void
ensure_textures(struct gl_buffer_state *gb, GLenum target, int num_textures)
{
int i;
assert(gb->num_textures == 0);
glGenTextures(num_textures, gb->textures);
gb->num_textures = num_textures;
for (i = 0; i < num_textures; i++)
gl_texture_parameters_init(gb->gr, &gb->parameters[i], target,
NULL, NULL,
gb->texture_format[i].swizzles.array,
false);
}
static void
gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer)
{
@ -3077,7 +3234,6 @@ gl_renderer_fill_buffer_info(struct weston_compositor *ec,
struct gl_buffer_state *gb;
EGLint format;
uint32_t fourcc;
GLenum target;
EGLint y_inverted;
bool rgb, ret = true;
int i;
@ -3195,9 +3351,6 @@ gl_renderer_fill_buffer_info(struct weston_compositor *ec,
}
}
target = gl_shader_texture_variant_get_target(gb->shader_variant);
ensure_textures(gb, target, gb->num_images);
buffer->renderer_private = gb;
gb->destroy_listener.notify = handle_buffer_destroy;
wl_signal_add(&buffer->destroy_signal, &gb->destroy_listener);
@ -3223,8 +3376,10 @@ gl_renderer_destroy_dmabuf(struct linux_dmabuf_buffer *dmabuf)
static EGLImageKHR
import_simple_dmabuf(struct gl_renderer *gr,
const struct dmabuf_attributes *attributes)
const struct dmabuf_attributes *attributes,
const struct weston_color_representation *color_rep)
{
const struct pixel_format_info *info;
EGLint attribs[53];
int atti = 0;
bool has_modifier;
@ -3314,11 +3469,41 @@ import_simple_dmabuf(struct gl_renderer *gr,
}
}
attribs[atti++] = EGL_YUV_COLOR_SPACE_HINT_EXT;
attribs[atti++] = EGL_ITU_REC709_EXT;
info = pixel_format_get_info(attributes->format);
assert(info);
if (info->color_model == COLOR_MODEL_YUV) {
assert(color_rep);
attribs[atti++] = EGL_SAMPLE_RANGE_HINT_EXT;
attribs[atti++] = EGL_YUV_NARROW_RANGE_EXT;
attribs[atti++] = EGL_YUV_COLOR_SPACE_HINT_EXT;
switch (color_rep->matrix_coefficients) {
case WESTON_COLOR_MATRIX_COEF_BT601:
attribs[atti++] = EGL_ITU_REC601_EXT;
break;
case WESTON_COLOR_MATRIX_COEF_BT709:
attribs[atti++] = EGL_ITU_REC709_EXT;
break;
case WESTON_COLOR_MATRIX_COEF_BT2020:
attribs[atti++] = EGL_ITU_REC2020_EXT;
break;
case WESTON_COLOR_MATRIX_COEF_UNSET:
case WESTON_COLOR_MATRIX_COEF_IDENTITY:
weston_assert_not_reached(gr->compositor,
"invalid matrix coefficients");
}
attribs[atti++] = EGL_SAMPLE_RANGE_HINT_EXT;
switch (color_rep->quant_range) {
case WESTON_COLOR_QUANT_RANGE_LIMITED:
attribs[atti++] = EGL_YUV_NARROW_RANGE_EXT;
break;
case WESTON_COLOR_QUANT_RANGE_FULL:
attribs[atti++] = EGL_YUV_FULL_RANGE_EXT;
break;
case WESTON_COLOR_QUANT_RANGE_UNSET:
weston_assert_not_reached(gr->compositor,
"invalid quantization range");
}
}
attribs[atti++] = EGL_NONE;
@ -3333,6 +3518,7 @@ import_dmabuf_single_plane(struct gl_renderer *gr,
const struct dmabuf_attributes *attributes,
const struct yuv_plane_descriptor *descriptor)
{
const struct pixel_format_info *plane_info;
struct dmabuf_attributes plane;
EGLImageKHR image;
char fmt[4];
@ -3348,7 +3534,10 @@ import_dmabuf_single_plane(struct gl_renderer *gr,
plane.stride[0] = attributes->stride[descriptor->plane_index];
plane.modifier = attributes->modifier;
image = import_simple_dmabuf(gr, &plane);
plane_info = pixel_format_get_info(plane.format);
assert(plane_info->color_model != COLOR_MODEL_YUV);
image = import_simple_dmabuf(gr, &plane, NULL);
if (image == EGL_NO_IMAGE_KHR) {
weston_log("Failed to import plane %d as %.4s\n",
descriptor->plane_index,
@ -3368,7 +3557,6 @@ import_yuv_dmabuf(struct gl_renderer *gr, struct gl_buffer_state *gb,
const struct yuv_format_descriptor *format = NULL;
const struct pixel_format_info *info;
int plane_count;
GLenum target;
char fmt[4];
for (i = 0; i < ARRAY_LENGTH(yuv_formats); ++i) {
@ -3419,9 +3607,6 @@ import_yuv_dmabuf(struct gl_renderer *gr, struct gl_buffer_state *gb,
gb->num_images = format->output_planes;
gb->shader_variant = format->shader_variant;
target = gl_shader_texture_variant_get_target(gb->shader_variant);
ensure_textures(gb, target, gb->num_images);
return true;
}
@ -3514,6 +3699,7 @@ import_dmabuf(struct gl_renderer *gr,
EGLImageKHR egl_image;
struct gl_buffer_state *gb;
const struct pixel_format_info *info;
struct weston_color_representation color_rep;
const struct weston_testsuite_quirks *quirks;
info = pixel_format_get_info(dmabuf->attributes.format);
@ -3533,13 +3719,18 @@ import_dmabuf(struct gl_renderer *gr,
info->color_model == COLOR_MODEL_YUV)
goto import_yuv;
egl_image = import_simple_dmabuf(gr, &dmabuf->attributes);
weston_reset_color_representation(&color_rep);
color_rep = weston_fill_color_representation(&color_rep, info);
egl_image = import_simple_dmabuf(gr, &dmabuf->attributes,
&color_rep);
if (egl_image != EGL_NO_IMAGE_KHR) {
const GLint swizzles[] = { GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA };
GLenum target = choose_texture_target(gr, &dmabuf->attributes);
gb->num_images = 1;
gb->images[0] = egl_image;
gb->egl_image_import_color_rep = color_rep;
/* The driver defines its own swizzles internally in the case of
* a successful dma-buf import so just set default values. */
@ -3553,8 +3744,6 @@ import_dmabuf(struct gl_renderer *gr,
gb->shader_variant = SHADER_VARIANT_EXTERNAL;
}
ensure_textures(gb, target, gb->num_images);
return gb;
}
@ -3704,30 +3893,12 @@ static void
gl_renderer_attach_buffer(struct weston_surface *surface,
struct weston_buffer *buffer)
{
struct gl_renderer *gr = get_renderer(surface->compositor);
struct gl_surface_state *gs = get_surface_state(surface);
struct gl_buffer_state *gb;
int i;
assert(buffer->renderer_private);
gb = buffer->renderer_private;
gs->buffer = gb;
if (gb->specified)
return;
for (i = 0; i < gb->num_images; ++i) {
glBindTexture(gb->parameters[i].target, gb->textures[i]);
if (gl_extensions_has(gr, EXTENSION_EXT_EGL_IMAGE_STORAGE))
gr->image_target_tex_storage(gb->parameters[i].target,
gb->images[i], NULL);
else
gr->image_target_texture_2d(gb->parameters[i].target,
gb->images[i]);
}
gb->specified = true;
}
static const struct weston_drm_format_array *
@ -4785,6 +4956,7 @@ gl_renderer_display_create(struct weston_compositor *ec,
ec->capabilities |= WESTON_CAP_EXPLICIT_SYNC;
if (gl_features_has(gr, FEATURE_COLOR_TRANSFORMS))
ec->capabilities |= WESTON_CAP_COLOR_OPS;
ec->capabilities |= WESTON_CAP_COLOR_REP;
return 0;

View file

@ -34,6 +34,7 @@
#include <string.h>
#include <assert.h>
#include <libweston/color-representation.h>
#include <libweston/libweston.h>
#include <libweston/weston-log.h>
@ -99,6 +100,8 @@ struct gl_shader {
union gl_shader_color_curve_uniforms color_pre_curve;
union gl_shader_color_mapping_uniforms color_mapping;
union gl_shader_color_curve_uniforms color_post_curve;
GLint yuv_offsets_uniform;
GLint yuv_coefficients_uniform;
};
static const char *
@ -503,6 +506,12 @@ gl_shader_create(struct gl_renderer *gr,
case SHADER_COLOR_MAPPING_IDENTITY:
break;
}
shader->yuv_coefficients_uniform =
glGetUniformLocation(shader->program, "yuv_coefficients");
shader->yuv_offsets_uniform = glGetUniformLocation(shader->program,
"yuv_offsets");
free(conf);
wl_list_insert(&gr->shader_list, &shader->link);
@ -752,6 +761,28 @@ gl_shader_load_config_mapping(struct weston_compositor *compositor,
weston_assert_not_reached(compositor, "unknown enum gl_shader_color_mapping value");
}
static void
gl_shader_load_config_representation(struct weston_compositor *compositor,
struct gl_shader *shader,
const struct gl_shader_config *sconf)
{
struct weston_color_representation_matrix cr_matrix;
if (sconf->yuv_coefficients == WESTON_COLOR_MATRIX_COEF_UNSET ||
sconf->yuv_coefficients == WESTON_COLOR_MATRIX_COEF_IDENTITY) {
assert(shader->yuv_coefficients_uniform == -1);
assert(shader->yuv_offsets_uniform == -1);
return;
}
weston_get_color_representation_matrix(compositor,
sconf->yuv_coefficients, sconf->yuv_range, &cr_matrix);
glUniformMatrix3fv(shader->yuv_coefficients_uniform, 1, GL_FALSE,
cr_matrix.matrix.colmaj);
glUniform3fv(shader->yuv_offsets_uniform, 1, cr_matrix.offset.el);
}
bool
gl_shader_texture_variant_can_be_premult(enum gl_shader_texture_variant v)
{
@ -859,6 +890,8 @@ gl_shader_load_config(struct gl_renderer *gr,
&sconf->color_post_curve, &shader->color_post_curve,
TEX_UNIT_COLOR_POST_CURVE);
gl_shader_load_config_representation(gr->compositor, shader, sconf);
if (sconf->req.wireframe)
glUniform1i(shader->tex_uniform_wireframe, TEX_UNIT_WIREFRAME);

View file

@ -34,6 +34,7 @@
#include "libweston-internal.h"
#include "backend.h"
#include "color-representation.h"
#include "pixel-formats.h"
#include "shared/fd-util.h"
#include "shared/timespec-util.h"
@ -156,6 +157,8 @@ weston_surface_state_init(struct weston_surface *surface,
state->color_profile = NULL;
state->render_intent = NULL;
weston_reset_color_representation(&state->color_representation);
state->fifo_barrier = false;
state->fifo_wait = false;
@ -376,6 +379,11 @@ weston_surface_apply_state(struct weston_surface *surface,
/* wp_presentation.feedback */
weston_presentation_feedback_discard_list(&surface->feedback_list);
/* wp_color_representation_v1.set_alpha_mode */
/* wp_color_representation_v1.set_coefficients_and_range */
/* wp_color_representation_v1.set_chroma_location */
surface->color_representation = state->color_representation;
/* wl_surface.attach */
if (status & WESTON_SURFACE_DIRTY_BUFFER) {
/* zwp_surface_synchronization_v1.set_acquire_fence */
@ -650,6 +658,8 @@ weston_surface_state_merge_from(struct weston_surface_state *dst,
dst->buffer_viewport.buffer = src->buffer_viewport.buffer;
dst->buffer_viewport.surface = src->buffer_viewport.surface;
dst->color_representation = src->color_representation;
weston_buffer_reference(&src->buffer_ref,
NULL, BUFFER_WILL_NOT_BE_ACCESSED);

View file

@ -1,7 +1,7 @@
dep_scanner = dependency('wayland-scanner', native: true)
prog_scanner = find_program(dep_scanner.get_variable(pkgconfig: 'wayland_scanner'))
dep_wp = dependency('wayland-protocols', version: '>= 1.41',
dep_wp = dependency('wayland-protocols', version: '>= 1.46',
fallback: ['wayland-protocols', 'wayland_protocols'])
dir_wp_base = dep_wp.get_variable(pkgconfig: 'pkgdatadir', internal: 'pkgdatadir')
@ -17,6 +17,7 @@ install_data(
generated_protocols = [
[ 'color-management', 'staging', 'v1' ],
[ 'color-representation', 'staging', 'v1' ],
[ 'commit-timing', 'staging', 'v1' ],
[ 'fifo', 'staging', 'v1' ],
[ 'fullscreen-shell', 'unstable', 'v1' ],

View file

@ -0,0 +1,326 @@
/*
* Copyright 2025 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "config.h"
#include "color-representation-common.h"
#include "image-iter.h"
#include "pixel-formats.h"
#include "shared/weston-drm-fourcc.h"
#include "xdg-client-helper.h"
static void
presentation_feedback_handle_sync_output(void *data,
struct wp_presentation_feedback *feedback,
struct wl_output *output)
{
}
static void
presentation_feedback_handle_presented(void *data,
struct wp_presentation_feedback *feedback,
uint32_t tv_sec_hi,
uint32_t tv_sec_lo,
uint32_t tv_nsec,
uint32_t refresh,
uint32_t seq_hi,
uint32_t seq_lo,
uint32_t flags)
{
enum feedback_result *result = data;
bool zero_copy = flags & WP_PRESENTATION_FEEDBACK_KIND_ZERO_COPY;
if (zero_copy)
*result = FB_PRESENTED_ZERO_COPY;
else
*result = FB_PRESENTED;
wp_presentation_feedback_destroy(feedback);
}
static void
presentation_feedback_handle_discarded(void *data,
struct wp_presentation_feedback *feedback)
{
enum feedback_result *result = data;
*result = FB_DISCARDED;
wp_presentation_feedback_destroy(feedback);
}
static const struct wp_presentation_feedback_listener presentation_feedback_listener = {
.sync_output = presentation_feedback_handle_sync_output,
.presented = presentation_feedback_handle_presented,
.discarded = presentation_feedback_handle_discarded,
};
static void
presentation_wait_nofail(struct client *client, enum feedback_result *result)
{
while (*result == FB_PENDING) {
if (!test_assert_int_ge(wl_display_dispatch(client->wl_display), 0))
break;
}
test_assert_u64_ne(*result, FB_PENDING);
}
static void
x8r8g8b8_to_ycbcr8(uint32_t xrgb,
const struct color_state *color_state,
uint8_t *y_out, uint8_t *cb_out, uint8_t *cr_out)
{
double y, cb, cr;
double r = (xrgb >> 16) & 0xff;
double g = (xrgb >> 8) & 0xff;
double b = (xrgb >> 0) & 0xff;
/* normalize to [0.0, 1.0] */
r /= 255.0;
g /= 255.0;
b /= 255.0;
/* Y normalized to [0.0, 1.0], Cb and Cr [-0.5, 0.5] */
switch ((int)color_state->coefficients) {
case 0:
case WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_BT709:
/* We choose BT709 as default */
y = 0.2126 * r + 0.7152 * g + 0.0722 * b;
cr = (r - y) / 1.5748;
cb = (b - y) / 1.8556;
break;
case WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_BT601:
y = 0.299 * r + 0.587 * g + 0.114 * b;
cr = (r - y) / 1.402;
cb = (b - y) / 1.772;
break;
case WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_BT2020:
y = 0.2627 * r + 0.678 * g + 0.0593 * b;
cr = (r - y) / 1.4746;
cb = (b - y) / 1.8814;
break;
case WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_IDENTITY:
case WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_FCC:
/* For protocol error testing ensure we create invalid output */
y = 0;
cr = 0;
cb = 0;
break;
default:
test_assert_not_reached("Coefficients not handled");
}
switch ((int)color_state->range) {
case 0:
case WP_COLOR_REPRESENTATION_SURFACE_V1_RANGE_LIMITED:
/* We choose narrow range as default */
*y_out = round(219.0 * y + 16.0);
if (cr_out)
*cr_out = round(224.0 * cr + 128.0);
if (cb_out)
*cb_out = round(224.0 * cb + 128.0);
break;
case WP_COLOR_REPRESENTATION_SURFACE_V1_RANGE_FULL:
*y_out = round(255.0 * y);
if (cr_out)
*cr_out = round(255.0 * cr + 128.0);
if (cb_out)
*cb_out = round(255.0 * cb + 128.0);
break;
default:
test_assert_not_reached("Range not handled");
}
}
static struct client_buffer*
create_and_fill_nv12_buffer_with_cake(struct client *client,
enum client_buffer_type buffer_type,
const struct color_state *color_state)
{
const struct pixel_format_info *fmt_info;
struct client_buffer *buffer;
pixman_image_t *rgb_image;
struct image_header src;
uint8_t *y_base;
uint16_t *uv_base;
char *fname;
int width, height;
fmt_info = pixel_format_get_info(DRM_FORMAT_NV12);
width = height = 256;
switch (buffer_type) {
case CLIENT_BUFFER_TYPE_SHM:
buffer = client_buffer_util_create_shm_buffer(client->wl_shm,
fmt_info,
width,
height);
break;
case CLIENT_BUFFER_TYPE_DMABUF:
buffer = client_buffer_util_create_dmabuf_buffer(client->wl_display,
client->dmabuf,
fmt_info,
width,
height);
break;
default:
test_assert_not_reached("Buffer type not handled");
break;
}
fname = image_filename("chocolate-cake");
rgb_image = load_image_from_png(fname);
free(fname);
test_assert_ptr_not_null(rgb_image);
src = image_header_from(rgb_image);
y_base = buffer->data + buffer->offsets[0];
uv_base = (uint16_t *)(buffer->data + buffer->offsets[1]);
client_buffer_util_maybe_sync_dmabuf_start(buffer);
for (int y = 0; y < src.height; y++) {
uint32_t *rgb_row;
uint8_t *y_row;
uint16_t *uv_row;
rgb_row = image_header_get_row_u32(&src, y / 2 * 2);
y_row = y_base + y * buffer->strides[0];
uv_row = uv_base + (y / 2) * (buffer->strides[1] / sizeof(uint16_t));
for (int x = 0; x < src.width; x++) {
uint32_t argb;
uint8_t cr;
uint8_t cb;
/*
* Sub-sample the source image instead, so that U and V
* sub-sampling does not require proper
* filtering/averaging/siting.
*/
argb = *(rgb_row + x / 2 * 2);
/*
* A stupid way of "sub-sampling" chroma. This does not
* do the necessary filtering/averaging/siting.
*/
if ((y & 1) == 0 && (x & 1) == 0) {
x8r8g8b8_to_ycbcr8(argb, color_state, y_row + x,
&cb, &cr);
*(uv_row + x / 2) = ((uint16_t) cb) |
((uint16_t) cr << 8);
} else {
x8r8g8b8_to_ycbcr8(argb, color_state, y_row + x,
NULL, NULL);
}
}
}
client_buffer_util_maybe_sync_dmabuf_end(buffer);
pixman_image_unref(rgb_image);
return buffer;
}
/*
* Test that a fullscreen client with smaller-than-fullscreen-sized NV12 buffer
* is correctly rendered with various YCbCr matrix coefficients and range
* combinations.
*/
enum test_result_code
test_color_representation(const struct color_state *color_state,
enum client_buffer_type buffer_type,
enum feedback_result expected_result)
{
struct xdg_client *xdg_client;
struct client *client;
struct xdg_surface_data *xdg_surface;
struct wl_surface *surface;
struct client_buffer *buffer;
struct wp_presentation_feedback *presentation_feedback;
struct wp_color_representation_surface_v1 *color_representation_surface = NULL;
enum feedback_result result;
struct buffer *screenshot;
bool match;
xdg_client = create_xdg_client();
client = xdg_client->client;
xdg_surface = create_xdg_surface(xdg_client);
surface = xdg_surface->surface->wl_surface;
xdg_surface_make_toplevel(xdg_surface,
"weston.test.color-representation", "one");
xdg_toplevel_set_fullscreen(xdg_surface->xdg_toplevel, NULL);
xdg_surface_wait_configure(xdg_surface);
buffer = create_and_fill_nv12_buffer_with_cake(client, buffer_type,
color_state);
wl_surface_attach(surface, buffer->wl_buffer, 0, 0);
wl_surface_damage(surface, 0, 0, INT32_MAX, INT32_MAX);
xdg_surface_maybe_ack_configure(xdg_surface);
if (color_state->create_color_representation_surface) {
color_representation_surface =
wp_color_representation_manager_v1_get_surface(
client->color_representation, surface);
if (color_state->coefficients != 0)
wp_color_representation_surface_v1_set_coefficients_and_range(
color_representation_surface,
color_state->coefficients, color_state->range);
}
result = FB_PENDING;
presentation_feedback = wp_presentation_feedback(client->presentation,
surface);
wp_presentation_feedback_add_listener(presentation_feedback,
&presentation_feedback_listener,
&result);
wl_surface_commit(surface);
presentation_wait_nofail(client, &result);
test_assert_enum(result, expected_result);
screenshot = client_capture_output(client, client->output,
WESTON_CAPTURE_V1_SOURCE_FRAMEBUFFER,
CLIENT_BUFFER_TYPE_SHM);
test_assert_ptr_not_null(screenshot);
client_buffer_util_maybe_sync_dmabuf_start(screenshot->buf);
match = verify_image(screenshot->image, "color-representation", 0,
NULL, 0);
client_buffer_util_maybe_sync_dmabuf_end(screenshot->buf);
buffer_destroy(screenshot);
if (color_representation_surface)
wp_color_representation_surface_v1_destroy(color_representation_surface);
client_buffer_util_destroy_buffer(buffer);
destroy_xdg_surface(xdg_surface);
xdg_client_destroy(xdg_client);
test_assert_true(match);
return RESULT_OK;
}

View file

@ -0,0 +1,66 @@
/*
* Copyright 2025 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include "weston-test-client-helper.h"
struct setup_args {
struct fixture_metadata meta;
enum weston_renderer_type renderer;
enum client_buffer_type buffer_type;
bool gl_force_import_yuv_fallback;
};
struct color_state {
bool create_color_representation_surface;
enum wp_color_representation_surface_v1_coefficients coefficients;
enum wp_color_representation_surface_v1_range range;
};
static const struct color_state color_state_cases[] = {
#define CRS(x) WP_COLOR_REPRESENTATION_SURFACE_V1_ ##x
{ false, 0, 0 },
{ true, 0, 0 },
{ true, CRS(COEFFICIENTS_BT601), CRS(RANGE_LIMITED) },
{ true, CRS(COEFFICIENTS_BT601), CRS(RANGE_FULL) },
{ true, CRS(COEFFICIENTS_BT709), CRS(RANGE_LIMITED) },
{ true, CRS(COEFFICIENTS_BT709), CRS(RANGE_FULL) },
{ true, CRS(COEFFICIENTS_BT2020), CRS(RANGE_LIMITED) },
{ true, CRS(COEFFICIENTS_BT2020), CRS(RANGE_FULL) },
#undef CRS
};
enum feedback_result {
FB_PENDING = 0,
FB_PRESENTED,
FB_PRESENTED_ZERO_COPY,
FB_DISCARDED
};
enum test_result_code
test_color_representation(const struct color_state *color_state,
enum client_buffer_type buffer_type,
enum feedback_result expected_result);

View file

@ -0,0 +1,74 @@
/*
* Copyright © 2025 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "config.h"
#include "color-representation-common.h"
#include "image-iter.h"
#include "pixel-formats.h"
#include "shared/weston-drm-fourcc.h"
#include "shared/xalloc.h"
#include "weston-test-client-helper.h"
#include "weston-test-assert.h"
#include "xdg-client-helper.h"
static const struct setup_args my_setup_args[] = {
{
.meta.name = "GL - dmabuf renderer",
.renderer = WESTON_RENDERER_GL,
.buffer_type = CLIENT_BUFFER_TYPE_DMABUF,
},
};
static enum test_result_code
fixture_setup(struct weston_test_harness *harness, const struct setup_args *arg)
{
struct compositor_setup setup;
compositor_setup_defaults(&setup);
setup.backend = WESTON_BACKEND_DRM;
setup.renderer = arg->renderer;
setup.logging_scopes = "log,drm-backend";
/* Currently enforced by vkms. Set as a reminder for the future. */
setup.width = 1024;
setup.height = 768;
setup.test_quirks.required_capabilities = WESTON_CAP_COLOR_REP;
setup.test_quirks.gl_force_import_yuv_fallback =
arg->gl_force_import_yuv_fallback;
return weston_test_harness_execute_as_client(harness, &setup);
}
DECLARE_FIXTURE_SETUP_WITH_ARG(fixture_setup, my_setup_args, meta);
TEST_P(color_representation_drm, color_state_cases) {
const struct color_state *color_state = data;
const struct setup_args *args = &my_setup_args[get_test_fixture_index()];
return test_color_representation(color_state, args->buffer_type,
FB_PRESENTED_ZERO_COPY);
}

View file

@ -0,0 +1,291 @@
/*
* Copyright 2025 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "config.h"
#include "pixel-formats.h"
#include "shared/weston-drm-fourcc.h"
#include "shared/xalloc.h"
#include "weston-test-assert.h"
#include "weston-test-client-helper.h"
#include "weston-test-fixture-compositor.h"
static enum test_result_code
fixture_setup(struct weston_test_harness *harness)
{
struct compositor_setup setup;
compositor_setup_defaults(&setup);
setup.renderer = WESTON_RENDERER_GL;
setup.test_quirks.required_capabilities = WESTON_CAP_COLOR_REP;
return weston_test_harness_execute_as_client(harness, &setup);
}
DECLARE_FIXTURE_SETUP(fixture_setup);
/*
* Test that the SURFACE_EXISTS error is send by the compositor.
*/
TEST(color_presentation_protocol_surface_exists)
{
struct wp_color_representation_surface_v1 *color_representation_surface;
struct wp_color_representation_surface_v1 *color_representation_surface_2;
struct client *client;
struct wl_surface *surface;
client = create_client();
client->surface = create_test_surface(client);
surface = client->surface->wl_surface;
color_representation_surface =
wp_color_representation_manager_v1_get_surface(
client->color_representation, surface);
color_representation_surface_2 =
wp_color_representation_manager_v1_get_surface(
client->color_representation, surface);
expect_protocol_error(client,
&wp_color_representation_manager_v1_interface,
WP_COLOR_REPRESENTATION_MANAGER_V1_ERROR_SURFACE_EXISTS);
wp_color_representation_surface_v1_destroy(color_representation_surface);
wp_color_representation_surface_v1_destroy(color_representation_surface_2);
client_destroy(client);
return RESULT_OK;
}
/*
* Test that a color representation can successfully be recreated after
* destruction without e.g. triggering a SURFACE_EXISTS error.
*/
TEST(color_presentation_protocol_surface_recreate)
{
struct wp_color_representation_surface_v1 *color_representation_surface;
struct wp_color_representation_surface_v1 *color_representation_surface_2;
struct client *client;
struct wl_surface *surface;
client = create_client();
client->surface = create_test_surface(client);
surface = client->surface->wl_surface;
color_representation_surface =
wp_color_representation_manager_v1_get_surface(
client->color_representation, surface);
wp_color_representation_surface_v1_destroy(color_representation_surface);
color_representation_surface_2 =
wp_color_representation_manager_v1_get_surface(
client->color_representation, surface);
client_roundtrip(client);
wp_color_representation_surface_v1_destroy(color_representation_surface_2);
client_destroy(client);
return RESULT_OK;
}
struct coefficients_case {
uint32_t drm_format;
enum wp_color_representation_surface_v1_coefficients coefficients;
enum wp_color_representation_surface_v1_range range;
enum wp_color_representation_surface_v1_error error_code;
};
#define VALID_CASE(format, coefs, range) { \
DRM_FORMAT_ ## format, \
WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_ ## coefs, \
WP_COLOR_REPRESENTATION_SURFACE_V1_RANGE_ ## range, \
0 \
}
#define INVALID_CASE(format, coefs, range, err) { \
DRM_FORMAT_ ## format, \
WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_ ## coefs, \
WP_COLOR_REPRESENTATION_SURFACE_V1_RANGE_ ## range, \
WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_ ## err \
}
static const struct coefficients_case coefficients_cases[] = {
VALID_CASE(ARGB8888, IDENTITY, FULL),
INVALID_CASE(ARGB8888, IDENTITY, LIMITED, COEFFICIENTS),
INVALID_CASE(ARGB8888, BT601, LIMITED, PIXEL_FORMAT),
INVALID_CASE(ARGB8888, BT601, FULL, PIXEL_FORMAT),
INVALID_CASE(ARGB8888, BT709, LIMITED, PIXEL_FORMAT),
INVALID_CASE(ARGB8888, BT709, FULL, PIXEL_FORMAT),
INVALID_CASE(ARGB8888, BT2020, LIMITED, PIXEL_FORMAT),
INVALID_CASE(ARGB8888, BT2020, FULL, PIXEL_FORMAT),
INVALID_CASE(ARGB8888, FCC, LIMITED, COEFFICIENTS),
INVALID_CASE(ARGB8888, FCC, FULL, COEFFICIENTS),
{DRM_FORMAT_ARGB8888, 0, 0, WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_COEFFICIENTS},
VALID_CASE(NV12, BT601, LIMITED),
VALID_CASE(NV12, BT601, FULL),
VALID_CASE(NV12, BT709, LIMITED),
VALID_CASE(NV12, BT709, FULL),
VALID_CASE(NV12, BT2020, LIMITED),
VALID_CASE(NV12, BT2020, FULL),
INVALID_CASE(NV12, IDENTITY, LIMITED, COEFFICIENTS),
INVALID_CASE(NV12, IDENTITY, FULL, PIXEL_FORMAT),
INVALID_CASE(NV12, FCC, LIMITED, COEFFICIENTS),
INVALID_CASE(NV12, FCC, FULL, COEFFICIENTS),
{DRM_FORMAT_NV12, 0, 0, WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_COEFFICIENTS},
};
/*
* Test that various protocol errors regarding invalid combinations of DRM
* format, matrix coefficients and quantization range are send by the compositor
* as required by the protocol.
*/
TEST_P(color_presentation_protocol_valid_coefficients, coefficients_cases)
{
const struct coefficients_case *coefficients_case = data;
struct wp_color_representation_surface_v1 *color_representation_surface;
struct client *client;
struct wl_surface *surface;
client = create_client();
client->surface = create_test_surface(client);
surface = client->surface->wl_surface;
client->surface->buffer = create_shm_buffer(client, 8, 8,
coefficients_case->drm_format);
wl_surface_attach(surface, client->surface->buffer->proxy, 0, 0);
wl_surface_damage(surface, 0, 0, INT32_MAX, INT32_MAX);
color_representation_surface =
wp_color_representation_manager_v1_get_surface(
client->color_representation,
surface);
wp_color_representation_surface_v1_set_coefficients_and_range(
color_representation_surface,
coefficients_case->coefficients,
coefficients_case->range);
wl_surface_commit(surface);
if (coefficients_case->error_code)
expect_protocol_error(client,
&wp_color_representation_surface_v1_interface,
coefficients_case->error_code);
else
client_roundtrip(client);
wp_color_representation_surface_v1_destroy(color_representation_surface);
client_destroy(client);
return RESULT_OK;
}
struct alpha_mode_case {
enum wp_color_representation_surface_v1_alpha_mode alpha_mode;
enum wp_color_representation_surface_v1_error error_code;
};
static const struct alpha_mode_case alpha_mode_cases[] = {
{WP_COLOR_REPRESENTATION_SURFACE_V1_ALPHA_MODE_PREMULTIPLIED_ELECTRICAL, 0},
{WP_COLOR_REPRESENTATION_SURFACE_V1_ALPHA_MODE_PREMULTIPLIED_OPTICAL, WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_ALPHA_MODE},
{WP_COLOR_REPRESENTATION_SURFACE_V1_ALPHA_MODE_STRAIGHT, WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_ALPHA_MODE},
{-1, WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_ALPHA_MODE},
};
/*
* Test that PREMULTIPLIED_ELECTRICAL is the only alpha mode currently supported.
*/
TEST_P(color_presentation_protocol_alpha_mode, alpha_mode_cases)
{
const struct alpha_mode_case *alpha_mode_case = data;
struct wp_color_representation_surface_v1 *color_representation_surface;
struct client *client;
client = create_client();
client->surface = create_test_surface(client);
color_representation_surface =
wp_color_representation_manager_v1_get_surface(
client->color_representation,
client->surface->wl_surface);
wp_color_representation_surface_v1_set_alpha_mode(color_representation_surface,
alpha_mode_case->alpha_mode);
if (alpha_mode_case->error_code)
expect_protocol_error(client,
&wp_color_representation_surface_v1_interface,
alpha_mode_case->error_code);
else
client_roundtrip(client);
wp_color_representation_surface_v1_destroy(color_representation_surface);
client_destroy(client);
return RESULT_OK;
}
struct chroma_location_case {
enum wp_color_representation_surface_v1_chroma_location chroma_location;
enum wp_color_representation_surface_v1_error error_code;
};
static const struct chroma_location_case chroma_location_cases[] = {
{WP_COLOR_REPRESENTATION_SURFACE_V1_CHROMA_LOCATION_TYPE_0, 0},
{WP_COLOR_REPRESENTATION_SURFACE_V1_CHROMA_LOCATION_TYPE_1, 0},
{WP_COLOR_REPRESENTATION_SURFACE_V1_CHROMA_LOCATION_TYPE_2, 0},
{WP_COLOR_REPRESENTATION_SURFACE_V1_CHROMA_LOCATION_TYPE_3, 0},
{WP_COLOR_REPRESENTATION_SURFACE_V1_CHROMA_LOCATION_TYPE_4, 0},
{WP_COLOR_REPRESENTATION_SURFACE_V1_CHROMA_LOCATION_TYPE_5, 0},
{0, WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_CHROMA_LOCATION},
};
/*
* Test that all chroma location values are accepted, but not invalid values.
*/
TEST_P(color_presentation_protocol_chroma_location, chroma_location_cases)
{
const struct chroma_location_case *chroma_location_case = data;
struct wp_color_representation_surface_v1 *color_representation_surface;
struct client *client;
client = create_client();
client->surface = create_test_surface(client);
color_representation_surface =
wp_color_representation_manager_v1_get_surface(
client->color_representation,
client->surface->wl_surface);
wp_color_representation_surface_v1_set_chroma_location(
color_representation_surface,
chroma_location_case->chroma_location);
if (chroma_location_case->error_code)
expect_protocol_error(client,
&wp_color_representation_surface_v1_interface,
chroma_location_case->error_code);
else
client_roundtrip(client);
wp_color_representation_surface_v1_destroy(color_representation_surface);
client_destroy(client);
return RESULT_OK;
}

View file

@ -0,0 +1,250 @@
/*
* Copyright 2025 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "config.h"
#include "color-representation-common.h"
#include "pixel-formats.h"
#include "shared/weston-drm-fourcc.h"
#include "weston-test-assert.h"
#include "xdg-client-helper.h"
static const struct setup_args my_setup_args[] = {
{
.meta.name = "GL - shm",
.renderer = WESTON_RENDERER_GL,
.buffer_type = CLIENT_BUFFER_TYPE_SHM,
},
{
.meta.name = "GL - dmabuf renderer",
.renderer = WESTON_RENDERER_GL,
.buffer_type = CLIENT_BUFFER_TYPE_DMABUF,
},
{
.meta.name = "GL - dmabuf renderer + force-import-yuv-fallback",
.renderer = WESTON_RENDERER_GL,
.buffer_type = CLIENT_BUFFER_TYPE_DMABUF,
.gl_force_import_yuv_fallback = true,
},
};
static enum test_result_code
fixture_setup(struct weston_test_harness *harness, const struct setup_args *arg)
{
struct compositor_setup setup;
compositor_setup_defaults(&setup);
setup.renderer = arg->renderer;
setup.refresh = HIGHEST_OUTPUT_REFRESH;
setup.logging_scopes = "log";
/* Required for test that also run on DRM */
setup.width = 1024;
setup.height = 768;
setup.test_quirks.required_capabilities = WESTON_CAP_COLOR_REP;
setup.test_quirks.gl_force_import_yuv_fallback =
arg->gl_force_import_yuv_fallback;
return weston_test_harness_execute_as_client(harness, &setup);
}
DECLARE_FIXTURE_SETUP_WITH_ARG(fixture_setup, my_setup_args, meta);
TEST_P(color_representation_renderer, color_state_cases) {
const struct color_state *color_state = data;
const struct setup_args *args = &my_setup_args[get_test_fixture_index()];
return test_color_representation(color_state, args->buffer_type,
FB_PRESENTED);
}
static struct client_buffer*
create_and_fill_nv12_buffer(struct client *client,
enum client_buffer_type buffer_type, int width,
int height)
{
const struct pixel_format_info *fmt_info;
struct client_buffer *buffer;
uint8_t *y_base;
uint16_t *uv_base;
fmt_info = pixel_format_get_info(DRM_FORMAT_NV12);
switch (buffer_type) {
case CLIENT_BUFFER_TYPE_SHM:
buffer = client_buffer_util_create_shm_buffer(client->wl_shm,
fmt_info,
width,
height);
break;
case CLIENT_BUFFER_TYPE_DMABUF:
buffer = client_buffer_util_create_dmabuf_buffer(client->wl_display,
client->dmabuf,
fmt_info,
width,
height);
break;
default:
test_assert_not_reached("Buffer type not handled");
break;
}
y_base = buffer->data + buffer->offsets[0];
uv_base = (uint16_t *)(buffer->data + buffer->offsets[1]);
client_buffer_util_maybe_sync_dmabuf_start(buffer);
for (int y = 0; y < height; y++) {
uint8_t *y_row;
uint16_t *uv_row;
y_row = y_base + y * buffer->strides[0];
uv_row = uv_base + (y / 2) * (buffer->strides[1] / sizeof(uint16_t));
for (int x = 0; x < width; x++) {
*(y_row + x) = 0x30;
*(uv_row + x / 2) = 0x5050;
}
}
client_buffer_util_maybe_sync_dmabuf_end(buffer);
return buffer;
}
static void
buffer_release(void *data, struct wl_buffer *buffer)
{
wl_buffer_destroy(buffer);
}
static const struct wl_buffer_listener buffer_listener = {
buffer_release
};
/*
* Test that the same NV12 buffer can be attached to multiple wl_surfaces with
* different color representation values.
*/
TEST(drm_color_representation_reuse_buffer)
{
const struct setup_args *args = &my_setup_args[get_test_fixture_index()];
struct xdg_client *xdg_client;
struct client *client;
struct xdg_surface_data *xdg_surface;
struct wl_surface *toplevel_surface;
int n_color_state_cases = ARRAY_LENGTH(color_state_cases);
struct wl_surface *surface[n_color_state_cases];
struct wl_subsurface *subsurface[n_color_state_cases];
struct wl_buffer *toplevel_buffer;
struct client_buffer *buffer;
struct wp_viewport *toplevel_viewport;
struct wp_color_representation_surface_v1 *color_representation_surface[n_color_state_cases];
struct rectangle clip = { .width = 128, .height = 128 };
bool match;
xdg_client = create_xdg_client();
client = xdg_client->client;
xdg_surface = create_xdg_surface(xdg_client);
toplevel_surface = xdg_surface->surface->wl_surface;
xdg_surface_make_toplevel(xdg_surface,
"weston.test.color-representation", "one");
xdg_toplevel_set_fullscreen(xdg_surface->xdg_toplevel, NULL);
xdg_surface_wait_configure(xdg_surface);
toplevel_viewport = wp_viewporter_get_viewport(client->viewporter,
toplevel_surface);
wp_viewport_set_destination(toplevel_viewport,
xdg_surface->configure.width, xdg_surface->configure.height);
toplevel_buffer =
wp_single_pixel_buffer_manager_v1_create_u32_rgba_buffer(client->single_pixel_manager,
0x0, 0x0, 0x0, 0x0);
wl_surface_attach(toplevel_surface, toplevel_buffer, 0, 0);
wl_buffer_add_listener(toplevel_buffer, &buffer_listener, NULL);
wl_surface_damage_buffer(toplevel_surface, 0, 0, 1, 1);
buffer = create_and_fill_nv12_buffer(client, args->buffer_type,
clip.width / 4, clip.height / 2 - 4);
for (int i = 0; i < n_color_state_cases; i++) {
surface[i] = wl_compositor_create_surface(client->wl_compositor);
subsurface[i] =
wl_subcompositor_get_subsurface(client->wl_subcompositor,
surface[i], toplevel_surface);
}
wl_subsurface_set_position(subsurface[0], 0, 4);
wl_subsurface_set_position(subsurface[1], 0, clip.height / 2);
wl_subsurface_set_position(subsurface[2], clip.width / 4, 4);
wl_subsurface_set_position(subsurface[3], clip.width / 4,
clip.height / 2);
wl_subsurface_set_position(subsurface[4], clip.width / 4 * 2, 4);
wl_subsurface_set_position(subsurface[5], clip.width / 4 * 2,
clip.height / 2);
wl_subsurface_set_position(subsurface[6], clip.width / 4 * 3, 4);
wl_subsurface_set_position(subsurface[7], clip.width / 4 * 3,
clip.height / 2);
for (int i = 0; i < n_color_state_cases; i++) {
if (color_state_cases[i].create_color_representation_surface) {
color_representation_surface[i] =
wp_color_representation_manager_v1_get_surface(
client->color_representation,
surface[i]);
if (color_state_cases[i].coefficients != 0)
wp_color_representation_surface_v1_set_coefficients_and_range(
color_representation_surface[i],
color_state_cases[i].coefficients,
color_state_cases[i].range);
} else {
color_representation_surface[i] = NULL;
}
wl_surface_attach(surface[i], buffer->wl_buffer, 0, 0);
wl_surface_damage(surface[i], 0, 0, INT32_MAX, INT32_MAX);
wl_surface_commit(surface[i]);
}
xdg_surface_maybe_ack_configure(xdg_surface);
wl_surface_commit(toplevel_surface);
match = verify_screen_content(client, "color-representation", 1,
&clip, 0, NULL, NO_DECORATIONS);
for (int i = 0; i < n_color_state_cases; i++) {
if (color_representation_surface[i])
wp_color_representation_surface_v1_destroy(color_representation_surface[i]);
wl_subsurface_destroy(subsurface[i]);
wl_surface_destroy(surface[i]);
}
wp_viewport_destroy(toplevel_viewport);
client_buffer_util_destroy_buffer(buffer);
destroy_xdg_surface(xdg_surface);
xdg_client_destroy(xdg_client);
test_assert_true(match);
return RESULT_OK;
}

View file

@ -30,7 +30,9 @@ lib_test_client = static_library(
[
'weston-test-client-helper.c',
'weston-test-fixture-compositor.c',
'xdg-client-helper.c',
'xdg-client-helper.c',
color_representation_v1_client_protocol_h,
color_representation_v1_protocol_c,
commit_timing_v1_client_protocol_h,
commit_timing_v1_protocol_c,
fifo_v1_client_protocol_h,
@ -140,6 +142,22 @@ tests = [
dep_libdisplay_info,
],
},
{ 'name': 'color-representation-drm',
'sources': [
'color-representation-common.c',
'color-representation-common.h',
'color-representation-drm-test.c',
],
'run_exclusive': true
},
{ 'name': 'color-representation-renderer',
'sources': [
'color-representation-common.c',
'color-representation-common.h',
'color-representation-renderer-test.c',
],
},
{ 'name': 'color-representation-protocol', },
{ 'name': 'commit-timing', },
{ 'name': 'config-parser', },
{

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 B

View file

@ -59,6 +59,11 @@ struct drm_format {
uint64_t modifier;
};
struct coefficients_and_range {
enum wp_color_representation_surface_v1_coefficients coefficients;
enum wp_color_representation_surface_v1_range range;
};
int
surface_contains(struct surface *surface, int x, int y)
{
@ -638,6 +643,54 @@ static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = {
dmabuf_modifier
};
bool
support_coefficients_and_range(struct client *client,
enum wp_color_representation_surface_v1_coefficients coefficients,
enum wp_color_representation_surface_v1_range range)
{
struct coefficients_and_range *p;
wl_array_for_each(p, &client->coefficients_and_ranges)
if (p->coefficients == coefficients && p->range == range)
return true;
return false;
}
static void
color_representation_supported_alpha_mode(void *data,
struct wp_color_representation_manager_v1 *wp_color_representation_manager_v1,
uint32_t alpha_mode)
{
}
static void
color_representation_supported_coefficients_and_ranges(void *data,
struct wp_color_representation_manager_v1 *wp_color_representation_manager_v1,
uint32_t coefficients,
uint32_t range)
{
struct client *client = data;
struct coefficients_and_range *p;
p = wl_array_add(&client->coefficients_and_ranges, sizeof *p);
assert(p);
p->coefficients = coefficients;
p->range = range;
}
static void
color_representation_done(void *data,
struct wp_color_representation_manager_v1 *wp_color_representation_manager_v1)
{
}
static const struct wp_color_representation_manager_v1_listener color_representation_listener = {
.supported_alpha_mode = color_representation_supported_alpha_mode,
.supported_coefficients_and_ranges = color_representation_supported_coefficients_and_ranges,
.done = color_representation_done,
};
static void
test_handle_pointer_position(void *data, struct weston_test *weston_test,
wl_fixed_t x, wl_fixed_t y)
@ -918,6 +971,13 @@ handle_global(void *data, struct wl_registry *registry,
wl_registry_bind(registry, id,
&wp_single_pixel_buffer_manager_v1_interface,
1);
} else if (strcmp(interface, wp_color_representation_manager_v1_interface.name) == 0) {
client->color_representation =
wl_registry_bind(registry, id,
&wp_color_representation_manager_v1_interface, version);
wp_color_representation_manager_v1_add_listener (client->color_representation,
&color_representation_listener,
client);
} else if (strcmp(interface, "wl_output") == 0) {
output = xzalloc(sizeof *output);
output->wl_output =
@ -1107,6 +1167,7 @@ create_client(void)
test_assert_ptr_not_null(client->wl_display);
wl_array_init(&client->shm_formats);
wl_array_init(&client->drm_formats);
wl_array_init(&client->coefficients_and_ranges);
wl_list_init(&client->global_list);
wl_list_init(&client->inputs);
wl_list_init(&client->output_list);
@ -1220,6 +1281,7 @@ client_destroy(struct client *client)
wl_array_release(&client->shm_formats);
wl_array_release(&client->drm_formats);
wl_array_release(&client->coefficients_and_ranges);
while (!wl_list_empty(&client->inputs)) {
input_destroy(container_of(client->inputs.next,
@ -1251,6 +1313,8 @@ client_destroy(struct client *client)
wp_presentation_destroy(client->presentation);
if (client->single_pixel_manager)
wp_single_pixel_buffer_manager_v1_destroy(client->single_pixel_manager);
if (client->color_representation)
wp_color_representation_manager_v1_destroy(client->color_representation);
if (client->wl_compositor)
wl_compositor_destroy(client->wl_compositor);
if (client->wl_subcompositor)

View file

@ -34,6 +34,7 @@
#include <pixman.h>
#include <wayland-client-protocol.h>
#include "color-representation-v1-client-protocol.h"
#include "linux-dmabuf-unstable-v1-client-protocol.h"
#include "presentation-time-client-protocol.h"
#include "shared/client-buffer-util.h"
@ -64,6 +65,7 @@ struct client {
struct wp_presentation *presentation;
struct wp_single_pixel_buffer_manager_v1 *single_pixel_manager;
struct wp_viewporter *viewporter;
struct wp_color_representation_manager_v1 *color_representation;
struct test *test;
struct wp_fifo_manager_v1 *fifo_manager;
@ -80,6 +82,7 @@ struct client {
struct surface *surface;
struct wl_array shm_formats;
struct wl_array drm_formats;
struct wl_array coefficients_and_ranges;
struct wl_list global_list;
struct wl_list output_list; /* struct output::link */
@ -258,6 +261,11 @@ create_shm_buffer_solid(struct client *client, int width, int height,
bool
support_drm_format(struct client *client, uint32_t format, uint64_t modifier);
bool
support_coefficients_and_range(struct client *client,
enum wp_color_representation_surface_v1_coefficients coefficients,
enum wp_color_representation_surface_v1_range range);
void
buffer_destroy(struct buffer *buf);