summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/CMakeLists.txt1
-rw-r--r--src/common/range_map.h139
-rw-r--r--src/common/settings.cpp2
-rw-r--r--src/common/settings.h4
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp4
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp4
-rw-r--r--src/core/internal_network/network.cpp6
-rw-r--r--src/core/memory.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp18
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp18
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp20
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp58
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp58
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.h2
-rw-r--r--src/shader_recompiler/environment.h10
-rw-r--r--src/shader_recompiler/frontend/ir/attribute.cpp6
-rw-r--r--src/shader_recompiler/frontend/ir/attribute.h5
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.cpp8
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.h2
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate_program.cpp118
-rw-r--r--src/shader_recompiler/host_translate_info.h3
-rw-r--r--src/shader_recompiler/ir_opt/constant_propagation_pass.cpp76
-rw-r--r--src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp13
-rw-r--r--src/shader_recompiler/ir_opt/passes.h4
-rw-r--r--src/shader_recompiler/profile.h2
-rw-r--r--src/shader_recompiler/shader_info.h18
-rw-r--r--src/shader_recompiler/varying_state.h2
-rw-r--r--src/tests/CMakeLists.txt1
-rw-r--r--src/tests/common/range_map.cpp70
-rw-r--r--src/tests/video_core/buffer_base.cpp2
-rw-r--r--src/video_core/CMakeLists.txt4
-rw-r--r--src/video_core/buffer_cache/buffer_base.h14
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h89
-rw-r--r--src/video_core/cache_types.h24
-rw-r--r--src/video_core/dma_pusher.cpp42
-rw-r--r--src/video_core/dma_pusher.h2
-rw-r--r--src/video_core/engines/draw_manager.cpp31
-rw-r--r--src/video_core/engines/draw_manager.h25
-rw-r--r--src/video_core/engines/engine_interface.h24
-rw-r--r--src/video_core/engines/engine_upload.cpp2
-rw-r--r--src/video_core/engines/fermi_2d.cpp16
-rw-r--r--src/video_core/engines/fermi_2d.h3
-rw-r--r--src/video_core/engines/kepler_compute.cpp14
-rw-r--r--src/video_core/engines/kepler_compute.h2
-rw-r--r--src/video_core/engines/kepler_memory.cpp11
-rw-r--r--src/video_core/engines/kepler_memory.h2
-rw-r--r--src/video_core/engines/maxwell_3d.cpp180
-rw-r--r--src/video_core/engines/maxwell_3d.h75
-rw-r--r--src/video_core/engines/maxwell_dma.cpp33
-rw-r--r--src/video_core/engines/maxwell_dma.h2
-rw-r--r--src/video_core/host_shaders/CMakeLists.txt1
-rw-r--r--src/video_core/host_shaders/vulkan_turbo_mode.comp29
-rw-r--r--src/video_core/invalidation_accumulator.h79
-rw-r--r--src/video_core/macro/macro.cpp19
-rw-r--r--src/video_core/macro/macro.h1
-rw-r--r--src/video_core/macro/macro_hle.cpp606
-rw-r--r--src/video_core/macro/macro_hle.h5
-rw-r--r--src/video_core/memory_manager.cpp216
-rw-r--r--src/video_core/memory_manager.h57
-rw-r--r--src/video_core/rasterizer_interface.h27
-rw-r--r--src/video_core/renderer_null/null_rasterizer.cpp8
-rw-r--r--src/video_core/renderer_null/null_rasterizer.h12
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h4
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.h1
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp190
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h17
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp5
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h2
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp8
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.cpp119
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.h76
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp8
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h5
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp19
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.cpp42
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp127
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.h19
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp161
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h11
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp444
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h25
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp56
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.h25
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.cpp102
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.h119
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp8
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_turbo_mode.cpp205
-rw-r--r--src/video_core/renderer_vulkan/vk_turbo_mode.h26
-rw-r--r--src/video_core/shader_environment.cpp58
-rw-r--r--src/video_core/shader_environment.h21
-rw-r--r--src/video_core/texture_cache/texture_cache.h107
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h8
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp164
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h81
-rw-r--r--src/video_core/vulkan_common/vulkan_instance.cpp14
-rw-r--r--src/video_core/vulkan_common/vulkan_instance.h5
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.cpp41
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.h102
-rw-r--r--src/yuzu/configuration/config.cpp8
-rw-r--r--src/yuzu/configuration/configure_debug.cpp3
-rw-r--r--src/yuzu/configuration/configure_debug.ui15
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp21
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.h2
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.ui20
-rw-r--r--src/yuzu/main.cpp20
-rw-r--r--src/yuzu/main.h1
-rw-r--r--src/yuzu_cmd/config.cpp3
111 files changed, 4058 insertions, 805 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index eb05e46a8..45332cf95 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -97,6 +97,7 @@ add_library(common STATIC
97 point.h 97 point.h
98 precompiled_headers.h 98 precompiled_headers.h
99 quaternion.h 99 quaternion.h
100 range_map.h
100 reader_writer_queue.h 101 reader_writer_queue.h
101 ring_buffer.h 102 ring_buffer.h
102 ${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp 103 ${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp
diff --git a/src/common/range_map.h b/src/common/range_map.h
new file mode 100644
index 000000000..79c7ef547
--- /dev/null
+++ b/src/common/range_map.h
@@ -0,0 +1,139 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <map>
7#include <type_traits>
8
9#include "common/common_types.h"
10
11namespace Common {
12
13template <typename KeyTBase, typename ValueT>
14class RangeMap {
15private:
16 using KeyT =
17 std::conditional_t<std::is_signed_v<KeyTBase>, KeyTBase, std::make_signed_t<KeyTBase>>;
18
19public:
20 explicit RangeMap(ValueT null_value_) : null_value{null_value_} {
21 container.emplace(std::numeric_limits<KeyT>::min(), null_value);
22 };
23 ~RangeMap() = default;
24
25 void Map(KeyTBase address, KeyTBase address_end, ValueT value) {
26 KeyT new_address = static_cast<KeyT>(address);
27 KeyT new_address_end = static_cast<KeyT>(address_end);
28 if (new_address < 0) {
29 new_address = 0;
30 }
31 if (new_address_end < 0) {
32 new_address_end = 0;
33 }
34 InternalMap(new_address, new_address_end, value);
35 }
36
37 void Unmap(KeyTBase address, KeyTBase address_end) {
38 Map(address, address_end, null_value);
39 }
40
41 [[nodiscard]] size_t GetContinousSizeFrom(KeyTBase address) const {
42 const KeyT new_address = static_cast<KeyT>(address);
43 if (new_address < 0) {
44 return 0;
45 }
46 return ContinousSizeInternal(new_address);
47 }
48
49 [[nodiscard]] ValueT GetValueAt(KeyT address) const {
50 const KeyT new_address = static_cast<KeyT>(address);
51 if (new_address < 0) {
52 return null_value;
53 }
54 return GetValueInternal(new_address);
55 }
56
57private:
58 using MapType = std::map<KeyT, ValueT>;
59 using IteratorType = typename MapType::iterator;
60 using ConstIteratorType = typename MapType::const_iterator;
61
62 size_t ContinousSizeInternal(KeyT address) const {
63 const auto it = GetFirstElementBeforeOrOn(address);
64 if (it == container.end() || it->second == null_value) {
65 return 0;
66 }
67 const auto it_end = std::next(it);
68 if (it_end == container.end()) {
69 return std::numeric_limits<KeyT>::max() - address;
70 }
71 return it_end->first - address;
72 }
73
74 ValueT GetValueInternal(KeyT address) const {
75 const auto it = GetFirstElementBeforeOrOn(address);
76 if (it == container.end()) {
77 return null_value;
78 }
79 return it->second;
80 }
81
82 ConstIteratorType GetFirstElementBeforeOrOn(KeyT address) const {
83 auto it = container.lower_bound(address);
84 if (it == container.begin()) {
85 return it;
86 }
87 if (it != container.end() && (it->first == address)) {
88 return it;
89 }
90 --it;
91 return it;
92 }
93
94 ValueT GetFirstValueWithin(KeyT address) {
95 auto it = container.lower_bound(address);
96 if (it == container.begin()) {
97 return it->second;
98 }
99 if (it == container.end()) [[unlikely]] { // this would be a bug
100 return null_value;
101 }
102 --it;
103 return it->second;
104 }
105
106 ValueT GetLastValueWithin(KeyT address) {
107 auto it = container.upper_bound(address);
108 if (it == container.end()) {
109 return null_value;
110 }
111 if (it == container.begin()) [[unlikely]] { // this would be a bug
112 return it->second;
113 }
114 --it;
115 return it->second;
116 }
117
118 void InternalMap(KeyT address, KeyT address_end, ValueT value) {
119 const bool must_add_start = GetFirstValueWithin(address) != value;
120 const ValueT last_value = GetLastValueWithin(address_end);
121 const bool must_add_end = last_value != value;
122 auto it = container.lower_bound(address);
123 const auto it_end = container.upper_bound(address_end);
124 while (it != it_end) {
125 it = container.erase(it);
126 }
127 if (must_add_start) {
128 container.emplace(address, value);
129 }
130 if (must_add_end) {
131 container.emplace(address_end, last_value);
132 }
133 }
134
135 ValueT null_value;
136 MapType container;
137};
138
139} // namespace Common
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 149e621f9..1638b79f5 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -185,6 +185,7 @@ void RestoreGlobalState(bool is_powered_on) {
185 // Renderer 185 // Renderer
186 values.fsr_sharpening_slider.SetGlobal(true); 186 values.fsr_sharpening_slider.SetGlobal(true);
187 values.renderer_backend.SetGlobal(true); 187 values.renderer_backend.SetGlobal(true);
188 values.renderer_force_max_clock.SetGlobal(true);
188 values.vulkan_device.SetGlobal(true); 189 values.vulkan_device.SetGlobal(true);
189 values.aspect_ratio.SetGlobal(true); 190 values.aspect_ratio.SetGlobal(true);
190 values.max_anisotropy.SetGlobal(true); 191 values.max_anisotropy.SetGlobal(true);
@@ -200,6 +201,7 @@ void RestoreGlobalState(bool is_powered_on) {
200 values.use_asynchronous_shaders.SetGlobal(true); 201 values.use_asynchronous_shaders.SetGlobal(true);
201 values.use_fast_gpu_time.SetGlobal(true); 202 values.use_fast_gpu_time.SetGlobal(true);
202 values.use_pessimistic_flushes.SetGlobal(true); 203 values.use_pessimistic_flushes.SetGlobal(true);
204 values.use_vulkan_driver_pipeline_cache.SetGlobal(true);
203 values.bg_red.SetGlobal(true); 205 values.bg_red.SetGlobal(true);
204 values.bg_green.SetGlobal(true); 206 values.bg_green.SetGlobal(true);
205 values.bg_blue.SetGlobal(true); 207 values.bg_blue.SetGlobal(true);
diff --git a/src/common/settings.h b/src/common/settings.h
index 6b199af93..9eb3711ca 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -415,6 +415,7 @@ struct Values {
415 // Renderer 415 // Renderer
416 SwitchableSetting<RendererBackend, true> renderer_backend{ 416 SwitchableSetting<RendererBackend, true> renderer_backend{
417 RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Null, "backend"}; 417 RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Null, "backend"};
418 SwitchableSetting<bool> renderer_force_max_clock{true, "force_max_clock"};
418 Setting<bool> renderer_debug{false, "debug"}; 419 Setting<bool> renderer_debug{false, "debug"};
419 Setting<bool> renderer_shader_feedback{false, "shader_feedback"}; 420 Setting<bool> renderer_shader_feedback{false, "shader_feedback"};
420 Setting<bool> enable_nsight_aftermath{false, "nsight_aftermath"}; 421 Setting<bool> enable_nsight_aftermath{false, "nsight_aftermath"};
@@ -451,6 +452,8 @@ struct Values {
451 SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; 452 SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
452 SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; 453 SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
453 SwitchableSetting<bool> use_pessimistic_flushes{false, "use_pessimistic_flushes"}; 454 SwitchableSetting<bool> use_pessimistic_flushes{false, "use_pessimistic_flushes"};
455 SwitchableSetting<bool> use_vulkan_driver_pipeline_cache{true,
456 "use_vulkan_driver_pipeline_cache"};
454 457
455 SwitchableSetting<u8> bg_red{0, "bg_red"}; 458 SwitchableSetting<u8> bg_red{0, "bg_red"};
456 SwitchableSetting<u8> bg_green{0, "bg_green"}; 459 SwitchableSetting<u8> bg_green{0, "bg_green"};
@@ -531,6 +534,7 @@ struct Values {
531 Setting<bool> reporting_services{false, "reporting_services"}; 534 Setting<bool> reporting_services{false, "reporting_services"};
532 Setting<bool> quest_flag{false, "quest_flag"}; 535 Setting<bool> quest_flag{false, "quest_flag"};
533 Setting<bool> disable_macro_jit{false, "disable_macro_jit"}; 536 Setting<bool> disable_macro_jit{false, "disable_macro_jit"};
537 Setting<bool> disable_macro_hle{false, "disable_macro_hle"};
534 Setting<bool> extended_logging{false, "extended_logging"}; 538 Setting<bool> extended_logging{false, "extended_logging"};
535 Setting<bool> use_debug_asserts{false, "use_debug_asserts"}; 539 Setting<bool> use_debug_asserts{false, "use_debug_asserts"};
536 Setting<bool> use_auto_stub{false, "use_auto_stub"}; 540 Setting<bool> use_auto_stub{false, "use_auto_stub"};
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 947747d36..2a7570073 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -229,7 +229,11 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
229 config.enable_cycle_counting = true; 229 config.enable_cycle_counting = true;
230 230
231 // Code cache size 231 // Code cache size
232#ifdef ARCHITECTURE_arm64
233 config.code_cache_size = 128_MiB;
234#else
232 config.code_cache_size = 512_MiB; 235 config.code_cache_size = 512_MiB;
236#endif
233 237
234 // Allow memory fault handling to work 238 // Allow memory fault handling to work
235 if (system.DebuggerEnabled()) { 239 if (system.DebuggerEnabled()) {
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 3df943df7..7229fdc2a 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -288,7 +288,11 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
288 config.enable_cycle_counting = true; 288 config.enable_cycle_counting = true;
289 289
290 // Code cache size 290 // Code cache size
291#ifdef ARCHITECTURE_arm64
292 config.code_cache_size = 128_MiB;
293#else
291 config.code_cache_size = 512_MiB; 294 config.code_cache_size = 512_MiB;
295#endif
292 296
293 // Allow memory fault handling to work 297 // Allow memory fault handling to work
294 if (system.DebuggerEnabled()) { 298 if (system.DebuggerEnabled()) {
diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp
index 447fbffaa..282ea1ff9 100644
--- a/src/core/internal_network/network.cpp
+++ b/src/core/internal_network/network.cpp
@@ -117,6 +117,8 @@ Errno TranslateNativeError(int e) {
117 return Errno::NETUNREACH; 117 return Errno::NETUNREACH;
118 case WSAEMSGSIZE: 118 case WSAEMSGSIZE:
119 return Errno::MSGSIZE; 119 return Errno::MSGSIZE;
120 case WSAETIMEDOUT:
121 return Errno::TIMEDOUT;
120 default: 122 default:
121 UNIMPLEMENTED_MSG("Unimplemented errno={}", e); 123 UNIMPLEMENTED_MSG("Unimplemented errno={}", e);
122 return Errno::OTHER; 124 return Errno::OTHER;
@@ -211,6 +213,8 @@ Errno TranslateNativeError(int e) {
211 return Errno::NETUNREACH; 213 return Errno::NETUNREACH;
212 case EMSGSIZE: 214 case EMSGSIZE:
213 return Errno::MSGSIZE; 215 return Errno::MSGSIZE;
216 case ETIMEDOUT:
217 return Errno::TIMEDOUT;
214 default: 218 default:
215 UNIMPLEMENTED_MSG("Unimplemented errno={}", e); 219 UNIMPLEMENTED_MSG("Unimplemented errno={}", e);
216 return Errno::OTHER; 220 return Errno::OTHER;
@@ -226,7 +230,7 @@ Errno GetAndLogLastError() {
226 int e = errno; 230 int e = errno;
227#endif 231#endif
228 const Errno err = TranslateNativeError(e); 232 const Errno err = TranslateNativeError(e);
229 if (err == Errno::AGAIN) { 233 if (err == Errno::AGAIN || err == Errno::TIMEDOUT) {
230 return err; 234 return err;
231 } 235 }
232 LOG_ERROR(Network, "Socket operation error: {}", Common::NativeErrorToString(e)); 236 LOG_ERROR(Network, "Socket operation error: {}", Common::NativeErrorToString(e));
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 26be74df4..a1e41faff 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -436,7 +436,7 @@ struct Memory::Impl {
436 } 436 }
437 437
438 if (Settings::IsFastmemEnabled()) { 438 if (Settings::IsFastmemEnabled()) {
439 const bool is_read_enable = Settings::IsGPULevelHigh() || !cached; 439 const bool is_read_enable = !Settings::IsGPULevelExtreme() || !cached;
440 system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached); 440 system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached);
441 } 441 }
442 442
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
index f0bd84ab2..c7d7d5fef 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
@@ -137,6 +137,15 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, Scal
137 case IR::Attribute::VertexId: 137 case IR::Attribute::VertexId:
138 ctx.Add("MOV.F {}.x,{}.id;", inst, ctx.attrib_name); 138 ctx.Add("MOV.F {}.x,{}.id;", inst, ctx.attrib_name);
139 break; 139 break;
140 case IR::Attribute::BaseInstance:
141 ctx.Add("MOV.F {}.x,{}.baseInstance;", inst, ctx.attrib_name);
142 break;
143 case IR::Attribute::BaseVertex:
144 ctx.Add("MOV.F {}.x,{}.baseVertex;", inst, ctx.attrib_name);
145 break;
146 case IR::Attribute::DrawID:
147 ctx.Add("MOV.F {}.x,{}.draw.id;", inst, ctx.attrib_name);
148 break;
140 case IR::Attribute::FrontFace: 149 case IR::Attribute::FrontFace:
141 ctx.Add("CMP.F {}.x,{}.facing.x,0,-1;", inst, ctx.attrib_name); 150 ctx.Add("CMP.F {}.x,{}.facing.x,0,-1;", inst, ctx.attrib_name);
142 break; 151 break;
@@ -156,6 +165,15 @@ void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, S
156 case IR::Attribute::VertexId: 165 case IR::Attribute::VertexId:
157 ctx.Add("MOV.S {}.x,{}.id;", inst, ctx.attrib_name); 166 ctx.Add("MOV.S {}.x,{}.id;", inst, ctx.attrib_name);
158 break; 167 break;
168 case IR::Attribute::BaseInstance:
169 ctx.Add("MOV.S {}.x,{}.baseInstance;", inst, ctx.attrib_name);
170 break;
171 case IR::Attribute::BaseVertex:
172 ctx.Add("MOV.S {}.x,{}.baseVertex;", inst, ctx.attrib_name);
173 break;
174 case IR::Attribute::DrawID:
175 ctx.Add("MOV.S {}.x,{}.draw.id;", inst, ctx.attrib_name);
176 break;
159 default: 177 default:
160 throw NotImplementedException("Get U32 attribute {}", attr); 178 throw NotImplementedException("Get U32 attribute {}", attr);
161 } 179 }
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl.cpp b/src/shader_recompiler/backend/glsl/emit_glsl.cpp
index e8a4390f6..d91e04446 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl.cpp
@@ -219,7 +219,7 @@ std::string EmitGLSL(const Profile& profile, const RuntimeInfo& runtime_info, IR
219 EmitContext ctx{program, bindings, profile, runtime_info}; 219 EmitContext ctx{program, bindings, profile, runtime_info};
220 Precolor(program); 220 Precolor(program);
221 EmitCode(ctx, program); 221 EmitCode(ctx, program);
222 const std::string version{fmt::format("#version 450{}\n", GlslVersionSpecifier(ctx))}; 222 const std::string version{fmt::format("#version 460{}\n", GlslVersionSpecifier(ctx))};
223 ctx.header.insert(0, version); 223 ctx.header.insert(0, version);
224 if (program.shared_memory_size > 0) { 224 if (program.shared_memory_size > 0) {
225 const auto requested_size{program.shared_memory_size}; 225 const auto requested_size{program.shared_memory_size};
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
index 39579cf5d..2e369ed72 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
@@ -234,6 +234,15 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
234 case IR::Attribute::FrontFace: 234 case IR::Attribute::FrontFace:
235 ctx.AddF32("{}=itof(gl_FrontFacing?-1:0);", inst); 235 ctx.AddF32("{}=itof(gl_FrontFacing?-1:0);", inst);
236 break; 236 break;
237 case IR::Attribute::BaseInstance:
238 ctx.AddF32("{}=itof(gl_BaseInstance);", inst);
239 break;
240 case IR::Attribute::BaseVertex:
241 ctx.AddF32("{}=itof(gl_BaseVertex);", inst);
242 break;
243 case IR::Attribute::DrawID:
244 ctx.AddF32("{}=itof(gl_DrawID);", inst);
245 break;
237 default: 246 default:
238 throw NotImplementedException("Get attribute {}", attr); 247 throw NotImplementedException("Get attribute {}", attr);
239 } 248 }
@@ -250,6 +259,15 @@ void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, s
250 case IR::Attribute::VertexId: 259 case IR::Attribute::VertexId:
251 ctx.AddU32("{}=uint(gl_VertexID);", inst); 260 ctx.AddU32("{}=uint(gl_VertexID);", inst);
252 break; 261 break;
262 case IR::Attribute::BaseInstance:
263 ctx.AddU32("{}=uint(gl_BaseInstance);", inst);
264 break;
265 case IR::Attribute::BaseVertex:
266 ctx.AddU32("{}=uint(gl_BaseVertex);", inst);
267 break;
268 case IR::Attribute::DrawID:
269 ctx.AddU32("{}=uint(gl_DrawID);", inst);
270 break;
253 default: 271 default:
254 throw NotImplementedException("Get U32 attribute {}", attr); 272 throw NotImplementedException("Get U32 attribute {}", attr);
255 } 273 }
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
index 73b67f0af..0cd87a48f 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
@@ -321,8 +321,12 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
321 case IR::Attribute::PositionY: 321 case IR::Attribute::PositionY:
322 case IR::Attribute::PositionZ: 322 case IR::Attribute::PositionZ:
323 case IR::Attribute::PositionW: 323 case IR::Attribute::PositionW:
324 return ctx.OpLoad(ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_position, 324 return ctx.OpLoad(
325 ctx.Const(element))); 325 ctx.F32[1],
326 ctx.need_input_position_indirect
327 ? AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_position, ctx.u32_zero_value,
328 ctx.Const(element))
329 : AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_position, ctx.Const(element)));
326 case IR::Attribute::InstanceId: 330 case IR::Attribute::InstanceId:
327 if (ctx.profile.support_vertex_instance_id) { 331 if (ctx.profile.support_vertex_instance_id) {
328 return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.instance_id)); 332 return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.instance_id));
@@ -339,6 +343,12 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
339 const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_vertex)}; 343 const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_vertex)};
340 return ctx.OpBitcast(ctx.F32[1], ctx.OpISub(ctx.U32[1], index, base)); 344 return ctx.OpBitcast(ctx.F32[1], ctx.OpISub(ctx.U32[1], index, base));
341 } 345 }
346 case IR::Attribute::BaseInstance:
347 return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.base_instance));
348 case IR::Attribute::BaseVertex:
349 return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.base_vertex));
350 case IR::Attribute::DrawID:
351 return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.draw_index));
342 case IR::Attribute::FrontFace: 352 case IR::Attribute::FrontFace:
343 return ctx.OpSelect(ctx.F32[1], ctx.OpLoad(ctx.U1, ctx.front_face), 353 return ctx.OpSelect(ctx.F32[1], ctx.OpLoad(ctx.U1, ctx.front_face),
344 ctx.OpBitcast(ctx.F32[1], ctx.Const(std::numeric_limits<u32>::max())), 354 ctx.OpBitcast(ctx.F32[1], ctx.Const(std::numeric_limits<u32>::max())),
@@ -380,6 +390,12 @@ Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, Id) {
380 const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_vertex)}; 390 const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_vertex)};
381 return ctx.OpISub(ctx.U32[1], index, base); 391 return ctx.OpISub(ctx.U32[1], index, base);
382 } 392 }
393 case IR::Attribute::BaseInstance:
394 return ctx.OpLoad(ctx.U32[1], ctx.base_instance);
395 case IR::Attribute::BaseVertex:
396 return ctx.OpLoad(ctx.U32[1], ctx.base_vertex);
397 case IR::Attribute::DrawID:
398 return ctx.OpLoad(ctx.U32[1], ctx.draw_index);
383 default: 399 default:
384 throw NotImplementedException("Read U32 attribute {}", attr); 400 throw NotImplementedException("Read U32 attribute {}", attr);
385 } 401 }
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
index 2c90f2368..c5db19d09 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
@@ -58,11 +58,10 @@ Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) {
58 ctx.OpGroupNonUniformShuffle(ctx.U32[1], SubgroupScope(ctx), value, src_thread_id), value); 58 ctx.OpGroupNonUniformShuffle(ctx.U32[1], SubgroupScope(ctx), value, src_thread_id), value);
59} 59}
60 60
61Id GetUpperClamp(EmitContext& ctx, Id invocation_id, Id clamp) { 61Id AddPartitionBase(EmitContext& ctx, Id thread_id) {
62 const Id thirty_two{ctx.Const(32u)}; 62 const Id partition_idx{ctx.OpShiftRightLogical(ctx.U32[1], GetThreadId(ctx), ctx.Const(5u))};
63 const Id is_upper_partition{ctx.OpSGreaterThanEqual(ctx.U1, invocation_id, thirty_two)}; 63 const Id partition_base{ctx.OpShiftLeftLogical(ctx.U32[1], partition_idx, ctx.Const(5u))};
64 const Id upper_clamp{ctx.OpIAdd(ctx.U32[1], thirty_two, clamp)}; 64 return ctx.OpIAdd(ctx.U32[1], thread_id, partition_base);
65 return ctx.OpSelect(ctx.U32[1], is_upper_partition, upper_clamp, clamp);
66} 65}
67} // Anonymous namespace 66} // Anonymous namespace
68 67
@@ -145,64 +144,63 @@ Id EmitSubgroupGeMask(EmitContext& ctx) {
145Id EmitShuffleIndex(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, 144Id EmitShuffleIndex(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
146 Id segmentation_mask) { 145 Id segmentation_mask) {
147 const Id not_seg_mask{ctx.OpNot(ctx.U32[1], segmentation_mask)}; 146 const Id not_seg_mask{ctx.OpNot(ctx.U32[1], segmentation_mask)};
148 const Id thread_id{GetThreadId(ctx)}; 147 const Id thread_id{EmitLaneId(ctx)};
149 if (ctx.profile.warp_size_potentially_larger_than_guest) {
150 const Id thirty_two{ctx.Const(32u)};
151 const Id is_upper_partition{ctx.OpSGreaterThanEqual(ctx.U1, thread_id, thirty_two)};
152 const Id upper_index{ctx.OpIAdd(ctx.U32[1], thirty_two, index)};
153 const Id upper_clamp{ctx.OpIAdd(ctx.U32[1], thirty_two, clamp)};
154 index = ctx.OpSelect(ctx.U32[1], is_upper_partition, upper_index, index);
155 clamp = ctx.OpSelect(ctx.U32[1], is_upper_partition, upper_clamp, clamp);
156 }
157 const Id min_thread_id{ComputeMinThreadId(ctx, thread_id, segmentation_mask)}; 148 const Id min_thread_id{ComputeMinThreadId(ctx, thread_id, segmentation_mask)};
158 const Id max_thread_id{ComputeMaxThreadId(ctx, min_thread_id, clamp, not_seg_mask)}; 149 const Id max_thread_id{ComputeMaxThreadId(ctx, min_thread_id, clamp, not_seg_mask)};
159 150
160 const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], index, not_seg_mask)}; 151 const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], index, not_seg_mask)};
161 const Id src_thread_id{ctx.OpBitwiseOr(ctx.U32[1], lhs, min_thread_id)}; 152 Id src_thread_id{ctx.OpBitwiseOr(ctx.U32[1], lhs, min_thread_id)};
162 const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)}; 153 const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)};
163 154
155 if (ctx.profile.warp_size_potentially_larger_than_guest) {
156 src_thread_id = AddPartitionBase(ctx, src_thread_id);
157 }
158
164 SetInBoundsFlag(inst, in_range); 159 SetInBoundsFlag(inst, in_range);
165 return SelectValue(ctx, in_range, value, src_thread_id); 160 return SelectValue(ctx, in_range, value, src_thread_id);
166} 161}
167 162
168Id EmitShuffleUp(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, 163Id EmitShuffleUp(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
169 Id segmentation_mask) { 164 Id segmentation_mask) {
170 const Id thread_id{GetThreadId(ctx)}; 165 const Id thread_id{EmitLaneId(ctx)};
171 if (ctx.profile.warp_size_potentially_larger_than_guest) {
172 clamp = GetUpperClamp(ctx, thread_id, clamp);
173 }
174 const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)}; 166 const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)};
175 const Id src_thread_id{ctx.OpISub(ctx.U32[1], thread_id, index)}; 167 Id src_thread_id{ctx.OpISub(ctx.U32[1], thread_id, index)};
176 const Id in_range{ctx.OpSGreaterThanEqual(ctx.U1, src_thread_id, max_thread_id)}; 168 const Id in_range{ctx.OpSGreaterThanEqual(ctx.U1, src_thread_id, max_thread_id)};
177 169
170 if (ctx.profile.warp_size_potentially_larger_than_guest) {
171 src_thread_id = AddPartitionBase(ctx, src_thread_id);
172 }
173
178 SetInBoundsFlag(inst, in_range); 174 SetInBoundsFlag(inst, in_range);
179 return SelectValue(ctx, in_range, value, src_thread_id); 175 return SelectValue(ctx, in_range, value, src_thread_id);
180} 176}
181 177
182Id EmitShuffleDown(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, 178Id EmitShuffleDown(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
183 Id segmentation_mask) { 179 Id segmentation_mask) {
184 const Id thread_id{GetThreadId(ctx)}; 180 const Id thread_id{EmitLaneId(ctx)};
185 if (ctx.profile.warp_size_potentially_larger_than_guest) {
186 clamp = GetUpperClamp(ctx, thread_id, clamp);
187 }
188 const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)}; 181 const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)};
189 const Id src_thread_id{ctx.OpIAdd(ctx.U32[1], thread_id, index)}; 182 Id src_thread_id{ctx.OpIAdd(ctx.U32[1], thread_id, index)};
190 const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)}; 183 const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)};
191 184
185 if (ctx.profile.warp_size_potentially_larger_than_guest) {
186 src_thread_id = AddPartitionBase(ctx, src_thread_id);
187 }
188
192 SetInBoundsFlag(inst, in_range); 189 SetInBoundsFlag(inst, in_range);
193 return SelectValue(ctx, in_range, value, src_thread_id); 190 return SelectValue(ctx, in_range, value, src_thread_id);
194} 191}
195 192
196Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, 193Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
197 Id segmentation_mask) { 194 Id segmentation_mask) {
198 const Id thread_id{GetThreadId(ctx)}; 195 const Id thread_id{EmitLaneId(ctx)};
199 if (ctx.profile.warp_size_potentially_larger_than_guest) {
200 clamp = GetUpperClamp(ctx, thread_id, clamp);
201 }
202 const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)}; 196 const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)};
203 const Id src_thread_id{ctx.OpBitwiseXor(ctx.U32[1], thread_id, index)}; 197 Id src_thread_id{ctx.OpBitwiseXor(ctx.U32[1], thread_id, index)};
204 const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)}; 198 const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)};
205 199
200 if (ctx.profile.warp_size_potentially_larger_than_guest) {
201 src_thread_id = AddPartitionBase(ctx, src_thread_id);
202 }
203
206 SetInBoundsFlag(inst, in_range); 204 SetInBoundsFlag(inst, in_range);
207 return SelectValue(ctx, in_range, value, src_thread_id); 205 return SelectValue(ctx, in_range, value, src_thread_id);
208} 206}
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index 41dc6d031..a0c155fdb 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -544,7 +544,7 @@ void EmitContext::DefineCommonTypes(const Info& info) {
544 U16 = Name(TypeInt(16, false), "u16"); 544 U16 = Name(TypeInt(16, false), "u16");
545 S16 = Name(TypeInt(16, true), "s16"); 545 S16 = Name(TypeInt(16, true), "s16");
546 } 546 }
547 if (info.uses_int64) { 547 if (info.uses_int64 && profile.support_int64) {
548 AddCapability(spv::Capability::Int64); 548 AddCapability(spv::Capability::Int64);
549 U64 = Name(TypeInt(64, false), "u64"); 549 U64 = Name(TypeInt(64, false), "u64");
550 } 550 }
@@ -721,9 +721,21 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) {
721 size_t label_index{0}; 721 size_t label_index{0};
722 if (info.loads.AnyComponent(IR::Attribute::PositionX)) { 722 if (info.loads.AnyComponent(IR::Attribute::PositionX)) {
723 AddLabel(labels[label_index]); 723 AddLabel(labels[label_index]);
724 const Id pointer{is_array 724 const Id pointer{[&]() {
725 ? OpAccessChain(input_f32, input_position, vertex, masked_index) 725 if (need_input_position_indirect) {
726 : OpAccessChain(input_f32, input_position, masked_index)}; 726 if (is_array)
727 return OpAccessChain(input_f32, input_position, vertex, u32_zero_value,
728 masked_index);
729 else
730 return OpAccessChain(input_f32, input_position, u32_zero_value,
731 masked_index);
732 } else {
733 if (is_array)
734 return OpAccessChain(input_f32, input_position, vertex, masked_index);
735 else
736 return OpAccessChain(input_f32, input_position, masked_index);
737 }
738 }()};
727 const Id result{OpLoad(F32[1], pointer)}; 739 const Id result{OpLoad(F32[1], pointer)};
728 OpReturnValue(result); 740 OpReturnValue(result);
729 ++label_index; 741 ++label_index;
@@ -1367,30 +1379,56 @@ void EmitContext::DefineInputs(const IR::Program& program) {
1367 Decorate(layer, spv::Decoration::Flat); 1379 Decorate(layer, spv::Decoration::Flat);
1368 } 1380 }
1369 if (loads.AnyComponent(IR::Attribute::PositionX)) { 1381 if (loads.AnyComponent(IR::Attribute::PositionX)) {
1370 const bool is_fragment{stage != Stage::Fragment}; 1382 const bool is_fragment{stage == Stage::Fragment};
1371 const spv::BuiltIn built_in{is_fragment ? spv::BuiltIn::Position : spv::BuiltIn::FragCoord}; 1383 if (!is_fragment && profile.has_broken_spirv_position_input) {
1372 input_position = DefineInput(*this, F32[4], true, built_in); 1384 need_input_position_indirect = true;
1373 if (profile.support_geometry_shader_passthrough) { 1385
1374 if (info.passthrough.AnyComponent(IR::Attribute::PositionX)) { 1386 const Id input_position_struct = TypeStruct(F32[4]);
1375 Decorate(input_position, spv::Decoration::PassthroughNV); 1387 input_position = DefineInput(*this, input_position_struct, true);
1388
1389 MemberDecorate(input_position_struct, 0, spv::Decoration::BuiltIn,
1390 static_cast<unsigned>(spv::BuiltIn::Position));
1391 Decorate(input_position_struct, spv::Decoration::Block);
1392 } else {
1393 const spv::BuiltIn built_in{is_fragment ? spv::BuiltIn::FragCoord
1394 : spv::BuiltIn::Position};
1395 input_position = DefineInput(*this, F32[4], true, built_in);
1396
1397 if (profile.support_geometry_shader_passthrough) {
1398 if (info.passthrough.AnyComponent(IR::Attribute::PositionX)) {
1399 Decorate(input_position, spv::Decoration::PassthroughNV);
1400 }
1376 } 1401 }
1377 } 1402 }
1378 } 1403 }
1379 if (loads[IR::Attribute::InstanceId]) { 1404 if (loads[IR::Attribute::InstanceId]) {
1380 if (profile.support_vertex_instance_id) { 1405 if (profile.support_vertex_instance_id) {
1381 instance_id = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceId); 1406 instance_id = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceId);
1407 if (loads[IR::Attribute::BaseInstance]) {
1408 base_instance = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseVertex);
1409 }
1382 } else { 1410 } else {
1383 instance_index = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceIndex); 1411 instance_index = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceIndex);
1384 base_instance = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseInstance); 1412 base_instance = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseInstance);
1385 } 1413 }
1414 } else if (loads[IR::Attribute::BaseInstance]) {
1415 base_instance = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseInstance);
1386 } 1416 }
1387 if (loads[IR::Attribute::VertexId]) { 1417 if (loads[IR::Attribute::VertexId]) {
1388 if (profile.support_vertex_instance_id) { 1418 if (profile.support_vertex_instance_id) {
1389 vertex_id = DefineInput(*this, U32[1], true, spv::BuiltIn::VertexId); 1419 vertex_id = DefineInput(*this, U32[1], true, spv::BuiltIn::VertexId);
1420 if (loads[IR::Attribute::BaseVertex]) {
1421 base_vertex = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseVertex);
1422 }
1390 } else { 1423 } else {
1391 vertex_index = DefineInput(*this, U32[1], true, spv::BuiltIn::VertexIndex); 1424 vertex_index = DefineInput(*this, U32[1], true, spv::BuiltIn::VertexIndex);
1392 base_vertex = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseVertex); 1425 base_vertex = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseVertex);
1393 } 1426 }
1427 } else if (loads[IR::Attribute::BaseVertex]) {
1428 base_vertex = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseVertex);
1429 }
1430 if (loads[IR::Attribute::DrawID]) {
1431 draw_index = DefineInput(*this, U32[1], true, spv::BuiltIn::DrawIndex);
1394 } 1432 }
1395 if (loads[IR::Attribute::FrontFace]) { 1433 if (loads[IR::Attribute::FrontFace]) {
1396 front_face = DefineInput(*this, U1, true, spv::BuiltIn::FrontFacing); 1434 front_face = DefineInput(*this, U1, true, spv::BuiltIn::FrontFacing);
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
index dde45b4bc..dbc5c55b9 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
@@ -218,6 +218,7 @@ public:
218 Id base_instance{}; 218 Id base_instance{};
219 Id vertex_id{}; 219 Id vertex_id{};
220 Id vertex_index{}; 220 Id vertex_index{};
221 Id draw_index{};
221 Id base_vertex{}; 222 Id base_vertex{};
222 Id front_face{}; 223 Id front_face{};
223 Id point_coord{}; 224 Id point_coord{};
@@ -279,6 +280,7 @@ public:
279 Id write_global_func_u32x2{}; 280 Id write_global_func_u32x2{};
280 Id write_global_func_u32x4{}; 281 Id write_global_func_u32x4{};
281 282
283 bool need_input_position_indirect{};
282 Id input_position{}; 284 Id input_position{};
283 std::array<Id, 32> input_generics{}; 285 std::array<Id, 32> input_generics{};
284 286
diff --git a/src/shader_recompiler/environment.h b/src/shader_recompiler/environment.h
index 402f2664f..26e8307c1 100644
--- a/src/shader_recompiler/environment.h
+++ b/src/shader_recompiler/environment.h
@@ -34,6 +34,11 @@ public:
34 34
35 [[nodiscard]] virtual std::array<u32, 3> WorkgroupSize() const = 0; 35 [[nodiscard]] virtual std::array<u32, 3> WorkgroupSize() const = 0;
36 36
37 [[nodiscard]] virtual bool HasHLEMacroState() const = 0;
38
39 [[nodiscard]] virtual std::optional<ReplaceConstant> GetReplaceConstBuffer(u32 bank,
40 u32 offset) = 0;
41
37 virtual void Dump(u64 hash) = 0; 42 virtual void Dump(u64 hash) = 0;
38 43
39 [[nodiscard]] const ProgramHeader& SPH() const noexcept { 44 [[nodiscard]] const ProgramHeader& SPH() const noexcept {
@@ -52,11 +57,16 @@ public:
52 return start_address; 57 return start_address;
53 } 58 }
54 59
60 [[nodiscard]] bool IsPropietaryDriver() const noexcept {
61 return is_propietary_driver;
62 }
63
55protected: 64protected:
56 ProgramHeader sph{}; 65 ProgramHeader sph{};
57 std::array<u32, 8> gp_passthrough_mask{}; 66 std::array<u32, 8> gp_passthrough_mask{};
58 Stage stage{}; 67 Stage stage{};
59 u32 start_address{}; 68 u32 start_address{};
69 bool is_propietary_driver{};
60}; 70};
61 71
62} // namespace Shader 72} // namespace Shader
diff --git a/src/shader_recompiler/frontend/ir/attribute.cpp b/src/shader_recompiler/frontend/ir/attribute.cpp
index 7d3d882e4..1bf9db935 100644
--- a/src/shader_recompiler/frontend/ir/attribute.cpp
+++ b/src/shader_recompiler/frontend/ir/attribute.cpp
@@ -446,6 +446,12 @@ std::string NameOf(Attribute attribute) {
446 return "ViewportMask"; 446 return "ViewportMask";
447 case Attribute::FrontFace: 447 case Attribute::FrontFace:
448 return "FrontFace"; 448 return "FrontFace";
449 case Attribute::BaseInstance:
450 return "BaseInstance";
451 case Attribute::BaseVertex:
452 return "BaseVertex";
453 case Attribute::DrawID:
454 return "DrawID";
449 } 455 }
450 return fmt::format("<reserved attribute {}>", static_cast<int>(attribute)); 456 return fmt::format("<reserved attribute {}>", static_cast<int>(attribute));
451} 457}
diff --git a/src/shader_recompiler/frontend/ir/attribute.h b/src/shader_recompiler/frontend/ir/attribute.h
index 6ee3947b1..5f039b6f6 100644
--- a/src/shader_recompiler/frontend/ir/attribute.h
+++ b/src/shader_recompiler/frontend/ir/attribute.h
@@ -219,6 +219,11 @@ enum class Attribute : u64 {
219 FixedFncTexture9Q = 231, 219 FixedFncTexture9Q = 231,
220 ViewportMask = 232, 220 ViewportMask = 232,
221 FrontFace = 255, 221 FrontFace = 255,
222
223 // Implementation attributes
224 BaseInstance = 256,
225 BaseVertex = 257,
226 DrawID = 258,
222}; 227};
223 228
224constexpr size_t NUM_GENERICS = 32; 229constexpr size_t NUM_GENERICS = 32;
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
index 0cdac0eff..eb2e49a68 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
@@ -294,6 +294,14 @@ F32 IREmitter::GetAttribute(IR::Attribute attribute, const U32& vertex) {
294 return Inst<F32>(Opcode::GetAttribute, attribute, vertex); 294 return Inst<F32>(Opcode::GetAttribute, attribute, vertex);
295} 295}
296 296
297U32 IREmitter::GetAttributeU32(IR::Attribute attribute) {
298 return GetAttributeU32(attribute, Imm32(0));
299}
300
301U32 IREmitter::GetAttributeU32(IR::Attribute attribute, const U32& vertex) {
302 return Inst<U32>(Opcode::GetAttributeU32, attribute, vertex);
303}
304
297void IREmitter::SetAttribute(IR::Attribute attribute, const F32& value, const U32& vertex) { 305void IREmitter::SetAttribute(IR::Attribute attribute, const F32& value, const U32& vertex) {
298 Inst(Opcode::SetAttribute, attribute, value, vertex); 306 Inst(Opcode::SetAttribute, attribute, value, vertex);
299} 307}
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h
index 2df992feb..7aaaa4ab0 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.h
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.h
@@ -74,6 +74,8 @@ public:
74 74
75 [[nodiscard]] F32 GetAttribute(IR::Attribute attribute); 75 [[nodiscard]] F32 GetAttribute(IR::Attribute attribute);
76 [[nodiscard]] F32 GetAttribute(IR::Attribute attribute, const U32& vertex); 76 [[nodiscard]] F32 GetAttribute(IR::Attribute attribute, const U32& vertex);
77 [[nodiscard]] U32 GetAttributeU32(IR::Attribute attribute);
78 [[nodiscard]] U32 GetAttributeU32(IR::Attribute attribute, const U32& vertex);
77 void SetAttribute(IR::Attribute attribute, const F32& value, const U32& vertex); 79 void SetAttribute(IR::Attribute attribute, const F32& value, const U32& vertex);
78 80
79 [[nodiscard]] F32 GetAttributeIndexed(const U32& phys_address); 81 [[nodiscard]] F32 GetAttributeIndexed(const U32& phys_address);
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
index 3adbd2b16..a42453e90 100644
--- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
@@ -171,6 +171,70 @@ std::map<IR::Attribute, IR::Attribute> GenerateLegacyToGenericMappings(
171 } 171 }
172 return mapping; 172 return mapping;
173} 173}
174
175void EmitGeometryPassthrough(IR::IREmitter& ir, const IR::Program& program,
176 const Shader::VaryingState& passthrough_mask,
177 bool passthrough_position,
178 std::optional<IR::Attribute> passthrough_layer_attr) {
179 for (u32 i = 0; i < program.output_vertices; i++) {
180 // Assign generics from input
181 for (u32 j = 0; j < 32; j++) {
182 if (!passthrough_mask.Generic(j)) {
183 continue;
184 }
185
186 const IR::Attribute attr = IR::Attribute::Generic0X + (j * 4);
187 ir.SetAttribute(attr + 0, ir.GetAttribute(attr + 0, ir.Imm32(i)), ir.Imm32(0));
188 ir.SetAttribute(attr + 1, ir.GetAttribute(attr + 1, ir.Imm32(i)), ir.Imm32(0));
189 ir.SetAttribute(attr + 2, ir.GetAttribute(attr + 2, ir.Imm32(i)), ir.Imm32(0));
190 ir.SetAttribute(attr + 3, ir.GetAttribute(attr + 3, ir.Imm32(i)), ir.Imm32(0));
191 }
192
193 if (passthrough_position) {
194 // Assign position from input
195 const IR::Attribute attr = IR::Attribute::PositionX;
196 ir.SetAttribute(attr + 0, ir.GetAttribute(attr + 0, ir.Imm32(i)), ir.Imm32(0));
197 ir.SetAttribute(attr + 1, ir.GetAttribute(attr + 1, ir.Imm32(i)), ir.Imm32(0));
198 ir.SetAttribute(attr + 2, ir.GetAttribute(attr + 2, ir.Imm32(i)), ir.Imm32(0));
199 ir.SetAttribute(attr + 3, ir.GetAttribute(attr + 3, ir.Imm32(i)), ir.Imm32(0));
200 }
201
202 if (passthrough_layer_attr) {
203 // Assign layer
204 ir.SetAttribute(IR::Attribute::Layer, ir.GetAttribute(*passthrough_layer_attr),
205 ir.Imm32(0));
206 }
207
208 // Emit vertex
209 ir.EmitVertex(ir.Imm32(0));
210 }
211 ir.EndPrimitive(ir.Imm32(0));
212}
213
214u32 GetOutputTopologyVertices(OutputTopology output_topology) {
215 switch (output_topology) {
216 case OutputTopology::PointList:
217 return 1;
218 case OutputTopology::LineStrip:
219 return 2;
220 default:
221 return 3;
222 }
223}
224
225void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInfo& host_info) {
226 for (IR::Block* const block : program.blocks) {
227 for (IR::Inst& inst : block->Instructions()) {
228 if (inst.GetOpcode() == IR::Opcode::Epilogue) {
229 IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)};
230 EmitGeometryPassthrough(
231 ir, program, program.info.passthrough,
232 program.info.passthrough.AnyComponent(IR::Attribute::PositionX), {});
233 }
234 }
235 }
236}
237
174} // Anonymous namespace 238} // Anonymous namespace
175 239
176IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool, 240IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
@@ -195,9 +259,14 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
195 program.is_geometry_passthrough = sph.common0.geometry_passthrough != 0; 259 program.is_geometry_passthrough = sph.common0.geometry_passthrough != 0;
196 if (program.is_geometry_passthrough) { 260 if (program.is_geometry_passthrough) {
197 const auto& mask{env.GpPassthroughMask()}; 261 const auto& mask{env.GpPassthroughMask()};
198 for (size_t i = 0; i < program.info.passthrough.mask.size(); ++i) { 262 for (size_t i = 0; i < mask.size() * 32; ++i) {
199 program.info.passthrough.mask[i] = ((mask[i / 32] >> (i % 32)) & 1) == 0; 263 program.info.passthrough.mask[i] = ((mask[i / 32] >> (i % 32)) & 1) == 0;
200 } 264 }
265
266 if (!host_info.support_geometry_shader_passthrough) {
267 program.output_vertices = GetOutputTopologyVertices(program.output_topology);
268 LowerGeometryPassthrough(program, host_info);
269 }
201 } 270 }
202 break; 271 break;
203 } 272 }
@@ -219,11 +288,11 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
219 } 288 }
220 Optimization::SsaRewritePass(program); 289 Optimization::SsaRewritePass(program);
221 290
222 Optimization::ConstantPropagationPass(program); 291 Optimization::ConstantPropagationPass(env, program);
223 292
224 Optimization::PositionPass(env, program); 293 Optimization::PositionPass(env, program);
225 294
226 Optimization::GlobalMemoryToStorageBufferPass(program); 295 Optimization::GlobalMemoryToStorageBufferPass(program, host_info);
227 Optimization::TexturePass(env, program, host_info); 296 Optimization::TexturePass(env, program, host_info);
228 297
229 if (Settings::values.resolution_info.active) { 298 if (Settings::values.resolution_info.active) {
@@ -342,17 +411,7 @@ IR::Program GenerateGeometryPassthrough(ObjectPool<IR::Inst>& inst_pool,
342 IR::Program program; 411 IR::Program program;
343 program.stage = Stage::Geometry; 412 program.stage = Stage::Geometry;
344 program.output_topology = output_topology; 413 program.output_topology = output_topology;
345 switch (output_topology) { 414 program.output_vertices = GetOutputTopologyVertices(output_topology);
346 case OutputTopology::PointList:
347 program.output_vertices = 1;
348 break;
349 case OutputTopology::LineStrip:
350 program.output_vertices = 2;
351 break;
352 default:
353 program.output_vertices = 3;
354 break;
355 }
356 415
357 program.is_geometry_passthrough = false; 416 program.is_geometry_passthrough = false;
358 program.info.loads.mask = source_program.info.stores.mask; 417 program.info.loads.mask = source_program.info.stores.mask;
@@ -366,35 +425,8 @@ IR::Program GenerateGeometryPassthrough(ObjectPool<IR::Inst>& inst_pool,
366 node.data.block = current_block; 425 node.data.block = current_block;
367 426
368 IR::IREmitter ir{*current_block}; 427 IR::IREmitter ir{*current_block};
369 for (u32 i = 0; i < program.output_vertices; i++) { 428 EmitGeometryPassthrough(ir, program, program.info.stores, true,
370 // Assign generics from input 429 source_program.info.emulated_layer);
371 for (u32 j = 0; j < 32; j++) {
372 if (!program.info.stores.Generic(j)) {
373 continue;
374 }
375
376 const IR::Attribute attr = IR::Attribute::Generic0X + (j * 4);
377 ir.SetAttribute(attr + 0, ir.GetAttribute(attr + 0, ir.Imm32(i)), ir.Imm32(0));
378 ir.SetAttribute(attr + 1, ir.GetAttribute(attr + 1, ir.Imm32(i)), ir.Imm32(0));
379 ir.SetAttribute(attr + 2, ir.GetAttribute(attr + 2, ir.Imm32(i)), ir.Imm32(0));
380 ir.SetAttribute(attr + 3, ir.GetAttribute(attr + 3, ir.Imm32(i)), ir.Imm32(0));
381 }
382
383 // Assign position from input
384 const IR::Attribute attr = IR::Attribute::PositionX;
385 ir.SetAttribute(attr + 0, ir.GetAttribute(attr + 0, ir.Imm32(i)), ir.Imm32(0));
386 ir.SetAttribute(attr + 1, ir.GetAttribute(attr + 1, ir.Imm32(i)), ir.Imm32(0));
387 ir.SetAttribute(attr + 2, ir.GetAttribute(attr + 2, ir.Imm32(i)), ir.Imm32(0));
388 ir.SetAttribute(attr + 3, ir.GetAttribute(attr + 3, ir.Imm32(i)), ir.Imm32(0));
389
390 // Assign layer
391 ir.SetAttribute(IR::Attribute::Layer, ir.GetAttribute(source_program.info.emulated_layer),
392 ir.Imm32(0));
393
394 // Emit vertex
395 ir.EmitVertex(ir.Imm32(0));
396 }
397 ir.EndPrimitive(ir.Imm32(0));
398 430
399 IR::Block* return_block{block_pool.Create(inst_pool)}; 431 IR::Block* return_block{block_pool.Create(inst_pool)};
400 IR::IREmitter{*return_block}.Epilogue(); 432 IR::IREmitter{*return_block}.Epilogue();
diff --git a/src/shader_recompiler/host_translate_info.h b/src/shader_recompiler/host_translate_info.h
index d5d279554..55fc48768 100644
--- a/src/shader_recompiler/host_translate_info.h
+++ b/src/shader_recompiler/host_translate_info.h
@@ -15,6 +15,9 @@ struct HostTranslateInfo {
15 bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered 15 bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered
16 bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers 16 bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers
17 bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS 17 bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS
18 u32 min_ssbo_alignment{}; ///< Minimum alignment supported by the device for SSBOs
19 bool support_geometry_shader_passthrough{}; ///< True when the device supports geometry
20 ///< passthrough shaders
18}; 21};
19 22
20} // namespace Shader 23} // namespace Shader
diff --git a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
index 826f9a54a..4d81e9336 100644
--- a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
+++ b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
@@ -7,6 +7,7 @@
7#include <type_traits> 7#include <type_traits>
8 8
9#include "common/bit_cast.h" 9#include "common/bit_cast.h"
10#include "shader_recompiler/environment.h"
10#include "shader_recompiler/exception.h" 11#include "shader_recompiler/exception.h"
11#include "shader_recompiler/frontend/ir/ir_emitter.h" 12#include "shader_recompiler/frontend/ir/ir_emitter.h"
12#include "shader_recompiler/frontend/ir/value.h" 13#include "shader_recompiler/frontend/ir/value.h"
@@ -515,6 +516,9 @@ void FoldBitCast(IR::Inst& inst, IR::Opcode reverse) {
515 case IR::Attribute::PrimitiveId: 516 case IR::Attribute::PrimitiveId:
516 case IR::Attribute::InstanceId: 517 case IR::Attribute::InstanceId:
517 case IR::Attribute::VertexId: 518 case IR::Attribute::VertexId:
519 case IR::Attribute::BaseVertex:
520 case IR::Attribute::BaseInstance:
521 case IR::Attribute::DrawID:
518 break; 522 break;
519 default: 523 default:
520 return; 524 return;
@@ -644,7 +648,63 @@ void FoldFSwizzleAdd(IR::Block& block, IR::Inst& inst) {
644 } 648 }
645} 649}
646 650
647void ConstantPropagation(IR::Block& block, IR::Inst& inst) { 651void FoldConstBuffer(Environment& env, IR::Block& block, IR::Inst& inst) {
652 const IR::Value bank{inst.Arg(0)};
653 const IR::Value offset{inst.Arg(1)};
654 if (!bank.IsImmediate() || !offset.IsImmediate()) {
655 return;
656 }
657 const auto bank_value = bank.U32();
658 const auto offset_value = offset.U32();
659 auto replacement = env.GetReplaceConstBuffer(bank_value, offset_value);
660 if (!replacement) {
661 return;
662 }
663 const auto new_attribute = [replacement]() {
664 switch (*replacement) {
665 case ReplaceConstant::BaseInstance:
666 return IR::Attribute::BaseInstance;
667 case ReplaceConstant::BaseVertex:
668 return IR::Attribute::BaseVertex;
669 case ReplaceConstant::DrawID:
670 return IR::Attribute::DrawID;
671 default:
672 throw NotImplementedException("Not implemented replacement variable {}", *replacement);
673 }
674 }();
675 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
676 if (inst.GetOpcode() == IR::Opcode::GetCbufU32) {
677 inst.ReplaceUsesWith(ir.GetAttributeU32(new_attribute));
678 } else {
679 inst.ReplaceUsesWith(ir.GetAttribute(new_attribute));
680 }
681}
682
683void FoldDriverConstBuffer(Environment& env, IR::Block& block, IR::Inst& inst, u32 which_bank,
684 u32 offset_start = 0, u32 offset_end = std::numeric_limits<u16>::max()) {
685 const IR::Value bank{inst.Arg(0)};
686 const IR::Value offset{inst.Arg(1)};
687 if (!bank.IsImmediate() || !offset.IsImmediate()) {
688 return;
689 }
690 const auto bank_value = bank.U32();
691 if (bank_value != which_bank) {
692 return;
693 }
694 const auto offset_value = offset.U32();
695 if (offset_value < offset_start || offset_value >= offset_end) {
696 return;
697 }
698 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
699 if (inst.GetOpcode() == IR::Opcode::GetCbufU32) {
700 inst.ReplaceUsesWith(IR::Value{env.ReadCbufValue(bank_value, offset_value)});
701 } else {
702 inst.ReplaceUsesWith(
703 IR::Value{Common::BitCast<f32>(env.ReadCbufValue(bank_value, offset_value))});
704 }
705}
706
707void ConstantPropagation(Environment& env, IR::Block& block, IR::Inst& inst) {
648 switch (inst.GetOpcode()) { 708 switch (inst.GetOpcode()) {
649 case IR::Opcode::GetRegister: 709 case IR::Opcode::GetRegister:
650 return FoldGetRegister(inst); 710 return FoldGetRegister(inst);
@@ -789,18 +849,28 @@ void ConstantPropagation(IR::Block& block, IR::Inst& inst) {
789 IR::Opcode::CompositeInsertF16x4); 849 IR::Opcode::CompositeInsertF16x4);
790 case IR::Opcode::FSwizzleAdd: 850 case IR::Opcode::FSwizzleAdd:
791 return FoldFSwizzleAdd(block, inst); 851 return FoldFSwizzleAdd(block, inst);
852 case IR::Opcode::GetCbufF32:
853 case IR::Opcode::GetCbufU32:
854 if (env.HasHLEMacroState()) {
855 FoldConstBuffer(env, block, inst);
856 }
857 if (env.IsPropietaryDriver()) {
858 FoldDriverConstBuffer(env, block, inst, 1);
859 }
860 break;
792 default: 861 default:
793 break; 862 break;
794 } 863 }
795} 864}
865
796} // Anonymous namespace 866} // Anonymous namespace
797 867
798void ConstantPropagationPass(IR::Program& program) { 868void ConstantPropagationPass(Environment& env, IR::Program& program) {
799 const auto end{program.post_order_blocks.rend()}; 869 const auto end{program.post_order_blocks.rend()};
800 for (auto it = program.post_order_blocks.rbegin(); it != end; ++it) { 870 for (auto it = program.post_order_blocks.rbegin(); it != end; ++it) {
801 IR::Block* const block{*it}; 871 IR::Block* const block{*it};
802 for (IR::Inst& inst : block->Instructions()) { 872 for (IR::Inst& inst : block->Instructions()) {
803 ConstantPropagation(*block, inst); 873 ConstantPropagation(env, *block, inst);
804 } 874 }
805 } 875 }
806} 876}
diff --git a/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp b/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp
index 336338e62..9101722ba 100644
--- a/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp
+++ b/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp
@@ -11,6 +11,7 @@
11#include "shader_recompiler/frontend/ir/breadth_first_search.h" 11#include "shader_recompiler/frontend/ir/breadth_first_search.h"
12#include "shader_recompiler/frontend/ir/ir_emitter.h" 12#include "shader_recompiler/frontend/ir/ir_emitter.h"
13#include "shader_recompiler/frontend/ir/value.h" 13#include "shader_recompiler/frontend/ir/value.h"
14#include "shader_recompiler/host_translate_info.h"
14#include "shader_recompiler/ir_opt/passes.h" 15#include "shader_recompiler/ir_opt/passes.h"
15 16
16namespace Shader::Optimization { 17namespace Shader::Optimization {
@@ -402,7 +403,7 @@ void CollectStorageBuffers(IR::Block& block, IR::Inst& inst, StorageInfo& info)
402} 403}
403 404
404/// Returns the offset in indices (not bytes) for an equivalent storage instruction 405/// Returns the offset in indices (not bytes) for an equivalent storage instruction
405IR::U32 StorageOffset(IR::Block& block, IR::Inst& inst, StorageBufferAddr buffer) { 406IR::U32 StorageOffset(IR::Block& block, IR::Inst& inst, StorageBufferAddr buffer, u32 alignment) {
406 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; 407 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
407 IR::U32 offset; 408 IR::U32 offset;
408 if (const std::optional<LowAddrInfo> low_addr{TrackLowAddress(&inst)}) { 409 if (const std::optional<LowAddrInfo> low_addr{TrackLowAddress(&inst)}) {
@@ -415,7 +416,10 @@ IR::U32 StorageOffset(IR::Block& block, IR::Inst& inst, StorageBufferAddr buffer
415 } 416 }
416 // Subtract the least significant 32 bits from the guest offset. The result is the storage 417 // Subtract the least significant 32 bits from the guest offset. The result is the storage
417 // buffer offset in bytes. 418 // buffer offset in bytes.
418 const IR::U32 low_cbuf{ir.GetCbuf(ir.Imm32(buffer.index), ir.Imm32(buffer.offset))}; 419 IR::U32 low_cbuf{ir.GetCbuf(ir.Imm32(buffer.index), ir.Imm32(buffer.offset))};
420
421 // Align the offset base to match the host alignment requirements
422 low_cbuf = ir.BitwiseAnd(low_cbuf, ir.Imm32(~(alignment - 1U)));
419 return ir.ISub(offset, low_cbuf); 423 return ir.ISub(offset, low_cbuf);
420} 424}
421 425
@@ -510,7 +514,7 @@ void Replace(IR::Block& block, IR::Inst& inst, const IR::U32& storage_index,
510} 514}
511} // Anonymous namespace 515} // Anonymous namespace
512 516
513void GlobalMemoryToStorageBufferPass(IR::Program& program) { 517void GlobalMemoryToStorageBufferPass(IR::Program& program, const HostTranslateInfo& host_info) {
514 StorageInfo info; 518 StorageInfo info;
515 for (IR::Block* const block : program.post_order_blocks) { 519 for (IR::Block* const block : program.post_order_blocks) {
516 for (IR::Inst& inst : block->Instructions()) { 520 for (IR::Inst& inst : block->Instructions()) {
@@ -534,7 +538,8 @@ void GlobalMemoryToStorageBufferPass(IR::Program& program) {
534 const IR::U32 index{IR::Value{static_cast<u32>(info.set.index_of(it))}}; 538 const IR::U32 index{IR::Value{static_cast<u32>(info.set.index_of(it))}};
535 IR::Block* const block{storage_inst.block}; 539 IR::Block* const block{storage_inst.block};
536 IR::Inst* const inst{storage_inst.inst}; 540 IR::Inst* const inst{storage_inst.inst};
537 const IR::U32 offset{StorageOffset(*block, *inst, storage_buffer)}; 541 const IR::U32 offset{
542 StorageOffset(*block, *inst, storage_buffer, host_info.min_ssbo_alignment)};
538 Replace(*block, *inst, index, offset); 543 Replace(*block, *inst, index, offset);
539 } 544 }
540} 545}
diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h
index 11bfe801a..4ffad1172 100644
--- a/src/shader_recompiler/ir_opt/passes.h
+++ b/src/shader_recompiler/ir_opt/passes.h
@@ -13,9 +13,9 @@ struct HostTranslateInfo;
13namespace Shader::Optimization { 13namespace Shader::Optimization {
14 14
15void CollectShaderInfoPass(Environment& env, IR::Program& program); 15void CollectShaderInfoPass(Environment& env, IR::Program& program);
16void ConstantPropagationPass(IR::Program& program); 16void ConstantPropagationPass(Environment& env, IR::Program& program);
17void DeadCodeEliminationPass(IR::Program& program); 17void DeadCodeEliminationPass(IR::Program& program);
18void GlobalMemoryToStorageBufferPass(IR::Program& program); 18void GlobalMemoryToStorageBufferPass(IR::Program& program, const HostTranslateInfo& host_info);
19void IdentityRemovalPass(IR::Program& program); 19void IdentityRemovalPass(IR::Program& program);
20void LowerFp16ToFp32(IR::Program& program); 20void LowerFp16ToFp32(IR::Program& program);
21void LowerInt64ToInt32(IR::Program& program); 21void LowerInt64ToInt32(IR::Program& program);
diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h
index b8841a536..253e0d0bd 100644
--- a/src/shader_recompiler/profile.h
+++ b/src/shader_recompiler/profile.h
@@ -55,6 +55,8 @@ struct Profile {
55 55
56 /// OpFClamp is broken and OpFMax + OpFMin should be used instead 56 /// OpFClamp is broken and OpFMax + OpFMin should be used instead
57 bool has_broken_spirv_clamp{}; 57 bool has_broken_spirv_clamp{};
58 /// The Position builtin needs to be wrapped in a struct when used as an input
59 bool has_broken_spirv_position_input{};
58 /// Offset image operands with an unsigned type do not work 60 /// Offset image operands with an unsigned type do not work
59 bool has_broken_unsigned_image_offsets{}; 61 bool has_broken_unsigned_image_offsets{};
60 /// Signed instructions with unsigned data types are misinterpreted 62 /// Signed instructions with unsigned data types are misinterpreted
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h
index d9c6e92db..f93181e1e 100644
--- a/src/shader_recompiler/shader_info.h
+++ b/src/shader_recompiler/shader_info.h
@@ -16,6 +16,12 @@
16 16
17namespace Shader { 17namespace Shader {
18 18
19enum class ReplaceConstant : u32 {
20 BaseInstance,
21 BaseVertex,
22 DrawID,
23};
24
19enum class TextureType : u32 { 25enum class TextureType : u32 {
20 Color1D, 26 Color1D,
21 ColorArray1D, 27 ColorArray1D,
@@ -59,6 +65,8 @@ enum class Interpolation {
59struct ConstantBufferDescriptor { 65struct ConstantBufferDescriptor {
60 u32 index; 66 u32 index;
61 u32 count; 67 u32 count;
68
69 auto operator<=>(const ConstantBufferDescriptor&) const = default;
62}; 70};
63 71
64struct StorageBufferDescriptor { 72struct StorageBufferDescriptor {
@@ -66,6 +74,8 @@ struct StorageBufferDescriptor {
66 u32 cbuf_offset; 74 u32 cbuf_offset;
67 u32 count; 75 u32 count;
68 bool is_written; 76 bool is_written;
77
78 auto operator<=>(const StorageBufferDescriptor&) const = default;
69}; 79};
70 80
71struct TextureBufferDescriptor { 81struct TextureBufferDescriptor {
@@ -78,6 +88,8 @@ struct TextureBufferDescriptor {
78 u32 secondary_shift_left; 88 u32 secondary_shift_left;
79 u32 count; 89 u32 count;
80 u32 size_shift; 90 u32 size_shift;
91
92 auto operator<=>(const TextureBufferDescriptor&) const = default;
81}; 93};
82using TextureBufferDescriptors = boost::container::small_vector<TextureBufferDescriptor, 6>; 94using TextureBufferDescriptors = boost::container::small_vector<TextureBufferDescriptor, 6>;
83 95
@@ -89,6 +101,8 @@ struct ImageBufferDescriptor {
89 u32 cbuf_offset; 101 u32 cbuf_offset;
90 u32 count; 102 u32 count;
91 u32 size_shift; 103 u32 size_shift;
104
105 auto operator<=>(const ImageBufferDescriptor&) const = default;
92}; 106};
93using ImageBufferDescriptors = boost::container::small_vector<ImageBufferDescriptor, 2>; 107using ImageBufferDescriptors = boost::container::small_vector<ImageBufferDescriptor, 2>;
94 108
@@ -104,6 +118,8 @@ struct TextureDescriptor {
104 u32 secondary_shift_left; 118 u32 secondary_shift_left;
105 u32 count; 119 u32 count;
106 u32 size_shift; 120 u32 size_shift;
121
122 auto operator<=>(const TextureDescriptor&) const = default;
107}; 123};
108using TextureDescriptors = boost::container::small_vector<TextureDescriptor, 12>; 124using TextureDescriptors = boost::container::small_vector<TextureDescriptor, 12>;
109 125
@@ -116,6 +132,8 @@ struct ImageDescriptor {
116 u32 cbuf_offset; 132 u32 cbuf_offset;
117 u32 count; 133 u32 count;
118 u32 size_shift; 134 u32 size_shift;
135
136 auto operator<=>(const ImageDescriptor&) const = default;
119}; 137};
120using ImageDescriptors = boost::container::small_vector<ImageDescriptor, 4>; 138using ImageDescriptors = boost::container::small_vector<ImageDescriptor, 4>;
121 139
diff --git a/src/shader_recompiler/varying_state.h b/src/shader_recompiler/varying_state.h
index 7b28a285f..18a9aaf50 100644
--- a/src/shader_recompiler/varying_state.h
+++ b/src/shader_recompiler/varying_state.h
@@ -11,7 +11,7 @@
11namespace Shader { 11namespace Shader {
12 12
13struct VaryingState { 13struct VaryingState {
14 std::bitset<256> mask{}; 14 std::bitset<512> mask{};
15 15
16 void Set(IR::Attribute attribute, bool state = true) { 16 void Set(IR::Attribute attribute, bool state = true) {
17 mask[static_cast<size_t>(attribute)] = state; 17 mask[static_cast<size_t>(attribute)] = state;
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 6a4022e45..9b65e79cb 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -7,6 +7,7 @@ add_executable(tests
7 common/fibers.cpp 7 common/fibers.cpp
8 common/host_memory.cpp 8 common/host_memory.cpp
9 common/param_package.cpp 9 common/param_package.cpp
10 common/range_map.cpp
10 common/ring_buffer.cpp 11 common/ring_buffer.cpp
11 common/scratch_buffer.cpp 12 common/scratch_buffer.cpp
12 common/unique_function.cpp 13 common/unique_function.cpp
diff --git a/src/tests/common/range_map.cpp b/src/tests/common/range_map.cpp
new file mode 100644
index 000000000..5a4630a38
--- /dev/null
+++ b/src/tests/common/range_map.cpp
@@ -0,0 +1,70 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include <stdexcept>
5
6#include <catch2/catch.hpp>
7
8#include "common/range_map.h"
9
10enum class MappedEnum : u32 {
11 Invalid = 0,
12 Valid_1 = 1,
13 Valid_2 = 2,
14 Valid_3 = 3,
15};
16
17TEST_CASE("Range Map: Setup", "[video_core]") {
18 Common::RangeMap<u64, MappedEnum> my_map(MappedEnum::Invalid);
19 my_map.Map(3000, 3500, MappedEnum::Valid_1);
20 my_map.Unmap(3200, 3600);
21 my_map.Map(4000, 4500, MappedEnum::Valid_2);
22 my_map.Map(4200, 4400, MappedEnum::Valid_2);
23 my_map.Map(4200, 4400, MappedEnum::Valid_1);
24 REQUIRE(my_map.GetContinousSizeFrom(4200) == 200);
25 REQUIRE(my_map.GetContinousSizeFrom(3000) == 200);
26 REQUIRE(my_map.GetContinousSizeFrom(2900) == 0);
27
28 REQUIRE(my_map.GetValueAt(2900) == MappedEnum::Invalid);
29 REQUIRE(my_map.GetValueAt(3100) == MappedEnum::Valid_1);
30 REQUIRE(my_map.GetValueAt(3000) == MappedEnum::Valid_1);
31 REQUIRE(my_map.GetValueAt(3200) == MappedEnum::Invalid);
32
33 REQUIRE(my_map.GetValueAt(4199) == MappedEnum::Valid_2);
34 REQUIRE(my_map.GetValueAt(4200) == MappedEnum::Valid_1);
35 REQUIRE(my_map.GetValueAt(4400) == MappedEnum::Valid_2);
36 REQUIRE(my_map.GetValueAt(4500) == MappedEnum::Invalid);
37 REQUIRE(my_map.GetValueAt(4600) == MappedEnum::Invalid);
38
39 my_map.Unmap(0, 6000);
40 for (u64 address = 0; address < 10000; address += 1000) {
41 REQUIRE(my_map.GetContinousSizeFrom(address) == 0);
42 }
43
44 my_map.Map(1000, 3000, MappedEnum::Valid_1);
45 my_map.Map(4000, 5000, MappedEnum::Valid_1);
46 my_map.Map(2500, 4100, MappedEnum::Valid_1);
47 REQUIRE(my_map.GetContinousSizeFrom(1000) == 4000);
48
49 my_map.Map(1000, 3000, MappedEnum::Valid_1);
50 my_map.Map(4000, 5000, MappedEnum::Valid_2);
51 my_map.Map(2500, 4100, MappedEnum::Valid_3);
52 REQUIRE(my_map.GetContinousSizeFrom(1000) == 1500);
53 REQUIRE(my_map.GetContinousSizeFrom(2500) == 1600);
54 REQUIRE(my_map.GetContinousSizeFrom(4100) == 900);
55 REQUIRE(my_map.GetValueAt(900) == MappedEnum::Invalid);
56 REQUIRE(my_map.GetValueAt(1000) == MappedEnum::Valid_1);
57 REQUIRE(my_map.GetValueAt(2500) == MappedEnum::Valid_3);
58 REQUIRE(my_map.GetValueAt(4100) == MappedEnum::Valid_2);
59 REQUIRE(my_map.GetValueAt(5000) == MappedEnum::Invalid);
60
61 my_map.Map(2000, 6000, MappedEnum::Valid_3);
62 REQUIRE(my_map.GetContinousSizeFrom(1000) == 1000);
63 REQUIRE(my_map.GetContinousSizeFrom(3000) == 3000);
64 REQUIRE(my_map.GetValueAt(1000) == MappedEnum::Valid_1);
65 REQUIRE(my_map.GetValueAt(1999) == MappedEnum::Valid_1);
66 REQUIRE(my_map.GetValueAt(1500) == MappedEnum::Valid_1);
67 REQUIRE(my_map.GetValueAt(2001) == MappedEnum::Valid_3);
68 REQUIRE(my_map.GetValueAt(5999) == MappedEnum::Valid_3);
69 REQUIRE(my_map.GetValueAt(6000) == MappedEnum::Invalid);
70}
diff --git a/src/tests/video_core/buffer_base.cpp b/src/tests/video_core/buffer_base.cpp
index f7236afab..5cd0628f2 100644
--- a/src/tests/video_core/buffer_base.cpp
+++ b/src/tests/video_core/buffer_base.cpp
@@ -538,7 +538,7 @@ TEST_CASE("BufferBase: Cached write downloads") {
538 int num = 0; 538 int num = 0;
539 buffer.ForEachDownloadRangeAndClear(c, WORD, [&](u64 offset, u64 size) { ++num; }); 539 buffer.ForEachDownloadRangeAndClear(c, WORD, [&](u64 offset, u64 size) { ++num; });
540 buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { ++num; }); 540 buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { ++num; });
541 REQUIRE(num == 0); 541 REQUIRE(num == 1);
542 REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE)); 542 REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE));
543 REQUIRE(!buffer.IsRegionGpuModified(c + PAGE, PAGE)); 543 REQUIRE(!buffer.IsRegionGpuModified(c + PAGE, PAGE));
544 buffer.FlushCachedWrites(); 544 buffer.FlushCachedWrites();
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index fd71bf186..f617665de 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -13,6 +13,7 @@ add_library(video_core STATIC
13 buffer_cache/buffer_base.h 13 buffer_cache/buffer_base.h
14 buffer_cache/buffer_cache.cpp 14 buffer_cache/buffer_cache.cpp
15 buffer_cache/buffer_cache.h 15 buffer_cache/buffer_cache.h
16 cache_types.h
16 cdma_pusher.cpp 17 cdma_pusher.cpp
17 cdma_pusher.h 18 cdma_pusher.h
18 compatible_formats.cpp 19 compatible_formats.cpp
@@ -84,6 +85,7 @@ add_library(video_core STATIC
84 gpu.h 85 gpu.h
85 gpu_thread.cpp 86 gpu_thread.cpp
86 gpu_thread.h 87 gpu_thread.h
88 invalidation_accumulator.h
87 memory_manager.cpp 89 memory_manager.cpp
88 memory_manager.h 90 memory_manager.h
89 precompiled_headers.h 91 precompiled_headers.h
@@ -189,6 +191,8 @@ add_library(video_core STATIC
189 renderer_vulkan/vk_texture_cache.cpp 191 renderer_vulkan/vk_texture_cache.cpp
190 renderer_vulkan/vk_texture_cache.h 192 renderer_vulkan/vk_texture_cache.h
191 renderer_vulkan/vk_texture_cache_base.cpp 193 renderer_vulkan/vk_texture_cache_base.cpp
194 renderer_vulkan/vk_turbo_mode.cpp
195 renderer_vulkan/vk_turbo_mode.h
192 renderer_vulkan/vk_update_descriptor.cpp 196 renderer_vulkan/vk_update_descriptor.cpp
193 renderer_vulkan/vk_update_descriptor.h 197 renderer_vulkan/vk_update_descriptor.h
194 shader_cache.cpp 198 shader_cache.cpp
diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h
index 92d77eef2..c47b7d866 100644
--- a/src/video_core/buffer_cache/buffer_base.h
+++ b/src/video_core/buffer_cache/buffer_base.h
@@ -430,7 +430,7 @@ private:
430 if (query_begin >= SizeBytes() || size < 0) { 430 if (query_begin >= SizeBytes() || size < 0) {
431 return; 431 return;
432 } 432 }
433 u64* const untracked_words = Array<Type::Untracked>(); 433 [[maybe_unused]] u64* const untracked_words = Array<Type::Untracked>();
434 u64* const state_words = Array<type>(); 434 u64* const state_words = Array<type>();
435 const u64 query_end = query_begin + std::min(static_cast<u64>(size), SizeBytes()); 435 const u64 query_end = query_begin + std::min(static_cast<u64>(size), SizeBytes());
436 u64* const words_begin = state_words + query_begin / BYTES_PER_WORD; 436 u64* const words_begin = state_words + query_begin / BYTES_PER_WORD;
@@ -483,7 +483,7 @@ private:
483 NotifyRasterizer<true>(word_index, current_bits, ~u64{0}); 483 NotifyRasterizer<true>(word_index, current_bits, ~u64{0});
484 } 484 }
485 // Exclude CPU modified pages when visiting GPU pages 485 // Exclude CPU modified pages when visiting GPU pages
486 const u64 word = current_word & ~(type == Type::GPU ? untracked_words[word_index] : 0); 486 const u64 word = current_word;
487 u64 page = page_begin; 487 u64 page = page_begin;
488 page_begin = 0; 488 page_begin = 0;
489 489
@@ -531,7 +531,7 @@ private:
531 [[nodiscard]] bool IsRegionModified(u64 offset, u64 size) const noexcept { 531 [[nodiscard]] bool IsRegionModified(u64 offset, u64 size) const noexcept {
532 static_assert(type != Type::Untracked); 532 static_assert(type != Type::Untracked);
533 533
534 const u64* const untracked_words = Array<Type::Untracked>(); 534 [[maybe_unused]] const u64* const untracked_words = Array<Type::Untracked>();
535 const u64* const state_words = Array<type>(); 535 const u64* const state_words = Array<type>();
536 const u64 num_query_words = size / BYTES_PER_WORD + 1; 536 const u64 num_query_words = size / BYTES_PER_WORD + 1;
537 const u64 word_begin = offset / BYTES_PER_WORD; 537 const u64 word_begin = offset / BYTES_PER_WORD;
@@ -539,8 +539,7 @@ private:
539 const u64 page_limit = Common::DivCeil(offset + size, BYTES_PER_PAGE); 539 const u64 page_limit = Common::DivCeil(offset + size, BYTES_PER_PAGE);
540 u64 page_index = (offset / BYTES_PER_PAGE) % PAGES_PER_WORD; 540 u64 page_index = (offset / BYTES_PER_PAGE) % PAGES_PER_WORD;
541 for (u64 word_index = word_begin; word_index < word_end; ++word_index, page_index = 0) { 541 for (u64 word_index = word_begin; word_index < word_end; ++word_index, page_index = 0) {
542 const u64 off_word = type == Type::GPU ? untracked_words[word_index] : 0; 542 const u64 word = state_words[word_index];
543 const u64 word = state_words[word_index] & ~off_word;
544 if (word == 0) { 543 if (word == 0) {
545 continue; 544 continue;
546 } 545 }
@@ -564,7 +563,7 @@ private:
564 [[nodiscard]] std::pair<u64, u64> ModifiedRegion(u64 offset, u64 size) const noexcept { 563 [[nodiscard]] std::pair<u64, u64> ModifiedRegion(u64 offset, u64 size) const noexcept {
565 static_assert(type != Type::Untracked); 564 static_assert(type != Type::Untracked);
566 565
567 const u64* const untracked_words = Array<Type::Untracked>(); 566 [[maybe_unused]] const u64* const untracked_words = Array<Type::Untracked>();
568 const u64* const state_words = Array<type>(); 567 const u64* const state_words = Array<type>();
569 const u64 num_query_words = size / BYTES_PER_WORD + 1; 568 const u64 num_query_words = size / BYTES_PER_WORD + 1;
570 const u64 word_begin = offset / BYTES_PER_WORD; 569 const u64 word_begin = offset / BYTES_PER_WORD;
@@ -574,8 +573,7 @@ private:
574 u64 begin = std::numeric_limits<u64>::max(); 573 u64 begin = std::numeric_limits<u64>::max();
575 u64 end = 0; 574 u64 end = 0;
576 for (u64 word_index = word_begin; word_index < word_end; ++word_index) { 575 for (u64 word_index = word_begin; word_index < word_end; ++word_index) {
577 const u64 off_word = type == Type::GPU ? untracked_words[word_index] : 0; 576 const u64 word = state_words[word_index];
578 const u64 word = state_words[word_index] & ~off_word;
579 if (word == 0) { 577 if (word == 0) {
580 continue; 578 continue;
581 } 579 }
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index f1c60d1f3..627917ab6 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -200,7 +200,16 @@ public:
200 /// Return true when a CPU region is modified from the CPU 200 /// Return true when a CPU region is modified from the CPU
201 [[nodiscard]] bool IsRegionCpuModified(VAddr addr, size_t size); 201 [[nodiscard]] bool IsRegionCpuModified(VAddr addr, size_t size);
202 202
203 std::mutex mutex; 203 void SetDrawIndirect(
204 const Tegra::Engines::DrawManager::IndirectParams* current_draw_indirect_) {
205 current_draw_indirect = current_draw_indirect_;
206 }
207
208 [[nodiscard]] std::pair<Buffer*, u32> GetDrawIndirectCount();
209
210 [[nodiscard]] std::pair<Buffer*, u32> GetDrawIndirectBuffer();
211
212 std::recursive_mutex mutex;
204 Runtime& runtime; 213 Runtime& runtime;
205 214
206private: 215private:
@@ -272,6 +281,8 @@ private:
272 281
273 void BindHostVertexBuffers(); 282 void BindHostVertexBuffers();
274 283
284 void BindHostDrawIndirectBuffers();
285
275 void BindHostGraphicsUniformBuffers(size_t stage); 286 void BindHostGraphicsUniformBuffers(size_t stage);
276 287
277 void BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 binding_index, bool needs_bind); 288 void BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 binding_index, bool needs_bind);
@@ -298,6 +309,8 @@ private:
298 309
299 void UpdateVertexBuffer(u32 index); 310 void UpdateVertexBuffer(u32 index);
300 311
312 void UpdateDrawIndirect();
313
301 void UpdateUniformBuffers(size_t stage); 314 void UpdateUniformBuffers(size_t stage);
302 315
303 void UpdateStorageBuffers(size_t stage); 316 void UpdateStorageBuffers(size_t stage);
@@ -372,6 +385,8 @@ private:
372 SlotVector<Buffer> slot_buffers; 385 SlotVector<Buffer> slot_buffers;
373 DelayedDestructionRing<Buffer, 8> delayed_destruction_ring; 386 DelayedDestructionRing<Buffer, 8> delayed_destruction_ring;
374 387
388 const Tegra::Engines::DrawManager::IndirectParams* current_draw_indirect{};
389
375 u32 last_index_count = 0; 390 u32 last_index_count = 0;
376 391
377 Binding index_buffer; 392 Binding index_buffer;
@@ -380,6 +395,8 @@ private:
380 std::array<std::array<Binding, NUM_STORAGE_BUFFERS>, NUM_STAGES> storage_buffers; 395 std::array<std::array<Binding, NUM_STORAGE_BUFFERS>, NUM_STAGES> storage_buffers;
381 std::array<std::array<TextureBufferBinding, NUM_TEXTURE_BUFFERS>, NUM_STAGES> texture_buffers; 396 std::array<std::array<TextureBufferBinding, NUM_TEXTURE_BUFFERS>, NUM_STAGES> texture_buffers;
382 std::array<Binding, NUM_TRANSFORM_FEEDBACK_BUFFERS> transform_feedback_buffers; 397 std::array<Binding, NUM_TRANSFORM_FEEDBACK_BUFFERS> transform_feedback_buffers;
398 Binding count_buffer_binding;
399 Binding indirect_buffer_binding;
383 400
384 std::array<Binding, NUM_COMPUTE_UNIFORM_BUFFERS> compute_uniform_buffers; 401 std::array<Binding, NUM_COMPUTE_UNIFORM_BUFFERS> compute_uniform_buffers;
385 std::array<Binding, NUM_STORAGE_BUFFERS> compute_storage_buffers; 402 std::array<Binding, NUM_STORAGE_BUFFERS> compute_storage_buffers;
@@ -674,6 +691,9 @@ void BufferCache<P>::BindHostGeometryBuffers(bool is_indexed) {
674 } 691 }
675 BindHostVertexBuffers(); 692 BindHostVertexBuffers();
676 BindHostTransformFeedbackBuffers(); 693 BindHostTransformFeedbackBuffers();
694 if (current_draw_indirect) {
695 BindHostDrawIndirectBuffers();
696 }
677} 697}
678 698
679template <class P> 699template <class P>
@@ -823,6 +843,7 @@ bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept {
823template <class P> 843template <class P>
824void BufferCache<P>::CommitAsyncFlushesHigh() { 844void BufferCache<P>::CommitAsyncFlushesHigh() {
825 AccumulateFlushes(); 845 AccumulateFlushes();
846
826 if (committed_ranges.empty()) { 847 if (committed_ranges.empty()) {
827 return; 848 return;
828 } 849 }
@@ -869,7 +890,7 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
869 buffer_id, 890 buffer_id,
870 }); 891 });
871 // Align up to avoid cache conflicts 892 // Align up to avoid cache conflicts
872 constexpr u64 align = 256ULL; 893 constexpr u64 align = 8ULL;
873 constexpr u64 mask = ~(align - 1ULL); 894 constexpr u64 mask = ~(align - 1ULL);
874 total_size_bytes += (new_size + align - 1) & mask; 895 total_size_bytes += (new_size + align - 1) & mask;
875 largest_copy = std::max(largest_copy, new_size); 896 largest_copy = std::max(largest_copy, new_size);
@@ -1042,6 +1063,19 @@ void BufferCache<P>::BindHostVertexBuffers() {
1042} 1063}
1043 1064
1044template <class P> 1065template <class P>
1066void BufferCache<P>::BindHostDrawIndirectBuffers() {
1067 const auto bind_buffer = [this](const Binding& binding) {
1068 Buffer& buffer = slot_buffers[binding.buffer_id];
1069 TouchBuffer(buffer, binding.buffer_id);
1070 SynchronizeBuffer(buffer, binding.cpu_addr, binding.size);
1071 };
1072 if (current_draw_indirect->include_count) {
1073 bind_buffer(count_buffer_binding);
1074 }
1075 bind_buffer(indirect_buffer_binding);
1076}
1077
1078template <class P>
1045void BufferCache<P>::BindHostGraphicsUniformBuffers(size_t stage) { 1079void BufferCache<P>::BindHostGraphicsUniformBuffers(size_t stage) {
1046 u32 dirty = ~0U; 1080 u32 dirty = ~0U;
1047 if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) { 1081 if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) {
@@ -1272,6 +1306,9 @@ void BufferCache<P>::DoUpdateGraphicsBuffers(bool is_indexed) {
1272 UpdateStorageBuffers(stage); 1306 UpdateStorageBuffers(stage);
1273 UpdateTextureBuffers(stage); 1307 UpdateTextureBuffers(stage);
1274 } 1308 }
1309 if (current_draw_indirect) {
1310 UpdateDrawIndirect();
1311 }
1275 } while (has_deleted_buffers); 1312 } while (has_deleted_buffers);
1276} 1313}
1277 1314
@@ -1289,7 +1326,7 @@ void BufferCache<P>::UpdateIndexBuffer() {
1289 const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); 1326 const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
1290 const auto& index_array = draw_state.index_buffer; 1327 const auto& index_array = draw_state.index_buffer;
1291 auto& flags = maxwell3d->dirty.flags; 1328 auto& flags = maxwell3d->dirty.flags;
1292 if (!flags[Dirty::IndexBuffer] && last_index_count == index_array.count) { 1329 if (!flags[Dirty::IndexBuffer]) {
1293 return; 1330 return;
1294 } 1331 }
1295 flags[Dirty::IndexBuffer] = false; 1332 flags[Dirty::IndexBuffer] = false;
@@ -1362,6 +1399,27 @@ void BufferCache<P>::UpdateVertexBuffer(u32 index) {
1362} 1399}
1363 1400
1364template <class P> 1401template <class P>
1402void BufferCache<P>::UpdateDrawIndirect() {
1403 const auto update = [this](GPUVAddr gpu_addr, size_t size, Binding& binding) {
1404 const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
1405 if (!cpu_addr) {
1406 binding = NULL_BINDING;
1407 return;
1408 }
1409 binding = Binding{
1410 .cpu_addr = *cpu_addr,
1411 .size = static_cast<u32>(size),
1412 .buffer_id = FindBuffer(*cpu_addr, static_cast<u32>(size)),
1413 };
1414 };
1415 if (current_draw_indirect->include_count) {
1416 update(current_draw_indirect->count_start_address, sizeof(u32), count_buffer_binding);
1417 }
1418 update(current_draw_indirect->indirect_start_address, current_draw_indirect->buffer_size,
1419 indirect_buffer_binding);
1420}
1421
1422template <class P>
1365void BufferCache<P>::UpdateUniformBuffers(size_t stage) { 1423void BufferCache<P>::UpdateUniformBuffers(size_t stage) {
1366 ForEachEnabledBit(enabled_uniform_buffer_masks[stage], [&](u32 index) { 1424 ForEachEnabledBit(enabled_uniform_buffer_masks[stage], [&](u32 index) {
1367 Binding& binding = uniform_buffers[stage][index]; 1425 Binding& binding = uniform_buffers[stage][index];
@@ -1880,14 +1938,21 @@ typename BufferCache<P>::Binding BufferCache<P>::StorageBufferBinding(GPUVAddr s
1880 bool is_written) const { 1938 bool is_written) const {
1881 const GPUVAddr gpu_addr = gpu_memory->Read<u64>(ssbo_addr); 1939 const GPUVAddr gpu_addr = gpu_memory->Read<u64>(ssbo_addr);
1882 const u32 size = gpu_memory->Read<u32>(ssbo_addr + 8); 1940 const u32 size = gpu_memory->Read<u32>(ssbo_addr + 8);
1883 const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); 1941 const u32 alignment = runtime.GetStorageBufferAlignment();
1942
1943 const GPUVAddr aligned_gpu_addr = Common::AlignDown(gpu_addr, alignment);
1944 const u32 aligned_size =
1945 Common::AlignUp(static_cast<u32>(gpu_addr - aligned_gpu_addr) + size, alignment);
1946
1947 const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(aligned_gpu_addr);
1884 if (!cpu_addr || size == 0) { 1948 if (!cpu_addr || size == 0) {
1885 return NULL_BINDING; 1949 return NULL_BINDING;
1886 } 1950 }
1887 const VAddr cpu_end = Common::AlignUp(*cpu_addr + size, Core::Memory::YUZU_PAGESIZE); 1951
1952 const VAddr cpu_end = Common::AlignUp(*cpu_addr + aligned_size, Core::Memory::YUZU_PAGESIZE);
1888 const Binding binding{ 1953 const Binding binding{
1889 .cpu_addr = *cpu_addr, 1954 .cpu_addr = *cpu_addr,
1890 .size = is_written ? size : static_cast<u32>(cpu_end - *cpu_addr), 1955 .size = is_written ? aligned_size : static_cast<u32>(cpu_end - *cpu_addr),
1891 .buffer_id = BufferId{}, 1956 .buffer_id = BufferId{},
1892 }; 1957 };
1893 return binding; 1958 return binding;
@@ -1941,4 +2006,16 @@ bool BufferCache<P>::HasFastUniformBufferBound(size_t stage, u32 binding_index)
1941 } 2006 }
1942} 2007}
1943 2008
2009template <class P>
2010std::pair<typename BufferCache<P>::Buffer*, u32> BufferCache<P>::GetDrawIndirectCount() {
2011 auto& buffer = slot_buffers[count_buffer_binding.buffer_id];
2012 return std::make_pair(&buffer, buffer.Offset(count_buffer_binding.cpu_addr));
2013}
2014
2015template <class P>
2016std::pair<typename BufferCache<P>::Buffer*, u32> BufferCache<P>::GetDrawIndirectBuffer() {
2017 auto& buffer = slot_buffers[indirect_buffer_binding.buffer_id];
2018 return std::make_pair(&buffer, buffer.Offset(indirect_buffer_binding.cpu_addr));
2019}
2020
1944} // namespace VideoCommon 2021} // namespace VideoCommon
diff --git a/src/video_core/cache_types.h b/src/video_core/cache_types.h
new file mode 100644
index 000000000..1a5db3c55
--- /dev/null
+++ b/src/video_core/cache_types.h
@@ -0,0 +1,24 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_funcs.h"
7#include "common/common_types.h"
8
9namespace VideoCommon {
10
11enum class CacheType : u32 {
12 None = 0,
13 TextureCache = 1 << 0,
14 QueryCache = 1 << 1,
15 BufferCache = 1 << 2,
16 ShaderCache = 1 << 3,
17 NoTextureCache = QueryCache | BufferCache | ShaderCache,
18 NoBufferCache = TextureCache | QueryCache | ShaderCache,
19 NoQueryCache = TextureCache | BufferCache | ShaderCache,
20 All = TextureCache | QueryCache | BufferCache | ShaderCache,
21};
22DECLARE_ENUM_FLAG_OPERATORS(CacheType)
23
24} // namespace VideoCommon
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp
index 322de2606..551929824 100644
--- a/src/video_core/dma_pusher.cpp
+++ b/src/video_core/dma_pusher.cpp
@@ -61,7 +61,7 @@ bool DmaPusher::Step() {
61 } else { 61 } else {
62 const CommandListHeader command_list_header{ 62 const CommandListHeader command_list_header{
63 command_list.command_lists[dma_pushbuffer_subindex++]}; 63 command_list.command_lists[dma_pushbuffer_subindex++]};
64 const GPUVAddr dma_get = command_list_header.addr; 64 dma_state.dma_get = command_list_header.addr;
65 65
66 if (dma_pushbuffer_subindex >= command_list.command_lists.size()) { 66 if (dma_pushbuffer_subindex >= command_list.command_lists.size()) {
67 // We've gone through the current list, remove it from the queue 67 // We've gone through the current list, remove it from the queue
@@ -75,12 +75,22 @@ bool DmaPusher::Step() {
75 75
76 // Push buffer non-empty, read a word 76 // Push buffer non-empty, read a word
77 command_headers.resize_destructive(command_list_header.size); 77 command_headers.resize_destructive(command_list_header.size);
78 if (Settings::IsGPULevelHigh()) { 78 constexpr u32 MacroRegistersStart = 0xE00;
79 memory_manager.ReadBlock(dma_get, command_headers.data(), 79 if (dma_state.method < MacroRegistersStart) {
80 command_list_header.size * sizeof(u32)); 80 if (Settings::IsGPULevelHigh()) {
81 memory_manager.ReadBlock(dma_state.dma_get, command_headers.data(),
82 command_list_header.size * sizeof(u32));
83 } else {
84 memory_manager.ReadBlockUnsafe(dma_state.dma_get, command_headers.data(),
85 command_list_header.size * sizeof(u32));
86 }
81 } else { 87 } else {
82 memory_manager.ReadBlockUnsafe(dma_get, command_headers.data(), 88 const size_t copy_size = command_list_header.size * sizeof(u32);
83 command_list_header.size * sizeof(u32)); 89 if (subchannels[dma_state.subchannel]) {
90 subchannels[dma_state.subchannel]->current_dirty =
91 memory_manager.IsMemoryDirty(dma_state.dma_get, copy_size);
92 }
93 memory_manager.ReadBlockUnsafe(dma_state.dma_get, command_headers.data(), copy_size);
84 } 94 }
85 ProcessCommands(command_headers); 95 ProcessCommands(command_headers);
86 } 96 }
@@ -94,6 +104,7 @@ void DmaPusher::ProcessCommands(std::span<const CommandHeader> commands) {
94 104
95 if (dma_state.method_count) { 105 if (dma_state.method_count) {
96 // Data word of methods command 106 // Data word of methods command
107 dma_state.dma_word_offset = static_cast<u32>(index * sizeof(u32));
97 if (dma_state.non_incrementing) { 108 if (dma_state.non_incrementing) {
98 const u32 max_write = static_cast<u32>( 109 const u32 max_write = static_cast<u32>(
99 std::min<std::size_t>(index + dma_state.method_count, commands.size()) - index); 110 std::min<std::size_t>(index + dma_state.method_count, commands.size()) - index);
@@ -132,6 +143,8 @@ void DmaPusher::ProcessCommands(std::span<const CommandHeader> commands) {
132 case SubmissionMode::Inline: 143 case SubmissionMode::Inline:
133 dma_state.method = command_header.method; 144 dma_state.method = command_header.method;
134 dma_state.subchannel = command_header.subchannel; 145 dma_state.subchannel = command_header.subchannel;
146 dma_state.dma_word_offset = static_cast<u64>(
147 -static_cast<s64>(dma_state.dma_get)); // negate to set address as 0
135 CallMethod(command_header.arg_count); 148 CallMethod(command_header.arg_count);
136 dma_state.non_incrementing = true; 149 dma_state.non_incrementing = true;
137 dma_increment_once = false; 150 dma_increment_once = false;
@@ -164,8 +177,14 @@ void DmaPusher::CallMethod(u32 argument) const {
164 dma_state.method_count, 177 dma_state.method_count,
165 }); 178 });
166 } else { 179 } else {
167 subchannels[dma_state.subchannel]->CallMethod(dma_state.method, argument, 180 auto subchannel = subchannels[dma_state.subchannel];
168 dma_state.is_last_call); 181 if (!subchannel->execution_mask[dma_state.method]) [[likely]] {
182 subchannel->method_sink.emplace_back(dma_state.method, argument);
183 return;
184 }
185 subchannel->ConsumeSink();
186 subchannel->current_dma_segment = dma_state.dma_get + dma_state.dma_word_offset;
187 subchannel->CallMethod(dma_state.method, argument, dma_state.is_last_call);
169 } 188 }
170} 189}
171 190
@@ -174,8 +193,11 @@ void DmaPusher::CallMultiMethod(const u32* base_start, u32 num_methods) const {
174 puller.CallMultiMethod(dma_state.method, dma_state.subchannel, base_start, num_methods, 193 puller.CallMultiMethod(dma_state.method, dma_state.subchannel, base_start, num_methods,
175 dma_state.method_count); 194 dma_state.method_count);
176 } else { 195 } else {
177 subchannels[dma_state.subchannel]->CallMultiMethod(dma_state.method, base_start, 196 auto subchannel = subchannels[dma_state.subchannel];
178 num_methods, dma_state.method_count); 197 subchannel->ConsumeSink();
198 subchannel->current_dma_segment = dma_state.dma_get + dma_state.dma_word_offset;
199 subchannel->CallMultiMethod(dma_state.method, base_start, num_methods,
200 dma_state.method_count);
179 } 201 }
180} 202}
181 203
diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h
index 6f00de937..1cdb690ed 100644
--- a/src/video_core/dma_pusher.h
+++ b/src/video_core/dma_pusher.h
@@ -156,6 +156,8 @@ private:
156 u32 subchannel; ///< Current subchannel 156 u32 subchannel; ///< Current subchannel
157 u32 method_count; ///< Current method count 157 u32 method_count; ///< Current method count
158 u32 length_pending; ///< Large NI command length pending 158 u32 length_pending; ///< Large NI command length pending
159 GPUVAddr dma_get; ///< Currently read segment
160 u64 dma_word_offset; ///< Current word ofset from address
159 bool non_incrementing; ///< Current command's NI flag 161 bool non_incrementing; ///< Current command's NI flag
160 bool is_last_call; 162 bool is_last_call;
161 }; 163 };
diff --git a/src/video_core/engines/draw_manager.cpp b/src/video_core/engines/draw_manager.cpp
index 3a78421f6..2437121ce 100644
--- a/src/video_core/engines/draw_manager.cpp
+++ b/src/video_core/engines/draw_manager.cpp
@@ -91,6 +91,23 @@ void DrawManager::DrawIndex(PrimitiveTopology topology, u32 index_first, u32 ind
91 ProcessDraw(true, num_instances); 91 ProcessDraw(true, num_instances);
92} 92}
93 93
94void DrawManager::DrawArrayIndirect(PrimitiveTopology topology) {
95 draw_state.topology = topology;
96
97 ProcessDrawIndirect();
98}
99
100void DrawManager::DrawIndexedIndirect(PrimitiveTopology topology, u32 index_first,
101 u32 index_count) {
102 const auto& regs{maxwell3d->regs};
103 draw_state.topology = topology;
104 draw_state.index_buffer = regs.index_buffer;
105 draw_state.index_buffer.first = index_first;
106 draw_state.index_buffer.count = index_count;
107
108 ProcessDrawIndirect();
109}
110
94void DrawManager::SetInlineIndexBuffer(u32 index) { 111void DrawManager::SetInlineIndexBuffer(u32 index) {
95 draw_state.inline_index_draw_indexes.push_back(static_cast<u8>(index & 0x000000ff)); 112 draw_state.inline_index_draw_indexes.push_back(static_cast<u8>(index & 0x000000ff));
96 draw_state.inline_index_draw_indexes.push_back(static_cast<u8>((index & 0x0000ff00) >> 8)); 113 draw_state.inline_index_draw_indexes.push_back(static_cast<u8>((index & 0x0000ff00) >> 8));
@@ -198,4 +215,18 @@ void DrawManager::ProcessDraw(bool draw_indexed, u32 instance_count) {
198 maxwell3d->rasterizer->Draw(draw_indexed, instance_count); 215 maxwell3d->rasterizer->Draw(draw_indexed, instance_count);
199 } 216 }
200} 217}
218
219void DrawManager::ProcessDrawIndirect() {
220 LOG_TRACE(
221 HW_GPU,
222 "called, topology={}, is_indexed={}, includes_count={}, buffer_size={}, max_draw_count={}",
223 draw_state.topology, indirect_state.is_indexed, indirect_state.include_count,
224 indirect_state.buffer_size, indirect_state.max_draw_counts);
225
226 UpdateTopology();
227
228 if (maxwell3d->ShouldExecute()) {
229 maxwell3d->rasterizer->DrawIndirect();
230 }
231}
201} // namespace Tegra::Engines 232} // namespace Tegra::Engines
diff --git a/src/video_core/engines/draw_manager.h b/src/video_core/engines/draw_manager.h
index 0e6930a9c..58d1b2d59 100644
--- a/src/video_core/engines/draw_manager.h
+++ b/src/video_core/engines/draw_manager.h
@@ -32,6 +32,16 @@ public:
32 std::vector<u8> inline_index_draw_indexes; 32 std::vector<u8> inline_index_draw_indexes;
33 }; 33 };
34 34
35 struct IndirectParams {
36 bool is_indexed;
37 bool include_count;
38 GPUVAddr count_start_address;
39 GPUVAddr indirect_start_address;
40 size_t buffer_size;
41 size_t max_draw_counts;
42 size_t stride;
43 };
44
35 explicit DrawManager(Maxwell3D* maxwell_3d); 45 explicit DrawManager(Maxwell3D* maxwell_3d);
36 46
37 void ProcessMethodCall(u32 method, u32 argument); 47 void ProcessMethodCall(u32 method, u32 argument);
@@ -46,10 +56,22 @@ public:
46 void DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count, u32 base_index, 56 void DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count, u32 base_index,
47 u32 base_instance, u32 num_instances); 57 u32 base_instance, u32 num_instances);
48 58
59 void DrawArrayIndirect(PrimitiveTopology topology);
60
61 void DrawIndexedIndirect(PrimitiveTopology topology, u32 index_first, u32 index_count);
62
49 const State& GetDrawState() const { 63 const State& GetDrawState() const {
50 return draw_state; 64 return draw_state;
51 } 65 }
52 66
67 IndirectParams& GetIndirectParams() {
68 return indirect_state;
69 }
70
71 const IndirectParams& GetIndirectParams() const {
72 return indirect_state;
73 }
74
53private: 75private:
54 void SetInlineIndexBuffer(u32 index); 76 void SetInlineIndexBuffer(u32 index);
55 77
@@ -63,7 +85,10 @@ private:
63 85
64 void ProcessDraw(bool draw_indexed, u32 instance_count); 86 void ProcessDraw(bool draw_indexed, u32 instance_count);
65 87
88 void ProcessDrawIndirect();
89
66 Maxwell3D* maxwell3d{}; 90 Maxwell3D* maxwell3d{};
67 State draw_state{}; 91 State draw_state{};
92 IndirectParams indirect_state{};
68}; 93};
69} // namespace Tegra::Engines 94} // namespace Tegra::Engines
diff --git a/src/video_core/engines/engine_interface.h b/src/video_core/engines/engine_interface.h
index 26cde8584..392322358 100644
--- a/src/video_core/engines/engine_interface.h
+++ b/src/video_core/engines/engine_interface.h
@@ -3,6 +3,10 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <bitset>
7#include <limits>
8#include <vector>
9
6#include "common/common_types.h" 10#include "common/common_types.h"
7 11
8namespace Tegra::Engines { 12namespace Tegra::Engines {
@@ -17,6 +21,26 @@ public:
17 /// Write multiple values to the register identified by method. 21 /// Write multiple values to the register identified by method.
18 virtual void CallMultiMethod(u32 method, const u32* base_start, u32 amount, 22 virtual void CallMultiMethod(u32 method, const u32* base_start, u32 amount,
19 u32 methods_pending) = 0; 23 u32 methods_pending) = 0;
24
25 void ConsumeSink() {
26 if (method_sink.empty()) {
27 return;
28 }
29 ConsumeSinkImpl();
30 }
31
32 std::bitset<std::numeric_limits<u16>::max()> execution_mask{};
33 std::vector<std::pair<u32, u32>> method_sink{};
34 bool current_dirty{};
35 GPUVAddr current_dma_segment;
36
37protected:
38 virtual void ConsumeSinkImpl() {
39 for (auto [method, value] : method_sink) {
40 CallMethod(method, value, true);
41 }
42 method_sink.clear();
43 }
20}; 44};
21 45
22} // namespace Tegra::Engines 46} // namespace Tegra::Engines
diff --git a/src/video_core/engines/engine_upload.cpp b/src/video_core/engines/engine_upload.cpp
index cea1dd8b0..7f5a0c29d 100644
--- a/src/video_core/engines/engine_upload.cpp
+++ b/src/video_core/engines/engine_upload.cpp
@@ -76,7 +76,7 @@ void State::ProcessData(std::span<const u8> read_buffer) {
76 regs.dest.height, regs.dest.depth, x_offset, regs.dest.y, 76 regs.dest.height, regs.dest.depth, x_offset, regs.dest.y,
77 x_elements, regs.line_count, regs.dest.BlockHeight(), 77 x_elements, regs.line_count, regs.dest.BlockHeight(),
78 regs.dest.BlockDepth(), regs.line_length_in); 78 regs.dest.BlockDepth(), regs.line_length_in);
79 memory_manager.WriteBlock(address, tmp_buffer.data(), dst_size); 79 memory_manager.WriteBlockCached(address, tmp_buffer.data(), dst_size);
80 } 80 }
81} 81}
82 82
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp
index c6478ae85..a126c359c 100644
--- a/src/video_core/engines/fermi_2d.cpp
+++ b/src/video_core/engines/fermi_2d.cpp
@@ -6,6 +6,7 @@
6#include "common/microprofile.h" 6#include "common/microprofile.h"
7#include "video_core/engines/fermi_2d.h" 7#include "video_core/engines/fermi_2d.h"
8#include "video_core/engines/sw_blitter/blitter.h" 8#include "video_core/engines/sw_blitter/blitter.h"
9#include "video_core/memory_manager.h"
9#include "video_core/rasterizer_interface.h" 10#include "video_core/rasterizer_interface.h"
10#include "video_core/surface.h" 11#include "video_core/surface.h"
11#include "video_core/textures/decoders.h" 12#include "video_core/textures/decoders.h"
@@ -20,11 +21,14 @@ namespace Tegra::Engines {
20 21
21using namespace Texture; 22using namespace Texture;
22 23
23Fermi2D::Fermi2D(MemoryManager& memory_manager_) { 24Fermi2D::Fermi2D(MemoryManager& memory_manager_) : memory_manager{memory_manager_} {
24 sw_blitter = std::make_unique<Blitter::SoftwareBlitEngine>(memory_manager_); 25 sw_blitter = std::make_unique<Blitter::SoftwareBlitEngine>(memory_manager);
25 // Nvidia's OpenGL driver seems to assume these values 26 // Nvidia's OpenGL driver seems to assume these values
26 regs.src.depth = 1; 27 regs.src.depth = 1;
27 regs.dst.depth = 1; 28 regs.dst.depth = 1;
29
30 execution_mask.reset();
31 execution_mask[FERMI2D_REG_INDEX(pixels_from_memory.src_y0) + 1] = true;
28} 32}
29 33
30Fermi2D::~Fermi2D() = default; 34Fermi2D::~Fermi2D() = default;
@@ -49,6 +53,13 @@ void Fermi2D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32
49 } 53 }
50} 54}
51 55
56void Fermi2D::ConsumeSinkImpl() {
57 for (auto [method, value] : method_sink) {
58 regs.reg_array[method] = value;
59 }
60 method_sink.clear();
61}
62
52void Fermi2D::Blit() { 63void Fermi2D::Blit() {
53 MICROPROFILE_SCOPE(GPU_BlitEngine); 64 MICROPROFILE_SCOPE(GPU_BlitEngine);
54 LOG_DEBUG(HW_GPU, "called. source address=0x{:x}, destination address=0x{:x}", 65 LOG_DEBUG(HW_GPU, "called. source address=0x{:x}, destination address=0x{:x}",
@@ -94,6 +105,7 @@ void Fermi2D::Blit() {
94 config.src_x0 = 0; 105 config.src_x0 = 0;
95 } 106 }
96 107
108 memory_manager.FlushCaching();
97 if (!rasterizer->AccelerateSurfaceCopy(src, regs.dst, config)) { 109 if (!rasterizer->AccelerateSurfaceCopy(src, regs.dst, config)) {
98 sw_blitter->Blit(src, regs.dst, config); 110 sw_blitter->Blit(src, regs.dst, config);
99 } 111 }
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h
index 100b21bac..705b323e1 100644
--- a/src/video_core/engines/fermi_2d.h
+++ b/src/video_core/engines/fermi_2d.h
@@ -305,10 +305,13 @@ public:
305private: 305private:
306 VideoCore::RasterizerInterface* rasterizer = nullptr; 306 VideoCore::RasterizerInterface* rasterizer = nullptr;
307 std::unique_ptr<Blitter::SoftwareBlitEngine> sw_blitter; 307 std::unique_ptr<Blitter::SoftwareBlitEngine> sw_blitter;
308 MemoryManager& memory_manager;
308 309
309 /// Performs the copy from the source surface to the destination surface as configured in the 310 /// Performs the copy from the source surface to the destination surface as configured in the
310 /// registers. 311 /// registers.
311 void Blit(); 312 void Blit();
313
314 void ConsumeSinkImpl() override;
312}; 315};
313 316
314#define ASSERT_REG_POSITION(field_name, position) \ 317#define ASSERT_REG_POSITION(field_name, position) \
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp
index e5c622155..601095f03 100644
--- a/src/video_core/engines/kepler_compute.cpp
+++ b/src/video_core/engines/kepler_compute.cpp
@@ -14,7 +14,12 @@
14namespace Tegra::Engines { 14namespace Tegra::Engines {
15 15
16KeplerCompute::KeplerCompute(Core::System& system_, MemoryManager& memory_manager_) 16KeplerCompute::KeplerCompute(Core::System& system_, MemoryManager& memory_manager_)
17 : system{system_}, memory_manager{memory_manager_}, upload_state{memory_manager, regs.upload} {} 17 : system{system_}, memory_manager{memory_manager_}, upload_state{memory_manager, regs.upload} {
18 execution_mask.reset();
19 execution_mask[KEPLER_COMPUTE_REG_INDEX(exec_upload)] = true;
20 execution_mask[KEPLER_COMPUTE_REG_INDEX(data_upload)] = true;
21 execution_mask[KEPLER_COMPUTE_REG_INDEX(launch)] = true;
22}
18 23
19KeplerCompute::~KeplerCompute() = default; 24KeplerCompute::~KeplerCompute() = default;
20 25
@@ -23,6 +28,13 @@ void KeplerCompute::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_)
23 upload_state.BindRasterizer(rasterizer); 28 upload_state.BindRasterizer(rasterizer);
24} 29}
25 30
31void KeplerCompute::ConsumeSinkImpl() {
32 for (auto [method, value] : method_sink) {
33 regs.reg_array[method] = value;
34 }
35 method_sink.clear();
36}
37
26void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_call) { 38void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
27 ASSERT_MSG(method < Regs::NUM_REGS, 39 ASSERT_MSG(method < Regs::NUM_REGS,
28 "Invalid KeplerCompute register, increase the size of the Regs structure"); 40 "Invalid KeplerCompute register, increase the size of the Regs structure");
diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h
index e154e3f06..2092e685f 100644
--- a/src/video_core/engines/kepler_compute.h
+++ b/src/video_core/engines/kepler_compute.h
@@ -204,6 +204,8 @@ public:
204private: 204private:
205 void ProcessLaunch(); 205 void ProcessLaunch();
206 206
207 void ConsumeSinkImpl() override;
208
207 /// Retrieves information about a specific TIC entry from the TIC buffer. 209 /// Retrieves information about a specific TIC entry from the TIC buffer.
208 Texture::TICEntry GetTICEntry(u32 tic_index) const; 210 Texture::TICEntry GetTICEntry(u32 tic_index) const;
209 211
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp
index 08045d1cf..c026801a3 100644
--- a/src/video_core/engines/kepler_memory.cpp
+++ b/src/video_core/engines/kepler_memory.cpp
@@ -18,6 +18,17 @@ KeplerMemory::~KeplerMemory() = default;
18 18
19void KeplerMemory::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) { 19void KeplerMemory::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
20 upload_state.BindRasterizer(rasterizer_); 20 upload_state.BindRasterizer(rasterizer_);
21
22 execution_mask.reset();
23 execution_mask[KEPLERMEMORY_REG_INDEX(exec)] = true;
24 execution_mask[KEPLERMEMORY_REG_INDEX(data)] = true;
25}
26
27void KeplerMemory::ConsumeSinkImpl() {
28 for (auto [method, value] : method_sink) {
29 regs.reg_array[method] = value;
30 }
31 method_sink.clear();
21} 32}
22 33
23void KeplerMemory::CallMethod(u32 method, u32 method_argument, bool is_last_call) { 34void KeplerMemory::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h
index 5fe7489f0..fb1eecbba 100644
--- a/src/video_core/engines/kepler_memory.h
+++ b/src/video_core/engines/kepler_memory.h
@@ -73,6 +73,8 @@ public:
73 } regs{}; 73 } regs{};
74 74
75private: 75private:
76 void ConsumeSinkImpl() override;
77
76 Core::System& system; 78 Core::System& system;
77 Upload::State upload_state; 79 Upload::State upload_state;
78}; 80};
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 9b182b653..97f547789 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -4,6 +4,8 @@
4#include <cstring> 4#include <cstring>
5#include <optional> 5#include <optional>
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/scope_exit.h"
8#include "common/settings.h"
7#include "core/core.h" 9#include "core/core.h"
8#include "core/core_timing.h" 10#include "core/core_timing.h"
9#include "video_core/dirty_flags.h" 11#include "video_core/dirty_flags.h"
@@ -28,6 +30,10 @@ Maxwell3D::Maxwell3D(Core::System& system_, MemoryManager& memory_manager_)
28 regs.upload} { 30 regs.upload} {
29 dirty.flags.flip(); 31 dirty.flags.flip();
30 InitializeRegisterDefaults(); 32 InitializeRegisterDefaults();
33 execution_mask.reset();
34 for (size_t i = 0; i < execution_mask.size(); i++) {
35 execution_mask[i] = IsMethodExecutable(static_cast<u32>(i));
36 }
31} 37}
32 38
33Maxwell3D::~Maxwell3D() = default; 39Maxwell3D::~Maxwell3D() = default;
@@ -121,6 +127,71 @@ void Maxwell3D::InitializeRegisterDefaults() {
121 shadow_state = regs; 127 shadow_state = regs;
122} 128}
123 129
130bool Maxwell3D::IsMethodExecutable(u32 method) {
131 if (method >= MacroRegistersStart) {
132 return true;
133 }
134 switch (method) {
135 case MAXWELL3D_REG_INDEX(draw.end):
136 case MAXWELL3D_REG_INDEX(draw.begin):
137 case MAXWELL3D_REG_INDEX(vertex_buffer.first):
138 case MAXWELL3D_REG_INDEX(vertex_buffer.count):
139 case MAXWELL3D_REG_INDEX(index_buffer.first):
140 case MAXWELL3D_REG_INDEX(index_buffer.count):
141 case MAXWELL3D_REG_INDEX(draw_inline_index):
142 case MAXWELL3D_REG_INDEX(index_buffer32_subsequent):
143 case MAXWELL3D_REG_INDEX(index_buffer16_subsequent):
144 case MAXWELL3D_REG_INDEX(index_buffer8_subsequent):
145 case MAXWELL3D_REG_INDEX(index_buffer32_first):
146 case MAXWELL3D_REG_INDEX(index_buffer16_first):
147 case MAXWELL3D_REG_INDEX(index_buffer8_first):
148 case MAXWELL3D_REG_INDEX(inline_index_2x16.even):
149 case MAXWELL3D_REG_INDEX(inline_index_4x8.index0):
150 case MAXWELL3D_REG_INDEX(vertex_array_instance_first):
151 case MAXWELL3D_REG_INDEX(vertex_array_instance_subsequent):
152 case MAXWELL3D_REG_INDEX(wait_for_idle):
153 case MAXWELL3D_REG_INDEX(shadow_ram_control):
154 case MAXWELL3D_REG_INDEX(load_mme.instruction_ptr):
155 case MAXWELL3D_REG_INDEX(load_mme.instruction):
156 case MAXWELL3D_REG_INDEX(load_mme.start_address):
157 case MAXWELL3D_REG_INDEX(falcon[4]):
158 case MAXWELL3D_REG_INDEX(const_buffer.buffer):
159 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 1:
160 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 2:
161 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 3:
162 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 4:
163 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 5:
164 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 6:
165 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 7:
166 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 8:
167 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 9:
168 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 10:
169 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 11:
170 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 12:
171 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 13:
172 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 14:
173 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 15:
174 case MAXWELL3D_REG_INDEX(bind_groups[0].raw_config):
175 case MAXWELL3D_REG_INDEX(bind_groups[1].raw_config):
176 case MAXWELL3D_REG_INDEX(bind_groups[2].raw_config):
177 case MAXWELL3D_REG_INDEX(bind_groups[3].raw_config):
178 case MAXWELL3D_REG_INDEX(bind_groups[4].raw_config):
179 case MAXWELL3D_REG_INDEX(topology_override):
180 case MAXWELL3D_REG_INDEX(clear_surface):
181 case MAXWELL3D_REG_INDEX(report_semaphore.query):
182 case MAXWELL3D_REG_INDEX(render_enable.mode):
183 case MAXWELL3D_REG_INDEX(clear_report_value):
184 case MAXWELL3D_REG_INDEX(sync_info):
185 case MAXWELL3D_REG_INDEX(launch_dma):
186 case MAXWELL3D_REG_INDEX(inline_data):
187 case MAXWELL3D_REG_INDEX(fragment_barrier):
188 case MAXWELL3D_REG_INDEX(tiled_cache_barrier):
189 return true;
190 default:
191 return false;
192 }
193}
194
124void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) { 195void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) {
125 if (executing_macro == 0) { 196 if (executing_macro == 0) {
126 // A macro call must begin by writing the macro method's register, not its argument. 197 // A macro call must begin by writing the macro method's register, not its argument.
@@ -130,12 +201,70 @@ void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool
130 } 201 }
131 202
132 macro_params.insert(macro_params.end(), base_start, base_start + amount); 203 macro_params.insert(macro_params.end(), base_start, base_start + amount);
204 for (size_t i = 0; i < amount; i++) {
205 macro_addresses.push_back(current_dma_segment + i * sizeof(u32));
206 }
207 macro_segments.emplace_back(current_dma_segment, amount);
208 current_macro_dirty |= current_dirty;
209 current_dirty = false;
133 210
134 // Call the macro when there are no more parameters in the command buffer 211 // Call the macro when there are no more parameters in the command buffer
135 if (is_last_call) { 212 if (is_last_call) {
213 ConsumeSink();
136 CallMacroMethod(executing_macro, macro_params); 214 CallMacroMethod(executing_macro, macro_params);
137 macro_params.clear(); 215 macro_params.clear();
216 macro_addresses.clear();
217 macro_segments.clear();
218 current_macro_dirty = false;
219 }
220}
221
222void Maxwell3D::RefreshParametersImpl() {
223 size_t current_index = 0;
224 for (auto& segment : macro_segments) {
225 if (segment.first == 0) {
226 current_index += segment.second;
227 continue;
228 }
229 memory_manager.ReadBlock(segment.first, &macro_params[current_index],
230 sizeof(u32) * segment.second);
231 current_index += segment.second;
232 }
233}
234
235u32 Maxwell3D::GetMaxCurrentVertices() {
236 u32 num_vertices = 0;
237 for (size_t index = 0; index < Regs::NumVertexArrays; ++index) {
238 const auto& array = regs.vertex_streams[index];
239 if (array.enable == 0) {
240 continue;
241 }
242 const auto& attribute = regs.vertex_attrib_format[index];
243 if (attribute.constant) {
244 num_vertices = std::max(num_vertices, 1U);
245 continue;
246 }
247 const auto& limit = regs.vertex_stream_limits[index];
248 const GPUVAddr gpu_addr_begin = array.Address();
249 const GPUVAddr gpu_addr_end = limit.Address() + 1;
250 const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin);
251 num_vertices = std::max(
252 num_vertices, address_size / std::max(attribute.SizeInBytes(), array.stride.Value()));
138 } 253 }
254 return num_vertices;
255}
256
257size_t Maxwell3D::EstimateIndexBufferSize() {
258 GPUVAddr start_address = regs.index_buffer.StartAddress();
259 GPUVAddr end_address = regs.index_buffer.EndAddress();
260 constexpr std::array<size_t, 4> max_sizes = {
261 std::numeric_limits<u8>::max(), std::numeric_limits<u16>::max(),
262 std::numeric_limits<u32>::max(), std::numeric_limits<u32>::max()};
263 const size_t byte_size = regs.index_buffer.FormatSizeInBytes();
264 return std::min<size_t>(
265 memory_manager.GetMemoryLayoutSize(start_address, byte_size * max_sizes[byte_size]) /
266 byte_size,
267 static_cast<size_t>(end_address - start_address));
139} 268}
140 269
141u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) { 270u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) {
@@ -152,6 +281,29 @@ u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) {
152 return argument; 281 return argument;
153} 282}
154 283
284void Maxwell3D::ConsumeSinkImpl() {
285 SCOPE_EXIT({ method_sink.clear(); });
286 const auto control = shadow_state.shadow_ram_control;
287 if (control == Regs::ShadowRamControl::Track ||
288 control == Regs::ShadowRamControl::TrackWithFilter) {
289
290 for (auto [method, value] : method_sink) {
291 shadow_state.reg_array[method] = value;
292 ProcessDirtyRegisters(method, value);
293 }
294 return;
295 }
296 if (control == Regs::ShadowRamControl::Replay) {
297 for (auto [method, value] : method_sink) {
298 ProcessDirtyRegisters(method, shadow_state.reg_array[method]);
299 }
300 return;
301 }
302 for (auto [method, value] : method_sink) {
303 ProcessDirtyRegisters(method, value);
304 }
305}
306
155void Maxwell3D::ProcessDirtyRegisters(u32 method, u32 argument) { 307void Maxwell3D::ProcessDirtyRegisters(u32 method, u32 argument) {
156 if (regs.reg_array[method] == argument) { 308 if (regs.reg_array[method] == argument) {
157 return; 309 return;
@@ -263,7 +415,6 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
263 415
264 const u32 argument = ProcessShadowRam(method, method_argument); 416 const u32 argument = ProcessShadowRam(method, method_argument);
265 ProcessDirtyRegisters(method, argument); 417 ProcessDirtyRegisters(method, argument);
266
267 ProcessMethodCall(method, argument, method_argument, is_last_call); 418 ProcessMethodCall(method, argument, method_argument, is_last_call);
268} 419}
269 420
@@ -294,9 +445,11 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
294 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 15: 445 case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 15:
295 ProcessCBMultiData(base_start, amount); 446 ProcessCBMultiData(base_start, amount);
296 break; 447 break;
297 case MAXWELL3D_REG_INDEX(inline_data): 448 case MAXWELL3D_REG_INDEX(inline_data): {
449 ASSERT(methods_pending == amount);
298 upload_state.ProcessData(base_start, amount); 450 upload_state.ProcessData(base_start, amount);
299 return; 451 return;
452 }
300 default: 453 default:
301 for (u32 i = 0; i < amount; i++) { 454 for (u32 i = 0; i < amount; i++) {
302 CallMethod(method, base_start[i], methods_pending - i <= 1); 455 CallMethod(method, base_start[i], methods_pending - i <= 1);
@@ -332,11 +485,6 @@ void Maxwell3D::StampQueryResult(u64 payload, bool long_query) {
332} 485}
333 486
334void Maxwell3D::ProcessQueryGet() { 487void Maxwell3D::ProcessQueryGet() {
335 // TODO(Subv): Support the other query units.
336 if (regs.report_semaphore.query.location != Regs::ReportSemaphore::Location::All) {
337 LOG_DEBUG(HW_GPU, "Locations other than ALL are unimplemented");
338 }
339
340 switch (regs.report_semaphore.query.operation) { 488 switch (regs.report_semaphore.query.operation) {
341 case Regs::ReportSemaphore::Operation::Release: 489 case Regs::ReportSemaphore::Operation::Release:
342 if (regs.report_semaphore.query.short_query != 0) { 490 if (regs.report_semaphore.query.short_query != 0) {
@@ -389,7 +537,11 @@ void Maxwell3D::ProcessQueryCondition() {
389 case Regs::RenderEnable::Override::NeverRender: 537 case Regs::RenderEnable::Override::NeverRender:
390 execute_on = false; 538 execute_on = false;
391 break; 539 break;
392 case Regs::RenderEnable::Override::UseRenderEnable: 540 case Regs::RenderEnable::Override::UseRenderEnable: {
541 if (rasterizer->AccelerateConditionalRendering()) {
542 execute_on = true;
543 return;
544 }
393 switch (regs.render_enable.mode) { 545 switch (regs.render_enable.mode) {
394 case Regs::RenderEnable::Mode::True: { 546 case Regs::RenderEnable::Mode::True: {
395 execute_on = true; 547 execute_on = true;
@@ -427,6 +579,7 @@ void Maxwell3D::ProcessQueryCondition() {
427 } 579 }
428 break; 580 break;
429 } 581 }
582 }
430} 583}
431 584
432void Maxwell3D::ProcessCounterReset() { 585void Maxwell3D::ProcessCounterReset() {
@@ -463,7 +616,8 @@ std::optional<u64> Maxwell3D::GetQueryResult() {
463} 616}
464 617
465void Maxwell3D::ProcessCBBind(size_t stage_index) { 618void Maxwell3D::ProcessCBBind(size_t stage_index) {
466 // Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader stage. 619 // Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader
620 // stage.
467 const auto& bind_data = regs.bind_groups[stage_index]; 621 const auto& bind_data = regs.bind_groups[stage_index];
468 auto& buffer = state.shader_stages[stage_index].const_buffers[bind_data.shader_slot]; 622 auto& buffer = state.shader_stages[stage_index].const_buffers[bind_data.shader_slot];
469 buffer.enabled = bind_data.valid.Value() != 0; 623 buffer.enabled = bind_data.valid.Value() != 0;
@@ -490,7 +644,7 @@ void Maxwell3D::ProcessCBMultiData(const u32* start_base, u32 amount) {
490 644
491 const GPUVAddr address{buffer_address + regs.const_buffer.offset}; 645 const GPUVAddr address{buffer_address + regs.const_buffer.offset};
492 const size_t copy_size = amount * sizeof(u32); 646 const size_t copy_size = amount * sizeof(u32);
493 memory_manager.WriteBlock(address, start_base, copy_size); 647 memory_manager.WriteBlockCached(address, start_base, copy_size);
494 648
495 // Increment the current buffer position. 649 // Increment the current buffer position.
496 regs.const_buffer.offset += static_cast<u32>(copy_size); 650 regs.const_buffer.offset += static_cast<u32>(copy_size);
@@ -524,4 +678,10 @@ u32 Maxwell3D::GetRegisterValue(u32 method) const {
524 return regs.reg_array[method]; 678 return regs.reg_array[method];
525} 679}
526 680
681void Maxwell3D::SetHLEReplacementAttributeType(u32 bank, u32 offset,
682 HLEReplacementAttributeType name) {
683 const u64 key = (static_cast<u64>(bank) << 32) | offset;
684 replace_table.emplace(key, name);
685}
686
527} // namespace Tegra::Engines 687} // namespace Tegra::Engines
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 22b904319..0b2fd2928 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -272,6 +272,7 @@ public:
272 }; 272 };
273 273
274 union { 274 union {
275 u32 raw;
275 BitField<0, 1, Mode> mode; 276 BitField<0, 1, Mode> mode;
276 BitField<4, 8, u32> pad; 277 BitField<4, 8, u32> pad;
277 }; 278 };
@@ -1217,10 +1218,12 @@ public:
1217 1218
1218 struct Window { 1219 struct Window {
1219 union { 1220 union {
1221 u32 raw_x;
1220 BitField<0, 16, u32> x_min; 1222 BitField<0, 16, u32> x_min;
1221 BitField<16, 16, u32> x_max; 1223 BitField<16, 16, u32> x_max;
1222 }; 1224 };
1223 union { 1225 union {
1226 u32 raw_y;
1224 BitField<0, 16, u32> y_min; 1227 BitField<0, 16, u32> y_min;
1225 BitField<16, 16, u32> y_max; 1228 BitField<16, 16, u32> y_max;
1226 }; 1229 };
@@ -2708,7 +2711,7 @@ public:
2708 u32 post_z_pixel_imask; ///< 0x0F1C 2711 u32 post_z_pixel_imask; ///< 0x0F1C
2709 INSERT_PADDING_BYTES_NOINIT(0x20); 2712 INSERT_PADDING_BYTES_NOINIT(0x20);
2710 ConstantColorRendering const_color_rendering; ///< 0x0F40 2713 ConstantColorRendering const_color_rendering; ///< 0x0F40
2711 s32 stencil_back_ref; ///< 0x0F54 2714 u32 stencil_back_ref; ///< 0x0F54
2712 u32 stencil_back_mask; ///< 0x0F58 2715 u32 stencil_back_mask; ///< 0x0F58
2713 u32 stencil_back_func_mask; ///< 0x0F5C 2716 u32 stencil_back_func_mask; ///< 0x0F5C
2714 INSERT_PADDING_BYTES_NOINIT(0x14); 2717 INSERT_PADDING_BYTES_NOINIT(0x14);
@@ -2832,9 +2835,9 @@ public:
2832 Blend blend; ///< 0x133C 2835 Blend blend; ///< 0x133C
2833 u32 stencil_enable; ///< 0x1380 2836 u32 stencil_enable; ///< 0x1380
2834 StencilOp stencil_front_op; ///< 0x1384 2837 StencilOp stencil_front_op; ///< 0x1384
2835 s32 stencil_front_ref; ///< 0x1394 2838 u32 stencil_front_ref; ///< 0x1394
2836 s32 stencil_front_func_mask; ///< 0x1398 2839 u32 stencil_front_func_mask; ///< 0x1398
2837 s32 stencil_front_mask; ///< 0x139C 2840 u32 stencil_front_mask; ///< 0x139C
2838 INSERT_PADDING_BYTES_NOINIT(0x4); 2841 INSERT_PADDING_BYTES_NOINIT(0x4);
2839 u32 draw_auto_start_byte_count; ///< 0x13A4 2842 u32 draw_auto_start_byte_count; ///< 0x13A4
2840 PsSaturate frag_color_clamp; ///< 0x13A8 2843 PsSaturate frag_color_clamp; ///< 0x13A8
@@ -3020,6 +3023,24 @@ public:
3020 /// Store temporary hw register values, used by some calls to restore state after a operation 3023 /// Store temporary hw register values, used by some calls to restore state after a operation
3021 Regs shadow_state; 3024 Regs shadow_state;
3022 3025
3026 // None Engine
3027 enum class EngineHint : u32 {
3028 None = 0x0,
3029 OnHLEMacro = 0x1,
3030 };
3031
3032 EngineHint engine_state{EngineHint::None};
3033
3034 enum class HLEReplacementAttributeType : u32 {
3035 BaseVertex = 0x0,
3036 BaseInstance = 0x1,
3037 DrawID = 0x2,
3038 };
3039
3040 void SetHLEReplacementAttributeType(u32 bank, u32 offset, HLEReplacementAttributeType name);
3041
3042 std::unordered_map<u64, HLEReplacementAttributeType> replace_table;
3043
3023 static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), "Maxwell3D Regs has wrong size"); 3044 static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), "Maxwell3D Regs has wrong size");
3024 static_assert(std::is_trivially_copyable_v<Regs>, "Maxwell3D Regs must be trivially copyable"); 3045 static_assert(std::is_trivially_copyable_v<Regs>, "Maxwell3D Regs must be trivially copyable");
3025 3046
@@ -3067,6 +3088,35 @@ public:
3067 std::unique_ptr<DrawManager> draw_manager; 3088 std::unique_ptr<DrawManager> draw_manager;
3068 friend class DrawManager; 3089 friend class DrawManager;
3069 3090
3091 GPUVAddr GetMacroAddress(size_t index) const {
3092 return macro_addresses[index];
3093 }
3094
3095 void RefreshParameters() {
3096 if (!current_macro_dirty) {
3097 return;
3098 }
3099 RefreshParametersImpl();
3100 }
3101
3102 bool AnyParametersDirty() const {
3103 return current_macro_dirty;
3104 }
3105
3106 u32 GetMaxCurrentVertices();
3107
3108 size_t EstimateIndexBufferSize();
3109
3110 /// Handles a write to the CLEAR_BUFFERS register.
3111 void ProcessClearBuffers(u32 layer_count);
3112
3113 /// Handles a write to the CB_BIND register.
3114 void ProcessCBBind(size_t stage_index);
3115
3116 /// Handles a write to the CB_DATA[i] register.
3117 void ProcessCBData(u32 value);
3118 void ProcessCBMultiData(const u32* start_base, u32 amount);
3119
3070private: 3120private:
3071 void InitializeRegisterDefaults(); 3121 void InitializeRegisterDefaults();
3072 3122
@@ -3076,6 +3126,8 @@ private:
3076 3126
3077 void ProcessDirtyRegisters(u32 method, u32 argument); 3127 void ProcessDirtyRegisters(u32 method, u32 argument);
3078 3128
3129 void ConsumeSinkImpl() override;
3130
3079 void ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argument, bool is_last_call); 3131 void ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argument, bool is_last_call);
3080 3132
3081 /// Retrieves information about a specific TIC entry from the TIC buffer. 3133 /// Retrieves information about a specific TIC entry from the TIC buffer.
@@ -3116,16 +3168,13 @@ private:
3116 /// Handles writes to syncing register. 3168 /// Handles writes to syncing register.
3117 void ProcessSyncPoint(); 3169 void ProcessSyncPoint();
3118 3170
3119 /// Handles a write to the CB_DATA[i] register.
3120 void ProcessCBData(u32 value);
3121 void ProcessCBMultiData(const u32* start_base, u32 amount);
3122
3123 /// Handles a write to the CB_BIND register.
3124 void ProcessCBBind(size_t stage_index);
3125
3126 /// Returns a query's value or an empty object if the value will be deferred through a cache. 3171 /// Returns a query's value or an empty object if the value will be deferred through a cache.
3127 std::optional<u64> GetQueryResult(); 3172 std::optional<u64> GetQueryResult();
3128 3173
3174 void RefreshParametersImpl();
3175
3176 bool IsMethodExecutable(u32 method);
3177
3129 Core::System& system; 3178 Core::System& system;
3130 MemoryManager& memory_manager; 3179 MemoryManager& memory_manager;
3131 3180
@@ -3145,6 +3194,10 @@ private:
3145 Upload::State upload_state; 3194 Upload::State upload_state;
3146 3195
3147 bool execute_on{true}; 3196 bool execute_on{true};
3197
3198 std::vector<std::pair<GPUVAddr, size_t>> macro_segments;
3199 std::vector<GPUVAddr> macro_addresses;
3200 bool current_macro_dirty{};
3148}; 3201};
3149 3202
3150#define ASSERT_REG_POSITION(field_name, position) \ 3203#define ASSERT_REG_POSITION(field_name, position) \
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index f73d7bf0f..7762c7d96 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -21,7 +21,10 @@ namespace Tegra::Engines {
21using namespace Texture; 21using namespace Texture;
22 22
23MaxwellDMA::MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_) 23MaxwellDMA::MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_)
24 : system{system_}, memory_manager{memory_manager_} {} 24 : system{system_}, memory_manager{memory_manager_} {
25 execution_mask.reset();
26 execution_mask[offsetof(Regs, launch_dma) / sizeof(u32)] = true;
27}
25 28
26MaxwellDMA::~MaxwellDMA() = default; 29MaxwellDMA::~MaxwellDMA() = default;
27 30
@@ -29,6 +32,13 @@ void MaxwellDMA::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
29 rasterizer = rasterizer_; 32 rasterizer = rasterizer_;
30} 33}
31 34
35void MaxwellDMA::ConsumeSinkImpl() {
36 for (auto [method, value] : method_sink) {
37 regs.reg_array[method] = value;
38 }
39 method_sink.clear();
40}
41
32void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call) { 42void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
33 ASSERT_MSG(method < NUM_REGS, "Invalid MaxwellDMA register"); 43 ASSERT_MSG(method < NUM_REGS, "Invalid MaxwellDMA register");
34 44
@@ -59,7 +69,7 @@ void MaxwellDMA::Launch() {
59 if (launch.multi_line_enable) { 69 if (launch.multi_line_enable) {
60 const bool is_src_pitch = launch.src_memory_layout == LaunchDMA::MemoryLayout::PITCH; 70 const bool is_src_pitch = launch.src_memory_layout == LaunchDMA::MemoryLayout::PITCH;
61 const bool is_dst_pitch = launch.dst_memory_layout == LaunchDMA::MemoryLayout::PITCH; 71 const bool is_dst_pitch = launch.dst_memory_layout == LaunchDMA::MemoryLayout::PITCH;
62 72 memory_manager.FlushCaching();
63 if (!is_src_pitch && !is_dst_pitch) { 73 if (!is_src_pitch && !is_dst_pitch) {
64 // If both the source and the destination are in block layout, assert. 74 // If both the source and the destination are in block layout, assert.
65 CopyBlockLinearToBlockLinear(); 75 CopyBlockLinearToBlockLinear();
@@ -94,6 +104,7 @@ void MaxwellDMA::Launch() {
94 reinterpret_cast<u8*>(tmp_buffer.data()), 104 reinterpret_cast<u8*>(tmp_buffer.data()),
95 regs.line_length_in * sizeof(u32)); 105 regs.line_length_in * sizeof(u32));
96 } else { 106 } else {
107 memory_manager.FlushCaching();
97 const auto convert_linear_2_blocklinear_addr = [](u64 address) { 108 const auto convert_linear_2_blocklinear_addr = [](u64 address) {
98 return (address & ~0x1f0ULL) | ((address & 0x40) >> 2) | ((address & 0x10) << 1) | 109 return (address & ~0x1f0ULL) | ((address & 0x40) >> 2) | ((address & 0x10) << 1) |
99 ((address & 0x180) >> 1) | ((address & 0x20) << 3); 110 ((address & 0x180) >> 1) | ((address & 0x20) << 3);
@@ -111,8 +122,8 @@ void MaxwellDMA::Launch() {
111 memory_manager.ReadBlockUnsafe( 122 memory_manager.ReadBlockUnsafe(
112 convert_linear_2_blocklinear_addr(regs.offset_in + offset), 123 convert_linear_2_blocklinear_addr(regs.offset_in + offset),
113 tmp_buffer.data(), tmp_buffer.size()); 124 tmp_buffer.data(), tmp_buffer.size());
114 memory_manager.WriteBlock(regs.offset_out + offset, tmp_buffer.data(), 125 memory_manager.WriteBlockCached(regs.offset_out + offset, tmp_buffer.data(),
115 tmp_buffer.size()); 126 tmp_buffer.size());
116 } 127 }
117 } else if (is_src_pitch && !is_dst_pitch) { 128 } else if (is_src_pitch && !is_dst_pitch) {
118 UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0); 129 UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0);
@@ -122,7 +133,7 @@ void MaxwellDMA::Launch() {
122 for (u32 offset = 0; offset < regs.line_length_in; offset += 16) { 133 for (u32 offset = 0; offset < regs.line_length_in; offset += 16) {
123 memory_manager.ReadBlockUnsafe(regs.offset_in + offset, tmp_buffer.data(), 134 memory_manager.ReadBlockUnsafe(regs.offset_in + offset, tmp_buffer.data(),
124 tmp_buffer.size()); 135 tmp_buffer.size());
125 memory_manager.WriteBlock( 136 memory_manager.WriteBlockCached(
126 convert_linear_2_blocklinear_addr(regs.offset_out + offset), 137 convert_linear_2_blocklinear_addr(regs.offset_out + offset),
127 tmp_buffer.data(), tmp_buffer.size()); 138 tmp_buffer.data(), tmp_buffer.size());
128 } 139 }
@@ -131,8 +142,8 @@ void MaxwellDMA::Launch() {
131 std::vector<u8> tmp_buffer(regs.line_length_in); 142 std::vector<u8> tmp_buffer(regs.line_length_in);
132 memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(), 143 memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(),
133 regs.line_length_in); 144 regs.line_length_in);
134 memory_manager.WriteBlock(regs.offset_out, tmp_buffer.data(), 145 memory_manager.WriteBlockCached(regs.offset_out, tmp_buffer.data(),
135 regs.line_length_in); 146 regs.line_length_in);
136 } 147 }
137 } 148 }
138 } 149 }
@@ -194,7 +205,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
194 src_params.origin.y, x_elements, regs.line_count, block_height, block_depth, 205 src_params.origin.y, x_elements, regs.line_count, block_height, block_depth,
195 regs.pitch_out); 206 regs.pitch_out);
196 207
197 memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); 208 memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size);
198} 209}
199 210
200void MaxwellDMA::CopyPitchToBlockLinear() { 211void MaxwellDMA::CopyPitchToBlockLinear() {
@@ -246,7 +257,7 @@ void MaxwellDMA::CopyPitchToBlockLinear() {
246 dst_params.origin.y, x_elements, regs.line_count, block_height, block_depth, 257 dst_params.origin.y, x_elements, regs.line_count, block_height, block_depth,
247 regs.pitch_in); 258 regs.pitch_in);
248 259
249 memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); 260 memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size);
250} 261}
251 262
252void MaxwellDMA::FastCopyBlockLinearToPitch() { 263void MaxwellDMA::FastCopyBlockLinearToPitch() {
@@ -277,7 +288,7 @@ void MaxwellDMA::FastCopyBlockLinearToPitch() {
277 regs.src_params.block_size.height, regs.src_params.block_size.depth, 288 regs.src_params.block_size.height, regs.src_params.block_size.depth,
278 regs.pitch_out); 289 regs.pitch_out);
279 290
280 memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); 291 memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size);
281} 292}
282 293
283void MaxwellDMA::CopyBlockLinearToBlockLinear() { 294void MaxwellDMA::CopyBlockLinearToBlockLinear() {
@@ -337,7 +348,7 @@ void MaxwellDMA::CopyBlockLinearToBlockLinear() {
337 dst.depth, dst_x_offset, dst.origin.y, x_elements, regs.line_count, 348 dst.depth, dst_x_offset, dst.origin.y, x_elements, regs.line_count,
338 dst.block_size.height, dst.block_size.depth, pitch); 349 dst.block_size.height, dst.block_size.depth, pitch);
339 350
340 memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); 351 memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size);
341} 352}
342 353
343void MaxwellDMA::ReleaseSemaphore() { 354void MaxwellDMA::ReleaseSemaphore() {
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h
index c88191a61..0e594fa74 100644
--- a/src/video_core/engines/maxwell_dma.h
+++ b/src/video_core/engines/maxwell_dma.h
@@ -231,6 +231,8 @@ private:
231 231
232 void ReleaseSemaphore(); 232 void ReleaseSemaphore();
233 233
234 void ConsumeSinkImpl() override;
235
234 Core::System& system; 236 Core::System& system;
235 237
236 MemoryManager& memory_manager; 238 MemoryManager& memory_manager;
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt
index e6dc24f22..f275b2aa9 100644
--- a/src/video_core/host_shaders/CMakeLists.txt
+++ b/src/video_core/host_shaders/CMakeLists.txt
@@ -47,6 +47,7 @@ set(SHADER_FILES
47 vulkan_present_scaleforce_fp16.frag 47 vulkan_present_scaleforce_fp16.frag
48 vulkan_present_scaleforce_fp32.frag 48 vulkan_present_scaleforce_fp32.frag
49 vulkan_quad_indexed.comp 49 vulkan_quad_indexed.comp
50 vulkan_turbo_mode.comp
50 vulkan_uint8.comp 51 vulkan_uint8.comp
51) 52)
52 53
diff --git a/src/video_core/host_shaders/vulkan_turbo_mode.comp b/src/video_core/host_shaders/vulkan_turbo_mode.comp
new file mode 100644
index 000000000..d651001d9
--- /dev/null
+++ b/src/video_core/host_shaders/vulkan_turbo_mode.comp
@@ -0,0 +1,29 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#version 460 core
5
6layout (local_size_x = 16, local_size_y = 8, local_size_z = 1) in;
7
8layout (binding = 0) buffer ThreadData {
9 uint data[];
10};
11
12uint xorshift32(uint x) {
13 x ^= x << 13;
14 x ^= x >> 17;
15 x ^= x << 5;
16 return x;
17}
18
19uint getGlobalIndex() {
20 return gl_GlobalInvocationID.x + gl_GlobalInvocationID.y * gl_WorkGroupSize.y * gl_NumWorkGroups.y;
21}
22
23void main() {
24 uint myIndex = xorshift32(getGlobalIndex());
25 uint otherIndex = xorshift32(myIndex);
26
27 uint otherValue = atomicAdd(data[otherIndex % data.length()], 0) + 1;
28 atomicAdd(data[myIndex % data.length()], otherValue);
29}
diff --git a/src/video_core/invalidation_accumulator.h b/src/video_core/invalidation_accumulator.h
new file mode 100644
index 000000000..2c2aaf7bb
--- /dev/null
+++ b/src/video_core/invalidation_accumulator.h
@@ -0,0 +1,79 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <utility>
7#include <vector>
8
9#include "common/common_types.h"
10
11namespace VideoCommon {
12
13class InvalidationAccumulator {
14public:
15 InvalidationAccumulator() = default;
16 ~InvalidationAccumulator() = default;
17
18 void Add(GPUVAddr address, size_t size) {
19 const auto reset_values = [&]() {
20 if (has_collected) {
21 buffer.emplace_back(start_address, accumulated_size);
22 }
23 start_address = address;
24 accumulated_size = size;
25 last_collection = start_address + size;
26 };
27 if (address >= start_address && address + size <= last_collection) [[likely]] {
28 return;
29 }
30 size = ((address + size + atomicity_size_mask) & atomicity_mask) - address;
31 address = address & atomicity_mask;
32 if (!has_collected) [[unlikely]] {
33 reset_values();
34 has_collected = true;
35 return;
36 }
37 if (address != last_collection) [[unlikely]] {
38 reset_values();
39 return;
40 }
41 accumulated_size += size;
42 last_collection += size;
43 }
44
45 void Clear() {
46 buffer.clear();
47 start_address = 0;
48 last_collection = 0;
49 has_collected = false;
50 }
51
52 bool AnyAccumulated() const {
53 return has_collected;
54 }
55
56 template <typename Func>
57 void Callback(Func&& func) {
58 if (!has_collected) {
59 return;
60 }
61 buffer.emplace_back(start_address, accumulated_size);
62 for (auto& [address, size] : buffer) {
63 func(address, size);
64 }
65 }
66
67private:
68 static constexpr size_t atomicity_bits = 5;
69 static constexpr size_t atomicity_size = 1ULL << atomicity_bits;
70 static constexpr size_t atomicity_size_mask = atomicity_size - 1;
71 static constexpr size_t atomicity_mask = ~atomicity_size_mask;
72 GPUVAddr start_address{};
73 GPUVAddr last_collection{};
74 size_t accumulated_size{};
75 bool has_collected{};
76 std::vector<std::pair<VAddr, size_t>> buffer;
77};
78
79} // namespace VideoCommon
diff --git a/src/video_core/macro/macro.cpp b/src/video_core/macro/macro.cpp
index 505d81c1e..82ad0477d 100644
--- a/src/video_core/macro/macro.cpp
+++ b/src/video_core/macro/macro.cpp
@@ -12,7 +12,9 @@
12#include "common/assert.h" 12#include "common/assert.h"
13#include "common/fs/fs.h" 13#include "common/fs/fs.h"
14#include "common/fs/path_util.h" 14#include "common/fs/path_util.h"
15#include "common/microprofile.h"
15#include "common/settings.h" 16#include "common/settings.h"
17#include "video_core/engines/maxwell_3d.h"
16#include "video_core/macro/macro.h" 18#include "video_core/macro/macro.h"
17#include "video_core/macro/macro_hle.h" 19#include "video_core/macro/macro_hle.h"
18#include "video_core/macro/macro_interpreter.h" 20#include "video_core/macro/macro_interpreter.h"
@@ -21,6 +23,8 @@
21#include "video_core/macro/macro_jit_x64.h" 23#include "video_core/macro/macro_jit_x64.h"
22#endif 24#endif
23 25
26MICROPROFILE_DEFINE(MacroHLE, "GPU", "Execute macro HLE", MP_RGB(128, 192, 192));
27
24namespace Tegra { 28namespace Tegra {
25 29
26static void Dump(u64 hash, std::span<const u32> code) { 30static void Dump(u64 hash, std::span<const u32> code) {
@@ -40,8 +44,8 @@ static void Dump(u64 hash, std::span<const u32> code) {
40 macro_file.write(reinterpret_cast<const char*>(code.data()), code.size_bytes()); 44 macro_file.write(reinterpret_cast<const char*>(code.data()), code.size_bytes());
41} 45}
42 46
43MacroEngine::MacroEngine(Engines::Maxwell3D& maxwell3d) 47MacroEngine::MacroEngine(Engines::Maxwell3D& maxwell3d_)
44 : hle_macros{std::make_unique<Tegra::HLEMacro>(maxwell3d)} {} 48 : hle_macros{std::make_unique<Tegra::HLEMacro>(maxwell3d_)}, maxwell3d{maxwell3d_} {}
45 49
46MacroEngine::~MacroEngine() = default; 50MacroEngine::~MacroEngine() = default;
47 51
@@ -59,8 +63,10 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
59 if (compiled_macro != macro_cache.end()) { 63 if (compiled_macro != macro_cache.end()) {
60 const auto& cache_info = compiled_macro->second; 64 const auto& cache_info = compiled_macro->second;
61 if (cache_info.has_hle_program) { 65 if (cache_info.has_hle_program) {
66 MICROPROFILE_SCOPE(MacroHLE);
62 cache_info.hle_program->Execute(parameters, method); 67 cache_info.hle_program->Execute(parameters, method);
63 } else { 68 } else {
69 maxwell3d.RefreshParameters();
64 cache_info.lle_program->Execute(parameters, method); 70 cache_info.lle_program->Execute(parameters, method);
65 } 71 }
66 } else { 72 } else {
@@ -101,12 +107,15 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
101 } 107 }
102 } 108 }
103 109
104 if (auto hle_program = hle_macros->GetHLEProgram(cache_info.hash)) { 110 auto hle_program = hle_macros->GetHLEProgram(cache_info.hash);
111 if (!hle_program || Settings::values.disable_macro_hle) {
112 maxwell3d.RefreshParameters();
113 cache_info.lle_program->Execute(parameters, method);
114 } else {
105 cache_info.has_hle_program = true; 115 cache_info.has_hle_program = true;
106 cache_info.hle_program = std::move(hle_program); 116 cache_info.hle_program = std::move(hle_program);
117 MICROPROFILE_SCOPE(MacroHLE);
107 cache_info.hle_program->Execute(parameters, method); 118 cache_info.hle_program->Execute(parameters, method);
108 } else {
109 cache_info.lle_program->Execute(parameters, method);
110 } 119 }
111 } 120 }
112} 121}
diff --git a/src/video_core/macro/macro.h b/src/video_core/macro/macro.h
index 07d97ba39..737ced9a4 100644
--- a/src/video_core/macro/macro.h
+++ b/src/video_core/macro/macro.h
@@ -137,6 +137,7 @@ private:
137 std::unordered_map<u32, CacheInfo> macro_cache; 137 std::unordered_map<u32, CacheInfo> macro_cache;
138 std::unordered_map<u32, std::vector<u32>> uploaded_macro_code; 138 std::unordered_map<u32, std::vector<u32>> uploaded_macro_code;
139 std::unique_ptr<HLEMacro> hle_macros; 139 std::unique_ptr<HLEMacro> hle_macros;
140 Engines::Maxwell3D& maxwell3d;
140}; 141};
141 142
142std::unique_ptr<MacroEngine> GetMacroEngine(Engines::Maxwell3D& maxwell3d); 143std::unique_ptr<MacroEngine> GetMacroEngine(Engines::Maxwell3D& maxwell3d);
diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp
index 8549db2e4..6272a4652 100644
--- a/src/video_core/macro/macro_hle.cpp
+++ b/src/video_core/macro/macro_hle.cpp
@@ -1,143 +1,551 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#include <array> 4#include <array>
5#include <vector> 5#include <vector>
6#include "common/assert.h"
6#include "common/scope_exit.h" 7#include "common/scope_exit.h"
7#include "video_core/dirty_flags.h" 8#include "video_core/dirty_flags.h"
8#include "video_core/engines/draw_manager.h" 9#include "video_core/engines/draw_manager.h"
9#include "video_core/engines/maxwell_3d.h" 10#include "video_core/engines/maxwell_3d.h"
10#include "video_core/macro/macro.h" 11#include "video_core/macro/macro.h"
11#include "video_core/macro/macro_hle.h" 12#include "video_core/macro/macro_hle.h"
13#include "video_core/memory_manager.h"
12#include "video_core/rasterizer_interface.h" 14#include "video_core/rasterizer_interface.h"
13 15
14namespace Tegra { 16namespace Tegra {
15namespace {
16 17
17using HLEFunction = void (*)(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters); 18using Maxwell3D = Engines::Maxwell3D;
18 19
19// HLE'd functions 20namespace {
20void HLE_771BB18C62444DA0(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) {
21 const u32 instance_count = parameters[2] & maxwell3d.GetRegisterValue(0xD1B);
22 maxwell3d.draw_manager->DrawIndex(
23 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0] & 0x3ffffff),
24 parameters[4], parameters[1], parameters[3], parameters[5], instance_count);
25}
26 21
27void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { 22bool IsTopologySafe(Maxwell3D::Regs::PrimitiveTopology topology) {
28 const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); 23 switch (topology) {
29 maxwell3d.draw_manager->DrawArray( 24 case Maxwell3D::Regs::PrimitiveTopology::Points:
30 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]), 25 case Maxwell3D::Regs::PrimitiveTopology::Lines:
31 parameters[3], parameters[1], parameters[4], instance_count); 26 case Maxwell3D::Regs::PrimitiveTopology::LineLoop:
27 case Maxwell3D::Regs::PrimitiveTopology::LineStrip:
28 case Maxwell3D::Regs::PrimitiveTopology::Triangles:
29 case Maxwell3D::Regs::PrimitiveTopology::TriangleStrip:
30 case Maxwell3D::Regs::PrimitiveTopology::TriangleFan:
31 case Maxwell3D::Regs::PrimitiveTopology::LinesAdjacency:
32 case Maxwell3D::Regs::PrimitiveTopology::LineStripAdjacency:
33 case Maxwell3D::Regs::PrimitiveTopology::TrianglesAdjacency:
34 case Maxwell3D::Regs::PrimitiveTopology::TriangleStripAdjacency:
35 case Maxwell3D::Regs::PrimitiveTopology::Patches:
36 return true;
37 case Maxwell3D::Regs::PrimitiveTopology::Quads:
38 case Maxwell3D::Regs::PrimitiveTopology::QuadStrip:
39 case Maxwell3D::Regs::PrimitiveTopology::Polygon:
40 default:
41 return false;
42 }
32} 43}
33 44
34void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { 45class HLEMacroImpl : public CachedMacro {
35 const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); 46public:
36 const u32 element_base = parameters[4]; 47 explicit HLEMacroImpl(Maxwell3D& maxwell3d_) : maxwell3d{maxwell3d_} {}
37 const u32 base_instance = parameters[5];
38 maxwell3d.regs.vertex_id_base = element_base;
39 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
40 maxwell3d.CallMethod(0x8e3, 0x640, true);
41 maxwell3d.CallMethod(0x8e4, element_base, true);
42 maxwell3d.CallMethod(0x8e5, base_instance, true);
43
44 maxwell3d.draw_manager->DrawIndex(
45 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]),
46 parameters[3], parameters[1], element_base, base_instance, instance_count);
47
48 maxwell3d.regs.vertex_id_base = 0x0;
49 maxwell3d.CallMethod(0x8e3, 0x640, true);
50 maxwell3d.CallMethod(0x8e4, 0x0, true);
51 maxwell3d.CallMethod(0x8e5, 0x0, true);
52}
53 48
54// Multidraw Indirect 49protected:
55void HLE_3F5E74B9C9A50164(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { 50 Maxwell3D& maxwell3d;
56 SCOPE_EXIT({ 51};
57 // Clean everything. 52
58 maxwell3d.regs.vertex_id_base = 0x0; 53/*
59 maxwell3d.CallMethod(0x8e3, 0x640, true); 54 * @note: these macros have two versions, a normal and extended version, with the extended version
60 maxwell3d.CallMethod(0x8e4, 0x0, true); 55 * also assigning the base vertex/instance.
61 maxwell3d.CallMethod(0x8e5, 0x0, true); 56 */
57template <bool extended>
58class HLE_DrawArraysIndirect final : public HLEMacroImpl {
59public:
60 explicit HLE_DrawArraysIndirect(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
61
62 void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
63 auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[0]);
64 if (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology)) {
65 Fallback(parameters);
66 return;
67 }
68
69 auto& params = maxwell3d.draw_manager->GetIndirectParams();
70 params.is_indexed = false;
71 params.include_count = false;
72 params.count_start_address = 0;
73 params.indirect_start_address = maxwell3d.GetMacroAddress(1);
74 params.buffer_size = 4 * sizeof(u32);
75 params.max_draw_counts = 1;
76 params.stride = 0;
77
78 if constexpr (extended) {
79 maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro;
80 maxwell3d.SetHLEReplacementAttributeType(
81 0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseInstance);
82 }
83
84 maxwell3d.draw_manager->DrawArrayIndirect(topology);
85
86 if constexpr (extended) {
87 maxwell3d.engine_state = Maxwell3D::EngineHint::None;
88 maxwell3d.replace_table.clear();
89 }
90 }
91
92private:
93 void Fallback(const std::vector<u32>& parameters) {
94 SCOPE_EXIT({
95 if (extended) {
96 maxwell3d.engine_state = Maxwell3D::EngineHint::None;
97 maxwell3d.replace_table.clear();
98 }
99 });
100 maxwell3d.RefreshParameters();
101 const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]);
102
103 auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[0]);
104 const u32 vertex_first = parameters[3];
105 const u32 vertex_count = parameters[1];
106
107 if (!IsTopologySafe(topology) &&
108 static_cast<size_t>(maxwell3d.GetMaxCurrentVertices()) <
109 static_cast<size_t>(vertex_first) + static_cast<size_t>(vertex_count)) {
110 ASSERT_MSG(false, "Faulty draw!");
111 return;
112 }
113
114 const u32 base_instance = parameters[4];
115 if constexpr (extended) {
116 maxwell3d.regs.global_base_instance_index = base_instance;
117 maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro;
118 maxwell3d.SetHLEReplacementAttributeType(
119 0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseInstance);
120 }
121
122 maxwell3d.draw_manager->DrawArray(topology, vertex_first, vertex_count, base_instance,
123 instance_count);
124
125 if constexpr (extended) {
126 maxwell3d.regs.global_base_instance_index = 0;
127 maxwell3d.engine_state = Maxwell3D::EngineHint::None;
128 maxwell3d.replace_table.clear();
129 }
130 }
131};
132
133/*
134 * @note: these macros have two versions, a normal and extended version, with the extended version
135 * also assigning the base vertex/instance.
136 */
137template <bool extended>
138class HLE_DrawIndexedIndirect final : public HLEMacroImpl {
139public:
140 explicit HLE_DrawIndexedIndirect(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
141
142 void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
143 auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[0]);
144 if (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology)) {
145 Fallback(parameters);
146 return;
147 }
148
149 const u32 estimate = static_cast<u32>(maxwell3d.EstimateIndexBufferSize());
150 const u32 element_base = parameters[4];
151 const u32 base_instance = parameters[5];
152 maxwell3d.regs.vertex_id_base = element_base;
153 maxwell3d.regs.global_base_vertex_index = element_base;
154 maxwell3d.regs.global_base_instance_index = base_instance;
155 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
156 if constexpr (extended) {
157 maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro;
158 maxwell3d.SetHLEReplacementAttributeType(
159 0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseVertex);
160 maxwell3d.SetHLEReplacementAttributeType(
161 0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance);
162 }
163 auto& params = maxwell3d.draw_manager->GetIndirectParams();
164 params.is_indexed = true;
165 params.include_count = false;
166 params.count_start_address = 0;
167 params.indirect_start_address = maxwell3d.GetMacroAddress(1);
168 params.buffer_size = 5 * sizeof(u32);
169 params.max_draw_counts = 1;
170 params.stride = 0;
62 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; 171 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
63 }); 172 maxwell3d.draw_manager->DrawIndexedIndirect(topology, 0, estimate);
64 const u32 start_indirect = parameters[0]; 173 maxwell3d.regs.vertex_id_base = 0x0;
65 const u32 end_indirect = parameters[1]; 174 maxwell3d.regs.global_base_vertex_index = 0x0;
66 if (start_indirect >= end_indirect) { 175 maxwell3d.regs.global_base_instance_index = 0x0;
67 // Nothing to do. 176 if constexpr (extended) {
68 return; 177 maxwell3d.engine_state = Maxwell3D::EngineHint::None;
69 } 178 maxwell3d.replace_table.clear();
70 const u32 padding = parameters[3]; 179 }
71 const std::size_t max_draws = parameters[4]; 180 }
72 181
73 const u32 indirect_words = 5 + padding; 182private:
74 const std::size_t first_draw = start_indirect; 183 void Fallback(const std::vector<u32>& parameters) {
75 const std::size_t effective_draws = end_indirect - start_indirect; 184 maxwell3d.RefreshParameters();
76 const std::size_t last_draw = start_indirect + std::min(effective_draws, max_draws); 185 const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]);
77 186 const u32 element_base = parameters[4];
78 for (std::size_t index = first_draw; index < last_draw; index++) { 187 const u32 base_instance = parameters[5];
79 const std::size_t base = index * indirect_words + 5; 188 maxwell3d.regs.vertex_id_base = element_base;
80 const u32 base_vertex = parameters[base + 3]; 189 maxwell3d.regs.global_base_vertex_index = element_base;
81 const u32 base_instance = parameters[base + 4]; 190 maxwell3d.regs.global_base_instance_index = base_instance;
82 maxwell3d.regs.vertex_id_base = base_vertex;
83 maxwell3d.CallMethod(0x8e3, 0x640, true);
84 maxwell3d.CallMethod(0x8e4, base_vertex, true);
85 maxwell3d.CallMethod(0x8e5, base_instance, true);
86 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; 191 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
192 if constexpr (extended) {
193 maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro;
194 maxwell3d.SetHLEReplacementAttributeType(
195 0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseVertex);
196 maxwell3d.SetHLEReplacementAttributeType(
197 0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance);
198 }
199
87 maxwell3d.draw_manager->DrawIndex( 200 maxwell3d.draw_manager->DrawIndex(
88 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[2]), 201 static_cast<Tegra::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]), parameters[3],
89 parameters[base + 2], parameters[base], base_vertex, base_instance, 202 parameters[1], element_base, base_instance, instance_count);
90 parameters[base + 1]); 203
204 maxwell3d.regs.vertex_id_base = 0x0;
205 maxwell3d.regs.global_base_vertex_index = 0x0;
206 maxwell3d.regs.global_base_instance_index = 0x0;
207 if constexpr (extended) {
208 maxwell3d.engine_state = Maxwell3D::EngineHint::None;
209 maxwell3d.replace_table.clear();
210 }
91 } 211 }
92} 212};
93 213
94// Multi-layer Clear 214class HLE_MultiLayerClear final : public HLEMacroImpl {
95void HLE_EAD26C3E2109B06B(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { 215public:
96 ASSERT(parameters.size() == 1); 216 explicit HLE_MultiLayerClear(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
97 217
98 const Engines::Maxwell3D::Regs::ClearSurface clear_params{parameters[0]}; 218 void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
99 const u32 rt_index = clear_params.RT; 219 maxwell3d.RefreshParameters();
100 const u32 num_layers = maxwell3d.regs.rt[rt_index].depth; 220 ASSERT(parameters.size() == 1);
101 ASSERT(clear_params.layer == 0);
102 221
103 maxwell3d.regs.clear_surface.raw = clear_params.raw; 222 const Maxwell3D::Regs::ClearSurface clear_params{parameters[0]};
104 maxwell3d.draw_manager->Clear(num_layers); 223 const u32 rt_index = clear_params.RT;
105} 224 const u32 num_layers = maxwell3d.regs.rt[rt_index].depth;
225 ASSERT(clear_params.layer == 0);
226
227 maxwell3d.regs.clear_surface.raw = clear_params.raw;
228 maxwell3d.draw_manager->Clear(num_layers);
229 }
230};
231
232class HLE_MultiDrawIndexedIndirectCount final : public HLEMacroImpl {
233public:
234 explicit HLE_MultiDrawIndexedIndirectCount(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
235
236 void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
237 const auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[2]);
238 if (!IsTopologySafe(topology)) {
239 Fallback(parameters);
240 return;
241 }
242
243 const u32 start_indirect = parameters[0];
244 const u32 end_indirect = parameters[1];
245 if (start_indirect >= end_indirect) {
246 // Nothing to do.
247 return;
248 }
106 249
107constexpr std::array<std::pair<u64, HLEFunction>, 5> hle_funcs{{ 250 const u32 padding = parameters[3]; // padding is in words
108 {0x771BB18C62444DA0, &HLE_771BB18C62444DA0},
109 {0x0D61FC9FAAC9FCAD, &HLE_0D61FC9FAAC9FCAD},
110 {0x0217920100488FF7, &HLE_0217920100488FF7},
111 {0x3F5E74B9C9A50164, &HLE_3F5E74B9C9A50164},
112 {0xEAD26C3E2109B06B, &HLE_EAD26C3E2109B06B},
113}};
114 251
115class HLEMacroImpl final : public CachedMacro { 252 // size of each indirect segment
253 const u32 indirect_words = 5 + padding;
254 const u32 stride = indirect_words * sizeof(u32);
255 const std::size_t draw_count = end_indirect - start_indirect;
256 const u32 estimate = static_cast<u32>(maxwell3d.EstimateIndexBufferSize());
257 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
258 auto& params = maxwell3d.draw_manager->GetIndirectParams();
259 params.is_indexed = true;
260 params.include_count = true;
261 params.count_start_address = maxwell3d.GetMacroAddress(4);
262 params.indirect_start_address = maxwell3d.GetMacroAddress(5);
263 params.buffer_size = stride * draw_count;
264 params.max_draw_counts = draw_count;
265 params.stride = stride;
266 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
267 maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro;
268 maxwell3d.SetHLEReplacementAttributeType(
269 0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseVertex);
270 maxwell3d.SetHLEReplacementAttributeType(
271 0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance);
272 maxwell3d.SetHLEReplacementAttributeType(0, 0x648,
273 Maxwell3D::HLEReplacementAttributeType::DrawID);
274 maxwell3d.draw_manager->DrawIndexedIndirect(topology, 0, estimate);
275 maxwell3d.engine_state = Maxwell3D::EngineHint::None;
276 maxwell3d.replace_table.clear();
277 }
278
279private:
280 void Fallback(const std::vector<u32>& parameters) {
281 SCOPE_EXIT({
282 // Clean everything.
283 maxwell3d.regs.vertex_id_base = 0x0;
284 maxwell3d.engine_state = Maxwell3D::EngineHint::None;
285 maxwell3d.replace_table.clear();
286 });
287 maxwell3d.RefreshParameters();
288 const u32 start_indirect = parameters[0];
289 const u32 end_indirect = parameters[1];
290 if (start_indirect >= end_indirect) {
291 // Nothing to do.
292 return;
293 }
294 const auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[2]);
295 const u32 padding = parameters[3];
296 const std::size_t max_draws = parameters[4];
297
298 const u32 indirect_words = 5 + padding;
299 const std::size_t first_draw = start_indirect;
300 const std::size_t effective_draws = end_indirect - start_indirect;
301 const std::size_t last_draw = start_indirect + std::min(effective_draws, max_draws);
302
303 for (std::size_t index = first_draw; index < last_draw; index++) {
304 const std::size_t base = index * indirect_words + 5;
305 const u32 base_vertex = parameters[base + 3];
306 const u32 base_instance = parameters[base + 4];
307 maxwell3d.regs.vertex_id_base = base_vertex;
308 maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro;
309 maxwell3d.SetHLEReplacementAttributeType(
310 0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseVertex);
311 maxwell3d.SetHLEReplacementAttributeType(
312 0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance);
313 maxwell3d.CallMethod(0x8e3, 0x648, true);
314 maxwell3d.CallMethod(0x8e4, static_cast<u32>(index), true);
315 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
316 maxwell3d.draw_manager->DrawIndex(topology, parameters[base + 2], parameters[base],
317 base_vertex, base_instance, parameters[base + 1]);
318 }
319 }
320};
321
322class HLE_C713C83D8F63CCF3 final : public HLEMacroImpl {
323public:
324 explicit HLE_C713C83D8F63CCF3(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
325
326 void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
327 maxwell3d.RefreshParameters();
328 const u32 offset = (parameters[0] & 0x3FFFFFFF) << 2;
329 const u32 address = maxwell3d.regs.shadow_scratch[24];
330 auto& const_buffer = maxwell3d.regs.const_buffer;
331 const_buffer.size = 0x7000;
332 const_buffer.address_high = (address >> 24) & 0xFF;
333 const_buffer.address_low = address << 8;
334 const_buffer.offset = offset;
335 }
336};
337
338class HLE_D7333D26E0A93EDE final : public HLEMacroImpl {
339public:
340 explicit HLE_D7333D26E0A93EDE(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
341
342 void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
343 maxwell3d.RefreshParameters();
344 const size_t index = parameters[0];
345 const u32 address = maxwell3d.regs.shadow_scratch[42 + index];
346 const u32 size = maxwell3d.regs.shadow_scratch[47 + index];
347 auto& const_buffer = maxwell3d.regs.const_buffer;
348 const_buffer.size = size;
349 const_buffer.address_high = (address >> 24) & 0xFF;
350 const_buffer.address_low = address << 8;
351 }
352};
353
354class HLE_BindShader final : public HLEMacroImpl {
355public:
356 explicit HLE_BindShader(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
357
358 void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
359 maxwell3d.RefreshParameters();
360 auto& regs = maxwell3d.regs;
361 const u32 index = parameters[0];
362 if ((parameters[1] - regs.shadow_scratch[28 + index]) == 0) {
363 return;
364 }
365
366 regs.pipelines[index & 0xF].offset = parameters[2];
367 maxwell3d.dirty.flags[VideoCommon::Dirty::Shaders] = true;
368 regs.shadow_scratch[28 + index] = parameters[1];
369 regs.shadow_scratch[34 + index] = parameters[2];
370
371 const u32 address = parameters[4];
372 auto& const_buffer = regs.const_buffer;
373 const_buffer.size = 0x10000;
374 const_buffer.address_high = (address >> 24) & 0xFF;
375 const_buffer.address_low = address << 8;
376
377 const size_t bind_group_id = parameters[3] & 0x7F;
378 auto& bind_group = regs.bind_groups[bind_group_id];
379 bind_group.raw_config = 0x11;
380 maxwell3d.ProcessCBBind(bind_group_id);
381 }
382};
383
384class HLE_SetRasterBoundingBox final : public HLEMacroImpl {
385public:
386 explicit HLE_SetRasterBoundingBox(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
387
388 void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
389 maxwell3d.RefreshParameters();
390 const u32 raster_mode = parameters[0];
391 auto& regs = maxwell3d.regs;
392 const u32 raster_enabled = maxwell3d.regs.conservative_raster_enable;
393 const u32 scratch_data = maxwell3d.regs.shadow_scratch[52];
394 regs.raster_bounding_box.raw = raster_mode & 0xFFFFF00F;
395 regs.raster_bounding_box.pad.Assign(scratch_data & raster_enabled);
396 }
397};
398
399template <size_t base_size>
400class HLE_ClearConstBuffer final : public HLEMacroImpl {
401public:
402 explicit HLE_ClearConstBuffer(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
403
404 void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
405 maxwell3d.RefreshParameters();
406 static constexpr std::array<u32, base_size> zeroes{};
407 auto& regs = maxwell3d.regs;
408 regs.const_buffer.size = static_cast<u32>(base_size);
409 regs.const_buffer.address_high = parameters[0];
410 regs.const_buffer.address_low = parameters[1];
411 regs.const_buffer.offset = 0;
412 maxwell3d.ProcessCBMultiData(zeroes.data(), parameters[2] * 4);
413 }
414};
415
416class HLE_ClearMemory final : public HLEMacroImpl {
116public: 417public:
117 explicit HLEMacroImpl(Engines::Maxwell3D& maxwell3d_, HLEFunction func_) 418 explicit HLE_ClearMemory(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
118 : maxwell3d{maxwell3d_}, func{func_} {} 419
420 void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
421 maxwell3d.RefreshParameters();
119 422
120 void Execute(const std::vector<u32>& parameters, u32 method) override { 423 const u32 needed_memory = parameters[2] / sizeof(u32);
121 func(maxwell3d, parameters); 424 if (needed_memory > zero_memory.size()) {
425 zero_memory.resize(needed_memory, 0);
426 }
427 auto& regs = maxwell3d.regs;
428 regs.upload.line_length_in = parameters[2];
429 regs.upload.line_count = 1;
430 regs.upload.dest.address_high = parameters[0];
431 regs.upload.dest.address_low = parameters[1];
432 maxwell3d.CallMethod(static_cast<size_t>(MAXWELL3D_REG_INDEX(launch_dma)), 0x1011, true);
433 maxwell3d.CallMultiMethod(static_cast<size_t>(MAXWELL3D_REG_INDEX(inline_data)),
434 zero_memory.data(), needed_memory, needed_memory);
122 } 435 }
123 436
124private: 437private:
125 Engines::Maxwell3D& maxwell3d; 438 std::vector<u32> zero_memory;
126 HLEFunction func; 439};
440
441class HLE_TransformFeedbackSetup final : public HLEMacroImpl {
442public:
443 explicit HLE_TransformFeedbackSetup(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
444
445 void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
446 maxwell3d.RefreshParameters();
447
448 auto& regs = maxwell3d.regs;
449 regs.transform_feedback_enabled = 1;
450 regs.transform_feedback.buffers[0].start_offset = 0;
451 regs.transform_feedback.buffers[1].start_offset = 0;
452 regs.transform_feedback.buffers[2].start_offset = 0;
453 regs.transform_feedback.buffers[3].start_offset = 0;
454
455 regs.upload.line_length_in = 4;
456 regs.upload.line_count = 1;
457 regs.upload.dest.address_high = parameters[0];
458 regs.upload.dest.address_low = parameters[1];
459 maxwell3d.CallMethod(static_cast<size_t>(MAXWELL3D_REG_INDEX(launch_dma)), 0x1011, true);
460 maxwell3d.CallMethod(static_cast<size_t>(MAXWELL3D_REG_INDEX(inline_data)),
461 regs.transform_feedback.controls[0].stride, true);
462 }
127}; 463};
128 464
129} // Anonymous namespace 465} // Anonymous namespace
130 466
131HLEMacro::HLEMacro(Engines::Maxwell3D& maxwell3d_) : maxwell3d{maxwell3d_} {} 467HLEMacro::HLEMacro(Maxwell3D& maxwell3d_) : maxwell3d{maxwell3d_} {
468 builders.emplace(0x0D61FC9FAAC9FCADULL,
469 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
470 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
471 return std::make_unique<HLE_DrawArraysIndirect<false>>(maxwell3d__);
472 }));
473 builders.emplace(0x8A4D173EB99A8603ULL,
474 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
475 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
476 return std::make_unique<HLE_DrawArraysIndirect<true>>(maxwell3d__);
477 }));
478 builders.emplace(0x771BB18C62444DA0ULL,
479 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
480 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
481 return std::make_unique<HLE_DrawIndexedIndirect<false>>(maxwell3d__);
482 }));
483 builders.emplace(0x0217920100488FF7ULL,
484 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
485 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
486 return std::make_unique<HLE_DrawIndexedIndirect<true>>(maxwell3d__);
487 }));
488 builders.emplace(0x3F5E74B9C9A50164ULL,
489 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
490 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
491 return std::make_unique<HLE_MultiDrawIndexedIndirectCount>(
492 maxwell3d__);
493 }));
494 builders.emplace(0xEAD26C3E2109B06BULL,
495 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
496 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
497 return std::make_unique<HLE_MultiLayerClear>(maxwell3d__);
498 }));
499 builders.emplace(0xC713C83D8F63CCF3ULL,
500 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
501 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
502 return std::make_unique<HLE_C713C83D8F63CCF3>(maxwell3d__);
503 }));
504 builders.emplace(0xD7333D26E0A93EDEULL,
505 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
506 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
507 return std::make_unique<HLE_D7333D26E0A93EDE>(maxwell3d__);
508 }));
509 builders.emplace(0xEB29B2A09AA06D38ULL,
510 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
511 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
512 return std::make_unique<HLE_BindShader>(maxwell3d__);
513 }));
514 builders.emplace(0xDB1341DBEB4C8AF7ULL,
515 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
516 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
517 return std::make_unique<HLE_SetRasterBoundingBox>(maxwell3d__);
518 }));
519 builders.emplace(0x6C97861D891EDf7EULL,
520 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
521 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
522 return std::make_unique<HLE_ClearConstBuffer<0x5F00>>(maxwell3d__);
523 }));
524 builders.emplace(0xD246FDDF3A6173D7ULL,
525 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
526 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
527 return std::make_unique<HLE_ClearConstBuffer<0x7000>>(maxwell3d__);
528 }));
529 builders.emplace(0xEE4D0004BEC8ECF4ULL,
530 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
531 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
532 return std::make_unique<HLE_ClearMemory>(maxwell3d__);
533 }));
534 builders.emplace(0xFC0CF27F5FFAA661ULL,
535 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
536 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
537 return std::make_unique<HLE_TransformFeedbackSetup>(maxwell3d__);
538 }));
539}
540
132HLEMacro::~HLEMacro() = default; 541HLEMacro::~HLEMacro() = default;
133 542
134std::unique_ptr<CachedMacro> HLEMacro::GetHLEProgram(u64 hash) const { 543std::unique_ptr<CachedMacro> HLEMacro::GetHLEProgram(u64 hash) const {
135 const auto it = std::find_if(hle_funcs.cbegin(), hle_funcs.cend(), 544 const auto it = builders.find(hash);
136 [hash](const auto& pair) { return pair.first == hash; }); 545 if (it == builders.end()) {
137 if (it == hle_funcs.end()) {
138 return nullptr; 546 return nullptr;
139 } 547 }
140 return std::make_unique<HLEMacroImpl>(maxwell3d, it->second); 548 return it->second(maxwell3d);
141} 549}
142 550
143} // namespace Tegra 551} // namespace Tegra
diff --git a/src/video_core/macro/macro_hle.h b/src/video_core/macro/macro_hle.h
index 625332c9d..33f92fab1 100644
--- a/src/video_core/macro/macro_hle.h
+++ b/src/video_core/macro/macro_hle.h
@@ -3,7 +3,10 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <functional>
6#include <memory> 7#include <memory>
8#include <unordered_map>
9
7#include "common/common_types.h" 10#include "common/common_types.h"
8 11
9namespace Tegra { 12namespace Tegra {
@@ -23,6 +26,8 @@ public:
23 26
24private: 27private:
25 Engines::Maxwell3D& maxwell3d; 28 Engines::Maxwell3D& maxwell3d;
29 std::unordered_map<u64, std::function<std::unique_ptr<CachedMacro>(Engines::Maxwell3D&)>>
30 builders;
26}; 31};
27 32
28} // namespace Tegra 33} // namespace Tegra
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 8c8dfcca6..3bcae3503 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -6,11 +6,13 @@
6#include "common/alignment.h" 6#include "common/alignment.h"
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "common/settings.h"
9#include "core/core.h" 10#include "core/core.h"
10#include "core/device_memory.h" 11#include "core/device_memory.h"
11#include "core/hle/kernel/k_page_table.h" 12#include "core/hle/kernel/k_page_table.h"
12#include "core/hle/kernel/k_process.h" 13#include "core/hle/kernel/k_process.h"
13#include "core/memory.h" 14#include "core/memory.h"
15#include "video_core/invalidation_accumulator.h"
14#include "video_core/memory_manager.h" 16#include "video_core/memory_manager.h"
15#include "video_core/rasterizer_interface.h" 17#include "video_core/rasterizer_interface.h"
16#include "video_core/renderer_base.h" 18#include "video_core/renderer_base.h"
@@ -25,7 +27,9 @@ MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, u64
25 address_space_bits{address_space_bits_}, page_bits{page_bits_}, big_page_bits{big_page_bits_}, 27 address_space_bits{address_space_bits_}, page_bits{page_bits_}, big_page_bits{big_page_bits_},
26 entries{}, big_entries{}, page_table{address_space_bits, address_space_bits + page_bits - 38, 28 entries{}, big_entries{}, page_table{address_space_bits, address_space_bits + page_bits - 38,
27 page_bits != big_page_bits ? page_bits : 0}, 29 page_bits != big_page_bits ? page_bits : 0},
28 unique_identifier{unique_identifier_generator.fetch_add(1, std::memory_order_acq_rel)} { 30 kind_map{PTEKind::INVALID}, unique_identifier{unique_identifier_generator.fetch_add(
31 1, std::memory_order_acq_rel)},
32 accumulator{std::make_unique<VideoCommon::InvalidationAccumulator>()} {
29 address_space_size = 1ULL << address_space_bits; 33 address_space_size = 1ULL << address_space_bits;
30 page_size = 1ULL << page_bits; 34 page_size = 1ULL << page_bits;
31 page_mask = page_size - 1ULL; 35 page_mask = page_size - 1ULL;
@@ -41,11 +45,12 @@ MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, u64
41 big_entries.resize(big_page_table_size / 32, 0); 45 big_entries.resize(big_page_table_size / 32, 0);
42 big_page_table_cpu.resize(big_page_table_size); 46 big_page_table_cpu.resize(big_page_table_size);
43 big_page_continous.resize(big_page_table_size / continous_bits, 0); 47 big_page_continous.resize(big_page_table_size / continous_bits, 0);
44 std::array<PTEKind, 32> kind_valus;
45 kind_valus.fill(PTEKind::INVALID);
46 big_kinds.resize(big_page_table_size / 32, kind_valus);
47 entries.resize(page_table_size / 32, 0); 48 entries.resize(page_table_size / 32, 0);
48 kinds.resize(page_table_size / 32, kind_valus); 49 if (!Settings::IsGPULevelExtreme() && Settings::IsFastmemEnabled()) {
50 fastmem_arena = system.DeviceMemory().buffer.VirtualBasePointer();
51 } else {
52 fastmem_arena = nullptr;
53 }
49} 54}
50 55
51MemoryManager::~MemoryManager() = default; 56MemoryManager::~MemoryManager() = default;
@@ -83,38 +88,7 @@ void MemoryManager::SetEntry(size_t position, MemoryManager::EntryType entry) {
83} 88}
84 89
85PTEKind MemoryManager::GetPageKind(GPUVAddr gpu_addr) const { 90PTEKind MemoryManager::GetPageKind(GPUVAddr gpu_addr) const {
86 auto entry = GetEntry<true>(gpu_addr); 91 return kind_map.GetValueAt(gpu_addr);
87 if (entry == EntryType::Mapped || entry == EntryType::Reserved) [[likely]] {
88 return GetKind<true>(gpu_addr);
89 } else {
90 return GetKind<false>(gpu_addr);
91 }
92}
93
94template <bool is_big_page>
95PTEKind MemoryManager::GetKind(size_t position) const {
96 if constexpr (is_big_page) {
97 position = position >> big_page_bits;
98 const size_t sub_index = position % 32;
99 return big_kinds[position / 32][sub_index];
100 } else {
101 position = position >> page_bits;
102 const size_t sub_index = position % 32;
103 return kinds[position / 32][sub_index];
104 }
105}
106
107template <bool is_big_page>
108void MemoryManager::SetKind(size_t position, PTEKind kind) {
109 if constexpr (is_big_page) {
110 position = position >> big_page_bits;
111 const size_t sub_index = position % 32;
112 big_kinds[position / 32][sub_index] = kind;
113 } else {
114 position = position >> page_bits;
115 const size_t sub_index = position % 32;
116 kinds[position / 32][sub_index] = kind;
117 }
118} 92}
119 93
120inline bool MemoryManager::IsBigPageContinous(size_t big_page_index) const { 94inline bool MemoryManager::IsBigPageContinous(size_t big_page_index) const {
@@ -141,7 +115,6 @@ GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cp
141 const GPUVAddr current_gpu_addr = gpu_addr + offset; 115 const GPUVAddr current_gpu_addr = gpu_addr + offset;
142 [[maybe_unused]] const auto current_entry_type = GetEntry<false>(current_gpu_addr); 116 [[maybe_unused]] const auto current_entry_type = GetEntry<false>(current_gpu_addr);
143 SetEntry<false>(current_gpu_addr, entry_type); 117 SetEntry<false>(current_gpu_addr, entry_type);
144 SetKind<false>(current_gpu_addr, kind);
145 if (current_entry_type != entry_type) { 118 if (current_entry_type != entry_type) {
146 rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, page_size); 119 rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, page_size);
147 } 120 }
@@ -153,6 +126,7 @@ GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cp
153 } 126 }
154 remaining_size -= page_size; 127 remaining_size -= page_size;
155 } 128 }
129 kind_map.Map(gpu_addr, gpu_addr + size, kind);
156 return gpu_addr; 130 return gpu_addr;
157} 131}
158 132
@@ -164,7 +138,6 @@ GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr
164 const GPUVAddr current_gpu_addr = gpu_addr + offset; 138 const GPUVAddr current_gpu_addr = gpu_addr + offset;
165 [[maybe_unused]] const auto current_entry_type = GetEntry<true>(current_gpu_addr); 139 [[maybe_unused]] const auto current_entry_type = GetEntry<true>(current_gpu_addr);
166 SetEntry<true>(current_gpu_addr, entry_type); 140 SetEntry<true>(current_gpu_addr, entry_type);
167 SetKind<true>(current_gpu_addr, kind);
168 if (current_entry_type != entry_type) { 141 if (current_entry_type != entry_type) {
169 rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, big_page_size); 142 rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, big_page_size);
170 } 143 }
@@ -193,6 +166,7 @@ GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr
193 } 166 }
194 remaining_size -= big_page_size; 167 remaining_size -= big_page_size;
195 } 168 }
169 kind_map.Map(gpu_addr, gpu_addr + size, kind);
196 return gpu_addr; 170 return gpu_addr;
197} 171}
198 172
@@ -219,15 +193,12 @@ void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) {
219 if (size == 0) { 193 if (size == 0) {
220 return; 194 return;
221 } 195 }
222 const auto submapped_ranges = GetSubmappedRange(gpu_addr, size); 196 GetSubmappedRangeImpl<false>(gpu_addr, size, page_stash);
223 197
224 for (const auto& [map_addr, map_size] : submapped_ranges) { 198 for (const auto& [map_addr, map_size] : page_stash) {
225 // Flush and invalidate through the GPU interface, to be asynchronous if possible. 199 rasterizer->UnmapMemory(map_addr, map_size);
226 const std::optional<VAddr> cpu_addr = GpuToCpuAddress(map_addr);
227 ASSERT(cpu_addr);
228
229 rasterizer->UnmapMemory(*cpu_addr, map_size);
230 } 200 }
201 page_stash.clear();
231 202
232 BigPageTableOp<EntryType::Free>(gpu_addr, 0, size, PTEKind::INVALID); 203 BigPageTableOp<EntryType::Free>(gpu_addr, 0, size, PTEKind::INVALID);
233 PageTableOp<EntryType::Free>(gpu_addr, 0, size, PTEKind::INVALID); 204 PageTableOp<EntryType::Free>(gpu_addr, 0, size, PTEKind::INVALID);
@@ -325,9 +296,15 @@ template <bool is_big_pages, typename FuncMapped, typename FuncReserved, typenam
325inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, 296inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size,
326 FuncMapped&& func_mapped, FuncReserved&& func_reserved, 297 FuncMapped&& func_mapped, FuncReserved&& func_reserved,
327 FuncUnmapped&& func_unmapped) const { 298 FuncUnmapped&& func_unmapped) const {
328 static constexpr bool BOOL_BREAK_MAPPED = std::is_same_v<FuncMapped, bool>; 299 using FuncMappedReturn =
329 static constexpr bool BOOL_BREAK_RESERVED = std::is_same_v<FuncReserved, bool>; 300 typename std::invoke_result<FuncMapped, std::size_t, std::size_t, std::size_t>::type;
330 static constexpr bool BOOL_BREAK_UNMAPPED = std::is_same_v<FuncUnmapped, bool>; 301 using FuncReservedReturn =
302 typename std::invoke_result<FuncReserved, std::size_t, std::size_t, std::size_t>::type;
303 using FuncUnmappedReturn =
304 typename std::invoke_result<FuncUnmapped, std::size_t, std::size_t, std::size_t>::type;
305 static constexpr bool BOOL_BREAK_MAPPED = std::is_same_v<FuncMappedReturn, bool>;
306 static constexpr bool BOOL_BREAK_RESERVED = std::is_same_v<FuncReservedReturn, bool>;
307 static constexpr bool BOOL_BREAK_UNMAPPED = std::is_same_v<FuncUnmappedReturn, bool>;
331 u64 used_page_size; 308 u64 used_page_size;
332 u64 used_page_mask; 309 u64 used_page_mask;
333 u64 used_page_bits; 310 u64 used_page_bits;
@@ -383,9 +360,9 @@ inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t si
383 } 360 }
384} 361}
385 362
386template <bool is_safe> 363template <bool is_safe, bool use_fastmem>
387void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, 364void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
388 std::size_t size) const { 365 [[maybe_unused]] VideoCommon::CacheType which) const {
389 auto set_to_zero = [&]([[maybe_unused]] std::size_t page_index, 366 auto set_to_zero = [&]([[maybe_unused]] std::size_t page_index,
390 [[maybe_unused]] std::size_t offset, std::size_t copy_amount) { 367 [[maybe_unused]] std::size_t offset, std::size_t copy_amount) {
391 std::memset(dest_buffer, 0, copy_amount); 368 std::memset(dest_buffer, 0, copy_amount);
@@ -395,23 +372,31 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer,
395 const VAddr cpu_addr_base = 372 const VAddr cpu_addr_base =
396 (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset; 373 (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
397 if constexpr (is_safe) { 374 if constexpr (is_safe) {
398 rasterizer->FlushRegion(cpu_addr_base, copy_amount); 375 rasterizer->FlushRegion(cpu_addr_base, copy_amount, which);
376 }
377 if constexpr (use_fastmem) {
378 std::memcpy(dest_buffer, &fastmem_arena[cpu_addr_base], copy_amount);
379 } else {
380 u8* physical = memory.GetPointer(cpu_addr_base);
381 std::memcpy(dest_buffer, physical, copy_amount);
399 } 382 }
400 u8* physical = memory.GetPointer(cpu_addr_base);
401 std::memcpy(dest_buffer, physical, copy_amount);
402 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; 383 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
403 }; 384 };
404 auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { 385 auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
405 const VAddr cpu_addr_base = 386 const VAddr cpu_addr_base =
406 (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; 387 (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
407 if constexpr (is_safe) { 388 if constexpr (is_safe) {
408 rasterizer->FlushRegion(cpu_addr_base, copy_amount); 389 rasterizer->FlushRegion(cpu_addr_base, copy_amount, which);
409 } 390 }
410 if (!IsBigPageContinous(page_index)) [[unlikely]] { 391 if constexpr (use_fastmem) {
411 memory.ReadBlockUnsafe(cpu_addr_base, dest_buffer, copy_amount); 392 std::memcpy(dest_buffer, &fastmem_arena[cpu_addr_base], copy_amount);
412 } else { 393 } else {
413 u8* physical = memory.GetPointer(cpu_addr_base); 394 if (!IsBigPageContinous(page_index)) [[unlikely]] {
414 std::memcpy(dest_buffer, physical, copy_amount); 395 memory.ReadBlockUnsafe(cpu_addr_base, dest_buffer, copy_amount);
396 } else {
397 u8* physical = memory.GetPointer(cpu_addr_base);
398 std::memcpy(dest_buffer, physical, copy_amount);
399 }
415 } 400 }
416 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; 401 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
417 }; 402 };
@@ -423,18 +408,27 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer,
423 MemoryOperation<true>(gpu_src_addr, size, mapped_big, set_to_zero, read_short_pages); 408 MemoryOperation<true>(gpu_src_addr, size, mapped_big, set_to_zero, read_short_pages);
424} 409}
425 410
426void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const { 411void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
427 ReadBlockImpl<true>(gpu_src_addr, dest_buffer, size); 412 VideoCommon::CacheType which) const {
413 if (fastmem_arena) [[likely]] {
414 ReadBlockImpl<true, true>(gpu_src_addr, dest_buffer, size, which);
415 return;
416 }
417 ReadBlockImpl<true, false>(gpu_src_addr, dest_buffer, size, which);
428} 418}
429 419
430void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer, 420void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer,
431 const std::size_t size) const { 421 const std::size_t size) const {
432 ReadBlockImpl<false>(gpu_src_addr, dest_buffer, size); 422 if (fastmem_arena) [[likely]] {
423 ReadBlockImpl<false, true>(gpu_src_addr, dest_buffer, size, VideoCommon::CacheType::None);
424 return;
425 }
426 ReadBlockImpl<false, false>(gpu_src_addr, dest_buffer, size, VideoCommon::CacheType::None);
433} 427}
434 428
435template <bool is_safe> 429template <bool is_safe>
436void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, 430void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size,
437 std::size_t size) { 431 [[maybe_unused]] VideoCommon::CacheType which) {
438 auto just_advance = [&]([[maybe_unused]] std::size_t page_index, 432 auto just_advance = [&]([[maybe_unused]] std::size_t page_index,
439 [[maybe_unused]] std::size_t offset, std::size_t copy_amount) { 433 [[maybe_unused]] std::size_t offset, std::size_t copy_amount) {
440 src_buffer = static_cast<const u8*>(src_buffer) + copy_amount; 434 src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
@@ -443,7 +437,7 @@ void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffe
443 const VAddr cpu_addr_base = 437 const VAddr cpu_addr_base =
444 (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset; 438 (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
445 if constexpr (is_safe) { 439 if constexpr (is_safe) {
446 rasterizer->InvalidateRegion(cpu_addr_base, copy_amount); 440 rasterizer->InvalidateRegion(cpu_addr_base, copy_amount, which);
447 } 441 }
448 u8* physical = memory.GetPointer(cpu_addr_base); 442 u8* physical = memory.GetPointer(cpu_addr_base);
449 std::memcpy(physical, src_buffer, copy_amount); 443 std::memcpy(physical, src_buffer, copy_amount);
@@ -453,7 +447,7 @@ void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffe
453 const VAddr cpu_addr_base = 447 const VAddr cpu_addr_base =
454 (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; 448 (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
455 if constexpr (is_safe) { 449 if constexpr (is_safe) {
456 rasterizer->InvalidateRegion(cpu_addr_base, copy_amount); 450 rasterizer->InvalidateRegion(cpu_addr_base, copy_amount, which);
457 } 451 }
458 if (!IsBigPageContinous(page_index)) [[unlikely]] { 452 if (!IsBigPageContinous(page_index)) [[unlikely]] {
459 memory.WriteBlockUnsafe(cpu_addr_base, src_buffer, copy_amount); 453 memory.WriteBlockUnsafe(cpu_addr_base, src_buffer, copy_amount);
@@ -471,16 +465,24 @@ void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffe
471 MemoryOperation<true>(gpu_dest_addr, size, mapped_big, just_advance, write_short_pages); 465 MemoryOperation<true>(gpu_dest_addr, size, mapped_big, just_advance, write_short_pages);
472} 466}
473 467
474void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size) { 468void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size,
475 WriteBlockImpl<true>(gpu_dest_addr, src_buffer, size); 469 VideoCommon::CacheType which) {
470 WriteBlockImpl<true>(gpu_dest_addr, src_buffer, size, which);
476} 471}
477 472
478void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer, 473void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer,
479 std::size_t size) { 474 std::size_t size) {
480 WriteBlockImpl<false>(gpu_dest_addr, src_buffer, size); 475 WriteBlockImpl<false>(gpu_dest_addr, src_buffer, size, VideoCommon::CacheType::None);
476}
477
478void MemoryManager::WriteBlockCached(GPUVAddr gpu_dest_addr, const void* src_buffer,
479 std::size_t size) {
480 WriteBlockImpl<false>(gpu_dest_addr, src_buffer, size, VideoCommon::CacheType::None);
481 accumulator->Add(gpu_dest_addr, size);
481} 482}
482 483
483void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size) const { 484void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size,
485 VideoCommon::CacheType which) const {
484 auto do_nothing = [&]([[maybe_unused]] std::size_t page_index, 486 auto do_nothing = [&]([[maybe_unused]] std::size_t page_index,
485 [[maybe_unused]] std::size_t offset, 487 [[maybe_unused]] std::size_t offset,
486 [[maybe_unused]] std::size_t copy_amount) {}; 488 [[maybe_unused]] std::size_t copy_amount) {};
@@ -488,12 +490,12 @@ void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size) const {
488 auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { 490 auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
489 const VAddr cpu_addr_base = 491 const VAddr cpu_addr_base =
490 (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset; 492 (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
491 rasterizer->FlushRegion(cpu_addr_base, copy_amount); 493 rasterizer->FlushRegion(cpu_addr_base, copy_amount, which);
492 }; 494 };
493 auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { 495 auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
494 const VAddr cpu_addr_base = 496 const VAddr cpu_addr_base =
495 (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; 497 (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
496 rasterizer->FlushRegion(cpu_addr_base, copy_amount); 498 rasterizer->FlushRegion(cpu_addr_base, copy_amount, which);
497 }; 499 };
498 auto flush_short_pages = [&](std::size_t page_index, std::size_t offset, 500 auto flush_short_pages = [&](std::size_t page_index, std::size_t offset,
499 std::size_t copy_amount) { 501 std::size_t copy_amount) {
@@ -503,7 +505,8 @@ void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size) const {
503 MemoryOperation<true>(gpu_addr, size, mapped_big, do_nothing, flush_short_pages); 505 MemoryOperation<true>(gpu_addr, size, mapped_big, do_nothing, flush_short_pages);
504} 506}
505 507
506bool MemoryManager::IsMemoryDirty(GPUVAddr gpu_addr, size_t size) const { 508bool MemoryManager::IsMemoryDirty(GPUVAddr gpu_addr, size_t size,
509 VideoCommon::CacheType which) const {
507 bool result = false; 510 bool result = false;
508 auto do_nothing = [&]([[maybe_unused]] std::size_t page_index, 511 auto do_nothing = [&]([[maybe_unused]] std::size_t page_index,
509 [[maybe_unused]] std::size_t offset, 512 [[maybe_unused]] std::size_t offset,
@@ -512,13 +515,13 @@ bool MemoryManager::IsMemoryDirty(GPUVAddr gpu_addr, size_t size) const {
512 auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { 515 auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
513 const VAddr cpu_addr_base = 516 const VAddr cpu_addr_base =
514 (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset; 517 (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
515 result |= rasterizer->MustFlushRegion(cpu_addr_base, copy_amount); 518 result |= rasterizer->MustFlushRegion(cpu_addr_base, copy_amount, which);
516 return result; 519 return result;
517 }; 520 };
518 auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { 521 auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
519 const VAddr cpu_addr_base = 522 const VAddr cpu_addr_base =
520 (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; 523 (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
521 result |= rasterizer->MustFlushRegion(cpu_addr_base, copy_amount); 524 result |= rasterizer->MustFlushRegion(cpu_addr_base, copy_amount, which);
522 return result; 525 return result;
523 }; 526 };
524 auto check_short_pages = [&](std::size_t page_index, std::size_t offset, 527 auto check_short_pages = [&](std::size_t page_index, std::size_t offset,
@@ -571,7 +574,12 @@ size_t MemoryManager::MaxContinousRange(GPUVAddr gpu_addr, size_t size) const {
571 return range_so_far; 574 return range_so_far;
572} 575}
573 576
574void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size) const { 577size_t MemoryManager::GetMemoryLayoutSize(GPUVAddr gpu_addr, size_t max_size) const {
578 return kind_map.GetContinousSizeFrom(gpu_addr);
579}
580
581void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size,
582 VideoCommon::CacheType which) const {
575 auto do_nothing = [&]([[maybe_unused]] std::size_t page_index, 583 auto do_nothing = [&]([[maybe_unused]] std::size_t page_index,
576 [[maybe_unused]] std::size_t offset, 584 [[maybe_unused]] std::size_t offset,
577 [[maybe_unused]] std::size_t copy_amount) {}; 585 [[maybe_unused]] std::size_t copy_amount) {};
@@ -579,12 +587,12 @@ void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size) const {
579 auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { 587 auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
580 const VAddr cpu_addr_base = 588 const VAddr cpu_addr_base =
581 (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset; 589 (static_cast<VAddr>(page_table[page_index]) << cpu_page_bits) + offset;
582 rasterizer->InvalidateRegion(cpu_addr_base, copy_amount); 590 rasterizer->InvalidateRegion(cpu_addr_base, copy_amount, which);
583 }; 591 };
584 auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { 592 auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
585 const VAddr cpu_addr_base = 593 const VAddr cpu_addr_base =
586 (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; 594 (static_cast<VAddr>(big_page_table_cpu[page_index]) << cpu_page_bits) + offset;
587 rasterizer->InvalidateRegion(cpu_addr_base, copy_amount); 595 rasterizer->InvalidateRegion(cpu_addr_base, copy_amount, which);
588 }; 596 };
589 auto invalidate_short_pages = [&](std::size_t page_index, std::size_t offset, 597 auto invalidate_short_pages = [&](std::size_t page_index, std::size_t offset,
590 std::size_t copy_amount) { 598 std::size_t copy_amount) {
@@ -594,14 +602,15 @@ void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size) const {
594 MemoryOperation<true>(gpu_addr, size, mapped_big, do_nothing, invalidate_short_pages); 602 MemoryOperation<true>(gpu_addr, size, mapped_big, do_nothing, invalidate_short_pages);
595} 603}
596 604
597void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size) { 605void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size,
606 VideoCommon::CacheType which) {
598 std::vector<u8> tmp_buffer(size); 607 std::vector<u8> tmp_buffer(size);
599 ReadBlock(gpu_src_addr, tmp_buffer.data(), size); 608 ReadBlock(gpu_src_addr, tmp_buffer.data(), size, which);
600 609
601 // The output block must be flushed in case it has data modified from the GPU. 610 // The output block must be flushed in case it has data modified from the GPU.
602 // Fixes NPC geometry in Zombie Panic in Wonderland DX 611 // Fixes NPC geometry in Zombie Panic in Wonderland DX
603 FlushRegion(gpu_dest_addr, size); 612 FlushRegion(gpu_dest_addr, size, which);
604 WriteBlock(gpu_dest_addr, tmp_buffer.data(), size); 613 WriteBlock(gpu_dest_addr, tmp_buffer.data(), size, which);
605} 614}
606 615
607bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const { 616bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const {
@@ -681,7 +690,17 @@ bool MemoryManager::IsFullyMappedRange(GPUVAddr gpu_addr, std::size_t size) cons
681std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange( 690std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange(
682 GPUVAddr gpu_addr, std::size_t size) const { 691 GPUVAddr gpu_addr, std::size_t size) const {
683 std::vector<std::pair<GPUVAddr, std::size_t>> result{}; 692 std::vector<std::pair<GPUVAddr, std::size_t>> result{};
684 std::optional<std::pair<GPUVAddr, std::size_t>> last_segment{}; 693 GetSubmappedRangeImpl<true>(gpu_addr, size, result);
694 return result;
695}
696
697template <bool is_gpu_address>
698void MemoryManager::GetSubmappedRangeImpl(
699 GPUVAddr gpu_addr, std::size_t size,
700 std::vector<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>>&
701 result) const {
702 std::optional<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>>
703 last_segment{};
685 std::optional<VAddr> old_page_addr{}; 704 std::optional<VAddr> old_page_addr{};
686 const auto split = [&last_segment, &result]([[maybe_unused]] std::size_t page_index, 705 const auto split = [&last_segment, &result]([[maybe_unused]] std::size_t page_index,
687 [[maybe_unused]] std::size_t offset, 706 [[maybe_unused]] std::size_t offset,
@@ -703,8 +722,12 @@ std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange(
703 } 722 }
704 old_page_addr = {cpu_addr_base + copy_amount}; 723 old_page_addr = {cpu_addr_base + copy_amount};
705 if (!last_segment) { 724 if (!last_segment) {
706 const GPUVAddr new_base_addr = (page_index << big_page_bits) + offset; 725 if constexpr (is_gpu_address) {
707 last_segment = {new_base_addr, copy_amount}; 726 const GPUVAddr new_base_addr = (page_index << big_page_bits) + offset;
727 last_segment = {new_base_addr, copy_amount};
728 } else {
729 last_segment = {cpu_addr_base, copy_amount};
730 }
708 } else { 731 } else {
709 last_segment->second += copy_amount; 732 last_segment->second += copy_amount;
710 } 733 }
@@ -721,8 +744,12 @@ std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange(
721 } 744 }
722 old_page_addr = {cpu_addr_base + copy_amount}; 745 old_page_addr = {cpu_addr_base + copy_amount};
723 if (!last_segment) { 746 if (!last_segment) {
724 const GPUVAddr new_base_addr = (page_index << page_bits) + offset; 747 if constexpr (is_gpu_address) {
725 last_segment = {new_base_addr, copy_amount}; 748 const GPUVAddr new_base_addr = (page_index << page_bits) + offset;
749 last_segment = {new_base_addr, copy_amount};
750 } else {
751 last_segment = {cpu_addr_base, copy_amount};
752 }
726 } else { 753 } else {
727 last_segment->second += copy_amount; 754 last_segment->second += copy_amount;
728 } 755 }
@@ -733,7 +760,18 @@ std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange(
733 }; 760 };
734 MemoryOperation<true>(gpu_addr, size, extend_size_big, split, do_short_pages); 761 MemoryOperation<true>(gpu_addr, size, extend_size_big, split, do_short_pages);
735 split(0, 0, 0); 762 split(0, 0, 0);
736 return result; 763}
764
765void MemoryManager::FlushCaching() {
766 if (!accumulator->AnyAccumulated()) {
767 return;
768 }
769 accumulator->Callback([this](GPUVAddr addr, size_t size) {
770 GetSubmappedRangeImpl<false>(addr, size, page_stash);
771 });
772 rasterizer->InnerInvalidation(page_stash);
773 page_stash.clear();
774 accumulator->Clear();
737} 775}
738 776
739} // namespace Tegra 777} // namespace Tegra
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index ab4bc9ec6..2936364f0 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -10,13 +10,19 @@
10 10
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/multi_level_page_table.h" 12#include "common/multi_level_page_table.h"
13#include "common/range_map.h"
13#include "common/virtual_buffer.h" 14#include "common/virtual_buffer.h"
15#include "video_core/cache_types.h"
14#include "video_core/pte_kind.h" 16#include "video_core/pte_kind.h"
15 17
16namespace VideoCore { 18namespace VideoCore {
17class RasterizerInterface; 19class RasterizerInterface;
18} 20}
19 21
22namespace VideoCommon {
23class InvalidationAccumulator;
24}
25
20namespace Core { 26namespace Core {
21class DeviceMemory; 27class DeviceMemory;
22namespace Memory { 28namespace Memory {
@@ -59,9 +65,12 @@ public:
59 * in the Host Memory counterpart. Note: This functions cause Host GPU Memory 65 * in the Host Memory counterpart. Note: This functions cause Host GPU Memory
60 * Flushes and Invalidations, respectively to each operation. 66 * Flushes and Invalidations, respectively to each operation.
61 */ 67 */
62 void ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const; 68 void ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
63 void WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size); 69 VideoCommon::CacheType which = VideoCommon::CacheType::All) const;
64 void CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size); 70 void WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size,
71 VideoCommon::CacheType which = VideoCommon::CacheType::All);
72 void CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size,
73 VideoCommon::CacheType which = VideoCommon::CacheType::All);
65 74
66 /** 75 /**
67 * ReadBlockUnsafe and WriteBlockUnsafe are special versions of ReadBlock and 76 * ReadBlockUnsafe and WriteBlockUnsafe are special versions of ReadBlock and
@@ -75,6 +84,7 @@ public:
75 */ 84 */
76 void ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const; 85 void ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const;
77 void WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size); 86 void WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size);
87 void WriteBlockCached(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size);
78 88
79 /** 89 /**
80 * Checks if a gpu region can be simply read with a pointer. 90 * Checks if a gpu region can be simply read with a pointer.
@@ -104,11 +114,14 @@ public:
104 GPUVAddr MapSparse(GPUVAddr gpu_addr, std::size_t size, bool is_big_pages = true); 114 GPUVAddr MapSparse(GPUVAddr gpu_addr, std::size_t size, bool is_big_pages = true);
105 void Unmap(GPUVAddr gpu_addr, std::size_t size); 115 void Unmap(GPUVAddr gpu_addr, std::size_t size);
106 116
107 void FlushRegion(GPUVAddr gpu_addr, size_t size) const; 117 void FlushRegion(GPUVAddr gpu_addr, size_t size,
118 VideoCommon::CacheType which = VideoCommon::CacheType::All) const;
108 119
109 void InvalidateRegion(GPUVAddr gpu_addr, size_t size) const; 120 void InvalidateRegion(GPUVAddr gpu_addr, size_t size,
121 VideoCommon::CacheType which = VideoCommon::CacheType::All) const;
110 122
111 bool IsMemoryDirty(GPUVAddr gpu_addr, size_t size) const; 123 bool IsMemoryDirty(GPUVAddr gpu_addr, size_t size,
124 VideoCommon::CacheType which = VideoCommon::CacheType::All) const;
112 125
113 size_t MaxContinousRange(GPUVAddr gpu_addr, size_t size) const; 126 size_t MaxContinousRange(GPUVAddr gpu_addr, size_t size) const;
114 127
@@ -118,16 +131,23 @@ public:
118 131
119 PTEKind GetPageKind(GPUVAddr gpu_addr) const; 132 PTEKind GetPageKind(GPUVAddr gpu_addr) const;
120 133
134 size_t GetMemoryLayoutSize(GPUVAddr gpu_addr,
135 size_t max_size = std::numeric_limits<size_t>::max()) const;
136
137 void FlushCaching();
138
121private: 139private:
122 template <bool is_big_pages, typename FuncMapped, typename FuncReserved, typename FuncUnmapped> 140 template <bool is_big_pages, typename FuncMapped, typename FuncReserved, typename FuncUnmapped>
123 inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped, 141 inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped,
124 FuncReserved&& func_reserved, FuncUnmapped&& func_unmapped) const; 142 FuncReserved&& func_reserved, FuncUnmapped&& func_unmapped) const;
125 143
126 template <bool is_safe> 144 template <bool is_safe, bool use_fastmem>
127 void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const; 145 void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
146 VideoCommon::CacheType which) const;
128 147
129 template <bool is_safe> 148 template <bool is_safe>
130 void WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size); 149 void WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size,
150 VideoCommon::CacheType which);
131 151
132 template <bool is_big_page> 152 template <bool is_big_page>
133 [[nodiscard]] std::size_t PageEntryIndex(GPUVAddr gpu_addr) const { 153 [[nodiscard]] std::size_t PageEntryIndex(GPUVAddr gpu_addr) const {
@@ -141,6 +161,12 @@ private:
141 inline bool IsBigPageContinous(size_t big_page_index) const; 161 inline bool IsBigPageContinous(size_t big_page_index) const;
142 inline void SetBigPageContinous(size_t big_page_index, bool value); 162 inline void SetBigPageContinous(size_t big_page_index, bool value);
143 163
164 template <bool is_gpu_address>
165 void GetSubmappedRangeImpl(
166 GPUVAddr gpu_addr, std::size_t size,
167 std::vector<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>>&
168 result) const;
169
144 Core::System& system; 170 Core::System& system;
145 Core::Memory::Memory& memory; 171 Core::Memory::Memory& memory;
146 Core::DeviceMemory& device_memory; 172 Core::DeviceMemory& device_memory;
@@ -183,23 +209,18 @@ private:
183 template <bool is_big_page> 209 template <bool is_big_page>
184 inline void SetEntry(size_t position, EntryType entry); 210 inline void SetEntry(size_t position, EntryType entry);
185 211
186 std::vector<std::array<PTEKind, 32>> kinds;
187 std::vector<std::array<PTEKind, 32>> big_kinds;
188
189 template <bool is_big_page>
190 inline PTEKind GetKind(size_t position) const;
191
192 template <bool is_big_page>
193 inline void SetKind(size_t position, PTEKind kind);
194
195 Common::MultiLevelPageTable<u32> page_table; 212 Common::MultiLevelPageTable<u32> page_table;
213 Common::RangeMap<GPUVAddr, PTEKind> kind_map;
196 Common::VirtualBuffer<u32> big_page_table_cpu; 214 Common::VirtualBuffer<u32> big_page_table_cpu;
197 215
198 std::vector<u64> big_page_continous; 216 std::vector<u64> big_page_continous;
217 std::vector<std::pair<VAddr, std::size_t>> page_stash{};
218 u8* fastmem_arena{};
199 219
200 constexpr static size_t continous_bits = 64; 220 constexpr static size_t continous_bits = 64;
201 221
202 const size_t unique_identifier; 222 const size_t unique_identifier;
223 std::unique_ptr<VideoCommon::InvalidationAccumulator> accumulator;
203 224
204 static std::atomic<size_t> unique_identifier_generator; 225 static std::atomic<size_t> unique_identifier_generator;
205}; 226};
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index b6907463c..1735b6164 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -6,8 +6,10 @@
6#include <functional> 6#include <functional>
7#include <optional> 7#include <optional>
8#include <span> 8#include <span>
9#include <utility>
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "common/polyfill_thread.h" 11#include "common/polyfill_thread.h"
12#include "video_core/cache_types.h"
11#include "video_core/engines/fermi_2d.h" 13#include "video_core/engines/fermi_2d.h"
12#include "video_core/gpu.h" 14#include "video_core/gpu.h"
13 15
@@ -42,6 +44,9 @@ public:
42 /// Dispatches a draw invocation 44 /// Dispatches a draw invocation
43 virtual void Draw(bool is_indexed, u32 instance_count) = 0; 45 virtual void Draw(bool is_indexed, u32 instance_count) = 0;
44 46
47 /// Dispatches an indirect draw invocation
48 virtual void DrawIndirect() {}
49
45 /// Clear the current framebuffer 50 /// Clear the current framebuffer
46 virtual void Clear(u32 layer_count) = 0; 51 virtual void Clear(u32 layer_count) = 0;
47 52
@@ -80,13 +85,22 @@ public:
80 virtual void FlushAll() = 0; 85 virtual void FlushAll() = 0;
81 86
82 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory 87 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
83 virtual void FlushRegion(VAddr addr, u64 size) = 0; 88 virtual void FlushRegion(VAddr addr, u64 size,
89 VideoCommon::CacheType which = VideoCommon::CacheType::All) = 0;
84 90
85 /// Check if the the specified memory area requires flushing to CPU Memory. 91 /// Check if the the specified memory area requires flushing to CPU Memory.
86 virtual bool MustFlushRegion(VAddr addr, u64 size) = 0; 92 virtual bool MustFlushRegion(VAddr addr, u64 size,
93 VideoCommon::CacheType which = VideoCommon::CacheType::All) = 0;
87 94
88 /// Notify rasterizer that any caches of the specified region should be invalidated 95 /// Notify rasterizer that any caches of the specified region should be invalidated
89 virtual void InvalidateRegion(VAddr addr, u64 size) = 0; 96 virtual void InvalidateRegion(VAddr addr, u64 size,
97 VideoCommon::CacheType which = VideoCommon::CacheType::All) = 0;
98
99 virtual void InnerInvalidation(std::span<const std::pair<VAddr, std::size_t>> sequences) {
100 for (const auto& [cpu_addr, size] : sequences) {
101 InvalidateRegion(cpu_addr, size);
102 }
103 }
90 104
91 /// Notify rasterizer that any caches of the specified region are desync with guest 105 /// Notify rasterizer that any caches of the specified region are desync with guest
92 virtual void OnCPUWrite(VAddr addr, u64 size) = 0; 106 virtual void OnCPUWrite(VAddr addr, u64 size) = 0;
@@ -102,7 +116,8 @@ public:
102 116
103 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory 117 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
104 /// and invalidated 118 /// and invalidated
105 virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0; 119 virtual void FlushAndInvalidateRegion(
120 VAddr addr, u64 size, VideoCommon::CacheType which = VideoCommon::CacheType::All) = 0;
106 121
107 /// Notify the host renderer to wait for previous primitive and compute operations. 122 /// Notify the host renderer to wait for previous primitive and compute operations.
108 virtual void WaitForIdle() = 0; 123 virtual void WaitForIdle() = 0;
@@ -119,6 +134,10 @@ public:
119 /// Notify rasterizer that a frame is about to finish 134 /// Notify rasterizer that a frame is about to finish
120 virtual void TickFrame() = 0; 135 virtual void TickFrame() = 0;
121 136
137 virtual bool AccelerateConditionalRendering() {
138 return false;
139 }
140
122 /// Attempt to use a faster method to perform a surface copy 141 /// Attempt to use a faster method to perform a surface copy
123 [[nodiscard]] virtual bool AccelerateSurfaceCopy( 142 [[nodiscard]] virtual bool AccelerateSurfaceCopy(
124 const Tegra::Engines::Fermi2D::Surface& src, const Tegra::Engines::Fermi2D::Surface& dst, 143 const Tegra::Engines::Fermi2D::Surface& src, const Tegra::Engines::Fermi2D::Surface& dst,
diff --git a/src/video_core/renderer_null/null_rasterizer.cpp b/src/video_core/renderer_null/null_rasterizer.cpp
index 9734d84bc..2c11345d7 100644
--- a/src/video_core/renderer_null/null_rasterizer.cpp
+++ b/src/video_core/renderer_null/null_rasterizer.cpp
@@ -39,11 +39,11 @@ void RasterizerNull::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr
39 u32 size) {} 39 u32 size) {}
40void RasterizerNull::DisableGraphicsUniformBuffer(size_t stage, u32 index) {} 40void RasterizerNull::DisableGraphicsUniformBuffer(size_t stage, u32 index) {}
41void RasterizerNull::FlushAll() {} 41void RasterizerNull::FlushAll() {}
42void RasterizerNull::FlushRegion(VAddr addr, u64 size) {} 42void RasterizerNull::FlushRegion(VAddr addr, u64 size, VideoCommon::CacheType) {}
43bool RasterizerNull::MustFlushRegion(VAddr addr, u64 size) { 43bool RasterizerNull::MustFlushRegion(VAddr addr, u64 size, VideoCommon::CacheType) {
44 return false; 44 return false;
45} 45}
46void RasterizerNull::InvalidateRegion(VAddr addr, u64 size) {} 46void RasterizerNull::InvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType) {}
47void RasterizerNull::OnCPUWrite(VAddr addr, u64 size) {} 47void RasterizerNull::OnCPUWrite(VAddr addr, u64 size) {}
48void RasterizerNull::InvalidateGPUCache() {} 48void RasterizerNull::InvalidateGPUCache() {}
49void RasterizerNull::UnmapMemory(VAddr addr, u64 size) {} 49void RasterizerNull::UnmapMemory(VAddr addr, u64 size) {}
@@ -61,7 +61,7 @@ void RasterizerNull::SignalSyncPoint(u32 value) {
61} 61}
62void RasterizerNull::SignalReference() {} 62void RasterizerNull::SignalReference() {}
63void RasterizerNull::ReleaseFences() {} 63void RasterizerNull::ReleaseFences() {}
64void RasterizerNull::FlushAndInvalidateRegion(VAddr addr, u64 size) {} 64void RasterizerNull::FlushAndInvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType) {}
65void RasterizerNull::WaitForIdle() {} 65void RasterizerNull::WaitForIdle() {}
66void RasterizerNull::FragmentBarrier() {} 66void RasterizerNull::FragmentBarrier() {}
67void RasterizerNull::TiledCacheBarrier() {} 67void RasterizerNull::TiledCacheBarrier() {}
diff --git a/src/video_core/renderer_null/null_rasterizer.h b/src/video_core/renderer_null/null_rasterizer.h
index ecf77ba42..2112aa70e 100644
--- a/src/video_core/renderer_null/null_rasterizer.h
+++ b/src/video_core/renderer_null/null_rasterizer.h
@@ -38,9 +38,12 @@ public:
38 void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override; 38 void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override;
39 void DisableGraphicsUniformBuffer(size_t stage, u32 index) override; 39 void DisableGraphicsUniformBuffer(size_t stage, u32 index) override;
40 void FlushAll() override; 40 void FlushAll() override;
41 void FlushRegion(VAddr addr, u64 size) override; 41 void FlushRegion(VAddr addr, u64 size,
42 bool MustFlushRegion(VAddr addr, u64 size) override; 42 VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
43 void InvalidateRegion(VAddr addr, u64 size) override; 43 bool MustFlushRegion(VAddr addr, u64 size,
44 VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
45 void InvalidateRegion(VAddr addr, u64 size,
46 VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
44 void OnCPUWrite(VAddr addr, u64 size) override; 47 void OnCPUWrite(VAddr addr, u64 size) override;
45 void InvalidateGPUCache() override; 48 void InvalidateGPUCache() override;
46 void UnmapMemory(VAddr addr, u64 size) override; 49 void UnmapMemory(VAddr addr, u64 size) override;
@@ -50,7 +53,8 @@ public:
50 void SignalSyncPoint(u32 value) override; 53 void SignalSyncPoint(u32 value) override;
51 void SignalReference() override; 54 void SignalReference() override;
52 void ReleaseFences() override; 55 void ReleaseFences() override;
53 void FlushAndInvalidateRegion(VAddr addr, u64 size) override; 56 void FlushAndInvalidateRegion(
57 VAddr addr, u64 size, VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
54 void WaitForIdle() override; 58 void WaitForIdle() override;
55 void FragmentBarrier() override; 59 void FragmentBarrier() override;
56 void TiledCacheBarrier() override; 60 void TiledCacheBarrier() override;
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index a8c3f8b67..bb1962073 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -160,6 +160,10 @@ public:
160 return device.CanReportMemoryUsage(); 160 return device.CanReportMemoryUsage();
161 } 161 }
162 162
163 u32 GetStorageBufferAlignment() const {
164 return static_cast<u32>(device.GetShaderStorageBufferAlignment());
165 }
166
163private: 167private:
164 static constexpr std::array PABO_LUT{ 168 static constexpr std::array PABO_LUT{
165 GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV, 169 GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV,
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.h b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
index ea53ddb46..1c06b3655 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.h
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
@@ -40,6 +40,7 @@ struct GraphicsPipelineKey {
40 BitField<6, 2, Maxwell::Tessellation::DomainType> tessellation_primitive; 40 BitField<6, 2, Maxwell::Tessellation::DomainType> tessellation_primitive;
41 BitField<8, 2, Maxwell::Tessellation::Spacing> tessellation_spacing; 41 BitField<8, 2, Maxwell::Tessellation::Spacing> tessellation_spacing;
42 BitField<10, 1, u32> tessellation_clockwise; 42 BitField<10, 1, u32> tessellation_clockwise;
43 BitField<11, 3, Tegra::Engines::Maxwell3D::EngineHint> app_stage;
43 }; 44 };
44 std::array<u32, 3> padding; 45 std::array<u32, 3> padding;
45 VideoCommon::TransformFeedbackState xfb_state; 46 VideoCommon::TransformFeedbackState xfb_state;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index a44b8c454..7d48af8e1 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -202,7 +202,8 @@ void RasterizerOpenGL::Clear(u32 layer_count) {
202 ++num_queued_commands; 202 ++num_queued_commands;
203} 203}
204 204
205void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) { 205template <typename Func>
206void RasterizerOpenGL::PrepareDraw(bool is_indexed, Func&& draw_func) {
206 MICROPROFILE_SCOPE(OpenGL_Drawing); 207 MICROPROFILE_SCOPE(OpenGL_Drawing);
207 208
208 SCOPE_EXIT({ gpu.TickWork(); }); 209 SCOPE_EXIT({ gpu.TickWork(); });
@@ -226,48 +227,97 @@ void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) {
226 const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(draw_state.topology); 227 const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(draw_state.topology);
227 BeginTransformFeedback(pipeline, primitive_mode); 228 BeginTransformFeedback(pipeline, primitive_mode);
228 229
229 const GLuint base_instance = static_cast<GLuint>(draw_state.base_instance); 230 draw_func(primitive_mode);
230 const GLsizei num_instances = static_cast<GLsizei>(instance_count); 231
231 if (is_indexed) {
232 const GLint base_vertex = static_cast<GLint>(draw_state.base_index);
233 const GLsizei num_vertices = static_cast<GLsizei>(draw_state.index_buffer.count);
234 const GLvoid* const offset = buffer_cache_runtime.IndexOffset();
235 const GLenum format = MaxwellToGL::IndexFormat(draw_state.index_buffer.format);
236 if (num_instances == 1 && base_instance == 0 && base_vertex == 0) {
237 glDrawElements(primitive_mode, num_vertices, format, offset);
238 } else if (num_instances == 1 && base_instance == 0) {
239 glDrawElementsBaseVertex(primitive_mode, num_vertices, format, offset, base_vertex);
240 } else if (base_vertex == 0 && base_instance == 0) {
241 glDrawElementsInstanced(primitive_mode, num_vertices, format, offset, num_instances);
242 } else if (base_vertex == 0) {
243 glDrawElementsInstancedBaseInstance(primitive_mode, num_vertices, format, offset,
244 num_instances, base_instance);
245 } else if (base_instance == 0) {
246 glDrawElementsInstancedBaseVertex(primitive_mode, num_vertices, format, offset,
247 num_instances, base_vertex);
248 } else {
249 glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, num_vertices, format,
250 offset, num_instances, base_vertex,
251 base_instance);
252 }
253 } else {
254 const GLint base_vertex = static_cast<GLint>(draw_state.vertex_buffer.first);
255 const GLsizei num_vertices = static_cast<GLsizei>(draw_state.vertex_buffer.count);
256 if (num_instances == 1 && base_instance == 0) {
257 glDrawArrays(primitive_mode, base_vertex, num_vertices);
258 } else if (base_instance == 0) {
259 glDrawArraysInstanced(primitive_mode, base_vertex, num_vertices, num_instances);
260 } else {
261 glDrawArraysInstancedBaseInstance(primitive_mode, base_vertex, num_vertices,
262 num_instances, base_instance);
263 }
264 }
265 EndTransformFeedback(); 232 EndTransformFeedback();
266 233
267 ++num_queued_commands; 234 ++num_queued_commands;
268 has_written_global_memory |= pipeline->WritesGlobalMemory(); 235 has_written_global_memory |= pipeline->WritesGlobalMemory();
269} 236}
270 237
238void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) {
239 PrepareDraw(is_indexed, [this, is_indexed, instance_count](GLenum primitive_mode) {
240 const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
241 const GLuint base_instance = static_cast<GLuint>(draw_state.base_instance);
242 const GLsizei num_instances = static_cast<GLsizei>(instance_count);
243 if (is_indexed) {
244 const GLint base_vertex = static_cast<GLint>(draw_state.base_index);
245 const GLsizei num_vertices = static_cast<GLsizei>(draw_state.index_buffer.count);
246 const GLvoid* const offset = buffer_cache_runtime.IndexOffset();
247 const GLenum format = MaxwellToGL::IndexFormat(draw_state.index_buffer.format);
248 if (num_instances == 1 && base_instance == 0 && base_vertex == 0) {
249 glDrawElements(primitive_mode, num_vertices, format, offset);
250 } else if (num_instances == 1 && base_instance == 0) {
251 glDrawElementsBaseVertex(primitive_mode, num_vertices, format, offset, base_vertex);
252 } else if (base_vertex == 0 && base_instance == 0) {
253 glDrawElementsInstanced(primitive_mode, num_vertices, format, offset,
254 num_instances);
255 } else if (base_vertex == 0) {
256 glDrawElementsInstancedBaseInstance(primitive_mode, num_vertices, format, offset,
257 num_instances, base_instance);
258 } else if (base_instance == 0) {
259 glDrawElementsInstancedBaseVertex(primitive_mode, num_vertices, format, offset,
260 num_instances, base_vertex);
261 } else {
262 glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, num_vertices, format,
263 offset, num_instances, base_vertex,
264 base_instance);
265 }
266 } else {
267 const GLint base_vertex = static_cast<GLint>(draw_state.vertex_buffer.first);
268 const GLsizei num_vertices = static_cast<GLsizei>(draw_state.vertex_buffer.count);
269 if (num_instances == 1 && base_instance == 0) {
270 glDrawArrays(primitive_mode, base_vertex, num_vertices);
271 } else if (base_instance == 0) {
272 glDrawArraysInstanced(primitive_mode, base_vertex, num_vertices, num_instances);
273 } else {
274 glDrawArraysInstancedBaseInstance(primitive_mode, base_vertex, num_vertices,
275 num_instances, base_instance);
276 }
277 }
278 });
279}
280
281void RasterizerOpenGL::DrawIndirect() {
282 const auto& params = maxwell3d->draw_manager->GetIndirectParams();
283 buffer_cache.SetDrawIndirect(&params);
284 PrepareDraw(params.is_indexed, [this, &params](GLenum primitive_mode) {
285 const auto [buffer, offset] = buffer_cache.GetDrawIndirectBuffer();
286 const GLvoid* const gl_offset =
287 reinterpret_cast<const GLvoid*>(static_cast<uintptr_t>(offset));
288 glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buffer->Handle());
289 if (params.include_count) {
290 const auto [draw_buffer, offset_base] = buffer_cache.GetDrawIndirectCount();
291 glBindBuffer(GL_PARAMETER_BUFFER, draw_buffer->Handle());
292
293 if (params.is_indexed) {
294 const GLenum format = MaxwellToGL::IndexFormat(maxwell3d->regs.index_buffer.format);
295 glMultiDrawElementsIndirectCount(primitive_mode, format, gl_offset,
296 static_cast<GLintptr>(offset_base),
297 static_cast<GLsizei>(params.max_draw_counts),
298 static_cast<GLsizei>(params.stride));
299 } else {
300 glMultiDrawArraysIndirectCount(primitive_mode, gl_offset,
301 static_cast<GLintptr>(offset_base),
302 static_cast<GLsizei>(params.max_draw_counts),
303 static_cast<GLsizei>(params.stride));
304 }
305 return;
306 }
307 if (params.is_indexed) {
308 const GLenum format = MaxwellToGL::IndexFormat(maxwell3d->regs.index_buffer.format);
309 glMultiDrawElementsIndirect(primitive_mode, format, gl_offset,
310 static_cast<GLsizei>(params.max_draw_counts),
311 static_cast<GLsizei>(params.stride));
312 } else {
313 glMultiDrawArraysIndirect(primitive_mode, gl_offset,
314 static_cast<GLsizei>(params.max_draw_counts),
315 static_cast<GLsizei>(params.stride));
316 }
317 });
318 buffer_cache.SetDrawIndirect(nullptr);
319}
320
271void RasterizerOpenGL::DispatchCompute() { 321void RasterizerOpenGL::DispatchCompute() {
272 ComputePipeline* const pipeline{shader_cache.CurrentComputePipeline()}; 322 ComputePipeline* const pipeline{shader_cache.CurrentComputePipeline()};
273 if (!pipeline) { 323 if (!pipeline) {
@@ -302,46 +352,60 @@ void RasterizerOpenGL::DisableGraphicsUniformBuffer(size_t stage, u32 index) {
302 352
303void RasterizerOpenGL::FlushAll() {} 353void RasterizerOpenGL::FlushAll() {}
304 354
305void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) { 355void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size, VideoCommon::CacheType which) {
306 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 356 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
307 if (addr == 0 || size == 0) { 357 if (addr == 0 || size == 0) {
308 return; 358 return;
309 } 359 }
310 { 360 if (True(which & VideoCommon::CacheType::TextureCache)) {
311 std::scoped_lock lock{texture_cache.mutex}; 361 std::scoped_lock lock{texture_cache.mutex};
312 texture_cache.DownloadMemory(addr, size); 362 texture_cache.DownloadMemory(addr, size);
313 } 363 }
314 { 364 if ((True(which & VideoCommon::CacheType::BufferCache))) {
315 std::scoped_lock lock{buffer_cache.mutex}; 365 std::scoped_lock lock{buffer_cache.mutex};
316 buffer_cache.DownloadMemory(addr, size); 366 buffer_cache.DownloadMemory(addr, size);
317 } 367 }
318 query_cache.FlushRegion(addr, size); 368 if ((True(which & VideoCommon::CacheType::QueryCache))) {
369 query_cache.FlushRegion(addr, size);
370 }
319} 371}
320 372
321bool RasterizerOpenGL::MustFlushRegion(VAddr addr, u64 size) { 373bool RasterizerOpenGL::MustFlushRegion(VAddr addr, u64 size, VideoCommon::CacheType which) {
322 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; 374 if ((True(which & VideoCommon::CacheType::BufferCache))) {
375 std::scoped_lock lock{buffer_cache.mutex};
376 if (buffer_cache.IsRegionGpuModified(addr, size)) {
377 return true;
378 }
379 }
323 if (!Settings::IsGPULevelHigh()) { 380 if (!Settings::IsGPULevelHigh()) {
324 return buffer_cache.IsRegionGpuModified(addr, size); 381 return false;
325 } 382 }
326 return texture_cache.IsRegionGpuModified(addr, size) || 383 if (True(which & VideoCommon::CacheType::TextureCache)) {
327 buffer_cache.IsRegionGpuModified(addr, size); 384 std::scoped_lock lock{texture_cache.mutex};
385 return texture_cache.IsRegionGpuModified(addr, size);
386 }
387 return false;
328} 388}
329 389
330void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) { 390void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType which) {
331 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 391 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
332 if (addr == 0 || size == 0) { 392 if (addr == 0 || size == 0) {
333 return; 393 return;
334 } 394 }
335 { 395 if (True(which & VideoCommon::CacheType::TextureCache)) {
336 std::scoped_lock lock{texture_cache.mutex}; 396 std::scoped_lock lock{texture_cache.mutex};
337 texture_cache.WriteMemory(addr, size); 397 texture_cache.WriteMemory(addr, size);
338 } 398 }
339 { 399 if (True(which & VideoCommon::CacheType::BufferCache)) {
340 std::scoped_lock lock{buffer_cache.mutex}; 400 std::scoped_lock lock{buffer_cache.mutex};
341 buffer_cache.WriteMemory(addr, size); 401 buffer_cache.WriteMemory(addr, size);
342 } 402 }
343 shader_cache.InvalidateRegion(addr, size); 403 if (True(which & VideoCommon::CacheType::ShaderCache)) {
344 query_cache.InvalidateRegion(addr, size); 404 shader_cache.InvalidateRegion(addr, size);
405 }
406 if (True(which & VideoCommon::CacheType::QueryCache)) {
407 query_cache.InvalidateRegion(addr, size);
408 }
345} 409}
346 410
347void RasterizerOpenGL::OnCPUWrite(VAddr addr, u64 size) { 411void RasterizerOpenGL::OnCPUWrite(VAddr addr, u64 size) {
@@ -408,11 +472,12 @@ void RasterizerOpenGL::ReleaseFences() {
408 fence_manager.WaitPendingFences(); 472 fence_manager.WaitPendingFences();
409} 473}
410 474
411void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) { 475void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size,
476 VideoCommon::CacheType which) {
412 if (Settings::IsGPULevelExtreme()) { 477 if (Settings::IsGPULevelExtreme()) {
413 FlushRegion(addr, size); 478 FlushRegion(addr, size, which);
414 } 479 }
415 InvalidateRegion(addr, size); 480 InvalidateRegion(addr, size, which);
416} 481}
417 482
418void RasterizerOpenGL::WaitForIdle() { 483void RasterizerOpenGL::WaitForIdle() {
@@ -460,6 +525,21 @@ void RasterizerOpenGL::TickFrame() {
460 } 525 }
461} 526}
462 527
528bool RasterizerOpenGL::AccelerateConditionalRendering() {
529 if (Settings::IsGPULevelHigh()) {
530 // Reimplement Host conditional rendering.
531 return false;
532 }
533 // Medium / Low Hack: stub any checks on queries writen into the buffer cache.
534 const GPUVAddr condition_address{maxwell3d->regs.render_enable.Address()};
535 Maxwell::ReportSemaphore::Compare cmp;
536 if (gpu_memory->IsMemoryDirty(condition_address, sizeof(cmp),
537 VideoCommon::CacheType::BufferCache)) {
538 return true;
539 }
540 return false;
541}
542
463bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, 543bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src,
464 const Tegra::Engines::Fermi2D::Surface& dst, 544 const Tegra::Engines::Fermi2D::Surface& dst,
465 const Tegra::Engines::Fermi2D::Config& copy_config) { 545 const Tegra::Engines::Fermi2D::Config& copy_config) {
@@ -481,7 +561,7 @@ void RasterizerOpenGL::AccelerateInlineToMemory(GPUVAddr address, size_t copy_si
481 } 561 }
482 gpu_memory->WriteBlockUnsafe(address, memory.data(), copy_size); 562 gpu_memory->WriteBlockUnsafe(address, memory.data(), copy_size);
483 { 563 {
484 std::unique_lock<std::mutex> lock{buffer_cache.mutex}; 564 std::unique_lock<std::recursive_mutex> lock{buffer_cache.mutex};
485 if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) { 565 if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) {
486 buffer_cache.WriteMemory(*cpu_addr, copy_size); 566 buffer_cache.WriteMemory(*cpu_addr, copy_size);
487 } 567 }
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index fc183c3ca..be4f76c18 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -69,6 +69,7 @@ public:
69 ~RasterizerOpenGL() override; 69 ~RasterizerOpenGL() override;
70 70
71 void Draw(bool is_indexed, u32 instance_count) override; 71 void Draw(bool is_indexed, u32 instance_count) override;
72 void DrawIndirect() override;
72 void Clear(u32 layer_count) override; 73 void Clear(u32 layer_count) override;
73 void DispatchCompute() override; 74 void DispatchCompute() override;
74 void ResetCounter(VideoCore::QueryType type) override; 75 void ResetCounter(VideoCore::QueryType type) override;
@@ -76,9 +77,12 @@ public:
76 void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override; 77 void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override;
77 void DisableGraphicsUniformBuffer(size_t stage, u32 index) override; 78 void DisableGraphicsUniformBuffer(size_t stage, u32 index) override;
78 void FlushAll() override; 79 void FlushAll() override;
79 void FlushRegion(VAddr addr, u64 size) override; 80 void FlushRegion(VAddr addr, u64 size,
80 bool MustFlushRegion(VAddr addr, u64 size) override; 81 VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
81 void InvalidateRegion(VAddr addr, u64 size) override; 82 bool MustFlushRegion(VAddr addr, u64 size,
83 VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
84 void InvalidateRegion(VAddr addr, u64 size,
85 VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
82 void OnCPUWrite(VAddr addr, u64 size) override; 86 void OnCPUWrite(VAddr addr, u64 size) override;
83 void InvalidateGPUCache() override; 87 void InvalidateGPUCache() override;
84 void UnmapMemory(VAddr addr, u64 size) override; 88 void UnmapMemory(VAddr addr, u64 size) override;
@@ -88,12 +92,14 @@ public:
88 void SignalSyncPoint(u32 value) override; 92 void SignalSyncPoint(u32 value) override;
89 void SignalReference() override; 93 void SignalReference() override;
90 void ReleaseFences() override; 94 void ReleaseFences() override;
91 void FlushAndInvalidateRegion(VAddr addr, u64 size) override; 95 void FlushAndInvalidateRegion(
96 VAddr addr, u64 size, VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
92 void WaitForIdle() override; 97 void WaitForIdle() override;
93 void FragmentBarrier() override; 98 void FragmentBarrier() override;
94 void TiledCacheBarrier() override; 99 void TiledCacheBarrier() override;
95 void FlushCommands() override; 100 void FlushCommands() override;
96 void TickFrame() override; 101 void TickFrame() override;
102 bool AccelerateConditionalRendering() override;
97 bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, 103 bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src,
98 const Tegra::Engines::Fermi2D::Surface& dst, 104 const Tegra::Engines::Fermi2D::Surface& dst,
99 const Tegra::Engines::Fermi2D::Config& copy_config) override; 105 const Tegra::Engines::Fermi2D::Config& copy_config) override;
@@ -121,6 +127,9 @@ private:
121 static constexpr size_t MAX_IMAGES = 48; 127 static constexpr size_t MAX_IMAGES = 48;
122 static constexpr size_t MAX_IMAGE_VIEWS = MAX_TEXTURES + MAX_IMAGES; 128 static constexpr size_t MAX_IMAGE_VIEWS = MAX_TEXTURES + MAX_IMAGES;
123 129
130 template <typename Func>
131 void PrepareDraw(bool is_indexed, Func&&);
132
124 /// Syncs state to match guest's 133 /// Syncs state to match guest's
125 void SyncState(); 134 void SyncState();
126 135
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index f8868a012..7dd854e0f 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -51,7 +51,7 @@ using VideoCommon::LoadPipelines;
51using VideoCommon::SerializePipeline; 51using VideoCommon::SerializePipeline;
52using Context = ShaderContext::Context; 52using Context = ShaderContext::Context;
53 53
54constexpr u32 CACHE_VERSION = 7; 54constexpr u32 CACHE_VERSION = 9;
55 55
56template <typename Container> 56template <typename Container>
57auto MakeSpan(Container& container) { 57auto MakeSpan(Container& container) {
@@ -236,6 +236,8 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
236 .needs_demote_reorder = device.IsAmd(), 236 .needs_demote_reorder = device.IsAmd(),
237 .support_snorm_render_buffer = false, 237 .support_snorm_render_buffer = false,
238 .support_viewport_index_layer = device.HasVertexViewportLayer(), 238 .support_viewport_index_layer = device.HasVertexViewportLayer(),
239 .min_ssbo_alignment = static_cast<u32>(device.GetShaderStorageBufferAlignment()),
240 .support_geometry_shader_passthrough = device.HasGeometryShaderPassthrough(),
239 } { 241 } {
240 if (use_asynchronous_shaders) { 242 if (use_asynchronous_shaders) {
241 workers = CreateWorkers(); 243 workers = CreateWorkers();
@@ -350,6 +352,7 @@ GraphicsPipeline* ShaderCache::CurrentGraphicsPipeline() {
350 regs.tessellation.params.output_primitives.Value() == 352 regs.tessellation.params.output_primitives.Value() ==
351 Maxwell::Tessellation::OutputPrimitives::Triangles_CW); 353 Maxwell::Tessellation::OutputPrimitives::Triangles_CW);
352 graphics_key.xfb_enabled.Assign(regs.transform_feedback_enabled != 0 ? 1 : 0); 354 graphics_key.xfb_enabled.Assign(regs.transform_feedback_enabled != 0 ? 1 : 0);
355 graphics_key.app_stage.Assign(maxwell3d->engine_state);
353 if (graphics_key.xfb_enabled) { 356 if (graphics_key.xfb_enabled) {
354 SetXfbState(graphics_key.xfb_state, regs); 357 SetXfbState(graphics_key.xfb_state, regs);
355 } 358 }
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index 113528e9b..5d9d370f2 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -354,6 +354,7 @@ struct TextureCacheParams {
354 static constexpr bool FRAMEBUFFER_BLITS = true; 354 static constexpr bool FRAMEBUFFER_BLITS = true;
355 static constexpr bool HAS_EMULATED_COPIES = true; 355 static constexpr bool HAS_EMULATED_COPIES = true;
356 static constexpr bool HAS_DEVICE_MEMORY_INFO = true; 356 static constexpr bool HAS_DEVICE_MEMORY_INFO = true;
357 static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = false;
357 358
358 using Runtime = OpenGL::TextureCacheRuntime; 359 using Runtime = OpenGL::TextureCacheRuntime;
359 using Image = OpenGL::Image; 360 using Image = OpenGL::Image;
@@ -361,6 +362,7 @@ struct TextureCacheParams {
361 using ImageView = OpenGL::ImageView; 362 using ImageView = OpenGL::ImageView;
362 using Sampler = OpenGL::Sampler; 363 using Sampler = OpenGL::Sampler;
363 using Framebuffer = OpenGL::Framebuffer; 364 using Framebuffer = OpenGL::Framebuffer;
365 using AsyncBuffer = u32;
364}; 366};
365 367
366using TextureCache = VideoCommon::TextureCache<TextureCacheParams>; 368using TextureCache = VideoCommon::TextureCache<TextureCacheParams>;
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index bc75680f0..de95f2634 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -442,7 +442,13 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
442 442
443 glBindTextureUnit(0, screen_info.display_texture); 443 glBindTextureUnit(0, screen_info.display_texture);
444 444
445 const auto anti_aliasing = Settings::values.anti_aliasing.GetValue(); 445 auto anti_aliasing = Settings::values.anti_aliasing.GetValue();
446 if (anti_aliasing > Settings::AntiAliasing::LastAA) {
447 LOG_ERROR(Render_OpenGL, "Invalid antialiasing option selected {}", anti_aliasing);
448 anti_aliasing = Settings::AntiAliasing::None;
449 Settings::values.anti_aliasing.SetValue(anti_aliasing);
450 }
451
446 if (anti_aliasing != Settings::AntiAliasing::None) { 452 if (anti_aliasing != Settings::AntiAliasing::None) {
447 glEnablei(GL_SCISSOR_TEST, 0); 453 glEnablei(GL_SCISSOR_TEST, 0);
448 auto viewport_width = screen_info.texture.width; 454 auto viewport_width = screen_info.texture.width;
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
index e62b36822..f8398b511 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
@@ -48,43 +48,30 @@ void RefreshXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell&
48} 48}
49} // Anonymous namespace 49} // Anonymous namespace
50 50
51void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, 51void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFeatures& features) {
52 bool has_extended_dynamic_state, bool has_dynamic_vertex_input) {
53 const Maxwell& regs = maxwell3d.regs; 52 const Maxwell& regs = maxwell3d.regs;
54 const auto topology_ = maxwell3d.draw_manager->GetDrawState().topology; 53 const auto topology_ = maxwell3d.draw_manager->GetDrawState().topology;
55 const std::array enabled_lut{
56 regs.polygon_offset_point_enable,
57 regs.polygon_offset_line_enable,
58 regs.polygon_offset_fill_enable,
59 };
60 const u32 topology_index = static_cast<u32>(topology_);
61 54
62 raw1 = 0; 55 raw1 = 0;
63 extended_dynamic_state.Assign(has_extended_dynamic_state ? 1 : 0); 56 extended_dynamic_state.Assign(features.has_extended_dynamic_state ? 1 : 0);
64 dynamic_vertex_input.Assign(has_dynamic_vertex_input ? 1 : 0); 57 extended_dynamic_state_2.Assign(features.has_extended_dynamic_state_2 ? 1 : 0);
58 extended_dynamic_state_2_extra.Assign(features.has_extended_dynamic_state_2_extra ? 1 : 0);
59 extended_dynamic_state_3_blend.Assign(features.has_extended_dynamic_state_3_blend ? 1 : 0);
60 extended_dynamic_state_3_enables.Assign(features.has_extended_dynamic_state_3_enables ? 1 : 0);
61 dynamic_vertex_input.Assign(features.has_dynamic_vertex_input ? 1 : 0);
65 xfb_enabled.Assign(regs.transform_feedback_enabled != 0); 62 xfb_enabled.Assign(regs.transform_feedback_enabled != 0);
66 primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0);
67 depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0);
68 depth_clamp_disabled.Assign(regs.viewport_clip_control.geometry_clip ==
69 Maxwell::ViewportClipControl::GeometryClip::Passthrough ||
70 regs.viewport_clip_control.geometry_clip ==
71 Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ ||
72 regs.viewport_clip_control.geometry_clip ==
73 Maxwell::ViewportClipControl::GeometryClip::FrustumZ);
74 ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0); 63 ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0);
75 polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front)); 64 polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front));
76 patch_control_points_minus_one.Assign(regs.patch_vertices - 1);
77 tessellation_primitive.Assign(static_cast<u32>(regs.tessellation.params.domain_type.Value())); 65 tessellation_primitive.Assign(static_cast<u32>(regs.tessellation.params.domain_type.Value()));
78 tessellation_spacing.Assign(static_cast<u32>(regs.tessellation.params.spacing.Value())); 66 tessellation_spacing.Assign(static_cast<u32>(regs.tessellation.params.spacing.Value()));
79 tessellation_clockwise.Assign(regs.tessellation.params.output_primitives.Value() == 67 tessellation_clockwise.Assign(regs.tessellation.params.output_primitives.Value() ==
80 Maxwell::Tessellation::OutputPrimitives::Triangles_CW); 68 Maxwell::Tessellation::OutputPrimitives::Triangles_CW);
81 logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); 69 patch_control_points_minus_one.Assign(regs.patch_vertices - 1);
82 logic_op.Assign(PackLogicOp(regs.logic_op.op));
83 topology.Assign(topology_); 70 topology.Assign(topology_);
84 msaa_mode.Assign(regs.anti_alias_samples_mode); 71 msaa_mode.Assign(regs.anti_alias_samples_mode);
85 72
86 raw2 = 0; 73 raw2 = 0;
87 rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0); 74
88 const auto test_func = 75 const auto test_func =
89 regs.alpha_test_enabled != 0 ? regs.alpha_test_func : Maxwell::ComparisonOp::Always_GL; 76 regs.alpha_test_enabled != 0 ? regs.alpha_test_func : Maxwell::ComparisonOp::Always_GL;
90 alpha_test_func.Assign(PackComparisonOp(test_func)); 77 alpha_test_func.Assign(PackComparisonOp(test_func));
@@ -97,6 +84,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
97 smooth_lines.Assign(regs.line_anti_alias_enable != 0 ? 1 : 0); 84 smooth_lines.Assign(regs.line_anti_alias_enable != 0 ? 1 : 0);
98 alpha_to_coverage_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_coverage != 0 ? 1 : 0); 85 alpha_to_coverage_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_coverage != 0 ? 1 : 0);
99 alpha_to_one_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_one != 0 ? 1 : 0); 86 alpha_to_one_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_one != 0 ? 1 : 0);
87 app_stage.Assign(maxwell3d.engine_state);
100 88
101 for (size_t i = 0; i < regs.rt.size(); ++i) { 89 for (size_t i = 0; i < regs.rt.size(); ++i) {
102 color_formats[i] = static_cast<u8>(regs.rt[i].format); 90 color_formats[i] = static_cast<u8>(regs.rt[i].format);
@@ -105,7 +93,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
105 point_size = Common::BitCast<u32>(regs.point_size); 93 point_size = Common::BitCast<u32>(regs.point_size);
106 94
107 if (maxwell3d.dirty.flags[Dirty::VertexInput]) { 95 if (maxwell3d.dirty.flags[Dirty::VertexInput]) {
108 if (has_dynamic_vertex_input) { 96 if (features.has_dynamic_vertex_input) {
109 // Dirty flag will be reset by the command buffer update 97 // Dirty flag will be reset by the command buffer update
110 static constexpr std::array LUT{ 98 static constexpr std::array LUT{
111 0u, // Invalid 99 0u, // Invalid
@@ -144,12 +132,6 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
144 } 132 }
145 } 133 }
146 } 134 }
147 if (maxwell3d.dirty.flags[Dirty::Blending]) {
148 maxwell3d.dirty.flags[Dirty::Blending] = false;
149 for (size_t index = 0; index < attachments.size(); ++index) {
150 attachments[index].Refresh(regs, index);
151 }
152 }
153 if (maxwell3d.dirty.flags[Dirty::ViewportSwizzles]) { 135 if (maxwell3d.dirty.flags[Dirty::ViewportSwizzles]) {
154 maxwell3d.dirty.flags[Dirty::ViewportSwizzles] = false; 136 maxwell3d.dirty.flags[Dirty::ViewportSwizzles] = false;
155 const auto& transform = regs.viewport_transform; 137 const auto& transform = regs.viewport_transform;
@@ -157,8 +139,27 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
157 return static_cast<u16>(viewport.swizzle.raw); 139 return static_cast<u16>(viewport.swizzle.raw);
158 }); 140 });
159 } 141 }
142 dynamic_state.raw1 = 0;
143 dynamic_state.raw2 = 0;
160 if (!extended_dynamic_state) { 144 if (!extended_dynamic_state) {
161 dynamic_state.Refresh(regs); 145 dynamic_state.Refresh(regs);
146 std::ranges::transform(regs.vertex_streams, vertex_strides.begin(), [](const auto& array) {
147 return static_cast<u16>(array.stride.Value());
148 });
149 }
150 if (!extended_dynamic_state_2_extra) {
151 dynamic_state.Refresh2(regs, topology_, extended_dynamic_state_2);
152 }
153 if (!extended_dynamic_state_3_blend) {
154 if (maxwell3d.dirty.flags[Dirty::Blending]) {
155 maxwell3d.dirty.flags[Dirty::Blending] = false;
156 for (size_t index = 0; index < attachments.size(); ++index) {
157 attachments[index].Refresh(regs, index);
158 }
159 }
160 }
161 if (!extended_dynamic_state_3_enables) {
162 dynamic_state.Refresh3(regs);
162 } 163 }
163 if (xfb_enabled) { 164 if (xfb_enabled) {
164 RefreshXfbState(xfb_state, regs); 165 RefreshXfbState(xfb_state, regs);
@@ -175,12 +176,11 @@ void FixedPipelineState::BlendingAttachment::Refresh(const Maxwell& regs, size_t
175 mask_a.Assign(mask.A); 176 mask_a.Assign(mask.A);
176 177
177 // TODO: C++20 Use templated lambda to deduplicate code 178 // TODO: C++20 Use templated lambda to deduplicate code
179 if (!regs.blend.enable[index]) {
180 return;
181 }
178 182
179 if (!regs.blend_per_target_enabled) { 183 const auto setup_blend = [&]<typename T>(const T& src) {
180 if (!regs.blend.enable[index]) {
181 return;
182 }
183 const auto& src = regs.blend;
184 equation_rgb.Assign(PackBlendEquation(src.color_op)); 184 equation_rgb.Assign(PackBlendEquation(src.color_op));
185 equation_a.Assign(PackBlendEquation(src.alpha_op)); 185 equation_a.Assign(PackBlendEquation(src.alpha_op));
186 factor_source_rgb.Assign(PackBlendFactor(src.color_source)); 186 factor_source_rgb.Assign(PackBlendFactor(src.color_source));
@@ -188,20 +188,13 @@ void FixedPipelineState::BlendingAttachment::Refresh(const Maxwell& regs, size_t
188 factor_source_a.Assign(PackBlendFactor(src.alpha_source)); 188 factor_source_a.Assign(PackBlendFactor(src.alpha_source));
189 factor_dest_a.Assign(PackBlendFactor(src.alpha_dest)); 189 factor_dest_a.Assign(PackBlendFactor(src.alpha_dest));
190 enable.Assign(1); 190 enable.Assign(1);
191 return; 191 };
192 }
193 192
194 if (!regs.blend.enable[index]) { 193 if (!regs.blend_per_target_enabled) {
194 setup_blend(regs.blend);
195 return; 195 return;
196 } 196 }
197 const auto& src = regs.blend_per_target[index]; 197 setup_blend(regs.blend_per_target[index]);
198 equation_rgb.Assign(PackBlendEquation(src.color_op));
199 equation_a.Assign(PackBlendEquation(src.alpha_op));
200 factor_source_rgb.Assign(PackBlendFactor(src.color_source));
201 factor_dest_rgb.Assign(PackBlendFactor(src.color_dest));
202 factor_source_a.Assign(PackBlendFactor(src.alpha_source));
203 factor_dest_a.Assign(PackBlendFactor(src.alpha_dest));
204 enable.Assign(1);
205} 198}
206 199
207void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) { 200void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) {
@@ -211,8 +204,6 @@ void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) {
211 packed_front_face = 1 - packed_front_face; 204 packed_front_face = 1 - packed_front_face;
212 } 205 }
213 206
214 raw1 = 0;
215 raw2 = 0;
216 front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op.fail)); 207 front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op.fail));
217 front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op.zfail)); 208 front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op.zfail));
218 front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op.zpass)); 209 front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op.zpass));
@@ -236,9 +227,37 @@ void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) {
236 depth_test_func.Assign(PackComparisonOp(regs.depth_test_func)); 227 depth_test_func.Assign(PackComparisonOp(regs.depth_test_func));
237 cull_face.Assign(PackCullFace(regs.gl_cull_face)); 228 cull_face.Assign(PackCullFace(regs.gl_cull_face));
238 cull_enable.Assign(regs.gl_cull_test_enabled != 0 ? 1 : 0); 229 cull_enable.Assign(regs.gl_cull_test_enabled != 0 ? 1 : 0);
239 std::ranges::transform(regs.vertex_streams, vertex_strides.begin(), [](const auto& array) { 230}
240 return static_cast<u16>(array.stride.Value()); 231
241 }); 232void FixedPipelineState::DynamicState::Refresh2(const Maxwell& regs,
233 Maxwell::PrimitiveTopology topology_,
234 bool base_feautures_supported) {
235 logic_op.Assign(PackLogicOp(regs.logic_op.op));
236
237 if (base_feautures_supported) {
238 return;
239 }
240
241 const std::array enabled_lut{
242 regs.polygon_offset_point_enable,
243 regs.polygon_offset_line_enable,
244 regs.polygon_offset_fill_enable,
245 };
246 const u32 topology_index = static_cast<u32>(topology_);
247
248 rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0);
249 primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0);
250 depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0);
251}
252
253void FixedPipelineState::DynamicState::Refresh3(const Maxwell& regs) {
254 logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0);
255 depth_clamp_disabled.Assign(regs.viewport_clip_control.geometry_clip ==
256 Maxwell::ViewportClipControl::GeometryClip::Passthrough ||
257 regs.viewport_clip_control.geometry_clip ==
258 Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ ||
259 regs.viewport_clip_control.geometry_clip ==
260 Maxwell::ViewportClipControl::GeometryClip::FrustumZ);
242} 261}
243 262
244size_t FixedPipelineState::Hash() const noexcept { 263size_t FixedPipelineState::Hash() const noexcept {
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
index ab79fb8f3..98ea20b42 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
@@ -17,6 +17,15 @@ namespace Vulkan {
17 17
18using Maxwell = Tegra::Engines::Maxwell3D::Regs; 18using Maxwell = Tegra::Engines::Maxwell3D::Regs;
19 19
20struct DynamicFeatures {
21 bool has_extended_dynamic_state;
22 bool has_extended_dynamic_state_2;
23 bool has_extended_dynamic_state_2_extra;
24 bool has_extended_dynamic_state_3_blend;
25 bool has_extended_dynamic_state_3_enables;
26 bool has_dynamic_vertex_input;
27};
28
20struct FixedPipelineState { 29struct FixedPipelineState {
21 static u32 PackComparisonOp(Maxwell::ComparisonOp op) noexcept; 30 static u32 PackComparisonOp(Maxwell::ComparisonOp op) noexcept;
22 static Maxwell::ComparisonOp UnpackComparisonOp(u32 packed) noexcept; 31 static Maxwell::ComparisonOp UnpackComparisonOp(u32 packed) noexcept;
@@ -133,6 +142,17 @@ struct FixedPipelineState {
133 struct DynamicState { 142 struct DynamicState {
134 union { 143 union {
135 u32 raw1; 144 u32 raw1;
145 BitField<0, 2, u32> cull_face;
146 BitField<2, 1, u32> cull_enable;
147 BitField<3, 1, u32> primitive_restart_enable;
148 BitField<4, 1, u32> depth_bias_enable;
149 BitField<5, 1, u32> rasterize_enable;
150 BitField<6, 4, u32> logic_op;
151 BitField<10, 1, u32> logic_op_enable;
152 BitField<11, 1, u32> depth_clamp_disabled;
153 };
154 union {
155 u32 raw2;
136 StencilFace<0> front; 156 StencilFace<0> front;
137 StencilFace<12> back; 157 StencilFace<12> back;
138 BitField<24, 1, u32> stencil_enable; 158 BitField<24, 1, u32> stencil_enable;
@@ -142,15 +162,11 @@ struct FixedPipelineState {
142 BitField<28, 1, u32> front_face; 162 BitField<28, 1, u32> front_face;
143 BitField<29, 3, u32> depth_test_func; 163 BitField<29, 3, u32> depth_test_func;
144 }; 164 };
145 union {
146 u32 raw2;
147 BitField<0, 2, u32> cull_face;
148 BitField<2, 1, u32> cull_enable;
149 };
150 // Vertex stride is a 12 bits value, we have 4 bits to spare per element
151 std::array<u16, Maxwell::NumVertexArrays> vertex_strides;
152 165
153 void Refresh(const Maxwell& regs); 166 void Refresh(const Maxwell& regs);
167 void Refresh2(const Maxwell& regs, Maxwell::PrimitiveTopology topology,
168 bool base_feautures_supported);
169 void Refresh3(const Maxwell& regs);
154 170
155 Maxwell::ComparisonOp DepthTestFunc() const noexcept { 171 Maxwell::ComparisonOp DepthTestFunc() const noexcept {
156 return UnpackComparisonOp(depth_test_func); 172 return UnpackComparisonOp(depth_test_func);
@@ -168,25 +184,24 @@ struct FixedPipelineState {
168 union { 184 union {
169 u32 raw1; 185 u32 raw1;
170 BitField<0, 1, u32> extended_dynamic_state; 186 BitField<0, 1, u32> extended_dynamic_state;
171 BitField<1, 1, u32> dynamic_vertex_input; 187 BitField<1, 1, u32> extended_dynamic_state_2;
172 BitField<2, 1, u32> xfb_enabled; 188 BitField<2, 1, u32> extended_dynamic_state_2_extra;
173 BitField<3, 1, u32> primitive_restart_enable; 189 BitField<3, 1, u32> extended_dynamic_state_3_blend;
174 BitField<4, 1, u32> depth_bias_enable; 190 BitField<4, 1, u32> extended_dynamic_state_3_enables;
175 BitField<5, 1, u32> depth_clamp_disabled; 191 BitField<5, 1, u32> dynamic_vertex_input;
176 BitField<6, 1, u32> ndc_minus_one_to_one; 192 BitField<6, 1, u32> xfb_enabled;
177 BitField<7, 2, u32> polygon_mode; 193 BitField<7, 1, u32> ndc_minus_one_to_one;
178 BitField<9, 5, u32> patch_control_points_minus_one; 194 BitField<8, 2, u32> polygon_mode;
179 BitField<14, 2, u32> tessellation_primitive; 195 BitField<10, 2, u32> tessellation_primitive;
180 BitField<16, 2, u32> tessellation_spacing; 196 BitField<12, 2, u32> tessellation_spacing;
181 BitField<18, 1, u32> tessellation_clockwise; 197 BitField<14, 1, u32> tessellation_clockwise;
182 BitField<19, 1, u32> logic_op_enable; 198 BitField<15, 5, u32> patch_control_points_minus_one;
183 BitField<20, 4, u32> logic_op; 199
184 BitField<24, 4, Maxwell::PrimitiveTopology> topology; 200 BitField<24, 4, Maxwell::PrimitiveTopology> topology;
185 BitField<28, 4, Tegra::Texture::MsaaMode> msaa_mode; 201 BitField<28, 4, Tegra::Texture::MsaaMode> msaa_mode;
186 }; 202 };
187 union { 203 union {
188 u32 raw2; 204 u32 raw2;
189 BitField<0, 1, u32> rasterize_enable;
190 BitField<1, 3, u32> alpha_test_func; 205 BitField<1, 3, u32> alpha_test_func;
191 BitField<4, 1, u32> early_z; 206 BitField<4, 1, u32> early_z;
192 BitField<5, 1, u32> depth_enabled; 207 BitField<5, 1, u32> depth_enabled;
@@ -197,25 +212,28 @@ struct FixedPipelineState {
197 BitField<14, 1, u32> smooth_lines; 212 BitField<14, 1, u32> smooth_lines;
198 BitField<15, 1, u32> alpha_to_coverage_enabled; 213 BitField<15, 1, u32> alpha_to_coverage_enabled;
199 BitField<16, 1, u32> alpha_to_one_enabled; 214 BitField<16, 1, u32> alpha_to_one_enabled;
215 BitField<17, 3, Tegra::Engines::Maxwell3D::EngineHint> app_stage;
200 }; 216 };
201 std::array<u8, Maxwell::NumRenderTargets> color_formats; 217 std::array<u8, Maxwell::NumRenderTargets> color_formats;
202 218
203 u32 alpha_test_ref; 219 u32 alpha_test_ref;
204 u32 point_size; 220 u32 point_size;
205 std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments;
206 std::array<u16, Maxwell::NumViewports> viewport_swizzles; 221 std::array<u16, Maxwell::NumViewports> viewport_swizzles;
207 union { 222 union {
208 u64 attribute_types; // Used with VK_EXT_vertex_input_dynamic_state 223 u64 attribute_types; // Used with VK_EXT_vertex_input_dynamic_state
209 u64 enabled_divisors; 224 u64 enabled_divisors;
210 }; 225 };
226
227 DynamicState dynamic_state;
228 std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments;
211 std::array<VertexAttribute, Maxwell::NumVertexAttributes> attributes; 229 std::array<VertexAttribute, Maxwell::NumVertexAttributes> attributes;
212 std::array<u32, Maxwell::NumVertexArrays> binding_divisors; 230 std::array<u32, Maxwell::NumVertexArrays> binding_divisors;
231 // Vertex stride is a 12 bits value, we have 4 bits to spare per element
232 std::array<u16, Maxwell::NumVertexArrays> vertex_strides;
213 233
214 DynamicState dynamic_state;
215 VideoCommon::TransformFeedbackState xfb_state; 234 VideoCommon::TransformFeedbackState xfb_state;
216 235
217 void Refresh(Tegra::Engines::Maxwell3D& maxwell3d, bool has_extended_dynamic_state, 236 void Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFeatures& features);
218 bool has_dynamic_vertex_input);
219 237
220 size_t Hash() const noexcept; 238 size_t Hash() const noexcept;
221 239
@@ -230,13 +248,17 @@ struct FixedPipelineState {
230 // When transform feedback is enabled, use the whole struct 248 // When transform feedback is enabled, use the whole struct
231 return sizeof(*this); 249 return sizeof(*this);
232 } 250 }
233 if (dynamic_vertex_input) { 251 if (dynamic_vertex_input && extended_dynamic_state_3_blend) {
234 // Exclude dynamic state and attributes 252 // Exclude dynamic state and attributes
253 return offsetof(FixedPipelineState, dynamic_state);
254 }
255 if (dynamic_vertex_input) {
256 // Exclude dynamic state
235 return offsetof(FixedPipelineState, attributes); 257 return offsetof(FixedPipelineState, attributes);
236 } 258 }
237 if (extended_dynamic_state) { 259 if (extended_dynamic_state) {
238 // Exclude dynamic state 260 // Exclude dynamic state
239 return offsetof(FixedPipelineState, dynamic_state); 261 return offsetof(FixedPipelineState, vertex_strides);
240 } 262 }
241 // Default 263 // Default
242 return offsetof(FixedPipelineState, xfb_state); 264 return offsetof(FixedPipelineState, xfb_state);
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index f502a7d09..52855120c 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -78,6 +78,8 @@ std::string BuildCommaSeparatedExtensions(std::vector<std::string> available_ext
78 return separated_extensions; 78 return separated_extensions;
79} 79}
80 80
81} // Anonymous namespace
82
81Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld, 83Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld,
82 VkSurfaceKHR surface) { 84 VkSurfaceKHR surface) {
83 const std::vector<VkPhysicalDevice> devices = instance.EnumeratePhysicalDevices(); 85 const std::vector<VkPhysicalDevice> devices = instance.EnumeratePhysicalDevices();
@@ -89,7 +91,6 @@ Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dl
89 const vk::PhysicalDevice physical_device(devices[device_index], dld); 91 const vk::PhysicalDevice physical_device(devices[device_index], dld);
90 return Device(*instance, physical_device, surface, dld); 92 return Device(*instance, physical_device, surface, dld);
91} 93}
92} // Anonymous namespace
93 94
94RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_, 95RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
95 Core::Frontend::EmuWindow& emu_window, 96 Core::Frontend::EmuWindow& emu_window,
@@ -98,7 +99,7 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
98 : RendererBase(emu_window, std::move(context_)), telemetry_session(telemetry_session_), 99 : RendererBase(emu_window, std::move(context_)), telemetry_session(telemetry_session_),
99 cpu_memory(cpu_memory_), gpu(gpu_), library(OpenLibrary()), 100 cpu_memory(cpu_memory_), gpu(gpu_), library(OpenLibrary()),
100 instance(CreateInstance(library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type, 101 instance(CreateInstance(library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type,
101 true, Settings::values.renderer_debug.GetValue())), 102 Settings::values.renderer_debug.GetValue())),
102 debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr), 103 debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr),
103 surface(CreateSurface(instance, render_window)), 104 surface(CreateSurface(instance, render_window)),
104 device(CreateDevice(instance, dld, *surface)), memory_allocator(device, false), 105 device(CreateDevice(instance, dld, *surface)), memory_allocator(device, false),
@@ -109,6 +110,9 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
109 screen_info), 110 screen_info),
110 rasterizer(render_window, gpu, cpu_memory, screen_info, device, memory_allocator, 111 rasterizer(render_window, gpu, cpu_memory, screen_info, device, memory_allocator,
111 state_tracker, scheduler) { 112 state_tracker, scheduler) {
113 if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) {
114 turbo_mode.emplace(instance, dld);
115 }
112 Report(); 116 Report();
113} catch (const vk::Exception& exception) { 117} catch (const vk::Exception& exception) {
114 LOG_ERROR(Render_Vulkan, "Vulkan initialization failed with error: {}", exception.what()); 118 LOG_ERROR(Render_Vulkan, "Vulkan initialization failed with error: {}", exception.what());
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index e7bfecb20..009e75e0d 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -13,6 +13,7 @@
13#include "video_core/renderer_vulkan/vk_scheduler.h" 13#include "video_core/renderer_vulkan/vk_scheduler.h"
14#include "video_core/renderer_vulkan/vk_state_tracker.h" 14#include "video_core/renderer_vulkan/vk_state_tracker.h"
15#include "video_core/renderer_vulkan/vk_swapchain.h" 15#include "video_core/renderer_vulkan/vk_swapchain.h"
16#include "video_core/renderer_vulkan/vk_turbo_mode.h"
16#include "video_core/vulkan_common/vulkan_device.h" 17#include "video_core/vulkan_common/vulkan_device.h"
17#include "video_core/vulkan_common/vulkan_memory_allocator.h" 18#include "video_core/vulkan_common/vulkan_memory_allocator.h"
18#include "video_core/vulkan_common/vulkan_wrapper.h" 19#include "video_core/vulkan_common/vulkan_wrapper.h"
@@ -31,6 +32,9 @@ class GPU;
31 32
32namespace Vulkan { 33namespace Vulkan {
33 34
35Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld,
36 VkSurfaceKHR surface);
37
34class RendererVulkan final : public VideoCore::RendererBase { 38class RendererVulkan final : public VideoCore::RendererBase {
35public: 39public:
36 explicit RendererVulkan(Core::TelemetrySession& telemtry_session, 40 explicit RendererVulkan(Core::TelemetrySession& telemtry_session,
@@ -74,6 +78,7 @@ private:
74 Swapchain swapchain; 78 Swapchain swapchain;
75 BlitScreen blit_screen; 79 BlitScreen blit_screen;
76 RasterizerVulkan rasterizer; 80 RasterizerVulkan rasterizer;
81 std::optional<TurboMode> turbo_mode;
77}; 82};
78 83
79} // namespace Vulkan 84} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 6b54d7111..1cfb4c2ff 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -56,7 +56,8 @@ vk::Buffer CreateBuffer(const Device& device, u64 size) {
56 VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | 56 VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
57 VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | 57 VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT |
58 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | 58 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
59 VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; 59 VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
60 VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
60 if (device.IsExtTransformFeedbackSupported()) { 61 if (device.IsExtTransformFeedbackSupported()) {
61 flags |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT; 62 flags |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
62 } 63 }
@@ -329,12 +330,19 @@ bool BufferCacheRuntime::CanReportMemoryUsage() const {
329 return device.CanReportMemoryUsage(); 330 return device.CanReportMemoryUsage();
330} 331}
331 332
333u32 BufferCacheRuntime::GetStorageBufferAlignment() const {
334 return static_cast<u32>(device.GetStorageBufferAlignment());
335}
336
332void BufferCacheRuntime::Finish() { 337void BufferCacheRuntime::Finish() {
333 scheduler.Finish(); 338 scheduler.Finish();
334} 339}
335 340
336void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer, 341void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer,
337 std::span<const VideoCommon::BufferCopy> copies, bool barrier) { 342 std::span<const VideoCommon::BufferCopy> copies, bool barrier) {
343 if (dst_buffer == VK_NULL_HANDLE || src_buffer == VK_NULL_HANDLE) {
344 return;
345 }
338 static constexpr VkMemoryBarrier READ_BARRIER{ 346 static constexpr VkMemoryBarrier READ_BARRIER{
339 .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, 347 .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
340 .pNext = nullptr, 348 .pNext = nullptr,
@@ -393,6 +401,9 @@ void BufferCacheRuntime::PostCopyBarrier() {
393} 401}
394 402
395void BufferCacheRuntime::ClearBuffer(VkBuffer dest_buffer, u32 offset, size_t size, u32 value) { 403void BufferCacheRuntime::ClearBuffer(VkBuffer dest_buffer, u32 offset, size_t size, u32 value) {
404 if (dest_buffer == VK_NULL_HANDLE) {
405 return;
406 }
396 static constexpr VkMemoryBarrier READ_BARRIER{ 407 static constexpr VkMemoryBarrier READ_BARRIER{
397 .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, 408 .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
398 .pNext = nullptr, 409 .pNext = nullptr,
@@ -472,6 +483,11 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset
472 cmdbuf.BindVertexBuffers2EXT(index, 1, &buffer, &vk_offset, &vk_size, &vk_stride); 483 cmdbuf.BindVertexBuffers2EXT(index, 1, &buffer, &vk_offset, &vk_size, &vk_stride);
473 }); 484 });
474 } else { 485 } else {
486 if (!device.HasNullDescriptor() && buffer == VK_NULL_HANDLE) {
487 ReserveNullBuffer();
488 buffer = *null_buffer;
489 offset = 0;
490 }
475 scheduler.Record([index, buffer, offset](vk::CommandBuffer cmdbuf) { 491 scheduler.Record([index, buffer, offset](vk::CommandBuffer cmdbuf) {
476 cmdbuf.BindVertexBuffer(index, buffer, offset); 492 cmdbuf.BindVertexBuffer(index, buffer, offset);
477 }); 493 });
@@ -516,6 +532,7 @@ void BufferCacheRuntime::ReserveNullBuffer() {
516 if (device.IsExtTransformFeedbackSupported()) { 532 if (device.IsExtTransformFeedbackSupported()) {
517 create_info.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT; 533 create_info.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
518 } 534 }
535 create_info.usage |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
519 null_buffer = device.GetLogical().CreateBuffer(create_info); 536 null_buffer = device.GetLogical().CreateBuffer(create_info);
520 if (device.HasDebuggingToolAttached()) { 537 if (device.HasDebuggingToolAttached()) {
521 null_buffer.SetObjectNameEXT("Null buffer"); 538 null_buffer.SetObjectNameEXT("Null buffer");
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index 183b33632..06539c733 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -73,6 +73,8 @@ public:
73 73
74 bool CanReportMemoryUsage() const; 74 bool CanReportMemoryUsage() const;
75 75
76 u32 GetStorageBufferAlignment() const;
77
76 [[nodiscard]] StagingBufferRef UploadStagingBuffer(size_t size); 78 [[nodiscard]] StagingBufferRef UploadStagingBuffer(size_t size);
77 79
78 [[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size); 80 [[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size);
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
index 04a3a861e..2a0f0dbf0 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
@@ -24,13 +24,15 @@ using Shader::ImageBufferDescriptor;
24using Shader::Backend::SPIRV::RESCALING_LAYOUT_WORDS_OFFSET; 24using Shader::Backend::SPIRV::RESCALING_LAYOUT_WORDS_OFFSET;
25using Tegra::Texture::TexturePair; 25using Tegra::Texture::TexturePair;
26 26
27ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descriptor_pool, 27ComputePipeline::ComputePipeline(const Device& device_, vk::PipelineCache& pipeline_cache_,
28 DescriptorPool& descriptor_pool,
28 UpdateDescriptorQueue& update_descriptor_queue_, 29 UpdateDescriptorQueue& update_descriptor_queue_,
29 Common::ThreadWorker* thread_worker, 30 Common::ThreadWorker* thread_worker,
30 PipelineStatistics* pipeline_statistics, 31 PipelineStatistics* pipeline_statistics,
31 VideoCore::ShaderNotify* shader_notify, const Shader::Info& info_, 32 VideoCore::ShaderNotify* shader_notify, const Shader::Info& info_,
32 vk::ShaderModule spv_module_) 33 vk::ShaderModule spv_module_)
33 : device{device_}, update_descriptor_queue{update_descriptor_queue_}, info{info_}, 34 : device{device_}, pipeline_cache(pipeline_cache_),
35 update_descriptor_queue{update_descriptor_queue_}, info{info_},
34 spv_module(std::move(spv_module_)) { 36 spv_module(std::move(spv_module_)) {
35 if (shader_notify) { 37 if (shader_notify) {
36 shader_notify->MarkShaderBuilding(); 38 shader_notify->MarkShaderBuilding();
@@ -56,23 +58,27 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript
56 if (device.IsKhrPipelineExecutablePropertiesEnabled()) { 58 if (device.IsKhrPipelineExecutablePropertiesEnabled()) {
57 flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; 59 flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR;
58 } 60 }
59 pipeline = device.GetLogical().CreateComputePipeline({ 61 pipeline = device.GetLogical().CreateComputePipeline(
60 .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, 62 {
61 .pNext = nullptr, 63 .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
62 .flags = flags, 64 .pNext = nullptr,
63 .stage{ 65 .flags = flags,
64 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, 66 .stage{
65 .pNext = device.IsExtSubgroupSizeControlSupported() ? &subgroup_size_ci : nullptr, 67 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
66 .flags = 0, 68 .pNext =
67 .stage = VK_SHADER_STAGE_COMPUTE_BIT, 69 device.IsExtSubgroupSizeControlSupported() ? &subgroup_size_ci : nullptr,
68 .module = *spv_module, 70 .flags = 0,
69 .pName = "main", 71 .stage = VK_SHADER_STAGE_COMPUTE_BIT,
70 .pSpecializationInfo = nullptr, 72 .module = *spv_module,
73 .pName = "main",
74 .pSpecializationInfo = nullptr,
75 },
76 .layout = *pipeline_layout,
77 .basePipelineHandle = 0,
78 .basePipelineIndex = 0,
71 }, 79 },
72 .layout = *pipeline_layout, 80 *pipeline_cache);
73 .basePipelineHandle = 0, 81
74 .basePipelineIndex = 0,
75 });
76 if (pipeline_statistics) { 82 if (pipeline_statistics) {
77 pipeline_statistics->Collect(*pipeline); 83 pipeline_statistics->Collect(*pipeline);
78 } 84 }
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.h b/src/video_core/renderer_vulkan/vk_compute_pipeline.h
index d70837fc5..78d77027f 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.h
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.h
@@ -28,7 +28,8 @@ class Scheduler;
28 28
29class ComputePipeline { 29class ComputePipeline {
30public: 30public:
31 explicit ComputePipeline(const Device& device, DescriptorPool& descriptor_pool, 31 explicit ComputePipeline(const Device& device, vk::PipelineCache& pipeline_cache,
32 DescriptorPool& descriptor_pool,
32 UpdateDescriptorQueue& update_descriptor_queue, 33 UpdateDescriptorQueue& update_descriptor_queue,
33 Common::ThreadWorker* thread_worker, 34 Common::ThreadWorker* thread_worker,
34 PipelineStatistics* pipeline_statistics, 35 PipelineStatistics* pipeline_statistics,
@@ -46,6 +47,7 @@ public:
46 47
47private: 48private:
48 const Device& device; 49 const Device& device;
50 vk::PipelineCache& pipeline_cache;
49 UpdateDescriptorQueue& update_descriptor_queue; 51 UpdateDescriptorQueue& update_descriptor_queue;
50 Shader::Info info; 52 Shader::Info info;
51 53
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 515d8d869..f91bb5a1d 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -201,6 +201,22 @@ struct SimpleVertexSpec {
201 static constexpr bool has_images = false; 201 static constexpr bool has_images = false;
202}; 202};
203 203
204struct SimpleStorageSpec {
205 static constexpr std::array<bool, 5> enabled_stages{true, false, false, false, true};
206 static constexpr bool has_storage_buffers = true;
207 static constexpr bool has_texture_buffers = false;
208 static constexpr bool has_image_buffers = false;
209 static constexpr bool has_images = false;
210};
211
212struct SimpleImageSpec {
213 static constexpr std::array<bool, 5> enabled_stages{true, false, false, false, true};
214 static constexpr bool has_storage_buffers = false;
215 static constexpr bool has_texture_buffers = false;
216 static constexpr bool has_image_buffers = false;
217 static constexpr bool has_images = true;
218};
219
204struct DefaultSpec { 220struct DefaultSpec {
205 static constexpr std::array<bool, 5> enabled_stages{true, true, true, true, true}; 221 static constexpr std::array<bool, 5> enabled_stages{true, true, true, true, true};
206 static constexpr bool has_storage_buffers = true; 222 static constexpr bool has_storage_buffers = true;
@@ -211,19 +227,21 @@ struct DefaultSpec {
211 227
212ConfigureFuncPtr ConfigureFunc(const std::array<vk::ShaderModule, NUM_STAGES>& modules, 228ConfigureFuncPtr ConfigureFunc(const std::array<vk::ShaderModule, NUM_STAGES>& modules,
213 const std::array<Shader::Info, NUM_STAGES>& infos) { 229 const std::array<Shader::Info, NUM_STAGES>& infos) {
214 return FindSpec<SimpleVertexSpec, SimpleVertexFragmentSpec, DefaultSpec>(modules, infos); 230 return FindSpec<SimpleVertexSpec, SimpleVertexFragmentSpec, SimpleStorageSpec, SimpleImageSpec,
231 DefaultSpec>(modules, infos);
215} 232}
216} // Anonymous namespace 233} // Anonymous namespace
217 234
218GraphicsPipeline::GraphicsPipeline( 235GraphicsPipeline::GraphicsPipeline(
219 Scheduler& scheduler_, BufferCache& buffer_cache_, TextureCache& texture_cache_, 236 Scheduler& scheduler_, BufferCache& buffer_cache_, TextureCache& texture_cache_,
220 VideoCore::ShaderNotify* shader_notify, const Device& device_, DescriptorPool& descriptor_pool, 237 vk::PipelineCache& pipeline_cache_, VideoCore::ShaderNotify* shader_notify,
238 const Device& device_, DescriptorPool& descriptor_pool,
221 UpdateDescriptorQueue& update_descriptor_queue_, Common::ThreadWorker* worker_thread, 239 UpdateDescriptorQueue& update_descriptor_queue_, Common::ThreadWorker* worker_thread,
222 PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache, 240 PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache,
223 const GraphicsPipelineCacheKey& key_, std::array<vk::ShaderModule, NUM_STAGES> stages, 241 const GraphicsPipelineCacheKey& key_, std::array<vk::ShaderModule, NUM_STAGES> stages,
224 const std::array<const Shader::Info*, NUM_STAGES>& infos) 242 const std::array<const Shader::Info*, NUM_STAGES>& infos)
225 : key{key_}, device{device_}, texture_cache{texture_cache_}, 243 : key{key_}, device{device_}, texture_cache{texture_cache_}, buffer_cache{buffer_cache_},
226 buffer_cache{buffer_cache_}, scheduler{scheduler_}, 244 pipeline_cache(pipeline_cache_), scheduler{scheduler_},
227 update_descriptor_queue{update_descriptor_queue_}, spv_modules{std::move(stages)} { 245 update_descriptor_queue{update_descriptor_queue_}, spv_modules{std::move(stages)} {
228 if (shader_notify) { 246 if (shader_notify) {
229 shader_notify->MarkShaderBuilding(); 247 shader_notify->MarkShaderBuilding();
@@ -524,6 +542,8 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
524 FixedPipelineState::DynamicState dynamic{}; 542 FixedPipelineState::DynamicState dynamic{};
525 if (!key.state.extended_dynamic_state) { 543 if (!key.state.extended_dynamic_state) {
526 dynamic = key.state.dynamic_state; 544 dynamic = key.state.dynamic_state;
545 } else {
546 dynamic.raw1 = key.state.dynamic_state.raw1;
527 } 547 }
528 static_vector<VkVertexInputBindingDescription, 32> vertex_bindings; 548 static_vector<VkVertexInputBindingDescription, 32> vertex_bindings;
529 static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors; 549 static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors;
@@ -561,7 +581,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
561 instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; 581 instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
562 vertex_bindings.push_back({ 582 vertex_bindings.push_back({
563 .binding = static_cast<u32>(index), 583 .binding = static_cast<u32>(index),
564 .stride = dynamic.vertex_strides[index], 584 .stride = key.state.vertex_strides[index],
565 .inputRate = rate, 585 .inputRate = rate,
566 }); 586 });
567 if (instanced) { 587 if (instanced) {
@@ -625,12 +645,15 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
625 .pNext = nullptr, 645 .pNext = nullptr,
626 .flags = 0, 646 .flags = 0,
627 .topology = input_assembly_topology, 647 .topology = input_assembly_topology,
628 .primitiveRestartEnable = key.state.primitive_restart_enable != 0 && 648 .primitiveRestartEnable =
629 ((input_assembly_topology != VK_PRIMITIVE_TOPOLOGY_PATCH_LIST && 649 dynamic.primitive_restart_enable != 0 &&
630 device.IsTopologyListPrimitiveRestartSupported()) || 650 ((input_assembly_topology != VK_PRIMITIVE_TOPOLOGY_PATCH_LIST &&
631 SupportsPrimitiveRestart(input_assembly_topology) || 651 device.IsTopologyListPrimitiveRestartSupported()) ||
632 (input_assembly_topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST && 652 SupportsPrimitiveRestart(input_assembly_topology) ||
633 device.IsPatchListPrimitiveRestartSupported())), 653 (input_assembly_topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST &&
654 device.IsPatchListPrimitiveRestartSupported()))
655 ? VK_TRUE
656 : VK_FALSE,
634 }; 657 };
635 const VkPipelineTessellationStateCreateInfo tessellation_ci{ 658 const VkPipelineTessellationStateCreateInfo tessellation_ci{
636 .sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO, 659 .sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO,
@@ -672,15 +695,15 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
672 .pNext = nullptr, 695 .pNext = nullptr,
673 .flags = 0, 696 .flags = 0,
674 .depthClampEnable = 697 .depthClampEnable =
675 static_cast<VkBool32>(key.state.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE), 698 static_cast<VkBool32>(dynamic.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE),
676 .rasterizerDiscardEnable = 699 .rasterizerDiscardEnable =
677 static_cast<VkBool32>(key.state.rasterize_enable == 0 ? VK_TRUE : VK_FALSE), 700 static_cast<VkBool32>(dynamic.rasterize_enable == 0 ? VK_TRUE : VK_FALSE),
678 .polygonMode = 701 .polygonMode =
679 MaxwellToVK::PolygonMode(FixedPipelineState::UnpackPolygonMode(key.state.polygon_mode)), 702 MaxwellToVK::PolygonMode(FixedPipelineState::UnpackPolygonMode(key.state.polygon_mode)),
680 .cullMode = static_cast<VkCullModeFlags>( 703 .cullMode = static_cast<VkCullModeFlags>(
681 dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE), 704 dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE),
682 .frontFace = MaxwellToVK::FrontFace(dynamic.FrontFace()), 705 .frontFace = MaxwellToVK::FrontFace(dynamic.FrontFace()),
683 .depthBiasEnable = key.state.depth_bias_enable, 706 .depthBiasEnable = (dynamic.depth_bias_enable != 0 ? VK_TRUE : VK_FALSE),
684 .depthBiasConstantFactor = 0.0f, 707 .depthBiasConstantFactor = 0.0f,
685 .depthBiasClamp = 0.0f, 708 .depthBiasClamp = 0.0f,
686 .depthBiasSlopeFactor = 0.0f, 709 .depthBiasSlopeFactor = 0.0f,
@@ -782,13 +805,13 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
782 .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, 805 .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
783 .pNext = nullptr, 806 .pNext = nullptr,
784 .flags = 0, 807 .flags = 0,
785 .logicOpEnable = key.state.logic_op_enable != 0, 808 .logicOpEnable = dynamic.logic_op_enable != 0,
786 .logicOp = static_cast<VkLogicOp>(key.state.logic_op.Value()), 809 .logicOp = static_cast<VkLogicOp>(dynamic.logic_op.Value()),
787 .attachmentCount = static_cast<u32>(cb_attachments.size()), 810 .attachmentCount = static_cast<u32>(cb_attachments.size()),
788 .pAttachments = cb_attachments.data(), 811 .pAttachments = cb_attachments.data(),
789 .blendConstants = {}, 812 .blendConstants = {},
790 }; 813 };
791 static_vector<VkDynamicState, 19> dynamic_states{ 814 static_vector<VkDynamicState, 28> dynamic_states{
792 VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, 815 VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR,
793 VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS, 816 VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS,
794 VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, 817 VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
@@ -811,6 +834,32 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
811 dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT); 834 dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT);
812 } 835 }
813 dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end()); 836 dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end());
837 if (key.state.extended_dynamic_state_2) {
838 static constexpr std::array extended2{
839 VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE_EXT,
840 VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT,
841 VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE_EXT,
842 };
843 dynamic_states.insert(dynamic_states.end(), extended2.begin(), extended2.end());
844 }
845 if (key.state.extended_dynamic_state_2_extra) {
846 dynamic_states.push_back(VK_DYNAMIC_STATE_LOGIC_OP_EXT);
847 }
848 if (key.state.extended_dynamic_state_3_blend) {
849 static constexpr std::array extended3{
850 VK_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT,
851 VK_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT,
852 VK_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT,
853 };
854 dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end());
855 }
856 if (key.state.extended_dynamic_state_3_enables) {
857 static constexpr std::array extended3{
858 VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT,
859 VK_DYNAMIC_STATE_LOGIC_OP_ENABLE_EXT,
860 };
861 dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end());
862 }
814 } 863 }
815 const VkPipelineDynamicStateCreateInfo dynamic_state_ci{ 864 const VkPipelineDynamicStateCreateInfo dynamic_state_ci{
816 .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, 865 .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
@@ -849,27 +898,29 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
849 if (device.IsKhrPipelineExecutablePropertiesEnabled()) { 898 if (device.IsKhrPipelineExecutablePropertiesEnabled()) {
850 flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; 899 flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR;
851 } 900 }
852 pipeline = device.GetLogical().CreateGraphicsPipeline({ 901 pipeline = device.GetLogical().CreateGraphicsPipeline(
853 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, 902 {
854 .pNext = nullptr, 903 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
855 .flags = flags, 904 .pNext = nullptr,
856 .stageCount = static_cast<u32>(shader_stages.size()), 905 .flags = flags,
857 .pStages = shader_stages.data(), 906 .stageCount = static_cast<u32>(shader_stages.size()),
858 .pVertexInputState = &vertex_input_ci, 907 .pStages = shader_stages.data(),
859 .pInputAssemblyState = &input_assembly_ci, 908 .pVertexInputState = &vertex_input_ci,
860 .pTessellationState = &tessellation_ci, 909 .pInputAssemblyState = &input_assembly_ci,
861 .pViewportState = &viewport_ci, 910 .pTessellationState = &tessellation_ci,
862 .pRasterizationState = &rasterization_ci, 911 .pViewportState = &viewport_ci,
863 .pMultisampleState = &multisample_ci, 912 .pRasterizationState = &rasterization_ci,
864 .pDepthStencilState = &depth_stencil_ci, 913 .pMultisampleState = &multisample_ci,
865 .pColorBlendState = &color_blend_ci, 914 .pDepthStencilState = &depth_stencil_ci,
866 .pDynamicState = &dynamic_state_ci, 915 .pColorBlendState = &color_blend_ci,
867 .layout = *pipeline_layout, 916 .pDynamicState = &dynamic_state_ci,
868 .renderPass = render_pass, 917 .layout = *pipeline_layout,
869 .subpass = 0, 918 .renderPass = render_pass,
870 .basePipelineHandle = nullptr, 919 .subpass = 0,
871 .basePipelineIndex = 0, 920 .basePipelineHandle = nullptr,
872 }); 921 .basePipelineIndex = 0,
922 },
923 *pipeline_cache);
873} 924}
874 925
875void GraphicsPipeline::Validate() { 926void GraphicsPipeline::Validate() {
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
index 1ed2967be..67c657d0e 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
@@ -70,16 +70,14 @@ class GraphicsPipeline {
70 static constexpr size_t NUM_STAGES = Tegra::Engines::Maxwell3D::Regs::MaxShaderStage; 70 static constexpr size_t NUM_STAGES = Tegra::Engines::Maxwell3D::Regs::MaxShaderStage;
71 71
72public: 72public:
73 explicit GraphicsPipeline(Scheduler& scheduler, BufferCache& buffer_cache, 73 explicit GraphicsPipeline(
74 TextureCache& texture_cache, VideoCore::ShaderNotify* shader_notify, 74 Scheduler& scheduler, BufferCache& buffer_cache, TextureCache& texture_cache,
75 const Device& device, DescriptorPool& descriptor_pool, 75 vk::PipelineCache& pipeline_cache, VideoCore::ShaderNotify* shader_notify,
76 UpdateDescriptorQueue& update_descriptor_queue, 76 const Device& device, DescriptorPool& descriptor_pool,
77 Common::ThreadWorker* worker_thread, 77 UpdateDescriptorQueue& update_descriptor_queue, Common::ThreadWorker* worker_thread,
78 PipelineStatistics* pipeline_statistics, 78 PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache,
79 RenderPassCache& render_pass_cache, 79 const GraphicsPipelineCacheKey& key, std::array<vk::ShaderModule, NUM_STAGES> stages,
80 const GraphicsPipelineCacheKey& key, 80 const std::array<const Shader::Info*, NUM_STAGES>& infos);
81 std::array<vk::ShaderModule, NUM_STAGES> stages,
82 const std::array<const Shader::Info*, NUM_STAGES>& infos);
83 81
84 GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete; 82 GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete;
85 GraphicsPipeline(GraphicsPipeline&&) noexcept = delete; 83 GraphicsPipeline(GraphicsPipeline&&) noexcept = delete;
@@ -133,6 +131,7 @@ private:
133 const Device& device; 131 const Device& device;
134 TextureCache& texture_cache; 132 TextureCache& texture_cache;
135 BufferCache& buffer_cache; 133 BufferCache& buffer_cache;
134 vk::PipelineCache& pipeline_cache;
136 Scheduler& scheduler; 135 Scheduler& scheduler;
137 UpdateDescriptorQueue& update_descriptor_queue; 136 UpdateDescriptorQueue& update_descriptor_queue;
138 137
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index e7262420c..7e69b11d8 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -54,7 +54,8 @@ using VideoCommon::FileEnvironment;
54using VideoCommon::GenericEnvironment; 54using VideoCommon::GenericEnvironment;
55using VideoCommon::GraphicsEnvironment; 55using VideoCommon::GraphicsEnvironment;
56 56
57constexpr u32 CACHE_VERSION = 8; 57constexpr u32 CACHE_VERSION = 10;
58constexpr std::array<char, 8> VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'};
58 59
59template <typename Container> 60template <typename Container>
60auto MakeSpan(Container& container) { 61auto MakeSpan(Container& container) {
@@ -284,6 +285,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
284 render_pass_cache{render_pass_cache_}, buffer_cache{buffer_cache_}, 285 render_pass_cache{render_pass_cache_}, buffer_cache{buffer_cache_},
285 texture_cache{texture_cache_}, shader_notify{shader_notify_}, 286 texture_cache{texture_cache_}, shader_notify{shader_notify_},
286 use_asynchronous_shaders{Settings::values.use_asynchronous_shaders.GetValue()}, 287 use_asynchronous_shaders{Settings::values.use_asynchronous_shaders.GetValue()},
288 use_vulkan_pipeline_cache{Settings::values.use_vulkan_driver_pipeline_cache.GetValue()},
287 workers(std::max(std::thread::hardware_concurrency(), 2U) - 1, "VkPipelineBuilder"), 289 workers(std::max(std::thread::hardware_concurrency(), 2U) - 1, "VkPipelineBuilder"),
288 serialization_thread(1, "VkPipelineSerialization") { 290 serialization_thread(1, "VkPipelineSerialization") {
289 const auto& float_control{device.FloatControlProperties()}; 291 const auto& float_control{device.FloatControlProperties()};
@@ -329,6 +331,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
329 .need_declared_frag_colors = false, 331 .need_declared_frag_colors = false,
330 332
331 .has_broken_spirv_clamp = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS, 333 .has_broken_spirv_clamp = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS,
334 .has_broken_spirv_position_input = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY,
332 .has_broken_unsigned_image_offsets = false, 335 .has_broken_unsigned_image_offsets = false,
333 .has_broken_signed_operations = false, 336 .has_broken_signed_operations = false,
334 .has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY, 337 .has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY,
@@ -341,6 +344,8 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
341 driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE, 344 driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE,
342 .support_snorm_render_buffer = true, 345 .support_snorm_render_buffer = true,
343 .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(), 346 .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(),
347 .min_ssbo_alignment = static_cast<u32>(device.GetStorageBufferAlignment()),
348 .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(),
344 }; 349 };
345 350
346 if (device.GetMaxVertexInputAttributes() < Maxwell::NumVertexAttributes) { 351 if (device.GetMaxVertexInputAttributes() < Maxwell::NumVertexAttributes) {
@@ -351,9 +356,23 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
351 LOG_WARNING(Render_Vulkan, "maxVertexInputBindings is too low: {} < {}", 356 LOG_WARNING(Render_Vulkan, "maxVertexInputBindings is too low: {} < {}",
352 device.GetMaxVertexInputBindings(), Maxwell::NumVertexArrays); 357 device.GetMaxVertexInputBindings(), Maxwell::NumVertexArrays);
353 } 358 }
359
360 dynamic_features = DynamicFeatures{
361 .has_extended_dynamic_state = device.IsExtExtendedDynamicStateSupported(),
362 .has_extended_dynamic_state_2 = device.IsExtExtendedDynamicState2Supported(),
363 .has_extended_dynamic_state_2_extra = device.IsExtExtendedDynamicState2ExtrasSupported(),
364 .has_extended_dynamic_state_3_blend = device.IsExtExtendedDynamicState3BlendingSupported(),
365 .has_extended_dynamic_state_3_enables = device.IsExtExtendedDynamicState3EnablesSupported(),
366 .has_dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported(),
367 };
354} 368}
355 369
356PipelineCache::~PipelineCache() = default; 370PipelineCache::~PipelineCache() {
371 if (use_vulkan_pipeline_cache && !vulkan_pipeline_cache_filename.empty()) {
372 SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache,
373 CACHE_VERSION);
374 }
375}
357 376
358GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() { 377GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() {
359 MICROPROFILE_SCOPE(Vulkan_PipelineCache); 378 MICROPROFILE_SCOPE(Vulkan_PipelineCache);
@@ -362,8 +381,7 @@ GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() {
362 current_pipeline = nullptr; 381 current_pipeline = nullptr;
363 return nullptr; 382 return nullptr;
364 } 383 }
365 graphics_key.state.Refresh(*maxwell3d, device.IsExtExtendedDynamicStateSupported(), 384 graphics_key.state.Refresh(*maxwell3d, dynamic_features);
366 device.IsExtVertexInputDynamicStateSupported());
367 385
368 if (current_pipeline) { 386 if (current_pipeline) {
369 GraphicsPipeline* const next{current_pipeline->Next(graphics_key)}; 387 GraphicsPipeline* const next{current_pipeline->Next(graphics_key)};
@@ -410,6 +428,12 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
410 } 428 }
411 pipeline_cache_filename = base_dir / "vulkan.bin"; 429 pipeline_cache_filename = base_dir / "vulkan.bin";
412 430
431 if (use_vulkan_pipeline_cache) {
432 vulkan_pipeline_cache_filename = base_dir / "vulkan_pipelines.bin";
433 vulkan_pipeline_cache =
434 LoadVulkanPipelineCache(vulkan_pipeline_cache_filename, CACHE_VERSION);
435 }
436
413 struct { 437 struct {
414 std::mutex mutex; 438 std::mutex mutex;
415 size_t total{}; 439 size_t total{};
@@ -439,14 +463,21 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
439 }); 463 });
440 ++state.total; 464 ++state.total;
441 }}; 465 }};
442 const bool extended_dynamic_state = device.IsExtExtendedDynamicStateSupported();
443 const bool dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported();
444 const auto load_graphics{[&](std::ifstream& file, std::vector<FileEnvironment> envs) { 466 const auto load_graphics{[&](std::ifstream& file, std::vector<FileEnvironment> envs) {
445 GraphicsPipelineCacheKey key; 467 GraphicsPipelineCacheKey key;
446 file.read(reinterpret_cast<char*>(&key), sizeof(key)); 468 file.read(reinterpret_cast<char*>(&key), sizeof(key));
447 469
448 if ((key.state.extended_dynamic_state != 0) != extended_dynamic_state || 470 if ((key.state.extended_dynamic_state != 0) !=
449 (key.state.dynamic_vertex_input != 0) != dynamic_vertex_input) { 471 dynamic_features.has_extended_dynamic_state ||
472 (key.state.extended_dynamic_state_2 != 0) !=
473 dynamic_features.has_extended_dynamic_state_2 ||
474 (key.state.extended_dynamic_state_2_extra != 0) !=
475 dynamic_features.has_extended_dynamic_state_2_extra ||
476 (key.state.extended_dynamic_state_3_blend != 0) !=
477 dynamic_features.has_extended_dynamic_state_3_blend ||
478 (key.state.extended_dynamic_state_3_enables != 0) !=
479 dynamic_features.has_extended_dynamic_state_3_enables ||
480 (key.state.dynamic_vertex_input != 0) != dynamic_features.has_dynamic_vertex_input) {
450 return; 481 return;
451 } 482 }
452 workers.QueueWork([this, key, envs = std::move(envs), &state, &callback]() mutable { 483 workers.QueueWork([this, key, envs = std::move(envs), &state, &callback]() mutable {
@@ -481,6 +512,11 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
481 512
482 workers.WaitForRequests(stop_loading); 513 workers.WaitForRequests(stop_loading);
483 514
515 if (use_vulkan_pipeline_cache) {
516 SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache,
517 CACHE_VERSION);
518 }
519
484 if (state.statistics) { 520 if (state.statistics) {
485 state.statistics->Report(); 521 state.statistics->Report();
486 } 522 }
@@ -601,10 +637,10 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
601 previous_stage = &program; 637 previous_stage = &program;
602 } 638 }
603 Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; 639 Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr};
604 return std::make_unique<GraphicsPipeline>(scheduler, buffer_cache, texture_cache, 640 return std::make_unique<GraphicsPipeline>(
605 &shader_notify, device, descriptor_pool, 641 scheduler, buffer_cache, texture_cache, vulkan_pipeline_cache, &shader_notify, device,
606 update_descriptor_queue, thread_worker, statistics, 642 descriptor_pool, update_descriptor_queue, thread_worker, statistics, render_pass_cache, key,
607 render_pass_cache, key, std::move(modules), infos); 643 std::move(modules), infos);
608 644
609} catch (const Shader::Exception& exception) { 645} catch (const Shader::Exception& exception) {
610 LOG_ERROR(Render_Vulkan, "{}", exception.what()); 646 LOG_ERROR(Render_Vulkan, "{}", exception.what());
@@ -674,13 +710,108 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
674 spv_module.SetObjectNameEXT(name.c_str()); 710 spv_module.SetObjectNameEXT(name.c_str());
675 } 711 }
676 Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; 712 Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr};
677 return std::make_unique<ComputePipeline>(device, descriptor_pool, update_descriptor_queue, 713 return std::make_unique<ComputePipeline>(device, vulkan_pipeline_cache, descriptor_pool,
678 thread_worker, statistics, &shader_notify, 714 update_descriptor_queue, thread_worker, statistics,
679 program.info, std::move(spv_module)); 715 &shader_notify, program.info, std::move(spv_module));
680 716
681} catch (const Shader::Exception& exception) { 717} catch (const Shader::Exception& exception) {
682 LOG_ERROR(Render_Vulkan, "{}", exception.what()); 718 LOG_ERROR(Render_Vulkan, "{}", exception.what());
683 return nullptr; 719 return nullptr;
684} 720}
685 721
722void PipelineCache::SerializeVulkanPipelineCache(const std::filesystem::path& filename,
723 const vk::PipelineCache& pipeline_cache,
724 u32 cache_version) try {
725 std::ofstream file(filename, std::ios::binary);
726 file.exceptions(std::ifstream::failbit);
727 if (!file.is_open()) {
728 LOG_ERROR(Common_Filesystem, "Failed to open Vulkan driver pipeline cache file {}",
729 Common::FS::PathToUTF8String(filename));
730 return;
731 }
732 file.write(VULKAN_CACHE_MAGIC_NUMBER.data(), VULKAN_CACHE_MAGIC_NUMBER.size())
733 .write(reinterpret_cast<const char*>(&cache_version), sizeof(cache_version));
734
735 size_t cache_size = 0;
736 std::vector<char> cache_data;
737 if (pipeline_cache) {
738 pipeline_cache.Read(&cache_size, nullptr);
739 cache_data.resize(cache_size);
740 pipeline_cache.Read(&cache_size, cache_data.data());
741 }
742 file.write(cache_data.data(), cache_size);
743
744 LOG_INFO(Render_Vulkan, "Vulkan driver pipelines cached at: {}",
745 Common::FS::PathToUTF8String(filename));
746
747} catch (const std::ios_base::failure& e) {
748 LOG_ERROR(Common_Filesystem, "{}", e.what());
749 if (!Common::FS::RemoveFile(filename)) {
750 LOG_ERROR(Common_Filesystem, "Failed to delete Vulkan driver pipeline cache file {}",
751 Common::FS::PathToUTF8String(filename));
752 }
753}
754
755vk::PipelineCache PipelineCache::LoadVulkanPipelineCache(const std::filesystem::path& filename,
756 u32 expected_cache_version) {
757 const auto create_pipeline_cache = [this](size_t data_size, const void* data) {
758 VkPipelineCacheCreateInfo pipeline_cache_ci = {
759 .sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO,
760 .pNext = nullptr,
761 .flags = 0,
762 .initialDataSize = data_size,
763 .pInitialData = data};
764 return device.GetLogical().CreatePipelineCache(pipeline_cache_ci);
765 };
766 try {
767 std::ifstream file(filename, std::ios::binary | std::ios::ate);
768 if (!file.is_open()) {
769 return create_pipeline_cache(0, nullptr);
770 }
771 file.exceptions(std::ifstream::failbit);
772 const auto end{file.tellg()};
773 file.seekg(0, std::ios::beg);
774
775 std::array<char, 8> magic_number;
776 u32 cache_version;
777 file.read(magic_number.data(), magic_number.size())
778 .read(reinterpret_cast<char*>(&cache_version), sizeof(cache_version));
779 if (magic_number != VULKAN_CACHE_MAGIC_NUMBER || cache_version != expected_cache_version) {
780 file.close();
781 if (Common::FS::RemoveFile(filename)) {
782 if (magic_number != VULKAN_CACHE_MAGIC_NUMBER) {
783 LOG_ERROR(Common_Filesystem, "Invalid Vulkan driver pipeline cache file");
784 }
785 if (cache_version != expected_cache_version) {
786 LOG_INFO(Common_Filesystem, "Deleting old Vulkan driver pipeline cache");
787 }
788 } else {
789 LOG_ERROR(Common_Filesystem,
790 "Invalid Vulkan pipeline cache file and failed to delete it in \"{}\"",
791 Common::FS::PathToUTF8String(filename));
792 }
793 return create_pipeline_cache(0, nullptr);
794 }
795
796 static constexpr size_t header_size = magic_number.size() + sizeof(cache_version);
797 const size_t cache_size = static_cast<size_t>(end) - header_size;
798 std::vector<char> cache_data(cache_size);
799 file.read(cache_data.data(), cache_size);
800
801 LOG_INFO(Render_Vulkan,
802 "Loaded Vulkan driver pipeline cache: ", Common::FS::PathToUTF8String(filename));
803
804 return create_pipeline_cache(cache_size, cache_data.data());
805
806 } catch (const std::ios_base::failure& e) {
807 LOG_ERROR(Common_Filesystem, "{}", e.what());
808 if (!Common::FS::RemoveFile(filename)) {
809 LOG_ERROR(Common_Filesystem, "Failed to delete Vulkan driver pipeline cache file {}",
810 Common::FS::PathToUTF8String(filename));
811 }
812
813 return create_pipeline_cache(0, nullptr);
814 }
815}
816
686} // namespace Vulkan 817} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
index 61f9e9366..5171912d7 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
@@ -135,6 +135,12 @@ private:
135 PipelineStatistics* statistics, 135 PipelineStatistics* statistics,
136 bool build_in_parallel); 136 bool build_in_parallel);
137 137
138 void SerializeVulkanPipelineCache(const std::filesystem::path& filename,
139 const vk::PipelineCache& pipeline_cache, u32 cache_version);
140
141 vk::PipelineCache LoadVulkanPipelineCache(const std::filesystem::path& filename,
142 u32 expected_cache_version);
143
138 const Device& device; 144 const Device& device;
139 Scheduler& scheduler; 145 Scheduler& scheduler;
140 DescriptorPool& descriptor_pool; 146 DescriptorPool& descriptor_pool;
@@ -144,6 +150,7 @@ private:
144 TextureCache& texture_cache; 150 TextureCache& texture_cache;
145 VideoCore::ShaderNotify& shader_notify; 151 VideoCore::ShaderNotify& shader_notify;
146 bool use_asynchronous_shaders{}; 152 bool use_asynchronous_shaders{};
153 bool use_vulkan_pipeline_cache{};
147 154
148 GraphicsPipelineCacheKey graphics_key{}; 155 GraphicsPipelineCacheKey graphics_key{};
149 GraphicsPipeline* current_pipeline{}; 156 GraphicsPipeline* current_pipeline{};
@@ -158,8 +165,12 @@ private:
158 165
159 std::filesystem::path pipeline_cache_filename; 166 std::filesystem::path pipeline_cache_filename;
160 167
168 std::filesystem::path vulkan_pipeline_cache_filename;
169 vk::PipelineCache vulkan_pipeline_cache;
170
161 Common::ThreadWorker workers; 171 Common::ThreadWorker workers;
162 Common::ThreadWorker serialization_thread; 172 Common::ThreadWorker serialization_thread;
173 DynamicFeatures dynamic_features;
163}; 174};
164 175
165} // namespace Vulkan 176} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index ac1eb9895..ed4a72166 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -180,11 +180,13 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra
180 180
181RasterizerVulkan::~RasterizerVulkan() = default; 181RasterizerVulkan::~RasterizerVulkan() = default;
182 182
183void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) { 183template <typename Func>
184void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
184 MICROPROFILE_SCOPE(Vulkan_Drawing); 185 MICROPROFILE_SCOPE(Vulkan_Drawing);
185 186
186 SCOPE_EXIT({ gpu.TickWork(); }); 187 SCOPE_EXIT({ gpu.TickWork(); });
187 FlushWork(); 188 FlushWork();
189 gpu_memory->FlushCaching();
188 190
189 query_cache.UpdateCounters(); 191 query_cache.UpdateCounters();
190 192
@@ -201,20 +203,67 @@ void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
201 203
202 UpdateDynamicStates(); 204 UpdateDynamicStates();
203 205
204 const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); 206 draw_func();
205 const u32 num_instances{instance_count}; 207
206 const DrawParams draw_params{MakeDrawParams(draw_state, num_instances, is_indexed)}; 208 EndTransformFeedback();
207 scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) { 209}
208 if (draw_params.is_indexed) { 210
209 cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances, 211void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
210 draw_params.first_index, draw_params.base_vertex, 212 PrepareDraw(is_indexed, [this, is_indexed, instance_count] {
211 draw_params.base_instance); 213 const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
212 } else { 214 const u32 num_instances{instance_count};
213 cmdbuf.Draw(draw_params.num_vertices, draw_params.num_instances, 215 const DrawParams draw_params{MakeDrawParams(draw_state, num_instances, is_indexed)};
214 draw_params.base_vertex, draw_params.base_instance); 216 scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) {
217 if (draw_params.is_indexed) {
218 cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances,
219 draw_params.first_index, draw_params.base_vertex,
220 draw_params.base_instance);
221 } else {
222 cmdbuf.Draw(draw_params.num_vertices, draw_params.num_instances,
223 draw_params.base_vertex, draw_params.base_instance);
224 }
225 });
226 });
227}
228
229void RasterizerVulkan::DrawIndirect() {
230 const auto& params = maxwell3d->draw_manager->GetIndirectParams();
231 buffer_cache.SetDrawIndirect(&params);
232 PrepareDraw(params.is_indexed, [this, &params] {
233 const auto indirect_buffer = buffer_cache.GetDrawIndirectBuffer();
234 const auto& buffer = indirect_buffer.first;
235 const auto& offset = indirect_buffer.second;
236 if (params.include_count) {
237 const auto count = buffer_cache.GetDrawIndirectCount();
238 const auto& draw_buffer = count.first;
239 const auto& offset_base = count.second;
240 scheduler.Record([draw_buffer_obj = draw_buffer->Handle(),
241 buffer_obj = buffer->Handle(), offset_base, offset,
242 params](vk::CommandBuffer cmdbuf) {
243 if (params.is_indexed) {
244 cmdbuf.DrawIndexedIndirectCount(
245 buffer_obj, offset, draw_buffer_obj, offset_base,
246 static_cast<u32>(params.max_draw_counts), static_cast<u32>(params.stride));
247 } else {
248 cmdbuf.DrawIndirectCount(buffer_obj, offset, draw_buffer_obj, offset_base,
249 static_cast<u32>(params.max_draw_counts),
250 static_cast<u32>(params.stride));
251 }
252 });
253 return;
215 } 254 }
255 scheduler.Record([buffer_obj = buffer->Handle(), offset, params](vk::CommandBuffer cmdbuf) {
256 if (params.is_indexed) {
257 cmdbuf.DrawIndexedIndirect(buffer_obj, offset,
258 static_cast<u32>(params.max_draw_counts),
259 static_cast<u32>(params.stride));
260 } else {
261 cmdbuf.DrawIndirect(buffer_obj, offset, static_cast<u32>(params.max_draw_counts),
262 static_cast<u32>(params.stride));
263 }
264 });
216 }); 265 });
217 EndTransformFeedback(); 266 buffer_cache.SetDrawIndirect(nullptr);
218} 267}
219 268
220void RasterizerVulkan::Clear(u32 layer_count) { 269void RasterizerVulkan::Clear(u32 layer_count) {
@@ -345,6 +394,7 @@ void RasterizerVulkan::Clear(u32 layer_count) {
345 394
346void RasterizerVulkan::DispatchCompute() { 395void RasterizerVulkan::DispatchCompute() {
347 FlushWork(); 396 FlushWork();
397 gpu_memory->FlushCaching();
348 398
349 ComputePipeline* const pipeline{pipeline_cache.CurrentComputePipeline()}; 399 ComputePipeline* const pipeline{pipeline_cache.CurrentComputePipeline()};
350 if (!pipeline) { 400 if (!pipeline) {
@@ -379,44 +429,79 @@ void Vulkan::RasterizerVulkan::DisableGraphicsUniformBuffer(size_t stage, u32 in
379 429
380void RasterizerVulkan::FlushAll() {} 430void RasterizerVulkan::FlushAll() {}
381 431
382void RasterizerVulkan::FlushRegion(VAddr addr, u64 size) { 432void RasterizerVulkan::FlushRegion(VAddr addr, u64 size, VideoCommon::CacheType which) {
383 if (addr == 0 || size == 0) { 433 if (addr == 0 || size == 0) {
384 return; 434 return;
385 } 435 }
386 { 436 if (True(which & VideoCommon::CacheType::TextureCache)) {
387 std::scoped_lock lock{texture_cache.mutex}; 437 std::scoped_lock lock{texture_cache.mutex};
388 texture_cache.DownloadMemory(addr, size); 438 texture_cache.DownloadMemory(addr, size);
389 } 439 }
390 { 440 if ((True(which & VideoCommon::CacheType::BufferCache))) {
391 std::scoped_lock lock{buffer_cache.mutex}; 441 std::scoped_lock lock{buffer_cache.mutex};
392 buffer_cache.DownloadMemory(addr, size); 442 buffer_cache.DownloadMemory(addr, size);
393 } 443 }
394 query_cache.FlushRegion(addr, size); 444 if ((True(which & VideoCommon::CacheType::QueryCache))) {
445 query_cache.FlushRegion(addr, size);
446 }
395} 447}
396 448
397bool RasterizerVulkan::MustFlushRegion(VAddr addr, u64 size) { 449bool RasterizerVulkan::MustFlushRegion(VAddr addr, u64 size, VideoCommon::CacheType which) {
398 std::scoped_lock lock{texture_cache.mutex, buffer_cache.mutex}; 450 if ((True(which & VideoCommon::CacheType::BufferCache))) {
451 std::scoped_lock lock{buffer_cache.mutex};
452 if (buffer_cache.IsRegionGpuModified(addr, size)) {
453 return true;
454 }
455 }
399 if (!Settings::IsGPULevelHigh()) { 456 if (!Settings::IsGPULevelHigh()) {
400 return buffer_cache.IsRegionGpuModified(addr, size); 457 return false;
458 }
459 if (True(which & VideoCommon::CacheType::TextureCache)) {
460 std::scoped_lock lock{texture_cache.mutex};
461 return texture_cache.IsRegionGpuModified(addr, size);
401 } 462 }
402 return texture_cache.IsRegionGpuModified(addr, size) || 463 return false;
403 buffer_cache.IsRegionGpuModified(addr, size);
404} 464}
405 465
406void RasterizerVulkan::InvalidateRegion(VAddr addr, u64 size) { 466void RasterizerVulkan::InvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType which) {
407 if (addr == 0 || size == 0) { 467 if (addr == 0 || size == 0) {
408 return; 468 return;
409 } 469 }
410 { 470 if (True(which & VideoCommon::CacheType::TextureCache)) {
411 std::scoped_lock lock{texture_cache.mutex}; 471 std::scoped_lock lock{texture_cache.mutex};
412 texture_cache.WriteMemory(addr, size); 472 texture_cache.WriteMemory(addr, size);
413 } 473 }
414 { 474 if ((True(which & VideoCommon::CacheType::BufferCache))) {
415 std::scoped_lock lock{buffer_cache.mutex}; 475 std::scoped_lock lock{buffer_cache.mutex};
416 buffer_cache.WriteMemory(addr, size); 476 buffer_cache.WriteMemory(addr, size);
417 } 477 }
418 pipeline_cache.InvalidateRegion(addr, size); 478 if ((True(which & VideoCommon::CacheType::QueryCache))) {
419 query_cache.InvalidateRegion(addr, size); 479 query_cache.InvalidateRegion(addr, size);
480 }
481 if ((True(which & VideoCommon::CacheType::ShaderCache))) {
482 pipeline_cache.InvalidateRegion(addr, size);
483 }
484}
485
486void RasterizerVulkan::InnerInvalidation(std::span<const std::pair<VAddr, std::size_t>> sequences) {
487 {
488 std::scoped_lock lock{texture_cache.mutex};
489 for (const auto& [addr, size] : sequences) {
490 texture_cache.WriteMemory(addr, size);
491 }
492 }
493 {
494 std::scoped_lock lock{buffer_cache.mutex};
495 for (const auto& [addr, size] : sequences) {
496 buffer_cache.WriteMemory(addr, size);
497 }
498 }
499 {
500 for (const auto& [addr, size] : sequences) {
501 query_cache.InvalidateRegion(addr, size);
502 pipeline_cache.InvalidateRegion(addr, size);
503 }
504 }
420} 505}
421 506
422void RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) { 507void RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) {
@@ -481,11 +566,12 @@ void RasterizerVulkan::ReleaseFences() {
481 fence_manager.WaitPendingFences(); 566 fence_manager.WaitPendingFences();
482} 567}
483 568
484void RasterizerVulkan::FlushAndInvalidateRegion(VAddr addr, u64 size) { 569void RasterizerVulkan::FlushAndInvalidateRegion(VAddr addr, u64 size,
570 VideoCommon::CacheType which) {
485 if (Settings::IsGPULevelExtreme()) { 571 if (Settings::IsGPULevelExtreme()) {
486 FlushRegion(addr, size); 572 FlushRegion(addr, size, which);
487 } 573 }
488 InvalidateRegion(addr, size); 574 InvalidateRegion(addr, size, which);
489} 575}
490 576
491void RasterizerVulkan::WaitForIdle() { 577void RasterizerVulkan::WaitForIdle() {
@@ -541,6 +627,21 @@ void RasterizerVulkan::TickFrame() {
541 } 627 }
542} 628}
543 629
630bool RasterizerVulkan::AccelerateConditionalRendering() {
631 if (Settings::IsGPULevelHigh()) {
632 // TODO(Blinkhawk): Reimplement Host conditional rendering.
633 return false;
634 }
635 // Medium / Low Hack: stub any checks on queries writen into the buffer cache.
636 const GPUVAddr condition_address{maxwell3d->regs.render_enable.Address()};
637 Maxwell::ReportSemaphore::Compare cmp;
638 if (gpu_memory->IsMemoryDirty(condition_address, sizeof(cmp),
639 VideoCommon::CacheType::BufferCache)) {
640 return true;
641 }
642 return false;
643}
644
544bool RasterizerVulkan::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, 645bool RasterizerVulkan::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src,
545 const Tegra::Engines::Fermi2D::Surface& dst, 646 const Tegra::Engines::Fermi2D::Surface& dst,
546 const Tegra::Engines::Fermi2D::Config& copy_config) { 647 const Tegra::Engines::Fermi2D::Config& copy_config) {
@@ -561,7 +662,7 @@ void RasterizerVulkan::AccelerateInlineToMemory(GPUVAddr address, size_t copy_si
561 } 662 }
562 gpu_memory->WriteBlockUnsafe(address, memory.data(), copy_size); 663 gpu_memory->WriteBlockUnsafe(address, memory.data(), copy_size);
563 { 664 {
564 std::unique_lock<std::mutex> lock{buffer_cache.mutex}; 665 std::unique_lock<std::recursive_mutex> lock{buffer_cache.mutex};
565 if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) { 666 if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) {
566 buffer_cache.WriteMemory(*cpu_addr, copy_size); 667 buffer_cache.WriteMemory(*cpu_addr, copy_size);
567 } 668 }
@@ -639,16 +740,35 @@ void RasterizerVulkan::UpdateDynamicStates() {
639 UpdateLineWidth(regs); 740 UpdateLineWidth(regs);
640 if (device.IsExtExtendedDynamicStateSupported()) { 741 if (device.IsExtExtendedDynamicStateSupported()) {
641 UpdateCullMode(regs); 742 UpdateCullMode(regs);
642 UpdateDepthBoundsTestEnable(regs);
643 UpdateDepthTestEnable(regs);
644 UpdateDepthWriteEnable(regs);
645 UpdateDepthCompareOp(regs); 743 UpdateDepthCompareOp(regs);
646 UpdateFrontFace(regs); 744 UpdateFrontFace(regs);
647 UpdateStencilOp(regs); 745 UpdateStencilOp(regs);
648 UpdateStencilTestEnable(regs); 746
649 if (device.IsExtVertexInputDynamicStateSupported()) { 747 if (device.IsExtVertexInputDynamicStateSupported()) {
650 UpdateVertexInput(regs); 748 UpdateVertexInput(regs);
651 } 749 }
750
751 if (state_tracker.TouchStateEnable()) {
752 UpdateDepthBoundsTestEnable(regs);
753 UpdateDepthTestEnable(regs);
754 UpdateDepthWriteEnable(regs);
755 UpdateStencilTestEnable(regs);
756 if (device.IsExtExtendedDynamicState2Supported()) {
757 UpdatePrimitiveRestartEnable(regs);
758 UpdateRasterizerDiscardEnable(regs);
759 UpdateDepthBiasEnable(regs);
760 }
761 if (device.IsExtExtendedDynamicState3EnablesSupported()) {
762 UpdateLogicOpEnable(regs);
763 UpdateDepthClampEnable(regs);
764 }
765 }
766 if (device.IsExtExtendedDynamicState2ExtrasSupported()) {
767 UpdateLogicOp(regs);
768 }
769 if (device.IsExtExtendedDynamicState3Supported()) {
770 UpdateBlending(regs);
771 }
652 } 772 }
653} 773}
654 774
@@ -789,32 +909,92 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs)
789 if (!state_tracker.TouchStencilProperties()) { 909 if (!state_tracker.TouchStencilProperties()) {
790 return; 910 return;
791 } 911 }
792 if (regs.stencil_two_side_enable) { 912 bool update_references = state_tracker.TouchStencilReference();
793 // Separate values per face 913 bool update_write_mask = state_tracker.TouchStencilWriteMask();
794 scheduler.Record( 914 bool update_compare_masks = state_tracker.TouchStencilCompare();
795 [front_ref = regs.stencil_front_ref, front_write_mask = regs.stencil_front_mask, 915 if (state_tracker.TouchStencilSide(regs.stencil_two_side_enable != 0)) {
796 front_test_mask = regs.stencil_front_func_mask, back_ref = regs.stencil_back_ref, 916 update_references = true;
797 back_write_mask = regs.stencil_back_mask, 917 update_write_mask = true;
798 back_test_mask = regs.stencil_back_func_mask](vk::CommandBuffer cmdbuf) { 918 update_compare_masks = true;
919 }
920 if (update_references) {
921 [&]() {
922 if (regs.stencil_two_side_enable) {
923 if (!state_tracker.CheckStencilReferenceFront(regs.stencil_front_ref) &&
924 !state_tracker.CheckStencilReferenceBack(regs.stencil_back_ref)) {
925 return;
926 }
927 } else {
928 if (!state_tracker.CheckStencilReferenceFront(regs.stencil_front_ref)) {
929 return;
930 }
931 }
932 scheduler.Record([front_ref = regs.stencil_front_ref, back_ref = regs.stencil_back_ref,
933 two_sided = regs.stencil_two_side_enable](vk::CommandBuffer cmdbuf) {
934 const bool set_back = two_sided && front_ref != back_ref;
799 // Front face 935 // Front face
800 cmdbuf.SetStencilReference(VK_STENCIL_FACE_FRONT_BIT, front_ref); 936 cmdbuf.SetStencilReference(set_back ? VK_STENCIL_FACE_FRONT_BIT
801 cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_FRONT_BIT, front_write_mask); 937 : VK_STENCIL_FACE_FRONT_AND_BACK,
802 cmdbuf.SetStencilCompareMask(VK_STENCIL_FACE_FRONT_BIT, front_test_mask); 938 front_ref);
803 939 if (set_back) {
804 // Back face 940 cmdbuf.SetStencilReference(VK_STENCIL_FACE_BACK_BIT, back_ref);
805 cmdbuf.SetStencilReference(VK_STENCIL_FACE_BACK_BIT, back_ref); 941 }
806 cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_BACK_BIT, back_write_mask);
807 cmdbuf.SetStencilCompareMask(VK_STENCIL_FACE_BACK_BIT, back_test_mask);
808 }); 942 });
809 } else { 943 }();
810 // Front face defines both faces 944 }
811 scheduler.Record([ref = regs.stencil_front_ref, write_mask = regs.stencil_front_mask, 945 if (update_write_mask) {
812 test_mask = regs.stencil_front_func_mask](vk::CommandBuffer cmdbuf) { 946 [&]() {
813 cmdbuf.SetStencilReference(VK_STENCIL_FACE_FRONT_AND_BACK, ref); 947 if (regs.stencil_two_side_enable) {
814 cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_FRONT_AND_BACK, write_mask); 948 if (!state_tracker.CheckStencilWriteMaskFront(regs.stencil_front_mask) &&
815 cmdbuf.SetStencilCompareMask(VK_STENCIL_FACE_FRONT_AND_BACK, test_mask); 949 !state_tracker.CheckStencilWriteMaskBack(regs.stencil_back_mask)) {
816 }); 950 return;
951 }
952 } else {
953 if (!state_tracker.CheckStencilWriteMaskFront(regs.stencil_front_mask)) {
954 return;
955 }
956 }
957 scheduler.Record([front_write_mask = regs.stencil_front_mask,
958 back_write_mask = regs.stencil_back_mask,
959 two_sided = regs.stencil_two_side_enable](vk::CommandBuffer cmdbuf) {
960 const bool set_back = two_sided && front_write_mask != back_write_mask;
961 // Front face
962 cmdbuf.SetStencilWriteMask(set_back ? VK_STENCIL_FACE_FRONT_BIT
963 : VK_STENCIL_FACE_FRONT_AND_BACK,
964 front_write_mask);
965 if (set_back) {
966 cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_BACK_BIT, back_write_mask);
967 }
968 });
969 }();
970 }
971 if (update_compare_masks) {
972 [&]() {
973 if (regs.stencil_two_side_enable) {
974 if (!state_tracker.CheckStencilCompareMaskFront(regs.stencil_front_func_mask) &&
975 !state_tracker.CheckStencilCompareMaskBack(regs.stencil_back_func_mask)) {
976 return;
977 }
978 } else {
979 if (!state_tracker.CheckStencilCompareMaskFront(regs.stencil_front_func_mask)) {
980 return;
981 }
982 }
983 scheduler.Record([front_test_mask = regs.stencil_front_func_mask,
984 back_test_mask = regs.stencil_back_func_mask,
985 two_sided = regs.stencil_two_side_enable](vk::CommandBuffer cmdbuf) {
986 const bool set_back = two_sided && front_test_mask != back_test_mask;
987 // Front face
988 cmdbuf.SetStencilCompareMask(set_back ? VK_STENCIL_FACE_FRONT_BIT
989 : VK_STENCIL_FACE_FRONT_AND_BACK,
990 front_test_mask);
991 if (set_back) {
992 cmdbuf.SetStencilCompareMask(VK_STENCIL_FACE_BACK_BIT, back_test_mask);
993 }
994 });
995 }();
817 } 996 }
997 state_tracker.ClearStencilReset();
818} 998}
819 999
820void RasterizerVulkan::UpdateLineWidth(Tegra::Engines::Maxwell3D::Regs& regs) { 1000void RasterizerVulkan::UpdateLineWidth(Tegra::Engines::Maxwell3D::Regs& regs) {
@@ -868,6 +1048,82 @@ void RasterizerVulkan::UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& r
868 }); 1048 });
869} 1049}
870 1050
1051void RasterizerVulkan::UpdatePrimitiveRestartEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
1052 if (!state_tracker.TouchPrimitiveRestartEnable()) {
1053 return;
1054 }
1055 scheduler.Record([enable = regs.primitive_restart.enabled](vk::CommandBuffer cmdbuf) {
1056 cmdbuf.SetPrimitiveRestartEnableEXT(enable);
1057 });
1058}
1059
1060void RasterizerVulkan::UpdateRasterizerDiscardEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
1061 if (!state_tracker.TouchRasterizerDiscardEnable()) {
1062 return;
1063 }
1064 scheduler.Record([disable = regs.rasterize_enable](vk::CommandBuffer cmdbuf) {
1065 cmdbuf.SetRasterizerDiscardEnableEXT(disable == 0);
1066 });
1067}
1068
1069void RasterizerVulkan::UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
1070 if (!state_tracker.TouchDepthBiasEnable()) {
1071 return;
1072 }
1073 constexpr size_t POINT = 0;
1074 constexpr size_t LINE = 1;
1075 constexpr size_t POLYGON = 2;
1076 static constexpr std::array POLYGON_OFFSET_ENABLE_LUT = {
1077 POINT, // Points
1078 LINE, // Lines
1079 LINE, // LineLoop
1080 LINE, // LineStrip
1081 POLYGON, // Triangles
1082 POLYGON, // TriangleStrip
1083 POLYGON, // TriangleFan
1084 POLYGON, // Quads
1085 POLYGON, // QuadStrip
1086 POLYGON, // Polygon
1087 LINE, // LinesAdjacency
1088 LINE, // LineStripAdjacency
1089 POLYGON, // TrianglesAdjacency
1090 POLYGON, // TriangleStripAdjacency
1091 POLYGON, // Patches
1092 };
1093 const std::array enabled_lut{
1094 regs.polygon_offset_point_enable,
1095 regs.polygon_offset_line_enable,
1096 regs.polygon_offset_fill_enable,
1097 };
1098 const u32 topology_index = static_cast<u32>(maxwell3d->draw_manager->GetDrawState().topology);
1099 const u32 enable = enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]];
1100 scheduler.Record(
1101 [enable](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthBiasEnableEXT(enable != 0); });
1102}
1103
1104void RasterizerVulkan::UpdateLogicOpEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
1105 if (!state_tracker.TouchLogicOpEnable()) {
1106 return;
1107 }
1108 scheduler.Record([enable = regs.logic_op.enable](vk::CommandBuffer cmdbuf) {
1109 cmdbuf.SetLogicOpEnableEXT(enable != 0);
1110 });
1111}
1112
1113void RasterizerVulkan::UpdateDepthClampEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
1114 if (!state_tracker.TouchDepthClampEnable()) {
1115 return;
1116 }
1117 bool is_enabled = !(regs.viewport_clip_control.geometry_clip ==
1118 Maxwell::ViewportClipControl::GeometryClip::Passthrough ||
1119 regs.viewport_clip_control.geometry_clip ==
1120 Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ ||
1121 regs.viewport_clip_control.geometry_clip ==
1122 Maxwell::ViewportClipControl::GeometryClip::FrustumZ);
1123 scheduler.Record(
1124 [is_enabled](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthClampEnableEXT(is_enabled); });
1125}
1126
871void RasterizerVulkan::UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs) { 1127void RasterizerVulkan::UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs) {
872 if (!state_tracker.TouchDepthCompareOp()) { 1128 if (!state_tracker.TouchDepthCompareOp()) {
873 return; 1129 return;
@@ -925,6 +1181,78 @@ void RasterizerVulkan::UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs) {
925 } 1181 }
926} 1182}
927 1183
1184void RasterizerVulkan::UpdateLogicOp(Tegra::Engines::Maxwell3D::Regs& regs) {
1185 if (!state_tracker.TouchLogicOp()) {
1186 return;
1187 }
1188 const auto op_value = static_cast<u32>(regs.logic_op.op);
1189 auto op = op_value >= 0x1500 && op_value < 0x1510 ? static_cast<VkLogicOp>(op_value - 0x1500)
1190 : VK_LOGIC_OP_NO_OP;
1191 scheduler.Record([op](vk::CommandBuffer cmdbuf) { cmdbuf.SetLogicOpEXT(op); });
1192}
1193
1194void RasterizerVulkan::UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs) {
1195 if (!state_tracker.TouchBlending()) {
1196 return;
1197 }
1198
1199 if (state_tracker.TouchColorMask()) {
1200 std::array<VkColorComponentFlags, Maxwell::NumRenderTargets> setup_masks{};
1201 for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) {
1202 const auto& mask = regs.color_mask[regs.color_mask_common ? 0 : index];
1203 auto& current = setup_masks[index];
1204 if (mask.R) {
1205 current |= VK_COLOR_COMPONENT_R_BIT;
1206 }
1207 if (mask.G) {
1208 current |= VK_COLOR_COMPONENT_G_BIT;
1209 }
1210 if (mask.B) {
1211 current |= VK_COLOR_COMPONENT_B_BIT;
1212 }
1213 if (mask.A) {
1214 current |= VK_COLOR_COMPONENT_A_BIT;
1215 }
1216 }
1217 scheduler.Record([setup_masks](vk::CommandBuffer cmdbuf) {
1218 cmdbuf.SetColorWriteMaskEXT(0, setup_masks);
1219 });
1220 }
1221
1222 if (state_tracker.TouchBlendEnable()) {
1223 std::array<VkBool32, Maxwell::NumRenderTargets> setup_enables{};
1224 std::ranges::transform(
1225 regs.blend.enable, setup_enables.begin(),
1226 [&](const auto& is_enabled) { return is_enabled != 0 ? VK_TRUE : VK_FALSE; });
1227 scheduler.Record([setup_enables](vk::CommandBuffer cmdbuf) {
1228 cmdbuf.SetColorBlendEnableEXT(0, setup_enables);
1229 });
1230 }
1231
1232 if (state_tracker.TouchBlendEquations()) {
1233 std::array<VkColorBlendEquationEXT, Maxwell::NumRenderTargets> setup_blends{};
1234 for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) {
1235 const auto blend_setup = [&]<typename T>(const T& guest_blend) {
1236 auto& host_blend = setup_blends[index];
1237 host_blend.srcColorBlendFactor = MaxwellToVK::BlendFactor(guest_blend.color_source);
1238 host_blend.dstColorBlendFactor = MaxwellToVK::BlendFactor(guest_blend.color_dest);
1239 host_blend.colorBlendOp = MaxwellToVK::BlendEquation(guest_blend.color_op);
1240 host_blend.srcAlphaBlendFactor = MaxwellToVK::BlendFactor(guest_blend.alpha_source);
1241 host_blend.dstAlphaBlendFactor = MaxwellToVK::BlendFactor(guest_blend.alpha_dest);
1242 host_blend.alphaBlendOp = MaxwellToVK::BlendEquation(guest_blend.alpha_op);
1243 };
1244 if (!regs.blend_per_target_enabled) {
1245 blend_setup(regs.blend);
1246 continue;
1247 }
1248 blend_setup(regs.blend_per_target[index]);
1249 }
1250 scheduler.Record([setup_blends](vk::CommandBuffer cmdbuf) {
1251 cmdbuf.SetColorBlendEquationEXT(0, setup_blends);
1252 });
1253 }
1254}
1255
928void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) { 1256void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
929 if (!state_tracker.TouchStencilTestEnable()) { 1257 if (!state_tracker.TouchStencilTestEnable()) {
930 return; 1258 return;
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index ee483cfd9..472cc64d9 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -65,6 +65,7 @@ public:
65 ~RasterizerVulkan() override; 65 ~RasterizerVulkan() override;
66 66
67 void Draw(bool is_indexed, u32 instance_count) override; 67 void Draw(bool is_indexed, u32 instance_count) override;
68 void DrawIndirect() override;
68 void Clear(u32 layer_count) override; 69 void Clear(u32 layer_count) override;
69 void DispatchCompute() override; 70 void DispatchCompute() override;
70 void ResetCounter(VideoCore::QueryType type) override; 71 void ResetCounter(VideoCore::QueryType type) override;
@@ -72,9 +73,13 @@ public:
72 void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override; 73 void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override;
73 void DisableGraphicsUniformBuffer(size_t stage, u32 index) override; 74 void DisableGraphicsUniformBuffer(size_t stage, u32 index) override;
74 void FlushAll() override; 75 void FlushAll() override;
75 void FlushRegion(VAddr addr, u64 size) override; 76 void FlushRegion(VAddr addr, u64 size,
76 bool MustFlushRegion(VAddr addr, u64 size) override; 77 VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
77 void InvalidateRegion(VAddr addr, u64 size) override; 78 bool MustFlushRegion(VAddr addr, u64 size,
79 VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
80 void InvalidateRegion(VAddr addr, u64 size,
81 VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
82 void InnerInvalidation(std::span<const std::pair<VAddr, std::size_t>> sequences) override;
78 void OnCPUWrite(VAddr addr, u64 size) override; 83 void OnCPUWrite(VAddr addr, u64 size) override;
79 void InvalidateGPUCache() override; 84 void InvalidateGPUCache() override;
80 void UnmapMemory(VAddr addr, u64 size) override; 85 void UnmapMemory(VAddr addr, u64 size) override;
@@ -84,12 +89,14 @@ public:
84 void SignalSyncPoint(u32 value) override; 89 void SignalSyncPoint(u32 value) override;
85 void SignalReference() override; 90 void SignalReference() override;
86 void ReleaseFences() override; 91 void ReleaseFences() override;
87 void FlushAndInvalidateRegion(VAddr addr, u64 size) override; 92 void FlushAndInvalidateRegion(
93 VAddr addr, u64 size, VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
88 void WaitForIdle() override; 94 void WaitForIdle() override;
89 void FragmentBarrier() override; 95 void FragmentBarrier() override;
90 void TiledCacheBarrier() override; 96 void TiledCacheBarrier() override;
91 void FlushCommands() override; 97 void FlushCommands() override;
92 void TickFrame() override; 98 void TickFrame() override;
99 bool AccelerateConditionalRendering() override;
93 bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, 100 bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src,
94 const Tegra::Engines::Fermi2D::Surface& dst, 101 const Tegra::Engines::Fermi2D::Surface& dst,
95 const Tegra::Engines::Fermi2D::Config& copy_config) override; 102 const Tegra::Engines::Fermi2D::Config& copy_config) override;
@@ -114,6 +121,9 @@ private:
114 121
115 static constexpr VkDeviceSize DEFAULT_BUFFER_SIZE = 4 * sizeof(float); 122 static constexpr VkDeviceSize DEFAULT_BUFFER_SIZE = 4 * sizeof(float);
116 123
124 template <typename Func>
125 void PrepareDraw(bool is_indexed, Func&&);
126
117 void FlushWork(); 127 void FlushWork();
118 128
119 void UpdateDynamicStates(); 129 void UpdateDynamicStates();
@@ -135,9 +145,16 @@ private:
135 void UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); 145 void UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
136 void UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs); 146 void UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs);
137 void UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs); 147 void UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs);
148 void UpdatePrimitiveRestartEnable(Tegra::Engines::Maxwell3D::Regs& regs);
149 void UpdateRasterizerDiscardEnable(Tegra::Engines::Maxwell3D::Regs& regs);
150 void UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& regs);
151 void UpdateLogicOpEnable(Tegra::Engines::Maxwell3D::Regs& regs);
152 void UpdateDepthClampEnable(Tegra::Engines::Maxwell3D::Regs& regs);
138 void UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs); 153 void UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs);
139 void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs); 154 void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs);
140 void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); 155 void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
156 void UpdateLogicOp(Tegra::Engines::Maxwell3D::Regs& regs);
157 void UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs);
141 158
142 void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs); 159 void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs);
143 160
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
index 06f68d09a..74ca77216 100644
--- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
+++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
@@ -1,5 +1,5 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#include <algorithm> 4#include <algorithm>
5#include <utility> 5#include <utility>
@@ -94,7 +94,8 @@ StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& mem
94 .flags = 0, 94 .flags = 0,
95 .size = STREAM_BUFFER_SIZE, 95 .size = STREAM_BUFFER_SIZE,
96 .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | 96 .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
97 VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, 97 VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
98 VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT,
98 .sharingMode = VK_SHARING_MODE_EXCLUSIVE, 99 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
99 .queueFamilyIndexCount = 0, 100 .queueFamilyIndexCount = 0,
100 .pQueueFamilyIndices = nullptr, 101 .pQueueFamilyIndices = nullptr,
@@ -142,11 +143,23 @@ StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& mem
142 143
143StagingBufferPool::~StagingBufferPool() = default; 144StagingBufferPool::~StagingBufferPool() = default;
144 145
145StagingBufferRef StagingBufferPool::Request(size_t size, MemoryUsage usage) { 146StagingBufferRef StagingBufferPool::Request(size_t size, MemoryUsage usage, bool deferred) {
146 if (usage == MemoryUsage::Upload && size <= MAX_STREAM_BUFFER_REQUEST_SIZE) { 147 if (!deferred && usage == MemoryUsage::Upload && size <= MAX_STREAM_BUFFER_REQUEST_SIZE) {
147 return GetStreamBuffer(size); 148 return GetStreamBuffer(size);
148 } 149 }
149 return GetStagingBuffer(size, usage); 150 return GetStagingBuffer(size, usage, deferred);
151}
152
153void StagingBufferPool::FreeDeferred(StagingBufferRef& ref) {
154 auto& entries = GetCache(ref.usage)[ref.log2_level].entries;
155 const auto is_this_one = [&ref](const StagingBuffer& entry) {
156 return entry.index == ref.index;
157 };
158 auto it = std::find_if(entries.begin(), entries.end(), is_this_one);
159 ASSERT(it != entries.end());
160 ASSERT(it->deferred);
161 it->tick = scheduler.CurrentTick();
162 it->deferred = false;
150} 163}
151 164
152void StagingBufferPool::TickFrame() { 165void StagingBufferPool::TickFrame() {
@@ -187,6 +200,9 @@ StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) {
187 .buffer = *stream_buffer, 200 .buffer = *stream_buffer,
188 .offset = static_cast<VkDeviceSize>(offset), 201 .offset = static_cast<VkDeviceSize>(offset),
189 .mapped_span = std::span<u8>(stream_pointer + offset, size), 202 .mapped_span = std::span<u8>(stream_pointer + offset, size),
203 .usage{},
204 .log2_level{},
205 .index{},
190 }; 206 };
191} 207}
192 208
@@ -196,19 +212,21 @@ bool StagingBufferPool::AreRegionsActive(size_t region_begin, size_t region_end)
196 [gpu_tick](u64 sync_tick) { return gpu_tick < sync_tick; }); 212 [gpu_tick](u64 sync_tick) { return gpu_tick < sync_tick; });
197}; 213};
198 214
199StagingBufferRef StagingBufferPool::GetStagingBuffer(size_t size, MemoryUsage usage) { 215StagingBufferRef StagingBufferPool::GetStagingBuffer(size_t size, MemoryUsage usage,
200 if (const std::optional<StagingBufferRef> ref = TryGetReservedBuffer(size, usage)) { 216 bool deferred) {
217 if (const std::optional<StagingBufferRef> ref = TryGetReservedBuffer(size, usage, deferred)) {
201 return *ref; 218 return *ref;
202 } 219 }
203 return CreateStagingBuffer(size, usage); 220 return CreateStagingBuffer(size, usage, deferred);
204} 221}
205 222
206std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t size, 223std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t size,
207 MemoryUsage usage) { 224 MemoryUsage usage,
225 bool deferred) {
208 StagingBuffers& cache_level = GetCache(usage)[Common::Log2Ceil64(size)]; 226 StagingBuffers& cache_level = GetCache(usage)[Common::Log2Ceil64(size)];
209 227
210 const auto is_free = [this](const StagingBuffer& entry) { 228 const auto is_free = [this](const StagingBuffer& entry) {
211 return scheduler.IsFree(entry.tick); 229 return !entry.deferred && scheduler.IsFree(entry.tick);
212 }; 230 };
213 auto& entries = cache_level.entries; 231 auto& entries = cache_level.entries;
214 const auto hint_it = entries.begin() + cache_level.iterate_index; 232 const auto hint_it = entries.begin() + cache_level.iterate_index;
@@ -220,11 +238,14 @@ std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t s
220 } 238 }
221 } 239 }
222 cache_level.iterate_index = std::distance(entries.begin(), it) + 1; 240 cache_level.iterate_index = std::distance(entries.begin(), it) + 1;
223 it->tick = scheduler.CurrentTick(); 241 it->tick = deferred ? std::numeric_limits<u64>::max() : scheduler.CurrentTick();
242 ASSERT(!it->deferred);
243 it->deferred = deferred;
224 return it->Ref(); 244 return it->Ref();
225} 245}
226 246
227StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage usage) { 247StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage usage,
248 bool deferred) {
228 const u32 log2 = Common::Log2Ceil64(size); 249 const u32 log2 = Common::Log2Ceil64(size);
229 vk::Buffer buffer = device.GetLogical().CreateBuffer({ 250 vk::Buffer buffer = device.GetLogical().CreateBuffer({
230 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 251 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
@@ -233,7 +254,8 @@ StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage
233 .size = 1ULL << log2, 254 .size = 1ULL << log2,
234 .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | 255 .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
235 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | 256 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
236 VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, 257 VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
258 VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT,
237 .sharingMode = VK_SHARING_MODE_EXCLUSIVE, 259 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
238 .queueFamilyIndexCount = 0, 260 .queueFamilyIndexCount = 0,
239 .pQueueFamilyIndices = nullptr, 261 .pQueueFamilyIndices = nullptr,
@@ -249,7 +271,11 @@ StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage
249 .buffer = std::move(buffer), 271 .buffer = std::move(buffer),
250 .commit = std::move(commit), 272 .commit = std::move(commit),
251 .mapped_span = mapped_span, 273 .mapped_span = mapped_span,
252 .tick = scheduler.CurrentTick(), 274 .usage = usage,
275 .log2_level = log2,
276 .index = unique_ids++,
277 .tick = deferred ? std::numeric_limits<u64>::max() : scheduler.CurrentTick(),
278 .deferred = deferred,
253 }); 279 });
254 return entry.Ref(); 280 return entry.Ref();
255} 281}
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
index 91dc84da8..4fd15f11a 100644
--- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
+++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
@@ -1,5 +1,5 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#pragma once 4#pragma once
5 5
@@ -20,6 +20,9 @@ struct StagingBufferRef {
20 VkBuffer buffer; 20 VkBuffer buffer;
21 VkDeviceSize offset; 21 VkDeviceSize offset;
22 std::span<u8> mapped_span; 22 std::span<u8> mapped_span;
23 MemoryUsage usage;
24 u32 log2_level;
25 u64 index;
23}; 26};
24 27
25class StagingBufferPool { 28class StagingBufferPool {
@@ -30,7 +33,8 @@ public:
30 Scheduler& scheduler); 33 Scheduler& scheduler);
31 ~StagingBufferPool(); 34 ~StagingBufferPool();
32 35
33 StagingBufferRef Request(size_t size, MemoryUsage usage); 36 StagingBufferRef Request(size_t size, MemoryUsage usage, bool deferred = false);
37 void FreeDeferred(StagingBufferRef& ref);
34 38
35 void TickFrame(); 39 void TickFrame();
36 40
@@ -44,13 +48,20 @@ private:
44 vk::Buffer buffer; 48 vk::Buffer buffer;
45 MemoryCommit commit; 49 MemoryCommit commit;
46 std::span<u8> mapped_span; 50 std::span<u8> mapped_span;
51 MemoryUsage usage;
52 u32 log2_level;
53 u64 index;
47 u64 tick = 0; 54 u64 tick = 0;
55 bool deferred{};
48 56
49 StagingBufferRef Ref() const noexcept { 57 StagingBufferRef Ref() const noexcept {
50 return { 58 return {
51 .buffer = *buffer, 59 .buffer = *buffer,
52 .offset = 0, 60 .offset = 0,
53 .mapped_span = mapped_span, 61 .mapped_span = mapped_span,
62 .usage = usage,
63 .log2_level = log2_level,
64 .index = index,
54 }; 65 };
55 } 66 }
56 }; 67 };
@@ -68,11 +79,12 @@ private:
68 79
69 bool AreRegionsActive(size_t region_begin, size_t region_end) const; 80 bool AreRegionsActive(size_t region_begin, size_t region_end) const;
70 81
71 StagingBufferRef GetStagingBuffer(size_t size, MemoryUsage usage); 82 StagingBufferRef GetStagingBuffer(size_t size, MemoryUsage usage, bool deferred = false);
72 83
73 std::optional<StagingBufferRef> TryGetReservedBuffer(size_t size, MemoryUsage usage); 84 std::optional<StagingBufferRef> TryGetReservedBuffer(size_t size, MemoryUsage usage,
85 bool deferred);
74 86
75 StagingBufferRef CreateStagingBuffer(size_t size, MemoryUsage usage); 87 StagingBufferRef CreateStagingBuffer(size_t size, MemoryUsage usage, bool deferred);
76 88
77 StagingBuffersCache& GetCache(MemoryUsage usage); 89 StagingBuffersCache& GetCache(MemoryUsage usage);
78 90
@@ -99,6 +111,7 @@ private:
99 111
100 size_t current_delete_level = 0; 112 size_t current_delete_level = 0;
101 u64 buffer_index = 0; 113 u64 buffer_index = 0;
114 u64 unique_ids{};
102}; 115};
103 116
104} // namespace Vulkan 117} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
index edb41b171..d56558a83 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
@@ -27,10 +27,37 @@ using Flags = Maxwell3D::DirtyState::Flags;
27 27
28Flags MakeInvalidationFlags() { 28Flags MakeInvalidationFlags() {
29 static constexpr int INVALIDATION_FLAGS[]{ 29 static constexpr int INVALIDATION_FLAGS[]{
30 Viewports, Scissors, DepthBias, BlendConstants, DepthBounds, 30 Viewports,
31 StencilProperties, LineWidth, CullMode, DepthBoundsEnable, DepthTestEnable, 31 Scissors,
32 DepthWriteEnable, DepthCompareOp, FrontFace, StencilOp, StencilTestEnable, 32 DepthBias,
33 VertexBuffers, VertexInput, 33 BlendConstants,
34 DepthBounds,
35 StencilProperties,
36 StencilReference,
37 StencilWriteMask,
38 StencilCompare,
39 LineWidth,
40 CullMode,
41 DepthBoundsEnable,
42 DepthTestEnable,
43 DepthWriteEnable,
44 DepthCompareOp,
45 FrontFace,
46 StencilOp,
47 StencilTestEnable,
48 VertexBuffers,
49 VertexInput,
50 StateEnable,
51 PrimitiveRestartEnable,
52 RasterizerDiscardEnable,
53 DepthBiasEnable,
54 LogicOpEnable,
55 DepthClampEnable,
56 LogicOp,
57 Blending,
58 ColorMask,
59 BlendEquations,
60 BlendEnable,
34 }; 61 };
35 Flags flags{}; 62 Flags flags{};
36 for (const int flag : INVALIDATION_FLAGS) { 63 for (const int flag : INVALIDATION_FLAGS) {
@@ -75,14 +102,17 @@ void SetupDirtyDepthBounds(Tables& tables) {
75} 102}
76 103
77void SetupDirtyStencilProperties(Tables& tables) { 104void SetupDirtyStencilProperties(Tables& tables) {
78 auto& table = tables[0]; 105 const auto setup = [&](size_t position, u8 flag) {
79 table[OFF(stencil_two_side_enable)] = StencilProperties; 106 tables[0][position] = flag;
80 table[OFF(stencil_front_ref)] = StencilProperties; 107 tables[1][position] = StencilProperties;
81 table[OFF(stencil_front_mask)] = StencilProperties; 108 };
82 table[OFF(stencil_front_func_mask)] = StencilProperties; 109 tables[0][OFF(stencil_two_side_enable)] = StencilProperties;
83 table[OFF(stencil_back_ref)] = StencilProperties; 110 setup(OFF(stencil_front_ref), StencilReference);
84 table[OFF(stencil_back_mask)] = StencilProperties; 111 setup(OFF(stencil_front_mask), StencilWriteMask);
85 table[OFF(stencil_back_func_mask)] = StencilProperties; 112 setup(OFF(stencil_front_func_mask), StencilCompare);
113 setup(OFF(stencil_back_ref), StencilReference);
114 setup(OFF(stencil_back_mask), StencilWriteMask);
115 setup(OFF(stencil_back_func_mask), StencilCompare);
86} 116}
87 117
88void SetupDirtyLineWidth(Tables& tables) { 118void SetupDirtyLineWidth(Tables& tables) {
@@ -96,16 +126,22 @@ void SetupDirtyCullMode(Tables& tables) {
96 table[OFF(gl_cull_test_enabled)] = CullMode; 126 table[OFF(gl_cull_test_enabled)] = CullMode;
97} 127}
98 128
99void SetupDirtyDepthBoundsEnable(Tables& tables) { 129void SetupDirtyStateEnable(Tables& tables) {
100 tables[0][OFF(depth_bounds_enable)] = DepthBoundsEnable; 130 const auto setup = [&](size_t position, u8 flag) {
101} 131 tables[0][position] = flag;
102 132 tables[1][position] = StateEnable;
103void SetupDirtyDepthTestEnable(Tables& tables) { 133 };
104 tables[0][OFF(depth_test_enable)] = DepthTestEnable; 134 setup(OFF(depth_bounds_enable), DepthBoundsEnable);
105} 135 setup(OFF(depth_test_enable), DepthTestEnable);
106 136 setup(OFF(depth_write_enabled), DepthWriteEnable);
107void SetupDirtyDepthWriteEnable(Tables& tables) { 137 setup(OFF(stencil_enable), StencilTestEnable);
108 tables[0][OFF(depth_write_enabled)] = DepthWriteEnable; 138 setup(OFF(primitive_restart.enabled), PrimitiveRestartEnable);
139 setup(OFF(rasterize_enable), RasterizerDiscardEnable);
140 setup(OFF(polygon_offset_point_enable), DepthBiasEnable);
141 setup(OFF(polygon_offset_line_enable), DepthBiasEnable);
142 setup(OFF(polygon_offset_fill_enable), DepthBiasEnable);
143 setup(OFF(logic_op.enable), LogicOpEnable);
144 setup(OFF(viewport_clip_control.geometry_clip), DepthClampEnable);
109} 145}
110 146
111void SetupDirtyDepthCompareOp(Tables& tables) { 147void SetupDirtyDepthCompareOp(Tables& tables) {
@@ -133,16 +169,22 @@ void SetupDirtyStencilOp(Tables& tables) {
133 tables[1][OFF(stencil_two_side_enable)] = StencilOp; 169 tables[1][OFF(stencil_two_side_enable)] = StencilOp;
134} 170}
135 171
136void SetupDirtyStencilTestEnable(Tables& tables) {
137 tables[0][OFF(stencil_enable)] = StencilTestEnable;
138}
139
140void SetupDirtyBlending(Tables& tables) { 172void SetupDirtyBlending(Tables& tables) {
141 tables[0][OFF(color_mask_common)] = Blending; 173 tables[0][OFF(color_mask_common)] = Blending;
174 tables[1][OFF(color_mask_common)] = ColorMask;
142 tables[0][OFF(blend_per_target_enabled)] = Blending; 175 tables[0][OFF(blend_per_target_enabled)] = Blending;
176 tables[1][OFF(blend_per_target_enabled)] = BlendEquations;
143 FillBlock(tables[0], OFF(color_mask), NUM(color_mask), Blending); 177 FillBlock(tables[0], OFF(color_mask), NUM(color_mask), Blending);
178 FillBlock(tables[1], OFF(color_mask), NUM(color_mask), ColorMask);
144 FillBlock(tables[0], OFF(blend), NUM(blend), Blending); 179 FillBlock(tables[0], OFF(blend), NUM(blend), Blending);
180 FillBlock(tables[1], OFF(blend), NUM(blend), BlendEquations);
181 FillBlock(tables[1], OFF(blend.enable), NUM(blend.enable), BlendEnable);
145 FillBlock(tables[0], OFF(blend_per_target), NUM(blend_per_target), Blending); 182 FillBlock(tables[0], OFF(blend_per_target), NUM(blend_per_target), Blending);
183 FillBlock(tables[1], OFF(blend_per_target), NUM(blend_per_target), BlendEquations);
184}
185
186void SetupDirtySpecialOps(Tables& tables) {
187 tables[0][OFF(logic_op.op)] = LogicOp;
146} 188}
147 189
148void SetupDirtyViewportSwizzles(Tables& tables) { 190void SetupDirtyViewportSwizzles(Tables& tables) {
@@ -185,17 +227,15 @@ void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) {
185 SetupDirtyStencilProperties(tables); 227 SetupDirtyStencilProperties(tables);
186 SetupDirtyLineWidth(tables); 228 SetupDirtyLineWidth(tables);
187 SetupDirtyCullMode(tables); 229 SetupDirtyCullMode(tables);
188 SetupDirtyDepthBoundsEnable(tables); 230 SetupDirtyStateEnable(tables);
189 SetupDirtyDepthTestEnable(tables);
190 SetupDirtyDepthWriteEnable(tables);
191 SetupDirtyDepthCompareOp(tables); 231 SetupDirtyDepthCompareOp(tables);
192 SetupDirtyFrontFace(tables); 232 SetupDirtyFrontFace(tables);
193 SetupDirtyStencilOp(tables); 233 SetupDirtyStencilOp(tables);
194 SetupDirtyStencilTestEnable(tables);
195 SetupDirtyBlending(tables); 234 SetupDirtyBlending(tables);
196 SetupDirtyViewportSwizzles(tables); 235 SetupDirtyViewportSwizzles(tables);
197 SetupDirtyVertexAttributes(tables); 236 SetupDirtyVertexAttributes(tables);
198 SetupDirtyVertexBindings(tables); 237 SetupDirtyVertexBindings(tables);
238 SetupDirtySpecialOps(tables);
199} 239}
200 240
201void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) { 241void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) {
@@ -204,6 +244,8 @@ void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) {
204 244
205void StateTracker::InvalidateState() { 245void StateTracker::InvalidateState() {
206 flags->set(); 246 flags->set();
247 current_topology = INVALID_TOPOLOGY;
248 stencil_reset = true;
207} 249}
208 250
209StateTracker::StateTracker() 251StateTracker::StateTracker()
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h
index 2296dea60..8010ad26c 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.h
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.h
@@ -35,6 +35,9 @@ enum : u8 {
35 BlendConstants, 35 BlendConstants,
36 DepthBounds, 36 DepthBounds,
37 StencilProperties, 37 StencilProperties,
38 StencilReference,
39 StencilWriteMask,
40 StencilCompare,
38 LineWidth, 41 LineWidth,
39 42
40 CullMode, 43 CullMode,
@@ -45,8 +48,18 @@ enum : u8 {
45 FrontFace, 48 FrontFace,
46 StencilOp, 49 StencilOp,
47 StencilTestEnable, 50 StencilTestEnable,
51 PrimitiveRestartEnable,
52 RasterizerDiscardEnable,
53 DepthBiasEnable,
54 StateEnable,
55 LogicOp,
56 LogicOpEnable,
57 DepthClampEnable,
48 58
49 Blending, 59 Blending,
60 BlendEnable,
61 BlendEquations,
62 ColorMask,
50 ViewportSwizzles, 63 ViewportSwizzles,
51 64
52 Last, 65 Last,
@@ -64,6 +77,7 @@ public:
64 void InvalidateCommandBufferState() { 77 void InvalidateCommandBufferState() {
65 (*flags) |= invalidation_flags; 78 (*flags) |= invalidation_flags;
66 current_topology = INVALID_TOPOLOGY; 79 current_topology = INVALID_TOPOLOGY;
80 stencil_reset = true;
67 } 81 }
68 82
69 void InvalidateViewports() { 83 void InvalidateViewports() {
@@ -103,6 +117,57 @@ public:
103 return Exchange(Dirty::StencilProperties, false); 117 return Exchange(Dirty::StencilProperties, false);
104 } 118 }
105 119
120 bool TouchStencilReference() {
121 return Exchange(Dirty::StencilReference, false);
122 }
123
124 bool TouchStencilWriteMask() {
125 return Exchange(Dirty::StencilWriteMask, false);
126 }
127
128 bool TouchStencilCompare() {
129 return Exchange(Dirty::StencilCompare, false);
130 }
131
132 template <typename T>
133 bool ExchangeCheck(T& old_value, T new_value) {
134 bool result = old_value != new_value;
135 old_value = new_value;
136 return result;
137 }
138
139 bool TouchStencilSide(bool two_sided_stencil_new) {
140 return ExchangeCheck(two_sided_stencil, two_sided_stencil_new) || stencil_reset;
141 }
142
143 bool CheckStencilReferenceFront(u32 new_value) {
144 return ExchangeCheck(front.ref, new_value) || stencil_reset;
145 }
146
147 bool CheckStencilReferenceBack(u32 new_value) {
148 return ExchangeCheck(back.ref, new_value) || stencil_reset;
149 }
150
151 bool CheckStencilWriteMaskFront(u32 new_value) {
152 return ExchangeCheck(front.write_mask, new_value) || stencil_reset;
153 }
154
155 bool CheckStencilWriteMaskBack(u32 new_value) {
156 return ExchangeCheck(back.write_mask, new_value) || stencil_reset;
157 }
158
159 bool CheckStencilCompareMaskFront(u32 new_value) {
160 return ExchangeCheck(front.compare_mask, new_value) || stencil_reset;
161 }
162
163 bool CheckStencilCompareMaskBack(u32 new_value) {
164 return ExchangeCheck(back.compare_mask, new_value) || stencil_reset;
165 }
166
167 void ClearStencilReset() {
168 stencil_reset = false;
169 }
170
106 bool TouchLineWidth() const { 171 bool TouchLineWidth() const {
107 return Exchange(Dirty::LineWidth, false); 172 return Exchange(Dirty::LineWidth, false);
108 } 173 }
@@ -111,6 +176,10 @@ public:
111 return Exchange(Dirty::CullMode, false); 176 return Exchange(Dirty::CullMode, false);
112 } 177 }
113 178
179 bool TouchStateEnable() {
180 return Exchange(Dirty::StateEnable, false);
181 }
182
114 bool TouchDepthBoundsTestEnable() { 183 bool TouchDepthBoundsTestEnable() {
115 return Exchange(Dirty::DepthBoundsEnable, false); 184 return Exchange(Dirty::DepthBoundsEnable, false);
116 } 185 }
@@ -123,6 +192,26 @@ public:
123 return Exchange(Dirty::DepthWriteEnable, false); 192 return Exchange(Dirty::DepthWriteEnable, false);
124 } 193 }
125 194
195 bool TouchPrimitiveRestartEnable() {
196 return Exchange(Dirty::PrimitiveRestartEnable, false);
197 }
198
199 bool TouchRasterizerDiscardEnable() {
200 return Exchange(Dirty::RasterizerDiscardEnable, false);
201 }
202
203 bool TouchDepthBiasEnable() {
204 return Exchange(Dirty::DepthBiasEnable, false);
205 }
206
207 bool TouchLogicOpEnable() {
208 return Exchange(Dirty::LogicOpEnable, false);
209 }
210
211 bool TouchDepthClampEnable() {
212 return Exchange(Dirty::DepthClampEnable, false);
213 }
214
126 bool TouchDepthCompareOp() { 215 bool TouchDepthCompareOp() {
127 return Exchange(Dirty::DepthCompareOp, false); 216 return Exchange(Dirty::DepthCompareOp, false);
128 } 217 }
@@ -135,10 +224,30 @@ public:
135 return Exchange(Dirty::StencilOp, false); 224 return Exchange(Dirty::StencilOp, false);
136 } 225 }
137 226
227 bool TouchBlending() {
228 return Exchange(Dirty::Blending, false);
229 }
230
231 bool TouchBlendEnable() {
232 return Exchange(Dirty::BlendEnable, false);
233 }
234
235 bool TouchBlendEquations() {
236 return Exchange(Dirty::BlendEquations, false);
237 }
238
239 bool TouchColorMask() {
240 return Exchange(Dirty::ColorMask, false);
241 }
242
138 bool TouchStencilTestEnable() { 243 bool TouchStencilTestEnable() {
139 return Exchange(Dirty::StencilTestEnable, false); 244 return Exchange(Dirty::StencilTestEnable, false);
140 } 245 }
141 246
247 bool TouchLogicOp() {
248 return Exchange(Dirty::LogicOp, false);
249 }
250
142 bool ChangePrimitiveTopology(Maxwell::PrimitiveTopology new_topology) { 251 bool ChangePrimitiveTopology(Maxwell::PrimitiveTopology new_topology) {
143 const bool has_changed = current_topology != new_topology; 252 const bool has_changed = current_topology != new_topology;
144 current_topology = new_topology; 253 current_topology = new_topology;
@@ -160,10 +269,20 @@ private:
160 return is_dirty; 269 return is_dirty;
161 } 270 }
162 271
272 struct StencilProperties {
273 u32 ref = 0;
274 u32 write_mask = 0;
275 u32 compare_mask = 0;
276 };
277
163 Tegra::Engines::Maxwell3D::DirtyState::Flags* flags; 278 Tegra::Engines::Maxwell3D::DirtyState::Flags* flags;
164 Tegra::Engines::Maxwell3D::DirtyState::Flags default_flags; 279 Tegra::Engines::Maxwell3D::DirtyState::Flags default_flags;
165 Tegra::Engines::Maxwell3D::DirtyState::Flags invalidation_flags; 280 Tegra::Engines::Maxwell3D::DirtyState::Flags invalidation_flags;
166 Maxwell::PrimitiveTopology current_topology = INVALID_TOPOLOGY; 281 Maxwell::PrimitiveTopology current_topology = INVALID_TOPOLOGY;
282 bool two_sided_stencil = false;
283 StencilProperties front{};
284 StencilProperties back{};
285 bool stencil_reset = false;
167}; 286};
168 287
169} // namespace Vulkan 288} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index a65bbeb1c..d39372ec4 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -812,8 +812,12 @@ StagingBufferRef TextureCacheRuntime::UploadStagingBuffer(size_t size) {
812 return staging_buffer_pool.Request(size, MemoryUsage::Upload); 812 return staging_buffer_pool.Request(size, MemoryUsage::Upload);
813} 813}
814 814
815StagingBufferRef TextureCacheRuntime::DownloadStagingBuffer(size_t size) { 815StagingBufferRef TextureCacheRuntime::DownloadStagingBuffer(size_t size, bool deferred) {
816 return staging_buffer_pool.Request(size, MemoryUsage::Download); 816 return staging_buffer_pool.Request(size, MemoryUsage::Download, deferred);
817}
818
819void TextureCacheRuntime::FreeDeferredStagingBuffer(StagingBufferRef& ref) {
820 staging_buffer_pool.FreeDeferred(ref);
817} 821}
818 822
819bool TextureCacheRuntime::ShouldReinterpret(Image& dst, Image& src) { 823bool TextureCacheRuntime::ShouldReinterpret(Image& dst, Image& src) {
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index 7ec0df134..1f27a3589 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -51,7 +51,9 @@ public:
51 51
52 StagingBufferRef UploadStagingBuffer(size_t size); 52 StagingBufferRef UploadStagingBuffer(size_t size);
53 53
54 StagingBufferRef DownloadStagingBuffer(size_t size); 54 StagingBufferRef DownloadStagingBuffer(size_t size, bool deferred = false);
55
56 void FreeDeferredStagingBuffer(StagingBufferRef& ref);
55 57
56 void TickFrame(); 58 void TickFrame();
57 59
@@ -347,6 +349,7 @@ struct TextureCacheParams {
347 static constexpr bool FRAMEBUFFER_BLITS = false; 349 static constexpr bool FRAMEBUFFER_BLITS = false;
348 static constexpr bool HAS_EMULATED_COPIES = false; 350 static constexpr bool HAS_EMULATED_COPIES = false;
349 static constexpr bool HAS_DEVICE_MEMORY_INFO = true; 351 static constexpr bool HAS_DEVICE_MEMORY_INFO = true;
352 static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true;
350 353
351 using Runtime = Vulkan::TextureCacheRuntime; 354 using Runtime = Vulkan::TextureCacheRuntime;
352 using Image = Vulkan::Image; 355 using Image = Vulkan::Image;
@@ -354,6 +357,7 @@ struct TextureCacheParams {
354 using ImageView = Vulkan::ImageView; 357 using ImageView = Vulkan::ImageView;
355 using Sampler = Vulkan::Sampler; 358 using Sampler = Vulkan::Sampler;
356 using Framebuffer = Vulkan::Framebuffer; 359 using Framebuffer = Vulkan::Framebuffer;
360 using AsyncBuffer = Vulkan::StagingBufferRef;
357}; 361};
358 362
359using TextureCache = VideoCommon::TextureCache<TextureCacheParams>; 363using TextureCache = VideoCommon::TextureCache<TextureCacheParams>;
diff --git a/src/video_core/renderer_vulkan/vk_turbo_mode.cpp b/src/video_core/renderer_vulkan/vk_turbo_mode.cpp
new file mode 100644
index 000000000..852b86f84
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_turbo_mode.cpp
@@ -0,0 +1,205 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/literals.h"
5#include "video_core/host_shaders/vulkan_turbo_mode_comp_spv.h"
6#include "video_core/renderer_vulkan/renderer_vulkan.h"
7#include "video_core/renderer_vulkan/vk_shader_util.h"
8#include "video_core/renderer_vulkan/vk_turbo_mode.h"
9#include "video_core/vulkan_common/vulkan_device.h"
10
11namespace Vulkan {
12
13using namespace Common::Literals;
14
15TurboMode::TurboMode(const vk::Instance& instance, const vk::InstanceDispatch& dld)
16 : m_device{CreateDevice(instance, dld, VK_NULL_HANDLE)}, m_allocator{m_device, false} {
17 m_thread = std::jthread([&](auto stop_token) { Run(stop_token); });
18}
19
20TurboMode::~TurboMode() = default;
21
22void TurboMode::Run(std::stop_token stop_token) {
23 auto& dld = m_device.GetLogical();
24
25 // Allocate buffer. 2MiB should be sufficient.
26 auto buffer = dld.CreateBuffer(VkBufferCreateInfo{
27 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
28 .pNext = nullptr,
29 .flags = 0,
30 .size = 2_MiB,
31 .usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
32 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
33 .queueFamilyIndexCount = 0,
34 .pQueueFamilyIndices = nullptr,
35 });
36
37 // Commit some device local memory for the buffer.
38 auto commit = m_allocator.Commit(buffer, MemoryUsage::DeviceLocal);
39
40 // Create the descriptor pool to contain our descriptor.
41 constexpr VkDescriptorPoolSize pool_size{
42 .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
43 .descriptorCount = 1,
44 };
45
46 auto descriptor_pool = dld.CreateDescriptorPool(VkDescriptorPoolCreateInfo{
47 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
48 .pNext = nullptr,
49 .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
50 .maxSets = 1,
51 .poolSizeCount = 1,
52 .pPoolSizes = &pool_size,
53 });
54
55 // Create the descriptor set layout from the pool.
56 constexpr VkDescriptorSetLayoutBinding layout_binding{
57 .binding = 0,
58 .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
59 .descriptorCount = 1,
60 .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
61 .pImmutableSamplers = nullptr,
62 };
63
64 auto descriptor_set_layout = dld.CreateDescriptorSetLayout(VkDescriptorSetLayoutCreateInfo{
65 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
66 .pNext = nullptr,
67 .flags = 0,
68 .bindingCount = 1,
69 .pBindings = &layout_binding,
70 });
71
72 // Actually create the descriptor set.
73 auto descriptor_set = descriptor_pool.Allocate(VkDescriptorSetAllocateInfo{
74 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
75 .pNext = nullptr,
76 .descriptorPool = *descriptor_pool,
77 .descriptorSetCount = 1,
78 .pSetLayouts = descriptor_set_layout.address(),
79 });
80
81 // Create the shader.
82 auto shader = BuildShader(m_device, VULKAN_TURBO_MODE_COMP_SPV);
83
84 // Create the pipeline layout.
85 auto pipeline_layout = dld.CreatePipelineLayout(VkPipelineLayoutCreateInfo{
86 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
87 .pNext = nullptr,
88 .flags = 0,
89 .setLayoutCount = 1,
90 .pSetLayouts = descriptor_set_layout.address(),
91 .pushConstantRangeCount = 0,
92 .pPushConstantRanges = nullptr,
93 });
94
95 // Actually create the pipeline.
96 const VkPipelineShaderStageCreateInfo shader_stage{
97 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
98 .pNext = nullptr,
99 .flags = 0,
100 .stage = VK_SHADER_STAGE_COMPUTE_BIT,
101 .module = *shader,
102 .pName = "main",
103 .pSpecializationInfo = nullptr,
104 };
105
106 auto pipeline = dld.CreateComputePipeline(VkComputePipelineCreateInfo{
107 .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
108 .pNext = nullptr,
109 .flags = 0,
110 .stage = shader_stage,
111 .layout = *pipeline_layout,
112 .basePipelineHandle = VK_NULL_HANDLE,
113 .basePipelineIndex = 0,
114 });
115
116 // Create a fence to wait on.
117 auto fence = dld.CreateFence(VkFenceCreateInfo{
118 .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
119 .pNext = nullptr,
120 .flags = 0,
121 });
122
123 // Create a command pool to allocate a command buffer from.
124 auto command_pool = dld.CreateCommandPool(VkCommandPoolCreateInfo{
125 .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
126 .pNext = nullptr,
127 .flags =
128 VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
129 .queueFamilyIndex = m_device.GetGraphicsFamily(),
130 });
131
132 // Create a single command buffer.
133 auto cmdbufs = command_pool.Allocate(1, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
134 auto cmdbuf = vk::CommandBuffer{cmdbufs[0], m_device.GetDispatchLoader()};
135
136 while (!stop_token.stop_requested()) {
137 // Reset the fence.
138 fence.Reset();
139
140 // Update descriptor set.
141 const VkDescriptorBufferInfo buffer_info{
142 .buffer = *buffer,
143 .offset = 0,
144 .range = VK_WHOLE_SIZE,
145 };
146
147 const VkWriteDescriptorSet buffer_write{
148 .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
149 .pNext = nullptr,
150 .dstSet = descriptor_set[0],
151 .dstBinding = 0,
152 .dstArrayElement = 0,
153 .descriptorCount = 1,
154 .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
155 .pImageInfo = nullptr,
156 .pBufferInfo = &buffer_info,
157 .pTexelBufferView = nullptr,
158 };
159
160 dld.UpdateDescriptorSets(std::array{buffer_write}, {});
161
162 // Set up the command buffer.
163 cmdbuf.Begin(VkCommandBufferBeginInfo{
164 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
165 .pNext = nullptr,
166 .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
167 .pInheritanceInfo = nullptr,
168 });
169
170 // Clear the buffer.
171 cmdbuf.FillBuffer(*buffer, 0, VK_WHOLE_SIZE, 0);
172
173 // Bind descriptor set.
174 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline_layout, 0,
175 descriptor_set, {});
176
177 // Bind the pipeline.
178 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
179
180 // Dispatch.
181 cmdbuf.Dispatch(64, 64, 1);
182
183 // Finish.
184 cmdbuf.End();
185
186 const VkSubmitInfo submit_info{
187 .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
188 .pNext = nullptr,
189 .waitSemaphoreCount = 0,
190 .pWaitSemaphores = nullptr,
191 .pWaitDstStageMask = nullptr,
192 .commandBufferCount = 1,
193 .pCommandBuffers = cmdbuf.address(),
194 .signalSemaphoreCount = 0,
195 .pSignalSemaphores = nullptr,
196 };
197
198 m_device.GetGraphicsQueue().Submit(std::array{submit_info}, *fence);
199
200 // Wait for completion.
201 fence.Wait();
202 }
203}
204
205} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_turbo_mode.h b/src/video_core/renderer_vulkan/vk_turbo_mode.h
new file mode 100644
index 000000000..2060e2395
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_turbo_mode.h
@@ -0,0 +1,26 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/polyfill_thread.h"
7#include "video_core/vulkan_common/vulkan_device.h"
8#include "video_core/vulkan_common/vulkan_memory_allocator.h"
9#include "video_core/vulkan_common/vulkan_wrapper.h"
10
11namespace Vulkan {
12
13class TurboMode {
14public:
15 explicit TurboMode(const vk::Instance& instance, const vk::InstanceDispatch& dld);
16 ~TurboMode();
17
18private:
19 void Run(std::stop_token stop_token);
20
21 Device m_device;
22 MemoryAllocator m_allocator;
23 std::jthread m_thread;
24};
25
26} // namespace Vulkan
diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp
index 958810747..574760f80 100644
--- a/src/video_core/shader_environment.cpp
+++ b/src/video_core/shader_environment.cpp
@@ -202,12 +202,15 @@ void GenericEnvironment::Serialize(std::ofstream& file) const {
202 const u64 num_texture_types{static_cast<u64>(texture_types.size())}; 202 const u64 num_texture_types{static_cast<u64>(texture_types.size())};
203 const u64 num_texture_pixel_formats{static_cast<u64>(texture_pixel_formats.size())}; 203 const u64 num_texture_pixel_formats{static_cast<u64>(texture_pixel_formats.size())};
204 const u64 num_cbuf_values{static_cast<u64>(cbuf_values.size())}; 204 const u64 num_cbuf_values{static_cast<u64>(cbuf_values.size())};
205 const u64 num_cbuf_replacement_values{static_cast<u64>(cbuf_replacements.size())};
205 206
206 file.write(reinterpret_cast<const char*>(&code_size), sizeof(code_size)) 207 file.write(reinterpret_cast<const char*>(&code_size), sizeof(code_size))
207 .write(reinterpret_cast<const char*>(&num_texture_types), sizeof(num_texture_types)) 208 .write(reinterpret_cast<const char*>(&num_texture_types), sizeof(num_texture_types))
208 .write(reinterpret_cast<const char*>(&num_texture_pixel_formats), 209 .write(reinterpret_cast<const char*>(&num_texture_pixel_formats),
209 sizeof(num_texture_pixel_formats)) 210 sizeof(num_texture_pixel_formats))
210 .write(reinterpret_cast<const char*>(&num_cbuf_values), sizeof(num_cbuf_values)) 211 .write(reinterpret_cast<const char*>(&num_cbuf_values), sizeof(num_cbuf_values))
212 .write(reinterpret_cast<const char*>(&num_cbuf_replacement_values),
213 sizeof(num_cbuf_replacement_values))
211 .write(reinterpret_cast<const char*>(&local_memory_size), sizeof(local_memory_size)) 214 .write(reinterpret_cast<const char*>(&local_memory_size), sizeof(local_memory_size))
212 .write(reinterpret_cast<const char*>(&texture_bound), sizeof(texture_bound)) 215 .write(reinterpret_cast<const char*>(&texture_bound), sizeof(texture_bound))
213 .write(reinterpret_cast<const char*>(&start_address), sizeof(start_address)) 216 .write(reinterpret_cast<const char*>(&start_address), sizeof(start_address))
@@ -229,6 +232,10 @@ void GenericEnvironment::Serialize(std::ofstream& file) const {
229 file.write(reinterpret_cast<const char*>(&key), sizeof(key)) 232 file.write(reinterpret_cast<const char*>(&key), sizeof(key))
230 .write(reinterpret_cast<const char*>(&type), sizeof(type)); 233 .write(reinterpret_cast<const char*>(&type), sizeof(type));
231 } 234 }
235 for (const auto& [key, type] : cbuf_replacements) {
236 file.write(reinterpret_cast<const char*>(&key), sizeof(key))
237 .write(reinterpret_cast<const char*>(&type), sizeof(type));
238 }
232 if (stage == Shader::Stage::Compute) { 239 if (stage == Shader::Stage::Compute) {
233 file.write(reinterpret_cast<const char*>(&workgroup_size), sizeof(workgroup_size)) 240 file.write(reinterpret_cast<const char*>(&workgroup_size), sizeof(workgroup_size))
234 .write(reinterpret_cast<const char*>(&shared_memory_size), sizeof(shared_memory_size)); 241 .write(reinterpret_cast<const char*>(&shared_memory_size), sizeof(shared_memory_size));
@@ -318,6 +325,9 @@ GraphicsEnvironment::GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_,
318 ASSERT(local_size <= std::numeric_limits<u32>::max()); 325 ASSERT(local_size <= std::numeric_limits<u32>::max());
319 local_memory_size = static_cast<u32>(local_size) + sph.common3.shader_local_memory_crs_size; 326 local_memory_size = static_cast<u32>(local_size) + sph.common3.shader_local_memory_crs_size;
320 texture_bound = maxwell3d->regs.bindless_texture_const_buffer_slot; 327 texture_bound = maxwell3d->regs.bindless_texture_const_buffer_slot;
328 is_propietary_driver = texture_bound == 2;
329 has_hle_engine_state =
330 maxwell3d->engine_state == Tegra::Engines::Maxwell3D::EngineHint::OnHLEMacro;
321} 331}
322 332
323u32 GraphicsEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) { 333u32 GraphicsEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) {
@@ -331,6 +341,32 @@ u32 GraphicsEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) {
331 return value; 341 return value;
332} 342}
333 343
344std::optional<Shader::ReplaceConstant> GraphicsEnvironment::GetReplaceConstBuffer(u32 bank,
345 u32 offset) {
346 if (!has_hle_engine_state) {
347 return std::nullopt;
348 }
349 const u64 key = (static_cast<u64>(bank) << 32) | static_cast<u64>(offset);
350 auto it = maxwell3d->replace_table.find(key);
351 if (it == maxwell3d->replace_table.end()) {
352 return std::nullopt;
353 }
354 const auto converted_value = [](Tegra::Engines::Maxwell3D::HLEReplacementAttributeType name) {
355 switch (name) {
356 case Tegra::Engines::Maxwell3D::HLEReplacementAttributeType::BaseVertex:
357 return Shader::ReplaceConstant::BaseVertex;
358 case Tegra::Engines::Maxwell3D::HLEReplacementAttributeType::BaseInstance:
359 return Shader::ReplaceConstant::BaseInstance;
360 case Tegra::Engines::Maxwell3D::HLEReplacementAttributeType::DrawID:
361 return Shader::ReplaceConstant::DrawID;
362 default:
363 UNREACHABLE();
364 }
365 }(it->second);
366 cbuf_replacements.emplace(key, converted_value);
367 return converted_value;
368}
369
334Shader::TextureType GraphicsEnvironment::ReadTextureType(u32 handle) { 370Shader::TextureType GraphicsEnvironment::ReadTextureType(u32 handle) {
335 const auto& regs{maxwell3d->regs}; 371 const auto& regs{maxwell3d->regs};
336 const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding}; 372 const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding};
@@ -366,6 +402,7 @@ ComputeEnvironment::ComputeEnvironment(Tegra::Engines::KeplerCompute& kepler_com
366 stage = Shader::Stage::Compute; 402 stage = Shader::Stage::Compute;
367 local_memory_size = qmd.local_pos_alloc + qmd.local_crs_alloc; 403 local_memory_size = qmd.local_pos_alloc + qmd.local_crs_alloc;
368 texture_bound = kepler_compute->regs.tex_cb_index; 404 texture_bound = kepler_compute->regs.tex_cb_index;
405 is_propietary_driver = texture_bound == 2;
369 shared_memory_size = qmd.shared_alloc; 406 shared_memory_size = qmd.shared_alloc;
370 workgroup_size = {qmd.block_dim_x, qmd.block_dim_y, qmd.block_dim_z}; 407 workgroup_size = {qmd.block_dim_x, qmd.block_dim_y, qmd.block_dim_z};
371} 408}
@@ -409,11 +446,14 @@ void FileEnvironment::Deserialize(std::ifstream& file) {
409 u64 num_texture_types{}; 446 u64 num_texture_types{};
410 u64 num_texture_pixel_formats{}; 447 u64 num_texture_pixel_formats{};
411 u64 num_cbuf_values{}; 448 u64 num_cbuf_values{};
449 u64 num_cbuf_replacement_values{};
412 file.read(reinterpret_cast<char*>(&code_size), sizeof(code_size)) 450 file.read(reinterpret_cast<char*>(&code_size), sizeof(code_size))
413 .read(reinterpret_cast<char*>(&num_texture_types), sizeof(num_texture_types)) 451 .read(reinterpret_cast<char*>(&num_texture_types), sizeof(num_texture_types))
414 .read(reinterpret_cast<char*>(&num_texture_pixel_formats), 452 .read(reinterpret_cast<char*>(&num_texture_pixel_formats),
415 sizeof(num_texture_pixel_formats)) 453 sizeof(num_texture_pixel_formats))
416 .read(reinterpret_cast<char*>(&num_cbuf_values), sizeof(num_cbuf_values)) 454 .read(reinterpret_cast<char*>(&num_cbuf_values), sizeof(num_cbuf_values))
455 .read(reinterpret_cast<char*>(&num_cbuf_replacement_values),
456 sizeof(num_cbuf_replacement_values))
417 .read(reinterpret_cast<char*>(&local_memory_size), sizeof(local_memory_size)) 457 .read(reinterpret_cast<char*>(&local_memory_size), sizeof(local_memory_size))
418 .read(reinterpret_cast<char*>(&texture_bound), sizeof(texture_bound)) 458 .read(reinterpret_cast<char*>(&texture_bound), sizeof(texture_bound))
419 .read(reinterpret_cast<char*>(&start_address), sizeof(start_address)) 459 .read(reinterpret_cast<char*>(&start_address), sizeof(start_address))
@@ -444,6 +484,13 @@ void FileEnvironment::Deserialize(std::ifstream& file) {
444 .read(reinterpret_cast<char*>(&value), sizeof(value)); 484 .read(reinterpret_cast<char*>(&value), sizeof(value));
445 cbuf_values.emplace(key, value); 485 cbuf_values.emplace(key, value);
446 } 486 }
487 for (size_t i = 0; i < num_cbuf_replacement_values; ++i) {
488 u64 key;
489 Shader::ReplaceConstant value;
490 file.read(reinterpret_cast<char*>(&key), sizeof(key))
491 .read(reinterpret_cast<char*>(&value), sizeof(value));
492 cbuf_replacements.emplace(key, value);
493 }
447 if (stage == Shader::Stage::Compute) { 494 if (stage == Shader::Stage::Compute) {
448 file.read(reinterpret_cast<char*>(&workgroup_size), sizeof(workgroup_size)) 495 file.read(reinterpret_cast<char*>(&workgroup_size), sizeof(workgroup_size))
449 .read(reinterpret_cast<char*>(&shared_memory_size), sizeof(shared_memory_size)); 496 .read(reinterpret_cast<char*>(&shared_memory_size), sizeof(shared_memory_size));
@@ -455,6 +502,7 @@ void FileEnvironment::Deserialize(std::ifstream& file) {
455 file.read(reinterpret_cast<char*>(&gp_passthrough_mask), sizeof(gp_passthrough_mask)); 502 file.read(reinterpret_cast<char*>(&gp_passthrough_mask), sizeof(gp_passthrough_mask));
456 } 503 }
457 } 504 }
505 is_propietary_driver = texture_bound == 2;
458} 506}
459 507
460void FileEnvironment::Dump(u64 hash) { 508void FileEnvironment::Dump(u64 hash) {
@@ -512,6 +560,16 @@ std::array<u32, 3> FileEnvironment::WorkgroupSize() const {
512 return workgroup_size; 560 return workgroup_size;
513} 561}
514 562
563std::optional<Shader::ReplaceConstant> FileEnvironment::GetReplaceConstBuffer(u32 bank,
564 u32 offset) {
565 const u64 key = (static_cast<u64>(bank) << 32) | static_cast<u64>(offset);
566 auto it = cbuf_replacements.find(key);
567 if (it == cbuf_replacements.end()) {
568 return std::nullopt;
569 }
570 return it->second;
571}
572
515void SerializePipeline(std::span<const char> key, std::span<const GenericEnvironment* const> envs, 573void SerializePipeline(std::span<const char> key, std::span<const GenericEnvironment* const> envs,
516 const std::filesystem::path& filename, u32 cache_version) try { 574 const std::filesystem::path& filename, u32 cache_version) try {
517 std::ofstream file(filename, std::ios::binary | std::ios::ate | std::ios::app); 575 std::ofstream file(filename, std::ios::binary | std::ios::ate | std::ios::app);
diff --git a/src/video_core/shader_environment.h b/src/video_core/shader_environment.h
index 1342fab1e..d75987a52 100644
--- a/src/video_core/shader_environment.h
+++ b/src/video_core/shader_environment.h
@@ -60,6 +60,10 @@ public:
60 60
61 void Serialize(std::ofstream& file) const; 61 void Serialize(std::ofstream& file) const;
62 62
63 bool HasHLEMacroState() const override {
64 return has_hle_engine_state;
65 }
66
63protected: 67protected:
64 std::optional<u64> TryFindSize(); 68 std::optional<u64> TryFindSize();
65 69
@@ -73,6 +77,7 @@ protected:
73 std::unordered_map<u32, Shader::TextureType> texture_types; 77 std::unordered_map<u32, Shader::TextureType> texture_types;
74 std::unordered_map<u32, Shader::TexturePixelFormat> texture_pixel_formats; 78 std::unordered_map<u32, Shader::TexturePixelFormat> texture_pixel_formats;
75 std::unordered_map<u64, u32> cbuf_values; 79 std::unordered_map<u64, u32> cbuf_values;
80 std::unordered_map<u64, Shader::ReplaceConstant> cbuf_replacements;
76 81
77 u32 local_memory_size{}; 82 u32 local_memory_size{};
78 u32 texture_bound{}; 83 u32 texture_bound{};
@@ -89,6 +94,7 @@ protected:
89 u32 viewport_transform_state = 1; 94 u32 viewport_transform_state = 1;
90 95
91 bool has_unbound_instructions = false; 96 bool has_unbound_instructions = false;
97 bool has_hle_engine_state = false;
92}; 98};
93 99
94class GraphicsEnvironment final : public GenericEnvironment { 100class GraphicsEnvironment final : public GenericEnvironment {
@@ -109,6 +115,8 @@ public:
109 115
110 u32 ReadViewportTransformState() override; 116 u32 ReadViewportTransformState() override;
111 117
118 std::optional<Shader::ReplaceConstant> GetReplaceConstBuffer(u32 bank, u32 offset) override;
119
112private: 120private:
113 Tegra::Engines::Maxwell3D* maxwell3d{}; 121 Tegra::Engines::Maxwell3D* maxwell3d{};
114 size_t stage_index{}; 122 size_t stage_index{};
@@ -131,6 +139,11 @@ public:
131 139
132 u32 ReadViewportTransformState() override; 140 u32 ReadViewportTransformState() override;
133 141
142 std::optional<Shader::ReplaceConstant> GetReplaceConstBuffer(
143 [[maybe_unused]] u32 bank, [[maybe_unused]] u32 offset) override {
144 return std::nullopt;
145 }
146
134private: 147private:
135 Tegra::Engines::KeplerCompute* kepler_compute{}; 148 Tegra::Engines::KeplerCompute* kepler_compute{};
136}; 149};
@@ -166,6 +179,13 @@ public:
166 179
167 [[nodiscard]] std::array<u32, 3> WorkgroupSize() const override; 180 [[nodiscard]] std::array<u32, 3> WorkgroupSize() const override;
168 181
182 [[nodiscard]] std::optional<Shader::ReplaceConstant> GetReplaceConstBuffer(u32 bank,
183 u32 offset) override;
184
185 [[nodiscard]] bool HasHLEMacroState() const override {
186 return cbuf_replacements.size() != 0;
187 }
188
169 void Dump(u64 hash) override; 189 void Dump(u64 hash) override;
170 190
171private: 191private:
@@ -173,6 +193,7 @@ private:
173 std::unordered_map<u32, Shader::TextureType> texture_types; 193 std::unordered_map<u32, Shader::TextureType> texture_types;
174 std::unordered_map<u32, Shader::TexturePixelFormat> texture_pixel_formats; 194 std::unordered_map<u32, Shader::TexturePixelFormat> texture_pixel_formats;
175 std::unordered_map<u64, u32> cbuf_values; 195 std::unordered_map<u64, u32> cbuf_values;
196 std::unordered_map<u64, Shader::ReplaceConstant> cbuf_replacements;
176 std::array<u32, 3> workgroup_size{}; 197 std::array<u32, 3> workgroup_size{};
177 u32 local_memory_size{}; 198 u32 local_memory_size{};
178 u32 shared_memory_size{}; 199 u32 shared_memory_size{};
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 27c82cd20..87152c8e9 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -646,7 +646,28 @@ bool TextureCache<P>::ShouldWaitAsyncFlushes() const noexcept {
646template <class P> 646template <class P>
647void TextureCache<P>::CommitAsyncFlushes() { 647void TextureCache<P>::CommitAsyncFlushes() {
648 // This is intentionally passing the value by copy 648 // This is intentionally passing the value by copy
649 committed_downloads.push(uncommitted_downloads); 649 if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
650 const std::span<const ImageId> download_ids = uncommitted_downloads;
651 if (download_ids.empty()) {
652 committed_downloads.emplace_back(std::move(uncommitted_downloads));
653 uncommitted_downloads.clear();
654 async_buffers.emplace_back(std::optional<AsyncBuffer>{});
655 return;
656 }
657 size_t total_size_bytes = 0;
658 for (const ImageId image_id : download_ids) {
659 total_size_bytes += slot_images[image_id].unswizzled_size_bytes;
660 }
661 auto download_map = runtime.DownloadStagingBuffer(total_size_bytes, true);
662 for (const ImageId image_id : download_ids) {
663 Image& image = slot_images[image_id];
664 const auto copies = FullDownloadCopies(image.info);
665 image.DownloadMemory(download_map, copies);
666 download_map.offset += Common::AlignUp(image.unswizzled_size_bytes, 64);
667 }
668 async_buffers.emplace_back(download_map);
669 }
670 committed_downloads.emplace_back(std::move(uncommitted_downloads));
650 uncommitted_downloads.clear(); 671 uncommitted_downloads.clear();
651} 672}
652 673
@@ -655,37 +676,58 @@ void TextureCache<P>::PopAsyncFlushes() {
655 if (committed_downloads.empty()) { 676 if (committed_downloads.empty()) {
656 return; 677 return;
657 } 678 }
658 const std::span<const ImageId> download_ids = committed_downloads.front(); 679 if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
659 if (download_ids.empty()) { 680 const std::span<const ImageId> download_ids = committed_downloads.front();
660 committed_downloads.pop(); 681 if (download_ids.empty()) {
661 return; 682 committed_downloads.pop_front();
662 } 683 async_buffers.pop_front();
663 size_t total_size_bytes = 0; 684 return;
664 for (const ImageId image_id : download_ids) { 685 }
665 total_size_bytes += slot_images[image_id].unswizzled_size_bytes; 686 auto download_map = *async_buffers.front();
666 } 687 std::span<u8> download_span = download_map.mapped_span;
667 auto download_map = runtime.DownloadStagingBuffer(total_size_bytes); 688 for (size_t i = download_ids.size(); i > 0; i--) {
668 const size_t original_offset = download_map.offset; 689 const ImageBase& image = slot_images[download_ids[i - 1]];
669 for (const ImageId image_id : download_ids) { 690 const auto copies = FullDownloadCopies(image.info);
670 Image& image = slot_images[image_id]; 691 download_map.offset -= Common::AlignUp(image.unswizzled_size_bytes, 64);
671 const auto copies = FullDownloadCopies(image.info); 692 std::span<u8> download_span_alt = download_span.subspan(download_map.offset);
672 image.DownloadMemory(download_map, copies); 693 SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, download_span_alt,
673 download_map.offset += image.unswizzled_size_bytes; 694 swizzle_data_buffer);
674 } 695 }
675 // Wait for downloads to finish 696 runtime.FreeDeferredStagingBuffer(download_map);
676 runtime.Finish(); 697 committed_downloads.pop_front();
677 698 async_buffers.pop_front();
678 download_map.offset = original_offset; 699 } else {
679 std::span<u8> download_span = download_map.mapped_span; 700 const std::span<const ImageId> download_ids = committed_downloads.front();
680 for (const ImageId image_id : download_ids) { 701 if (download_ids.empty()) {
681 const ImageBase& image = slot_images[image_id]; 702 committed_downloads.pop_front();
682 const auto copies = FullDownloadCopies(image.info); 703 return;
683 SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, download_span, 704 }
684 swizzle_data_buffer); 705 size_t total_size_bytes = 0;
685 download_map.offset += image.unswizzled_size_bytes; 706 for (const ImageId image_id : download_ids) {
686 download_span = download_span.subspan(image.unswizzled_size_bytes); 707 total_size_bytes += slot_images[image_id].unswizzled_size_bytes;
708 }
709 auto download_map = runtime.DownloadStagingBuffer(total_size_bytes);
710 const size_t original_offset = download_map.offset;
711 for (const ImageId image_id : download_ids) {
712 Image& image = slot_images[image_id];
713 const auto copies = FullDownloadCopies(image.info);
714 image.DownloadMemory(download_map, copies);
715 download_map.offset += image.unswizzled_size_bytes;
716 }
717 // Wait for downloads to finish
718 runtime.Finish();
719 download_map.offset = original_offset;
720 std::span<u8> download_span = download_map.mapped_span;
721 for (const ImageId image_id : download_ids) {
722 const ImageBase& image = slot_images[image_id];
723 const auto copies = FullDownloadCopies(image.info);
724 SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, download_span,
725 swizzle_data_buffer);
726 download_map.offset += image.unswizzled_size_bytes;
727 download_span = download_span.subspan(image.unswizzled_size_bytes);
728 }
729 committed_downloads.pop_front();
687 } 730 }
688 committed_downloads.pop();
689} 731}
690 732
691template <class P> 733template <class P>
@@ -740,7 +782,8 @@ void TextureCache<P>::UploadImageContents(Image& image, StagingBuffer& staging)
740 const GPUVAddr gpu_addr = image.gpu_addr; 782 const GPUVAddr gpu_addr = image.gpu_addr;
741 783
742 if (True(image.flags & ImageFlagBits::AcceleratedUpload)) { 784 if (True(image.flags & ImageFlagBits::AcceleratedUpload)) {
743 gpu_memory->ReadBlockUnsafe(gpu_addr, mapped_span.data(), mapped_span.size_bytes()); 785 gpu_memory->ReadBlock(gpu_addr, mapped_span.data(), mapped_span.size_bytes(),
786 VideoCommon::CacheType::NoTextureCache);
744 const auto uploads = FullUploadSwizzles(image.info); 787 const auto uploads = FullUploadSwizzles(image.info);
745 runtime.AccelerateImageUpload(image, staging, uploads); 788 runtime.AccelerateImageUpload(image, staging, uploads);
746 return; 789 return;
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index 4fd677a80..4eea1f609 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -92,6 +92,8 @@ class TextureCache : public VideoCommon::ChannelSetupCaches<TextureCacheChannelI
92 static constexpr bool HAS_EMULATED_COPIES = P::HAS_EMULATED_COPIES; 92 static constexpr bool HAS_EMULATED_COPIES = P::HAS_EMULATED_COPIES;
93 /// True when the API can provide info about the memory of the device. 93 /// True when the API can provide info about the memory of the device.
94 static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO; 94 static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO;
95 /// True when the API can do asynchronous texture downloads.
96 static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = P::IMPLEMENTS_ASYNC_DOWNLOADS;
95 97
96 static constexpr size_t UNSET_CHANNEL{std::numeric_limits<size_t>::max()}; 98 static constexpr size_t UNSET_CHANNEL{std::numeric_limits<size_t>::max()};
97 99
@@ -106,6 +108,7 @@ class TextureCache : public VideoCommon::ChannelSetupCaches<TextureCacheChannelI
106 using ImageView = typename P::ImageView; 108 using ImageView = typename P::ImageView;
107 using Sampler = typename P::Sampler; 109 using Sampler = typename P::Sampler;
108 using Framebuffer = typename P::Framebuffer; 110 using Framebuffer = typename P::Framebuffer;
111 using AsyncBuffer = typename P::AsyncBuffer;
109 112
110 struct BlitImages { 113 struct BlitImages {
111 ImageId dst_id; 114 ImageId dst_id;
@@ -203,7 +206,7 @@ public:
203 /// Create channel state. 206 /// Create channel state.
204 void CreateChannel(Tegra::Control::ChannelState& channel) final override; 207 void CreateChannel(Tegra::Control::ChannelState& channel) final override;
205 208
206 std::mutex mutex; 209 std::recursive_mutex mutex;
207 210
208private: 211private:
209 /// Iterate over all page indices in a range 212 /// Iterate over all page indices in a range
@@ -403,7 +406,8 @@ private:
403 406
404 // TODO: This data structure is not optimal and it should be reworked 407 // TODO: This data structure is not optimal and it should be reworked
405 std::vector<ImageId> uncommitted_downloads; 408 std::vector<ImageId> uncommitted_downloads;
406 std::queue<std::vector<ImageId>> committed_downloads; 409 std::deque<std::vector<ImageId>> committed_downloads;
410 std::deque<std::optional<AsyncBuffer>> async_buffers;
407 411
408 struct LRUItemParams { 412 struct LRUItemParams {
409 using ObjectType = ImageId; 413 using ObjectType = ImageId;
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index c4d31681a..fd1c5a683 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -350,8 +350,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
350 .sampleRateShading = true, 350 .sampleRateShading = true,
351 .dualSrcBlend = true, 351 .dualSrcBlend = true,
352 .logicOp = true, 352 .logicOp = true,
353 .multiDrawIndirect = false, 353 .multiDrawIndirect = true,
354 .drawIndirectFirstInstance = false, 354 .drawIndirectFirstInstance = true,
355 .depthClamp = true, 355 .depthClamp = true,
356 .depthBiasClamp = true, 356 .depthBiasClamp = true,
357 .fillModeNonSolid = true, 357 .fillModeNonSolid = true,
@@ -569,6 +569,67 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
569 LOG_INFO(Render_Vulkan, "Device doesn't support extended dynamic state"); 569 LOG_INFO(Render_Vulkan, "Device doesn't support extended dynamic state");
570 } 570 }
571 571
572 VkPhysicalDeviceExtendedDynamicState2FeaturesEXT dynamic_state_2;
573 if (ext_extended_dynamic_state_2) {
574 dynamic_state_2 = {
575 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_2_FEATURES_EXT,
576 .pNext = nullptr,
577 .extendedDynamicState2 = VK_TRUE,
578 .extendedDynamicState2LogicOp = ext_extended_dynamic_state_2_extra ? VK_TRUE : VK_FALSE,
579 .extendedDynamicState2PatchControlPoints = VK_FALSE,
580 };
581 SetNext(next, dynamic_state_2);
582 } else {
583 LOG_INFO(Render_Vulkan, "Device doesn't support extended dynamic state 2");
584 }
585
586 VkPhysicalDeviceExtendedDynamicState3FeaturesEXT dynamic_state_3;
587 if (ext_extended_dynamic_state_3) {
588 dynamic_state_3 = {
589 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_3_FEATURES_EXT,
590 .pNext = nullptr,
591 .extendedDynamicState3TessellationDomainOrigin = VK_FALSE,
592 .extendedDynamicState3DepthClampEnable =
593 ext_extended_dynamic_state_3_enables ? VK_TRUE : VK_FALSE,
594 .extendedDynamicState3PolygonMode = VK_FALSE,
595 .extendedDynamicState3RasterizationSamples = VK_FALSE,
596 .extendedDynamicState3SampleMask = VK_FALSE,
597 .extendedDynamicState3AlphaToCoverageEnable = VK_FALSE,
598 .extendedDynamicState3AlphaToOneEnable = VK_FALSE,
599 .extendedDynamicState3LogicOpEnable =
600 ext_extended_dynamic_state_3_enables ? VK_TRUE : VK_FALSE,
601 .extendedDynamicState3ColorBlendEnable =
602 ext_extended_dynamic_state_3_blend ? VK_TRUE : VK_FALSE,
603 .extendedDynamicState3ColorBlendEquation =
604 ext_extended_dynamic_state_3_blend ? VK_TRUE : VK_FALSE,
605 .extendedDynamicState3ColorWriteMask =
606 ext_extended_dynamic_state_3_blend ? VK_TRUE : VK_FALSE,
607 .extendedDynamicState3RasterizationStream = VK_FALSE,
608 .extendedDynamicState3ConservativeRasterizationMode = VK_FALSE,
609 .extendedDynamicState3ExtraPrimitiveOverestimationSize = VK_FALSE,
610 .extendedDynamicState3DepthClipEnable = VK_FALSE,
611 .extendedDynamicState3SampleLocationsEnable = VK_FALSE,
612 .extendedDynamicState3ColorBlendAdvanced = VK_FALSE,
613 .extendedDynamicState3ProvokingVertexMode = VK_FALSE,
614 .extendedDynamicState3LineRasterizationMode = VK_FALSE,
615 .extendedDynamicState3LineStippleEnable = VK_FALSE,
616 .extendedDynamicState3DepthClipNegativeOneToOne = VK_FALSE,
617 .extendedDynamicState3ViewportWScalingEnable = VK_FALSE,
618 .extendedDynamicState3ViewportSwizzle = VK_FALSE,
619 .extendedDynamicState3CoverageToColorEnable = VK_FALSE,
620 .extendedDynamicState3CoverageToColorLocation = VK_FALSE,
621 .extendedDynamicState3CoverageModulationMode = VK_FALSE,
622 .extendedDynamicState3CoverageModulationTableEnable = VK_FALSE,
623 .extendedDynamicState3CoverageModulationTable = VK_FALSE,
624 .extendedDynamicState3CoverageReductionMode = VK_FALSE,
625 .extendedDynamicState3RepresentativeFragmentTestEnable = VK_FALSE,
626 .extendedDynamicState3ShadingRateImageEnable = VK_FALSE,
627 };
628 SetNext(next, dynamic_state_3);
629 } else {
630 LOG_INFO(Render_Vulkan, "Device doesn't support extended dynamic state 3");
631 }
632
572 VkPhysicalDeviceLineRasterizationFeaturesEXT line_raster; 633 VkPhysicalDeviceLineRasterizationFeaturesEXT line_raster;
573 if (ext_line_rasterization) { 634 if (ext_line_rasterization) {
574 line_raster = { 635 line_raster = {
@@ -695,6 +756,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
695 CollectToolingInfo(); 756 CollectToolingInfo();
696 757
697 if (driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY_KHR) { 758 if (driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY_KHR) {
759 const u32 nv_major_version = (properties.driverVersion >> 22) & 0x3ff;
760
698 const auto arch = GetNvidiaArchitecture(physical, supported_extensions); 761 const auto arch = GetNvidiaArchitecture(physical, supported_extensions);
699 switch (arch) { 762 switch (arch) {
700 case NvidiaArchitecture::AmpereOrNewer: 763 case NvidiaArchitecture::AmpereOrNewer:
@@ -704,11 +767,13 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
704 case NvidiaArchitecture::Turing: 767 case NvidiaArchitecture::Turing:
705 break; 768 break;
706 case NvidiaArchitecture::VoltaOrOlder: 769 case NvidiaArchitecture::VoltaOrOlder:
707 LOG_WARNING(Render_Vulkan, "Blacklisting Volta and older from VK_KHR_push_descriptor"); 770 if (nv_major_version < 527) {
708 khr_push_descriptor = false; 771 LOG_WARNING(Render_Vulkan,
772 "Blacklisting Volta and older from VK_KHR_push_descriptor");
773 khr_push_descriptor = false;
774 }
709 break; 775 break;
710 } 776 }
711 const u32 nv_major_version = (properties.driverVersion >> 22) & 0x3ff;
712 if (nv_major_version >= 510) { 777 if (nv_major_version >= 510) {
713 LOG_WARNING(Render_Vulkan, "NVIDIA Drivers >= 510 do not support MSAA image blits"); 778 LOG_WARNING(Render_Vulkan, "NVIDIA Drivers >= 510 do not support MSAA image blits");
714 cant_blit_msaa = true; 779 cant_blit_msaa = true;
@@ -735,6 +800,16 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
735 ext_vertex_input_dynamic_state = false; 800 ext_vertex_input_dynamic_state = false;
736 } 801 }
737 } 802 }
803 if (ext_extended_dynamic_state_2 && is_radv) {
804 const u32 version = (properties.driverVersion << 3) >> 3;
805 if (version < VK_MAKE_API_VERSION(0, 22, 3, 1)) {
806 LOG_WARNING(
807 Render_Vulkan,
808 "RADV versions older than 22.3.1 have broken VK_EXT_extended_dynamic_state2");
809 ext_extended_dynamic_state_2 = false;
810 ext_extended_dynamic_state_2_extra = false;
811 }
812 }
738 sets_per_pool = 64; 813 sets_per_pool = 64;
739 814
740 const bool is_amd = 815 const bool is_amd =
@@ -763,8 +838,11 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
763 const bool is_intel_windows = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS; 838 const bool is_intel_windows = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS;
764 const bool is_intel_anv = driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA; 839 const bool is_intel_anv = driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA;
765 if (ext_vertex_input_dynamic_state && is_intel_windows) { 840 if (ext_vertex_input_dynamic_state && is_intel_windows) {
766 LOG_WARNING(Render_Vulkan, "Blacklisting Intel for VK_EXT_vertex_input_dynamic_state"); 841 const u32 version = (properties.driverVersion << 3) >> 3;
767 ext_vertex_input_dynamic_state = false; 842 if (version < VK_MAKE_API_VERSION(27, 20, 100, 0)) {
843 LOG_WARNING(Render_Vulkan, "Blacklisting Intel for VK_EXT_vertex_input_dynamic_state");
844 ext_vertex_input_dynamic_state = false;
845 }
768 } 846 }
769 if (is_float16_supported && is_intel_windows) { 847 if (is_float16_supported && is_intel_windows) {
770 // Intel's compiler crashes when using fp16 on Astral Chain, disable it for the time being. 848 // Intel's compiler crashes when using fp16 on Astral Chain, disable it for the time being.
@@ -913,6 +991,18 @@ std::string Device::GetDriverName() const {
913 } 991 }
914} 992}
915 993
994bool Device::ShouldBoostClocks() const {
995 const bool validated_driver =
996 driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE ||
997 driver_id == VK_DRIVER_ID_MESA_RADV || driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY ||
998 driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS ||
999 driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA;
1000
1001 const bool is_steam_deck = properties.vendorID == 0x1002 && properties.deviceID == 0x163F;
1002
1003 return validated_driver && !is_steam_deck;
1004}
1005
916static std::vector<const char*> ExtensionsRequiredForInstanceVersion(u32 available_version) { 1006static std::vector<const char*> ExtensionsRequiredForInstanceVersion(u32 available_version) {
917 std::vector<const char*> extensions{REQUIRED_EXTENSIONS.begin(), REQUIRED_EXTENSIONS.end()}; 1007 std::vector<const char*> extensions{REQUIRED_EXTENSIONS.begin(), REQUIRED_EXTENSIONS.end()};
918 1008
@@ -1024,6 +1114,8 @@ void Device::CheckSuitability(bool requires_swapchain) const {
1024 std::make_pair(features.vertexPipelineStoresAndAtomics, "vertexPipelineStoresAndAtomics"), 1114 std::make_pair(features.vertexPipelineStoresAndAtomics, "vertexPipelineStoresAndAtomics"),
1025 std::make_pair(features.imageCubeArray, "imageCubeArray"), 1115 std::make_pair(features.imageCubeArray, "imageCubeArray"),
1026 std::make_pair(features.independentBlend, "independentBlend"), 1116 std::make_pair(features.independentBlend, "independentBlend"),
1117 std::make_pair(features.multiDrawIndirect, "multiDrawIndirect"),
1118 std::make_pair(features.drawIndirectFirstInstance, "drawIndirectFirstInstance"),
1027 std::make_pair(features.depthClamp, "depthClamp"), 1119 std::make_pair(features.depthClamp, "depthClamp"),
1028 std::make_pair(features.samplerAnisotropy, "samplerAnisotropy"), 1120 std::make_pair(features.samplerAnisotropy, "samplerAnisotropy"),
1029 std::make_pair(features.largePoints, "largePoints"), 1121 std::make_pair(features.largePoints, "largePoints"),
@@ -1089,6 +1181,8 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
1089 bool has_ext_transform_feedback{}; 1181 bool has_ext_transform_feedback{};
1090 bool has_ext_custom_border_color{}; 1182 bool has_ext_custom_border_color{};
1091 bool has_ext_extended_dynamic_state{}; 1183 bool has_ext_extended_dynamic_state{};
1184 bool has_ext_extended_dynamic_state_2{};
1185 bool has_ext_extended_dynamic_state_3{};
1092 bool has_ext_shader_atomic_int64{}; 1186 bool has_ext_shader_atomic_int64{};
1093 bool has_ext_provoking_vertex{}; 1187 bool has_ext_provoking_vertex{};
1094 bool has_ext_vertex_input_dynamic_state{}; 1188 bool has_ext_vertex_input_dynamic_state{};
@@ -1117,6 +1211,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
1117 test(khr_spirv_1_4, VK_KHR_SPIRV_1_4_EXTENSION_NAME, true); 1211 test(khr_spirv_1_4, VK_KHR_SPIRV_1_4_EXTENSION_NAME, true);
1118 test(khr_push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME, true); 1212 test(khr_push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME, true);
1119 test(has_khr_shader_float16_int8, VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, false); 1213 test(has_khr_shader_float16_int8, VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, false);
1214 test(khr_draw_indirect_count, VK_KHR_DRAW_INDIRECT_COUNT_EXTENSION_NAME, true);
1120 test(ext_depth_range_unrestricted, VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME, true); 1215 test(ext_depth_range_unrestricted, VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME, true);
1121 test(ext_index_type_uint8, VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME, true); 1216 test(ext_index_type_uint8, VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME, true);
1122 test(has_ext_primitive_topology_list_restart, 1217 test(has_ext_primitive_topology_list_restart,
@@ -1132,6 +1227,10 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
1132 test(has_ext_transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, false); 1227 test(has_ext_transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, false);
1133 test(has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, false); 1228 test(has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, false);
1134 test(has_ext_extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false); 1229 test(has_ext_extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false);
1230 test(has_ext_extended_dynamic_state_2, VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME,
1231 false);
1232 test(has_ext_extended_dynamic_state_3, VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME,
1233 false);
1135 test(has_ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, true); 1234 test(has_ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, true);
1136 test(has_ext_provoking_vertex, VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME, false); 1235 test(has_ext_provoking_vertex, VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME, false);
1137 test(has_ext_vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME, 1236 test(has_ext_vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME,
@@ -1281,6 +1380,44 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
1281 ext_extended_dynamic_state = true; 1380 ext_extended_dynamic_state = true;
1282 } 1381 }
1283 } 1382 }
1383 if (has_ext_extended_dynamic_state_2) {
1384 VkPhysicalDeviceExtendedDynamicState2FeaturesEXT extended_dynamic_state_2;
1385 extended_dynamic_state_2.sType =
1386 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_2_FEATURES_EXT;
1387 extended_dynamic_state_2.pNext = nullptr;
1388 features.pNext = &extended_dynamic_state_2;
1389 physical.GetFeatures2(features);
1390
1391 if (extended_dynamic_state_2.extendedDynamicState2) {
1392 extensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
1393 ext_extended_dynamic_state_2 = true;
1394 ext_extended_dynamic_state_2_extra =
1395 extended_dynamic_state_2.extendedDynamicState2LogicOp;
1396 }
1397 }
1398 if (has_ext_extended_dynamic_state_3) {
1399 VkPhysicalDeviceExtendedDynamicState3FeaturesEXT extended_dynamic_state_3;
1400 extended_dynamic_state_3.sType =
1401 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_3_FEATURES_EXT;
1402 extended_dynamic_state_3.pNext = nullptr;
1403 features.pNext = &extended_dynamic_state_3;
1404 physical.GetFeatures2(features);
1405
1406 ext_extended_dynamic_state_3_blend =
1407 extended_dynamic_state_3.extendedDynamicState3ColorBlendEnable &&
1408 extended_dynamic_state_3.extendedDynamicState3ColorBlendEquation &&
1409 extended_dynamic_state_3.extendedDynamicState3ColorWriteMask;
1410
1411 ext_extended_dynamic_state_3_enables =
1412 extended_dynamic_state_3.extendedDynamicState3DepthClampEnable &&
1413 extended_dynamic_state_3.extendedDynamicState3LogicOpEnable;
1414
1415 ext_extended_dynamic_state_3 =
1416 ext_extended_dynamic_state_3_blend || ext_extended_dynamic_state_3_enables;
1417 if (ext_extended_dynamic_state_3) {
1418 extensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
1419 }
1420 }
1284 if (has_ext_line_rasterization) { 1421 if (has_ext_line_rasterization) {
1285 VkPhysicalDeviceLineRasterizationFeaturesEXT line_raster; 1422 VkPhysicalDeviceLineRasterizationFeaturesEXT line_raster;
1286 line_raster.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT; 1423 line_raster.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT;
@@ -1347,7 +1484,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
1347 is_patch_list_restart_supported = 1484 is_patch_list_restart_supported =
1348 primitive_topology_list_restart.primitiveTopologyPatchListRestart; 1485 primitive_topology_list_restart.primitiveTopologyPatchListRestart;
1349 } 1486 }
1350 if (has_khr_image_format_list && has_khr_swapchain_mutable_format) { 1487 if (requires_surface && has_khr_image_format_list && has_khr_swapchain_mutable_format) {
1351 extensions.push_back(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME); 1488 extensions.push_back(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME);
1352 extensions.push_back(VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME); 1489 extensions.push_back(VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME);
1353 khr_swapchain_mutable_format = true; 1490 khr_swapchain_mutable_format = true;
@@ -1362,6 +1499,9 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
1362 1499
1363 max_push_descriptors = push_descriptor.maxPushDescriptors; 1500 max_push_descriptors = push_descriptor.maxPushDescriptors;
1364 } 1501 }
1502
1503 has_null_descriptor = true;
1504
1365 return extensions; 1505 return extensions;
1366} 1506}
1367 1507
@@ -1392,8 +1532,12 @@ void Device::SetupFamilies(VkSurfaceKHR surface) {
1392 LOG_ERROR(Render_Vulkan, "Device lacks a present queue"); 1532 LOG_ERROR(Render_Vulkan, "Device lacks a present queue");
1393 throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT); 1533 throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT);
1394 } 1534 }
1395 graphics_family = *graphics; 1535 if (graphics) {
1396 present_family = *present; 1536 graphics_family = *graphics;
1537 }
1538 if (present) {
1539 present_family = *present;
1540 }
1397} 1541}
1398 1542
1399void Device::SetupFeatures() { 1543void Device::SetupFeatures() {
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index 6a26c4e6e..4bc267163 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -106,6 +106,8 @@ public:
106 return driver_id; 106 return driver_id;
107 } 107 }
108 108
109 bool ShouldBoostClocks() const;
110
109 /// Returns uniform buffer alignment requeriment. 111 /// Returns uniform buffer alignment requeriment.
110 VkDeviceSize GetUniformBufferAlignment() const { 112 VkDeviceSize GetUniformBufferAlignment() const {
111 return properties.limits.minUniformBufferOffsetAlignment; 113 return properties.limits.minUniformBufferOffsetAlignment;
@@ -286,6 +288,30 @@ public:
286 return ext_extended_dynamic_state; 288 return ext_extended_dynamic_state;
287 } 289 }
288 290
291 /// Returns true if the device supports VK_EXT_extended_dynamic_state2.
292 bool IsExtExtendedDynamicState2Supported() const {
293 return ext_extended_dynamic_state_2;
294 }
295
296 bool IsExtExtendedDynamicState2ExtrasSupported() const {
297 return ext_extended_dynamic_state_2_extra;
298 }
299
300 /// Returns true if the device supports VK_EXT_extended_dynamic_state3.
301 bool IsExtExtendedDynamicState3Supported() const {
302 return ext_extended_dynamic_state_3;
303 }
304
305 /// Returns true if the device supports VK_EXT_extended_dynamic_state3.
306 bool IsExtExtendedDynamicState3BlendingSupported() const {
307 return ext_extended_dynamic_state_3_blend;
308 }
309
310 /// Returns true if the device supports VK_EXT_extended_dynamic_state3.
311 bool IsExtExtendedDynamicState3EnablesSupported() const {
312 return ext_extended_dynamic_state_3_enables;
313 }
314
289 /// Returns true if the device supports VK_EXT_line_rasterization. 315 /// Returns true if the device supports VK_EXT_line_rasterization.
290 bool IsExtLineRasterizationSupported() const { 316 bool IsExtLineRasterizationSupported() const {
291 return ext_line_rasterization; 317 return ext_line_rasterization;
@@ -373,6 +399,10 @@ public:
373 return must_emulate_bgr565; 399 return must_emulate_bgr565;
374 } 400 }
375 401
402 bool HasNullDescriptor() const {
403 return has_null_descriptor;
404 }
405
376 u32 GetMaxVertexInputAttributes() const { 406 u32 GetMaxVertexInputAttributes() const {
377 return max_vertex_input_attributes; 407 return max_vertex_input_attributes;
378 } 408 }
@@ -451,6 +481,7 @@ private:
451 bool nv_viewport_swizzle{}; ///< Support for VK_NV_viewport_swizzle. 481 bool nv_viewport_swizzle{}; ///< Support for VK_NV_viewport_swizzle.
452 bool nv_viewport_array2{}; ///< Support for VK_NV_viewport_array2. 482 bool nv_viewport_array2{}; ///< Support for VK_NV_viewport_array2.
453 bool nv_geometry_shader_passthrough{}; ///< Support for VK_NV_geometry_shader_passthrough. 483 bool nv_geometry_shader_passthrough{}; ///< Support for VK_NV_geometry_shader_passthrough.
484 bool khr_draw_indirect_count{}; ///< Support for VK_KHR_draw_indirect_count.
454 bool khr_uniform_buffer_standard_layout{}; ///< Support for scalar uniform buffer layouts. 485 bool khr_uniform_buffer_standard_layout{}; ///< Support for scalar uniform buffer layouts.
455 bool khr_spirv_1_4{}; ///< Support for VK_KHR_spirv_1_4. 486 bool khr_spirv_1_4{}; ///< Support for VK_KHR_spirv_1_4.
456 bool khr_workgroup_memory_explicit_layout{}; ///< Support for explicit workgroup layouts. 487 bool khr_workgroup_memory_explicit_layout{}; ///< Support for explicit workgroup layouts.
@@ -461,28 +492,34 @@ private:
461 bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax. 492 bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax.
462 bool ext_depth_clip_control{}; ///< Support for VK_EXT_depth_clip_control 493 bool ext_depth_clip_control{}; ///< Support for VK_EXT_depth_clip_control
463 bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted. 494 bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted.
464 bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer. 495 bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer.
465 bool ext_tooling_info{}; ///< Support for VK_EXT_tooling_info. 496 bool ext_tooling_info{}; ///< Support for VK_EXT_tooling_info.
466 bool ext_subgroup_size_control{}; ///< Support for VK_EXT_subgroup_size_control. 497 bool ext_subgroup_size_control{}; ///< Support for VK_EXT_subgroup_size_control.
467 bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback. 498 bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback.
468 bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color. 499 bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color.
469 bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state. 500 bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state.
470 bool ext_line_rasterization{}; ///< Support for VK_EXT_line_rasterization. 501 bool ext_extended_dynamic_state_2{}; ///< Support for VK_EXT_extended_dynamic_state2.
471 bool ext_vertex_input_dynamic_state{}; ///< Support for VK_EXT_vertex_input_dynamic_state. 502 bool ext_extended_dynamic_state_2_extra{}; ///< Support for VK_EXT_extended_dynamic_state2.
472 bool ext_shader_stencil_export{}; ///< Support for VK_EXT_shader_stencil_export. 503 bool ext_extended_dynamic_state_3{}; ///< Support for VK_EXT_extended_dynamic_state3.
473 bool ext_shader_atomic_int64{}; ///< Support for VK_KHR_shader_atomic_int64. 504 bool ext_extended_dynamic_state_3_blend{}; ///< Support for VK_EXT_extended_dynamic_state3.
474 bool ext_conservative_rasterization{}; ///< Support for VK_EXT_conservative_rasterization. 505 bool ext_extended_dynamic_state_3_enables{}; ///< Support for VK_EXT_extended_dynamic_state3.
475 bool ext_provoking_vertex{}; ///< Support for VK_EXT_provoking_vertex. 506 bool ext_line_rasterization{}; ///< Support for VK_EXT_line_rasterization.
476 bool ext_memory_budget{}; ///< Support for VK_EXT_memory_budget. 507 bool ext_vertex_input_dynamic_state{}; ///< Support for VK_EXT_vertex_input_dynamic_state.
477 bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config. 508 bool ext_shader_stencil_export{}; ///< Support for VK_EXT_shader_stencil_export.
478 bool has_broken_cube_compatibility{}; ///< Has broken cube compatiblity bit 509 bool ext_shader_atomic_int64{}; ///< Support for VK_KHR_shader_atomic_int64.
479 bool has_renderdoc{}; ///< Has RenderDoc attached 510 bool ext_conservative_rasterization{}; ///< Support for VK_EXT_conservative_rasterization.
480 bool has_nsight_graphics{}; ///< Has Nsight Graphics attached 511 bool ext_provoking_vertex{}; ///< Support for VK_EXT_provoking_vertex.
481 bool supports_d24_depth{}; ///< Supports D24 depth buffers. 512 bool ext_memory_budget{}; ///< Support for VK_EXT_memory_budget.
482 bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting. 513 bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config.
483 bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format. 514 bool has_broken_cube_compatibility{}; ///< Has broken cube compatiblity bit
484 u32 max_vertex_input_attributes{}; ///< Max vertex input attributes in pipeline 515 bool has_renderdoc{}; ///< Has RenderDoc attached
485 u32 max_vertex_input_bindings{}; ///< Max vertex input buffers in pipeline 516 bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
517 bool supports_d24_depth{}; ///< Supports D24 depth buffers.
518 bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting.
519 bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format.
520 bool has_null_descriptor{}; ///< Has support for null descriptors.
521 u32 max_vertex_input_attributes{}; ///< Max vertex input attributes in pipeline
522 u32 max_vertex_input_bindings{}; ///< Max vertex input buffers in pipeline
486 523
487 // Telemetry parameters 524 // Telemetry parameters
488 std::string vendor_name; ///< Device's driver name. 525 std::string vendor_name; ///< Device's driver name.
diff --git a/src/video_core/vulkan_common/vulkan_instance.cpp b/src/video_core/vulkan_common/vulkan_instance.cpp
index 562039b56..b6d83e446 100644
--- a/src/video_core/vulkan_common/vulkan_instance.cpp
+++ b/src/video_core/vulkan_common/vulkan_instance.cpp
@@ -32,7 +32,7 @@
32namespace Vulkan { 32namespace Vulkan {
33namespace { 33namespace {
34[[nodiscard]] std::vector<const char*> RequiredExtensions( 34[[nodiscard]] std::vector<const char*> RequiredExtensions(
35 Core::Frontend::WindowSystemType window_type, bool enable_debug_utils) { 35 Core::Frontend::WindowSystemType window_type, bool enable_validation) {
36 std::vector<const char*> extensions; 36 std::vector<const char*> extensions;
37 extensions.reserve(6); 37 extensions.reserve(6);
38 switch (window_type) { 38 switch (window_type) {
@@ -65,7 +65,7 @@ namespace {
65 if (window_type != Core::Frontend::WindowSystemType::Headless) { 65 if (window_type != Core::Frontend::WindowSystemType::Headless) {
66 extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); 66 extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
67 } 67 }
68 if (enable_debug_utils) { 68 if (enable_validation) {
69 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); 69 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
70 } 70 }
71 extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); 71 extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
@@ -95,9 +95,9 @@ namespace {
95 return true; 95 return true;
96} 96}
97 97
98[[nodiscard]] std::vector<const char*> Layers(bool enable_layers) { 98[[nodiscard]] std::vector<const char*> Layers(bool enable_validation) {
99 std::vector<const char*> layers; 99 std::vector<const char*> layers;
100 if (enable_layers) { 100 if (enable_validation) {
101 layers.push_back("VK_LAYER_KHRONOS_validation"); 101 layers.push_back("VK_LAYER_KHRONOS_validation");
102 } 102 }
103 return layers; 103 return layers;
@@ -125,7 +125,7 @@ void RemoveUnavailableLayers(const vk::InstanceDispatch& dld, std::vector<const
125 125
126vk::Instance CreateInstance(const Common::DynamicLibrary& library, vk::InstanceDispatch& dld, 126vk::Instance CreateInstance(const Common::DynamicLibrary& library, vk::InstanceDispatch& dld,
127 u32 required_version, Core::Frontend::WindowSystemType window_type, 127 u32 required_version, Core::Frontend::WindowSystemType window_type,
128 bool enable_debug_utils, bool enable_layers) { 128 bool enable_validation) {
129 if (!library.IsOpen()) { 129 if (!library.IsOpen()) {
130 LOG_ERROR(Render_Vulkan, "Vulkan library not available"); 130 LOG_ERROR(Render_Vulkan, "Vulkan library not available");
131 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); 131 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
@@ -138,11 +138,11 @@ vk::Instance CreateInstance(const Common::DynamicLibrary& library, vk::InstanceD
138 LOG_ERROR(Render_Vulkan, "Failed to load Vulkan function pointers"); 138 LOG_ERROR(Render_Vulkan, "Failed to load Vulkan function pointers");
139 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); 139 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
140 } 140 }
141 const std::vector<const char*> extensions = RequiredExtensions(window_type, enable_debug_utils); 141 const std::vector<const char*> extensions = RequiredExtensions(window_type, enable_validation);
142 if (!AreExtensionsSupported(dld, extensions)) { 142 if (!AreExtensionsSupported(dld, extensions)) {
143 throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT); 143 throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT);
144 } 144 }
145 std::vector<const char*> layers = Layers(enable_layers); 145 std::vector<const char*> layers = Layers(enable_validation);
146 RemoveUnavailableLayers(dld, layers); 146 RemoveUnavailableLayers(dld, layers);
147 147
148 const u32 available_version = vk::AvailableVersion(dld); 148 const u32 available_version = vk::AvailableVersion(dld);
diff --git a/src/video_core/vulkan_common/vulkan_instance.h b/src/video_core/vulkan_common/vulkan_instance.h
index 40419d802..b59b92f83 100644
--- a/src/video_core/vulkan_common/vulkan_instance.h
+++ b/src/video_core/vulkan_common/vulkan_instance.h
@@ -17,8 +17,7 @@ namespace Vulkan {
17 * @param dld Dispatch table to load function pointers into 17 * @param dld Dispatch table to load function pointers into
18 * @param required_version Required Vulkan version (for example, VK_API_VERSION_1_1) 18 * @param required_version Required Vulkan version (for example, VK_API_VERSION_1_1)
19 * @param window_type Window system type's enabled extension 19 * @param window_type Window system type's enabled extension
20 * @param enable_debug_utils Whether to enable VK_EXT_debug_utils_extension_name or not 20 * @param enable_validation Whether to enable Vulkan validation layers or not
21 * @param enable_layers Whether to enable Vulkan validation layers or not
22 * 21 *
23 * @return A new Vulkan instance 22 * @return A new Vulkan instance
24 * @throw vk::Exception on failure 23 * @throw vk::Exception on failure
@@ -26,6 +25,6 @@ namespace Vulkan {
26[[nodiscard]] vk::Instance CreateInstance( 25[[nodiscard]] vk::Instance CreateInstance(
27 const Common::DynamicLibrary& library, vk::InstanceDispatch& dld, u32 required_version, 26 const Common::DynamicLibrary& library, vk::InstanceDispatch& dld, u32 required_version,
28 Core::Frontend::WindowSystemType window_type = Core::Frontend::WindowSystemType::Headless, 27 Core::Frontend::WindowSystemType window_type = Core::Frontend::WindowSystemType::Headless,
29 bool enable_debug_utils = false, bool enable_layers = false); 28 bool enable_validation = false);
30 29
31} // namespace Vulkan 30} // namespace Vulkan
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp
index 7dca7341c..61be1fce1 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.cpp
+++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp
@@ -94,6 +94,10 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
94 X(vkCmdDispatch); 94 X(vkCmdDispatch);
95 X(vkCmdDraw); 95 X(vkCmdDraw);
96 X(vkCmdDrawIndexed); 96 X(vkCmdDrawIndexed);
97 X(vkCmdDrawIndirect);
98 X(vkCmdDrawIndexedIndirect);
99 X(vkCmdDrawIndirectCountKHR);
100 X(vkCmdDrawIndexedIndirectCountKHR);
97 X(vkCmdEndQuery); 101 X(vkCmdEndQuery);
98 X(vkCmdEndRenderPass); 102 X(vkCmdEndRenderPass);
99 X(vkCmdEndTransformFeedbackEXT); 103 X(vkCmdEndTransformFeedbackEXT);
@@ -118,12 +122,22 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
118 X(vkCmdSetDepthCompareOpEXT); 122 X(vkCmdSetDepthCompareOpEXT);
119 X(vkCmdSetDepthTestEnableEXT); 123 X(vkCmdSetDepthTestEnableEXT);
120 X(vkCmdSetDepthWriteEnableEXT); 124 X(vkCmdSetDepthWriteEnableEXT);
125 X(vkCmdSetPrimitiveRestartEnableEXT);
126 X(vkCmdSetRasterizerDiscardEnableEXT);
127 X(vkCmdSetDepthBiasEnableEXT);
128 X(vkCmdSetLogicOpEnableEXT);
129 X(vkCmdSetDepthClampEnableEXT);
121 X(vkCmdSetFrontFaceEXT); 130 X(vkCmdSetFrontFaceEXT);
131 X(vkCmdSetLogicOpEXT);
132 X(vkCmdSetPatchControlPointsEXT);
122 X(vkCmdSetLineWidth); 133 X(vkCmdSetLineWidth);
123 X(vkCmdSetPrimitiveTopologyEXT); 134 X(vkCmdSetPrimitiveTopologyEXT);
124 X(vkCmdSetStencilOpEXT); 135 X(vkCmdSetStencilOpEXT);
125 X(vkCmdSetStencilTestEnableEXT); 136 X(vkCmdSetStencilTestEnableEXT);
126 X(vkCmdSetVertexInputEXT); 137 X(vkCmdSetVertexInputEXT);
138 X(vkCmdSetColorWriteMaskEXT);
139 X(vkCmdSetColorBlendEnableEXT);
140 X(vkCmdSetColorBlendEquationEXT);
127 X(vkCmdResolveImage); 141 X(vkCmdResolveImage);
128 X(vkCreateBuffer); 142 X(vkCreateBuffer);
129 X(vkCreateBufferView); 143 X(vkCreateBufferView);
@@ -138,6 +152,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
138 X(vkCreateGraphicsPipelines); 152 X(vkCreateGraphicsPipelines);
139 X(vkCreateImage); 153 X(vkCreateImage);
140 X(vkCreateImageView); 154 X(vkCreateImageView);
155 X(vkCreatePipelineCache);
141 X(vkCreatePipelineLayout); 156 X(vkCreatePipelineLayout);
142 X(vkCreateQueryPool); 157 X(vkCreateQueryPool);
143 X(vkCreateRenderPass); 158 X(vkCreateRenderPass);
@@ -157,6 +172,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
157 X(vkDestroyImage); 172 X(vkDestroyImage);
158 X(vkDestroyImageView); 173 X(vkDestroyImageView);
159 X(vkDestroyPipeline); 174 X(vkDestroyPipeline);
175 X(vkDestroyPipelineCache);
160 X(vkDestroyPipelineLayout); 176 X(vkDestroyPipelineLayout);
161 X(vkDestroyQueryPool); 177 X(vkDestroyQueryPool);
162 X(vkDestroyRenderPass); 178 X(vkDestroyRenderPass);
@@ -174,6 +190,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
174 X(vkGetEventStatus); 190 X(vkGetEventStatus);
175 X(vkGetFenceStatus); 191 X(vkGetFenceStatus);
176 X(vkGetImageMemoryRequirements); 192 X(vkGetImageMemoryRequirements);
193 X(vkGetPipelineCacheData);
177 X(vkGetMemoryFdKHR); 194 X(vkGetMemoryFdKHR);
178#ifdef _WIN32 195#ifdef _WIN32
179 X(vkGetMemoryWin32HandleKHR); 196 X(vkGetMemoryWin32HandleKHR);
@@ -417,6 +434,10 @@ void Destroy(VkDevice device, VkPipeline handle, const DeviceDispatch& dld) noex
417 dld.vkDestroyPipeline(device, handle, nullptr); 434 dld.vkDestroyPipeline(device, handle, nullptr);
418} 435}
419 436
437void Destroy(VkDevice device, VkPipelineCache handle, const DeviceDispatch& dld) noexcept {
438 dld.vkDestroyPipelineCache(device, handle, nullptr);
439}
440
420void Destroy(VkDevice device, VkPipelineLayout handle, const DeviceDispatch& dld) noexcept { 441void Destroy(VkDevice device, VkPipelineLayout handle, const DeviceDispatch& dld) noexcept {
421 dld.vkDestroyPipelineLayout(device, handle, nullptr); 442 dld.vkDestroyPipelineLayout(device, handle, nullptr);
422} 443}
@@ -637,6 +658,10 @@ void ShaderModule::SetObjectNameEXT(const char* name) const {
637 SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_SHADER_MODULE, name); 658 SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_SHADER_MODULE, name);
638} 659}
639 660
661void PipelineCache::SetObjectNameEXT(const char* name) const {
662 SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_PIPELINE_CACHE, name);
663}
664
640void Semaphore::SetObjectNameEXT(const char* name) const { 665void Semaphore::SetObjectNameEXT(const char* name) const {
641 SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_SEMAPHORE, name); 666 SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_SEMAPHORE, name);
642} 667}
@@ -732,21 +757,29 @@ DescriptorSetLayout Device::CreateDescriptorSetLayout(
732 return DescriptorSetLayout(object, handle, *dld); 757 return DescriptorSetLayout(object, handle, *dld);
733} 758}
734 759
760PipelineCache Device::CreatePipelineCache(const VkPipelineCacheCreateInfo& ci) const {
761 VkPipelineCache cache;
762 Check(dld->vkCreatePipelineCache(handle, &ci, nullptr, &cache));
763 return PipelineCache(cache, handle, *dld);
764}
765
735PipelineLayout Device::CreatePipelineLayout(const VkPipelineLayoutCreateInfo& ci) const { 766PipelineLayout Device::CreatePipelineLayout(const VkPipelineLayoutCreateInfo& ci) const {
736 VkPipelineLayout object; 767 VkPipelineLayout object;
737 Check(dld->vkCreatePipelineLayout(handle, &ci, nullptr, &object)); 768 Check(dld->vkCreatePipelineLayout(handle, &ci, nullptr, &object));
738 return PipelineLayout(object, handle, *dld); 769 return PipelineLayout(object, handle, *dld);
739} 770}
740 771
741Pipeline Device::CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci) const { 772Pipeline Device::CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci,
773 VkPipelineCache cache) const {
742 VkPipeline object; 774 VkPipeline object;
743 Check(dld->vkCreateGraphicsPipelines(handle, nullptr, 1, &ci, nullptr, &object)); 775 Check(dld->vkCreateGraphicsPipelines(handle, cache, 1, &ci, nullptr, &object));
744 return Pipeline(object, handle, *dld); 776 return Pipeline(object, handle, *dld);
745} 777}
746 778
747Pipeline Device::CreateComputePipeline(const VkComputePipelineCreateInfo& ci) const { 779Pipeline Device::CreateComputePipeline(const VkComputePipelineCreateInfo& ci,
780 VkPipelineCache cache) const {
748 VkPipeline object; 781 VkPipeline object;
749 Check(dld->vkCreateComputePipelines(handle, nullptr, 1, &ci, nullptr, &object)); 782 Check(dld->vkCreateComputePipelines(handle, cache, 1, &ci, nullptr, &object));
750 return Pipeline(object, handle, *dld); 783 return Pipeline(object, handle, *dld);
751} 784}
752 785
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h
index 8bd4fd4d9..412779b51 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.h
+++ b/src/video_core/vulkan_common/vulkan_wrapper.h
@@ -213,6 +213,10 @@ struct DeviceDispatch : InstanceDispatch {
213 PFN_vkCmdDispatch vkCmdDispatch{}; 213 PFN_vkCmdDispatch vkCmdDispatch{};
214 PFN_vkCmdDraw vkCmdDraw{}; 214 PFN_vkCmdDraw vkCmdDraw{};
215 PFN_vkCmdDrawIndexed vkCmdDrawIndexed{}; 215 PFN_vkCmdDrawIndexed vkCmdDrawIndexed{};
216 PFN_vkCmdDrawIndirect vkCmdDrawIndirect{};
217 PFN_vkCmdDrawIndexedIndirect vkCmdDrawIndexedIndirect{};
218 PFN_vkCmdDrawIndirectCountKHR vkCmdDrawIndirectCountKHR{};
219 PFN_vkCmdDrawIndexedIndirectCountKHR vkCmdDrawIndexedIndirectCountKHR{};
216 PFN_vkCmdEndDebugUtilsLabelEXT vkCmdEndDebugUtilsLabelEXT{}; 220 PFN_vkCmdEndDebugUtilsLabelEXT vkCmdEndDebugUtilsLabelEXT{};
217 PFN_vkCmdEndQuery vkCmdEndQuery{}; 221 PFN_vkCmdEndQuery vkCmdEndQuery{};
218 PFN_vkCmdEndRenderPass vkCmdEndRenderPass{}; 222 PFN_vkCmdEndRenderPass vkCmdEndRenderPass{};
@@ -230,8 +234,15 @@ struct DeviceDispatch : InstanceDispatch {
230 PFN_vkCmdSetDepthCompareOpEXT vkCmdSetDepthCompareOpEXT{}; 234 PFN_vkCmdSetDepthCompareOpEXT vkCmdSetDepthCompareOpEXT{};
231 PFN_vkCmdSetDepthTestEnableEXT vkCmdSetDepthTestEnableEXT{}; 235 PFN_vkCmdSetDepthTestEnableEXT vkCmdSetDepthTestEnableEXT{};
232 PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT{}; 236 PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT{};
237 PFN_vkCmdSetPrimitiveRestartEnableEXT vkCmdSetPrimitiveRestartEnableEXT{};
238 PFN_vkCmdSetRasterizerDiscardEnableEXT vkCmdSetRasterizerDiscardEnableEXT{};
239 PFN_vkCmdSetDepthBiasEnableEXT vkCmdSetDepthBiasEnableEXT{};
240 PFN_vkCmdSetLogicOpEnableEXT vkCmdSetLogicOpEnableEXT{};
241 PFN_vkCmdSetDepthClampEnableEXT vkCmdSetDepthClampEnableEXT{};
233 PFN_vkCmdSetEvent vkCmdSetEvent{}; 242 PFN_vkCmdSetEvent vkCmdSetEvent{};
234 PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT{}; 243 PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT{};
244 PFN_vkCmdSetPatchControlPointsEXT vkCmdSetPatchControlPointsEXT{};
245 PFN_vkCmdSetLogicOpEXT vkCmdSetLogicOpEXT{};
235 PFN_vkCmdSetLineWidth vkCmdSetLineWidth{}; 246 PFN_vkCmdSetLineWidth vkCmdSetLineWidth{};
236 PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT{}; 247 PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT{};
237 PFN_vkCmdSetScissor vkCmdSetScissor{}; 248 PFN_vkCmdSetScissor vkCmdSetScissor{};
@@ -242,6 +253,9 @@ struct DeviceDispatch : InstanceDispatch {
242 PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask{}; 253 PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask{};
243 PFN_vkCmdSetVertexInputEXT vkCmdSetVertexInputEXT{}; 254 PFN_vkCmdSetVertexInputEXT vkCmdSetVertexInputEXT{};
244 PFN_vkCmdSetViewport vkCmdSetViewport{}; 255 PFN_vkCmdSetViewport vkCmdSetViewport{};
256 PFN_vkCmdSetColorWriteMaskEXT vkCmdSetColorWriteMaskEXT{};
257 PFN_vkCmdSetColorBlendEnableEXT vkCmdSetColorBlendEnableEXT{};
258 PFN_vkCmdSetColorBlendEquationEXT vkCmdSetColorBlendEquationEXT{};
245 PFN_vkCmdWaitEvents vkCmdWaitEvents{}; 259 PFN_vkCmdWaitEvents vkCmdWaitEvents{};
246 PFN_vkCreateBuffer vkCreateBuffer{}; 260 PFN_vkCreateBuffer vkCreateBuffer{};
247 PFN_vkCreateBufferView vkCreateBufferView{}; 261 PFN_vkCreateBufferView vkCreateBufferView{};
@@ -256,6 +270,7 @@ struct DeviceDispatch : InstanceDispatch {
256 PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines{}; 270 PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines{};
257 PFN_vkCreateImage vkCreateImage{}; 271 PFN_vkCreateImage vkCreateImage{};
258 PFN_vkCreateImageView vkCreateImageView{}; 272 PFN_vkCreateImageView vkCreateImageView{};
273 PFN_vkCreatePipelineCache vkCreatePipelineCache{};
259 PFN_vkCreatePipelineLayout vkCreatePipelineLayout{}; 274 PFN_vkCreatePipelineLayout vkCreatePipelineLayout{};
260 PFN_vkCreateQueryPool vkCreateQueryPool{}; 275 PFN_vkCreateQueryPool vkCreateQueryPool{};
261 PFN_vkCreateRenderPass vkCreateRenderPass{}; 276 PFN_vkCreateRenderPass vkCreateRenderPass{};
@@ -275,6 +290,7 @@ struct DeviceDispatch : InstanceDispatch {
275 PFN_vkDestroyImage vkDestroyImage{}; 290 PFN_vkDestroyImage vkDestroyImage{};
276 PFN_vkDestroyImageView vkDestroyImageView{}; 291 PFN_vkDestroyImageView vkDestroyImageView{};
277 PFN_vkDestroyPipeline vkDestroyPipeline{}; 292 PFN_vkDestroyPipeline vkDestroyPipeline{};
293 PFN_vkDestroyPipelineCache vkDestroyPipelineCache{};
278 PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout{}; 294 PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout{};
279 PFN_vkDestroyQueryPool vkDestroyQueryPool{}; 295 PFN_vkDestroyQueryPool vkDestroyQueryPool{};
280 PFN_vkDestroyRenderPass vkDestroyRenderPass{}; 296 PFN_vkDestroyRenderPass vkDestroyRenderPass{};
@@ -292,6 +308,7 @@ struct DeviceDispatch : InstanceDispatch {
292 PFN_vkGetEventStatus vkGetEventStatus{}; 308 PFN_vkGetEventStatus vkGetEventStatus{};
293 PFN_vkGetFenceStatus vkGetFenceStatus{}; 309 PFN_vkGetFenceStatus vkGetFenceStatus{};
294 PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements{}; 310 PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements{};
311 PFN_vkGetPipelineCacheData vkGetPipelineCacheData{};
295 PFN_vkGetMemoryFdKHR vkGetMemoryFdKHR{}; 312 PFN_vkGetMemoryFdKHR vkGetMemoryFdKHR{};
296#ifdef _WIN32 313#ifdef _WIN32
297 PFN_vkGetMemoryWin32HandleKHR vkGetMemoryWin32HandleKHR{}; 314 PFN_vkGetMemoryWin32HandleKHR vkGetMemoryWin32HandleKHR{};
@@ -337,6 +354,7 @@ void Destroy(VkDevice, VkFramebuffer, const DeviceDispatch&) noexcept;
337void Destroy(VkDevice, VkImage, const DeviceDispatch&) noexcept; 354void Destroy(VkDevice, VkImage, const DeviceDispatch&) noexcept;
338void Destroy(VkDevice, VkImageView, const DeviceDispatch&) noexcept; 355void Destroy(VkDevice, VkImageView, const DeviceDispatch&) noexcept;
339void Destroy(VkDevice, VkPipeline, const DeviceDispatch&) noexcept; 356void Destroy(VkDevice, VkPipeline, const DeviceDispatch&) noexcept;
357void Destroy(VkDevice, VkPipelineCache, const DeviceDispatch&) noexcept;
340void Destroy(VkDevice, VkPipelineLayout, const DeviceDispatch&) noexcept; 358void Destroy(VkDevice, VkPipelineLayout, const DeviceDispatch&) noexcept;
341void Destroy(VkDevice, VkQueryPool, const DeviceDispatch&) noexcept; 359void Destroy(VkDevice, VkQueryPool, const DeviceDispatch&) noexcept;
342void Destroy(VkDevice, VkRenderPass, const DeviceDispatch&) noexcept; 360void Destroy(VkDevice, VkRenderPass, const DeviceDispatch&) noexcept;
@@ -759,6 +777,18 @@ public:
759 void SetObjectNameEXT(const char* name) const; 777 void SetObjectNameEXT(const char* name) const;
760}; 778};
761 779
780class PipelineCache : public Handle<VkPipelineCache, VkDevice, DeviceDispatch> {
781 using Handle<VkPipelineCache, VkDevice, DeviceDispatch>::Handle;
782
783public:
784 /// Set object name.
785 void SetObjectNameEXT(const char* name) const;
786
787 VkResult Read(size_t* size, void* data) const noexcept {
788 return dld->vkGetPipelineCacheData(owner, handle, size, data);
789 }
790};
791
762class Semaphore : public Handle<VkSemaphore, VkDevice, DeviceDispatch> { 792class Semaphore : public Handle<VkSemaphore, VkDevice, DeviceDispatch> {
763 using Handle<VkSemaphore, VkDevice, DeviceDispatch>::Handle; 793 using Handle<VkSemaphore, VkDevice, DeviceDispatch>::Handle;
764 794
@@ -830,11 +860,15 @@ public:
830 860
831 DescriptorSetLayout CreateDescriptorSetLayout(const VkDescriptorSetLayoutCreateInfo& ci) const; 861 DescriptorSetLayout CreateDescriptorSetLayout(const VkDescriptorSetLayoutCreateInfo& ci) const;
832 862
863 PipelineCache CreatePipelineCache(const VkPipelineCacheCreateInfo& ci) const;
864
833 PipelineLayout CreatePipelineLayout(const VkPipelineLayoutCreateInfo& ci) const; 865 PipelineLayout CreatePipelineLayout(const VkPipelineLayoutCreateInfo& ci) const;
834 866
835 Pipeline CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci) const; 867 Pipeline CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci,
868 VkPipelineCache cache = nullptr) const;
836 869
837 Pipeline CreateComputePipeline(const VkComputePipelineCreateInfo& ci) const; 870 Pipeline CreateComputePipeline(const VkComputePipelineCreateInfo& ci,
871 VkPipelineCache cache = nullptr) const;
838 872
839 Sampler CreateSampler(const VkSamplerCreateInfo& ci) const; 873 Sampler CreateSampler(const VkSamplerCreateInfo& ci) const;
840 874
@@ -1019,6 +1053,29 @@ public:
1019 first_instance); 1053 first_instance);
1020 } 1054 }
1021 1055
1056 void DrawIndirect(VkBuffer src_buffer, VkDeviceSize src_offset, u32 draw_count,
1057 u32 stride) const noexcept {
1058 dld->vkCmdDrawIndirect(handle, src_buffer, src_offset, draw_count, stride);
1059 }
1060
1061 void DrawIndexedIndirect(VkBuffer src_buffer, VkDeviceSize src_offset, u32 draw_count,
1062 u32 stride) const noexcept {
1063 dld->vkCmdDrawIndexedIndirect(handle, src_buffer, src_offset, draw_count, stride);
1064 }
1065
1066 void DrawIndirectCount(VkBuffer src_buffer, VkDeviceSize src_offset, VkBuffer count_buffer,
1067 VkDeviceSize count_offset, u32 draw_count, u32 stride) const noexcept {
1068 dld->vkCmdDrawIndirectCountKHR(handle, src_buffer, src_offset, count_buffer, count_offset,
1069 draw_count, stride);
1070 }
1071
1072 void DrawIndexedIndirectCount(VkBuffer src_buffer, VkDeviceSize src_offset,
1073 VkBuffer count_buffer, VkDeviceSize count_offset, u32 draw_count,
1074 u32 stride) const noexcept {
1075 dld->vkCmdDrawIndexedIndirectCountKHR(handle, src_buffer, src_offset, count_buffer,
1076 count_offset, draw_count, stride);
1077 }
1078
1022 void ClearAttachments(Span<VkClearAttachment> attachments, 1079 void ClearAttachments(Span<VkClearAttachment> attachments,
1023 Span<VkClearRect> rects) const noexcept { 1080 Span<VkClearRect> rects) const noexcept {
1024 dld->vkCmdClearAttachments(handle, attachments.size(), attachments.data(), rects.size(), 1081 dld->vkCmdClearAttachments(handle, attachments.size(), attachments.data(), rects.size(),
@@ -1192,10 +1249,51 @@ public:
1192 dld->vkCmdSetDepthWriteEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); 1249 dld->vkCmdSetDepthWriteEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
1193 } 1250 }
1194 1251
1252 void SetPrimitiveRestartEnableEXT(bool enable) const noexcept {
1253 dld->vkCmdSetPrimitiveRestartEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
1254 }
1255
1256 void SetRasterizerDiscardEnableEXT(bool enable) const noexcept {
1257 dld->vkCmdSetRasterizerDiscardEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
1258 }
1259
1260 void SetDepthBiasEnableEXT(bool enable) const noexcept {
1261 dld->vkCmdSetDepthBiasEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
1262 }
1263
1264 void SetLogicOpEnableEXT(bool enable) const noexcept {
1265 dld->vkCmdSetLogicOpEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
1266 }
1267
1268 void SetDepthClampEnableEXT(bool enable) const noexcept {
1269 dld->vkCmdSetDepthClampEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
1270 }
1271
1195 void SetFrontFaceEXT(VkFrontFace front_face) const noexcept { 1272 void SetFrontFaceEXT(VkFrontFace front_face) const noexcept {
1196 dld->vkCmdSetFrontFaceEXT(handle, front_face); 1273 dld->vkCmdSetFrontFaceEXT(handle, front_face);
1197 } 1274 }
1198 1275
1276 void SetLogicOpEXT(VkLogicOp logic_op) const noexcept {
1277 dld->vkCmdSetLogicOpEXT(handle, logic_op);
1278 }
1279
1280 void SetPatchControlPointsEXT(uint32_t patch_control_points) const noexcept {
1281 dld->vkCmdSetPatchControlPointsEXT(handle, patch_control_points);
1282 }
1283
1284 void SetColorWriteMaskEXT(u32 first, Span<VkColorComponentFlags> masks) const noexcept {
1285 dld->vkCmdSetColorWriteMaskEXT(handle, first, masks.size(), masks.data());
1286 }
1287
1288 void SetColorBlendEnableEXT(u32 first, Span<VkBool32> enables) const noexcept {
1289 dld->vkCmdSetColorBlendEnableEXT(handle, first, enables.size(), enables.data());
1290 }
1291
1292 void SetColorBlendEquationEXT(u32 first,
1293 Span<VkColorBlendEquationEXT> equations) const noexcept {
1294 dld->vkCmdSetColorBlendEquationEXT(handle, first, equations.size(), equations.data());
1295 }
1296
1199 void SetLineWidth(float line_width) const noexcept { 1297 void SetLineWidth(float line_width) const noexcept {
1200 dld->vkCmdSetLineWidth(handle, line_width); 1298 dld->vkCmdSetLineWidth(handle, line_width);
1201 } 1299 }
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 3e51426c8..0db62baa3 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -562,6 +562,7 @@ void Config::ReadDebuggingValues() {
562 ReadBasicSetting(Settings::values.reporting_services); 562 ReadBasicSetting(Settings::values.reporting_services);
563 ReadBasicSetting(Settings::values.quest_flag); 563 ReadBasicSetting(Settings::values.quest_flag);
564 ReadBasicSetting(Settings::values.disable_macro_jit); 564 ReadBasicSetting(Settings::values.disable_macro_jit);
565 ReadBasicSetting(Settings::values.disable_macro_hle);
565 ReadBasicSetting(Settings::values.extended_logging); 566 ReadBasicSetting(Settings::values.extended_logging);
566 ReadBasicSetting(Settings::values.use_debug_asserts); 567 ReadBasicSetting(Settings::values.use_debug_asserts);
567 ReadBasicSetting(Settings::values.use_auto_stub); 568 ReadBasicSetting(Settings::values.use_auto_stub);
@@ -689,6 +690,7 @@ void Config::ReadRendererValues() {
689 qt_config->beginGroup(QStringLiteral("Renderer")); 690 qt_config->beginGroup(QStringLiteral("Renderer"));
690 691
691 ReadGlobalSetting(Settings::values.renderer_backend); 692 ReadGlobalSetting(Settings::values.renderer_backend);
693 ReadGlobalSetting(Settings::values.renderer_force_max_clock);
692 ReadGlobalSetting(Settings::values.vulkan_device); 694 ReadGlobalSetting(Settings::values.vulkan_device);
693 ReadGlobalSetting(Settings::values.fullscreen_mode); 695 ReadGlobalSetting(Settings::values.fullscreen_mode);
694 ReadGlobalSetting(Settings::values.aspect_ratio); 696 ReadGlobalSetting(Settings::values.aspect_ratio);
@@ -708,6 +710,7 @@ void Config::ReadRendererValues() {
708 ReadGlobalSetting(Settings::values.use_asynchronous_shaders); 710 ReadGlobalSetting(Settings::values.use_asynchronous_shaders);
709 ReadGlobalSetting(Settings::values.use_fast_gpu_time); 711 ReadGlobalSetting(Settings::values.use_fast_gpu_time);
710 ReadGlobalSetting(Settings::values.use_pessimistic_flushes); 712 ReadGlobalSetting(Settings::values.use_pessimistic_flushes);
713 ReadGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache);
711 ReadGlobalSetting(Settings::values.bg_red); 714 ReadGlobalSetting(Settings::values.bg_red);
712 ReadGlobalSetting(Settings::values.bg_green); 715 ReadGlobalSetting(Settings::values.bg_green);
713 ReadGlobalSetting(Settings::values.bg_blue); 716 ReadGlobalSetting(Settings::values.bg_blue);
@@ -1198,6 +1201,7 @@ void Config::SaveDebuggingValues() {
1198 WriteBasicSetting(Settings::values.quest_flag); 1201 WriteBasicSetting(Settings::values.quest_flag);
1199 WriteBasicSetting(Settings::values.use_debug_asserts); 1202 WriteBasicSetting(Settings::values.use_debug_asserts);
1200 WriteBasicSetting(Settings::values.disable_macro_jit); 1203 WriteBasicSetting(Settings::values.disable_macro_jit);
1204 WriteBasicSetting(Settings::values.disable_macro_hle);
1201 WriteBasicSetting(Settings::values.enable_all_controllers); 1205 WriteBasicSetting(Settings::values.enable_all_controllers);
1202 WriteBasicSetting(Settings::values.create_crash_dumps); 1206 WriteBasicSetting(Settings::values.create_crash_dumps);
1203 WriteBasicSetting(Settings::values.perform_vulkan_check); 1207 WriteBasicSetting(Settings::values.perform_vulkan_check);
@@ -1303,6 +1307,9 @@ void Config::SaveRendererValues() {
1303 static_cast<u32>(Settings::values.renderer_backend.GetValue(global)), 1307 static_cast<u32>(Settings::values.renderer_backend.GetValue(global)),
1304 static_cast<u32>(Settings::values.renderer_backend.GetDefault()), 1308 static_cast<u32>(Settings::values.renderer_backend.GetDefault()),
1305 Settings::values.renderer_backend.UsingGlobal()); 1309 Settings::values.renderer_backend.UsingGlobal());
1310 WriteSetting(QString::fromStdString(Settings::values.renderer_force_max_clock.GetLabel()),
1311 static_cast<u32>(Settings::values.renderer_force_max_clock.GetValue(global)),
1312 static_cast<u32>(Settings::values.renderer_force_max_clock.GetDefault()));
1306 WriteGlobalSetting(Settings::values.vulkan_device); 1313 WriteGlobalSetting(Settings::values.vulkan_device);
1307 WriteSetting(QString::fromStdString(Settings::values.fullscreen_mode.GetLabel()), 1314 WriteSetting(QString::fromStdString(Settings::values.fullscreen_mode.GetLabel()),
1308 static_cast<u32>(Settings::values.fullscreen_mode.GetValue(global)), 1315 static_cast<u32>(Settings::values.fullscreen_mode.GetValue(global)),
@@ -1346,6 +1353,7 @@ void Config::SaveRendererValues() {
1346 WriteGlobalSetting(Settings::values.use_asynchronous_shaders); 1353 WriteGlobalSetting(Settings::values.use_asynchronous_shaders);
1347 WriteGlobalSetting(Settings::values.use_fast_gpu_time); 1354 WriteGlobalSetting(Settings::values.use_fast_gpu_time);
1348 WriteGlobalSetting(Settings::values.use_pessimistic_flushes); 1355 WriteGlobalSetting(Settings::values.use_pessimistic_flushes);
1356 WriteGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache);
1349 WriteGlobalSetting(Settings::values.bg_red); 1357 WriteGlobalSetting(Settings::values.bg_red);
1350 WriteGlobalSetting(Settings::values.bg_green); 1358 WriteGlobalSetting(Settings::values.bg_green);
1351 WriteGlobalSetting(Settings::values.bg_blue); 1359 WriteGlobalSetting(Settings::values.bg_blue);
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index dacc75a20..cbeb8f168 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -73,6 +73,8 @@ void ConfigureDebug::SetConfiguration() {
73 ui->dump_macros->setChecked(Settings::values.dump_macros.GetValue()); 73 ui->dump_macros->setChecked(Settings::values.dump_macros.GetValue());
74 ui->disable_macro_jit->setEnabled(runtime_lock); 74 ui->disable_macro_jit->setEnabled(runtime_lock);
75 ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit.GetValue()); 75 ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit.GetValue());
76 ui->disable_macro_hle->setEnabled(runtime_lock);
77 ui->disable_macro_hle->setChecked(Settings::values.disable_macro_hle.GetValue());
76 ui->disable_loop_safety_checks->setEnabled(runtime_lock); 78 ui->disable_loop_safety_checks->setEnabled(runtime_lock);
77 ui->disable_loop_safety_checks->setChecked( 79 ui->disable_loop_safety_checks->setChecked(
78 Settings::values.disable_shader_loop_safety_checks.GetValue()); 80 Settings::values.disable_shader_loop_safety_checks.GetValue());
@@ -117,6 +119,7 @@ void ConfigureDebug::ApplyConfiguration() {
117 Settings::values.disable_shader_loop_safety_checks = 119 Settings::values.disable_shader_loop_safety_checks =
118 ui->disable_loop_safety_checks->isChecked(); 120 ui->disable_loop_safety_checks->isChecked();
119 Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked(); 121 Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked();
122 Settings::values.disable_macro_hle = ui->disable_macro_hle->isChecked();
120 Settings::values.extended_logging = ui->extended_logging->isChecked(); 123 Settings::values.extended_logging = ui->extended_logging->isChecked();
121 Settings::values.perform_vulkan_check = ui->perform_vulkan_check->isChecked(); 124 Settings::values.perform_vulkan_check = ui->perform_vulkan_check->isChecked();
122 UISettings::values.disable_web_applet = ui->disable_web_applet->isChecked(); 125 UISettings::values.disable_web_applet = ui->disable_web_applet->isChecked();
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 102c8c66c..15acefe33 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -176,7 +176,7 @@
176 </property> 176 </property>
177 </widget> 177 </widget>
178 </item> 178 </item>
179 <item row="0" column="2"> 179 <item row="1" column="2">
180 <widget class="QCheckBox" name="dump_macros"> 180 <widget class="QCheckBox" name="dump_macros">
181 <property name="enabled"> 181 <property name="enabled">
182 <bool>true</bool> 182 <bool>true</bool>
@@ -202,6 +202,19 @@
202 </property> 202 </property>
203 </widget> 203 </widget>
204 </item> 204 </item>
205 <item row="0" column="2">
206 <widget class="QCheckBox" name="disable_macro_hle">
207 <property name="enabled">
208 <bool>true</bool>
209 </property>
210 <property name="toolTip">
211 <string>When checked, it disables the macro HLE functions. Enabling this makes games run slower</string>
212 </property>
213 <property name="text">
214 <string>Disable Macro HLE</string>
215 </property>
216 </widget>
217 </item>
205 <item row="1" column="0"> 218 <item row="1" column="0">
206 <widget class="QCheckBox" name="enable_shader_feedback"> 219 <widget class="QCheckBox" name="enable_shader_feedback">
207 <property name="toolTip"> 220 <property name="toolTip">
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp
index 01f074699..fdf8485ce 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.cpp
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -25,10 +25,13 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
25 ui->use_asynchronous_shaders->setEnabled(runtime_lock); 25 ui->use_asynchronous_shaders->setEnabled(runtime_lock);
26 ui->anisotropic_filtering_combobox->setEnabled(runtime_lock); 26 ui->anisotropic_filtering_combobox->setEnabled(runtime_lock);
27 27
28 ui->renderer_force_max_clock->setChecked(Settings::values.renderer_force_max_clock.GetValue());
28 ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue()); 29 ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue());
29 ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue()); 30 ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue());
30 ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue()); 31 ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue());
31 ui->use_pessimistic_flushes->setChecked(Settings::values.use_pessimistic_flushes.GetValue()); 32 ui->use_pessimistic_flushes->setChecked(Settings::values.use_pessimistic_flushes.GetValue());
33 ui->use_vulkan_driver_pipeline_cache->setChecked(
34 Settings::values.use_vulkan_driver_pipeline_cache.GetValue());
32 35
33 if (Settings::IsConfiguringGlobal()) { 36 if (Settings::IsConfiguringGlobal()) {
34 ui->gpu_accuracy->setCurrentIndex( 37 ui->gpu_accuracy->setCurrentIndex(
@@ -37,6 +40,8 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
37 Settings::values.max_anisotropy.GetValue()); 40 Settings::values.max_anisotropy.GetValue());
38 } else { 41 } else {
39 ConfigurationShared::SetPerGameSetting(ui->gpu_accuracy, &Settings::values.gpu_accuracy); 42 ConfigurationShared::SetPerGameSetting(ui->gpu_accuracy, &Settings::values.gpu_accuracy);
43 ConfigurationShared::SetPerGameSetting(ui->renderer_force_max_clock,
44 &Settings::values.renderer_force_max_clock);
40 ConfigurationShared::SetPerGameSetting(ui->anisotropic_filtering_combobox, 45 ConfigurationShared::SetPerGameSetting(ui->anisotropic_filtering_combobox,
41 &Settings::values.max_anisotropy); 46 &Settings::values.max_anisotropy);
42 ConfigurationShared::SetHighlight(ui->label_gpu_accuracy, 47 ConfigurationShared::SetHighlight(ui->label_gpu_accuracy,
@@ -48,6 +53,9 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
48 53
49void ConfigureGraphicsAdvanced::ApplyConfiguration() { 54void ConfigureGraphicsAdvanced::ApplyConfiguration() {
50 ConfigurationShared::ApplyPerGameSetting(&Settings::values.gpu_accuracy, ui->gpu_accuracy); 55 ConfigurationShared::ApplyPerGameSetting(&Settings::values.gpu_accuracy, ui->gpu_accuracy);
56 ConfigurationShared::ApplyPerGameSetting(&Settings::values.renderer_force_max_clock,
57 ui->renderer_force_max_clock,
58 renderer_force_max_clock);
51 ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy, 59 ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy,
52 ui->anisotropic_filtering_combobox); 60 ui->anisotropic_filtering_combobox);
53 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync, use_vsync); 61 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync, use_vsync);
@@ -58,6 +66,9 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
58 ui->use_fast_gpu_time, use_fast_gpu_time); 66 ui->use_fast_gpu_time, use_fast_gpu_time);
59 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_pessimistic_flushes, 67 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_pessimistic_flushes,
60 ui->use_pessimistic_flushes, use_pessimistic_flushes); 68 ui->use_pessimistic_flushes, use_pessimistic_flushes);
69 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vulkan_driver_pipeline_cache,
70 ui->use_vulkan_driver_pipeline_cache,
71 use_vulkan_driver_pipeline_cache);
61} 72}
62 73
63void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) { 74void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
@@ -76,18 +87,25 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
76 // Disable if not global (only happens during game) 87 // Disable if not global (only happens during game)
77 if (Settings::IsConfiguringGlobal()) { 88 if (Settings::IsConfiguringGlobal()) {
78 ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal()); 89 ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal());
90 ui->renderer_force_max_clock->setEnabled(
91 Settings::values.renderer_force_max_clock.UsingGlobal());
79 ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal()); 92 ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal());
80 ui->use_asynchronous_shaders->setEnabled( 93 ui->use_asynchronous_shaders->setEnabled(
81 Settings::values.use_asynchronous_shaders.UsingGlobal()); 94 Settings::values.use_asynchronous_shaders.UsingGlobal());
82 ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal()); 95 ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal());
83 ui->use_pessimistic_flushes->setEnabled( 96 ui->use_pessimistic_flushes->setEnabled(
84 Settings::values.use_pessimistic_flushes.UsingGlobal()); 97 Settings::values.use_pessimistic_flushes.UsingGlobal());
98 ui->use_vulkan_driver_pipeline_cache->setEnabled(
99 Settings::values.use_vulkan_driver_pipeline_cache.UsingGlobal());
85 ui->anisotropic_filtering_combobox->setEnabled( 100 ui->anisotropic_filtering_combobox->setEnabled(
86 Settings::values.max_anisotropy.UsingGlobal()); 101 Settings::values.max_anisotropy.UsingGlobal());
87 102
88 return; 103 return;
89 } 104 }
90 105
106 ConfigurationShared::SetColoredTristate(ui->renderer_force_max_clock,
107 Settings::values.renderer_force_max_clock,
108 renderer_force_max_clock);
91 ConfigurationShared::SetColoredTristate(ui->use_vsync, Settings::values.use_vsync, use_vsync); 109 ConfigurationShared::SetColoredTristate(ui->use_vsync, Settings::values.use_vsync, use_vsync);
92 ConfigurationShared::SetColoredTristate(ui->use_asynchronous_shaders, 110 ConfigurationShared::SetColoredTristate(ui->use_asynchronous_shaders,
93 Settings::values.use_asynchronous_shaders, 111 Settings::values.use_asynchronous_shaders,
@@ -97,6 +115,9 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
97 ConfigurationShared::SetColoredTristate(ui->use_pessimistic_flushes, 115 ConfigurationShared::SetColoredTristate(ui->use_pessimistic_flushes,
98 Settings::values.use_pessimistic_flushes, 116 Settings::values.use_pessimistic_flushes,
99 use_pessimistic_flushes); 117 use_pessimistic_flushes);
118 ConfigurationShared::SetColoredTristate(ui->use_vulkan_driver_pipeline_cache,
119 Settings::values.use_vulkan_driver_pipeline_cache,
120 use_vulkan_driver_pipeline_cache);
100 ConfigurationShared::SetColoredComboBox( 121 ConfigurationShared::SetColoredComboBox(
101 ui->gpu_accuracy, ui->label_gpu_accuracy, 122 ui->gpu_accuracy, ui->label_gpu_accuracy,
102 static_cast<int>(Settings::values.gpu_accuracy.GetValue(true))); 123 static_cast<int>(Settings::values.gpu_accuracy.GetValue(true)));
diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h
index 12e816905..df557d585 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.h
+++ b/src/yuzu/configuration/configure_graphics_advanced.h
@@ -36,10 +36,12 @@ private:
36 36
37 std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui; 37 std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui;
38 38
39 ConfigurationShared::CheckState renderer_force_max_clock;
39 ConfigurationShared::CheckState use_vsync; 40 ConfigurationShared::CheckState use_vsync;
40 ConfigurationShared::CheckState use_asynchronous_shaders; 41 ConfigurationShared::CheckState use_asynchronous_shaders;
41 ConfigurationShared::CheckState use_fast_gpu_time; 42 ConfigurationShared::CheckState use_fast_gpu_time;
42 ConfigurationShared::CheckState use_pessimistic_flushes; 43 ConfigurationShared::CheckState use_pessimistic_flushes;
44 ConfigurationShared::CheckState use_vulkan_driver_pipeline_cache;
43 45
44 const Core::System& system; 46 const Core::System& system;
45}; 47};
diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui
index 87a121471..061885e30 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.ui
+++ b/src/yuzu/configuration/configure_graphics_advanced.ui
@@ -70,6 +70,16 @@
70 </widget> 70 </widget>
71 </item> 71 </item>
72 <item> 72 <item>
73 <widget class="QCheckBox" name="renderer_force_max_clock">
74 <property name="toolTip">
75 <string>Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed.</string>
76 </property>
77 <property name="text">
78 <string>Force maximum clocks (Vulkan only)</string>
79 </property>
80 </widget>
81 </item>
82 <item>
73 <widget class="QCheckBox" name="use_vsync"> 83 <widget class="QCheckBox" name="use_vsync">
74 <property name="toolTip"> 84 <property name="toolTip">
75 <string>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don't notice a performance difference.</string> 85 <string>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don't notice a performance difference.</string>
@@ -110,6 +120,16 @@
110 </widget> 120 </widget>
111 </item> 121 </item>
112 <item> 122 <item>
123 <widget class="QCheckBox" name="use_vulkan_driver_pipeline_cache">
124 <property name="toolTip">
125 <string>Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally.</string>
126 </property>
127 <property name="text">
128 <string>Use Vulkan pipeline cache</string>
129 </property>
130 </widget>
131 </item>
132 <item>
113 <widget class="QWidget" name="af_layout" native="true"> 133 <widget class="QWidget" name="af_layout" native="true">
114 <layout class="QHBoxLayout" name="horizontalLayout_1"> 134 <layout class="QHBoxLayout" name="horizontalLayout_1">
115 <property name="leftMargin"> 135 <property name="leftMargin">
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 524650144..c55f81c2f 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -2229,8 +2229,10 @@ void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget targ
2229 } 2229 }
2230 2230
2231 switch (target) { 2231 switch (target) {
2232 case GameListRemoveTarget::GlShaderCache:
2233 case GameListRemoveTarget::VkShaderCache: 2232 case GameListRemoveTarget::VkShaderCache:
2233 RemoveVulkanDriverPipelineCache(program_id);
2234 [[fallthrough]];
2235 case GameListRemoveTarget::GlShaderCache:
2234 RemoveTransferableShaderCache(program_id, target); 2236 RemoveTransferableShaderCache(program_id, target);
2235 break; 2237 break;
2236 case GameListRemoveTarget::AllShaderCache: 2238 case GameListRemoveTarget::AllShaderCache:
@@ -2271,6 +2273,22 @@ void GMainWindow::RemoveTransferableShaderCache(u64 program_id, GameListRemoveTa
2271 } 2273 }
2272} 2274}
2273 2275
2276void GMainWindow::RemoveVulkanDriverPipelineCache(u64 program_id) {
2277 static constexpr std::string_view target_file_name = "vulkan_pipelines.bin";
2278
2279 const auto shader_cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ShaderDir);
2280 const auto shader_cache_folder_path = shader_cache_dir / fmt::format("{:016x}", program_id);
2281 const auto target_file = shader_cache_folder_path / target_file_name;
2282
2283 if (!Common::FS::Exists(target_file)) {
2284 return;
2285 }
2286 if (!Common::FS::RemoveFile(target_file)) {
2287 QMessageBox::warning(this, tr("Error Removing Vulkan Driver Pipeline Cache"),
2288 tr("Failed to remove the driver pipeline cache."));
2289 }
2290}
2291
2274void GMainWindow::RemoveAllTransferableShaderCaches(u64 program_id) { 2292void GMainWindow::RemoveAllTransferableShaderCaches(u64 program_id) {
2275 const auto shader_cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ShaderDir); 2293 const auto shader_cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ShaderDir);
2276 const auto program_shader_cache_dir = shader_cache_dir / fmt::format("{:016x}", program_id); 2294 const auto program_shader_cache_dir = shader_cache_dir / fmt::format("{:016x}", program_id);
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index db318485d..f25ce65a8 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -347,6 +347,7 @@ private:
347 void RemoveUpdateContent(u64 program_id, InstalledEntryType type); 347 void RemoveUpdateContent(u64 program_id, InstalledEntryType type);
348 void RemoveAddOnContent(u64 program_id, InstalledEntryType type); 348 void RemoveAddOnContent(u64 program_id, InstalledEntryType type);
349 void RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target); 349 void RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target);
350 void RemoveVulkanDriverPipelineCache(u64 program_id);
350 void RemoveAllTransferableShaderCaches(u64 program_id); 351 void RemoveAllTransferableShaderCaches(u64 program_id);
351 void RemoveCustomConfiguration(u64 program_id, const std::string& game_path); 352 void RemoveCustomConfiguration(u64 program_id, const std::string& game_path);
352 std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); 353 std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id);
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index de9b220da..527017282 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -296,6 +296,7 @@ void Config::ReadValues() {
296 296
297 // Renderer 297 // Renderer
298 ReadSetting("Renderer", Settings::values.renderer_backend); 298 ReadSetting("Renderer", Settings::values.renderer_backend);
299 ReadSetting("Renderer", Settings::values.renderer_force_max_clock);
299 ReadSetting("Renderer", Settings::values.renderer_debug); 300 ReadSetting("Renderer", Settings::values.renderer_debug);
300 ReadSetting("Renderer", Settings::values.renderer_shader_feedback); 301 ReadSetting("Renderer", Settings::values.renderer_shader_feedback);
301 ReadSetting("Renderer", Settings::values.enable_nsight_aftermath); 302 ReadSetting("Renderer", Settings::values.enable_nsight_aftermath);
@@ -321,6 +322,7 @@ void Config::ReadValues() {
321 ReadSetting("Renderer", Settings::values.accelerate_astc); 322 ReadSetting("Renderer", Settings::values.accelerate_astc);
322 ReadSetting("Renderer", Settings::values.use_fast_gpu_time); 323 ReadSetting("Renderer", Settings::values.use_fast_gpu_time);
323 ReadSetting("Renderer", Settings::values.use_pessimistic_flushes); 324 ReadSetting("Renderer", Settings::values.use_pessimistic_flushes);
325 ReadSetting("Renderer", Settings::values.use_vulkan_driver_pipeline_cache);
324 326
325 ReadSetting("Renderer", Settings::values.bg_red); 327 ReadSetting("Renderer", Settings::values.bg_red);
326 ReadSetting("Renderer", Settings::values.bg_green); 328 ReadSetting("Renderer", Settings::values.bg_green);
@@ -348,6 +350,7 @@ void Config::ReadValues() {
348 ReadSetting("Debugging", Settings::values.use_debug_asserts); 350 ReadSetting("Debugging", Settings::values.use_debug_asserts);
349 ReadSetting("Debugging", Settings::values.use_auto_stub); 351 ReadSetting("Debugging", Settings::values.use_auto_stub);
350 ReadSetting("Debugging", Settings::values.disable_macro_jit); 352 ReadSetting("Debugging", Settings::values.disable_macro_jit);
353 ReadSetting("Debugging", Settings::values.disable_macro_hle);
351 ReadSetting("Debugging", Settings::values.use_gdbstub); 354 ReadSetting("Debugging", Settings::values.use_gdbstub);
352 ReadSetting("Debugging", Settings::values.gdbstub_port); 355 ReadSetting("Debugging", Settings::values.gdbstub_port);
353 356