diff --git a/include/render/vulkan.h b/include/render/vulkan.h index b4a2852af..530fa1d27 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -163,7 +163,8 @@ enum wlr_vk_shader_source { // fragment shader. Must match those in shaders/output.frag enum wlr_vk_output_transform { WLR_VK_OUTPUT_TRANSFORM_INVERSE_SRGB = 0, - WLR_VK_OUTPUT_TRANSFORM_LUT3D = 1, + WLR_VK_OUTPUT_TRANSFORM_INVERSE_ST2084_PQ = 1, + WLR_VK_OUTPUT_TRANSFORM_LUT3D = 2, }; struct wlr_vk_pipeline_key { @@ -193,6 +194,7 @@ struct wlr_vk_render_format_setup { VkRenderPass render_pass; VkPipeline output_pipe_srgb; + VkPipeline output_pipe_pq; VkPipeline output_pipe_lut3d; struct wlr_vk_renderer *renderer; diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index b2feca013..98701626f 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -159,6 +159,7 @@ static void destroy_render_format_setup(struct wlr_vk_renderer *renderer, VkDevice dev = renderer->dev->dev; vkDestroyRenderPass(dev, setup->render_pass, NULL); vkDestroyPipeline(dev, setup->output_pipe_srgb, NULL); + vkDestroyPipeline(dev, setup->output_pipe_pq, NULL); vkDestroyPipeline(dev, setup->output_pipe_lut3d, NULL); struct wlr_vk_pipeline *pipeline, *tmp_pipeline; @@ -2299,8 +2300,13 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup( goto error; } if (!init_blend_to_output_pipeline( - renderer, setup->render_pass, renderer->output_pipe_layout, - &setup->output_pipe_srgb, WLR_VK_OUTPUT_TRANSFORM_INVERSE_SRGB)) { + renderer, setup->render_pass, renderer->output_pipe_layout, + &setup->output_pipe_srgb, WLR_VK_OUTPUT_TRANSFORM_INVERSE_SRGB)) { + goto error; + } + if (!init_blend_to_output_pipeline( + renderer, setup->render_pass, renderer->output_pipe_layout, + &setup->output_pipe_pq, WLR_VK_OUTPUT_TRANSFORM_INVERSE_ST2084_PQ)) { goto error; } } else { diff --git a/render/vulkan/shaders/output.frag b/render/vulkan/shaders/output.frag index 783817707..7478e249d 100644 --- a/render/vulkan/shaders/output.frag +++ b/render/vulkan/shaders/output.frag @@ -18,7 +18,8 @@ layout (constant_id = 0) const int OUTPUT_TRANSFORM = 0; // Matches enum wlr_vk_output_transform #define OUTPUT_TRANSFORM_INVERSE_SRGB 0 -#define OUTPUT_TRANSFORM_LUT_3D 1 +#define OUTPUT_TRANSFORM_INVERSE_ST2084_PQ 1 +#define OUTPUT_TRANSFORM_LUT_3D 2 float linear_channel_to_srgb(float x) { return max(min(x * 12.92, 0.04045), 1.055 * pow(x, 1. / 2.4) - 0.055); @@ -32,6 +33,17 @@ vec3 linear_color_to_srgb(vec3 color) { ); } +vec3 linear_color_to_pq(vec3 color) { + // H.273 TransferCharacteristics code point 16 + float c1 = 0.8359375; + float c2 = 18.8515625; + float c3 = 18.6875; + float m = 78.84375; + float n = 0.1593017578125; + vec3 pow_n = pow(clamp(color, vec3(0), vec3(1)), vec3(n)); + return pow((vec3(c1) + c2 * pow_n) / (vec3(1) + c3 * pow_n), vec3(m)); +} + void main() { vec4 in_color = subpassLoad(in_color).rgba; @@ -50,6 +62,8 @@ void main() { // Apply 3D LUT vec3 pos = data.lut_3d_offset + rgb * data.lut_3d_scale; rgb = texture(lut_3d, pos).rgb; + } else if (OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_INVERSE_ST2084_PQ) { + rgb = linear_color_to_pq(rgb); } else { // OUTPUT_TRANSFORM_INVERSE_SRGB // Produce sRGB encoded values rgb = linear_color_to_srgb(rgb);