diff --git a/src/tests/native-kms-render.c b/src/tests/native-kms-render.c index 784fac518..43f1b09db 100644 --- a/src/tests/native-kms-render.c +++ b/src/tests/native-kms-render.c @@ -39,29 +39,22 @@ #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; GMainLoop *loop; struct { - int n_frames_started; - int n_presentations; - int n_direct_scanouts; - GList *fb_ids; - gboolean wait_for_scanout; - gboolean expect_double_buffering; + int n_paints; + uint32_t fb_id; } scanout; + gboolean wait_for_scanout; + struct { - int last_frame_started; - int last_frame_presented; - int frame_sabotaged; - int first_scanout; - int fallbacks_painted; - gboolean first_scanout_presented; + gboolean scanout_sabotaged; + gboolean fallback_painted; + guint repaint_guard_id; ClutterStageView *scanout_failed_view; } scanout_fallback; } KmsRenderingTest; @@ -108,7 +101,7 @@ meta_test_kms_render_basic (void) gulong handler_id; test = (KmsRenderingTest) { - .number_of_frames_left = N_FRAMES_PER_TEST, + .number_of_frames_left = 10, .loop = g_main_loop_new (NULL, FALSE), }; handler_id = g_signal_connect (stage, "after-update", @@ -123,6 +116,16 @@ meta_test_kms_render_basic (void) g_signal_handler_disconnect (stage, handler_id); } +static void +on_scanout_before_update (ClutterStage *stage, + ClutterStageView *stage_view, + ClutterFrame *frame, + KmsRenderingTest *test) +{ + test->scanout.n_paints = 0; + test->scanout.fb_id = 0; +} + static void on_scanout_before_paint (ClutterStage *stage, ClutterStageView *stage_view, @@ -132,9 +135,6 @@ on_scanout_before_paint (ClutterStage *stage, CoglScanout *scanout; CoglScanoutBuffer *scanout_buffer; MetaDrmBuffer *buffer; - uint32_t fb_id; - - test->scanout.n_frames_started++; scanout = clutter_stage_view_peek_scanout (stage_view); if (!scanout) @@ -143,13 +143,18 @@ 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); - 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)); + test->scanout.fb_id = meta_drm_buffer_get_fb_id (buffer); + g_assert_cmpuint (test->scanout.fb_id, >, 0); +} - /* Triple buffering, but no higher */ - g_assert_cmpuint (g_list_length (test->scanout.fb_ids), <=, 2); +static void +on_scanout_paint_view (ClutterStage *stage, + ClutterStageView *stage_view, + MtkRegion *region, + ClutterFrame *frame, + KmsRenderingTest *test) +{ + test->scanout.n_paints++; } static void @@ -168,17 +173,13 @@ on_scanout_presented (ClutterStage *stage, MetaDeviceFile *device_file; GError *error = NULL; drmModeCrtc *drm_crtc; - uint32_t first_fb_id_expected; - /* Ignore frames from previous sub-tests */ - if (test->scanout.n_frames_started <= 0) + if (test->wait_for_scanout && test->scanout.n_paints > 0) return; - if (test->scanout.wait_for_scanout && test->scanout.fb_ids == NULL) + 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); fb = clutter_stage_view_get_onscreen (stage_view); @@ -196,37 +197,15 @@ 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); - - /* Triple buffering remains in effect even when transitioning to - * direct scanout. So we expect the first presentation after - * wait_for_scanout will still be composited and won't match the head of - * fb_ids yet... - */ - if (test->scanout.fb_ids && - (test->scanout.expect_double_buffering || - test->scanout.n_presentations > 1)) - { - 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); - g_assert_cmpuint (drm_crtc->buffer_id, ==, first_fb_id_expected); - } + if (test->scanout.fb_id == 0) + g_assert_cmpuint (drm_crtc->buffer_id, !=, test->scanout.fb_id); else - { - first_fb_id_expected = 0; - g_assert_cmpuint (drm_crtc->buffer_id, !=, first_fb_id_expected); - } - + g_assert_cmpuint (drm_crtc->buffer_id, ==, test->scanout.fb_id); drmModeFreeCrtc (drm_crtc); meta_device_file_release (device_file); - 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)); + g_main_loop_quit (test->loop); } typedef enum @@ -247,7 +226,9 @@ meta_test_kms_render_client_scanout (void) KmsRenderingTest test; MetaWaylandTestClient *wayland_test_client; g_autoptr (MetaWaylandTestDriver) test_driver = NULL; + gulong before_update_handler_id; gulong before_paint_handler_id; + gulong paint_view_handler_id; gulong presented_handler_id; MetaWindow *window; MtkRectangle view_rect; @@ -263,11 +244,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, }; - test.scanout.wait_for_scanout = TRUE; g_assert_cmpuint (g_list_length (clutter_stage_peek_stage_views (stage)), ==, @@ -275,6 +254,12 @@ meta_test_kms_render_client_scanout (void) 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); + before_update_handler_id = + g_signal_connect (stage, "before-update", + G_CALLBACK (on_scanout_before_update), &test); before_paint_handler_id = g_signal_connect (stage, "before-paint", G_CALLBACK (on_scanout_before_paint), &test); @@ -285,8 +270,7 @@ meta_test_kms_render_client_scanout (void) clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); g_main_loop_run (test.loop); - g_assert_cmpint (test.scanout.n_presentations, ==, N_FRAMES_PER_TEST); - g_assert_cmpint (test.scanout.n_direct_scanouts, ==, N_FRAMES_PER_TEST - 1); + g_assert_cmpuint (test.scanout.fb_id, >, 0); g_debug ("Unmake fullscreen"); window = meta_find_window_from_title (test_context, "dma-buf-scanout-test"); @@ -307,18 +291,11 @@ meta_test_kms_render_client_scanout (void) g_assert_cmpint (buffer_rect.x, ==, 10); g_assert_cmpint (buffer_rect.y, ==, 10); - test.number_of_frames_left = N_FRAMES_PER_TEST; - test.scanout.wait_for_scanout = FALSE; - test.scanout.expect_double_buffering = TRUE; /* because wait_for_sync_point */ - test.scanout.n_frames_started = 0; - test.scanout.n_presentations = 0; - test.scanout.n_direct_scanouts = 0; - + test.wait_for_scanout = FALSE; clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); g_main_loop_run (test.loop); - g_assert_cmpint (test.scanout.n_presentations, ==, N_FRAMES_PER_TEST); - g_assert_cmpint (test.scanout.n_direct_scanouts, ==, 1); + g_assert_cmpuint (test.scanout.fb_id, ==, 0); g_debug ("Moving back to 0, 0"); meta_window_move_frame (window, TRUE, 0, 0); @@ -329,20 +306,15 @@ meta_test_kms_render_client_scanout (void) g_assert_cmpint (buffer_rect.x, ==, 0); g_assert_cmpint (buffer_rect.y, ==, 0); - test.number_of_frames_left = N_FRAMES_PER_TEST; - test.scanout.wait_for_scanout = TRUE; - test.scanout.expect_double_buffering = FALSE; - test.scanout.n_frames_started = 0; - test.scanout.n_presentations = 0; - test.scanout.n_direct_scanouts = 0; - + test.wait_for_scanout = TRUE; clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); g_main_loop_run (test.loop); - g_assert_cmpint (test.scanout.n_presentations, ==, N_FRAMES_PER_TEST); - g_assert_cmpint (test.scanout.n_direct_scanouts, ==, N_FRAMES_PER_TEST - 1); + g_assert_cmpuint (test.scanout.fb_id, >, 0); + g_signal_handler_disconnect (stage, before_update_handler_id); g_signal_handler_disconnect (stage, before_paint_handler_id); + g_signal_handler_disconnect (stage, paint_view_handler_id); g_signal_handler_disconnect (stage, presented_handler_id); meta_wayland_test_driver_emit_sync_event (test_driver, 0); @@ -350,6 +322,30 @@ meta_test_kms_render_client_scanout (void) g_main_loop_unref (test.loop); } +static gboolean +needs_repainted_guard (gpointer user_data) +{ + g_assert_not_reached (); + return G_SOURCE_REMOVE; +} + +static void +scanout_fallback_result_feedback (const MetaKmsFeedback *kms_feedback, + gpointer user_data) +{ + KmsRenderingTest *test = user_data; + + g_assert_cmpuint (test->scanout_fallback.repaint_guard_id, ==, 0); + g_assert_nonnull (test->scanout_fallback.scanout_failed_view); + + test->scanout_fallback.repaint_guard_id = + g_idle_add_full (G_PRIORITY_LOW, needs_repainted_guard, test, NULL); +} + +static const MetaKmsResultListenerVtable scanout_fallback_result_listener_vtable = { + .feedback = scanout_fallback_result_feedback, +}; + static void on_scanout_fallback_before_paint (ClutterStage *stage, ClutterStageView *stage_view, @@ -360,43 +356,15 @@ on_scanout_fallback_before_paint (ClutterStage *stage, MetaCrtc *crtc = meta_renderer_view_get_crtc (view); MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (META_CRTC_KMS (crtc)); MetaKmsDevice *kms_device = meta_kms_crtc_get_device (kms_crtc); + MetaFrameNative *frame_native = meta_frame_native_from_frame (frame); CoglScanout *scanout; - int this_frame; - - /* We don't know exactly how many frames the test will take due to: - * 1. Client scanouts taking a while to get started. - * 2. Triple buffering being asynchronous so one can't infer which DRM - * calls have completed from just the painting state. - * 3. Atomic commits now live in a separate thread! - * - * So ensure there's always a reason to start the next frame and - * the test never hangs; - */ - clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); - - this_frame = ++test->scanout_fallback.last_frame_started; + MetaKmsUpdate *kms_update; scanout = clutter_stage_view_peek_scanout (stage_view); if (!scanout) return; - if (!test->scanout_fallback.first_scanout) - { - test->scanout_fallback.first_scanout = this_frame; - return; - } - - /* Keep the test simple: Only one frame is ever sabotaged and it is - * definitely a direct scanout. But we can't rely on the value of 'scanout' - * alone because that may be non-NULL even when the next commit is going - * to be composited (triple buffering). So wait until first_scanout_presented - * before doing the sabotage. - */ - if (test->scanout_fallback.frame_sabotaged || - !test->scanout_fallback.first_scanout_presented) - return; - - test->scanout_fallback.frame_sabotaged = this_frame; + g_assert_false (test->scanout_fallback.scanout_sabotaged); if (is_atomic_mode_setting (kms_device)) { @@ -407,6 +375,17 @@ on_scanout_fallback_before_paint (ClutterStage *stage, drm_mock_queue_error (DRM_MOCK_CALL_PAGE_FLIP, EINVAL); drm_mock_queue_error (DRM_MOCK_CALL_SET_CRTC, EINVAL); } + + test->scanout_fallback.scanout_sabotaged = TRUE; + + kms_update = meta_frame_native_ensure_kms_update (frame_native, kms_device); + meta_kms_update_add_result_listener (kms_update, + &scanout_fallback_result_listener_vtable, + NULL, + test, + NULL); + + test->scanout_fallback.scanout_failed_view = stage_view; } static void @@ -416,14 +395,13 @@ on_scanout_fallback_paint_view (ClutterStage *stage, ClutterFrame *frame, KmsRenderingTest *test) { - /* With triple buffering, usable fallback paints may occur even before the - * failing commit they are needed to replace. So it would be too racy to - * check if the a notification of the failed commit has been emitted yet. - * Just make sure there has been at least one repaint after the sabotage AND - * that at the end of the test g_test_assert_expected_messages passes. - */ - if (test->scanout_fallback.frame_sabotaged) - test->scanout_fallback.fallbacks_painted++; + if (test->scanout_fallback.scanout_sabotaged) + { + g_assert_cmpuint (test->scanout_fallback.repaint_guard_id, !=, 0); + g_clear_handle_id (&test->scanout_fallback.repaint_guard_id, + g_source_remove); + test->scanout_fallback.fallback_painted = TRUE; + } } static void @@ -432,21 +410,11 @@ on_scanout_fallback_presented (ClutterStage *stage, ClutterFrameInfo *frame_info, KmsRenderingTest *test) { - int this_frame; + if (!test->scanout_fallback.scanout_sabotaged) + return; - if (test->scanout_fallback.last_frame_started <= 0) - return; /* Leftovers from previous tests. Ignore. */ - - this_frame = ++test->scanout_fallback.last_frame_presented; - if (this_frame >= test->scanout_fallback.first_scanout) - test->scanout_fallback.first_scanout_presented = TRUE; - - if (test->scanout_fallback.fallbacks_painted > 0) - g_main_loop_quit (test->loop); - - test->number_of_frames_left--; - g_assert_cmpint (test->number_of_frames_left, >, 0); - clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + g_assert_true (test->scanout_fallback.fallback_painted); + g_main_loop_quit (test->loop); } static void @@ -475,7 +443,6 @@ 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), }; @@ -497,16 +464,6 @@ meta_test_kms_render_client_scanout_fallback (void) g_main_loop_run (test.loop); g_main_loop_unref (test.loop); - g_test_message ("Test ending with:\n" - "\tfallbacks_painted: %d\n" - "\tlast_frame_started: %d\n" - "\tlast_frame_presented: %d\n" - "\tframe_sabotaged: %d", - test.scanout_fallback.fallbacks_painted, - test.scanout_fallback.last_frame_started, - test.scanout_fallback.last_frame_presented, - test.scanout_fallback.frame_sabotaged); - g_test_assert_expected_messages (); g_signal_handler_disconnect (stage, before_paint_handler_id); @@ -565,6 +522,9 @@ main (int argc, g_autoptr (MetaContext) context = NULL; g_autoptr (GError) error = NULL; + /* See https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1441#note_2350786 */ + g_setenv ("CLUTTER_PAINT", "disable-triple-buffering", TRUE); + context = meta_create_test_context (META_CONTEXT_TEST_TYPE_VKMS, META_CONTEXT_TEST_FLAG_NO_X11); g_assert_true (meta_context_configure (context, &argc, &argv, NULL));