diff options
-rw-r--r-- | backend/drm/atomic.c | 1 | ||||
-rw-r--r-- | backend/drm/backend.c | 1 | ||||
-rw-r--r-- | backend/drm/drm.c | 1 | ||||
-rw-r--r-- | backend/drm/fb.c | 251 | ||||
-rw-r--r-- | backend/drm/legacy.c | 1 | ||||
-rw-r--r-- | backend/drm/libliftoff.c | 1 | ||||
-rw-r--r-- | backend/drm/meson.build | 1 | ||||
-rw-r--r-- | backend/drm/renderer.c | 250 | ||||
-rw-r--r-- | include/backend/drm/fb.h | 23 | ||||
-rw-r--r-- | include/backend/drm/renderer.h | 17 |
10 files changed, 282 insertions, 265 deletions
diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index 618a9f43..530ee254 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -6,6 +6,7 @@ #include <xf86drm.h> #include <xf86drmMode.h> #include "backend/drm/drm.h" +#include "backend/drm/fb.h" #include "backend/drm/iface.h" #include "backend/drm/util.h" diff --git a/backend/drm/backend.c b/backend/drm/backend.c index 40597dae..4697234d 100644 --- a/backend/drm/backend.c +++ b/backend/drm/backend.c @@ -12,6 +12,7 @@ #include <wlr/util/log.h> #include <xf86drm.h> #include "backend/drm/drm.h" +#include "backend/drm/fb.h" struct wlr_drm_backend *get_drm_backend_from_backend( struct wlr_backend *wlr_backend) { diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 47126dce..41b4ea96 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -22,6 +22,7 @@ #include <xf86drm.h> #include <xf86drmMode.h> #include "backend/drm/drm.h" +#include "backend/drm/fb.h" #include "backend/drm/iface.h" #include "backend/drm/util.h" #include "render/pixel_format.h" diff --git a/backend/drm/fb.c b/backend/drm/fb.c new file mode 100644 index 00000000..24d80583 --- /dev/null +++ b/backend/drm/fb.c @@ -0,0 +1,251 @@ +#include <drm_fourcc.h> +#include <stdlib.h> +#include <wlr/render/drm_format_set.h> +#include <wlr/types/wlr_buffer.h> +#include <wlr/util/addon.h> +#include <wlr/util/log.h> +#include <xf86drm.h> +#include <xf86drmMode.h> +#include "backend/drm/drm.h" +#include "backend/drm/fb.h" +#include "render/pixel_format.h" + +void drm_fb_clear(struct wlr_drm_fb **fb_ptr) { + if (*fb_ptr == NULL) { + return; + } + + struct wlr_drm_fb *fb = *fb_ptr; + wlr_buffer_unlock(fb->wlr_buf); // may destroy the buffer + + *fb_ptr = NULL; +} + +struct wlr_drm_fb *drm_fb_lock(struct wlr_drm_fb *fb) { + wlr_buffer_lock(fb->wlr_buf); + return fb; +} + +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); +} + +static const struct wlr_addon_interface fb_addon_impl = { + .name = "wlr_drm_fb", + .destroy = drm_fb_handle_destroy, +}; + +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 { + if (dmabuf->modifier != DRM_FORMAT_MOD_INVALID && + dmabuf->modifier != DRM_FORMAT_MOD_LINEAR) { + wlr_log(WLR_ERROR, "Cannot import DRM framebuffer with explicit " + "modifier 0x%"PRIX64, dmabuf->modifier); + return 0; + } + + 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 void close_all_bo_handles(struct wlr_drm_backend *drm, + uint32_t handles[static 4]) { + for (int i = 0; i < 4; ++i) { + if (handles[i] == 0) { + continue; + } + + // If multiple planes share the same BO handle, avoid double-closing it + bool already_closed = false; + for (int j = 0; j < i; ++j) { + if (handles[i] == handles[j]) { + already_closed = true; + break; + } + } + if (already_closed) { + continue; + } + + if (drmCloseBufferHandle(drm->fd, handles[i]) != 0) { + wlr_log_errno(WLR_ERROR, "drmCloseBufferHandle failed"); + } + } +} + +static void drm_poisoned_fb_handle_destroy(struct wlr_addon *addon) { + wlr_addon_finish(addon); + free(addon); +} + +static const struct wlr_addon_interface poisoned_fb_addon_impl = { + .name = "wlr_drm_poisoned_fb", + .destroy = drm_poisoned_fb_handle_destroy, +}; + +static bool is_buffer_poisoned(struct wlr_drm_backend *drm, + struct wlr_buffer *buf) { + return wlr_addon_find(&buf->addons, drm, &poisoned_fb_addon_impl) != NULL; +} + +/** + * Mark the buffer as "poisoned", ie. it cannot be imported into KMS. This + * allows us to avoid repeatedly trying to import it when it's not + * scanout-capable. + */ +static void poison_buffer(struct wlr_drm_backend *drm, + struct wlr_buffer *buf) { + struct wlr_addon *addon = calloc(1, sizeof(*addon)); + if (addon == NULL) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + return; + } + wlr_addon_init(addon, &buf->addons, drm, &poisoned_fb_addon_impl); + wlr_log(WLR_DEBUG, "Poisoning buffer"); +} + +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_dmabuf_attributes attribs; + if (!wlr_buffer_get_dmabuf(buf, &attribs)) { + wlr_log(WLR_DEBUG, "Failed to get DMA-BUF from buffer"); + return NULL; + } + + if (is_buffer_poisoned(drm, buf)) { + wlr_log(WLR_DEBUG, "Buffer is poisoned"); + return NULL; + } + + struct wlr_drm_fb *fb = calloc(1, sizeof(*fb)); + if (!fb) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + return NULL; + } + + if (formats && !wlr_drm_format_set_has(formats, attribs.format, + attribs.modifier)) { + // The format isn't supported by the plane. Try stripping the alpha + // channel, if any. + const struct wlr_pixel_format_info *info = + drm_get_pixel_format_info(attribs.format); + if (info != NULL && info->opaque_substitute != DRM_FORMAT_INVALID && + wlr_drm_format_set_has(formats, info->opaque_substitute, attribs.modifier)) { + attribs.format = info->opaque_substitute; + } else { + wlr_log(WLR_DEBUG, "Buffer format 0x%"PRIX32" with modifier " + "0x%"PRIX64" cannot be scanned out", + attribs.format, attribs.modifier); + goto error_fb; + } + } + + uint32_t handles[4] = {0}; + for (int i = 0; i < attribs.n_planes; ++i) { + int ret = drmPrimeFDToHandle(drm->fd, attribs.fd[i], &handles[i]); + if (ret != 0) { + wlr_log_errno(WLR_DEBUG, "drmPrimeFDToHandle failed"); + goto error_bo_handle; + } + } + + fb->id = get_fb_for_bo(drm, &attribs, handles); + if (!fb->id) { + wlr_log(WLR_DEBUG, "Failed to import BO in KMS"); + poison_buffer(drm, buf); + goto error_bo_handle; + } + + close_all_bo_handles(drm, handles); + + fb->backend = drm; + fb->wlr_buf = buf; + + wlr_addon_init(&fb->addon, &buf->addons, drm, &fb_addon_impl); + wl_list_insert(&drm->fbs, &fb->link); + + return fb; + +error_bo_handle: + close_all_bo_handles(drm, handles); +error_fb: + 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); + + int ret = drmModeCloseFB(drm->fd, fb->id); + if (ret == -EINVAL) { + ret = drmModeRmFB(drm->fd, fb->id); + } + if (ret != 0) { + wlr_log(WLR_ERROR, "Failed to close FB: %s", strerror(-ret)); + } + + free(fb); +} + +bool drm_fb_import(struct wlr_drm_fb **fb_ptr, struct wlr_drm_backend *drm, + struct wlr_buffer *buf, const struct wlr_drm_format_set *formats) { + struct wlr_drm_fb *fb; + struct wlr_addon *addon = wlr_addon_find(&buf->addons, drm, &fb_addon_impl); + if (addon != NULL) { + fb = wl_container_of(addon, fb, addon); + } else { + fb = drm_fb_create(drm, buf, formats); + if (!fb) { + return false; + } + } + + wlr_buffer_lock(buf); + drm_fb_move(fb_ptr, &fb); + return true; +} + +void drm_fb_move(struct wlr_drm_fb **new, struct wlr_drm_fb **old) { + drm_fb_clear(new); + *new = *old; + *old = NULL; +} diff --git a/backend/drm/legacy.c b/backend/drm/legacy.c index 117fb9e5..1c9ff46d 100644 --- a/backend/drm/legacy.c +++ b/backend/drm/legacy.c @@ -4,6 +4,7 @@ #include <xf86drm.h> #include <xf86drmMode.h> #include "backend/drm/drm.h" +#include "backend/drm/fb.h" #include "backend/drm/iface.h" #include "backend/drm/util.h" diff --git a/backend/drm/libliftoff.c b/backend/drm/libliftoff.c index cafae807..b53c1571 100644 --- a/backend/drm/libliftoff.c +++ b/backend/drm/libliftoff.c @@ -6,6 +6,7 @@ #include <wlr/util/log.h> #include "backend/drm/drm.h" +#include "backend/drm/fb.h" #include "backend/drm/iface.h" static bool init(struct wlr_drm_backend *drm) { diff --git a/backend/drm/meson.build b/backend/drm/meson.build index 6fcb2c15..5d2f2b1f 100644 --- a/backend/drm/meson.build +++ b/backend/drm/meson.build @@ -38,6 +38,7 @@ wlr_files += files( 'atomic.c', 'backend.c', 'drm.c', + 'fb.c', 'legacy.c', 'monitor.c', 'properties.c', diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c index 29903f19..6619d6e9 100644 --- a/backend/drm/renderer.c +++ b/backend/drm/renderer.c @@ -1,17 +1,11 @@ -#define _POSIX_C_SOURCE 200809L #include <assert.h> #include <drm_fourcc.h> -#include <fcntl.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <wayland-util.h> #include <wlr/render/swapchain.h> #include <wlr/render/wlr_renderer.h> #include <wlr/util/log.h> #include "backend/drm/drm.h" -#include "backend/drm/util.h" +#include "backend/drm/fb.h" +#include "backend/drm/renderer.h" #include "render/drm_format_set.h" #include "render/allocator/allocator.h" #include "render/pixel_format.h" @@ -188,243 +182,3 @@ bool drm_plane_pick_render_format(struct wlr_drm_plane *plane, return true; } - -void drm_fb_clear(struct wlr_drm_fb **fb_ptr) { - if (*fb_ptr == NULL) { - return; - } - - struct wlr_drm_fb *fb = *fb_ptr; - wlr_buffer_unlock(fb->wlr_buf); // may destroy the buffer - - *fb_ptr = NULL; -} - -struct wlr_drm_fb *drm_fb_lock(struct wlr_drm_fb *fb) { - wlr_buffer_lock(fb->wlr_buf); - return fb; -} - -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); -} - -static const struct wlr_addon_interface fb_addon_impl = { - .name = "wlr_drm_fb", - .destroy = drm_fb_handle_destroy, -}; - -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 { - if (dmabuf->modifier != DRM_FORMAT_MOD_INVALID && - dmabuf->modifier != DRM_FORMAT_MOD_LINEAR) { - wlr_log(WLR_ERROR, "Cannot import DRM framebuffer with explicit " - "modifier 0x%"PRIX64, dmabuf->modifier); - return 0; - } - - 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 void close_all_bo_handles(struct wlr_drm_backend *drm, - uint32_t handles[static 4]) { - for (int i = 0; i < 4; ++i) { - if (handles[i] == 0) { - continue; - } - - // If multiple planes share the same BO handle, avoid double-closing it - bool already_closed = false; - for (int j = 0; j < i; ++j) { - if (handles[i] == handles[j]) { - already_closed = true; - break; - } - } - if (already_closed) { - continue; - } - - if (drmCloseBufferHandle(drm->fd, handles[i]) != 0) { - wlr_log_errno(WLR_ERROR, "drmCloseBufferHandle failed"); - } - } -} - -static void drm_poisoned_fb_handle_destroy(struct wlr_addon *addon) { - wlr_addon_finish(addon); - free(addon); -} - -static const struct wlr_addon_interface poisoned_fb_addon_impl = { - .name = "wlr_drm_poisoned_fb", - .destroy = drm_poisoned_fb_handle_destroy, -}; - -static bool is_buffer_poisoned(struct wlr_drm_backend *drm, - struct wlr_buffer *buf) { - return wlr_addon_find(&buf->addons, drm, &poisoned_fb_addon_impl) != NULL; -} - -/** - * Mark the buffer as "poisoned", ie. it cannot be imported into KMS. This - * allows us to avoid repeatedly trying to import it when it's not - * scanout-capable. - */ -static void poison_buffer(struct wlr_drm_backend *drm, - struct wlr_buffer *buf) { - struct wlr_addon *addon = calloc(1, sizeof(*addon)); - if (addon == NULL) { - wlr_log_errno(WLR_ERROR, "Allocation failed"); - return; - } - wlr_addon_init(addon, &buf->addons, drm, &poisoned_fb_addon_impl); - wlr_log(WLR_DEBUG, "Poisoning buffer"); -} - -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_dmabuf_attributes attribs; - if (!wlr_buffer_get_dmabuf(buf, &attribs)) { - wlr_log(WLR_DEBUG, "Failed to get DMA-BUF from buffer"); - return NULL; - } - - if (is_buffer_poisoned(drm, buf)) { - wlr_log(WLR_DEBUG, "Buffer is poisoned"); - return NULL; - } - - struct wlr_drm_fb *fb = calloc(1, sizeof(*fb)); - if (!fb) { - wlr_log_errno(WLR_ERROR, "Allocation failed"); - return NULL; - } - - if (formats && !wlr_drm_format_set_has(formats, attribs.format, - attribs.modifier)) { - // The format isn't supported by the plane. Try stripping the alpha - // channel, if any. - const struct wlr_pixel_format_info *info = - drm_get_pixel_format_info(attribs.format); - if (info != NULL && info->opaque_substitute != DRM_FORMAT_INVALID && - wlr_drm_format_set_has(formats, info->opaque_substitute, attribs.modifier)) { - attribs.format = info->opaque_substitute; - } else { - wlr_log(WLR_DEBUG, "Buffer format 0x%"PRIX32" with modifier " - "0x%"PRIX64" cannot be scanned out", - attribs.format, attribs.modifier); - goto error_fb; - } - } - - uint32_t handles[4] = {0}; - for (int i = 0; i < attribs.n_planes; ++i) { - int ret = drmPrimeFDToHandle(drm->fd, attribs.fd[i], &handles[i]); - if (ret != 0) { - wlr_log_errno(WLR_DEBUG, "drmPrimeFDToHandle failed"); - goto error_bo_handle; - } - } - - fb->id = get_fb_for_bo(drm, &attribs, handles); - if (!fb->id) { - wlr_log(WLR_DEBUG, "Failed to import BO in KMS"); - poison_buffer(drm, buf); - goto error_bo_handle; - } - - close_all_bo_handles(drm, handles); - - fb->backend = drm; - fb->wlr_buf = buf; - - wlr_addon_init(&fb->addon, &buf->addons, drm, &fb_addon_impl); - wl_list_insert(&drm->fbs, &fb->link); - - return fb; - -error_bo_handle: - close_all_bo_handles(drm, handles); -error_fb: - 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); - - int ret = drmModeCloseFB(drm->fd, fb->id); - if (ret == -EINVAL) { - ret = drmModeRmFB(drm->fd, fb->id); - } - if (ret != 0) { - wlr_log(WLR_ERROR, "Failed to close FB: %s", strerror(-ret)); - } - - free(fb); -} - -bool drm_fb_import(struct wlr_drm_fb **fb_ptr, struct wlr_drm_backend *drm, - struct wlr_buffer *buf, const struct wlr_drm_format_set *formats) { - struct wlr_drm_fb *fb; - struct wlr_addon *addon = wlr_addon_find(&buf->addons, drm, &fb_addon_impl); - if (addon != NULL) { - fb = wl_container_of(addon, fb, addon); - } else { - fb = drm_fb_create(drm, buf, formats); - if (!fb) { - return false; - } - } - - wlr_buffer_lock(buf); - drm_fb_move(fb_ptr, &fb); - return true; -} - -void drm_fb_move(struct wlr_drm_fb **new, struct wlr_drm_fb **old) { - drm_fb_clear(new); - *new = *old; - *old = NULL; -} diff --git a/include/backend/drm/fb.h b/include/backend/drm/fb.h new file mode 100644 index 00000000..7d451bc6 --- /dev/null +++ b/include/backend/drm/fb.h @@ -0,0 +1,23 @@ +#ifndef BACKEND_DRM_FB_H +#define BACKEND_DRM_FB_H + +#include <stdbool.h> + +struct wlr_drm_fb { + struct wlr_buffer *wlr_buf; + struct wlr_addon addon; + struct wlr_drm_backend *backend; + struct wl_list link; // wlr_drm_backend.fbs + + uint32_t id; +}; + +bool drm_fb_import(struct wlr_drm_fb **fb, struct wlr_drm_backend *drm, + struct wlr_buffer *buf, const struct wlr_drm_format_set *formats); +void drm_fb_destroy(struct wlr_drm_fb *fb); + +void drm_fb_clear(struct wlr_drm_fb **fb); +void drm_fb_move(struct wlr_drm_fb **new, struct wlr_drm_fb **old); +struct wlr_drm_fb *drm_fb_lock(struct wlr_drm_fb *fb); + +#endif diff --git a/include/backend/drm/renderer.h b/include/backend/drm/renderer.h index d29fe2ad..c39d4c15 100644 --- a/include/backend/drm/renderer.h +++ b/include/backend/drm/renderer.h @@ -24,15 +24,6 @@ struct wlr_drm_surface { struct wlr_swapchain *swapchain; }; -struct wlr_drm_fb { - struct wlr_buffer *wlr_buf; - struct wlr_addon addon; - struct wlr_drm_backend *backend; - struct wl_list link; // wlr_drm_backend.fbs - - uint32_t id; -}; - bool init_drm_renderer(struct wlr_drm_backend *drm, struct wlr_drm_renderer *renderer); void finish_drm_renderer(struct wlr_drm_renderer *renderer); @@ -41,14 +32,6 @@ bool init_drm_surface(struct wlr_drm_surface *surf, struct wlr_drm_renderer *renderer, int width, int height, const struct wlr_drm_format *drm_format); -bool drm_fb_import(struct wlr_drm_fb **fb, struct wlr_drm_backend *drm, - struct wlr_buffer *buf, const struct wlr_drm_format_set *formats); -void drm_fb_destroy(struct wlr_drm_fb *fb); - -void drm_fb_clear(struct wlr_drm_fb **fb); -void drm_fb_move(struct wlr_drm_fb **new, struct wlr_drm_fb **old); -struct wlr_drm_fb *drm_fb_lock(struct wlr_drm_fb *fb); - struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf, struct wlr_buffer *buffer); |