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 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
|
#include <assert.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <wlr/backend.h>
#include <wlr/config.h>
#include <wlr/interfaces/wlr_buffer.h>
#include <wlr/render/allocator.h>
#include <wlr/util/log.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include "render/allocator/drm_dumb.h"
#include "render/allocator/shm.h"
#include "render/wlr_renderer.h"
#if WLR_HAS_GBM_ALLOCATOR
#include "render/allocator/gbm.h"
#endif
#if WLR_HAS_UDMABUF_ALLOCATOR
#include "render/allocator/udmabuf.h"
#endif
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 = (struct wlr_allocator){
.impl = impl,
.buffer_caps = buffer_caps,
};
wl_signal_init(&alloc->events.destroy);
}
/* Re-open the DRM node to avoid GEM handle ref'counting issues. See:
* https://gitlab.freedesktop.org/mesa/drm/-/merge_requests/110
*/
static int reopen_drm_node(int drm_fd, bool allow_render_node) {
if (drmIsMaster(drm_fd)) {
// Only recent kernels support empty leases
uint32_t lessee_id;
int lease_fd = drmModeCreateLease(drm_fd, NULL, 0, O_CLOEXEC, &lessee_id);
if (lease_fd >= 0) {
return lease_fd;
} else if (lease_fd != -EINVAL && lease_fd != -EOPNOTSUPP) {
wlr_log_errno(WLR_ERROR, "drmModeCreateLease failed");
return -1;
}
wlr_log(WLR_DEBUG, "drmModeCreateLease failed, "
"falling back to plain open");
}
char *name = NULL;
if (allow_render_node) {
name = drmGetRenderDeviceNameFromFd(drm_fd);
}
if (name == NULL) {
// Either the DRM device has no render node, either the caller wants
// a primary node
name = drmGetDeviceNameFromFd2(drm_fd);
if (name == NULL) {
wlr_log(WLR_ERROR, "drmGetDeviceNameFromFd2 failed");
return -1;
}
}
int new_fd = open(name, O_RDWR | O_CLOEXEC);
if (new_fd < 0) {
wlr_log_errno(WLR_ERROR, "Failed to open DRM node '%s'", name);
free(name);
return -1;
}
free(name);
// If we're using a DRM primary node and we are DRM master (e.g. because
// we're running under the DRM backend), we need to use the legacy DRM
// authentication mechanism to have the permission to manipulate DRM dumb
// buffers.
if (drmIsMaster(drm_fd) && drmGetNodeTypeFromFd(new_fd) == DRM_NODE_PRIMARY) {
drm_magic_t magic;
if (drmGetMagic(new_fd, &magic) < 0) {
wlr_log_errno(WLR_ERROR, "drmGetMagic failed");
close(new_fd);
return -1;
}
if (drmAuthMagic(drm_fd, magic) < 0) {
wlr_log_errno(WLR_ERROR, "drmAuthMagic failed");
close(new_fd);
return -1;
}
}
return new_fd;
}
struct wlr_allocator *wlr_allocator_autocreate(struct wlr_backend *backend,
struct wlr_renderer *renderer) {
uint32_t backend_caps = backend->buffer_caps;
uint32_t renderer_caps = renderer->render_buffer_caps;
// Note, drm_fd may be negative if unavailable
int drm_fd = wlr_backend_get_drm_fd(backend);
if (drm_fd < 0) {
drm_fd = wlr_renderer_get_drm_fd(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 >= 0) {
#if WLR_HAS_GBM_ALLOCATOR
wlr_log(WLR_DEBUG, "Trying to create gbm allocator");
int gbm_fd = reopen_drm_node(drm_fd, true);
if (gbm_fd < 0) {
return NULL;
}
if ((alloc = wlr_gbm_allocator_create(gbm_fd)) != NULL) {
return alloc;
}
close(gbm_fd);
wlr_log(WLR_DEBUG, "Failed to create gbm allocator");
#else
wlr_log(WLR_DEBUG, "Skipping gbm allocator: disabled at compile-time");
#endif
}
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 >= 0 && drmIsMaster(drm_fd)) {
wlr_log(WLR_DEBUG, "Trying to create drm dumb allocator");
int dumb_fd = reopen_drm_node(drm_fd, false);
if (dumb_fd < 0) {
return NULL;
}
if ((alloc = wlr_drm_dumb_allocator_create(dumb_fd)) != NULL) {
return alloc;
}
close(dumb_fd);
wlr_log(WLR_DEBUG, "Failed to create drm dumb allocator");
}
uint32_t udmabuf_caps = WLR_BUFFER_CAP_DMABUF | WLR_BUFFER_CAP_SHM;
if ((backend_caps & udmabuf_caps) && (renderer_caps & udmabuf_caps) &&
drm_fd < 0) {
#if WLR_HAS_UDMABUF_ALLOCATOR
wlr_log(WLR_DEBUG, "Trying udmabuf allocator");
if ((alloc = wlr_udmabuf_allocator_create()) != NULL) {
return alloc;
}
wlr_log(WLR_DEBUG, "Failed to create udmabuf allocator");
#else
wlr_log(WLR_DEBUG, "Skipping udmabuf allocator: disabled at compile-time");
#endif
}
wlr_log(WLR_ERROR, "Failed to create allocator");
return NULL;
}
void wlr_allocator_destroy(struct wlr_allocator *alloc) {
if (alloc == NULL) {
return;
}
wl_signal_emit_mutable(&alloc->events.destroy, NULL);
assert(wl_list_empty(&alloc->events.destroy.listener_list));
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;
}
|