diff --git a/backend/drm/drm.c b/backend/drm/drm.c index bd3441ec1..caa589162 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -423,6 +423,11 @@ static bool drm_crtc_commit(struct wlr_drm_connector *conn, if (crtc->cursor != NULL) { drm_fb_move(&crtc->cursor->queued_fb, &crtc->cursor->pending_fb); } + + struct wlr_drm_layer *layer; + wl_list_for_each(layer, &state->base->layers, base.pending.link) { + drm_fb_move(&layer->queued_fb, &layer->pending_fb); + } } else { drm_fb_clear(&crtc->primary->pending_fb); // The set_cursor() hook is a bit special: it's not really synchronized @@ -431,6 +436,11 @@ static bool drm_crtc_commit(struct wlr_drm_connector *conn, // risk ending up in a state where we don't have a cursor FB but // wlr_drm_connector.cursor_enabled is true. // TODO: fix our output interface to avoid this issue. + + struct wlr_drm_layer *layer; + wl_list_for_each(layer, &state->base->layers, base.pending.link) { + drm_fb_clear(&layer->pending_fb); + } } return ok; } @@ -545,6 +555,23 @@ static bool drm_connector_set_pending_fb(struct wlr_drm_connector *conn, return true; } +static void drm_connector_set_pending_layer_fbs(struct wlr_drm_connector *conn, + const struct wlr_output_state *state) { + struct wlr_drm_backend *drm = conn->backend; + + struct wlr_drm_layer *layer; + wl_list_for_each(layer, &state->layers, base.pending.link) { + if (!(layer->base.pending.committed & WLR_OUTPUT_LAYER_STATE_BUFFER)) { + continue; + } + struct wlr_buffer *buffer = layer->base.pending.buffer; + drm_fb_clear(&layer->pending_fb); + if (!drm_fb_import(&layer->pending_fb, drm, buffer, NULL)) { + wlr_drm_conn_log(conn, WLR_DEBUG, "Failed to import layer buffer"); + } + } +} + static bool drm_connector_alloc_crtc(struct wlr_drm_connector *conn); static bool drm_connector_test(struct wlr_output *output) { @@ -602,6 +629,8 @@ static bool drm_connector_test(struct wlr_output *output) { return true; } + drm_connector_set_pending_layer_fbs(conn, pending.base); + if (output->pending.committed & WLR_OUTPUT_STATE_BUFFER) { if (!drm_connector_set_pending_fb(conn, pending.base)) { return false; @@ -659,6 +688,8 @@ bool drm_connector_commit_state(struct wlr_drm_connector *conn, } } + drm_connector_set_pending_layer_fbs(conn, pending.base); + if (pending.base->committed & WLR_OUTPUT_STATE_BUFFER) { if (!drm_connector_set_pending_fb(conn, pending.base)) { return false; @@ -1036,6 +1067,25 @@ static const struct wlr_drm_format_set *drm_connector_get_primary_formats( return &conn->crtc->primary->formats; } +static struct wlr_output_layer *drm_connector_create_layer( + struct wlr_output *wlr_output) { + struct wlr_drm_layer *layer = calloc(1, sizeof(*layer)); + if (layer == NULL) { + return NULL; + } + wlr_output_layer_init(&layer->base, wlr_output); + return &layer->base; +} + +static void drm_connector_destroy_layer(struct wlr_output_layer *wlr_layer) { + struct wlr_drm_layer *layer = (struct wlr_drm_layer *)wlr_layer; + drm_fb_clear(&layer->pending_fb); + drm_fb_clear(&layer->queued_fb); + drm_fb_clear(&layer->current_fb); + liftoff_layer_destroy(layer->liftoff); + free(layer); +} + static const struct wlr_output_impl output_impl = { .set_cursor = drm_connector_set_cursor, .move_cursor = drm_connector_move_cursor, @@ -1046,6 +1096,8 @@ static const struct wlr_output_impl output_impl = { .get_cursor_formats = drm_connector_get_cursor_formats, .get_cursor_size = drm_connector_get_cursor_size, .get_primary_formats = drm_connector_get_primary_formats, + .create_layer = drm_connector_create_layer, + .destroy_layer = drm_connector_destroy_layer, }; bool wlr_output_is_drm(struct wlr_output *output) { @@ -1543,6 +1595,11 @@ static void handle_page_flip(int fd, unsigned seq, &conn->crtc->cursor->queued_fb); } + struct wlr_drm_layer *layer; + wl_list_for_each(layer, &conn->output.layers, base.current.link) { + drm_fb_move(&layer->current_fb, &layer->queued_fb); + } + uint32_t present_flags = WLR_OUTPUT_PRESENT_VSYNC | WLR_OUTPUT_PRESENT_HW_CLOCK | WLR_OUTPUT_PRESENT_HW_COMPLETION; /* Don't report ZERO_COPY in multi-gpu situations, because we had to copy diff --git a/backend/drm/libliftoff.c b/backend/drm/libliftoff.c index 394c4f7f3..1a1d6949e 100644 --- a/backend/drm/libliftoff.c +++ b/backend/drm/libliftoff.c @@ -76,6 +76,37 @@ static bool disable_plane(struct wlr_drm_plane *plane) { return liftoff_layer_set_property(plane->liftoff_layer, "FB_ID", 0) == 0; } +static bool set_layer_props(struct wlr_drm_layer *layer, uint64_t zpos) { + bool ok = liftoff_layer_set_property(layer->liftoff, "zpos", zpos) == 0 && + liftoff_layer_set_property(layer->liftoff, "CRTC_X", layer->base.pending.x) == 0 && + liftoff_layer_set_property(layer->liftoff, "CRTC_Y", layer->base.pending.y) == 0; + + if (layer->base.pending.committed & WLR_OUTPUT_LAYER_STATE_BUFFER) { + struct wlr_buffer *buffer = layer->base.pending.buffer; + uint64_t width = buffer->width; + uint64_t height = buffer->height; + + ok = ok && + liftoff_layer_set_property(layer->liftoff, "SRC_X", 0) == 0 && + liftoff_layer_set_property(layer->liftoff, "SRC_Y", 0) == 0 && + liftoff_layer_set_property(layer->liftoff, "SRC_W", width << 16) == 0 && + liftoff_layer_set_property(layer->liftoff, "SRC_H", height << 16) == 0 && + liftoff_layer_set_property(layer->liftoff, "CRTC_W", width) == 0 && + liftoff_layer_set_property(layer->liftoff, "CRTC_H", height) == 0; + + struct wlr_drm_fb *fb = layer->pending_fb; + if (fb == NULL) { + // We couldn't import the buffer to KMS. Force the layer to be + // composited. + liftoff_layer_set_fb_composited(layer->liftoff); + } else { + ok = ok && liftoff_layer_set_property(layer->liftoff, "FB_ID", fb->id) == 0; + } + } + + return ok; +} + static bool liftoff_crtc_commit(struct wlr_drm_connector *conn, const struct wlr_drm_connector_state *state, uint32_t flags, bool test_only) { @@ -116,17 +147,41 @@ static bool liftoff_crtc_commit(struct wlr_drm_connector *conn, if (crtc->cursor) { if (drm_connector_is_cursor_visible(conn)) { ok = ok && set_plane_props(crtc->cursor, - crtc->cursor->liftoff_layer, - conn->cursor_x, conn->cursor_y, 1); + crtc->cursor->liftoff_layer, conn->cursor_x, conn->cursor_y, + wl_list_length(&state->base->layers) + 1); } else { ok = ok && disable_plane(crtc->cursor); } } + + size_t i = 0; + struct wlr_drm_layer *layer; + wl_list_for_each(layer, &state->base->layers, base.pending.link) { + if (layer->liftoff == NULL) { + layer->liftoff = liftoff_layer_create(crtc->liftoff); + if (layer->liftoff == NULL) { + wlr_log(WLR_ERROR, "Failed to create libliftoff layer"); + continue; + } + } + + layer->base.accepted = false; + ok = ok && set_layer_props(layer, i + 1); + + i++; + } } else { ok = ok && disable_plane(crtc->primary); if (crtc->cursor) { ok = ok && disable_plane(crtc->cursor); } + + struct wlr_drm_layer *layer; + wl_list_for_each(layer, &state->base->layers, base.pending.link) { + if (layer->liftoff != NULL) { + liftoff_layer_set_property(layer->liftoff, "FB_ID", 0); + } + } } if (!ok) { @@ -148,6 +203,13 @@ static bool liftoff_crtc_commit(struct wlr_drm_connector *conn, goto out; } + struct wlr_drm_layer *layer; + wl_list_for_each(layer, &state->base->layers, base.pending.link) { + if (layer->liftoff != NULL) { + layer->base.accepted = !liftoff_layer_needs_composition(layer->liftoff); + } + } + ret = drmModeAtomicCommit(drm->fd, req, flags, drm); if (ret != 0) { wlr_drm_conn_log_errno(conn, test_only ? WLR_DEBUG : WLR_ERROR, diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index 7ea599763..d70ee3e66 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include "backend/drm/iface.h" #include "backend/drm/properties.h" @@ -37,6 +38,18 @@ struct wlr_drm_plane { struct liftoff_layer *liftoff_layer; }; +struct wlr_drm_layer { + struct wlr_output_layer base; + struct liftoff_layer *liftoff; + + /* 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; +}; + struct wlr_drm_crtc { uint32_t id; uint32_t lessee_id;