aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--backend/drm/atomic.c2
-rw-r--r--backend/drm/backend.c9
-rw-r--r--backend/drm/drm.c255
-rw-r--r--backend/drm/renderer.c321
-rw-r--r--backend/drm/util.c8
-rw-r--r--include/backend/drm/drm.h24
-rw-r--r--include/backend/drm/renderer.h56
-rw-r--r--include/backend/drm/util.h3
8 files changed, 395 insertions, 283 deletions
diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c
index 71b18f98..05dd9d1f 100644
--- a/backend/drm/atomic.c
+++ b/backend/drm/atomic.c
@@ -172,7 +172,7 @@ static bool atomic_crtc_set_cursor(struct wlr_drm_backend *drm,
if (bo) {
uint32_t fb_id =
- get_fb_for_bo(bo, plane->drm_format, drm->addfb2_modifiers);
+ get_fb_for_bo(bo, drm->addfb2_modifiers);
set_plane_props(&atom, plane, crtc->id, fb_id, false);
} else {
atomic_add(&atom, plane->id, plane->props.fb_id, 0);
diff --git a/backend/drm/backend.c b/backend/drm/backend.c
index a58af02c..7aeaa01e 100644
--- a/backend/drm/backend.c
+++ b/backend/drm/backend.c
@@ -105,8 +105,13 @@ static void session_signal(struct wl_listener *listener, void *data) {
}
struct wlr_drm_plane *plane = conn->crtc->cursor;
- drm->iface->crtc_set_cursor(drm, conn->crtc,
- (plane && plane->cursor_enabled) ? plane->surf.back : NULL);
+ struct gbm_bo *bo = NULL;
+ if (plane->cursor_enabled) {
+ bo = drm_fb_acquire(&plane->current_fb, drm,
+ &plane->mgpu_surf);
+ }
+
+ drm->iface->crtc_set_cursor(drm, conn->crtc, bo);
drm->iface->crtc_move_cursor(drm, conn->crtc, conn->cursor_x,
conn->cursor_y);
diff --git a/backend/drm/drm.c b/backend/drm/drm.c
index 9be67244..ed74c7d1 100644
--- a/backend/drm/drm.c
+++ b/backend/drm/drm.c
@@ -331,7 +331,37 @@ static struct wlr_drm_connector *get_drm_connector_from_output(
static bool drm_connector_attach_render(struct wlr_output *output,
int *buffer_age) {
struct wlr_drm_connector *conn = get_drm_connector_from_output(output);
- return make_drm_surface_current(&conn->crtc->primary->surf, buffer_age);
+ return drm_surface_make_current(&conn->crtc->primary->surf, buffer_age);
+}
+
+static bool drm_crtc_page_flip(struct wlr_drm_connector *conn,
+ struct wlr_drm_mode *mode) {
+ struct wlr_drm_backend *drm = get_drm_backend_from_backend(conn->output.backend);
+ struct wlr_drm_crtc *crtc = conn->crtc;
+ struct wlr_drm_plane *plane = crtc->primary;
+ struct gbm_bo *bo;
+ uint32_t fb_id;
+ drmModeModeInfo *drm_mode = mode ? &mode->drm_mode : NULL;
+
+ if (conn->pageflip_pending) {
+ wlr_log(WLR_ERROR, "Skipping pageflip on output '%s'", conn->output.name);
+ return false;
+ }
+
+ bo = drm_fb_acquire(&plane->queued_fb, drm, &plane->mgpu_surf);
+ if (!bo) {
+ return false;
+ }
+
+ fb_id = get_fb_for_bo(bo, drm->addfb2_modifiers);
+ if (!drm->iface->crtc_pageflip(drm, conn, crtc, fb_id, drm_mode)) {
+ return false;
+ }
+
+ conn->pageflip_pending = true;
+ drm_fb_move(&crtc->primary->queued_fb, &crtc->primary->pending_fb);
+ wlr_output_update_enabled(&conn->output, true);
+ return true;
}
static uint32_t strip_alpha_channel(uint32_t format) {
@@ -405,76 +435,31 @@ static bool drm_connector_commit_buffer(struct wlr_output *output) {
}
struct wlr_drm_plane *plane = crtc->primary;
- pixman_region32_t *damage = NULL;
- if (output->pending.committed & WLR_OUTPUT_STATE_DAMAGE) {
- damage = &output->pending.damage;
- }
-
- struct gbm_bo *bo;
- uint32_t fb_id = 0;
assert(output->pending.committed & WLR_OUTPUT_STATE_BUFFER);
switch (output->pending.buffer_type) {
case WLR_OUTPUT_STATE_BUFFER_RENDER:
- bo = swap_drm_surface_buffers(&plane->surf, damage);
- if (bo == NULL) {
- wlr_log(WLR_ERROR, "swap_drm_surface_buffers failed");
- return false;
- }
-
- if (drm->parent) {
- bo = copy_drm_surface_mgpu(&plane->mgpu_surf, bo);
- if (bo == NULL) {
- wlr_log(WLR_ERROR, "copy_drm_surface_mgpu failed");
- return false;
- }
- }
- fb_id = get_fb_for_bo(bo, plane->drm_format, drm->addfb2_modifiers);
- if (fb_id == 0) {
- wlr_log(WLR_ERROR, "get_fb_for_bo failed");
+ if (!drm_fb_lock_surface(&plane->pending_fb, &plane->surf)) {
+ wlr_log(WLR_ERROR, "drm_fb_lock_surface failed");
return false;
}
break;
case WLR_OUTPUT_STATE_BUFFER_SCANOUT:;
- struct wlr_dmabuf_attributes attribs;
- if (!wlr_buffer_get_dmabuf(output->pending.buffer, &attribs)) {
- return false;
- }
-
- bo = import_gbm_bo(&drm->renderer, &attribs);
- if (bo == NULL) {
- wlr_log(WLR_ERROR, "import_gbm_bo failed");
+ struct wlr_buffer *buffer = output->pending.buffer;
+ if (!test_buffer(conn, output->pending.buffer)) {
return false;
}
-
- if (conn->pending_bo != NULL) {
- gbm_bo_destroy(conn->pending_bo);
- }
- conn->pending_bo = bo;
-
- fb_id = get_fb_for_bo(bo, gbm_bo_get_format(bo), drm->addfb2_modifiers);
- if (fb_id == 0) {
- wlr_log(WLR_ERROR, "get_fb_for_bo failed");
+ if (!drm_fb_import_wlr(&plane->pending_fb, &drm->renderer, buffer,
+ &crtc->primary->formats)) {
return false;
}
break;
}
- if (conn->pageflip_pending) {
- wlr_log(WLR_ERROR, "Skipping pageflip on output '%s'", conn->output.name);
- return false;
- }
-
- if (!drm->iface->crtc_pageflip(drm, conn, crtc, fb_id, NULL)) {
+ if (!drm_crtc_page_flip(conn, NULL)) {
+ drm_fb_clear(&plane->pending_fb);
return false;
}
- conn->pageflip_pending = true;
- if (output->pending.buffer_type == WLR_OUTPUT_STATE_BUFFER_SCANOUT) {
- wlr_buffer_unlock(conn->pending_buffer);
- conn->pending_buffer = wlr_buffer_lock(output->pending.buffer);
- }
-
- wlr_output_update_enabled(output, true);
return true;
}
@@ -648,37 +633,54 @@ static bool drm_connector_export_dmabuf(struct wlr_output *output,
struct wlr_dmabuf_attributes *attribs) {
struct wlr_drm_connector *conn = get_drm_connector_from_output(output);
struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend);
+ struct wlr_drm_crtc *crtc = conn->crtc;
if (!drm->session->active) {
return false;
}
- struct wlr_drm_crtc *crtc = conn->crtc;
if (!crtc) {
return false;
}
+
struct wlr_drm_plane *plane = crtc->primary;
- struct wlr_drm_surface *surf = &plane->surf;
- return export_drm_bo(surf->back, attribs);
+ if (plane->current_fb.type == WLR_DRM_FB_TYPE_NONE) {
+ return false;
+ }
+
+ return export_drm_bo(plane->current_fb.bo, attribs);
+}
+
+struct wlr_drm_fb *plane_get_next_fb(struct wlr_drm_plane *plane) {
+ if (plane->pending_fb.type != WLR_DRM_FB_TYPE_NONE) {
+ return &plane->pending_fb;
+ }
+ if (plane->queued_fb.type != WLR_DRM_FB_TYPE_NONE) {
+ return &plane->queued_fb;
+ }
+ return &plane->current_fb;
}
static bool drm_connector_pageflip_renderer(struct wlr_drm_connector *conn,
struct wlr_drm_mode *mode) {
- struct wlr_drm_backend *drm =
- get_drm_backend_from_backend(conn->output.backend);
struct wlr_drm_crtc *crtc = conn->crtc;
if (!crtc) {
wlr_log(WLR_ERROR, "Page-flip failed on connector '%s': no CRTC",
conn->output.name);
return false;
}
+
+ // drm_crtc_page_flip expects a FB to be available
struct wlr_drm_plane *plane = crtc->primary;
+ if (plane_get_next_fb(plane)->type == WLR_DRM_FB_TYPE_NONE) {
+ drm_surface_render_black_frame(&plane->surf);
+ if (!drm_fb_lock_surface(&plane->pending_fb, &plane->surf)) {
+ return false;
+ }
+ }
- struct gbm_bo *bo = get_drm_surface_front(
- drm->parent ? &plane->mgpu_surf : &plane->surf);
- uint32_t fb_id = get_fb_for_bo(bo, plane->drm_format, drm->addfb2_modifiers);
- return drm->iface->crtc_pageflip(drm, conn, crtc, fb_id, &mode->drm_mode);
+ return drm_crtc_page_flip(conn, mode);
}
static void drm_connector_start_renderer(struct wlr_drm_connector *conn) {
@@ -689,10 +691,7 @@ static void drm_connector_start_renderer(struct wlr_drm_connector *conn) {
wlr_log(WLR_DEBUG, "Starting renderer on output '%s'", conn->output.name);
struct wlr_drm_mode *mode = (struct wlr_drm_mode *)conn->output.current_mode;
- if (drm_connector_pageflip_renderer(conn, mode)) {
- conn->pageflip_pending = true;
- wlr_output_update_enabled(&conn->output, true);
- } else {
+ if (!drm_connector_pageflip_renderer(conn, mode)) {
wl_event_source_timer_update(conn->retry_pageflip,
1000000.0f / conn->output.current_mode->refresh);
}
@@ -731,7 +730,7 @@ static bool drm_connector_init_renderer(struct wlr_drm_connector *conn,
modifiers = false;
}
- if (!init_drm_plane_surfaces(plane, drm, width, height, format, modifiers) ||
+ if (!drm_plane_init_surface(plane, drm, width, height, format, 0, modifiers) ||
!drm_connector_pageflip_renderer(conn, mode)) {
if (!modifiers) {
wlr_log(WLR_ERROR, "Failed to initialize renderer "
@@ -742,14 +741,12 @@ static bool drm_connector_init_renderer(struct wlr_drm_connector *conn,
// If page-flipping with modifiers enabled doesn't work, retry without
// modifiers
- finish_drm_surface(&plane->surf);
- finish_drm_surface(&plane->mgpu_surf);
wlr_log(WLR_INFO, "Page-flip failed with primary FB modifiers enabled, "
"retrying without modifiers");
modifiers = false;
- if (!init_drm_plane_surfaces(plane, drm, width, height, format,
- modifiers)) {
+ if (!drm_plane_init_surface(plane, drm, width, height, format,
+ 0, modifiers)) {
return false;
}
if (!drm_connector_pageflip_renderer(conn, mode)) {
@@ -908,8 +905,8 @@ static bool drm_connector_set_cursor(struct wlr_output *output,
int32_t hotspot_x, int32_t hotspot_y, bool update_texture) {
struct wlr_drm_connector *conn = get_drm_connector_from_output(output);
struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend);
-
struct wlr_drm_crtc *crtc = conn->crtc;
+
if (!crtc) {
return false;
}
@@ -933,31 +930,15 @@ static bool drm_connector_set_cursor(struct wlr_output *output,
ret = drmGetCap(drm->fd, DRM_CAP_CURSOR_HEIGHT, &h);
h = ret ? 64 : h;
- if (!drm->parent) {
- if (!init_drm_surface(&plane->surf, &drm->renderer, w, h,
- drm->renderer.gbm_format, NULL,
- GBM_BO_USE_LINEAR | GBM_BO_USE_SCANOUT)) {
- wlr_log(WLR_ERROR, "Cannot allocate cursor resources");
- return false;
- }
- } else {
- if (!init_drm_surface(&plane->surf, &drm->parent->renderer, w, h,
- drm->parent->renderer.gbm_format, NULL,
- GBM_BO_USE_LINEAR)) {
- wlr_log(WLR_ERROR, "Cannot allocate cursor resources");
- return false;
- }
-
- if (!init_drm_surface(&plane->mgpu_surf, &drm->renderer, w, h,
- drm->renderer.gbm_format, NULL,
- GBM_BO_USE_LINEAR | GBM_BO_USE_SCANOUT)) {
- wlr_log(WLR_ERROR, "Cannot allocate cursor resources");
- return false;
- }
+ if (!drm_plane_init_surface(plane, drm, w, h,
+ DRM_FORMAT_ARGB8888, GBM_BO_USE_LINEAR, false)) {
+ wlr_log(WLR_ERROR, "Cannot allocate cursor resources");
+ return false;
}
}
- wlr_matrix_projection(plane->matrix, plane->surf.width,
+ float hotspot_proj[9];
+ wlr_matrix_projection(hotspot_proj, plane->surf.width,
plane->surf.height, output->transform);
struct wlr_box hotspot = { .x = hotspot_x, .y = hotspot_y };
@@ -999,21 +980,23 @@ static bool drm_connector_set_cursor(struct wlr_output *output,
return false;
}
- make_drm_surface_current(&plane->surf, NULL);
+ drm_surface_make_current(&plane->surf, NULL);
struct wlr_renderer *rend = plane->surf.renderer->wlr_rend;
struct wlr_box cursor_box = { .width = width, .height = height };
float matrix[9];
- wlr_matrix_project_box(matrix, &cursor_box, transform, 0, plane->matrix);
+ wlr_matrix_project_box(matrix, &cursor_box, transform, 0, hotspot_proj);
wlr_renderer_begin(rend, plane->surf.width, plane->surf.height);
wlr_renderer_clear(rend, (float[]){ 0.0, 0.0, 0.0, 0.0 });
wlr_render_texture_with_matrix(rend, texture, matrix, 1.0);
wlr_renderer_end(rend);
- swap_drm_surface_buffers(&plane->surf, NULL);
+ if (!drm_fb_lock_surface(&plane->pending_fb, &plane->surf)) {
+ return false;
+ }
plane->cursor_enabled = true;
}
@@ -1022,25 +1005,30 @@ static bool drm_connector_set_cursor(struct wlr_output *output,
return true; // will be committed when session is resumed
}
- struct gbm_bo *bo = plane->cursor_enabled ? plane->surf.back : NULL;
- if (bo && drm->parent) {
- bo = copy_drm_surface_mgpu(&plane->mgpu_surf, bo);
- }
+ struct gbm_bo *bo = NULL;
+
+ if (plane->cursor_enabled) {
+ bo = drm_fb_acquire(&plane->pending_fb, drm, &plane->mgpu_surf);
+
+ wlr_log(WLR_DEBUG, "SET_CURSOR %p %p", plane->pending_fb.bo, bo);
- if (bo) {
- // workaround for nouveau
- // Buffers created with GBM_BO_USER_LINEAR are placed in NOUVEAU_GEM_DOMAIN_GART.
- // When the bo is attached to the cursor plane it is moved to NOUVEAU_GEM_DOMAIN_VRAM.
- // However, this does not wait for the render operations to complete, leaving an empty surface.
- // see https://bugs.freedesktop.org/show_bug.cgi?id=109631
- // The render operations can be waited for using:
+ /* Workaround for nouveau buffers created with GBM_BO_USER_LINEAR are
+ * placed in NOUVEAU_GEM_DOMAIN_GART. When the bo is attached to the
+ * cursor plane it is moved to NOUVEAU_GEM_DOMAIN_VRAM. However, this
+ * does not wait for the render operations to complete, leaving an
+ * empty surface. See
+ * https://gitlab.freedesktop.org/xorg/driver/xf86-video-nouveau/issues/480
+ * The render operations can be waited for using:
+ */
glFinish();
}
- bool ok = drm->iface->crtc_set_cursor(drm, crtc, bo);
- if (ok) {
- wlr_output_update_needs_frame(output);
+
+ if (!drm->iface->crtc_set_cursor(drm, crtc, bo)) {
+ return false;
}
- return ok;
+
+ wlr_output_update_needs_frame(output);
+ return true;
}
static bool drm_connector_move_cursor(struct wlr_output *output,
@@ -1133,8 +1121,8 @@ static void dealloc_crtc(struct wlr_drm_connector *conn) {
conn->crtc - drm->crtcs, conn->output.name);
set_drm_connector_gamma(&conn->output, 0, NULL, NULL, NULL);
- finish_drm_surface(&conn->crtc->primary->surf);
- finish_drm_surface(&conn->crtc->cursor->surf);
+ drm_plane_finish_surface(conn->crtc->primary);
+ drm_plane_finish_surface(conn->crtc->cursor);
drm->iface->conn_enable(drm, conn, false);
@@ -1544,15 +1532,15 @@ static int mhz_to_nsec(int mhz) {
static void page_flip_handler(int fd, unsigned seq,
unsigned tv_sec, unsigned tv_usec, unsigned crtc_id, void *data) {
struct wlr_drm_backend *drm = data;
+
struct wlr_drm_connector *conn = NULL;
struct wlr_drm_connector *search;
-
wl_list_for_each(search, &drm->outputs, link) {
if (search->crtc && search->crtc->id == crtc_id) {
conn = search;
+ break;
}
}
-
if (!conn) {
wlr_log(WLR_DEBUG, "No connector for crtc_id %u", crtc_id);
return;
@@ -1564,27 +1552,24 @@ static void page_flip_handler(int fd, unsigned seq,
return;
}
- // Release the old buffer as it's not displayed anymore. The pending
- // buffer becomes the current buffer.
- wlr_buffer_unlock(conn->current_buffer);
- conn->current_buffer = conn->pending_buffer;
- conn->pending_buffer = NULL;
-
- if (conn->current_bo != NULL) {
- gbm_bo_destroy(conn->current_bo);
+ struct wlr_drm_plane *plane = conn->crtc->primary;
+ if (plane->queued_fb.type != WLR_DRM_FB_TYPE_NONE) {
+ drm_fb_move(&plane->current_fb, &plane->queued_fb);
+ }
+ if (conn->crtc->cursor &&
+ conn->crtc->cursor->queued_fb.type != WLR_DRM_FB_TYPE_NONE) {
+ drm_fb_move(&conn->crtc->cursor->current_fb,
+ &conn->crtc->cursor->queued_fb);
}
- conn->current_bo = conn->pending_bo;
- conn->pending_bo = NULL;
uint32_t present_flags = WLR_OUTPUT_PRESENT_VSYNC |
WLR_OUTPUT_PRESENT_HW_CLOCK | WLR_OUTPUT_PRESENT_HW_COMPLETION;
- if (conn->current_buffer != NULL) {
+ /* Don't report ZERO_COPY in multi-gpu situations, because we had to copy
+ * data between the GPUs, even if we were using the direct scanout
+ * interface.
+ */
+ if (!drm->parent && plane->current_fb.type == WLR_DRM_FB_TYPE_WLR_BUFFER) {
present_flags |= WLR_OUTPUT_PRESENT_ZERO_COPY;
- } else {
- post_drm_surface(&conn->crtc->primary->surf);
- if (drm->parent) {
- post_drm_surface(&conn->crtc->primary->mgpu_surf);
- }
}
struct timespec present_time = {
@@ -1687,10 +1672,6 @@ static void drm_connector_cleanup(struct wlr_drm_connector *conn) {
conn->output.needs_frame = false;
conn->output.frame_pending = false;
- wlr_buffer_unlock(conn->pending_buffer);
- wlr_buffer_unlock(conn->current_buffer);
- conn->pending_buffer = conn->current_buffer = NULL;
-
/* Fallthrough */
case WLR_DRM_CONN_NEEDS_MODESET:
wlr_log(WLR_INFO, "Emitting destruction signal for '%s'",
diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c
index 8b295db1..4a16732c 100644
--- a/backend/drm/renderer.c
+++ b/backend/drm/renderer.c
@@ -3,6 +3,7 @@
#include <gbm.h>
#include <stdbool.h>
#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
#include <wayland-util.h>
#include <wlr/render/egl.h>
@@ -61,7 +62,7 @@ void finish_drm_renderer(struct wlr_drm_renderer *renderer) {
gbm_device_destroy(renderer->gbm);
}
-bool init_drm_surface(struct wlr_drm_surface *surf,
+static bool init_drm_surface(struct wlr_drm_surface *surf,
struct wlr_drm_renderer *renderer, uint32_t width, uint32_t height,
uint32_t format, struct wlr_drm_format_set *set, uint32_t flags) {
if (surf->width == width && surf->height == height) {
@@ -73,14 +74,6 @@ bool init_drm_surface(struct wlr_drm_surface *surf,
surf->height = height;
if (surf->gbm) {
- if (surf->front) {
- gbm_surface_release_buffer(surf->gbm, surf->front);
- surf->front = NULL;
- }
- if (surf->back) {
- gbm_surface_release_buffer(surf->gbm, surf->back);
- surf->back = NULL;
- }
gbm_surface_destroy(surf->gbm);
surf->gbm = NULL;
}
@@ -119,18 +112,11 @@ error_zero:
return false;
}
-void finish_drm_surface(struct wlr_drm_surface *surf) {
+static void finish_drm_surface(struct wlr_drm_surface *surf) {
if (!surf || !surf->renderer) {
return;
}
- if (surf->front) {
- gbm_surface_release_buffer(surf->gbm, surf->front);
- }
- if (surf->back) {
- gbm_surface_release_buffer(surf->gbm, surf->back);
- }
-
wlr_egl_destroy_surface(&surf->renderer->egl, surf->egl);
if (surf->gbm) {
gbm_surface_destroy(surf->gbm);
@@ -139,82 +125,11 @@ void finish_drm_surface(struct wlr_drm_surface *surf) {
memset(surf, 0, sizeof(*surf));
}
-bool make_drm_surface_current(struct wlr_drm_surface *surf,
+bool drm_surface_make_current(struct wlr_drm_surface *surf,
int *buffer_damage) {
return wlr_egl_make_current(&surf->renderer->egl, surf->egl, buffer_damage);
}
-struct gbm_bo *swap_drm_surface_buffers(struct wlr_drm_surface *surf,
- pixman_region32_t *damage) {
- if (surf->front) {
- gbm_surface_release_buffer(surf->gbm, surf->front);
- }
-
- wlr_egl_swap_buffers(&surf->renderer->egl, surf->egl, damage);
-
- surf->front = surf->back;
- surf->back = gbm_surface_lock_front_buffer(surf->gbm);
- return surf->back;
-}
-
-struct gbm_bo *get_drm_surface_front(struct wlr_drm_surface *surf) {
- if (surf->front) {
- return surf->front;
- }
-
- make_drm_surface_current(surf, NULL);
- struct wlr_renderer *renderer = surf->renderer->wlr_rend;
- wlr_renderer_begin(renderer, surf->width, surf->height);
- wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 1.0 });
- wlr_renderer_end(renderer);
- return swap_drm_surface_buffers(surf, NULL);
-}
-
-void post_drm_surface(struct wlr_drm_surface *surf) {
- if (surf->front) {
- gbm_surface_release_buffer(surf->gbm, surf->front);
- surf->front = NULL;
- }
-}
-
-struct gbm_bo *import_gbm_bo(struct wlr_drm_renderer *renderer,
- struct wlr_dmabuf_attributes *attribs) {
- if (attribs->modifier == DRM_FORMAT_MOD_INVALID && attribs->n_planes == 1
- && attribs->offset[0] == 0) {
- 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(renderer->gbm, GBM_BO_IMPORT_FD,
- &data, GBM_BO_USE_SCANOUT);
- } else {
- 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 NULL;
- }
-
- 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(renderer->gbm, GBM_BO_IMPORT_FD_MODIFIER,
- &data, GBM_BO_USE_SCANOUT);
- }
-}
-
bool export_drm_bo(struct gbm_bo *bo, struct wlr_dmabuf_attributes *attribs) {
memset(attribs, 0, sizeof(struct wlr_dmabuf_attributes));
@@ -268,46 +183,234 @@ static struct wlr_texture *get_tex_for_bo(struct wlr_drm_renderer *renderer,
return tex;
}
-struct gbm_bo *copy_drm_surface_mgpu(struct wlr_drm_surface *dest,
- struct gbm_bo *src) {
- make_drm_surface_current(dest, NULL);
-
- struct wlr_texture *tex = get_tex_for_bo(dest->renderer, src);
- assert(tex);
-
- float mat[9];
- wlr_matrix_projection(mat, 1, 1, WL_OUTPUT_TRANSFORM_NORMAL);
+void drm_plane_finish_surface(struct wlr_drm_plane *plane) {
+ if (!plane) {
+ return;
+ }
- struct wlr_renderer *renderer = dest->renderer->wlr_rend;
- wlr_renderer_begin(renderer, dest->width, dest->height);
- wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 0.0 });
- wlr_render_texture_with_matrix(renderer, tex, mat, 1.0f);
- wlr_renderer_end(renderer);
+ drm_fb_clear(&plane->pending_fb);
+ drm_fb_clear(&plane->queued_fb);
+ drm_fb_clear(&plane->current_fb);
- return swap_drm_surface_buffers(dest, NULL);
+ finish_drm_surface(&plane->surf);
+ finish_drm_surface(&plane->mgpu_surf);
}
-bool init_drm_plane_surfaces(struct wlr_drm_plane *plane,
+bool drm_plane_init_surface(struct wlr_drm_plane *plane,
struct wlr_drm_backend *drm, int32_t width, uint32_t height,
- uint32_t format, bool with_modifiers) {
+ uint32_t format, uint32_t flags, bool with_modifiers) {
struct wlr_drm_format_set *format_set =
with_modifiers ? &plane->formats : NULL;
+ drm_plane_finish_surface(plane);
+
if (!drm->parent) {
return init_drm_surface(&plane->surf, &drm->renderer, width, height,
- format, format_set, GBM_BO_USE_SCANOUT);
+ format, format_set, flags | GBM_BO_USE_SCANOUT);
}
if (!init_drm_surface(&plane->surf, &drm->parent->renderer,
- width, height, format, NULL, GBM_BO_USE_LINEAR)) {
+ width, height, format, NULL,
+ flags | GBM_BO_USE_LINEAR)) {
return false;
}
if (!init_drm_surface(&plane->mgpu_surf, &drm->renderer,
- width, height, format, format_set, GBM_BO_USE_SCANOUT)) {
+ width, height, format, format_set,
+ flags | GBM_BO_USE_SCANOUT)) {
finish_drm_surface(&plane->surf);
return false;
}
return true;
}
+
+void drm_fb_clear(struct wlr_drm_fb *fb) {
+ switch (fb->type) {
+ case WLR_DRM_FB_TYPE_NONE:
+ assert(!fb->bo);
+ break;
+ case WLR_DRM_FB_TYPE_SURFACE:
+ gbm_surface_release_buffer(fb->surf->gbm, fb->bo);
+ break;
+ case WLR_DRM_FB_TYPE_WLR_BUFFER:
+ gbm_bo_destroy(fb->bo);
+ wlr_buffer_unlock(fb->wlr_buf);
+ fb->wlr_buf = NULL;
+ break;
+ }
+
+ fb->type = WLR_DRM_FB_TYPE_NONE;
+ fb->bo = NULL;
+
+ if (fb->mgpu_bo) {
+ assert(fb->mgpu_surf);
+ gbm_surface_release_buffer(fb->mgpu_surf->gbm, fb->mgpu_bo);
+ fb->mgpu_bo = NULL;
+ fb->mgpu_surf = NULL;
+ }
+}
+
+bool drm_fb_lock_surface(struct wlr_drm_fb *fb, struct wlr_drm_surface *surf) {
+ drm_fb_clear(fb);
+
+ if (!wlr_egl_swap_buffers(&surf->renderer->egl, surf->egl, NULL)) {
+ wlr_log(WLR_ERROR, "Failed to swap buffers");
+ return false;
+ }
+
+ fb->bo = gbm_surface_lock_front_buffer(surf->gbm);
+ if (!fb->bo) {
+ wlr_log(WLR_ERROR, "Failed to lock front buffer");
+ return false;
+ }
+
+ fb->type = WLR_DRM_FB_TYPE_SURFACE;
+ fb->surf = surf;
+ return true;
+}
+
+static uint32_t strip_alpha_channel(uint32_t format) {
+ switch (format) {
+ case DRM_FORMAT_ARGB8888:
+ return DRM_FORMAT_XRGB8888;
+ default:
+ return DRM_FORMAT_INVALID;
+ }
+}
+
+bool drm_fb_import_wlr(struct wlr_drm_fb *fb, struct wlr_drm_renderer *renderer,
+ struct wlr_buffer *buf, struct wlr_drm_format_set *set) {
+ drm_fb_clear(fb);
+
+ struct wlr_dmabuf_attributes attribs;
+ if (!wlr_buffer_get_dmabuf(buf, &attribs)) {
+ return false;
+ }
+
+ if (!wlr_drm_format_set_has(set, attribs.format, attribs.modifier)) {
+ // The format isn't supported by the plane. Try stripping the alpha
+ // channel, if any.
+ uint32_t format = strip_alpha_channel(attribs.format);
+ if (wlr_drm_format_set_has(set, format, attribs.modifier)) {
+ attribs.format = format;
+ } else {
+ return false;
+ }
+ }
+
+ 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];
+ }
+
+ fb->bo = gbm_bo_import(renderer->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,
+ };
+
+ fb->bo = gbm_bo_import(renderer->gbm, GBM_BO_IMPORT_FD,
+ &data, GBM_BO_USE_SCANOUT);
+ }
+
+ if (!fb->bo) {
+ return false;
+ }
+
+ fb->type = WLR_DRM_FB_TYPE_WLR_BUFFER;
+ fb->wlr_buf = wlr_buffer_lock(buf);
+
+ return true;
+}
+
+void drm_fb_move(struct wlr_drm_fb *new, struct wlr_drm_fb *old) {
+ drm_fb_clear(new);
+
+ *new = *old;
+ memset(old, 0, sizeof(*old));
+}
+
+bool drm_surface_render_black_frame(struct wlr_drm_surface *surf) {
+ struct wlr_renderer *renderer = surf->renderer->wlr_rend;
+
+ if (!drm_surface_make_current(surf, NULL)) {
+ return false;
+ }
+
+ wlr_renderer_begin(renderer, surf->width, surf->height);
+ wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 1.0 });
+ wlr_renderer_end(renderer);
+ return true;
+}
+
+struct gbm_bo *drm_fb_acquire(struct wlr_drm_fb *fb, struct wlr_drm_backend *drm,
+ struct wlr_drm_surface *mgpu) {
+ if (!fb->bo) {
+ wlr_log(WLR_ERROR, "Tried to acquire an FB with a NULL BO");
+ return NULL;
+ }
+
+ if (!drm->parent) {
+ return fb->bo;
+ }
+
+ if (fb->mgpu_bo) {
+ return fb->mgpu_bo;
+ }
+
+ /* Perform copy across GPUs */
+
+ struct wlr_renderer *renderer = mgpu->renderer->wlr_rend;
+
+ if (!drm_surface_make_current(mgpu, NULL)) {
+ return NULL;
+ }
+
+ struct wlr_texture *tex = get_tex_for_bo(mgpu->renderer, fb->bo);
+ if (!tex) {
+ return NULL;
+ }
+
+ float mat[9];
+ wlr_matrix_projection(mat, 1, 1, WL_OUTPUT_TRANSFORM_NORMAL);
+
+ wlr_renderer_begin(renderer, mgpu->width, mgpu->height);
+ wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 0.0 });
+ wlr_render_texture_with_matrix(renderer, tex, mat, 1.0f);
+ wlr_renderer_end(renderer);
+
+ if (!wlr_egl_swap_buffers(&mgpu->renderer->egl, mgpu->egl, NULL)) {
+ wlr_log(WLR_ERROR, "Failed to swap buffers");
+ return NULL;
+ }
+
+ fb->mgpu_bo = gbm_surface_lock_front_buffer(mgpu->gbm);
+ if (!fb->mgpu_bo) {
+ wlr_log(WLR_ERROR, "Failed to lock front buffer");
+ return NULL;
+ }
+
+ fb->mgpu_surf = mgpu;
+ return fb->mgpu_bo;
+}
diff --git a/backend/drm/util.c b/backend/drm/util.c
index f8a344d1..86d66be4 100644
--- a/backend/drm/util.c
+++ b/backend/drm/util.c
@@ -179,8 +179,7 @@ static void free_fb(struct gbm_bo *bo, void *data) {
}
}
-uint32_t get_fb_for_bo(struct gbm_bo *bo, uint32_t drm_format,
- bool with_modifiers) {
+uint32_t get_fb_for_bo(struct gbm_bo *bo, bool with_modifiers) {
uint32_t id = (uintptr_t)gbm_bo_get_user_data(bo);
if (id) {
return id;
@@ -191,6 +190,7 @@ uint32_t get_fb_for_bo(struct gbm_bo *bo, uint32_t drm_format,
int fd = gbm_device_get_fd(gbm);
uint32_t width = gbm_bo_get_width(bo);
uint32_t height = gbm_bo_get_height(bo);
+ uint32_t format = gbm_bo_get_format(bo);
uint32_t handles[4] = {0};
uint32_t strides[4] = {0};
@@ -205,12 +205,12 @@ uint32_t get_fb_for_bo(struct gbm_bo *bo, uint32_t drm_format,
}
if (with_modifiers && gbm_bo_get_modifier(bo) != DRM_FORMAT_MOD_INVALID) {
- if (drmModeAddFB2WithModifiers(fd, width, height, drm_format, handles,
+ if (drmModeAddFB2WithModifiers(fd, width, height, format, handles,
strides, offsets, modifiers, &id, DRM_MODE_FB_MODIFIERS)) {
wlr_log_errno(WLR_ERROR, "Unable to add DRM framebuffer");
}
} else {
- if (drmModeAddFB2(fd, width, height, drm_format, handles, strides,
+ if (drmModeAddFB2(fd, width, height, format, handles, strides,
offsets, &id, 0)) {
wlr_log_errno(WLR_ERROR, "Unable to add DRM framebuffer");
}
diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h
index ffd3138f..0ab4ff21 100644
--- a/include/backend/drm/drm.h
+++ b/include/backend/drm/drm.h
@@ -25,11 +25,17 @@ struct wlr_drm_plane {
struct wlr_drm_surface surf;
struct wlr_drm_surface mgpu_surf;
+ /* Buffer to be submitted to the kernel on the next page-flip */
+ struct wlr_drm_fb pending_fb;
+ /* Buffer submitted to the kernel, will be presented on next vblank */
+ struct wlr_drm_fb queued_fb;
+ /* Buffer currently displayed on screen */
+ struct wlr_drm_fb current_fb;
+
uint32_t drm_format; // ARGB8888 or XRGB8888
struct wlr_drm_format_set formats;
// Only used by cursor
- float matrix[9];
bool cursor_enabled;
int32_t cursor_hotspot_x, cursor_hotspot_y;
@@ -124,16 +130,16 @@ struct wlr_drm_connector {
drmModeCrtc *old_crtc;
- bool pageflip_pending;
struct wl_event_source *retry_pageflip;
struct wl_list link;
- // Buffer submitted to the kernel but not yet displayed
- struct wlr_buffer *pending_buffer;
- struct gbm_bo *pending_bo;
- // Buffer currently being displayed
- struct wlr_buffer *current_buffer;
- struct gbm_bo *current_bo;
+ /*
+ * We've asked for a state change in the kernel, and yet to recieve a
+ * notification for its completion. Currently, the kernel only has a
+ * queue length of 1, and no way to modify your submissions after
+ * they're sent.
+ */
+ bool pageflip_pending;
};
struct wlr_drm_backend *get_drm_backend_from_backend(
@@ -150,6 +156,8 @@ bool set_drm_connector_gamma(struct wlr_output *output, size_t size,
bool drm_connector_set_mode(struct wlr_output *output,
struct wlr_output_mode *mode);
+struct wlr_drm_fb *plane_get_next_fb(struct wlr_drm_plane *plane);
+
bool legacy_crtc_set_cursor(struct wlr_drm_backend *drm,
struct wlr_drm_crtc *crtc, struct gbm_bo *bo);
bool legacy_crtc_move_cursor(struct wlr_drm_backend *drm,
diff --git a/include/backend/drm/renderer.h b/include/backend/drm/renderer.h
index 567cf95e..bfccf9d5 100644
--- a/include/backend/drm/renderer.h
+++ b/include/backend/drm/renderer.h
@@ -10,6 +10,7 @@
struct wlr_drm_backend;
struct wlr_drm_plane;
+struct wlr_buffer;
struct wlr_drm_renderer {
int fd;
@@ -29,33 +30,48 @@ struct wlr_drm_surface {
struct gbm_surface *gbm;
EGLSurface egl;
+};
+
+enum wlr_drm_fb_type {
+ WLR_DRM_FB_TYPE_NONE,
+ WLR_DRM_FB_TYPE_SURFACE,
+ WLR_DRM_FB_TYPE_WLR_BUFFER
+};
+
+struct wlr_drm_fb {
+ enum wlr_drm_fb_type type;
+ struct gbm_bo *bo;
+
+ struct wlr_drm_surface *mgpu_surf;
+ struct gbm_bo *mgpu_bo;
- struct gbm_bo *front;
- struct gbm_bo *back;
+ union {
+ struct wlr_drm_surface *surf;
+ struct wlr_buffer *wlr_buf;
+ };
};
bool init_drm_renderer(struct wlr_drm_backend *drm,
struct wlr_drm_renderer *renderer, wlr_renderer_create_func_t create_render);
void finish_drm_renderer(struct wlr_drm_renderer *renderer);
-bool init_drm_surface(struct wlr_drm_surface *surf,
- struct wlr_drm_renderer *renderer, uint32_t width, uint32_t height,
- uint32_t format, struct wlr_drm_format_set *set, uint32_t flags);
-
-bool init_drm_plane_surfaces(struct wlr_drm_plane *plane,
- struct wlr_drm_backend *drm, int32_t width, uint32_t height,
- uint32_t format, bool with_modifiers);
-
-void finish_drm_surface(struct wlr_drm_surface *surf);
-bool make_drm_surface_current(struct wlr_drm_surface *surf, int *buffer_age);
-struct gbm_bo *swap_drm_surface_buffers(struct wlr_drm_surface *surf,
- pixman_region32_t *damage);
-struct gbm_bo *get_drm_surface_front(struct wlr_drm_surface *surf);
-void post_drm_surface(struct wlr_drm_surface *surf);
-struct gbm_bo *copy_drm_surface_mgpu(struct wlr_drm_surface *dest,
- struct gbm_bo *src);
-struct gbm_bo *import_gbm_bo(struct wlr_drm_renderer *renderer,
- struct wlr_dmabuf_attributes *attribs);
+bool drm_surface_make_current(struct wlr_drm_surface *surf, int *buffer_age);
bool export_drm_bo(struct gbm_bo *bo, struct wlr_dmabuf_attributes *attribs);
+void drm_fb_clear(struct wlr_drm_fb *fb);
+bool drm_fb_lock_surface(struct wlr_drm_fb *fb, struct wlr_drm_surface *surf);
+bool drm_fb_import_wlr(struct wlr_drm_fb *fb, struct wlr_drm_renderer *renderer,
+ struct wlr_buffer *buf, struct wlr_drm_format_set *set);
+
+void drm_fb_move(struct wlr_drm_fb *new, struct wlr_drm_fb *old);
+
+bool drm_surface_render_black_frame(struct wlr_drm_surface *surf);
+struct gbm_bo *drm_fb_acquire(struct wlr_drm_fb *fb, struct wlr_drm_backend *drm,
+ struct wlr_drm_surface *mgpu);
+
+bool drm_plane_init_surface(struct wlr_drm_plane *plane,
+ struct wlr_drm_backend *drm, int32_t width, uint32_t height,
+ uint32_t format, uint32_t flags, bool with_modifiers);
+void drm_plane_finish_surface(struct wlr_drm_plane *plane);
+
#endif
diff --git a/include/backend/drm/util.h b/include/backend/drm/util.h
index 2d9d11f4..15895ec6 100644
--- a/include/backend/drm/util.h
+++ b/include/backend/drm/util.h
@@ -14,8 +14,7 @@ void parse_edid(struct wlr_output *restrict output, size_t len,
// Returns the string representation of a DRM output type
const char *conn_get_name(uint32_t type_id);
// Returns the DRM framebuffer id for a gbm_bo
-uint32_t get_fb_for_bo(struct gbm_bo *bo, uint32_t drm_format,
- bool with_modifiers);
+uint32_t get_fb_for_bo(struct gbm_bo *bo, bool with_modifiers);
// Part of match_obj
enum {