diff options
Diffstat (limited to 'src')
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 | |||
| 11 | namespace Common { | ||
| 12 | |||
| 13 | template <typename KeyTBase, typename ValueT> | ||
| 14 | class RangeMap { | ||
| 15 | private: | ||
| 16 | using KeyT = | ||
| 17 | std::conditional_t<std::is_signed_v<KeyTBase>, KeyTBase, std::make_signed_t<KeyTBase>>; | ||
| 18 | |||
| 19 | public: | ||
| 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 | |||
| 57 | private: | ||
| 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 | ||
| 61 | Id GetUpperClamp(EmitContext& ctx, Id invocation_id, Id clamp) { | 61 | Id 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) { | |||
| 145 | Id EmitShuffleIndex(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, | 144 | Id 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 | ||
| 168 | Id EmitShuffleUp(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, | 163 | Id 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 | ||
| 182 | Id EmitShuffleDown(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, | 178 | Id 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 | ||
| 196 | Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, | 193 | Id 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 | |||
| 55 | protected: | 64 | protected: |
| 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 | ||
| 224 | constexpr size_t NUM_GENERICS = 32; | 229 | constexpr 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 | ||
| 297 | U32 IREmitter::GetAttributeU32(IR::Attribute attribute) { | ||
| 298 | return GetAttributeU32(attribute, Imm32(0)); | ||
| 299 | } | ||
| 300 | |||
| 301 | U32 IREmitter::GetAttributeU32(IR::Attribute attribute, const U32& vertex) { | ||
| 302 | return Inst<U32>(Opcode::GetAttributeU32, attribute, vertex); | ||
| 303 | } | ||
| 304 | |||
| 297 | void IREmitter::SetAttribute(IR::Attribute attribute, const F32& value, const U32& vertex) { | 305 | void 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 | |||
| 175 | void 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 | |||
| 214 | u32 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 | |||
| 225 | void 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 | ||
| 176 | IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool, | 240 | IR::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 | ||
| 647 | void ConstantPropagation(IR::Block& block, IR::Inst& inst) { | 651 | void 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 | |||
| 683 | void 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 | |||
| 707 | void 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 | ||
| 798 | void ConstantPropagationPass(IR::Program& program) { | 868 | void 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 | ||
| 16 | namespace Shader::Optimization { | 17 | namespace 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 |
| 405 | IR::U32 StorageOffset(IR::Block& block, IR::Inst& inst, StorageBufferAddr buffer) { | 406 | IR::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 | ||
| 513 | void GlobalMemoryToStorageBufferPass(IR::Program& program) { | 517 | void 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; | |||
| 13 | namespace Shader::Optimization { | 13 | namespace Shader::Optimization { |
| 14 | 14 | ||
| 15 | void CollectShaderInfoPass(Environment& env, IR::Program& program); | 15 | void CollectShaderInfoPass(Environment& env, IR::Program& program); |
| 16 | void ConstantPropagationPass(IR::Program& program); | 16 | void ConstantPropagationPass(Environment& env, IR::Program& program); |
| 17 | void DeadCodeEliminationPass(IR::Program& program); | 17 | void DeadCodeEliminationPass(IR::Program& program); |
| 18 | void GlobalMemoryToStorageBufferPass(IR::Program& program); | 18 | void GlobalMemoryToStorageBufferPass(IR::Program& program, const HostTranslateInfo& host_info); |
| 19 | void IdentityRemovalPass(IR::Program& program); | 19 | void IdentityRemovalPass(IR::Program& program); |
| 20 | void LowerFp16ToFp32(IR::Program& program); | 20 | void LowerFp16ToFp32(IR::Program& program); |
| 21 | void LowerInt64ToInt32(IR::Program& program); | 21 | void 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 | ||
| 17 | namespace Shader { | 17 | namespace Shader { |
| 18 | 18 | ||
| 19 | enum class ReplaceConstant : u32 { | ||
| 20 | BaseInstance, | ||
| 21 | BaseVertex, | ||
| 22 | DrawID, | ||
| 23 | }; | ||
| 24 | |||
| 19 | enum class TextureType : u32 { | 25 | enum class TextureType : u32 { |
| 20 | Color1D, | 26 | Color1D, |
| 21 | ColorArray1D, | 27 | ColorArray1D, |
| @@ -59,6 +65,8 @@ enum class Interpolation { | |||
| 59 | struct ConstantBufferDescriptor { | 65 | struct 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 | ||
| 64 | struct StorageBufferDescriptor { | 72 | struct 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 | ||
| 71 | struct TextureBufferDescriptor { | 81 | struct 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 | }; |
| 82 | using TextureBufferDescriptors = boost::container::small_vector<TextureBufferDescriptor, 6>; | 94 | using 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 | }; |
| 93 | using ImageBufferDescriptors = boost::container::small_vector<ImageBufferDescriptor, 2>; | 107 | using 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 | }; |
| 108 | using TextureDescriptors = boost::container::small_vector<TextureDescriptor, 12>; | 124 | using 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 | }; |
| 120 | using ImageDescriptors = boost::container::small_vector<ImageDescriptor, 4>; | 138 | using 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 @@ | |||
| 11 | namespace Shader { | 11 | namespace Shader { |
| 12 | 12 | ||
| 13 | struct VaryingState { | 13 | struct 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 | |||
| 10 | enum class MappedEnum : u32 { | ||
| 11 | Invalid = 0, | ||
| 12 | Valid_1 = 1, | ||
| 13 | Valid_2 = 2, | ||
| 14 | Valid_3 = 3, | ||
| 15 | }; | ||
| 16 | |||
| 17 | TEST_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 | ||
| 206 | private: | 215 | private: |
| @@ -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 | ||
| 679 | template <class P> | 699 | template <class P> |
| @@ -823,6 +843,7 @@ bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept { | |||
| 823 | template <class P> | 843 | template <class P> |
| 824 | void BufferCache<P>::CommitAsyncFlushesHigh() { | 844 | void 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 | ||
| 1044 | template <class P> | 1065 | template <class P> |
| 1066 | void 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 | |||
| 1078 | template <class P> | ||
| 1045 | void BufferCache<P>::BindHostGraphicsUniformBuffers(size_t stage) { | 1079 | void 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 | ||
| 1364 | template <class P> | 1401 | template <class P> |
| 1402 | void 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 | |||
| 1422 | template <class P> | ||
| 1365 | void BufferCache<P>::UpdateUniformBuffers(size_t stage) { | 1423 | void 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 | ||
| 2009 | template <class P> | ||
| 2010 | std::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 | |||
| 2015 | template <class P> | ||
| 2016 | std::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 | |||
| 9 | namespace VideoCommon { | ||
| 10 | |||
| 11 | enum 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 | }; | ||
| 22 | DECLARE_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 | ||
| 94 | void DrawManager::DrawArrayIndirect(PrimitiveTopology topology) { | ||
| 95 | draw_state.topology = topology; | ||
| 96 | |||
| 97 | ProcessDrawIndirect(); | ||
| 98 | } | ||
| 99 | |||
| 100 | void 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 | |||
| 94 | void DrawManager::SetInlineIndexBuffer(u32 index) { | 111 | void 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 | |||
| 219 | void 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 | |||
| 53 | private: | 75 | private: |
| 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 | ||
| 8 | namespace Tegra::Engines { | 12 | namespace 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 | |||
| 37 | protected: | ||
| 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 | ||
| 21 | using namespace Texture; | 22 | using namespace Texture; |
| 22 | 23 | ||
| 23 | Fermi2D::Fermi2D(MemoryManager& memory_manager_) { | 24 | Fermi2D::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 | ||
| 30 | Fermi2D::~Fermi2D() = default; | 34 | Fermi2D::~Fermi2D() = default; |
| @@ -49,6 +53,13 @@ void Fermi2D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 | |||
| 49 | } | 53 | } |
| 50 | } | 54 | } |
| 51 | 55 | ||
| 56 | void Fermi2D::ConsumeSinkImpl() { | ||
| 57 | for (auto [method, value] : method_sink) { | ||
| 58 | regs.reg_array[method] = value; | ||
| 59 | } | ||
| 60 | method_sink.clear(); | ||
| 61 | } | ||
| 62 | |||
| 52 | void Fermi2D::Blit() { | 63 | void 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: | |||
| 305 | private: | 305 | private: |
| 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 @@ | |||
| 14 | namespace Tegra::Engines { | 14 | namespace Tegra::Engines { |
| 15 | 15 | ||
| 16 | KeplerCompute::KeplerCompute(Core::System& system_, MemoryManager& memory_manager_) | 16 | KeplerCompute::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 | ||
| 19 | KeplerCompute::~KeplerCompute() = default; | 24 | KeplerCompute::~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 | ||
| 31 | void KeplerCompute::ConsumeSinkImpl() { | ||
| 32 | for (auto [method, value] : method_sink) { | ||
| 33 | regs.reg_array[method] = value; | ||
| 34 | } | ||
| 35 | method_sink.clear(); | ||
| 36 | } | ||
| 37 | |||
| 26 | void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_call) { | 38 | void 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: | |||
| 204 | private: | 204 | private: |
| 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 | ||
| 19 | void KeplerMemory::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) { | 19 | void 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 | |||
| 27 | void KeplerMemory::ConsumeSinkImpl() { | ||
| 28 | for (auto [method, value] : method_sink) { | ||
| 29 | regs.reg_array[method] = value; | ||
| 30 | } | ||
| 31 | method_sink.clear(); | ||
| 21 | } | 32 | } |
| 22 | 33 | ||
| 23 | void KeplerMemory::CallMethod(u32 method, u32 method_argument, bool is_last_call) { | 34 | void 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 | ||
| 75 | private: | 75 | private: |
| 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 | ||
| 33 | Maxwell3D::~Maxwell3D() = default; | 39 | Maxwell3D::~Maxwell3D() = default; |
| @@ -121,6 +127,71 @@ void Maxwell3D::InitializeRegisterDefaults() { | |||
| 121 | shadow_state = regs; | 127 | shadow_state = regs; |
| 122 | } | 128 | } |
| 123 | 129 | ||
| 130 | bool 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 | |||
| 124 | void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) { | 195 | void 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 | |||
| 222 | void 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, ¯o_params[current_index], | ||
| 230 | sizeof(u32) * segment.second); | ||
| 231 | current_index += segment.second; | ||
| 232 | } | ||
| 233 | } | ||
| 234 | |||
| 235 | u32 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 | |||
| 257 | size_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 | ||
| 141 | u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) { | 270 | u32 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 | ||
| 284 | void 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 | |||
| 155 | void Maxwell3D::ProcessDirtyRegisters(u32 method, u32 argument) { | 307 | void 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 | ||
| 334 | void Maxwell3D::ProcessQueryGet() { | 487 | void 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 | ||
| 432 | void Maxwell3D::ProcessCounterReset() { | 585 | void Maxwell3D::ProcessCounterReset() { |
| @@ -463,7 +616,8 @@ std::optional<u64> Maxwell3D::GetQueryResult() { | |||
| 463 | } | 616 | } |
| 464 | 617 | ||
| 465 | void Maxwell3D::ProcessCBBind(size_t stage_index) { | 618 | void 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 | ||
| 681 | void 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 | |||
| 3070 | private: | 3120 | private: |
| 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 { | |||
| 21 | using namespace Texture; | 21 | using namespace Texture; |
| 22 | 22 | ||
| 23 | MaxwellDMA::MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_) | 23 | MaxwellDMA::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 | ||
| 26 | MaxwellDMA::~MaxwellDMA() = default; | 29 | MaxwellDMA::~MaxwellDMA() = default; |
| 27 | 30 | ||
| @@ -29,6 +32,13 @@ void MaxwellDMA::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) { | |||
| 29 | rasterizer = rasterizer_; | 32 | rasterizer = rasterizer_; |
| 30 | } | 33 | } |
| 31 | 34 | ||
| 35 | void MaxwellDMA::ConsumeSinkImpl() { | ||
| 36 | for (auto [method, value] : method_sink) { | ||
| 37 | regs.reg_array[method] = value; | ||
| 38 | } | ||
| 39 | method_sink.clear(); | ||
| 40 | } | ||
| 41 | |||
| 32 | void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call) { | 42 | void 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 | ||
| 200 | void MaxwellDMA::CopyPitchToBlockLinear() { | 211 | void 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 | ||
| 252 | void MaxwellDMA::FastCopyBlockLinearToPitch() { | 263 | void 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 | ||
| 283 | void MaxwellDMA::CopyBlockLinearToBlockLinear() { | 294 | void 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 | ||
| 343 | void MaxwellDMA::ReleaseSemaphore() { | 354 | void 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 | |||
| 6 | layout (local_size_x = 16, local_size_y = 8, local_size_z = 1) in; | ||
| 7 | |||
| 8 | layout (binding = 0) buffer ThreadData { | ||
| 9 | uint data[]; | ||
| 10 | }; | ||
| 11 | |||
| 12 | uint xorshift32(uint x) { | ||
| 13 | x ^= x << 13; | ||
| 14 | x ^= x >> 17; | ||
| 15 | x ^= x << 5; | ||
| 16 | return x; | ||
| 17 | } | ||
| 18 | |||
| 19 | uint getGlobalIndex() { | ||
| 20 | return gl_GlobalInvocationID.x + gl_GlobalInvocationID.y * gl_WorkGroupSize.y * gl_NumWorkGroups.y; | ||
| 21 | } | ||
| 22 | |||
| 23 | void 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 | |||
| 11 | namespace VideoCommon { | ||
| 12 | |||
| 13 | class InvalidationAccumulator { | ||
| 14 | public: | ||
| 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 | |||
| 67 | private: | ||
| 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 | ||
| 26 | MICROPROFILE_DEFINE(MacroHLE, "GPU", "Execute macro HLE", MP_RGB(128, 192, 192)); | ||
| 27 | |||
| 24 | namespace Tegra { | 28 | namespace Tegra { |
| 25 | 29 | ||
| 26 | static void Dump(u64 hash, std::span<const u32> code) { | 30 | static 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 | ||
| 43 | MacroEngine::MacroEngine(Engines::Maxwell3D& maxwell3d) | 47 | MacroEngine::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 | ||
| 46 | MacroEngine::~MacroEngine() = default; | 50 | MacroEngine::~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 | ||
| 142 | std::unique_ptr<MacroEngine> GetMacroEngine(Engines::Maxwell3D& maxwell3d); | 143 | std::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 | ||
| 14 | namespace Tegra { | 16 | namespace Tegra { |
| 15 | namespace { | ||
| 16 | 17 | ||
| 17 | using HLEFunction = void (*)(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters); | 18 | using Maxwell3D = Engines::Maxwell3D; |
| 18 | 19 | ||
| 19 | // HLE'd functions | 20 | namespace { |
| 20 | void 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 | ||
| 27 | void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { | 22 | bool 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 | ||
| 34 | void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { | 45 | class HLEMacroImpl : public CachedMacro { |
| 35 | const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); | 46 | public: |
| 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 | 49 | protected: |
| 55 | void 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 | */ |
| 57 | template <bool extended> | ||
| 58 | class HLE_DrawArraysIndirect final : public HLEMacroImpl { | ||
| 59 | public: | ||
| 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 | |||
| 92 | private: | ||
| 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 | */ | ||
| 137 | template <bool extended> | ||
| 138 | class HLE_DrawIndexedIndirect final : public HLEMacroImpl { | ||
| 139 | public: | ||
| 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; | 182 | private: |
| 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 | 214 | class HLE_MultiLayerClear final : public HLEMacroImpl { |
| 95 | void HLE_EAD26C3E2109B06B(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { | 215 | public: |
| 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 | |||
| 232 | class HLE_MultiDrawIndexedIndirectCount final : public HLEMacroImpl { | ||
| 233 | public: | ||
| 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 | ||
| 107 | constexpr 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 | ||
| 115 | class 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 | |||
| 279 | private: | ||
| 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 | |||
| 322 | class HLE_C713C83D8F63CCF3 final : public HLEMacroImpl { | ||
| 323 | public: | ||
| 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 | |||
| 338 | class HLE_D7333D26E0A93EDE final : public HLEMacroImpl { | ||
| 339 | public: | ||
| 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 | |||
| 354 | class HLE_BindShader final : public HLEMacroImpl { | ||
| 355 | public: | ||
| 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 | |||
| 384 | class HLE_SetRasterBoundingBox final : public HLEMacroImpl { | ||
| 385 | public: | ||
| 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 | |||
| 399 | template <size_t base_size> | ||
| 400 | class HLE_ClearConstBuffer final : public HLEMacroImpl { | ||
| 401 | public: | ||
| 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 | |||
| 416 | class HLE_ClearMemory final : public HLEMacroImpl { | ||
| 116 | public: | 417 | public: |
| 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 | ||
| 124 | private: | 437 | private: |
| 125 | Engines::Maxwell3D& maxwell3d; | 438 | std::vector<u32> zero_memory; |
| 126 | HLEFunction func; | 439 | }; |
| 440 | |||
| 441 | class HLE_TransformFeedbackSetup final : public HLEMacroImpl { | ||
| 442 | public: | ||
| 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 | ||
| 131 | HLEMacro::HLEMacro(Engines::Maxwell3D& maxwell3d_) : maxwell3d{maxwell3d_} {} | 467 | HLEMacro::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 | |||
| 132 | HLEMacro::~HLEMacro() = default; | 541 | HLEMacro::~HLEMacro() = default; |
| 133 | 542 | ||
| 134 | std::unique_ptr<CachedMacro> HLEMacro::GetHLEProgram(u64 hash) const { | 543 | std::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 | ||
| 9 | namespace Tegra { | 12 | namespace Tegra { |
| @@ -23,6 +26,8 @@ public: | |||
| 23 | 26 | ||
| 24 | private: | 27 | private: |
| 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 | ||
| 51 | MemoryManager::~MemoryManager() = default; | 56 | MemoryManager::~MemoryManager() = default; |
| @@ -83,38 +88,7 @@ void MemoryManager::SetEntry(size_t position, MemoryManager::EntryType entry) { | |||
| 83 | } | 88 | } |
| 84 | 89 | ||
| 85 | PTEKind MemoryManager::GetPageKind(GPUVAddr gpu_addr) const { | 90 | PTEKind 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 | |||
| 94 | template <bool is_big_page> | ||
| 95 | PTEKind 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 | |||
| 107 | template <bool is_big_page> | ||
| 108 | void 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 | ||
| 120 | inline bool MemoryManager::IsBigPageContinous(size_t big_page_index) const { | 94 | inline 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 | |||
| 325 | inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, | 296 | inline 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 | ||
| 386 | template <bool is_safe> | 363 | template <bool is_safe, bool use_fastmem> |
| 387 | void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, | 364 | void 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 | ||
| 426 | void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const { | 411 | void 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 | ||
| 430 | void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer, | 420 | void 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 | ||
| 435 | template <bool is_safe> | 429 | template <bool is_safe> |
| 436 | void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, | 430 | void 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 | ||
| 474 | void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size) { | 468 | void 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 | ||
| 478 | void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer, | 473 | void 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 | |||
| 478 | void 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 | ||
| 483 | void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size) const { | 484 | void 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 | ||
| 506 | bool MemoryManager::IsMemoryDirty(GPUVAddr gpu_addr, size_t size) const { | 508 | bool 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 | ||
| 574 | void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size) const { | 577 | size_t MemoryManager::GetMemoryLayoutSize(GPUVAddr gpu_addr, size_t max_size) const { |
| 578 | return kind_map.GetContinousSizeFrom(gpu_addr); | ||
| 579 | } | ||
| 580 | |||
| 581 | void 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 | ||
| 597 | void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size) { | 605 | void 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 | ||
| 607 | bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const { | 616 | bool 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 | |||
| 681 | std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange( | 690 | std::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 | |||
| 697 | template <bool is_gpu_address> | ||
| 698 | void 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 | |||
| 765 | void 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 | ||
| 16 | namespace VideoCore { | 18 | namespace VideoCore { |
| 17 | class RasterizerInterface; | 19 | class RasterizerInterface; |
| 18 | } | 20 | } |
| 19 | 21 | ||
| 22 | namespace VideoCommon { | ||
| 23 | class InvalidationAccumulator; | ||
| 24 | } | ||
| 25 | |||
| 20 | namespace Core { | 26 | namespace Core { |
| 21 | class DeviceMemory; | 27 | class DeviceMemory; |
| 22 | namespace Memory { | 28 | namespace 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 | |||
| 121 | private: | 139 | private: |
| 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) {} |
| 40 | void RasterizerNull::DisableGraphicsUniformBuffer(size_t stage, u32 index) {} | 40 | void RasterizerNull::DisableGraphicsUniformBuffer(size_t stage, u32 index) {} |
| 41 | void RasterizerNull::FlushAll() {} | 41 | void RasterizerNull::FlushAll() {} |
| 42 | void RasterizerNull::FlushRegion(VAddr addr, u64 size) {} | 42 | void RasterizerNull::FlushRegion(VAddr addr, u64 size, VideoCommon::CacheType) {} |
| 43 | bool RasterizerNull::MustFlushRegion(VAddr addr, u64 size) { | 43 | bool RasterizerNull::MustFlushRegion(VAddr addr, u64 size, VideoCommon::CacheType) { |
| 44 | return false; | 44 | return false; |
| 45 | } | 45 | } |
| 46 | void RasterizerNull::InvalidateRegion(VAddr addr, u64 size) {} | 46 | void RasterizerNull::InvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType) {} |
| 47 | void RasterizerNull::OnCPUWrite(VAddr addr, u64 size) {} | 47 | void RasterizerNull::OnCPUWrite(VAddr addr, u64 size) {} |
| 48 | void RasterizerNull::InvalidateGPUCache() {} | 48 | void RasterizerNull::InvalidateGPUCache() {} |
| 49 | void RasterizerNull::UnmapMemory(VAddr addr, u64 size) {} | 49 | void RasterizerNull::UnmapMemory(VAddr addr, u64 size) {} |
| @@ -61,7 +61,7 @@ void RasterizerNull::SignalSyncPoint(u32 value) { | |||
| 61 | } | 61 | } |
| 62 | void RasterizerNull::SignalReference() {} | 62 | void RasterizerNull::SignalReference() {} |
| 63 | void RasterizerNull::ReleaseFences() {} | 63 | void RasterizerNull::ReleaseFences() {} |
| 64 | void RasterizerNull::FlushAndInvalidateRegion(VAddr addr, u64 size) {} | 64 | void RasterizerNull::FlushAndInvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType) {} |
| 65 | void RasterizerNull::WaitForIdle() {} | 65 | void RasterizerNull::WaitForIdle() {} |
| 66 | void RasterizerNull::FragmentBarrier() {} | 66 | void RasterizerNull::FragmentBarrier() {} |
| 67 | void RasterizerNull::TiledCacheBarrier() {} | 67 | void 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 | |||
| 163 | private: | 167 | private: |
| 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 | ||
| 205 | void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) { | 205 | template <typename Func> |
| 206 | void 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 | ||
| 238 | void 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 | |||
| 281 | void RasterizerOpenGL::DrawIndirect() { | ||
| 282 | const auto& params = maxwell3d->draw_manager->GetIndirectParams(); | ||
| 283 | buffer_cache.SetDrawIndirect(¶ms); | ||
| 284 | PrepareDraw(params.is_indexed, [this, ¶ms](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 | |||
| 271 | void RasterizerOpenGL::DispatchCompute() { | 321 | void 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 | ||
| 303 | void RasterizerOpenGL::FlushAll() {} | 353 | void RasterizerOpenGL::FlushAll() {} |
| 304 | 354 | ||
| 305 | void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) { | 355 | void 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 | ||
| 321 | bool RasterizerOpenGL::MustFlushRegion(VAddr addr, u64 size) { | 373 | bool 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 | ||
| 330 | void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) { | 390 | void 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 | ||
| 347 | void RasterizerOpenGL::OnCPUWrite(VAddr addr, u64 size) { | 411 | void 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 | ||
| 411 | void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) { | 475 | void 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 | ||
| 418 | void RasterizerOpenGL::WaitForIdle() { | 483 | void RasterizerOpenGL::WaitForIdle() { |
| @@ -460,6 +525,21 @@ void RasterizerOpenGL::TickFrame() { | |||
| 460 | } | 525 | } |
| 461 | } | 526 | } |
| 462 | 527 | ||
| 528 | bool 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 | |||
| 463 | bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, | 543 | bool 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; | |||
| 51 | using VideoCommon::SerializePipeline; | 51 | using VideoCommon::SerializePipeline; |
| 52 | using Context = ShaderContext::Context; | 52 | using Context = ShaderContext::Context; |
| 53 | 53 | ||
| 54 | constexpr u32 CACHE_VERSION = 7; | 54 | constexpr u32 CACHE_VERSION = 9; |
| 55 | 55 | ||
| 56 | template <typename Container> | 56 | template <typename Container> |
| 57 | auto MakeSpan(Container& container) { | 57 | auto 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 | ||
| 366 | using TextureCache = VideoCommon::TextureCache<TextureCacheParams>; | 368 | using 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 | ||
| 51 | void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, | 51 | void 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 | ||
| 207 | void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) { | 200 | void 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 | }); | 232 | void 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 | |||
| 253 | void 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 | ||
| 244 | size_t FixedPipelineState::Hash() const noexcept { | 263 | size_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 | ||
| 18 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; | 18 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; |
| 19 | 19 | ||
| 20 | struct 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 | |||
| 20 | struct FixedPipelineState { | 29 | struct 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 | |||
| 81 | Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld, | 83 | Device 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 | ||
| 94 | RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_, | 95 | RendererVulkan::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 | ||
| 32 | namespace Vulkan { | 33 | namespace Vulkan { |
| 33 | 34 | ||
| 35 | Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld, | ||
| 36 | VkSurfaceKHR surface); | ||
| 37 | |||
| 34 | class RendererVulkan final : public VideoCore::RendererBase { | 38 | class RendererVulkan final : public VideoCore::RendererBase { |
| 35 | public: | 39 | public: |
| 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 | ||
| 333 | u32 BufferCacheRuntime::GetStorageBufferAlignment() const { | ||
| 334 | return static_cast<u32>(device.GetStorageBufferAlignment()); | ||
| 335 | } | ||
| 336 | |||
| 332 | void BufferCacheRuntime::Finish() { | 337 | void BufferCacheRuntime::Finish() { |
| 333 | scheduler.Finish(); | 338 | scheduler.Finish(); |
| 334 | } | 339 | } |
| 335 | 340 | ||
| 336 | void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer, | 341 | void 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 | ||
| 395 | void BufferCacheRuntime::ClearBuffer(VkBuffer dest_buffer, u32 offset, size_t size, u32 value) { | 403 | void 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; | |||
| 24 | using Shader::Backend::SPIRV::RESCALING_LAYOUT_WORDS_OFFSET; | 24 | using Shader::Backend::SPIRV::RESCALING_LAYOUT_WORDS_OFFSET; |
| 25 | using Tegra::Texture::TexturePair; | 25 | using Tegra::Texture::TexturePair; |
| 26 | 26 | ||
| 27 | ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descriptor_pool, | 27 | ComputePipeline::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 | ||
| 29 | class ComputePipeline { | 29 | class ComputePipeline { |
| 30 | public: | 30 | public: |
| 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 | ||
| 47 | private: | 48 | private: |
| 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 | ||
| 204 | struct 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 | |||
| 212 | struct 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 | |||
| 204 | struct DefaultSpec { | 220 | struct 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 | ||
| 212 | ConfigureFuncPtr ConfigureFunc(const std::array<vk::ShaderModule, NUM_STAGES>& modules, | 228 | ConfigureFuncPtr 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 | ||
| 218 | GraphicsPipeline::GraphicsPipeline( | 235 | GraphicsPipeline::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 | ||
| 875 | void GraphicsPipeline::Validate() { | 926 | void 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 | ||
| 72 | public: | 72 | public: |
| 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; | |||
| 54 | using VideoCommon::GenericEnvironment; | 54 | using VideoCommon::GenericEnvironment; |
| 55 | using VideoCommon::GraphicsEnvironment; | 55 | using VideoCommon::GraphicsEnvironment; |
| 56 | 56 | ||
| 57 | constexpr u32 CACHE_VERSION = 8; | 57 | constexpr u32 CACHE_VERSION = 10; |
| 58 | constexpr std::array<char, 8> VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'}; | ||
| 58 | 59 | ||
| 59 | template <typename Container> | 60 | template <typename Container> |
| 60 | auto MakeSpan(Container& container) { | 61 | auto 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 | ||
| 356 | PipelineCache::~PipelineCache() = default; | 370 | PipelineCache::~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 | ||
| 358 | GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() { | 377 | GraphicsPipeline* 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 | ||
| 722 | void 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 | |||
| 755 | vk::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 | ||
| 181 | RasterizerVulkan::~RasterizerVulkan() = default; | 181 | RasterizerVulkan::~RasterizerVulkan() = default; |
| 182 | 182 | ||
| 183 | void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) { | 183 | template <typename Func> |
| 184 | void 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, | 211 | void 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 | |||
| 229 | void RasterizerVulkan::DrawIndirect() { | ||
| 230 | const auto& params = maxwell3d->draw_manager->GetIndirectParams(); | ||
| 231 | buffer_cache.SetDrawIndirect(¶ms); | ||
| 232 | PrepareDraw(params.is_indexed, [this, ¶ms] { | ||
| 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 | ||
| 220 | void RasterizerVulkan::Clear(u32 layer_count) { | 269 | void RasterizerVulkan::Clear(u32 layer_count) { |
| @@ -345,6 +394,7 @@ void RasterizerVulkan::Clear(u32 layer_count) { | |||
| 345 | 394 | ||
| 346 | void RasterizerVulkan::DispatchCompute() { | 395 | void 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 | ||
| 380 | void RasterizerVulkan::FlushAll() {} | 430 | void RasterizerVulkan::FlushAll() {} |
| 381 | 431 | ||
| 382 | void RasterizerVulkan::FlushRegion(VAddr addr, u64 size) { | 432 | void 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 | ||
| 397 | bool RasterizerVulkan::MustFlushRegion(VAddr addr, u64 size) { | 449 | bool 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 | ||
| 406 | void RasterizerVulkan::InvalidateRegion(VAddr addr, u64 size) { | 466 | void 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 | |||
| 486 | void 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 | ||
| 422 | void RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) { | 507 | void 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 | ||
| 484 | void RasterizerVulkan::FlushAndInvalidateRegion(VAddr addr, u64 size) { | 569 | void 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 | ||
| 491 | void RasterizerVulkan::WaitForIdle() { | 577 | void RasterizerVulkan::WaitForIdle() { |
| @@ -541,6 +627,21 @@ void RasterizerVulkan::TickFrame() { | |||
| 541 | } | 627 | } |
| 542 | } | 628 | } |
| 543 | 629 | ||
| 630 | bool 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 | |||
| 544 | bool RasterizerVulkan::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, | 645 | bool 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 | ||
| 820 | void RasterizerVulkan::UpdateLineWidth(Tegra::Engines::Maxwell3D::Regs& regs) { | 1000 | void 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 | ||
| 1051 | void 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 | |||
| 1060 | void 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 | |||
| 1069 | void 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 | |||
| 1104 | void 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 | |||
| 1113 | void 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 | |||
| 871 | void RasterizerVulkan::UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs) { | 1127 | void 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 | ||
| 1184 | void 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 | |||
| 1194 | void 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 | |||
| 928 | void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) { | 1256 | void 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 | ||
| 143 | StagingBufferPool::~StagingBufferPool() = default; | 144 | StagingBufferPool::~StagingBufferPool() = default; |
| 144 | 145 | ||
| 145 | StagingBufferRef StagingBufferPool::Request(size_t size, MemoryUsage usage) { | 146 | StagingBufferRef 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 | |||
| 153 | void 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 | ||
| 152 | void StagingBufferPool::TickFrame() { | 165 | void 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 | ||
| 199 | StagingBufferRef StagingBufferPool::GetStagingBuffer(size_t size, MemoryUsage usage) { | 215 | StagingBufferRef 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 | ||
| 206 | std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t size, | 223 | std::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 | ||
| 227 | StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage usage) { | 247 | StagingBufferRef 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 | ||
| 25 | class StagingBufferPool { | 28 | class 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 | ||
| 28 | Flags MakeInvalidationFlags() { | 28 | Flags 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 | ||
| 77 | void SetupDirtyStencilProperties(Tables& tables) { | 104 | void 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 | ||
| 88 | void SetupDirtyLineWidth(Tables& tables) { | 118 | void 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 | ||
| 99 | void SetupDirtyDepthBoundsEnable(Tables& tables) { | 129 | void 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; | |
| 103 | void 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); | |
| 107 | void 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 | ||
| 111 | void SetupDirtyDepthCompareOp(Tables& tables) { | 147 | void 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 | ||
| 136 | void SetupDirtyStencilTestEnable(Tables& tables) { | ||
| 137 | tables[0][OFF(stencil_enable)] = StencilTestEnable; | ||
| 138 | } | ||
| 139 | |||
| 140 | void SetupDirtyBlending(Tables& tables) { | 172 | void 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 | |||
| 186 | void SetupDirtySpecialOps(Tables& tables) { | ||
| 187 | tables[0][OFF(logic_op.op)] = LogicOp; | ||
| 146 | } | 188 | } |
| 147 | 189 | ||
| 148 | void SetupDirtyViewportSwizzles(Tables& tables) { | 190 | void 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 | ||
| 201 | void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) { | 241 | void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) { |
| @@ -204,6 +244,8 @@ void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) { | |||
| 204 | 244 | ||
| 205 | void StateTracker::InvalidateState() { | 245 | void StateTracker::InvalidateState() { |
| 206 | flags->set(); | 246 | flags->set(); |
| 247 | current_topology = INVALID_TOPOLOGY; | ||
| 248 | stencil_reset = true; | ||
| 207 | } | 249 | } |
| 208 | 250 | ||
| 209 | StateTracker::StateTracker() | 251 | StateTracker::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 | ||
| 815 | StagingBufferRef TextureCacheRuntime::DownloadStagingBuffer(size_t size) { | 815 | StagingBufferRef 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 | |||
| 819 | void TextureCacheRuntime::FreeDeferredStagingBuffer(StagingBufferRef& ref) { | ||
| 820 | staging_buffer_pool.FreeDeferred(ref); | ||
| 817 | } | 821 | } |
| 818 | 822 | ||
| 819 | bool TextureCacheRuntime::ShouldReinterpret(Image& dst, Image& src) { | 823 | bool 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 | ||
| 359 | using TextureCache = VideoCommon::TextureCache<TextureCacheParams>; | 363 | using 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 | |||
| 11 | namespace Vulkan { | ||
| 12 | |||
| 13 | using namespace Common::Literals; | ||
| 14 | |||
| 15 | TurboMode::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 | |||
| 20 | TurboMode::~TurboMode() = default; | ||
| 21 | |||
| 22 | void 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 | |||
| 11 | namespace Vulkan { | ||
| 12 | |||
| 13 | class TurboMode { | ||
| 14 | public: | ||
| 15 | explicit TurboMode(const vk::Instance& instance, const vk::InstanceDispatch& dld); | ||
| 16 | ~TurboMode(); | ||
| 17 | |||
| 18 | private: | ||
| 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 | ||
| 323 | u32 GraphicsEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) { | 333 | u32 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 | ||
| 344 | std::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 | |||
| 334 | Shader::TextureType GraphicsEnvironment::ReadTextureType(u32 handle) { | 370 | Shader::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 | ||
| 460 | void FileEnvironment::Dump(u64 hash) { | 508 | void 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 | ||
| 563 | std::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 | |||
| 515 | void SerializePipeline(std::span<const char> key, std::span<const GenericEnvironment* const> envs, | 573 | void 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 | |||
| 63 | protected: | 67 | protected: |
| 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 | ||
| 94 | class GraphicsEnvironment final : public GenericEnvironment { | 100 | class 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 | |||
| 112 | private: | 120 | private: |
| 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 | |||
| 134 | private: | 147 | private: |
| 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 | ||
| 171 | private: | 191 | private: |
| @@ -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 { | |||
| 646 | template <class P> | 646 | template <class P> |
| 647 | void TextureCache<P>::CommitAsyncFlushes() { | 647 | void 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 | ||
| 691 | template <class P> | 733 | template <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 | ||
| 208 | private: | 211 | private: |
| 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 | ||
| 994 | bool 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 | |||
| 916 | static std::vector<const char*> ExtensionsRequiredForInstanceVersion(u32 available_version) { | 1006 | static 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 | ||
| 1399 | void Device::SetupFeatures() { | 1543 | void 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 @@ | |||
| 32 | namespace Vulkan { | 32 | namespace Vulkan { |
| 33 | namespace { | 33 | namespace { |
| 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 | ||
| 126 | vk::Instance CreateInstance(const Common::DynamicLibrary& library, vk::InstanceDispatch& dld, | 126 | vk::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 | ||
| 437 | void Destroy(VkDevice device, VkPipelineCache handle, const DeviceDispatch& dld) noexcept { | ||
| 438 | dld.vkDestroyPipelineCache(device, handle, nullptr); | ||
| 439 | } | ||
| 440 | |||
| 420 | void Destroy(VkDevice device, VkPipelineLayout handle, const DeviceDispatch& dld) noexcept { | 441 | void 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 | ||
| 661 | void PipelineCache::SetObjectNameEXT(const char* name) const { | ||
| 662 | SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_PIPELINE_CACHE, name); | ||
| 663 | } | ||
| 664 | |||
| 640 | void Semaphore::SetObjectNameEXT(const char* name) const { | 665 | void 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 | ||
| 760 | PipelineCache 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 | |||
| 735 | PipelineLayout Device::CreatePipelineLayout(const VkPipelineLayoutCreateInfo& ci) const { | 766 | PipelineLayout 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 | ||
| 741 | Pipeline Device::CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci) const { | 772 | Pipeline 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 | ||
| 747 | Pipeline Device::CreateComputePipeline(const VkComputePipelineCreateInfo& ci) const { | 779 | Pipeline 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; | |||
| 337 | void Destroy(VkDevice, VkImage, const DeviceDispatch&) noexcept; | 354 | void Destroy(VkDevice, VkImage, const DeviceDispatch&) noexcept; |
| 338 | void Destroy(VkDevice, VkImageView, const DeviceDispatch&) noexcept; | 355 | void Destroy(VkDevice, VkImageView, const DeviceDispatch&) noexcept; |
| 339 | void Destroy(VkDevice, VkPipeline, const DeviceDispatch&) noexcept; | 356 | void Destroy(VkDevice, VkPipeline, const DeviceDispatch&) noexcept; |
| 357 | void Destroy(VkDevice, VkPipelineCache, const DeviceDispatch&) noexcept; | ||
| 340 | void Destroy(VkDevice, VkPipelineLayout, const DeviceDispatch&) noexcept; | 358 | void Destroy(VkDevice, VkPipelineLayout, const DeviceDispatch&) noexcept; |
| 341 | void Destroy(VkDevice, VkQueryPool, const DeviceDispatch&) noexcept; | 359 | void Destroy(VkDevice, VkQueryPool, const DeviceDispatch&) noexcept; |
| 342 | void Destroy(VkDevice, VkRenderPass, const DeviceDispatch&) noexcept; | 360 | void 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 | ||
| 780 | class PipelineCache : public Handle<VkPipelineCache, VkDevice, DeviceDispatch> { | ||
| 781 | using Handle<VkPipelineCache, VkDevice, DeviceDispatch>::Handle; | ||
| 782 | |||
| 783 | public: | ||
| 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 | |||
| 762 | class Semaphore : public Handle<VkSemaphore, VkDevice, DeviceDispatch> { | 792 | class 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 | ||
| 49 | void ConfigureGraphicsAdvanced::ApplyConfiguration() { | 54 | void 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 | ||
| 63 | void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) { | 74 | void 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 | ||
| 2276 | void 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 | |||
| 2274 | void GMainWindow::RemoveAllTransferableShaderCaches(u64 program_id) { | 2292 | void 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 | ||