1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
|
#undef _POSIX_C_SOURCE
#define _GNU_SOURCE // for memfd_create() and F_ADD_SEALS
#include <drm_fourcc.h>
#include <fcntl.h>
#include <linux/udmabuf.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <wlr/interfaces/wlr_buffer.h>
#include <wlr/render/allocator.h>
#include <wlr/render/drm_format_set.h>
#include <wlr/util/log.h>
#include "render/allocator/udmabuf.h"
#include "render/pixel_format.h"
static bool buffer_get_shm(struct wlr_buffer *wlr_buffer, struct wlr_shm_attributes *shm) {
struct wlr_udmabuf_buffer *buffer = wl_container_of(wlr_buffer, buffer, base);
*shm = buffer->shm;
return true;
}
static bool buffer_get_dmabuf(struct wlr_buffer *wlr_buffer, struct wlr_dmabuf_attributes *dmabuf) {
struct wlr_udmabuf_buffer *buffer = wl_container_of(wlr_buffer, buffer, base);
*dmabuf = buffer->dmabuf;
return true;
}
static void buffer_destroy(struct wlr_buffer *wlr_buffer) {
struct wlr_udmabuf_buffer *buffer = wl_container_of(wlr_buffer, buffer, base);
wlr_dmabuf_attributes_finish(&buffer->dmabuf);
close(buffer->shm.fd);
free(buffer);
}
static const struct wlr_buffer_impl buffer_impl = {
.destroy = buffer_destroy,
.get_shm = buffer_get_shm,
.get_dmabuf = buffer_get_dmabuf,
};
static struct wlr_buffer *allocator_create_buffer(
struct wlr_allocator *wlr_allocator, int width, int height,
const struct wlr_drm_format *format) {
struct wlr_udmabuf_allocator *allocator = wl_container_of(wlr_allocator, allocator, base);
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;
}
long page_size = sysconf(_SC_PAGE_SIZE);
if (page_size == -1) {
wlr_log_errno(WLR_ERROR, "Failed to query page size");
return NULL;
}
struct wlr_udmabuf_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 stride = pixel_format_info_min_stride(info, width); // TODO: align?
size_t size = stride * height;
if (size % page_size != 0) {
size += page_size - (size % page_size);
}
int memfd = memfd_create("wlroots", MFD_CLOEXEC | MFD_ALLOW_SEALING);
if (memfd < 0) {
wlr_log_errno(WLR_ERROR, "memfd_create() failed");
goto err_buffer;
}
if (ftruncate(memfd, size) < 0) {
wlr_log_errno(WLR_ERROR, "ftruncate() failed");
goto err_memfd;
}
if (fcntl(memfd, F_ADD_SEALS, F_SEAL_SEAL | F_SEAL_SHRINK) < 0) {
wlr_log_errno(WLR_ERROR, "fcntl(F_ADD_SEALS) failed");
goto err_memfd;
}
struct udmabuf_create udmabuf_create = {
.memfd = memfd,
.flags = UDMABUF_FLAGS_CLOEXEC,
.offset = 0,
.size = size,
};
int dmabuf_fd = ioctl(allocator->fd, UDMABUF_CREATE, &udmabuf_create);
if (dmabuf_fd < 0) {
wlr_log_errno(WLR_ERROR, "ioctl(UDMABUF_CREATE) failed");
goto err_memfd;
}
buffer->size = size;
buffer->shm = (struct wlr_shm_attributes){
.width = width,
.height = height,
.format = format->format,
.offset = 0,
.stride = stride,
.fd = memfd,
};
buffer->dmabuf = (struct wlr_dmabuf_attributes){
.width = width,
.height = height,
.format = format->format,
.modifier = DRM_FORMAT_MOD_LINEAR,
.n_planes = 1,
.offset[0] = 0,
.stride[0] = stride,
.fd[0] = dmabuf_fd,
};
return &buffer->base;
err_memfd:
close(memfd);
err_buffer:
free(buffer);
return NULL;
}
static void allocator_destroy(struct wlr_allocator *wlr_allocator) {
struct wlr_udmabuf_allocator *allocator = wl_container_of(wlr_allocator, allocator, base);
close(allocator->fd);
free(allocator);
}
static const struct wlr_allocator_interface allocator_impl = {
.destroy = allocator_destroy,
.create_buffer = allocator_create_buffer,
};
struct wlr_allocator *wlr_udmabuf_allocator_create(void) {
int fd = open("/dev/udmabuf", O_RDWR | O_CLOEXEC);
if (fd < 0) {
wlr_log_errno(WLR_ERROR, "Failed to open /dev/udmabuf");
return NULL;
}
struct wlr_udmabuf_allocator *allocator = calloc(1, sizeof(*allocator));
if (allocator == NULL) {
close(fd);
return NULL;
}
wlr_allocator_init(&allocator->base, &allocator_impl,
WLR_BUFFER_CAP_SHM | WLR_BUFFER_CAP_DMABUF);
allocator->fd = fd;
return &allocator->base;
}
|