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.
This commit is contained in:
Alexander Orzechowski 2024-02-02 20:38:56 -05:00
parent 5eb9a2ea10
commit b0e8e6eae7
3 changed files with 107 additions and 5 deletions

View file

@ -17,6 +17,8 @@
#include <wlr/render/drm_format_set.h>
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

View file

@ -7,6 +7,7 @@
#include <wlr/backend.h>
#include <wlr/config.h>
#include <wlr/interfaces/wlr_buffer.h>
#include <wlr/render/allocator.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_linux_dmabuf_v1.h>
@ -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;
}

View file

@ -6,8 +6,8 @@
#include <wlr/render/wlr_texture.h>
#include <wlr/types/wlr_buffer.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_linux_dmabuf_v1.h>
#include <wlr/types/wlr_linux_drm_syncobj_v1.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_raster.h>
#include <wlr/render/drm_syncobj.h>
#include <wlr/render/wlr_texture.h>
@ -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) {