From 41f488a65ca67ee7e83a952c02af2fae60cc7583 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 14 Sep 2022 23:12:00 +0200 Subject: [PATCH 1/3] tests/wayland-unit: Move out sync point wait helper Part-of: --- src/tests/meta-wayland-test-driver.c | 25 +++++++++++++++++++++++++ src/tests/meta-wayland-test-driver.h | 3 +++ src/tests/wayland-unit-tests.c | 20 +------------------- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/tests/meta-wayland-test-driver.c b/src/tests/meta-wayland-test-driver.c index d418fa14f3..a9bb251234 100644 --- a/src/tests/meta-wayland-test-driver.c +++ b/src/tests/meta-wayland-test-driver.c @@ -331,3 +331,28 @@ meta_wayland_test_driver_set_property (MetaWaylandTestDriver *test_driver, g_strdup (name), g_strdup (value)); } + +static void +on_sync_point (MetaWaylandTestDriver *test_driver, + unsigned int sequence, + struct wl_resource *surface_resource, + struct wl_client *wl_client, + unsigned int *latest_sequence) +{ + *latest_sequence = sequence; +} + +void +meta_wayland_test_driver_wait_for_sync_point (MetaWaylandTestDriver *test_driver, + unsigned int sync_point) +{ + gulong handler_id; + unsigned int latest_sequence = sync_point - 1; + + handler_id = g_signal_connect (test_driver, "sync-point", + G_CALLBACK (on_sync_point), + &latest_sequence); + while (latest_sequence != sync_point) + g_main_context_iteration (NULL, TRUE); + g_signal_handler_disconnect (test_driver, handler_id); +} diff --git a/src/tests/meta-wayland-test-driver.h b/src/tests/meta-wayland-test-driver.h index b20af74496..dbd6ab82ba 100644 --- a/src/tests/meta-wayland-test-driver.h +++ b/src/tests/meta-wayland-test-driver.h @@ -34,4 +34,7 @@ void meta_wayland_test_driver_set_property (MetaWaylandTestDriver *test_driver, const char *name, const char *value); +void meta_wayland_test_driver_wait_for_sync_point (MetaWaylandTestDriver *test_driver, + unsigned int sync_point); + #endif /* META_WAYLAND_TEST_DRIVER_H */ diff --git a/src/tests/wayland-unit-tests.c b/src/tests/wayland-unit-tests.c index 12dc6d8d36..2dc2f43815 100644 --- a/src/tests/wayland-unit-tests.c +++ b/src/tests/wayland-unit-tests.c @@ -385,28 +385,10 @@ toplevel_activation (void) meta_wayland_test_client_finish (data.wayland_test_client); } -static void -on_sync_point (MetaWaylandTestDriver *test_driver, - unsigned int sequence, - struct wl_resource *surface_resource, - struct wl_client *wl_client, - unsigned int *latest_sequence) -{ - *latest_sequence = sequence; -} - static void wait_for_sync_point (unsigned int sync_point) { - gulong handler_id; - unsigned int latest_sequence = 0; - - handler_id = g_signal_connect (test_driver, "sync-point", - G_CALLBACK (on_sync_point), - &latest_sequence); - while (latest_sequence != sync_point) - g_main_context_iteration (NULL, TRUE); - g_signal_handler_disconnect (test_driver, handler_id); + meta_wayland_test_driver_wait_for_sync_point (test_driver, sync_point); } static gboolean -- GitLab From 85ef0d1a46b9c8d179f33208b5396b6a1f4225c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 14 Sep 2022 23:12:52 +0200 Subject: [PATCH 2/3] compositor-view/native: Check that the actor covers the the view If we have a window that match the size (i.e. will pass the "fits framebuffer" low level check), that doesn't mean it matches the position. For example, if we have two monitors 2K monitors, with two 2K sized windows, one on monitor A, and one on monitor both monitor A and B, overlapping both, if the latter window is above the former, it'll end up bing scanned out on both if it ends up fitting all the other requirements. Fix this by checking that the paint box matches the stage view layout, as that makes sure the actor we're painting isn't just partially on the right view. Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/2387 Part-of: --- clutter/clutter/clutter-mutter.h | 5 +++++ src/compositor/meta-compositor-view-native.c | 17 +++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/clutter/clutter/clutter-mutter.h b/clutter/clutter/clutter-mutter.h index b383ff518c..04efa5d86d 100644 --- a/clutter/clutter/clutter-mutter.h +++ b/clutter/clutter/clutter-mutter.h @@ -38,6 +38,11 @@ #include "clutter-stage-view-private.h" #include "clutter.h" +/* An epsilon larger than FLT_EPSILON that is useful when comparing coordinates + * while ignoring floating point precision loss that might happen during + * various matrix calculations. */ +#define CLUTTER_COORDINATE_EPSILON (1.0 / 256.0) + typedef struct _ClutterMainContext ClutterContext; typedef ClutterBackend * (* ClutterBackendConstructor) (gpointer user_data); diff --git a/src/compositor/meta-compositor-view-native.c b/src/compositor/meta-compositor-view-native.c index 932d43a3b9..4c7e7f7a89 100644 --- a/src/compositor/meta-compositor-view-native.c +++ b/src/compositor/meta-compositor-view-native.c @@ -83,6 +83,8 @@ find_scanout_candidate (MetaCompositorView *compositor_view, CoglFramebuffer *framebuffer; MetaWindowActor *window_actor; MetaWindow *window; + MetaRectangle view_rect; + ClutterActorBox actor_box; MetaSurfaceActor *surface_actor; MetaSurfaceActorWayland *surface_actor_wayland; MetaWaylandSurface *surface; @@ -126,6 +128,21 @@ find_scanout_candidate (MetaCompositorView *compositor_view, if (meta_surface_actor_is_obscured (surface_actor)) return FALSE; + if (!clutter_actor_get_paint_box (CLUTTER_ACTOR (surface_actor), + &actor_box)) + return FALSE; + + clutter_stage_view_get_layout (stage_view, &view_rect); + if (!G_APPROX_VALUE (actor_box.x1, view_rect.x, + CLUTTER_COORDINATE_EPSILON) || + !G_APPROX_VALUE (actor_box.y1, view_rect.y, + CLUTTER_COORDINATE_EPSILON) || + !G_APPROX_VALUE (actor_box.x2, view_rect.x + view_rect.width, + CLUTTER_COORDINATE_EPSILON) || + !G_APPROX_VALUE (actor_box.y2, view_rect.y + view_rect.height, + CLUTTER_COORDINATE_EPSILON)) + return FALSE; + surface_actor_wayland = META_SURFACE_ACTOR_WAYLAND (surface_actor); surface = meta_surface_actor_wayland_get_surface (surface_actor_wayland); if (!surface) -- GitLab From 6601c3b02ab9d3fddce71c26618138122bc3fe7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 14 Sep 2022 23:18:37 +0200 Subject: [PATCH 3/3] tests: Add partial-overlapping scanout test Make sure that if we wiggle a scan-out capable surface a bit, it won't scan out if it's not exactly in the right position. Do this by first making the window not fullscreen, then moving it back and forth, verifying the correct scanout state for each presented frame. This test addition reproduces the issue described in https://gitlab.gnome.org/GNOME/mutter/-/issues/2387. Part-of: --- src/tests/native-kms-render.c | 70 +++++++++++++++++-- .../wayland-test-clients/dma-buf-scanout.c | 46 +++++++++++- 2 files changed, 109 insertions(+), 7 deletions(-) diff --git a/src/tests/native-kms-render.c b/src/tests/native-kms-render.c index 278edf4bd2..6595eb5a29 100644 --- a/src/tests/native-kms-render.c +++ b/src/tests/native-kms-render.c @@ -32,6 +32,7 @@ #include "core/display-private.h" #include "meta/meta-backend.h" #include "meta-test/meta-context-test.h" +#include "tests/meta-test-utils.h" #include "tests/meta-wayland-test-driver.h" #include "tests/meta-wayland-test-utils.h" @@ -44,6 +45,8 @@ typedef struct int n_paints; uint32_t fb_id; } scanout; + + gboolean wait_for_scanout; } KmsRenderingTest; static MetaContext *test_context; @@ -139,7 +142,7 @@ on_scanout_presented (ClutterStage *stage, if (test->scanout.n_paints > 0) return; - if (test->scanout.fb_id == 0) + if (test->wait_for_scanout && test->scanout.fb_id == 0) return; device_pool = meta_backend_native_get_device_pool (backend_native); @@ -159,7 +162,10 @@ on_scanout_presented (ClutterStage *stage, drm_crtc = drmModeGetCrtc (meta_device_file_get_fd (device_file), meta_kms_crtc_get_id (kms_crtc)); g_assert_nonnull (drm_crtc); - g_assert_cmpuint (drm_crtc->buffer_id, ==, test->scanout.fb_id); + if (test->scanout.fb_id == 0) + g_assert_cmpuint (drm_crtc->buffer_id, !=, test->scanout.fb_id); + else + g_assert_cmpuint (drm_crtc->buffer_id, ==, test->scanout.fb_id); drmModeFreeCrtc (drm_crtc); meta_device_file_release (device_file); @@ -167,13 +173,19 @@ on_scanout_presented (ClutterStage *stage, g_main_loop_quit (test->loop); } +typedef enum +{ + SCANOUT_WINDOW_STATE_NONE, + SCANOUT_WINDOW_STATE_FULLSCREEN, +} ScanoutWindowState; + static void meta_test_kms_render_client_scanout (void) { MetaBackend *backend = meta_context_get_backend (test_context); MetaWaylandCompositor *wayland_compositor = meta_context_get_wayland_compositor (test_context); - ClutterActor *stage = meta_backend_get_stage (backend); + ClutterStage *stage = CLUTTER_STAGE (meta_backend_get_stage (backend)); MetaKms *kms = meta_backend_native_get_kms (META_BACKEND_NATIVE (backend)); MetaKmsDevice *kms_device = meta_kms_get_devices (kms)->data; KmsRenderingTest test; @@ -183,6 +195,9 @@ meta_test_kms_render_client_scanout (void) gulong before_paint_handler_id; gulong paint_view_handler_id; gulong presented_handler_id; + MetaWindow *window; + MetaRectangle view_rect; + MetaRectangle buffer_rect; test_driver = meta_wayland_test_driver_new (wayland_compositor); meta_wayland_test_driver_set_property (test_driver, @@ -195,8 +210,15 @@ meta_test_kms_render_client_scanout (void) test = (KmsRenderingTest) { .loop = g_main_loop_new (NULL, FALSE), + .wait_for_scanout = TRUE, }; + g_assert_cmpuint (g_list_length (clutter_stage_peek_stage_views (stage)), + ==, + 1); + clutter_stage_view_get_layout (clutter_stage_peek_stage_views (stage)->data, + &view_rect); + paint_view_handler_id = g_signal_connect (stage, "paint-view", G_CALLBACK (on_scanout_paint_view), &test); @@ -212,7 +234,46 @@ meta_test_kms_render_client_scanout (void) clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); g_main_loop_run (test.loop); - g_main_loop_unref (test.loop); + + g_assert_cmpuint (test.scanout.fb_id, >, 0); + + g_debug ("Unmake fullscreen"); + window = meta_find_window_from_title (test_context, "dma-buf-scanout-test"); + g_assert_true (meta_window_is_fullscreen (window)); + meta_window_unmake_fullscreen (window); + + g_debug ("Wait for fullscreen"); + meta_wayland_test_driver_wait_for_sync_point (test_driver, + SCANOUT_WINDOW_STATE_NONE); + g_assert_false (meta_window_is_fullscreen (window)); + + g_debug ("Moving to 10, 10"); + meta_window_move_frame (window, TRUE, 10, 10); + + meta_window_get_buffer_rect (window, &buffer_rect); + g_assert_cmpint (buffer_rect.width, ==, view_rect.width); + g_assert_cmpint (buffer_rect.height, ==, view_rect.height); + g_assert_cmpint (buffer_rect.x, ==, 10); + g_assert_cmpint (buffer_rect.y, ==, 10); + + test.wait_for_scanout = FALSE; + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + g_main_loop_run (test.loop); + + g_assert_cmpuint (test.scanout.fb_id, ==, 0); + + g_debug ("Moving back to 0, 0"); + meta_window_move_frame (window, TRUE, 0, 0); + + meta_window_get_buffer_rect (window, &buffer_rect); + g_assert_cmpint (buffer_rect.width, ==, view_rect.width); + g_assert_cmpint (buffer_rect.height, ==, view_rect.height); + g_assert_cmpint (buffer_rect.x, ==, 0); + g_assert_cmpint (buffer_rect.y, ==, 0); + + test.wait_for_scanout = TRUE; + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + g_main_loop_run (test.loop); g_assert_cmpuint (test.scanout.fb_id, >, 0); @@ -223,6 +284,7 @@ meta_test_kms_render_client_scanout (void) meta_wayland_test_driver_emit_sync_event (test_driver, 0); meta_wayland_test_client_finish (wayland_test_client); + g_main_loop_unref (test.loop); } static void diff --git a/src/tests/wayland-test-clients/dma-buf-scanout.c b/src/tests/wayland-test-clients/dma-buf-scanout.c index 25b9066965..273413f43f 100644 --- a/src/tests/wayland-test-clients/dma-buf-scanout.c +++ b/src/tests/wayland-test-clients/dma-buf-scanout.c @@ -53,6 +53,12 @@ #include "linux-dmabuf-unstable-v1-client-protocol.h" +typedef enum +{ + WINDOW_STATE_NONE, + WINDOW_STATE_FULLSCREEN, +} WindowState; + typedef struct _Buffer { struct wl_buffer *buffer; @@ -78,6 +84,10 @@ struct gbm_device *gbm_device; static GList *active_buffers; +static int prev_width; +static int prev_height; +static WindowState window_state; + static struct { uint32_t format; @@ -214,15 +224,44 @@ draw_main (int width, wl_surface_attach (surface, buffer->buffer, 0, 0); } +static WindowState +parse_xdg_toplevel_state (struct wl_array *states) +{ + uint32_t *state_ptr; + + wl_array_for_each (state_ptr, states) + { + uint32_t state = *state_ptr; + + if (state == XDG_TOPLEVEL_STATE_FULLSCREEN) + return WINDOW_STATE_FULLSCREEN; + } + + return WINDOW_STATE_NONE; +} + static void handle_xdg_toplevel_configure (void *user_data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, - struct wl_array *state) + struct wl_array *states) { - g_assert_cmpint (width, >, 0); - g_assert_cmpint (height, >, 0); + g_assert (width > 0 || prev_width > 0); + g_assert (height > 0 || prev_width > 0); + + if (width > 0 && height > 0) + { + prev_width = width; + prev_height = height; + } + else + { + width = prev_width; + height = prev_height; + } + + window_state = parse_xdg_toplevel_state (states); draw_main (width, height); } @@ -270,6 +309,7 @@ handle_xdg_surface_configure (void *user_data, frame_callback = wl_surface_frame (surface); wl_callback_add_listener (frame_callback, &frame_listener, NULL); wl_surface_commit (surface); + test_driver_sync_point (display->test_driver, window_state, NULL); wl_display_flush (display->display); } -- GitLab