From f9a27c5b6f615be4f3e73e403cc7ca7e768df478 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 21 Jun 2025 12:50:46 +0200 Subject: [PATCH 1/4] render/color: move inverse EOTF eval helper to private header This will be used by the DRM backend in the next commit. --- include/render/color.h | 5 +++++ render/color.c | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/include/render/color.h b/include/render/color.h index 128b345e1..7f133f5bd 100644 --- a/include/render/color.h +++ b/include/render/color.h @@ -97,4 +97,9 @@ void wlr_color_primaries_to_xyz(const struct wlr_color_primaries *primaries, flo void wlr_color_transfer_function_get_default_luminance(enum wlr_color_transfer_function tf, struct wlr_color_luminances *lum); +/** + * Apply a color transfer function's EOTF⁻¹ operation. + */ +float wlr_color_transfer_function_eval_inverse_eotf(enum wlr_color_transfer_function tf, float x); + #endif diff --git a/render/color.c b/render/color.c index 0a1a67be3..198e1c26d 100644 --- a/render/color.c +++ b/render/color.c @@ -177,7 +177,7 @@ static float bt1886_eval_inverse_eotf(float x) { return powf(x / a, 1.0 / 2.4) - b; } -static float transfer_function_eval_inverse_eotf( +float wlr_color_transfer_function_eval_inverse_eotf( enum wlr_color_transfer_function tf, float x) { switch (tf) { case WLR_COLOR_TRANSFER_FUNCTION_SRGB: @@ -198,7 +198,7 @@ static void color_transform_inverse_eotf_eval( struct wlr_color_transform_inverse_eotf *tr, float out[static 3], const float in[static 3]) { for (size_t i = 0; i < 3; i++) { - out[i] = transfer_function_eval_inverse_eotf(tr->tf, in[i]); + out[i] = wlr_color_transfer_function_eval_inverse_eotf(tr->tf, in[i]); } } From 2984b011b63210b7ad2ab538bbf86535de9cad35 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 17 Jun 2025 15:17:31 +0200 Subject: [PATCH 2/4] backend/drm: generalize color transform handling --- backend/drm/atomic.c | 9 +-- backend/drm/color.c | 147 ++++++++++++++++++++++++++++++++++++ backend/drm/drm.c | 16 ++-- backend/drm/legacy.c | 9 +-- backend/drm/meson.build | 1 + include/backend/drm/color.h | 23 ++++++ include/backend/drm/drm.h | 4 + 7 files changed, 194 insertions(+), 15 deletions(-) create mode 100644 backend/drm/color.c create mode 100644 include/backend/drm/color.h diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index 41773d4f5..a694b06d1 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -6,6 +6,7 @@ #include #include #include +#include "backend/drm/color.h" #include "backend/drm/drm.h" #include "backend/drm/fb.h" #include "backend/drm/iface.h" @@ -334,11 +335,9 @@ bool drm_atomic_connector_prepare(struct wlr_drm_connector_state *state, bool mo if (state->base->committed & WLR_OUTPUT_STATE_COLOR_TRANSFORM) { size_t dim = 0; uint16_t *lut = NULL; - if (state->base->color_transform != NULL) { - struct wlr_color_transform_lut_3x1d *tr = - color_transform_lut_3x1d_from_base(state->base->color_transform); - dim = tr->dim; - lut = tr->lut_3x1d; + if (state->crtc_color_transform != NULL) { + dim = state->crtc_color_transform->lut_3x1d->dim; + lut = state->crtc_color_transform->lut_3x1d->lut_3x1d; } // Fallback to legacy gamma interface when gamma properties are not diff --git a/backend/drm/color.c b/backend/drm/color.c new file mode 100644 index 000000000..8d86b28eb --- /dev/null +++ b/backend/drm/color.c @@ -0,0 +1,147 @@ +#include + +#include "backend/drm/color.h" +#include "backend/drm/drm.h" +#include "render/color.h" + +static struct wlr_color_transform_lut_3x1d *create_identity_3x1dlut(size_t dim) { + if (dim == 0) { + return NULL; + } + uint16_t *lut = malloc(dim * sizeof(lut[0])); + if (lut == NULL) { + return NULL; + } + for (size_t i = 0; i < dim; i++) { + float x = (float)i / (dim - 1); + lut[i] = (uint16_t)roundf(UINT16_MAX * x); + } + struct wlr_color_transform *out = wlr_color_transform_init_lut_3x1d(dim, lut, lut, lut); + free(lut); + return color_transform_lut_3x1d_from_base(out); +} + +static bool drm_crtc_color_transform_init_lut_3x1d(struct wlr_drm_crtc_color_transform *out, + size_t dim) { + if (out->lut_3x1d != NULL) { + return true; + } + out->lut_3x1d = create_identity_3x1dlut(dim); + return out->lut_3x1d != NULL; +} + +static bool drm_crtc_color_transform_convert(struct wlr_drm_crtc_color_transform *out, + struct wlr_color_transform *in, size_t lut_3x1d_dim) { + switch (in->type) { + case COLOR_TRANSFORM_INVERSE_EOTF:; + struct wlr_color_transform_inverse_eotf *inv_eotf = + wlr_color_transform_inverse_eotf_from_base(in); + + if (!drm_crtc_color_transform_init_lut_3x1d(out, lut_3x1d_dim)) { + return false; + } + + for (size_t i = 0; i < 3; i++) { + for (size_t j = 0; j < lut_3x1d_dim; j++) { + size_t offset = i * lut_3x1d_dim + j; + float v = (float)out->lut_3x1d->lut_3x1d[offset] / UINT16_MAX; + v = wlr_color_transfer_function_eval_inverse_eotf(inv_eotf->tf, v); + out->lut_3x1d->lut_3x1d[offset] = (uint16_t)roundf(UINT16_MAX * v); + } + } + + return true; + case COLOR_TRANSFORM_LUT_3X1D:; + struct wlr_color_transform_lut_3x1d *lut_3x1d = + color_transform_lut_3x1d_from_base(in); + if (lut_3x1d->dim != lut_3x1d_dim) { + return false; + } + + if (!drm_crtc_color_transform_init_lut_3x1d(out, lut_3x1d_dim)) { + return false; + } + + // TODO: we loose precision when the input color transform is a lone 3×1D LUT + for (size_t i = 0; i < 3 * lut_3x1d->dim; i++) { + out->lut_3x1d->lut_3x1d[i] *= lut_3x1d->lut_3x1d[i]; + } + + return true; + case COLOR_TRANSFORM_LCMS2: + return false; // unsupported + case COLOR_TRANSFORM_PIPELINE:; + struct wlr_color_transform_pipeline *pipeline = wl_container_of(in, pipeline, base); + + for (size_t i = 0; i < pipeline->len; i++) { + if (!drm_crtc_color_transform_convert(out, pipeline->transforms[i], lut_3x1d_dim)) { + return false; + } + } + + return true; + } + abort(); // unreachable +} + +static void addon_destroy(struct wlr_addon *addon) { + struct wlr_drm_crtc_color_transform *tr = wl_container_of(addon, tr, addon); + if (tr->lut_3x1d != NULL) { + wlr_color_transform_unref(&tr->lut_3x1d->base); + } + free(tr); +} + +static const struct wlr_addon_interface addon_impl = { + .name = "wlr_drm_crtc_color_transform", + .destroy = addon_destroy, +}; + +static struct wlr_drm_crtc_color_transform *drm_crtc_color_transform_create( + struct wlr_drm_backend *backend, struct wlr_drm_crtc *crtc, + struct wlr_color_transform *base) { + struct wlr_drm_crtc_color_transform *tr = calloc(1, sizeof(*tr)); + if (tr == NULL) { + return NULL; + } + + tr->base = base; + wlr_addon_init(&tr->addon, &base->addons, crtc, &addon_impl); + + size_t lut_3x1d_dim = drm_crtc_get_gamma_lut_size(backend, crtc); + tr->failed = !drm_crtc_color_transform_convert(tr, base, lut_3x1d_dim); + + return tr; +} + +struct wlr_drm_crtc_color_transform *drm_crtc_color_transform_import( + struct wlr_drm_backend *backend, struct wlr_drm_crtc *crtc, + struct wlr_color_transform *base) { + struct wlr_drm_crtc_color_transform *tr; + struct wlr_addon *addon = wlr_addon_find(&base->addons, crtc, &addon_impl); + if (addon != NULL) { + tr = wl_container_of(addon, tr, addon); + } else { + tr = drm_crtc_color_transform_create(backend, crtc, base); + if (tr == NULL) { + return NULL; + } + } + + if (tr->failed) { + // We failed to convert the color transform to a 3×1D LUT. Keep the + // addon attached so that we remember that this color transform cannot + // be imported next time a commit contains it. + return NULL; + } + + wlr_color_transform_ref(tr->base); + return tr; +} + +void drm_crtc_color_transform_unref(struct wlr_drm_crtc_color_transform *tr) { + if (tr == NULL) { + return; + } + wlr_color_transform_unref(tr->base); +} diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 86b52c684..f347f7a43 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -20,6 +20,7 @@ #include #include #include +#include "backend/drm/color.h" #include "backend/drm/drm.h" #include "backend/drm/fb.h" #include "backend/drm/iface.h" @@ -707,6 +708,7 @@ static void drm_connector_state_finish(struct wlr_drm_connector_state *state) { drm_fb_clear(&state->primary_fb); drm_fb_clear(&state->cursor_fb); wlr_drm_syncobj_timeline_unref(state->wait_timeline); + drm_crtc_color_transform_unref(state->crtc_color_transform); } static bool drm_connector_state_update_primary_fb(struct wlr_drm_connector *conn, @@ -858,11 +860,15 @@ 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) { - wlr_drm_conn_log(conn, WLR_DEBUG, - "Only 3x1D LUT color transforms are supported"); - return false; + if ((state->committed & WLR_OUTPUT_STATE_COLOR_TRANSFORM) && state->color_transform != NULL) { + assert(conn_state->crtc_color_transform == NULL); + conn_state->crtc_color_transform = drm_crtc_color_transform_import(conn->backend, + conn->crtc, state->color_transform); + if (conn_state->crtc_color_transform == NULL) { + wlr_drm_conn_log(conn, WLR_DEBUG, + "Failed to import color transform"); + return false; + } } if ((state->committed & WLR_OUTPUT_STATE_IMAGE_DESCRIPTION) && diff --git a/backend/drm/legacy.c b/backend/drm/legacy.c index 223852ec1..972c913bd 100644 --- a/backend/drm/legacy.c +++ b/backend/drm/legacy.c @@ -3,6 +3,7 @@ #include #include #include +#include "backend/drm/color.h" #include "backend/drm/drm.h" #include "backend/drm/fb.h" #include "backend/drm/iface.h" @@ -128,11 +129,9 @@ static bool legacy_crtc_commit(const struct wlr_drm_connector_state *state, if (state->base->committed & WLR_OUTPUT_STATE_COLOR_TRANSFORM) { size_t dim = 0; uint16_t *lut = NULL; - if (state->base->color_transform != NULL) { - struct wlr_color_transform_lut_3x1d *tr = - color_transform_lut_3x1d_from_base(state->base->color_transform); - dim = tr->dim; - lut = tr->lut_3x1d; + if (state->crtc_color_transform != NULL) { + dim = state->crtc_color_transform->lut_3x1d->dim; + lut = state->crtc_color_transform->lut_3x1d->lut_3x1d; } if (!drm_legacy_crtc_set_gamma(drm, crtc, dim, lut)) { diff --git a/backend/drm/meson.build b/backend/drm/meson.build index d9e5b77f5..0f02588be 100644 --- a/backend/drm/meson.build +++ b/backend/drm/meson.build @@ -38,6 +38,7 @@ wlr_files += pnpids_c wlr_files += files( 'atomic.c', 'backend.c', + 'color.c', 'drm.c', 'fb.c', 'legacy.c', diff --git a/include/backend/drm/color.h b/include/backend/drm/color.h new file mode 100644 index 000000000..63f5377d1 --- /dev/null +++ b/include/backend/drm/color.h @@ -0,0 +1,23 @@ +#ifndef BACKEND_DRN_COLOR_H +#define BACKEND_DRN_COLOR_H + +#include + +struct wlr_drm_backend; +struct wlr_drm_crtc; +struct wlr_color_transform; + +struct wlr_drm_crtc_color_transform { + struct wlr_color_transform *base; + struct wlr_addon addon; // wlr_color_transform.addons + bool failed; + struct wlr_color_transform_lut_3x1d *lut_3x1d; +}; + +struct wlr_drm_crtc_color_transform *drm_crtc_color_transform_import( + struct wlr_drm_backend *backend, struct wlr_drm_crtc *crtc, + struct wlr_color_transform *base); + +void drm_crtc_color_transform_unref(struct wlr_drm_crtc_color_transform *tr); + +#endif diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index af4231f54..90839bbad 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -15,6 +15,8 @@ #include "backend/drm/properties.h" #include "backend/drm/renderer.h" +struct wlr_drm_crtc_color_transform; + struct wlr_drm_viewport { struct wlr_fbox src_box; struct wlr_box dst_box; @@ -152,6 +154,8 @@ struct wlr_drm_connector_state { struct wlr_drm_syncobj_timeline *wait_timeline; uint64_t wait_point; + struct wlr_drm_crtc_color_transform *crtc_color_transform; + // used by atomic uint32_t mode_id; uint32_t gamma_lut; From 92ba6ffdf89dc51022eadbf2a1752097c27f7de5 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 28 Oct 2025 18:37:37 +0100 Subject: [PATCH 3/4] render/color: introduce wlr_color_transform_matrix This is useful to perform color space conversions. --- backend/drm/color.c | 2 ++ include/render/color.h | 7 +++++++ include/wlr/render/color.h | 6 ++++++ render/color.c | 19 ++++++++++++++++++- 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/backend/drm/color.c b/backend/drm/color.c index 8d86b28eb..55371124e 100644 --- a/backend/drm/color.c +++ b/backend/drm/color.c @@ -68,6 +68,8 @@ static bool drm_crtc_color_transform_convert(struct wlr_drm_crtc_color_transform } return true; + case COLOR_TRANSFORM_MATRIX: + return false; // TODO: add support for CTM case COLOR_TRANSFORM_LCMS2: return false; // unsupported case COLOR_TRANSFORM_PIPELINE:; diff --git a/include/render/color.h b/include/render/color.h index 7f133f5bd..a9923093a 100644 --- a/include/render/color.h +++ b/include/render/color.h @@ -9,6 +9,7 @@ enum wlr_color_transform_type { COLOR_TRANSFORM_INVERSE_EOTF, COLOR_TRANSFORM_LCMS2, COLOR_TRANSFORM_LUT_3X1D, + COLOR_TRANSFORM_MATRIX, COLOR_TRANSFORM_PIPELINE, }; @@ -40,6 +41,12 @@ struct wlr_color_transform_lut_3x1d { size_t dim; }; +struct wlr_color_transform_matrix { + struct wlr_color_transform base; + + float matrix[9]; +}; + struct wlr_color_transform_pipeline { struct wlr_color_transform base; diff --git a/include/wlr/render/color.h b/include/wlr/render/color.h index bc3baf181..ed6dad8b9 100644 --- a/include/wlr/render/color.h +++ b/include/wlr/render/color.h @@ -141,6 +141,12 @@ struct wlr_color_transform *wlr_color_transform_init_linear_to_inverse_eotf( struct wlr_color_transform *wlr_color_transform_init_lut_3x1d(size_t dim, const uint16_t *r, const uint16_t *g, const uint16_t *b); +/** + * Initialize a color transformation to apply a 3×3 matrix. Returns NULL on + * failure. + */ +struct wlr_color_transform *wlr_color_transform_init_matrix(const float matrix[static 9]); + /** * Initialize a color transformation to apply a sequence of color transforms * one after another. diff --git a/render/color.c b/render/color.c index 198e1c26d..e4a0efa89 100644 --- a/render/color.c +++ b/render/color.c @@ -62,6 +62,16 @@ struct wlr_color_transform *wlr_color_transform_init_lut_3x1d(size_t dim, return &tx->base; } +struct wlr_color_transform *wlr_color_transform_init_matrix(const float matrix[static 9]) { + struct wlr_color_transform_matrix *tx = calloc(1, sizeof(*tx)); + if (!tx) { + return NULL; + } + wlr_color_transform_init(&tx->base, COLOR_TRANSFORM_MATRIX); + memcpy(tx->matrix, matrix, sizeof(tx->matrix)); + return &tx->base; +} + struct wlr_color_transform *wlr_color_transform_init_pipeline( struct wlr_color_transform **transforms, size_t len) { assert(len > 0); @@ -92,6 +102,7 @@ struct wlr_color_transform *wlr_color_transform_init_pipeline( static void color_transform_destroy(struct wlr_color_transform *tr) { switch (tr->type) { case COLOR_TRANSFORM_INVERSE_EOTF: + case COLOR_TRANSFORM_MATRIX: break; case COLOR_TRANSFORM_LCMS2: color_transform_lcms2_finish(color_transform_lcms2_from_base(tr)); @@ -226,6 +237,8 @@ static void color_transform_lut_3x1d_eval(struct wlr_color_transform_lut_3x1d *t } } +static void multiply_matrix_vector(float out[static 3], float m[static 9], const float v[static 3]); + void wlr_color_transform_eval(struct wlr_color_transform *tr, float out[static 3], const float in[static 3]) { switch (tr->type) { @@ -238,6 +251,10 @@ void wlr_color_transform_eval(struct wlr_color_transform *tr, case COLOR_TRANSFORM_LUT_3X1D: color_transform_lut_3x1d_eval(color_transform_lut_3x1d_from_base(tr), out, in); break; + case COLOR_TRANSFORM_MATRIX:; + struct wlr_color_transform_matrix *matrix = wl_container_of(tr, matrix, base); + multiply_matrix_vector(out, matrix->matrix, in); + break; case COLOR_TRANSFORM_PIPELINE:; struct wlr_color_transform_pipeline *pipeline = wl_container_of(tr, pipeline, base); @@ -264,7 +281,7 @@ void wlr_color_primaries_from_named(struct wlr_color_primaries *out, abort(); } -static void multiply_matrix_vector(float out[static 3], float m[static 9], float v[static 3]) { +static void multiply_matrix_vector(float out[static 3], float m[static 9], const float v[static 3]) { float result[3] = { m[0] * v[0] + m[1] * v[1] + m[2] * v[2], m[3] * v[0] + m[4] * v[1] + m[5] * v[2], From 2db00a9872a05eb0496266d9e830e5ea30fdd810 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 13 Nov 2025 14:31:14 +0100 Subject: [PATCH 4/4] backend/drm: add support for CTM --- backend/drm/atomic.c | 52 +++++++++++++++++++++++++++++--- backend/drm/color.c | 44 ++++++++++++++++++++++----- backend/drm/drm.c | 3 ++ backend/drm/libliftoff.c | 3 ++ backend/drm/properties.c | 1 + include/backend/drm/color.h | 2 ++ include/backend/drm/drm.h | 2 ++ include/backend/drm/properties.h | 1 + 8 files changed, 96 insertions(+), 12 deletions(-) diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index a694b06d1..1c32841e0 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -154,6 +154,31 @@ static bool create_gamma_lut_blob(struct wlr_drm_backend *drm, return true; } +static bool create_ctm_blob(struct wlr_drm_backend *drm, + const float *matrix, uint32_t *blob_id) { + if (matrix == NULL) { + *blob_id = 0; + return true; + } + + // Convert to S31.32 sign-magnitude fixed floating-point encoding + struct drm_color_ctm ctm = {0}; + for (size_t i = 0; i < 9; i++) { + uint64_t v = fabs(matrix[i]) * ((uint64_t)1 << 32); + if (matrix[i] < 0) { + v |= (uint64_t)1 << 63; + } + ctm.matrix[i] = v; + } + + if (drmModeCreatePropertyBlob(drm->fd, &ctm, sizeof(ctm), blob_id) != 0) { + wlr_log_errno(WLR_ERROR, "Unable to create CTM property blob"); + return false; + } + + return true; +} + bool create_fb_damage_clips_blob(struct wlr_drm_backend *drm, int width, int height, const pixman_region32_t *damage, uint32_t *blob_id) { pixman_region32_t clipped; @@ -332,26 +357,39 @@ bool drm_atomic_connector_prepare(struct wlr_drm_connector_state *state, bool mo } uint32_t gamma_lut = crtc->gamma_lut; + uint32_t ctm = crtc->ctm; if (state->base->committed & WLR_OUTPUT_STATE_COLOR_TRANSFORM) { - size_t dim = 0; + size_t lut_dim = 0; uint16_t *lut = NULL; + const float *matrix = NULL; if (state->crtc_color_transform != NULL) { - dim = state->crtc_color_transform->lut_3x1d->dim; + lut_dim = state->crtc_color_transform->lut_3x1d->dim; lut = state->crtc_color_transform->lut_3x1d->lut_3x1d; + if (state->crtc_color_transform->has_matrix) { + matrix = state->crtc_color_transform->matrix; + } } // Fallback to legacy gamma interface when gamma properties are not // available (can happen on older Intel GPUs that support gamma but not // degamma). if (crtc->props.gamma_lut == 0) { - if (!drm_legacy_crtc_set_gamma(drm, crtc, dim, lut)) { + if (!drm_legacy_crtc_set_gamma(drm, crtc, lut_dim, lut)) { return false; } } else { - if (!create_gamma_lut_blob(drm, dim, lut, &gamma_lut)) { + if (!create_gamma_lut_blob(drm, lut_dim, lut, &gamma_lut)) { return false; } } + + if (matrix != NULL && crtc->props.ctm == 0) { + wlr_log(WLR_DEBUG, "CRTC %"PRIu32" doesn't support CTM", crtc->id); + return false; + } + if (!create_ctm_blob(drm, matrix, &ctm)) { + return false; + } } uint32_t fb_damage_clips = 0; @@ -394,6 +432,7 @@ bool drm_atomic_connector_prepare(struct wlr_drm_connector_state *state, bool mo state->mode_id = mode_id; state->gamma_lut = gamma_lut; + state->ctm = ctm; state->fb_damage_clips = fb_damage_clips; state->primary_in_fence_fd = in_fence_fd; state->vrr_enabled = vrr_enabled; @@ -413,6 +452,7 @@ void drm_atomic_connector_apply_commit(struct wlr_drm_connector_state *state) { crtc->own_mode_id = true; commit_blob(drm, &crtc->mode_id, state->mode_id); commit_blob(drm, &crtc->gamma_lut, state->gamma_lut); + commit_blob(drm, &crtc->ctm, state->ctm); commit_blob(drm, &conn->hdr_output_metadata, state->hdr_output_metadata); conn->output.adaptive_sync_status = state->vrr_enabled ? @@ -439,6 +479,7 @@ void drm_atomic_connector_rollback_commit(struct wlr_drm_connector_state *state) rollback_blob(drm, &crtc->mode_id, state->mode_id); rollback_blob(drm, &crtc->gamma_lut, state->gamma_lut); + rollback_blob(drm, &crtc->ctm, state->ctm); rollback_blob(drm, &conn->hdr_output_metadata, state->hdr_output_metadata); destroy_blob(drm, state->fb_damage_clips); @@ -543,6 +584,9 @@ static void atomic_connector_add(struct atomic *atom, if (crtc->props.gamma_lut != 0) { atomic_add(atom, crtc->id, crtc->props.gamma_lut, state->gamma_lut); } + if (crtc->props.ctm != 0) { + atomic_add(atom, crtc->id, crtc->props.ctm, state->ctm); + } if (crtc->props.vrr_enabled != 0) { atomic_add(atom, crtc->id, crtc->props.vrr_enabled, state->vrr_enabled); } diff --git a/backend/drm/color.c b/backend/drm/color.c index 55371124e..7c041d4b6 100644 --- a/backend/drm/color.c +++ b/backend/drm/color.c @@ -3,6 +3,12 @@ #include "backend/drm/color.h" #include "backend/drm/drm.h" #include "render/color.h" +#include "util/matrix.h" + +enum wlr_drm_crtc_color_transform_stage { + WLR_DRM_CRTC_COLOR_TRANSFORM_MATRIX, + WLR_DRM_CRTC_COLOR_TRANSFORM_LUT_3X1D, +}; static struct wlr_color_transform_lut_3x1d *create_identity_3x1dlut(size_t dim) { if (dim == 0) { @@ -21,8 +27,21 @@ static struct wlr_color_transform_lut_3x1d *create_identity_3x1dlut(size_t dim) return color_transform_lut_3x1d_from_base(out); } +static bool set_stage(enum wlr_drm_crtc_color_transform_stage *current, + enum wlr_drm_crtc_color_transform_stage next) { + // Enforce that we fill the pipeline in order: first CTM then GAMMA_LUT + if (*current > next) { + return false; + } + *current = next; + return true; +} + static bool drm_crtc_color_transform_init_lut_3x1d(struct wlr_drm_crtc_color_transform *out, - size_t dim) { + enum wlr_drm_crtc_color_transform_stage *stage, size_t dim) { + if (!set_stage(stage, WLR_DRM_CRTC_COLOR_TRANSFORM_LUT_3X1D)) { + return false; + } if (out->lut_3x1d != NULL) { return true; } @@ -31,13 +50,14 @@ static bool drm_crtc_color_transform_init_lut_3x1d(struct wlr_drm_crtc_color_tra } static bool drm_crtc_color_transform_convert(struct wlr_drm_crtc_color_transform *out, - struct wlr_color_transform *in, size_t lut_3x1d_dim) { + struct wlr_color_transform *in, enum wlr_drm_crtc_color_transform_stage *stage, + size_t lut_3x1d_dim) { switch (in->type) { case COLOR_TRANSFORM_INVERSE_EOTF:; struct wlr_color_transform_inverse_eotf *inv_eotf = wlr_color_transform_inverse_eotf_from_base(in); - if (!drm_crtc_color_transform_init_lut_3x1d(out, lut_3x1d_dim)) { + if (!drm_crtc_color_transform_init_lut_3x1d(out, stage, lut_3x1d_dim)) { return false; } @@ -58,7 +78,7 @@ static bool drm_crtc_color_transform_convert(struct wlr_drm_crtc_color_transform return false; } - if (!drm_crtc_color_transform_init_lut_3x1d(out, lut_3x1d_dim)) { + if (!drm_crtc_color_transform_init_lut_3x1d(out, stage, lut_3x1d_dim)) { return false; } @@ -68,15 +88,21 @@ static bool drm_crtc_color_transform_convert(struct wlr_drm_crtc_color_transform } return true; - case COLOR_TRANSFORM_MATRIX: - return false; // TODO: add support for CTM + case COLOR_TRANSFORM_MATRIX:; + struct wlr_color_transform_matrix *matrix = wl_container_of(in, matrix, base); + if (!set_stage(stage, WLR_DRM_CRTC_COLOR_TRANSFORM_MATRIX)) { + return false; + } + wlr_matrix_multiply(out->matrix, matrix->matrix, out->matrix); + out->has_matrix = true; + return true; case COLOR_TRANSFORM_LCMS2: return false; // unsupported case COLOR_TRANSFORM_PIPELINE:; struct wlr_color_transform_pipeline *pipeline = wl_container_of(in, pipeline, base); for (size_t i = 0; i < pipeline->len; i++) { - if (!drm_crtc_color_transform_convert(out, pipeline->transforms[i], lut_3x1d_dim)) { + if (!drm_crtc_color_transform_convert(out, pipeline->transforms[i], stage, lut_3x1d_dim)) { return false; } } @@ -109,9 +135,11 @@ static struct wlr_drm_crtc_color_transform *drm_crtc_color_transform_create( tr->base = base; wlr_addon_init(&tr->addon, &base->addons, crtc, &addon_impl); + wlr_matrix_identity(tr->matrix); size_t lut_3x1d_dim = drm_crtc_get_gamma_lut_size(backend, crtc); - tr->failed = !drm_crtc_color_transform_convert(tr, base, lut_3x1d_dim); + enum wlr_drm_crtc_color_transform_stage stage = WLR_DRM_CRTC_COLOR_TRANSFORM_MATRIX; + tr->failed = !drm_crtc_color_transform_convert(tr, base, &stage, lut_3x1d_dim); return tr; } diff --git a/backend/drm/drm.c b/backend/drm/drm.c index f347f7a43..8bf66be94 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -391,6 +391,9 @@ void finish_drm_resources(struct wlr_drm_backend *drm) { if (crtc->gamma_lut) { drmModeDestroyPropertyBlob(drm->fd, crtc->gamma_lut); } + if (crtc->ctm) { + drmModeDestroyPropertyBlob(drm->fd, crtc->ctm); + } } free(drm->crtcs); diff --git a/backend/drm/libliftoff.c b/backend/drm/libliftoff.c index 12761afd4..3bad8f471 100644 --- a/backend/drm/libliftoff.c +++ b/backend/drm/libliftoff.c @@ -328,6 +328,9 @@ static bool add_connector(drmModeAtomicReq *req, if (crtc->props.gamma_lut != 0) { ok = ok && add_prop(req, crtc->id, crtc->props.gamma_lut, state->gamma_lut); } + if (crtc->props.ctm != 0) { + ok = ok && add_prop(req, crtc->id, crtc->props.ctm, state->ctm); + } if (crtc->props.vrr_enabled != 0) { ok = ok && add_prop(req, crtc->id, crtc->props.vrr_enabled, state->vrr_enabled); } diff --git a/backend/drm/properties.c b/backend/drm/properties.c index 314023954..520579ca1 100644 --- a/backend/drm/properties.c +++ b/backend/drm/properties.c @@ -40,6 +40,7 @@ static const struct prop_info connector_info[] = { static const struct prop_info crtc_info[] = { #define INDEX(name) (offsetof(struct wlr_drm_crtc_props, name) / sizeof(uint32_t)) { "ACTIVE", INDEX(active) }, + { "CTM", INDEX(ctm) }, { "GAMMA_LUT", INDEX(gamma_lut) }, { "GAMMA_LUT_SIZE", INDEX(gamma_lut_size) }, { "MODE_ID", INDEX(mode_id) }, diff --git a/include/backend/drm/color.h b/include/backend/drm/color.h index 63f5377d1..857dd9d40 100644 --- a/include/backend/drm/color.h +++ b/include/backend/drm/color.h @@ -12,6 +12,8 @@ struct wlr_drm_crtc_color_transform { struct wlr_addon addon; // wlr_color_transform.addons bool failed; struct wlr_color_transform_lut_3x1d *lut_3x1d; + float matrix[9]; + bool has_matrix; }; struct wlr_drm_crtc_color_transform *drm_crtc_color_transform_import( diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index 90839bbad..5c47606a9 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -76,6 +76,7 @@ struct wlr_drm_crtc { bool own_mode_id; uint32_t mode_id; uint32_t gamma_lut; + uint32_t ctm; // Legacy only int legacy_gamma_size; @@ -159,6 +160,7 @@ struct wlr_drm_connector_state { // used by atomic uint32_t mode_id; uint32_t gamma_lut; + uint32_t ctm; uint32_t fb_damage_clips; int primary_in_fence_fd, out_fence_fd; bool vrr_enabled; diff --git a/include/backend/drm/properties.h b/include/backend/drm/properties.h index c02d655ba..911a7ba04 100644 --- a/include/backend/drm/properties.h +++ b/include/backend/drm/properties.h @@ -35,6 +35,7 @@ struct wlr_drm_crtc_props { uint32_t vrr_enabled; uint32_t gamma_lut; uint32_t gamma_lut_size; + uint32_t ctm; // atomic-modesetting only