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 <derek.foreman@collabora.com>
This commit is contained in:
Derek Foreman 2023-11-06 12:02:13 -06:00
parent bbaba601c8
commit 78657c5ff3
2 changed files with 84 additions and 66 deletions

View file

@ -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

View file

@ -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