diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index abc2e736..303b78ac 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -1198,6 +1198,7 @@ struct weston_buffer { int32_t width, height; uint32_t busy_count; int y_inverted; + void *backend_private; }; struct weston_buffer_reference { diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index 9cd80d66..73859f24 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -349,6 +349,12 @@ struct drm_fb { void *map; }; +struct drm_buffer_fb { + struct drm_fb *fb; + enum try_view_on_plane_failure_reasons failure_reasons; + struct wl_listener buffer_destroy_listener; +}; + struct drm_edid { char eisa_id[13]; char monitor_name[13]; diff --git a/libweston/backend-drm/fb.c b/libweston/backend-drm/fb.c index dd1cfb06..cc994929 100644 --- a/libweston/backend-drm/fb.c +++ b/libweston/backend-drm/fb.c @@ -461,6 +461,21 @@ drm_can_scanout_dmabuf(struct weston_compositor *ec, return ret; } +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); + + if (buf_fb->fb) { + assert(buf_fb->fb->type == BUFFER_CLIENT || + buf_fb->fb->type == BUFFER_DMABUF); + drm_fb_unref(buf_fb->fb); + } + + free(buf_fb); +} + struct drm_fb * drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev, uint32_t *try_view_on_plane_failure_reasons) @@ -468,6 +483,7 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev, struct drm_output *output = state->output; struct drm_backend *b = to_drm_backend(output->base.compositor); struct weston_buffer *buffer = ev->surface->buffer_ref.buffer; + struct drm_buffer_fb *buf_fb; bool is_opaque = weston_view_is_opaque(ev, &ev->transform.boundingbox); struct linux_dmabuf_buffer *dmabuf; struct drm_fb *fb; @@ -485,36 +501,55 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev, if (!buffer) return NULL; + if (buffer->backend_private) { + buf_fb = buffer->backend_private; + *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); + if (wl_shm_buffer_get(buffer->resource)) - return NULL; + goto unsuitable; /* GBM is used for dmabuf import as well as from client wl_buffer. */ if (!b->gbm) - return NULL; + goto unsuitable; dmabuf = linux_dmabuf_buffer_get(buffer->resource); if (dmabuf) { fb = drm_fb_get_from_dmabuf(dmabuf, b, is_opaque, - try_view_on_plane_failure_reasons); + &buf_fb->failure_reasons); if (!fb) - return NULL; + goto unsuitable; } else { struct gbm_bo *bo; bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER, buffer->resource, GBM_BO_USE_SCANOUT); if (!bo) - return NULL; + goto unsuitable; fb = drm_fb_get_from_bo(bo, b, is_opaque, BUFFER_CLIENT); if (!fb) { gbm_bo_destroy(bo); - return NULL; + goto unsuitable; } } + /* The caller holds its own ref to the drm_fb, so when creating a new + * drm_fb we take an additional ref for the weston_buffer's cache. */ + buf_fb->fb = drm_fb_ref(fb); + drm_debug(b, "\t\t\t[view] view %p format: %s\n", ev, fb->format->drm_format_name); return fb; + +unsuitable: + *try_view_on_plane_failure_reasons |= buf_fb->failure_reasons; + return NULL; } #endif