From ed9a704e690fde27ff010946a2f2a0afb8b9edcb Mon Sep 17 00:00:00 2001 From: hujianwei Date: Sat, 19 Nov 2022 06:36:56 +0000 Subject: [PATCH] Add tearing control protocol * 12to11-test.xml (test_surface) : New event. * 12to11.c (XLMain): Initialize tearing control. * Imakefile (SRCS, OBJS): Add tearing_control.c/.o. (tearing-control-v1): New scanner target. * compositor.h (enum _PresentationHint): New enum. (struct _State): Add fields for tearing control. (enum _ClientDataType): Add tearing control type. * surface.c (SavePendingState, InternalCommit1): Handle presentation hints. * test.c (Commit): Send new committed event. * tests/Imakefile (OBJS16, SRCS16): Add tearing_control_test.c/tearing_control_test.o. (PROGRAMS): Add tearing_control_test. (tearing-control-v1): New scanner target. * tests/buffer_test.c (test_names): Fix typos. * tests/run_tests.sh (standard_tests): Add tearing_control_test. * tests/svnignore.txt: Add tearing_control_test. * xdg_surface.c (UpdateFrameRefreshPrediction): Return whether or not refresh prediction is on. (MaybeRunLateFrame): Don't clear StateLateFrame until after SubcompositorUpdate. (WasFrameQueued): New function. (NoteFrame): If a frame was queued and async presentation was requested, present now. --- 12to11-test.xml | 22 ++++++++++++++++++---- 12to11.c | 1 + Imakefile | 5 +++-- compositor.h | 40 ++++++++++++++++++++++++++++------------ surface.c | 8 ++++++++ test.c | 5 +++++ tests/Imakefile | 20 +++++++++++++++++++- tests/buffer_test.c | 2 +- tests/run_tests.sh | 1 + tests/svnignore.txt | 1 + xdg_surface.c | 45 +++++++++++++++++++++++++++++++++++++++------ 11 files changed, 124 insertions(+), 26 deletions(-) diff --git a/12to11-test.xml b/12to11-test.xml index 7604a4c..eeac4b5 100644 --- a/12to11-test.xml +++ b/12to11-test.xml @@ -178,10 +178,10 @@ - The activated is sent when the xdg_activation protocol causes - the surface associated with the role to be activated. Its - parameters constitute the timestamp at which the activation - occurred. + The activated event is sent when the xdg_activation protocol + causes the surface associated with the role to be activated. + Its parameters constitute the timestamp at which the + activation occurred. If the surface that created the activation token used to activate this test surface belongs to the same client that @@ -194,6 +194,20 @@ + + + + The committed event is sent immediately after the role is + committed, and contains some information about what choices + were taken by the protocol translator during presentation. + + presentation_hint is the presentation hint used by the + protocol translator during drawing. Its value is that of the + enumerator used internally, where 1 means async and 0 means + vsync. + + + diff --git a/12to11.c b/12to11.c index 3a40370..92ba0f3 100644 --- a/12to11.c +++ b/12to11.c @@ -249,6 +249,7 @@ XLMain (int argc, char **argv) XLInitIdleInhibit (); XLInitPointerGestures (); XLInitXdgActivation (); + XLInitTearingControl (); XLInitTest (); /* This has to come after the rest of the initialization. */ diff --git a/Imakefile b/Imakefile index 54456cc..06fae2e 100644 --- a/Imakefile +++ b/Imakefile @@ -18,8 +18,8 @@ MakeSubdirs($(SUBDIRS)) DependSubdirs($(SUBDIRS)) #endif - SRCS = 12to11.c run.c alloc.c fns.c output.c compositor.c surface.c region.c shm.c atoms.c subcompositor.c positioner.c xdg_wm.c xdg_surface.c xdg_toplevel.c frame_clock.c xerror.c ewmh.c timer.c subsurface.c seat.c data_device.c xdg_popup.c dmabuf.c buffer.c select.c xdata.c xsettings.c dnd.c icon_surface.c primary_selection.c renderer.c picture_renderer.c explicit_synchronization.c transform.c wp_viewporter.c decoration.c text_input.c single_pixel_buffer.c drm_lease.c pointer_constraints.c time.c relative_pointer.c keyboard_shortcuts_inhibit.c idle_inhibit.c process.c fence_ring.c pointer_gestures.c test.c buffer_release.c xdg_activation.c - OBJS = 12to11.o run.o alloc.o fns.o output.o compositor.o surface.o region.o shm.o atoms.o subcompositor.o positioner.o xdg_wm.o xdg_surface.o xdg_toplevel.o frame_clock.o xerror.o ewmh.o timer.o subsurface.o seat.o data_device.o xdg_popup.o dmabuf.o buffer.o select.o xdata.o xsettings.o dnd.o icon_surface.o primary_selection.o renderer.o picture_renderer.o explicit_synchronization.o transform.o wp_viewporter.o decoration.o text_input.o single_pixel_buffer.o drm_lease.o pointer_constraints.o time.o relative_pointer.o keyboard_shortcuts_inhibit.o idle_inhibit.o process.o fence_ring.o pointer_gestures.o test.o buffer_release.o xdg_activation.o + SRCS = 12to11.c run.c alloc.c fns.c output.c compositor.c surface.c region.c shm.c atoms.c subcompositor.c positioner.c xdg_wm.c xdg_surface.c xdg_toplevel.c frame_clock.c xerror.c ewmh.c timer.c subsurface.c seat.c data_device.c xdg_popup.c dmabuf.c buffer.c select.c xdata.c xsettings.c dnd.c icon_surface.c primary_selection.c renderer.c picture_renderer.c explicit_synchronization.c transform.c wp_viewporter.c decoration.c text_input.c single_pixel_buffer.c drm_lease.c pointer_constraints.c time.c relative_pointer.c keyboard_shortcuts_inhibit.c idle_inhibit.c process.c fence_ring.c pointer_gestures.c test.c buffer_release.c xdg_activation.c tearing_control.c + OBJS = 12to11.o run.o alloc.o fns.o output.o compositor.o surface.o region.o shm.o atoms.o subcompositor.o positioner.o xdg_wm.o xdg_surface.o xdg_toplevel.o frame_clock.o xerror.o ewmh.o timer.o subsurface.o seat.o data_device.o xdg_popup.o dmabuf.o buffer.o select.o xdata.o xsettings.o dnd.o icon_surface.o primary_selection.o renderer.o picture_renderer.o explicit_synchronization.o transform.o wp_viewporter.o decoration.o text_input.o single_pixel_buffer.o drm_lease.o pointer_constraints.o time.o relative_pointer.o keyboard_shortcuts_inhibit.o idle_inhibit.o process.o fence_ring.o pointer_gestures.o test.o buffer_release.o xdg_activation.o tearing_control.o GENHEADERS = transfer_atoms.h drm_modifiers.h HEADER = $(GENHEADERS) compositor.h @@ -106,6 +106,7 @@ ScannerTarget(idle-inhibit-unstable-v1) ScannerTarget(pointer-gestures-unstable-v1) ScannerTarget(12to11-test) ScannerTarget(xdg-activation-v1) +ScannerTarget(tearing-control-v1) /* Make seat.o depend on test_seat.c, as it includes that. Both files are rather special. */ diff --git a/compositor.h b/compositor.h index a3fcef7..118eb5b 100644 --- a/compositor.h +++ b/compositor.h @@ -876,6 +876,7 @@ typedef struct _State State; typedef struct _FrameCallback FrameCallback; typedef enum _RoleType RoleType; typedef enum _FocusMode FocusMode; +typedef enum _PresentationHint PresentationHint; enum _FocusMode { @@ -901,24 +902,31 @@ enum _RoleType enum { - PendingNone = 0, - PendingOpaqueRegion = 1, - PendingInputRegion = (1 << 2), - PendingDamage = (1 << 3), - PendingSurfaceDamage = (1 << 4), - PendingBuffer = (1 << 5), - PendingFrameCallbacks = (1 << 6), - PendingBufferScale = (1 << 7), - PendingAttachments = (1 << 8), - PendingViewportSrc = (1 << 9), - PendingViewportDest = (1 << 10), - PendingBufferTransform = (1 << 11), + PendingNone = 0, + PendingOpaqueRegion = 1, + PendingInputRegion = (1 << 2), + PendingDamage = (1 << 3), + PendingSurfaceDamage = (1 << 4), + PendingBuffer = (1 << 5), + PendingFrameCallbacks = (1 << 6), + PendingBufferScale = (1 << 7), + PendingAttachments = (1 << 8), + PendingViewportSrc = (1 << 9), + PendingViewportDest = (1 << 10), + PendingBufferTransform = (1 << 11), + PendingPresentationHint = (1 << 12), /* Flags here are stored in `pending' of the current state for space reasons. */ BufferAlreadyReleased = (1 << 19), }; +enum _PresentationHint + { + PresentationHintVsync, + PresentationHintAsync, + }; + struct _FrameCallback { /* The next and last callbacks. */ @@ -968,6 +976,9 @@ struct _State /* Viewport source rectangle. */ double src_x, src_y, src_width, src_height; + + /* The presentation hint. Defaults to PresentationHintVsync. */ + PresentationHint presentation_hint; }; typedef enum _ClientDataType ClientDataType; @@ -987,6 +998,7 @@ enum _ClientDataType IdleInhibitData, MaxClientData, XdgActivationData, + TearingControlData, }; struct _DestroyCallback @@ -1889,6 +1901,10 @@ extern void XLGetTestSeat (struct wl_client *, struct wl_resource *, extern void XLInitXdgActivation (void); +/* Defined in tearing_control.c. */ + +extern void XLInitTearingControl (void); + /* Utility functions that don't belong in a specific file. */ #define ArrayElements(arr) (sizeof (arr) / sizeof (arr)[0]) diff --git a/surface.c b/surface.c index e46e005..0e1c04b 100644 --- a/surface.c +++ b/surface.c @@ -917,6 +917,10 @@ SavePendingState (Surface *surface) pixman_region32_copy (&surface->cached_state.opaque, &surface->pending_state.opaque); + if (surface->pending_state.pending & PendingPresentationHint) + surface->cached_state.presentation_hint + = surface->pending_state.presentation_hint; + if (surface->pending_state.pending & PendingBufferScale) surface->cached_state.buffer_scale = surface->pending_state.buffer_scale; @@ -1052,6 +1056,10 @@ InternalCommit1 (Surface *surface, State *pending) } } + if (pending->pending & PendingPresentationHint) + surface->current_state.presentation_hint + = pending->presentation_hint; + if (pending->pending & PendingBufferScale) { surface->current_state.buffer_scale = pending->buffer_scale; diff --git a/test.c b/test.c index 48d47a4..7f07b2e 100644 --- a/test.c +++ b/test.c @@ -226,6 +226,11 @@ Commit (Surface *surface, Role *role) mapped. */ if (test->flags & IsSurfaceMapped) SubcompositorUpdate (test->subcompositor); + + /* And send the presentation hint. */ + if (test->role.resource) + test_surface_send_committed (test->role.resource, + surface->current_state.presentation_hint); } static Bool diff --git a/tests/Imakefile b/tests/Imakefile index fe00a46..e13dd20 100644 --- a/tests/Imakefile +++ b/tests/Imakefile @@ -28,6 +28,7 @@ ScannerTarget(viewporter) ScannerTarget(linux-dmabuf-unstable-v1) ScannerTarget(xdg-activation-v1) ScannerTarget(single-pixel-buffer-v1) +ScannerTarget(tearing-control-v1) /* Not actually a test. */ SRCS1 = $(COMMONSRCS) imgview.c @@ -60,10 +61,25 @@ ScannerTarget(single-pixel-buffer-v1) OBJS14 = $(COMMONSRCS) single_pixel_buffer_test.o SRCS15 = $(COMMONSRCS) buffer_test.c OBJS15 = $(COMMONSRCS) buffer_test.o - PROGRAMS = imgview simple_test damage_test transform_test viewporter_test subsurface_test scale_test seat_test dmabuf_test select_test select_helper select_helper_multiple xdg_activation_test single_pixel_buffer_test buffer_test + SRCS16 = $(COMMONSRCS) tearing_control_test.c + OBJS16 = $(COMMONSRCS) tearing_control_test.o + PROGRAMS = imgview simple_test damage_test transform_test viewporter_test subsurface_test scale_test seat_test dmabuf_test select_test select_helper select_helper_multiple xdg_activation_test single_pixel_buffer_test buffer_test tearing_control_test /* Make all objects depend on HEADER. */ $(OBJS1): $(HEADER) +$(OBJS2): $(HEADER) +$(OBJS3): $(HEADER) +$(OBJS4): $(HEADER) +$(OBJS5): $(HEADER) +$(OBJS6): $(HEADER) +$(OBJS7): $(HEADER) +$(OBJS8): $(HEADER) +$(OBJS9): $(HEADER) +$(OBJS10): $(HEADER) +$(OBJS13): $(HEADER) +$(OBJS14): $(HEADER) +$(OBJS15): $(HEADER) +$(OBJS16): $(HEADER) /* And depend on all sources and headers. */ depend:: $(HEADER) $(COMMONSRCS) @@ -83,11 +99,13 @@ NormalProgramTarget(select_helper_multiple,$(OBJS12),NullParameter,$(XLIB),NullP NormalProgramTarget(xdg_activation_test,$(OBJS13),NullParameter,$(LOCAL_LIBRARIES),NullParameter) NormalProgramTarget(single_pixel_buffer_test,$(OBJS14),NullParameter,$(LOCAL_LIBRARIES),NullParameter) NormalProgramTarget(buffer_test,$(OBJS15),NullParameter,$(LOCAL_LIBRARIES),NullParameter) +NormalProgramTarget(tearing_control_test,$(OBJS16),NullParameter,$(LOCAL_LIBRARIES),NullParameter) DependTarget3($(SRCS1),$(SRCS2),$(SRCS3)) DependTarget3($(SRCS4),$(SRCS5),$(SRCS6)) DependTarget3($(SRCS7),$(SRCS8),$(SRCS9)) DependTarget3($(SRCS10),$(SRCS11),$(SRCS12)) DependTarget3($(SRCS13),$(SRCS14),$(SRCS15)) +DependTarget3($(SRCS16),NullParameter,NullParameter) all:: $(PROGRAMS) diff --git a/tests/buffer_test.c b/tests/buffer_test.c index e8967bb..a9ac825 100644 --- a/tests/buffer_test.c +++ b/tests/buffer_test.c @@ -30,7 +30,7 @@ enum test_kind static const char *test_names[] = { "buffer_release", - "buffer_destroy_kind", + "buffer_destroy", }; #define LAST_TEST BUFFER_DESTROY_KIND diff --git a/tests/run_tests.sh b/tests/run_tests.sh index 72b125f..375ff24 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -23,6 +23,7 @@ declare -a standard_tests=( simple_test damage_test transform_test viewporter_test subsurface_test scale_test seat_test dmabuf_test xdg_activation_test single_pixel_buffer_test buffer_test + tearing_control_test ) make -C . "${standard_tests[@]}" diff --git a/tests/svnignore.txt b/tests/svnignore.txt index 7c81a58..f8e0cf1 100644 --- a/tests/svnignore.txt +++ b/tests/svnignore.txt @@ -15,6 +15,7 @@ select_helper_multiple xdg_activation_test single_pixel_buffer_test buffer_test +tearing_control_test imgview reject.dump Makefile diff --git a/xdg_surface.c b/xdg_surface.c index 5bca78a..0de8d46 100644 --- a/xdg_surface.c +++ b/xdg_surface.c @@ -276,7 +276,7 @@ RunFrameCallbacksConditionally (XdgRole *role) role->state |= StatePendingFrameCallback; } -static void +static Bool UpdateFrameRefreshPrediction (XdgRole *role) { int desync_children; @@ -296,7 +296,11 @@ UpdateFrameRefreshPrediction (XdgRole *role) XLFrameClockSetPredictRefresh (role->clock); else XLFrameClockDisablePredictRefresh (role->clock); + + return desync_children > 0; } + + return False; } static void @@ -862,15 +866,15 @@ MaybeRunLateFrame (XdgRole *role) if (role->state & StateLateFrame) { - /* Clear the late frame flag. */ - role->state &= ~StateLateFrame; - if (role->state & StateLateFrameAcked) XLFrameClockUnfreeze (role->clock); /* Now apply the state in the late frame. */ SubcompositorUpdate (role->subcompositor); + /* Clear the late frame flag. */ + role->state &= ~StateLateFrame; + /* Return True, as a new update has started. */ return True; } @@ -1169,10 +1173,20 @@ WriteRedirectProperty (XdgRole *role) (unsigned char *) &bypass_compositor, 1); } +static Bool +WasFrameQueued (XdgRole *role) +{ + /* Return whether or not the translator slept before displaying this + frame in response to a frame drawn event. */ + + return (role->state & StateLateFrame) != 0; +} + static void NoteFrame (FrameMode mode, uint64_t id, void *data) { XdgRole *role; + Bool predict_refresh, urgent; role = data; @@ -1186,9 +1200,28 @@ NoteFrame (FrameMode mode, uint64_t id, void *data) { /* Update whether or not frame refresh prediction is to be used. */ - UpdateFrameRefreshPrediction (role); + predict_refresh = UpdateFrameRefreshPrediction (role); - if (XLFrameClockStartFrame (role->clock, False)) + /* A rule of thumb is to never let the compositing manager + read or try to scan out incomplete buffer contents. + Thus, if the client asked for async presentation, Commit + will still wait for the compositor to finish drawing the + last frame. + + In addition, how subsurfaces fit into all of this is + unclear. At present, the presentation hint of + subsurfaces is simply discarded, and the hint of the + topmost surface used instead. In addition, asynchronous + subsurfaces will prevent async presentation from taking + place at all. */ + + urgent = (WasFrameQueued (role) + && !predict_refresh + && role->role.surface + && (role->role.surface->current_state.presentation_hint + == PresentationHintAsync)); + + if (XLFrameClockStartFrame (role->clock, urgent)) role->state |= StateFrameStarted; }