From 23e37e7b1d8004fb5361c147239d2e628efbd5e8 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 22 Apr 2019 12:42:37 +0300 Subject: [PATCH] output: refactor frame submission API This is necessary for direct scan-out and other upcoming features. This patch changes the output API to look like the wl_surface API. Outputs now have some double-buffered state: the frame to be submitted (currently only wlr_renderer frames are supported) and the damaged region. To attach a pending frame, use wlr_output_attach_render. To set the pending damaged region, use wlr_output_set_damage. To submit the pending state, call wlr_output_commit. This will submit the pending frame to the backend. To migrate from the old API to the new one: - Replace wlr_output_make_current calls by wlr_output_attach_render - Replace wlr_output_swap_buffers calls by wlr_output_set_damage and wlr_output_commit --- examples/fullscreen-shell.c | 4 +- examples/multi-pointer.c | 4 +- examples/output-layout.c | 4 +- examples/pointer.c | 4 +- examples/rotation.c | 4 +- examples/simple.c | 4 +- examples/tablet.c | 4 +- examples/touch.c | 4 +- include/wlr/types/wlr_output.h | 53 ++++++++++++++------ tinywl/tinywl.c | 6 +-- types/wlr_output.c | 91 ++++++++++++++++++++-------------- types/wlr_output_damage.c | 7 ++- 12 files changed, 115 insertions(+), 74 deletions(-) diff --git a/examples/fullscreen-shell.c b/examples/fullscreen-shell.c index 7f2c9c2b..e9e64ce1 100644 --- a/examples/fullscreen-shell.c +++ b/examples/fullscreen-shell.c @@ -89,7 +89,7 @@ static void output_handle_frame(struct wl_listener *listener, void *data) { int width, height; wlr_output_effective_resolution(output->wlr_output, &width, &height); - if (!wlr_output_make_current(output->wlr_output, NULL)) { + if (!wlr_output_attach_render(output->wlr_output, NULL)) { return; } @@ -108,7 +108,7 @@ static void output_handle_frame(struct wl_listener *listener, void *data) { } wlr_renderer_end(renderer); - wlr_output_swap_buffers(output->wlr_output, NULL, NULL); + wlr_output_commit(output->wlr_output); } static void output_set_surface(struct fullscreen_output *output, diff --git a/examples/multi-pointer.c b/examples/multi-pointer.c index a007f712..44cf26d5 100644 --- a/examples/multi-pointer.c +++ b/examples/multi-pointer.c @@ -94,14 +94,14 @@ void output_frame_notify(struct wl_listener *listener, void *data) { struct sample_state *sample = output->sample; struct wlr_output *wlr_output = output->output; - wlr_output_make_current(wlr_output, NULL); + wlr_output_attach_render(wlr_output, NULL); glClearColor(sample->clear_color[0], sample->clear_color[1], sample->clear_color[2], sample->clear_color[3]); glClear(GL_COLOR_BUFFER_BIT); wlr_output_render_software_cursors(wlr_output, NULL); - wlr_output_swap_buffers(wlr_output, NULL, NULL); + wlr_output_commit(wlr_output); } static void handle_cursor_motion(struct wl_listener *listener, void *data) { diff --git a/examples/output-layout.c b/examples/output-layout.c index 440b3188..5e406af0 100644 --- a/examples/output-layout.c +++ b/examples/output-layout.c @@ -115,7 +115,7 @@ void output_frame_notify(struct wl_listener *listener, void *data) { struct wlr_output *wlr_output = output->output; - wlr_output_make_current(wlr_output, NULL); + wlr_output_attach_render(wlr_output, NULL); wlr_renderer_begin(sample->renderer, wlr_output->width, wlr_output->height); wlr_renderer_clear(sample->renderer, (float[]){0.25f, 0.25f, 0.25f, 1}); @@ -137,7 +137,7 @@ void output_frame_notify(struct wl_listener *listener, void *data) { } wlr_renderer_end(sample->renderer); - wlr_output_swap_buffers(wlr_output, NULL, NULL); + wlr_output_commit(wlr_output); } static void update_velocities(struct sample_state *sample, diff --git a/examples/pointer.c b/examples/pointer.c index c7140d97..cf4b122d 100644 --- a/examples/pointer.c +++ b/examples/pointer.c @@ -98,11 +98,11 @@ void output_frame_notify(struct wl_listener *listener, void *data) { struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend); assert(renderer); - wlr_output_make_current(wlr_output, NULL); + wlr_output_attach_render(wlr_output, NULL); wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); wlr_renderer_clear(renderer, state->clear_color); wlr_output_render_software_cursors(wlr_output, NULL); - wlr_output_swap_buffers(wlr_output, NULL, NULL); + wlr_output_commit(wlr_output); wlr_renderer_end(renderer); } diff --git a/examples/rotation.c b/examples/rotation.c index 7cf5727b..cfcd001b 100644 --- a/examples/rotation.c +++ b/examples/rotation.c @@ -60,7 +60,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) { int32_t width, height; wlr_output_effective_resolution(wlr_output, &width, &height); - wlr_output_make_current(wlr_output, NULL); + wlr_output_attach_render(wlr_output, NULL); wlr_renderer_begin(sample->renderer, wlr_output->width, wlr_output->height); wlr_renderer_clear(sample->renderer, (float[]){0.25f, 0.25f, 0.25f, 1}); @@ -72,7 +72,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) { } wlr_renderer_end(sample->renderer); - wlr_output_swap_buffers(wlr_output, NULL, NULL); + wlr_output_commit(wlr_output); long ms = (now.tv_sec - sample->last_frame.tv_sec) * 1000 + (now.tv_nsec - sample->last_frame.tv_nsec) / 1000000; diff --git a/examples/simple.c b/examples/simple.c index e1c10906..27eb6c20 100644 --- a/examples/simple.c +++ b/examples/simple.c @@ -56,12 +56,12 @@ void output_frame_notify(struct wl_listener *listener, void *data) { sample->dec = inc; } - wlr_output_make_current(sample_output->output, NULL); + wlr_output_attach_render(sample_output->output, NULL); glClearColor(sample->color[0], sample->color[1], sample->color[2], 1.0); glClear(GL_COLOR_BUFFER_BIT); - wlr_output_swap_buffers(sample_output->output, NULL, NULL); + wlr_output_commit(sample_output->output); sample->last_frame = now; } diff --git a/examples/tablet.c b/examples/tablet.c index fad30d52..6df28586 100644 --- a/examples/tablet.c +++ b/examples/tablet.c @@ -86,7 +86,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) { int32_t width, height; wlr_output_effective_resolution(wlr_output, &width, &height); - wlr_output_make_current(wlr_output, NULL); + wlr_output_attach_render(wlr_output, NULL); wlr_renderer_begin(sample->renderer, wlr_output->width, wlr_output->height); wlr_renderer_clear(sample->renderer, (float[]){0.25f, 0.25f, 0.25f, 1}); @@ -129,7 +129,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) { } wlr_renderer_end(sample->renderer); - wlr_output_swap_buffers(wlr_output, NULL, NULL); + wlr_output_commit(wlr_output); sample->last_frame = now; } diff --git a/examples/touch.c b/examples/touch.c index 9ed20a28..4ca9b993 100644 --- a/examples/touch.c +++ b/examples/touch.c @@ -73,7 +73,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) { int32_t width, height; wlr_output_effective_resolution(wlr_output, &width, &height); - wlr_output_make_current(wlr_output, NULL); + wlr_output_attach_render(wlr_output, NULL); wlr_renderer_begin(sample->renderer, wlr_output->width, wlr_output->height); wlr_renderer_clear(sample->renderer, (float[]){0.25f, 0.25f, 0.25f, 1}); @@ -89,7 +89,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) { } wlr_renderer_end(sample->renderer); - wlr_output_swap_buffers(wlr_output, NULL, NULL); + wlr_output_commit(wlr_output); sample->last_frame = now; } diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 820b7740..cd34dc99 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -46,16 +46,31 @@ struct wlr_output_cursor { } events; }; +enum wlr_output_state_field { + WLR_OUTPUT_STATE_BUFFER = 1 << 0, + WLR_OUTPUT_STATE_DAMAGE = 1 << 1, +}; + +/** + * Holds the double-buffered output state. + */ +struct wlr_output_state { + uint32_t committed; // enum wlr_output_state_field + pixman_region32_t damage; +}; + struct wlr_output_impl; /** * A compositor output region. This typically corresponds to a monitor that * displays part of the compositor space. * - * Compositors should listen to the `frame` event to render an output. They - * should call `wlr_output_make_current`, render and then call - * `wlr_output_swap_buffers`. No rendering should happen outside a `frame` event - * handler. + * The `frame` event will be emitted when it is a good time for the compositor + * to submit a new frame. + * + * To render a new frame, compositors should call `wlr_output_attach_render`, + * render and call `wlr_output_commit`. No rendering should happen outside a + * `frame` event handler or before `wlr_output_attach_render`. */ struct wlr_output { const struct wlr_output_impl *impl; @@ -88,6 +103,8 @@ struct wlr_output { bool frame_pending; float transform_matrix[9]; + struct wlr_output_state pending; + struct { // Request to render a frame struct wl_signal frame; @@ -189,12 +206,14 @@ void wlr_output_transformed_resolution(struct wlr_output *output, void wlr_output_effective_resolution(struct wlr_output *output, int *width, int *height); /** - * Makes the output rendering context current. + * Attach the renderer's buffer to the output. Compositors must call this + * function before rendering. After they are done rendering, they should call + * `wlr_output_commit` to submit the new frame. * - * `buffer_age` is set to the drawing buffer age in number of frames or -1 if - * unknown. This is useful for damage tracking. + * If non-NULL, `buffer_age` is set to the drawing buffer age in number of + * frames or -1 if unknown. This is useful for damage tracking. */ -bool wlr_output_make_current(struct wlr_output *output, int *buffer_age); +bool wlr_output_attach_render(struct wlr_output *output, int *buffer_age); /** * Get the preferred format for reading pixels. * This function might change the current rendering context. @@ -202,17 +221,21 @@ bool wlr_output_make_current(struct wlr_output *output, int *buffer_age); bool wlr_output_preferred_read_format(struct wlr_output *output, enum wl_shm_format *fmt); /** - * Swaps the output buffers. If the time of the frame isn't known, set `when` to - * NULL. If the compositor doesn't support damage tracking, set `damage` to - * NULL. + * Set the damage region for the frame to be submitted. * - * Damage is given in output-buffer-local coordinates (ie. scaled and + * Compositors implementing damage tracking should call this function with the + * damaged region in output-buffer-local coordinates (ie. scaled and * transformed). - * - * Swapping buffers schedules a `frame` event. */ -bool wlr_output_swap_buffers(struct wlr_output *output, struct timespec *when, +void wlr_output_set_damage(struct wlr_output *output, pixman_region32_t *damage); +/** + * Commit the pending output state. If `wlr_output_attach_render` has been + * called, the pending frame will be submitted for display. + * + * This function schedules a `frame` event. + */ +bool wlr_output_commit(struct wlr_output *output); /** * Manually schedules a `frame` event. If a `frame` event is already pending, * it is a no-op. diff --git a/tinywl/tinywl.c b/tinywl/tinywl.c index 16c1f85c..ca5cdc91 100644 --- a/tinywl/tinywl.c +++ b/tinywl/tinywl.c @@ -591,8 +591,8 @@ static void output_frame(struct wl_listener *listener, void *data) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - /* wlr_output_make_current makes the OpenGL context current. */ - if (!wlr_output_make_current(output->wlr_output, NULL)) { + /* wlr_output_attach_render makes the OpenGL context current. */ + if (!wlr_output_attach_render(output->wlr_output, NULL)) { return; } /* The "effective" resolution can change if you rotate your outputs. */ @@ -635,7 +635,7 @@ static void output_frame(struct wl_listener *listener, void *data) { /* Conclude rendering and swap the buffers, showing the final frame * on-screen. */ wlr_renderer_end(renderer); - wlr_output_swap_buffers(output->wlr_output, NULL, NULL); + wlr_output_commit(output->wlr_output); } static void server_new_output(struct wl_listener *listener, void *data) { diff --git a/types/wlr_output.c b/types/wlr_output.c index a2d7c16e..c9b2689e 100644 --- a/types/wlr_output.c +++ b/types/wlr_output.c @@ -282,6 +282,7 @@ void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend, wl_signal_init(&output->events.transform); wl_signal_init(&output->events.destroy); pixman_region32_init(&output->damage); + pixman_region32_init(&output->pending.damage); const char *no_hardware_cursors = getenv("WLR_NO_HARDWARE_CURSORS"); if (no_hardware_cursors != NULL && strcmp(no_hardware_cursors, "1") == 0) { @@ -317,6 +318,7 @@ void wlr_output_destroy(struct wlr_output *output) { wl_event_source_remove(output->idle_frame); } + pixman_region32_fini(&output->pending.damage); pixman_region32_fini(&output->damage); if (output->impl && output->impl->destroy) { @@ -360,13 +362,18 @@ struct wlr_output_mode *wlr_output_preferred_mode(struct wlr_output *output) { return mode; } -bool wlr_output_make_current(struct wlr_output *output, int *buffer_age) { - return output->impl->make_current(output, buffer_age); +bool wlr_output_attach_render(struct wlr_output *output, int *buffer_age) { + if (!output->impl->make_current(output, buffer_age)) { + return false; + } + + output->pending.committed |= WLR_OUTPUT_STATE_BUFFER; + return true; } bool wlr_output_preferred_read_format(struct wlr_output *output, enum wl_shm_format *fmt) { - if (!wlr_output_make_current(output, NULL)) { + if (!output->impl->make_current(output, NULL)) { return false; } @@ -378,10 +385,21 @@ bool wlr_output_preferred_read_format(struct wlr_output *output, return true; } -bool wlr_output_swap_buffers(struct wlr_output *output, struct timespec *when, +void wlr_output_set_damage(struct wlr_output *output, pixman_region32_t *damage) { + pixman_region32_intersect_rect(&output->pending.damage, damage, + 0, 0, output->width, output->height); + output->pending.committed |= WLR_OUTPUT_STATE_DAMAGE; +} + +static void output_state_clear(struct wlr_output_state *state) { + state->committed = 0; + pixman_region32_clear(&state->damage); +} + +bool wlr_output_commit(struct wlr_output *output) { if (output->frame_pending) { - wlr_log(WLR_ERROR, "Tried to swap buffers when a frame is pending"); + wlr_log(WLR_ERROR, "Tried to commit when a frame is pending"); return false; } if (output->idle_frame != NULL) { @@ -389,46 +407,43 @@ bool wlr_output_swap_buffers(struct wlr_output *output, struct timespec *when, output->idle_frame = NULL; } - struct timespec now; - if (when == NULL) { + if (output->pending.committed & WLR_OUTPUT_STATE_BUFFER) { + struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - when = &now; - } - struct wlr_output_event_swap_buffers event = { - .output = output, - .when = when, - .damage = damage, - }; - wlr_signal_emit_safe(&output->events.swap_buffers, &event); + pixman_region32_t *damage = NULL; + if (output->pending.committed & WLR_OUTPUT_STATE_DAMAGE) { + damage = &output->pending.damage; + } - pixman_region32_t render_damage; - pixman_region32_init(&render_damage); - pixman_region32_union_rect(&render_damage, &render_damage, 0, 0, - output->width, output->height); - if (damage != NULL) { - // Damage tracking supported - pixman_region32_intersect(&render_damage, &render_damage, damage); - } + struct wlr_output_event_swap_buffers event = { + .output = output, + .when = &now, + .damage = damage, + }; + wlr_signal_emit_safe(&output->events.swap_buffers, &event); - if (!output->impl->swap_buffers(output, damage ? &render_damage : NULL)) { - pixman_region32_fini(&render_damage); + if (!output->impl->swap_buffers(output, damage)) { + return false; + } + + struct wlr_output_cursor *cursor; + wl_list_for_each(cursor, &output->cursors, link) { + if (!cursor->enabled || !cursor->visible || cursor->surface == NULL) { + continue; + } + wlr_surface_send_frame_done(cursor->surface, &now); + } + + output->frame_pending = true; + output->needs_swap = false; + output_state_clear(&output->pending); + return true; + } else { + wlr_log(WLR_ERROR, "Tried to commit without attaching a buffer"); return false; } - pixman_region32_fini(&render_damage); - - struct wlr_output_cursor *cursor; - wl_list_for_each(cursor, &output->cursors, link) { - if (!cursor->enabled || !cursor->visible || cursor->surface == NULL) { - continue; - } - wlr_surface_send_frame_done(cursor->surface, when); - } - - output->frame_pending = true; - output->needs_swap = false; - pixman_region32_clear(&output->damage); return true; } diff --git a/types/wlr_output_damage.c b/types/wlr_output_damage.c index bf3a671d..d7949769 100644 --- a/types/wlr_output_damage.c +++ b/types/wlr_output_damage.c @@ -106,7 +106,7 @@ bool wlr_output_damage_make_current(struct wlr_output_damage *output_damage, struct wlr_output *output = output_damage->output; int buffer_age = -1; - if (!wlr_output_make_current(output, &buffer_age)) { + if (!wlr_output_attach_render(output, &buffer_age)) { return false; } @@ -142,7 +142,10 @@ bool wlr_output_damage_make_current(struct wlr_output_damage *output_damage, bool wlr_output_damage_swap_buffers(struct wlr_output_damage *output_damage, struct timespec *when, pixman_region32_t *damage) { - if (!wlr_output_swap_buffers(output_damage->output, when, damage)) { + if (damage != NULL) { + wlr_output_set_damage(output_damage->output, damage); + } + if (!wlr_output_commit(output_damage->output)) { return false; }