From c868e509b7f41dc308726e15551e02897946db70 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 30 Jun 2021 15:41:56 +0200 Subject: [PATCH] drm: add support for DMA-BUFs Mesa's Vulkan WSI still uses wl_drm when modifiers aren't supported. This has been fixed in [1] but will take some time to be propagated to users. In the meantime, add a fallback. [1]: https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/4942 --- include/wlr/types/wlr_drm.h | 16 +++++ types/wlr_drm.c | 131 ++++++++++++++++++++++++++++++++++-- 2 files changed, 141 insertions(+), 6 deletions(-) diff --git a/include/wlr/types/wlr_drm.h b/include/wlr/types/wlr_drm.h index 51764928..efcf139f 100644 --- a/include/wlr/types/wlr_drm.h +++ b/include/wlr/types/wlr_drm.h @@ -13,6 +13,15 @@ struct wlr_renderer; +struct wlr_drm_buffer { + struct wlr_buffer base; + + struct wl_resource *resource; // can be NULL if the client destroyed it + struct wlr_dmabuf_attributes dmabuf; + + struct wl_listener release; +}; + /** * A stub implementation of Mesa's wl_drm protocol. * @@ -21,6 +30,7 @@ struct wlr_renderer; */ struct wlr_drm { struct wl_global *global; + struct wlr_renderer *renderer; char *node_name; struct { @@ -28,8 +38,14 @@ struct wlr_drm { } events; struct wl_listener display_destroy; + struct wl_listener renderer_destroy; }; +bool wlr_drm_buffer_is_resource(struct wl_resource *resource); + +struct wlr_drm_buffer *wlr_drm_buffer_from_resource( + struct wl_resource *resource); + struct wlr_drm *wlr_drm_create(struct wl_display *display, struct wlr_renderer *renderer); diff --git a/types/wlr_drm.c b/types/wlr_drm.c index f37b8060..a02fba84 100644 --- a/types/wlr_drm.c +++ b/types/wlr_drm.c @@ -1,9 +1,12 @@ #define _POSIX_C_SOURCE 200809L #include +#include #include #include #include +#include #include +#include #include #include #include "drm-protocol.h" @@ -11,6 +14,69 @@ #define WLR_DRM_VERSION 2 +static void buffer_handle_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static const struct wl_buffer_interface wl_buffer_impl = { + .destroy = buffer_handle_destroy, +}; + +static const struct wlr_buffer_impl buffer_impl; + +static struct wlr_drm_buffer *drm_buffer_from_buffer( + struct wlr_buffer *buffer) { + assert(buffer->impl == &buffer_impl); + return (struct wlr_drm_buffer *)buffer; +} + +static void buffer_destroy(struct wlr_buffer *wlr_buffer) { + struct wlr_drm_buffer *buffer = drm_buffer_from_buffer(wlr_buffer); + if (buffer->resource != NULL) { + wl_resource_set_user_data(buffer->resource, NULL); + } + wlr_dmabuf_attributes_finish(&buffer->dmabuf); + wl_list_remove(&buffer->release.link); + free(buffer); +} + +static bool buffer_get_dmabuf(struct wlr_buffer *wlr_buffer, + struct wlr_dmabuf_attributes *dmabuf) { + struct wlr_drm_buffer *buffer = drm_buffer_from_buffer(wlr_buffer); + *dmabuf = buffer->dmabuf; + return true; +} + +static const struct wlr_buffer_impl buffer_impl = { + .destroy = buffer_destroy, + .get_dmabuf = buffer_get_dmabuf, +}; + +bool wlr_drm_buffer_is_resource(struct wl_resource *resource) { + return wl_resource_instance_of(resource, &wl_buffer_interface, + &wl_buffer_impl); +} + +struct wlr_drm_buffer *wlr_drm_buffer_from_resource( + struct wl_resource *resource) { + assert(wlr_drm_buffer_is_resource(resource)); + return wl_resource_get_user_data(resource); +} + +static void buffer_handle_resource_destroy(struct wl_resource *resource) { + struct wlr_drm_buffer *buffer = wlr_drm_buffer_from_resource(resource); + buffer->resource = NULL; + wlr_buffer_drop(&buffer->base); +} + +static void buffer_handle_release(struct wl_listener *listener, void *data) { + struct wlr_drm_buffer *buffer = wl_container_of(listener, buffer, release); + if (buffer->resource != NULL) { + wl_buffer_send_release(buffer->resource); + } +} + static void drm_handle_authenticate(struct wl_client *client, struct wl_resource *resource, uint32_t id) { // We only use render nodes, which don't need authentication @@ -36,9 +102,39 @@ static void drm_handle_create_prime_buffer(struct wl_client *client, struct wl_resource *resource, uint32_t id, int fd, int32_t width, int32_t height, uint32_t format, int32_t offset0, int32_t stride0, int32_t offset1, int32_t stride1, int32_t offset2, int32_t stride2) { - close(fd); - wl_resource_post_error(resource, WL_DRM_ERROR_INVALID_NAME, - "DMA-BUF imports via wl_drm are not supported, use linux-dmabuf instead"); + struct wlr_dmabuf_attributes dmabuf = { + .width = width, + .height = height, + .format = format, + .modifier = DRM_FORMAT_MOD_INVALID, + .n_planes = 1, + .offset[0] = offset0, + .stride[0] = stride0, + .fd[0] = fd, + }; + + struct wlr_drm_buffer *buffer = calloc(1, sizeof(*buffer)); + if (buffer == NULL) { + close(fd); + wl_resource_post_no_memory(resource); + return; + } + wlr_buffer_init(&buffer->base, &buffer_impl, width, height); + + buffer->resource = wl_resource_create(client, &wl_buffer_interface, 1, id); + if (buffer->resource == NULL) { + free(buffer); + close(fd); + wl_resource_post_no_memory(resource); + return; + } + wl_resource_set_implementation(buffer->resource, &wl_buffer_impl, buffer, + buffer_handle_resource_destroy); + + buffer->dmabuf = dmabuf; + + buffer->release.notify = buffer_handle_release; + wl_signal_add(&buffer->base.events.release, &buffer->release); } static const struct wl_drm_interface drm_impl = { @@ -62,20 +158,39 @@ static void drm_bind(struct wl_client *client, void *data, wl_drm_send_device(resource, drm->node_name); wl_drm_send_capabilities(resource, WL_DRM_CAPABILITY_PRIME); + + const struct wlr_drm_format_set *formats = + wlr_renderer_get_dmabuf_texture_formats(drm->renderer); + if (formats == NULL) { + return; + } + + for (size_t i = 0; i < formats->len; i++) { + wl_drm_send_format(resource, formats->formats[i]->format); + } } -static void handle_display_destroy(struct wl_listener *listener, void *data) { - struct wlr_drm *drm = wl_container_of(listener, drm, display_destroy); - +static void drm_destroy(struct wlr_drm *drm) { wlr_signal_emit_safe(&drm->events.destroy, NULL); wl_list_remove(&drm->display_destroy.link); + wl_list_remove(&drm->renderer_destroy.link); free(drm->node_name); wl_global_destroy(drm->global); free(drm); } +static void handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_drm *drm = wl_container_of(listener, drm, display_destroy); + drm_destroy(drm); +} + +static void handle_renderer_destroy(struct wl_listener *listener, void *data) { + struct wlr_drm *drm = wl_container_of(listener, drm, renderer_destroy); + drm_destroy(drm); +} + struct wlr_drm *wlr_drm_create(struct wl_display *display, struct wlr_renderer *renderer) { int drm_fd = wlr_renderer_get_drm_fd(renderer); @@ -111,6 +226,7 @@ struct wlr_drm *wlr_drm_create(struct wl_display *display, } drm->node_name = node_name; + drm->renderer = renderer; wl_signal_init(&drm->events.destroy); drm->global = wl_global_create(display, &wl_drm_interface, WLR_DRM_VERSION, @@ -124,5 +240,8 @@ struct wlr_drm *wlr_drm_create(struct wl_display *display, drm->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &drm->display_destroy); + drm->renderer_destroy.notify = handle_renderer_destroy; + wl_signal_add(&renderer->events.destroy, &drm->renderer_destroy); + return drm; }