libweston/backends: Move damage flush into backends

Currently we flush damage for the "primary plane" every repaint, but this
is folly.

The drm backend may skip rendering entirely if using an all-planes
composition. This could leave the renderer plane in a messy state if a
surface on an overlay plane disappears.

Instead, let the backends flush the primary plane damage when they know
they need to render.

Fixes #864

Signed-off-by: Derek Foreman <derek.foreman@collabora.com>
(cherry picked from commit 2abe4efcf7)
This commit is contained in:
Derek Foreman 2024-01-10 14:13:09 -06:00 committed by Marius Vlad
parent cebcf8ff7c
commit 0126e6f275
12 changed files with 118 additions and 56 deletions

View File

@ -548,7 +548,7 @@ struct weston_output {
struct weston_log_pacer pixman_overdraw_pacer; struct weston_log_pacer pixman_overdraw_pacer;
int (*start_repaint_loop)(struct weston_output *output); int (*start_repaint_loop)(struct weston_output *output);
int (*repaint)(struct weston_output *output, pixman_region32_t *damage); int (*repaint)(struct weston_output *output);
void (*destroy)(struct weston_output *output); void (*destroy)(struct weston_output *output);
void (*assign_planes)(struct weston_output *output); void (*assign_planes)(struct weston_output *output);
int (*switch_mode)(struct weston_output *output, struct weston_mode *mode); int (*switch_mode)(struct weston_output *output, struct weston_mode *mode);

View File

@ -965,7 +965,7 @@ bool
drm_plane_is_available(struct drm_plane *plane, struct drm_output *output); drm_plane_is_available(struct drm_plane *plane, struct drm_output *output);
void void
drm_output_render(struct drm_output_state *state, pixman_region32_t *damage); drm_output_render(struct drm_output_state *state);
int int
parse_gbm_format(const char *s, const struct pixel_format_info *default_format, parse_gbm_format(const char *s, const struct pixel_format_info *default_format,

View File

@ -202,8 +202,7 @@ drm_virtual_output_submit_frame(struct drm_output *output,
} }
static int static int
drm_virtual_output_repaint(struct weston_output *output_base, drm_virtual_output_repaint(struct weston_output *output_base)
pixman_region32_t *damage)
{ {
struct drm_output_state *state = NULL; struct drm_output_state *state = NULL;
struct drm_output *output = to_drm_output(output_base); struct drm_output *output = to_drm_output(output_base);
@ -237,7 +236,7 @@ drm_virtual_output_repaint(struct weston_output *output_base,
pending_state, pending_state,
DRM_OUTPUT_STATE_CLEAR_PLANES); DRM_OUTPUT_STATE_CLEAR_PLANES);
drm_output_render(state, damage); drm_output_render(state);
scanout_state = drm_output_state_get_plane(state, scanout_plane); scanout_state = drm_output_state_get_plane(state, scanout_plane);
if (!scanout_state || !scanout_state->fb) if (!scanout_state || !scanout_state->fb)
goto err; goto err;

View File

@ -355,7 +355,7 @@ drm_output_render_pixman(struct drm_output_state *state,
} }
void void
drm_output_render(struct drm_output_state *state, pixman_region32_t *damage) drm_output_render(struct drm_output_state *state)
{ {
struct drm_output *output = state->output; struct drm_output *output = state->output;
struct drm_device *device = output->device; struct drm_device *device = output->device;
@ -365,7 +365,7 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage)
struct drm_property_info *damage_info = struct drm_property_info *damage_info =
&scanout_plane->props[WDRM_PLANE_FB_DAMAGE_CLIPS]; &scanout_plane->props[WDRM_PLANE_FB_DAMAGE_CLIPS];
struct drm_fb *fb; struct drm_fb *fb;
pixman_region32_t scanout_damage; pixman_region32_t damage, scanout_damage;
pixman_box32_t *rects; pixman_box32_t *rects;
int n_rects; int n_rects;
@ -375,6 +375,10 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage)
if (scanout_state->fb) if (scanout_state->fb)
return; return;
pixman_region32_init(&damage);
weston_output_flush_damage_for_primary_plane(&output->base, &damage);
/* /*
* If we don't have any damage on the primary plane, and we already * If we don't have any damage on the primary plane, and we already
* have a renderer buffer active, we can reuse it; else we pass * have a renderer buffer active, we can reuse it; else we pass
@ -382,7 +386,7 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage)
* area. But, we still have to call the renderer anyway if any screen * area. But, we still have to call the renderer anyway if any screen
* capture is pending, otherwise the capture will not complete. * capture is pending, otherwise the capture will not complete.
*/ */
if (!pixman_region32_not_empty(damage) && if (!pixman_region32_not_empty(&damage) &&
wl_list_empty(&output->base.frame_signal.listener_list) && wl_list_empty(&output->base.frame_signal.listener_list) &&
!weston_output_has_renderer_capture_tasks(&output->base) && !weston_output_has_renderer_capture_tasks(&output->base) &&
scanout_plane->state_cur->fb && scanout_plane->state_cur->fb &&
@ -390,14 +394,14 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage)
scanout_plane->state_cur->fb->type == BUFFER_PIXMAN_DUMB)) { scanout_plane->state_cur->fb->type == BUFFER_PIXMAN_DUMB)) {
fb = drm_fb_ref(scanout_plane->state_cur->fb); fb = drm_fb_ref(scanout_plane->state_cur->fb);
} else if (c->renderer->type == WESTON_RENDERER_PIXMAN) { } else if (c->renderer->type == WESTON_RENDERER_PIXMAN) {
fb = drm_output_render_pixman(state, damage); fb = drm_output_render_pixman(state, &damage);
} else { } else {
fb = drm_output_render_gl(state, damage); fb = drm_output_render_gl(state, &damage);
} }
if (!fb) { if (!fb) {
drm_plane_state_put_back(scanout_state); drm_plane_state_put_back(scanout_state);
return; goto out;
} }
scanout_state->fb = fb; scanout_state->fb = fb;
@ -417,13 +421,13 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage)
/* Don't bother calculating plane damage if the plane doesn't support it */ /* Don't bother calculating plane damage if the plane doesn't support it */
if (damage_info->prop_id == 0) if (damage_info->prop_id == 0)
return; goto out;
pixman_region32_init(&scanout_damage); pixman_region32_init(&scanout_damage);
weston_region_global_to_output(&scanout_damage, weston_region_global_to_output(&scanout_damage,
&output->base, &output->base,
damage); &damage);
assert(scanout_state->damage_blob_id == 0); assert(scanout_state->damage_blob_id == 0);
@ -440,6 +444,8 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage)
&scanout_state->damage_blob_id); &scanout_state->damage_blob_id);
pixman_region32_fini(&scanout_damage); pixman_region32_fini(&scanout_damage);
out:
pixman_region32_fini(&damage);
} }
static uint32_t static uint32_t
@ -651,7 +657,7 @@ cursor_bo_update(struct drm_output *output, struct weston_view *ev)
#endif #endif
static int static int
drm_output_repaint(struct weston_output *output_base, pixman_region32_t *damage) drm_output_repaint(struct weston_output *output_base)
{ {
struct drm_output *output = to_drm_output(output_base); struct drm_output *output = to_drm_output(output_base);
struct drm_output_state *state = NULL; struct drm_output_state *state = NULL;
@ -718,7 +724,7 @@ drm_output_repaint(struct weston_output *output_base, pixman_region32_t *damage)
if (device->atomic_modeset) if (device->atomic_modeset)
drm_output_pick_writeback_capture_task(output); drm_output_pick_writeback_capture_task(output);
drm_output_render(state, damage); drm_output_render(state);
scanout_state = drm_output_state_get_plane(state, scanout_state = drm_output_state_get_plane(state,
output->scanout_plane); output->scanout_plane);
if (!scanout_state || !scanout_state->fb) if (!scanout_state || !scanout_state->fb)

View File

@ -152,11 +152,11 @@ headless_output_update_gl_border(struct headless_output *output)
} }
static int static int
headless_output_repaint(struct weston_output *output_base, headless_output_repaint(struct weston_output *output_base)
pixman_region32_t *damage)
{ {
struct headless_output *output = to_headless_output(output_base); struct headless_output *output = to_headless_output(output_base);
struct weston_compositor *ec; struct weston_compositor *ec;
pixman_region32_t damage;
assert(output); assert(output);
@ -164,9 +164,15 @@ headless_output_repaint(struct weston_output *output_base,
headless_output_update_gl_border(output); headless_output_update_gl_border(output);
ec->renderer->repaint_output(&output->base, damage, pixman_region32_init(&damage);
weston_output_flush_damage_for_primary_plane(output_base, &damage);
ec->renderer->repaint_output(&output->base, &damage,
output->renderbuffer); output->renderbuffer);
pixman_region32_fini(&damage);
wl_event_source_timer_update(output->finish_frame_timer, 16); wl_event_source_timer_update(output->finish_frame_timer, 16);
return 0; return 0;

View File

@ -725,19 +725,24 @@ pipewire_submit_buffer(struct pipewire_output *output,
} }
static int static int
pipewire_output_repaint(struct weston_output *base, pixman_region32_t *damage) pipewire_output_repaint(struct weston_output *base)
{ {
struct pipewire_output *output = to_pipewire_output(base); struct pipewire_output *output = to_pipewire_output(base);
struct weston_compositor *ec = output->base.compositor; struct weston_compositor *ec = output->base.compositor;
struct pw_buffer *buffer; struct pw_buffer *buffer;
struct pipewire_frame_data *frame_data; struct pipewire_frame_data *frame_data;
pixman_region32_t damage;
assert(output); assert(output);
if (pw_stream_get_state(output->stream, NULL) != PW_STREAM_STATE_STREAMING) if (pw_stream_get_state(output->stream, NULL) != PW_STREAM_STATE_STREAMING)
goto out; goto out;
if (!pixman_region32_not_empty(damage)) pixman_region32_init(&damage);
weston_output_flush_damage_for_primary_plane(base, &damage);
if (!pixman_region32_not_empty(&damage))
goto out; goto out;
buffer = pw_stream_dequeue_buffer(output->stream); buffer = pw_stream_dequeue_buffer(output->stream);
@ -748,12 +753,14 @@ pipewire_output_repaint(struct weston_output *base, pixman_region32_t *damage)
pipewire_output_debug(output, "dequeued buffer: %p", buffer); pipewire_output_debug(output, "dequeued buffer: %p", buffer);
frame_data = buffer->user_data; frame_data = buffer->user_data;
ec->renderer->repaint_output(&output->base, damage, frame_data->renderbuffer); ec->renderer->repaint_output(&output->base, &damage, frame_data->renderbuffer);
pipewire_submit_buffer(output, buffer); pipewire_submit_buffer(output, buffer);
out: out:
pixman_region32_fini(&damage);
weston_output_arm_frame_timer(base, output->finish_frame_timer); weston_output_arm_frame_timer(base, output->finish_frame_timer);
return 0; return 0;

View File

@ -272,24 +272,29 @@ rdp_output_start_repaint_loop(struct weston_output *output)
} }
static int static int
rdp_output_repaint(struct weston_output *output_base, pixman_region32_t *damage) rdp_output_repaint(struct weston_output *output_base)
{ {
struct rdp_output *output = container_of(output_base, struct rdp_output, base); struct rdp_output *output = container_of(output_base, struct rdp_output, base);
struct weston_compositor *ec = output->base.compositor; struct weston_compositor *ec = output->base.compositor;
struct rdp_backend *b = output->backend; struct rdp_backend *b = output->backend;
struct rdp_peers_item *peer; struct rdp_peers_item *peer;
pixman_region32_t damage;
assert(output); assert(output);
ec->renderer->repaint_output(&output->base, damage, pixman_region32_init(&damage);
weston_output_flush_damage_for_primary_plane(output_base, &damage);
ec->renderer->repaint_output(&output->base, &damage,
output->renderbuffer); output->renderbuffer);
if (pixman_region32_not_empty(damage)) { if (pixman_region32_not_empty(&damage)) {
pixman_region32_t transformed_damage; pixman_region32_t transformed_damage;
pixman_region32_init(&transformed_damage); pixman_region32_init(&transformed_damage);
weston_region_global_to_output(&transformed_damage, weston_region_global_to_output(&transformed_damage,
output_base, output_base,
damage); &damage);
wl_list_for_each(peer, &b->peers, link) { wl_list_for_each(peer, &b->peers, link) {
if ((peer->flags & RDP_PEER_ACTIVATED) && if ((peer->flags & RDP_PEER_ACTIVATED) &&
(peer->flags & RDP_PEER_OUTPUT_ENABLED)) { (peer->flags & RDP_PEER_OUTPUT_ENABLED)) {
@ -299,6 +304,8 @@ rdp_output_repaint(struct weston_output *output_base, pixman_region32_t *damage)
pixman_region32_fini(&transformed_damage); pixman_region32_fini(&transformed_damage);
} }
pixman_region32_fini(&damage);
weston_output_arm_frame_timer(output_base, output->finish_frame_timer); weston_output_arm_frame_timer(output_base, output->finish_frame_timer);
return 0; return 0;

View File

@ -1004,10 +1004,11 @@ vnc_output_start_repaint_loop(struct weston_output *output)
} }
static int static int
vnc_output_repaint(struct weston_output *base, pixman_region32_t *damage) vnc_output_repaint(struct weston_output *base)
{ {
struct vnc_output *output = to_vnc_output(base); struct vnc_output *output = to_vnc_output(base);
struct vnc_backend *backend = output->backend; struct vnc_backend *backend = output->backend;
pixman_region32_t damage;
assert(output); assert(output);
@ -1016,10 +1017,16 @@ vnc_output_repaint(struct weston_output *base, pixman_region32_t *damage)
vnc_output_update_cursor(output); vnc_output_update_cursor(output);
if (pixman_region32_not_empty(damage)) { pixman_region32_init(&damage);
vnc_update_buffer(output->display, damage);
weston_output_flush_damage_for_primary_plane(base, &damage);
if (pixman_region32_not_empty(&damage)) {
vnc_update_buffer(output->display, &damage);
} }
pixman_region32_fini(&damage);
/* /*
* Make sure damage of this (or previous) damage is handled * Make sure damage of this (or previous) damage is handled
* *

View File

@ -498,22 +498,28 @@ wayland_output_start_repaint_loop(struct weston_output *output_base)
#ifdef ENABLE_EGL #ifdef ENABLE_EGL
static int static int
wayland_output_repaint_gl(struct weston_output *output_base, wayland_output_repaint_gl(struct weston_output *output_base)
pixman_region32_t *damage)
{ {
struct wayland_output *output = to_wayland_output(output_base); struct wayland_output *output = to_wayland_output(output_base);
struct weston_compositor *ec; struct weston_compositor *ec;
pixman_region32_t damage;
assert(output); assert(output);
ec = output->base.compositor; ec = output->base.compositor;
pixman_region32_init(&damage);
weston_output_flush_damage_for_primary_plane(output_base, &damage);
output->frame_cb = wl_surface_frame(output->parent.surface); output->frame_cb = wl_surface_frame(output->parent.surface);
wl_callback_add_listener(output->frame_cb, &frame_listener, output); wl_callback_add_listener(output->frame_cb, &frame_listener, output);
wayland_output_update_gl_border(output); wayland_output_update_gl_border(output);
ec->renderer->repaint_output(&output->base, damage, NULL); ec->renderer->repaint_output(&output->base, &damage, NULL);
pixman_region32_fini(&damage);
return 0; return 0;
} }
@ -604,17 +610,21 @@ wayland_shm_buffer_attach(struct wayland_shm_buffer *sb,
} }
static int static int
wayland_output_repaint_pixman(struct weston_output *output_base, wayland_output_repaint_pixman(struct weston_output *output_base)
pixman_region32_t *damage)
{ {
struct wayland_output *output = to_wayland_output(output_base); struct wayland_output *output = to_wayland_output(output_base);
struct wayland_backend *b; struct wayland_backend *b;
struct wayland_shm_buffer *sb; struct wayland_shm_buffer *sb;
pixman_region32_t damage;
assert(output); assert(output);
b = output->backend; b = output->backend;
pixman_region32_init(&damage);
weston_output_flush_damage_for_primary_plane(output_base, &damage);
if (output->frame) { if (output->frame) {
if (frame_status(output->frame) & FRAME_STATUS_REPAINT) if (frame_status(output->frame) & FRAME_STATUS_REPAINT)
wl_list_for_each(sb, &output->shm.buffers, link) wl_list_for_each(sb, &output->shm.buffers, link)
@ -624,10 +634,12 @@ wayland_output_repaint_pixman(struct weston_output *output_base,
sb = wayland_output_get_shm_buffer(output); sb = wayland_output_get_shm_buffer(output);
wayland_output_update_shm_border(sb); wayland_output_update_shm_border(sb);
b->compositor->renderer->repaint_output(output_base, damage, b->compositor->renderer->repaint_output(output_base, &damage,
sb->renderbuffer); sb->renderbuffer);
wayland_shm_buffer_attach(sb, damage); wayland_shm_buffer_attach(sb, &damage);
pixman_region32_fini(&damage);
output->frame_cb = wl_surface_frame(output->parent.surface); output->frame_cb = wl_surface_frame(output->parent.surface);
wl_callback_add_listener(output->frame_cb, &frame_listener, output); wl_callback_add_listener(output->frame_cb, &frame_listener, output);

View File

@ -429,17 +429,23 @@ x11_output_start_repaint_loop(struct weston_output *output)
} }
static int static int
x11_output_repaint_gl(struct weston_output *output_base, x11_output_repaint_gl(struct weston_output *output_base)
pixman_region32_t *damage)
{ {
struct x11_output *output = to_x11_output(output_base); struct x11_output *output = to_x11_output(output_base);
struct weston_compositor *ec; struct weston_compositor *ec;
pixman_region32_t damage;
assert(output); assert(output);
ec = output->base.compositor; ec = output->base.compositor;
ec->renderer->repaint_output(output_base, damage, NULL); pixman_region32_init(&damage);
weston_output_flush_damage_for_primary_plane(output_base, &damage);
ec->renderer->repaint_output(output_base, &damage, NULL);
pixman_region32_fini(&damage);
weston_output_arm_frame_timer(output_base, output->finish_frame_timer); weston_output_arm_frame_timer(output_base, output->finish_frame_timer);
return 0; return 0;
@ -498,8 +504,7 @@ set_clip_for_output(struct weston_output *output_base, pixman_region32_t *region
static int static int
x11_output_repaint_shm(struct weston_output *output_base, x11_output_repaint_shm(struct weston_output *output_base)
pixman_region32_t *damage)
{ {
struct x11_output *output = to_x11_output(output_base); struct x11_output *output = to_x11_output(output_base);
const struct weston_renderer *renderer; const struct weston_renderer *renderer;
@ -508,6 +513,7 @@ x11_output_repaint_shm(struct weston_output *output_base,
struct x11_backend *b; struct x11_backend *b;
xcb_void_cookie_t cookie; xcb_void_cookie_t cookie;
xcb_generic_error_t *err; xcb_generic_error_t *err;
pixman_region32_t damage;
assert(output); assert(output);
@ -517,9 +523,16 @@ x11_output_repaint_shm(struct weston_output *output_base,
image = renderer->pixman->renderbuffer_get_image(output->renderbuffer); image = renderer->pixman->renderbuffer_get_image(output->renderbuffer);
ec->renderer->repaint_output(output_base, damage, output->renderbuffer); pixman_region32_init(&damage);
weston_output_flush_damage_for_primary_plane(output_base, &damage);
ec->renderer->repaint_output(output_base, &damage, output->renderbuffer);
set_clip_for_output(output_base, &damage);
pixman_region32_fini(&damage);
set_clip_for_output(output_base, damage);
cookie = xcb_shm_put_image_checked(b->conn, output->window, output->gc, cookie = xcb_shm_put_image_checked(b->conn, output->window, output->gc,
pixman_image_get_width(image), pixman_image_get_width(image),
pixman_image_get_height(image), pixman_image_get_height(image),

View File

@ -327,4 +327,8 @@ weston_output_flush_damage_for_plane(struct weston_output *output,
struct weston_plane *plane, struct weston_plane *plane,
pixman_region32_t *damage); pixman_region32_t *damage);
void
weston_output_flush_damage_for_primary_plane(struct weston_output *output,
pixman_region32_t *damage);
#endif #endif

View File

@ -3391,6 +3391,20 @@ weston_output_flush_damage_for_plane(struct weston_output *output,
return changed; return changed;
} }
WL_EXPORT void
weston_output_flush_damage_for_primary_plane(struct weston_output *output,
pixman_region32_t *damage)
{
weston_output_flush_damage_for_plane(output,
&output->primary_plane,
damage);
if (output->full_repaint_needed) {
pixman_region32_copy(damage, &output->region);
output->full_repaint_needed = false;
}
}
static int static int
weston_output_repaint(struct weston_output *output) weston_output_repaint(struct weston_output *output)
{ {
@ -3399,7 +3413,6 @@ weston_output_repaint(struct weston_output *output)
struct weston_animation *animation, *next; struct weston_animation *animation, *next;
struct wl_resource *cb, *cnext; struct wl_resource *cb, *cnext;
struct wl_list frame_callback_list; struct wl_list frame_callback_list;
pixman_region32_t output_damage;
int r; int r;
uint32_t frame_time_msec; uint32_t frame_time_msec;
enum weston_hdcp_protection highest_requested = WESTON_HDCP_DISABLE; enum weston_hdcp_protection highest_requested = WESTON_HDCP_DISABLE;
@ -3471,19 +3484,7 @@ weston_output_repaint(struct weston_output *output)
output_accumulate_damage(output); output_accumulate_damage(output);
pixman_region32_init(&output_damage); r = output->repaint(output);
weston_output_flush_damage_for_plane(output, &output->primary_plane,
&output_damage);
if (output->full_repaint_needed) {
pixman_region32_copy(&output_damage, &output->region);
output->full_repaint_needed = false;
}
r = output->repaint(output, &output_damage);
pixman_region32_fini(&output_damage);
output->repaint_needed = false; output->repaint_needed = false;
if (r == 0) if (r == 0)