From 78657c5ff35efcd9b1c8f2c37e8708838a3cad77 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Mon, 6 Nov 2023 12:02:13 -0600 Subject: [PATCH] drm-backend: Fix cursor updates with overlapping heads We can't use the surface damage to determine when to upload new cursor images because when heads overlap the first repainted head will accumulate that damage as plane damage. We can't easily use plane damage either because the plane isn't really assigned until after an atomic test, which requires the cursor fb to be current. Untangle this mess a little by always testing with the first cursor fb, which is identical to the second in all ways, then replace with the correct fb in repaint. Signed-off-by: Derek Foreman --- libweston/backend-drm/drm.c | 76 +++++++++++++++++++++++++++ libweston/backend-drm/state-propose.c | 74 +++----------------------- 2 files changed, 84 insertions(+), 66 deletions(-) diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index b61ff1f5..80927897 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -601,12 +601,62 @@ err: weston_capture_task_retire_failed(ct, msg); } +#ifdef BUILD_DRM_GBM +/** + * Update the image for the current cursor surface + * + * @param plane_state DRM cursor plane state + * @param ev Source view for cursor + */ +static void +cursor_bo_update(struct drm_output *output, struct weston_view *ev) +{ + struct drm_device *device = output->device; + struct gbm_bo *bo = output->gbm_cursor_fb[output->current_cursor]->bo; + struct weston_buffer *buffer = ev->surface->buffer_ref.buffer; + uint32_t buf[device->cursor_width * device->cursor_height]; + int32_t stride; + uint8_t *s; + int i; + + assert(buffer && buffer->shm_buffer); + assert(buffer->width <= device->cursor_width); + assert(buffer->height <= device->cursor_height); + + memset(buf, 0, sizeof buf); + + stride = wl_shm_buffer_get_stride(buffer->shm_buffer); + s = wl_shm_buffer_get_data(buffer->shm_buffer); + + wl_shm_buffer_begin_access(buffer->shm_buffer); + for (i = 0; i < buffer->height; i++) + memcpy(buf + i * device->cursor_width, + s + i * stride, + buffer->width * 4); + wl_shm_buffer_end_access(buffer->shm_buffer); + + if (bo) { + if (gbm_bo_write(bo, buf, sizeof buf) < 0) + weston_log("failed update cursor: %s\n", strerror(errno)); + } else { + memcpy(output->gbm_cursor_fb[output->current_cursor]->map, + buf, sizeof buf); + } +} +#else +static void +cursor_bo_update(struct drm_output *output, struct weston_view *ev) +{ +} +#endif + static int drm_output_repaint(struct weston_output *output_base, pixman_region32_t *damage) { struct drm_output *output = to_drm_output(output_base); struct drm_output_state *state = NULL; struct drm_plane_state *scanout_state; + struct drm_plane_state *cursor_state; struct drm_pending_state *pending_state; struct drm_device *device; @@ -631,6 +681,32 @@ drm_output_repaint(struct weston_output *output_base, pixman_region32_t *damage) DRM_OUTPUT_STATE_CLEAR_PLANES); state->dpms = WESTON_DPMS_ON; + cursor_state = drm_output_state_get_existing_plane(state, + output->cursor_plane); + if (cursor_state && cursor_state->fb) { + pixman_region32_t damage; + + assert(cursor_state->plane == output->cursor_plane); + assert(cursor_state->fb == output->gbm_cursor_fb[0]); + + pixman_region32_init(&damage); + weston_output_flush_damage_for_plane(&output->base, + &output->cursor_plane->base, + &damage); + if (pixman_region32_not_empty(&damage)) { + output->current_cursor++; + output->current_cursor = + output->current_cursor % + ARRAY_LENGTH(output->gbm_cursor_fb); + cursor_bo_update(output, output->cursor_view); + } + pixman_region32_fini(&damage); + + cursor_state->fb = drm_fb_ref(output->gbm_cursor_fb[output->current_cursor]); + drm_fb_unref(output->gbm_cursor_fb[0]); + } + + if (output_base->allow_protection) state->protection = output_base->desired_protection; else diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index b4a19b0f..18a6d628 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -155,48 +155,6 @@ out: } #ifdef BUILD_DRM_GBM -/** - * Update the image for the current cursor surface - * - * @param plane_state DRM cursor plane state - * @param ev Source view for cursor - */ -static void -cursor_bo_update(struct drm_plane_state *plane_state, struct weston_view *ev) -{ - struct drm_output *output = plane_state->output; - struct drm_device *device = output->device; - struct gbm_bo *bo = plane_state->fb->bo; - struct weston_buffer *buffer = ev->surface->buffer_ref.buffer; - uint32_t buf[device->cursor_width * device->cursor_height]; - int32_t stride; - uint8_t *s; - int i; - - assert(buffer && buffer->shm_buffer); - assert(buffer->width <= device->cursor_width); - assert(buffer->height <= device->cursor_height); - - memset(buf, 0, sizeof buf); - stride = wl_shm_buffer_get_stride(buffer->shm_buffer); - s = wl_shm_buffer_get_data(buffer->shm_buffer); - - wl_shm_buffer_begin_access(buffer->shm_buffer); - for (i = 0; i < buffer->height; i++) - memcpy(buf + i * device->cursor_width, - s + i * stride, - buffer->width * 4); - wl_shm_buffer_end_access(buffer->shm_buffer); - - if (bo) { - if (gbm_bo_write(bo, buf, sizeof buf) < 0) - weston_log("failed update cursor: %s\n", strerror(errno)); - } else { - memcpy(output->gbm_cursor_fb[output->current_cursor]->map, - buf, sizeof buf); - } -} - static struct drm_plane_state * drm_output_prepare_cursor_paint_node(struct drm_output_state *output_state, struct weston_paint_node *node, @@ -208,7 +166,6 @@ drm_output_prepare_cursor_paint_node(struct drm_output_state *output_state, struct drm_plane *plane = output->cursor_plane; struct weston_view *ev = node->view; struct drm_plane_state *plane_state; - bool needs_update = false; const char *p_name = drm_output_get_plane_type_name(plane); assert(!device->cursors_are_broken); @@ -242,31 +199,16 @@ drm_output_prepare_cursor_paint_node(struct drm_output_state *output_state, goto err; } - /* Since we're setting plane state up front, we need to work out - * whether or not we need to upload a new cursor. We can't use the - * plane damage, since the planes haven't actually been calculated - * yet: instead try to figure it out directly. KMS cursor planes are - * pretty unique here, in that they lie partway between a Weston plane - * (direct scanout) and a renderer. */ - if (ev != output->cursor_view || - pixman_region32_not_empty(&ev->surface->damage)) { - output->current_cursor++; - output->current_cursor = - output->current_cursor % - ARRAY_LENGTH(output->gbm_cursor_fb); - needs_update = true; - } - drm_output_set_cursor_view(output, ev); plane_state->ev = ev; - - plane_state->fb = - drm_fb_ref(output->gbm_cursor_fb[output->current_cursor]); - - if (needs_update) { - drm_debug(b, "\t\t\t\t[%s] copying new content to cursor BO\n", p_name); - cursor_bo_update(plane_state, ev); - } + /* We always test with cursor fb 0. There are two potential fbs, and + * they are identically allocated for cursor use specifically, so if + * one works the other almost certainly should as well. + * + * Later when we determine if the cursor needs an update, we'll + * select the correct fb to use. + */ + plane_state->fb = drm_fb_ref(output->gbm_cursor_fb[0]); /* The cursor API is somewhat special: in cursor_bo_update(), we upload * a buffer which is always cursor_width x cursor_height, even if the