mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-09 03:29:43 -10:00
VULKAN: Build SDR display pipeline set for HDR offscreen compositing
Follow-up tod250194(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 thatd250194fixed 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:
1 parent
d2501944bd
commit
8fa8418762
2 files changed
+196
-6
No files matched your search
@@ -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
@@ -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
|
||||
|
||||
Reference in new issue
Block a user