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/render/gles2.h b/include/render/gles2.h index 6b852dcb7..797624c1e 100644 --- a/include/render/gles2.h +++ b/include/render/gles2.h @@ -41,7 +41,6 @@ struct wlr_gles2_renderer { struct wlr_renderer wlr_renderer; struct wlr_egl *egl; - int drm_fd; struct wlr_drm_format_set shm_texture_formats; diff --git a/include/render/vulkan.h b/include/render/vulkan.h index deff0eac3..ea9d32732 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -39,8 +39,6 @@ struct wlr_vk_device { VkPhysicalDevice phdev; VkDevice dev; - int drm_fd; - bool sync_file_import_export; bool implicit_sync_interop; bool sampler_ycbcr_conversion; diff --git a/include/wlr/render/interface.h b/include/wlr/render/interface.h index 89f6de970..d46caa80f 100644 --- a/include/wlr/render/interface.h +++ b/include/wlr/render/interface.h @@ -25,7 +25,6 @@ struct wlr_renderer_impl { const struct wlr_drm_format_set *(*get_render_formats)( struct wlr_renderer *renderer); void (*destroy)(struct wlr_renderer *renderer); - int (*get_drm_fd)(struct wlr_renderer *renderer); struct wlr_texture *(*texture_from_buffer)(struct wlr_renderer *renderer, struct wlr_buffer *buffer); struct wlr_render_pass *(*begin_buffer_pass)(struct wlr_renderer *renderer, diff --git a/include/wlr/render/pixman.h b/include/wlr/render/pixman.h index 206dd78c6..db38df11c 100644 --- a/include/wlr/render/pixman.h +++ b/include/wlr/render/pixman.h @@ -13,7 +13,6 @@ #include struct wlr_renderer *wlr_pixman_renderer_create(void); - bool wlr_renderer_is_pixman(struct wlr_renderer *wlr_renderer); bool wlr_texture_is_pixman(struct wlr_texture *texture); diff --git a/include/wlr/render/wlr_renderer.h b/include/wlr/render/wlr_renderer.h index 62a1cca2d..994cf0773 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 { @@ -59,6 +59,7 @@ struct wlr_renderer { struct { const struct wlr_renderer_impl *impl; + int drm_fd; } WLR_PRIVATE; }; 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/gles2/renderer.c b/render/gles2/renderer.c index e362daee8..11e7544af 100644 --- a/render/gles2/renderer.c +++ b/render/gles2/renderer.c @@ -182,17 +182,6 @@ static const struct wlr_drm_format_set *gles2_get_render_formats( return wlr_egl_get_dmabuf_render_formats(renderer->egl); } -static int gles2_get_drm_fd(struct wlr_renderer *wlr_renderer) { - struct wlr_gles2_renderer *renderer = - gles2_get_renderer(wlr_renderer); - - if (renderer->drm_fd < 0) { - renderer->drm_fd = wlr_egl_dup_drm_fd(renderer->egl); - } - - return renderer->drm_fd; -} - struct wlr_egl *wlr_gles2_renderer_get_egl(struct wlr_renderer *wlr_renderer) { struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); @@ -231,8 +220,8 @@ static void gles2_destroy(struct wlr_renderer *wlr_renderer) { wlr_drm_format_set_finish(&renderer->shm_texture_formats); - if (renderer->drm_fd >= 0) { - close(renderer->drm_fd); + if (wlr_renderer->drm_fd >= 0) { + close(wlr_renderer->drm_fd); } free(renderer); @@ -356,7 +345,6 @@ static const struct wlr_renderer_impl renderer_impl = { .destroy = gles2_destroy, .get_texture_formats = gles2_get_texture_formats, .get_render_formats = gles2_get_render_formats, - .get_drm_fd = gles2_get_drm_fd, .texture_from_buffer = gles2_texture_from_buffer, .begin_buffer_pass = gles2_begin_buffer_pass, .render_timer_create = gles2_render_timer_create, @@ -533,7 +521,7 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { renderer->egl = egl; renderer->exts_str = exts_str; - renderer->drm_fd = -1; + renderer->wlr_renderer.drm_fd = -1; wlr_log(WLR_INFO, "Creating GLES2 renderer"); wlr_log(WLR_INFO, "Using %s", glGetString(GL_VERSION)); @@ -683,7 +671,8 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { get_gles2_shm_formats(renderer, &renderer->shm_texture_formats); - int drm_fd = wlr_renderer_get_drm_fd(&renderer->wlr_renderer); + int drm_fd = wlr_egl_dup_drm_fd(renderer->egl); + renderer->wlr_renderer.drm_fd = drm_fd; uint64_t cap_syncobj_timeline; if (drm_fd >= 0 && drmGetCap(drm_fd, DRM_CAP_SYNCOBJ_TIMELINE, &cap_syncobj_timeline) == 0) { renderer->wlr_renderer.features.timeline = egl->procs.eglDupNativeFenceFDANDROID && diff --git a/render/pixman/renderer.c b/render/pixman/renderer.c index 4631a33ab..7d778dc9c 100644 --- a/render/pixman/renderer.c +++ b/render/pixman/renderer.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -252,6 +253,11 @@ static struct wlr_texture *pixman_texture_from_buffer( &data, &drm_format, &stride)) { return NULL; } + // This looks bad, because we're saying "end access" but also storing the + // pointer for later use. However, we only access the texture data from + // render_pass_add_texture() which does a begin/end access, and + // begin_pixman_data_ptr_access() will handle if the data pointer changes + // between accesses. So everything should be fine. wlr_buffer_end_data_ptr_access(buffer); struct wlr_pixman_texture *texture = pixman_texture_create(renderer, @@ -335,8 +341,9 @@ struct wlr_renderer *wlr_pixman_renderer_create(void) { const uint32_t *formats = get_pixman_drm_formats(&len); for (size_t i = 0; i < len; ++i) { - wlr_drm_format_set_add(&renderer->drm_formats, formats[i], - DRM_FORMAT_MOD_INVALID); + // Only support linear buffers. MOD_INVALID could mean the driver + // can do whatever it thinks appropriate, but pixman definitely + // only supports linear. wlr_drm_format_set_add(&renderer->drm_formats, formats[i], DRM_FORMAT_MOD_LINEAR); } diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index a0e456580..1e999cdc0 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -1155,6 +1155,11 @@ static void vulkan_destroy(struct wlr_renderer *wlr_renderer) { struct wlr_vk_instance *ini = dev->instance; vulkan_device_destroy(dev); vulkan_instance_destroy(ini); + + if (wlr_renderer->drm_fd > 0) { + close(wlr_renderer->drm_fd); + } + free(renderer); } @@ -1404,11 +1409,6 @@ destroy_image: return false; } -static int vulkan_get_drm_fd(struct wlr_renderer *wlr_renderer) { - struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); - return renderer->dev->drm_fd; -} - static struct wlr_render_pass *vulkan_begin_buffer_pass(struct wlr_renderer *wlr_renderer, struct wlr_buffer *buffer, const struct wlr_buffer_pass_options *options) { struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); @@ -1433,7 +1433,6 @@ static const struct wlr_renderer_impl renderer_impl = { .get_texture_formats = vulkan_get_texture_formats, .get_render_formats = vulkan_get_render_formats, .destroy = vulkan_destroy, - .get_drm_fd = vulkan_get_drm_fd, .texture_from_buffer = vulkan_texture_from_buffer, .begin_buffer_pass = vulkan_begin_buffer_pass, }; @@ -2455,7 +2454,8 @@ struct wlr_renderer *vulkan_renderer_create_for_device(struct wlr_vk_device *dev wl_list_init(&renderer->pipeline_layouts); uint64_t cap_syncobj_timeline; - if (dev->drm_fd >= 0 && drmGetCap(dev->drm_fd, DRM_CAP_SYNCOBJ_TIMELINE, &cap_syncobj_timeline) == 0) { + if (renderer->wlr_renderer.drm_fd >= 0 && + drmGetCap(renderer->wlr_renderer.drm_fd, DRM_CAP_SYNCOBJ_TIMELINE, &cap_syncobj_timeline) == 0) { renderer->wlr_renderer.features.timeline = dev->sync_file_import_export && cap_syncobj_timeline != 0; } @@ -2526,9 +2526,11 @@ struct wlr_renderer *wlr_vk_renderer_create_with_drm_fd(int drm_fd) { // Do not use the drm_fd that was passed in: we should prefer the render // node even if a primary node was provided - dev->drm_fd = vulkan_open_phdev_drm_fd(phdev); + int render_drm_fd = vulkan_open_phdev_drm_fd(phdev); - return vulkan_renderer_create_for_device(dev); + struct wlr_renderer *wlr_renderer = vulkan_renderer_create_for_device(dev); + wlr_renderer->drm_fd = render_drm_fd; + return wlr_renderer; } VkInstance wlr_vk_renderer_get_instance(struct wlr_renderer *renderer) { diff --git a/render/vulkan/vulkan.c b/render/vulkan/vulkan.c index 78bc25941..5bace73cc 100644 --- a/render/vulkan/vulkan.c +++ b/render/vulkan/vulkan.c @@ -458,7 +458,6 @@ struct wlr_vk_device *vulkan_device_create(struct wlr_vk_instance *ini, dev->phdev = phdev; dev->instance = ini; - dev->drm_fd = -1; // For dmabuf import we require at least the external_memory_fd, // external_memory_dma_buf, queue_family_foreign, @@ -665,10 +664,6 @@ void vulkan_device_destroy(struct wlr_vk_device *dev) { vkDestroyDevice(dev->dev, NULL); } - if (dev->drm_fd > 0) { - close(dev->drm_fd); - } - wlr_drm_format_set_finish(&dev->dmabuf_render_formats); wlr_drm_format_set_finish(&dev->dmabuf_texture_formats); wlr_drm_format_set_finish(&dev->shm_texture_formats); diff --git a/render/wlr_renderer.c b/render/wlr_renderer.c index e65314ccc..63b2bc903 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; @@ -267,6 +267,14 @@ static struct wlr_renderer *renderer_autocreate(struct wlr_backend *backend, int if ((is_auto && !has_render_node(backend)) || strcmp(renderer_name, "pixman") == 0) { renderer = wlr_pixman_renderer_create(); + if (open_preferred_drm_fd(backend, &drm_fd, &own_drm_fd)) { + wlr_log(WLR_DEBUG, "Creating pixman renderer with DRM FD %d", drm_fd); + renderer->drm_fd = drm_fd; + } else { + wlr_log(WLR_DEBUG, "Creating pixman renderer without DRM"); + renderer->drm_fd = -1; + } + if (renderer) { goto out; } else { @@ -297,11 +305,8 @@ struct wlr_renderer *wlr_renderer_autocreate(struct wlr_backend *backend) { return renderer_autocreate(backend, -1); } -int wlr_renderer_get_drm_fd(struct wlr_renderer *r) { - if (!r->impl->get_drm_fd) { - return -1; - } - return r->impl->get_drm_fd(r); +int wlr_renderer_get_drm_fd(struct wlr_renderer *renderer) { + return renderer->drm_fd; } struct wlr_render_pass *wlr_renderer_begin_buffer_pass(struct wlr_renderer *renderer, 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;