diff options
Diffstat (limited to 'render/allocator')
-rw-r--r-- | render/allocator/allocator.c | 93 | ||||
-rw-r--r-- | render/allocator/drm_dumb.c | 248 | ||||
-rw-r--r-- | render/allocator/gbm.c | 236 | ||||
-rw-r--r-- | render/allocator/meson.build | 9 | ||||
-rw-r--r-- | render/allocator/shm.c | 116 |
5 files changed, 702 insertions, 0 deletions
diff --git a/render/allocator/allocator.c b/render/allocator/allocator.c new file mode 100644 index 00000000..8664202f --- /dev/null +++ b/render/allocator/allocator.c @@ -0,0 +1,93 @@ +#include <assert.h> +#include <stdlib.h> +#include <wlr/util/log.h> +#include <xf86drm.h> +#include "backend/backend.h" +#include "render/allocator/allocator.h" +#include "render/allocator/drm_dumb.h" +#include "render/allocator/gbm.h" +#include "render/allocator/shm.h" +#include "render/wlr_renderer.h" + +void wlr_allocator_init(struct wlr_allocator *alloc, + const struct wlr_allocator_interface *impl, uint32_t buffer_caps) { + assert(impl && impl->destroy && impl->create_buffer); + alloc->impl = impl; + alloc->buffer_caps = buffer_caps; + wl_signal_init(&alloc->events.destroy); +} + +struct wlr_allocator *allocator_autocreate_with_drm_fd( + struct wlr_backend *backend, struct wlr_renderer *renderer, + int drm_fd) { + uint32_t backend_caps = backend_get_buffer_caps(backend); + uint32_t renderer_caps = renderer_get_render_buffer_caps(renderer); + + struct wlr_allocator *alloc = NULL; + uint32_t gbm_caps = WLR_BUFFER_CAP_DMABUF; + if ((backend_caps & gbm_caps) && (renderer_caps & gbm_caps) + && drm_fd != -1) { + wlr_log(WLR_DEBUG, "Trying to create gbm allocator"); + if ((alloc = wlr_gbm_allocator_create(drm_fd)) != NULL) { + return alloc; + } + wlr_log(WLR_DEBUG, "Failed to create gbm allocator"); + } + + uint32_t shm_caps = WLR_BUFFER_CAP_SHM | WLR_BUFFER_CAP_DATA_PTR; + if ((backend_caps & shm_caps) && (renderer_caps & shm_caps)) { + wlr_log(WLR_DEBUG, "Trying to create shm allocator"); + if ((alloc = wlr_shm_allocator_create()) != NULL) { + return alloc; + } + wlr_log(WLR_DEBUG, "Failed to create shm allocator"); + } + + uint32_t drm_caps = WLR_BUFFER_CAP_DMABUF | WLR_BUFFER_CAP_DATA_PTR; + if ((backend_caps & drm_caps) && (renderer_caps & drm_caps) + && drm_fd != -1) { + wlr_log(WLR_DEBUG, "Trying to create drm dumb allocator"); + if ((alloc = wlr_drm_dumb_allocator_create(drm_fd)) != NULL) { + return alloc; + } + wlr_log(WLR_DEBUG, "Failed to create drm dumb allocator"); + } + + wlr_log(WLR_ERROR, "Failed to create allocator"); + return NULL; +} + +struct wlr_allocator *wlr_allocator_autocreate(struct wlr_backend *backend, + struct wlr_renderer *renderer) { + // Note, drm_fd may be negative if unavailable + int drm_fd = wlr_backend_get_drm_fd(backend); + return allocator_autocreate_with_drm_fd(backend, renderer, drm_fd); +} + +void wlr_allocator_destroy(struct wlr_allocator *alloc) { + if (alloc == NULL) { + return; + } + wl_signal_emit(&alloc->events.destroy, NULL); + alloc->impl->destroy(alloc); +} + +struct wlr_buffer *wlr_allocator_create_buffer(struct wlr_allocator *alloc, + int width, int height, const struct wlr_drm_format *format) { + struct wlr_buffer *buffer = + alloc->impl->create_buffer(alloc, width, height, format); + if (buffer == NULL) { + return NULL; + } + if (alloc->buffer_caps & WLR_BUFFER_CAP_DATA_PTR) { + assert(buffer->impl->begin_data_ptr_access && + buffer->impl->end_data_ptr_access); + } + if (alloc->buffer_caps & WLR_BUFFER_CAP_DMABUF) { + assert(buffer->impl->get_dmabuf); + } + if (alloc->buffer_caps & WLR_BUFFER_CAP_SHM) { + assert(buffer->impl->get_shm); + } + return buffer; +} 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; +} diff --git a/render/allocator/gbm.c b/render/allocator/gbm.c new file mode 100644 index 00000000..ace7ca68 --- /dev/null +++ b/render/allocator/gbm.c @@ -0,0 +1,236 @@ +#define _POSIX_C_SOURCE 200809L +#include <assert.h> +#include <drm_fourcc.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> +#include <wlr/util/log.h> +#include <xf86drm.h> +#include "render/allocator/gbm.h" + +static const struct wlr_buffer_impl buffer_impl; + +static struct wlr_gbm_buffer *get_gbm_buffer_from_buffer( + struct wlr_buffer *buffer) { + assert(buffer->impl == &buffer_impl); + return (struct wlr_gbm_buffer *)buffer; +} + +static bool export_gbm_bo(struct gbm_bo *bo, + struct wlr_dmabuf_attributes *out) { + struct wlr_dmabuf_attributes attribs = {0}; + + attribs.n_planes = gbm_bo_get_plane_count(bo); + if (attribs.n_planes > WLR_DMABUF_MAX_PLANES) { + wlr_log(WLR_ERROR, "GBM BO contains too many planes (%d)", + attribs.n_planes); + return false; + } + + attribs.width = gbm_bo_get_width(bo); + attribs.height = gbm_bo_get_height(bo); + attribs.format = gbm_bo_get_format(bo); + attribs.modifier = gbm_bo_get_modifier(bo); + + int i; + int32_t handle = -1; + for (i = 0; i < attribs.n_planes; ++i) { +#if HAS_GBM_BO_GET_FD_FOR_PLANE + attribs.fd[i] = gbm_bo_get_fd_for_plane(bo, i); + (void)handle; +#else + // GBM is lacking a function to get a FD for a given plane. Instead, + // check all planes have the same handle. We can't use + // drmPrimeHandleToFD because that messes up handle ref'counting in + // the user-space driver. + union gbm_bo_handle plane_handle = gbm_bo_get_handle_for_plane(bo, i); + if (plane_handle.s32 < 0) { + wlr_log(WLR_ERROR, "gbm_bo_get_handle_for_plane failed"); + goto error_fd; + } + if (i == 0) { + handle = plane_handle.s32; + } else if (plane_handle.s32 != handle) { + wlr_log(WLR_ERROR, "Failed to export GBM BO: " + "all planes don't have the same GEM handle"); + goto error_fd; + } + + attribs.fd[i] = gbm_bo_get_fd(bo); +#endif + + if (attribs.fd[i] < 0) { + wlr_log(WLR_ERROR, "gbm_bo_get_fd failed"); + goto error_fd; + } + + attribs.offset[i] = gbm_bo_get_offset(bo, i); + attribs.stride[i] = gbm_bo_get_stride_for_plane(bo, i); + } + + memcpy(out, &attribs, sizeof(attribs)); + return true; + +error_fd: + for (int j = 0; j < i; ++j) { + close(attribs.fd[j]); + } + return false; +} + +static struct wlr_gbm_buffer *create_buffer(struct wlr_gbm_allocator *alloc, + int width, int height, const struct wlr_drm_format *format) { + struct gbm_device *gbm_device = alloc->gbm_device; + + struct gbm_bo *bo = NULL; + bool has_modifier = true; + if (format->len > 0) { + bo = gbm_bo_create_with_modifiers(gbm_device, width, height, + format->format, format->modifiers, format->len); + } + if (bo == NULL) { + uint32_t usage = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING; + if (format->len == 1 && + format->modifiers[0] == DRM_FORMAT_MOD_LINEAR) { + usage |= GBM_BO_USE_LINEAR; + } + bo = gbm_bo_create(gbm_device, width, height, format->format, usage); + has_modifier = false; + } + if (bo == NULL) { + wlr_log(WLR_ERROR, "gbm_bo_create failed"); + return NULL; + } + + struct wlr_gbm_buffer *buffer = calloc(1, sizeof(*buffer)); + if (buffer == NULL) { + gbm_bo_destroy(bo); + return NULL; + } + wlr_buffer_init(&buffer->base, &buffer_impl, width, height); + buffer->gbm_bo = bo; + wl_list_insert(&alloc->buffers, &buffer->link); + + if (!export_gbm_bo(bo, &buffer->dmabuf)) { + free(buffer); + gbm_bo_destroy(bo); + return NULL; + } + + // If the buffer has been allocated with an implicit modifier, make sure we + // don't populate the modifier field: other parts of the stack may not + // understand modifiers, and they can't strip the modifier. + if (!has_modifier) { + buffer->dmabuf.modifier = DRM_FORMAT_MOD_INVALID; + } + + wlr_log(WLR_DEBUG, "Allocated %dx%d GBM buffer (format 0x%"PRIX32", " + "modifier 0x%"PRIX64")", buffer->base.width, buffer->base.height, + buffer->dmabuf.format, buffer->dmabuf.modifier); + + return buffer; +} + +static void buffer_destroy(struct wlr_buffer *wlr_buffer) { + struct wlr_gbm_buffer *buffer = + get_gbm_buffer_from_buffer(wlr_buffer); + wlr_dmabuf_attributes_finish(&buffer->dmabuf); + if (buffer->gbm_bo != NULL) { + gbm_bo_destroy(buffer->gbm_bo); + } + wl_list_remove(&buffer->link); + free(buffer); +} + +static bool buffer_get_dmabuf(struct wlr_buffer *wlr_buffer, + struct wlr_dmabuf_attributes *attribs) { + struct wlr_gbm_buffer *buffer = + get_gbm_buffer_from_buffer(wlr_buffer); + memcpy(attribs, &buffer->dmabuf, sizeof(buffer->dmabuf)); + return true; +} + +static const struct wlr_buffer_impl buffer_impl = { + .destroy = buffer_destroy, + .get_dmabuf = buffer_get_dmabuf, +}; + +static const struct wlr_allocator_interface allocator_impl; + +static struct wlr_gbm_allocator *get_gbm_alloc_from_alloc( + struct wlr_allocator *alloc) { + assert(alloc->impl == &allocator_impl); + return (struct wlr_gbm_allocator *)alloc; +} + +struct wlr_allocator *wlr_gbm_allocator_create(int drm_fd) { + int fd = fcntl(drm_fd, F_DUPFD_CLOEXEC, 0); + if (fd < 0) { + wlr_log(WLR_ERROR, "fcntl(F_DUPFD_CLOEXEC) failed"); + return NULL; + } + + uint64_t cap; + if (drmGetCap(fd, DRM_CAP_PRIME, &cap) || + !(cap & DRM_PRIME_CAP_EXPORT)) { + wlr_log(WLR_ERROR, "PRIME export not supported"); + return NULL; + } + + struct wlr_gbm_allocator *alloc = calloc(1, sizeof(*alloc)); + if (alloc == NULL) { + return NULL; + } + wlr_allocator_init(&alloc->base, &allocator_impl, WLR_BUFFER_CAP_DMABUF); + + alloc->fd = fd; + wl_list_init(&alloc->buffers); + + alloc->gbm_device = gbm_create_device(fd); + if (alloc->gbm_device == NULL) { + wlr_log(WLR_ERROR, "gbm_create_device failed"); + free(alloc); + return NULL; + } + + wlr_log(WLR_DEBUG, "Created GBM allocator with backend %s", + gbm_device_get_backend_name(alloc->gbm_device)); + char *drm_name = drmGetDeviceNameFromFd2(fd); + wlr_log(WLR_DEBUG, "Using DRM node %s", drm_name); + free(drm_name); + + return &alloc->base; +} + +static void allocator_destroy(struct wlr_allocator *wlr_alloc) { + struct wlr_gbm_allocator *alloc = get_gbm_alloc_from_alloc(wlr_alloc); + + // The gbm_bo objects need to be destroyed before the gbm_device + struct wlr_gbm_buffer *buf, *buf_tmp; + wl_list_for_each_safe(buf, buf_tmp, &alloc->buffers, link) { + gbm_bo_destroy(buf->gbm_bo); + buf->gbm_bo = NULL; + wl_list_remove(&buf->link); + wl_list_init(&buf->link); + } + + gbm_device_destroy(alloc->gbm_device); + close(alloc->fd); + free(alloc); +} + +static struct wlr_buffer *allocator_create_buffer( + struct wlr_allocator *wlr_alloc, int width, int height, + const struct wlr_drm_format *format) { + struct wlr_gbm_allocator *alloc = get_gbm_alloc_from_alloc(wlr_alloc); + struct wlr_gbm_buffer *buffer = create_buffer(alloc, width, height, format); + if (buffer == NULL) { + return NULL; + } + return &buffer->base; +} + +static const struct wlr_allocator_interface allocator_impl = { + .destroy = allocator_destroy, + .create_buffer = allocator_create_buffer, +}; diff --git a/render/allocator/meson.build b/render/allocator/meson.build new file mode 100644 index 00000000..db17ccb2 --- /dev/null +++ b/render/allocator/meson.build @@ -0,0 +1,9 @@ +wlr_files += files( + 'allocator.c', + 'gbm.c', + 'shm.c', + 'drm_dumb.c', +) + +has = cc.has_function('gbm_bo_get_fd_for_plane', dependencies: [gbm]) +add_project_arguments('-DHAS_GBM_BO_GET_FD_FOR_PLANE=@0@'.format(has.to_int()), language: 'c') diff --git a/render/allocator/shm.c b/render/allocator/shm.c new file mode 100644 index 00000000..7b32cf8d --- /dev/null +++ b/render/allocator/shm.c @@ -0,0 +1,116 @@ +#include <assert.h> +#include <drm_fourcc.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <unistd.h> +#include <wlr/util/log.h> +#include "render/pixel_format.h" +#include "render/allocator/shm.h" +#include "util/shm.h" + +static const struct wlr_buffer_impl buffer_impl; + +static struct wlr_shm_buffer *shm_buffer_from_buffer( + struct wlr_buffer *wlr_buffer) { + assert(wlr_buffer->impl == &buffer_impl); + return (struct wlr_shm_buffer *)wlr_buffer; +} + +static void buffer_destroy(struct wlr_buffer *wlr_buffer) { + struct wlr_shm_buffer *buffer = shm_buffer_from_buffer(wlr_buffer); + munmap(buffer->data, buffer->size); + close(buffer->shm.fd); + free(buffer); +} + +static bool buffer_get_shm(struct wlr_buffer *wlr_buffer, + struct wlr_shm_attributes *shm) { + struct wlr_shm_buffer *buffer = shm_buffer_from_buffer(wlr_buffer); + memcpy(shm, &buffer->shm, sizeof(*shm)); + return true; +} + +static bool shm_buffer_begin_data_ptr_access(struct wlr_buffer *wlr_buffer, + void **data, uint32_t *format, size_t *stride) { + struct wlr_shm_buffer *buffer = shm_buffer_from_buffer(wlr_buffer); + *data = buffer->data; + *format = buffer->shm.format; + *stride = buffer->shm.stride; + return true; +} + +static void shm_buffer_end_data_ptr_access(struct wlr_buffer *wlr_buffer) { + // This space is intentionally left blank +} + +static const struct wlr_buffer_impl buffer_impl = { + .destroy = buffer_destroy, + .get_shm = buffer_get_shm, + .begin_data_ptr_access = shm_buffer_begin_data_ptr_access, + .end_data_ptr_access = shm_buffer_end_data_ptr_access, +}; + +static struct wlr_buffer *allocator_create_buffer( + struct wlr_allocator *wlr_allocator, int width, int height, + const struct wlr_drm_format *format) { + const struct wlr_pixel_format_info *info = + drm_get_pixel_format_info(format->format); + if (info == NULL) { + wlr_log(WLR_ERROR, "Unsupported pixel format 0x%"PRIX32, format->format); + return NULL; + } + + struct wlr_shm_buffer *buffer = calloc(1, sizeof(*buffer)); + if (buffer == NULL) { + return NULL; + } + wlr_buffer_init(&buffer->base, &buffer_impl, width, height); + + // TODO: consider using a single file for multiple buffers + int bytes_per_pixel = info->bpp / 8; + int stride = width * bytes_per_pixel; // TODO: align? + buffer->size = stride * height; + buffer->shm.fd = allocate_shm_file(buffer->size); + if (buffer->shm.fd < 0) { + free(buffer); + return NULL; + } + + buffer->shm.format = format->format; + buffer->shm.width = width; + buffer->shm.height = height; + buffer->shm.stride = stride; + buffer->shm.offset = 0; + + buffer->data = mmap(NULL, buffer->size, PROT_READ | PROT_WRITE, MAP_SHARED, + buffer->shm.fd, 0); + if (buffer->data == MAP_FAILED) { + wlr_log_errno(WLR_ERROR, "mmap failed"); + close(buffer->shm.fd); + free(buffer); + return NULL; + } + + return &buffer->base; +} + +static void allocator_destroy(struct wlr_allocator *wlr_allocator) { + free(wlr_allocator); +} + +static const struct wlr_allocator_interface allocator_impl = { + .destroy = allocator_destroy, + .create_buffer = allocator_create_buffer, +}; + +struct wlr_allocator *wlr_shm_allocator_create(void) { + struct wlr_shm_allocator *allocator = calloc(1, sizeof(*allocator)); + if (allocator == NULL) { + return NULL; + } + wlr_allocator_init(&allocator->base, &allocator_impl, + WLR_BUFFER_CAP_DATA_PTR | WLR_BUFFER_CAP_SHM); + + wlr_log(WLR_DEBUG, "Created shm allocator"); + return &allocator->base; +} |