VULKAN: Build SDR display pipeline set for HDR offscreen compositing

Follow-up to d250194 (build display pipelines against swapchain
render pass).  That commit fixed the KMS crash (#18761) by
building all display/menu pipelines against vk->render_pass
(the swapchain format) instead of vk->sdr_render_pass
(hardcoded B8G8R8A8_UNORM).  This was correct for the non-HDR
path but left a gap in the HDR offscreen compositing path.

When HDR is active, the menu is rendered into a B8G8R8A8
offscreen buffer under sdr_render_pass, then composited onto
the HDR swapchain.  If the swapchain format differs from
B8G8R8A8 (which is the common HDR case -- the swapchain uses
a wide-gamut format like R16G16B16A16_SFLOAT or
A2B10G10R10_UNORM), the main pipeline set (built against
render_pass) is incompatible with sdr_render_pass.  Using
those pipelines in the HDR offscreen render pass would be the
same class of render-pass compatibility violation that d250194
fixed for the non-HDR path.

Fix: when VK_CTX_FLAG_HDR_SUPPORT is set, build a parallel set
of display/menu pipelines against sdr_render_pass.  At runtime,
switch to the SDR pipeline variants while rendering into the
HDR offscreen buffer, and switch back when the render pass ends.

New storage:
  - vk->pipelines.font_sdr
  - vk->pipelines.alpha_blend_sdr
  - vk->display.pipelines_sdr[9 * 2]

These are created alongside the existing pipeline set during
vulkan_init_pipelines, using identical shader modules and
pipeline state -- only pipe.renderPass differs.  Destroyed
in vulkan_deinit_pipelines.

Runtime switching via VK_FLAG_SDR_PIPELINE (new flag, bit 16):
  - Set after vkCmdBeginRenderPass with sdr_render_pass on the
    HDR offscreen compositing path
  - Cleared before vkCmdEndRenderPass
  - Checked in four draw paths:
    * gfx_display_vk_draw menu shader case (ribbon/snow/bokeh)
    * gfx_display_vk_draw default case (display quads)
    * vulkan_raster_font_render_msg (font pipeline)
    * vulkan_frame menu texture overlay (alpha_blend pipeline)

Non-HDR builds: all SDR pipeline code is #ifdef
VULKAN_HDR_SWAPCHAIN guarded.  Zero impact on builds without
HDR support.

Non-HDR runtime: VK_FLAG_SDR_PIPELINE is never set.  All draw
paths use the main pipeline set unconditionally.  Zero overhead.

HDR with B8G8R8A8 swapchain (unusual but possible):
render_pass and sdr_render_pass have the same format, so both
pipeline sets are identical.  No visual difference, small extra
memory for the duplicate pipelines.

Cost: 18 additional VkPipeline objects + 2 named pipelines when
HDR is supported.  One-time creation cost at driver init.
Runtime cost is one flag check per draw call on HDR paths only.
This commit is contained in:
U-DESKTOP-SPFP6AQ\twistedtechre committed 2026-04-16 17:31:25 +02:00
1 parent d2501944bd
commit 8fa8418762
2 files changed
+196 -6

No files matched your search

+2 -1
View File
@@ -107,7 +107,8 @@ enum vk_flags
VK_FLAG_READBACK_PENDING = (1 << 12),
VK_FLAG_READBACK_STREAMED = (1 << 13),
VK_FLAG_OVERLAY_ENABLE = (1 << 14),
VK_FLAG_OVERLAY_FULLSCREEN = (1 << 15)
VK_FLAG_OVERLAY_FULLSCREEN = (1 << 15),
VK_FLAG_SDR_PIPELINE = (1 << 16)
};
enum vk_texture_type
+194 -5
View File
@@ -288,6 +288,8 @@ typedef struct vk
#ifdef VULKAN_HDR_SWAPCHAIN
VkPipeline hdr;
VkPipeline hdr_to_sdr; /* for readback */
VkPipeline alpha_blend_sdr; /* for SDR offscreen compositing */
VkPipeline font_sdr; /* for SDR offscreen compositing */
#endif /* VULKAN_HDR_SWAPCHAIN */
VkDescriptorSetLayout set_layout;
VkPipelineLayout layout;
@@ -297,6 +299,9 @@ typedef struct vk
struct
{
VkPipeline pipelines[9 * 2];
#ifdef VULKAN_HDR_SWAPCHAIN
VkPipeline pipelines_sdr[9 * 2]; /* SDR offscreen variants */
#endif
struct vk_texture blank_texture;
} display;
@@ -1946,9 +1951,15 @@ static void gfx_display_vk_draw(gfx_display_ctx_draw_t *draw,
case VIDEO_SHADER_MENU_6:
{
struct vk_draw_triangles call;
unsigned idx = to_menu_pipeline(draw->prim_type, draw->pipeline_id);
call.pipeline = vk->display.pipelines[
to_menu_pipeline(draw->prim_type, draw->pipeline_id)];
#ifdef VULKAN_HDR_SWAPCHAIN
call.pipeline = (vk->flags & VK_FLAG_SDR_PIPELINE)
? vk->display.pipelines_sdr[idx]
: vk->display.pipelines[idx];
#else
call.pipeline = vk->display.pipelines[idx];
#endif
call.texture = NULL;
call.sampler = VK_NULL_HANDLE;
call.uniform = draw->backend_data;
@@ -1969,7 +1980,13 @@ static void gfx_display_vk_draw(gfx_display_ctx_draw_t *draw,
disp_pipeline =
((draw->prim_type == GFX_DISPLAY_PRIM_TRIANGLESTRIP) << 1)
| (((vk->flags & VK_FLAG_DISPLAY_BLEND) > 0) << 0);
#ifdef VULKAN_HDR_SWAPCHAIN
call.pipeline = (vk->flags & VK_FLAG_SDR_PIPELINE)
? vk->display.pipelines_sdr[disp_pipeline]
: vk->display.pipelines[disp_pipeline];
#else
call.pipeline = vk->display.pipelines[disp_pipeline];
#endif
call.texture = texture;
call.sampler = (texture->flags & VK_TEX_FLAG_MIPMAP)
? vk->samplers.mipmap_linear
@@ -2643,12 +2660,20 @@ static void vulkan_font_render_msg(
vulkan_transition_texture(vk, vk->cmd, &font->texture_optimal);
/* Pipeline and dynamic state. */
if (vk->pipelines.font != vk->tracker.pipeline)
{
#ifdef VULKAN_HDR_SWAPCHAIN
VkPipeline font_pipe = (vk->flags & VK_FLAG_SDR_PIPELINE)
? vk->pipelines.font_sdr
: vk->pipelines.font;
#else
VkPipeline font_pipe = vk->pipelines.font;
#endif
if (font_pipe != vk->tracker.pipeline)
{
VkRect2D sci;
vkCmdBindPipeline(vk->cmd,
VK_PIPELINE_BIND_POINT_GRAPHICS, vk->pipelines.font);
vk->tracker.pipeline = vk->pipelines.font;
VK_PIPELINE_BIND_POINT_GRAPHICS, font_pipe);
vk->tracker.pipeline = font_pipe;
vk->tracker.dirty |= VULKAN_DIRTY_DYNAMIC_BIT;
if (vk->flags & VK_FLAG_TRACKER_USE_SCISSOR)
@@ -2682,6 +2707,7 @@ static void vulkan_font_render_msg(
vkCmdSetScissor (vk->cmd, 0, 1, &sci);
vk->tracker.dirty &= ~VULKAN_DIRTY_DYNAMIC_BIT;
}
} /* end of font_pipe scope */
/* Descriptor set: UBO (mvp) + combined image sampler (font atlas). */
{
@@ -3619,6 +3645,148 @@ static void vulkan_init_pipelines(vk_t *vk)
vkDestroyShaderModule(vk->context->device, shader_stages[1].module, NULL);
}
#ifdef VULKAN_HDR_SWAPCHAIN
/* When HDR is supported, the menu is rendered into an SDR
* offscreen buffer (B8G8R8A8_UNORM) under sdr_render_pass,
* then composited onto the HDR swapchain. If the swapchain
* format differs from B8G8R8A8, the main pipeline set (built
* against render_pass) is incompatible with sdr_render_pass.
* Build a parallel SDR set against sdr_render_pass. */
if (vk->context->flags & VK_CTX_FLAG_HDR_SUPPORT)
{
pipe.renderPass = vk->sdr_render_pass;
/* SDR font pipeline */
module_info.codeSize = sizeof(alpha_blend_vert);
module_info.pCode = alpha_blend_vert;
shader_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
shader_stages[0].pName = "main";
vkCreateShaderModule(vk->context->device,
&module_info, NULL, &shader_stages[0].module);
module_info.codeSize = sizeof(font_frag);
module_info.pCode = font_frag;
shader_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
shader_stages[1].pName = "main";
vkCreateShaderModule(vk->context->device,
&module_info, NULL, &shader_stages[1].module);
blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
blend_attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
blend_attachment.blendEnable = VK_TRUE;
vkCreateGraphicsPipelines(vk->context->device, vk->pipelines.cache,
1, &pipe, NULL, &vk->pipelines.font_sdr);
vkDestroyShaderModule(vk->context->device, shader_stages[1].module, NULL);
/* SDR alpha_blend pipeline */
module_info.codeSize = sizeof(alpha_blend_frag);
module_info.pCode = alpha_blend_frag;
shader_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
shader_stages[1].pName = "main";
vkCreateShaderModule(vk->context->device,
&module_info, NULL, &shader_stages[1].module);
vkCreateGraphicsPipelines(vk->context->device, vk->pipelines.cache,
1, &pipe, NULL, &vk->pipelines.alpha_blend_sdr);
vkDestroyShaderModule(vk->context->device, shader_stages[1].module, NULL);
/* SDR display pipelines 0-3 */
for (i = 0; i < 4; i++)
{
input_assembly.topology = i & 2 ?
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP :
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
blend_attachment.blendEnable = i & 1;
vkCreateGraphicsPipelines(vk->context->device, vk->pipelines.cache,
1, &pipe, NULL, &vk->display.pipelines_sdr[i]);
}
/* SDR menu shader pipelines 6+ */
for (i = 0; i < (int)ARRAY_SIZE(vk->display.pipelines) - 6; i++)
{
switch (i >> 1)
{
case 0:
module_info.codeSize = sizeof(pipeline_ribbon_vert);
module_info.pCode = pipeline_ribbon_vert;
break;
case 1:
module_info.codeSize = sizeof(pipeline_ribbon_simple_vert);
module_info.pCode = pipeline_ribbon_simple_vert;
break;
default:
module_info.codeSize = sizeof(alpha_blend_vert);
module_info.pCode = alpha_blend_vert;
break;
}
shader_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
shader_stages[0].pName = "main";
vkCreateShaderModule(vk->context->device,
&module_info, NULL, &shader_stages[0].module);
switch (i >> 1)
{
case 0:
module_info.codeSize = sizeof(pipeline_ribbon_frag);
module_info.pCode = pipeline_ribbon_frag;
break;
case 1:
module_info.codeSize = sizeof(pipeline_ribbon_simple_frag);
module_info.pCode = pipeline_ribbon_simple_frag;
break;
case 2:
module_info.codeSize = sizeof(pipeline_snow_simple_frag);
module_info.pCode = pipeline_snow_simple_frag;
break;
case 3:
module_info.codeSize = sizeof(pipeline_snow_frag);
module_info.pCode = pipeline_snow_frag;
break;
case 4:
module_info.codeSize = sizeof(pipeline_bokeh_frag);
module_info.pCode = pipeline_bokeh_frag;
break;
case 5:
module_info.codeSize = sizeof(pipeline_snowflake_frag);
module_info.pCode = pipeline_snowflake_frag;
break;
default:
break;
}
shader_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
shader_stages[1].pName = "main";
vkCreateShaderModule(vk->context->device,
&module_info, NULL, &shader_stages[1].module);
switch (i >> 1)
{
case 0:
case 1:
blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_DST_COLOR;
blend_attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
break;
default:
blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
blend_attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
break;
}
input_assembly.topology = i & 1 ?
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP :
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
vkCreateGraphicsPipelines(vk->context->device, vk->pipelines.cache,
1, &pipe, NULL, &vk->display.pipelines_sdr[6 + i]);
vkDestroyShaderModule(vk->context->device, shader_stages[0].module, NULL);
vkDestroyShaderModule(vk->context->device, shader_stages[1].module, NULL);
}
vkDestroyShaderModule(vk->context->device, shader_stages[0].module, NULL);
}
#endif /* VULKAN_HDR_SWAPCHAIN */
cpipe.layout = vk->pipelines.layout;
cpipe.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
cpipe.stage.pName = "main";
@@ -3817,6 +3985,14 @@ if (vk->context->flags & VK_CTX_FLAG_HDR_SUPPORT)
vk->pipelines.hdr, NULL);
vkDestroyPipeline(vk->context->device,
vk->pipelines.hdr_to_sdr, NULL);
vkDestroyPipeline(vk->context->device,
vk->pipelines.font_sdr, NULL);
vkDestroyPipeline(vk->context->device,
vk->pipelines.alpha_blend_sdr, NULL);
for (i = 0; i < (int)ARRAY_SIZE(vk->display.pipelines_sdr); i++)
vkDestroyPipeline(vk->context->device,
vk->display.pipelines_sdr[i], NULL);
}
#endif /* VULKAN_HDR_SWAPCHAIN */
@@ -6568,6 +6744,10 @@ static bool vulkan_frame(void *data, const void *frame,
/* Begin render pass and set up viewport */
vkCmdBeginRenderPass(vk->cmd, &rp_info, VK_SUBPASS_CONTENTS_INLINE);
/* Signal draw functions to use SDR pipeline variants
* while rendering into the B8G8R8A8 offscreen buffer. */
vk->flags |= VK_FLAG_SDR_PIPELINE;
}
#endif /* VULKAN_HDR_SWAPCHAIN */
@@ -6592,7 +6772,13 @@ static bool vulkan_frame(void *data, const void *frame,
vulkan_set_viewport(vk, width, height, ((vk->flags &
VK_FLAG_MENU_FULLSCREEN) > 0), false);
#ifdef VULKAN_HDR_SWAPCHAIN
quad.pipeline = (vk->flags & VK_FLAG_SDR_PIPELINE)
? vk->pipelines.alpha_blend_sdr
: vk->pipelines.alpha_blend;
#else
quad.pipeline = vk->pipelines.alpha_blend;
#endif
quad.texture = &vk->menu.textures[vk->menu.last_index];
if (optimal->memory != VK_NULL_HANDLE)
@@ -6636,6 +6822,9 @@ static bool vulkan_frame(void *data, const void *frame,
#endif
/* End the render pass. We're done rendering to backbuffer now. */
#ifdef VULKAN_HDR_SWAPCHAIN
vk->flags &= ~VK_FLAG_SDR_PIPELINE;
#endif
if(end_main_pass) vkCmdEndRenderPass(vk->cmd);
#ifdef VULKAN_HDR_SWAPCHAIN