aboutsummaryrefslogtreecommitdiff
path: root/backend/drm/renderer.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/drm/renderer.c')
-rw-r--r--backend/drm/renderer.c147
1 files changed, 97 insertions, 50 deletions
diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c
index 4f1b102a..2d81ccd5 100644
--- a/backend/drm/renderer.c
+++ b/backend/drm/renderer.c
@@ -2,7 +2,6 @@
#include <assert.h>
#include <drm_fourcc.h>
#include <fcntl.h>
-#include <gbm.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
@@ -195,43 +194,6 @@ void drm_fb_clear(struct wlr_drm_fb **fb_ptr) {
*fb_ptr = NULL;
}
-static struct gbm_bo *get_bo_for_dmabuf(struct gbm_device *gbm,
- struct wlr_dmabuf_attributes *attribs) {
- if (attribs->modifier != DRM_FORMAT_MOD_INVALID ||
- attribs->n_planes > 1 || attribs->offset[0] != 0) {
- struct gbm_import_fd_modifier_data data = {
- .width = attribs->width,
- .height = attribs->height,
- .format = attribs->format,
- .num_fds = attribs->n_planes,
- .modifier = attribs->modifier,
- };
-
- if ((size_t)attribs->n_planes > sizeof(data.fds) / sizeof(data.fds[0])) {
- return false;
- }
-
- for (size_t i = 0; i < (size_t)attribs->n_planes; ++i) {
- data.fds[i] = attribs->fd[i];
- data.strides[i] = attribs->stride[i];
- data.offsets[i] = attribs->offset[i];
- }
-
- return gbm_bo_import(gbm, GBM_BO_IMPORT_FD_MODIFIER,
- &data, GBM_BO_USE_SCANOUT);
- } else {
- struct gbm_import_fd_data data = {
- .fd = attribs->fd[0],
- .width = attribs->width,
- .height = attribs->height,
- .stride = attribs->stride[0],
- .format = attribs->format,
- };
-
- return gbm_bo_import(gbm, GBM_BO_IMPORT_FD, &data, GBM_BO_USE_SCANOUT);
- }
-}
-
static void drm_fb_handle_destroy(struct wlr_addon *addon) {
struct wlr_drm_fb *fb = wl_container_of(addon, fb, addon);
drm_fb_destroy(fb);
@@ -242,6 +204,83 @@ static const struct wlr_addon_interface fb_addon_impl = {
.destroy = drm_fb_handle_destroy,
};
+static uint32_t get_bo_handle_for_fd(struct wlr_drm_backend *drm,
+ int dmabuf_fd) {
+ uint32_t handle = 0;
+ int ret = drmPrimeFDToHandle(drm->fd, dmabuf_fd, &handle);
+ if (ret != 0) {
+ wlr_log_errno(WLR_DEBUG, "drmPrimeFDToHandle failed");
+ return 0;
+ }
+
+ if (!drm_bo_handle_table_ref(&drm->bo_handles, handle)) {
+ // If that failed, the handle wasn't ref'ed in the table previously,
+ // so safe to delete
+ struct drm_gem_close args = { .handle = handle };
+ drmIoctl(drm->fd, DRM_IOCTL_GEM_CLOSE, &args);
+ return 0;
+ }
+
+ return handle;
+}
+
+static void close_bo_handle(struct wlr_drm_backend *drm, uint32_t handle) {
+ if (handle == 0) {
+ return;
+ }
+
+ size_t nrefs = drm_bo_handle_table_unref(&drm->bo_handles, handle);
+ if (nrefs > 0) {
+ return;
+ }
+
+ struct drm_gem_close args = { .handle = handle };
+ if (drmIoctl(drm->fd, DRM_IOCTL_GEM_CLOSE, &args) != 0) {
+ wlr_log_errno(WLR_ERROR, "drmIoctl(GEM_CLOSE) failed");
+ }
+}
+
+static uint32_t get_fb_for_bo(struct wlr_drm_backend *drm,
+ struct wlr_dmabuf_attributes *dmabuf, uint32_t handles[static 4]) {
+ uint64_t modifiers[4] = {0};
+ for (int i = 0; i < dmabuf->n_planes; i++) {
+ // KMS requires all BO planes to have the same modifier
+ modifiers[i] = dmabuf->modifier;
+ }
+
+ uint32_t id = 0;
+ if (drm->addfb2_modifiers && dmabuf->modifier != DRM_FORMAT_MOD_INVALID) {
+ if (drmModeAddFB2WithModifiers(drm->fd, dmabuf->width, dmabuf->height,
+ dmabuf->format, handles, dmabuf->stride, dmabuf->offset,
+ modifiers, &id, DRM_MODE_FB_MODIFIERS) != 0) {
+ wlr_log_errno(WLR_DEBUG, "drmModeAddFB2WithModifiers failed");
+ }
+ } else {
+ int ret = drmModeAddFB2(drm->fd, dmabuf->width, dmabuf->height,
+ dmabuf->format, handles, dmabuf->stride, dmabuf->offset, &id, 0);
+ if (ret != 0 && dmabuf->format == DRM_FORMAT_ARGB8888 &&
+ dmabuf->n_planes == 1 && dmabuf->offset[0] == 0) {
+ // Some big-endian machines don't support drmModeAddFB2. Try a
+ // last-resort fallback for ARGB8888 buffers, like Xorg's
+ // modesetting driver does.
+ wlr_log(WLR_DEBUG, "drmModeAddFB2 failed (%s), falling back to "
+ "legacy drmModeAddFB", strerror(-ret));
+
+ uint32_t depth = 32;
+ uint32_t bpp = 32;
+ ret = drmModeAddFB(drm->fd, dmabuf->width, dmabuf->height, depth,
+ bpp, dmabuf->stride[0], handles[0], &id);
+ if (ret != 0) {
+ wlr_log_errno(WLR_DEBUG, "drmModeAddFB failed");
+ }
+ } else if (ret != 0) {
+ wlr_log_errno(WLR_DEBUG, "drmModeAddFB2 failed");
+ }
+ }
+
+ return id;
+}
+
static struct wlr_drm_fb *drm_fb_create(struct wlr_drm_backend *drm,
struct wlr_buffer *buf, const struct wlr_drm_format_set *formats) {
struct wlr_drm_fb *fb = calloc(1, sizeof(*fb));
@@ -279,18 +318,20 @@ static struct wlr_drm_fb *drm_fb_create(struct wlr_drm_backend *drm,
}
}
- fb->bo = get_bo_for_dmabuf(drm->gbm, &attribs);
- if (!fb->bo) {
- wlr_log(WLR_DEBUG, "Failed to import DMA-BUF in GBM");
- goto error_get_dmabuf;
+ for (int i = 0; i < attribs.n_planes; ++i) {
+ fb->handles[i] = get_bo_handle_for_fd(drm, attribs.fd[i]);
+ if (fb->handles[i] == 0) {
+ goto error_bo_handle;
+ }
}
- fb->id = get_fb_for_bo(fb->bo, drm->addfb2_modifiers);
+ fb->id = get_fb_for_bo(drm, &attribs, fb->handles);
if (!fb->id) {
- wlr_log(WLR_DEBUG, "Failed to import GBM BO in KMS");
- goto error_get_fb_for_bo;
+ wlr_log(WLR_DEBUG, "Failed to import BO in KMS");
+ goto error_bo_handle;
}
+ fb->backend = drm;
fb->wlr_buf = buf;
wlr_addon_init(&fb->addon, &buf->addons, drm, &fb_addon_impl);
@@ -298,23 +339,29 @@ static struct wlr_drm_fb *drm_fb_create(struct wlr_drm_backend *drm,
return fb;
-error_get_fb_for_bo:
- gbm_bo_destroy(fb->bo);
+error_bo_handle:
+ for (int i = 0; i < attribs.n_planes; ++i) {
+ close_bo_handle(drm, fb->handles[i]);
+ }
error_get_dmabuf:
free(fb);
return NULL;
}
void drm_fb_destroy(struct wlr_drm_fb *fb) {
+ struct wlr_drm_backend *drm = fb->backend;
+
wl_list_remove(&fb->link);
wlr_addon_finish(&fb->addon);
- struct gbm_device *gbm = gbm_bo_get_device(fb->bo);
- if (drmModeRmFB(gbm_device_get_fd(gbm), fb->id) != 0) {
+ if (drmModeRmFB(drm->fd, fb->id) != 0) {
wlr_log(WLR_ERROR, "drmModeRmFB failed");
}
- gbm_bo_destroy(fb->bo);
+ for (size_t i = 0; i < sizeof(fb->handles) / sizeof(fb->handles[0]); ++i) {
+ close_bo_handle(drm, fb->handles[i]);
+ }
+
free(fb);
}