diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 5b8f47a7a6..c4e7ac3f6f 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -18,6 +18,7 @@ set(SHADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/blit_color_float.frag ${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d.comp ${CMAKE_CURRENT_SOURCE_DIR}/blit_color_msaa.frag + ${CMAKE_CURRENT_SOURCE_DIR}/blit_depth.frag ${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_msaa.frag ${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_stencil_msaa.frag ${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d.comp @@ -30,8 +31,12 @@ set(SHADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/convert_float_to_depth.frag ${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa.comp ${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa.frag + ${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa_depth.frag + ${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa_depth_stencil.frag ${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa.comp ${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa.frag + ${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa_depth.frag + ${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa_depth_stencil.frag ${CMAKE_CURRENT_SOURCE_DIR}/convert_s8d24_to_abgr8.frag ${CMAKE_CURRENT_SOURCE_DIR}/full_screen_triangle.vert ${CMAKE_CURRENT_SOURCE_DIR}/fxaa.frag @@ -163,6 +168,44 @@ foreach(SOURCE_FILE IN ITEMS ${SHADER_FILES}) endif() endforeach() +# Integer variants of the MSAA conversion shaders. They only differ from the float +# source in the sampler and output types, so they are generated from it via defines. +set(SHADER_TYPE_VARIANTS + "convert_msaa_to_non_msaa.frag|sint|isampler2DMS|ivec4" + "convert_msaa_to_non_msaa.frag|uint|usampler2DMS|uvec4" + "convert_non_msaa_to_msaa.frag|sint|isampler2D|ivec4" + "convert_non_msaa_to_msaa.frag|uint|usampler2D|uvec4" +) + +foreach(VARIANT IN ITEMS ${SHADER_TYPE_VARIANTS}) + string(REPLACE "|" ";" VARIANT_PARTS ${VARIANT}) + list(GET VARIANT_PARTS 0 VARIANT_FILENAME) + list(GET VARIANT_PARTS 1 VARIANT_SUFFIX) + list(GET VARIANT_PARTS 2 VARIANT_SAMPLER) + list(GET VARIANT_PARTS 3 VARIANT_TEXEL) + + set(VARIANT_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/${VARIANT_FILENAME}) + get_filename_component(VARIANT_STEM ${VARIANT_FILENAME} NAME_WE) + get_filename_component(VARIANT_EXT ${VARIANT_FILENAME} EXT) + string(REPLACE "." "" VARIANT_EXT ${VARIANT_EXT}) + set(VARIANT_NAME ${VARIANT_STEM}_${VARIANT_SUFFIX}_${VARIANT_EXT}) + + string(TOUPPER ${VARIANT_NAME}_SPV VARIANT_VARIABLE_NAME) + set(VARIANT_HEADER_FILE ${SHADER_DIR}/${VARIANT_NAME}_spv.h) + add_custom_command( + OUTPUT + ${VARIANT_HEADER_FILE} + COMMAND + ${GLSLANGVALIDATOR} -V ${QUIET_FLAG} -I"${FIDELITYFX_INCLUDE_DIR}" ${GLSL_FLAGS} + -DSAMPLER_TYPE=${VARIANT_SAMPLER} -DTEXEL_TYPE=${VARIANT_TEXEL} + --variable-name ${VARIANT_VARIABLE_NAME} -o ${VARIANT_HEADER_FILE} ${VARIANT_SOURCE} + --target-env ${SPIR_V_VERSION} + MAIN_DEPENDENCY + ${VARIANT_SOURCE} + ) + set(SHADER_HEADERS ${SHADER_HEADERS} ${VARIANT_HEADER_FILE}) +endforeach() + foreach(FILEPATH IN ITEMS ${FIDELITYFX_FILES}) get_filename_component(FILENAME ${FILEPATH} NAME) string(REPLACE "." "_" HEADER_NAME ${FILENAME}) diff --git a/src/video_core/host_shaders/blit_depth.frag b/src/video_core/host_shaders/blit_depth.frag new file mode 100644 index 0000000000..9e19c767aa --- /dev/null +++ b/src/video_core/host_shaders/blit_depth.frag @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#version 450 core + +layout(binding = 0) uniform sampler2D depth_tex; + +layout(location = 0) in vec2 texcoord; + +void main() { + gl_FragDepth = textureLod(depth_tex, texcoord, 0).r; +} diff --git a/src/video_core/host_shaders/blit_depth_msaa.frag b/src/video_core/host_shaders/blit_depth_msaa.frag index 064289ad04..9e955d471e 100644 --- a/src/video_core/host_shaders/blit_depth_msaa.frag +++ b/src/video_core/host_shaders/blit_depth_msaa.frag @@ -8,5 +8,5 @@ layout(binding = 0) uniform sampler2DMS depth_tex; layout(location = 0) in vec2 texcoord; void main() { - gl_FragDepth = texelFetch(depth_tex, ivec2(texcoord), 0).r; + gl_FragDepth = texelFetch(depth_tex, ivec2(texcoord), gl_SampleID).r; } diff --git a/src/video_core/host_shaders/blit_depth_stencil_msaa.frag b/src/video_core/host_shaders/blit_depth_stencil_msaa.frag index d4264f2d14..7d4555bfd4 100644 --- a/src/video_core/host_shaders/blit_depth_stencil_msaa.frag +++ b/src/video_core/host_shaders/blit_depth_stencil_msaa.frag @@ -10,6 +10,6 @@ layout(binding = 1) uniform usampler2DMS stencil_tex; layout(location = 0) in vec2 texcoord; void main() { - gl_FragDepth = texelFetch(depth_tex, ivec2(texcoord), 0).r; - gl_FragStencilRefARB = int(texelFetch(stencil_tex, ivec2(texcoord), 0).r); + gl_FragDepth = texelFetch(depth_tex, ivec2(texcoord), gl_SampleID).r; + gl_FragStencilRefARB = int(texelFetch(stencil_tex, ivec2(texcoord), gl_SampleID).r); } diff --git a/src/video_core/host_shaders/convert_msaa_to_non_msaa.frag b/src/video_core/host_shaders/convert_msaa_to_non_msaa.frag index 6c4aac3c82..8f7ec34d1a 100644 --- a/src/video_core/host_shaders/convert_msaa_to_non_msaa.frag +++ b/src/video_core/host_shaders/convert_msaa_to_non_msaa.frag @@ -3,7 +3,14 @@ #version 450 core -layout(binding = 0) uniform sampler2DMS msaa_in; +#ifndef SAMPLER_TYPE +#define SAMPLER_TYPE sampler2DMS +#endif +#ifndef TEXEL_TYPE +#define TEXEL_TYPE vec4 +#endif + +layout(binding = 0) uniform SAMPLER_TYPE msaa_in; layout(push_constant) uniform PushConstants { ivec2 dst_offset; @@ -11,7 +18,7 @@ layout(push_constant) uniform PushConstants { ivec2 scale; }; -layout(location = 0) out vec4 frag_color; +layout(location = 0) out TEXEL_TYPE frag_color; void main() { const ivec2 coord = ivec2(gl_FragCoord.xy) - dst_offset + src_offset; diff --git a/src/video_core/host_shaders/convert_msaa_to_non_msaa_depth.frag b/src/video_core/host_shaders/convert_msaa_to_non_msaa_depth.frag new file mode 100644 index 0000000000..26fdd48b36 --- /dev/null +++ b/src/video_core/host_shaders/convert_msaa_to_non_msaa_depth.frag @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#version 450 core + +layout(binding = 0) uniform sampler2DMS msaa_in; + +layout(push_constant) uniform PushConstants { + ivec2 dst_offset; + ivec2 src_offset; + ivec2 scale; +}; + +void main() { + const ivec2 coord = ivec2(gl_FragCoord.xy) - dst_offset + src_offset; + const ivec2 msaa_coord = coord / scale; + const ivec2 sample_offset = coord % scale; + const int sample_id = sample_offset.x + scale.x * sample_offset.y; + gl_FragDepth = texelFetch(msaa_in, msaa_coord, sample_id).r; +} diff --git a/src/video_core/host_shaders/convert_msaa_to_non_msaa_depth_stencil.frag b/src/video_core/host_shaders/convert_msaa_to_non_msaa_depth_stencil.frag new file mode 100644 index 0000000000..cd34a1a025 --- /dev/null +++ b/src/video_core/host_shaders/convert_msaa_to_non_msaa_depth_stencil.frag @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#version 450 core +#extension GL_ARB_shader_stencil_export : require + +layout(binding = 0) uniform sampler2DMS depth_tex; +layout(binding = 1) uniform usampler2DMS stencil_tex; + +layout(push_constant) uniform PushConstants { + ivec2 dst_offset; + ivec2 src_offset; + ivec2 scale; +}; + +void main() { + const ivec2 coord = ivec2(gl_FragCoord.xy) - dst_offset + src_offset; + const ivec2 msaa_coord = coord / scale; + const ivec2 sample_offset = coord % scale; + const int sample_id = sample_offset.x + scale.x * sample_offset.y; + gl_FragDepth = texelFetch(depth_tex, msaa_coord, sample_id).r; + gl_FragStencilRefARB = int(texelFetch(stencil_tex, msaa_coord, sample_id).r); +} diff --git a/src/video_core/host_shaders/convert_non_msaa_to_msaa.frag b/src/video_core/host_shaders/convert_non_msaa_to_msaa.frag index 19e23849d2..b5a93068a9 100644 --- a/src/video_core/host_shaders/convert_non_msaa_to_msaa.frag +++ b/src/video_core/host_shaders/convert_non_msaa_to_msaa.frag @@ -3,7 +3,14 @@ #version 450 core -layout(binding = 0) uniform sampler2D img_in; +#ifndef SAMPLER_TYPE +#define SAMPLER_TYPE sampler2D +#endif +#ifndef TEXEL_TYPE +#define TEXEL_TYPE vec4 +#endif + +layout(binding = 0) uniform SAMPLER_TYPE img_in; layout(push_constant) uniform PushConstants { ivec2 dst_offset; @@ -11,7 +18,7 @@ layout(push_constant) uniform PushConstants { ivec2 scale; }; -layout(location = 0) out vec4 frag_color; +layout(location = 0) out TEXEL_TYPE frag_color; void main() { const ivec2 msaa_coord = ivec2(gl_FragCoord.xy) - dst_offset; diff --git a/src/video_core/host_shaders/convert_non_msaa_to_msaa_depth.frag b/src/video_core/host_shaders/convert_non_msaa_to_msaa_depth.frag new file mode 100644 index 0000000000..f883b3196f --- /dev/null +++ b/src/video_core/host_shaders/convert_non_msaa_to_msaa_depth.frag @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#version 450 core + +layout(binding = 0) uniform sampler2D img_in; + +layout(push_constant) uniform PushConstants { + ivec2 dst_offset; + ivec2 src_offset; + ivec2 scale; +}; + +void main() { + const ivec2 msaa_coord = ivec2(gl_FragCoord.xy) - dst_offset; + const ivec2 sample_offset = ivec2(gl_SampleID % scale.x, gl_SampleID / scale.x); + const ivec2 coord = msaa_coord * scale + sample_offset + src_offset; + gl_FragDepth = texelFetch(img_in, coord, 0).r; +} diff --git a/src/video_core/host_shaders/convert_non_msaa_to_msaa_depth_stencil.frag b/src/video_core/host_shaders/convert_non_msaa_to_msaa_depth_stencil.frag new file mode 100644 index 0000000000..d9a308f5c3 --- /dev/null +++ b/src/video_core/host_shaders/convert_non_msaa_to_msaa_depth_stencil.frag @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#version 450 core +#extension GL_ARB_shader_stencil_export : require + +layout(binding = 0) uniform sampler2D depth_tex; +layout(binding = 1) uniform usampler2D stencil_tex; + +layout(push_constant) uniform PushConstants { + ivec2 dst_offset; + ivec2 src_offset; + ivec2 scale; +}; + +void main() { + const ivec2 msaa_coord = ivec2(gl_FragCoord.xy) - dst_offset; + const ivec2 sample_offset = ivec2(gl_SampleID % scale.x, gl_SampleID / scale.x); + const ivec2 coord = msaa_coord * scale + sample_offset + src_offset; + gl_FragDepth = texelFetch(depth_tex, coord, 0).r; + gl_FragStencilRefARB = int(texelFetch(stencil_tex, coord, 0).r); +} diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h index dfcef4b0b6..8aed499bb7 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.h +++ b/src/video_core/renderer_opengl/gl_texture_cache.h @@ -75,6 +75,8 @@ public: void Finish(); + void FlushDeferredClear() {} + StagingBufferMap UploadStagingBuffer(size_t size, bool deferred = false); StagingBufferMap DownloadStagingBuffer(size_t size, bool deferred = false); @@ -370,6 +372,7 @@ struct TextureCacheParams { static constexpr bool HAS_EMULATED_COPIES = true; static constexpr bool HAS_DEVICE_MEMORY_INFO = true; static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true; + static constexpr bool HAS_MSAA_DOWNLOADS = false; using Runtime = OpenGL::TextureCacheRuntime; using Image = OpenGL::Image; diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index 89083d29fb..f9d9460c37 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp @@ -12,6 +12,7 @@ #include "common/settings.h" #include "video_core/host_shaders/blit_color_float_frag_spv.h" #include "video_core/host_shaders/blit_color_msaa_frag_spv.h" +#include "video_core/host_shaders/blit_depth_frag_spv.h" #include "video_core/host_shaders/blit_depth_msaa_frag_spv.h" #include "video_core/host_shaders/blit_depth_stencil_msaa_frag_spv.h" #include "video_core/host_shaders/convert_abgr8_to_d24s8_frag_spv.h" @@ -21,7 +22,15 @@ #include "video_core/host_shaders/convert_depth_to_float_frag_spv.h" #include "video_core/host_shaders/convert_float_to_depth_frag_spv.h" #include "video_core/host_shaders/convert_msaa_to_non_msaa_frag_spv.h" +#include "video_core/host_shaders/convert_msaa_to_non_msaa_depth_frag_spv.h" +#include "video_core/host_shaders/convert_msaa_to_non_msaa_depth_stencil_frag_spv.h" +#include "video_core/host_shaders/convert_msaa_to_non_msaa_sint_frag_spv.h" +#include "video_core/host_shaders/convert_msaa_to_non_msaa_uint_frag_spv.h" #include "video_core/host_shaders/convert_non_msaa_to_msaa_frag_spv.h" +#include "video_core/host_shaders/convert_non_msaa_to_msaa_sint_frag_spv.h" +#include "video_core/host_shaders/convert_non_msaa_to_msaa_uint_frag_spv.h" +#include "video_core/host_shaders/convert_non_msaa_to_msaa_depth_frag_spv.h" +#include "video_core/host_shaders/convert_non_msaa_to_msaa_depth_stencil_frag_spv.h" #include "video_core/host_shaders/convert_s8d24_to_abgr8_frag_spv.h" #include "video_core/host_shaders/full_screen_triangle_vert_spv.h" #include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h" @@ -518,8 +527,19 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view) } } +[[nodiscard]] MSAACopyFormatClass FormatClass(VideoCore::Surface::PixelFormat format) { + if (!VideoCore::Surface::IsPixelFormatInteger(format)) { + return MSAACopyFormatClass::Float; + } + if (VideoCore::Surface::IsPixelFormatSignedInteger(format)) { + return MSAACopyFormatClass::SignedInteger; + } + return MSAACopyFormatClass::UnsignedInteger; +} + [[nodiscard]] vk::ImageView MakeMSAACopyView(const vk::Device& device, VkImage image, - VkFormat format, u32 base_level) { + VkFormat format, u32 base_level, u32 base_layer, + VkImageAspectFlags aspect_mask) { return device.CreateImageView(VkImageViewCreateInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .pNext = nullptr, @@ -534,10 +554,10 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view) .a = VK_COMPONENT_SWIZZLE_IDENTITY, }, .subresourceRange{ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .aspectMask = aspect_mask, .baseMipLevel = base_level, .levelCount = 1, - .baseArrayLayer = 0, + .baseArrayLayer = base_layer, .layerCount = 1, }, }); @@ -586,12 +606,17 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_, msaa_copy_pipeline_layout(device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo( one_texture_set_layout.address(), PUSH_CONSTANT_RANGE))), + msaa_copy_depth_stencil_pipeline_layout( + device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo( + two_textures_set_layout.address(), + PUSH_CONSTANT_RANGE))), full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)), blit_color_to_color_frag(BuildShader(device, BLIT_COLOR_FLOAT_FRAG_SPV)), blit_color_msaa_frag(BuildShader(device, BLIT_COLOR_MSAA_FRAG_SPV)), blit_depth_stencil_frag(device.IsExtShaderStencilExportSupported() ? BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV) : vk::ShaderModule{}), + blit_depth_frag(BuildShader(device, BLIT_DEPTH_FRAG_SPV)), blit_depth_msaa_frag(BuildShader(device, BLIT_DEPTH_MSAA_FRAG_SPV)), blit_depth_stencil_msaa_frag(device.IsExtShaderStencilExportSupported() ? BuildShader(device, BLIT_DEPTH_STENCIL_MSAA_FRAG_SPV) @@ -609,7 +634,25 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_, convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)), convert_s8d24_to_abgr8_frag(BuildShader(device, CONVERT_S8D24_TO_ABGR8_FRAG_SPV)), convert_msaa_to_non_msaa_frag(BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_FRAG_SPV)), + convert_msaa_to_non_msaa_sint_frag( + BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_SINT_FRAG_SPV)), + convert_msaa_to_non_msaa_uint_frag( + BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_UINT_FRAG_SPV)), + convert_msaa_to_non_msaa_depth_frag( + BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_DEPTH_FRAG_SPV)), + convert_msaa_to_non_msaa_depth_stencil_frag( + BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_DEPTH_STENCIL_FRAG_SPV)), convert_non_msaa_to_msaa_frag(BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_FRAG_SPV)), + convert_non_msaa_to_msaa_sint_frag( + BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_SINT_FRAG_SPV)), + convert_non_msaa_to_msaa_uint_frag( + BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_UINT_FRAG_SPV)), + convert_non_msaa_to_msaa_depth_frag( + BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_DEPTH_FRAG_SPV)), + convert_non_msaa_to_msaa_depth_stencil_frag( + device.IsExtShaderStencilExportSupported() + ? BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_DEPTH_STENCIL_FRAG_SPV) + : vk::ShaderModule{}), linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO)), nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO)) {} @@ -624,24 +667,13 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, const ImageV .renderpass = dst_framebuffer->RenderPass(), .operation = operation, }; - const VkPipelineLayout layout = *one_texture_pipeline_layout; - const VkSampler sampler = is_linear ? *linear_sampler : *nearest_sampler; - const VkPipeline pipeline = FindOrEmplaceColorPipeline(key); - const VkImageView src_view = src_image_view.Handle(Shader::TextureType::Color2D); - - RecordShaderReadBarrier(scheduler, src_image_view); - scheduler.RequestRenderpass(dst_framebuffer); - scheduler.Record([this, dst_region, src_region, pipeline, layout, sampler, - src_view](vk::CommandBuffer cmdbuf) { - const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit(); - UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view); - cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set, - nullptr); - BindBlitState(cmdbuf, layout, dst_region, src_region); - cmdbuf.Draw(3, 1, 0, 0); - }); - scheduler.InvalidateState(); + VkSampler sampler = *nearest_sampler; + if (is_linear) { + sampler = *linear_sampler; + } + BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region, + FindOrEmplaceColorPipeline(key), sampler, + src_image_view.Handle(Shader::TextureType::Color2D), VK_NULL_HANDLE, false); } void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView src_image_view, @@ -670,6 +702,38 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView }); } +void BlitImageHelper::BlitImpl(const Framebuffer* dst_framebuffer, + const ImageView& src_image_view, const Region2D& dst_region, + const Region2D& src_region, VkPipeline pipeline, VkSampler sampler, + VkImageView src_view, VkImageView src_stencil_view, + bool blit_stencil) { + VkPipelineLayout layout = *one_texture_pipeline_layout; + if (blit_stencil) { + layout = *two_textures_pipeline_layout; + } + + RecordShaderReadBarrier(scheduler, src_image_view); + scheduler.RequestRenderpass(dst_framebuffer); + scheduler.Record([this, dst_region, src_region, pipeline, layout, sampler, src_view, + src_stencil_view, blit_stencil](vk::CommandBuffer cmdbuf) { + VkDescriptorSet descriptor_set = VK_NULL_HANDLE; + if (blit_stencil) { + descriptor_set = two_textures_descriptor_allocator.Commit(); + UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_view, + src_stencil_view); + } else { + descriptor_set = one_texture_descriptor_allocator.Commit(); + UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view); + } + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set, + nullptr); + BindBlitState(cmdbuf, layout, dst_region, src_region); + cmdbuf.Draw(3, 1, 0, 0); + }); + scheduler.InvalidateState(); +} + void BlitImageHelper::BlitColorMSAA(const Framebuffer* dst_framebuffer, const ImageView& src_image_view, const Region2D& dst_region, const Region2D& src_region) { @@ -677,24 +741,34 @@ void BlitImageHelper::BlitColorMSAA(const Framebuffer* dst_framebuffer, .renderpass = dst_framebuffer->RenderPass(), .samples = dst_framebuffer->Samples(), }; - const VkPipelineLayout layout = *one_texture_pipeline_layout; - const VkSampler sampler = *nearest_sampler; - const VkPipeline pipeline = FindOrEmplaceBlitColorMSAAPipeline(key); - const VkImageView src_view = src_image_view.Handle(Shader::TextureType::Color2D); + BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region, + FindOrEmplaceBlitColorMSAAPipeline(key), *nearest_sampler, + src_image_view.Handle(Shader::TextureType::Color2D), VK_NULL_HANDLE, false); +} - RecordShaderReadBarrier(scheduler, src_image_view); - scheduler.RequestRenderpass(dst_framebuffer); - scheduler.Record([this, dst_region, src_region, pipeline, layout, sampler, - src_view](vk::CommandBuffer cmdbuf) { - const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit(); - UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view); - cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set, - nullptr); - BindBlitState(cmdbuf, layout, dst_region, src_region); - cmdbuf.Draw(3, 1, 0, 0); - }); - scheduler.InvalidateState(); +void BlitImageHelper::BlitDepthStencilMSAA(const Framebuffer* dst_framebuffer, + ImageView& src_image_view, const Region2D& dst_region, + const Region2D& src_region) { + const bool blit_stencil = + dst_framebuffer->HasAspectStencilBit() && device.IsExtShaderStencilExportSupported(); + const BlitMSAAPipelineKey key{ + .renderpass = dst_framebuffer->RenderPass(), + .samples = dst_framebuffer->Samples(), + }; + VkImageView src_stencil_view = VK_NULL_HANDLE; + if (blit_stencil) { + src_stencil_view = src_image_view.StencilView(); + } + BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region, + FindOrEmplaceBlitDepthStencilMSAAPipeline(key, blit_stencil), *nearest_sampler, + src_image_view.DepthView(), src_stencil_view, blit_stencil); +} + +void BlitImageHelper::BlitDepth(const Framebuffer* dst_framebuffer, ImageView& src_image_view, + const Region2D& dst_region, const Region2D& src_region) { + BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region, + FindOrEmplaceBlitDepthPipeline(dst_framebuffer->RenderPass()), *nearest_sampler, + src_image_view.DepthView(), VK_NULL_HANDLE, false); } void BlitImageHelper::ResolveDepthStencil(const Framebuffer* dst_framebuffer, @@ -702,36 +776,14 @@ void BlitImageHelper::ResolveDepthStencil(const Framebuffer* dst_framebuffer, const Region2D& src_region) { const bool resolve_stencil = dst_framebuffer->HasAspectStencilBit() && device.IsExtShaderStencilExportSupported(); - const VkPipeline pipeline = - FindOrEmplaceResolveDepthStencilPipeline(dst_framebuffer->RenderPass(), resolve_stencil); - const VkPipelineLayout layout = - resolve_stencil ? *two_textures_pipeline_layout : *one_texture_pipeline_layout; - const VkSampler sampler = *nearest_sampler; - const VkImageView src_depth_view = src_image_view.DepthView(); - const VkImageView src_stencil_view = - resolve_stencil ? src_image_view.StencilView() : VK_NULL_HANDLE; - - RecordShaderReadBarrier(scheduler, src_image_view); - scheduler.RequestRenderpass(dst_framebuffer); - scheduler.Record([this, dst_region, src_region, pipeline, layout, sampler, src_depth_view, - src_stencil_view, resolve_stencil](vk::CommandBuffer cmdbuf) { - if (resolve_stencil) { - const VkDescriptorSet descriptor_set = two_textures_descriptor_allocator.Commit(); - UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_depth_view, - src_stencil_view); - cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set, - nullptr); - } else { - const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit(); - UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_depth_view); - cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set, - nullptr); - } - cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - BindBlitState(cmdbuf, layout, dst_region, src_region); - cmdbuf.Draw(3, 1, 0, 0); - }); - scheduler.InvalidateState(); + VkImageView src_stencil_view = VK_NULL_HANDLE; + if (resolve_stencil) { + src_stencil_view = src_image_view.StencilView(); + } + BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region, + FindOrEmplaceResolveDepthStencilPipeline(dst_framebuffer->RenderPass(), + resolve_stencil), + *nearest_sampler, src_image_view.DepthView(), src_stencil_view, resolve_stencil); } void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer, @@ -739,35 +791,23 @@ void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer, const Region2D& dst_region, const Region2D& src_region, Tegra::Engines::Fermi2D::Filter filter, Tegra::Engines::Fermi2D::Operation operation) { - if (!device.IsExtShaderStencilExportSupported()) { - return; - } ASSERT(filter == Tegra::Engines::Fermi2D::Filter::Point); ASSERT(operation == Tegra::Engines::Fermi2D::Operation::SrcCopy); + const bool blit_stencil = device.IsExtShaderStencilExportSupported(); const BlitImagePipelineKey key{ .renderpass = dst_framebuffer->RenderPass(), .operation = operation, }; - const VkPipelineLayout layout = *two_textures_pipeline_layout; - const VkSampler sampler = *nearest_sampler; - const VkPipeline pipeline = FindOrEmplaceDepthStencilPipeline(key); - const VkImageView src_depth_view = src_image_view.DepthView(); - const VkImageView src_stencil_view = src_image_view.StencilView(); - - RecordShaderReadBarrier(scheduler, src_image_view); - scheduler.RequestRenderpass(dst_framebuffer); - scheduler.Record([dst_region, src_region, pipeline, layout, sampler, src_depth_view, - src_stencil_view, this](vk::CommandBuffer cmdbuf) { - const VkDescriptorSet descriptor_set = two_textures_descriptor_allocator.Commit(); - UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_depth_view, - src_stencil_view); - cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set, - nullptr); - BindBlitState(cmdbuf, layout, dst_region, src_region); - cmdbuf.Draw(3, 1, 0, 0); - }); - scheduler.InvalidateState(); + VkPipeline pipeline{}; + VkImageView src_stencil_view = VK_NULL_HANDLE; + if (blit_stencil) { + pipeline = FindOrEmplaceDepthStencilPipeline(key); + src_stencil_view = src_image_view.StencilView(); + } else { + pipeline = FindOrEmplaceBlitDepthPipeline(key.renderpass); + } + BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region, pipeline, *nearest_sampler, + src_image_view.DepthView(), src_stencil_view, blit_stencil); } void BlitImageHelper::ConvertD32ToR32(const Framebuffer* dst_framebuffer, @@ -882,19 +922,183 @@ void BlitImageHelper::ClearDepthStencil(const Framebuffer* dst_framebuffer, bool scheduler.InvalidateState(); } +void BlitImageHelper::CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline, + VkPipelineLayout layout, VkImage dst_image, + VkFormat dst_vk_format, VkImage src_image, + VkFormat src_vk_format, s32 scale_x, s32 scale_y, + std::span copies, + const MSAACopyAspectInfo& aspect_info, bool copy_stencil) { + while (!msaa_copy_resources.empty() && scheduler.IsFree(msaa_copy_resources.front().tick)) { + msaa_copy_resources.pop_front(); + } + const VkSampler sampler = *nearest_sampler; + for (const VideoCommon::ImageCopy& copy : copies) { + const s32 num_layers = (std::min)(copy.src_subresource.num_layers, + copy.dst_subresource.num_layers); + for (s32 layer = 0; layer < num_layers; ++layer) { + const u32 src_level = static_cast(copy.src_subresource.base_level); + const u32 src_layer = static_cast(copy.src_subresource.base_layer + layer); + vk::ImageView src_view = + MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format, src_level, + src_layer, aspect_info.src_view_aspect); + vk::ImageView src_stencil_view; + if (copy_stencil) { + src_stencil_view = + MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format, src_level, + src_layer, VK_IMAGE_ASPECT_STENCIL_BIT); + } + vk::ImageView dst_view = + MakeMSAACopyView(device.GetLogical(), dst_image, dst_vk_format, + static_cast(copy.dst_subresource.base_level), + static_cast(copy.dst_subresource.base_layer + layer), + aspect_info.attachment_aspect); + const VkOffset2D dst_offset{copy.dst_offset.x, copy.dst_offset.y}; + const VkExtent2D dst_extent{copy.extent.width, copy.extent.height}; + const VkRect2D render_area{ + .offset = dst_offset, + .extent = dst_extent, + }; + vk::Framebuffer framebuffer = device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{ + .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .renderPass = renderpass, + .attachmentCount = 1, + .pAttachments = dst_view.address(), + .width = static_cast(dst_offset.x) + dst_extent.width, + .height = static_cast(dst_offset.y) + dst_extent.height, + .layers = 1, + }); + const MSAACopyPushConstants push_constants{ + .dst_offset = {dst_offset.x, dst_offset.y}, + .src_offset = {copy.src_offset.x, copy.src_offset.y}, + .scale = {scale_x, scale_y}, + }; + VkImageView src_stencil_handle = VK_NULL_HANDLE; + if (copy_stencil) { + src_stencil_handle = *src_stencil_view; + } + scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.Record([this, pipeline, layout, sampler, renderpass, + framebuffer_handle = *framebuffer, src_view_handle = *src_view, + src_stencil_handle, src = src_image, dst = dst_image, render_area, + aspect_info, push_constants](vk::CommandBuffer cmdbuf) { + const VkImageSubresourceRange barrier_range{ + .aspectMask = aspect_info.barrier_aspect, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }; + const std::array pre_barriers{ + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = aspect_info.pre_src_access, + .dstAccessMask = aspect_info.pre_src_dst_access, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = src, + .subresourceRange = barrier_range, + }, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = aspect_info.pre_src_access, + .dstAccessMask = aspect_info.pre_dst_dst_access, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = dst, + .subresourceRange = barrier_range, + }, + }; + cmdbuf.PipelineBarrier(aspect_info.pre_src_stages, aspect_info.pre_dst_stages, 0, + nullptr, nullptr, pre_barriers); + const VkRenderPassBeginInfo renderpass_bi{ + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + .pNext = nullptr, + .renderPass = renderpass, + .framebuffer = framebuffer_handle, + .renderArea = render_area, + .clearValueCount = 0, + .pClearValues = nullptr, + }; + cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE); + VkDescriptorSet descriptor_set = VK_NULL_HANDLE; + if (src_stencil_handle != VK_NULL_HANDLE) { + descriptor_set = two_textures_descriptor_allocator.Commit(); + UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_view_handle, + src_stencil_handle); + } else { + descriptor_set = one_texture_descriptor_allocator.Commit(); + UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view_handle); + } + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set, + nullptr); + const VkViewport viewport{ + .x = static_cast(render_area.offset.x), + .y = static_cast(render_area.offset.y), + .width = static_cast(render_area.extent.width), + .height = static_cast(render_area.extent.height), + .minDepth = 0.0f, + .maxDepth = 1.0f, + }; + cmdbuf.SetViewport(0, viewport); + cmdbuf.SetScissor(0, render_area); + cmdbuf.PushConstants(layout, VK_SHADER_STAGE_FRAGMENT_BIT, push_constants); + cmdbuf.Draw(3, 1, 0, 0); + cmdbuf.EndRenderPass(); + const VkImageMemoryBarrier post_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = aspect_info.post_src_access, + .dstAccessMask = aspect_info.post_dst_access, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = dst, + .subresourceRange = barrier_range, + }; + cmdbuf.PipelineBarrier(aspect_info.post_src_stages, aspect_info.post_dst_stages, 0, + post_barrier); + }); + msaa_copy_resources.push_back(MSAACopyResources{ + .tick = scheduler.CurrentTick(), + .src_view = std::move(src_view), + .dst_view = std::move(dst_view), + .framebuffer = std::move(framebuffer), + }); + if (copy_stencil) { + msaa_copy_resources.push_back(MSAACopyResources{ + .tick = scheduler.CurrentTick(), + .src_view = std::move(src_stencil_view), + .dst_view = vk::ImageView{}, + .framebuffer = vk::Framebuffer{}, + }); + } + } + } + scheduler.InvalidateState(); +} + void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_image, VideoCore::Surface::PixelFormat dst_format, VkImage src_image, VideoCore::Surface::PixelFormat src_format, u32 num_samples, std::span copies, bool msaa_to_non_msaa) { - while (!msaa_copy_resources.empty() && scheduler.IsFree(msaa_copy_resources.front().tick)) { - msaa_copy_resources.pop_front(); - } const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(static_cast(num_samples)); const s32 scale_x = 1 << samples_x; const s32 scale_y = 1 << samples_y; - const VkSampleCountFlagBits samples = - msaa_to_non_msaa ? VK_SAMPLE_COUNT_1_BIT : SampleCountFlag(num_samples); + VkSampleCountFlagBits samples = SampleCountFlag(num_samples); + if (msaa_to_non_msaa) { + samples = VK_SAMPLE_COUNT_1_BIT; + } RenderPassKey renderpass_key{}; renderpass_key.color_formats.fill(VideoCore::Surface::PixelFormat::Invalid); renderpass_key.color_formats[0] = dst_format; @@ -905,149 +1109,35 @@ void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_i .renderpass = renderpass, .samples = samples, .msaa_to_non_msaa = msaa_to_non_msaa, + .format_class = FormatClass(dst_format), + }; + const MSAACopyAspectInfo aspect_info{ + .src_view_aspect = VK_IMAGE_ASPECT_COLOR_BIT, + .attachment_aspect = VK_IMAGE_ASPECT_COLOR_BIT, + .barrier_aspect = VK_IMAGE_ASPECT_COLOR_BIT, + .pre_src_access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_WRITE_BIT | + VK_ACCESS_TRANSFER_WRITE_BIT, + .pre_src_dst_access = VK_ACCESS_SHADER_READ_BIT, + .pre_dst_dst_access = + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .pre_src_stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, + .pre_dst_stages = + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .post_src_access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .post_dst_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT, + .post_src_stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .post_dst_stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, }; - const VkPipeline pipeline = FindOrEmplaceMSAACopyPipeline(key); - const VkPipelineLayout layout = *msaa_copy_pipeline_layout; - const VkSampler sampler = *nearest_sampler; const VkFormat src_vk_format = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, src_format).format; const VkFormat dst_vk_format = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, dst_format).format; - for (const VideoCommon::ImageCopy& copy : copies) { - ASSERT(copy.src_subresource.base_layer == 0); - ASSERT(copy.src_subresource.num_layers == 1); - ASSERT(copy.dst_subresource.base_layer == 0); - ASSERT(copy.dst_subresource.num_layers == 1); - vk::ImageView src_view = - MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format, - static_cast(copy.src_subresource.base_level)); - vk::ImageView dst_view = - MakeMSAACopyView(device.GetLogical(), dst_image, dst_vk_format, - static_cast(copy.dst_subresource.base_level)); - const VkOffset2D dst_offset{copy.dst_offset.x, copy.dst_offset.y}; - const VkExtent2D dst_extent{copy.extent.width, copy.extent.height}; - const VkRect2D render_area{ - .offset = dst_offset, - .extent = dst_extent, - }; - vk::Framebuffer framebuffer = device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{ - .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .renderPass = renderpass, - .attachmentCount = 1, - .pAttachments = dst_view.address(), - .width = static_cast(dst_offset.x) + dst_extent.width, - .height = static_cast(dst_offset.y) + dst_extent.height, - .layers = 1, - }); - const MSAACopyPushConstants push_constants{ - .dst_offset = {dst_offset.x, dst_offset.y}, - .src_offset = {copy.src_offset.x, copy.src_offset.y}, - .scale = {scale_x, scale_y}, - }; - scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([this, pipeline, layout, sampler, renderpass, - framebuffer_handle = *framebuffer, src_view_handle = *src_view, - src = src_image, dst = dst_image, render_area, - push_constants](vk::CommandBuffer cmdbuf) { - constexpr VkImageSubresourceRange color_range{ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = VK_REMAINING_MIP_LEVELS, - .baseArrayLayer = 0, - .layerCount = VK_REMAINING_ARRAY_LAYERS, - }; - const std::array pre_barriers{ - VkImageMemoryBarrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | - VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, - .oldLayout = VK_IMAGE_LAYOUT_GENERAL, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = src, - .subresourceRange = color_range, - }, - VkImageMemoryBarrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | - VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, - .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - .oldLayout = VK_IMAGE_LAYOUT_GENERAL, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = dst, - .subresourceRange = color_range, - }, - }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - 0, nullptr, nullptr, pre_barriers); - const VkRenderPassBeginInfo renderpass_bi{ - .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, - .pNext = nullptr, - .renderPass = renderpass, - .framebuffer = framebuffer_handle, - .renderArea = render_area, - .clearValueCount = 0, - .pClearValues = nullptr, - }; - cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE); - const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit(); - UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view_handle); - cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set, - nullptr); - const VkViewport viewport{ - .x = static_cast(render_area.offset.x), - .y = static_cast(render_area.offset.y), - .width = static_cast(render_area.extent.width), - .height = static_cast(render_area.extent.height), - .minDepth = 0.0f, - .maxDepth = 1.0f, - }; - cmdbuf.SetViewport(0, viewport); - cmdbuf.SetScissor(0, render_area); - cmdbuf.PushConstants(layout, VK_SHADER_STAGE_FRAGMENT_BIT, push_constants); - cmdbuf.Draw(3, 1, 0, 0); - cmdbuf.EndRenderPass(); - const VkImageMemoryBarrier post_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT, - .oldLayout = VK_IMAGE_LAYOUT_GENERAL, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = dst, - .subresourceRange = color_range, - }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | - VK_PIPELINE_STAGE_TRANSFER_BIT, - 0, post_barrier); - }); - msaa_copy_resources.push_back(MSAACopyResources{ - .tick = scheduler.CurrentTick(), - .src_view = std::move(src_view), - .dst_view = std::move(dst_view), - .framebuffer = std::move(framebuffer), - }); - } - scheduler.InvalidateState(); + CopyMSAAImpl(renderpass, FindOrEmplaceMSAACopyPipeline(key), *msaa_copy_pipeline_layout, + dst_image, dst_vk_format, src_image, src_vk_format, scale_x, scale_y, copies, + aspect_info, false); } void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, @@ -1379,6 +1469,87 @@ VkPipeline BlitImageHelper::FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPip return *blit_msaa_color_pipelines.back(); } +VkPipeline BlitImageHelper::FindOrEmplaceBlitDepthStencilMSAAPipeline( + const BlitMSAAPipelineKey& key, bool blit_stencil) { + auto& keys = blit_stencil ? blit_msaa_depth_stencil_keys : blit_msaa_depth_keys; + auto& pipelines = blit_stencil ? blit_msaa_depth_stencil_pipelines : blit_msaa_depth_pipelines; + const auto it = std::ranges::find(keys, key); + if (it != keys.end()) { + return *pipelines[std::distance(keys.begin(), it)]; + } + keys.push_back(key); + const std::array stages = + MakeStages(*full_screen_vert, + blit_stencil ? *blit_depth_stencil_msaa_frag : *blit_depth_msaa_frag); + const VkPipelineMultisampleStateCreateInfo multisample_ci{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .rasterizationSamples = key.samples, + .sampleShadingEnable = VK_TRUE, + .minSampleShading = 1.0f, + .pSampleMask = nullptr, + .alphaToCoverageEnable = VK_FALSE, + .alphaToOneEnable = VK_FALSE, + }; + const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device); + pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({ + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .stageCount = static_cast(stages.size()), + .pStages = stages.data(), + .pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + .pInputAssemblyState = &input_assembly_ci, + .pTessellationState = nullptr, + .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO, + .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + .pMultisampleState = &multisample_ci, + .pDepthStencilState = blit_stencil ? &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO + : &PIPELINE_DEPTH_ONLY_STATE_CREATE_INFO, + .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO, + .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO, + .layout = blit_stencil ? *two_textures_pipeline_layout : *one_texture_pipeline_layout, + .renderPass = key.renderpass, + .subpass = 0, + .basePipelineHandle = VK_NULL_HANDLE, + .basePipelineIndex = 0, + })); + return *pipelines.back(); +} + +VkPipeline BlitImageHelper::FindOrEmplaceBlitDepthPipeline(VkRenderPass renderpass) { + const auto it = std::ranges::find(blit_depth_keys, renderpass); + if (it != blit_depth_keys.end()) { + return *blit_depth_pipelines[std::distance(blit_depth_keys.begin(), it)]; + } + blit_depth_keys.push_back(renderpass); + const std::array stages = MakeStages(*full_screen_vert, *blit_depth_frag); + const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device); + blit_depth_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({ + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .stageCount = static_cast(stages.size()), + .pStages = stages.data(), + .pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + .pInputAssemblyState = &input_assembly_ci, + .pTessellationState = nullptr, + .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO, + .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + .pDepthStencilState = &PIPELINE_DEPTH_ONLY_STATE_CREATE_INFO, + .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO, + .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO, + .layout = *one_texture_pipeline_layout, + .renderPass = renderpass, + .subpass = 0, + .basePipelineHandle = VK_NULL_HANDLE, + .basePipelineIndex = 0, + })); + return *blit_depth_pipelines.back(); +} + VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass, bool resolve_stencil) { auto& keys = resolve_stencil ? resolve_depth_stencil_keys : resolve_depth_keys; @@ -1417,15 +1588,83 @@ VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline(VkRenderPas return *pipelines.back(); } +void BlitImageHelper::CopyMSAADepth(RenderPassCache& render_pass_cache, VkImage dst_image, + VideoCore::Surface::PixelFormat dst_format, VkImage src_image, + VideoCore::Surface::PixelFormat src_format, u32 num_samples, + std::span copies, + bool copy_stencil, bool msaa_to_non_msaa) { + const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(static_cast(num_samples)); + const s32 scale_x = 1 << samples_x; + const s32 scale_y = 1 << samples_y; + VkSampleCountFlagBits samples = SampleCountFlag(num_samples); + if (msaa_to_non_msaa) { + samples = VK_SAMPLE_COUNT_1_BIT; + } + RenderPassKey renderpass_key{}; + renderpass_key.color_formats.fill(VideoCore::Surface::PixelFormat::Invalid); + renderpass_key.depth_format = dst_format; + renderpass_key.samples = samples; + const VkRenderPass renderpass = render_pass_cache.Get(renderpass_key); + const MSAACopyPipelineKey key{ + .renderpass = renderpass, + .samples = samples, + .msaa_to_non_msaa = msaa_to_non_msaa, + .format_class = MSAACopyFormatClass::Float, + }; + VkImageAspectFlags attachment_aspect = VK_IMAGE_ASPECT_DEPTH_BIT; + if (VideoCore::Surface::GetFormatType(dst_format) == + VideoCore::Surface::SurfaceType::DepthStencil) { + attachment_aspect |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + VkPipelineLayout layout = *msaa_copy_pipeline_layout; + if (copy_stencil) { + layout = *msaa_copy_depth_stencil_pipeline_layout; + } + const MSAACopyAspectInfo aspect_info{ + .src_view_aspect = VK_IMAGE_ASPECT_DEPTH_BIT, + .attachment_aspect = attachment_aspect, + .barrier_aspect = attachment_aspect, + .pre_src_access = + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, + .pre_src_dst_access = VK_ACCESS_SHADER_READ_BIT, + .pre_dst_dst_access = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + .pre_src_stages = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_TRANSFER_BIT, + .pre_dst_stages = + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, + .post_src_access = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + .post_dst_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT, + .post_src_stages = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, + .post_dst_stages = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, + }; + const VkFormat src_vk_format = + MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, src_format).format; + const VkFormat dst_vk_format = + MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, dst_format).format; + CopyMSAAImpl(renderpass, FindOrEmplaceMSAACopyDepthPipeline(key, copy_stencil), layout, + dst_image, dst_vk_format, src_image, src_vk_format, scale_x, scale_y, copies, + aspect_info, copy_stencil); +} + VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipelineKey& key) { const auto it = std::ranges::find(msaa_copy_keys, key); if (it != msaa_copy_keys.end()) { return *msaa_copy_pipelines[std::distance(msaa_copy_keys.begin(), it)]; } msaa_copy_keys.push_back(key); - const std::array stages = MakeStages(*clear_color_vert, key.msaa_to_non_msaa - ? *convert_msaa_to_non_msaa_frag - : *convert_non_msaa_to_msaa_frag); + VkShaderModule frag_module = key.msaa_to_non_msaa ? *convert_msaa_to_non_msaa_frag + : *convert_non_msaa_to_msaa_frag; + if (key.format_class == MSAACopyFormatClass::SignedInteger) { + frag_module = key.msaa_to_non_msaa ? *convert_msaa_to_non_msaa_sint_frag + : *convert_non_msaa_to_msaa_sint_frag; + } else if (key.format_class == MSAACopyFormatClass::UnsignedInteger) { + frag_module = key.msaa_to_non_msaa ? *convert_msaa_to_non_msaa_uint_frag + : *convert_non_msaa_to_msaa_uint_frag; + } + const std::array stages = MakeStages(*clear_color_vert, frag_module); const VkPipelineMultisampleStateCreateInfo multisample_ci{ .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, .pNext = nullptr, @@ -1462,6 +1701,85 @@ VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipeline return *msaa_copy_pipelines.back(); } +VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyDepthPipeline(const MSAACopyPipelineKey& key, + bool copy_stencil) { + auto& keys = copy_stencil ? msaa_copy_depth_stencil_keys : msaa_copy_depth_keys; + auto& pipelines = copy_stencil ? msaa_copy_depth_stencil_pipelines : msaa_copy_depth_pipelines; + const auto it = std::ranges::find(keys, key); + if (it != keys.end()) { + return *pipelines[std::distance(keys.begin(), it)]; + } + keys.push_back(key); + VkShaderModule frag_module; + if (key.msaa_to_non_msaa) { + frag_module = copy_stencil ? *convert_msaa_to_non_msaa_depth_stencil_frag + : *convert_msaa_to_non_msaa_depth_frag; + } else { + frag_module = copy_stencil ? *convert_non_msaa_to_msaa_depth_stencil_frag + : *convert_non_msaa_to_msaa_depth_frag; + } + const std::array stages = MakeStages(*clear_color_vert, frag_module); + const VkPipelineMultisampleStateCreateInfo multisample_ci{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .rasterizationSamples = key.samples, + .sampleShadingEnable = key.msaa_to_non_msaa ? VK_FALSE : VK_TRUE, + .minSampleShading = key.msaa_to_non_msaa ? 0.0f : 1.0f, + .pSampleMask = nullptr, + .alphaToCoverageEnable = VK_FALSE, + .alphaToOneEnable = VK_FALSE, + }; + static constexpr VkStencilOpState REPLACE_STENCIL_OP{ + .failOp = VK_STENCIL_OP_REPLACE, + .passOp = VK_STENCIL_OP_REPLACE, + .depthFailOp = VK_STENCIL_OP_REPLACE, + .compareOp = VK_COMPARE_OP_ALWAYS, + .compareMask = 0xFF, + .writeMask = 0xFF, + .reference = 0, + }; + const VkPipelineDepthStencilStateCreateInfo depth_stencil_ci{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .depthTestEnable = VK_TRUE, + .depthWriteEnable = VK_TRUE, + .depthCompareOp = VK_COMPARE_OP_ALWAYS, + .depthBoundsTestEnable = VK_FALSE, + .stencilTestEnable = copy_stencil ? VK_TRUE : VK_FALSE, + .front = copy_stencil ? REPLACE_STENCIL_OP : VkStencilOpState{}, + .back = copy_stencil ? REPLACE_STENCIL_OP : VkStencilOpState{}, + .minDepthBounds = 0.0f, + .maxDepthBounds = 0.0f, + }; + const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = + GetPipelineInputAssemblyStateCreateInfo(device); + pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({ + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .stageCount = static_cast(stages.size()), + .pStages = stages.data(), + .pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + .pInputAssemblyState = &input_assembly_ci, + .pTessellationState = nullptr, + .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO, + .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + .pMultisampleState = &multisample_ci, + .pDepthStencilState = &depth_stencil_ci, + .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO, + .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO, + .layout = copy_stencil ? *msaa_copy_depth_stencil_pipeline_layout + : *msaa_copy_pipeline_layout, + .renderPass = key.renderpass, + .subpass = 0, + .basePipelineHandle = VK_NULL_HANDLE, + .basePipelineIndex = 0, + })); + return *pipelines.back(); +} + void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) { ConvertPipeline(pipeline, renderpass, false); } diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h index 67294d594b..62e6743a23 100644 --- a/src/video_core/renderer_vulkan/blit_image.h +++ b/src/video_core/renderer_vulkan/blit_image.h @@ -45,12 +45,19 @@ struct BlitDepthStencilPipelineKey { u32 stencil_ref; }; +enum class MSAACopyFormatClass : u32 { + Float, + SignedInteger, + UnsignedInteger, +}; + struct MSAACopyPipelineKey { constexpr auto operator<=>(const MSAACopyPipelineKey&) const noexcept = default; VkRenderPass renderpass; VkSampleCountFlagBits samples; bool msaa_to_non_msaa; + MSAACopyFormatClass format_class; }; struct BlitMSAAPipelineKey { @@ -78,6 +85,12 @@ public: void BlitColorMSAA(const Framebuffer* dst_framebuffer, const ImageView& src_image_view, const Region2D& dst_region, const Region2D& src_region); + void BlitDepthStencilMSAA(const Framebuffer* dst_framebuffer, ImageView& src_image_view, + const Region2D& dst_region, const Region2D& src_region); + + void BlitDepth(const Framebuffer* dst_framebuffer, ImageView& src_image_view, + const Region2D& dst_region, const Region2D& src_region); + void ResolveDepthStencil(const Framebuffer* dst_framebuffer, ImageView& src_image_view, const Region2D& dst_region, const Region2D& src_region); @@ -116,7 +129,39 @@ public: VideoCore::Surface::PixelFormat src_format, u32 num_samples, std::span copies, bool msaa_to_non_msaa); + void CopyMSAADepth(RenderPassCache& render_pass_cache, VkImage dst_image, + VideoCore::Surface::PixelFormat dst_format, VkImage src_image, + VideoCore::Surface::PixelFormat src_format, u32 num_samples, + std::span copies, bool copy_stencil, + bool msaa_to_non_msaa); + private: + struct MSAACopyAspectInfo { + VkImageAspectFlags src_view_aspect; + VkImageAspectFlags attachment_aspect; + VkImageAspectFlags barrier_aspect; + VkAccessFlags pre_src_access; + VkAccessFlags pre_src_dst_access; + VkAccessFlags pre_dst_dst_access; + VkPipelineStageFlags pre_src_stages; + VkPipelineStageFlags pre_dst_stages; + VkAccessFlags post_src_access; + VkAccessFlags post_dst_access; + VkPipelineStageFlags post_src_stages; + VkPipelineStageFlags post_dst_stages; + }; + + void BlitImpl(const Framebuffer* dst_framebuffer, const ImageView& src_image_view, + const Region2D& dst_region, const Region2D& src_region, VkPipeline pipeline, + VkSampler sampler, VkImageView src_view, VkImageView src_stencil_view, + bool blit_stencil); + + void CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline, VkPipelineLayout layout, + VkImage dst_image, VkFormat dst_vk_format, VkImage src_image, + VkFormat src_vk_format, s32 scale_x, s32 scale_y, + std::span copies, + const MSAACopyAspectInfo& aspect_info, bool copy_stencil); + void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, const ImageView& src_image_view); @@ -131,7 +176,13 @@ private: [[nodiscard]] VkPipeline FindOrEmplaceClearStencilPipeline( const BlitDepthStencilPipelineKey& key); [[nodiscard]] VkPipeline FindOrEmplaceMSAACopyPipeline(const MSAACopyPipelineKey& key); + + [[nodiscard]] VkPipeline FindOrEmplaceMSAACopyDepthPipeline(const MSAACopyPipelineKey& key, + bool copy_stencil); [[nodiscard]] VkPipeline FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPipelineKey& key); + [[nodiscard]] VkPipeline FindOrEmplaceBlitDepthStencilMSAAPipeline( + const BlitMSAAPipelineKey& key, bool blit_stencil); + [[nodiscard]] VkPipeline FindOrEmplaceBlitDepthPipeline(VkRenderPass renderpass); [[nodiscard]] VkPipeline FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass, bool resolve_stencil); @@ -162,10 +213,12 @@ private: vk::PipelineLayout two_textures_pipeline_layout; vk::PipelineLayout clear_color_pipeline_layout; vk::PipelineLayout msaa_copy_pipeline_layout; + vk::PipelineLayout msaa_copy_depth_stencil_pipeline_layout; vk::ShaderModule full_screen_vert; vk::ShaderModule blit_color_to_color_frag; vk::ShaderModule blit_color_msaa_frag; vk::ShaderModule blit_depth_stencil_frag; + vk::ShaderModule blit_depth_frag; vk::ShaderModule blit_depth_msaa_frag; vk::ShaderModule blit_depth_stencil_msaa_frag; vk::ShaderModule clear_color_vert; @@ -179,7 +232,15 @@ private: vk::ShaderModule convert_d24s8_to_abgr8_frag; vk::ShaderModule convert_s8d24_to_abgr8_frag; vk::ShaderModule convert_msaa_to_non_msaa_frag; + vk::ShaderModule convert_msaa_to_non_msaa_sint_frag; + vk::ShaderModule convert_msaa_to_non_msaa_uint_frag; + vk::ShaderModule convert_msaa_to_non_msaa_depth_frag; + vk::ShaderModule convert_msaa_to_non_msaa_depth_stencil_frag; vk::ShaderModule convert_non_msaa_to_msaa_frag; + vk::ShaderModule convert_non_msaa_to_msaa_sint_frag; + vk::ShaderModule convert_non_msaa_to_msaa_uint_frag; + vk::ShaderModule convert_non_msaa_to_msaa_depth_frag; + vk::ShaderModule convert_non_msaa_to_msaa_depth_stencil_frag; vk::Sampler linear_sampler; vk::Sampler nearest_sampler; @@ -193,8 +254,18 @@ private: std::vector clear_stencil_pipelines; std::vector msaa_copy_keys; std::vector msaa_copy_pipelines; + std::vector msaa_copy_depth_keys; + std::vector msaa_copy_depth_pipelines; + std::vector msaa_copy_depth_stencil_keys; + std::vector msaa_copy_depth_stencil_pipelines; std::vector blit_msaa_color_keys; std::vector blit_msaa_color_pipelines; + std::vector blit_depth_keys; + std::vector blit_depth_pipelines; + std::vector blit_msaa_depth_keys; + std::vector blit_msaa_depth_pipelines; + std::vector blit_msaa_depth_stencil_keys; + std::vector blit_msaa_depth_stencil_pipelines; std::vector resolve_depth_keys; std::vector resolve_depth_pipelines; std::vector resolve_depth_stencil_keys; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index a10d2e01e6..3b0fea2fec 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -424,24 +424,12 @@ void RasterizerVulkan::Clear(u32 layer_count) { const bool ds_deferrable = !ds_used || ((!framebuffer->HasAspectDepthBit() || use_depth) && (!framebuffer->HasAspectStencilBit() || use_stencil) && !stencil_partial); - const bool can_defer_clear = ENABLE_DEFERRED_CLEAR && !regs.clear_control.use_scissor && - regs.clear_surface.layer == 0 && - !scheduler.IsRenderPassActive() && - (!use_color || color_full_channels) && ds_deferrable; - if (!can_defer_clear) { - scheduler.RequestRenderpass(framebuffer); - } - - query_cache.NotifySegment(true); - query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, maxwell3d->regs.zpass_pixel_count_enable); u32 up_scale = 1; u32 down_shift = 0; if (texture_cache.IsRescaling()) { up_scale = Settings::values.resolution_info.up_scale; down_shift = Settings::values.resolution_info.down_shift; } - UpdateViewportsState(regs); - VkRect2D default_scissor{}; default_scissor.offset.x = 0; default_scissor.offset.y = 0; @@ -497,6 +485,22 @@ void RasterizerVulkan::Clear(u32 layer_count) { return; } + const bool clear_covers_render_area = + clear_rect.rect.offset.x == 0 && clear_rect.rect.offset.y == 0 && + clear_rect.rect.extent.width >= render_area.width && + clear_rect.rect.extent.height >= render_area.height; + const bool can_defer_clear = ENABLE_DEFERRED_CLEAR && (!regs.clear_control.use_scissor || clear_covers_render_area) && + regs.clear_surface.layer == 0 && + !scheduler.IsRenderPassActive() && + (!use_color || color_full_channels) && ds_deferrable; + if (!can_defer_clear) { + scheduler.RequestRenderpass(framebuffer); + } + + query_cache.NotifySegment(true); + query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, maxwell3d->regs.zpass_pixel_count_enable); + UpdateViewportsState(regs); + const u32 color_attachment = regs.clear_surface.RT; if (use_color && framebuffer->HasAspectColorBit(color_attachment)) { const auto format = VideoCore::Surface::PixelFormatFromRenderTargetFormat(regs.rt[color_attachment].format); diff --git a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp index 66bade9a2e..1846f1b3cc 100644 --- a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp @@ -65,8 +65,72 @@ using VideoCore::Surface::SurfaceType; .finalLayout = VK_IMAGE_LAYOUT_GENERAL, }; } + + struct ResolveAspects { + bool depth; + bool stencil; + }; + + struct ResolveModes { + VkResolveModeFlagBits depth; + VkResolveModeFlagBits stencil; + }; + + constexpr ResolveAspects GetResolveAspects(PixelFormat format) { + const SurfaceType surface_type = GetSurfaceType(format); + return ResolveAspects{ + .depth = surface_type == SurfaceType::Depth || + surface_type == SurfaceType::DepthStencil, + .stencil = surface_type == SurfaceType::Stencil || + surface_type == SurfaceType::DepthStencil, + }; + } + + ResolveModes PickResolveModes(const Device& device, PixelFormat format) { + constexpr VkResolveModeFlagBits mode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT; + + const ResolveAspects aspects = GetResolveAspects(format); + const bool depth_mode_supported = (device.GetDepthResolveModes() & mode) != 0; + const bool stencil_mode_supported = (device.GetStencilResolveModes() & mode) != 0; + + ResolveModes modes{ + .depth = VK_RESOLVE_MODE_NONE, + .stencil = VK_RESOLVE_MODE_NONE, + }; + if (aspects.depth && depth_mode_supported) { + modes.depth = mode; + } + if (aspects.stencil && stencil_mode_supported) { + modes.stencil = mode; + } + if (modes.depth == modes.stencil || device.SupportsIndependentResolveNone()) { + return modes; + } + if (modes.depth != VK_RESOLVE_MODE_NONE && stencil_mode_supported) { + modes.stencil = mode; + } else if (modes.stencil != VK_RESOLVE_MODE_NONE && depth_mode_supported) { + modes.depth = mode; + } + return modes; + } } // Anonymous namespace +bool SupportsDepthStencilResolve(const Device& device, PixelFormat depth_format) { + if (depth_format == PixelFormat::Invalid || !device.IsKhrDepthStencilResolveSupported()) { + return false; + } + const ResolveAspects aspects = GetResolveAspects(depth_format); + if (!aspects.depth && !aspects.stencil) { + return false; + } + const ResolveModes modes = PickResolveModes(device, depth_format); + if ((aspects.depth && modes.depth == VK_RESOLVE_MODE_NONE) || + (aspects.stencil && modes.stencil == VK_RESOLVE_MODE_NONE)) { + return false; + } + return modes.depth == modes.stencil || device.SupportsIndependentResolveNone(); +} + RenderPassCache::RenderPassCache(const Device& device_) : device{&device_} {} VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { @@ -75,7 +139,9 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { if (!is_new) { return *pair->second; } - boost::container::static_vector descriptions; + static constexpr size_t MAX_ATTACHMENTS = + 2 * std::tuple_size_v + 2; + boost::container::static_vector descriptions; std::array references{}; u32 num_attachments{}; u32 num_colors{}; @@ -109,8 +175,11 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { const VkAttachmentLoadOp depth_load_op = key.depth_stencil_clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD; + const VkAttachmentStoreOp depth_store_op = key.depth_stencil_discard + ? VK_ATTACHMENT_STORE_OP_DONT_CARE + : VK_ATTACHMENT_STORE_OP_STORE; descriptions.push_back(AttachmentDescription(*device, key.depth_format, key.samples, - depth_load_op, VK_ATTACHMENT_STORE_OP_STORE)); + depth_load_op, depth_store_op)); } std::array resolve_references{}; const bool do_resolve_color = @@ -133,6 +202,21 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { } } } + const bool do_resolve_depth_stencil = key.resolve_depth_stencil && has_depth && + key.samples != VK_SAMPLE_COUNT_1_BIT && + SupportsDepthStencilResolve(*device, key.depth_format); + VkAttachmentReference depth_resolve_reference{}; + if (do_resolve_depth_stencil) { + depth_resolve_reference = VkAttachmentReference{ + .attachment = static_cast(descriptions.size()), + .layout = VK_IMAGE_LAYOUT_GENERAL, + }; + VkAttachmentDescription resolve_desc = + AttachmentDescription(*device, key.depth_format, VK_SAMPLE_COUNT_1_BIT, + VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_STORE); + resolve_desc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + descriptions.push_back(resolve_desc); + } const VkSubpassDescription subpass{ .flags = 0, .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, @@ -157,6 +241,92 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT }; + + if (device->IsKhrCreateRenderPass2Supported()) { + boost::container::static_vector descriptions2; + for (const VkAttachmentDescription& description : descriptions) { + descriptions2.push_back(VkAttachmentDescription2{ + .sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2, + .pNext = nullptr, + .flags = description.flags, + .format = description.format, + .samples = description.samples, + .loadOp = description.loadOp, + .storeOp = description.storeOp, + .stencilLoadOp = description.stencilLoadOp, + .stencilStoreOp = description.stencilStoreOp, + .initialLayout = description.initialLayout, + .finalLayout = description.finalLayout, + }); + } + const auto promote = [](const VkAttachmentReference& reference) { + return VkAttachmentReference2{ + .sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2, + .pNext = nullptr, + .attachment = reference.attachment, + .layout = reference.layout, + .aspectMask = 0, + }; + }; + std::array references2{}; + std::array resolve_references2{}; + for (size_t index = 0; index < references.size(); ++index) { + references2[index] = promote(references[index]); + resolve_references2[index] = promote(resolve_references[index]); + } + const VkAttachmentReference2 depth_reference2 = promote(depth_reference); + const VkAttachmentReference2 depth_resolve_reference2 = promote(depth_resolve_reference); + const ResolveModes resolve_modes = PickResolveModes(*device, key.depth_format); + const VkSubpassDescriptionDepthStencilResolve depth_stencil_resolve{ + .sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE, + .pNext = nullptr, + .depthResolveMode = resolve_modes.depth, + .stencilResolveMode = resolve_modes.stencil, + .pDepthStencilResolveAttachment = &depth_resolve_reference2, + }; + const VkSubpassDescription2 subpass2{ + .sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2, + .pNext = do_resolve_depth_stencil ? &depth_stencil_resolve : nullptr, + .flags = 0, + .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, + .viewMask = 0, + .inputAttachmentCount = 0, + .pInputAttachments = nullptr, + .colorAttachmentCount = num_attachments, + .pColorAttachments = references2.data(), + .pResolveAttachments = do_resolve_color ? resolve_references2.data() : nullptr, + .pDepthStencilAttachment = has_depth ? &depth_reference2 : nullptr, + .preserveAttachmentCount = 0, + .pPreserveAttachments = nullptr, + }; + const VkSubpassDependency2 dependency2{ + .sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2, + .pNext = nullptr, + .srcSubpass = dependency.srcSubpass, + .dstSubpass = dependency.dstSubpass, + .srcStageMask = dependency.srcStageMask, + .dstStageMask = dependency.dstStageMask, + .srcAccessMask = dependency.srcAccessMask, + .dstAccessMask = dependency.dstAccessMask, + .dependencyFlags = dependency.dependencyFlags, + .viewOffset = 0, + }; + pair->second = device->GetLogical().CreateRenderPass2({ + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2, + .pNext = nullptr, + .flags = 0, + .attachmentCount = static_cast(descriptions2.size()), + .pAttachments = descriptions2.empty() ? nullptr : descriptions2.data(), + .subpassCount = 1, + .pSubpasses = &subpass2, + .dependencyCount = 1, + .pDependencies = &dependency2, + .correlatedViewMaskCount = 0, + .pCorrelatedViewMasks = nullptr, + }); + return *pair->second; + } + pair->second = device->GetLogical().CreateRenderPass({ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, .pNext = nullptr, diff --git a/src/video_core/renderer_vulkan/vk_render_pass_cache.h b/src/video_core/renderer_vulkan/vk_render_pass_cache.h index 75999a655e..f9923be75a 100644 --- a/src/video_core/renderer_vulkan/vk_render_pass_cache.h +++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.h @@ -9,6 +9,7 @@ #include #include +#include "common/container_hash.h" #include "video_core/surface.h" #include "video_core/vulkan_common/vulkan_wrapper.h" @@ -21,9 +22,11 @@ struct RenderPassKey { VideoCore::Surface::PixelFormat depth_format; VkSampleCountFlagBits samples; bool resolve_color; + bool resolve_depth_stencil; u32 color_clear_mask; bool depth_stencil_clear; u32 color_discard_mask; + bool depth_stencil_discard; }; } // namespace Vulkan @@ -31,17 +34,28 @@ struct RenderPassKey { namespace std { template <> struct hash { + static_assert(std::tuple_size_v <= 8); + static_assert(static_cast(VideoCore::Surface::PixelFormat::Invalid) <= 0xFF); + static_assert(static_cast(VideoCore::Surface::PixelFormat::Max) <= 0xFF); + static_assert(VK_SAMPLE_COUNT_64_BIT <= 0xFF); + [[nodiscard]] size_t operator()(const Vulkan::RenderPassKey& key) const noexcept { - size_t value = static_cast(key.depth_format) << 48; - value ^= static_cast(key.samples) << 52; - value ^= static_cast(key.resolve_color) << 63; - value ^= static_cast(key.color_clear_mask) << 54; - value ^= static_cast(key.depth_stencil_clear) << 62; - value ^= static_cast(key.color_discard_mask) << 24; - for (size_t i = 0; i < key.color_formats.size(); ++i) { - value ^= static_cast(key.color_formats[i]) << (i * 6); + u64 formats = 0; + for (size_t index = 0; index < key.color_formats.size(); ++index) { + formats |= static_cast(key.color_formats[index]) << (index * 8); } - return value; + const u64 state = static_cast(key.depth_format) | + (static_cast(key.samples) << 8) | + (static_cast(key.color_clear_mask) << 16) | + (static_cast(key.color_discard_mask) << 24) | + (static_cast(key.resolve_color) << 32) | + (static_cast(key.depth_stencil_clear) << 33) | + (static_cast(key.resolve_depth_stencil) << 34) | + (static_cast(key.depth_stencil_discard) << 35); + size_t seed = 0; + Common::HashCombine(seed, formats); + Common::HashCombine(seed, state); + return seed; } }; } // namespace std @@ -50,6 +64,9 @@ namespace Vulkan { class Device; +[[nodiscard]] bool SupportsDepthStencilResolve(const Device& device, + VideoCore::Surface::PixelFormat depth_format); + class RenderPassCache { public: explicit RenderPassCache(const Device& device_); diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 3601a3e891..92dcd20111 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -132,6 +132,7 @@ void Scheduler::BeginRenderPassImpl(const Framebuffer* framebuffer, VkRenderPass num_renderpass_images = framebuffer->NumImages(); renderpass_images = framebuffer->Images(); renderpass_image_ranges = framebuffer->ImageRanges(); + framebuffer->MarkResolveShadowsUpToDate(); } void Scheduler::RealizeDeferredClear() { @@ -155,8 +156,10 @@ void Scheduler::RealizeDeferredClear() { } const u32 color_discard_mask = dc.framebuffer->DiscardsMsaaColor() ? dc.color_clear_mask : 0u; + const bool depth_stencil_discard = + dc.depth_stencil && dc.framebuffer->DiscardsMsaaDepthStencil(); const VkRenderPass renderpass = dc.framebuffer->RenderPassVariant( - dc.color_clear_mask, dc.depth_stencil, color_discard_mask); + dc.color_clear_mask, dc.depth_stencil, color_discard_mask, depth_stencil_discard); EndRenderPass(); BeginRenderPassImpl(dc.framebuffer, renderpass, clear_values.data(), count); } @@ -190,6 +193,14 @@ bool Scheduler::DeferDepthStencilClear(const Framebuffer* framebuffer, const VkC return true; } +void Scheduler::FlushDeferredClear() { + if (deferred_clear.framebuffer == nullptr) { + return; + } + RealizeDeferredClear(); + EndRenderPass(); +} + void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) { if (deferred_clear.framebuffer == framebuffer) { RealizeDeferredClear(); diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index c2aa3fb996..d4bc8d0ec5 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -65,6 +65,9 @@ public: /// Defers a full depth/stencil clear so it becomes the next render pass. bool DeferDepthStencilClear(const Framebuffer* framebuffer, const VkClearValue& value); + /// Realizes any pending deferred clear before its framebuffer can be moved or freed. + void FlushDeferredClear(); + /// Requests the current execution context to be able to execute operations only allowed outside /// of a renderpass. void RequestOutsideRenderPassOperationContext(); diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index bd3117037f..0a80d03ef5 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -5,10 +5,12 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include +#include #include #include #include #include +#include #include #include #include @@ -52,8 +54,10 @@ using VideoCore::Surface::IsPixelFormatInteger; using VideoCore::Surface::SurfaceType; namespace { +constexpr bool ENABLE_MSAA_TILER_RESOLVE = true; constexpr bool ENABLE_MSAA_RESOLVE_CONSUME = true; constexpr bool ENABLE_MSAA_COLOR_DISCARD = true; +constexpr bool ENABLE_MSAA_DEPTH_STENCIL_DISCARD = true; constexpr VkBorderColor ConvertBorderColor(const std::array& color) { if (color == std::array{0, 0, 0, 0}) { @@ -108,7 +112,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& color) { } [[nodiscard]] VkImageUsageFlags ImageUsageFlags(const MaxwellToVK::FormatInfo& info, - PixelFormat format) { + PixelFormat format, bool allow_storage = true) { VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; if (info.attachable) { @@ -126,7 +130,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& color) { break; } } - if (info.storage) { + if (info.storage && allow_storage) { usage |= VK_IMAGE_USAGE_STORAGE_BIT; } return usage; @@ -162,6 +166,8 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& color) { flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT; } const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(info.num_samples); + const bool allow_storage = + info.num_samples == 1 || device.IsStorageImageMultisampleSupported(); return VkImageCreateInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .pNext = nullptr, @@ -177,7 +183,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& color) { .arrayLayers = static_cast(info.resources.layers), .samples = ConvertSampleCount(info.num_samples), .tiling = VK_IMAGE_TILING_OPTIMAL, - .usage = ImageUsageFlags(format_info, info.format), + .usage = ImageUsageFlags(format_info, info.format, allow_storage), .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, @@ -185,6 +191,18 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& color) { }; } +[[nodiscard]] VkImageCreateInfo MakeMsaaScratchImageCreateInfo(const Device& device, + const ImageInfo& info, + VkImageUsageFlags usage) { + ImageInfo temp_info = info; + temp_info.num_samples = 1; + VkImageCreateInfo image_ci = MakeImageCreateInfo(device, temp_info); + image_ci.format = + MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, info.format).format; + image_ci.usage = usage; + return image_ci; +} + [[nodiscard]] vk::Image MakeImage(const Device& device, const MemoryAllocator& allocator, const ImageInfo& info, std::span view_formats, std::optional format_override = {}) { @@ -677,6 +695,11 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im }; } +[[nodiscard]] bool HaveSameExtent(const Region2D& dst_region, const Region2D& src_region) { + return dst_region.end.x - dst_region.start.x == src_region.end.x - src_region.start.x && + dst_region.end.y - dst_region.start.y == src_region.end.y - src_region.start.y; +} + [[nodiscard]] VkImageResolve MakeImageResolve(const Region2D& dst_region, const Region2D& src_region, const VkImageSubresourceLayers& dst_layers, @@ -774,15 +797,15 @@ void BlitScale(Scheduler& scheduler, VkImage src_image, VkImage dst_image, const scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([dst_image, src_image, extent, resources, aspect_mask, resolution, is_2d, vk_filter, up_scaling](vk::CommandBuffer cmdbuf) { + const u32 scaled_width = resolution.ScaleUp(extent.width); + const u32 scaled_height = is_2d ? resolution.ScaleUp(extent.height) : extent.height; const VkOffset2D src_size{ - .x = static_cast(up_scaling ? extent.width : resolution.ScaleUp(extent.width)), - .y = static_cast(is_2d && up_scaling ? extent.height - : resolution.ScaleUp(extent.height)), + .x = static_cast(up_scaling ? extent.width : scaled_width), + .y = static_cast(up_scaling ? extent.height : scaled_height), }; const VkOffset2D dst_size{ - .x = static_cast(up_scaling ? resolution.ScaleUp(extent.width) : extent.width), - .y = static_cast(is_2d && up_scaling ? resolution.ScaleUp(extent.height) - : extent.height), + .x = static_cast(up_scaling ? scaled_width : extent.width), + .y = static_cast(up_scaling ? scaled_height : extent.height), }; boost::container::small_vector regions; regions.reserve(resources.levels); @@ -995,13 +1018,24 @@ VkBuffer TextureCacheRuntime::GetTemporaryBuffer(size_t needed_size) { } VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, VkFormat format, - VkExtent2D extent, u32 layers) { + VkExtent2D extent, u32 layers, + VkImageAspectFlags aspect_mask) { ResolveShadow& shadow = resolve_shadows[msaa_image]; if (shadow.image && shadow.format == format && shadow.extent.width == extent.width && - shadow.extent.height == extent.height && shadow.layers == layers) { - shadow.up_to_date = true; + shadow.extent.height == extent.height && shadow.layers == layers && + shadow.aspect_mask == aspect_mask) { return *shadow.view; } + VkImageUsageFlags shadow_usage = + VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) { + shadow_usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + } else { + shadow_usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + } + if (shadow.image) { + pending_resolve_shadows.emplace_back(scheduler.CurrentTick(), std::move(shadow)); + } shadow.image = memory_allocator.CreateImage(VkImageCreateInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .pNext = nullptr, @@ -1013,8 +1047,7 @@ VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, Vk .arrayLayers = layers, .samples = VK_SAMPLE_COUNT_1_BIT, .tiling = VK_IMAGE_TILING_OPTIMAL, - .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | - VK_IMAGE_USAGE_TRANSFER_SRC_BIT, + .usage = shadow_usage, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, @@ -1029,7 +1062,7 @@ VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, Vk .format = format, .components{}, .subresourceRange{ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .aspectMask = aspect_mask, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, @@ -1039,7 +1072,8 @@ VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, Vk shadow.format = format; shadow.extent = extent; shadow.layers = layers; - shadow.up_to_date = true; + shadow.aspect_mask = aspect_mask; + shadow.up_to_date = false; return *shadow.view; } @@ -1052,6 +1086,13 @@ const TextureCacheRuntime::ResolveShadow* TextureCacheRuntime::GetValidResolveSh return &it->second; } +void TextureCacheRuntime::MarkResolveShadowUpToDate(VkImage msaa_image) { + const auto it = resolve_shadows.find(msaa_image); + if (it != resolve_shadows.end()) { + it->second.up_to_date = true; + } +} + void TextureCacheRuntime::InvalidateResolveShadow(VkImage msaa_image) { const auto it = resolve_shadows.find(msaa_image); if (it != resolve_shadows.end()) { @@ -1060,7 +1101,14 @@ void TextureCacheRuntime::InvalidateResolveShadow(VkImage msaa_image) { } void TextureCacheRuntime::EraseResolveShadow(VkImage msaa_image) { - resolve_shadows.erase(msaa_image); + const auto it = resolve_shadows.find(msaa_image); + if (it == resolve_shadows.end()) { + return; + } + if (it->second.image) { + pending_resolve_shadows.emplace_back(scheduler.CurrentTick(), std::move(it->second)); + } + resolve_shadows.erase(it); } void TextureCacheRuntime::BarrierFeedbackLoop() { @@ -1257,8 +1305,14 @@ void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst blit_image_helper.BlitColorMSAA(dst_framebuffer, src, dst_region, src_region); return; } - if (is_msaa_to_msaa && device.CantBlitMSAA()) { - UNIMPLEMENTED_MSG("MSAA to MSAA depth-stencil blit is not supported on this driver"); + if (is_msaa_to_msaa) { + blit_image_helper.BlitDepthStencilMSAA(dst_framebuffer, src, dst_region, src_region); + return; + } + + const bool is_resolve = is_src_msaa && !is_dst_msaa; + if (is_resolve && !HaveSameExtent(dst_region, src_region)) { + blit_image_helper.BlitColorMSAA(dst_framebuffer, src, dst_region, src_region); return; } @@ -1266,7 +1320,6 @@ void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst const VkImage src_image = src.ImageHandle(); const VkImageSubresourceLayers dst_layers = MakeSubresourceLayers(&dst); const VkImageSubresourceLayers src_layers = MakeSubresourceLayers(&src); - const bool is_resolve = is_src_msaa && !is_dst_msaa; scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([filter, dst_region, src_region, dst_image, src_image, dst_layers, src_layers, aspect_mask, is_resolve](vk::CommandBuffer cmdbuf) { @@ -1357,133 +1410,83 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im } switch (dst_view.format) { - case PixelFormat::D24_UNORM_S8_UINT: - if (src_view.format == PixelFormat::A8B8G8R8_UNORM - || src_view.format == PixelFormat::B8G8R8A8_UNORM - || src_view.format == PixelFormat::A8B8G8R8_SRGB - || src_view.format == PixelFormat::B8G8R8A8_SRGB) { - return blit_image_helper.ConvertABGR8ToD24S8(dst, src_view); + case PixelFormat::R16_UNORM: + if (src_view.format == PixelFormat::D16_UNORM) { + return blit_image_helper.ConvertD16ToR16(dst, src_view); + } + break; + case PixelFormat::A8B8G8R8_SRGB: + case PixelFormat::B8G8R8A8_SRGB: + case PixelFormat::B8G8R8A8_UNORM: + if (src_view.format == PixelFormat::D32_FLOAT) { + return blit_image_helper.ConvertD32FToABGR8(dst, src_view); } break; case PixelFormat::A8B8G8R8_UNORM: - case PixelFormat::A8B8G8R8_SNORM: - case PixelFormat::A8B8G8R8_SINT: - case PixelFormat::A8B8G8R8_UINT: - case PixelFormat::R5G6B5_UNORM: - case PixelFormat::B5G6R5_UNORM: - case PixelFormat::A1R5G5B5_UNORM: - case PixelFormat::A2B10G10R10_UNORM: - case PixelFormat::A2B10G10R10_UINT: - case PixelFormat::A2R10G10B10_UNORM: - case PixelFormat::A1B5G5R5_UNORM: - case PixelFormat::A5B5G5R1_UNORM: - case PixelFormat::R8_UNORM: - case PixelFormat::R8_SNORM: - case PixelFormat::R8_SINT: - case PixelFormat::R8_UINT: - case PixelFormat::R16G16B16A16_FLOAT: - case PixelFormat::R16G16B16A16_UNORM: - case PixelFormat::R16G16B16A16_SNORM: - case PixelFormat::R16G16B16A16_SINT: - case PixelFormat::R16G16B16A16_UINT: - case PixelFormat::B10G11R11_FLOAT: - case PixelFormat::R32G32B32A32_UINT: - case PixelFormat::BC1_RGBA_UNORM: - case PixelFormat::BC2_UNORM: - case PixelFormat::BC3_UNORM: - case PixelFormat::BC4_UNORM: - case PixelFormat::BC4_SNORM: - case PixelFormat::BC5_UNORM: - case PixelFormat::BC5_SNORM: - case PixelFormat::BC7_UNORM: - case PixelFormat::BC6H_UFLOAT: - case PixelFormat::BC6H_SFLOAT: - case PixelFormat::ASTC_2D_4X4_UNORM: - case PixelFormat::B8G8R8A8_UNORM: - case PixelFormat::R32G32B32A32_FLOAT: - case PixelFormat::R32G32B32A32_SINT: - case PixelFormat::R32G32_FLOAT: - case PixelFormat::R32G32_SINT: - case PixelFormat::R32_FLOAT: - if (src_view.format == PixelFormat::D32_FLOAT && - (dst_view.format == PixelFormat::B5G6R5_UNORM || - Settings::values.fix_bloom_effects.GetValue())) { - const Region2D region{ - .start = {0, 0}, - .end = {static_cast(dst->RenderArea().width), - static_cast(dst->RenderArea().height)}, - }; - return blit_image_helper.BlitColor(dst, src_view, region, region, - Tegra::Engines::Fermi2D::Filter::Point, - Tegra::Engines::Fermi2D::Operation::SrcCopy); + if (src_view.format == PixelFormat::S8_UINT_D24_UNORM) { + return blit_image_helper.ConvertD24S8ToABGR8(dst, src_view); + } + if (src_view.format == PixelFormat::D24_UNORM_S8_UINT) { + return blit_image_helper.ConvertS8D24ToABGR8(dst, src_view); + } + if (src_view.format == PixelFormat::D32_FLOAT) { + return blit_image_helper.ConvertD32FToABGR8(dst, src_view); + } + break; + case PixelFormat::R32_FLOAT: + if (src_view.format == PixelFormat::D32_FLOAT) { + return blit_image_helper.ConvertD32ToR32(dst, src_view); } break; - case PixelFormat::R16_FLOAT: - case PixelFormat::R16_UNORM: - case PixelFormat::R16_SNORM: - case PixelFormat::R16_UINT: - case PixelFormat::R16_SINT: - case PixelFormat::R16G16_UNORM: - case PixelFormat::R16G16_FLOAT: - case PixelFormat::R16G16_UINT: - case PixelFormat::R16G16_SINT: - case PixelFormat::R16G16_SNORM: - case PixelFormat::R32G32B32_FLOAT: - case PixelFormat::A8B8G8R8_SRGB: - case PixelFormat::R8G8_UNORM: - case PixelFormat::R8G8_SNORM: - case PixelFormat::R8G8_SINT: - case PixelFormat::R8G8_UINT: - case PixelFormat::R32G32_UINT: - case PixelFormat::R16G16B16X16_FLOAT: - case PixelFormat::R32_UINT: - case PixelFormat::R32_SINT: - case PixelFormat::ASTC_2D_8X8_UNORM: - case PixelFormat::ASTC_2D_8X5_UNORM: - case PixelFormat::ASTC_2D_5X4_UNORM: - case PixelFormat::B8G8R8A8_SRGB: - case PixelFormat::BC1_RGBA_SRGB: - case PixelFormat::BC2_SRGB: - case PixelFormat::BC3_SRGB: - case PixelFormat::BC7_SRGB: - case PixelFormat::A4B4G4R4_UNORM: - case PixelFormat::G4R4_UNORM: - case PixelFormat::ASTC_2D_4X4_SRGB: - case PixelFormat::ASTC_2D_8X8_SRGB: - case PixelFormat::ASTC_2D_8X5_SRGB: - case PixelFormat::ASTC_2D_5X4_SRGB: - case PixelFormat::ASTC_2D_5X5_UNORM: - case PixelFormat::ASTC_2D_5X5_SRGB: - case PixelFormat::ASTC_2D_10X8_UNORM: - case PixelFormat::ASTC_2D_10X8_SRGB: - case PixelFormat::ASTC_2D_6X6_UNORM: - case PixelFormat::ASTC_2D_6X6_SRGB: - case PixelFormat::ASTC_2D_10X6_UNORM: - case PixelFormat::ASTC_2D_10X6_SRGB: - case PixelFormat::ASTC_2D_10X5_UNORM: - case PixelFormat::ASTC_2D_10X5_SRGB: - case PixelFormat::ASTC_2D_10X10_UNORM: - case PixelFormat::ASTC_2D_10X10_SRGB: - case PixelFormat::ASTC_2D_12X10_UNORM: - case PixelFormat::ASTC_2D_12X10_SRGB: - case PixelFormat::ASTC_2D_12X12_UNORM: - case PixelFormat::ASTC_2D_12X12_SRGB: - case PixelFormat::ASTC_2D_8X6_UNORM: - case PixelFormat::ASTC_2D_8X6_SRGB: - case PixelFormat::ASTC_2D_6X5_UNORM: - case PixelFormat::ASTC_2D_6X5_SRGB: - case PixelFormat::E5B9G9R9_FLOAT: - case PixelFormat::D32_FLOAT: case PixelFormat::D16_UNORM: - case PixelFormat::X8_D24_UNORM: - case PixelFormat::S8_UINT: + if (src_view.format == PixelFormat::R16_UNORM) { + return blit_image_helper.ConvertR16ToD16(dst, src_view); + } + break; case PixelFormat::S8_UINT_D24_UNORM: - case PixelFormat::D32_FLOAT_S8_UINT: - case PixelFormat::Invalid: + if (src_view.format == PixelFormat::A8B8G8R8_UNORM || + src_view.format == PixelFormat::B8G8R8A8_UNORM) { + return blit_image_helper.ConvertABGR8ToD24S8(dst, src_view); + } + break; + case PixelFormat::D32_FLOAT: + if (src_view.format == PixelFormat::A8B8G8R8_UNORM || + src_view.format == PixelFormat::B8G8R8A8_UNORM || + src_view.format == PixelFormat::A8B8G8R8_SRGB || + src_view.format == PixelFormat::B8G8R8A8_SRGB) { + return blit_image_helper.ConvertABGR8ToD32F(dst, src_view); + } + if (src_view.format == PixelFormat::R32_FLOAT) { + return blit_image_helper.ConvertR32ToD32(dst, src_view); + } + break; + case PixelFormat::D24_UNORM_S8_UINT: + if (src_view.format == PixelFormat::A8B8G8R8_UNORM || + src_view.format == PixelFormat::B8G8R8A8_UNORM || + src_view.format == PixelFormat::A8B8G8R8_SRGB || + src_view.format == PixelFormat::B8G8R8A8_SRGB) { + return blit_image_helper.ConvertABGR8ToD24S8(dst, src_view); + } + break; default: - LOG_DEBUG(Render_Vulkan, "Unimplemented texture conversion from {} to {} format type", src_view.format, dst_view.format); break; } + + if (src_view.format == PixelFormat::D32_FLOAT && + VideoCore::Surface::GetFormatType(dst_view.format) == SurfaceType::ColorTexture && + (dst_view.format == PixelFormat::B5G6R5_UNORM || + Settings::values.fix_bloom_effects.GetValue())) { + const Region2D region{ + .start = {0, 0}, + .end = {static_cast(dst->RenderArea().width), + static_cast(dst->RenderArea().height)}, + }; + return blit_image_helper.BlitColor(dst, src_view, region, region, + Tegra::Engines::Fermi2D::Filter::Point, + Tegra::Engines::Fermi2D::Operation::SrcCopy); + } + + LOG_DEBUG(Render_Vulkan, "Unimplemented texture conversion from {} to {} format type", src_view.format, dst_view.format); } VkFormat TextureCacheRuntime::GetSupportedFormat(VkFormat requested_format, @@ -1640,31 +1643,41 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src, std::span copies) { const bool msaa_to_non_msaa = src.info.num_samples > 1 && dst.info.num_samples == 1; const u32 num_samples = msaa_to_non_msaa ? src.info.num_samples : dst.info.num_samples; - if (dst.AspectMask() != VK_IMAGE_ASPECT_COLOR_BIT || - VideoCore::Surface::IsPixelFormatInteger(dst.info.format)) { - UNIMPLEMENTED_MSG("Copying images with different samples is not supported."); - return; - } if (ENABLE_MSAA_RESOLVE_CONSUME && msaa_to_non_msaa && copies.size() == 1 && src.info.format == dst.info.format) { const VideoCommon::ImageCopy& copy = copies.front(); const ResolveShadow* const shadow = GetValidResolveShadow(src.Handle()); - if (shadow != nullptr && copy.src_offset.x == 0 && copy.src_offset.y == 0 && + if (shadow != nullptr && shadow->aspect_mask == dst.AspectMask() && + copy.src_offset.x == 0 && copy.src_offset.y == 0 && copy.src_subresource.base_level == 0 && static_cast(copy.extent.width) <= shadow->extent.width && - static_cast(copy.extent.height) <= shadow->extent.height) { + static_cast(copy.extent.height) <= shadow->extent.height && + static_cast(copy.src_subresource.base_layer + copy.src_subresource.num_layers) <= + shadow->layers) { + const VkImageAspectFlags aspect_mask = shadow->aspect_mask; + VkPipelineStageFlags attachment_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + VkAccessFlags attachment_write = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + VkAccessFlags attachment_read_write = + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) { + attachment_stage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + attachment_write = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + attachment_read_write = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + } const VkImage shadow_image = *shadow->image; const VkImage dst_image = dst.Handle(); const VkImageCopy region{ .srcSubresource{ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .aspectMask = aspect_mask, .mipLevel = 0, .baseArrayLayer = static_cast(copy.src_subresource.base_layer), .layerCount = static_cast(copy.src_subresource.num_layers), }, .srcOffset = {0, 0, 0}, .dstSubresource{ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .aspectMask = aspect_mask, .mipLevel = static_cast(copy.dst_subresource.base_level), .baseArrayLayer = static_cast(copy.dst_subresource.base_layer), .layerCount = static_cast(copy.dst_subresource.num_layers), @@ -1673,26 +1686,27 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src, .extent = {copy.extent.width, copy.extent.height, 1}, }; scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([shadow_image, dst_image, region](vk::CommandBuffer cmdbuf) { + scheduler.Record([shadow_image, dst_image, region, aspect_mask, attachment_stage, + attachment_write, + attachment_read_write](vk::CommandBuffer cmdbuf) { const std::array pre_barriers{ VkImageMemoryBarrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .srcAccessMask = attachment_write, .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = shadow_image, - .subresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0, + .subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS}, }, VkImageMemoryBarrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT | - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT | attachment_write | VK_ACCESS_TRANSFER_WRITE_BIT, .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, @@ -1700,7 +1714,7 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = dst_image, - .subresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0, + .subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS}, }, }; @@ -1715,28 +1729,25 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = shadow_image, - .subresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0, + .subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS}, }, VkImageMemoryBarrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | attachment_read_write | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = dst_image, - .subresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0, + .subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS}, }, }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | - VK_PIPELINE_STAGE_TRANSFER_BIT, + cmdbuf.PipelineBarrier(attachment_stage | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, nullptr, nullptr, pre_barriers); cmdbuf.CopyImage(shadow_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_image, @@ -1748,6 +1759,19 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src, return; } } + const VkImageAspectFlags dst_aspect_mask = dst.AspectMask(); + if ((dst_aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0) { + const bool copies_stencil = (dst_aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0 && + device.IsExtShaderStencilExportSupported(); + blit_image_helper.CopyMSAADepth(render_pass_cache, dst.Handle(), dst.info.format, + src.Handle(), src.info.format, num_samples, copies, + copies_stencil, msaa_to_non_msaa); + return; + } + if ((dst_aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) == 0) { + UNIMPLEMENTED_MSG("Copying images with different samples is not supported."); + return; + } blit_image_helper.CopyMSAA(render_pass_cache, dst.Handle(), dst.info.format, src.Handle(), src.info.format, num_samples, copies, msaa_to_non_msaa); } @@ -1760,6 +1784,20 @@ u64 TextureCacheRuntime::GetDeviceMemoryUsage() const { return device.GetDeviceMemoryUsage(); } +bool TextureCacheRuntime::CanDownloadMsaa(const VideoCommon::ImageInfo& info) const { + const VkImageAspectFlags aspect_mask = ImageAspectMask(info.format); + if ((aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) != 0) { + return true; + } + if ((aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) == 0) { + return false; + } + if ((aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0) { + return device.IsExtShaderStencilExportSupported(); + } + return true; +} + bool TextureCacheRuntime::CanReportMemoryUsage() const { return device.CanReportMemoryUsage(); } @@ -1768,8 +1806,60 @@ std::optional TextureCacheRuntime::GetSamplerHeapBudget() const { return device.GetSamplerHeapBudget(); } +void TextureCacheRuntime::FlushDeferredClear() { + scheduler.FlushDeferredClear(); +} + +VkImage TextureCacheRuntime::AcquireMsaaScratchImage(const VkImageCreateInfo& image_ci) { + const MsaaScratchKey key{ + .format = image_ci.format, + .type = image_ci.imageType, + .width = image_ci.extent.width, + .height = image_ci.extent.height, + .depth = image_ci.extent.depth, + .levels = image_ci.mipLevels, + .layers = image_ci.arrayLayers, + .usage = image_ci.usage, + .flags = image_ci.flags, + }; + for (MsaaScratchImage& scratch : msaa_scratch_images) { + if (scratch.key != key || !scheduler.IsFree(scratch.tick)) { + continue; + } + scratch.tick = (std::numeric_limits::max)(); + scratch.unused_frames = 0; + return *scratch.image; + } + MsaaScratchImage scratch{ + .key = key, + .image = memory_allocator.CreateImage(image_ci), + .tick = (std::numeric_limits::max)(), + .unused_frames = 0, + }; + const VkImage handle = *scratch.image; + msaa_scratch_images.push_back(std::move(scratch)); + return handle; +} + +void TextureCacheRuntime::ReleaseMsaaScratchImage(VkImage image) { + for (MsaaScratchImage& scratch : msaa_scratch_images) { + if (*scratch.image == image) { + scratch.tick = scheduler.CurrentTick(); + return; + } + } +} + void TextureCacheRuntime::TickFrame() { - std::erase_if(pending_msaa_images, [this](const auto& pending) { + static constexpr u32 MAX_UNUSED_SCRATCH_FRAMES = 60; + std::erase_if(msaa_scratch_images, [this](MsaaScratchImage& scratch) { + if (!scheduler.IsFree(scratch.tick)) { + scratch.unused_frames = 0; + return false; + } + return ++scratch.unused_frames > MAX_UNUSED_SCRATCH_FRAMES; + }); + std::erase_if(pending_resolve_shadows, [this](const auto& pending) { return scheduler.IsFree(pending.first); }); } @@ -1882,26 +1972,29 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset, ScaleDown(true); } + const bool msaa_upload_is_depth = (aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0; const bool wants_msaa_upload = info.num_samples > 1 - && (aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) != 0 - && !VideoCore::Surface::IsPixelFormatInteger(info.format); + && ((aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) != 0 || msaa_upload_is_depth); if (wants_msaa_upload) { - ImageInfo temp_info = info; - temp_info.num_samples = 1; - - VkImageCreateInfo image_ci = MakeImageCreateInfo(runtime->device, temp_info); - image_ci.format = - MaxwellToVK::SurfaceFormat(runtime->device, FormatType::Optimal, true, info.format) - .format; - image_ci.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; - vk::Image temp_image = runtime->memory_allocator.CreateImage(image_ci); + const bool msaa_upload_copies_stencil = + msaa_upload_is_depth && (aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0 && + runtime->device.IsExtShaderStencilExportSupported(); + VkImageAspectFlags upload_aspect_mask = aspect_mask; + if (msaa_upload_is_depth) { + upload_aspect_mask = VK_IMAGE_ASPECT_DEPTH_BIT; + if (msaa_upload_copies_stencil) { + upload_aspect_mask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + } + const VkImageCreateInfo image_ci = MakeMsaaScratchImageCreateInfo( + runtime->device, info, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); + const VkImage temp_vk_image = runtime->AcquireMsaaScratchImage(image_ci); scheduler->RequestOutsideRenderPassOperationContext(); - auto vk_copies = TransformBufferImageCopies(copies, offset, aspect_mask); + auto vk_copies = TransformBufferImageCopies(copies, offset, upload_aspect_mask); const VkBuffer src_buffer = buffer; - const VkImage temp_vk_image = *temp_image; - const VkImageAspectFlags vk_aspect_mask = aspect_mask; + const VkImageAspectFlags vk_aspect_mask = upload_aspect_mask; scheduler->Record([src_buffer, temp_vk_image, vk_aspect_mask, vk_copies](vk::CommandBuffer cmdbuf) { @@ -1909,7 +2002,7 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset, }); const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(info.num_samples); - std::vector image_copies; + boost::container::small_vector image_copies; image_copies.reserve(copies.size()); for (const auto& copy : copies) { VideoCommon::ImageCopy image_copy{}; @@ -1923,11 +2016,19 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset, image_copies.push_back(image_copy); } - runtime->blit_image_helper.CopyMSAA(runtime->render_pass_cache, Handle(), info.format, - temp_vk_image, info.format, info.num_samples, - image_copies, false); + if (msaa_upload_is_depth) { + runtime->blit_image_helper.CopyMSAADepth(runtime->render_pass_cache, Handle(), + info.format, temp_vk_image, info.format, + info.num_samples, + {image_copies.data(), image_copies.size()}, + msaa_upload_copies_stencil, false); + } else { + runtime->blit_image_helper.CopyMSAA(runtime->render_pass_cache, Handle(), info.format, + temp_vk_image, info.format, info.num_samples, + {image_copies.data(), image_copies.size()}, false); + } initialized = true; - runtime->pending_msaa_images.emplace_back(scheduler->CurrentTick(), std::move(temp_image)); + runtime->ReleaseMsaaScratchImage(temp_vk_image); if (is_rescaled) { ScaleUp(); @@ -1936,7 +2037,6 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset, } if (info.num_samples > 1) { - LOG_WARNING(Render_Vulkan, "MSAA upload not implemented for format {}", info.format); if (is_rescaled) { ScaleUp(); } @@ -1983,46 +2083,60 @@ void Image::DownloadMemory(std::span buffers_span, std::span o } if (info.num_samples > 1) { - if (aspect_mask == VK_IMAGE_ASPECT_COLOR_BIT && - !VideoCore::Surface::IsPixelFormatInteger(info.format)) { - ImageInfo temp_info = info; - temp_info.num_samples = 1; + if (runtime->CanDownloadMsaa(info)) { + const bool msaa_download_is_depth = + (aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0; + const bool msaa_download_copies_stencil = + msaa_download_is_depth && (aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0 && + runtime->device.IsExtShaderStencilExportSupported(); + VkImageUsageFlags scratch_usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + if (msaa_download_is_depth) { + scratch_usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + } else { + scratch_usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + } + const VkImageCreateInfo image_ci = + MakeMsaaScratchImageCreateInfo(runtime->device, info, scratch_usage); + const VkImage temp_vk_image = runtime->AcquireMsaaScratchImage(image_ci); - VkImageCreateInfo image_ci = MakeImageCreateInfo(runtime->device, temp_info); - image_ci.format = - MaxwellToVK::SurfaceFormat(runtime->device, FormatType::Optimal, true, info.format) - .format; - image_ci.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - vk::Image temp_image = runtime->memory_allocator.CreateImage(image_ci); - const VkImage temp_vk_image = *temp_image; + const VkImageAspectFlags temp_aspect_mask = aspect_mask; + const VkAccessFlags attachment_access = + msaa_download_is_depth ? (VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT) + : (VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); + const VkPipelineStageFlags attachment_stage = + msaa_download_is_depth ? (VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT) + : VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; scheduler->RequestOutsideRenderPassOperationContext(); - scheduler->Record([temp_vk_image](vk::CommandBuffer cmdbuf) { + scheduler->Record([temp_vk_image, temp_aspect_mask, attachment_access, + attachment_stage](vk::CommandBuffer cmdbuf) { const VkImageMemoryBarrier init_barrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .dstAccessMask = attachment_access, .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = temp_vk_image, .subresourceRange{ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .aspectMask = temp_aspect_mask, .baseMipLevel = 0, .levelCount = VK_REMAINING_MIP_LEVELS, .baseArrayLayer = 0, .layerCount = VK_REMAINING_ARRAY_LAYERS, }, }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, attachment_stage, 0, init_barrier); }); - std::vector image_copies; + boost::container::small_vector image_copies; + image_copies.reserve(copies.size()); for (const auto& copy : copies) { VideoCommon::ImageCopy image_copy; image_copy.src_offset = copy.image_offset; @@ -2033,9 +2147,18 @@ void Image::DownloadMemory(std::span buffers_span, std::span o image_copies.push_back(image_copy); } - runtime->blit_image_helper.CopyMSAA(runtime->render_pass_cache, temp_vk_image, - info.format, Handle(), info.format, - info.num_samples, image_copies, true); + if (msaa_download_is_depth) { + runtime->blit_image_helper.CopyMSAADepth( + runtime->render_pass_cache, temp_vk_image, info.format, Handle(), info.format, + info.num_samples, {image_copies.data(), image_copies.size()}, + msaa_download_copies_stencil, true); + } else { + runtime->blit_image_helper.CopyMSAA(runtime->render_pass_cache, temp_vk_image, + info.format, Handle(), info.format, + info.num_samples, + {image_copies.data(), image_copies.size()}, + true); + } boost::container::small_vector buffers_vector{}; boost::container::small_vector, 8> @@ -2102,10 +2225,12 @@ void Image::DownloadMemory(std::span buffers_span, std::span o cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, memory_write_barrier, nullptr, image_write_barrier); }); - runtime->pending_msaa_images.emplace_back(scheduler->CurrentTick(), - std::move(temp_image)); - return; + runtime->ReleaseMsaaScratchImage(temp_vk_image); } + if (is_rescaled) { + ScaleUp(true); + } + return; } else { boost::container::small_vector buffers_vector{}; boost::container::small_vector, 8> @@ -2236,7 +2361,10 @@ bool Image::ScaleUp(bool ignore) { aspect_mask = ImageAspectMask(info.format); } if (NeedsScaleHelper()) { - return BlitScaleHelper(true); + if (!BlitScaleHelper(true)) { + current_image = &Image::original_image; + return false; + } } else { BlitScale(*scheduler, *original_image, *scaled_image, info, aspect_mask, resolution); } @@ -2261,7 +2389,11 @@ bool Image::ScaleDown(bool ignore) { aspect_mask = ImageAspectMask(info.format); } if (NeedsScaleHelper()) { - return BlitScaleHelper(false); + if (!BlitScaleHelper(false)) { + current_image = &Image::scaled_image; + flags |= ImageFlagBits::Rescaled; + return false; + } } else { BlitScale(*scheduler, *scaled_image, *original_image, info, aspect_mask, resolution, false); } @@ -2316,12 +2448,22 @@ bool Image::BlitScaleHelper(bool scale_up) { runtime->blit_image_helper.BlitColor(&*blit_framebuffer, *blit_view, dst_region, src_region, operation, BLIT_OPERATION); } - } else if (aspect_mask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT) && - info.num_samples == 1) { + } else if ((aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0) { if (!blit_framebuffer) blit_framebuffer.emplace(*runtime, nullptr, view_ptr, extent, scale_up); - runtime->blit_image_helper.BlitDepthStencil(&*blit_framebuffer, *blit_view, - dst_region, src_region, operation, BLIT_OPERATION); + const bool has_stencil = (aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0; + const bool can_blit_stencil = + has_stencil && runtime->device.IsExtShaderStencilExportSupported(); + if (info.num_samples > 1) { + runtime->blit_image_helper.BlitDepthStencilMSAA(&*blit_framebuffer, *blit_view, + dst_region, src_region); + } else if (can_blit_stencil) { + runtime->blit_image_helper.BlitDepthStencil(&*blit_framebuffer, *blit_view, + dst_region, src_region, operation, BLIT_OPERATION); + } else { + runtime->blit_image_helper.BlitDepth(&*blit_framebuffer, *blit_view, dst_region, + src_region); + } } else { // TODO: Use helper blits where applicable flags &= ~ImageFlagBits::Rescaled; @@ -2333,9 +2475,7 @@ bool Image::BlitScaleHelper(bool scale_up) { bool Image::NeedsScaleHelper() const { const auto& device = runtime->device; - const bool needs_msaa_helper = info.num_samples > 1 && - (device.CantBlitMSAA() || aspect_mask == VK_IMAGE_ASPECT_COLOR_BIT); - if (needs_msaa_helper) { + if (info.num_samples > 1) { return true; } static constexpr auto OPTIMAL_FORMAT = FormatType::Optimal; @@ -2690,7 +2830,7 @@ Framebuffer::~Framebuffer() = default; void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, std::span color_buffers, ImageView* depth_buffer, bool is_rescaled_) { - boost::container::small_vector attachments; + boost::container::small_vector attachments; RenderPassKey renderpass_key{}; s32 num_layers = 1; @@ -2719,6 +2859,8 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, ++num_images; } const size_t num_colors = attachments.size(); + VkImage depth_image = VK_NULL_HANDLE; + VkImageAspectFlags depth_aspect_mask = 0; if (depth_buffer) { width = (std::min)(width, is_rescaled ? resolution.ScaleUp(depth_buffer->size.width) : depth_buffer->size.width); @@ -2734,20 +2876,31 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, ++num_images; has_depth = (subresource_range.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0; has_stencil = (subresource_range.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0; + depth_image = depth_buffer->ImageHandle(); + depth_aspect_mask = subresource_range.aspectMask; } else { renderpass_key.depth_format = PixelFormat::Invalid; } renderpass_key.samples = samples; - const bool do_resolve_color = + const bool do_resolve_color = ENABLE_MSAA_TILER_RESOLVE && samples != VK_SAMPLE_COUNT_1_BIT && num_colors > 0 && runtime.device.IsTiler(); renderpass_key.resolve_color = do_resolve_color; + const bool do_resolve_depth_stencil = ENABLE_MSAA_TILER_RESOLVE && + samples != VK_SAMPLE_COUNT_1_BIT && depth_image != VK_NULL_HANDLE && + runtime.device.IsTiler() && + SupportsDepthStencilResolve(runtime.device, renderpass_key.depth_format); + renderpass_key.resolve_depth_stencil = do_resolve_depth_stencil; + discard_msaa_color = ENABLE_MSAA_RESOLVE_CONSUME && ENABLE_MSAA_COLOR_DISCARD && do_resolve_color; + discard_msaa_depth_stencil = ENABLE_MSAA_RESOLVE_CONSUME && + ENABLE_MSAA_DEPTH_STENCIL_DISCARD && do_resolve_depth_stencil; renderpass = runtime.render_pass_cache.Get(renderpass_key); render_pass_key = renderpass_key; render_pass_cache = &runtime.render_pass_cache; + runtime_ptr = &runtime; render_area.width = (std::min)(render_area.width, width); render_area.height = (std::min)(render_area.height, height); @@ -2760,54 +2913,24 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, } const VkFormat vk_format = MaxwellToVK::SurfaceFormat(runtime.device, FormatType::Optimal, true, format).format; - if (ENABLE_MSAA_RESOLVE_CONSUME) { - const VkImage msaa_image = images[rt_map[index]]; - attachments.push_back(runtime.GetOrCreateResolveShadow(msaa_image, vk_format, - render_area, layers)); - continue; - } - VkImageCreateInfo resolve_ci{ - .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .imageType = VK_IMAGE_TYPE_2D, - .format = vk_format, - .extent = {render_area.width, render_area.height, 1}, - .mipLevels = 1, - .arrayLayers = layers, - .samples = VK_SAMPLE_COUNT_1_BIT, - .tiling = VK_IMAGE_TILING_OPTIMAL, - .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | - VK_IMAGE_USAGE_TRANSFER_SRC_BIT, - .sharingMode = VK_SHARING_MODE_EXCLUSIVE, - .queueFamilyIndexCount = 0, - .pQueueFamilyIndices = nullptr, - .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, - }; - vk::Image resolve_image = runtime.memory_allocator.CreateImage(resolve_ci); - vk::ImageView resolve_view = - runtime.device.GetLogical().CreateImageView(VkImageViewCreateInfo{ - .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .image = *resolve_image, - .viewType = layers > 1 ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D, - .format = vk_format, - .components{}, - .subresourceRange{ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = layers, - }, - }); - attachments.push_back(*resolve_view); - resolve_images.push_back(std::move(resolve_image)); - resolve_image_views.push_back(std::move(resolve_view)); + const VkImage msaa_image = images[rt_map[index]]; + attachments.push_back(runtime.GetOrCreateResolveShadow( + msaa_image, vk_format, render_area, layers, VK_IMAGE_ASPECT_COLOR_BIT)); + resolve_shadow_images[num_resolve_shadows++] = msaa_image; } } + if (do_resolve_depth_stencil) { + const u32 layers = static_cast((std::max)(num_layers, 1)); + const VkFormat vk_format = + MaxwellToVK::SurfaceFormat(runtime.device, FormatType::Optimal, true, + renderpass_key.depth_format) + .format; + attachments.push_back(runtime.GetOrCreateResolveShadow(depth_image, vk_format, render_area, + layers, depth_aspect_mask)); + resolve_shadow_images[num_resolve_shadows++] = depth_image; + } + num_color_buffers = static_cast(num_colors); framebuffer = runtime.device.GetLogical().CreateFramebuffer({ .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, @@ -2822,16 +2945,43 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, }); } +void Framebuffer::MarkResolveShadowsUpToDate() const { + if (runtime_ptr == nullptr) { + return; + } + for (u32 index = 0; index < num_resolve_shadows; ++index) { + runtime_ptr->MarkResolveShadowUpToDate(resolve_shadow_images[index]); + } +} + VkRenderPass Framebuffer::RenderPassVariant(u32 color_clear_mask, bool depth_stencil_clear, - u32 color_discard_mask) const { - if (color_clear_mask == 0 && !depth_stencil_clear && color_discard_mask == 0) { + u32 color_discard_mask, + bool depth_stencil_discard) const { + if (color_clear_mask == 0 && !depth_stencil_clear && color_discard_mask == 0 && + !depth_stencil_discard) { return renderpass; } + static_assert(NUM_RT <= 8); + const u32 variant_key = color_clear_mask | (color_discard_mask << 8) | + (static_cast(depth_stencil_clear) << 16) | + (static_cast(depth_stencil_discard) << 17); + for (u32 index = 0; index < num_memoized_variants; ++index) { + if (variant_keys[index] == variant_key) { + return variant_render_passes[index]; + } + } RenderPassKey key = render_pass_key; key.color_clear_mask = color_clear_mask; key.depth_stencil_clear = depth_stencil_clear; key.color_discard_mask = color_discard_mask; - return render_pass_cache->Get(key); + key.depth_stencil_discard = depth_stencil_discard; + const VkRenderPass variant = render_pass_cache->Get(key); + if (num_memoized_variants < variant_keys.size()) { + variant_keys[num_memoized_variants] = variant_key; + variant_render_passes[num_memoized_variants] = variant; + ++num_memoized_variants; + } + return variant; } void TextureCacheRuntime::AccelerateImageUpload( diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 4b4900e5d6..b2f32db1ff 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -60,6 +60,8 @@ public: void TickFrame(); + void FlushDeferredClear(); + u64 GetDeviceLocalMemory() const; u64 GetDeviceMemoryUsage() const; @@ -68,6 +70,12 @@ public: std::optional GetSamplerHeapBudget() const; + bool CanDownloadMsaa(const VideoCommon::ImageInfo& info) const; + + [[nodiscard]] VkImage AcquireMsaaScratchImage(const VkImageCreateInfo& image_ci); + + void ReleaseMsaaScratchImage(VkImage image); + void BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src, const Region2D& dst_region, const Region2D& src_region, Tegra::Engines::Fermi2D::Filter filter, @@ -117,16 +125,20 @@ public: VkFormat format = VK_FORMAT_UNDEFINED; VkExtent2D extent{}; u32 layers = 0; + VkImageAspectFlags aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT; bool up_to_date = false; }; [[nodiscard]] VkImageView GetOrCreateResolveShadow(VkImage msaa_image, VkFormat format, - VkExtent2D extent, u32 layers); + VkExtent2D extent, u32 layers, + VkImageAspectFlags aspect_mask); [[nodiscard]] const ResolveShadow* GetValidResolveShadow(VkImage msaa_image) const; void InvalidateResolveShadow(VkImage msaa_image); + void MarkResolveShadowUpToDate(VkImage msaa_image); + void EraseResolveShadow(VkImage msaa_image); std::span ViewFormats(PixelFormat format) { @@ -154,8 +166,30 @@ public: static constexpr size_t indexing_slots = 8 * sizeof(size_t); std::array buffers{}; - std::vector> pending_msaa_images; + struct MsaaScratchKey { + VkFormat format; + VkImageType type; + u32 width; + u32 height; + u32 depth; + u32 levels; + u32 layers; + VkImageUsageFlags usage; + VkImageCreateFlags flags; + + bool operator==(const MsaaScratchKey&) const noexcept = default; + }; + + struct MsaaScratchImage { + MsaaScratchKey key; + vk::Image image; + u64 tick; + u32 unused_frames; + }; + + std::vector msaa_scratch_images; ankerl::unordered_dense::map resolve_shadows; + std::vector> pending_resolve_shadows; }; class Framebuffer { @@ -191,7 +225,8 @@ public: } [[nodiscard]] VkRenderPass RenderPassVariant(u32 color_clear_mask, bool depth_stencil_clear, - u32 color_discard_mask) const; + u32 color_discard_mask, + bool depth_stencil_discard) const; [[nodiscard]] VkExtent2D RenderArea() const noexcept { return render_area; @@ -233,19 +268,21 @@ public: return is_rescaled; } - [[nodiscard]] bool HasResolveColor() const noexcept { - return !resolve_images.empty(); - } - - [[nodiscard]] VkImage ResolveColorImage(size_t index) const noexcept { - return index < resolve_images.size() ? *resolve_images[index] : VK_NULL_HANDLE; - } - [[nodiscard]] bool DiscardsMsaaColor() const noexcept { return discard_msaa_color; } + [[nodiscard]] bool DiscardsMsaaDepthStencil() const noexcept { + return discard_msaa_depth_stencil; + } + + /// Records that a render pass has begun, so its resolve attachments will hold valid contents + /// once it ends. + void MarkResolveShadowsUpToDate() const; + private: + static constexpr size_t NUM_MEMOIZED_RENDER_PASS_VARIANTS = 8; + vk::Framebuffer framebuffer; VkRenderPass renderpass{}; VkExtent2D render_area{}; @@ -258,11 +295,16 @@ private: bool has_depth{}; bool has_stencil{}; bool is_rescaled{}; - std::vector resolve_images; - std::vector resolve_image_views; + std::array resolve_shadow_images{}; + u32 num_resolve_shadows = 0; + TextureCacheRuntime* runtime_ptr{nullptr}; RenderPassKey render_pass_key{}; RenderPassCache* render_pass_cache{nullptr}; bool discard_msaa_color{}; + bool discard_msaa_depth_stencil{}; + mutable std::array variant_keys{}; + mutable std::array variant_render_passes{}; + mutable u32 num_memoized_variants{}; }; class Image : public VideoCommon::ImageBase { @@ -486,6 +528,7 @@ struct TextureCacheParams { static constexpr bool HAS_EMULATED_COPIES = false; static constexpr bool HAS_DEVICE_MEMORY_INFO = true; static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true; + static constexpr bool HAS_MSAA_DOWNLOADS = true; using Runtime = Vulkan::TextureCacheRuntime; using Image = Vulkan::Image; diff --git a/src/video_core/texture_cache/image_base.cpp b/src/video_core/texture_cache/image_base.cpp index 077df28fb3..9b0f33220d 100644 --- a/src/video_core/texture_cache/image_base.cpp +++ b/src/video_core/texture_cache/image_base.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project @@ -121,7 +121,7 @@ void ImageBase::InsertView(const ImageViewInfo& view_info, ImageViewId image_vie image_view_ids.push_back(image_view_id); } -bool ImageBase::IsSafeDownload() const noexcept { +bool ImageBase::IsSafeGpuCopy() const noexcept { // Skip images that were not modified from the GPU if (False(flags & ImageFlagBits::GpuModified)) { return false; @@ -131,10 +131,6 @@ bool ImageBase::IsSafeDownload() const noexcept { if (True(flags & ImageFlagBits::CpuModified)) { return false; } - if (info.num_samples > 1) { - LOG_WARNING(HW_GPU, "MSAA image downloads are not implemented"); - return false; - } return true; } diff --git a/src/video_core/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h index 0587d7b724..79fc8505d6 100644 --- a/src/video_core/texture_cache/image_base.h +++ b/src/video_core/texture_cache/image_base.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -64,7 +67,7 @@ struct ImageBase { void InsertView(const ImageViewInfo& view_info, ImageViewId image_view_id); - [[nodiscard]] bool IsSafeDownload() const noexcept; + [[nodiscard]] bool IsSafeGpuCopy() const noexcept; [[nodiscard]] bool Overlaps(VAddr overlap_cpu_addr, size_t overlap_size) const noexcept { const VAddr overlap_end = overlap_cpu_addr + overlap_size; diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 98a5e053a7..bcc40afde0 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -134,7 +134,7 @@ void TextureCache

::RunGarbageCollector() { if (True(image.flags & ImageFlagBits::IsDecoding)) { return false; } - const bool must_download = image.IsSafeDownload() && False(image.flags & ImageFlagBits::BadOverlap); + const bool must_download = IsDownloadable(image) && False(image.flags & ImageFlagBits::BadOverlap); if ((!aggressive_mode && True(image.flags & ImageFlagBits::CostlyLoad)) || (!high_priority_mode && must_download)) { return false; } @@ -577,6 +577,7 @@ FramebufferId TextureCache

::GetFramebufferId(const RenderTargets& key) { return id ? &slot_image_views[id] : nullptr; }); ImageView* const depth_buffer = key.depth_buffer_id ? &slot_image_views[key.depth_buffer_id] : nullptr; + runtime.FlushDeferredClear(); framebuffer_id = slot_framebuffers.insert(runtime, color_buffers, depth_buffer, key); return framebuffer_id; } @@ -594,11 +595,26 @@ void TextureCache

::WriteMemory(DAddr cpu_addr, size_t size) { }); } +template +bool TextureCache

::IsDownloadable(const ImageBase& image) const noexcept { + if (!image.IsSafeGpuCopy()) { + return false; + } + if (image.info.num_samples == 1) { + return true; + } + if constexpr (P::HAS_MSAA_DOWNLOADS) { + return runtime.CanDownloadMsaa(image.info); + } else { + return false; + } +} + template void TextureCache

::DownloadMemory(DAddr cpu_addr, size_t size) { boost::container::small_vector images; - ForEachImageInRegion(cpu_addr, size, [&images](ImageId image_id, ImageBase& image) { - if (!image.IsSafeDownload()) { + ForEachImageInRegion(cpu_addr, size, [this, &images](ImageId image_id, ImageBase& image) { + if (!IsDownloadable(image)) { return; } image.flags &= ~ImageFlagBits::GpuModified; @@ -1476,12 +1492,12 @@ template bool TextureCache

::ScaleUp(Image& image) { const bool has_copy = image.HasScaled(); const bool rescaled = image.ScaleUp(); + if (!has_copy && image.HasScaled()) { + total_used_memory += GetScaledImageSizeBytes(image); + } if (!rescaled) { return false; } - if (!has_copy) { - total_used_memory += GetScaledImageSizeBytes(image); - } InvalidateScale(image); return true; } @@ -1691,7 +1707,10 @@ ImageId TextureCache

::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, DA for (const auto& copy_object : join_copies_to_do) { Image& overlap = slot_images[copy_object.id]; if (copy_object.is_alias) { - if (!overlap.IsSafeDownload()) { + if (!overlap.IsSafeGpuCopy()) { + continue; + } + if (overlap.info.num_samples != new_image.info.num_samples) { continue; } const auto alias_pointer = join_alias_indices.find(copy_object.id); @@ -2461,6 +2480,7 @@ void TextureCache

::RemoveImageViewReferences(std::span rem template void TextureCache

::RemoveFramebuffers(std::span removed_views) { + runtime.FlushDeferredClear(); auto it = framebuffers.begin(); while (it != framebuffers.end()) { if (it->first.Contains(removed_views)) { diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index fafc331ef7..77947e83c7 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -303,6 +303,8 @@ private: FramebufferId GetFramebufferId(const RenderTargets& key); /// Refresh the contents (pixel data) of an image + [[nodiscard]] bool IsDownloadable(const ImageBase& image) const noexcept; + void RefreshContents(Image& image, ImageId image_id); /// Upload data from guest to an image diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 8c41a7eb67..fc67b96a85 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -650,13 +650,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR features.shader_float16_int8.shaderFloat16 = false; } - if (is_intel_windows) { - LOG_WARNING(Render_Vulkan, - "Intel proprietary drivers do not support MSAA->MSAA image blits. " - "MSAA scaling will use 3D helpers. MSAA resolves work normally."); - cant_blit_msaa = true; - } - has_broken_compute = CheckBrokenCompute(properties.driver.driverID, properties.properties.driverVersion) && !Settings::values.enable_compute_pipelines.GetValue(); @@ -975,6 +968,12 @@ bool Device::GetSuitability(bool requires_swapchain) { FOR_EACH_VK_FEATURE_EXT(FEATURE_EXTENSION); FOR_EACH_VK_EXTENSION(EXTENSION); + extensions.depth_stencil_resolve = + extensions.depth_stencil_resolve && + (instance_version >= VK_API_VERSION_1_2 || extensions.create_renderpass2); + RemoveExtensionIfUnsuitable(extensions.depth_stencil_resolve, + VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME); + if (supported_extensions.contains(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME)) { loaded_extensions.erase(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME); loaded_extensions.insert(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME); @@ -1122,6 +1121,11 @@ bool Device::GetSuitability(bool requires_swapchain) { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR; SetNext(next, properties.push_descriptor); } + if (extensions.depth_stencil_resolve || instance_version >= VK_API_VERSION_1_2) { + properties.depth_stencil_resolve.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES; + SetNext(next, properties.depth_stencil_resolve); + } if (extensions.descriptor_buffer) { properties.descriptor_buffer.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_PROPERTIES_EXT; diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 1ae073c279..a0aabea1ad 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -91,6 +91,8 @@ VK_DEFINE_HANDLE(VmaAllocator) EXTENSION(EXT, SHADER_VIEWPORT_INDEX_LAYER, shader_viewport_index_layer) \ EXTENSION(EXT, TOOLING_INFO, tooling_info) \ EXTENSION(EXT, VERTEX_ATTRIBUTE_DIVISOR, vertex_attribute_divisor) \ + EXTENSION(KHR, CREATE_RENDERPASS_2, create_renderpass2) \ + EXTENSION(KHR, DEPTH_STENCIL_RESOLVE, depth_stencil_resolve) \ EXTENSION(KHR, DRAW_INDIRECT_COUNT, draw_indirect_count) \ EXTENSION(KHR, DRIVER_PROPERTIES, driver_properties) \ EXTENSION(KHR, PUSH_DESCRIPTOR, push_descriptor) \ @@ -613,6 +615,32 @@ FN_MAX_LIMIT_LIST return extensions.shader_stencil_export; } + /// Returns true if the device supports VK_KHR_create_renderpass2. + bool IsKhrCreateRenderPass2Supported() const { + return extensions.create_renderpass2 || instance_version >= VK_API_VERSION_1_2; + } + + /// Returns true if the device supports VK_KHR_depth_stencil_resolve. + bool IsKhrDepthStencilResolveSupported() const { + return (extensions.depth_stencil_resolve || instance_version >= VK_API_VERSION_1_2) && + IsKhrCreateRenderPass2Supported(); + } + + /// Returns the supported resolve modes for the depth aspect. + VkResolveModeFlags GetDepthResolveModes() const { + return properties.depth_stencil_resolve.supportedDepthResolveModes; + } + + /// Returns the supported resolve modes for the stencil aspect. + VkResolveModeFlags GetStencilResolveModes() const { + return properties.depth_stencil_resolve.supportedStencilResolveModes; + } + + /// Returns true if only one of the depth and stencil aspects may be resolved. + bool SupportsIndependentResolveNone() const { + return properties.depth_stencil_resolve.independentResolveNone == VK_TRUE; + } + /// Returns true if depth/stencil operations can be performed efficiently. /// Either through shader export or hardware blits. bool CanPerformDepthStencilOperations() const { @@ -921,10 +949,6 @@ FN_MAX_LIMIT_LIST return supports_d24_depth; } - bool CantBlitMSAA() const { - return cant_blit_msaa; - } - bool MustEmulateScaledFormats() const { return must_emulate_scaled_formats; } @@ -1148,6 +1172,7 @@ private: VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control{}; VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{}; VkPhysicalDeviceMaintenance5PropertiesKHR maintenance5{}; + VkPhysicalDeviceDepthStencilResolveProperties depth_stencil_resolve{}; VkPhysicalDeviceProperties properties{}; }; @@ -1175,7 +1200,6 @@ private: bool has_nsight_graphics{}; ///< Has Nsight Graphics attached bool has_radeon_gpu_profiler{}; ///< Has Radeon GPU Profiler attached. bool supports_d24_depth{}; ///< Supports D24 depth buffers. - bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting. bool must_emulate_scaled_formats{}; ///< Requires scaled vertex format emulation bool dynamic_state3_blending{}; ///< Has blending features of dynamic_state3. bool dynamic_state3_enables{}; ///< Has at least one enable feature of dynamic_state3. diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index 24f9da0a9e..09fcf2eaa0 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -184,6 +184,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkCreatePipelineLayout); X(vkCreateQueryPool); X(vkCreateRenderPass); + X(vkCreateRenderPass2); X(vkCreateSampler); X(vkCreateSemaphore); X(vkCreateShaderModule); @@ -270,6 +271,10 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { if (!dld.vkQueueSubmit2) { Proc(dld.vkQueueSubmit2, dld, "vkQueueSubmit2KHR", device); } + + if (!dld.vkCreateRenderPass2) { + Proc(dld.vkCreateRenderPass2, dld, "vkCreateRenderPass2KHR", device); + } #undef X } @@ -725,6 +730,12 @@ RenderPass Device::CreateRenderPass(const VkRenderPassCreateInfo& ci) const { return RenderPass(object, handle, *dld); } +RenderPass Device::CreateRenderPass2(const VkRenderPassCreateInfo2& ci) const { + VkRenderPass object; + Check(dld->vkCreateRenderPass2(handle, &ci, nullptr, &object)); + return RenderPass(object, handle, *dld); +} + DescriptorSetLayout Device::CreateDescriptorSetLayout( const VkDescriptorSetLayoutCreateInfo& ci) const { VkDescriptorSetLayout object; diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index c2d867838d..dd9a3a21d5 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -300,6 +300,7 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkCreatePipelineLayout vkCreatePipelineLayout{}; PFN_vkCreateQueryPool vkCreateQueryPool{}; PFN_vkCreateRenderPass vkCreateRenderPass{}; + PFN_vkCreateRenderPass2 vkCreateRenderPass2{}; PFN_vkCreateSampler vkCreateSampler{}; PFN_vkCreateSemaphore vkCreateSemaphore{}; PFN_vkCreateShaderModule vkCreateShaderModule{}; @@ -1045,6 +1046,8 @@ public: [[nodiscard]] RenderPass CreateRenderPass(const VkRenderPassCreateInfo& ci) const; + [[nodiscard]] RenderPass CreateRenderPass2(const VkRenderPassCreateInfo2& ci) const; + [[nodiscard]] DescriptorSetLayout CreateDescriptorSetLayout( const VkDescriptorSetLayoutCreateInfo& ci) const;