diff --git a/backend/drm/drm.c b/backend/drm/drm.c index b0b36a4eb..109669401 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -349,6 +349,7 @@ static void layer_handle_addon_destroy(struct wlr_addon *addon) { drm_fb_clear(&layer->pending_fb); drm_fb_clear(&layer->queued_fb); drm_fb_clear(&layer->current_fb); + free(layer->candidate_planes); free(layer); } @@ -381,6 +382,8 @@ static struct wlr_drm_layer *get_or_create_layer(struct wlr_drm_backend *drm, return NULL; } + layer->wlr = wlr_layer; + #if HAVE_LIBLIFTOFF layer->liftoff = liftoff_layer_create(crtc->liftoff); if (layer->liftoff == NULL) { @@ -391,6 +394,15 @@ static struct wlr_drm_layer *get_or_create_layer(struct wlr_drm_backend *drm, abort(); // unreachable #endif + layer->candidate_planes = calloc(sizeof(bool), drm->num_planes); + if (layer->candidate_planes == NULL) { +#if HAVE_LIBLIFTOFF + liftoff_layer_destroy(layer->liftoff); +#endif + free(layer); + return NULL; + } + wlr_addon_init(&layer->addon, &wlr_layer->addons, drm, &layer_impl); wl_list_insert(&crtc->layers, &layer->link); diff --git a/backend/drm/libliftoff.c b/backend/drm/libliftoff.c index 1ba75416e..812ce7f87 100644 --- a/backend/drm/libliftoff.c +++ b/backend/drm/libliftoff.c @@ -1,6 +1,7 @@ #define _POSIX_C_SOURCE 200809L #include #include +#include #include #include @@ -214,6 +215,62 @@ static bool set_layer_props(struct wlr_drm_backend *drm, liftoff_layer_set_property(layer->liftoff, "SRC_H", src_h) == 0; } +static bool devid_from_fd(int fd, dev_t *devid) { + struct stat stat; + if (fstat(fd, &stat) != 0) { + wlr_log_errno(WLR_ERROR, "fstat failed"); + return false; + } + *devid = stat.st_rdev; + return true; +} + +static void update_layer_feedback(struct wlr_drm_backend *drm, + struct wlr_drm_layer *layer) { + bool changed = false; + for (size_t i = 0; i < drm->num_planes; i++) { + struct wlr_drm_plane *plane = &drm->planes[i]; + bool is_candidate = liftoff_layer_is_candidate_plane(layer->liftoff, + plane->liftoff); + if (layer->candidate_planes[i] != is_candidate) { + layer->candidate_planes[i] = is_candidate; + changed = true; + } + } + if (!changed) { + return; + } + + dev_t target_device; + if (!devid_from_fd(drm->fd, &target_device)) { + return; + } + + struct wlr_drm_format_set formats = {0}; + for (size_t i = 0; i < drm->num_planes; i++) { + struct wlr_drm_plane *plane = &drm->planes[i]; + if (!layer->candidate_planes[i]) { + continue; + } + + for (size_t j = 0; j < plane->formats.len; j++) { + const struct wlr_drm_format *format = plane->formats.formats[j]; + for (size_t k = 0; k < format->len; k++) { + wlr_drm_format_set_add(&formats, format->format, + format->modifiers[k]); + } + } + } + + struct wlr_output_layer_feedback_event event = { + .target_device = target_device, + .formats = &formats, + }; + wl_signal_emit_mutable(&layer->wlr->events.feedback, &event); + + wlr_drm_format_set_finish(&formats); +} + static bool crtc_commit(struct wlr_drm_connector *conn, const struct wlr_drm_connector_state *state, uint32_t flags, bool test_only) { @@ -380,6 +437,9 @@ static bool crtc_commit(struct wlr_drm_connector *conn, struct wlr_drm_layer *layer = get_drm_layer(drm, layer_state->layer); layer_state->accepted = !liftoff_layer_needs_composition(layer->liftoff); + if (!test_only && !layer_state->accepted) { + update_layer_feedback(drm, layer); + } } } diff --git a/backend/drm/meson.build b/backend/drm/meson.build index 8d1c18457..3fb011c1e 100644 --- a/backend/drm/meson.build +++ b/backend/drm/meson.build @@ -7,7 +7,7 @@ hwdata = dependency( libliftoff = dependency( 'libliftoff', - version: '>=0.2.0', + version: '>=0.4.0', fallback: 'libliftoff', required: false, ) diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index 36e72b7a9..3b895db33 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -38,6 +38,7 @@ struct wlr_drm_plane { }; struct wlr_drm_layer { + struct wlr_output_layer *wlr; struct liftoff_layer *liftoff; struct wlr_addon addon; // wlr_output_layer.addons struct wl_list link; // wlr_drm_crtc.layers @@ -50,6 +51,9 @@ struct wlr_drm_layer { struct wlr_drm_fb *current_fb; int pending_width, pending_height; + + // One entry per wlr_drm_backend.planes + bool *candidate_planes; }; struct wlr_drm_crtc {