From 75d75ac6cd17092e3555e4d2a864cdf0a5e8f0bd Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Mon, 27 Oct 2025 17:37:27 +0100 Subject: [PATCH] tests: Add color-representation tests for DRM and GL Test the various combinations of: 1. Renderer backends - currently GL only. 2. Renderer modes - plane-offloading/vkms, shaders in Mesa, internal shaders. 3. Buffer-types - SHM and DMABuf. 4. Coefficient/range combinations. Signed-off-by: Robert Mader --- tests/color-representation-common.c | 326 ++++++++++++++++++++ tests/color-representation-common.h | 66 ++++ tests/color-representation-drm-test.c | 74 +++++ tests/color-representation-renderer-test.c | 250 +++++++++++++++ tests/meson.build | 15 + tests/reference/color-representation-00.png | Bin 0 -> 50497 bytes tests/reference/color-representation-01.png | Bin 0 -> 393 bytes 7 files changed, 731 insertions(+) create mode 100644 tests/color-representation-common.c create mode 100644 tests/color-representation-common.h create mode 100644 tests/color-representation-drm-test.c create mode 100644 tests/color-representation-renderer-test.c create mode 100644 tests/reference/color-representation-00.png create mode 100644 tests/reference/color-representation-01.png diff --git a/tests/color-representation-common.c b/tests/color-representation-common.c new file mode 100644 index 000000000..0c5c78cc5 --- /dev/null +++ b/tests/color-representation-common.c @@ -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; +} diff --git a/tests/color-representation-common.h b/tests/color-representation-common.h new file mode 100644 index 000000000..2a9cea247 --- /dev/null +++ b/tests/color-representation-common.h @@ -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); diff --git a/tests/color-representation-drm-test.c b/tests/color-representation-drm-test.c new file mode 100644 index 000000000..28a3cba58 --- /dev/null +++ b/tests/color-representation-drm-test.c @@ -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); +} diff --git a/tests/color-representation-renderer-test.c b/tests/color-representation-renderer-test.c new file mode 100644 index 000000000..352a8f5ea --- /dev/null +++ b/tests/color-representation-renderer-test.c @@ -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; +} diff --git a/tests/meson.build b/tests/meson.build index 82f5952a0..40d36af4a 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -142,6 +142,21 @@ 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': 'commit-timing', }, { 'name': 'config-parser', }, { diff --git a/tests/reference/color-representation-00.png b/tests/reference/color-representation-00.png new file mode 100644 index 0000000000000000000000000000000000000000..46fbd7d30bdf89f978df064669b4ef92635248b3 GIT binary patch literal 50497 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 G=l=mRDzPU3 literal 0 HcmV?d00001 diff --git a/tests/reference/color-representation-01.png b/tests/reference/color-representation-01.png new file mode 100644 index 0000000000000000000000000000000000000000..6b7b49046c7114c3e345d808a11634301993ff10 GIT binary patch literal 393 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1SEZ8zRdwrY)RhkE)4%caKYZ?lNlHo?L1u^ zLn`LHy|alM{j<9ra3J5ExIrz{`VB^E{C(L6k+P?qot1}xICxCp;;P8b1h60oC U?mfBlfnm$w>FVdQ&MBb@00WJ3RR910 literal 0 HcmV?d00001