backend-drm: allow to create multiple drm_fb for a weston_view

Weston uses a cached drm_fb when a view is shown multiple times. If the view is
shown on multiple outputs backed by different DRM devices, Weston returns the
cached drm_fb for the first device that was used for the import. This causes a
failure when adding the fb to the other device.

Use a list of all drm_fbs to cache the buf_fb per device, and check for the
device before reusing a drm_fb.

Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
This commit is contained in:
Michael Tretter 2022-05-05 12:24:41 +02:00 committed by Daniel Stone
parent a8fb329335
commit f05029127c
2 changed files with 38 additions and 15 deletions

View File

@ -403,6 +403,12 @@ struct drm_fb {
struct drm_buffer_fb {
struct drm_fb *fb;
enum try_view_on_plane_failure_reasons failure_reasons;
struct drm_device *device;
struct wl_list link;
};
struct drm_fb_private {
struct wl_list buffer_fb_list;
struct wl_listener buffer_destroy_listener;
};

View File

@ -512,18 +512,24 @@ drm_fb_compatible_with_plane(struct drm_fb *fb, struct drm_plane *plane)
static void
drm_fb_handle_buffer_destroy(struct wl_listener *listener, void *data)
{
struct drm_buffer_fb *buf_fb =
container_of(listener, struct drm_buffer_fb, buffer_destroy_listener);
struct drm_fb_private *private =
container_of(listener, struct drm_fb_private, buffer_destroy_listener);
struct drm_buffer_fb *buf_fb;
struct drm_buffer_fb *tmp;
wl_list_remove(&buf_fb->buffer_destroy_listener.link);
wl_list_remove(&private->buffer_destroy_listener.link);
if (buf_fb->fb) {
assert(buf_fb->fb->type == BUFFER_CLIENT ||
buf_fb->fb->type == BUFFER_DMABUF);
drm_fb_unref(buf_fb->fb);
wl_list_for_each_safe(buf_fb, tmp, &private->buffer_fb_list, link) {
if (buf_fb->fb) {
assert(buf_fb->fb->type == BUFFER_CLIENT ||
buf_fb->fb->type == BUFFER_DMABUF);
drm_fb_unref(buf_fb->fb);
}
wl_list_remove(&buf_fb->link);
free(buf_fb);
}
free(buf_fb);
free(private);
}
struct drm_fb *
@ -535,6 +541,7 @@ drm_fb_get_from_paint_node(struct drm_output_state *state,
struct drm_device *device = output->device;
struct weston_view *ev = pnode->view;
struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
struct drm_fb_private *private;
struct drm_buffer_fb *buf_fb;
bool is_opaque = weston_view_is_opaque(ev, &ev->transform.boundingbox);
struct drm_fb *fb;
@ -558,16 +565,26 @@ drm_fb_get_from_paint_node(struct drm_output_state *state,
return NULL;
}
if (buffer->backend_private) {
buf_fb = buffer->backend_private;
pnode->try_view_on_plane_failure_reasons |= buf_fb->failure_reasons;
return buf_fb->fb ? drm_fb_ref(buf_fb->fb) : NULL;
if (!buffer->backend_private) {
private = zalloc(sizeof(*private));
buffer->backend_private = private;
wl_list_init(&private->buffer_fb_list);
private->buffer_destroy_listener.notify = drm_fb_handle_buffer_destroy;
wl_signal_add(&buffer->destroy_signal, &private->buffer_destroy_listener);
} else {
private = buffer->backend_private;
}
wl_list_for_each(buf_fb, &private->buffer_fb_list, link) {
if (buf_fb->device == device) {
pnode->try_view_on_plane_failure_reasons |= buf_fb->failure_reasons;
return buf_fb->fb ? drm_fb_ref(buf_fb->fb) : NULL;
}
}
buf_fb = zalloc(sizeof(*buf_fb));
buffer->backend_private = buf_fb;
buf_fb->buffer_destroy_listener.notify = drm_fb_handle_buffer_destroy;
wl_signal_add(&buffer->destroy_signal, &buf_fb->buffer_destroy_listener);
buf_fb->device = device;
wl_list_insert(&private->buffer_fb_list, &buf_fb->link);
/* GBM is used for dmabuf import as well as from client wl_buffer. */
if (!b->gbm) {