From cb97abd4f7e8a3d7d4acdfd9e08cc800c271f036 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Mon, 1 Dec 2025 13:34:00 -0600 Subject: [PATCH] compositor: Add the fifo-v1 protocol Add support for the fifo protocol, which allows an application to submit a content update that can only be applied after the previous content update has been active for a display refresh. Signed-off-by: Derek Foreman --- include/libweston/libweston.h | 12 + libweston/compositor.c | 24 +- libweston/fifo.c | 270 +++++++ libweston/fifo.h | 46 ++ libweston/meson.build | 3 + libweston/surface-state.c | 19 + tests/fifo-test.c | 691 ++++++++++++++++++ tests/meson.build | 1 + tests/reference/fifo_occlude_restack-00.png | Bin 0 -> 829 bytes tests/reference/fifo_occlude_start-00.png | Bin 0 -> 840 bytes tests/reference/fifo_subsurface_start-00.png | Bin 0 -> 850 bytes tests/reference/occlusion_change_start-00.png | Bin 0 -> 828 bytes 12 files changed, 1064 insertions(+), 2 deletions(-) create mode 100644 libweston/fifo.c create mode 100644 libweston/fifo.h create mode 100644 tests/fifo-test.c create mode 100644 tests/reference/fifo_occlude_restack-00.png create mode 100644 tests/reference/fifo_occlude_start-00.png create mode 100644 tests/reference/fifo_subsurface_start-00.png create mode 100644 tests/reference/occlusion_change_start-00.png diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 66ce98a92..29014ffa7 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -556,6 +556,9 @@ struct weston_output { /** Needs to rebuild the paint node z ordered list */ bool paint_node_list_needs_rebuild; + + /** fifo_v1 - list of surfaces to clear next repaint */ + struct wl_list fifo_barrier_surfaces; }; enum weston_pointer_motion_mask { @@ -1796,6 +1799,10 @@ struct weston_surface_state { * color_management_surface_v1_interface.unset_image_description */ struct weston_color_profile *color_profile; const struct weston_render_intent_info *render_intent; + + /* wp_fifo_v1 */ + bool fifo_barrier; + bool fifo_wait; }; struct weston_surface_activation_data { @@ -1975,6 +1982,11 @@ struct weston_surface { * calculations when considering this surface's visibility. */ uint32_t output_visibility_dirty_mask; + + /** fifo_v1 */ + struct weston_fifo *fifo; + bool fifo_barrier; /* Cleared after display */ + struct wl_list fifo_barrier_link; /* output::fifo_barrier_surfaces */ }; struct weston_subsurface { diff --git a/libweston/compositor.c b/libweston/compositor.c index e5c1e5257..e635d60e1 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -90,6 +90,7 @@ #include "weston-trace.h" #include "renderer-vulkan/vulkan-renderer.h" +#include #include "weston-log-internal.h" /** @@ -1006,6 +1007,8 @@ weston_surface_create(struct weston_compositor *compositor) /* Also part of the CM&HDR protocol extension implementation. */ weston_surface_update_preferred_color_profile(surface); + wl_list_init(&surface->fifo_barrier_link); + return surface; } @@ -2722,6 +2725,8 @@ weston_surface_unref(struct weston_surface *surface) if (surface->cm_surface) wl_resource_set_user_data(surface->cm_surface, NULL); + wl_list_remove(&surface->fifo_barrier_link); + free(surface); } @@ -3652,6 +3657,13 @@ weston_output_latch(struct weston_output *output) compositor->latched = true; wl_signal_emit(&output->post_latch_signal, output); + + /* We have now latched. It doesn't matter that the repaint + * hasn't happened yet - it is inevitable. We can now clear + * fifo barriers, but must not apply transactions again + * until after the repaint. + */ + weston_fifo_output_clear_barriers(output); } static int @@ -3819,8 +3831,9 @@ weston_output_check_repaint(struct weston_output *output, struct timespec *now) compositor->state == WESTON_COMPOSITOR_OFFSCREEN) goto out; - /* We don't actually need to repaint this output; drop it from - * repaint until something causes damage. */ + if (weston_fifo_output_has_barriers(output)) + output->repaint_needed = true; + if (!output->repaint_needed) goto out; @@ -7604,6 +7617,8 @@ weston_compositor_remove_output(struct weston_output *output) weston_output_capture_info_destroy(&output->capture_info); + weston_fifo_output_clear_barriers(output); + compositor->output_id_pool &= ~(1u << output->id); output->id = 0xffffffff; /* invalid */ } @@ -8040,6 +8055,8 @@ weston_output_init(struct weston_output *output, output->gpu_track_id = 0; output->paint_track_id = 0; output->presentation_track_id = 0; + + wl_list_init(&output->fifo_barrier_surfaces); } /** Adds weston_output object to pending output list. @@ -9692,6 +9709,9 @@ weston_compositor_create(struct wl_display *display, ec, bind_tearing_controller)) goto fail; + if (fifo_setup(ec) != 0) + goto fail; + if (weston_input_init(ec) != 0) goto fail; diff --git a/libweston/fifo.c b/libweston/fifo.c new file mode 100644 index 000000000..6117c0d29 --- /dev/null +++ b/libweston/fifo.c @@ -0,0 +1,270 @@ +/* + * 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 + +#include +#include +#include "libweston-internal.h" +#include "shared/helpers.h" +#include "shared/xalloc.h" + +struct weston_fifo { + struct weston_surface *surface; + struct wl_listener surface_destroy_listener; + uint64_t flow_id; +}; + +static void +fifo_destructor(struct wl_resource *resource) +{ + struct weston_fifo *fifo = wl_resource_get_user_data(resource); + + if (fifo->surface) + wl_list_remove(&fifo->surface_destroy_listener.link); + + free(fifo); +} + +static void +fifo_set_barrier(struct wl_client *client, struct wl_resource *resource) +{ + struct weston_fifo *fifo = wl_resource_get_user_data(resource); + struct weston_surface *surface = fifo->surface; + + if (!surface) { + wl_resource_post_error(resource, + WP_FIFO_V1_ERROR_SURFACE_DESTROYED, + "surface destroyed"); + return; + } + surface->pending.fifo_barrier = true; +} + +static void +fifo_wait_barrier(struct wl_client *client, struct wl_resource *resource) +{ + struct weston_fifo *fifo = wl_resource_get_user_data(resource); + struct weston_surface *surface = fifo->surface; + + if (!surface) { + wl_resource_post_error(resource, + WP_FIFO_V1_ERROR_SURFACE_DESTROYED, + "surface destroyed"); + return; + } + surface->pending.fifo_wait = true; +} + +static void +fifo_destroy(struct wl_client *client, struct wl_resource *resource) +{ + struct weston_fifo *fifo = wl_resource_get_user_data(resource); + struct weston_surface *surface = fifo->surface; + + wl_resource_destroy(resource); + if (!surface) + return; + + surface->fifo = NULL; +} + +static const struct wp_fifo_v1_interface weston_fifo_interface = { + .set_barrier = fifo_set_barrier, + .wait_barrier = fifo_wait_barrier, + .destroy = fifo_destroy, +}; + +static void +fifo_manager_destroy(struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static void +fifo_surface_destroy_cb(struct wl_listener *listener, void *data) +{ + struct weston_fifo *fifo = + container_of(listener, + struct weston_fifo, surface_destroy_listener); + + fifo->surface = NULL; +} + +static void +fifo_manager_get_fifo(struct wl_client *client, + struct wl_resource *fm_resource, + uint32_t id, + struct wl_resource *surface_resource) +{ + struct weston_fifo *fifo; + struct weston_surface *surface = wl_resource_get_user_data(surface_resource); + struct wl_resource *res; + + if (surface->fifo) { + wl_resource_post_error(fm_resource, + WP_FIFO_MANAGER_V1_ERROR_ALREADY_EXISTS, + "Fifo resource already exists on surface"); + return; + } + + res = wl_resource_create(client, &wp_fifo_v1_interface, + wl_resource_get_version(fm_resource), id); + fifo = xzalloc(sizeof *fifo); + fifo->surface = surface; + fifo->surface_destroy_listener.notify = fifo_surface_destroy_cb; + wl_signal_add(&surface->destroy_signal, &fifo->surface_destroy_listener); + wl_resource_set_implementation(res, &weston_fifo_interface, fifo, + fifo_destructor); + surface->fifo = fifo; +} + +static const struct wp_fifo_manager_v1_interface fifo_manager_interface_v1 = { + .destroy = fifo_manager_destroy, + .get_fifo = fifo_manager_get_fifo, +}; + +static void +bind_fifo_manager(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_fifo_manager_v1_interface, + version, id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(resource, + &fifo_manager_interface_v1, + compositor, NULL); +} + +/** Advertise fifo protocol support + * + * Sets up fifo_v1 support so it is advertiszed to clients. + * + * \param compositor The compositor to init for. + * \return Zero on success, -1 on failure. + */ +int +fifo_setup(struct weston_compositor *compositor) +{ + if (!wl_global_create(compositor->wl_display, + &wp_fifo_manager_v1_interface, + 1, compositor, + bind_fifo_manager)) + return -1; + + return 0; +} + +static void +weston_fifo_surface_clear_barrier(struct weston_surface *surface) +{ + surface->fifo_barrier = false; + wl_list_remove(&surface->fifo_barrier_link); + wl_list_init(&surface->fifo_barrier_link); +} + +void +weston_fifo_surface_set_barrier(struct weston_surface *surface) +{ + /* If nothing is waiting on barriers, we could set multiple times + * before a repaint occurs. + * + * Theoretically, this surface could have a different primary + * output than the last time a barrier was created, so let's + * just blow away any old barrier (should one exist) before + * setting the current one. + */ + weston_fifo_surface_clear_barrier(surface); + + /* If the surface isn't associated with an output, we have no way + * to clear a barrier - so just don't set one. + */ + if (!surface->output) + return; + + surface->fifo_barrier = true; + wl_list_insert(&surface->output->fifo_barrier_surfaces, + &surface->fifo_barrier_link); +} + +void +weston_fifo_output_clear_barriers(struct weston_output *output) +{ + struct weston_surface *surf, *tmp; + + wl_list_for_each_safe(surf, tmp, + &output->fifo_barrier_surfaces, + fifo_barrier_link) + weston_fifo_surface_clear_barrier(surf); +} + +bool +weston_fifo_output_has_barriers(struct weston_output *output) +{ + return !wl_list_empty(&output->fifo_barrier_surfaces); +} + +bool +weston_fifo_surface_state_ready(struct weston_surface *surface, + struct weston_surface_state *state) +{ + struct weston_subsurface *sub = weston_surface_to_subsurface(surface); + bool e_sync = sub && sub->effectively_synchronized; + + if (!state->fifo_wait) + return true; + + /* The barrier is clear. */ + if (!surface->fifo_barrier) + return true; + + /* Effectively synchronized surfaces ignore fifo */ + if (e_sync) + return true; + + /* If there's no driving output, fifo will never clear, + * so just ignore the condition. + */ + if (!surface->output) + return true; + + /* Occluded surfaces ignore fifo */ + if (!weston_surface_visibility_mask(surface)) + return true; + + return false; +} diff --git a/libweston/fifo.h b/libweston/fifo.h new file mode 100644 index 000000000..4fee2e940 --- /dev/null +++ b/libweston/fifo.h @@ -0,0 +1,46 @@ +/* + * 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 "config.h" + +#include "fifo-v1-server-protocol.h" + +int +fifo_setup(struct weston_compositor *compositor); + +void +weston_fifo_surface_set_barrier(struct weston_surface *surface); + +void +weston_fifo_output_clear_barriers(struct weston_output *output); + +bool +weston_fifo_output_has_barriers(struct weston_output *output); + +bool +weston_fifo_surface_state_ready(struct weston_surface *surface, + struct weston_surface_state *state); diff --git a/libweston/meson.build b/libweston/meson.build index 23ed1af58..2d44bdfcc 100644 --- a/libweston/meson.build +++ b/libweston/meson.build @@ -25,6 +25,7 @@ srcs_libweston = [ 'content-protection.c', 'data-device.c', 'drm-formats.c', + 'fifo.c', 'id-number-allocator.c', 'input.c', 'linux-dmabuf.c', @@ -47,6 +48,8 @@ srcs_libweston = [ 'weston-direct-display.c', color_management_v1_protocol_c, color_management_v1_server_protocol_h, + fifo_v1_protocol_c, + fifo_v1_server_protocol_h, linux_dmabuf_unstable_v1_protocol_c, linux_dmabuf_unstable_v1_server_protocol_h, linux_explicit_synchronization_unstable_v1_protocol_c, diff --git a/libweston/surface-state.c b/libweston/surface-state.c index c194a0bcd..44b9e4d25 100644 --- a/libweston/surface-state.c +++ b/libweston/surface-state.c @@ -29,6 +29,7 @@ #include "config.h" #include +#include #include "libweston-internal.h" #include "backend.h" @@ -151,6 +152,9 @@ weston_surface_state_init(struct weston_surface *surface, state->color_profile = NULL; state->render_intent = NULL; + + state->fifo_barrier = false; + state->fifo_wait = false; } void @@ -480,6 +484,10 @@ weston_surface_apply_state(struct weston_surface *surface, surface->is_unmapping = false; surface->is_mapping = false; + if (state->fifo_barrier) + weston_fifo_surface_set_barrier(surface); + state->fifo_barrier = false; + if (weston_surface_status_invalidates_visibility(status)) surface->output_visibility_dirty_mask |= surface->output_mask; @@ -523,6 +531,9 @@ weston_surface_schedule_repaint(struct weston_surface *surface, struct weston_output *output; uint32_t visible_mask; + if (surface->output && surface->fifo_barrier) + weston_output_schedule_repaint(surface->output); + if (status == WESTON_SURFACE_CLEAN) return; @@ -628,6 +639,11 @@ weston_surface_state_merge_from(struct weston_surface_state *dst, &src->feedback_list); wl_list_init(&src->feedback_list); + dst->fifo_barrier = src->fifo_barrier; + src->fifo_barrier = false; + dst->fifo_wait = src->fifo_wait; + src->fifo_wait = false; + dst->status |= src->status; src->status = WESTON_SURFACE_CLEAN; } @@ -748,6 +764,9 @@ static bool weston_surface_state_ready(struct weston_surface *surface, struct weston_surface_state *state) { + if (!weston_fifo_surface_state_ready(surface, state)) + return false; + return true; } diff --git a/tests/fifo-test.c b/tests/fifo-test.c new file mode 100644 index 000000000..0bd15a806 --- /dev/null +++ b/tests/fifo-test.c @@ -0,0 +1,691 @@ +/* + * 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 +#include +#include + +#include "libweston-internal.h" +#include "weston-test-client-helper.h" +#include "weston-test-fixture-compositor.h" +#include "weston-test-assert.h" +#include "presentation-time-client-protocol.h" +#include "shared/xalloc.h" + +static int feedback_count; + +struct feedback { + struct client *client; + struct wp_presentation_feedback *obj; + bool expect_present; +}; + +enum fifo_barrier_status { + FIFO_BARRIER_INACTIVE = 0, + FIFO_BARRIER_ACTIVE, +}; + +enum rearm_breakpoint { + REARM_BREAKPOINT_NO = 0, + REARM_BREAKPOINT_YES, +}; + +static enum test_result_code +fixture_setup(struct weston_test_harness *harness) +{ + struct compositor_setup setup; + + compositor_setup_defaults(&setup); + setup.renderer = WESTON_RENDERER_PIXMAN; + setup.width = 320; + setup.height = 240; + setup.shell = SHELL_TEST_DESKTOP; + setup.logging_scopes = "log,test-harness-plugin"; + setup.refresh = HIGHEST_OUTPUT_REFRESH; + + return weston_test_harness_execute_as_client(harness, &setup); +} +DECLARE_FIXTURE_SETUP(fixture_setup); + +static struct buffer * +surface_commit_color(struct client *client, struct wl_surface *surface, + pixman_color_t *color, int width, int height) +{ + struct buffer *buf; + + buf = create_shm_buffer_a8r8g8b8(client, width, height); + fill_image_with_color(buf->image, color); + wl_surface_attach(surface, buf->proxy, 0, 0); + wl_surface_damage_buffer(surface, 0, 0, width, height); + wl_surface_commit(surface); + + return buf; +} + +/* Ensure we can only have one fifo object for a surface */ +TEST(get_two_fifos) +{ + struct client *client; + struct wp_fifo_v1 *fifo1, *fifo2; + + client = create_client_and_test_surface(100, 50, 100, 100); + test_assert_ptr_not_null(client); + + fifo1 = wp_fifo_manager_v1_get_fifo(client->fifo_manager, client->surface->wl_surface); + fifo2 = wp_fifo_manager_v1_get_fifo(client->fifo_manager, client->surface->wl_surface); + expect_protocol_error(client, &wp_fifo_manager_v1_interface, + WP_FIFO_MANAGER_V1_ERROR_ALREADY_EXISTS); + wp_fifo_v1_destroy(fifo2); + wp_fifo_v1_destroy(fifo1); + client_destroy(client); + + return RESULT_OK; +} + +/* Ensure we can get a second fifo for a surface if we destroy the first. */ +TEST(get_two_fifos_safely) +{ + struct client *client; + struct wp_fifo_v1 *fifo; + + client = create_client_and_test_surface(100, 50, 100, 100); + test_assert_ptr_not_null(client); + + fifo = wp_fifo_manager_v1_get_fifo(client->fifo_manager, client->surface->wl_surface); + wp_fifo_v1_destroy(fifo); + fifo = wp_fifo_manager_v1_get_fifo(client->fifo_manager, client->surface->wl_surface); + wp_fifo_v1_destroy(fifo); + client_roundtrip(client); + client_destroy(client); + + return RESULT_OK; +} + +/* Ensure the appropriate error occurs for using a fifo object associated + * with a destroyed surface. + */ +TEST(use_fifo_on_destroyed_surface) +{ + struct client *client; + struct wp_fifo_v1 *fifo; + + client = create_client_and_test_surface(100, 50, 100, 100); + test_assert_ptr_not_null(client); + + fifo = wp_fifo_manager_v1_get_fifo(client->fifo_manager, client->surface->wl_surface); + surface_destroy(client->surface); + client->surface = NULL; + wp_fifo_v1_set_barrier(fifo); + expect_protocol_error(client, &wp_fifo_v1_interface, + WP_FIFO_V1_ERROR_SURFACE_DESTROYED); + wp_fifo_v1_destroy(fifo); + client_destroy(client); + + return RESULT_OK; +} + +/* Ensure the compositor doesn't explode if we delete a surface with + * active barriers + */ +TEST(fifo_delete_surface_with_barriers) +{ + struct client *client; + struct buffer *buf; + struct wp_fifo_v1 *fifo; + pixman_color_t red; + int i; + + color_rgb888(&red, 255, 0, 0); + + client = create_client_and_test_surface(100, 50, 100, 100); + test_assert_ptr_not_null(client); + + fifo = wp_fifo_manager_v1_get_fifo(client->fifo_manager, client->surface->wl_surface); + wp_fifo_v1_set_barrier(fifo); + buf = surface_commit_color(client, client->surface->wl_surface, &red, 100, 100); + + /* Load up some future transactions */ + for (i = 0; i < 10; i++) { + wp_fifo_v1_set_barrier(fifo); + wp_fifo_v1_wait_barrier(fifo); + wl_surface_commit(client->surface->wl_surface); + } + + /* Steal and destroy the surface */ + wl_surface_destroy(client->surface->wl_surface); + client->surface->wl_surface = NULL; + + client_roundtrip(client); + + wp_fifo_v1_destroy(fifo); + buffer_destroy(buf); + client_destroy(client); + + return RESULT_OK; +} + +static void +check_fifo_status(struct client *client, + struct wet_testsuite_data *suite_data, + enum fifo_barrier_status expected, + enum rearm_breakpoint rearm) +{ + bool expected_fifo_barrier = expected == FIFO_BARRIER_ACTIVE; + + RUN_INSIDE_BREAKPOINT(client, suite_data) { + struct weston_surface *surface; + struct wl_resource *surface_res; + + test_assert_enum(breakpoint->template_->breakpoint, + WESTON_TEST_BREAKPOINT_POST_LATCH); + surface_res = wl_client_get_object(suite_data->wl_client, + wl_proxy_get_id((struct wl_proxy *)client->surface->wl_surface)); + surface = wl_resource_get_user_data(surface_res); + + test_assert_int_eq(surface->fifo_barrier, expected_fifo_barrier); + if (rearm == REARM_BREAKPOINT_YES) + REARM_BREAKPOINT(breakpoint); + } +} + +/* Make sure N barriers provokes N redraws */ +TEST(fifo_many_barriers) +{ + struct wet_testsuite_data *suite_data = TEST_GET_SUITE_DATA(); + struct client *client; + struct buffer *buf, *buf2; + struct wp_fifo_v1 *fifo; + pixman_color_t red; + int i; + + color_rgb888(&red, 255, 0, 0); + + client = create_client_and_test_surface(100, 50, 100, 100); + test_assert_ptr_not_null(client); + + client_push_breakpoint(client, suite_data, + WESTON_TEST_BREAKPOINT_POST_LATCH, + (struct wl_proxy *) client->output->wl_output); + + fifo = wp_fifo_manager_v1_get_fifo(client->fifo_manager, client->surface->wl_surface); + wp_fifo_v1_set_barrier(fifo); + buf = surface_commit_color(client, client->surface->wl_surface, &red, 100, 100); + /* Check that a string of commits with fifo set result in that + * number of repaints. + */ + for (i = 0; i < 10; i++) { + wp_fifo_v1_set_barrier(fifo); + wp_fifo_v1_wait_barrier(fifo); + wl_surface_commit(client->surface->wl_surface); + } + client_roundtrip(client); + + for (i = 0; i < 11; i++) + check_fifo_status(client, suite_data, FIFO_BARRIER_ACTIVE, REARM_BREAKPOINT_YES); + + /* A new commit with a visible change will cause a repaint now, and we can + * check for clear fifo status after. + */ + buf2 = surface_commit_color(client, client->surface->wl_surface, &red, 100, 100); + wl_surface_commit(client->surface->wl_surface); + client_roundtrip(client); + check_fifo_status(client, suite_data, FIFO_BARRIER_INACTIVE, REARM_BREAKPOINT_NO); + + wp_fifo_v1_destroy(fifo); + buffer_destroy(buf2); + buffer_destroy(buf); + client_destroy(client); + + return RESULT_OK; +} + +static void +feedback_sync_output(void *data, + struct wp_presentation_feedback *presentation_feedback, + struct wl_output *output) +{ + /* do nothing */ +} + +static void +feedback_presented(void *data, + struct wp_presentation_feedback *presentation_feedback, + uint32_t tv_sec_hi, + uint32_t tv_sec_lo, + uint32_t tv_nsec, + uint32_t refresh_nsec, + uint32_t seq_hi, + uint32_t seq_lo, + uint32_t flags) +{ + struct feedback *fb = data; + + test_assert_true(fb->expect_present); + + wp_presentation_feedback_destroy(fb->obj); + free(fb); + feedback_count--; +} + +static void +feedback_discarded(void *data, + struct wp_presentation_feedback *presentation_feedback) +{ + struct feedback *fb = data; + + test_assert_false(fb->expect_present); + + wp_presentation_feedback_destroy(fb->obj); + free(fb); + feedback_count--; +} + +static const struct wp_presentation_feedback_listener feedback_listener = { + feedback_sync_output, + feedback_presented, + feedback_discarded +}; + +static void +feedback_create(struct client *client, + struct wl_surface *surface, + struct wp_presentation *pres, + bool expect_present) +{ + struct feedback *fb; + + fb = xzalloc(sizeof *fb); + fb->client = client; + fb->obj = wp_presentation_feedback(pres, surface); + wp_presentation_feedback_add_listener(fb->obj, &feedback_listener, fb); + fb->expect_present = expect_present; + feedback_count++; +} + +/* Make sure fifo is ignored on occluded surfaces. + * This is a "may" in the spec, so this isn't necessarily rigorous, + * but a strong effort. + */ +TEST(fifo_on_occluded_surface) +{ + struct wl_subcompositor *subco; + struct wl_surface *oc_surf; + struct wl_region *opaque_region; + struct wl_subsurface *oc_subsurf; + struct client *client; + struct buffer *buf_main, *buf_main_small, *buf_sub; + struct wp_fifo_v1 *fifo; + struct wp_presentation *pres; + pixman_color_t red, green, blue; + int i; + bool match; + + color_rgb888(&red, 255, 0, 0); + color_rgb888(&green, 0, 255, 0); + color_rgb888(&blue, 0, 0, 255); + + client = create_client_and_test_surface(10, 10, 100, 100); + test_assert_ptr_not_null(client); + + pres = client_get_presentation(client); + + /* move the pointer clearly away from our screenshooting area */ + weston_test_move_pointer(client->test->weston_test, 0, 1, 0, 2, 30); + + subco = client_get_subcompositor(client); + oc_surf = wl_compositor_create_surface(client->wl_compositor); + oc_subsurf = wl_subcompositor_get_subsurface(subco, oc_surf, + client->surface->wl_surface); + buf_main = surface_commit_color(client, client->surface->wl_surface, &red, 100, 100); + buf_sub = surface_commit_color(client, oc_surf, &green, 50, 50); + wl_subsurface_set_position(oc_subsurf, 0, 0); + wl_subsurface_place_above(oc_subsurf, client->surface->wl_surface); + wl_subsurface_set_desync(oc_subsurf); + + /* Tell the compositor our subsurface is opaque so it knows it should + * occlude the parent later + */ + opaque_region = wl_compositor_create_region(client->wl_compositor); + wl_region_add(opaque_region, 0, 0, 50, 50); + wl_surface_set_opaque_region(oc_surf, opaque_region); + wl_region_destroy(opaque_region); + wl_surface_commit(oc_surf); + + /* Let's take a shot to make sure the smaller red parent surface is above the + * large green subsurface at this point. + */ + match = verify_screen_content(client, "fifo_occlude_start", 0, NULL, + 0, NULL, NO_DECORATIONS); + test_assert_true(match); + + fifo = wp_fifo_manager_v1_get_fifo(client->fifo_manager, client->surface->wl_surface); + wp_fifo_v1_set_barrier(fifo); + wl_surface_commit(client->surface->wl_surface); + + feedback_count = 0; + for (i = 0; i < 10; i++) { + wp_fifo_v1_set_barrier(fifo); + wp_fifo_v1_wait_barrier(fifo); + feedback_create(client, client->surface->wl_surface, pres, true); + wl_surface_commit(client->surface->wl_surface); + } + + /* Commit a buffer on the main surface that is smaller than the opaque subsurface + * that is above it. This will cause the main surface to become occluded. + */ + wp_fifo_v1_wait_barrier(fifo); + buf_main_small = surface_commit_color(client, client->surface->wl_surface, &red, 25, 25); + wl_surface_commit(client->surface->wl_surface); + + /* These waits shouldn't happen, so all the feedback should be discarded */ + for (i = 0; i < 10; i++) { + wp_fifo_v1_set_barrier(fifo); + wp_fifo_v1_wait_barrier(fifo); + feedback_create(client, client->surface->wl_surface, pres, false); + wl_surface_commit(client->surface->wl_surface); + } + + /* Kick that last feedback out as discarded */ + wp_fifo_v1_wait_barrier(fifo); + wl_surface_commit(client->surface->wl_surface); + + /* Destroy the fifo early so we can be sure destroying a fifo proxy + * doesn't change existing content updates. + */ + wp_fifo_v1_destroy(fifo); + client_roundtrip(client); + + while (feedback_count) + test_assert_int_ge(wl_display_dispatch(client->wl_display), 0); + + /* And let's make sure what we're seeing is just the subsurface */ + match = verify_screen_content(client, "fifo_occlude_restack", 0, NULL, + 0, NULL, NO_DECORATIONS); + test_assert_true(match); + + wp_presentation_destroy(pres); + wl_subcompositor_destroy(subco); + wl_subsurface_destroy(oc_subsurf); + wl_surface_destroy(oc_surf); + buffer_destroy(buf_main); + buffer_destroy(buf_main_small); + buffer_destroy(buf_sub); + client_destroy(client); + + return RESULT_OK; +} + +static int +count_barriers(struct client *client, + struct wl_surface *wlsurface, + struct wet_testsuite_data *suite_data) +{ + int barrier_count = 0; + bool barrier; + + do { + RUN_INSIDE_BREAKPOINT(client, suite_data) { + struct weston_surface *surface; + struct wl_resource *surface_res; + + test_assert_enum(breakpoint->template_->breakpoint, + WESTON_TEST_BREAKPOINT_POST_LATCH); + surface_res = wl_client_get_object(suite_data->wl_client, + wl_proxy_get_id((struct wl_proxy *)wlsurface)); + surface = wl_resource_get_user_data(surface_res); + + barrier = surface->fifo_barrier; + if (barrier) { + barrier_count++; + REARM_BREAKPOINT(breakpoint); + } + } + } while (barrier); + return barrier_count; +} + +static int +get_surface_width(struct client *client, + struct wl_surface *wlsurface, + struct wet_testsuite_data *suite_data, + bool rearm) +{ + int width; + + RUN_INSIDE_BREAKPOINT(client, suite_data) { + struct weston_surface *surface; + struct wl_resource *surface_res; + + test_assert_enum(breakpoint->template_->breakpoint, + WESTON_TEST_BREAKPOINT_POST_LATCH); + surface_res = wl_client_get_object(suite_data->wl_client, + wl_proxy_get_id((struct wl_proxy *)wlsurface)); + surface = wl_resource_get_user_data(surface_res); + + width = surface->width; + if (rearm) + REARM_BREAKPOINT(breakpoint); + } + return width; +} + +/* Make sure fifo is ignored on synchronous subsurfaces, but works on desync */ +TEST(fifo_on_subsurface) +{ + struct wet_testsuite_data *suite_data = TEST_GET_SUITE_DATA(); + struct wl_subcompositor *subco; + struct wl_surface *surf; + struct wl_subsurface *subsurf; + struct client *client; + struct buffer *buf_main, *buf_sub, *buf_sub_2; + struct buffer *buf[10]; + struct wp_fifo_v1 *fifo; + pixman_color_t red, green, blue; + bool match; + int i; + + color_rgb888(&red, 255, 0, 0); + color_rgb888(&green, 0, 255, 0); + color_rgb888(&blue, 0, 0, 255); + + client = create_client_and_test_surface(100, 50, 100, 100); + test_assert_ptr_not_null(client); + + subco = client_get_subcompositor(client); + surf = wl_compositor_create_surface(client->wl_compositor); + + subsurf = wl_subcompositor_get_subsurface(subco, surf, + client->surface->wl_surface); + buf_main = surface_commit_color(client, client->surface->wl_surface, &red, 150, 150); + weston_test_move_surface(client->test->weston_test, client->surface->wl_surface, 50, 50); + buf_sub = surface_commit_color(client, surf, &green, 200, 200); + wl_subsurface_set_position(subsurf, -25, -25); + wl_subsurface_place_below(subsurf, client->surface->wl_surface); + + /* surf is implicitly in synchronized mode */ + wl_surface_commit(surf); + wl_surface_commit(client->surface->wl_surface); + + fifo = wp_fifo_manager_v1_get_fifo(client->fifo_manager, surf); + + match = verify_screen_content(client, "fifo_subsurface_start", 0, + NULL, 0, NULL, NO_DECORATIONS); + test_assert_true(match); + + client_push_breakpoint(client, suite_data, + WESTON_TEST_BREAKPOINT_POST_LATCH, + (struct wl_proxy *) client->output->wl_output); + + /* Since the surface is synchronized, weston will push all of these + * into the suburface cache. And also because it's synchronized, + * the fifo_wait won't wait. + */ + for (i = 0; i < 20; i++) { + if (i < 19) + wp_fifo_v1_set_barrier(fifo); + wp_fifo_v1_wait_barrier(fifo); + wl_surface_commit(surf); + } + wl_surface_commit(client->surface->wl_surface); + + /* Change the surface width so we have something to look for. */ + buf_sub_2 = surface_commit_color(client, surf, &green, 201, 201); + wl_surface_commit(surf); + wl_surface_commit(client->surface->wl_surface); + + /* This effectively serializes with the compositor, breaking at the first + * latch. If our width is updated at the first latch, then the sync + * subsurface commits were properly consumed. + */ + test_assert_int_eq(get_surface_width(client, surf, suite_data, false), 201); + + client_push_breakpoint(client, suite_data, + WESTON_TEST_BREAKPOINT_POST_LATCH, + (struct wl_proxy *) client->output->wl_output); + + /* Let's make sure desynchronized surfaces work properly too */ + wl_subsurface_set_desync(subsurf); + for (i = 0; i < 10; i++) { + /* Skip the last barrier so we're assured a redraw with no + * barrier set to give count_barriers a terminal case */ + if (i < 9) + wp_fifo_v1_set_barrier(fifo); + wp_fifo_v1_wait_barrier(fifo); + /* Commit a new buffer so there's scene damage. */ + buf[i] = surface_commit_color(client, surf, &red, 100, 100); + } + test_assert_int_eq(count_barriers(client, surf, suite_data), 9); + + wp_fifo_v1_destroy(fifo); + wl_subcompositor_destroy(subco); + wl_subsurface_destroy(subsurf); + wl_surface_destroy(surf); + buffer_destroy(buf_main); + buffer_destroy(buf_sub_2); + buffer_destroy(buf_sub); + for (i = 0; i < 10; i++) + buffer_destroy(buf[i]); + client_destroy(client); + + return RESULT_OK; +} + +/* Make sure that surface state changes that can change occlusion status are + * properly noticed before a redraw. + */ +TEST(fifo_when_occlusion_changes) +{ + struct wet_testsuite_data *suite_data = TEST_GET_SUITE_DATA(); + struct wl_subcompositor *subco; + struct wl_surface *surf; + struct wl_subsurface *subsurf; + struct wl_region *opaque_region; + struct client *client; + struct buffer *buf_main[3], *buf_sub; + struct wp_fifo_v1 *fifo; + pixman_color_t red, green, blue; + int i, width; + bool match; + + color_rgb888(&red, 255, 0, 0); + color_rgb888(&green, 0, 255, 0); + color_rgb888(&blue, 0, 0, 255); + + client = create_client_and_test_surface(100, 50, 100, 100); + test_assert_ptr_not_null(client); + + subco = client_get_subcompositor(client); + surf = wl_compositor_create_surface(client->wl_compositor); + + subsurf = wl_subcompositor_get_subsurface(subco, surf, + client->surface->wl_surface); + buf_main[0] = surface_commit_color(client, client->surface->wl_surface, &red, 150, 150); + weston_test_move_surface(client->test->weston_test, client->surface->wl_surface, 50, 50); + buf_sub = surface_commit_color(client, surf, &green, 200, 200); + wl_subsurface_set_position(subsurf, -25, -25); + /* Make the subsurface opaque and above the parent */ + opaque_region = wl_compositor_create_region(client->wl_compositor); + wl_region_add(opaque_region, 0, 0, 200, 200); + wl_surface_set_opaque_region(surf, opaque_region); + wl_region_destroy(opaque_region); + wl_subsurface_place_above(subsurf, client->surface->wl_surface); + + /* surf is implicitly in synchronized mode */ + wl_surface_commit(surf); + wl_surface_commit(client->surface->wl_surface); + + fifo = wp_fifo_manager_v1_get_fifo(client->fifo_manager, + client->surface->wl_surface); + + /* Wait for a render before we start queuing up fifo requests */ + match = verify_screen_content(client, "occlusion_change_start", 0, + NULL, 0, NULL, NO_DECORATIONS); + test_assert_true(match); + + client_push_breakpoint(client, suite_data, + WESTON_TEST_BREAKPOINT_POST_LATCH, + (struct wl_proxy *) client->output->wl_output); + + /* fifo operations should do nothing, as the surface is occluded. */ + for (i = 0; i < 30; i++) { + wp_fifo_v1_set_barrier(fifo); + wp_fifo_v1_wait_barrier(fifo); + wl_surface_commit(client->surface->wl_surface); + } + + /* Bigger buffer, the surface will no longer be fully occluded */ + wp_fifo_v1_set_barrier(fifo); + wp_fifo_v1_wait_barrier(fifo); + buf_main[1] = surface_commit_color(client, client->surface->wl_surface, &red, 200, 200); + + /* Another buffer - if visibility is improperly tracked, we'll only + * see this one and not the previous. + */ + wp_fifo_v1_set_barrier(fifo); + wp_fifo_v1_wait_barrier(fifo); + buf_main[2] = surface_commit_color(client, client->surface->wl_surface, &red, 210, 210); + client_roundtrip(client); + + width = get_surface_width(client, client->surface->wl_surface, + suite_data, true); + test_assert_int_eq(width, 200); + + width = get_surface_width(client, client->surface->wl_surface, + suite_data, false); + test_assert_int_eq(width, 210); + + wp_fifo_v1_destroy(fifo); + wl_subcompositor_destroy(subco); + wl_subsurface_destroy(subsurf); + wl_surface_destroy(surf); + for (i = 0; i < 3; i++) + buffer_destroy(buf_main[i]); + buffer_destroy(buf_sub); + client_destroy(client); + + return RESULT_OK; +} diff --git a/tests/meson.build b/tests/meson.build index 4f5d1b2a0..ad46afae9 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -156,6 +156,7 @@ tests = [ { 'name': 'drm-smoke', 'run_exclusive': true }, { 'name': 'drm-writeback-screenshot', 'run_exclusive': true }, { 'name': 'event', }, + { 'name': 'fifo', }, { 'name': 'idalloc', }, { 'name': 'keyboard', diff --git a/tests/reference/fifo_occlude_restack-00.png b/tests/reference/fifo_occlude_restack-00.png new file mode 100644 index 0000000000000000000000000000000000000000..4e0a64077e36f83ea626bdcb1eec31fce8d6f5f6 GIT binary patch literal 829 zcmeAS@N?(olHy`uVBq!ia0y~yU~~YoKX5Ps$$$P@Hb9Ck$=lt9;Xep2*t>i(0|V11 zPZ!6KiaBp@ZRBls5Me!dxXAE2pI$qY#__K^WXc;@7&Dg6{4#0!n!2LXG20nU_Rf=I zh*3}A5H@JxlsLq~lgQL&!00@qLDA=cz&E?Pf3@GM%jOu_*YzKi&@)(qp+pV4PTC9W v7T=wBk0Is>6V$D=w{h_Jebgl=sg&JhBir%K=V#vs<_88(S3j3^P6i(0|V1v zPZ!6KiaBp@ZRBls5Mgzk?`pV1=GH=n>5r@K@a7$GYGT-O$*1b1cbNT7L;csx8CCae z87%w{2&g4+2phC;N*rS0Nn~m>V050*pqO*3{$Jg_>u)Slq|5bxJb&shJ-xV5(FeQY z0T2ed@7^&};|AjxERG)l_du+a{=DhlUPgTZ94GU3O?Uy! O8VsJUelF{r5}E+%_V1$r literal 0 HcmV?d00001 diff --git a/tests/reference/fifo_subsurface_start-00.png b/tests/reference/fifo_subsurface_start-00.png new file mode 100644 index 0000000000000000000000000000000000000000..8e9e7c9b474217dd0fbd8b0c6adffb3182bcda51 GIT binary patch literal 850 zcmeAS@N?(olHy`uVBq!ia0y~yU~~YoKX5Ps$$$P@Hb9Ck$=lt9;Xep2*t>i(0|PU= zr;B4q#hkZyPxBr!5OHi(0|V1X zPZ!6KiaBrZI`TFIeRw#U+S9M+!H}~4{+d@a)o0Yme-u2(N|rICSvsnk r(r6o;AvkSg-ez+K@ts7bzzxFs7Hr3PrLX=0W(NjOS3j3^P6