From 17ab8cbd707975762e4b0cf7580ec228fc4a6434 Mon Sep 17 00:00:00 2001 From: Daniel van Vugt Date: Thu, 30 Jun 2022 18:56:06 +0800 Subject: [PATCH] tests/native-kms-render: Fix failing client-scanout test It was assuming an immediate transition from compositing (triple buffering) to direct scanout (double buffering), whereas there is a one frame delay in that transition as the buffer queue shrinks. We don't lose any frames in the transition. (cherry picked from commit d41b85398b32ed8c65830e7dfcd418604c9a53cd) Part-of: Signed-off-by: Mingi Sung --- src/tests/native-kms-render.c | 106 ++++++++++++++++++++++++++++------ 1 file changed, 87 insertions(+), 19 deletions(-) diff --git a/src/tests/native-kms-render.c b/src/tests/native-kms-render.c index f5ebc23fe..2f870fdc3 100644 --- a/src/tests/native-kms-render.c +++ b/src/tests/native-kms-render.c @@ -39,6 +39,8 @@ #include "tests/meta-wayland-test-driver.h" #include "tests/meta-wayland-test-utils.h" +#define N_FRAMES_PER_TEST 30 + typedef struct { int number_of_frames_left; @@ -46,12 +48,15 @@ typedef struct struct { int n_paints; - uint32_t fb_id; + int n_presentations; + int n_direct_scanouts; + GList *fb_ids; } scanout; gboolean wait_for_scanout; struct { + int scanouts_attempted; gboolean scanout_sabotaged; gboolean fallback_painted; guint repaint_guard_id; @@ -101,7 +106,7 @@ meta_test_kms_render_basic (void) gulong handler_id; test = (KmsRenderingTest) { - .number_of_frames_left = 10, + .number_of_frames_left = N_FRAMES_PER_TEST, .loop = g_main_loop_new (NULL, FALSE), }; handler_id = g_signal_connect (stage, "after-update", @@ -123,7 +128,6 @@ on_scanout_before_update (ClutterStage *stage, KmsRenderingTest *test) { test->scanout.n_paints = 0; - test->scanout.fb_id = 0; } static void @@ -135,6 +139,7 @@ on_scanout_before_paint (ClutterStage *stage, CoglScanout *scanout; CoglScanoutBuffer *scanout_buffer; MetaDrmBuffer *buffer; + uint32_t fb_id; scanout = clutter_stage_view_peek_scanout (stage_view); if (!scanout) @@ -143,8 +148,13 @@ on_scanout_before_paint (ClutterStage *stage, scanout_buffer = cogl_scanout_get_buffer (scanout); g_assert_true (META_IS_DRM_BUFFER (scanout_buffer)); buffer = META_DRM_BUFFER (scanout_buffer); - test->scanout.fb_id = meta_drm_buffer_get_fb_id (buffer); - g_assert_cmpuint (test->scanout.fb_id, >, 0); + fb_id = meta_drm_buffer_get_fb_id (buffer); + g_assert_cmpuint (fb_id, >, 0); + test->scanout.fb_ids = g_list_append (test->scanout.fb_ids, + GUINT_TO_POINTER (fb_id)); + + /* Triple buffering, but no higher */ + g_assert_cmpuint (g_list_length (test->scanout.fb_ids), <=, 2); } static void @@ -173,12 +183,12 @@ on_scanout_presented (ClutterStage *stage, MetaDeviceFile *device_file; GError *error = NULL; drmModeCrtc *drm_crtc; + uint32_t first_fb_id_expected; - if (test->wait_for_scanout && test->scanout.n_paints > 0) + if (test->wait_for_scanout && test->scanout.fb_ids == NULL) return; - if (test->wait_for_scanout && test->scanout.fb_id == 0) - return; + test->scanout.n_presentations++; device_pool = meta_backend_native_get_device_pool (backend_native); @@ -197,15 +207,41 @@ 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); - if (test->scanout.fb_id == 0) - g_assert_cmpuint (drm_crtc->buffer_id, !=, test->scanout.fb_id); + + if (test->scanout.fb_ids) + { + test->scanout.n_direct_scanouts++; + first_fb_id_expected = GPOINTER_TO_UINT (test->scanout.fb_ids->data); + test->scanout.fb_ids = g_list_delete_link (test->scanout.fb_ids, + test->scanout.fb_ids); + } else - g_assert_cmpuint (drm_crtc->buffer_id, ==, test->scanout.fb_id); + { + first_fb_id_expected = 0; + } + + /* The buffer ID won't match on the first frame because switching from + * triple buffered compositing to double buffered direct scanout takes + * an extra frame to drain the queue. Thereafter we are in direct scanout + * mode and expect the buffer IDs to match. + */ + if (test->scanout.n_presentations > 1) + { + if (first_fb_id_expected == 0) + g_assert_cmpuint (drm_crtc->buffer_id, !=, first_fb_id_expected); + else + g_assert_cmpuint (drm_crtc->buffer_id, ==, first_fb_id_expected); + } + drmModeFreeCrtc (drm_crtc); meta_device_file_release (device_file); - g_main_loop_quit (test->loop); + test->number_of_frames_left--; + if (test->number_of_frames_left <= 0) + g_main_loop_quit (test->loop); + else + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); } typedef enum @@ -244,7 +280,9 @@ meta_test_kms_render_client_scanout (void) g_assert_nonnull (wayland_test_client); test = (KmsRenderingTest) { + .number_of_frames_left = N_FRAMES_PER_TEST, .loop = g_main_loop_new (NULL, FALSE), + .scanout = {0}, .wait_for_scanout = TRUE, }; @@ -270,7 +308,8 @@ meta_test_kms_render_client_scanout (void) clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); g_main_loop_run (test.loop); - g_assert_cmpuint (test.scanout.fb_id, >, 0); + g_assert_cmpint (test.scanout.n_presentations, ==, N_FRAMES_PER_TEST); + g_assert_cmpint (test.scanout.n_direct_scanouts, ==, N_FRAMES_PER_TEST); g_debug ("Unmake fullscreen"); window = meta_find_window_from_title (test_context, "dma-buf-scanout-test"); @@ -292,10 +331,15 @@ meta_test_kms_render_client_scanout (void) g_assert_cmpint (buffer_rect.y, ==, 10); test.wait_for_scanout = FALSE; + test.number_of_frames_left = N_FRAMES_PER_TEST; + test.scanout.n_presentations = 0; + test.scanout.n_direct_scanouts = 0; + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); g_main_loop_run (test.loop); - g_assert_cmpuint (test.scanout.fb_id, ==, 0); + g_assert_cmpint (test.scanout.n_presentations, ==, N_FRAMES_PER_TEST); + g_assert_cmpint (test.scanout.n_direct_scanouts, ==, 0); g_debug ("Moving back to 0, 0"); meta_window_move_frame (window, TRUE, 0, 0); @@ -307,10 +351,15 @@ meta_test_kms_render_client_scanout (void) g_assert_cmpint (buffer_rect.y, ==, 0); test.wait_for_scanout = TRUE; + test.number_of_frames_left = N_FRAMES_PER_TEST; + test.scanout.n_presentations = 0; + test.scanout.n_direct_scanouts = 0; + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); g_main_loop_run (test.loop); - g_assert_cmpuint (test.scanout.fb_id, >, 0); + g_assert_cmpint (test.scanout.n_presentations, ==, N_FRAMES_PER_TEST); + g_assert_cmpint (test.scanout.n_direct_scanouts, ==, N_FRAMES_PER_TEST); g_signal_handler_disconnect (stage, before_update_handler_id); g_signal_handler_disconnect (stage, before_paint_handler_id); @@ -364,6 +413,15 @@ on_scanout_fallback_before_paint (ClutterStage *stage, if (!scanout) return; + test->scanout_fallback.scanouts_attempted++; + + /* The first scanout candidate frame will get composited due to triple + * buffering draining the queue to drop to double buffering. So don't + * sabotage that first frame. + */ + if (test->scanout_fallback.scanouts_attempted < 2) + return; + g_assert_false (test->scanout_fallback.scanout_sabotaged); if (is_atomic_mode_setting (kms_device)) @@ -401,6 +459,15 @@ on_scanout_fallback_paint_view (ClutterStage *stage, g_clear_handle_id (&test->scanout_fallback.repaint_guard_id, g_source_remove); test->scanout_fallback.fallback_painted = TRUE; + test->scanout_fallback.scanout_sabotaged = FALSE; + } + else if (test->scanout_fallback.scanouts_attempted == 1) + { + /* Now that we've seen the first scanout attempt that was inhibited by + * triple buffering, try a second frame. The second one should scanout + * and will be sabotaged. + */ + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); } } @@ -410,11 +477,11 @@ on_scanout_fallback_presented (ClutterStage *stage, ClutterFrameInfo *frame_info, KmsRenderingTest *test) { - if (!test->scanout_fallback.scanout_sabotaged) - return; + if (test->scanout_fallback.fallback_painted) + g_main_loop_quit (test->loop); - g_assert_true (test->scanout_fallback.fallback_painted); - g_main_loop_quit (test->loop); + test->number_of_frames_left--; + g_assert_cmpint (test->number_of_frames_left, >, 0); } static void @@ -443,6 +510,7 @@ meta_test_kms_render_client_scanout_fallback (void) g_assert_nonnull (wayland_test_client); test = (KmsRenderingTest) { + .number_of_frames_left = N_FRAMES_PER_TEST, .loop = g_main_loop_new (NULL, FALSE), };