diff options
author | Simon Ser <contact@emersion.fr> | 2021-08-25 09:33:19 +0200 |
---|---|---|
committer | Simon Zeni <simon@bl4ckb0ne.ca> | 2021-08-25 09:57:20 -0400 |
commit | 3ce2ea9e16fcd1bfab4ec9d5994fa721c5d58dcd (patch) | |
tree | 1bce6445158968c4d73cd73be30c141468dbc84c /render/allocator/drm_dumb.c | |
parent | b37731cdbbef4dc52033c2d26b04d2329720fa07 (diff) |
Move allocator stuff into new directory
Add render/allocator/ and include/render/allocator/ to hold
everything allocator-related.
Diffstat (limited to 'render/allocator/drm_dumb.c')
-rw-r--r-- | render/allocator/drm_dumb.c | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/render/allocator/drm_dumb.c b/render/allocator/drm_dumb.c new file mode 100644 index 00000000..a47df624 --- /dev/null +++ b/render/allocator/drm_dumb.c @@ -0,0 +1,248 @@ +#define _POSIX_C_SOURCE 200809L + +#include <assert.h> +#include <drm_fourcc.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <unistd.h> +#include <wlr/util/log.h> +#include <xf86drm.h> +#include <xf86drmMode.h> +#include <wlr/backend.h> +#include <wlr/backend/session.h> + +#include "render/allocator/drm_dumb.h" +#include "render/pixel_format.h" + +static const struct wlr_buffer_impl buffer_impl; + +static struct wlr_drm_dumb_buffer *drm_dumb_buffer_from_buffer( + struct wlr_buffer *wlr_buf) { + assert(wlr_buf->impl == &buffer_impl); + return (struct wlr_drm_dumb_buffer *)wlr_buf; +} + +static void finish_buffer(struct wlr_drm_dumb_buffer *buf) { + if (buf->data) { + munmap(buf->data, buf->size); + } + + wlr_dmabuf_attributes_finish(&buf->dmabuf); + + if (buf->drm_fd >= 0) { + struct drm_mode_destroy_dumb destroy = { .handle = buf->handle }; + if (drmIoctl(buf->drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy)) { + wlr_log_errno(WLR_ERROR, "Failed to destroy DRM dumb buffer"); + } + } + + wl_list_remove(&buf->link); +} + +static struct wlr_drm_dumb_buffer *create_buffer( + struct wlr_drm_dumb_allocator *alloc, int width, int height, + const struct wlr_drm_format *format) { + struct wlr_drm_dumb_buffer *buffer = calloc(1, sizeof(*buffer)); + if (buffer == NULL) { + return NULL; + } + wlr_buffer_init(&buffer->base, &buffer_impl, width, height); + wl_list_insert(&alloc->buffers, &buffer->link); + + const struct wlr_pixel_format_info *info = + drm_get_pixel_format_info(format->format); + if (info == NULL) { + wlr_log(WLR_ERROR, "DRM format 0x%"PRIX32" not supported", + format->format); + goto create_err; + } + + struct drm_mode_create_dumb create = {0}; + create.width = (uint32_t)width; + create.height = (uint32_t)height; + create.bpp = info->bpp; + + if (drmIoctl(alloc->drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create) != 0) { + wlr_log_errno(WLR_ERROR, "Failed to create DRM dumb buffer"); + goto create_err; + } + + buffer->width = create.width; + buffer->height = create.height; + + buffer->stride = create.pitch; + buffer->handle = create.handle; + buffer->format = format->format; + + buffer->drm_fd = alloc->drm_fd; + + struct drm_mode_map_dumb map = {0}; + map.handle = buffer->handle; + + if (drmIoctl(alloc->drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map) != 0) { + wlr_log_errno(WLR_ERROR, "Failed to map DRM dumb buffer"); + goto create_destroy; + } + + buffer->data = mmap(NULL, create.size, PROT_READ | PROT_WRITE, MAP_SHARED, + alloc->drm_fd, map.offset); + if (buffer->data == MAP_FAILED) { + wlr_log_errno(WLR_ERROR, "Failed to mmap DRM dumb buffer"); + goto create_destroy; + } + + buffer->size = create.size; + + memset(buffer->data, 0, create.size); + + int prime_fd; + if (drmPrimeHandleToFD(alloc->drm_fd, buffer->handle, DRM_CLOEXEC, + &prime_fd) != 0) { + wlr_log_errno(WLR_ERROR, "Failed to get PRIME handle from GEM handle"); + goto create_destroy; + } + + buffer->dmabuf = (struct wlr_dmabuf_attributes){ + .width = buffer->width, + .height = buffer->height, + .format = format->format, + .modifier = DRM_FORMAT_MOD_INVALID, + .n_planes = 1, + .offset[0] = 0, + .stride[0] = buffer->stride, + .fd[0] = prime_fd, + }; + + wlr_log(WLR_DEBUG, "Allocated %"PRIu32"x%"PRIu32" DRM dumb buffer", + buffer->width, buffer->height); + + return buffer; + +create_destroy: + finish_buffer(buffer); +create_err: + free(buffer); + return NULL; +} + +static bool drm_dumb_buffer_begin_data_ptr_access(struct wlr_buffer *wlr_buffer, + void **data, uint32_t *format, size_t *stride) { + struct wlr_drm_dumb_buffer *buf = drm_dumb_buffer_from_buffer(wlr_buffer); + *data = buf->data; + *stride = buf->stride; + *format = buf->format; + return true; +} + +static void drm_dumb_buffer_end_data_ptr_access(struct wlr_buffer *wlr_buffer) { + // This space is intentionally left blank +} + +static bool buffer_get_dmabuf(struct wlr_buffer *wlr_buffer, + struct wlr_dmabuf_attributes *attribs) { + struct wlr_drm_dumb_buffer *buf = drm_dumb_buffer_from_buffer(wlr_buffer); + memcpy(attribs, &buf->dmabuf, sizeof(buf->dmabuf)); + return true; +} + +static void buffer_destroy(struct wlr_buffer *wlr_buffer) { + struct wlr_drm_dumb_buffer *buf = drm_dumb_buffer_from_buffer(wlr_buffer); + finish_buffer(buf); + free(buf); +} + +static const struct wlr_buffer_impl buffer_impl = { + .destroy = buffer_destroy, + .get_dmabuf = buffer_get_dmabuf, + .begin_data_ptr_access = drm_dumb_buffer_begin_data_ptr_access, + .end_data_ptr_access = drm_dumb_buffer_end_data_ptr_access, +}; + +static const struct wlr_allocator_interface allocator_impl; + +static struct wlr_drm_dumb_allocator *drm_dumb_alloc_from_alloc( + struct wlr_allocator *wlr_alloc) { + assert(wlr_alloc->impl == &allocator_impl); + return (struct wlr_drm_dumb_allocator *)wlr_alloc; +} + +static struct wlr_buffer *allocator_create_buffer( + struct wlr_allocator *wlr_alloc, int width, int height, + const struct wlr_drm_format *drm_format) { + struct wlr_drm_dumb_allocator *alloc = drm_dumb_alloc_from_alloc(wlr_alloc); + struct wlr_drm_dumb_buffer *buffer = create_buffer(alloc, width, height, + drm_format); + if (buffer == NULL) { + return NULL; + } + return &buffer->base; +} + +static void allocator_destroy(struct wlr_allocator *wlr_alloc) { + struct wlr_drm_dumb_allocator *alloc = drm_dumb_alloc_from_alloc(wlr_alloc); + + struct wlr_drm_dumb_buffer *buf, *buf_tmp; + wl_list_for_each_safe(buf, buf_tmp, &alloc->buffers, link) { + buf->drm_fd = -1; + wl_list_remove(&buf->link); + wl_list_init(&buf->link); + } + + close(alloc->drm_fd); + free(alloc); +} + +static const struct wlr_allocator_interface allocator_impl = { + .create_buffer = allocator_create_buffer, + .destroy = allocator_destroy, +}; + +struct wlr_allocator *wlr_drm_dumb_allocator_create(int fd) { + if (!drmIsMaster(fd)) { + wlr_log(WLR_ERROR, "Cannot use DRM dumb buffers with non-master DRM FD"); + return NULL; + } + + /* Re-open the DRM node to avoid GEM handle ref'counting issues. See: + * https://gitlab.freedesktop.org/mesa/drm/-/merge_requests/110 + * TODO: don't assume we have the permission to just open the DRM node, + * find another way to re-open it. + */ + char *path = drmGetDeviceNameFromFd2(fd); + int drm_fd = open(path, O_RDWR | O_CLOEXEC); + if (drm_fd < 0) { + wlr_log_errno(WLR_ERROR, "Failed to open DRM node %s", path); + free(path); + return NULL; + } + + uint64_t has_dumb = 0; + if (drmGetCap(drm_fd, DRM_CAP_DUMB_BUFFER, &has_dumb) < 0) { + wlr_log(WLR_ERROR, "Failed to get DRM capabilities"); + free(path); + return NULL; + } + + if (has_dumb == 0) { + wlr_log(WLR_ERROR, "DRM dumb buffers not supported"); + free(path); + return NULL; + } + + struct wlr_drm_dumb_allocator *alloc = calloc(1, sizeof(*alloc)); + if (alloc == NULL) { + free(path); + return NULL; + } + wlr_allocator_init(&alloc->base, &allocator_impl, + WLR_BUFFER_CAP_DATA_PTR | WLR_BUFFER_CAP_DMABUF); + + alloc->drm_fd = drm_fd; + wl_list_init(&alloc->buffers); + + wlr_log(WLR_DEBUG, "Created DRM dumb allocator with node %s", path); + free(path); + return &alloc->base; +} |