diff --git a/include/render/vulkan.h b/include/render/vulkan.h index 021749c27..889d60d8a 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -374,7 +374,6 @@ struct wlr_vk_vert_pcr_data { struct wlr_vk_frag_texture_pcr_data { float matrix[4][4]; // only a 3x3 subset is used float alpha; - float luminance_multiplier; }; struct wlr_vk_frag_output_pcr_data { diff --git a/include/wlr/render/pass.h b/include/wlr/render/pass.h index 61ab77132..407e65366 100644 --- a/include/wlr/render/pass.h +++ b/include/wlr/render/pass.h @@ -101,16 +101,12 @@ struct wlr_render_texture_options { enum wlr_scale_filter_mode filter_mode; /* Blend mode */ enum wlr_render_blend_mode blend_mode; - /* Transfer function the source texture is encoded with */ - enum wlr_color_transfer_function transfer_function; - /* Primaries describing the color volume of the source texture */ - const struct wlr_color_primaries *primaries; + /* Applied to convert from source texture to blend space */ + struct wlr_color_transform *color_transform; /* Color encoding of the source texture for YCbCr conversion to RGB */ enum wlr_color_encoding color_encoding; /* Color range of the source texture */ enum wlr_color_range color_range; - /* Default: 1.0 */ - const float *luminance_multiplier; /* Wait for a timeline synchronization point before texturing. * diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index c8e44b0e6..654cedaff 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -53,7 +53,8 @@ struct wlr_output_cursor { struct { struct wl_listener renderer_destroy; - struct wlr_color_transform *color_transform; + struct wlr_color_transform *color_transform_linearize; + struct wlr_color_transform *color_transform_encode; } WLR_PRIVATE; }; diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 6f1702583..d2b740ee5 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -134,7 +134,7 @@ static bool render_pass_wait_render_buffer(struct wlr_vk_render_pass *pass, return true; } -static bool unwrap_color_transform(struct wlr_color_transform *transform, +static bool unwrap_output_color_transform(struct wlr_color_transform *transform, float matrix[static 9], enum wlr_color_transfer_function *tf) { if (transform == NULL) { wlr_matrix_identity(matrix); @@ -175,6 +175,47 @@ static bool unwrap_color_transform(struct wlr_color_transform *transform, return false; } +static bool unwrap_texture_color_transform(struct wlr_color_transform *transform, + float matrix[static 9], enum wlr_color_transfer_function *tf) { + if (transform == NULL) { + wlr_matrix_identity(matrix); + *tf = WLR_COLOR_TRANSFER_FUNCTION_GAMMA22; + return true; + } + struct wlr_color_transform_eotf *eotf; + struct wlr_color_transform_matrix *as_matrix; + struct wlr_color_transform_pipeline *pipeline; + switch (transform->type) { + case COLOR_TRANSFORM_EOTF: + eotf = wlr_color_transform_eotf_from_base(transform); + wlr_matrix_identity(matrix); + *tf = eotf->tf; + return true; + case COLOR_TRANSFORM_MATRIX: + as_matrix = wl_container_of(transform, as_matrix, base); + memcpy(matrix, as_matrix->matrix, sizeof(float[9])); + *tf = WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR; + return true; + case COLOR_TRANSFORM_PIPELINE: + pipeline = wl_container_of(transform, pipeline, base); + if (pipeline->len != 2 + || pipeline->transforms[0]->type != COLOR_TRANSFORM_EOTF + || pipeline->transforms[1]->type != COLOR_TRANSFORM_MATRIX) { + return false; + } + eotf = wlr_color_transform_eotf_from_base(pipeline->transforms[0]); + *tf = eotf->tf; + as_matrix = wl_container_of(pipeline->transforms[1], as_matrix, base); + memcpy(matrix, as_matrix->matrix, sizeof(float[9])); + return true; + case COLOR_TRANSFORM_INVERSE_EOTF: + case COLOR_TRANSFORM_LCMS2: + case COLOR_TRANSFORM_LUT_3X1D: + return false; + } + return false; +} + static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { struct wlr_vk_render_pass *pass = get_render_pass(wlr_pass); struct wlr_vk_renderer *renderer = pass->renderer; @@ -789,9 +830,12 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, }; encode_proj_matrix(matrix, vert_pcr_data.mat4); - enum wlr_color_transfer_function tf = options->transfer_function; - if (tf == 0) { + enum wlr_color_transfer_function tf; + float color_matrix[9]; + if (!unwrap_texture_color_transform(options->color_transform, color_matrix, &tf)) + { tf = WLR_COLOR_TRANSFER_FUNCTION_GAMMA22; + wlr_matrix_identity(color_matrix); } bool srgb_image_view = false; @@ -858,25 +902,8 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, return; } - float color_matrix[9]; - if (options->primaries != NULL) { - struct wlr_color_primaries srgb; - wlr_color_primaries_from_named(&srgb, WLR_COLOR_NAMED_PRIMARIES_SRGB); - - wlr_color_primaries_transform_absolute_colorimetric(options->primaries, - &srgb, color_matrix); - } else { - wlr_matrix_identity(color_matrix); - } - - float luminance_multiplier = 1; - if (options->luminance_multiplier != NULL) { - luminance_multiplier = *options->luminance_multiplier; - } - struct wlr_vk_frag_texture_pcr_data frag_pcr_data = { .alpha = alpha, - .luminance_multiplier = luminance_multiplier, }; encode_color_matrix(color_matrix, frag_pcr_data.matrix); @@ -1147,7 +1174,7 @@ static struct wlr_vk_color_transform *vk_color_transform_create( return NULL; } - bool need_lut = !unwrap_color_transform(transform, vk_transform->color_matrix, + bool need_lut = !unwrap_output_color_transform(transform, vk_transform->color_matrix, &vk_transform->inverse_eotf); if (need_lut) { diff --git a/render/vulkan/shaders/texture.frag b/render/vulkan/shaders/texture.frag index 3ec974235..03c24eceb 100644 --- a/render/vulkan/shaders/texture.frag +++ b/render/vulkan/shaders/texture.frag @@ -9,7 +9,6 @@ layout(location = 0) out vec4 out_color; layout(push_constant, row_major) uniform UBO { layout(offset = 80) mat4 matrix; float alpha; - float luminance_multiplier; } data; layout (constant_id = 0) const int TEXTURE_TRANSFORM = 0; @@ -82,8 +81,6 @@ void main() { rgb = bt1886_color_to_linear(rgb); } - rgb *= data.luminance_multiplier; - rgb = mat3(data.matrix) * rgb; // Back to pre-multiplied alpha diff --git a/types/output/cursor.c b/types/output/cursor.c index 4a823dab1..58f58752f 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -256,7 +256,7 @@ static struct wlr_buffer *render_cursor_buffer(struct wlr_output_cursor *cursor) buffer->width, buffer->height); struct wlr_buffer_pass_options options = { - .color_transform = cursor->color_transform, + .color_transform = cursor->color_transform_encode, }; struct wlr_render_pass *pass = wlr_renderer_begin_buffer_pass(renderer, buffer, &options); if (pass == NULL) { @@ -278,6 +278,7 @@ static struct wlr_buffer *render_cursor_buffer(struct wlr_output_cursor *cursor) .transform = transform, .wait_timeline = cursor->wait_timeline, .wait_point = cursor->wait_point, + .color_transform = cursor->color_transform_linearize, }); if (!wlr_render_pass_submit(pass)) { @@ -493,18 +494,27 @@ void wlr_output_cursor_destroy(struct wlr_output_cursor *cursor) { } wlr_drm_syncobj_timeline_unref(cursor->wait_timeline); wl_list_remove(&cursor->link); - wlr_color_transform_unref(cursor->color_transform); + wlr_color_transform_unref(cursor->color_transform_linearize); + wlr_color_transform_unref(cursor->color_transform_encode); free(cursor); } bool output_cursor_refresh_color_transform(struct wlr_output_cursor *output_cursor, const struct wlr_output_image_description *img_desc) { - wlr_color_transform_unref(output_cursor->color_transform); - output_cursor->color_transform = NULL; + wlr_color_transform_unref(output_cursor->color_transform_linearize); + output_cursor->color_transform_linearize = NULL; + wlr_color_transform_unref(output_cursor->color_transform_encode); + output_cursor->color_transform_encode = NULL; if (img_desc == NULL) { return true; } + output_cursor->color_transform_linearize = wlr_color_transform_init_eotf_to_linear( + WLR_COLOR_TRANSFER_FUNCTION_GAMMA22); + if (output_cursor->color_transform_linearize == NULL) { + goto err; + } + struct wlr_color_primaries primaries_srgb; wlr_color_primaries_from_named(&primaries_srgb, WLR_COLOR_NAMED_PRIMARIES_SRGB); struct wlr_color_primaries primaries; @@ -525,13 +535,22 @@ bool output_cursor_refresh_color_transform(struct wlr_output_cursor *output_curs wlr_color_transform_init_linear_to_inverse_eotf(img_desc->transfer_function), }; if (transforms[0] == NULL || transforms[1] == NULL) { - wlr_color_transform_unref(transforms[0]); - wlr_color_transform_unref(transforms[1]); - return false; + goto err; } - output_cursor->color_transform = wlr_color_transform_init_pipeline(transforms, + output_cursor->color_transform_encode = wlr_color_transform_init_pipeline(transforms, sizeof(transforms) / sizeof(transforms[0])); + if (output_cursor->color_transform_encode == NULL) { + goto err; + } + wlr_color_transform_unref(transforms[0]); wlr_color_transform_unref(transforms[1]); - return output_cursor->color_transform != NULL; + return true; + +err: + wlr_color_transform_unref(output_cursor->color_transform_linearize); + output_cursor->color_transform_linearize = NULL; + wlr_color_transform_unref(transforms[0]); + wlr_color_transform_unref(transforms[1]); + return false; } diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 7231422e8..c086f8676 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -20,6 +20,7 @@ #include "types/wlr_scene.h" #include "util/array.h" #include "util/env.h" +#include "util/matrix.h" #include "util/time.h" #include @@ -1411,6 +1412,63 @@ static float get_luminance_multiplier(const struct wlr_color_luminances *src_lum return (dst_lum->reference / src_lum->reference) * (src_lum->max / dst_lum->max); } +static struct wlr_color_transform *scene_texture_to_blend_space( + struct wlr_scene_buffer *source, enum wlr_color_named_primaries dest_primaries, + const struct wlr_color_luminances *dest_luminance) { + struct wlr_color_transform *color_matrix = NULL; + struct wlr_color_transform *eotf = NULL; + struct wlr_color_transform *combined = NULL; + + enum wlr_color_transfer_function source_tf = WLR_COLOR_TRANSFER_FUNCTION_GAMMA22; + if (source->transfer_function != 0) { + source_tf = source->transfer_function; + } + eotf = wlr_color_transform_init_eotf_to_linear(source_tf); + if (eotf == NULL) { + goto cleanup_transforms; + } + + struct wlr_color_luminances source_lum; + wlr_color_transfer_function_get_default_luminance(source_tf, &source_lum); + float luminance_multiplier = get_luminance_multiplier(&source_lum, dest_luminance); + + float matrix[9]; + + enum wlr_color_named_primaries source_primaries = WLR_COLOR_NAMED_PRIMARIES_SRGB; + if (source->primaries != 0) { + source_primaries = source->primaries; + } + if (source_primaries != dest_primaries) { + struct wlr_color_primaries primaries; + wlr_color_primaries_from_named(&primaries, source_primaries); + struct wlr_color_primaries primaries_blend; + wlr_color_primaries_from_named(&primaries_blend, dest_primaries); + wlr_color_primaries_transform_absolute_colorimetric(&primaries, &primaries_blend, matrix); + } else { + wlr_matrix_identity(matrix); + } + + for (int i = 0; i < 9; ++i) { + matrix[i] *= luminance_multiplier; + } + color_matrix = wlr_color_transform_init_matrix(matrix); + if (color_matrix == NULL) { + goto cleanup_transforms; + } + + struct wlr_color_transform *transforms[] = { + eotf, + color_matrix, + }; + const size_t transforms_len = sizeof(transforms) / sizeof(transforms[0]); + color_transform_compose(&combined, transforms, transforms_len); + +cleanup_transforms: + wlr_color_transform_unref(eotf); + wlr_color_transform_unref(color_matrix); + return combined; +} + static void scene_entry_render(struct render_list_entry *entry, const struct render_data *data) { struct wlr_scene_node *node = entry->node; @@ -1489,17 +1547,14 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren wlr_output_transform_invert(scene_buffer->transform); transform = wlr_output_transform_compose(transform, data->transform); - struct wlr_color_primaries primaries = {0}; - if (scene_buffer->primaries != 0) { - wlr_color_primaries_from_named(&primaries, scene_buffer->primaries); - } + struct wlr_color_transform *source_to_blend = NULL; + if (data->output->output->renderer->features.input_color_transform) { + struct wlr_color_luminances srgb_lum; + wlr_color_transfer_function_get_default_luminance(WLR_COLOR_TRANSFER_FUNCTION_SRGB, &srgb_lum); - struct wlr_color_luminances src_lum, srgb_lum; - wlr_color_transfer_function_get_default_luminance( - scene_buffer->transfer_function, &src_lum); - wlr_color_transfer_function_get_default_luminance( - WLR_COLOR_TRANSFER_FUNCTION_SRGB, &srgb_lum); - float luminance_multiplier = get_luminance_multiplier(&src_lum, &srgb_lum); + source_to_blend = scene_texture_to_blend_space( + scene_buffer, WLR_COLOR_NAMED_PRIMARIES_SRGB, &srgb_lum); + } wlr_render_pass_add_texture(data->render_pass, &(struct wlr_render_texture_options) { .texture = texture, @@ -1512,15 +1567,15 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren .blend_mode = !data->output->scene->calculate_visibility || !pixman_region32_empty(&opaque) ? WLR_RENDER_BLEND_MODE_PREMULTIPLIED : WLR_RENDER_BLEND_MODE_NONE, - .transfer_function = scene_buffer->transfer_function, - .primaries = scene_buffer->primaries != 0 ? &primaries : NULL, + .color_transform = source_to_blend, .color_encoding = scene_buffer->color_encoding, .color_range = scene_buffer->color_range, - .luminance_multiplier = &luminance_multiplier, .wait_timeline = scene_buffer->wait_timeline, .wait_point = scene_buffer->wait_point, }); + wlr_color_transform_unref(source_to_blend); + struct wlr_scene_output_sample_event sample_event = { .output = data->output, .direct_scanout = false,