From b0e8e6eae796bb08fa1fffe84d26d525c8d54268 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Fri, 2 Feb 2024 20:38:56 -0500 Subject: [PATCH] linux_dmabuf_v1: Introduce main device Introduce properties for the main renderer and allocator the compositor wants wlroots to use when it needs to blit to a staging buffer during multigpu. We need to perform an additional blit if the modifier is not compatible with the target GPU. --- include/wlr/types/wlr_linux_dmabuf_v1.h | 30 +++++++++++++ types/wlr_linux_dmabuf_v1.c | 56 +++++++++++++++++++++++-- types/wlr_raster.c | 26 +++++++++++- 3 files changed, 107 insertions(+), 5 deletions(-) diff --git a/include/wlr/types/wlr_linux_dmabuf_v1.h b/include/wlr/types/wlr_linux_dmabuf_v1.h index cf967f952..ebf13364d 100644 --- a/include/wlr/types/wlr_linux_dmabuf_v1.h +++ b/include/wlr/types/wlr_linux_dmabuf_v1.h @@ -17,6 +17,8 @@ #include struct wlr_surface; +struct wlr_renderer; +struct wlr_allocator; struct wlr_dmabuf_v1_buffer { struct wlr_buffer base; @@ -27,6 +29,8 @@ struct wlr_dmabuf_v1_buffer { // private state struct wl_listener release; + + struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1; }; /** @@ -63,7 +67,13 @@ struct wlr_linux_dmabuf_v1 { int main_device_fd; // to sanity check FDs sent by clients, -1 if unavailable + // used for multigpu + struct wlr_renderer *main_renderer; + struct wlr_allocator *main_allocator; + struct wl_listener display_destroy; + struct wl_listener main_renderer_destroy; + struct wl_listener main_allocator_destroy; bool (*check_dmabuf_callback)(struct wlr_dmabuf_attributes *attribs, void *data); void *check_dmabuf_callback_data; @@ -78,6 +88,23 @@ struct wlr_linux_dmabuf_v1 { struct wlr_linux_dmabuf_v1 *wlr_linux_dmabuf_v1_create(struct wl_display *display, uint32_t version, const struct wlr_linux_dmabuf_feedback_v1 *default_feedback); +/** + * Returns the associated dmabuf object from a generic buffer. Returnns + * NULL if the generic buffer is not a dmabuf. + */ +struct wlr_dmabuf_v1_buffer *wlr_dmabuf_v1_buffer_try_from_buffer( + struct wlr_buffer *buffer); + +/** + * Sets the main blit device used for multigpu. With multigpu, dmabufs with + * implicit modifiers or just modifiers that aren't supported by other GPUs + * might need to be blitted into a staging buffer with correct modifiers. This + * will be done with this allocator (to allocate the staging buffer) and renderer + * to render into the staging buffer. + */ +void wlr_linux_dmabuf_v1_set_main_blit_device(struct wlr_linux_dmabuf_v1 *linux_dmabuf, + struct wlr_renderer *renderer, struct wlr_allocator *allocator); + /** * Create the linux-dmabuf-v1 global. * @@ -120,6 +147,9 @@ void wlr_linux_dmabuf_feedback_v1_finish(struct wlr_linux_dmabuf_feedback_v1 *fe struct wlr_linux_dmabuf_feedback_v1_init_options { // Main renderer used by the compositor struct wlr_renderer *main_renderer; + // Optional allocator created for the primary GPU used by the default feedback. + // This is used for multi gpu for allocating staging buffers. + struct wlr_allocator *main_allocator; // Output on which direct scan-out is possible on the primary plane, or NULL struct wlr_output *scanout_primary_output; // Output layer feedback event, or NULL diff --git a/types/wlr_linux_dmabuf_v1.c b/types/wlr_linux_dmabuf_v1.c index 4a2c88bad..cd22fd02d 100644 --- a/types/wlr_linux_dmabuf_v1.c +++ b/types/wlr_linux_dmabuf_v1.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -92,16 +93,19 @@ struct wlr_dmabuf_v1_buffer *wlr_dmabuf_v1_buffer_try_from_buffer_resource( static const struct wlr_buffer_impl buffer_impl; -static struct wlr_dmabuf_v1_buffer *dmabuf_v1_buffer_from_buffer( +struct wlr_dmabuf_v1_buffer *wlr_dmabuf_v1_buffer_try_from_buffer( struct wlr_buffer *wlr_buffer) { - assert(wlr_buffer->impl == &buffer_impl); + if (wlr_buffer->impl != &buffer_impl) { + return NULL; + } + struct wlr_dmabuf_v1_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); return buffer; } static void buffer_destroy(struct wlr_buffer *wlr_buffer) { struct wlr_dmabuf_v1_buffer *buffer = - dmabuf_v1_buffer_from_buffer(wlr_buffer); + wlr_dmabuf_v1_buffer_try_from_buffer(wlr_buffer); if (buffer->resource != NULL) { wl_resource_set_user_data(buffer->resource, NULL); } @@ -113,7 +117,7 @@ static void buffer_destroy(struct wlr_buffer *wlr_buffer) { static bool buffer_get_dmabuf(struct wlr_buffer *wlr_buffer, struct wlr_dmabuf_attributes *attribs) { struct wlr_dmabuf_v1_buffer *buffer = - dmabuf_v1_buffer_from_buffer(wlr_buffer); + wlr_dmabuf_v1_buffer_try_from_buffer(wlr_buffer); *attribs = buffer->attributes; return true; } @@ -368,6 +372,7 @@ static void params_create_common(struct wl_resource *params_resource, &wl_buffer_impl, buffer, buffer_handle_resource_destroy); buffer->attributes = attribs; + buffer->linux_dmabuf_v1 = linux_dmabuf; buffer->release.notify = buffer_handle_release; wl_signal_add(&buffer->base.events.release, &buffer->release); @@ -872,6 +877,8 @@ static void linux_dmabuf_v1_destroy(struct wlr_linux_dmabuf_v1 *linux_dmabuf) { } wl_list_remove(&linux_dmabuf->display_destroy.link); + wl_list_remove(&linux_dmabuf->main_renderer_destroy.link); + wl_list_remove(&linux_dmabuf->main_allocator_destroy.link); wl_global_destroy(linux_dmabuf->global); free(linux_dmabuf); @@ -959,6 +966,8 @@ struct wlr_linux_dmabuf_v1 *wlr_linux_dmabuf_v1_create(struct wl_display *displa linux_dmabuf->main_device_fd = -1; wl_list_init(&linux_dmabuf->surfaces); + wl_list_init(&linux_dmabuf->main_renderer_destroy.link); + wl_list_init(&linux_dmabuf->main_allocator_destroy.link); wl_signal_init(&linux_dmabuf->events.destroy); linux_dmabuf->global = wl_global_create(display, &zwp_linux_dmabuf_v1_interface, @@ -1161,3 +1170,42 @@ error: wlr_linux_dmabuf_feedback_v1_finish(feedback); return false; } + +static void linux_dmabuf_unregister_main_blit_device(struct wlr_linux_dmabuf_v1 *linux_dmabuf) { + wl_list_remove(&linux_dmabuf->main_renderer_destroy.link); + wl_list_remove(&linux_dmabuf->main_allocator_destroy.link); + wl_list_init(&linux_dmabuf->main_renderer_destroy.link); + wl_list_init(&linux_dmabuf->main_allocator_destroy.link); + + linux_dmabuf->main_renderer = NULL; + linux_dmabuf->main_allocator = NULL; +} + +static void linux_dmabuf_handle_main_renderer_destroy(struct wl_listener *listener, void *data) { + struct wlr_linux_dmabuf_v1 *linux_dmabuf = wl_container_of( + listener, linux_dmabuf, main_renderer_destroy); + linux_dmabuf_unregister_main_blit_device(linux_dmabuf); +} + +static void linux_dmabuf_handle_main_allocator_destroy(struct wl_listener *listener, void *data) { + struct wlr_linux_dmabuf_v1 *linux_dmabuf = wl_container_of( + listener, linux_dmabuf, main_allocator_destroy); + linux_dmabuf_unregister_main_blit_device(linux_dmabuf); +} + +void wlr_linux_dmabuf_v1_set_main_blit_device(struct wlr_linux_dmabuf_v1 *linux_dmabuf, + struct wlr_renderer *renderer, struct wlr_allocator *allocator) { + assert(renderer != NULL && allocator != NULL); + + wl_list_remove(&linux_dmabuf->main_renderer_destroy.link); + wl_list_remove(&linux_dmabuf->main_allocator_destroy.link); + + linux_dmabuf->main_renderer_destroy.notify = linux_dmabuf_handle_main_renderer_destroy; + wl_signal_add(&renderer->events.destroy, &linux_dmabuf->main_renderer_destroy); + + linux_dmabuf->main_allocator_destroy.notify = linux_dmabuf_handle_main_allocator_destroy; + wl_signal_add(&allocator->events.destroy, &linux_dmabuf->main_allocator_destroy); + + linux_dmabuf->main_renderer = renderer; + linux_dmabuf->main_allocator = allocator; +} diff --git a/types/wlr_raster.c b/types/wlr_raster.c index 6347e3ef1..4cdfe365a 100644 --- a/types/wlr_raster.c +++ b/types/wlr_raster.c @@ -6,8 +6,8 @@ #include #include #include +#include #include -#include #include #include #include @@ -368,6 +368,30 @@ struct wlr_texture *wlr_raster_obtain_texture_with_allocator(struct wlr_raster * return texture; } + // if this is a linux_dmabuf_v1 buffer, then we can try to use the + // main device for blitting which should support all the modifiers we + // advertise. + if (raster->buffer) { + struct wlr_dmabuf_v1_buffer *dmabuf_buffer = + wlr_dmabuf_v1_buffer_try_from_buffer(raster->buffer); + if (dmabuf_buffer && dmabuf_buffer->linux_dmabuf_v1->main_renderer) { + struct wlr_linux_dmabuf_v1 *linux_dmabuf = dmabuf_buffer->linux_dmabuf_v1; + struct wlr_texture *texture = wlr_texture_from_buffer( + linux_dmabuf->main_renderer, raster->buffer); + if (texture) { + wlr_raster_attach_with_allocator(raster, texture, + linux_dmabuf->main_allocator); + + // try to create a blit but this time through the primary device + texture = raster_try_texture_from_blit(raster, renderer); + if (texture) { + wlr_raster_attach_with_allocator(raster, texture, allocator); + return texture; + } + } + } + } + // as a last resort we need to do a copy through the CPU texture = raster_try_cpu_copy(raster, renderer); if (texture) {