From 2122e49beaac2453707c136e418e196a41537912 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 17 Nov 2019 00:20:26 +0100 Subject: [PATCH] presentation-time: add helper for common case Most of the time, compositors just display the surface's current buffer on an output. Add an helper to make it easy to support presentation-time in this case. --- include/wlr/types/wlr_presentation_time.h | 30 +++++++++ types/wlr_presentation_time.c | 81 +++++++++++++++++++++++ 2 files changed, 111 insertions(+) diff --git a/include/wlr/types/wlr_presentation_time.h b/include/wlr/types/wlr_presentation_time.h index f25bd1a1..9a7e6e70 100644 --- a/include/wlr/types/wlr_presentation_time.h +++ b/include/wlr/types/wlr_presentation_time.h @@ -14,6 +14,9 @@ #include #include +struct wlr_output; +struct wlr_output_event_present; + struct wlr_presentation { struct wl_global *global; struct wl_list resources; // wl_resource_get_link @@ -42,8 +45,17 @@ struct wlr_presentation_feedback { bool sampled; bool presented; + // Only when the wlr_presentation_surface_sampled_on_output helper has been + // called + struct wlr_output *output; + bool output_committed; + uint32_t output_commit_seq; + struct wl_listener surface_commit; struct wl_listener surface_destroy; + struct wl_listener output_commit; + struct wl_listener output_present; + struct wl_listener output_destroy; }; struct wlr_presentation_event { @@ -81,4 +93,22 @@ void wlr_presentation_feedback_send_presented( void wlr_presentation_feedback_destroy( struct wlr_presentation_feedback *feedback); +/** + * Fill a wlr_presentation_event from a wlr_output_event_present. + */ +void wlr_presentation_event_from_output(struct wlr_presentation_event *event, + const struct wlr_output_event_present *output_event); + +/** + * Mark the current surface's buffer as sampled on the given output. + * + * Instead of calling wlr_presentation_surface_sampled and managing the + * wlr_presentation_feedback itself, the compositor can call this function + * before a wlr_output_commit call to indicate that the surface's current + * contents will be displayed on the output. + */ +void wlr_presentation_surface_sampled_on_output( + struct wlr_presentation *presentation, struct wlr_surface *surface, + struct wlr_output *output); + #endif diff --git a/types/wlr_presentation_time.c b/types/wlr_presentation_time.c index f0b8965e..97d24042 100644 --- a/types/wlr_presentation_time.c +++ b/types/wlr_presentation_time.c @@ -1,6 +1,7 @@ #define _POSIX_C_SOURCE 199309L #include #include +#include #include #include #include @@ -250,6 +251,8 @@ struct wlr_presentation_feedback *wlr_presentation_surface_sampled( return NULL; } +static void feedback_unset_output(struct wlr_presentation_feedback *feedback); + void wlr_presentation_feedback_destroy( struct wlr_presentation_feedback *feedback) { if (feedback == NULL) { @@ -265,6 +268,84 @@ void wlr_presentation_feedback_destroy( assert(wl_list_empty(&feedback->resources)); feedback_unset_surface(feedback); + feedback_unset_output(feedback); wl_list_remove(&feedback->link); free(feedback); } + +void wlr_presentation_event_from_output(struct wlr_presentation_event *event, + const struct wlr_output_event_present *output_event) { + memset(event, 0, sizeof(*event)); + event->output = output_event->output; + event->tv_sec = (uint64_t)output_event->when->tv_sec; + event->tv_nsec = (uint32_t)output_event->when->tv_nsec; + event->refresh = (uint32_t)output_event->refresh; + event->seq = (uint64_t)output_event->seq; + event->flags = output_event->flags; +} + +static void feedback_unset_output(struct wlr_presentation_feedback *feedback) { + if (feedback->output == NULL) { + return; + } + + feedback->output = NULL; + wl_list_remove(&feedback->output_commit.link); + wl_list_remove(&feedback->output_present.link); + wl_list_remove(&feedback->output_destroy.link); +} + +static void feedback_handle_output_commit(struct wl_listener *listener, + void *data) { + struct wlr_presentation_feedback *feedback = + wl_container_of(listener, feedback, output_commit); + if (feedback->output_committed) { + return; + } + feedback->output_committed = true; + feedback->output_commit_seq = feedback->output->commit_seq; +} + +static void feedback_handle_output_present(struct wl_listener *listener, + void *data) { + struct wlr_presentation_feedback *feedback = + wl_container_of(listener, feedback, output_present); + struct wlr_output_event_present *output_event = data; + + if (!feedback->output_committed || + output_event->commit_seq != feedback->output_commit_seq) { + return; + } + + struct wlr_presentation_event event = {0}; + wlr_presentation_event_from_output(&event, output_event); + wlr_presentation_feedback_send_presented(feedback, &event); + wlr_presentation_feedback_destroy(feedback); +} + +static void feedback_handle_output_destroy(struct wl_listener *listener, + void *data) { + struct wlr_presentation_feedback *feedback = + wl_container_of(listener, feedback, output_destroy); + wlr_presentation_feedback_destroy(feedback); +} + +void wlr_presentation_surface_sampled_on_output( + struct wlr_presentation *presentation, struct wlr_surface *surface, + struct wlr_output *output) { + struct wlr_presentation_feedback *feedback = + wlr_presentation_surface_sampled(presentation, surface); + if (feedback == NULL) { + return; + } + + assert(feedback->output == NULL); + feedback->output = output; + + feedback->output_commit.notify = feedback_handle_output_commit; + wl_signal_add(&output->events.commit, &feedback->output_commit); + feedback->output_present.notify = feedback_handle_output_present; + wl_signal_add(&output->events.present, &feedback->output_present); + feedback->output_destroy.notify = feedback_handle_output_destroy; + wl_signal_add(&output->events.destroy, &feedback->output_destroy); +}