Merge branch 'kms-plane-color-pipeline' into 'master'

Draft: backend/drm: add support for plane color pipelines

See merge request wlroots/wlroots!5220
This commit is contained in:
Simon Ser 2026-06-13 15:12:52 +00:00
commit e3a6813e38
11 changed files with 549 additions and 36 deletions

View file

@ -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;
@ -332,12 +563,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;
}
@ -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) {

View file

@ -42,7 +42,8 @@ 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_PRE_COLOR_TRANSFORM |
WLR_OUTPUT_STATE_POST_COLOR_TRANSFORM |
WLR_OUTPUT_STATE_IMAGE_DESCRIPTION |
WLR_OUTPUT_STATE_COLOR_REPRESENTATION;
@ -158,6 +159,86 @@ 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;
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;
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;
}
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 +321,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 +468,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 +500,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);
@ -880,8 +979,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;

View file

@ -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;
}
@ -125,12 +131,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;
}

View file

@ -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);
@ -216,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;
}

View file

@ -20,6 +20,18 @@ struct wlr_drm_viewport {
struct wlr_box dst_box;
};
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
uint32_t data; // for 1D_LUT, CTM_3X4, 3D_LUT
};
struct wlr_drm_plane {
uint32_t type;
uint32_t id;
@ -43,11 +55,17 @@ 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;
struct liftoff_plane *liftoff;
struct liftoff_layer *liftoff_layer;
// Atomic modesetting only
uint32_t color_pipeline;
};
struct wlr_drm_layer {
@ -164,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
};
/**

View file

@ -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,33 @@ 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;
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);
@ -94,5 +118,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

View file

@ -75,9 +75,10 @@ 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,
WLR_OUTPUT_STATE_PRE_COLOR_TRANSFORM = 1 << 15,
};
enum wlr_output_state_mode_type {
@ -162,7 +163,10 @@ struct wlr_output_state {
struct wlr_drm_syncobj_timeline *signal_timeline;
uint64_t signal_point;
struct wlr_color_transform *color_transform;
// Pre-blending color transform
struct wlr_color_transform *pre_color_transform;
// Post-blending color transform
struct wlr_color_transform *post_color_transform;
struct wlr_output_image_description *image_description;
};
@ -276,7 +280,8 @@ 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 *pre_color_transform;
struct wlr_color_transform *post_color_transform;
struct wlr_color_primaries default_primaries_value;
} WLR_PRIVATE;
};
@ -619,11 +624,18 @@ 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 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.
*
* 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);
/**

View file

@ -252,12 +252,21 @@ 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_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->color_transform = NULL;
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) {
output->post_color_transform = wlr_color_transform_ref(state->post_color_transform);
} else {
output->post_color_transform = NULL;
}
}
@ -426,7 +435,8 @@ 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->pre_color_transform);
wlr_color_transform_unref(output->post_color_transform);
wlr_swapchain_destroy(output->swapchain);
@ -581,9 +591,13 @@ 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_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;
}
if ((state->committed & WLR_OUTPUT_STATE_COLOR_REPRESENTATION) &&
output->color_encoding == state->color_encoding &&
@ -685,7 +699,8 @@ 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_PRE_COLOR_TRANSFORM, "pre color transform" },
{ WLR_OUTPUT_STATE_POST_COLOR_TRANSFORM, "post color transform" },
{ WLR_OUTPUT_STATE_IMAGE_DESCRIPTION, "image description" },
};
if (!enabled) {

View file

@ -19,7 +19,8 @@ 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->pre_color_transform);
wlr_color_transform_unref(state->post_color_transform);
free(state->image_description);
}
@ -113,14 +114,25 @@ 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_pre_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_PRE_COLOR_TRANSFORM;
wlr_color_transform_unref(state->pre_color_transform);
if (tr) {
state->color_transform = wlr_color_transform_ref(tr);
state->pre_color_transform = wlr_color_transform_ref(tr);
} else {
state->color_transform = NULL;
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;
wlr_color_transform_unref(state->post_color_transform);
if (tr) {
state->post_color_transform = wlr_color_transform_ref(tr);
} else {
state->post_color_transform = NULL;
}
}
@ -156,7 +168,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 +176,8 @@ bool wlr_output_state_copy(struct wlr_output_state *dst,
pixman_region32_init(&copy.damage);
copy.wait_timeline = NULL;
copy.signal_timeline = NULL;
copy.color_transform = NULL;
copy.pre_color_transform = NULL;
copy.post_color_transform = NULL;
copy.image_description = NULL;
if (src->committed & WLR_OUTPUT_STATE_BUFFER) {
@ -186,8 +199,11 @@ 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(&copy, src->color_transform);
if (src->committed & WLR_OUTPUT_STATE_PRE_COLOR_TRANSFORM) {
wlr_output_state_set_pre_color_transform(&copy, src->pre_color_transform);
}
if (src->committed & WLR_OUTPUT_STATE_POST_COLOR_TRANSFORM) {
wlr_output_state_set_post_color_transform(&copy, src->post_color_transform);
}
if (src->committed & WLR_OUTPUT_STATE_IMAGE_DESCRIPTION) {
if (!wlr_output_state_set_image_description(&copy, src->image_description)) {

View file

@ -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)) {

View file

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