vulkan: perform sRGB-to-linear conversion correctly for premultiplied values

Pre-multipled sRGB values need to be un-multiplied before conversion
to linear and then re-multiplied after. Compare shaders/texture.frag.

This fixes an issue in labwc where titlebar corners (rendered as
ARGB textures) did not match the rest of the titlebar (rendered as
a solid wlr_scene_rect).
This commit is contained in:
John Lindgren 2024-04-07 02:00:14 -04:00
parent 6dce6ae2ed
commit d98a3eb492
2 changed files with 17 additions and 9 deletions

View file

@ -48,6 +48,10 @@ static float color_to_linear(float non_linear) {
non_linear / 12.92; non_linear / 12.92;
} }
static float color_to_linear_premult(float non_linear, float alpha) {
return (alpha == 0) ? 0 : color_to_linear(non_linear / alpha) * alpha;
}
static void mat3_to_mat4(const float mat3[9], float mat4[4][4]) { static void mat3_to_mat4(const float mat3[9], float mat4[4][4]) {
memset(mat4, 0, sizeof(float) * 16); memset(mat4, 0, sizeof(float) * 16);
mat4[0][0] = mat3[0]; mat4[0][0] = mat3[0];
@ -439,9 +443,9 @@ static void render_pass_add_rect(struct wlr_render_pass *wlr_pass,
// colors in linear space as well (and vulkan then automatically // colors in linear space as well (and vulkan then automatically
// does the conversion for out sRGB render targets). // does the conversion for out sRGB render targets).
float linear_color[] = { float linear_color[] = {
color_to_linear(options->color.r), color_to_linear_premult(options->color.r, options->color.a),
color_to_linear(options->color.g), color_to_linear_premult(options->color.g, options->color.a),
color_to_linear(options->color.b), color_to_linear_premult(options->color.b, options->color.a),
options->color.a, // no conversion for alpha options->color.a, // no conversion for alpha
}; };

View file

@ -67,6 +67,10 @@ static float color_to_linear(float non_linear) {
non_linear / 12.92; non_linear / 12.92;
} }
static float color_to_linear_premult(float non_linear, float alpha) {
return (alpha == 0) ? 0 : color_to_linear(non_linear / alpha) * alpha;
}
static void mat3_to_mat4(const float mat3[9], float mat4[4][4]) { static void mat3_to_mat4(const float mat3[9], float mat4[4][4]) {
memset(mat4, 0, sizeof(float) * 16); memset(mat4, 0, sizeof(float) * 16);
mat4[0][0] = mat3[0]; mat4[0][0] = mat3[0];
@ -1475,9 +1479,9 @@ static void vulkan_clear(struct wlr_renderer *wlr_renderer,
// But in other parts of wlroots we just always assume // But in other parts of wlroots we just always assume
// srgb so that's why we have to convert here. // srgb so that's why we have to convert here.
.clearValue.color.float32 = { .clearValue.color.float32 = {
color_to_linear(color[0]), color_to_linear_premult(color[0], color[3]),
color_to_linear(color[1]), color_to_linear_premult(color[1], color[3]),
color_to_linear(color[2]), color_to_linear_premult(color[2], color[3]),
color[3], // no conversion for alpha color[3], // no conversion for alpha
}, },
}; };
@ -1553,9 +1557,9 @@ static void vulkan_render_quad_with_matrix(struct wlr_renderer *wlr_renderer,
// But in other parts of wlroots we just always assume // But in other parts of wlroots we just always assume
// srgb so that's why we have to convert here. // srgb so that's why we have to convert here.
float linear_color[4]; float linear_color[4];
linear_color[0] = color_to_linear(color[0]); linear_color[0] = color_to_linear_premult(color[0], color[3]);
linear_color[1] = color_to_linear(color[1]); linear_color[1] = color_to_linear_premult(color[1], color[3]);
linear_color[2] = color_to_linear(color[2]); linear_color[2] = color_to_linear_premult(color[2], color[3]);
linear_color[3] = color[3]; // no conversion for alpha linear_color[3] = color[3]; // no conversion for alpha
vkCmdPushConstants(cb, pipe->layout->vk, vkCmdPushConstants(cb, pipe->layout->vk,