summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt4
-rw-r--r--src/CMakeLists.txt33
-rw-r--r--src/audio_core/CMakeLists.txt11
-rw-r--r--src/audio_core/renderer/behavior/info_updater.cpp2
-rw-r--r--src/audio_core/renderer/command/effect/biquad_filter.cpp2
-rw-r--r--src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp2
-rw-r--r--src/common/CMakeLists.txt7
-rw-r--r--src/common/bit_field.h15
-rw-r--r--src/common/bounded_threadsafe_queue.h9
-rw-r--r--src/common/concepts.h16
-rw-r--r--src/common/fs/file.h12
-rw-r--r--src/common/input.h5
-rw-r--r--src/core/CMakeLists.txt8
-rw-r--r--src/core/core.cpp1
-rw-r--r--src/core/file_sys/card_image.cpp4
-rw-r--r--src/core/file_sys/program_metadata.cpp2
-rw-r--r--src/core/hid/emulated_controller.cpp68
-rw-r--r--src/core/hid/emulated_controller.h5
-rw-r--r--src/core/hle/ipc_helpers.h4
-rw-r--r--src/core/hle/kernel/hle_ipc.h2
-rw-r--r--src/core/hle/kernel/kernel.cpp12
-rw-r--r--src/core/hle/kernel/svc.cpp4
-rw-r--r--src/core/hle/service/am/applets/applets.h2
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp24
-rw-r--r--src/core/hle/service/nfp/amiibo_crypto.cpp8
-rw-r--r--src/core/hle/service/nfp/amiibo_crypto.h3
-rw-r--r--src/core/hle/service/nfp/nfp_device.cpp9
-rw-r--r--src/core/hle/service/nfp/nfp_device.h1
-rw-r--r--src/core/hle/service/nfp/nfp_types.h5
-rw-r--r--src/core/hle/service/nfp/nfp_user.cpp3
-rw-r--r--src/core/hle/service/nfp/nfp_user.h8
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp2
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h6
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp12
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h2
-rw-r--r--src/core/hle/service/service.cpp4
-rw-r--r--src/core/hle/service/service.h2
-rw-r--r--src/core/memory.cpp37
-rw-r--r--src/input_common/CMakeLists.txt9
-rw-r--r--src/input_common/drivers/gc_adapter.cpp6
-rw-r--r--src/input_common/drivers/gc_adapter.h4
-rw-r--r--src/input_common/drivers/sdl_driver.cpp68
-rw-r--r--src/input_common/drivers/sdl_driver.h4
-rw-r--r--src/input_common/input_engine.h7
-rw-r--r--src/input_common/input_poller.cpp24
-rw-r--r--src/shader_recompiler/CMakeLists.txt14
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp4
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp4
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate_program.cpp47
-rw-r--r--src/shader_recompiler/runtime_info.h2
-rw-r--r--src/shader_recompiler/shader_info.h3
-rw-r--r--src/tests/video_core/buffer_base.cpp2
-rw-r--r--src/video_core/CMakeLists.txt8
-rw-r--r--src/video_core/engines/maxwell_3d.cpp285
-rw-r--r--src/video_core/engines/maxwell_3d.h46
-rw-r--r--src/video_core/macro/macro_hle.cpp47
-rw-r--r--src/video_core/macro/macro_interpreter.cpp2
-rw-r--r--src/video_core/macro/macro_jit_x64.cpp2
-rw-r--r--src/video_core/memory_manager.cpp4
-rw-r--r--src/video_core/rasterizer_interface.h2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp17
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h4
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp26
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h4
-rw-r--r--src/video_core/texture_cache/texture_cache.h8
-rw-r--r--src/video_core/textures/astc.cpp4
-rw-r--r--src/video_core/textures/decoders.cpp2
-rw-r--r--src/yuzu/multiplayer/state.cpp2
-rw-r--r--src/yuzu/startup_checks.cpp17
71 files changed, 514 insertions, 522 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 957df54f5..b625743ea 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -541,9 +541,9 @@ add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY
541# Adjustments for MSVC + Ninja 541# Adjustments for MSVC + Ninja
542if (MSVC AND CMAKE_GENERATOR STREQUAL "Ninja") 542if (MSVC AND CMAKE_GENERATOR STREQUAL "Ninja")
543 add_compile_options( 543 add_compile_options(
544 /wd4711 # function 'function' selected for automatic inline expansion
545 /wd4464 # relative include path contains '..' 544 /wd4464 # relative include path contains '..'
546 /wd4820 # 'identifier1': '4' bytes padding added after data member 'identifier2' 545 /wd4711 # function 'function' selected for automatic inline expansion
546 /wd4820 # 'bytes' bytes padding added after construct 'member_name'
547 ) 547 )
548endif() 548endif()
549 549
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 3575a3cb3..0ac3d254e 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -58,13 +58,11 @@ if (MSVC)
58 58
59 # Warnings 59 # Warnings
60 /W3 60 /W3
61 /we4018 # 'expression': signed/unsigned mismatch 61 /WX
62
62 /we4062 # Enumerator 'identifier' in a switch of enum 'enumeration' is not handled 63 /we4062 # Enumerator 'identifier' in a switch of enum 'enumeration' is not handled
63 /we4101 # 'identifier': unreferenced local variable
64 /we4189 # 'identifier': local variable is initialized but not referenced 64 /we4189 # 'identifier': local variable is initialized but not referenced
65 /we4265 # 'class': class has virtual functions, but destructor is not virtual 65 /we4265 # 'class': class has virtual functions, but destructor is not virtual
66 /we4267 # 'var': conversion from 'size_t' to 'type', possible loss of data
67 /we4305 # 'context': truncation from 'type1' to 'type2'
68 /we4388 # 'expression': signed/unsigned mismatch 66 /we4388 # 'expression': signed/unsigned mismatch
69 /we4389 # 'operator': signed/unsigned mismatch 67 /we4389 # 'operator': signed/unsigned mismatch
70 /we4456 # Declaration of 'identifier' hides previous local declaration 68 /we4456 # Declaration of 'identifier' hides previous local declaration
@@ -75,10 +73,13 @@ if (MSVC)
75 /we4547 # 'operator': operator before comma has no effect; expected operator with side-effect 73 /we4547 # 'operator': operator before comma has no effect; expected operator with side-effect
76 /we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'? 74 /we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'?
77 /we4555 # Expression has no effect; expected expression with side-effect 75 /we4555 # Expression has no effect; expected expression with side-effect
78 /we4715 # 'function': not all control paths return a value 76 /we4826 # Conversion from 'type1' to 'type2' is sign-extended. This may cause unexpected runtime behavior.
79 /we4834 # Discarding return value of function with 'nodiscard' attribute
80 /we5038 # data member 'member1' will be initialized after data member 'member2' 77 /we5038 # data member 'member1' will be initialized after data member 'member2'
78 /we5233 # explicit lambda capture 'identifier' is not used
81 /we5245 # 'function': unreferenced function with internal linkage has been removed 79 /we5245 # 'function': unreferenced function with internal linkage has been removed
80
81 /wd4100 # 'identifier': unreferenced formal parameter
82 /wd4324 # 'struct_name': structure was padded due to __declspec(align())
82 ) 83 )
83 84
84 if (USE_CCACHE) 85 if (USE_CCACHE)
@@ -99,24 +100,18 @@ if (MSVC)
99 set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE) 100 set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE)
100else() 101else()
101 add_compile_options( 102 add_compile_options(
102 -Wall 103 -Werror=all
103 -Werror=array-bounds 104 -Werror=extra
104 -Werror=implicit-fallthrough
105 -Werror=missing-declarations 105 -Werror=missing-declarations
106 -Werror=missing-field-initializers
107 -Werror=reorder
108 -Werror=shadow 106 -Werror=shadow
109 -Werror=sign-compare 107 -Werror=unused
110 -Werror=switch 108
111 -Werror=uninitialized
112 -Werror=unused-function
113 -Werror=unused-result
114 -Werror=unused-variable
115 -Wextra
116 -Wmissing-declarations
117 -Wno-attributes 109 -Wno-attributes
118 -Wno-invalid-offsetof 110 -Wno-invalid-offsetof
119 -Wno-unused-parameter 111 -Wno-unused-parameter
112
113 $<$<CXX_COMPILER_ID:Clang>:-Wno-braced-scalar-init>
114 $<$<CXX_COMPILER_ID:Clang>:-Wno-unused-private-field>
120 ) 115 )
121 116
122 if (ARCHITECTURE_x86_64) 117 if (ARCHITECTURE_x86_64)
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index 144f1bab2..0a1f3bf18 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -206,20 +206,11 @@ if (MSVC)
206 /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data 206 /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data
207 /we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch 207 /we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch
208 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data 208 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
209 /we4456 # Declaration of 'identifier' hides previous local declaration 209 /we4800 # Implicit conversion from 'type' to bool. Possible information loss
210 /we4457 # Declaration of 'identifier' hides function parameter
211 /we4458 # Declaration of 'identifier' hides class member
212 /we4459 # Declaration of 'identifier' hides global declaration
213 ) 210 )
214else() 211else()
215 target_compile_options(audio_core PRIVATE 212 target_compile_options(audio_core PRIVATE
216 -Werror=conversion 213 -Werror=conversion
217 -Werror=ignored-qualifiers
218 -Werror=shadow
219 -Werror=unused-variable
220
221 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
222 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
223 214
224 -Wno-sign-conversion 215 -Wno-sign-conversion
225 ) 216 )
diff --git a/src/audio_core/renderer/behavior/info_updater.cpp b/src/audio_core/renderer/behavior/info_updater.cpp
index c0a307b89..574cf0982 100644
--- a/src/audio_core/renderer/behavior/info_updater.cpp
+++ b/src/audio_core/renderer/behavior/info_updater.cpp
@@ -91,7 +91,7 @@ Result InfoUpdater::UpdateVoices(VoiceContext& voice_context,
91 voice_info.Initialize(); 91 voice_info.Initialize();
92 92
93 for (u32 channel = 0; channel < in_param.channel_count; channel++) { 93 for (u32 channel = 0; channel < in_param.channel_count; channel++) {
94 std::memset(voice_states[channel], 0, sizeof(VoiceState)); 94 *voice_states[channel] = {};
95 } 95 }
96 } 96 }
97 97
diff --git a/src/audio_core/renderer/command/effect/biquad_filter.cpp b/src/audio_core/renderer/command/effect/biquad_filter.cpp
index 1baae74fd..edb30ce72 100644
--- a/src/audio_core/renderer/command/effect/biquad_filter.cpp
+++ b/src/audio_core/renderer/command/effect/biquad_filter.cpp
@@ -94,7 +94,7 @@ void BiquadFilterCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor
94void BiquadFilterCommand::Process(const ADSP::CommandListProcessor& processor) { 94void BiquadFilterCommand::Process(const ADSP::CommandListProcessor& processor) {
95 auto state_{reinterpret_cast<VoiceState::BiquadFilterState*>(state)}; 95 auto state_{reinterpret_cast<VoiceState::BiquadFilterState*>(state)};
96 if (needs_init) { 96 if (needs_init) {
97 std::memset(state_, 0, sizeof(VoiceState::BiquadFilterState)); 97 *state_ = {};
98 } 98 }
99 99
100 auto input_buffer{ 100 auto input_buffer{
diff --git a/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp b/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp
index b3c3ba4ba..48a7cba8a 100644
--- a/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp
+++ b/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp
@@ -30,7 +30,7 @@ void MultiTapBiquadFilterCommand::Process(const ADSP::CommandListProcessor& proc
30 for (u32 i = 0; i < filter_tap_count; i++) { 30 for (u32 i = 0; i < filter_tap_count; i++) {
31 auto state{reinterpret_cast<VoiceState::BiquadFilterState*>(states[i])}; 31 auto state{reinterpret_cast<VoiceState::BiquadFilterState*>(states[i])};
32 if (needs_init[i]) { 32 if (needs_init[i]) {
33 std::memset(state, 0, sizeof(VoiceState::BiquadFilterState)); 33 *state = {};
34 } 34 }
35 35
36 ApplyBiquadFilterFloat(output_buffer, input_buffer, biquads[i].b, biquads[i].a, *state, 36 ApplyBiquadFilterFloat(output_buffer, input_buffer, biquads[i].b, biquads[i].a, *state,
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 46cf75fde..c0555f840 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -156,12 +156,13 @@ if (MSVC)
156 ) 156 )
157 target_compile_options(common PRIVATE 157 target_compile_options(common PRIVATE
158 /W4 158 /W4
159 /WX 159
160 /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data
161 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
162 /we4800 # Implicit conversion from 'type' to bool. Possible information loss
160 ) 163 )
161else() 164else()
162 target_compile_options(common PRIVATE 165 target_compile_options(common PRIVATE
163 -Werror
164
165 $<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation> 166 $<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation>
166 ) 167 )
167endif() 168endif()
diff --git a/src/common/bit_field.h b/src/common/bit_field.h
index 7e1df62b1..e4e58ea45 100644
--- a/src/common/bit_field.h
+++ b/src/common/bit_field.h
@@ -141,10 +141,6 @@ public:
141 constexpr BitField(BitField&&) noexcept = default; 141 constexpr BitField(BitField&&) noexcept = default;
142 constexpr BitField& operator=(BitField&&) noexcept = default; 142 constexpr BitField& operator=(BitField&&) noexcept = default;
143 143
144 [[nodiscard]] constexpr operator T() const {
145 return Value();
146 }
147
148 constexpr void Assign(const T& value) { 144 constexpr void Assign(const T& value) {
149#ifdef _MSC_VER 145#ifdef _MSC_VER
150 storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value)); 146 storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value));
@@ -162,6 +158,17 @@ public:
162 return ExtractValue(storage); 158 return ExtractValue(storage);
163 } 159 }
164 160
161 template <typename ConvertedToType>
162 [[nodiscard]] constexpr ConvertedToType As() const {
163 static_assert(!std::is_same_v<T, ConvertedToType>,
164 "Unnecessary cast. Use Value() instead.");
165 return static_cast<ConvertedToType>(Value());
166 }
167
168 [[nodiscard]] constexpr operator T() const {
169 return Value();
170 }
171
165 [[nodiscard]] constexpr explicit operator bool() const { 172 [[nodiscard]] constexpr explicit operator bool() const {
166 return Value() != 0; 173 return Value() != 0;
167 } 174 }
diff --git a/src/common/bounded_threadsafe_queue.h b/src/common/bounded_threadsafe_queue.h
index 7e465549b..21217801e 100644
--- a/src/common/bounded_threadsafe_queue.h
+++ b/src/common/bounded_threadsafe_queue.h
@@ -21,11 +21,6 @@ constexpr size_t hardware_interference_size = std::hardware_destructive_interfer
21constexpr size_t hardware_interference_size = 64; 21constexpr size_t hardware_interference_size = 64;
22#endif 22#endif
23 23
24#ifdef _MSC_VER
25#pragma warning(push)
26#pragma warning(disable : 4324)
27#endif
28
29template <typename T, size_t capacity = 0x400> 24template <typename T, size_t capacity = 0x400>
30class MPSCQueue { 25class MPSCQueue {
31public: 26public:
@@ -160,8 +155,4 @@ private:
160 static_assert(std::is_nothrow_destructible_v<T>, "T must be nothrow destructible"); 155 static_assert(std::is_nothrow_destructible_v<T>, "T must be nothrow destructible");
161}; 156};
162 157
163#ifdef _MSC_VER
164#pragma warning(pop)
165#endif
166
167} // namespace Common 158} // namespace Common
diff --git a/src/common/concepts.h b/src/common/concepts.h
index e8ce30dfe..a9acff3e7 100644
--- a/src/common/concepts.h
+++ b/src/common/concepts.h
@@ -3,24 +3,14 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <iterator>
6#include <type_traits> 7#include <type_traits>
7 8
8namespace Common { 9namespace Common {
9 10
10// Check if type is like an STL container 11// Check if type satisfies the ContiguousContainer named requirement.
11template <typename T> 12template <typename T>
12concept IsSTLContainer = requires(T t) { 13concept IsContiguousContainer = std::contiguous_iterator<typename T::iterator>;
13 typename T::value_type;
14 typename T::iterator;
15 typename T::const_iterator;
16 // TODO(ogniK): Replace below is std::same_as<void> when MSVC supports it.
17 t.begin();
18 t.end();
19 t.cbegin();
20 t.cend();
21 t.data();
22 t.size();
23};
24 14
25// TODO: Replace with std::derived_from when the <concepts> header 15// TODO: Replace with std::derived_from when the <concepts> header
26// is available on all supported platforms. 16// is available on all supported platforms.
diff --git a/src/common/fs/file.h b/src/common/fs/file.h
index 69b53384c..167c4d826 100644
--- a/src/common/fs/file.h
+++ b/src/common/fs/file.h
@@ -209,8 +209,8 @@ public:
209 209
210 /** 210 /**
211 * Helper function which deduces the value type of a contiguous STL container used in ReadSpan. 211 * Helper function which deduces the value type of a contiguous STL container used in ReadSpan.
212 * If T is not a contiguous STL container as defined by the concept IsSTLContainer, this calls 212 * If T is not a contiguous container as defined by the concept IsContiguousContainer, this
213 * ReadObject and T must be a trivially copyable object. 213 * calls ReadObject and T must be a trivially copyable object.
214 * 214 *
215 * See ReadSpan for more details if T is a contiguous container. 215 * See ReadSpan for more details if T is a contiguous container.
216 * See ReadObject for more details if T is a trivially copyable object. 216 * See ReadObject for more details if T is a trivially copyable object.
@@ -223,7 +223,7 @@ public:
223 */ 223 */
224 template <typename T> 224 template <typename T>
225 [[nodiscard]] size_t Read(T& data) const { 225 [[nodiscard]] size_t Read(T& data) const {
226 if constexpr (IsSTLContainer<T>) { 226 if constexpr (IsContiguousContainer<T>) {
227 using ContiguousType = typename T::value_type; 227 using ContiguousType = typename T::value_type;
228 static_assert(std::is_trivially_copyable_v<ContiguousType>, 228 static_assert(std::is_trivially_copyable_v<ContiguousType>,
229 "Data type must be trivially copyable."); 229 "Data type must be trivially copyable.");
@@ -235,8 +235,8 @@ public:
235 235
236 /** 236 /**
237 * Helper function which deduces the value type of a contiguous STL container used in WriteSpan. 237 * Helper function which deduces the value type of a contiguous STL container used in WriteSpan.
238 * If T is not a contiguous STL container as defined by the concept IsSTLContainer, this calls 238 * If T is not a contiguous STL container as defined by the concept IsContiguousContainer, this
239 * WriteObject and T must be a trivially copyable object. 239 * calls WriteObject and T must be a trivially copyable object.
240 * 240 *
241 * See WriteSpan for more details if T is a contiguous container. 241 * See WriteSpan for more details if T is a contiguous container.
242 * See WriteObject for more details if T is a trivially copyable object. 242 * See WriteObject for more details if T is a trivially copyable object.
@@ -249,7 +249,7 @@ public:
249 */ 249 */
250 template <typename T> 250 template <typename T>
251 [[nodiscard]] size_t Write(const T& data) const { 251 [[nodiscard]] size_t Write(const T& data) const {
252 if constexpr (IsSTLContainer<T>) { 252 if constexpr (IsContiguousContainer<T>) {
253 using ContiguousType = typename T::value_type; 253 using ContiguousType = typename T::value_type;
254 static_assert(std::is_trivially_copyable_v<ContiguousType>, 254 static_assert(std::is_trivially_copyable_v<ContiguousType>,
255 "Data type must be trivially copyable."); 255 "Data type must be trivially copyable.");
diff --git a/src/common/input.h b/src/common/input.h
index b533f3844..cb30b7254 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -100,7 +100,6 @@ enum class CameraError {
100enum class VibrationAmplificationType { 100enum class VibrationAmplificationType {
101 Linear, 101 Linear,
102 Exponential, 102 Exponential,
103 Test,
104}; 103};
105 104
106// Analog properties for calibration 105// Analog properties for calibration
@@ -325,6 +324,10 @@ public:
325 return VibrationError::NotSupported; 324 return VibrationError::NotSupported;
326 } 325 }
327 326
327 virtual bool IsVibrationEnabled() {
328 return false;
329 }
330
328 virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) { 331 virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) {
329 return PollingError::NotSupported; 332 return PollingError::NotSupported;
330 } 333 }
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 055bea641..113e663b5 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -774,19 +774,15 @@ if (MSVC)
774 /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data 774 /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data
775 /we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch 775 /we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch
776 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data 776 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
777 /we4800 # Implicit conversion from 'type' to bool. Possible information loss
777 ) 778 )
778else() 779else()
779 target_compile_options(core PRIVATE 780 target_compile_options(core PRIVATE
780 -Werror=conversion 781 -Werror=conversion
781 -Werror=ignored-qualifiers
782 782
783 $<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess> 783 -Wno-sign-conversion
784 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
785 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
786 784
787 $<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation> 785 $<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation>
788
789 -Wno-sign-conversion
790 ) 786 )
791endif() 787endif()
792 788
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 7fb8bc019..40a610435 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -384,6 +384,7 @@ struct System::Impl {
384 kernel.ShutdownCores(); 384 kernel.ShutdownCores();
385 cpu_manager.Shutdown(); 385 cpu_manager.Shutdown();
386 debugger.reset(); 386 debugger.reset();
387 services->KillNVNFlinger();
387 kernel.CloseServices(); 388 kernel.CloseServices();
388 services.reset(); 389 services.reset();
389 service_manager.reset(); 390 service_manager.reset();
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index f23d9373b..5d02865f4 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -232,8 +232,8 @@ const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const {
232 232
233std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const { 233std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const {
234 const auto program_id = secure_partition->GetProgramTitleID(); 234 const auto program_id = secure_partition->GetProgramTitleID();
235 const auto iter = std::find_if( 235 const auto iter =
236 ncas.begin(), ncas.end(), [this, type, program_id](const std::shared_ptr<NCA>& nca) { 236 std::find_if(ncas.begin(), ncas.end(), [type, program_id](const std::shared_ptr<NCA>& nca) {
237 return nca->GetType() == type && nca->GetTitleId() == program_id; 237 return nca->GetType() == type && nca->GetTitleId() == program_id;
238 }); 238 });
239 return iter == ncas.end() ? nullptr : *iter; 239 return iter == ncas.end() ? nullptr : *iter;
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp
index 08d489eab..f00479bd3 100644
--- a/src/core/file_sys/program_metadata.cpp
+++ b/src/core/file_sys/program_metadata.cpp
@@ -127,7 +127,7 @@ void ProgramMetadata::LoadManual(bool is_64_bit, ProgramAddressSpaceType address
127} 127}
128 128
129bool ProgramMetadata::Is64BitProgram() const { 129bool ProgramMetadata::Is64BitProgram() const {
130 return npdm_header.has_64_bit_instructions; 130 return npdm_header.has_64_bit_instructions.As<bool>();
131} 131}
132 132
133ProgramAddressSpaceType ProgramMetadata::GetAddressSpaceType() const { 133ProgramAddressSpaceType ProgramMetadata::GetAddressSpaceType() const {
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 025f1c78e..ec1364452 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -970,14 +970,7 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v
970 Common::Input::VibrationError::None; 970 Common::Input::VibrationError::None;
971} 971}
972 972
973bool EmulatedController::TestVibration(std::size_t device_index) { 973bool EmulatedController::IsVibrationEnabled(std::size_t device_index) {
974 if (device_index >= output_devices.size()) {
975 return false;
976 }
977 if (!output_devices[device_index]) {
978 return false;
979 }
980
981 const auto player_index = NpadIdTypeToIndex(npad_id_type); 974 const auto player_index = NpadIdTypeToIndex(npad_id_type);
982 const auto& player = Settings::values.players.GetValue()[player_index]; 975 const auto& player = Settings::values.players.GetValue()[player_index];
983 976
@@ -985,31 +978,15 @@ bool EmulatedController::TestVibration(std::size_t device_index) {
985 return false; 978 return false;
986 } 979 }
987 980
988 const Common::Input::VibrationStatus test_vibration = { 981 if (device_index >= output_devices.size()) {
989 .low_amplitude = 0.001f, 982 return false;
990 .low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency, 983 }
991 .high_amplitude = 0.001f,
992 .high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency,
993 .type = Common::Input::VibrationAmplificationType::Test,
994 };
995
996 const Common::Input::VibrationStatus zero_vibration = {
997 .low_amplitude = DEFAULT_VIBRATION_VALUE.low_amplitude,
998 .low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency,
999 .high_amplitude = DEFAULT_VIBRATION_VALUE.high_amplitude,
1000 .high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency,
1001 .type = Common::Input::VibrationAmplificationType::Test,
1002 };
1003
1004 // Send a slight vibration to test for rumble support
1005 output_devices[device_index]->SetVibration(test_vibration);
1006 984
1007 // Wait for about 15ms to ensure the controller is ready for the stop command 985 if (!output_devices[device_index]) {
1008 std::this_thread::sleep_for(std::chrono::milliseconds(15)); 986 return false;
987 }
1009 988
1010 // Stop any vibration and return the result 989 return output_devices[device_index]->IsVibrationEnabled();
1011 return output_devices[device_index]->SetVibration(zero_vibration) ==
1012 Common::Input::VibrationError::None;
1013} 990}
1014 991
1015bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) { 992bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) {
@@ -1048,6 +1025,7 @@ bool EmulatedController::HasNfc() const {
1048 case NpadStyleIndex::JoyconRight: 1025 case NpadStyleIndex::JoyconRight:
1049 case NpadStyleIndex::JoyconDual: 1026 case NpadStyleIndex::JoyconDual:
1050 case NpadStyleIndex::ProController: 1027 case NpadStyleIndex::ProController:
1028 case NpadStyleIndex::Handheld:
1051 break; 1029 break;
1052 default: 1030 default:
1053 return false; 1031 return false;
@@ -1158,27 +1136,27 @@ bool EmulatedController::IsControllerSupported(bool use_temporary_value) const {
1158 const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; 1136 const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
1159 switch (type) { 1137 switch (type) {
1160 case NpadStyleIndex::ProController: 1138 case NpadStyleIndex::ProController:
1161 return supported_style_tag.fullkey; 1139 return supported_style_tag.fullkey.As<bool>();
1162 case NpadStyleIndex::Handheld: 1140 case NpadStyleIndex::Handheld:
1163 return supported_style_tag.handheld; 1141 return supported_style_tag.handheld.As<bool>();
1164 case NpadStyleIndex::JoyconDual: 1142 case NpadStyleIndex::JoyconDual:
1165 return supported_style_tag.joycon_dual; 1143 return supported_style_tag.joycon_dual.As<bool>();
1166 case NpadStyleIndex::JoyconLeft: 1144 case NpadStyleIndex::JoyconLeft:
1167 return supported_style_tag.joycon_left; 1145 return supported_style_tag.joycon_left.As<bool>();
1168 case NpadStyleIndex::JoyconRight: 1146 case NpadStyleIndex::JoyconRight:
1169 return supported_style_tag.joycon_right; 1147 return supported_style_tag.joycon_right.As<bool>();
1170 case NpadStyleIndex::GameCube: 1148 case NpadStyleIndex::GameCube:
1171 return supported_style_tag.gamecube; 1149 return supported_style_tag.gamecube.As<bool>();
1172 case NpadStyleIndex::Pokeball: 1150 case NpadStyleIndex::Pokeball:
1173 return supported_style_tag.palma; 1151 return supported_style_tag.palma.As<bool>();
1174 case NpadStyleIndex::NES: 1152 case NpadStyleIndex::NES:
1175 return supported_style_tag.lark; 1153 return supported_style_tag.lark.As<bool>();
1176 case NpadStyleIndex::SNES: 1154 case NpadStyleIndex::SNES:
1177 return supported_style_tag.lucia; 1155 return supported_style_tag.lucia.As<bool>();
1178 case NpadStyleIndex::N64: 1156 case NpadStyleIndex::N64:
1179 return supported_style_tag.lagoon; 1157 return supported_style_tag.lagoon.As<bool>();
1180 case NpadStyleIndex::SegaGenesis: 1158 case NpadStyleIndex::SegaGenesis:
1181 return supported_style_tag.lager; 1159 return supported_style_tag.lager.As<bool>();
1182 default: 1160 default:
1183 return false; 1161 return false;
1184 } 1162 }
@@ -1234,12 +1212,6 @@ bool EmulatedController::IsConnected(bool get_temporary_value) const {
1234 return is_connected; 1212 return is_connected;
1235} 1213}
1236 1214
1237bool EmulatedController::IsVibrationEnabled() const {
1238 const auto player_index = NpadIdTypeToIndex(npad_id_type);
1239 const auto& player = Settings::values.players.GetValue()[player_index];
1240 return player.vibration_enabled;
1241}
1242
1243NpadIdType EmulatedController::GetNpadIdType() const { 1215NpadIdType EmulatedController::GetNpadIdType() const {
1244 std::scoped_lock lock{mutex}; 1216 std::scoped_lock lock{mutex};
1245 return npad_id_type; 1217 return npad_id_type;
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index 319226bf8..d004ca56a 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -206,9 +206,6 @@ public:
206 */ 206 */
207 bool IsConnected(bool get_temporary_value = false) const; 207 bool IsConnected(bool get_temporary_value = false) const;
208 208
209 /// Returns true if vibration is enabled
210 bool IsVibrationEnabled() const;
211
212 /// Removes all callbacks created from input devices 209 /// Removes all callbacks created from input devices
213 void UnloadInput(); 210 void UnloadInput();
214 211
@@ -339,7 +336,7 @@ public:
339 * Sends a small vibration to the output device 336 * Sends a small vibration to the output device
340 * @return true if SetVibration was successfull 337 * @return true if SetVibration was successfull
341 */ 338 */
342 bool TestVibration(std::size_t device_index); 339 bool IsVibrationEnabled(std::size_t device_index);
343 340
344 /** 341 /**
345 * Sets the desired data to be polled from a controller 342 * Sets the desired data to be polled from a controller
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index aa27be767..18fde8bd6 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -406,7 +406,7 @@ inline s32 RequestParser::Pop() {
406} 406}
407 407
408// Ignore the -Wclass-memaccess warning on memcpy for non-trivially default constructible objects. 408// Ignore the -Wclass-memaccess warning on memcpy for non-trivially default constructible objects.
409#if defined(__GNUC__) 409#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
410#pragma GCC diagnostic push 410#pragma GCC diagnostic push
411#pragma GCC diagnostic ignored "-Wclass-memaccess" 411#pragma GCC diagnostic ignored "-Wclass-memaccess"
412#endif 412#endif
@@ -417,7 +417,7 @@ void RequestParser::PopRaw(T& value) {
417 std::memcpy(&value, cmdbuf + index, sizeof(T)); 417 std::memcpy(&value, cmdbuf + index, sizeof(T));
418 index += (sizeof(T) + 3) / 4; // round up to word length 418 index += (sizeof(T) + 3) / 4; // round up to word length
419} 419}
420#if defined(__GNUC__) 420#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
421#pragma GCC diagnostic pop 421#pragma GCC diagnostic pop
422#endif 422#endif
423 423
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index a0522bca0..1083638a9 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -304,7 +304,7 @@ public:
304 */ 304 */
305 template <typename T, typename = std::enable_if_t<!std::is_pointer_v<T>>> 305 template <typename T, typename = std::enable_if_t<!std::is_pointer_v<T>>>
306 std::size_t WriteBuffer(const T& data, std::size_t buffer_index = 0) const { 306 std::size_t WriteBuffer(const T& data, std::size_t buffer_index = 0) const {
307 if constexpr (Common::IsSTLContainer<T>) { 307 if constexpr (Common::IsContiguousContainer<T>) {
308 using ContiguousType = typename T::value_type; 308 using ContiguousType = typename T::value_type;
309 static_assert(std::is_trivially_copyable_v<ContiguousType>, 309 static_assert(std::is_trivially_copyable_v<ContiguousType>,
310 "Container to WriteBuffer must contain trivially copyable objects"); 310 "Container to WriteBuffer must contain trivially copyable objects");
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index eed2dc9f3..fdc774e30 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -48,8 +48,8 @@ namespace Kernel {
48 48
49struct KernelCore::Impl { 49struct KernelCore::Impl {
50 explicit Impl(Core::System& system_, KernelCore& kernel_) 50 explicit Impl(Core::System& system_, KernelCore& kernel_)
51 : time_manager{system_}, 51 : time_manager{system_}, service_threads_manager{1, "ServiceThreadsManager"},
52 service_threads_manager{1, "ServiceThreadsManager"}, system{system_} {} 52 service_thread_barrier{2}, system{system_} {}
53 53
54 void SetMulticore(bool is_multi) { 54 void SetMulticore(bool is_multi) {
55 is_multicore = is_multi; 55 is_multicore = is_multi;
@@ -737,7 +737,12 @@ struct KernelCore::Impl {
737 } 737 }
738 738
739 void ClearServiceThreads() { 739 void ClearServiceThreads() {
740 service_threads_manager.QueueWork([this]() { service_threads.clear(); }); 740 service_threads_manager.QueueWork([this] {
741 service_threads.clear();
742 default_service_thread.reset();
743 service_thread_barrier.Sync();
744 });
745 service_thread_barrier.Sync();
741 } 746 }
742 747
743 std::mutex server_objects_lock; 748 std::mutex server_objects_lock;
@@ -802,6 +807,7 @@ struct KernelCore::Impl {
802 std::unordered_set<std::shared_ptr<ServiceThread>> service_threads; 807 std::unordered_set<std::shared_ptr<ServiceThread>> service_threads;
803 std::weak_ptr<ServiceThread> default_service_thread; 808 std::weak_ptr<ServiceThread> default_service_thread;
804 Common::ThreadWorker service_threads_manager; 809 Common::ThreadWorker service_threads_manager;
810 Common::Barrier service_thread_barrier;
805 811
806 std::array<KThread*, Core::Hardware::NUM_CPU_CORES> shutdown_threads; 812 std::array<KThread*, Core::Hardware::NUM_CPU_CORES> shutdown_threads;
807 std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{}; 813 std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index b07ae3f02..4aca5b27d 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -751,8 +751,8 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
751 } 751 }
752 752
753 system.GetReporter().SaveSvcBreakReport( 753 system.GetReporter().SaveSvcBreakReport(
754 static_cast<u32>(break_reason.break_type.Value()), break_reason.signal_debugger, info1, 754 static_cast<u32>(break_reason.break_type.Value()), break_reason.signal_debugger.As<bool>(),
755 info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt); 755 info1, info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt);
756 756
757 if (!break_reason.signal_debugger) { 757 if (!break_reason.signal_debugger) {
758 LOG_CRITICAL( 758 LOG_CRITICAL(
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index e78a57657..12c6a5b1a 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -164,7 +164,7 @@ protected:
164 u32_le size; 164 u32_le size;
165 u32_le library_version; 165 u32_le library_version;
166 u32_le theme_color; 166 u32_le theme_color;
167 u8 play_startup_sound; 167 bool play_startup_sound;
168 u64_le system_tick; 168 u64_le system_tick;
169 }; 169 };
170 static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size."); 170 static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size.");
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index ba8a1f786..2f871de31 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -868,7 +868,7 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
868 return false; 868 return false;
869 } 869 }
870 870
871 if (!controller.device->IsVibrationEnabled()) { 871 if (!controller.device->IsVibrationEnabled(device_index)) {
872 if (controller.vibration[device_index].latest_vibration_value.low_amplitude != 0.0f || 872 if (controller.vibration[device_index].latest_vibration_value.low_amplitude != 0.0f ||
873 controller.vibration[device_index].latest_vibration_value.high_amplitude != 0.0f) { 873 controller.vibration[device_index].latest_vibration_value.high_amplitude != 0.0f) {
874 // Send an empty vibration to stop any vibrations. 874 // Send an empty vibration to stop any vibrations.
@@ -1001,7 +1001,7 @@ void Controller_NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npa
1001 } 1001 }
1002 1002
1003 controller.vibration[device_index].device_mounted = 1003 controller.vibration[device_index].device_mounted =
1004 controller.device->TestVibration(device_index); 1004 controller.device->IsVibrationEnabled(device_index);
1005} 1005}
1006 1006
1007void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) { 1007void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) {
@@ -1502,25 +1502,25 @@ bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller
1502 Core::HID::NpadStyleTag style = GetSupportedStyleSet(); 1502 Core::HID::NpadStyleTag style = GetSupportedStyleSet();
1503 switch (controller) { 1503 switch (controller) {
1504 case Core::HID::NpadStyleIndex::ProController: 1504 case Core::HID::NpadStyleIndex::ProController:
1505 return style.fullkey; 1505 return style.fullkey.As<bool>();
1506 case Core::HID::NpadStyleIndex::JoyconDual: 1506 case Core::HID::NpadStyleIndex::JoyconDual:
1507 return style.joycon_dual; 1507 return style.joycon_dual.As<bool>();
1508 case Core::HID::NpadStyleIndex::JoyconLeft: 1508 case Core::HID::NpadStyleIndex::JoyconLeft:
1509 return style.joycon_left; 1509 return style.joycon_left.As<bool>();
1510 case Core::HID::NpadStyleIndex::JoyconRight: 1510 case Core::HID::NpadStyleIndex::JoyconRight:
1511 return style.joycon_right; 1511 return style.joycon_right.As<bool>();
1512 case Core::HID::NpadStyleIndex::GameCube: 1512 case Core::HID::NpadStyleIndex::GameCube:
1513 return style.gamecube; 1513 return style.gamecube.As<bool>();
1514 case Core::HID::NpadStyleIndex::Pokeball: 1514 case Core::HID::NpadStyleIndex::Pokeball:
1515 return style.palma; 1515 return style.palma.As<bool>();
1516 case Core::HID::NpadStyleIndex::NES: 1516 case Core::HID::NpadStyleIndex::NES:
1517 return style.lark; 1517 return style.lark.As<bool>();
1518 case Core::HID::NpadStyleIndex::SNES: 1518 case Core::HID::NpadStyleIndex::SNES:
1519 return style.lucia; 1519 return style.lucia.As<bool>();
1520 case Core::HID::NpadStyleIndex::N64: 1520 case Core::HID::NpadStyleIndex::N64:
1521 return style.lagoon; 1521 return style.lagoon.As<bool>();
1522 case Core::HID::NpadStyleIndex::SegaGenesis: 1522 case Core::HID::NpadStyleIndex::SegaGenesis:
1523 return style.lager; 1523 return style.lager.As<bool>();
1524 default: 1524 default:
1525 return false; 1525 return false;
1526 } 1526 }
diff --git a/src/core/hle/service/nfp/amiibo_crypto.cpp b/src/core/hle/service/nfp/amiibo_crypto.cpp
index c32a6816b..167e29572 100644
--- a/src/core/hle/service/nfp/amiibo_crypto.cpp
+++ b/src/core/hle/service/nfp/amiibo_crypto.cpp
@@ -9,6 +9,7 @@
9#include <mbedtls/hmac_drbg.h> 9#include <mbedtls/hmac_drbg.h>
10 10
11#include "common/fs/file.h" 11#include "common/fs/file.h"
12#include "common/fs/fs.h"
12#include "common/fs/path_util.h" 13#include "common/fs/path_util.h"
13#include "common/logging/log.h" 14#include "common/logging/log.h"
14#include "core/hle/service/mii/mii_manager.h" 15#include "core/hle/service/mii/mii_manager.h"
@@ -279,7 +280,7 @@ bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info) {
279 Common::FS::FileType::BinaryFile}; 280 Common::FS::FileType::BinaryFile};
280 281
281 if (!keys_file.IsOpen()) { 282 if (!keys_file.IsOpen()) {
282 LOG_ERROR(Service_NFP, "No keys detected"); 283 LOG_ERROR(Service_NFP, "Failed to open key file");
283 return false; 284 return false;
284 } 285 }
285 286
@@ -295,6 +296,11 @@ bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info) {
295 return true; 296 return true;
296} 297}
297 298
299bool IsKeyAvailable() {
300 const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir);
301 return Common::FS::Exists(yuzu_keys_dir / "key_retail.bin");
302}
303
298bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data) { 304bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data) {
299 InternalKey locked_secret{}; 305 InternalKey locked_secret{};
300 InternalKey unfixed_info{}; 306 InternalKey unfixed_info{};
diff --git a/src/core/hle/service/nfp/amiibo_crypto.h b/src/core/hle/service/nfp/amiibo_crypto.h
index 0175ced91..1fa61174e 100644
--- a/src/core/hle/service/nfp/amiibo_crypto.h
+++ b/src/core/hle/service/nfp/amiibo_crypto.h
@@ -91,6 +91,9 @@ void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& ou
91/// Loads both amiibo keys from key_retail.bin 91/// Loads both amiibo keys from key_retail.bin
92bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info); 92bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info);
93 93
94/// Returns true if key_retail.bin exist
95bool IsKeyAvailable();
96
94/// Decodes encripted amiibo data returns true if output is valid 97/// Decodes encripted amiibo data returns true if output is valid
95bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data); 98bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data);
96 99
diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp
index 76f8a267a..b19672560 100644
--- a/src/core/hle/service/nfp/nfp_device.cpp
+++ b/src/core/hle/service/nfp/nfp_device.cpp
@@ -17,6 +17,7 @@
17#include "core/hle/ipc_helpers.h" 17#include "core/hle/ipc_helpers.h"
18#include "core/hle/kernel/k_event.h" 18#include "core/hle/kernel/k_event.h"
19#include "core/hle/service/mii/mii_manager.h" 19#include "core/hle/service/mii/mii_manager.h"
20#include "core/hle/service/mii/types.h"
20#include "core/hle/service/nfp/amiibo_crypto.h" 21#include "core/hle/service/nfp/amiibo_crypto.h"
21#include "core/hle/service/nfp/nfp.h" 22#include "core/hle/service/nfp/nfp.h"
22#include "core/hle/service/nfp/nfp_device.h" 23#include "core/hle/service/nfp/nfp_device.h"
@@ -233,6 +234,14 @@ Result NfpDevice::Mount(MountTarget mount_target_) {
233 return NotAnAmiibo; 234 return NotAnAmiibo;
234 } 235 }
235 236
237 // Mark amiibos as read only when keys are missing
238 if (!AmiiboCrypto::IsKeyAvailable()) {
239 LOG_ERROR(Service_NFP, "No keys detected");
240 device_state = DeviceState::TagMounted;
241 mount_target = MountTarget::Rom;
242 return ResultSuccess;
243 }
244
236 if (!AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) { 245 if (!AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) {
237 LOG_ERROR(Service_NFP, "Can't decode amiibo {}", device_state); 246 LOG_ERROR(Service_NFP, "Can't decode amiibo {}", device_state);
238 return CorruptedData; 247 return CorruptedData;
diff --git a/src/core/hle/service/nfp/nfp_device.h b/src/core/hle/service/nfp/nfp_device.h
index a5b72cf19..76d0e9ae4 100644
--- a/src/core/hle/service/nfp/nfp_device.h
+++ b/src/core/hle/service/nfp/nfp_device.h
@@ -8,7 +8,6 @@
8 8
9#include "common/common_funcs.h" 9#include "common/common_funcs.h"
10#include "core/hle/service/kernel_helpers.h" 10#include "core/hle/service/kernel_helpers.h"
11#include "core/hle/service/mii/types.h"
12#include "core/hle/service/nfp/nfp_types.h" 11#include "core/hle/service/nfp/nfp_types.h"
13#include "core/hle/service/service.h" 12#include "core/hle/service/service.h"
14 13
diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h
index c09f9ddb6..63d5917cb 100644
--- a/src/core/hle/service/nfp/nfp_types.h
+++ b/src/core/hle/service/nfp/nfp_types.h
@@ -17,11 +17,6 @@ enum class ServiceType : u32 {
17 System, 17 System,
18}; 18};
19 19
20enum class State : u32 {
21 NonInitialized,
22 Initialized,
23};
24
25enum class DeviceState : u32 { 20enum class DeviceState : u32 {
26 Initialized, 21 Initialized,
27 SearchingForTag, 22 SearchingForTag,
diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp
index 4ed53b534..33e2ef518 100644
--- a/src/core/hle/service/nfp/nfp_user.cpp
+++ b/src/core/hle/service/nfp/nfp_user.cpp
@@ -6,12 +6,9 @@
6 6
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/core.h" 8#include "core/core.h"
9#include "core/hid/emulated_controller.h"
10#include "core/hid/hid_core.h"
11#include "core/hid/hid_types.h" 9#include "core/hid/hid_types.h"
12#include "core/hle/ipc_helpers.h" 10#include "core/hle/ipc_helpers.h"
13#include "core/hle/kernel/k_event.h" 11#include "core/hle/kernel/k_event.h"
14#include "core/hle/service/mii/mii_manager.h"
15#include "core/hle/service/nfp/nfp_device.h" 12#include "core/hle/service/nfp/nfp_device.h"
16#include "core/hle/service/nfp/nfp_result.h" 13#include "core/hle/service/nfp/nfp_result.h"
17#include "core/hle/service/nfp/nfp_user.h" 14#include "core/hle/service/nfp/nfp_user.h"
diff --git a/src/core/hle/service/nfp/nfp_user.h b/src/core/hle/service/nfp/nfp_user.h
index 68c60ae82..47aff3695 100644
--- a/src/core/hle/service/nfp/nfp_user.h
+++ b/src/core/hle/service/nfp/nfp_user.h
@@ -4,8 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include "core/hle/service/kernel_helpers.h" 6#include "core/hle/service/kernel_helpers.h"
7#include "core/hle/service/nfp/nfp.h" 7#include "core/hle/service/service.h"
8#include "core/hle/service/nfp/nfp_types.h"
9 8
10namespace Service::NFP { 9namespace Service::NFP {
11class NfpDevice; 10class NfpDevice;
@@ -15,6 +14,11 @@ public:
15 explicit IUser(Core::System& system_); 14 explicit IUser(Core::System& system_);
16 15
17private: 16private:
17 enum class State : u32 {
18 NonInitialized,
19 Initialized,
20 };
21
18 void Initialize(Kernel::HLERequestContext& ctx); 22 void Initialize(Kernel::HLERequestContext& ctx);
19 void Finalize(Kernel::HLERequestContext& ctx); 23 void Finalize(Kernel::HLERequestContext& ctx);
20 void ListDevices(Kernel::HLERequestContext& ctx); 24 void ListDevices(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 9d9924395..9f4c7c99a 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -53,7 +53,7 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger
53} 53}
54 54
55Module::Module(Core::System& system) 55Module::Module(Core::System& system)
56 : service_context{system, "nvdrv"}, events_interface{*this}, container{system.Host1x()} { 56 : container{system.Host1x()}, service_context{system, "nvdrv"}, events_interface{*this} {
57 builders["/dev/nvhost-as-gpu"] = [this, &system](DeviceFD fd) { 57 builders["/dev/nvhost-as-gpu"] = [this, &system](DeviceFD fd) {
58 std::shared_ptr<Devices::nvdevice> device = 58 std::shared_ptr<Devices::nvdevice> device =
59 std::make_shared<Devices::nvhost_as_gpu>(system, *this, container); 59 std::make_shared<Devices::nvhost_as_gpu>(system, *this, container);
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index 146d046a9..f3c81bd88 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -97,6 +97,9 @@ private:
97 friend class EventInterface; 97 friend class EventInterface;
98 friend class Service::NVFlinger::NVFlinger; 98 friend class Service::NVFlinger::NVFlinger;
99 99
100 /// Manages syncpoints on the host
101 NvCore::Container container;
102
100 /// Id to use for the next open file descriptor. 103 /// Id to use for the next open file descriptor.
101 DeviceFD next_fd = 1; 104 DeviceFD next_fd = 1;
102 105
@@ -108,9 +111,6 @@ private:
108 111
109 EventInterface events_interface; 112 EventInterface events_interface;
110 113
111 /// Manages syncpoints on the host
112 NvCore::Container container;
113
114 std::unordered_map<std::string, std::function<FilesContainerType::iterator(DeviceFD)>> builders; 114 std::unordered_map<std::string, std::function<FilesContainerType::iterator(DeviceFD)>> builders;
115}; 115};
116 116
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index aa14d2cbc..dad93b38e 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -102,15 +102,19 @@ NVFlinger::~NVFlinger() {
102 system.CoreTiming().UnscheduleEvent(single_composition_event, {}); 102 system.CoreTiming().UnscheduleEvent(single_composition_event, {});
103 } 103 }
104 104
105 ShutdownLayers();
106
107 if (nvdrv) {
108 nvdrv->Close(disp_fd);
109 }
110}
111
112void NVFlinger::ShutdownLayers() {
105 for (auto& display : displays) { 113 for (auto& display : displays) {
106 for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) { 114 for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) {
107 display.GetLayer(layer).Core().NotifyShutdown(); 115 display.GetLayer(layer).Core().NotifyShutdown();
108 } 116 }
109 } 117 }
110
111 if (nvdrv) {
112 nvdrv->Close(disp_fd);
113 }
114} 118}
115 119
116void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { 120void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index 99509bc5b..b8191c595 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -48,6 +48,8 @@ public:
48 explicit NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_); 48 explicit NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_);
49 ~NVFlinger(); 49 ~NVFlinger();
50 50
51 void ShutdownLayers();
52
51 /// Sets the NVDrv module instance to use to send buffers to the GPU. 53 /// Sets the NVDrv module instance to use to send buffers to the GPU.
52 void SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance); 54 void SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance);
53 55
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index dadaf897f..5db6588e4 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -303,4 +303,8 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
303 303
304Services::~Services() = default; 304Services::~Services() = default;
305 305
306void Services::KillNVNFlinger() {
307 nv_flinger->ShutdownLayers();
308}
309
306} // namespace Service 310} // namespace Service
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 5bf197c51..ec9deeee4 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -238,6 +238,8 @@ public:
238 explicit Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system); 238 explicit Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system);
239 ~Services(); 239 ~Services();
240 240
241 void KillNVNFlinger();
242
241private: 243private:
242 std::unique_ptr<NVFlinger::HosBinderDriverServer> hos_binder_driver_server; 244 std::unique_ptr<NVFlinger::HosBinderDriverServer> hos_binder_driver_server;
243 std::unique_ptr<NVFlinger::NVFlinger> nv_flinger; 245 std::unique_ptr<NVFlinger::NVFlinger> nv_flinger;
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 9637cb5b1..3ca80c8ff 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -233,18 +233,17 @@ struct Memory::Impl {
233 current_vaddr, src_addr, size); 233 current_vaddr, src_addr, size);
234 std::memset(dest_buffer, 0, copy_amount); 234 std::memset(dest_buffer, 0, copy_amount);
235 }, 235 },
236 [&dest_buffer](const std::size_t copy_amount, const u8* const src_ptr) { 236 [&](const std::size_t copy_amount, const u8* const src_ptr) {
237 std::memcpy(dest_buffer, src_ptr, copy_amount); 237 std::memcpy(dest_buffer, src_ptr, copy_amount);
238 }, 238 },
239 [&system = system, &dest_buffer](const VAddr current_vaddr, 239 [&](const VAddr current_vaddr, const std::size_t copy_amount,
240 const std::size_t copy_amount, 240 const u8* const host_ptr) {
241 const u8* const host_ptr) {
242 if constexpr (!UNSAFE) { 241 if constexpr (!UNSAFE) {
243 system.GPU().FlushRegion(current_vaddr, copy_amount); 242 system.GPU().FlushRegion(current_vaddr, copy_amount);
244 } 243 }
245 std::memcpy(dest_buffer, host_ptr, copy_amount); 244 std::memcpy(dest_buffer, host_ptr, copy_amount);
246 }, 245 },
247 [&dest_buffer](const std::size_t copy_amount) { 246 [&](const std::size_t copy_amount) {
248 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; 247 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
249 }); 248 });
250 } 249 }
@@ -267,17 +266,16 @@ struct Memory::Impl {
267 "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 266 "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
268 current_vaddr, dest_addr, size); 267 current_vaddr, dest_addr, size);
269 }, 268 },
270 [&src_buffer](const std::size_t copy_amount, u8* const dest_ptr) { 269 [&](const std::size_t copy_amount, u8* const dest_ptr) {
271 std::memcpy(dest_ptr, src_buffer, copy_amount); 270 std::memcpy(dest_ptr, src_buffer, copy_amount);
272 }, 271 },
273 [&system = system, &src_buffer](const VAddr current_vaddr, 272 [&](const VAddr current_vaddr, const std::size_t copy_amount, u8* const host_ptr) {
274 const std::size_t copy_amount, u8* const host_ptr) {
275 if constexpr (!UNSAFE) { 273 if constexpr (!UNSAFE) {
276 system.GPU().InvalidateRegion(current_vaddr, copy_amount); 274 system.GPU().InvalidateRegion(current_vaddr, copy_amount);
277 } 275 }
278 std::memcpy(host_ptr, src_buffer, copy_amount); 276 std::memcpy(host_ptr, src_buffer, copy_amount);
279 }, 277 },
280 [&src_buffer](const std::size_t copy_amount) { 278 [&](const std::size_t copy_amount) {
281 src_buffer = static_cast<const u8*>(src_buffer) + copy_amount; 279 src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
282 }); 280 });
283 } 281 }
@@ -301,8 +299,7 @@ struct Memory::Impl {
301 [](const std::size_t copy_amount, u8* const dest_ptr) { 299 [](const std::size_t copy_amount, u8* const dest_ptr) {
302 std::memset(dest_ptr, 0, copy_amount); 300 std::memset(dest_ptr, 0, copy_amount);
303 }, 301 },
304 [&system = system](const VAddr current_vaddr, const std::size_t copy_amount, 302 [&](const VAddr current_vaddr, const std::size_t copy_amount, u8* const host_ptr) {
305 u8* const host_ptr) {
306 system.GPU().InvalidateRegion(current_vaddr, copy_amount); 303 system.GPU().InvalidateRegion(current_vaddr, copy_amount);
307 std::memset(host_ptr, 0, copy_amount); 304 std::memset(host_ptr, 0, copy_amount);
308 }, 305 },
@@ -313,22 +310,20 @@ struct Memory::Impl {
313 const std::size_t size) { 310 const std::size_t size) {
314 WalkBlock( 311 WalkBlock(
315 process, dest_addr, size, 312 process, dest_addr, size,
316 [this, &process, &dest_addr, &src_addr, size](const std::size_t copy_amount, 313 [&](const std::size_t copy_amount, const VAddr current_vaddr) {
317 const VAddr current_vaddr) {
318 LOG_ERROR(HW_Memory, 314 LOG_ERROR(HW_Memory,
319 "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 315 "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
320 current_vaddr, src_addr, size); 316 current_vaddr, src_addr, size);
321 ZeroBlock(process, dest_addr, copy_amount); 317 ZeroBlock(process, dest_addr, copy_amount);
322 }, 318 },
323 [this, &process, &dest_addr](const std::size_t copy_amount, const u8* const src_ptr) { 319 [&](const std::size_t copy_amount, const u8* const src_ptr) {
324 WriteBlockImpl<false>(process, dest_addr, src_ptr, copy_amount); 320 WriteBlockImpl<false>(process, dest_addr, src_ptr, copy_amount);
325 }, 321 },
326 [this, &system = system, &process, &dest_addr]( 322 [&](const VAddr current_vaddr, const std::size_t copy_amount, u8* const host_ptr) {
327 const VAddr current_vaddr, const std::size_t copy_amount, u8* const host_ptr) {
328 system.GPU().FlushRegion(current_vaddr, copy_amount); 323 system.GPU().FlushRegion(current_vaddr, copy_amount);
329 WriteBlockImpl<false>(process, dest_addr, host_ptr, copy_amount); 324 WriteBlockImpl<false>(process, dest_addr, host_ptr, copy_amount);
330 }, 325 },
331 [&dest_addr, &src_addr](const std::size_t copy_amount) { 326 [&](const std::size_t copy_amount) {
332 dest_addr += static_cast<VAddr>(copy_amount); 327 dest_addr += static_cast<VAddr>(copy_amount);
333 src_addr += static_cast<VAddr>(copy_amount); 328 src_addr += static_cast<VAddr>(copy_amount);
334 }); 329 });
@@ -575,7 +570,7 @@ struct Memory::Impl {
575 [vaddr]() { 570 [vaddr]() {
576 LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8, vaddr); 571 LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8, vaddr);
577 }, 572 },
578 [&system = system, vaddr]() { system.GPU().FlushRegion(vaddr, sizeof(T)); }); 573 [&]() { system.GPU().FlushRegion(vaddr, sizeof(T)); });
579 if (ptr) { 574 if (ptr) {
580 std::memcpy(&result, ptr, sizeof(T)); 575 std::memcpy(&result, ptr, sizeof(T));
581 } 576 }
@@ -599,7 +594,7 @@ struct Memory::Impl {
599 LOG_ERROR(HW_Memory, "Unmapped Write{} @ 0x{:016X} = 0x{:016X}", sizeof(T) * 8, 594 LOG_ERROR(HW_Memory, "Unmapped Write{} @ 0x{:016X} = 0x{:016X}", sizeof(T) * 8,
600 vaddr, static_cast<u64>(data)); 595 vaddr, static_cast<u64>(data));
601 }, 596 },
602 [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); }); 597 [&]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); });
603 if (ptr) { 598 if (ptr) {
604 std::memcpy(ptr, &data, sizeof(T)); 599 std::memcpy(ptr, &data, sizeof(T));
605 } 600 }
@@ -613,7 +608,7 @@ struct Memory::Impl {
613 LOG_ERROR(HW_Memory, "Unmapped WriteExclusive{} @ 0x{:016X} = 0x{:016X}", 608 LOG_ERROR(HW_Memory, "Unmapped WriteExclusive{} @ 0x{:016X} = 0x{:016X}",
614 sizeof(T) * 8, vaddr, static_cast<u64>(data)); 609 sizeof(T) * 8, vaddr, static_cast<u64>(data));
615 }, 610 },
616 [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); }); 611 [&]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); });
617 if (ptr) { 612 if (ptr) {
618 const auto volatile_pointer = reinterpret_cast<volatile T*>(ptr); 613 const auto volatile_pointer = reinterpret_cast<volatile T*>(ptr);
619 return Common::AtomicCompareAndSwap(volatile_pointer, data, expected); 614 return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);
@@ -628,7 +623,7 @@ struct Memory::Impl {
628 LOG_ERROR(HW_Memory, "Unmapped WriteExclusive128 @ 0x{:016X} = 0x{:016X}{:016X}", 623 LOG_ERROR(HW_Memory, "Unmapped WriteExclusive128 @ 0x{:016X} = 0x{:016X}{:016X}",
629 vaddr, static_cast<u64>(data[1]), static_cast<u64>(data[0])); 624 vaddr, static_cast<u64>(data[1]), static_cast<u64>(data[0]));
630 }, 625 },
631 [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(u128)); }); 626 [&]() { system.GPU().InvalidateRegion(vaddr, sizeof(u128)); });
632 if (ptr) { 627 if (ptr) {
633 const auto volatile_pointer = reinterpret_cast<volatile u64*>(ptr); 628 const auto volatile_pointer = reinterpret_cast<volatile u64*>(ptr);
634 return Common::AtomicCompareAndSwap(volatile_pointer, data, expected); 629 return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index 2cf9eb97f..cc6f0ffc0 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -39,21 +39,14 @@ add_library(input_common STATIC
39if (MSVC) 39if (MSVC)
40 target_compile_options(input_common PRIVATE 40 target_compile_options(input_common PRIVATE
41 /W4 41 /W4
42 /WX
43 42
44 /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data 43 /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data
45 /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data
46 /we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch
47 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data 44 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
45 /we4800 # Implicit conversion from 'type' to bool. Possible information loss
48 ) 46 )
49else() 47else()
50 target_compile_options(input_common PRIVATE 48 target_compile_options(input_common PRIVATE
51 -Werror
52 -Werror=conversion 49 -Werror=conversion
53 -Werror=ignored-qualifiers
54 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
55 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
56 -Werror=unused-variable
57 ) 50 )
58endif() 51endif()
59 52
diff --git a/src/input_common/drivers/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp
index f4dd24e7d..826fa2109 100644
--- a/src/input_common/drivers/gc_adapter.cpp
+++ b/src/input_common/drivers/gc_adapter.cpp
@@ -324,7 +324,7 @@ bool GCAdapter::GetGCEndpoint(libusb_device* device) {
324 return true; 324 return true;
325} 325}
326 326
327Common::Input::VibrationError GCAdapter::SetRumble( 327Common::Input::VibrationError GCAdapter::SetVibration(
328 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { 328 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
329 const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f; 329 const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f;
330 const auto processed_amplitude = 330 const auto processed_amplitude =
@@ -338,6 +338,10 @@ Common::Input::VibrationError GCAdapter::SetRumble(
338 return Common::Input::VibrationError::None; 338 return Common::Input::VibrationError::None;
339} 339}
340 340
341bool GCAdapter::IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) {
342 return rumble_enabled;
343}
344
341void GCAdapter::UpdateVibrations() { 345void GCAdapter::UpdateVibrations() {
342 // Use 8 states to keep the switching between on/off fast enough for 346 // Use 8 states to keep the switching between on/off fast enough for
343 // a human to feel different vibration strenght 347 // a human to feel different vibration strenght
diff --git a/src/input_common/drivers/gc_adapter.h b/src/input_common/drivers/gc_adapter.h
index 8682da847..7f81767f7 100644
--- a/src/input_common/drivers/gc_adapter.h
+++ b/src/input_common/drivers/gc_adapter.h
@@ -25,9 +25,11 @@ public:
25 explicit GCAdapter(std::string input_engine_); 25 explicit GCAdapter(std::string input_engine_);
26 ~GCAdapter() override; 26 ~GCAdapter() override;
27 27
28 Common::Input::VibrationError SetRumble( 28 Common::Input::VibrationError SetVibration(
29 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; 29 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
30 30
31 bool IsVibrationEnabled(const PadIdentifier& identifier) override;
32
31 /// Used for automapping features 33 /// Used for automapping features
32 std::vector<Common::ParamPackage> GetInputDevices() const override; 34 std::vector<Common::ParamPackage> GetInputDevices() const override;
33 ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override; 35 ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp
index b72e4b397..45ce588f0 100644
--- a/src/input_common/drivers/sdl_driver.cpp
+++ b/src/input_common/drivers/sdl_driver.cpp
@@ -40,8 +40,8 @@ public:
40 void EnableMotion() { 40 void EnableMotion() {
41 if (sdl_controller) { 41 if (sdl_controller) {
42 SDL_GameController* controller = sdl_controller.get(); 42 SDL_GameController* controller = sdl_controller.get();
43 has_accel = SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL); 43 has_accel = SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) == SDL_TRUE;
44 has_gyro = SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO); 44 has_gyro = SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) == SDL_TRUE;
45 if (has_accel) { 45 if (has_accel) {
46 SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE); 46 SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE);
47 } 47 }
@@ -114,6 +114,20 @@ public:
114 } 114 }
115 return false; 115 return false;
116 } 116 }
117
118 void EnableVibration(bool is_enabled) {
119 has_vibration = is_enabled;
120 is_vibration_tested = true;
121 }
122
123 bool HasVibration() const {
124 return has_vibration;
125 }
126
127 bool IsVibrationTested() const {
128 return is_vibration_tested;
129 }
130
117 /** 131 /**
118 * The Pad identifier of the joystick 132 * The Pad identifier of the joystick
119 */ 133 */
@@ -236,6 +250,8 @@ private:
236 u64 last_motion_update{}; 250 u64 last_motion_update{};
237 bool has_gyro{false}; 251 bool has_gyro{false};
238 bool has_accel{false}; 252 bool has_accel{false};
253 bool has_vibration{false};
254 bool is_vibration_tested{false};
239 BasicMotion motion; 255 BasicMotion motion;
240}; 256};
241 257
@@ -517,7 +533,7 @@ std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const {
517 return devices; 533 return devices;
518} 534}
519 535
520Common::Input::VibrationError SDLDriver::SetRumble( 536Common::Input::VibrationError SDLDriver::SetVibration(
521 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { 537 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
522 const auto joystick = 538 const auto joystick =
523 GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port)); 539 GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port));
@@ -546,13 +562,6 @@ Common::Input::VibrationError SDLDriver::SetRumble(
546 .type = Common::Input::VibrationAmplificationType::Exponential, 562 .type = Common::Input::VibrationAmplificationType::Exponential,
547 }; 563 };
548 564
549 if (vibration.type == Common::Input::VibrationAmplificationType::Test) {
550 if (!joystick->RumblePlay(new_vibration)) {
551 return Common::Input::VibrationError::Unknown;
552 }
553 return Common::Input::VibrationError::None;
554 }
555
556 vibration_queue.Push(VibrationRequest{ 565 vibration_queue.Push(VibrationRequest{
557 .identifier = identifier, 566 .identifier = identifier,
558 .vibration = new_vibration, 567 .vibration = new_vibration,
@@ -561,6 +570,45 @@ Common::Input::VibrationError SDLDriver::SetRumble(
561 return Common::Input::VibrationError::None; 570 return Common::Input::VibrationError::None;
562} 571}
563 572
573bool SDLDriver::IsVibrationEnabled(const PadIdentifier& identifier) {
574 const auto joystick =
575 GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port));
576
577 constexpr Common::Input::VibrationStatus test_vibration{
578 .low_amplitude = 1,
579 .low_frequency = 160.0f,
580 .high_amplitude = 1,
581 .high_frequency = 320.0f,
582 .type = Common::Input::VibrationAmplificationType::Exponential,
583 };
584
585 constexpr Common::Input::VibrationStatus zero_vibration{
586 .low_amplitude = 0,
587 .low_frequency = 160.0f,
588 .high_amplitude = 0,
589 .high_frequency = 320.0f,
590 .type = Common::Input::VibrationAmplificationType::Exponential,
591 };
592
593 if (joystick->IsVibrationTested()) {
594 return joystick->HasVibration();
595 }
596
597 // First vibration might fail
598 joystick->RumblePlay(test_vibration);
599
600 // Wait for about 15ms to ensure the controller is ready for the stop command
601 std::this_thread::sleep_for(std::chrono::milliseconds(15));
602
603 if (!joystick->RumblePlay(zero_vibration)) {
604 joystick->EnableVibration(false);
605 return false;
606 }
607
608 joystick->EnableVibration(true);
609 return true;
610}
611
564void SDLDriver::SendVibrations() { 612void SDLDriver::SendVibrations() {
565 while (!vibration_queue.Empty()) { 613 while (!vibration_queue.Empty()) {
566 VibrationRequest request; 614 VibrationRequest request;
diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h
index fc3a44572..d1b4471cf 100644
--- a/src/input_common/drivers/sdl_driver.h
+++ b/src/input_common/drivers/sdl_driver.h
@@ -61,9 +61,11 @@ public:
61 61
62 bool IsStickInverted(const Common::ParamPackage& params) override; 62 bool IsStickInverted(const Common::ParamPackage& params) override;
63 63
64 Common::Input::VibrationError SetRumble( 64 Common::Input::VibrationError SetVibration(
65 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; 65 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
66 66
67 bool IsVibrationEnabled(const PadIdentifier& identifier) override;
68
67private: 69private:
68 struct VibrationRequest { 70 struct VibrationRequest {
69 PadIdentifier identifier; 71 PadIdentifier identifier;
diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h
index cfbdb26bd..d4c264a8e 100644
--- a/src/input_common/input_engine.h
+++ b/src/input_common/input_engine.h
@@ -108,12 +108,17 @@ public:
108 [[maybe_unused]] const Common::Input::LedStatus& led_status) {} 108 [[maybe_unused]] const Common::Input::LedStatus& led_status) {}
109 109
110 // Sets rumble to a controller 110 // Sets rumble to a controller
111 virtual Common::Input::VibrationError SetRumble( 111 virtual Common::Input::VibrationError SetVibration(
112 [[maybe_unused]] const PadIdentifier& identifier, 112 [[maybe_unused]] const PadIdentifier& identifier,
113 [[maybe_unused]] const Common::Input::VibrationStatus& vibration) { 113 [[maybe_unused]] const Common::Input::VibrationStatus& vibration) {
114 return Common::Input::VibrationError::NotSupported; 114 return Common::Input::VibrationError::NotSupported;
115 } 115 }
116 116
117 // Returns true if device supports vibrations
118 virtual bool IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) {
119 return false;
120 }
121
117 // Sets polling mode to a controller 122 // Sets polling mode to a controller
118 virtual Common::Input::PollingError SetPollingMode( 123 virtual Common::Input::PollingError SetPollingMode(
119 [[maybe_unused]] const PadIdentifier& identifier, 124 [[maybe_unused]] const PadIdentifier& identifier,
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp
index ca33fb4eb..4ac182147 100644
--- a/src/input_common/input_poller.cpp
+++ b/src/input_common/input_poller.cpp
@@ -763,7 +763,11 @@ public:
763 763
764 Common::Input::VibrationError SetVibration( 764 Common::Input::VibrationError SetVibration(
765 const Common::Input::VibrationStatus& vibration_status) override { 765 const Common::Input::VibrationStatus& vibration_status) override {
766 return input_engine->SetRumble(identifier, vibration_status); 766 return input_engine->SetVibration(identifier, vibration_status);
767 }
768
769 bool IsVibrationEnabled() override {
770 return input_engine->IsVibrationEnabled(identifier);
767 } 771 }
768 772
769 Common::Input::PollingError SetPollingMode(Common::Input::PollingMode polling_mode) override { 773 Common::Input::PollingError SetPollingMode(Common::Input::PollingMode polling_mode) override {
@@ -797,8 +801,8 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateButtonDevice(
797 801
798 const auto button_id = params.Get("button", 0); 802 const auto button_id = params.Get("button", 0);
799 const auto keyboard_key = params.Get("code", 0); 803 const auto keyboard_key = params.Get("code", 0);
800 const auto toggle = params.Get("toggle", false); 804 const auto toggle = params.Get("toggle", false) != 0;
801 const auto inverted = params.Get("inverted", false); 805 const auto inverted = params.Get("inverted", false) != 0;
802 input_engine->PreSetController(identifier); 806 input_engine->PreSetController(identifier);
803 input_engine->PreSetButton(identifier, button_id); 807 input_engine->PreSetButton(identifier, button_id);
804 input_engine->PreSetButton(identifier, keyboard_key); 808 input_engine->PreSetButton(identifier, keyboard_key);
@@ -820,8 +824,8 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateHatButtonDevice(
820 824
821 const auto button_id = params.Get("hat", 0); 825 const auto button_id = params.Get("hat", 0);
822 const auto direction = input_engine->GetHatButtonId(params.Get("direction", "")); 826 const auto direction = input_engine->GetHatButtonId(params.Get("direction", ""));
823 const auto toggle = params.Get("toggle", false); 827 const auto toggle = params.Get("toggle", false) != 0;
824 const auto inverted = params.Get("inverted", false); 828 const auto inverted = params.Get("inverted", false) != 0;
825 829
826 input_engine->PreSetController(identifier); 830 input_engine->PreSetController(identifier);
827 input_engine->PreSetHatButton(identifier, button_id); 831 input_engine->PreSetHatButton(identifier, button_id);
@@ -879,7 +883,7 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateAnalogDevice(
879 .threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f), 883 .threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f),
880 .offset = std::clamp(params.Get("offset", 0.0f), -1.0f, 1.0f), 884 .offset = std::clamp(params.Get("offset", 0.0f), -1.0f, 1.0f),
881 .inverted = params.Get("invert", "+") == "-", 885 .inverted = params.Get("invert", "+") == "-",
882 .toggle = static_cast<bool>(params.Get("toggle", false)), 886 .toggle = params.Get("toggle", false) != 0,
883 }; 887 };
884 input_engine->PreSetController(identifier); 888 input_engine->PreSetController(identifier);
885 input_engine->PreSetAxis(identifier, axis); 889 input_engine->PreSetAxis(identifier, axis);
@@ -895,8 +899,8 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTriggerDevice(
895 }; 899 };
896 900
897 const auto button = params.Get("button", 0); 901 const auto button = params.Get("button", 0);
898 const auto toggle = params.Get("toggle", false); 902 const auto toggle = params.Get("toggle", false) != 0;
899 const auto inverted = params.Get("inverted", false); 903 const auto inverted = params.Get("inverted", false) != 0;
900 904
901 const auto axis = params.Get("axis", 0); 905 const auto axis = params.Get("axis", 0);
902 const Common::Input::AnalogProperties properties = { 906 const Common::Input::AnalogProperties properties = {
@@ -926,8 +930,8 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTouchDevice(
926 }; 930 };
927 931
928 const auto button = params.Get("button", 0); 932 const auto button = params.Get("button", 0);
929 const auto toggle = params.Get("toggle", false); 933 const auto toggle = params.Get("toggle", false) != 0;
930 const auto inverted = params.Get("inverted", false); 934 const auto inverted = params.Get("inverted", false) != 0;
931 935
932 const auto axis_x = params.Get("axis_x", 0); 936 const auto axis_x = params.Get("axis_x", 0);
933 const Common::Input::AnalogProperties properties_x = { 937 const Common::Input::AnalogProperties properties_x = {
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt
index af8e51fe8..bcdd60db9 100644
--- a/src/shader_recompiler/CMakeLists.txt
+++ b/src/shader_recompiler/CMakeLists.txt
@@ -241,24 +241,14 @@ target_link_libraries(shader_recompiler PUBLIC common fmt::fmt sirit)
241if (MSVC) 241if (MSVC)
242 target_compile_options(shader_recompiler PRIVATE 242 target_compile_options(shader_recompiler PRIVATE
243 /W4 243 /W4
244 /WX 244
245 /we4018 # 'expression' : signed/unsigned mismatch 245 /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data
246 /we4244 # 'argument' : conversion from 'type1' to 'type2', possible loss of data (floating-point)
247 /we4245 # 'conversion' : conversion from 'type1' to 'type2', signed/unsigned mismatch
248 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data 246 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
249 /we4267 # 'var' : conversion from 'size_t' to 'type', possible loss of data
250 /we4305 # 'context' : truncation from 'type1' to 'type2'
251 /we4800 # Implicit conversion from 'type' to bool. Possible information loss 247 /we4800 # Implicit conversion from 'type' to bool. Possible information loss
252 /we4826 # Conversion from 'type1' to 'type2' is sign-extended. This may cause unexpected runtime behavior.
253 ) 248 )
254else() 249else()
255 target_compile_options(shader_recompiler PRIVATE 250 target_compile_options(shader_recompiler PRIVATE
256 -Werror
257 -Werror=conversion 251 -Werror=conversion
258 -Werror=ignored-qualifiers
259 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
260 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
261 -Werror=unused-variable
262 252
263 # Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6. 253 # Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6.
264 # And this in turns limits the size of a std::array. 254 # And this in turns limits the size of a std::array.
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp
index 7094d8e42..1f4ffdd62 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp
@@ -5,10 +5,6 @@
5#include "shader_recompiler/backend/glasm/glasm_emit_context.h" 5#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
6#include "shader_recompiler/frontend/ir/value.h" 6#include "shader_recompiler/frontend/ir/value.h"
7 7
8#ifdef _MSC_VER
9#pragma warning(disable : 4100)
10#endif
11
12namespace Shader::Backend::GLASM { 8namespace Shader::Backend::GLASM {
13 9
14#define NotImplemented() throw NotImplementedException("GLASM instruction {}", __LINE__) 10#define NotImplemented() throw NotImplementedException("GLASM instruction {}", __LINE__)
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp
index b03a8ba1e..9f1ed95a4 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp
@@ -7,10 +7,6 @@
7#include "shader_recompiler/backend/glsl/glsl_emit_context.h" 7#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
8#include "shader_recompiler/frontend/ir/value.h" 8#include "shader_recompiler/frontend/ir/value.h"
9 9
10#ifdef _MSC_VER
11#pragma warning(disable : 4100)
12#endif
13
14namespace Shader::Backend::GLSL { 10namespace Shader::Backend::GLSL {
15 11
16void EmitGetRegister(EmitContext& ctx) { 12void EmitGetRegister(EmitContext& ctx) {
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
index 77efb4f57..b58741d4d 100644
--- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
@@ -137,28 +137,35 @@ bool IsLegacyAttribute(IR::Attribute attribute) {
137} 137}
138 138
139std::map<IR::Attribute, IR::Attribute> GenerateLegacyToGenericMappings( 139std::map<IR::Attribute, IR::Attribute> GenerateLegacyToGenericMappings(
140 const VaryingState& state, std::queue<IR::Attribute> ununsed_generics) { 140 const VaryingState& state, std::queue<IR::Attribute> unused_generics,
141 const std::map<IR::Attribute, IR::Attribute>& previous_stage_mapping) {
141 std::map<IR::Attribute, IR::Attribute> mapping; 142 std::map<IR::Attribute, IR::Attribute> mapping;
143 auto update_mapping = [&mapping, &unused_generics, previous_stage_mapping](IR::Attribute attr,
144 size_t count) {
145 if (previous_stage_mapping.find(attr) != previous_stage_mapping.end()) {
146 for (size_t i = 0; i < count; ++i) {
147 mapping.insert({attr + i, previous_stage_mapping.at(attr + i)});
148 }
149 } else {
150 for (size_t i = 0; i < count; ++i) {
151 mapping.insert({attr + i, unused_generics.front() + i});
152 }
153 unused_generics.pop();
154 }
155 };
142 for (size_t index = 0; index < 4; ++index) { 156 for (size_t index = 0; index < 4; ++index) {
143 auto attr = IR::Attribute::ColorFrontDiffuseR + index * 4; 157 auto attr = IR::Attribute::ColorFrontDiffuseR + index * 4;
144 if (state.AnyComponent(attr)) { 158 if (state.AnyComponent(attr)) {
145 for (size_t i = 0; i < 4; ++i) { 159 update_mapping(attr, 4);
146 mapping.insert({attr + i, ununsed_generics.front() + i});
147 }
148 ununsed_generics.pop();
149 } 160 }
150 } 161 }
151 if (state[IR::Attribute::FogCoordinate]) { 162 if (state[IR::Attribute::FogCoordinate]) {
152 mapping.insert({IR::Attribute::FogCoordinate, ununsed_generics.front()}); 163 update_mapping(IR::Attribute::FogCoordinate, 1);
153 ununsed_generics.pop();
154 } 164 }
155 for (size_t index = 0; index < IR::NUM_FIXEDFNCTEXTURE; ++index) { 165 for (size_t index = 0; index < IR::NUM_FIXEDFNCTEXTURE; ++index) {
156 auto attr = IR::Attribute::FixedFncTexture0S + index * 4; 166 auto attr = IR::Attribute::FixedFncTexture0S + index * 4;
157 if (state.AnyComponent(attr)) { 167 if (state.AnyComponent(attr)) {
158 for (size_t i = 0; i < 4; ++i) { 168 update_mapping(attr, 4);
159 mapping.insert({attr + i, ununsed_generics.front() + i});
160 }
161 ununsed_generics.pop();
162 } 169 }
163 } 170 }
164 return mapping; 171 return mapping;
@@ -265,21 +272,22 @@ IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b
265void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& runtime_info) { 272void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& runtime_info) {
266 auto& stores = program.info.stores; 273 auto& stores = program.info.stores;
267 if (stores.Legacy()) { 274 if (stores.Legacy()) {
268 std::queue<IR::Attribute> ununsed_output_generics{}; 275 std::queue<IR::Attribute> unused_output_generics{};
269 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) { 276 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
270 if (!stores.Generic(index)) { 277 if (!stores.Generic(index)) {
271 ununsed_output_generics.push(IR::Attribute::Generic0X + index * 4); 278 unused_output_generics.push(IR::Attribute::Generic0X + index * 4);
272 } 279 }
273 } 280 }
274 auto mappings = GenerateLegacyToGenericMappings(stores, ununsed_output_generics); 281 program.info.legacy_stores_mapping =
282 GenerateLegacyToGenericMappings(stores, unused_output_generics, {});
275 for (IR::Block* const block : program.post_order_blocks) { 283 for (IR::Block* const block : program.post_order_blocks) {
276 for (IR::Inst& inst : block->Instructions()) { 284 for (IR::Inst& inst : block->Instructions()) {
277 switch (inst.GetOpcode()) { 285 switch (inst.GetOpcode()) {
278 case IR::Opcode::SetAttribute: { 286 case IR::Opcode::SetAttribute: {
279 const auto attr = inst.Arg(0).Attribute(); 287 const auto attr = inst.Arg(0).Attribute();
280 if (IsLegacyAttribute(attr)) { 288 if (IsLegacyAttribute(attr)) {
281 stores.Set(mappings[attr], true); 289 stores.Set(program.info.legacy_stores_mapping[attr], true);
282 inst.SetArg(0, Shader::IR::Value(mappings[attr])); 290 inst.SetArg(0, Shader::IR::Value(program.info.legacy_stores_mapping[attr]));
283 } 291 }
284 break; 292 break;
285 } 293 }
@@ -292,15 +300,16 @@ void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& run
292 300
293 auto& loads = program.info.loads; 301 auto& loads = program.info.loads;
294 if (loads.Legacy()) { 302 if (loads.Legacy()) {
295 std::queue<IR::Attribute> ununsed_input_generics{}; 303 std::queue<IR::Attribute> unused_input_generics{};
296 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) { 304 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
297 const AttributeType input_type{runtime_info.generic_input_types[index]}; 305 const AttributeType input_type{runtime_info.generic_input_types[index]};
298 if (!runtime_info.previous_stage_stores.Generic(index) || !loads.Generic(index) || 306 if (!runtime_info.previous_stage_stores.Generic(index) || !loads.Generic(index) ||
299 input_type == AttributeType::Disabled) { 307 input_type == AttributeType::Disabled) {
300 ununsed_input_generics.push(IR::Attribute::Generic0X + index * 4); 308 unused_input_generics.push(IR::Attribute::Generic0X + index * 4);
301 } 309 }
302 } 310 }
303 auto mappings = GenerateLegacyToGenericMappings(loads, ununsed_input_generics); 311 auto mappings = GenerateLegacyToGenericMappings(
312 loads, unused_input_generics, runtime_info.previous_stage_legacy_stores_mapping);
304 for (IR::Block* const block : program.post_order_blocks) { 313 for (IR::Block* const block : program.post_order_blocks) {
305 for (IR::Inst& inst : block->Instructions()) { 314 for (IR::Inst& inst : block->Instructions()) {
306 switch (inst.GetOpcode()) { 315 switch (inst.GetOpcode()) {
diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h
index dcb5ab158..549b81ef7 100644
--- a/src/shader_recompiler/runtime_info.h
+++ b/src/shader_recompiler/runtime_info.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include <array> 6#include <array>
7#include <map>
7#include <optional> 8#include <optional>
8#include <vector> 9#include <vector>
9 10
@@ -60,6 +61,7 @@ struct TransformFeedbackVarying {
60struct RuntimeInfo { 61struct RuntimeInfo {
61 std::array<AttributeType, 32> generic_input_types{}; 62 std::array<AttributeType, 32> generic_input_types{};
62 VaryingState previous_stage_stores; 63 VaryingState previous_stage_stores;
64 std::map<IR::Attribute, IR::Attribute> previous_stage_legacy_stores_mapping;
63 65
64 bool convert_depth_mode{}; 66 bool convert_depth_mode{};
65 bool force_early_z{}; 67 bool force_early_z{};
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h
index cc596da4f..81097bf1a 100644
--- a/src/shader_recompiler/shader_info.h
+++ b/src/shader_recompiler/shader_info.h
@@ -5,6 +5,7 @@
5 5
6#include <array> 6#include <array>
7#include <bitset> 7#include <bitset>
8#include <map>
8 9
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "shader_recompiler/frontend/ir/type.h" 11#include "shader_recompiler/frontend/ir/type.h"
@@ -127,6 +128,8 @@ struct Info {
127 VaryingState stores; 128 VaryingState stores;
128 VaryingState passthrough; 129 VaryingState passthrough;
129 130
131 std::map<IR::Attribute, IR::Attribute> legacy_stores_mapping;
132
130 bool loads_indexed_attributes{}; 133 bool loads_indexed_attributes{};
131 134
132 std::array<bool, 8> stores_frag_color{}; 135 std::array<bool, 8> stores_frag_color{};
diff --git a/src/tests/video_core/buffer_base.cpp b/src/tests/video_core/buffer_base.cpp
index 71121e42a..f7236afab 100644
--- a/src/tests/video_core/buffer_base.cpp
+++ b/src/tests/video_core/buffer_base.cpp
@@ -44,7 +44,7 @@ public:
44 44
45 [[nodiscard]] unsigned Count() const noexcept { 45 [[nodiscard]] unsigned Count() const noexcept {
46 unsigned count = 0; 46 unsigned count = 0;
47 for (const auto [index, value] : page_table) { 47 for (const auto& [index, value] : page_table) {
48 count += value; 48 count += value;
49 } 49 }
50 return count; 50 return count;
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index cb8b46edf..106991969 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -279,14 +279,8 @@ if (MSVC)
279else() 279else()
280 target_compile_options(video_core PRIVATE 280 target_compile_options(video_core PRIVATE
281 -Werror=conversion 281 -Werror=conversion
282 -Wno-error=sign-conversion
283 -Werror=pessimizing-move
284 -Werror=redundant-move
285 -Werror=type-limits
286 282
287 $<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess> 283 -Wno-sign-conversion
288 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
289 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
290 ) 284 )
291endif() 285endif()
292 286
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index b1a22b76c..5208bea75 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -117,10 +117,18 @@ void Maxwell3D::InitializeRegisterDefaults() {
117 117
118 shadow_state = regs; 118 shadow_state = regs;
119 119
120 mme_inline[MAXWELL3D_REG_INDEX(draw.end)] = true; 120 draw_command[MAXWELL3D_REG_INDEX(draw.end)] = true;
121 mme_inline[MAXWELL3D_REG_INDEX(draw.begin)] = true; 121 draw_command[MAXWELL3D_REG_INDEX(draw.begin)] = true;
122 mme_inline[MAXWELL3D_REG_INDEX(vertex_buffer.count)] = true; 122 draw_command[MAXWELL3D_REG_INDEX(vertex_buffer.first)] = true;
123 mme_inline[MAXWELL3D_REG_INDEX(index_buffer.count)] = true; 123 draw_command[MAXWELL3D_REG_INDEX(vertex_buffer.count)] = true;
124 draw_command[MAXWELL3D_REG_INDEX(index_buffer.first)] = true;
125 draw_command[MAXWELL3D_REG_INDEX(index_buffer.count)] = true;
126 draw_command[MAXWELL3D_REG_INDEX(index_buffer32_first)] = true;
127 draw_command[MAXWELL3D_REG_INDEX(index_buffer16_first)] = true;
128 draw_command[MAXWELL3D_REG_INDEX(index_buffer8_first)] = true;
129 draw_command[MAXWELL3D_REG_INDEX(draw_inline_index)] = true;
130 draw_command[MAXWELL3D_REG_INDEX(inline_index_2x16.even)] = true;
131 draw_command[MAXWELL3D_REG_INDEX(inline_index_4x8.index0)] = true;
124} 132}
125 133
126void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) { 134void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) {
@@ -208,25 +216,6 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
208 return ProcessCBBind(3); 216 return ProcessCBBind(3);
209 case MAXWELL3D_REG_INDEX(bind_groups[4].raw_config): 217 case MAXWELL3D_REG_INDEX(bind_groups[4].raw_config):
210 return ProcessCBBind(4); 218 return ProcessCBBind(4);
211 case MAXWELL3D_REG_INDEX(draw.end):
212 return DrawArrays();
213 case MAXWELL3D_REG_INDEX(index_buffer32_first):
214 regs.index_buffer.count = regs.index_buffer32_first.count;
215 regs.index_buffer.first = regs.index_buffer32_first.first;
216 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
217 return DrawArrays();
218 case MAXWELL3D_REG_INDEX(index_buffer16_first):
219 regs.index_buffer.count = regs.index_buffer16_first.count;
220 regs.index_buffer.first = regs.index_buffer16_first.first;
221 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
222 return DrawArrays();
223 case MAXWELL3D_REG_INDEX(index_buffer8_first):
224 regs.index_buffer.count = regs.index_buffer8_first.count;
225 regs.index_buffer.first = regs.index_buffer8_first.first;
226 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
227 // a macro calls this one over and over, should it increase instancing?
228 // Used by Hades and likely other Vulkan games.
229 return DrawArrays();
230 case MAXWELL3D_REG_INDEX(topology_override): 219 case MAXWELL3D_REG_INDEX(topology_override):
231 use_topology_override = true; 220 use_topology_override = true;
232 return; 221 return;
@@ -261,14 +250,13 @@ void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters)
261 250
262 // Execute the current macro. 251 // Execute the current macro.
263 macro_engine->Execute(macro_positions[entry], parameters); 252 macro_engine->Execute(macro_positions[entry], parameters);
264 if (mme_draw.current_mode != MMEDrawMode::Undefined) { 253
265 FlushMMEInlineDraw(); 254 ProcessDeferredDraw();
266 }
267} 255}
268 256
269void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { 257void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
270 // It is an error to write to a register other than the current macro's ARG register before it 258 // It is an error to write to a register other than the current macro's ARG register before
271 // has finished execution. 259 // it has finished execution.
272 if (executing_macro != 0) { 260 if (executing_macro != 0) {
273 ASSERT(method == executing_macro + 1); 261 ASSERT(method == executing_macro + 1);
274 } 262 }
@@ -283,9 +271,33 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
283 ASSERT_MSG(method < Regs::NUM_REGS, 271 ASSERT_MSG(method < Regs::NUM_REGS,
284 "Invalid Maxwell3D register, increase the size of the Regs structure"); 272 "Invalid Maxwell3D register, increase the size of the Regs structure");
285 273
286 const u32 argument = ProcessShadowRam(method, method_argument); 274 if (draw_command[method]) {
287 ProcessDirtyRegisters(method, argument); 275 regs.reg_array[method] = method_argument;
288 ProcessMethodCall(method, argument, method_argument, is_last_call); 276 deferred_draw_method.push_back(method);
277 auto u32_to_u8 = [&](const u32 argument) {
278 inline_index_draw_indexes.push_back(static_cast<u8>(argument & 0x000000ff));
279 inline_index_draw_indexes.push_back(static_cast<u8>((argument & 0x0000ff00) >> 8));
280 inline_index_draw_indexes.push_back(static_cast<u8>((argument & 0x00ff0000) >> 16));
281 inline_index_draw_indexes.push_back(static_cast<u8>((argument & 0xff000000) >> 24));
282 };
283 if (MAXWELL3D_REG_INDEX(draw_inline_index) == method) {
284 u32_to_u8(method_argument);
285 } else if (MAXWELL3D_REG_INDEX(inline_index_2x16.even) == method) {
286 u32_to_u8(regs.inline_index_2x16.even);
287 u32_to_u8(regs.inline_index_2x16.odd);
288 } else if (MAXWELL3D_REG_INDEX(inline_index_4x8.index0) == method) {
289 u32_to_u8(regs.inline_index_4x8.index0);
290 u32_to_u8(regs.inline_index_4x8.index1);
291 u32_to_u8(regs.inline_index_4x8.index2);
292 u32_to_u8(regs.inline_index_4x8.index3);
293 }
294 } else {
295 ProcessDeferredDraw();
296
297 const u32 argument = ProcessShadowRam(method, method_argument);
298 ProcessDirtyRegisters(method, argument);
299 ProcessMethodCall(method, argument, method_argument, is_last_call);
300 }
289} 301}
290 302
291void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, 303void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
@@ -326,55 +338,6 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
326 } 338 }
327} 339}
328 340
329void Maxwell3D::StepInstance(const MMEDrawMode expected_mode, const u32 count) {
330 if (mme_draw.current_mode == MMEDrawMode::Undefined) {
331 if (mme_draw.gl_begin_consume) {
332 mme_draw.current_mode = expected_mode;
333 mme_draw.current_count = count;
334 mme_draw.instance_count = 1;
335 mme_draw.gl_begin_consume = false;
336 mme_draw.gl_end_count = 0;
337 }
338 return;
339 } else {
340 if (mme_draw.current_mode == expected_mode && count == mme_draw.current_count &&
341 mme_draw.instance_mode && mme_draw.gl_begin_consume) {
342 mme_draw.instance_count++;
343 mme_draw.gl_begin_consume = false;
344 return;
345 } else {
346 FlushMMEInlineDraw();
347 }
348 }
349 // Tail call in case it needs to retry.
350 StepInstance(expected_mode, count);
351}
352
353void Maxwell3D::CallMethodFromMME(u32 method, u32 method_argument) {
354 if (mme_inline[method]) {
355 regs.reg_array[method] = method_argument;
356 if (method == MAXWELL3D_REG_INDEX(vertex_buffer.count) ||
357 method == MAXWELL3D_REG_INDEX(index_buffer.count)) {
358 const MMEDrawMode expected_mode = method == MAXWELL3D_REG_INDEX(vertex_buffer.count)
359 ? MMEDrawMode::Array
360 : MMEDrawMode::Indexed;
361 StepInstance(expected_mode, method_argument);
362 } else if (method == MAXWELL3D_REG_INDEX(draw.begin)) {
363 mme_draw.instance_mode =
364 (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) ||
365 (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Unchanged);
366 mme_draw.gl_begin_consume = true;
367 } else {
368 mme_draw.gl_end_count++;
369 }
370 } else {
371 if (mme_draw.current_mode != MMEDrawMode::Undefined) {
372 FlushMMEInlineDraw();
373 }
374 CallMethod(method, method_argument, true);
375 }
376}
377
378void Maxwell3D::ProcessTopologyOverride() { 341void Maxwell3D::ProcessTopologyOverride() {
379 using PrimitiveTopology = Maxwell3D::Regs::PrimitiveTopology; 342 using PrimitiveTopology = Maxwell3D::Regs::PrimitiveTopology;
380 using PrimitiveTopologyOverride = Maxwell3D::Regs::PrimitiveTopologyOverride; 343 using PrimitiveTopologyOverride = Maxwell3D::Regs::PrimitiveTopologyOverride;
@@ -404,41 +367,6 @@ void Maxwell3D::ProcessTopologyOverride() {
404 } 367 }
405} 368}
406 369
407void Maxwell3D::FlushMMEInlineDraw() {
408 LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(),
409 regs.vertex_buffer.count);
410 ASSERT_MSG(!(regs.index_buffer.count && regs.vertex_buffer.count), "Both indexed and direct?");
411 ASSERT(mme_draw.instance_count == mme_draw.gl_end_count);
412
413 // Both instance configuration registers can not be set at the same time.
414 ASSERT_MSG(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::First ||
415 regs.draw.instance_id != Maxwell3D::Regs::Draw::InstanceId::Unchanged,
416 "Illegal combination of instancing parameters");
417
418 ProcessTopologyOverride();
419
420 const bool is_indexed = mme_draw.current_mode == MMEDrawMode::Indexed;
421 if (ShouldExecute()) {
422 rasterizer->Draw(is_indexed, true);
423 }
424
425 // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
426 // the game is trying to draw indexed or direct mode. This needs to be verified on HW still -
427 // it's possible that it is incorrect and that there is some other register used to specify the
428 // drawing mode.
429 if (is_indexed) {
430 regs.index_buffer.count = 0;
431 } else {
432 regs.vertex_buffer.count = 0;
433 }
434 mme_draw.current_mode = MMEDrawMode::Undefined;
435 mme_draw.current_count = 0;
436 mme_draw.instance_count = 0;
437 mme_draw.instance_mode = false;
438 mme_draw.gl_begin_consume = false;
439 mme_draw.gl_end_count = 0;
440}
441
442void Maxwell3D::ProcessMacroUpload(u32 data) { 370void Maxwell3D::ProcessMacroUpload(u32 data) {
443 macro_engine->AddCode(regs.load_mme.instruction_ptr++, data); 371 macro_engine->AddCode(regs.load_mme.instruction_ptr++, data);
444} 372}
@@ -573,42 +501,6 @@ void Maxwell3D::ProcessSyncPoint() {
573 rasterizer->SignalSyncPoint(sync_point); 501 rasterizer->SignalSyncPoint(sync_point);
574} 502}
575 503
576void Maxwell3D::DrawArrays() {
577 LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(),
578 regs.vertex_buffer.count);
579 ASSERT_MSG(!(regs.index_buffer.count && regs.vertex_buffer.count), "Both indexed and direct?");
580
581 // Both instance configuration registers can not be set at the same time.
582 ASSERT_MSG(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::First ||
583 regs.draw.instance_id != Maxwell3D::Regs::Draw::InstanceId::Unchanged,
584 "Illegal combination of instancing parameters");
585
586 ProcessTopologyOverride();
587
588 if (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) {
589 // Increment the current instance *before* drawing.
590 state.current_instance++;
591 } else if (regs.draw.instance_id != Maxwell3D::Regs::Draw::InstanceId::Unchanged) {
592 // Reset the current instance to 0.
593 state.current_instance = 0;
594 }
595
596 const bool is_indexed{regs.index_buffer.count && !regs.vertex_buffer.count};
597 if (ShouldExecute()) {
598 rasterizer->Draw(is_indexed, false);
599 }
600
601 // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
602 // the game is trying to draw indexed or direct mode. This needs to be verified on HW still -
603 // it's possible that it is incorrect and that there is some other register used to specify the
604 // drawing mode.
605 if (is_indexed) {
606 regs.index_buffer.count = 0;
607 } else {
608 regs.vertex_buffer.count = 0;
609 }
610}
611
612std::optional<u64> Maxwell3D::GetQueryResult() { 504std::optional<u64> Maxwell3D::GetQueryResult() {
613 switch (regs.report_semaphore.query.report) { 505 switch (regs.report_semaphore.query.report) {
614 case Regs::ReportSemaphore::Report::Payload: 506 case Regs::ReportSemaphore::Report::Payload:
@@ -691,4 +583,95 @@ void Maxwell3D::ProcessClearBuffers() {
691 rasterizer->Clear(); 583 rasterizer->Clear();
692} 584}
693 585
586void Maxwell3D::ProcessDeferredDraw() {
587 if (deferred_draw_method.empty()) {
588 return;
589 }
590
591 enum class DrawMode {
592 Undefined,
593 General,
594 Instance,
595 };
596 DrawMode draw_mode{DrawMode::Undefined};
597 u32 instance_count = 1;
598
599 auto first_method = deferred_draw_method[0];
600 if (MAXWELL3D_REG_INDEX(draw.begin) == first_method) {
601 // The minimum number of methods for drawing must be greater than or equal to
602 // 3[draw.begin->vertex(index)count->draw.end] to avoid errors in index mode drawing
603 if (deferred_draw_method.size() < 3) {
604 return;
605 }
606 draw_mode = (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) ||
607 (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Unchanged)
608 ? DrawMode::Instance
609 : DrawMode::General;
610 } else if (MAXWELL3D_REG_INDEX(index_buffer32_first) == first_method ||
611 MAXWELL3D_REG_INDEX(index_buffer16_first) == first_method ||
612 MAXWELL3D_REG_INDEX(index_buffer8_first) == first_method) {
613 draw_mode = DrawMode::General;
614 }
615
616 // Drawing will only begin with draw.begin or index_buffer method, other methods directly
617 // clear
618 if (draw_mode == DrawMode::Undefined) {
619 deferred_draw_method.clear();
620 return;
621 }
622
623 if (draw_mode == DrawMode::Instance) {
624 ASSERT_MSG(deferred_draw_method.size() % 4 == 0, "Instance mode method size error");
625 instance_count = static_cast<u32>(deferred_draw_method.size()) / 4;
626 } else {
627 if (MAXWELL3D_REG_INDEX(index_buffer32_first) == first_method) {
628 regs.index_buffer.count = regs.index_buffer32_first.count;
629 regs.index_buffer.first = regs.index_buffer32_first.first;
630 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
631 } else if (MAXWELL3D_REG_INDEX(index_buffer32_first) == first_method) {
632 regs.index_buffer.count = regs.index_buffer16_first.count;
633 regs.index_buffer.first = regs.index_buffer16_first.first;
634 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
635 } else if (MAXWELL3D_REG_INDEX(index_buffer32_first) == first_method) {
636 regs.index_buffer.count = regs.index_buffer8_first.count;
637 regs.index_buffer.first = regs.index_buffer8_first.first;
638 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
639 } else {
640 auto second_method = deferred_draw_method[1];
641 if (MAXWELL3D_REG_INDEX(draw_inline_index) == second_method ||
642 MAXWELL3D_REG_INDEX(inline_index_2x16.even) == second_method ||
643 MAXWELL3D_REG_INDEX(inline_index_4x8.index0) == second_method) {
644 regs.index_buffer.count = static_cast<u32>(inline_index_draw_indexes.size() / 4);
645 regs.index_buffer.format = Regs::IndexFormat::UnsignedInt;
646 }
647 }
648 }
649
650 LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(),
651 regs.vertex_buffer.count);
652
653 ASSERT_MSG(!(regs.index_buffer.count && regs.vertex_buffer.count), "Both indexed and direct?");
654
655 // Both instance configuration registers can not be set at the same time.
656 ASSERT_MSG(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::First ||
657 regs.draw.instance_id != Maxwell3D::Regs::Draw::InstanceId::Unchanged,
658 "Illegal combination of instancing parameters");
659
660 ProcessTopologyOverride();
661
662 const bool is_indexed = regs.index_buffer.count && !regs.vertex_buffer.count;
663 if (ShouldExecute()) {
664 rasterizer->Draw(is_indexed, instance_count);
665 }
666
667 if (is_indexed) {
668 regs.index_buffer.count = 0;
669 } else {
670 regs.vertex_buffer.count = 0;
671 }
672
673 deferred_draw_method.clear();
674 inline_index_draw_indexes.clear();
675}
676
694} // namespace Tegra::Engines 677} // namespace Tegra::Engines
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 75e3b868d..bd23ebc12 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -1739,14 +1739,11 @@ public:
1739 Footprint_1x1_Virtual = 2, 1739 Footprint_1x1_Virtual = 2,
1740 }; 1740 };
1741 1741
1742 struct InlineIndex4x8Align { 1742 struct InlineIndex4x8 {
1743 union { 1743 union {
1744 BitField<0, 30, u32> count; 1744 BitField<0, 30, u32> count;
1745 BitField<30, 2, u32> start; 1745 BitField<30, 2, u32> start;
1746 }; 1746 };
1747 };
1748
1749 struct InlineIndex4x8Index {
1750 union { 1747 union {
1751 BitField<0, 8, u32> index0; 1748 BitField<0, 8, u32> index0;
1752 BitField<8, 8, u32> index1; 1749 BitField<8, 8, u32> index1;
@@ -2836,8 +2833,7 @@ public:
2836 u32 depth_write_enabled; ///< 0x12E8 2833 u32 depth_write_enabled; ///< 0x12E8
2837 u32 alpha_test_enabled; ///< 0x12EC 2834 u32 alpha_test_enabled; ///< 0x12EC
2838 INSERT_PADDING_BYTES_NOINIT(0x10); 2835 INSERT_PADDING_BYTES_NOINIT(0x10);
2839 InlineIndex4x8Align inline_index_4x8_align; ///< 0x1300 2836 InlineIndex4x8 inline_index_4x8; ///< 0x1300
2840 InlineIndex4x8Index inline_index_4x8_index; ///< 0x1304
2841 D3DCullMode d3d_cull_mode; ///< 0x1308 2837 D3DCullMode d3d_cull_mode; ///< 0x1308
2842 ComparisonOp depth_test_func; ///< 0x130C 2838 ComparisonOp depth_test_func; ///< 0x130C
2843 f32 alpha_test_ref; ///< 0x1310 2839 f32 alpha_test_ref; ///< 0x1310
@@ -3048,8 +3044,6 @@ public:
3048 }; 3044 };
3049 3045
3050 std::array<ShaderStageInfo, Regs::MaxShaderStage> shader_stages; 3046 std::array<ShaderStageInfo, Regs::MaxShaderStage> shader_stages;
3051
3052 u32 current_instance = 0; ///< Current instance to be used to simulate instanced rendering.
3053 }; 3047 };
3054 3048
3055 State state{}; 3049 State state{};
@@ -3064,11 +3058,6 @@ public:
3064 void CallMultiMethod(u32 method, const u32* base_start, u32 amount, 3058 void CallMultiMethod(u32 method, const u32* base_start, u32 amount,
3065 u32 methods_pending) override; 3059 u32 methods_pending) override;
3066 3060
3067 /// Write the value to the register identified by method.
3068 void CallMethodFromMME(u32 method, u32 method_argument);
3069
3070 void FlushMMEInlineDraw();
3071
3072 bool ShouldExecute() const { 3061 bool ShouldExecute() const {
3073 return execute_on; 3062 return execute_on;
3074 } 3063 }
@@ -3081,21 +3070,6 @@ public:
3081 return *rasterizer; 3070 return *rasterizer;
3082 } 3071 }
3083 3072
3084 enum class MMEDrawMode : u32 {
3085 Undefined,
3086 Array,
3087 Indexed,
3088 };
3089
3090 struct MMEDrawState {
3091 MMEDrawMode current_mode{MMEDrawMode::Undefined};
3092 u32 current_count{};
3093 u32 instance_count{};
3094 bool instance_mode{};
3095 bool gl_begin_consume{};
3096 u32 gl_end_count{};
3097 } mme_draw;
3098
3099 struct DirtyState { 3073 struct DirtyState {
3100 using Flags = std::bitset<std::numeric_limits<u8>::max()>; 3074 using Flags = std::bitset<std::numeric_limits<u8>::max()>;
3101 using Table = std::array<u8, Regs::NUM_REGS>; 3075 using Table = std::array<u8, Regs::NUM_REGS>;
@@ -3105,6 +3079,8 @@ public:
3105 Tables tables{}; 3079 Tables tables{};
3106 } dirty; 3080 } dirty;
3107 3081
3082 std::vector<u8> inline_index_draw_indexes;
3083
3108private: 3084private:
3109 void InitializeRegisterDefaults(); 3085 void InitializeRegisterDefaults();
3110 3086
@@ -3164,14 +3140,10 @@ private:
3164 /// Handles a write to the CB_BIND register. 3140 /// Handles a write to the CB_BIND register.
3165 void ProcessCBBind(size_t stage_index); 3141 void ProcessCBBind(size_t stage_index);
3166 3142
3167 /// Handles a write to the VERTEX_END_GL register, triggering a draw.
3168 void DrawArrays();
3169
3170 /// Handles use of topology overrides (e.g., to avoid using a topology assigned from a macro) 3143 /// Handles use of topology overrides (e.g., to avoid using a topology assigned from a macro)
3171 void ProcessTopologyOverride(); 3144 void ProcessTopologyOverride();
3172 3145
3173 // Handles a instance drawcall from MME 3146 void ProcessDeferredDraw();
3174 void StepInstance(MMEDrawMode expected_mode, u32 count);
3175 3147
3176 /// Returns a query's value or an empty object if the value will be deferred through a cache. 3148 /// Returns a query's value or an empty object if the value will be deferred through a cache.
3177 std::optional<u64> GetQueryResult(); 3149 std::optional<u64> GetQueryResult();
@@ -3184,8 +3156,6 @@ private:
3184 /// Start offsets of each macro in macro_memory 3156 /// Start offsets of each macro in macro_memory
3185 std::array<u32, 0x80> macro_positions{}; 3157 std::array<u32, 0x80> macro_positions{};
3186 3158
3187 std::array<bool, Regs::NUM_REGS> mme_inline{};
3188
3189 /// Macro method that is currently being executed / being fed parameters. 3159 /// Macro method that is currently being executed / being fed parameters.
3190 u32 executing_macro = 0; 3160 u32 executing_macro = 0;
3191 /// Parameters that have been submitted to the macro call so far. 3161 /// Parameters that have been submitted to the macro call so far.
@@ -3198,6 +3168,9 @@ private:
3198 3168
3199 bool execute_on{true}; 3169 bool execute_on{true};
3200 bool use_topology_override{false}; 3170 bool use_topology_override{false};
3171
3172 std::array<bool, Regs::NUM_REGS> draw_command{};
3173 std::vector<u32> deferred_draw_method;
3201}; 3174};
3202 3175
3203#define ASSERT_REG_POSITION(field_name, position) \ 3176#define ASSERT_REG_POSITION(field_name, position) \
@@ -3402,8 +3375,7 @@ ASSERT_REG_POSITION(alpha_to_coverage_dither, 0x12E0);
3402ASSERT_REG_POSITION(blend_per_target_enabled, 0x12E4); 3375ASSERT_REG_POSITION(blend_per_target_enabled, 0x12E4);
3403ASSERT_REG_POSITION(depth_write_enabled, 0x12E8); 3376ASSERT_REG_POSITION(depth_write_enabled, 0x12E8);
3404ASSERT_REG_POSITION(alpha_test_enabled, 0x12EC); 3377ASSERT_REG_POSITION(alpha_test_enabled, 0x12EC);
3405ASSERT_REG_POSITION(inline_index_4x8_align, 0x1300); 3378ASSERT_REG_POSITION(inline_index_4x8, 0x1300);
3406ASSERT_REG_POSITION(inline_index_4x8_index, 0x1304);
3407ASSERT_REG_POSITION(d3d_cull_mode, 0x1308); 3379ASSERT_REG_POSITION(d3d_cull_mode, 0x1308);
3408ASSERT_REG_POSITION(depth_test_func, 0x130C); 3380ASSERT_REG_POSITION(depth_test_func, 0x130C);
3409ASSERT_REG_POSITION(alpha_test_ref, 0x1310); 3381ASSERT_REG_POSITION(alpha_test_ref, 0x1310);
diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp
index 8a8adbb42..f896591bf 100644
--- a/src/video_core/macro/macro_hle.cpp
+++ b/src/video_core/macro/macro_hle.cpp
@@ -22,35 +22,29 @@ void HLE_771BB18C62444DA0(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
22 maxwell3d.regs.draw.topology.Assign( 22 maxwell3d.regs.draw.topology.Assign(
23 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0] & 0x3ffffff)); 23 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0] & 0x3ffffff));
24 maxwell3d.regs.global_base_instance_index = parameters[5]; 24 maxwell3d.regs.global_base_instance_index = parameters[5];
25 maxwell3d.mme_draw.instance_count = instance_count;
26 maxwell3d.regs.global_base_vertex_index = parameters[3]; 25 maxwell3d.regs.global_base_vertex_index = parameters[3];
27 maxwell3d.regs.index_buffer.count = parameters[1]; 26 maxwell3d.regs.index_buffer.count = parameters[1];
28 maxwell3d.regs.index_buffer.first = parameters[4]; 27 maxwell3d.regs.index_buffer.first = parameters[4];
29 28
30 if (maxwell3d.ShouldExecute()) { 29 if (maxwell3d.ShouldExecute()) {
31 maxwell3d.Rasterizer().Draw(true, true); 30 maxwell3d.Rasterizer().Draw(true, instance_count);
32 } 31 }
33 maxwell3d.regs.index_buffer.count = 0; 32 maxwell3d.regs.index_buffer.count = 0;
34 maxwell3d.mme_draw.instance_count = 0;
35 maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined;
36} 33}
37 34
38void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { 35void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) {
39 const u32 count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); 36 const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]);
40 37
41 maxwell3d.regs.vertex_buffer.first = parameters[3]; 38 maxwell3d.regs.vertex_buffer.first = parameters[3];
42 maxwell3d.regs.vertex_buffer.count = parameters[1]; 39 maxwell3d.regs.vertex_buffer.count = parameters[1];
43 maxwell3d.regs.global_base_instance_index = parameters[4]; 40 maxwell3d.regs.global_base_instance_index = parameters[4];
44 maxwell3d.regs.draw.topology.Assign( 41 maxwell3d.regs.draw.topology.Assign(
45 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0])); 42 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]));
46 maxwell3d.mme_draw.instance_count = count;
47 43
48 if (maxwell3d.ShouldExecute()) { 44 if (maxwell3d.ShouldExecute()) {
49 maxwell3d.Rasterizer().Draw(false, true); 45 maxwell3d.Rasterizer().Draw(false, instance_count);
50 } 46 }
51 maxwell3d.regs.vertex_buffer.count = 0; 47 maxwell3d.regs.vertex_buffer.count = 0;
52 maxwell3d.mme_draw.instance_count = 0;
53 maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined;
54} 48}
55 49
56void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { 50void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) {
@@ -63,24 +57,21 @@ void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
63 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; 57 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
64 maxwell3d.regs.global_base_vertex_index = element_base; 58 maxwell3d.regs.global_base_vertex_index = element_base;
65 maxwell3d.regs.global_base_instance_index = base_instance; 59 maxwell3d.regs.global_base_instance_index = base_instance;
66 maxwell3d.mme_draw.instance_count = instance_count; 60 maxwell3d.CallMethod(0x8e3, 0x640, true);
67 maxwell3d.CallMethodFromMME(0x8e3, 0x640); 61 maxwell3d.CallMethod(0x8e4, element_base, true);
68 maxwell3d.CallMethodFromMME(0x8e4, element_base); 62 maxwell3d.CallMethod(0x8e5, base_instance, true);
69 maxwell3d.CallMethodFromMME(0x8e5, base_instance);
70 maxwell3d.regs.draw.topology.Assign( 63 maxwell3d.regs.draw.topology.Assign(
71 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0])); 64 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]));
72 if (maxwell3d.ShouldExecute()) { 65 if (maxwell3d.ShouldExecute()) {
73 maxwell3d.Rasterizer().Draw(true, true); 66 maxwell3d.Rasterizer().Draw(true, instance_count);
74 } 67 }
75 maxwell3d.regs.vertex_id_base = 0x0; 68 maxwell3d.regs.vertex_id_base = 0x0;
76 maxwell3d.regs.index_buffer.count = 0; 69 maxwell3d.regs.index_buffer.count = 0;
77 maxwell3d.regs.global_base_vertex_index = 0x0; 70 maxwell3d.regs.global_base_vertex_index = 0x0;
78 maxwell3d.regs.global_base_instance_index = 0x0; 71 maxwell3d.regs.global_base_instance_index = 0x0;
79 maxwell3d.mme_draw.instance_count = 0; 72 maxwell3d.CallMethod(0x8e3, 0x640, true);
80 maxwell3d.CallMethodFromMME(0x8e3, 0x640); 73 maxwell3d.CallMethod(0x8e4, 0x0, true);
81 maxwell3d.CallMethodFromMME(0x8e4, 0x0); 74 maxwell3d.CallMethod(0x8e5, 0x0, true);
82 maxwell3d.CallMethodFromMME(0x8e5, 0x0);
83 maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined;
84} 75}
85 76
86// Multidraw Indirect 77// Multidraw Indirect
@@ -91,11 +82,9 @@ void HLE_3F5E74B9C9A50164(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
91 maxwell3d.regs.index_buffer.count = 0; 82 maxwell3d.regs.index_buffer.count = 0;
92 maxwell3d.regs.global_base_vertex_index = 0x0; 83 maxwell3d.regs.global_base_vertex_index = 0x0;
93 maxwell3d.regs.global_base_instance_index = 0x0; 84 maxwell3d.regs.global_base_instance_index = 0x0;
94 maxwell3d.mme_draw.instance_count = 0; 85 maxwell3d.CallMethod(0x8e3, 0x640, true);
95 maxwell3d.CallMethodFromMME(0x8e3, 0x640); 86 maxwell3d.CallMethod(0x8e4, 0x0, true);
96 maxwell3d.CallMethodFromMME(0x8e4, 0x0); 87 maxwell3d.CallMethod(0x8e5, 0x0, true);
97 maxwell3d.CallMethodFromMME(0x8e5, 0x0);
98 maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined;
99 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; 88 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
100 }); 89 });
101 const u32 start_indirect = parameters[0]; 90 const u32 start_indirect = parameters[0];
@@ -127,15 +116,13 @@ void HLE_3F5E74B9C9A50164(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
127 maxwell3d.regs.index_buffer.count = num_vertices; 116 maxwell3d.regs.index_buffer.count = num_vertices;
128 maxwell3d.regs.global_base_vertex_index = base_vertex; 117 maxwell3d.regs.global_base_vertex_index = base_vertex;
129 maxwell3d.regs.global_base_instance_index = base_instance; 118 maxwell3d.regs.global_base_instance_index = base_instance;
130 maxwell3d.mme_draw.instance_count = instance_count; 119 maxwell3d.CallMethod(0x8e3, 0x640, true);
131 maxwell3d.CallMethodFromMME(0x8e3, 0x640); 120 maxwell3d.CallMethod(0x8e4, base_vertex, true);
132 maxwell3d.CallMethodFromMME(0x8e4, base_vertex); 121 maxwell3d.CallMethod(0x8e5, base_instance, true);
133 maxwell3d.CallMethodFromMME(0x8e5, base_instance);
134 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; 122 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
135 if (maxwell3d.ShouldExecute()) { 123 if (maxwell3d.ShouldExecute()) {
136 maxwell3d.Rasterizer().Draw(true, true); 124 maxwell3d.Rasterizer().Draw(true, instance_count);
137 } 125 }
138 maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined;
139 } 126 }
140} 127}
141 128
diff --git a/src/video_core/macro/macro_interpreter.cpp b/src/video_core/macro/macro_interpreter.cpp
index f670b1bca..c0d32c112 100644
--- a/src/video_core/macro/macro_interpreter.cpp
+++ b/src/video_core/macro/macro_interpreter.cpp
@@ -335,7 +335,7 @@ void MacroInterpreterImpl::SetMethodAddress(u32 address) {
335} 335}
336 336
337void MacroInterpreterImpl::Send(u32 value) { 337void MacroInterpreterImpl::Send(u32 value) {
338 maxwell3d.CallMethodFromMME(method_address.address, value); 338 maxwell3d.CallMethod(method_address.address, value, true);
339 // Increment the method address by the method increment. 339 // Increment the method address by the method increment.
340 method_address.address.Assign(method_address.address.Value() + 340 method_address.address.Assign(method_address.address.Value() +
341 method_address.increment.Value()); 341 method_address.increment.Value());
diff --git a/src/video_core/macro/macro_jit_x64.cpp b/src/video_core/macro/macro_jit_x64.cpp
index a302a9603..25c1ce798 100644
--- a/src/video_core/macro/macro_jit_x64.cpp
+++ b/src/video_core/macro/macro_jit_x64.cpp
@@ -346,7 +346,7 @@ void MacroJITx64Impl::Compile_Read(Macro::Opcode opcode) {
346} 346}
347 347
348void Send(Engines::Maxwell3D* maxwell3d, Macro::MethodAddress method_address, u32 value) { 348void Send(Engines::Maxwell3D* maxwell3d, Macro::MethodAddress method_address, u32 value) {
349 maxwell3d->CallMethodFromMME(method_address.address, value); 349 maxwell3d->CallMethod(method_address.address, value, true);
350} 350}
351 351
352void MacroJITx64Impl::Compile_Send(Xbyak::Reg32 value) { 352void MacroJITx64Impl::Compile_Send(Xbyak::Reg32 value) {
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index d07b21bd6..384350dbd 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -133,7 +133,7 @@ inline void MemoryManager::SetBigPageContinous(size_t big_page_index, bool value
133template <MemoryManager::EntryType entry_type> 133template <MemoryManager::EntryType entry_type>
134GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr, size_t size, 134GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr, size_t size,
135 PTEKind kind) { 135 PTEKind kind) {
136 u64 remaining_size{size}; 136 [[maybe_unused]] u64 remaining_size{size};
137 if constexpr (entry_type == EntryType::Mapped) { 137 if constexpr (entry_type == EntryType::Mapped) {
138 page_table.ReserveRange(gpu_addr, size); 138 page_table.ReserveRange(gpu_addr, size);
139 } 139 }
@@ -159,7 +159,7 @@ GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cp
159template <MemoryManager::EntryType entry_type> 159template <MemoryManager::EntryType entry_type>
160GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr, 160GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr,
161 size_t size, PTEKind kind) { 161 size_t size, PTEKind kind) {
162 u64 remaining_size{size}; 162 [[maybe_unused]] u64 remaining_size{size};
163 for (u64 offset{}; offset < size; offset += big_page_size) { 163 for (u64 offset{}; offset < size; offset += big_page_size) {
164 const GPUVAddr current_gpu_addr = gpu_addr + offset; 164 const GPUVAddr current_gpu_addr = gpu_addr + offset;
165 [[maybe_unused]] const auto current_entry_type = GetEntry<true>(current_gpu_addr); 165 [[maybe_unused]] const auto current_entry_type = GetEntry<true>(current_gpu_addr);
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index d2d40884c..1cbfef090 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -40,7 +40,7 @@ public:
40 virtual ~RasterizerInterface() = default; 40 virtual ~RasterizerInterface() = default;
41 41
42 /// Dispatches a draw invocation 42 /// Dispatches a draw invocation
43 virtual void Draw(bool is_indexed, bool is_instanced) = 0; 43 virtual void Draw(bool is_indexed, u32 instance_count) = 0;
44 44
45 /// Clear the current framebuffer 45 /// Clear the current framebuffer
46 virtual void Clear() = 0; 46 virtual void Clear() = 0;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index e5c09a969..1590b21de 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -205,7 +205,7 @@ void RasterizerOpenGL::Clear() {
205 ++num_queued_commands; 205 ++num_queued_commands;
206} 206}
207 207
208void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { 208void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) {
209 MICROPROFILE_SCOPE(OpenGL_Drawing); 209 MICROPROFILE_SCOPE(OpenGL_Drawing);
210 210
211 SCOPE_EXIT({ gpu.TickWork(); }); 211 SCOPE_EXIT({ gpu.TickWork(); });
@@ -222,14 +222,15 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
222 pipeline->SetEngine(maxwell3d, gpu_memory); 222 pipeline->SetEngine(maxwell3d, gpu_memory);
223 pipeline->Configure(is_indexed); 223 pipeline->Configure(is_indexed);
224 224
225 BindInlineIndexBuffer();
226
225 SyncState(); 227 SyncState();
226 228
227 const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(maxwell3d->regs.draw.topology); 229 const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(maxwell3d->regs.draw.topology);
228 BeginTransformFeedback(pipeline, primitive_mode); 230 BeginTransformFeedback(pipeline, primitive_mode);
229 231
230 const GLuint base_instance = static_cast<GLuint>(maxwell3d->regs.global_base_instance_index); 232 const GLuint base_instance = static_cast<GLuint>(maxwell3d->regs.global_base_instance_index);
231 const GLsizei num_instances = 233 const GLsizei num_instances = static_cast<GLsizei>(instance_count);
232 static_cast<GLsizei>(is_instanced ? maxwell3d->mme_draw.instance_count : 1);
233 if (is_indexed) { 234 if (is_indexed) {
234 const GLint base_vertex = static_cast<GLint>(maxwell3d->regs.global_base_vertex_index); 235 const GLint base_vertex = static_cast<GLint>(maxwell3d->regs.global_base_vertex_index);
235 const GLsizei num_vertices = static_cast<GLsizei>(maxwell3d->regs.index_buffer.count); 236 const GLsizei num_vertices = static_cast<GLsizei>(maxwell3d->regs.index_buffer.count);
@@ -1129,6 +1130,16 @@ void RasterizerOpenGL::ReleaseChannel(s32 channel_id) {
1129 query_cache.EraseChannel(channel_id); 1130 query_cache.EraseChannel(channel_id);
1130} 1131}
1131 1132
1133void RasterizerOpenGL::BindInlineIndexBuffer() {
1134 if (maxwell3d->inline_index_draw_indexes.empty()) {
1135 return;
1136 }
1137 const auto data_count = static_cast<u32>(maxwell3d->inline_index_draw_indexes.size());
1138 auto buffer = Buffer(buffer_cache_runtime, *this, 0, data_count);
1139 buffer.ImmediateUpload(0, maxwell3d->inline_index_draw_indexes);
1140 buffer_cache_runtime.BindIndexBuffer(buffer, 0, data_count);
1141}
1142
1132AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_) : buffer_cache{buffer_cache_} {} 1143AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_) : buffer_cache{buffer_cache_} {}
1133 1144
1134bool AccelerateDMA::BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) { 1145bool AccelerateDMA::BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 45131b785..793e0d608 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -68,7 +68,7 @@ public:
68 StateTracker& state_tracker_); 68 StateTracker& state_tracker_);
69 ~RasterizerOpenGL() override; 69 ~RasterizerOpenGL() override;
70 70
71 void Draw(bool is_indexed, bool is_instanced) override; 71 void Draw(bool is_indexed, u32 instance_count) override;
72 void Clear() override; 72 void Clear() override;
73 void DispatchCompute() override; 73 void DispatchCompute() override;
74 void ResetCounter(VideoCore::QueryType type) override; 74 void ResetCounter(VideoCore::QueryType type) override;
@@ -199,6 +199,8 @@ private:
199 /// End a transform feedback 199 /// End a transform feedback
200 void EndTransformFeedback(); 200 void EndTransformFeedback();
201 201
202 void BindInlineIndexBuffer();
203
202 Tegra::GPU& gpu; 204 Tegra::GPU& gpu;
203 205
204 const Device& device; 206 const Device& device;
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 609f0a772..e94cfdb1a 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -63,6 +63,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key,
63 Shader::RuntimeInfo info; 63 Shader::RuntimeInfo info;
64 if (previous_program) { 64 if (previous_program) {
65 info.previous_stage_stores = previous_program->info.stores; 65 info.previous_stage_stores = previous_program->info.stores;
66 info.previous_stage_legacy_stores_mapping = previous_program->info.legacy_stores_mapping;
66 } else { 67 } else {
67 // Mark all stores as available for vertex shaders 68 // Mark all stores as available for vertex shaders
68 info.previous_stage_stores.mask.set(); 69 info.previous_stage_stores.mask.set();
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 20f1d6584..13d5a1f67 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -134,6 +134,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
134 Shader::RuntimeInfo info; 134 Shader::RuntimeInfo info;
135 if (previous_program) { 135 if (previous_program) {
136 info.previous_stage_stores = previous_program->info.stores; 136 info.previous_stage_stores = previous_program->info.stores;
137 info.previous_stage_legacy_stores_mapping = previous_program->info.legacy_stores_mapping;
137 if (previous_program->is_geometry_passthrough) { 138 if (previous_program->is_geometry_passthrough) {
138 info.previous_stage_stores.mask |= previous_program->info.passthrough.mask; 139 info.previous_stage_stores.mask |= previous_program->info.passthrough.mask;
139 } 140 }
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 47dfb45a1..9f05a7a18 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -127,11 +127,10 @@ VkRect2D GetScissorState(const Maxwell& regs, size_t index, u32 up_scale = 1, u3
127 return scissor; 127 return scissor;
128} 128}
129 129
130DrawParams MakeDrawParams(const Maxwell& regs, u32 num_instances, bool is_instanced, 130DrawParams MakeDrawParams(const Maxwell& regs, u32 num_instances, bool is_indexed) {
131 bool is_indexed) {
132 DrawParams params{ 131 DrawParams params{
133 .base_instance = regs.global_base_instance_index, 132 .base_instance = regs.global_base_instance_index,
134 .num_instances = is_instanced ? num_instances : 1, 133 .num_instances = num_instances,
135 .base_vertex = is_indexed ? regs.global_base_vertex_index : regs.vertex_buffer.first, 134 .base_vertex = is_indexed ? regs.global_base_vertex_index : regs.vertex_buffer.first,
136 .num_vertices = is_indexed ? regs.index_buffer.count : regs.vertex_buffer.count, 135 .num_vertices = is_indexed ? regs.index_buffer.count : regs.vertex_buffer.count,
137 .first_index = is_indexed ? regs.index_buffer.first : 0, 136 .first_index = is_indexed ? regs.index_buffer.first : 0,
@@ -177,7 +176,7 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra
177 176
178RasterizerVulkan::~RasterizerVulkan() = default; 177RasterizerVulkan::~RasterizerVulkan() = default;
179 178
180void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) { 179void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
181 MICROPROFILE_SCOPE(Vulkan_Drawing); 180 MICROPROFILE_SCOPE(Vulkan_Drawing);
182 181
183 SCOPE_EXIT({ gpu.TickWork(); }); 182 SCOPE_EXIT({ gpu.TickWork(); });
@@ -194,13 +193,15 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
194 pipeline->SetEngine(maxwell3d, gpu_memory); 193 pipeline->SetEngine(maxwell3d, gpu_memory);
195 pipeline->Configure(is_indexed); 194 pipeline->Configure(is_indexed);
196 195
196 BindInlineIndexBuffer();
197
197 BeginTransformFeedback(); 198 BeginTransformFeedback();
198 199
199 UpdateDynamicStates(); 200 UpdateDynamicStates();
200 201
201 const auto& regs{maxwell3d->regs}; 202 const auto& regs{maxwell3d->regs};
202 const u32 num_instances{maxwell3d->mme_draw.instance_count}; 203 const u32 num_instances{instance_count};
203 const DrawParams draw_params{MakeDrawParams(regs, num_instances, is_instanced, is_indexed)}; 204 const DrawParams draw_params{MakeDrawParams(regs, num_instances, is_indexed)};
204 scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) { 205 scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) {
205 if (draw_params.is_indexed) { 206 if (draw_params.is_indexed) {
206 cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances, 207 cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances,
@@ -1009,4 +1010,17 @@ void RasterizerVulkan::ReleaseChannel(s32 channel_id) {
1009 query_cache.EraseChannel(channel_id); 1010 query_cache.EraseChannel(channel_id);
1010} 1011}
1011 1012
1013void RasterizerVulkan::BindInlineIndexBuffer() {
1014 if (maxwell3d->inline_index_draw_indexes.empty()) {
1015 return;
1016 }
1017 const auto data_count = static_cast<u32>(maxwell3d->inline_index_draw_indexes.size());
1018 auto buffer = buffer_cache_runtime.UploadStagingBuffer(data_count);
1019 std::memcpy(buffer.mapped_span.data(), maxwell3d->inline_index_draw_indexes.data(), data_count);
1020 buffer_cache_runtime.BindIndexBuffer(
1021 maxwell3d->regs.draw.topology, maxwell3d->regs.index_buffer.format,
1022 maxwell3d->regs.index_buffer.first, maxwell3d->regs.index_buffer.count, buffer.buffer,
1023 static_cast<u32>(buffer.offset), data_count);
1024}
1025
1012} // namespace Vulkan 1026} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index 4cde3c983..e2fdc7611 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -64,7 +64,7 @@ public:
64 StateTracker& state_tracker_, Scheduler& scheduler_); 64 StateTracker& state_tracker_, Scheduler& scheduler_);
65 ~RasterizerVulkan() override; 65 ~RasterizerVulkan() override;
66 66
67 void Draw(bool is_indexed, bool is_instanced) override; 67 void Draw(bool is_indexed, u32 instance_count) override;
68 void Clear() override; 68 void Clear() override;
69 void DispatchCompute() override; 69 void DispatchCompute() override;
70 void ResetCounter(VideoCore::QueryType type) override; 70 void ResetCounter(VideoCore::QueryType type) override;
@@ -141,6 +141,8 @@ private:
141 141
142 void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs); 142 void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs);
143 143
144 void BindInlineIndexBuffer();
145
144 Tegra::GPU& gpu; 146 Tegra::GPU& gpu;
145 147
146 ScreenInfo& screen_info; 148 ScreenInfo& screen_info;
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 0e0fd410f..8ef75fe73 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -442,7 +442,7 @@ void TextureCache<P>::WriteMemory(VAddr cpu_addr, size_t size) {
442template <class P> 442template <class P>
443void TextureCache<P>::DownloadMemory(VAddr cpu_addr, size_t size) { 443void TextureCache<P>::DownloadMemory(VAddr cpu_addr, size_t size) {
444 std::vector<ImageId> images; 444 std::vector<ImageId> images;
445 ForEachImageInRegion(cpu_addr, size, [this, &images](ImageId image_id, ImageBase& image) { 445 ForEachImageInRegion(cpu_addr, size, [&images](ImageId image_id, ImageBase& image) {
446 if (!image.IsSafeDownload()) { 446 if (!image.IsSafeDownload()) {
447 return; 447 return;
448 } 448 }
@@ -1502,9 +1502,9 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
1502 image.flags &= ~ImageFlagBits::BadOverlap; 1502 image.flags &= ~ImageFlagBits::BadOverlap;
1503 lru_cache.Free(image.lru_index); 1503 lru_cache.Free(image.lru_index);
1504 const auto& clear_page_table = 1504 const auto& clear_page_table =
1505 [this, image_id](u64 page, 1505 [image_id](u64 page,
1506 std::unordered_map<u64, std::vector<ImageId>, Common::IdentityHash<u64>>& 1506 std::unordered_map<u64, std::vector<ImageId>, Common::IdentityHash<u64>>&
1507 selected_page_table) { 1507 selected_page_table) {
1508 const auto page_it = selected_page_table.find(page); 1508 const auto page_it = selected_page_table.find(page);
1509 if (page_it == selected_page_table.end()) { 1509 if (page_it == selected_page_table.end()) {
1510 ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << YUZU_PAGEBITS); 1510 ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << YUZU_PAGEBITS);
diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp
index 15b9d4182..69a32819a 100644
--- a/src/video_core/textures/astc.cpp
+++ b/src/video_core/textures/astc.cpp
@@ -1661,8 +1661,8 @@ void Decompress(std::span<const uint8_t> data, uint32_t width, uint32_t height,
1661 for (u32 z = 0; z < depth; ++z) { 1661 for (u32 z = 0; z < depth; ++z) {
1662 const u32 depth_offset = z * height * width * 4; 1662 const u32 depth_offset = z * height * width * 4;
1663 for (u32 y_index = 0; y_index < rows; ++y_index) { 1663 for (u32 y_index = 0; y_index < rows; ++y_index) {
1664 auto decompress_stride = [data, width, height, depth, block_width, block_height, output, 1664 auto decompress_stride = [data, width, height, block_width, block_height, output, rows,
1665 rows, cols, z, depth_offset, y_index] { 1665 cols, z, depth_offset, y_index] {
1666 const u32 y = y_index * block_height; 1666 const u32 y = y_index * block_height;
1667 for (u32 x_index = 0; x_index < cols; ++x_index) { 1667 for (u32 x_index = 0; x_index < cols; ++x_index) {
1668 const u32 block_index = (z * rows * cols) + (y_index * cols) + x_index; 1668 const u32 block_index = (z * rows * cols) + (y_index * cols) + x_index;
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 52d067a2d..fd1a4b987 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -21,7 +21,7 @@ constexpr u32 pdep(u32 value) {
21 u32 m = mask; 21 u32 m = mask;
22 for (u32 bit = 1; m; bit += bit) { 22 for (u32 bit = 1; m; bit += bit) {
23 if (value & bit) 23 if (value & bit)
24 result |= m & -m; 24 result |= m & (~m + 1);
25 m &= m - 1; 25 m &= m - 1;
26 } 26 }
27 return result; 27 return result;
diff --git a/src/yuzu/multiplayer/state.cpp b/src/yuzu/multiplayer/state.cpp
index ae2738ad4..285bb150d 100644
--- a/src/yuzu/multiplayer/state.cpp
+++ b/src/yuzu/multiplayer/state.cpp
@@ -268,7 +268,7 @@ bool MultiplayerState::OnCloseRoom() {
268 return true; 268 return true;
269 } 269 }
270 // Save ban list 270 // Save ban list
271 UISettings::values.multiplayer_ban_list = std::move(room->GetBanList()); 271 UISettings::values.multiplayer_ban_list = room->GetBanList();
272 272
273 room->Destroy(); 273 room->Destroy();
274 announce_multiplayer_session->Stop(); 274 announce_multiplayer_session->Stop();
diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp
index fc2693f9d..6a91212e2 100644
--- a/src/yuzu/startup_checks.cpp
+++ b/src/yuzu/startup_checks.cpp
@@ -49,7 +49,7 @@ bool CheckEnvVars(bool* is_child) {
49 *is_child = true; 49 *is_child = true;
50 return false; 50 return false;
51 } else if (!SetEnvironmentVariableA(IS_CHILD_ENV_VAR, ENV_VAR_ENABLED_TEXT)) { 51 } else if (!SetEnvironmentVariableA(IS_CHILD_ENV_VAR, ENV_VAR_ENABLED_TEXT)) {
52 std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %d\n", 52 std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %lu\n",
53 IS_CHILD_ENV_VAR, GetLastError()); 53 IS_CHILD_ENV_VAR, GetLastError());
54 return true; 54 return true;
55 } 55 }
@@ -62,7 +62,7 @@ bool StartupChecks(const char* arg0, bool* has_broken_vulkan, bool perform_vulka
62 // Set the startup variable for child processes 62 // Set the startup variable for child processes
63 const bool env_var_set = SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, ENV_VAR_ENABLED_TEXT); 63 const bool env_var_set = SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, ENV_VAR_ENABLED_TEXT);
64 if (!env_var_set) { 64 if (!env_var_set) {
65 std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %d\n", 65 std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %lu\n",
66 STARTUP_CHECK_ENV_VAR, GetLastError()); 66 STARTUP_CHECK_ENV_VAR, GetLastError());
67 return false; 67 return false;
68 } 68 }
@@ -81,22 +81,22 @@ bool StartupChecks(const char* arg0, bool* has_broken_vulkan, bool perform_vulka
81 DWORD exit_code = STILL_ACTIVE; 81 DWORD exit_code = STILL_ACTIVE;
82 const int err = GetExitCodeProcess(process_info.hProcess, &exit_code); 82 const int err = GetExitCodeProcess(process_info.hProcess, &exit_code);
83 if (err == 0) { 83 if (err == 0) {
84 std::fprintf(stderr, "GetExitCodeProcess failed with error %d\n", GetLastError()); 84 std::fprintf(stderr, "GetExitCodeProcess failed with error %lu\n", GetLastError());
85 } 85 }
86 86
87 // Vulkan is broken if the child crashed (return value is not zero) 87 // Vulkan is broken if the child crashed (return value is not zero)
88 *has_broken_vulkan = (exit_code != 0); 88 *has_broken_vulkan = (exit_code != 0);
89 89
90 if (CloseHandle(process_info.hProcess) == 0) { 90 if (CloseHandle(process_info.hProcess) == 0) {
91 std::fprintf(stderr, "CloseHandle failed with error %d\n", GetLastError()); 91 std::fprintf(stderr, "CloseHandle failed with error %lu\n", GetLastError());
92 } 92 }
93 if (CloseHandle(process_info.hThread) == 0) { 93 if (CloseHandle(process_info.hThread) == 0) {
94 std::fprintf(stderr, "CloseHandle failed with error %d\n", GetLastError()); 94 std::fprintf(stderr, "CloseHandle failed with error %lu\n", GetLastError());
95 } 95 }
96 } 96 }
97 97
98 if (!SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, nullptr)) { 98 if (!SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, nullptr)) {
99 std::fprintf(stderr, "SetEnvironmentVariableA failed to clear %s with error %d\n", 99 std::fprintf(stderr, "SetEnvironmentVariableA failed to clear %s with error %lu\n",
100 STARTUP_CHECK_ENV_VAR, GetLastError()); 100 STARTUP_CHECK_ENV_VAR, GetLastError());
101 } 101 }
102 102
@@ -135,7 +135,8 @@ bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi, int flags) {
135 startup_info.cb = sizeof(startup_info); 135 startup_info.cb = sizeof(startup_info);
136 136
137 char p_name[255]; 137 char p_name[255];
138 std::strncpy(p_name, arg0, 255); 138 std::strncpy(p_name, arg0, 254);
139 p_name[254] = '\0';
139 140
140 const bool process_created = CreateProcessA(nullptr, // lpApplicationName 141 const bool process_created = CreateProcessA(nullptr, // lpApplicationName
141 p_name, // lpCommandLine 142 p_name, // lpCommandLine
@@ -149,7 +150,7 @@ bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi, int flags) {
149 pi // lpProcessInformation 150 pi // lpProcessInformation
150 ); 151 );
151 if (!process_created) { 152 if (!process_created) {
152 std::fprintf(stderr, "CreateProcessA failed with error %d\n", GetLastError()); 153 std::fprintf(stderr, "CreateProcessA failed with error %lu\n", GetLastError());
153 return false; 154 return false;
154 } 155 }
155 156