@@ -809,46 +810,48 @@ void BufferCache::BindHostVertexBuffers() {
if (use_optimized_vertex_buffers) {
auto& flags = maxwell3d->dirty.flags;
- u32 enabled_mask = enabled_vertex_buffers_mask;
- HostBindings bindings{};
- u32 last_index = (std::numeric_limits::max)();
- const auto flush_bindings = [&]() {
- if (bindings.buffers.empty()) {
- return;
- }
- bindings.max_index = bindings.min_index + static_cast(bindings.buffers.size());
- runtime.BindVertexBuffers(bindings);
- bindings = HostBindings{};
- last_index = (std::numeric_limits::max)();
- };
- while (enabled_mask != 0) {
- const u32 index = std::countr_zero(enabled_mask);
- enabled_mask &= (enabled_mask - 1);
+ const u32 enabled_mask = enabled_vertex_buffers_mask;
+ u32 dirty_mask = 0;
+ u32 pending_mask = enabled_mask;
+ while (pending_mask != 0) {
+ const u32 index = std::countr_zero(pending_mask);
+ pending_mask &= (pending_mask - 1);
const Binding& binding = VertexBufferSlot(index);
Buffer& buffer = slot_buffers[binding.buffer_id];
TouchBuffer(buffer, binding.buffer_id);
SynchronizeBuffer(buffer, binding.device_addr, binding.size);
- if (!flags[Dirty::VertexBuffer0 + index]) {
- flush_bindings();
- continue;
+ if (flags[Dirty::VertexBuffer0 + index]) {
+ dirty_mask |= 1u << index;
}
+ }
+ if (dirty_mask == 0) {
+ return;
+ }
+ const u32 min_index = static_cast(std::countr_zero(dirty_mask));
+ const u32 max_index = 32u - static_cast(std::countl_zero(dirty_mask));
+ HostBindings bindings{};
+ bindings.min_index = min_index;
+ bindings.max_index = max_index;
+ for (u32 index = min_index; index < max_index; ++index) {
flags[Dirty::VertexBuffer0 + index] = false;
const u32 stride = maxwell3d->regs.vertex_streams[index].stride;
+ if ((enabled_mask & (1u << index)) == 0) {
+ bindings.buffers.push_back(&slot_buffers[NULL_BUFFER_ID]);
+ bindings.offsets.push_back(0);
+ bindings.sizes.push_back(0);
+ bindings.strides.push_back(stride);
+ continue;
+ }
+ const Binding& binding = VertexBufferSlot(index);
+ Buffer& buffer = slot_buffers[binding.buffer_id];
const u32 offset = buffer.Offset(binding.device_addr);
buffer.MarkUsage(offset, binding.size);
- if (!bindings.buffers.empty() && index != last_index + 1) {
- flush_bindings();
- }
- if (bindings.buffers.empty()) {
- bindings.min_index = index;
- }
bindings.buffers.push_back(&buffer);
bindings.offsets.push_back(offset);
bindings.sizes.push_back(binding.size);
bindings.strides.push_back(stride);
- last_index = index;
}
- flush_bindings();
+ runtime.BindVertexBuffers(bindings);
} else {
HostBindings host_bindings;
bool any_valid{false};
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt
index 5b8f47a7a6..f8b64c57e8 100644
--- a/src/video_core/host_shaders/CMakeLists.txt
+++ b/src/video_core/host_shaders/CMakeLists.txt
@@ -17,11 +17,14 @@ set(SHADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/astc_decoder.comp
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_float.frag
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d.comp
+ ${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d_buffer.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
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d_bcn.comp
+ ${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d_buffer.comp
${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d24s8.frag
${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d32f.frag
${CMAKE_CURRENT_SOURCE_DIR}/convert_d32f_to_abgr8.frag
@@ -30,8 +33,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 +170,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/block_linear_unswizzle_2d_buffer.comp b/src/video_core/host_shaders/block_linear_unswizzle_2d_buffer.comp
new file mode 100644
index 0000000000..3fc71e05b6
--- /dev/null
+++ b/src/video_core/host_shaders/block_linear_unswizzle_2d_buffer.comp
@@ -0,0 +1,104 @@
+// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#version 430
+
+#extension GL_EXT_shader_16bit_storage : require
+#extension GL_EXT_shader_8bit_storage : require
+
+#define BINDING_INPUT_BUFFER 0
+#define BINDING_OUTPUT_BUFFER 1
+
+layout(push_constant) uniform PushConstants {
+ uvec3 dim;
+ uint bytes_per_block_log2;
+
+ uvec3 origin;
+ uint layer_stride;
+
+ uint block_size;
+ uint x_shift;
+ uint block_height;
+ uint block_height_mask;
+} pc;
+
+layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU32 { uint u32data[]; };
+layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU64 { uvec2 u64data[]; };
+layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU128 { uvec4 u128data[]; };
+
+layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBuffer {
+ uint out_u32[];
+};
+
+layout(local_size_x = 16, local_size_y = 8, local_size_z = 1) in;
+
+const uint GOB_SIZE_X = 64;
+const uint GOB_SIZE_Y = 8;
+
+const uint GOB_SIZE_X_SHIFT = 6;
+const uint GOB_SIZE_Y_SHIFT = 3;
+const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT;
+
+const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1u, GOB_SIZE_Y - 1u);
+
+uint SwizzleTable(uint pos) {
+ const uint t[8] = uint[](
+ 0x12100200, 0x13110301, 0x16140604, 0x17150705,
+ 0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d
+ );
+ const uint i = pos >> 4;
+ const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff;
+ return (h << 4) | (pos & 0xf);
+}
+
+uint SwizzleOffset(uvec2 pos) {
+ pos = pos & SWIZZLE_MASK;
+ return SwizzleTable(pos.y * 64u + pos.x);
+}
+
+uvec4 ReadTexel(uint offset) {
+ switch (pc.bytes_per_block_log2) {
+ case 2u:
+ return uvec4(u32data[offset / 4u], 0u, 0u, 0u);
+ case 3u:
+ return uvec4(u64data[offset / 8u], 0u, 0u);
+ case 4u:
+ return u128data[offset / 16u];
+ }
+ return uvec4(0u);
+}
+
+void main() {
+ uvec3 coord = gl_GlobalInvocationID;
+ if (coord.x >= pc.dim.x || coord.y >= pc.dim.y || coord.z >= pc.dim.z) {
+ return;
+ }
+
+ uvec3 pos = coord + pc.origin;
+ pos.x <<= pc.bytes_per_block_log2;
+
+ uint swizzle = SwizzleOffset(pos.xy);
+ uint block_y = pos.y >> GOB_SIZE_Y_SHIFT;
+
+ uint offset = 0u;
+ offset += pos.z * pc.layer_stride;
+ offset += (block_y >> pc.block_height) * pc.block_size;
+ offset += (block_y & pc.block_height_mask) << GOB_SIZE_SHIFT;
+ offset += (pos.x >> GOB_SIZE_X_SHIFT) << pc.x_shift;
+ offset += swizzle;
+
+ uvec4 texel = ReadTexel(offset);
+
+ uint words = 1u << (pc.bytes_per_block_log2 - 2u);
+ uint linear_index = coord.x + coord.y * pc.dim.x + coord.z * pc.dim.x * pc.dim.y;
+ uint out_idx = linear_index * words;
+
+ out_u32[out_idx] = texel.x;
+ if (words > 1u) {
+ out_u32[out_idx + 1u] = texel.y;
+ }
+ if (words > 2u) {
+ out_u32[out_idx + 2u] = texel.z;
+ out_u32[out_idx + 3u] = texel.w;
+ }
+}
diff --git a/src/video_core/host_shaders/block_linear_unswizzle_3d_buffer.comp b/src/video_core/host_shaders/block_linear_unswizzle_3d_buffer.comp
new file mode 100644
index 0000000000..7333568eb7
--- /dev/null
+++ b/src/video_core/host_shaders/block_linear_unswizzle_3d_buffer.comp
@@ -0,0 +1,105 @@
+// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#version 430
+
+#define BINDING_INPUT_BUFFER 0
+#define BINDING_OUTPUT_BUFFER 1
+
+layout(push_constant) uniform PushConstants {
+ uvec3 dim;
+ uint bytes_per_block_log2;
+
+ uvec3 origin;
+ uint slice_size;
+
+ uint block_size;
+ uint x_shift;
+ uint block_height;
+ uint block_height_mask;
+
+ uint block_depth;
+ uint block_depth_mask;
+} pc;
+
+layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU32 { uint u32data[]; };
+layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU64 { uvec2 u64data[]; };
+layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU128 { uvec4 u128data[]; };
+
+layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBuffer {
+ uint out_u32[];
+};
+
+layout(local_size_x = 8, local_size_y = 8, local_size_z = 4) in;
+
+const uint GOB_SIZE_X = 64;
+const uint GOB_SIZE_Y = 8;
+
+const uint GOB_SIZE_X_SHIFT = 6;
+const uint GOB_SIZE_Y_SHIFT = 3;
+const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT;
+
+const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1u, GOB_SIZE_Y - 1u);
+
+uint SwizzleTable(uint pos) {
+ const uint t[8] = uint[](
+ 0x12100200, 0x13110301, 0x16140604, 0x17150705,
+ 0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d
+ );
+ const uint i = pos >> 4;
+ const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff;
+ return (h << 4) | (pos & 0xf);
+}
+
+uint SwizzleOffset(uvec2 pos) {
+ pos = pos & SWIZZLE_MASK;
+ return SwizzleTable(pos.y * 64u + pos.x);
+}
+
+uvec4 ReadTexel(uint offset) {
+ switch (pc.bytes_per_block_log2) {
+ case 2u:
+ return uvec4(u32data[offset / 4u], 0u, 0u, 0u);
+ case 3u:
+ return uvec4(u64data[offset / 8u], 0u, 0u);
+ case 4u:
+ return u128data[offset / 16u];
+ }
+ return uvec4(0u);
+}
+
+void main() {
+ uvec3 coord = gl_GlobalInvocationID;
+ if (coord.x >= pc.dim.x || coord.y >= pc.dim.y || coord.z >= pc.dim.z) {
+ return;
+ }
+
+ uvec3 pos = coord + pc.origin;
+ pos.x <<= pc.bytes_per_block_log2;
+
+ uint swizzle = SwizzleOffset(pos.xy);
+ uint block_y = pos.y >> GOB_SIZE_Y_SHIFT;
+
+ uint offset = 0u;
+ offset += (pos.z >> pc.block_depth) * pc.slice_size;
+ offset += (pos.z & pc.block_depth_mask) << (GOB_SIZE_SHIFT + pc.block_height);
+ offset += (block_y >> pc.block_height) * pc.block_size;
+ offset += (block_y & pc.block_height_mask) << GOB_SIZE_SHIFT;
+ offset += (pos.x >> GOB_SIZE_X_SHIFT) << pc.x_shift;
+ offset += swizzle;
+
+ uvec4 texel = ReadTexel(offset);
+
+ uint words = 1u << (pc.bytes_per_block_log2 - 2u);
+ uint linear_index = coord.x + coord.y * pc.dim.x + coord.z * pc.dim.x * pc.dim.y;
+ uint out_idx = linear_index * words;
+
+ out_u32[out_idx] = texel.x;
+ if (words > 1u) {
+ out_u32[out_idx + 1u] = texel.y;
+ }
+ if (words > 2u) {
+ out_u32[out_idx + 2u] = texel.z;
+ out_u32[out_idx + 3u] = texel.w;
+ }
+}
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/host_shaders/sgsr1_shader.vert b/src/video_core/host_shaders/sgsr1_shader.vert
index 0d2a935a9d..e995997060 100644
--- a/src/video_core/host_shaders/sgsr1_shader.vert
+++ b/src/video_core/host_shaders/sgsr1_shader.vert
@@ -7,6 +7,7 @@ layout(push_constant) uniform constants {
vec2 scale;
vec2 size;
vec2 resize_factor;
+ vec2 crop_offset;
float edge_sharpness;
};
layout(location = 0) out highp vec2 texcoord;
@@ -15,5 +16,5 @@ void main() {
float x = float((gl_VertexIndex & 1) << 2);
float y = float((gl_VertexIndex & 2) << 1);
gl_Position = vec4(x - 1.0f, y - 1.0f, 0.0, 1.0f) * vec4(sign(resize_factor), 1.f, 1.f);
- texcoord = vec2(x, y) * abs(resize_factor) * 0.5;
+ texcoord = crop_offset + vec2(x, y) * abs(resize_factor) * 0.5;
}
diff --git a/src/video_core/host_shaders/sgsr1_shader_mobile.frag b/src/video_core/host_shaders/sgsr1_shader_mobile.frag
index 2e62d60af3..3453ca2cae 100644
--- a/src/video_core/host_shaders/sgsr1_shader_mobile.frag
+++ b/src/video_core/host_shaders/sgsr1_shader_mobile.frag
@@ -14,6 +14,7 @@ layout(push_constant) uniform constants {
vec2 scale;
vec2 size;
vec2 resize_factor;
+ vec2 crop_offset;
float edge_sharpness;
};
layout(set = 0, binding = 0) uniform sampler2D sampler0;
diff --git a/src/video_core/host_shaders/sgsr1_shader_mobile_edge_direction.frag b/src/video_core/host_shaders/sgsr1_shader_mobile_edge_direction.frag
index 10a8ff8e00..66417e9c46 100644
--- a/src/video_core/host_shaders/sgsr1_shader_mobile_edge_direction.frag
+++ b/src/video_core/host_shaders/sgsr1_shader_mobile_edge_direction.frag
@@ -13,6 +13,7 @@
layout( push_constant ) uniform constants {
vec4 ViewportInfo[1];
vec2 ResizeFactor;
+ vec2 CropOffset;
float EdgeSharpness;
};
layout(set = 0, binding = 0) uniform sampler2D ps0;
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.h b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
index 66ab677919..a5a573ca46 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.h
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
@@ -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 2021 yuzu Emulator Project
@@ -44,6 +44,7 @@ struct GraphicsPipelineKey {
BitField<8, 2, Maxwell::Tessellation::Spacing> tessellation_spacing;
BitField<10, 1, u32> tessellation_clockwise;
BitField<11, 3, Tegra::Engines::Maxwell3D::EngineHint> app_stage;
+ BitField<14, 1, u32> viewport_scale_offset_enabled;
};
std::array padding;
VideoCommon::TransformFeedbackState xfb_state;
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index cde03d5822..8a375a2c7b 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -45,6 +45,7 @@ using Shader::Backend::SPIRV::EmitSPIRV;
using Shader::Maxwell::ConvertLegacyToGeneric;
using Shader::Maxwell::GenerateGeometryPassthrough;
using Shader::Maxwell::MergeDualVertexPrograms;
+using Shader::Maxwell::PrunePassthroughStores;
using Shader::Maxwell::TranslateProgram;
using VideoCommon::ComputeEnvironment;
using VideoCommon::FileEnvironment;
@@ -54,22 +55,54 @@ using VideoCommon::LoadPipelines;
using VideoCommon::SerializePipeline;
using Context = ShaderContext::Context;
-constexpr u32 CACHE_VERSION = 15;
+constexpr u32 CACHE_VERSION = 21;
template
auto MakeSpan(Container& container) {
return std::span(container.data(), container.size());
}
-Shader::OutputTopology MaxwellToOutputTopology(Maxwell::PrimitiveTopology topology) {
+Shader::OutputTopology InputToOutputTopology(Shader::InputTopology topology) {
switch (topology) {
- case Maxwell::PrimitiveTopology::Points:
+ case Shader::InputTopology::Points:
return Shader::OutputTopology::PointList;
- case Maxwell::PrimitiveTopology::LineStrip:
+ case Shader::InputTopology::Lines:
+ case Shader::InputTopology::LinesAdjacency:
return Shader::OutputTopology::LineStrip;
- default:
+ case Shader::InputTopology::Triangles:
+ case Shader::InputTopology::TrianglesAdjacency:
return Shader::OutputTopology::TriangleStrip;
}
+ return Shader::OutputTopology::TriangleStrip;
+}
+
+Shader::InputTopology MaxwellToInputTopology(Maxwell::PrimitiveTopology topology) {
+ switch (topology) {
+ case Maxwell::PrimitiveTopology::Points:
+ return Shader::InputTopology::Points;
+ case Maxwell::PrimitiveTopology::Lines:
+ case Maxwell::PrimitiveTopology::LineLoop:
+ case Maxwell::PrimitiveTopology::LineStrip:
+ return Shader::InputTopology::Lines;
+ case Maxwell::PrimitiveTopology::LinesAdjacency:
+ case Maxwell::PrimitiveTopology::LineStripAdjacency:
+ return Shader::InputTopology::LinesAdjacency;
+ case Maxwell::PrimitiveTopology::TrianglesAdjacency:
+ case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
+ return Shader::InputTopology::TrianglesAdjacency;
+ default:
+ return Shader::InputTopology::Triangles;
+ }
+}
+
+Shader::InputTopology GeometryInputTopology(const GraphicsPipelineKey& key) {
+ if (key.unique_hashes[static_cast(Maxwell::ShaderType::Tessellation)] == 0) {
+ return MaxwellToInputTopology(key.gs_input_topology);
+ }
+ if (key.tessellation_primitive == Maxwell::Tessellation::DomainType::Isolines) {
+ return Shader::InputTopology::Lines;
+ }
+ return Shader::InputTopology::Triangles;
}
Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key,
@@ -127,33 +160,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key,
default:
break;
}
- switch (key.gs_input_topology) {
- case Maxwell::PrimitiveTopology::Points:
- info.input_topology = Shader::InputTopology::Points;
- break;
- case Maxwell::PrimitiveTopology::Lines:
- case Maxwell::PrimitiveTopology::LineLoop:
- case Maxwell::PrimitiveTopology::LineStrip:
- info.input_topology = Shader::InputTopology::Lines;
- break;
- case Maxwell::PrimitiveTopology::Triangles:
- case Maxwell::PrimitiveTopology::TriangleStrip:
- case Maxwell::PrimitiveTopology::TriangleFan:
- case Maxwell::PrimitiveTopology::Quads:
- case Maxwell::PrimitiveTopology::QuadStrip:
- case Maxwell::PrimitiveTopology::Polygon:
- case Maxwell::PrimitiveTopology::Patches:
- info.input_topology = Shader::InputTopology::Triangles;
- break;
- case Maxwell::PrimitiveTopology::LinesAdjacency:
- case Maxwell::PrimitiveTopology::LineStripAdjacency:
- info.input_topology = Shader::InputTopology::LinesAdjacency;
- break;
- case Maxwell::PrimitiveTopology::TrianglesAdjacency:
- case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
- info.input_topology = Shader::InputTopology::TrianglesAdjacency;
- break;
- }
+ info.input_topology = GeometryInputTopology(key);
info.glasm_use_storage_buffers = glasm_use_storage_buffers;
return info;
}
@@ -231,6 +238,8 @@ ShaderCache::ShaderCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
.has_broken_unsigned_image_offsets = true,
.has_broken_signed_operations = true,
.has_broken_fp16_float_controls = false,
+ .has_broken_fp32_signed_zero_nan_preserve = false,
+ .has_broken_fp32_denorm_flush = false,
.has_gl_component_indexing_bug = device.HasComponentIndexingBug(),
.has_gl_precise_bug = device.HasPreciseBug(),
.has_gl_cbuf_ftou_bug = device.HasCbufFtouBug(),
@@ -383,6 +392,7 @@ GraphicsPipeline* ShaderCache::CurrentGraphicsPipeline() {
regs.tessellation.params.output_primitives.Value() ==
Maxwell::Tessellation::OutputPrimitives::Triangles_CW);
graphics_key.xfb_enabled.Assign(regs.transform_feedback_enabled != 0 ? 1 : 0);
+ graphics_key.viewport_scale_offset_enabled.Assign(regs.viewport_scale_offset_enabled != 0);
graphics_key.app_stage.Assign(maxwell3d->engine_state);
if (graphics_key.xfb_enabled) {
SetXfbState(graphics_key.xfb_state, regs);
@@ -482,9 +492,10 @@ std::unique_ptr ShaderCache::CreateGraphicsPipeline(
const bool is_emulated_stage = layer_source_program != nullptr
&& index == u32(Maxwell::ShaderType::Geometry);
if (key.unique_hashes[index] == 0 && is_emulated_stage) {
- auto topology = MaxwellToOutputTopology(key.gs_input_topology);
- programs[index] = GenerateGeometryPassthrough(pools.inst, pools.block, host_info,
- *layer_source_program, topology);
+ const auto input_topology = GeometryInputTopology(key);
+ programs[index] = GenerateGeometryPassthrough(
+ pools.inst, pools.block, host_info, *layer_source_program,
+ InputToOutputTopology(input_topology), input_topology);
continue;
}
if (key.unique_hashes[index] == 0) {
@@ -502,13 +513,15 @@ std::unique_ptr ShaderCache::CreateGraphicsPipeline(
if (!uses_vertex_a || index != 1) {
// Normal path
- programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info);
+ programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info,
+ GeometryInputTopology(key));
total_storage_buffers += Shader::NumDescriptors(programs[index].info.storage_buffers_descriptors);
} else {
// VertexB path when VertexA is present.
auto& program_va{programs[0]};
- auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
+ auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info,
+ GeometryInputTopology(key))};
total_storage_buffers += Shader::NumDescriptors(program_vb.info.storage_buffers_descriptors);
programs[index] = MergeDualVertexPrograms(program_va, program_vb, env);
}
@@ -540,6 +553,9 @@ std::unique_ptr ShaderCache::CreateGraphicsPipeline(
const size_t stage_index{index - 1};
infos[stage_index] = &program.info;
+ if (previous_program) {
+ PrunePassthroughStores(program, previous_program->info.stores);
+ }
const auto runtime_info = MakeRuntimeInfo(key, program, previous_program, glasm_use_storage_buffers, use_glasm);
switch (::Settings::values.renderer_backend.GetValue()) {
case Settings::RendererBackend::OpenGL_GLSL:
@@ -597,7 +613,8 @@ std::unique_ptr ShaderCache::CreateComputePipeline(
env.Dump(hash, key.unique_hash);
}
- auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
+ auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info,
+ Shader::InputTopology::Points)};
const u32 num_storage_buffers{Shader::NumDescriptors(program.info.storage_buffers_descriptors)};
Shader::RuntimeInfo info;
info.glasm_use_storage_buffers = num_storage_buffers <= device.GetMaxGLASMStorageBufferBlocks();
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..f93145fa67 100644
--- a/src/video_core/renderer_vulkan/blit_image.cpp
+++ b/src/video_core/renderer_vulkan/blit_image.cpp
@@ -12,6 +12,7 @@
#include "common/settings.h"
#include "video_core/host_shaders/blit_color_float_frag_spv.h"
#include "video_core/host_shaders/blit_color_msaa_frag_spv.h"
+#include "video_core/host_shaders/blit_depth_frag_spv.h"
#include "video_core/host_shaders/blit_depth_msaa_frag_spv.h"
#include "video_core/host_shaders/blit_depth_stencil_msaa_frag_spv.h"
#include "video_core/host_shaders/convert_abgr8_to_d24s8_frag_spv.h"
@@ -21,7 +22,15 @@
#include "video_core/host_shaders/convert_depth_to_float_frag_spv.h"
#include "video_core/host_shaders/convert_float_to_depth_frag_spv.h"
#include "video_core/host_shaders/convert_msaa_to_non_msaa_frag_spv.h"
+#include "video_core/host_shaders/convert_msaa_to_non_msaa_depth_frag_spv.h"
+#include "video_core/host_shaders/convert_msaa_to_non_msaa_depth_stencil_frag_spv.h"
+#include "video_core/host_shaders/convert_msaa_to_non_msaa_sint_frag_spv.h"
+#include "video_core/host_shaders/convert_msaa_to_non_msaa_uint_frag_spv.h"
#include "video_core/host_shaders/convert_non_msaa_to_msaa_frag_spv.h"
+#include "video_core/host_shaders/convert_non_msaa_to_msaa_sint_frag_spv.h"
+#include "video_core/host_shaders/convert_non_msaa_to_msaa_uint_frag_spv.h"
+#include "video_core/host_shaders/convert_non_msaa_to_msaa_depth_frag_spv.h"
+#include "video_core/host_shaders/convert_non_msaa_to_msaa_depth_stencil_frag_spv.h"
#include "video_core/host_shaders/convert_s8d24_to_abgr8_frag_spv.h"
#include "video_core/host_shaders/full_screen_triangle_vert_spv.h"
#include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h"
@@ -518,8 +527,19 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view)
}
}
+[[nodiscard]] MSAACopyFormatClass FormatClass(VideoCore::Surface::PixelFormat format) {
+ if (!VideoCore::Surface::IsPixelFormatInteger(format)) {
+ return MSAACopyFormatClass::Float;
+ }
+ if (VideoCore::Surface::IsPixelFormatSignedInteger(format)) {
+ return MSAACopyFormatClass::SignedInteger;
+ }
+ return MSAACopyFormatClass::UnsignedInteger;
+}
+
[[nodiscard]] vk::ImageView MakeMSAACopyView(const vk::Device& device, VkImage image,
- VkFormat format, u32 base_level) {
+ VkFormat format, u32 base_level, u32 base_layer,
+ VkImageAspectFlags aspect_mask) {
return device.CreateImageView(VkImageViewCreateInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = nullptr,
@@ -534,10 +554,10 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view)
.a = VK_COMPONENT_SWIZZLE_IDENTITY,
},
.subresourceRange{
- .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .aspectMask = aspect_mask,
.baseMipLevel = base_level,
.levelCount = 1,
- .baseArrayLayer = 0,
+ .baseArrayLayer = base_layer,
.layerCount = 1,
},
});
@@ -586,12 +606,17 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
msaa_copy_pipeline_layout(device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo(
one_texture_set_layout.address(),
PUSH_CONSTANT_RANGE))),
+ msaa_copy_depth_stencil_pipeline_layout(
+ device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo(
+ two_textures_set_layout.address(),
+ PUSH_CONSTANT_RANGE))),
full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)),
blit_color_to_color_frag(BuildShader(device, BLIT_COLOR_FLOAT_FRAG_SPV)),
blit_color_msaa_frag(BuildShader(device, BLIT_COLOR_MSAA_FRAG_SPV)),
blit_depth_stencil_frag(device.IsExtShaderStencilExportSupported()
? BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV)
: vk::ShaderModule{}),
+ blit_depth_frag(BuildShader(device, BLIT_DEPTH_FRAG_SPV)),
blit_depth_msaa_frag(BuildShader(device, BLIT_DEPTH_MSAA_FRAG_SPV)),
blit_depth_stencil_msaa_frag(device.IsExtShaderStencilExportSupported()
? BuildShader(device, BLIT_DEPTH_STENCIL_MSAA_FRAG_SPV)
@@ -609,7 +634,25 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)),
convert_s8d24_to_abgr8_frag(BuildShader(device, CONVERT_S8D24_TO_ABGR8_FRAG_SPV)),
convert_msaa_to_non_msaa_frag(BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_FRAG_SPV)),
+ convert_msaa_to_non_msaa_sint_frag(
+ BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_SINT_FRAG_SPV)),
+ convert_msaa_to_non_msaa_uint_frag(
+ BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_UINT_FRAG_SPV)),
+ convert_msaa_to_non_msaa_depth_frag(
+ BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_DEPTH_FRAG_SPV)),
+ convert_msaa_to_non_msaa_depth_stencil_frag(
+ BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_DEPTH_STENCIL_FRAG_SPV)),
convert_non_msaa_to_msaa_frag(BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_FRAG_SPV)),
+ convert_non_msaa_to_msaa_sint_frag(
+ BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_SINT_FRAG_SPV)),
+ convert_non_msaa_to_msaa_uint_frag(
+ BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_UINT_FRAG_SPV)),
+ convert_non_msaa_to_msaa_depth_frag(
+ BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_DEPTH_FRAG_SPV)),
+ convert_non_msaa_to_msaa_depth_stencil_frag(
+ device.IsExtShaderStencilExportSupported()
+ ? BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_DEPTH_STENCIL_FRAG_SPV)
+ : vk::ShaderModule{}),
linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO)),
nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO)) {}
@@ -624,24 +667,13 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, const ImageV
.renderpass = dst_framebuffer->RenderPass(),
.operation = operation,
};
- const VkPipelineLayout layout = *one_texture_pipeline_layout;
- const VkSampler sampler = is_linear ? *linear_sampler : *nearest_sampler;
- const VkPipeline pipeline = FindOrEmplaceColorPipeline(key);
- const VkImageView src_view = src_image_view.Handle(Shader::TextureType::Color2D);
-
- RecordShaderReadBarrier(scheduler, src_image_view);
- scheduler.RequestRenderpass(dst_framebuffer);
- scheduler.Record([this, dst_region, src_region, pipeline, layout, sampler,
- src_view](vk::CommandBuffer cmdbuf) {
- const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
- UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view);
- cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
- cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
- nullptr);
- BindBlitState(cmdbuf, layout, dst_region, src_region);
- cmdbuf.Draw(3, 1, 0, 0);
- });
- scheduler.InvalidateState();
+ VkSampler sampler = *nearest_sampler;
+ if (is_linear) {
+ sampler = *linear_sampler;
+ }
+ BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region,
+ FindOrEmplaceColorPipeline(key), sampler,
+ src_image_view.Handle(Shader::TextureType::Color2D), VK_NULL_HANDLE, false);
}
void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView src_image_view,
@@ -670,6 +702,38 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView
});
}
+void BlitImageHelper::BlitImpl(const Framebuffer* dst_framebuffer,
+ const ImageView& src_image_view, const Region2D& dst_region,
+ const Region2D& src_region, VkPipeline pipeline, VkSampler sampler,
+ VkImageView src_view, VkImageView src_stencil_view,
+ bool blit_stencil) {
+ VkPipelineLayout layout = *one_texture_pipeline_layout;
+ if (blit_stencil) {
+ layout = *two_textures_pipeline_layout;
+ }
+
+ RecordShaderReadBarrier(scheduler, src_image_view);
+ scheduler.RequestRenderpass(dst_framebuffer);
+ scheduler.Record([this, dst_region, src_region, pipeline, layout, sampler, src_view,
+ src_stencil_view, blit_stencil](vk::CommandBuffer cmdbuf) {
+ VkDescriptorSet descriptor_set = VK_NULL_HANDLE;
+ if (blit_stencil) {
+ descriptor_set = two_textures_descriptor_allocator.Commit();
+ UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_view,
+ src_stencil_view);
+ } else {
+ descriptor_set = one_texture_descriptor_allocator.Commit();
+ UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view);
+ }
+ cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
+ cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
+ nullptr);
+ BindBlitState(cmdbuf, layout, dst_region, src_region);
+ cmdbuf.Draw(3, 1, 0, 0);
+ });
+ scheduler.InvalidateState();
+}
+
void BlitImageHelper::BlitColorMSAA(const Framebuffer* dst_framebuffer,
const ImageView& src_image_view, const Region2D& dst_region,
const Region2D& src_region) {
@@ -677,24 +741,34 @@ void BlitImageHelper::BlitColorMSAA(const Framebuffer* dst_framebuffer,
.renderpass = dst_framebuffer->RenderPass(),
.samples = dst_framebuffer->Samples(),
};
- const VkPipelineLayout layout = *one_texture_pipeline_layout;
- const VkSampler sampler = *nearest_sampler;
- const VkPipeline pipeline = FindOrEmplaceBlitColorMSAAPipeline(key);
- const VkImageView src_view = src_image_view.Handle(Shader::TextureType::Color2D);
+ BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region,
+ FindOrEmplaceBlitColorMSAAPipeline(key), *nearest_sampler,
+ src_image_view.Handle(Shader::TextureType::Color2D), VK_NULL_HANDLE, false);
+}
- RecordShaderReadBarrier(scheduler, src_image_view);
- scheduler.RequestRenderpass(dst_framebuffer);
- scheduler.Record([this, dst_region, src_region, pipeline, layout, sampler,
- src_view](vk::CommandBuffer cmdbuf) {
- const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
- UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view);
- cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
- cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
- nullptr);
- BindBlitState(cmdbuf, layout, dst_region, src_region);
- cmdbuf.Draw(3, 1, 0, 0);
- });
- scheduler.InvalidateState();
+void BlitImageHelper::BlitDepthStencilMSAA(const Framebuffer* dst_framebuffer,
+ ImageView& src_image_view, const Region2D& dst_region,
+ const Region2D& src_region) {
+ const bool blit_stencil =
+ dst_framebuffer->HasAspectStencilBit() && device.IsExtShaderStencilExportSupported();
+ const BlitMSAAPipelineKey key{
+ .renderpass = dst_framebuffer->RenderPass(),
+ .samples = dst_framebuffer->Samples(),
+ };
+ VkImageView src_stencil_view = VK_NULL_HANDLE;
+ if (blit_stencil) {
+ src_stencil_view = src_image_view.StencilView();
+ }
+ BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region,
+ FindOrEmplaceBlitDepthStencilMSAAPipeline(key, blit_stencil), *nearest_sampler,
+ src_image_view.DepthView(), src_stencil_view, blit_stencil);
+}
+
+void BlitImageHelper::BlitDepth(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
+ const Region2D& dst_region, const Region2D& src_region) {
+ BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region,
+ FindOrEmplaceBlitDepthPipeline(dst_framebuffer->RenderPass()), *nearest_sampler,
+ src_image_view.DepthView(), VK_NULL_HANDLE, false);
}
void BlitImageHelper::ResolveDepthStencil(const Framebuffer* dst_framebuffer,
@@ -702,36 +776,14 @@ void BlitImageHelper::ResolveDepthStencil(const Framebuffer* dst_framebuffer,
const Region2D& src_region) {
const bool resolve_stencil =
dst_framebuffer->HasAspectStencilBit() && device.IsExtShaderStencilExportSupported();
- const VkPipeline pipeline =
- FindOrEmplaceResolveDepthStencilPipeline(dst_framebuffer->RenderPass(), resolve_stencil);
- const VkPipelineLayout layout =
- resolve_stencil ? *two_textures_pipeline_layout : *one_texture_pipeline_layout;
- const VkSampler sampler = *nearest_sampler;
- const VkImageView src_depth_view = src_image_view.DepthView();
- const VkImageView src_stencil_view =
- resolve_stencil ? src_image_view.StencilView() : VK_NULL_HANDLE;
-
- RecordShaderReadBarrier(scheduler, src_image_view);
- scheduler.RequestRenderpass(dst_framebuffer);
- scheduler.Record([this, dst_region, src_region, pipeline, layout, sampler, src_depth_view,
- src_stencil_view, resolve_stencil](vk::CommandBuffer cmdbuf) {
- if (resolve_stencil) {
- const VkDescriptorSet descriptor_set = two_textures_descriptor_allocator.Commit();
- UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_depth_view,
- src_stencil_view);
- cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
- nullptr);
- } else {
- const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
- UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_depth_view);
- cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
- nullptr);
- }
- cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
- BindBlitState(cmdbuf, layout, dst_region, src_region);
- cmdbuf.Draw(3, 1, 0, 0);
- });
- scheduler.InvalidateState();
+ VkImageView src_stencil_view = VK_NULL_HANDLE;
+ if (resolve_stencil) {
+ src_stencil_view = src_image_view.StencilView();
+ }
+ BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region,
+ FindOrEmplaceResolveDepthStencilPipeline(dst_framebuffer->RenderPass(),
+ resolve_stencil),
+ *nearest_sampler, src_image_view.DepthView(), src_stencil_view, resolve_stencil);
}
void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
@@ -739,35 +791,23 @@ void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
const Region2D& dst_region, const Region2D& src_region,
Tegra::Engines::Fermi2D::Filter filter,
Tegra::Engines::Fermi2D::Operation operation) {
- if (!device.IsExtShaderStencilExportSupported()) {
- return;
- }
ASSERT(filter == Tegra::Engines::Fermi2D::Filter::Point);
ASSERT(operation == Tegra::Engines::Fermi2D::Operation::SrcCopy);
+ const bool blit_stencil = device.IsExtShaderStencilExportSupported();
const BlitImagePipelineKey key{
.renderpass = dst_framebuffer->RenderPass(),
.operation = operation,
};
- const VkPipelineLayout layout = *two_textures_pipeline_layout;
- const VkSampler sampler = *nearest_sampler;
- const VkPipeline pipeline = FindOrEmplaceDepthStencilPipeline(key);
- const VkImageView src_depth_view = src_image_view.DepthView();
- const VkImageView src_stencil_view = src_image_view.StencilView();
-
- RecordShaderReadBarrier(scheduler, src_image_view);
- scheduler.RequestRenderpass(dst_framebuffer);
- scheduler.Record([dst_region, src_region, pipeline, layout, sampler, src_depth_view,
- src_stencil_view, this](vk::CommandBuffer cmdbuf) {
- const VkDescriptorSet descriptor_set = two_textures_descriptor_allocator.Commit();
- UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_depth_view,
- src_stencil_view);
- cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
- cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
- nullptr);
- BindBlitState(cmdbuf, layout, dst_region, src_region);
- cmdbuf.Draw(3, 1, 0, 0);
- });
- scheduler.InvalidateState();
+ VkPipeline pipeline{};
+ VkImageView src_stencil_view = VK_NULL_HANDLE;
+ if (blit_stencil) {
+ pipeline = FindOrEmplaceDepthStencilPipeline(key);
+ src_stencil_view = src_image_view.StencilView();
+ } else {
+ pipeline = FindOrEmplaceBlitDepthPipeline(key.renderpass);
+ }
+ BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region, pipeline, *nearest_sampler,
+ src_image_view.DepthView(), src_stencil_view, blit_stencil);
}
void BlitImageHelper::ConvertD32ToR32(const Framebuffer* dst_framebuffer,
@@ -882,19 +922,183 @@ void BlitImageHelper::ClearDepthStencil(const Framebuffer* dst_framebuffer, bool
scheduler.InvalidateState();
}
+void BlitImageHelper::CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline,
+ VkPipelineLayout layout, VkImage dst_image,
+ VkFormat dst_vk_format, VkImage src_image,
+ VkFormat src_vk_format, s32 scale_x, s32 scale_y,
+ std::span copies,
+ const MSAACopyAspectInfo& aspect_info, bool copy_stencil) {
+ while (!msaa_copy_resources.empty() && scheduler.IsFree(msaa_copy_resources.front().tick)) {
+ msaa_copy_resources.pop_front();
+ }
+ const VkSampler sampler = *nearest_sampler;
+ for (const VideoCommon::ImageCopy& copy : copies) {
+ const s32 num_layers = (std::min)(copy.src_subresource.num_layers,
+ copy.dst_subresource.num_layers);
+ for (s32 layer = 0; layer < num_layers; ++layer) {
+ const u32 src_level = static_cast(copy.src_subresource.base_level);
+ const u32 src_layer = static_cast(copy.src_subresource.base_layer + layer);
+ vk::ImageView src_view =
+ MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format, src_level,
+ src_layer, aspect_info.src_view_aspect);
+ vk::ImageView src_stencil_view;
+ if (copy_stencil) {
+ src_stencil_view =
+ MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format, src_level,
+ src_layer, VK_IMAGE_ASPECT_STENCIL_BIT);
+ }
+ vk::ImageView dst_view =
+ MakeMSAACopyView(device.GetLogical(), dst_image, dst_vk_format,
+ static_cast(copy.dst_subresource.base_level),
+ static_cast(copy.dst_subresource.base_layer + layer),
+ aspect_info.attachment_aspect);
+ const VkOffset2D dst_offset{copy.dst_offset.x, copy.dst_offset.y};
+ const VkExtent2D dst_extent{copy.extent.width, copy.extent.height};
+ const VkRect2D render_area{
+ .offset = dst_offset,
+ .extent = dst_extent,
+ };
+ vk::Framebuffer framebuffer = device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{
+ .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = 0,
+ .renderPass = renderpass,
+ .attachmentCount = 1,
+ .pAttachments = dst_view.address(),
+ .width = static_cast(dst_offset.x) + dst_extent.width,
+ .height = static_cast(dst_offset.y) + dst_extent.height,
+ .layers = 1,
+ });
+ const MSAACopyPushConstants push_constants{
+ .dst_offset = {dst_offset.x, dst_offset.y},
+ .src_offset = {copy.src_offset.x, copy.src_offset.y},
+ .scale = {scale_x, scale_y},
+ };
+ VkImageView src_stencil_handle = VK_NULL_HANDLE;
+ if (copy_stencil) {
+ src_stencil_handle = *src_stencil_view;
+ }
+ scheduler.RequestOutsideRenderPassOperationContext();
+ scheduler.Record([this, pipeline, layout, sampler, renderpass,
+ framebuffer_handle = *framebuffer, src_view_handle = *src_view,
+ src_stencil_handle, src = src_image, dst = dst_image, render_area,
+ aspect_info, push_constants](vk::CommandBuffer cmdbuf) {
+ const VkImageSubresourceRange barrier_range{
+ .aspectMask = aspect_info.barrier_aspect,
+ .baseMipLevel = 0,
+ .levelCount = VK_REMAINING_MIP_LEVELS,
+ .baseArrayLayer = 0,
+ .layerCount = VK_REMAINING_ARRAY_LAYERS,
+ };
+ const std::array pre_barriers{
+ VkImageMemoryBarrier{
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = aspect_info.pre_src_access,
+ .dstAccessMask = aspect_info.pre_src_dst_access,
+ .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
+ .newLayout = VK_IMAGE_LAYOUT_GENERAL,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = src,
+ .subresourceRange = barrier_range,
+ },
+ VkImageMemoryBarrier{
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = aspect_info.pre_src_access,
+ .dstAccessMask = aspect_info.pre_dst_dst_access,
+ .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
+ .newLayout = VK_IMAGE_LAYOUT_GENERAL,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = dst,
+ .subresourceRange = barrier_range,
+ },
+ };
+ cmdbuf.PipelineBarrier(aspect_info.pre_src_stages, aspect_info.pre_dst_stages, 0,
+ nullptr, nullptr, pre_barriers);
+ const VkRenderPassBeginInfo renderpass_bi{
+ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
+ .pNext = nullptr,
+ .renderPass = renderpass,
+ .framebuffer = framebuffer_handle,
+ .renderArea = render_area,
+ .clearValueCount = 0,
+ .pClearValues = nullptr,
+ };
+ cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
+ VkDescriptorSet descriptor_set = VK_NULL_HANDLE;
+ if (src_stencil_handle != VK_NULL_HANDLE) {
+ descriptor_set = two_textures_descriptor_allocator.Commit();
+ UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_view_handle,
+ src_stencil_handle);
+ } else {
+ descriptor_set = one_texture_descriptor_allocator.Commit();
+ UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view_handle);
+ }
+ cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
+ cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
+ nullptr);
+ const VkViewport viewport{
+ .x = static_cast(render_area.offset.x),
+ .y = static_cast(render_area.offset.y),
+ .width = static_cast(render_area.extent.width),
+ .height = static_cast(render_area.extent.height),
+ .minDepth = 0.0f,
+ .maxDepth = 1.0f,
+ };
+ cmdbuf.SetViewport(0, viewport);
+ cmdbuf.SetScissor(0, render_area);
+ cmdbuf.PushConstants(layout, VK_SHADER_STAGE_FRAGMENT_BIT, push_constants);
+ cmdbuf.Draw(3, 1, 0, 0);
+ cmdbuf.EndRenderPass();
+ const VkImageMemoryBarrier post_barrier{
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = aspect_info.post_src_access,
+ .dstAccessMask = aspect_info.post_dst_access,
+ .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
+ .newLayout = VK_IMAGE_LAYOUT_GENERAL,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = dst,
+ .subresourceRange = barrier_range,
+ };
+ cmdbuf.PipelineBarrier(aspect_info.post_src_stages, aspect_info.post_dst_stages, 0,
+ post_barrier);
+ });
+ msaa_copy_resources.push_back(MSAACopyResources{
+ .tick = scheduler.CurrentTick(),
+ .src_view = std::move(src_view),
+ .dst_view = std::move(dst_view),
+ .framebuffer = std::move(framebuffer),
+ });
+ if (copy_stencil) {
+ msaa_copy_resources.push_back(MSAACopyResources{
+ .tick = scheduler.CurrentTick(),
+ .src_view = std::move(src_stencil_view),
+ .dst_view = vk::ImageView{},
+ .framebuffer = vk::Framebuffer{},
+ });
+ }
+ }
+ }
+ scheduler.InvalidateState();
+}
+
void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_image,
VideoCore::Surface::PixelFormat dst_format, VkImage src_image,
VideoCore::Surface::PixelFormat src_format, u32 num_samples,
std::span copies,
bool msaa_to_non_msaa) {
- while (!msaa_copy_resources.empty() && scheduler.IsFree(msaa_copy_resources.front().tick)) {
- msaa_copy_resources.pop_front();
- }
const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(static_cast(num_samples));
const s32 scale_x = 1 << samples_x;
const s32 scale_y = 1 << samples_y;
- const VkSampleCountFlagBits samples =
- msaa_to_non_msaa ? VK_SAMPLE_COUNT_1_BIT : SampleCountFlag(num_samples);
+ VkSampleCountFlagBits samples = SampleCountFlag(num_samples);
+ if (msaa_to_non_msaa) {
+ samples = VK_SAMPLE_COUNT_1_BIT;
+ }
RenderPassKey renderpass_key{};
renderpass_key.color_formats.fill(VideoCore::Surface::PixelFormat::Invalid);
renderpass_key.color_formats[0] = dst_format;
@@ -905,149 +1109,35 @@ void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_i
.renderpass = renderpass,
.samples = samples,
.msaa_to_non_msaa = msaa_to_non_msaa,
+ .format_class = FormatClass(dst_format),
+ };
+ const MSAACopyAspectInfo aspect_info{
+ .src_view_aspect = VK_IMAGE_ASPECT_COLOR_BIT,
+ .attachment_aspect = VK_IMAGE_ASPECT_COLOR_BIT,
+ .barrier_aspect = VK_IMAGE_ASPECT_COLOR_BIT,
+ .pre_src_access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_WRITE_BIT |
+ VK_ACCESS_TRANSFER_WRITE_BIT,
+ .pre_src_dst_access = VK_ACCESS_SHADER_READ_BIT,
+ .pre_dst_dst_access =
+ VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ .pre_src_stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
+ VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
+ VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT,
+ .pre_dst_stages =
+ VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+ .post_src_access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ .post_dst_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT,
+ .post_src_stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+ .post_dst_stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
+ VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT,
};
- const VkPipeline pipeline = FindOrEmplaceMSAACopyPipeline(key);
- const VkPipelineLayout layout = *msaa_copy_pipeline_layout;
- const VkSampler sampler = *nearest_sampler;
const VkFormat src_vk_format =
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, src_format).format;
const VkFormat dst_vk_format =
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, dst_format).format;
- for (const VideoCommon::ImageCopy& copy : copies) {
- ASSERT(copy.src_subresource.base_layer == 0);
- ASSERT(copy.src_subresource.num_layers == 1);
- ASSERT(copy.dst_subresource.base_layer == 0);
- ASSERT(copy.dst_subresource.num_layers == 1);
- vk::ImageView src_view =
- MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format,
- static_cast(copy.src_subresource.base_level));
- vk::ImageView dst_view =
- MakeMSAACopyView(device.GetLogical(), dst_image, dst_vk_format,
- static_cast(copy.dst_subresource.base_level));
- const VkOffset2D dst_offset{copy.dst_offset.x, copy.dst_offset.y};
- const VkExtent2D dst_extent{copy.extent.width, copy.extent.height};
- const VkRect2D render_area{
- .offset = dst_offset,
- .extent = dst_extent,
- };
- vk::Framebuffer framebuffer = device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{
- .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
- .pNext = nullptr,
- .flags = 0,
- .renderPass = renderpass,
- .attachmentCount = 1,
- .pAttachments = dst_view.address(),
- .width = static_cast(dst_offset.x) + dst_extent.width,
- .height = static_cast(dst_offset.y) + dst_extent.height,
- .layers = 1,
- });
- const MSAACopyPushConstants push_constants{
- .dst_offset = {dst_offset.x, dst_offset.y},
- .src_offset = {copy.src_offset.x, copy.src_offset.y},
- .scale = {scale_x, scale_y},
- };
- scheduler.RequestOutsideRenderPassOperationContext();
- scheduler.Record([this, pipeline, layout, sampler, renderpass,
- framebuffer_handle = *framebuffer, src_view_handle = *src_view,
- src = src_image, dst = dst_image, render_area,
- push_constants](vk::CommandBuffer cmdbuf) {
- constexpr VkImageSubresourceRange color_range{
- .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
- .baseMipLevel = 0,
- .levelCount = VK_REMAINING_MIP_LEVELS,
- .baseArrayLayer = 0,
- .layerCount = VK_REMAINING_ARRAY_LAYERS,
- };
- const std::array pre_barriers{
- VkImageMemoryBarrier{
- .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
- .pNext = nullptr,
- .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
- VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
- .dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
- .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
- .newLayout = VK_IMAGE_LAYOUT_GENERAL,
- .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
- .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
- .image = src,
- .subresourceRange = color_range,
- },
- VkImageMemoryBarrier{
- .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
- .pNext = nullptr,
- .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
- VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
- .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
- VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
- .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
- .newLayout = VK_IMAGE_LAYOUT_GENERAL,
- .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
- .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
- .image = dst,
- .subresourceRange = color_range,
- },
- };
- cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
- VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
- VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
- VK_PIPELINE_STAGE_TRANSFER_BIT,
- VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
- VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
- 0, nullptr, nullptr, pre_barriers);
- const VkRenderPassBeginInfo renderpass_bi{
- .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
- .pNext = nullptr,
- .renderPass = renderpass,
- .framebuffer = framebuffer_handle,
- .renderArea = render_area,
- .clearValueCount = 0,
- .pClearValues = nullptr,
- };
- cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
- const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
- UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view_handle);
- cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
- cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
- nullptr);
- const VkViewport viewport{
- .x = static_cast(render_area.offset.x),
- .y = static_cast(render_area.offset.y),
- .width = static_cast(render_area.extent.width),
- .height = static_cast(render_area.extent.height),
- .minDepth = 0.0f,
- .maxDepth = 1.0f,
- };
- cmdbuf.SetViewport(0, viewport);
- cmdbuf.SetScissor(0, render_area);
- cmdbuf.PushConstants(layout, VK_SHADER_STAGE_FRAGMENT_BIT, push_constants);
- cmdbuf.Draw(3, 1, 0, 0);
- cmdbuf.EndRenderPass();
- const VkImageMemoryBarrier post_barrier{
- .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
- .pNext = nullptr,
- .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
- .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT,
- .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
- .newLayout = VK_IMAGE_LAYOUT_GENERAL,
- .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
- .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
- .image = dst,
- .subresourceRange = color_range,
- };
- cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
- VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
- VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
- VK_PIPELINE_STAGE_TRANSFER_BIT,
- 0, post_barrier);
- });
- msaa_copy_resources.push_back(MSAACopyResources{
- .tick = scheduler.CurrentTick(),
- .src_view = std::move(src_view),
- .dst_view = std::move(dst_view),
- .framebuffer = std::move(framebuffer),
- });
- }
- scheduler.InvalidateState();
+ CopyMSAAImpl(renderpass, FindOrEmplaceMSAACopyPipeline(key), *msaa_copy_pipeline_layout,
+ dst_image, dst_vk_format, src_image, src_vk_format, scale_x, scale_y, copies,
+ aspect_info, false);
}
void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
@@ -1191,7 +1281,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKe
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
- }));
+ }, device.StaticPipelineCache()));
return *blit_color_pipelines.back();
}
@@ -1223,7 +1313,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePip
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
- }));
+ }, device.StaticPipelineCache()));
return *blit_depth_stencil_pipelines.back();
}
@@ -1276,7 +1366,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipel
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
- }));
+ }, device.StaticPipelineCache()));
return *clear_color_pipelines.back();
}
@@ -1332,7 +1422,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline(
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
- }));
+ }, device.StaticPipelineCache()));
return *clear_stencil_pipelines.back();
}
@@ -1375,10 +1465,91 @@ VkPipeline BlitImageHelper::FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPip
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
- }));
+ }, device.StaticPipelineCache()));
return *blit_msaa_color_pipelines.back();
}
+VkPipeline BlitImageHelper::FindOrEmplaceBlitDepthStencilMSAAPipeline(
+ const BlitMSAAPipelineKey& key, bool blit_stencil) {
+ auto& keys = blit_stencil ? blit_msaa_depth_stencil_keys : blit_msaa_depth_keys;
+ auto& pipelines = blit_stencil ? blit_msaa_depth_stencil_pipelines : blit_msaa_depth_pipelines;
+ const auto it = std::ranges::find(keys, key);
+ if (it != keys.end()) {
+ return *pipelines[std::distance(keys.begin(), it)];
+ }
+ keys.push_back(key);
+ const std::array stages =
+ MakeStages(*full_screen_vert,
+ blit_stencil ? *blit_depth_stencil_msaa_frag : *blit_depth_msaa_frag);
+ const VkPipelineMultisampleStateCreateInfo multisample_ci{
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = 0,
+ .rasterizationSamples = key.samples,
+ .sampleShadingEnable = VK_TRUE,
+ .minSampleShading = 1.0f,
+ .pSampleMask = nullptr,
+ .alphaToCoverageEnable = VK_FALSE,
+ .alphaToOneEnable = VK_FALSE,
+ };
+ const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
+ pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
+ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = 0,
+ .stageCount = static_cast(stages.size()),
+ .pStages = stages.data(),
+ .pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
+ .pInputAssemblyState = &input_assembly_ci,
+ .pTessellationState = nullptr,
+ .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
+ .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
+ .pMultisampleState = &multisample_ci,
+ .pDepthStencilState = blit_stencil ? &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO
+ : &PIPELINE_DEPTH_ONLY_STATE_CREATE_INFO,
+ .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO,
+ .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
+ .layout = blit_stencil ? *two_textures_pipeline_layout : *one_texture_pipeline_layout,
+ .renderPass = key.renderpass,
+ .subpass = 0,
+ .basePipelineHandle = VK_NULL_HANDLE,
+ .basePipelineIndex = 0,
+ }, device.StaticPipelineCache()));
+ return *pipelines.back();
+}
+
+VkPipeline BlitImageHelper::FindOrEmplaceBlitDepthPipeline(VkRenderPass renderpass) {
+ const auto it = std::ranges::find(blit_depth_keys, renderpass);
+ if (it != blit_depth_keys.end()) {
+ return *blit_depth_pipelines[std::distance(blit_depth_keys.begin(), it)];
+ }
+ blit_depth_keys.push_back(renderpass);
+ const std::array stages = MakeStages(*full_screen_vert, *blit_depth_frag);
+ const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
+ blit_depth_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
+ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = 0,
+ .stageCount = static_cast(stages.size()),
+ .pStages = stages.data(),
+ .pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
+ .pInputAssemblyState = &input_assembly_ci,
+ .pTessellationState = nullptr,
+ .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
+ .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
+ .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
+ .pDepthStencilState = &PIPELINE_DEPTH_ONLY_STATE_CREATE_INFO,
+ .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO,
+ .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
+ .layout = *one_texture_pipeline_layout,
+ .renderPass = renderpass,
+ .subpass = 0,
+ .basePipelineHandle = VK_NULL_HANDLE,
+ .basePipelineIndex = 0,
+ }, device.StaticPipelineCache()));
+ return *blit_depth_pipelines.back();
+}
+
VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass,
bool resolve_stencil) {
auto& keys = resolve_stencil ? resolve_depth_stencil_keys : resolve_depth_keys;
@@ -1413,19 +1584,87 @@ VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline(VkRenderPas
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
- }));
+ }, device.StaticPipelineCache()));
return *pipelines.back();
}
+void BlitImageHelper::CopyMSAADepth(RenderPassCache& render_pass_cache, VkImage dst_image,
+ VideoCore::Surface::PixelFormat dst_format, VkImage src_image,
+ VideoCore::Surface::PixelFormat src_format, u32 num_samples,
+ std::span copies,
+ bool copy_stencil, bool msaa_to_non_msaa) {
+ const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(static_cast(num_samples));
+ const s32 scale_x = 1 << samples_x;
+ const s32 scale_y = 1 << samples_y;
+ VkSampleCountFlagBits samples = SampleCountFlag(num_samples);
+ if (msaa_to_non_msaa) {
+ samples = VK_SAMPLE_COUNT_1_BIT;
+ }
+ RenderPassKey renderpass_key{};
+ renderpass_key.color_formats.fill(VideoCore::Surface::PixelFormat::Invalid);
+ renderpass_key.depth_format = dst_format;
+ renderpass_key.samples = samples;
+ const VkRenderPass renderpass = render_pass_cache.Get(renderpass_key);
+ const MSAACopyPipelineKey key{
+ .renderpass = renderpass,
+ .samples = samples,
+ .msaa_to_non_msaa = msaa_to_non_msaa,
+ .format_class = MSAACopyFormatClass::Float,
+ };
+ VkImageAspectFlags attachment_aspect = VK_IMAGE_ASPECT_DEPTH_BIT;
+ if (VideoCore::Surface::GetFormatType(dst_format) ==
+ VideoCore::Surface::SurfaceType::DepthStencil) {
+ attachment_aspect |= VK_IMAGE_ASPECT_STENCIL_BIT;
+ }
+ VkPipelineLayout layout = *msaa_copy_pipeline_layout;
+ if (copy_stencil) {
+ layout = *msaa_copy_depth_stencil_pipeline_layout;
+ }
+ const MSAACopyAspectInfo aspect_info{
+ .src_view_aspect = VK_IMAGE_ASPECT_DEPTH_BIT,
+ .attachment_aspect = attachment_aspect,
+ .barrier_aspect = attachment_aspect,
+ .pre_src_access =
+ VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
+ .pre_src_dst_access = VK_ACCESS_SHADER_READ_BIT,
+ .pre_dst_dst_access = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
+ VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
+ .pre_src_stages = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
+ VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
+ VK_PIPELINE_STAGE_TRANSFER_BIT,
+ .pre_dst_stages =
+ VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT,
+ .post_src_access = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
+ .post_dst_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT |
+ VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT,
+ .post_src_stages = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
+ .post_dst_stages = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER,
+ };
+ const VkFormat src_vk_format =
+ MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, src_format).format;
+ const VkFormat dst_vk_format =
+ MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, dst_format).format;
+ CopyMSAAImpl(renderpass, FindOrEmplaceMSAACopyDepthPipeline(key, copy_stencil), layout,
+ dst_image, dst_vk_format, src_image, src_vk_format, scale_x, scale_y, copies,
+ aspect_info, copy_stencil);
+}
+
VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipelineKey& key) {
const auto it = std::ranges::find(msaa_copy_keys, key);
if (it != msaa_copy_keys.end()) {
return *msaa_copy_pipelines[std::distance(msaa_copy_keys.begin(), it)];
}
msaa_copy_keys.push_back(key);
- const std::array stages = MakeStages(*clear_color_vert, key.msaa_to_non_msaa
- ? *convert_msaa_to_non_msaa_frag
- : *convert_non_msaa_to_msaa_frag);
+ VkShaderModule frag_module = key.msaa_to_non_msaa ? *convert_msaa_to_non_msaa_frag
+ : *convert_non_msaa_to_msaa_frag;
+ if (key.format_class == MSAACopyFormatClass::SignedInteger) {
+ frag_module = key.msaa_to_non_msaa ? *convert_msaa_to_non_msaa_sint_frag
+ : *convert_non_msaa_to_msaa_sint_frag;
+ } else if (key.format_class == MSAACopyFormatClass::UnsignedInteger) {
+ frag_module = key.msaa_to_non_msaa ? *convert_msaa_to_non_msaa_uint_frag
+ : *convert_non_msaa_to_msaa_uint_frag;
+ }
+ const std::array stages = MakeStages(*clear_color_vert, frag_module);
const VkPipelineMultisampleStateCreateInfo multisample_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.pNext = nullptr,
@@ -1458,10 +1697,89 @@ VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipeline
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
- }));
+ }, device.StaticPipelineCache()));
return *msaa_copy_pipelines.back();
}
+VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyDepthPipeline(const MSAACopyPipelineKey& key,
+ bool copy_stencil) {
+ auto& keys = copy_stencil ? msaa_copy_depth_stencil_keys : msaa_copy_depth_keys;
+ auto& pipelines = copy_stencil ? msaa_copy_depth_stencil_pipelines : msaa_copy_depth_pipelines;
+ const auto it = std::ranges::find(keys, key);
+ if (it != keys.end()) {
+ return *pipelines[std::distance(keys.begin(), it)];
+ }
+ keys.push_back(key);
+ VkShaderModule frag_module;
+ if (key.msaa_to_non_msaa) {
+ frag_module = copy_stencil ? *convert_msaa_to_non_msaa_depth_stencil_frag
+ : *convert_msaa_to_non_msaa_depth_frag;
+ } else {
+ frag_module = copy_stencil ? *convert_non_msaa_to_msaa_depth_stencil_frag
+ : *convert_non_msaa_to_msaa_depth_frag;
+ }
+ const std::array stages = MakeStages(*clear_color_vert, frag_module);
+ const VkPipelineMultisampleStateCreateInfo multisample_ci{
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = 0,
+ .rasterizationSamples = key.samples,
+ .sampleShadingEnable = key.msaa_to_non_msaa ? VK_FALSE : VK_TRUE,
+ .minSampleShading = key.msaa_to_non_msaa ? 0.0f : 1.0f,
+ .pSampleMask = nullptr,
+ .alphaToCoverageEnable = VK_FALSE,
+ .alphaToOneEnable = VK_FALSE,
+ };
+ static constexpr VkStencilOpState REPLACE_STENCIL_OP{
+ .failOp = VK_STENCIL_OP_REPLACE,
+ .passOp = VK_STENCIL_OP_REPLACE,
+ .depthFailOp = VK_STENCIL_OP_REPLACE,
+ .compareOp = VK_COMPARE_OP_ALWAYS,
+ .compareMask = 0xFF,
+ .writeMask = 0xFF,
+ .reference = 0,
+ };
+ const VkPipelineDepthStencilStateCreateInfo depth_stencil_ci{
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = 0,
+ .depthTestEnable = VK_TRUE,
+ .depthWriteEnable = VK_TRUE,
+ .depthCompareOp = VK_COMPARE_OP_ALWAYS,
+ .depthBoundsTestEnable = VK_FALSE,
+ .stencilTestEnable = copy_stencil ? VK_TRUE : VK_FALSE,
+ .front = copy_stencil ? REPLACE_STENCIL_OP : VkStencilOpState{},
+ .back = copy_stencil ? REPLACE_STENCIL_OP : VkStencilOpState{},
+ .minDepthBounds = 0.0f,
+ .maxDepthBounds = 0.0f,
+ };
+ const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci =
+ GetPipelineInputAssemblyStateCreateInfo(device);
+ pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
+ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = 0,
+ .stageCount = static_cast(stages.size()),
+ .pStages = stages.data(),
+ .pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
+ .pInputAssemblyState = &input_assembly_ci,
+ .pTessellationState = nullptr,
+ .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
+ .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
+ .pMultisampleState = &multisample_ci,
+ .pDepthStencilState = &depth_stencil_ci,
+ .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO,
+ .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
+ .layout = copy_stencil ? *msaa_copy_depth_stencil_pipeline_layout
+ : *msaa_copy_pipeline_layout,
+ .renderPass = key.renderpass,
+ .subpass = 0,
+ .basePipelineHandle = VK_NULL_HANDLE,
+ .basePipelineIndex = 0,
+ }, device.StaticPipelineCache()));
+ return *pipelines.back();
+}
+
void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) {
ConvertPipeline(pipeline, renderpass, false);
}
@@ -1499,7 +1817,7 @@ void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass ren
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
- });
+ }, device.StaticPipelineCache());
}
void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
@@ -1542,7 +1860,7 @@ void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass rende
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
- });
+ }, device.StaticPipelineCache());
}
} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h
index 67294d594b..62e6743a23 100644
--- a/src/video_core/renderer_vulkan/blit_image.h
+++ b/src/video_core/renderer_vulkan/blit_image.h
@@ -45,12 +45,19 @@ struct BlitDepthStencilPipelineKey {
u32 stencil_ref;
};
+enum class MSAACopyFormatClass : u32 {
+ Float,
+ SignedInteger,
+ UnsignedInteger,
+};
+
struct MSAACopyPipelineKey {
constexpr auto operator<=>(const MSAACopyPipelineKey&) const noexcept = default;
VkRenderPass renderpass;
VkSampleCountFlagBits samples;
bool msaa_to_non_msaa;
+ MSAACopyFormatClass format_class;
};
struct BlitMSAAPipelineKey {
@@ -78,6 +85,12 @@ public:
void BlitColorMSAA(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
const Region2D& dst_region, const Region2D& src_region);
+ void BlitDepthStencilMSAA(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
+ const Region2D& dst_region, const Region2D& src_region);
+
+ void BlitDepth(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
+ const Region2D& dst_region, const Region2D& src_region);
+
void ResolveDepthStencil(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
const Region2D& dst_region, const Region2D& src_region);
@@ -116,7 +129,39 @@ public:
VideoCore::Surface::PixelFormat src_format, u32 num_samples,
std::span copies, bool msaa_to_non_msaa);
+ void CopyMSAADepth(RenderPassCache& render_pass_cache, VkImage dst_image,
+ VideoCore::Surface::PixelFormat dst_format, VkImage src_image,
+ VideoCore::Surface::PixelFormat src_format, u32 num_samples,
+ std::span copies, bool copy_stencil,
+ bool msaa_to_non_msaa);
+
private:
+ struct MSAACopyAspectInfo {
+ VkImageAspectFlags src_view_aspect;
+ VkImageAspectFlags attachment_aspect;
+ VkImageAspectFlags barrier_aspect;
+ VkAccessFlags pre_src_access;
+ VkAccessFlags pre_src_dst_access;
+ VkAccessFlags pre_dst_dst_access;
+ VkPipelineStageFlags pre_src_stages;
+ VkPipelineStageFlags pre_dst_stages;
+ VkAccessFlags post_src_access;
+ VkAccessFlags post_dst_access;
+ VkPipelineStageFlags post_src_stages;
+ VkPipelineStageFlags post_dst_stages;
+ };
+
+ void BlitImpl(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
+ const Region2D& dst_region, const Region2D& src_region, VkPipeline pipeline,
+ VkSampler sampler, VkImageView src_view, VkImageView src_stencil_view,
+ bool blit_stencil);
+
+ void CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline, VkPipelineLayout layout,
+ VkImage dst_image, VkFormat dst_vk_format, VkImage src_image,
+ VkFormat src_vk_format, s32 scale_x, s32 scale_y,
+ std::span copies,
+ const MSAACopyAspectInfo& aspect_info, bool copy_stencil);
+
void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
const ImageView& src_image_view);
@@ -131,7 +176,13 @@ private:
[[nodiscard]] VkPipeline FindOrEmplaceClearStencilPipeline(
const BlitDepthStencilPipelineKey& key);
[[nodiscard]] VkPipeline FindOrEmplaceMSAACopyPipeline(const MSAACopyPipelineKey& key);
+
+ [[nodiscard]] VkPipeline FindOrEmplaceMSAACopyDepthPipeline(const MSAACopyPipelineKey& key,
+ bool copy_stencil);
[[nodiscard]] VkPipeline FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPipelineKey& key);
+ [[nodiscard]] VkPipeline FindOrEmplaceBlitDepthStencilMSAAPipeline(
+ const BlitMSAAPipelineKey& key, bool blit_stencil);
+ [[nodiscard]] VkPipeline FindOrEmplaceBlitDepthPipeline(VkRenderPass renderpass);
[[nodiscard]] VkPipeline FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass,
bool resolve_stencil);
@@ -162,10 +213,12 @@ private:
vk::PipelineLayout two_textures_pipeline_layout;
vk::PipelineLayout clear_color_pipeline_layout;
vk::PipelineLayout msaa_copy_pipeline_layout;
+ vk::PipelineLayout msaa_copy_depth_stencil_pipeline_layout;
vk::ShaderModule full_screen_vert;
vk::ShaderModule blit_color_to_color_frag;
vk::ShaderModule blit_color_msaa_frag;
vk::ShaderModule blit_depth_stencil_frag;
+ vk::ShaderModule blit_depth_frag;
vk::ShaderModule blit_depth_msaa_frag;
vk::ShaderModule blit_depth_stencil_msaa_frag;
vk::ShaderModule clear_color_vert;
@@ -179,7 +232,15 @@ private:
vk::ShaderModule convert_d24s8_to_abgr8_frag;
vk::ShaderModule convert_s8d24_to_abgr8_frag;
vk::ShaderModule convert_msaa_to_non_msaa_frag;
+ vk::ShaderModule convert_msaa_to_non_msaa_sint_frag;
+ vk::ShaderModule convert_msaa_to_non_msaa_uint_frag;
+ vk::ShaderModule convert_msaa_to_non_msaa_depth_frag;
+ vk::ShaderModule convert_msaa_to_non_msaa_depth_stencil_frag;
vk::ShaderModule convert_non_msaa_to_msaa_frag;
+ vk::ShaderModule convert_non_msaa_to_msaa_sint_frag;
+ vk::ShaderModule convert_non_msaa_to_msaa_uint_frag;
+ vk::ShaderModule convert_non_msaa_to_msaa_depth_frag;
+ vk::ShaderModule convert_non_msaa_to_msaa_depth_stencil_frag;
vk::Sampler linear_sampler;
vk::Sampler nearest_sampler;
@@ -193,8 +254,18 @@ private:
std::vector clear_stencil_pipelines;
std::vector msaa_copy_keys;
std::vector msaa_copy_pipelines;
+ std::vector msaa_copy_depth_keys;
+ std::vector msaa_copy_depth_pipelines;
+ std::vector msaa_copy_depth_stencil_keys;
+ std::vector msaa_copy_depth_stencil_pipelines;
std::vector blit_msaa_color_keys;
std::vector blit_msaa_color_pipelines;
+ std::vector blit_depth_keys;
+ std::vector blit_depth_pipelines;
+ std::vector blit_msaa_depth_keys;
+ std::vector blit_msaa_depth_pipelines;
+ std::vector blit_msaa_depth_stencil_keys;
+ std::vector blit_msaa_depth_stencil_pipelines;
std::vector resolve_depth_keys;
std::vector resolve_depth_pipelines;
std::vector resolve_depth_stencil_keys;
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
index 0ced890929..3eb9bf841a 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
@@ -141,6 +141,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
depth_enabled.Assign(regs.zeta_enable != 0 ? 1 : 0);
depth_format.Assign(static_cast(regs.zeta.format));
y_negate.Assign(regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft ? 1 : 0);
+ viewport_scale_offset_enabled.Assign(regs.viewport_scale_offset_enabled != 0);
bool use_last_provoking_vertex = false;
const bool provoking_vertex_available = features.has_provoking_vertex;
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
index 36a4b4b7a2..27710ab806 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
@@ -231,6 +231,7 @@ struct FixedPipelineState {
BitField<15, 1, u32> alpha_to_coverage_enabled;
BitField<16, 1, u32> alpha_to_one_enabled;
BitField<17, 3, Tegra::Engines::Maxwell3D::EngineHint> app_stage;
+ BitField<20, 1, u32> viewport_scale_offset_enabled;
};
std::array color_formats;
diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h
index 051305ec42..481a4d8181 100644
--- a/src/video_core/renderer_vulkan/pipeline_helper.h
+++ b/src/video_core/renderer_vulkan/pipeline_helper.h
@@ -370,7 +370,7 @@ inline void PushImageDescriptors(TextureCache& texture_cache,
const VkImageView null_image_view{texture_cache.GetImageView(VideoCommon::NULL_IMAGE_VIEW_ID).Handle(desc.type)};
if (null_image_view != VK_NULL_HANDLE) vk_image_view = null_image_view;
}
- const Sampler& sampler{texture_cache.GetSampler(sampler_id)};
+ Sampler& sampler{texture_cache.GetSampler(sampler_id)};
const bool use_fallback_sampler{sampler.HasAddedAnisotropy() &&
!image_view.SupportsAnisotropy()};
VkSampler vk_sampler{use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy()
@@ -383,6 +383,19 @@ inline void PushImageDescriptors(TextureCache& texture_cache,
!image_view.SupportsDepthComparison()) {
vk_sampler = sampler.HandleWithoutDepthComparison();
}
+ const bool srgb_border{sampler.HasSrgbBorderColor() &&
+ VideoCore::Surface::IsPixelFormatSRGB(image_view.format)};
+ if (sampler.NeedsSwizzleMapping() && !image_view.HasIdentitySwizzle()) {
+ vk_sampler = sampler.HandleWithSwizzle(image_view.Swizzle(), srgb_border);
+ } else if (srgb_border) {
+ vk_sampler = sampler.HandleWithSrgbBorderColor();
+ }
+ if (sampler.HasMinmaxReduction() && !image_view.SupportsMinmaxFilter()) {
+ vk_sampler = sampler.HandleWithDefaultReduction();
+ }
+ if (sampler.HasCustomBorderColor() && image_view.RequiresBorderColorFormat()) {
+ vk_sampler = sampler.HandleWithDefaultBorderColor();
+ }
guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler);
const bool element_rescaled{texture_cache.IsRescaling(image_view)};
is_rescaled |= element_rescaled;
diff --git a/src/video_core/renderer_vulkan/present/sgsr.cpp b/src/video_core/renderer_vulkan/present/sgsr.cpp
index 5fc746458d..fa37225e43 100644
--- a/src/video_core/renderer_vulkan/present/sgsr.cpp
+++ b/src/video_core/renderer_vulkan/present/sgsr.cpp
@@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
+#include
+
#include "common/common_types.h"
#include "common/div_ceil.h"
#include "common/settings.h"
@@ -17,7 +19,7 @@
namespace Vulkan {
-using PushConstants = std::array;
+using PushConstants = std::array;
SGSR::SGSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, VkExtent2D extent, bool edge_dir)
: m_memory_allocator{memory_allocator}
@@ -100,26 +102,28 @@ VkImageView SGSR::Draw(const Device& device, Scheduler& scheduler, size_t image_
const f32 input_image_width = f32(input_image_extent.width);
const f32 input_image_height = f32(input_image_extent.height);
- const f32 viewport_width = (crop_rect.right - crop_rect.left) * input_image_width;
- const f32 viewport_height = (crop_rect.bottom - crop_rect.top) * input_image_height;
- // expected [0, 2]
- const f32 sharpening = f32(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f;
+ const f32 crop_width = (crop_rect.right - crop_rect.left) * input_image_width;
+ const f32 crop_height = (crop_rect.bottom - crop_rect.top) * input_image_height;
+ static constexpr f32 EDGE_SHARPNESS_MAX = 2.0f;
+ const f32 edge_sharpness =
+ EDGE_SHARPNESS_MAX - f32(Settings::values.fsr_sharpening_slider.GetValue()) / 200.0f;
- // p = (tex * viewport) / input = [0,n] (normalized texcoords)
- // p * input = [0,1024], [0,768]
// layout( push_constant ) uniform constants {
// highp vec4 ViewportInfo[1];
// highp vec2 ResizeFactor;
+ // highp vec2 CropOffset;
// highp float EdgeSharpness;
// };
PushConstants viewport_con{};
- viewport_con[0] = std::bit_cast(std::abs(1.f / viewport_width));
- viewport_con[1] = std::bit_cast(std::abs(1.f / viewport_height));
- viewport_con[2] = std::bit_cast(std::abs(viewport_width));
- viewport_con[3] = std::bit_cast(std::abs(viewport_height));
- viewport_con[4] = std::bit_cast(viewport_width / input_image_width);
- viewport_con[5] = std::bit_cast(viewport_height / input_image_height);
- viewport_con[6] = std::bit_cast(sharpening);
+ viewport_con[0] = std::bit_cast(1.f / input_image_width);
+ viewport_con[1] = std::bit_cast(1.f / input_image_height);
+ viewport_con[2] = std::bit_cast(input_image_width);
+ viewport_con[3] = std::bit_cast(input_image_height);
+ viewport_con[4] = std::bit_cast(crop_width / input_image_width);
+ viewport_con[5] = std::bit_cast(crop_height / input_image_height);
+ viewport_con[6] = std::bit_cast((std::min)(crop_rect.left, crop_rect.right));
+ viewport_con[7] = std::bit_cast((std::min)(crop_rect.top, crop_rect.bottom));
+ viewport_con[8] = std::bit_cast(edge_sharpness);
UploadImages(device, scheduler);
UpdateDescriptorSets(device, source_image_view, image_index);
diff --git a/src/video_core/renderer_vulkan/present/util.cpp b/src/video_core/renderer_vulkan/present/util.cpp
index 11250687d6..2ea7870f01 100644
--- a/src/video_core/renderer_vulkan/present/util.cpp
+++ b/src/video_core/renderer_vulkan/present/util.cpp
@@ -521,7 +521,7 @@ static vk::Pipeline CreateWrappedPipelineImpl(
.subpass = 0,
.basePipelineHandle = 0,
.basePipelineIndex = 0,
- });
+ }, device.StaticPipelineCache());
}
vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass,
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
index 80c434dc8f..f5c0c50768 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
@@ -22,7 +22,9 @@
#include "video_core/host_shaders/resolve_conditional_render_comp_spv.h"
#include "video_core/host_shaders/vulkan_quad_indexed_comp_spv.h"
#include "video_core/host_shaders/vulkan_uint8_comp_spv.h"
+#include "video_core/host_shaders/block_linear_unswizzle_2d_buffer_comp_spv.h"
#include "video_core/host_shaders/block_linear_unswizzle_3d_bcn_comp_spv.h"
+#include "video_core/host_shaders/block_linear_unswizzle_3d_buffer_comp_spv.h"
#include "video_core/renderer_vulkan/vk_compute_pass.h"
#include "video_core/surface.h"
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
@@ -268,7 +270,7 @@ ComputePass::ComputePass(const Device& device_, Scheduler& scheduler, Descriptor
.layout = *layout,
.basePipelineHandle = {},
.basePipelineIndex = 0,
- });
+ }, device.StaticPipelineCache());
}
ComputePass::~ComputePass() = default;
@@ -613,7 +615,6 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, image_barrier);
});
- scheduler.Finish();
}
constexpr u32 BL3D_BINDING_INPUT_BUFFER = 0;
@@ -813,7 +814,6 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk(
.size = barrier_size,
};
- // Image layout transition
const VkImageMemoryBarrier pre_barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
@@ -872,4 +872,493 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk(
});
}
+namespace {
+constexpr u32 BL2D_BINDING_INPUT_BUFFER = 0;
+constexpr u32 BL2D_BINDING_OUTPUT_BUFFER = 1;
+
+struct alignas(16) BlockLinearUnswizzle2DPushConstants {
+ std::array dim;
+ u32 bytes_per_block_log2;
+ std::array origin;
+ u32 layer_stride;
+ u32 block_size;
+ u32 x_shift;
+ u32 block_height;
+ u32 block_height_mask;
+};
+static_assert(sizeof(BlockLinearUnswizzle2DPushConstants) <= 128);
+
+constexpr std::array BL2D_BINDINGS{{
+ {
+ .binding = BL2D_BINDING_INPUT_BUFFER,
+ .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+ .descriptorCount = 1,
+ .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
+ .pImmutableSamplers = nullptr,
+ },
+ {
+ .binding = BL2D_BINDING_OUTPUT_BUFFER,
+ .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+ .descriptorCount = 1,
+ .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
+ .pImmutableSamplers = nullptr,
+ },
+}};
+
+constexpr std::array BL2D_TEMPLATE{{
+ {
+ .dstBinding = BL2D_BINDING_INPUT_BUFFER,
+ .dstArrayElement = 0,
+ .descriptorCount = 1,
+ .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+ .offset = BL2D_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry),
+ .stride = sizeof(DescriptorUpdateEntry),
+ },
+ {
+ .dstBinding = BL2D_BINDING_OUTPUT_BUFFER,
+ .dstArrayElement = 0,
+ .descriptorCount = 1,
+ .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+ .offset = BL2D_BINDING_OUTPUT_BUFFER * sizeof(DescriptorUpdateEntry),
+ .stride = sizeof(DescriptorUpdateEntry),
+ },
+}};
+
+constexpr DescriptorBankInfo BL2D_BANK_INFO{
+ .uniform_buffers = 0,
+ .storage_buffers = 2,
+ .texture_buffers = 0,
+ .image_buffers = 0,
+ .textures = 0,
+ .images = 0,
+ .score = 2,
+};
+} // Anonymous namespace
+
+BlockLinearUnswizzle2DPass::BlockLinearUnswizzle2DPass(
+ const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
+ StagingBufferPool& staging_buffer_pool_,
+ ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
+ : ComputePass(device_, scheduler_, descriptor_pool_, BL2D_BINDINGS, BL2D_TEMPLATE,
+ BL2D_BANK_INFO,
+ COMPUTE_PUSH_CONSTANT_RANGE,
+ BLOCK_LINEAR_UNSWIZZLE_2D_BUFFER_COMP_SPV),
+ scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_},
+ compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
+
+BlockLinearUnswizzle2DPass::~BlockLinearUnswizzle2DPass() = default;
+
+bool BlockLinearUnswizzle2DPass::IsSupported(const VideoCommon::ImageInfo& info) {
+ if (info.type != VideoCommon::ImageType::e2D) {
+ return false;
+ }
+ if (info.resources.levels != 1 || info.resources.layers != 1) {
+ return false;
+ }
+ if (info.num_samples > 1) {
+ return false;
+ }
+ if (VideoCore::Surface::IsPixelFormatASTC(info.format) ||
+ VideoCore::Surface::IsPixelFormatBCn(info.format)) {
+ return false;
+ }
+ if (info.format >= VideoCore::Surface::PixelFormat::MaxColorFormat) {
+ return false;
+ }
+ const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(info.format);
+ if (bytes_per_block != 4 && bytes_per_block != 8 && bytes_per_block != 16) {
+ return false;
+ }
+ return VideoCore::Surface::DefaultBlockWidth(info.format) == 1 &&
+ VideoCore::Surface::DefaultBlockHeight(info.format) == 1;
+}
+
+void BlockLinearUnswizzle2DPass::Unswizzle(
+ Image& image, const StagingBufferRef& swizzled,
+ std::span swizzles) {
+ if (swizzles.empty()) {
+ return;
+ }
+ const VideoCommon::SwizzleParameters& sw = swizzles.front();
+ const auto params = VideoCommon::Accelerated::MakeBlockLinearSwizzle2DParams(sw, image.info);
+
+ const u32 width = sw.num_tiles.width;
+ const u32 height = sw.num_tiles.height;
+ const u32 depth = image.info.resources.layers;
+ const u32 bytes_per_block = 1u << params.bytes_per_block_log2;
+ const VkDeviceSize output_size =
+ static_cast(width) * height * depth * bytes_per_block;
+
+ const StagingBufferRef output =
+ staging_buffer_pool.Request(static_cast(output_size), MemoryUsage::DeviceLocal);
+
+ BlockLinearUnswizzle2DPushConstants pc{};
+ pc.dim = {width, height, depth};
+ pc.bytes_per_block_log2 = params.bytes_per_block_log2;
+ pc.origin = params.origin;
+ pc.layer_stride = params.layer_stride;
+ pc.block_size = params.block_size;
+ pc.x_shift = params.x_shift;
+ pc.block_height = params.block_height;
+ pc.block_height_mask = params.block_height_mask;
+
+ scheduler.RequestOutsideRenderPassOperationContext();
+
+ compute_pass_descriptor_queue.Acquire(scheduler, 2);
+ compute_pass_descriptor_queue.AddBuffer(swizzled.buffer, sw.buffer_offset + swizzled.offset,
+ image.guest_size_bytes - sw.buffer_offset);
+ compute_pass_descriptor_queue.AddBuffer(output.buffer, output.offset, output_size);
+
+ const void* descriptor_data = compute_pass_descriptor_queue.UpdateData();
+ const VkDescriptorSet set = descriptor_allocator.Commit();
+
+ const u32 gx = Common::DivCeil(width, 16u);
+ const u32 gy = Common::DivCeil(height, 8u);
+ const bool is_initialized = image.ExchangeInitialization();
+
+ const VkBuffer out_buffer = output.buffer;
+ const VkDeviceSize out_offset = output.offset;
+ const VkImage dst_image = image.Handle();
+ const VkImageAspectFlags aspect = image.AspectMask();
+
+ scheduler.Record([this, set, descriptor_data, pc, gx, gy, depth, output_size, out_buffer,
+ out_offset, dst_image, aspect, width, height,
+ is_initialized](vk::CommandBuffer cmdbuf) {
+ if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) {
+ return;
+ }
+ device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
+ cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
+ cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
+ cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(pc), &pc);
+ cmdbuf.Dispatch(gx, gy, depth);
+
+ const VkBufferMemoryBarrier buffer_barrier{
+ .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
+ .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .buffer = out_buffer,
+ .offset = out_offset,
+ .size = output_size,
+ };
+ const VkImageMemoryBarrier pre_copy{
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = static_cast(is_initialized ? VK_ACCESS_SHADER_READ_BIT
+ : VK_ACCESS_NONE),
+ .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
+ .oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED,
+ .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = dst_image,
+ .subresourceRange{
+ .aspectMask = aspect,
+ .baseMipLevel = 0,
+ .levelCount = VK_REMAINING_MIP_LEVELS,
+ .baseArrayLayer = 0,
+ .layerCount = VK_REMAINING_ARRAY_LAYERS,
+ },
+ };
+ VkPipelineStageFlags pre_copy_src_stages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
+ if (is_initialized) {
+ pre_copy_src_stages |= vk::PIPELINE_STAGE_GRAPHICS_COMPUTE;
+ } else {
+ pre_copy_src_stages |= VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+ }
+ cmdbuf.PipelineBarrier(pre_copy_src_stages, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {},
+ buffer_barrier, pre_copy);
+
+ const VkBufferImageCopy copy{
+ .bufferOffset = out_offset,
+ .bufferRowLength = 0,
+ .bufferImageHeight = 0,
+ .imageSubresource{
+ .aspectMask = aspect,
+ .mipLevel = 0,
+ .baseArrayLayer = 0,
+ .layerCount = depth,
+ },
+ .imageOffset = {0, 0, 0},
+ .imageExtent = {width, height, 1},
+ };
+ cmdbuf.CopyBufferToImage(out_buffer, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy);
+
+ const VkImageMemoryBarrier post_copy{
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
+ .dstAccessMask = VK_ACCESS_SHADER_READ_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{
+ .aspectMask = aspect,
+ .baseMipLevel = 0,
+ .levelCount = VK_REMAINING_MIP_LEVELS,
+ .baseArrayLayer = 0,
+ .layerCount = VK_REMAINING_ARRAY_LAYERS,
+ },
+ };
+ cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE,
+ 0, {}, {}, post_copy);
+ });
+}
+
+namespace {
+constexpr u32 BL3DB_BINDING_INPUT_BUFFER = 0;
+constexpr u32 BL3DB_BINDING_OUTPUT_BUFFER = 1;
+
+struct alignas(16) BlockLinearUnswizzle3DBufferPushConstants {
+ std::array dim;
+ u32 bytes_per_block_log2;
+ std::array origin;
+ u32 slice_size;
+ u32 block_size;
+ u32 x_shift;
+ u32 block_height;
+ u32 block_height_mask;
+ u32 block_depth;
+ u32 block_depth_mask;
+};
+static_assert(sizeof(BlockLinearUnswizzle3DBufferPushConstants) <= 128);
+
+constexpr std::array BL3DB_BINDINGS{{
+ {
+ .binding = BL3DB_BINDING_INPUT_BUFFER,
+ .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+ .descriptorCount = 1,
+ .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
+ .pImmutableSamplers = nullptr,
+ },
+ {
+ .binding = BL3DB_BINDING_OUTPUT_BUFFER,
+ .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+ .descriptorCount = 1,
+ .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
+ .pImmutableSamplers = nullptr,
+ },
+}};
+
+constexpr std::array BL3DB_TEMPLATE{{
+ {
+ .dstBinding = BL3DB_BINDING_INPUT_BUFFER,
+ .dstArrayElement = 0,
+ .descriptorCount = 1,
+ .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+ .offset = BL3DB_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry),
+ .stride = sizeof(DescriptorUpdateEntry),
+ },
+ {
+ .dstBinding = BL3DB_BINDING_OUTPUT_BUFFER,
+ .dstArrayElement = 0,
+ .descriptorCount = 1,
+ .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+ .offset = BL3DB_BINDING_OUTPUT_BUFFER * sizeof(DescriptorUpdateEntry),
+ .stride = sizeof(DescriptorUpdateEntry),
+ },
+}};
+
+constexpr DescriptorBankInfo BL3DB_BANK_INFO{
+ .uniform_buffers = 0,
+ .storage_buffers = 2,
+ .texture_buffers = 0,
+ .image_buffers = 0,
+ .textures = 0,
+ .images = 0,
+ .score = 2,
+};
+} // Anonymous namespace
+
+BlockLinearUnswizzle3DBufferPass::BlockLinearUnswizzle3DBufferPass(
+ const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
+ StagingBufferPool& staging_buffer_pool_,
+ ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
+ : ComputePass(device_, scheduler_, descriptor_pool_, BL3DB_BINDINGS, BL3DB_TEMPLATE,
+ BL3DB_BANK_INFO,
+ COMPUTE_PUSH_CONSTANT_RANGE,
+ BLOCK_LINEAR_UNSWIZZLE_3D_BUFFER_COMP_SPV),
+ scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_},
+ compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
+
+BlockLinearUnswizzle3DBufferPass::~BlockLinearUnswizzle3DBufferPass() = default;
+
+bool BlockLinearUnswizzle3DBufferPass::IsSupported(const Device& device,
+ const VideoCommon::ImageInfo& info) {
+ if (info.type != VideoCommon::ImageType::e3D) {
+ return false;
+ }
+ if (info.resources.levels != 1 || info.resources.layers != 1) {
+ return false;
+ }
+ if (info.num_samples > 1) {
+ return false;
+ }
+ if (info.size.depth <= 1) {
+ return false;
+ }
+ if (VideoCore::Surface::IsPixelFormatASTC(info.format)) {
+ return false;
+ }
+ if (VideoCore::Surface::IsPixelFormatBCn(info.format) && !device.IsOptimalBcnSupported()) {
+ return false;
+ }
+ if (info.format >= VideoCore::Surface::PixelFormat::MaxColorFormat) {
+ return false;
+ }
+ const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(info.format);
+ return bytes_per_block == 4 || bytes_per_block == 8 || bytes_per_block == 16;
+}
+
+void BlockLinearUnswizzle3DBufferPass::Unswizzle(
+ Image& image, const StagingBufferRef& swizzled,
+ std::span swizzles) {
+ if (swizzles.empty()) {
+ return;
+ }
+
+ const VideoCommon::SwizzleParameters& sw = swizzles.front();
+ const auto params = VideoCommon::Accelerated::MakeBlockLinearSwizzle3DParams(sw, image.info);
+
+ const u32 blocks_x = sw.num_tiles.width;
+ const u32 blocks_y = sw.num_tiles.height;
+ const u32 blocks_z = sw.num_tiles.depth;
+ const u32 bytes_per_block = 1u << params.bytes_per_block_log2;
+ const VkDeviceSize output_size =
+ static_cast(blocks_x) * blocks_y * blocks_z * bytes_per_block;
+
+ const StagingBufferRef output =
+ staging_buffer_pool.Request(static_cast(output_size), MemoryUsage::DeviceLocal);
+
+ BlockLinearUnswizzle3DBufferPushConstants pc{};
+ pc.dim = {blocks_x, blocks_y, blocks_z};
+ pc.bytes_per_block_log2 = params.bytes_per_block_log2;
+ pc.origin = params.origin;
+ pc.slice_size = params.slice_size;
+ pc.block_size = params.block_size;
+ pc.x_shift = params.x_shift;
+ pc.block_height = params.block_height;
+ pc.block_height_mask = params.block_height_mask;
+ pc.block_depth = params.block_depth;
+ pc.block_depth_mask = params.block_depth_mask;
+
+ scheduler.RequestOutsideRenderPassOperationContext();
+
+ compute_pass_descriptor_queue.Acquire(scheduler, 2);
+ compute_pass_descriptor_queue.AddBuffer(swizzled.buffer, sw.buffer_offset + swizzled.offset,
+ image.guest_size_bytes - sw.buffer_offset);
+ compute_pass_descriptor_queue.AddBuffer(output.buffer, output.offset, output_size);
+
+ const void* descriptor_data = compute_pass_descriptor_queue.UpdateData();
+ const VkDescriptorSet set = descriptor_allocator.Commit();
+
+ const u32 gx = Common::DivCeil(blocks_x, 8u);
+ const u32 gy = Common::DivCeil(blocks_y, 8u);
+ const u32 gz = Common::DivCeil(blocks_z, 4u);
+ const bool is_initialized = image.ExchangeInitialization();
+
+ const VkBuffer out_buffer = output.buffer;
+ const VkDeviceSize out_offset = output.offset;
+ const VkImage dst_image = image.Handle();
+ const VkImageAspectFlags aspect = image.AspectMask();
+ const VkExtent3D extent{
+ .width = image.info.size.width,
+ .height = image.info.size.height,
+ .depth = image.info.size.depth,
+ };
+
+ scheduler.Record([this, set, descriptor_data, pc, gx, gy, gz, output_size, out_buffer,
+ out_offset, dst_image, aspect, extent,
+ is_initialized](vk::CommandBuffer cmdbuf) {
+ if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) {
+ return;
+ }
+ device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
+ cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
+ cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
+ cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(pc), &pc);
+ cmdbuf.Dispatch(gx, gy, gz);
+
+ const VkBufferMemoryBarrier buffer_barrier{
+ .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
+ .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .buffer = out_buffer,
+ .offset = out_offset,
+ .size = output_size,
+ };
+ const VkImageMemoryBarrier pre_copy{
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = static_cast(is_initialized ? VK_ACCESS_SHADER_READ_BIT
+ : VK_ACCESS_NONE),
+ .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
+ .oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED,
+ .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = dst_image,
+ .subresourceRange{
+ .aspectMask = aspect,
+ .baseMipLevel = 0,
+ .levelCount = VK_REMAINING_MIP_LEVELS,
+ .baseArrayLayer = 0,
+ .layerCount = VK_REMAINING_ARRAY_LAYERS,
+ },
+ };
+ VkPipelineStageFlags pre_copy_src_stages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
+ if (is_initialized) {
+ pre_copy_src_stages |= vk::PIPELINE_STAGE_GRAPHICS_COMPUTE;
+ } else {
+ pre_copy_src_stages |= VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+ }
+ cmdbuf.PipelineBarrier(pre_copy_src_stages, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {},
+ buffer_barrier, pre_copy);
+
+ const VkBufferImageCopy copy{
+ .bufferOffset = out_offset,
+ .bufferRowLength = 0,
+ .bufferImageHeight = 0,
+ .imageSubresource{
+ .aspectMask = aspect,
+ .mipLevel = 0,
+ .baseArrayLayer = 0,
+ .layerCount = 1,
+ },
+ .imageOffset = {0, 0, 0},
+ .imageExtent = extent,
+ };
+ cmdbuf.CopyBufferToImage(out_buffer, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy);
+
+ const VkImageMemoryBarrier post_copy{
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
+ .dstAccessMask = VK_ACCESS_SHADER_READ_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{
+ .aspectMask = aspect,
+ .baseMipLevel = 0,
+ .levelCount = VK_REMAINING_MIP_LEVELS,
+ .baseArrayLayer = 0,
+ .layerCount = VK_REMAINING_ARRAY_LAYERS,
+ },
+ };
+ cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE,
+ 0, {}, {}, post_copy);
+ });
+}
+
} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.h b/src/video_core/renderer_vulkan/vk_compute_pass.h
index 3d30aa6bdc..5150e95b90 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.h
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.h
@@ -164,4 +164,42 @@ private:
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
};
+class BlockLinearUnswizzle2DPass final : public ComputePass {
+public:
+ explicit BlockLinearUnswizzle2DPass(const Device& device_, Scheduler& scheduler_,
+ DescriptorPool& descriptor_pool_,
+ StagingBufferPool& staging_buffer_pool_,
+ ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
+ ~BlockLinearUnswizzle2DPass();
+
+ [[nodiscard]] static bool IsSupported(const VideoCommon::ImageInfo& info);
+
+ void Unswizzle(Image& image, const StagingBufferRef& swizzled,
+ std::span swizzles);
+
+private:
+ Scheduler& scheduler;
+ StagingBufferPool& staging_buffer_pool;
+ ComputePassDescriptorQueue& compute_pass_descriptor_queue;
+};
+
+class BlockLinearUnswizzle3DBufferPass final : public ComputePass {
+public:
+ explicit BlockLinearUnswizzle3DBufferPass(
+ const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
+ StagingBufferPool& staging_buffer_pool_,
+ ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
+ ~BlockLinearUnswizzle3DBufferPass();
+
+ [[nodiscard]] static bool IsSupported(const Device& device, const VideoCommon::ImageInfo& info);
+
+ void Unswizzle(Image& image, const StagingBufferRef& swizzled,
+ std::span swizzles);
+
+private:
+ Scheduler& scheduler;
+ StagingBufferPool& staging_buffer_pool;
+ ComputePassDescriptorQueue& compute_pass_descriptor_queue;
+};
+
} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
index 29356cfcec..6c2ba89026 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
@@ -32,6 +32,7 @@ using Shader::Backend::SPIRV::RESCALING_LAYOUT_WORDS_OFFSET;
using Tegra::Texture::TexturePair;
ComputePipeline::ComputePipeline(const Device& device_, Scheduler& scheduler, vk::PipelineCache& pipeline_cache_,
+ std::shared_mutex& pipeline_cache_mutex_,
DescriptorPool& descriptor_pool,
GuestDescriptorQueue& guest_descriptor_queue_,
DescriptorBufferRing& descriptor_buffer_ring_,
@@ -40,7 +41,8 @@ ComputePipeline::ComputePipeline(const Device& device_, Scheduler& scheduler, vk
VideoCore::ShaderNotify* shader_notify, const Shader::Info& info_,
vk::ShaderModule spv_module_, u64 shader_hash_)
: device{device_},
- pipeline_cache(pipeline_cache_), guest_descriptor_queue{guest_descriptor_queue_},
+ pipeline_cache(pipeline_cache_), pipeline_cache_mutex(pipeline_cache_mutex_),
+ guest_descriptor_queue{guest_descriptor_queue_},
descriptor_buffer_ring{descriptor_buffer_ring_}, info{info_},
shader_hash{shader_hash_}, spv_module(std::move(spv_module_)) {
if (shader_notify) {
@@ -111,6 +113,7 @@ ComputePipeline::ComputePipeline(const Device& device_, Scheduler& scheduler, vk
.basePipelineIndex = 0,
};
try {
+ std::shared_lock cache_lock{pipeline_cache_mutex};
pipeline = device.GetLogical().CreateComputePipeline(compute_ci, *pipeline_cache);
} catch (const vk::Exception& exception) {
LOG_CRITICAL(Render_Vulkan, "Adreno rejected compute shader {:016X}: {}", shader_hash,
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.h b/src/video_core/renderer_vulkan/vk_compute_pipeline.h
index fba0519b58..65d4bb1382 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.h
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.h
@@ -9,6 +9,7 @@
#include
#include
#include
+#include
#include "common/common_types.h"
#include "common/thread_worker.h"
@@ -34,6 +35,7 @@ class Scheduler;
class ComputePipeline {
public:
explicit ComputePipeline(const Device& device, Scheduler& scheduler, vk::PipelineCache& pipeline_cache,
+ std::shared_mutex& pipeline_cache_mutex,
DescriptorPool& descriptor_pool,
GuestDescriptorQueue& guest_descriptor_queue,
DescriptorBufferRing& descriptor_buffer_ring,
@@ -59,6 +61,7 @@ public:
private:
const Device& device;
vk::PipelineCache& pipeline_cache;
+ std::shared_mutex& pipeline_cache_mutex;
GuestDescriptorQueue& guest_descriptor_queue;
DescriptorBufferRing& descriptor_buffer_ring;
Shader::Info info;
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 8b5b0bc9c5..554cc038ef 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -140,6 +140,8 @@ RenderPassKey MakeRenderPassKey(const FixedPipelineState& state, const Device& d
});
key.resolve_color =
key.samples != VK_SAMPLE_COUNT_1_BIT && has_color && device.IsTiler();
+ key.resolve_depth_stencil = key.samples != VK_SAMPLE_COUNT_1_BIT && device.IsTiler() &&
+ SupportsDepthStencilResolve(device, key.depth_format);
return key;
}
@@ -249,7 +251,8 @@ ConfigureFuncPtr ConfigureFunc(const std::array& m
// TODO(crueter): This is the worst-formatted code I have EVER seen
GraphicsPipeline::GraphicsPipeline(
Scheduler& scheduler_, BufferCache& buffer_cache_, TextureCache& texture_cache_,
- vk::PipelineCache& pipeline_cache_, VideoCore::ShaderNotify* shader_notify,
+ vk::PipelineCache& pipeline_cache_, std::shared_mutex& pipeline_cache_mutex_,
+ VideoCore::ShaderNotify* shader_notify,
const Device& device_, DescriptorPool& descriptor_pool,
GuestDescriptorQueue& guest_descriptor_queue_, DescriptorBufferRing& descriptor_buffer_ring_,
Common::ThreadWorker* worker_thread,
@@ -257,7 +260,8 @@ GraphicsPipeline::GraphicsPipeline(
const GraphicsPipelineCacheKey& key_, std::array stages,
const std::array& infos)
: key{key_}, device{device_}, texture_cache{texture_cache_}, buffer_cache{buffer_cache_},
- pipeline_cache(pipeline_cache_), scheduler{scheduler_},
+ pipeline_cache(pipeline_cache_), pipeline_cache_mutex(pipeline_cache_mutex_),
+ scheduler{scheduler_},
guest_descriptor_queue{guest_descriptor_queue_},
descriptor_buffer_ring{descriptor_buffer_ring_}, spv_modules{std::move(stages)} {
if (shader_notify) {
@@ -693,23 +697,30 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
if (!key.state.dynamic_vertex_input) {
const size_t num_vertex_arrays = (std::min)(
Maxwell::NumVertexArrays, static_cast(device.GetMaxVertexInputBindings()));
+ const u32 max_stride = device.GetMaxVertexInputBindingStride();
+ const u32 max_divisor = device.GetMaxVertexAttribDivisor();
for (size_t index = 0; index < num_vertex_arrays; ++index) {
const bool instanced = key.state.binding_divisors[index] != 0;
const auto rate =
instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
vertex_bindings.push_back({
.binding = static_cast(index),
- .stride = key.state.vertex_strides[index],
+ .stride = (std::min)(u32{key.state.vertex_strides[index]}, max_stride),
.inputRate = rate,
});
if (instanced) {
vertex_binding_divisors.push_back({
.binding = static_cast(index),
- .divisor = key.state.binding_divisors[index],
+ .divisor = (std::min)(key.state.binding_divisors[index], max_divisor),
});
}
}
+ const size_t max_attributes = static_cast(device.GetMaxVertexInputAttributes());
+ const u32 max_offset = device.GetMaxVertexInputAttributeOffset();
for (size_t index = 0; index < key.state.attributes.size(); ++index) {
+ if (vertex_attributes.size() >= max_attributes) {
+ break;
+ }
const auto& attribute = key.state.attributes[index];
if (!attribute.enabled || !stage_infos[0].loads.Generic(index)) {
continue;
@@ -718,11 +729,10 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
.location = static_cast(index),
.binding = attribute.buffer,
.format = MaxwellToVK::VertexFormat(device, attribute.Type(), attribute.Size()),
- .offset = attribute.offset,
+ .offset = (std::min)(attribute.offset.Value(), max_offset),
});
}
}
- ASSERT(vertex_attributes.size() <= device.GetMaxVertexInputAttributes());
VkPipelineVertexInputStateCreateInfo vertex_input_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
@@ -1073,6 +1083,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
flags |= VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT;
}
+ std::shared_lock cache_lock{pipeline_cache_mutex};
pipeline = device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
index b1ac5a3fcc..ebb92a8f16 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
@@ -11,6 +11,7 @@
#include
#include
#include
+#include
#include
#include
@@ -78,7 +79,8 @@ class GraphicsPipeline {
public:
explicit GraphicsPipeline(
Scheduler& scheduler, BufferCache& buffer_cache, TextureCache& texture_cache,
- vk::PipelineCache& pipeline_cache, VideoCore::ShaderNotify* shader_notify,
+ vk::PipelineCache& pipeline_cache, std::shared_mutex& pipeline_cache_mutex,
+ VideoCore::ShaderNotify* shader_notify,
const Device& device, DescriptorPool& descriptor_pool,
GuestDescriptorQueue& guest_descriptor_queue,
DescriptorBufferRing& descriptor_buffer_ring, Common::ThreadWorker* worker_thread,
@@ -151,6 +153,7 @@ private:
TextureCache& texture_cache;
BufferCache& buffer_cache;
vk::PipelineCache& pipeline_cache;
+ std::shared_mutex& pipeline_cache_mutex;
Scheduler& scheduler;
GuestDescriptorQueue& guest_descriptor_queue;
DescriptorBufferRing& descriptor_buffer_ring;
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 785602b33a..358695bcd6 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -56,13 +56,16 @@ using Shader::Backend::SPIRV::EmitSPIRV;
using Shader::Maxwell::ConvertLegacyToGeneric;
using Shader::Maxwell::GenerateGeometryPassthrough;
using Shader::Maxwell::MergeDualVertexPrograms;
+using Shader::Maxwell::PrunePassthroughStores;
using Shader::Maxwell::TranslateProgram;
using VideoCommon::ComputeEnvironment;
using VideoCommon::FileEnvironment;
using VideoCommon::GenericEnvironment;
using VideoCommon::GraphicsEnvironment;
-constexpr u32 CACHE_VERSION = 18;
+constexpr u32 CACHE_VERSION = 26;
+constexpr size_t VULKAN_CACHE_FLUSH_PIPELINES = 128;
+constexpr size_t VULKAN_CACHE_FLUSH_MIN_SECONDS = 30;
constexpr std::array VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'};
template
@@ -70,15 +73,18 @@ auto MakeSpan(Container& container) {
return std::span(container.data(), container.size());
}
-Shader::OutputTopology MaxwellToOutputTopology(Maxwell::PrimitiveTopology topology) {
+Shader::OutputTopology InputToOutputTopology(Shader::InputTopology topology) {
switch (topology) {
- case Maxwell::PrimitiveTopology::Points:
+ case Shader::InputTopology::Points:
return Shader::OutputTopology::PointList;
- case Maxwell::PrimitiveTopology::LineStrip:
+ case Shader::InputTopology::Lines:
+ case Shader::InputTopology::LinesAdjacency:
return Shader::OutputTopology::LineStrip;
- default:
+ case Shader::InputTopology::Triangles:
+ case Shader::InputTopology::TrianglesAdjacency:
return Shader::OutputTopology::TriangleStrip;
}
+ return Shader::OutputTopology::TriangleStrip;
}
Shader::CompareFunction MaxwellToCompareFunction(Maxwell::ComparisonOp comparison) {
@@ -150,6 +156,37 @@ Shader::AttributeType AttributeType(const FixedPipelineState& state, size_t inde
return Shader::AttributeType::Disabled;
}
+Shader::InputTopology MaxwellToInputTopology(Maxwell::PrimitiveTopology topology) {
+ switch (topology) {
+ case Maxwell::PrimitiveTopology::Points:
+ return Shader::InputTopology::Points;
+ case Maxwell::PrimitiveTopology::Lines:
+ case Maxwell::PrimitiveTopology::LineLoop:
+ case Maxwell::PrimitiveTopology::LineStrip:
+ return Shader::InputTopology::Lines;
+ case Maxwell::PrimitiveTopology::LinesAdjacency:
+ case Maxwell::PrimitiveTopology::LineStripAdjacency:
+ return Shader::InputTopology::LinesAdjacency;
+ case Maxwell::PrimitiveTopology::TrianglesAdjacency:
+ case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
+ return Shader::InputTopology::TrianglesAdjacency;
+ default:
+ return Shader::InputTopology::Triangles;
+ }
+}
+
+Shader::InputTopology GeometryInputTopology(const GraphicsPipelineCacheKey& key) {
+ if (key.unique_hashes[static_cast(Maxwell::ShaderType::Tessellation)] == 0) {
+ return MaxwellToInputTopology(key.state.topology);
+ }
+ const auto domain{
+ static_cast(key.state.tessellation_primitive.Value())};
+ if (domain == Maxwell::Tessellation::DomainType::Isolines) {
+ return Shader::InputTopology::Lines;
+ }
+ return Shader::InputTopology::Triangles;
+}
+
Shader::RuntimeInfo MakeRuntimeInfo(std::span programs,
const GraphicsPipelineCacheKey& key,
const Shader::IR::Program& program,
@@ -268,33 +305,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span program
default:
break;
}
- switch (key.state.topology) {
- case Maxwell::PrimitiveTopology::Points:
- info.input_topology = Shader::InputTopology::Points;
- break;
- case Maxwell::PrimitiveTopology::Lines:
- case Maxwell::PrimitiveTopology::LineLoop:
- case Maxwell::PrimitiveTopology::LineStrip:
- info.input_topology = Shader::InputTopology::Lines;
- break;
- case Maxwell::PrimitiveTopology::Triangles:
- case Maxwell::PrimitiveTopology::TriangleStrip:
- case Maxwell::PrimitiveTopology::TriangleFan:
- case Maxwell::PrimitiveTopology::Quads:
- case Maxwell::PrimitiveTopology::QuadStrip:
- case Maxwell::PrimitiveTopology::Polygon:
- case Maxwell::PrimitiveTopology::Patches:
- info.input_topology = Shader::InputTopology::Triangles;
- break;
- case Maxwell::PrimitiveTopology::LinesAdjacency:
- case Maxwell::PrimitiveTopology::LineStripAdjacency:
- info.input_topology = Shader::InputTopology::LinesAdjacency;
- break;
- case Maxwell::PrimitiveTopology::TrianglesAdjacency:
- case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
- info.input_topology = Shader::InputTopology::TrianglesAdjacency;
- break;
- }
+ info.input_topology = GeometryInputTopology(key);
info.force_early_z = key.state.early_z != 0;
info.y_negate = key.state.y_negate != 0;
return info;
@@ -444,6 +455,8 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
.has_broken_unsigned_image_offsets = false,
.has_broken_signed_operations = false,
.has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY,
+ .has_broken_fp32_signed_zero_nan_preserve = false,
+ .has_broken_fp32_denorm_flush = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY,
.ignore_nan_fp_comparisons = false,
.has_broken_spirv_subgroup_mask_vector_extract_dynamic = false,
.has_broken_robust =
@@ -540,6 +553,7 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
PipelineCache::~PipelineCache() {
if (use_vulkan_pipeline_cache && !vulkan_pipeline_cache_filename.empty()) {
+ std::unique_lock lock{vulkan_pipeline_cache_mutex};
SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache,
CACHE_VERSION);
}
@@ -599,6 +613,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
if (use_vulkan_pipeline_cache) {
vulkan_pipeline_cache_filename = base_dir / "vulkan_pipelines.bin";
+ std::unique_lock lock{vulkan_pipeline_cache_mutex};
vulkan_pipeline_cache =
LoadVulkanPipelineCache(vulkan_pipeline_cache_filename, CACHE_VERSION);
}
@@ -697,8 +712,13 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
workers.WaitForRequests(stop_loading);
if (use_vulkan_pipeline_cache) {
+ std::shared_lock cache_lock{vulkan_pipeline_cache_mutex};
SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache,
CACHE_VERSION);
+ size_t size = 0;
+ vulkan_pipeline_cache.Read(&size, nullptr);
+ last_cache_size.store(size, std::memory_order_relaxed);
+ last_flush = std::chrono::steady_clock::now();
}
if (state.statistics) {
@@ -706,6 +726,38 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
}
}
+void PipelineCache::QueueVulkanPipelineCacheFlush() {
+ if (!use_vulkan_pipeline_cache || vulkan_pipeline_cache_filename.empty()) {
+ return;
+ }
+ if (++pipelines_since_flush < VULKAN_CACHE_FLUSH_PIPELINES) {
+ return;
+ }
+ const auto now = std::chrono::steady_clock::now();
+ const auto megabytes = last_cache_size.load(std::memory_order_relaxed) / (1024 * 1024);
+ const std::chrono::seconds interval{
+ std::max(VULKAN_CACHE_FLUSH_MIN_SECONDS, megabytes)};
+ if (last_flush.time_since_epoch().count() != 0 && now - last_flush < interval) {
+ return;
+ }
+ if (flush_in_flight.exchange(true, std::memory_order_acq_rel)) {
+ return;
+ }
+ pipelines_since_flush = 0;
+ last_flush = now;
+ serialization_thread.QueueWork([this] {
+ {
+ std::shared_lock lock{vulkan_pipeline_cache_mutex};
+ SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache,
+ CACHE_VERSION);
+ size_t size = 0;
+ vulkan_pipeline_cache.Read(&size, nullptr);
+ last_cache_size.store(size, std::memory_order_relaxed);
+ }
+ flush_in_flight.store(false, std::memory_order_release);
+ });
+}
+
GraphicsPipeline* PipelineCache::CurrentGraphicsPipelineSlowPath() {
const auto [pair, is_new]{graphics_cache.try_emplace(graphics_key)};
auto& pipeline{pair->second};
@@ -744,7 +796,7 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline(
std::span envs, PipelineStatistics* statistics,
bool build_in_parallel) try {
auto hash = key.Hash();
- LOG_INFO(Render_Vulkan, "{:#016x}", hash);
+ LOG_DEBUG(Render_Vulkan, "{:#016x}", hash);
size_t env_index{0};
std::array programs;
const bool uses_vertex_a{key.unique_hashes[0] != 0};
@@ -757,9 +809,10 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline(
const bool is_emulated_stage = layer_source_program != nullptr &&
index == static_cast(Maxwell::ShaderType::Geometry);
if (key.unique_hashes[index] == 0 && is_emulated_stage) {
- auto topology = MaxwellToOutputTopology(key.state.topology);
- programs[index] = GenerateGeometryPassthrough(pools.inst, pools.block, host_info,
- *layer_source_program, topology);
+ const auto input_topology = GeometryInputTopology(key);
+ programs[index] = GenerateGeometryPassthrough(
+ pools.inst, pools.block, host_info, *layer_source_program,
+ InputToOutputTopology(input_topology), input_topology);
continue;
}
if (key.unique_hashes[index] == 0) {
@@ -772,11 +825,13 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline(
Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0);
if (!uses_vertex_a || index != 1) {
// Normal path
- programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info);
+ programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info,
+ GeometryInputTopology(key));
} else {
// VertexB path when VertexA is present.
auto& program_va{programs[0]};
- auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
+ auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info,
+ GeometryInputTopology(key))};
programs[index] = MergeDualVertexPrograms(program_va, program_vb, env);
}
@@ -806,6 +861,9 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline(
const size_t stage_index{index - 1};
infos[stage_index] = &program.info;
+ if (previous_stage) {
+ PrunePassthroughStores(program, previous_stage->info.stores);
+ }
const auto runtime_info{MakeRuntimeInfo(programs, key, program, previous_stage, device)};
ConvertLegacyToGeneric(program, runtime_info);
const std::vector code{EmitSPIRV(profile, runtime_info, program, binding)};
@@ -838,7 +896,8 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline(
}
Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr};
return std::make_unique(
- scheduler, buffer_cache, texture_cache, vulkan_pipeline_cache, &shader_notify, device,
+ scheduler, buffer_cache, texture_cache, vulkan_pipeline_cache,
+ vulkan_pipeline_cache_mutex, &shader_notify, device,
descriptor_pool, guest_descriptor_queue, descriptor_buffer_ring, thread_worker, statistics,
render_pass_cache, key, std::move(modules), infos);
@@ -880,6 +939,7 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline() {
}
SerializePipeline(key, env_ptrs, pipeline_cache_filename, CACHE_VERSION);
});
+ QueueVulkanPipelineCacheFlush();
return pipeline;
}
@@ -899,6 +959,7 @@ std::unique_ptr PipelineCache::CreateComputePipeline(
SerializePipeline(key, std::array{&env_},
pipeline_cache_filename, CACHE_VERSION);
});
+ QueueVulkanPipelineCacheFlush();
return pipeline;
}
@@ -911,7 +972,7 @@ std::unique_ptr PipelineCache::CreateComputePipeline(
return nullptr;
}
- LOG_INFO(Render_Vulkan, "{:#016x}", hash);
+ LOG_DEBUG(Render_Vulkan, "{:#016x}", hash);
Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
@@ -920,7 +981,8 @@ std::unique_ptr PipelineCache::CreateComputePipeline(
env.Dump(hash, key.unique_hash);
}
- auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
+ auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info,
+ Shader::InputTopology::Points)};
const VkDriverIdKHR driver_id = device.GetDriverID();
const bool needs_shared_mem_clamp =
driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
@@ -959,7 +1021,8 @@ std::unique_ptr PipelineCache::CreateComputePipeline(
spv_module.SetObjectNameEXT(name.c_str());
}
Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr};
- return std::make_unique(device, scheduler, vulkan_pipeline_cache, descriptor_pool,
+ return std::make_unique(device, scheduler, vulkan_pipeline_cache,
+ vulkan_pipeline_cache_mutex, descriptor_pool,
guest_descriptor_queue, descriptor_buffer_ring,
thread_worker, statistics,
&shader_notify, program.info, std::move(spv_module),
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
index ce89e981d2..94cf733890 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
@@ -7,9 +7,13 @@
#pragma once
#include
+#include
+#include
#include
#include
#include
+#include
+#include
#include
#include
#include
@@ -144,6 +148,8 @@ private:
vk::PipelineCache LoadVulkanPipelineCache(const std::filesystem::path& filename,
u32 expected_cache_version);
+ void QueueVulkanPipelineCacheFlush();
+
const Device& device;
Scheduler& scheduler;
DescriptorPool& descriptor_pool;
@@ -171,6 +177,11 @@ private:
std::filesystem::path vulkan_pipeline_cache_filename;
vk::PipelineCache vulkan_pipeline_cache;
+ std::shared_mutex vulkan_pipeline_cache_mutex;
+ size_t pipelines_since_flush{};
+ std::chrono::steady_clock::time_point last_flush{};
+ std::atomic last_cache_size{};
+ std::atomic_bool flush_in_flight{};
Common::ThreadWorker workers;
Common::ThreadWorker serialization_thread;
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp
index a12dc00771..24ea8e5ce9 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp
@@ -725,6 +725,9 @@ public:
if (!device.IsExtTransformFeedbackSupported()) {
return;
}
+ if (!scheduler.IsRenderPassActive()) {
+ return;
+ }
FlushBeginTFB();
has_started = true;
}
@@ -741,6 +744,9 @@ public:
if (has_flushed_end_pending) {
if (scheduler.IsRenderPassActive()) {
FlushEndTFB();
+ } else {
+ has_flushed_end_pending = false;
+ has_started = false;
}
}
runtime.View3DRegs([this](Maxwell3D& maxwell3d) {
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index a10d2e01e6..eeb58b9919 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);
@@ -852,8 +856,11 @@ void RasterizerVulkan::WaitForIdle() {
VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT | VK_PIPELINE_STAGE_VERTEX_INPUT_BIT |
VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT |
VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT |
- VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
- VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT;
+ VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
+ VK_PIPELINE_STAGE_TRANSFER_BIT;
+ if (device.SupportsGeometryShader()) {
+ flags |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT;
+ }
if (device.IsExtTransformFeedbackSupported()) {
flags |= VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT;
}
@@ -1286,33 +1293,42 @@ void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) {
regs.zeta.format == Tegra::DepthFormat::X8Z24_UNORM ||
regs.zeta.format == Tegra::DepthFormat::S8Z24_UNORM ||
regs.zeta.format == Tegra::DepthFormat::V8Z24_UNORM;
+ const bool forces_unorm_representation = device.IsExtDepthBiasControlSupported();
if (is_d24 && !device.SupportsD24DepthBuffer()) {
- static constexpr const size_t length = sizeof(NEEDS_D24) / sizeof(NEEDS_D24[0]);
+ static constexpr double GUEST_TO_HOST_UNORM_BITS =
+ static_cast(1ULL << (32 - 24));
- static constexpr const u64* start = NEEDS_D24;
- static constexpr const u64* end = NEEDS_D24 + length;
+ if (forces_unorm_representation) {
+ units = static_cast(static_cast(units) * GUEST_TO_HOST_UNORM_BITS);
+ } else {
+ static constexpr const size_t length = sizeof(NEEDS_D24) / sizeof(NEEDS_D24[0]);
- const u64* it = std::find(start, end, program_id);
+ static constexpr const u64* start = NEEDS_D24;
+ static constexpr const u64* end = NEEDS_D24 + length;
- if (it != end) {
- // the base formulas can be obtained from here:
- // https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-output-merger-stage-depth-bias
- const double rescale_factor =
- static_cast(1ULL << (32 - 24)) / (static_cast(0x1.ep+127));
- units = static_cast(static_cast(units) * rescale_factor);
+ const u64* it = std::find(start, end, program_id);
+
+ if (it != end) {
+ // the base formulas can be obtained from here:
+ // https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-output-merger-stage-depth-bias
+ const double rescale_factor =
+ GUEST_TO_HOST_UNORM_BITS / (static_cast(0x1.ep+127));
+ units = static_cast(static_cast(units) * rescale_factor);
+ }
}
}
scheduler.Record([constant = units, clamp = regs.depth_bias_clamp,
- factor = regs.slope_scale_depth_bias, this](vk::CommandBuffer cmdbuf) {
- if (device.IsExtDepthBiasControlSupported()) {
- static VkDepthBiasRepresentationInfoEXT bias_info{
+ factor = regs.slope_scale_depth_bias,
+ forces_unorm_representation, this](vk::CommandBuffer cmdbuf) {
+ if (forces_unorm_representation) {
+ const VkDepthBiasRepresentationInfoEXT bias_info{
.sType = VK_STRUCTURE_TYPE_DEPTH_BIAS_REPRESENTATION_INFO_EXT,
.pNext = nullptr,
.depthBiasRepresentation =
VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORCE_UNORM_EXT,
- .depthBiasExact = VK_FALSE,
+ .depthBiasExact = static_cast(device.HasExactDepthBiasControl()),
};
cmdbuf.SetDepthBias(constant, clamp, factor, &bias_info);
@@ -1336,8 +1352,14 @@ void RasterizerVulkan::UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs)
if (!state_tracker.TouchDepthBounds()) {
return;
}
- scheduler.Record([min = regs.depth_bounds[0], max = regs.depth_bounds[1]](
- vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthBounds(min, max); });
+ float min = regs.depth_bounds[0];
+ float max = regs.depth_bounds[1];
+ if (!device.IsExtDepthRangeUnrestrictedSupported()) {
+ min = std::clamp(min, 0.0f, 1.0f);
+ max = std::clamp(max, 0.0f, 1.0f);
+ }
+ scheduler.Record(
+ [min, max](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthBounds(min, max); });
}
void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs) {
@@ -1892,7 +1914,9 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs)
const u32 max_bindings =
static_cast(std::min(Maxwell::NumVertexArrays,
device.GetMaxVertexInputBindings()));
-
+ const u32 max_offset = device.GetMaxVertexInputAttributeOffset();
+ const u32 max_stride = device.GetMaxVertexInputBindingStride();
+ const u32 max_divisor = device.GetMaxVertexAttribDivisor();
for (u32 index = 0; index < max_attributes; ++index) {
const Maxwell::VertexAttribute attribute{regs.vertex_attrib_format[index]};
@@ -1906,20 +1930,24 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs)
.location = index,
.binding = binding,
.format = MaxwellToVK::VertexFormat(device, attribute.type, attribute.size),
- .offset = attribute.offset,
+ .offset = (std::min)(attribute.offset.Value(), max_offset),
});
}
for (u32 binding = 0; binding < max_bindings; ++binding) {
const auto& input_binding{regs.vertex_streams[binding]};
const bool is_instanced{regs.vertex_stream_instances.IsInstancingEnabled(binding)};
+ u32 divisor = 1;
+ if (is_instanced) {
+ divisor = (std::min)(input_binding.frequency, max_divisor);
+ }
bindings.push_back({
.sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_BINDING_DESCRIPTION_2_EXT,
.pNext = nullptr,
.binding = binding,
- .stride = input_binding.stride,
+ .stride = (std::min)(input_binding.stride.Value(), max_stride),
.inputRate = is_instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX,
- .divisor = is_instanced ? input_binding.frequency : 1,
+ .divisor = divisor,
});
}
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..023771832b 100644
--- a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp
@@ -65,8 +65,72 @@ using VideoCore::Surface::SurfaceType;
.finalLayout = VK_IMAGE_LAYOUT_GENERAL,
};
}
+
+ struct ResolveAspects {
+ bool depth;
+ bool stencil;
+ };
+
+ struct ResolveModes {
+ VkResolveModeFlagBits depth;
+ VkResolveModeFlagBits stencil;
+ };
+
+ constexpr ResolveAspects GetResolveAspects(PixelFormat format) {
+ const SurfaceType surface_type = GetSurfaceType(format);
+ return ResolveAspects{
+ .depth = surface_type == SurfaceType::Depth ||
+ surface_type == SurfaceType::DepthStencil,
+ .stencil = surface_type == SurfaceType::Stencil ||
+ surface_type == SurfaceType::DepthStencil,
+ };
+ }
+
+ ResolveModes PickResolveModes(const Device& device, PixelFormat format) {
+ constexpr VkResolveModeFlagBits mode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
+
+ const ResolveAspects aspects = GetResolveAspects(format);
+ const bool depth_mode_supported = (device.GetDepthResolveModes() & mode) != 0;
+ const bool stencil_mode_supported = (device.GetStencilResolveModes() & mode) != 0;
+
+ ResolveModes modes{
+ .depth = VK_RESOLVE_MODE_NONE,
+ .stencil = VK_RESOLVE_MODE_NONE,
+ };
+ if (aspects.depth && depth_mode_supported) {
+ modes.depth = mode;
+ }
+ if (aspects.stencil && stencil_mode_supported) {
+ modes.stencil = mode;
+ }
+ if (modes.depth == modes.stencil || device.SupportsIndependentResolveNone()) {
+ return modes;
+ }
+ if (modes.depth != VK_RESOLVE_MODE_NONE && stencil_mode_supported) {
+ modes.stencil = mode;
+ } else if (modes.stencil != VK_RESOLVE_MODE_NONE && depth_mode_supported) {
+ modes.depth = mode;
+ }
+ return modes;
+ }
} // Anonymous namespace
+bool SupportsDepthStencilResolve(const Device& device, PixelFormat depth_format) {
+ if (depth_format == PixelFormat::Invalid || !device.IsKhrDepthStencilResolveSupported()) {
+ return false;
+ }
+ const ResolveAspects aspects = GetResolveAspects(depth_format);
+ if (!aspects.depth && !aspects.stencil) {
+ return false;
+ }
+ const ResolveModes modes = PickResolveModes(device, depth_format);
+ if ((aspects.depth && modes.depth == VK_RESOLVE_MODE_NONE) ||
+ (aspects.stencil && modes.stencil == VK_RESOLVE_MODE_NONE)) {
+ return false;
+ }
+ return modes.depth == modes.stencil || device.SupportsIndependentResolveNone();
+}
+
RenderPassCache::RenderPassCache(const Device& device_) : device{&device_} {}
VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
@@ -75,7 +139,9 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
if (!is_new) {
return *pair->second;
}
- boost::container::static_vector descriptions;
+ static constexpr size_t MAX_ATTACHMENTS =
+ 2 * std::tuple_size_v + 2;
+ boost::container::static_vector descriptions;
std::array references{};
u32 num_attachments{};
u32 num_colors{};
@@ -109,8 +175,11 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
const VkAttachmentLoadOp depth_load_op = key.depth_stencil_clear
? VK_ATTACHMENT_LOAD_OP_CLEAR
: VK_ATTACHMENT_LOAD_OP_LOAD;
+ const VkAttachmentStoreOp depth_store_op = key.depth_stencil_discard
+ ? VK_ATTACHMENT_STORE_OP_DONT_CARE
+ : VK_ATTACHMENT_STORE_OP_STORE;
descriptions.push_back(AttachmentDescription(*device, key.depth_format, key.samples,
- depth_load_op, VK_ATTACHMENT_STORE_OP_STORE));
+ depth_load_op, depth_store_op));
}
std::array resolve_references{};
const bool do_resolve_color =
@@ -133,6 +202,21 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
}
}
}
+ const bool do_resolve_depth_stencil = key.resolve_depth_stencil && has_depth &&
+ key.samples != VK_SAMPLE_COUNT_1_BIT &&
+ SupportsDepthStencilResolve(*device, key.depth_format);
+ VkAttachmentReference depth_resolve_reference{};
+ if (do_resolve_depth_stencil) {
+ depth_resolve_reference = VkAttachmentReference{
+ .attachment = static_cast(descriptions.size()),
+ .layout = VK_IMAGE_LAYOUT_GENERAL,
+ };
+ VkAttachmentDescription resolve_desc =
+ AttachmentDescription(*device, key.depth_format, VK_SAMPLE_COUNT_1_BIT,
+ VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_STORE);
+ resolve_desc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ descriptions.push_back(resolve_desc);
+ }
const VkSubpassDescription subpass{
.flags = 0,
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
@@ -145,18 +229,125 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
.preserveAttachmentCount = 0,
.pPreserveAttachments = nullptr,
};
- const VkSubpassDependency dependency{
- .srcSubpass = 0, // Current subpass
- .dstSubpass = 0, // Same subpass (self-dependency)
- .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
- VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
- VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
- .dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
- .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
- VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
- .dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
- .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT
+ const VkSubpassDependency counter_resume_dependency{
+ .srcSubpass = 0,
+ .dstSubpass = 0,
+ .srcStageMask = VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT,
+ .dstStageMask = VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT,
+ .srcAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT,
+ .dstAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT,
+ .dependencyFlags = 0,
};
+ const bool can_resume_transform_feedback = device->IsExtTransformFeedbackSupported();
+
+ if (device->IsKhrCreateRenderPass2Supported()) {
+ boost::container::static_vector descriptions2;
+ for (const VkAttachmentDescription& description : descriptions) {
+ descriptions2.push_back(VkAttachmentDescription2{
+ .sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2,
+ .pNext = nullptr,
+ .flags = description.flags,
+ .format = description.format,
+ .samples = description.samples,
+ .loadOp = description.loadOp,
+ .storeOp = description.storeOp,
+ .stencilLoadOp = description.stencilLoadOp,
+ .stencilStoreOp = description.stencilStoreOp,
+ .initialLayout = description.initialLayout,
+ .finalLayout = description.finalLayout,
+ });
+ }
+ const auto promote = [](const VkAttachmentReference& reference) {
+ return VkAttachmentReference2{
+ .sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2,
+ .pNext = nullptr,
+ .attachment = reference.attachment,
+ .layout = reference.layout,
+ .aspectMask = 0,
+ };
+ };
+ std::array references2{};
+ std::array resolve_references2{};
+ for (size_t index = 0; index < references.size(); ++index) {
+ references2[index] = promote(references[index]);
+ resolve_references2[index] = promote(resolve_references[index]);
+ }
+ const VkAttachmentReference2 depth_reference2 = promote(depth_reference);
+ const VkAttachmentReference2 depth_resolve_reference2 = promote(depth_resolve_reference);
+ const ResolveModes resolve_modes = PickResolveModes(*device, key.depth_format);
+ const VkSubpassDescriptionDepthStencilResolve depth_stencil_resolve{
+ .sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE,
+ .pNext = nullptr,
+ .depthResolveMode = resolve_modes.depth,
+ .stencilResolveMode = resolve_modes.stencil,
+ .pDepthStencilResolveAttachment = &depth_resolve_reference2,
+ };
+ const VkSubpassDescription2 subpass2{
+ .sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2,
+ .pNext = do_resolve_depth_stencil ? &depth_stencil_resolve : nullptr,
+ .flags = 0,
+ .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
+ .viewMask = 0,
+ .inputAttachmentCount = 0,
+ .pInputAttachments = nullptr,
+ .colorAttachmentCount = num_attachments,
+ .pColorAttachments = references2.data(),
+ .pResolveAttachments = do_resolve_color ? resolve_references2.data() : nullptr,
+ .pDepthStencilAttachment = has_depth ? &depth_reference2 : nullptr,
+ .preserveAttachmentCount = 0,
+ .pPreserveAttachments = nullptr,
+ };
+ const VkMemoryBarrier2 counter_resume_barrier{
+ .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2,
+ .pNext = nullptr,
+ .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFORM_FEEDBACK_BIT_EXT,
+ .srcAccessMask = VK_ACCESS_2_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT,
+ .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFORM_FEEDBACK_BIT_EXT,
+ .dstAccessMask = VK_ACCESS_2_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT,
+ };
+ VkSubpassDependency2 counter_resume_dependency2{
+ .sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2,
+ .pNext = nullptr,
+ .srcSubpass = counter_resume_dependency.srcSubpass,
+ .dstSubpass = counter_resume_dependency.dstSubpass,
+ .srcStageMask = counter_resume_dependency.srcStageMask,
+ .dstStageMask = counter_resume_dependency.dstStageMask,
+ .srcAccessMask = counter_resume_dependency.srcAccessMask,
+ .dstAccessMask = counter_resume_dependency.dstAccessMask,
+ .dependencyFlags = counter_resume_dependency.dependencyFlags,
+ .viewOffset = 0,
+ };
+ if (device->HasSynchronization2()) {
+ counter_resume_dependency2.pNext = &counter_resume_barrier;
+ counter_resume_dependency2.srcStageMask = 0;
+ counter_resume_dependency2.dstStageMask = 0;
+ counter_resume_dependency2.srcAccessMask = 0;
+ counter_resume_dependency2.dstAccessMask = 0;
+ }
+ boost::container::static_vector dependencies2;
+ if (can_resume_transform_feedback) {
+ dependencies2.push_back(counter_resume_dependency2);
+ }
+ pair->second = device->GetLogical().CreateRenderPass2({
+ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2,
+ .pNext = nullptr,
+ .flags = 0,
+ .attachmentCount = static_cast(descriptions2.size()),
+ .pAttachments = descriptions2.empty() ? nullptr : descriptions2.data(),
+ .subpassCount = 1,
+ .pSubpasses = &subpass2,
+ .dependencyCount = static_cast(dependencies2.size()),
+ .pDependencies = dependencies2.data(),
+ .correlatedViewMaskCount = 0,
+ .pCorrelatedViewMasks = nullptr,
+ });
+ return *pair->second;
+ }
+
+ boost::container::static_vector dependencies;
+ if (can_resume_transform_feedback) {
+ dependencies.push_back(counter_resume_dependency);
+ }
pair->second = device->GetLogical().CreateRenderPass({
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.pNext = nullptr,
@@ -165,8 +356,8 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
.pAttachments = descriptions.empty() ? nullptr : descriptions.data(),
.subpassCount = 1,
.pSubpasses = &subpass,
- .dependencyCount = 1,
- .pDependencies = &dependency,
+ .dependencyCount = static_cast(dependencies.size()),
+ .pDependencies = dependencies.data(),
});
return *pair->second;
}
diff --git a/src/video_core/renderer_vulkan/vk_render_pass_cache.h b/src/video_core/renderer_vulkan/vk_render_pass_cache.h
index 75999a655e..f9923be75a 100644
--- a/src/video_core/renderer_vulkan/vk_render_pass_cache.h
+++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.h
@@ -9,6 +9,7 @@
#include
#include
+#include "common/container_hash.h"
#include "video_core/surface.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
@@ -21,9 +22,11 @@ struct RenderPassKey {
VideoCore::Surface::PixelFormat depth_format;
VkSampleCountFlagBits samples;
bool resolve_color;
+ bool resolve_depth_stencil;
u32 color_clear_mask;
bool depth_stencil_clear;
u32 color_discard_mask;
+ bool depth_stencil_discard;
};
} // namespace Vulkan
@@ -31,17 +34,28 @@ struct RenderPassKey {
namespace std {
template <>
struct hash {
+ static_assert(std::tuple_size_v <= 8);
+ static_assert(static_cast(VideoCore::Surface::PixelFormat::Invalid) <= 0xFF);
+ static_assert(static_cast(VideoCore::Surface::PixelFormat::Max) <= 0xFF);
+ static_assert(VK_SAMPLE_COUNT_64_BIT <= 0xFF);
+
[[nodiscard]] size_t operator()(const Vulkan::RenderPassKey& key) const noexcept {
- size_t value = static_cast(key.depth_format) << 48;
- value ^= static_cast(key.samples) << 52;
- value ^= static_cast(key.resolve_color) << 63;
- value ^= static_cast(key.color_clear_mask) << 54;
- value ^= static_cast(key.depth_stencil_clear) << 62;
- value ^= static_cast(key.color_discard_mask) << 24;
- for (size_t i = 0; i < key.color_formats.size(); ++i) {
- value ^= static_cast(key.color_formats[i]) << (i * 6);
+ u64 formats = 0;
+ for (size_t index = 0; index < key.color_formats.size(); ++index) {
+ formats |= static_cast(key.color_formats[index]) << (index * 8);
}
- return value;
+ const u64 state = static_cast(key.depth_format) |
+ (static_cast(key.samples) << 8) |
+ (static_cast(key.color_clear_mask) << 16) |
+ (static_cast(key.color_discard_mask) << 24) |
+ (static_cast(key.resolve_color) << 32) |
+ (static_cast(key.depth_stencil_clear) << 33) |
+ (static_cast(key.resolve_depth_stencil) << 34) |
+ (static_cast(key.depth_stencil_discard) << 35);
+ size_t seed = 0;
+ Common::HashCombine(seed, formats);
+ Common::HashCombine(seed, state);
+ return seed;
}
};
} // namespace std
@@ -50,6 +64,9 @@ namespace Vulkan {
class Device;
+[[nodiscard]] bool SupportsDepthStencilResolve(const Device& device,
+ VideoCore::Surface::PixelFormat depth_format);
+
class RenderPassCache {
public:
explicit RenderPassCache(const Device& device_);
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index 3601a3e891..31e852e943 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();
@@ -410,8 +421,17 @@ void Scheduler::EndRenderPass()
Record([num_images = num_renderpass_images,
images = renderpass_images,
ranges = renderpass_image_ranges,
+ consumer_stages = device.AttachmentConsumerStages(),
has_transform_feedback = device.IsExtTransformFeedbackSupported()](
vk::CommandBuffer cmdbuf) {
+ static constexpr VkAccessFlags SHADER_ACCESS =
+ VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
+ static constexpr VkAccessFlags COLOR_ACCESS =
+ VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ static constexpr VkAccessFlags DEPTH_STENCIL_ACCESS =
+ VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
+ VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
+
std::array