aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/video_core/host_shaders/CMakeLists.txt43
-rw-r--r--src/video_core/host_shaders/blit_depth.frag12
-rw-r--r--src/video_core/host_shaders/blit_depth_msaa.frag2
-rw-r--r--src/video_core/host_shaders/blit_depth_stencil_msaa.frag4
-rw-r--r--src/video_core/host_shaders/convert_msaa_to_non_msaa.frag11
-rw-r--r--src/video_core/host_shaders/convert_msaa_to_non_msaa_depth.frag20
-rw-r--r--src/video_core/host_shaders/convert_msaa_to_non_msaa_depth_stencil.frag23
-rw-r--r--src/video_core/host_shaders/convert_non_msaa_to_msaa.frag11
-rw-r--r--src/video_core/host_shaders/convert_non_msaa_to_msaa_depth.frag19
-rw-r--r--src/video_core/host_shaders/convert_non_msaa_to_msaa_depth_stencil.frag22
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h3
-rw-r--r--src/video_core/renderer_vulkan/blit_image.cpp788
-rw-r--r--src/video_core/renderer_vulkan/blit_image.h71
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp28
-rw-r--r--src/video_core/renderer_vulkan/vk_render_pass_cache.cpp174
-rw-r--r--src/video_core/renderer_vulkan/vk_render_pass_cache.h35
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp13
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.h3
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp676
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h67
-rw-r--r--src/video_core/texture_cache/image_base.cpp8
-rw-r--r--src/video_core/texture_cache/image_base.h5
-rw-r--r--src/video_core/texture_cache/texture_cache.h34
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h2
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp18
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h34
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.cpp11
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.h3
28 files changed, 1573 insertions, 567 deletions
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<VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(MSAACopyPushConstants)>))),
+ msaa_copy_depth_stencil_pipeline_layout(
+ device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo(
+ two_textures_set_layout.address(),
+ PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(MSAACopyPushConstants)>))),
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<VK_FILTER_LINEAR>)),
nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) {}
@@ -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,24 +702,29 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView
});
}
-void BlitImageHelper::BlitColorMSAA(const Framebuffer* dst_framebuffer,
- const ImageView& src_image_view, const Region2D& dst_region,
- const Region2D& src_region) {
- const BlitMSAAPipelineKey key{
- .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);
+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](vk::CommandBuffer cmdbuf) {
- const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
- UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view);
+ 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);
@@ -697,41 +734,56 @@ void BlitImageHelper::BlitColorMSAA(const Framebuffer* dst_framebuffer,
scheduler.InvalidateState();
}
+void BlitImageHelper::BlitColorMSAA(const Framebuffer* dst_framebuffer,
+ const ImageView& src_image_view, const Region2D& dst_region,
+ const Region2D& src_region) {
+ const BlitMSAAPipelineKey key{
+ .renderpass = dst_framebuffer->RenderPass(),
+ .samples = dst_framebuffer->Samples(),
+ };
+ 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);
+}
+
+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,
ImageView& src_image_view, const Region2D& dst_region,
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<const VideoCommon::ImageCopy> 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<u32>(copy.src_subresource.base_level);
+ const u32 src_layer = static_cast<u32>(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<u32>(copy.dst_subresource.base_level),
+ static_cast<u32>(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<u32>(dst_offset.x) + dst_extent.width,
+ .height = static_cast<u32>(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<float>(render_area.offset.x),
+ .y = static_cast<float>(render_area.offset.y),
+ .width = static_cast<float>(render_area.extent.width),
+ .height = static_cast<float>(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<const VideoCommon::ImageCopy> 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<int>(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<u32>(copy.src_subresource.base_level));
- vk::ImageView dst_view =
- MakeMSAACopyView(device.GetLogical(), dst_image, dst_vk_format,
- static_cast<u32>(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<u32>(dst_offset.x) + dst_extent.width,
- .height = static_cast<u32>(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<float>(render_area.offset.x),
- .y = static_cast<float>(render_area.offset.y),
- .width = static_cast<float>(render_area.extent.width),
- .height = static_cast<float>(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<u32>(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<u32>(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<const VideoCommon::ImageCopy> copies,
+ bool copy_stencil, bool msaa_to_non_msaa) {
+ const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(static_cast<int>(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<u32>(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<const VideoCommon::ImageCopy> 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<const VideoCommon::ImageCopy> 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<const VideoCommon::ImageCopy> 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<vk::Pipeline> clear_stencil_pipelines;
std::vector<MSAACopyPipelineKey> msaa_copy_keys;
std::vector<vk::Pipeline> msaa_copy_pipelines;
+ std::vector<MSAACopyPipelineKey> msaa_copy_depth_keys;
+ std::vector<vk::Pipeline> msaa_copy_depth_pipelines;
+ std::vector<MSAACopyPipelineKey> msaa_copy_depth_stencil_keys;
+ std::vector<vk::Pipeline> msaa_copy_depth_stencil_pipelines;
std::vector<BlitMSAAPipelineKey> blit_msaa_color_keys;
std::vector<vk::Pipeline> blit_msaa_color_pipelines;
+ std::vector<VkRenderPass> blit_depth_keys;
+ std::vector<vk::Pipeline> blit_depth_pipelines;
+ std::vector<BlitMSAAPipelineKey> blit_msaa_depth_keys;
+ std::vector<vk::Pipeline> blit_msaa_depth_pipelines;
+ std::vector<BlitMSAAPipelineKey> blit_msaa_depth_stencil_keys;
+ std::vector<vk::Pipeline> blit_msaa_depth_stencil_pipelines;
std::vector<VkRenderPass> resolve_depth_keys;
std::vector<vk::Pipeline> resolve_depth_pipelines;
std::vector<VkRenderPass> 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<VkAttachmentDescription, 9> descriptions;
+ static constexpr size_t MAX_ATTACHMENTS =
+ 2 * std::tuple_size_v<decltype(RenderPassKey::color_formats)> + 2;
+ boost::container::static_vector<VkAttachmentDescription, MAX_ATTACHMENTS> descriptions;
std::array<VkAttachmentReference, 8> 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<VkAttachmentReference, 8> 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<u32>(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<VkAttachmentDescription2, MAX_ATTACHMENTS> 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<VkAttachmentReference2, 8> references2{};
+ std::array<VkAttachmentReference2, 8> 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<u32>(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 <mutex>
#include <ankerl/unordered_dense.h>
+#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<Vulkan::RenderPassKey> {
+ static_assert(std::tuple_size_v<decltype(Vulkan::RenderPassKey::color_formats)> <= 8);
+ static_assert(static_cast<u32>(VideoCore::Surface::PixelFormat::Invalid) <= 0xFF);
+ static_assert(static_cast<u32>(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<size_t>(key.depth_format) << 48;
- value ^= static_cast<size_t>(key.samples) << 52;
- value ^= static_cast<size_t>(key.resolve_color) << 63;
- value ^= static_cast<size_t>(key.color_clear_mask) << 54;
- value ^= static_cast<size_t>(key.depth_stencil_clear) << 62;
- value ^= static_cast<size_t>(key.color_discard_mask) << 24;
- for (size_t i = 0; i < key.color_formats.size(); ++i) {
- value ^= static_cast<size_t>(key.color_formats[i]) << (i * 6);
+ u64 formats = 0;
+ for (size_t index = 0; index < key.color_formats.size(); ++index) {
+ formats |= static_cast<u64>(key.color_formats[index]) << (index * 8);
}
- return value;
+ const u64 state = static_cast<u64>(key.depth_format) |
+ (static_cast<u64>(key.samples) << 8) |
+ (static_cast<u64>(key.color_clear_mask) << 16) |
+ (static_cast<u64>(key.color_discard_mask) << 24) |
+ (static_cast<u64>(key.resolve_color) << 32) |
+ (static_cast<u64>(key.depth_stencil_clear) << 33) |
+ (static_cast<u64>(key.resolve_depth_stencil) << 34) |
+ (static_cast<u64>(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 <algorithm>
+#include <limits>
#include <array>
#include <optional>
#include <span>
#include <memory>
+#include <utility>
#include <vector>
#include <boost/container/small_vector.hpp>
#include <bit>
@@ -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<float, 4>& color) {
if (color == std::array<float, 4>{0, 0, 0, 0}) {
@@ -108,7 +112,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& 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<float, 4>& 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<float, 4>& 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<float, 4>& color) {
.arrayLayers = static_cast<u32>(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<float, 4>& 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<const VkFormat> view_formats,
std::optional<VkFormat> 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<s32>(up_scaling ? extent.width : resolution.ScaleUp(extent.width)),
- .y = static_cast<s32>(is_2d && up_scaling ? extent.height
- : resolution.ScaleUp(extent.height)),
+ .x = static_cast<s32>(up_scaling ? extent.width : scaled_width),
+ .y = static_cast<s32>(up_scaling ? extent.height : scaled_height),
};
const VkOffset2D dst_size{
- .x = static_cast<s32>(up_scaling ? resolution.ScaleUp(extent.width) : extent.width),
- .y = static_cast<s32>(is_2d && up_scaling ? resolution.ScaleUp(extent.height)
- : extent.height),
+ .x = static_cast<s32>(up_scaling ? scaled_width : extent.width),
+ .y = static_cast<s32>(up_scaling ? scaled_height : extent.height),
};
boost::container::small_vector<VkImageBlit, 4> 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_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::A8B8G8R8_SRGB:
+ case PixelFormat::B8G8R8A8_SRGB:
case PixelFormat::B8G8R8A8_UNORM:
- case PixelFormat::R32G32B32A32_FLOAT:
- case PixelFormat::R32G32B32A32_SINT:
- case PixelFormat::R32G32_FLOAT:
- case PixelFormat::R32G32_SINT:
+ if (src_view.format == PixelFormat::D32_FLOAT) {
+ return blit_image_helper.ConvertD32FToABGR8(dst, src_view);
+ }
+ break;
+ case PixelFormat::A8B8G8R8_UNORM:
+ 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 &&
- (dst_view.format == PixelFormat::B5G6R5_UNORM ||
- Settings::values.fix_bloom_effects.GetValue())) {
- const Region2D region{
- .start = {0, 0},
- .end = {static_cast<s32>(dst->RenderArea().width),
- static_cast<s32>(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::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<s32>(dst->RenderArea().width),
+ static_cast<s32>(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<const VideoCommon::ImageCopy> 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<u32>(copy.extent.width) <= shadow->extent.width &&
- static_cast<u32>(copy.extent.height) <= shadow->extent.height) {
+ static_cast<u32>(copy.extent.height) <= shadow->extent.height &&
+ static_cast<u32>(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<u32>(copy.src_subresource.base_layer),
.layerCount = static_cast<u32>(copy.src_subresource.num_layers),
},
.srcOffset = {0, 0, 0},
.dstSubresource{
- .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .aspectMask = aspect_mask,
.mipLevel = static_cast<u32>(copy.dst_subresource.base_level),
.baseArrayLayer = static_cast<u32>(copy.dst_subresource.base_layer),
.layerCount = static_cast<u32>(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<size_t> 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<u64>::max)();
+ scratch.unused_frames = 0;
+ return *scratch.image;
+ }
+ MsaaScratchImage scratch{
+ .key = key,
+ .image = memory_allocator.CreateImage(image_ci),
+ .tick = (std::numeric_limits<u64>::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<VideoCommon::ImageCopy> image_copies;
+ boost::container::small_vector<VideoCommon::ImageCopy, 16> 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<VkBuffer> buffers_span, std::span<size_t> 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;
-
- 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;
+ 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);
+
+ 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<VideoCommon::ImageCopy> image_copies;
+ boost::container::small_vector<VideoCommon::ImageCopy, 16> 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<VkBuffer> buffers_span, std::span<size_t> 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<VkBuffer, 8> buffers_vector{};
boost::container::small_vector<boost::container::small_vector<VkBufferImageCopy, 16>, 8>
@@ -2102,10 +2225,12 @@ void Image::DownloadMemory(std::span<VkBuffer> buffers_span, std::span<size_t> 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<VkBuffer, 8> buffers_vector{};
boost::container::small_vector<boost::container::small_vector<VkBufferImageCopy, 16>, 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<ImageView*, NUM_RT> color_buffers,
ImageView* depth_buffer, bool is_rescaled_) {
- boost::container::small_vector<VkImageView, NUM_RT + 1> attachments;
+ boost::container::small_vector<VkImageView, NUM_RT * 2 + 2> 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<u32>((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<u32>(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<u32>(depth_stencil_clear) << 16) |
+ (static_cast<u32>(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<size_t> 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<const VkFormat> ViewFormats(PixelFormat format) {
@@ -154,8 +166,30 @@ public:
static constexpr size_t indexing_slots = 8 * sizeof(size_t);
std::array<vk::Buffer, indexing_slots> buffers{};
- std::vector<std::pair<u64, vk::Image>> 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<MsaaScratchImage> msaa_scratch_images;
ankerl::unordered_dense::map<VkImage, ResolveShadow> resolve_shadows;
+ std::vector<std::pair<u64, ResolveShadow>> 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]] bool DiscardsMsaaColor() const noexcept {
+ return discard_msaa_color;
}
- [[nodiscard]] VkImage ResolveColorImage(size_t index) const noexcept {
- return index < resolve_images.size() ? *resolve_images[index] : VK_NULL_HANDLE;
+ [[nodiscard]] bool DiscardsMsaaDepthStencil() const noexcept {
+ return discard_msaa_depth_stencil;
}
- [[nodiscard]] bool DiscardsMsaaColor() const noexcept {
- return discard_msaa_color;
- }
+ /// 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<vk::Image> resolve_images;
- std::vector<vk::ImageView> resolve_image_views;
+ std::array<VkImage, 9> 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<u32, NUM_MEMOIZED_RENDER_PASS_VARIANTS> variant_keys{};
+ mutable std::array<VkRenderPass, NUM_MEMOIZED_RENDER_PASS_VARIANTS> 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<P>::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<P>::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;
}
@@ -595,10 +596,25 @@ void TextureCache<P>::WriteMemory(DAddr cpu_addr, size_t size) {
}
template <class P>
+bool TextureCache<P>::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 <class P>
void TextureCache<P>::DownloadMemory(DAddr cpu_addr, size_t size) {
boost::container::small_vector<ImageId, 16> 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 <class P>
bool TextureCache<P>::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<P>::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<P>::RemoveImageViewReferences(std::span<const ImageViewId> rem
template <class P>
void TextureCache<P>::RemoveFramebuffers(std::span<const ImageViewId> 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;