From fd0a4dc40e69ad9ba68e7935ae613af0666ccce3 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 7 Dec 2025 00:12:46 +0100 Subject: [PATCH 1/7] backend/drm: enumerate plane color pipelines --- backend/drm/drm.c | 80 ++++++++++++++++++++++++++++++++ backend/drm/properties.c | 18 +++++++ include/backend/drm/drm.h | 11 +++++ include/backend/drm/properties.h | 12 +++++ 4 files changed, 121 insertions(+) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index d2f75f71f..979cd8e78 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -158,6 +158,68 @@ static bool init_plane_cursor_sizes(struct wlr_drm_plane *plane, return true; } +static bool init_color_pipeline(struct wlr_drm_backend *drm, + uint32_t head_id, struct wl_list *list) { + uint32_t id = head_id; + while (id != 0) { + struct wlr_drm_colorop *colorop = calloc(1, sizeof(*colorop)); + if (colorop == NULL) { + return false; + } + + colorop->id = id; + wl_list_insert(list->prev, &colorop->link); + + if (!get_drm_colorop_props(drm->fd, id, &colorop->props)) { + return false; + } + + uint64_t type = 0, next = 0; + if (!get_drm_prop(drm->fd, id, colorop->props.type, &type) || + !get_drm_prop(drm->fd, id, colorop->props.next, &next)) { + return false; + } + + colorop->type = (uint32_t)type; + id = (uint32_t)next; + } + + return true; +} + +static bool init_plane_color_pipelines(struct wlr_drm_backend *drm, + struct wlr_drm_plane *plane) { + if (plane->props.color_pipeline == 0) { + return true; + } + + drmModePropertyRes *prop = drmModeGetProperty(drm->fd, plane->props.color_pipeline); + if (prop == NULL) { + return false; + } + + plane->color_pipelines = calloc(prop->count_enums, sizeof(plane->color_pipelines[0])); + if (plane->color_pipelines == NULL) { + goto error; + } + + for (int i = 0; i < prop->count_enums; i++) { + wl_list_init(&plane->color_pipelines[i]); + plane->color_pipelines_len++; + + if (!init_color_pipeline(drm, prop->enums[i].value, &plane->color_pipelines[i])) { + goto error; + } + } + + drmModeFreeProperty(prop); + return true; + +error: + drmModeFreeProperty(prop); + return false; +} + static bool init_plane(struct wlr_drm_backend *drm, struct wlr_drm_plane *p, const drmModePlane *drm_plane) { uint32_t id = drm_plane->plane_id; @@ -240,6 +302,10 @@ static bool init_plane(struct wlr_drm_backend *drm, } } + if (!init_plane_color_pipelines(drm, p)) { + return false; + } + assert(drm->num_crtcs <= 32); for (size_t j = 0; j < drm->num_crtcs; j++) { uint32_t crtc_bit = 1 << j; @@ -383,6 +449,14 @@ static void drm_plane_finish_surface(struct wlr_drm_plane *plane) { finish_drm_surface(&plane->mgpu_surf); } +static void finish_color_pipeline(struct wl_list *list) { + struct wlr_drm_colorop *colorop, *tmp; + wl_list_for_each_safe(colorop, tmp, list, link) { + wl_list_remove(&colorop->link); + free(colorop); + } +} + void finish_drm_resources(struct wlr_drm_backend *drm) { if (!drm) { return; @@ -407,9 +481,15 @@ void finish_drm_resources(struct wlr_drm_backend *drm) { for (size_t i = 0; i < drm->num_planes; ++i) { struct wlr_drm_plane *plane = &drm->planes[i]; + drm_plane_finish_surface(plane); wlr_drm_format_set_finish(&plane->formats); free(plane->cursor_sizes); + + for (size_t j = 0; j < plane->color_pipelines_len; j++) { + finish_color_pipeline(&plane->color_pipelines[j]); + } + free(plane->color_pipelines); } free(drm->planes); diff --git a/backend/drm/properties.c b/backend/drm/properties.c index 4c6bdcb36..75881f69b 100644 --- a/backend/drm/properties.c +++ b/backend/drm/properties.c @@ -51,6 +51,7 @@ static const struct prop_info crtc_info[] = { static const struct prop_info plane_info[] = { #define INDEX(name) (offsetof(struct wlr_drm_plane_props, name) / sizeof(uint32_t)) { "COLOR_ENCODING", INDEX(color_encoding) }, + { "COLOR_PIPELINE", INDEX(color_pipeline) }, { "COLOR_RANGE", INDEX(color_range) }, { "CRTC_H", INDEX(crtc_h) }, { "CRTC_ID", INDEX(crtc_id) }, @@ -73,6 +74,18 @@ static const struct prop_info plane_info[] = { #undef INDEX }; +static const struct prop_info colorop_info[] = { +#define INDEX(name) (offsetof(struct wlr_drm_colorop_props, name) / sizeof(uint32_t)) + { "BYPASS", INDEX(bypass) }, + { "CURVE_1D_TYPE", INDEX(curve_1d_type) }, + { "DATA", INDEX(data) }, + { "MULTIPLIER", INDEX(multiplier) }, + { "NEXT", INDEX(next) }, + { "SIZE", INDEX(size) }, + { "TYPE", INDEX(type) }, +#undef INDEX +}; + static int cmp_prop_info(const void *arg1, const void *arg2) { const char *key = arg1; const struct prop_info *elem = arg2; @@ -123,6 +136,11 @@ bool get_drm_plane_props(int fd, uint32_t id, struct wlr_drm_plane_props *out) { plane_info, sizeof(plane_info) / sizeof(plane_info[0])); } +bool get_drm_colorop_props(int fd, uint32_t id, struct wlr_drm_colorop_props *out) { + return scan_properties(fd, id, DRM_MODE_OBJECT_COLOROP, (uint32_t *)out, + colorop_info, sizeof(colorop_info) / sizeof(colorop_info[0])); +} + bool get_drm_prop(int fd, uint32_t obj, uint32_t prop, uint64_t *ret) { drmModeObjectProperties *props = drmModeObjectGetProperties(fd, obj, DRM_MODE_OBJECT_ANY); diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index a8c5e077a..05c065ced 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -20,6 +20,14 @@ struct wlr_drm_viewport { struct wlr_box dst_box; }; +struct wlr_drm_colorop { + uint32_t id; + uint32_t type; // enum drm_colorop_type + + struct wlr_drm_colorop_props props; + struct wl_list link; // wlr_drm_plane.color_pipelines +}; + struct wlr_drm_plane { uint32_t type; uint32_t id; @@ -43,6 +51,9 @@ struct wlr_drm_plane { struct wlr_output_cursor_size *cursor_sizes; size_t cursor_sizes_len; + struct wl_list *color_pipelines; // wlr_drm_colorop.link + size_t color_pipelines_len; + struct wlr_drm_plane_props props; uint32_t initial_crtc_id; diff --git a/include/backend/drm/properties.h b/include/backend/drm/properties.h index 20e0a5bff..09fb00881 100644 --- a/include/backend/drm/properties.h +++ b/include/backend/drm/properties.h @@ -68,6 +68,7 @@ struct wlr_drm_plane_props { uint32_t color_encoding; // Not guaranteed to exist uint32_t color_range; // Not guaranteed to exist + uint32_t color_pipeline; // Not guaranteed to exist }; // Equivalent to wlr_drm_color_encoding defined in the kernel (but not exported) @@ -83,10 +84,21 @@ enum wlr_drm_color_range { WLR_DRM_COLOR_YCBCR_LIMITED_RANGE, }; +struct wlr_drm_colorop_props { + uint32_t type; + uint32_t next; + uint32_t bypass; + uint32_t data; // for 1D_LUT, CTM_3X4, 3D_LUT + uint32_t size; // for 1D_LUT, 3D_LUT + uint32_t curve_1d_type; // for 1D_CURVE + uint32_t multiplier; // for MULTIPLIER +}; + bool get_drm_connector_props(int fd, uint32_t id, struct wlr_drm_connector_props *out); bool get_drm_crtc_props(int fd, uint32_t id, struct wlr_drm_crtc_props *out); bool get_drm_plane_props(int fd, uint32_t id, struct wlr_drm_plane_props *out); +bool get_drm_colorop_props(int fd, uint32_t id, struct wlr_drm_colorop_props *out); bool get_drm_prop(int fd, uint32_t obj, uint32_t prop, uint64_t *ret); void *get_drm_prop_blob(int fd, uint32_t obj, uint32_t prop, size_t *ret_len); From 03712270ef96cb5c2ee5adaf8c2954ae2a487d11 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 31 May 2026 22:41:18 +0200 Subject: [PATCH 2/7] output: rename color_transform to post_color_transform We'll soon introduce pre_color_transform for the pre-blending counterpart. --- backend/drm/atomic.c | 6 +++--- backend/drm/drm.c | 6 +++--- backend/drm/legacy.c | 6 +++--- include/wlr/types/wlr_output.h | 11 ++++++----- types/output/output.c | 20 ++++++++++---------- types/output/state.c | 20 ++++++++++---------- types/scene/wlr_scene.c | 2 +- types/wlr_gamma_control_v1.c | 2 +- 8 files changed, 37 insertions(+), 36 deletions(-) diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index daa8ba9bf..abccf4c38 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -332,12 +332,12 @@ bool drm_atomic_connector_prepare(struct wlr_drm_connector_state *state, bool mo } uint32_t gamma_lut = crtc->gamma_lut; - if (state->base->committed & WLR_OUTPUT_STATE_COLOR_TRANSFORM) { + if (state->base->committed & WLR_OUTPUT_STATE_POST_COLOR_TRANSFORM) { size_t dim = 0; uint16_t *lut = NULL; - if (state->base->color_transform != NULL) { + if (state->base->post_color_transform != NULL) { struct wlr_color_transform_lut_3x1d *tr = - color_transform_lut_3x1d_from_base(state->base->color_transform); + color_transform_lut_3x1d_from_base(state->base->post_color_transform); dim = tr->dim; lut = tr->lut_3x1d; } diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 979cd8e78..76decd758 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -42,7 +42,7 @@ static const uint32_t COMMIT_OUTPUT_STATE = WLR_OUTPUT_STATE_LAYERS | WLR_OUTPUT_STATE_WAIT_TIMELINE | WLR_OUTPUT_STATE_SIGNAL_TIMELINE | - WLR_OUTPUT_STATE_COLOR_TRANSFORM | + WLR_OUTPUT_STATE_POST_COLOR_TRANSFORM | WLR_OUTPUT_STATE_IMAGE_DESCRIPTION | WLR_OUTPUT_STATE_COLOR_REPRESENTATION; @@ -960,8 +960,8 @@ static bool drm_connector_prepare(struct wlr_drm_connector_state *conn_state, bo } } - if ((state->committed & WLR_OUTPUT_STATE_COLOR_TRANSFORM) && state->color_transform != NULL && - state->color_transform->type != COLOR_TRANSFORM_LUT_3X1D) { + if ((state->committed & WLR_OUTPUT_STATE_POST_COLOR_TRANSFORM) && state->post_color_transform != NULL && + state->post_color_transform->type != COLOR_TRANSFORM_LUT_3X1D) { wlr_drm_conn_log(conn, WLR_DEBUG, "Only 3x1D LUT color transforms are supported"); return false; diff --git a/backend/drm/legacy.c b/backend/drm/legacy.c index 223852ec1..96fe19883 100644 --- a/backend/drm/legacy.c +++ b/backend/drm/legacy.c @@ -125,12 +125,12 @@ static bool legacy_crtc_commit(const struct wlr_drm_connector_state *state, } } - if (state->base->committed & WLR_OUTPUT_STATE_COLOR_TRANSFORM) { + if (state->base->committed & WLR_OUTPUT_STATE_POST_COLOR_TRANSFORM) { size_t dim = 0; uint16_t *lut = NULL; - if (state->base->color_transform != NULL) { + if (state->base->post_color_transform != NULL) { struct wlr_color_transform_lut_3x1d *tr = - color_transform_lut_3x1d_from_base(state->base->color_transform); + color_transform_lut_3x1d_from_base(state->base->post_color_transform); dim = tr->dim; lut = tr->lut_3x1d; } diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index c8e44b0e6..103e57555 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -75,7 +75,7 @@ enum wlr_output_state_field { WLR_OUTPUT_STATE_LAYERS = 1 << 9, WLR_OUTPUT_STATE_WAIT_TIMELINE = 1 << 10, WLR_OUTPUT_STATE_SIGNAL_TIMELINE = 1 << 11, - WLR_OUTPUT_STATE_COLOR_TRANSFORM = 1 << 12, + WLR_OUTPUT_STATE_POST_COLOR_TRANSFORM = 1 << 12, WLR_OUTPUT_STATE_IMAGE_DESCRIPTION = 1 << 13, WLR_OUTPUT_STATE_COLOR_REPRESENTATION = 1 << 14, }; @@ -162,7 +162,8 @@ struct wlr_output_state { struct wlr_drm_syncobj_timeline *signal_timeline; uint64_t signal_point; - struct wlr_color_transform *color_transform; + // Post-blending color transform + struct wlr_color_transform *post_color_transform; struct wlr_output_image_description *image_description; }; @@ -276,7 +277,7 @@ struct wlr_output { struct { struct wl_listener display_destroy; struct wlr_output_image_description image_description_value; - struct wlr_color_transform *color_transform; + struct wlr_color_transform *post_color_transform; struct wlr_color_primaries default_primaries_value; } WLR_PRIVATE; }; @@ -619,11 +620,11 @@ void wlr_output_state_set_wait_timeline(struct wlr_output_state *state, void wlr_output_state_set_signal_timeline(struct wlr_output_state *state, struct wlr_drm_syncobj_timeline *timeline, uint64_t dst_point); /** - * Set the color transform for an output. + * Set the post-blending color transform for an output. * * The color transform is applied after blending output layers. */ -void wlr_output_state_set_color_transform(struct wlr_output_state *state, +void wlr_output_state_set_post_color_transform(struct wlr_output_state *state, struct wlr_color_transform *tr); /** diff --git a/types/output/output.c b/types/output/output.c index 46da1f425..3f13eec2f 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -252,12 +252,12 @@ static void output_apply_state(struct wlr_output *output, } } - if (state->committed & WLR_OUTPUT_STATE_COLOR_TRANSFORM) { - wlr_color_transform_unref(output->color_transform); - if (state->color_transform != NULL) { - output->color_transform = wlr_color_transform_ref(state->color_transform); + if (state->committed & WLR_OUTPUT_STATE_POST_COLOR_TRANSFORM) { + wlr_color_transform_unref(output->post_color_transform); + if (state->post_color_transform != NULL) { + output->post_color_transform = wlr_color_transform_ref(state->post_color_transform); } else { - output->color_transform = NULL; + output->post_color_transform = NULL; } } @@ -426,7 +426,7 @@ void wlr_output_finish(struct wlr_output *output) { wlr_swapchain_destroy(output->cursor_swapchain); wlr_buffer_unlock(output->cursor_front_buffer); - wlr_color_transform_unref(output->color_transform); + wlr_color_transform_unref(output->post_color_transform); wlr_swapchain_destroy(output->swapchain); @@ -581,9 +581,9 @@ static uint32_t output_compare_state(struct wlr_output *output, output->subpixel == state->subpixel) { fields |= WLR_OUTPUT_STATE_SUBPIXEL; } - if ((state->committed & WLR_OUTPUT_STATE_COLOR_TRANSFORM) && - output->color_transform == state->color_transform) { - fields |= WLR_OUTPUT_STATE_COLOR_TRANSFORM; + if ((state->committed & WLR_OUTPUT_STATE_POST_COLOR_TRANSFORM) && + output->post_color_transform == state->post_color_transform) { + fields |= WLR_OUTPUT_STATE_POST_COLOR_TRANSFORM; } if ((state->committed & WLR_OUTPUT_STATE_COLOR_REPRESENTATION) && output->color_encoding == state->color_encoding && @@ -685,7 +685,7 @@ static bool output_basic_test(struct wlr_output *output, { WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED, "adaptive sync" }, { WLR_OUTPUT_STATE_RENDER_FORMAT, "render format" }, { WLR_OUTPUT_STATE_SUBPIXEL, "subpixel" }, - { WLR_OUTPUT_STATE_COLOR_TRANSFORM, "color transform" }, + { WLR_OUTPUT_STATE_POST_COLOR_TRANSFORM, "post color transform" }, { WLR_OUTPUT_STATE_IMAGE_DESCRIPTION, "image description" }, }; if (!enabled) { diff --git a/types/output/state.c b/types/output/state.c index 5f44c18e8..94764e69a 100644 --- a/types/output/state.c +++ b/types/output/state.c @@ -19,7 +19,7 @@ void wlr_output_state_finish(struct wlr_output_state *state) { pixman_region32_fini(&state->damage); wlr_drm_syncobj_timeline_unref(state->wait_timeline); wlr_drm_syncobj_timeline_unref(state->signal_timeline); - wlr_color_transform_unref(state->color_transform); + wlr_color_transform_unref(state->post_color_transform); free(state->image_description); } @@ -113,14 +113,14 @@ void wlr_output_state_set_signal_timeline(struct wlr_output_state *state, state->signal_point = dst_point; } -void wlr_output_state_set_color_transform(struct wlr_output_state *state, +void wlr_output_state_set_post_color_transform(struct wlr_output_state *state, struct wlr_color_transform *tr) { - state->committed |= WLR_OUTPUT_STATE_COLOR_TRANSFORM; - wlr_color_transform_unref(state->color_transform); + state->committed |= WLR_OUTPUT_STATE_POST_COLOR_TRANSFORM; + wlr_color_transform_unref(state->post_color_transform); if (tr) { - state->color_transform = wlr_color_transform_ref(tr); + state->post_color_transform = wlr_color_transform_ref(tr); } else { - state->color_transform = NULL; + state->post_color_transform = NULL; } } @@ -156,7 +156,7 @@ bool wlr_output_state_copy(struct wlr_output_state *dst, WLR_OUTPUT_STATE_DAMAGE | WLR_OUTPUT_STATE_WAIT_TIMELINE | WLR_OUTPUT_STATE_SIGNAL_TIMELINE | - WLR_OUTPUT_STATE_COLOR_TRANSFORM | + WLR_OUTPUT_STATE_POST_COLOR_TRANSFORM | WLR_OUTPUT_STATE_IMAGE_DESCRIPTION); copy.buffer = NULL; copy.buffer_src_box = (struct wlr_fbox){0}; @@ -164,7 +164,7 @@ bool wlr_output_state_copy(struct wlr_output_state *dst, pixman_region32_init(©.damage); copy.wait_timeline = NULL; copy.signal_timeline = NULL; - copy.color_transform = NULL; + copy.post_color_transform = NULL; copy.image_description = NULL; if (src->committed & WLR_OUTPUT_STATE_BUFFER) { @@ -186,8 +186,8 @@ bool wlr_output_state_copy(struct wlr_output_state *dst, src->signal_point); } - if (src->committed & WLR_OUTPUT_STATE_COLOR_TRANSFORM) { - wlr_output_state_set_color_transform(©, src->color_transform); + if (src->committed & WLR_OUTPUT_STATE_POST_COLOR_TRANSFORM) { + wlr_output_state_set_post_color_transform(©, src->post_color_transform); } if (src->committed & WLR_OUTPUT_STATE_IMAGE_DESCRIPTION) { if (!wlr_output_state_set_image_description(©, src->image_description)) { diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 7e4a81004..fb63f025b 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -2180,7 +2180,7 @@ static void scene_output_state_attempt_gamma(struct wlr_scene_output *scene_outp return; } - wlr_output_state_set_color_transform(&gamma_pending, scene_output->gamma_lut_color_transform); + wlr_output_state_set_post_color_transform(&gamma_pending, scene_output->gamma_lut_color_transform); scene_output->gamma_lut_changed = false; if (!wlr_output_test_state(scene_output->output, &gamma_pending)) { diff --git a/types/wlr_gamma_control_v1.c b/types/wlr_gamma_control_v1.c index 42d14799d..0f3b40792 100644 --- a/types/wlr_gamma_control_v1.c +++ b/types/wlr_gamma_control_v1.c @@ -288,7 +288,7 @@ bool wlr_gamma_control_v1_apply(struct wlr_gamma_control_v1 *gamma_control, } } - wlr_output_state_set_color_transform(output_state, tr); + wlr_output_state_set_post_color_transform(output_state, tr); return true; } From 97b945beee1505b4844aaf8baaaa032b0cd42c5f Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 4 Jun 2026 16:00:12 +0200 Subject: [PATCH 3/7] output: add pre-blending color transform --- include/wlr/types/wlr_output.h | 11 +++++++++++ types/output/output.c | 15 +++++++++++++++ types/output/state.c | 16 ++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 103e57555..34310c543 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -78,6 +78,7 @@ enum wlr_output_state_field { WLR_OUTPUT_STATE_POST_COLOR_TRANSFORM = 1 << 12, WLR_OUTPUT_STATE_IMAGE_DESCRIPTION = 1 << 13, WLR_OUTPUT_STATE_COLOR_REPRESENTATION = 1 << 14, + WLR_OUTPUT_STATE_PRE_COLOR_TRANSFORM = 1 << 15, }; enum wlr_output_state_mode_type { @@ -162,6 +163,8 @@ struct wlr_output_state { struct wlr_drm_syncobj_timeline *signal_timeline; uint64_t signal_point; + // Pre-blending color transform + struct wlr_color_transform *pre_color_transform; // Post-blending color transform struct wlr_color_transform *post_color_transform; @@ -277,6 +280,7 @@ struct wlr_output { struct { struct wl_listener display_destroy; struct wlr_output_image_description image_description_value; + struct wlr_color_transform *pre_color_transform; struct wlr_color_transform *post_color_transform; struct wlr_color_primaries default_primaries_value; } WLR_PRIVATE; @@ -619,6 +623,13 @@ void wlr_output_state_set_wait_timeline(struct wlr_output_state *state, */ void wlr_output_state_set_signal_timeline(struct wlr_output_state *state, struct wlr_drm_syncobj_timeline *timeline, uint64_t dst_point); +/** + * Set the pre-blending color transform for an output. + * + * The color transform is applied before blending output layers. + */ +void wlr_output_state_set_pre_color_transform(struct wlr_output_state *state, + struct wlr_color_transform *tr); /** * Set the post-blending color transform for an output. * diff --git a/types/output/output.c b/types/output/output.c index 3f13eec2f..a3bd4a9af 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -252,6 +252,15 @@ static void output_apply_state(struct wlr_output *output, } } + if (state->committed & WLR_OUTPUT_STATE_PRE_COLOR_TRANSFORM) { + wlr_color_transform_unref(output->pre_color_transform); + if (state->pre_color_transform != NULL) { + output->pre_color_transform = wlr_color_transform_ref(state->pre_color_transform); + } else { + output->pre_color_transform = NULL; + } + } + if (state->committed & WLR_OUTPUT_STATE_POST_COLOR_TRANSFORM) { wlr_color_transform_unref(output->post_color_transform); if (state->post_color_transform != NULL) { @@ -426,6 +435,7 @@ void wlr_output_finish(struct wlr_output *output) { wlr_swapchain_destroy(output->cursor_swapchain); wlr_buffer_unlock(output->cursor_front_buffer); + wlr_color_transform_unref(output->pre_color_transform); wlr_color_transform_unref(output->post_color_transform); wlr_swapchain_destroy(output->swapchain); @@ -581,6 +591,10 @@ static uint32_t output_compare_state(struct wlr_output *output, output->subpixel == state->subpixel) { fields |= WLR_OUTPUT_STATE_SUBPIXEL; } + if ((state->committed & WLR_OUTPUT_STATE_PRE_COLOR_TRANSFORM) && + output->pre_color_transform == state->pre_color_transform) { + fields |= WLR_OUTPUT_STATE_PRE_COLOR_TRANSFORM; + } if ((state->committed & WLR_OUTPUT_STATE_POST_COLOR_TRANSFORM) && output->post_color_transform == state->post_color_transform) { fields |= WLR_OUTPUT_STATE_POST_COLOR_TRANSFORM; @@ -685,6 +699,7 @@ static bool output_basic_test(struct wlr_output *output, { WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED, "adaptive sync" }, { WLR_OUTPUT_STATE_RENDER_FORMAT, "render format" }, { WLR_OUTPUT_STATE_SUBPIXEL, "subpixel" }, + { WLR_OUTPUT_STATE_PRE_COLOR_TRANSFORM, "pre color transform" }, { WLR_OUTPUT_STATE_POST_COLOR_TRANSFORM, "post color transform" }, { WLR_OUTPUT_STATE_IMAGE_DESCRIPTION, "image description" }, }; diff --git a/types/output/state.c b/types/output/state.c index 94764e69a..c1aeb3319 100644 --- a/types/output/state.c +++ b/types/output/state.c @@ -19,6 +19,7 @@ void wlr_output_state_finish(struct wlr_output_state *state) { pixman_region32_fini(&state->damage); wlr_drm_syncobj_timeline_unref(state->wait_timeline); wlr_drm_syncobj_timeline_unref(state->signal_timeline); + wlr_color_transform_unref(state->pre_color_transform); wlr_color_transform_unref(state->post_color_transform); free(state->image_description); } @@ -113,6 +114,17 @@ void wlr_output_state_set_signal_timeline(struct wlr_output_state *state, state->signal_point = dst_point; } +void wlr_output_state_set_pre_color_transform(struct wlr_output_state *state, + struct wlr_color_transform *tr) { + state->committed |= WLR_OUTPUT_STATE_PRE_COLOR_TRANSFORM; + wlr_color_transform_unref(state->pre_color_transform); + if (tr) { + state->pre_color_transform = wlr_color_transform_ref(tr); + } else { + state->pre_color_transform = NULL; + } +} + void wlr_output_state_set_post_color_transform(struct wlr_output_state *state, struct wlr_color_transform *tr) { state->committed |= WLR_OUTPUT_STATE_POST_COLOR_TRANSFORM; @@ -164,6 +176,7 @@ bool wlr_output_state_copy(struct wlr_output_state *dst, pixman_region32_init(©.damage); copy.wait_timeline = NULL; copy.signal_timeline = NULL; + copy.pre_color_transform = NULL; copy.post_color_transform = NULL; copy.image_description = NULL; @@ -186,6 +199,9 @@ bool wlr_output_state_copy(struct wlr_output_state *dst, src->signal_point); } + if (src->committed & WLR_OUTPUT_STATE_PRE_COLOR_TRANSFORM) { + wlr_output_state_set_pre_color_transform(©, src->pre_color_transform); + } if (src->committed & WLR_OUTPUT_STATE_POST_COLOR_TRANSFORM) { wlr_output_state_set_post_color_transform(©, src->post_color_transform); } From 94a982d4e36892c6a200a4aae8ebb1f4ebb7606a Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 4 Jun 2026 16:57:52 +0200 Subject: [PATCH 4/7] backend/drm: introduce introspect_drm_prop_enum() helper --- backend/drm/properties.c | 22 ++++++++++++++++++++++ include/backend/drm/properties.h | 1 + 2 files changed, 23 insertions(+) diff --git a/backend/drm/properties.c b/backend/drm/properties.c index 75881f69b..9b69530d8 100644 --- a/backend/drm/properties.c +++ b/backend/drm/properties.c @@ -234,3 +234,25 @@ bool introspect_drm_prop_range(int fd, uint32_t prop_id, drmModeFreeProperty(prop); return true; } + +bool introspect_drm_prop_enum(int fd, uint32_t prop_id, uint64_t *bitmask) { + drmModePropertyRes *prop = drmModeGetProperty(fd, prop_id); + if (!prop) { + return false; + } + + if (drmModeGetPropertyType(prop) != DRM_MODE_PROP_ENUM) { + drmModeFreeProperty(prop); + return false; + } + + *bitmask = 0; + for (int i = 0; i < prop->count_enums; i++) { + uint64_t value = prop->enums[i].value; + assert(value < 64); + *bitmask |= 1 << value; + } + + drmModeFreeProperty(prop); + return true; +} diff --git a/include/backend/drm/properties.h b/include/backend/drm/properties.h index 09fb00881..e18f87cf3 100644 --- a/include/backend/drm/properties.h +++ b/include/backend/drm/properties.h @@ -106,5 +106,6 @@ char *get_drm_prop_enum(int fd, uint32_t obj, uint32_t prop); bool introspect_drm_prop_range(int fd, uint32_t prop_id, uint64_t *min, uint64_t *max); +bool introspect_drm_prop_enum(int fd, uint32_t prop_id, uint64_t *bitmask); #endif From 83605d212d3243ce14a991a52609a12a2f883501 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 4 Jun 2026 16:59:27 +0200 Subject: [PATCH 5/7] backend/drm: read SIZE colorop property --- backend/drm/drm.c | 12 ++++++++++++ include/backend/drm/drm.h | 1 + 2 files changed, 13 insertions(+) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 76decd758..cefc14a60 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -181,6 +181,18 @@ static bool init_color_pipeline(struct wlr_drm_backend *drm, } colorop->type = (uint32_t)type; + + switch (type) { + case DRM_COLOROP_1D_LUT: + case DRM_COLOROP_3D_LUT:; + uint64_t size = 0; + if (!get_drm_prop(drm->fd, id, colorop->props.size, &size)) { + return false; + } + colorop->size = size; + break; + } + id = (uint32_t)next; } diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index 05c065ced..22da263f6 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -23,6 +23,7 @@ struct wlr_drm_viewport { struct wlr_drm_colorop { uint32_t id; uint32_t type; // enum drm_colorop_type + uint32_t size; // for 1D_LUT, 3D_LUT struct wlr_drm_colorop_props props; struct wl_list link; // wlr_drm_plane.color_pipelines From def08f373a02c6c235e73b482cba2029bd28f8c5 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 4 Jun 2026 16:59:58 +0200 Subject: [PATCH 6/7] backend/drm: read CURVE_1D_TYPE property enum values --- backend/drm/drm.c | 6 ++++++ include/backend/drm/drm.h | 1 + include/backend/drm/properties.h | 12 ++++++++++++ 3 files changed, 19 insertions(+) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index cefc14a60..b2e7c03fe 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -191,6 +191,12 @@ static bool init_color_pipeline(struct wlr_drm_backend *drm, } colorop->size = size; break; + case DRM_COLOROP_1D_CURVE: + if (!introspect_drm_prop_enum(drm->fd, colorop->props.curve_1d_type, + &colorop->curve_1d_types)) { + return false; + } + break; } id = (uint32_t)next; diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index 22da263f6..8b14ccb81 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -24,6 +24,7 @@ struct wlr_drm_colorop { uint32_t id; uint32_t type; // enum drm_colorop_type uint32_t size; // for 1D_LUT, 3D_LUT + uint64_t curve_1d_types; // for 1D_CURVE, bitmask of 1 << WLR_DRM_COLOROP_1D_CURVE_* struct wlr_drm_colorop_props props; struct wl_list link; // wlr_drm_plane.color_pipelines diff --git a/include/backend/drm/properties.h b/include/backend/drm/properties.h index e18f87cf3..17405bf47 100644 --- a/include/backend/drm/properties.h +++ b/include/backend/drm/properties.h @@ -84,6 +84,18 @@ enum wlr_drm_color_range { WLR_DRM_COLOR_YCBCR_LIMITED_RANGE, }; +// Equivalent to drm_colorop_curve_1d_type defined in the kernel (but not exported) +enum wlr_drm_colorop_curve_1d_type { + WLR_DRM_COLOROP_1D_CURVE_SRGB_EOTF, + WLR_DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF, + WLR_DRM_COLOROP_1D_CURVE_PQ_125_EOTF, + WLR_DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF, + WLR_DRM_COLOROP_1D_CURVE_BT2020_INV_OETF, + WLR_DRM_COLOROP_1D_CURVE_BT2020_OETF, + WLR_DRM_COLOROP_1D_CURVE_GAMMA22, + WLR_DRM_COLOROP_1D_CURVE_GAMMA22_INV, +}; + struct wlr_drm_colorop_props { uint32_t type; uint32_t next; From 9ba7d8c276e918fafae7dc19123a1555c619b269 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 4 Jun 2026 17:00:33 +0200 Subject: [PATCH 7/7] backend/drm: setup plane color pipeline --- backend/drm/atomic.c | 273 ++++++++++++++++++++++++++++++++++++++ backend/drm/drm.c | 1 + backend/drm/legacy.c | 6 + include/backend/drm/drm.h | 14 ++ 4 files changed, 294 insertions(+) diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index abccf4c38..10ef14f4a 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -292,6 +292,237 @@ static uint64_t pick_max_bpc(struct wlr_drm_connector *conn, struct wlr_drm_fb * return target_bpc; } +/** Convert a double to S31.32 sign-magnitude format */ +static uint64_t to_fixed_s31_32(double v) { + uint64_t u = fabs(v) * ((uint64_t)1 << 32); + if (v < 0) { + u |= (uint64_t)1 << 63; + } + return u; +} + +enum colorop_setup_status { + COLOROP_SETUP_SUCCESS, + COLOROP_SETUP_FAILURE, + COLOROP_SETUP_INCOMPATIBLE, +}; + +static enum colorop_setup_status setup_inverse_eotf_colorop(struct wlr_drm_backend *drm, + struct wlr_drm_colorop *colorop, + struct wlr_color_transform_inverse_eotf *inv_eotf, + struct wlr_drm_colorop_state *state) { + if (colorop->type != DRM_COLOROP_1D_CURVE) { + return COLOROP_SETUP_INCOMPATIBLE; + } + + enum wlr_drm_colorop_curve_1d_type type = (uint32_t)-1; + switch (inv_eotf->tf) { + case WLR_COLOR_TRANSFER_FUNCTION_SRGB: + type = WLR_DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF; + break; + case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ: + break; // TODO: account for 125x scale + case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR: + if (!colorop->props.bypass) { + return COLOROP_SETUP_INCOMPATIBLE; + } + state->bypass = true; + return COLOROP_SETUP_SUCCESS; + case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22: + type = WLR_DRM_COLOROP_1D_CURVE_GAMMA22_INV; + break; + case WLR_COLOR_TRANSFER_FUNCTION_BT1886: + break; // not supported by KMS yet + } + assert(type != (uint32_t)-1); + + if (!(colorop->curve_1d_types & (1 << type))) { + return COLOROP_SETUP_INCOMPATIBLE; + } + + state->curve_1d_type = type; + return COLOROP_SETUP_SUCCESS; +} + +static enum colorop_setup_status setup_matrix_colorop(struct wlr_drm_backend *drm, + struct wlr_drm_colorop *colorop, + const struct wlr_color_transform_matrix *tr_matrix, + struct wlr_drm_colorop_state *state) { + if (colorop->type != DRM_COLOROP_CTM_3X4) { + return COLOROP_SETUP_INCOMPATIBLE; + } + const float *matrix = tr_matrix->matrix; + struct drm_color_ctm_3x4 data = { + .matrix = { + to_fixed_s31_32(matrix[0]), to_fixed_s31_32(matrix[1]), to_fixed_s31_32(matrix[2]), 0, + to_fixed_s31_32(matrix[3]), to_fixed_s31_32(matrix[4]), to_fixed_s31_32(matrix[5]), 0, + to_fixed_s31_32(matrix[6]), to_fixed_s31_32(matrix[7]), to_fixed_s31_32(matrix[8]), 0, + }, + }; + uint32_t blob_id = 0; + if (drmModeCreatePropertyBlob(drm->fd, &data, sizeof(data), &blob_id) != 0) { + wlr_log_errno(WLR_ERROR, "Failed to create DATA property"); + return COLOROP_SETUP_FAILURE; + } + state->data = blob_id; + return COLOROP_SETUP_SUCCESS; +} + +static enum colorop_setup_status setup_lut_3x1d_colorop(struct wlr_drm_backend *drm, + struct wlr_drm_colorop *colorop, + const struct wlr_color_transform_lut_3x1d *lut_3x1d, + struct wlr_drm_colorop_state *state) { + if (colorop->type != DRM_COLOROP_1D_LUT || colorop->size != lut_3x1d->dim) { + return COLOROP_SETUP_INCOMPATIBLE; + } + + size_t size = lut_3x1d->dim * sizeof(struct drm_color_lut32); + struct drm_color_lut32 *data = malloc(size); + if (data == NULL) { + return COLOROP_SETUP_FAILURE; + } + + for (size_t i = 0; i < lut_3x1d->dim; i++) { + uint32_t factor = UINT32_MAX / UINT16_MAX; + data[i] = (struct drm_color_lut32){ + .red = lut_3x1d->lut_3x1d[0 * lut_3x1d->dim + i] * factor, + .green = lut_3x1d->lut_3x1d[1 * lut_3x1d->dim + i] * factor, + .blue = lut_3x1d->lut_3x1d[2 * lut_3x1d->dim + i] * factor, + }; + } + + uint32_t blob_id = 0; + int ret = drmModeCreatePropertyBlob(drm->fd, data, size, &blob_id); + free(data); + if (ret != 0) { + wlr_log_errno(WLR_ERROR, "Failed to create DATA property"); + return COLOROP_SETUP_FAILURE; + } + + state->data = blob_id; + return COLOROP_SETUP_SUCCESS; +} + +static enum colorop_setup_status setup_colorop(struct wlr_drm_backend *drm, + struct wlr_drm_colorop *colorop, struct wlr_color_transform *tr, + struct wlr_drm_colorop_state *state) { + switch (tr->type) { + case COLOR_TRANSFORM_INVERSE_EOTF:; + struct wlr_color_transform_inverse_eotf *inv_eotf = + wl_container_of(tr, inv_eotf, base); + return setup_inverse_eotf_colorop(drm, colorop, inv_eotf, state); + case COLOR_TRANSFORM_LCMS2: + return COLOROP_SETUP_INCOMPATIBLE; // TODO: setup 3D LUT + case COLOR_TRANSFORM_LUT_3X1D:; + struct wlr_color_transform_lut_3x1d *lut_3x1d = + wl_container_of(tr, lut_3x1d, base); + return setup_lut_3x1d_colorop(drm, colorop, lut_3x1d, state); + case COLOR_TRANSFORM_MATRIX:; + struct wlr_color_transform_matrix *matrix = + wl_container_of(tr, matrix, base); + return setup_matrix_colorop(drm, colorop, matrix, state); + case COLOR_TRANSFORM_PIPELINE: + break; // nested pipelines are not supported + } + return COLOROP_SETUP_INCOMPATIBLE; +} + +static enum colorop_setup_status setup_color_pipeline(struct wlr_drm_backend *drm, + struct wl_list *pipeline, struct wlr_color_transform **transforms, + size_t transforms_len, struct wl_array *out_state_arr) { + struct wl_array state_arr = {0}; + + size_t i = 0; + struct wlr_drm_colorop *colorop; + wl_list_for_each(colorop, pipeline, link) { + struct wlr_drm_colorop_state *state = wl_array_add(&state_arr, sizeof(*state)); + if (state == NULL) { + wl_array_release(&state_arr); + return COLOROP_SETUP_FAILURE; + } + state->colorop = colorop; + + enum colorop_setup_status status = COLOROP_SETUP_INCOMPATIBLE; + if (i < transforms_len) { + status = setup_colorop(drm, colorop, transforms[i], state); + } + switch (status) { + case COLOROP_SETUP_SUCCESS: + i++; + break; + case COLOROP_SETUP_FAILURE: + return status; + case COLOROP_SETUP_INCOMPATIBLE: + if (!colorop->props.bypass) { + wl_array_release(&state_arr); + return COLOROP_SETUP_INCOMPATIBLE; + } + state->bypass = true; + break; + } + } + + if (i < transforms_len) { + wl_array_release(&state_arr); + return COLOROP_SETUP_INCOMPATIBLE; + } + + *out_state_arr = state_arr; + return COLOROP_SETUP_SUCCESS; +} + +static bool pick_plane_color_pipeline(struct wlr_drm_backend *drm, + struct wlr_drm_plane *plane, struct wlr_drm_connector_state *state, + uint32_t *out_pipeline_id) { + struct wlr_color_transform *base_tr = state->base->pre_color_transform; + struct wlr_color_transform *tr = NULL; + if (!color_transform_compose(&tr, &base_tr, 1)) { + return false; + } + + if (tr == NULL) { + *out_pipeline_id = 0; + return true; + } + + struct wlr_color_transform **transforms; + size_t transforms_len; + if (tr->type == COLOR_TRANSFORM_PIPELINE) { + struct wlr_color_transform_pipeline *tr_pipeline = + wl_container_of(tr, tr_pipeline, base); + transforms = tr_pipeline->transforms; + transforms_len = tr_pipeline->len; + } else { + transforms = &tr; + transforms_len = 1; + } + + for (size_t i = 0; i < plane->color_pipelines_len; i++) { + struct wl_list *pipeline = &plane->color_pipelines[i]; + struct wl_array colorops = {0}; + enum colorop_setup_status status = + setup_color_pipeline(drm, pipeline, transforms, transforms_len, &colorops); + switch (status) { + case COLOROP_SETUP_SUCCESS:; + struct wlr_drm_colorop *colorop = + wl_container_of(&pipeline->next, colorop, link); + *out_pipeline_id = colorop->id; + assert(state->colorops.size == 0); + state->colorops = colorops; + wlr_color_transform_unref(tr); + return true; + case COLOROP_SETUP_FAILURE: + wlr_color_transform_unref(tr); + return false; + case COLOROP_SETUP_INCOMPATIBLE: + break; // try the next pipeline + } + } + + wlr_color_transform_unref(tr); + return false; +} + static void destroy_blob(struct wlr_drm_backend *drm, uint32_t id) { if (id == 0) { return; @@ -356,6 +587,13 @@ bool drm_atomic_connector_prepare(struct wlr_drm_connector_state *state, bool mo } } + uint32_t primary_color_pipeline = crtc->primary->color_pipeline; + if (state->base->committed & WLR_OUTPUT_STATE_PRE_COLOR_TRANSFORM) { + if (!pick_plane_color_pipeline(drm, crtc->primary, state, &primary_color_pipeline)) { + return false; + } + } + uint32_t fb_damage_clips = 0; if ((state->base->committed & WLR_OUTPUT_STATE_DAMAGE) && crtc->primary->props.fb_damage_clips != 0) { @@ -401,6 +639,7 @@ bool drm_atomic_connector_prepare(struct wlr_drm_connector_state *state, bool mo state->vrr_enabled = vrr_enabled; state->colorspace = colorspace; state->hdr_output_metadata = hdr_output_metadata; + state->primary_color_pipeline = primary_color_pipeline; return true; } @@ -426,6 +665,13 @@ void drm_atomic_connector_apply_commit(struct wlr_drm_connector_state *state) { } conn->colorspace = state->colorspace; + crtc->primary->color_pipeline = state->primary_color_pipeline; + + struct wlr_drm_colorop_state *colorop_state; + wl_array_for_each(colorop_state, &state->colorops) { + commit_blob(drm, &colorop_state->colorop->data, colorop_state->data); + } + wl_array_release(&state->colorops); } void drm_atomic_connector_rollback_commit(struct wlr_drm_connector_state *state) { @@ -441,6 +687,12 @@ void drm_atomic_connector_rollback_commit(struct wlr_drm_connector_state *state) if (state->primary_in_fence_fd >= 0) { close(state->primary_in_fence_fd); } + + struct wlr_drm_colorop_state *colorop_state; + wl_array_for_each(colorop_state, &state->colorops) { + destroy_blob(drm, colorop_state->data); + } + wl_array_release(&state->colorops); } static void plane_disable(struct atomic *atom, struct wlr_drm_plane *plane) { @@ -596,6 +848,10 @@ static void atomic_connector_add(struct atomic *atom, if (state->primary_in_fence_fd >= 0) { set_plane_in_fence_fd(atom, crtc->primary, state->primary_in_fence_fd); } + if (crtc->primary->props.color_pipeline != 0) { + atomic_add(atom, crtc->primary->id, + crtc->primary->props.color_pipeline, state->primary_color_pipeline); + } if (crtc->cursor) { if (drm_connector_is_cursor_visible(conn)) { struct wlr_fbox cursor_src = { @@ -620,6 +876,23 @@ static void atomic_connector_add(struct atomic *atom, plane_disable(atom, crtc->cursor); } } + + struct wlr_drm_colorop_state *colorop_state; + wl_array_for_each(colorop_state, &state->colorops) { + struct wlr_drm_colorop *colorop = colorop_state->colorop; + if (colorop->props.bypass != 0) { + atomic_add(atom, colorop->id, colorop->props.bypass, + colorop_state->bypass); + } + if (colorop->props.curve_1d_type != 0) { + atomic_add(atom, colorop->id, colorop->props.curve_1d_type, + colorop_state->curve_1d_type); + } + if (colorop->props.data != 0) { + atomic_add(atom, colorop->id, colorop->props.data, + colorop_state->data); + } + } } else { plane_disable(atom, crtc->primary); if (crtc->cursor) { diff --git a/backend/drm/drm.c b/backend/drm/drm.c index b2e7c03fe..1f9feb619 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -42,6 +42,7 @@ static const uint32_t COMMIT_OUTPUT_STATE = WLR_OUTPUT_STATE_LAYERS | WLR_OUTPUT_STATE_WAIT_TIMELINE | WLR_OUTPUT_STATE_SIGNAL_TIMELINE | + WLR_OUTPUT_STATE_PRE_COLOR_TRANSFORM | WLR_OUTPUT_STATE_POST_COLOR_TRANSFORM | WLR_OUTPUT_STATE_IMAGE_DESCRIPTION | WLR_OUTPUT_STATE_COLOR_REPRESENTATION; diff --git a/backend/drm/legacy.c b/backend/drm/legacy.c index 96fe19883..8c98e7d7b 100644 --- a/backend/drm/legacy.c +++ b/backend/drm/legacy.c @@ -79,6 +79,12 @@ static bool legacy_crtc_test(const struct wlr_drm_connector_state *state, } } + if (state->base->committed & WLR_OUTPUT_STATE_PRE_COLOR_TRANSFORM) { + wlr_drm_conn_log(conn, WLR_DEBUG, + "Pre-blending color pipeline not supported for legacy KMS API"); + return false; + } + return true; } diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index 8b14ccb81..41494bcf6 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -28,6 +28,8 @@ struct wlr_drm_colorop { struct wlr_drm_colorop_props props; struct wl_list link; // wlr_drm_plane.color_pipelines + + uint32_t data; // for 1D_LUT, CTM_3X4, 3D_LUT }; struct wlr_drm_plane { @@ -61,6 +63,9 @@ struct wlr_drm_plane { uint32_t initial_crtc_id; struct liftoff_plane *liftoff; struct liftoff_layer *liftoff_layer; + + // Atomic modesetting only + uint32_t color_pipeline; }; struct wlr_drm_layer { @@ -177,6 +182,15 @@ struct wlr_drm_connector_state { bool vrr_enabled; uint32_t colorspace; uint32_t hdr_output_metadata; + uint32_t primary_color_pipeline; + struct wl_array colorops; // struct wlr_drm_colorop_state +}; + +struct wlr_drm_colorop_state { + struct wlr_drm_colorop *colorop; + bool bypass; + uint32_t curve_1d_type; // for 1D_CURVE + uint32_t data; // for 1D_LUT, CTM_3X4, 3D_LUT }; /**