From 27bbb91abff165c962cfb28de2ea9ba52981653c Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 6 Jun 2024 11:16:49 +0100 Subject: [PATCH] linux_dmabuf_v1: Implement data_ptr_access Allow direct access to the pixel data of linux_dmabuf_v1 buffers by mmapping the FD. This causes a wait on any outstanding fences and also triggers the DMA_BUF_SYNC mechanism to do any cache fiddling needed. This doesn't support multi-planar formats (e.g. YUV420 from hardware codecs) I also fix the comment on wlr_renderer.render_buffer_caps (it's used for textures, not the render target) and update wlr_renderer_init_wl_display() and wlr_linux_dmabuf_feedback_v1_init_with_options() to use the renderer's own claimed buffer_caps instead of hardcoding DMABUF as required. Loosely based on https://github.com/raspberrypi-ui/wlroots/commit/46ef2cfa3c2f206fba88604154ae01567fad2de6 --- include/meson.build | 3 + include/wlr/render/wlr_renderer.h | 4 +- include/wlr/types/wlr_linux_dmabuf_v1.h | 6 ++ render/wlr_renderer.c | 2 +- types/wlr_linux_dmabuf_v1.c | 109 +++++++++++++++++++++++- 5 files changed, 120 insertions(+), 4 deletions(-) diff --git a/include/meson.build b/include/meson.build index 165166c33..74d3c56b1 100644 --- a/include/meson.build +++ b/include/meson.build @@ -32,6 +32,9 @@ install_subdir('wlr', foreach name, have : internal_features internal_config.set10('HAVE_' + name.underscorify().to_upper(), have) endforeach + +internal_config.set10('HAVE_LINUX_DMA_BUF_H', cc.has_header('linux/dma-buf.h')) + wlr_files += configure_file( output: 'config.h', configuration: internal_config, diff --git a/include/wlr/render/wlr_renderer.h b/include/wlr/render/wlr_renderer.h index debeb4e29..a284005de 100644 --- a/include/wlr/render/wlr_renderer.h +++ b/include/wlr/render/wlr_renderer.h @@ -26,8 +26,8 @@ struct wlr_fbox; * A renderer for basic 2D operations. */ struct wlr_renderer { - // Capabilities required for the buffer used as a render target (bitmask of - // enum wlr_buffer_cap) + // Capabilities required for the buffers used as texture sources and + // render target (bitmask of enum wlr_buffer_cap) uint32_t render_buffer_caps; struct { diff --git a/include/wlr/types/wlr_linux_dmabuf_v1.h b/include/wlr/types/wlr_linux_dmabuf_v1.h index 2193f9141..436af2779 100644 --- a/include/wlr/types/wlr_linux_dmabuf_v1.h +++ b/include/wlr/types/wlr_linux_dmabuf_v1.h @@ -26,6 +26,12 @@ struct wlr_dmabuf_v1_buffer { struct { struct wl_listener release; + + // Cache mapped address while ptr_data_access is open + void *addr; + // WLR_BUFFER_DATA_PTR_ACCESS_* flags describing the type of + // the current ptr_data_access + uint32_t access_flags; } WLR_PRIVATE; }; diff --git a/render/wlr_renderer.c b/render/wlr_renderer.c index e65314ccc..e201a9c51 100644 --- a/render/wlr_renderer.c +++ b/render/wlr_renderer.c @@ -84,7 +84,7 @@ bool wlr_renderer_init_wl_display(struct wlr_renderer *r, return false; } - if (wlr_renderer_get_texture_formats(r, WLR_BUFFER_CAP_DMABUF) != NULL && + if (wlr_renderer_get_texture_formats(r, r->render_buffer_caps) != NULL && wlr_renderer_get_drm_fd(r) >= 0 && wlr_linux_dmabuf_v1_create_with_renderer(wl_display, 4, r) == NULL) { return false; diff --git a/types/wlr_linux_dmabuf_v1.c b/types/wlr_linux_dmabuf_v1.c index 3165e8805..d8c3f9217 100644 --- a/types/wlr_linux_dmabuf_v1.c +++ b/types/wlr_linux_dmabuf_v1.c @@ -1,7 +1,13 @@ #include +#include "config.h" #include #include #include +#include +#if HAVE_LINUX_DMA_BUF_H +#include +#include +#endif #include #include #include @@ -119,9 +125,109 @@ static bool buffer_get_dmabuf(struct wlr_buffer *wlr_buffer, return true; } +static bool buffer_begin_data_ptr_access(struct wlr_buffer *wlr_buffer, + uint32_t flags, void **data, uint32_t *format, size_t *stride) { + struct wlr_dmabuf_v1_buffer *buffer = + dmabuf_v1_buffer_from_buffer(wlr_buffer); + + if (buffer->attributes.n_planes != 1) { + // The current data_ptr_access interface can't support buffers + // split across multiple planes. + wlr_log(WLR_DEBUG, "Can't do data access on multi-planar dmabuf"); + return false; + } + + *format = buffer->attributes.format; + *stride = buffer->attributes.stride[0]; + int fd = buffer->attributes.fd[0]; + + // Poll fences on the dmabuf, check it's finished rendering + struct pollfd fence_poll = { + .fd = fd, + .events = POLLIN, + }; + if (poll(&fence_poll, 1, -1) < 0) { + wlr_log(WLR_ERROR, "Fence poll failed: %s", strerror(errno)); + } + +#if HAVE_LINUX_DMA_BUF_H + struct dma_buf_sync sync = { + .flags = DMA_BUF_SYNC_START, + }; + if (flags & WLR_BUFFER_DATA_PTR_ACCESS_READ) { + sync.flags |= DMA_BUF_SYNC_READ; + } + if (flags & WLR_BUFFER_DATA_PTR_ACCESS_WRITE) { + sync.flags |= DMA_BUF_SYNC_WRITE; + } +#endif + + int mmap_flags = 0; + if (flags & WLR_BUFFER_DATA_PTR_ACCESS_READ) { + mmap_flags |= PROT_READ; + } + if (flags & WLR_BUFFER_DATA_PTR_ACCESS_WRITE) { + mmap_flags |= PROT_WRITE; + } + + if (!buffer->addr) { + int size = *stride * buffer->attributes.height; + buffer->addr = mmap(NULL, size, mmap_flags, + MAP_SHARED, fd, buffer->attributes.offset[0]); + buffer->access_flags = flags; + +#if HAVE_LINUX_DMA_BUF_H + // dmabuf sync - this is for cache coherency + if (ioctl(fd, DMA_BUF_IOCTL_SYNC, &sync) < 0) { + wlr_log(WLR_ERROR, "dmabuf sync start failed: %s", + strerror(errno)); + } +#endif + } + if (buffer->addr == MAP_FAILED) { + wlr_log(WLR_ERROR, "Failed to map linux_dmabuf: %s", + strerror(errno)); + *data = NULL; + return false; + } + + *data = buffer->addr; + return true; +} + +static void buffer_end_data_ptr_access(struct wlr_buffer *wlr_buffer) { + struct wlr_dmabuf_v1_buffer *buffer = + dmabuf_v1_buffer_from_buffer(wlr_buffer); + +#if HAVE_LINUX_DMA_BUF_H + struct dma_buf_sync sync = { + .flags = DMA_BUF_SYNC_END, + }; + if (buffer->access_flags & WLR_BUFFER_DATA_PTR_ACCESS_READ) { + sync.flags |= DMA_BUF_SYNC_READ; + } + if (buffer->access_flags & WLR_BUFFER_DATA_PTR_ACCESS_WRITE) { + sync.flags |= DMA_BUF_SYNC_WRITE; + } + if (ioctl(buffer->attributes.fd[0], DMA_BUF_IOCTL_SYNC, &sync) < 0) { + wlr_log(WLR_ERROR, "dmabuf sync end failed: %s", strerror(errno)); + } +#endif + + int res = munmap(buffer->addr, + buffer->attributes.stride[0] * buffer->attributes.height); + if (res < 0) { + wlr_log(WLR_ERROR, "Failed to munmap dmabuf: %s", strerror(errno)); + } + + buffer->addr = NULL; +} + static const struct wlr_buffer_impl buffer_impl = { .destroy = buffer_destroy, .get_dmabuf = buffer_get_dmabuf, + .begin_data_ptr_access = buffer_begin_data_ptr_access, + .end_data_ptr_access = buffer_end_data_ptr_access, }; static void buffer_handle_release(struct wl_listener *listener, void *data) { @@ -1101,8 +1207,9 @@ bool wlr_linux_dmabuf_feedback_v1_init_with_options(struct wlr_linux_dmabuf_feed feedback->main_device = renderer_dev; + uint32_t buffer_caps = WLR_BUFFER_CAP_DMABUF | WLR_BUFFER_CAP_DATA_PTR; const struct wlr_drm_format_set *renderer_formats = - wlr_renderer_get_texture_formats(options->main_renderer, WLR_BUFFER_CAP_DMABUF); + wlr_renderer_get_texture_formats(options->main_renderer, buffer_caps); if (renderer_formats == NULL) { wlr_log(WLR_ERROR, "Failed to get renderer DMA-BUF texture formats"); goto error;