From a09d372cafd48162de919fd5054fd4db5d264825 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Mon, 13 Jan 2025 12:37:20 +0100 Subject: [PATCH] [dxvk] Prepare swapchain blitter for compositing HUD and cursor This will be necessary for non-linear color spaces to get proper blending. --- src/dxvk/dxvk_swapchain_blitter.cpp | 62 ++++++++++++++++--- src/dxvk/dxvk_swapchain_blitter.h | 19 +++++- src/dxvk/shaders/dxvk_present_common.glsl | 37 +++++++++++ src/dxvk/shaders/dxvk_present_frag.frag | 1 + src/dxvk/shaders/dxvk_present_frag_blit.frag | 1 + src/dxvk/shaders/dxvk_present_frag_ms.frag | 3 +- .../shaders/dxvk_present_frag_ms_amd.frag | 3 +- .../shaders/dxvk_present_frag_ms_blit.frag | 3 +- 8 files changed, 115 insertions(+), 14 deletions(-) diff --git a/src/dxvk/dxvk_swapchain_blitter.cpp b/src/dxvk/dxvk_swapchain_blitter.cpp index 18d856554..5b6b571e4 100644 --- a/src/dxvk/dxvk_swapchain_blitter.cpp +++ b/src/dxvk/dxvk_swapchain_blitter.cpp @@ -318,11 +318,36 @@ namespace dxvk { gammaDescriptor.imageLayout = m_gammaView->image()->info().layout; } - std::array descriptorWrites = {{ + VkDescriptorImageInfo hudDescriptor = { }; + + if (m_hudView) { + hudDescriptor.imageView = m_hudView->handle(); + hudDescriptor.imageLayout = m_hudImage->info().layout; + } + + VkDescriptorImageInfo cursorDescriptor = { }; + cursorDescriptor.sampler = m_samplerCursorNearest->handle(); + + if (m_cursorView) { + VkExtent3D extent = m_cursorImage->info().extent; + + if (m_cursorRect.extent.width != extent.width + || m_cursorRect.extent.height != extent.height) + cursorDescriptor.sampler = m_samplerCursorLinear->handle(); + + cursorDescriptor.imageLayout = m_cursorImage->info().layout; + cursorDescriptor.imageView = m_cursorView->handle(); + } + + std::array descriptorWrites = {{ { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, set, 0, 0, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &imageDescriptor }, { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, set, 1, 0, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &gammaDescriptor }, + { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, + set, 2, 0, 1, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, &hudDescriptor }, + { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, + set, 3, 0, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &cursorDescriptor }, }}; ctx.cmd->updateDescriptorSets( @@ -474,6 +499,18 @@ namespace dxvk { samplerInfo.setUsePixelCoordinates(false); m_samplerGamma = m_device->createSampler(samplerInfo); + + samplerInfo.setAddressModes( + VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, + VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, + VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER); + + m_samplerCursorLinear = m_device->createSampler(samplerInfo); + + samplerInfo.setFilter(VK_FILTER_NEAREST, VK_FILTER_NEAREST, + VK_SAMPLER_MIPMAP_MODE_NEAREST); + + m_samplerCursorNearest = m_device->createSampler(samplerInfo); } @@ -527,10 +564,11 @@ namespace dxvk { VkDescriptorSetLayout DxvkSwapchainBlitter::createSetLayout() { auto vk = m_device->vkd(); - std::array bindings = {{ + std::array bindings = {{ { 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT }, { 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT }, - { 2, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT }, + { 2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT }, + { 3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT }, }}; VkDescriptorSetLayoutCreateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; @@ -574,13 +612,15 @@ namespace dxvk { const DxvkSwapchainPipelineKey& key) { auto vk = m_device->vkd(); - static const std::array specMap = {{ - { 0, offsetof(SpecConstants, sampleCount), sizeof(VkSampleCountFlagBits) }, - { 1, offsetof(SpecConstants, gammaBound), sizeof(VkBool32) }, - { 2, offsetof(SpecConstants, srcSpace), sizeof(VkColorSpaceKHR) }, - { 3, offsetof(SpecConstants, srcIsSrgb), sizeof(VkBool32) }, - { 4, offsetof(SpecConstants, dstSpace), sizeof(VkColorSpaceKHR) }, - { 5, offsetof(SpecConstants, dstIsSrgb), sizeof(VkBool32) }, + static const std::array specMap = {{ + { 0, offsetof(SpecConstants, sampleCount), sizeof(VkSampleCountFlagBits) }, + { 1, offsetof(SpecConstants, gammaBound), sizeof(VkBool32) }, + { 2, offsetof(SpecConstants, srcSpace), sizeof(VkColorSpaceKHR) }, + { 3, offsetof(SpecConstants, srcIsSrgb), sizeof(VkBool32) }, + { 4, offsetof(SpecConstants, dstSpace), sizeof(VkColorSpaceKHR) }, + { 5, offsetof(SpecConstants, dstIsSrgb), sizeof(VkBool32) }, + { 6, offsetof(SpecConstants, compositeHud), sizeof(VkBool32) }, + { 7, offsetof(SpecConstants, compositeCursor),sizeof(VkBool32) }, }}; SpecConstants specConstants = { }; @@ -590,6 +630,8 @@ namespace dxvk { specConstants.srcIsSrgb = key.srcIsSrgb; specConstants.dstSpace = key.dstSpace; specConstants.dstIsSrgb = lookupFormatInfo(key.dstFormat)->flags.test(DxvkFormatFlag::ColorSpaceSrgb); + specConstants.compositeCursor = key.compositeCursor; + specConstants.compositeHud = key.compositeHud; // Avoid redundant color space conversions if color spaces // and images properties match and we don't do a resolve diff --git a/src/dxvk/dxvk_swapchain_blitter.h b/src/dxvk/dxvk_swapchain_blitter.h index 210d4f989..4187407e6 100644 --- a/src/dxvk/dxvk_swapchain_blitter.h +++ b/src/dxvk/dxvk_swapchain_blitter.h @@ -47,6 +47,10 @@ namespace dxvk { VkBool32 needsGamma = VK_FALSE; /// Bit indicating whether alpha blending is required VkBool32 needsBlending = VK_FALSE; + /// Bit indicating whether the HUD needs to be composited + VkBool32 compositeHud = VK_FALSE; + /// Bit indicating whether the software cursor needs to be composited + VkBool32 compositeCursor = VK_FALSE; size_t hash() const { DxvkHashState hash; @@ -58,6 +62,8 @@ namespace dxvk { hash.add(uint32_t(needsBlit)); hash.add(uint32_t(needsGamma)); hash.add(uint32_t(needsBlending)); + hash.add(uint32_t(compositeHud)); + hash.add(uint32_t(compositeCursor)); return hash; } @@ -69,7 +75,9 @@ namespace dxvk { && dstFormat == other.dstFormat && needsBlit == other.needsBlit && needsGamma == other.needsGamma - && needsBlending == other.needsBlending; + && needsBlending == other.needsBlending + && compositeHud == other.compositeHud + && compositeCursor == other.compositeCursor; } }; @@ -155,12 +163,16 @@ namespace dxvk { VkBool32 srcIsSrgb; VkColorSpaceKHR dstSpace; VkBool32 dstIsSrgb; + VkBool32 compositeHud; + VkBool32 compositeCursor; }; struct PushConstants { VkOffset2D srcOffset; VkExtent2D srcExtent; VkOffset2D dstOffset; + VkOffset2D cursorOffset; + VkExtent2D cursorExtent; }; struct ShaderModule { @@ -190,6 +202,11 @@ namespace dxvk { Rc m_samplerPresent; Rc m_samplerGamma; + Rc m_samplerCursorLinear; + Rc m_samplerCursorNearest; + + Rc m_hudImage; + Rc m_hudView; VkDescriptorSetLayout m_setLayout = VK_NULL_HANDLE; VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE; diff --git a/src/dxvk/shaders/dxvk_present_common.glsl b/src/dxvk/shaders/dxvk_present_common.glsl index df07724dd..3eb81a404 100644 --- a/src/dxvk/shaders/dxvk_present_common.glsl +++ b/src/dxvk/shaders/dxvk_present_common.glsl @@ -1,5 +1,7 @@ #include "dxvk_color_space.glsl" +#extension GL_EXT_samplerless_texture_functions : require + layout(constant_id = 0) const uint c_samples = 0u; layout(constant_id = 1) const bool c_gamma = false; @@ -7,18 +9,53 @@ layout(constant_id = 2) const uint c_src_color_space = VK_COLOR_SPACE_SRGB_NONLI layout(constant_id = 3) const bool c_src_is_srgb = true; layout(constant_id = 4) const uint c_dst_color_space = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; layout(constant_id = 5) const bool c_dst_is_srgb = true; +layout(constant_id = 6) const bool c_composite_hud = false; +layout(constant_id = 7) const bool c_composite_cursor = false; layout(set = 0, binding = 0) uniform sampler2D s_image; layout(set = 0, binding = 0) uniform sampler2DMS s_image_ms; layout(set = 0, binding = 1) uniform sampler1D s_gamma; +layout(set = 0, binding = 2) uniform texture2D s_hud; +layout(set = 0, binding = 3) uniform sampler2D s_cursor; layout(push_constant) uniform present_info_t { ivec2 src_offset; ivec2 src_extent; ivec2 dst_offset; + ivec2 cursor_offset; + ivec2 cursor_extent; }; + +vec4 blend_sc_rgb(vec4 dst, vec4 src) { + return mix(dst, vec4(src.rgb, 1.0f), src.aaaa); +} + + +vec4 blend_linear_sdr(vec4 dst, vec4 src) { + src.rgb = nits_to_sc_rgb(src.rgb * SDR_NITS); + return blend_sc_rgb(dst, src); +} + + +vec4 composite_image(vec4 color) { + ivec2 coord = ivec2(gl_FragCoord.xy); + + if (c_composite_hud) + color = blend_linear_sdr(color, texelFetch(s_hud, coord, 0)); + + if (c_composite_cursor) { + ivec2 rel_ofs = coord - cursor_offset; + + if (max(rel_ofs.x, rel_ofs.y) >= 0 && all(lessThan(rel_ofs, cursor_extent))) + color = blend_linear_sdr(color, texture(s_cursor, vec2(rel_ofs) / vec2(cursor_extent))); + } + + return color; +} + + vec4 input_to_sc_rgb(vec4 color) { switch (c_src_color_space) { default: diff --git a/src/dxvk/shaders/dxvk_present_frag.frag b/src/dxvk/shaders/dxvk_present_frag.frag index 94fce7b2d..f934e91fe 100644 --- a/src/dxvk/shaders/dxvk_present_frag.frag +++ b/src/dxvk/shaders/dxvk_present_frag.frag @@ -10,5 +10,6 @@ void main() { ivec2 coord = ivec2(gl_FragCoord.xy) + src_offset - dst_offset; o_color = input_to_sc_rgb(texelFetch(s_image, coord, 0)); + o_color = composite_image(o_color); o_color = sc_rgb_to_output(o_color); } diff --git a/src/dxvk/shaders/dxvk_present_frag_blit.frag b/src/dxvk/shaders/dxvk_present_frag_blit.frag index 4c9e2dd47..4ebd5c8b3 100644 --- a/src/dxvk/shaders/dxvk_present_frag_blit.frag +++ b/src/dxvk/shaders/dxvk_present_frag_blit.frag @@ -10,5 +10,6 @@ layout(location = 0) out vec4 o_color; void main() { vec2 coord = vec2(src_offset) + vec2(src_extent) * i_coord; o_color = input_to_sc_rgb(textureLod(s_image, coord, 0.0f)); + o_color = composite_image(o_color); o_color = sc_rgb_to_output(o_color); } diff --git a/src/dxvk/shaders/dxvk_present_frag_ms.frag b/src/dxvk/shaders/dxvk_present_frag_ms.frag index 8f109c4b6..e5bf1aef5 100644 --- a/src/dxvk/shaders/dxvk_present_frag_ms.frag +++ b/src/dxvk/shaders/dxvk_present_frag_ms.frag @@ -13,5 +13,6 @@ void main() { for (uint i = 1; i < c_samples; i++) o_color += input_to_sc_rgb(texelFetch(s_image_ms, coord, int(i))); - o_color = sc_rgb_to_output(o_color / float(c_samples)); + o_color = composite_image(o_color / float(c_samples)); + o_color = sc_rgb_to_output(o_color); } diff --git a/src/dxvk/shaders/dxvk_present_frag_ms_amd.frag b/src/dxvk/shaders/dxvk_present_frag_ms_amd.frag index 6f332fac4..d33fa9af6 100644 --- a/src/dxvk/shaders/dxvk_present_frag_ms_amd.frag +++ b/src/dxvk/shaders/dxvk_present_frag_ms_amd.frag @@ -31,5 +31,6 @@ void main() { fragCount = bitfieldInsert(fragCount, 0, fragShift, 4); } - o_color = sc_rgb_to_output(o_color / float(c_samples)); + o_color = composite_image(o_color / float(c_samples)); + o_color = sc_rgb_to_output(o_color); } diff --git a/src/dxvk/shaders/dxvk_present_frag_ms_blit.frag b/src/dxvk/shaders/dxvk_present_frag_ms_blit.frag index 710c4fc19..a565f88e0 100644 --- a/src/dxvk/shaders/dxvk_present_frag_ms_blit.frag +++ b/src/dxvk/shaders/dxvk_present_frag_ms_blit.frag @@ -61,5 +61,6 @@ void main() { o_color += input_to_sc_rgb(texelFetch(s_image_ms, cint + coffset, int(i))); } - o_color = sc_rgb_to_output(o_color / float(c_samples)); + o_color = composite_image(o_color / float(c_samples)); + o_color = sc_rgb_to_output(o_color); }