backend/drm: generalize color transform handling

This commit is contained in:
Simon Ser 2025-06-17 15:17:31 +02:00
parent f9a27c5b6f
commit 2984b011b6
7 changed files with 194 additions and 15 deletions

View file

@ -6,6 +6,7 @@
#include <wlr/util/box.h>
#include <wlr/util/log.h>
#include <xf86drmMode.h>
#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

147
backend/drm/color.c Normal file
View file

@ -0,0 +1,147 @@
#include <stdlib.h>
#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);
}

View file

@ -20,6 +20,7 @@
#include <wlr/util/transform.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#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) &&

View file

@ -3,6 +3,7 @@
#include <wlr/util/log.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#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)) {

View file

@ -38,6 +38,7 @@ wlr_files += pnpids_c
wlr_files += files(
'atomic.c',
'backend.c',
'color.c',
'drm.c',
'fb.c',
'legacy.c',

View file

@ -0,0 +1,23 @@
#ifndef BACKEND_DRN_COLOR_H
#define BACKEND_DRN_COLOR_H
#include <wlr/util/addon.h>
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

View file

@ -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;