diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index ead470396..1dae4b7db 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -66,6 +66,8 @@ enum wlr_output_state_field { WLR_OUTPUT_STATE_RENDER_FORMAT = 1 << 8, WLR_OUTPUT_STATE_SUBPIXEL = 1 << 9, WLR_OUTPUT_STATE_LAYERS = 1 << 10, + WLR_OUTPUT_STATE_WAIT_TIMELINE = 1 << 11, + WLR_OUTPUT_STATE_SIGNAL_TIMELINE = 1 << 12, }; enum wlr_output_state_mode_type { @@ -109,6 +111,11 @@ struct wlr_output_state { struct wlr_output_layer_state *layers; size_t layers_len; + + struct wlr_drm_syncobj_timeline *wait_timeline; + uint64_t wait_point; + struct wlr_drm_syncobj_timeline *signal_timeline; + uint64_t signal_point; }; struct wlr_output_impl; @@ -162,6 +169,8 @@ struct wlr_output { // true for example with VR headsets bool non_desktop; + // Whether wait/signal timelines are supported + bool timeline; // Commit sequence number. Incremented on each commit, may overflow. uint32_t commit_seq; @@ -536,6 +545,33 @@ void wlr_output_state_set_damage(struct wlr_output_state *state, */ void wlr_output_state_set_layers(struct wlr_output_state *state, struct wlr_output_layer_state *layers, size_t layers_len); +/** + * Set a timeline point to wait on before displaying the next frame. + * + * Committing a wait timeline point without a buffer is invalid. + * + * There is only a single wait timeline point, waiting for multiple timeline + * points is unsupported. + * + * Support for this feature is advertised by the timeline field in + * struct wlr_output. + */ +void wlr_output_state_set_wait_timeline(struct wlr_output_state *state, + struct wlr_drm_syncobj_timeline *timeline, uint64_t src_point); +/** + * Set a timeline point to be signalled when the frame is no longer being used + * by the backend. + * + * Committing a signal timeline point without a buffer is invalid. + * + * There is only a single signal timeline point, signalling multiple timeline + * points is unsupported. + * + * Support for this feature is advertised by the timeline field in + * struct wlr_output. + */ +void wlr_output_state_set_signal_timeline(struct wlr_output_state *state, + struct wlr_drm_syncobj_timeline *timeline, uint64_t dst_point); /** * Copies the output state from src to dst. It is safe to then diff --git a/types/output/output.c b/types/output/output.c index 818f4549e..1c9a00779 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -563,9 +563,19 @@ static bool output_basic_test(struct wlr_output *output, wlr_log(WLR_DEBUG, "Primary buffer size mismatch"); return false; } - } else if (state->tearing_page_flip) { - wlr_log(WLR_ERROR, "Trying to commit a tearing page flip without a buffer?"); - return false; + } else { + if (state->tearing_page_flip) { + wlr_log(WLR_ERROR, "Tried to commit a tearing page flip without a buffer"); + return false; + } + if (state->committed & WLR_OUTPUT_STATE_WAIT_TIMELINE) { + wlr_log(WLR_DEBUG, "Tried to set wait timeline without a buffer"); + return false; + } + if (state->committed & WLR_OUTPUT_STATE_SIGNAL_TIMELINE) { + wlr_log(WLR_DEBUG, "Tried to set signal timeline without a buffer"); + return false; + } } if (state->committed & WLR_OUTPUT_STATE_RENDER_FORMAT) { @@ -632,6 +642,12 @@ static bool output_basic_test(struct wlr_output *output, } } + if ((state->committed & (WLR_OUTPUT_STATE_WAIT_TIMELINE | WLR_OUTPUT_STATE_SIGNAL_TIMELINE)) && + !output->timeline) { + wlr_log(WLR_DEBUG, "Wait/signal timelines are not supported for this output"); + return false; + } + return true; } diff --git a/types/output/state.c b/types/output/state.c index 0909b3e8a..465b54ada 100644 --- a/types/output/state.c +++ b/types/output/state.c @@ -1,5 +1,6 @@ #include #include +#include #include #include "types/wlr_output.h" @@ -16,6 +17,8 @@ void wlr_output_state_finish(struct wlr_output_state *state) { state->buffer = NULL; pixman_region32_fini(&state->damage); free(state->gamma_lut); + wlr_drm_syncobj_timeline_unref(state->wait_timeline); + wlr_drm_syncobj_timeline_unref(state->signal_timeline); } void wlr_output_state_set_enabled(struct wlr_output_state *state, @@ -114,16 +117,36 @@ void wlr_output_state_set_layers(struct wlr_output_state *state, state->layers_len = layers_len; } +void wlr_output_state_set_wait_timeline(struct wlr_output_state *state, + struct wlr_drm_syncobj_timeline *timeline, uint64_t src_point) { + state->committed |= WLR_OUTPUT_STATE_WAIT_TIMELINE; + wlr_drm_syncobj_timeline_unref(state->wait_timeline); + state->wait_timeline = wlr_drm_syncobj_timeline_ref(timeline); + state->wait_point = src_point; +} + +void wlr_output_state_set_signal_timeline(struct wlr_output_state *state, + struct wlr_drm_syncobj_timeline *timeline, uint64_t dst_point) { + state->committed |= WLR_OUTPUT_STATE_SIGNAL_TIMELINE; + wlr_drm_syncobj_timeline_unref(state->signal_timeline); + state->signal_timeline = wlr_drm_syncobj_timeline_ref(timeline); + state->signal_point = dst_point; +} + bool wlr_output_state_copy(struct wlr_output_state *dst, const struct wlr_output_state *src) { struct wlr_output_state copy = *src; copy.committed &= ~(WLR_OUTPUT_STATE_BUFFER | WLR_OUTPUT_STATE_DAMAGE | - WLR_OUTPUT_STATE_GAMMA_LUT); + WLR_OUTPUT_STATE_GAMMA_LUT | + WLR_OUTPUT_STATE_WAIT_TIMELINE | + WLR_OUTPUT_STATE_SIGNAL_TIMELINE); copy.buffer = NULL; pixman_region32_init(©.damage); copy.gamma_lut = NULL; copy.gamma_lut_size = 0; + copy.wait_timeline = NULL; + copy.signal_timeline = NULL; if (src->committed & WLR_OUTPUT_STATE_BUFFER) { wlr_output_state_set_buffer(©, src->buffer); @@ -142,6 +165,15 @@ bool wlr_output_state_copy(struct wlr_output_state *dst, } } + if (src->committed & WLR_OUTPUT_STATE_WAIT_TIMELINE) { + wlr_output_state_set_wait_timeline(©, src->wait_timeline, + src->wait_point); + } + if (src->committed & WLR_OUTPUT_STATE_SIGNAL_TIMELINE) { + wlr_output_state_set_signal_timeline(©, src->signal_timeline, + src->signal_point); + } + wlr_output_state_finish(dst); *dst = copy; return true;