summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt9
-rw-r--r--src/common/CMakeLists.txt16
-rw-r--r--src/common/fiber.cpp8
-rw-r--r--src/common/fiber.h2
-rw-r--r--src/common/file_util.cpp31
-rw-r--r--src/common/file_util.h2
-rw-r--r--src/common/logging/backend.cpp2
-rw-r--r--src/common/misc.cpp15
-rw-r--r--src/common/stream.h12
-rw-r--r--src/common/string_util.cpp5
-rw-r--r--src/common/timer.cpp12
-rw-r--r--src/common/wall_clock.cpp2
-rw-r--r--src/common/x64/native_clock.h2
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/arm/arm_interface.cpp12
-rw-r--r--src/core/core.cpp10
-rw-r--r--src/core/hle/kernel/process.cpp3
-rw-r--r--src/core/hle/service/am/am.cpp2
-rw-r--r--src/core/hle/service/hid/hid.cpp2
-rw-r--r--src/core/hle/service/ldr/ldr.cpp16
-rw-r--r--src/core/hle/service/mii/mii.cpp1
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp51
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.h10
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp135
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.h20
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp2
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp17
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h14
-rw-r--r--src/core/hle/service/nvdrv/syncpoint_manager.cpp39
-rw-r--r--src/core/hle/service/nvdrv/syncpoint_manager.h85
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp4
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp4
-rw-r--r--src/core/hle/service/set/set.cpp1
-rw-r--r--src/core/hle/service/set/set_sys.cpp2
-rw-r--r--src/core/settings.cpp20
-rw-r--r--src/core/settings.h11
-rw-r--r--src/input_common/gcadapter/gc_adapter.cpp462
-rw-r--r--src/input_common/gcadapter/gc_adapter.h146
-rw-r--r--src/input_common/gcadapter/gc_poller.cpp128
-rw-r--r--src/video_core/cdma_pusher.cpp2
-rw-r--r--src/video_core/command_classes/codecs/codec.cpp1
-rw-r--r--src/video_core/command_classes/codecs/codec.h8
-rw-r--r--src/video_core/command_classes/codecs/h264.cpp31
-rw-r--r--src/video_core/command_classes/codecs/h264.h24
-rw-r--r--src/video_core/command_classes/codecs/vp9.cpp168
-rw-r--r--src/video_core/command_classes/codecs/vp9.h82
-rw-r--r--src/video_core/command_classes/codecs/vp9_types.h11
-rw-r--r--src/video_core/command_classes/host1x.cpp2
-rw-r--r--src/video_core/command_classes/host1x.h2
-rw-r--r--src/video_core/command_classes/nvdec.cpp6
-rw-r--r--src/video_core/command_classes/nvdec.h10
-rw-r--r--src/video_core/command_classes/sync_manager.cpp14
-rw-r--r--src/video_core/command_classes/sync_manager.h4
-rw-r--r--src/video_core/command_classes/vic.cpp2
-rw-r--r--src/video_core/command_classes/vic.h4
-rw-r--r--src/video_core/dma_pusher.cpp80
-rw-r--r--src/video_core/dma_pusher.h49
-rw-r--r--src/video_core/engines/shader_bytecode.h2
-rw-r--r--src/video_core/gpu.cpp48
-rw-r--r--src/video_core/gpu.h25
-rw-r--r--src/video_core/renderer_vulkan/vk_device.cpp9
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp2
-rw-r--r--src/video_core/shader/async_shaders.cpp17
-rw-r--r--src/video_core/shader/decode/arithmetic.cpp3
-rw-r--r--src/video_core/texture_cache/surface_params.cpp1
-rw-r--r--src/web_service/web_backend.cpp6
-rw-r--r--src/yuzu/configuration/configure_audio.cpp10
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp2
-rw-r--r--src/yuzu/configuration/configure_general.cpp8
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp18
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp8
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp2
-rw-r--r--src/yuzu/configuration/configure_system.cpp10
73 files changed, 1275 insertions, 713 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 71efbb40d..dbda528ce 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -32,7 +32,6 @@ if (MSVC)
32 # /Zc:inline - Let codegen omit inline functions in object files 32 # /Zc:inline - Let codegen omit inline functions in object files
33 # /Zc:throwingNew - Let codegen assume `operator new` (without std::nothrow) will never return null 33 # /Zc:throwingNew - Let codegen assume `operator new` (without std::nothrow) will never return null
34 add_compile_options( 34 add_compile_options(
35 /W3
36 /MP 35 /MP
37 /Zi 36 /Zi
38 /Zo 37 /Zo
@@ -43,6 +42,13 @@ if (MSVC)
43 /Zc:externConstexpr 42 /Zc:externConstexpr
44 /Zc:inline 43 /Zc:inline
45 /Zc:throwingNew 44 /Zc:throwingNew
45
46 # Warnings
47 /W3
48 /we4547 # 'operator' : operator before comma has no effect; expected operator with side-effect
49 /we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'?
50 /we4555 # Expression has no effect; expected expression with side-effect
51 /we4834 # Discarding return value of function with 'nodiscard' attribute
46 ) 52 )
47 53
48 # /GS- - No stack buffer overflow checks 54 # /GS- - No stack buffer overflow checks
@@ -56,6 +62,7 @@ else()
56 -Werror=implicit-fallthrough 62 -Werror=implicit-fallthrough
57 -Werror=missing-declarations 63 -Werror=missing-declarations
58 -Werror=reorder 64 -Werror=reorder
65 -Werror=unused-result
59 -Wextra 66 -Wextra
60 -Wmissing-declarations 67 -Wmissing-declarations
61 -Wno-attributes 68 -Wno-attributes
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index e50ab2922..207c7a0a6 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -190,6 +190,22 @@ if(ARCHITECTURE_x86_64)
190 ) 190 )
191endif() 191endif()
192 192
193if (MSVC)
194 target_compile_definitions(common PRIVATE
195 # The standard library doesn't provide any replacement for codecvt yet
196 # so we can disable this deprecation warning for the time being.
197 _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING
198 )
199 target_compile_options(common PRIVATE
200 /W4
201 /WX
202 )
203else()
204 target_compile_options(common PRIVATE
205 -Werror
206 )
207endif()
208
193create_target_directory_groups(common) 209create_target_directory_groups(common)
194find_package(Boost 1.71 COMPONENTS context headers REQUIRED) 210find_package(Boost 1.71 COMPONENTS context headers REQUIRED)
195 211
diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp
index e186ed880..b209f52fc 100644
--- a/src/common/fiber.cpp
+++ b/src/common/fiber.cpp
@@ -79,9 +79,9 @@ void Fiber::Exit() {
79 released = true; 79 released = true;
80} 80}
81 81
82void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter) { 82void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param) {
83 rewind_point = std::move(rewind_func); 83 rewind_point = std::move(rewind_func);
84 rewind_parameter = start_parameter; 84 rewind_parameter = rewind_param;
85} 85}
86 86
87void Fiber::Rewind() { 87void Fiber::Rewind() {
@@ -161,9 +161,9 @@ Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_paramete
161 boost::context::detail::make_fcontext(stack_base, impl->stack.size(), FiberStartFunc); 161 boost::context::detail::make_fcontext(stack_base, impl->stack.size(), FiberStartFunc);
162} 162}
163 163
164void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter) { 164void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param) {
165 rewind_point = std::move(rewind_func); 165 rewind_point = std::move(rewind_func);
166 rewind_parameter = start_parameter; 166 rewind_parameter = rewind_param;
167} 167}
168 168
169Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {} 169Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
diff --git a/src/common/fiber.h b/src/common/fiber.h
index cefd61df9..699286ee2 100644
--- a/src/common/fiber.h
+++ b/src/common/fiber.h
@@ -49,7 +49,7 @@ public:
49 static void YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to); 49 static void YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to);
50 [[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber(); 50 [[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();
51 51
52 void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter); 52 void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param);
53 53
54 void Rewind(); 54 void Rewind();
55 55
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 16c3713e0..18fbfa25b 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -472,13 +472,14 @@ u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
472} 472}
473 473
474bool DeleteDirRecursively(const std::string& directory, unsigned int recursion) { 474bool DeleteDirRecursively(const std::string& directory, unsigned int recursion) {
475 const auto callback = [recursion](u64* num_entries_out, const std::string& directory, 475 const auto callback = [recursion](u64*, const std::string& directory,
476 const std::string& virtual_name) -> bool { 476 const std::string& virtual_name) {
477 std::string new_path = directory + DIR_SEP_CHR + virtual_name; 477 const std::string new_path = directory + DIR_SEP_CHR + virtual_name;
478 478
479 if (IsDirectory(new_path)) { 479 if (IsDirectory(new_path)) {
480 if (recursion == 0) 480 if (recursion == 0) {
481 return false; 481 return false;
482 }
482 return DeleteDirRecursively(new_path, recursion - 1); 483 return DeleteDirRecursively(new_path, recursion - 1);
483 } 484 }
484 return Delete(new_path); 485 return Delete(new_path);
@@ -492,7 +493,8 @@ bool DeleteDirRecursively(const std::string& directory, unsigned int recursion)
492 return true; 493 return true;
493} 494}
494 495
495void CopyDir(const std::string& source_path, const std::string& dest_path) { 496void CopyDir([[maybe_unused]] const std::string& source_path,
497 [[maybe_unused]] const std::string& dest_path) {
496#ifndef _WIN32 498#ifndef _WIN32
497 if (source_path == dest_path) { 499 if (source_path == dest_path) {
498 return; 500 return;
@@ -553,7 +555,7 @@ std::optional<std::string> GetCurrentDir() {
553 std::string strDir = dir; 555 std::string strDir = dir;
554#endif 556#endif
555 free(dir); 557 free(dir);
556 return std::move(strDir); 558 return strDir;
557} 559}
558 560
559bool SetCurrentDir(const std::string& directory) { 561bool SetCurrentDir(const std::string& directory) {
@@ -772,21 +774,23 @@ std::size_t ReadFileToString(bool text_file, const std::string& filename, std::s
772 774
773void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name, 775void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name,
774 std::array<char, 4>& extension) { 776 std::array<char, 4>& extension) {
775 const std::string forbidden_characters = ".\"/\\[]:;=, "; 777 static constexpr std::string_view forbidden_characters = ".\"/\\[]:;=, ";
776 778
777 // On a FAT32 partition, 8.3 names are stored as a 11 bytes array, filled with spaces. 779 // On a FAT32 partition, 8.3 names are stored as a 11 bytes array, filled with spaces.
778 short_name = {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '\0'}}; 780 short_name = {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '\0'}};
779 extension = {{' ', ' ', ' ', '\0'}}; 781 extension = {{' ', ' ', ' ', '\0'}};
780 782
781 std::string::size_type point = filename.rfind('.'); 783 auto point = filename.rfind('.');
782 if (point == filename.size() - 1) 784 if (point == filename.size() - 1) {
783 point = filename.rfind('.', point); 785 point = filename.rfind('.', point);
786 }
784 787
785 // Get short name. 788 // Get short name.
786 int j = 0; 789 int j = 0;
787 for (char letter : filename.substr(0, point)) { 790 for (char letter : filename.substr(0, point)) {
788 if (forbidden_characters.find(letter, 0) != std::string::npos) 791 if (forbidden_characters.find(letter, 0) != std::string::npos) {
789 continue; 792 continue;
793 }
790 if (j == 8) { 794 if (j == 8) {
791 // TODO(Link Mauve): also do that for filenames containing a space. 795 // TODO(Link Mauve): also do that for filenames containing a space.
792 // TODO(Link Mauve): handle multiple files having the same short name. 796 // TODO(Link Mauve): handle multiple files having the same short name.
@@ -794,14 +798,15 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
794 short_name[7] = '1'; 798 short_name[7] = '1';
795 break; 799 break;
796 } 800 }
797 short_name[j++] = toupper(letter); 801 short_name[j++] = static_cast<char>(std::toupper(letter));
798 } 802 }
799 803
800 // Get extension. 804 // Get extension.
801 if (point != std::string::npos) { 805 if (point != std::string::npos) {
802 j = 0; 806 j = 0;
803 for (char letter : filename.substr(point + 1, 3)) 807 for (char letter : filename.substr(point + 1, 3)) {
804 extension[j++] = toupper(letter); 808 extension[j++] = static_cast<char>(std::toupper(letter));
809 }
805 } 810 }
806} 811}
807 812
diff --git a/src/common/file_util.h b/src/common/file_util.h
index 8b587320f..840cde2a6 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -232,7 +232,7 @@ public:
232 232
233 void Swap(IOFile& other) noexcept; 233 void Swap(IOFile& other) noexcept;
234 234
235 [[nodiscard]] bool Open(const std::string& filename, const char openmode[], int flags = 0); 235 bool Open(const std::string& filename, const char openmode[], int flags = 0);
236 bool Close(); 236 bool Close();
237 237
238 template <typename T> 238 template <typename T>
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 62cfde397..90dfa22ca 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -274,7 +274,6 @@ const char* GetLogClassName(Class log_class) {
274 case Class::Count: 274 case Class::Count:
275 break; 275 break;
276 } 276 }
277 UNREACHABLE();
278 return "Invalid"; 277 return "Invalid";
279} 278}
280 279
@@ -293,7 +292,6 @@ const char* GetLevelName(Level log_level) {
293 break; 292 break;
294 } 293 }
295#undef LVL 294#undef LVL
296 UNREACHABLE();
297 return "Invalid"; 295 return "Invalid";
298} 296}
299 297
diff --git a/src/common/misc.cpp b/src/common/misc.cpp
index 68cb86cd1..1d5393597 100644
--- a/src/common/misc.cpp
+++ b/src/common/misc.cpp
@@ -16,16 +16,23 @@
16// Call directly after the command or use the error num. 16// Call directly after the command or use the error num.
17// This function might change the error code. 17// This function might change the error code.
18std::string GetLastErrorMsg() { 18std::string GetLastErrorMsg() {
19 static const std::size_t buff_size = 255; 19 static constexpr std::size_t buff_size = 255;
20 char err_str[buff_size]; 20 char err_str[buff_size];
21 21
22#ifdef _WIN32 22#ifdef _WIN32
23 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(), 23 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(),
24 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err_str, buff_size, nullptr); 24 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err_str, buff_size, nullptr);
25 return std::string(err_str, buff_size);
26#elif defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600))
27 // Thread safe (GNU-specific)
28 const char* str = strerror_r(errno, err_str, buff_size);
29 return std::string(str);
25#else 30#else
26 // Thread safe (XSI-compliant) 31 // Thread safe (XSI-compliant)
27 strerror_r(errno, err_str, buff_size); 32 const int success = strerror_r(errno, err_str, buff_size);
33 if (success != 0) {
34 return {};
35 }
36 return std::string(err_str);
28#endif 37#endif
29
30 return std::string(err_str, buff_size);
31} 38}
diff --git a/src/common/stream.h b/src/common/stream.h
index 2585c16af..0e40692de 100644
--- a/src/common/stream.h
+++ b/src/common/stream.h
@@ -21,6 +21,12 @@ public:
21 explicit Stream(); 21 explicit Stream();
22 ~Stream(); 22 ~Stream();
23 23
24 Stream(const Stream&) = delete;
25 Stream& operator=(const Stream&) = delete;
26
27 Stream(Stream&&) = default;
28 Stream& operator=(Stream&&) = default;
29
24 /// Reposition bitstream "cursor" to the specified offset from origin 30 /// Reposition bitstream "cursor" to the specified offset from origin
25 void Seek(s32 offset, SeekOrigin origin); 31 void Seek(s32 offset, SeekOrigin origin);
26 32
@@ -30,15 +36,15 @@ public:
30 /// Writes byte at current position 36 /// Writes byte at current position
31 void WriteByte(u8 byte); 37 void WriteByte(u8 byte);
32 38
33 std::size_t GetPosition() const { 39 [[nodiscard]] std::size_t GetPosition() const {
34 return position; 40 return position;
35 } 41 }
36 42
37 std::vector<u8>& GetBuffer() { 43 [[nodiscard]] std::vector<u8>& GetBuffer() {
38 return buffer; 44 return buffer;
39 } 45 }
40 46
41 const std::vector<u8>& GetBuffer() const { 47 [[nodiscard]] const std::vector<u8>& GetBuffer() const {
42 return buffer; 48 return buffer;
43 } 49 }
44 50
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp
index 84883a1d3..4cba2aaa4 100644
--- a/src/common/string_util.cpp
+++ b/src/common/string_util.cpp
@@ -8,6 +8,7 @@
8#include <cstdlib> 8#include <cstdlib>
9#include <locale> 9#include <locale>
10#include <sstream> 10#include <sstream>
11
11#include "common/common_paths.h" 12#include "common/common_paths.h"
12#include "common/logging/log.h" 13#include "common/logging/log.h"
13#include "common/string_util.h" 14#include "common/string_util.h"
@@ -21,14 +22,14 @@ namespace Common {
21/// Make a string lowercase 22/// Make a string lowercase
22std::string ToLower(std::string str) { 23std::string ToLower(std::string str) {
23 std::transform(str.begin(), str.end(), str.begin(), 24 std::transform(str.begin(), str.end(), str.begin(),
24 [](unsigned char c) { return std::tolower(c); }); 25 [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
25 return str; 26 return str;
26} 27}
27 28
28/// Make a string uppercase 29/// Make a string uppercase
29std::string ToUpper(std::string str) { 30std::string ToUpper(std::string str) {
30 std::transform(str.begin(), str.end(), str.begin(), 31 std::transform(str.begin(), str.end(), str.begin(),
31 [](unsigned char c) { return std::toupper(c); }); 32 [](unsigned char c) { return static_cast<char>(std::toupper(c)); });
32 return str; 33 return str;
33} 34}
34 35
diff --git a/src/common/timer.cpp b/src/common/timer.cpp
index 2dc15e434..d17dc2a50 100644
--- a/src/common/timer.cpp
+++ b/src/common/timer.cpp
@@ -142,20 +142,18 @@ std::string Timer::GetTimeFormatted() {
142// ---------------- 142// ----------------
143double Timer::GetDoubleTime() { 143double Timer::GetDoubleTime() {
144 // Get continuous timestamp 144 // Get continuous timestamp
145 u64 TmpSeconds = static_cast<u64>(Common::Timer::GetTimeSinceJan1970().count()); 145 auto tmp_seconds = static_cast<u64>(GetTimeSinceJan1970().count());
146 double ms = static_cast<u64>(GetTimeMs().count()) % 1000; 146 const auto ms = static_cast<double>(static_cast<u64>(GetTimeMs().count()) % 1000);
147 147
148 // Remove a few years. We only really want enough seconds to make 148 // Remove a few years. We only really want enough seconds to make
149 // sure that we are detecting actual actions, perhaps 60 seconds is 149 // sure that we are detecting actual actions, perhaps 60 seconds is
150 // enough really, but I leave a year of seconds anyway, in case the 150 // enough really, but I leave a year of seconds anyway, in case the
151 // user's clock is incorrect or something like that. 151 // user's clock is incorrect or something like that.
152 TmpSeconds = TmpSeconds - (38 * 365 * 24 * 60 * 60); 152 tmp_seconds = tmp_seconds - (38 * 365 * 24 * 60 * 60);
153 153
154 // Make a smaller integer that fits in the double 154 // Make a smaller integer that fits in the double
155 u32 Seconds = static_cast<u32>(TmpSeconds); 155 const auto seconds = static_cast<u32>(tmp_seconds);
156 double TmpTime = Seconds + ms; 156 return seconds + ms;
157
158 return TmpTime;
159} 157}
160 158
161} // Namespace Common 159} // Namespace Common
diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp
index 7a20e95b7..452a2837e 100644
--- a/src/common/wall_clock.cpp
+++ b/src/common/wall_clock.cpp
@@ -53,7 +53,7 @@ public:
53 return Common::Divide128On32(temporary, 1000000000).first; 53 return Common::Divide128On32(temporary, 1000000000).first;
54 } 54 }
55 55
56 void Pause(bool is_paused) override { 56 void Pause([[maybe_unused]] bool is_paused) override {
57 // Do nothing in this clock type. 57 // Do nothing in this clock type.
58 } 58 }
59 59
diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h
index 7c503df26..97aab6ac9 100644
--- a/src/common/x64/native_clock.h
+++ b/src/common/x64/native_clock.h
@@ -34,7 +34,7 @@ private:
34 /// value used to reduce the native clocks accuracy as some apss rely on 34 /// value used to reduce the native clocks accuracy as some apss rely on
35 /// undefined behavior where the level of accuracy in the clock shouldn't 35 /// undefined behavior where the level of accuracy in the clock shouldn't
36 /// be higher. 36 /// be higher.
37 static constexpr u64 inaccuracy_mask = ~(0x400 - 1); 37 static constexpr u64 inaccuracy_mask = ~(UINT64_C(0x400) - 1);
38 38
39 SpinLock rtsc_serialize{}; 39 SpinLock rtsc_serialize{};
40 u64 last_measure{}; 40 u64 last_measure{};
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index e0f207f3e..9a983e81d 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -454,6 +454,8 @@ add_library(core STATIC
454 hle/service/nvdrv/nvdrv.h 454 hle/service/nvdrv/nvdrv.h
455 hle/service/nvdrv/nvmemp.cpp 455 hle/service/nvdrv/nvmemp.cpp
456 hle/service/nvdrv/nvmemp.h 456 hle/service/nvdrv/nvmemp.h
457 hle/service/nvdrv/syncpoint_manager.cpp
458 hle/service/nvdrv/syncpoint_manager.h
457 hle/service/nvflinger/buffer_queue.cpp 459 hle/service/nvflinger/buffer_queue.cpp
458 hle/service/nvflinger/buffer_queue.h 460 hle/service/nvflinger/buffer_queue.h
459 hle/service/nvflinger/nvflinger.cpp 461 hle/service/nvflinger/nvflinger.cpp
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index d2295ed90..0951e1976 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -147,10 +147,18 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContex
147 auto fp = ctx.cpu_registers[29]; 147 auto fp = ctx.cpu_registers[29];
148 auto lr = ctx.cpu_registers[30]; 148 auto lr = ctx.cpu_registers[30];
149 while (true) { 149 while (true) {
150 out.push_back({"", 0, lr, 0}); 150 out.push_back({
151 if (!fp) { 151 .module = "",
152 .address = 0,
153 .original_address = lr,
154 .offset = 0,
155 .name = {},
156 });
157
158 if (fp == 0) {
152 break; 159 break;
153 } 160 }
161
154 lr = memory.Read64(fp + 8) - 4; 162 lr = memory.Read64(fp + 8) - 4;
155 fp = memory.Read64(fp); 163 fp = memory.Read64(fp);
156 } 164 }
diff --git a/src/core/core.cpp b/src/core/core.cpp
index fde2ccc09..242796008 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -179,16 +179,18 @@ struct System::Impl {
179 arp_manager.ResetAll(); 179 arp_manager.ResetAll();
180 180
181 telemetry_session = std::make_unique<Core::TelemetrySession>(); 181 telemetry_session = std::make_unique<Core::TelemetrySession>();
182
183 gpu_core = VideoCore::CreateGPU(emu_window, system);
184 if (!gpu_core) {
185 return ResultStatus::ErrorVideoCore;
186 }
187
182 service_manager = std::make_shared<Service::SM::ServiceManager>(kernel); 188 service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
183 189
184 Service::Init(service_manager, system); 190 Service::Init(service_manager, system);
185 GDBStub::DeferStart(); 191 GDBStub::DeferStart();
186 192
187 interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system); 193 interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system);
188 gpu_core = VideoCore::CreateGPU(emu_window, system);
189 if (!gpu_core) {
190 return ResultStatus::ErrorVideoCore;
191 }
192 194
193 // Initialize time manager, which must happen after kernel is created 195 // Initialize time manager, which must happen after kernel is created
194 time_manager.Initialize(); 196 time_manager.Initialize();
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index ff9d9248b..b17529dee 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -4,6 +4,7 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <bitset> 6#include <bitset>
7#include <ctime>
7#include <memory> 8#include <memory>
8#include <random> 9#include <random>
9#include "common/alignment.h" 10#include "common/alignment.h"
@@ -123,7 +124,7 @@ std::shared_ptr<Process> Process::Create(Core::System& system, std::string name,
123 : kernel.CreateNewUserProcessID(); 124 : kernel.CreateNewUserProcessID();
124 process->capabilities.InitializeForMetadatalessProcess(); 125 process->capabilities.InitializeForMetadatalessProcess();
125 126
126 std::mt19937 rng(Settings::values.rng_seed.GetValue().value_or(0)); 127 std::mt19937 rng(Settings::values.rng_seed.GetValue().value_or(std::time(nullptr)));
127 std::uniform_int_distribution<u64> distribution; 128 std::uniform_int_distribution<u64> distribution;
128 std::generate(process->random_entropy.begin(), process->random_entropy.end(), 129 std::generate(process->random_entropy.begin(), process->random_entropy.end(),
129 [&] { return distribution(rng); }); 130 [&] { return distribution(rng); });
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index d7a81f64a..2ce742e35 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -1201,6 +1201,8 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
1201 {151, nullptr, "TryPopFromNotificationStorageChannel"}, 1201 {151, nullptr, "TryPopFromNotificationStorageChannel"},
1202 {160, nullptr, "GetHealthWarningDisappearedSystemEvent"}, 1202 {160, nullptr, "GetHealthWarningDisappearedSystemEvent"},
1203 {170, nullptr, "SetHdcpAuthenticationActivated"}, 1203 {170, nullptr, "SetHdcpAuthenticationActivated"},
1204 {180, nullptr, "GetLaunchRequiredVersion"},
1205 {181, nullptr, "UpgradeLaunchRequiredVersion"},
1204 {500, nullptr, "StartContinuousRecordingFlushForDebug"}, 1206 {500, nullptr, "StartContinuousRecordingFlushForDebug"},
1205 {1000, nullptr, "CreateMovieMaker"}, 1207 {1000, nullptr, "CreateMovieMaker"},
1206 {1001, nullptr, "PrepareForJit"}, 1208 {1001, nullptr, "PrepareForJit"},
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 8918946a1..50f709b25 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -260,7 +260,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
260 {404, nullptr, "HasLeftRightBattery"}, 260 {404, nullptr, "HasLeftRightBattery"},
261 {405, nullptr, "GetNpadInterfaceType"}, 261 {405, nullptr, "GetNpadInterfaceType"},
262 {406, nullptr, "GetNpadLeftRightInterfaceType"}, 262 {406, nullptr, "GetNpadLeftRightInterfaceType"},
263 {407, nullptr, "GetNpadOfHighestBatteryLevelForJoyLeft"}, 263 {407, nullptr, "GetNpadOfHighestBatteryLevel"},
264 {408, nullptr, "GetNpadOfHighestBatteryLevelForJoyRight"}, 264 {408, nullptr, "GetNpadOfHighestBatteryLevelForJoyRight"},
265 {500, nullptr, "GetPalmaConnectionHandle"}, 265 {500, nullptr, "GetPalmaConnectionHandle"},
266 {501, nullptr, "InitializePalma"}, 266 {501, nullptr, "InitializePalma"},
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 9ad5bbf0d..eeaca44b6 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -166,7 +166,7 @@ public:
166 {0, &RelocatableObject::LoadNro, "LoadNro"}, 166 {0, &RelocatableObject::LoadNro, "LoadNro"},
167 {1, &RelocatableObject::UnloadNro, "UnloadNro"}, 167 {1, &RelocatableObject::UnloadNro, "UnloadNro"},
168 {2, &RelocatableObject::LoadNrr, "LoadNrr"}, 168 {2, &RelocatableObject::LoadNrr, "LoadNrr"},
169 {3, nullptr, "UnloadNrr"}, 169 {3, &RelocatableObject::UnloadNrr, "UnloadNrr"},
170 {4, &RelocatableObject::Initialize, "Initialize"}, 170 {4, &RelocatableObject::Initialize, "Initialize"},
171 {10, nullptr, "LoadNrrEx"}, 171 {10, nullptr, "LoadNrrEx"},
172 }; 172 };
@@ -272,6 +272,20 @@ public:
272 rb.Push(RESULT_SUCCESS); 272 rb.Push(RESULT_SUCCESS);
273 } 273 }
274 274
275 void UnloadNrr(Kernel::HLERequestContext& ctx) {
276 IPC::RequestParser rp{ctx};
277 const auto pid = rp.Pop<u64>();
278 const auto nrr_address = rp.Pop<VAddr>();
279
280 LOG_DEBUG(Service_LDR, "called with pid={}, nrr_address={:016X}", pid, nrr_address);
281
282 nrr.erase(nrr_address);
283
284 IPC::ResponseBuilder rb{ctx, 2};
285
286 rb.Push(RESULT_SUCCESS);
287 }
288
275 bool ValidateRegionForMap(Kernel::Memory::PageTable& page_table, VAddr start, 289 bool ValidateRegionForMap(Kernel::Memory::PageTable& page_table, VAddr start,
276 std::size_t size) const { 290 std::size_t size) const {
277 constexpr std::size_t padding_size{4 * Kernel::Memory::PageSize}; 291 constexpr std::size_t padding_size{4 * Kernel::Memory::PageSize};
diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp
index b81bf6277..d7080b715 100644
--- a/src/core/hle/service/mii/mii.cpp
+++ b/src/core/hle/service/mii/mii.cpp
@@ -47,6 +47,7 @@ public:
47 {23, nullptr, "Convert"}, 47 {23, nullptr, "Convert"},
48 {24, nullptr, "ConvertCoreDataToCharInfo"}, 48 {24, nullptr, "ConvertCoreDataToCharInfo"},
49 {25, nullptr, "ConvertCharInfoToCoreData"}, 49 {25, nullptr, "ConvertCharInfoToCoreData"},
50 {26, nullptr, "Append"},
50 }; 51 };
51 // clang-format on 52 // clang-format on
52 53
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index b27ee0502..8356a8139 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -15,8 +15,9 @@
15 15
16namespace Service::Nvidia::Devices { 16namespace Service::Nvidia::Devices {
17 17
18nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface) 18nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface,
19 : nvdevice(system), events_interface{events_interface} {} 19 SyncpointManager& syncpoint_manager)
20 : nvdevice(system), events_interface{events_interface}, syncpoint_manager{syncpoint_manager} {}
20nvhost_ctrl::~nvhost_ctrl() = default; 21nvhost_ctrl::~nvhost_ctrl() = default;
21 22
22u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 23u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
@@ -36,8 +37,8 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, const std::v
36 return IocCtrlEventRegister(input, output); 37 return IocCtrlEventRegister(input, output);
37 case IoctlCommand::IocCtrlEventUnregisterCommand: 38 case IoctlCommand::IocCtrlEventUnregisterCommand:
38 return IocCtrlEventUnregister(input, output); 39 return IocCtrlEventUnregister(input, output);
39 case IoctlCommand::IocCtrlEventSignalCommand: 40 case IoctlCommand::IocCtrlClearEventWaitCommand:
40 return IocCtrlEventSignal(input, output); 41 return IocCtrlClearEventWait(input, output);
41 default: 42 default:
42 UNIMPLEMENTED_MSG("Unimplemented ioctl"); 43 UNIMPLEMENTED_MSG("Unimplemented ioctl");
43 return 0; 44 return 0;
@@ -70,19 +71,33 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
70 return NvResult::BadParameter; 71 return NvResult::BadParameter;
71 } 72 }
72 73
74 if (syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) {
75 params.value = syncpoint_manager.GetSyncpointMin(params.syncpt_id);
76 std::memcpy(output.data(), &params, sizeof(params));
77 return NvResult::Success;
78 }
79
80 if (const auto new_value = syncpoint_manager.RefreshSyncpoint(params.syncpt_id);
81 syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) {
82 params.value = new_value;
83 std::memcpy(output.data(), &params, sizeof(params));
84 return NvResult::Success;
85 }
86
73 auto event = events_interface.events[event_id]; 87 auto event = events_interface.events[event_id];
74 auto& gpu = system.GPU(); 88 auto& gpu = system.GPU();
89
75 // This is mostly to take into account unimplemented features. As synced 90 // This is mostly to take into account unimplemented features. As synced
76 // gpu is always synced. 91 // gpu is always synced.
77 if (!gpu.IsAsync()) { 92 if (!gpu.IsAsync()) {
78 event.writable->Signal(); 93 event.event.writable->Signal();
79 return NvResult::Success; 94 return NvResult::Success;
80 } 95 }
81 auto lock = gpu.LockSync(); 96 auto lock = gpu.LockSync();
82 const u32 current_syncpoint_value = gpu.GetSyncpointValue(params.syncpt_id); 97 const u32 current_syncpoint_value = event.fence.value;
83 const s32 diff = current_syncpoint_value - params.threshold; 98 const s32 diff = current_syncpoint_value - params.threshold;
84 if (diff >= 0) { 99 if (diff >= 0) {
85 event.writable->Signal(); 100 event.event.writable->Signal();
86 params.value = current_syncpoint_value; 101 params.value = current_syncpoint_value;
87 std::memcpy(output.data(), &params, sizeof(params)); 102 std::memcpy(output.data(), &params, sizeof(params));
88 return NvResult::Success; 103 return NvResult::Success;
@@ -109,7 +124,7 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
109 params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000; 124 params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000;
110 } 125 }
111 params.value |= event_id; 126 params.value |= event_id;
112 event.writable->Clear(); 127 event.event.writable->Clear();
113 gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value); 128 gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value);
114 if (!is_async && ctrl.fresh_call) { 129 if (!is_async && ctrl.fresh_call) {
115 ctrl.must_delay = true; 130 ctrl.must_delay = true;
@@ -154,24 +169,22 @@ u32 nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::vecto
154 return NvResult::Success; 169 return NvResult::Success;
155} 170}
156 171
157u32 nvhost_ctrl::IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8>& output) { 172u32 nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) {
158 IocCtrlEventSignalParams params{}; 173 IocCtrlEventSignalParams params{};
159 std::memcpy(&params, input.data(), sizeof(params)); 174 std::memcpy(&params, input.data(), sizeof(params));
160 // TODO(Blinkhawk): This is normally called when an NvEvents timeout on WaitSynchronization 175
161 // It is believed from RE to cancel the GPU Event. However, better research is required 176 u32 event_id = params.event_id & 0x00FF;
162 u32 event_id = params.user_event_id & 0x00FF; 177 LOG_WARNING(Service_NVDRV, "cleared event wait on, event_id: {:X}", event_id);
163 LOG_WARNING(Service_NVDRV, "(STUBBED) called, user_event_id: {:X}", event_id); 178
164 if (event_id >= MaxNvEvents) { 179 if (event_id >= MaxNvEvents) {
165 return NvResult::BadParameter; 180 return NvResult::BadParameter;
166 } 181 }
167 if (events_interface.status[event_id] == EventState::Waiting) { 182 if (events_interface.status[event_id] == EventState::Waiting) {
168 auto& gpu = system.GPU(); 183 events_interface.LiberateEvent(event_id);
169 if (gpu.CancelSyncptInterrupt(events_interface.assigned_syncpt[event_id],
170 events_interface.assigned_value[event_id])) {
171 events_interface.LiberateEvent(event_id);
172 events_interface.events[event_id].writable->Signal();
173 }
174 } 184 }
185
186 syncpoint_manager.RefreshSyncpoint(events_interface.events[event_id].fence.id);
187
175 return NvResult::Success; 188 return NvResult::Success;
176} 189}
177 190
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index 9898623de..24ad96cb9 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -14,7 +14,8 @@ namespace Service::Nvidia::Devices {
14 14
15class nvhost_ctrl final : public nvdevice { 15class nvhost_ctrl final : public nvdevice {
16public: 16public:
17 explicit nvhost_ctrl(Core::System& system, EventInterface& events_interface); 17 explicit nvhost_ctrl(Core::System& system, EventInterface& events_interface,
18 SyncpointManager& syncpoint_manager);
18 ~nvhost_ctrl() override; 19 ~nvhost_ctrl() override;
19 20
20 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 21 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
@@ -31,7 +32,7 @@ private:
31 IocSyncptWaitexCommand = 0xC0100019, 32 IocSyncptWaitexCommand = 0xC0100019,
32 IocSyncptReadMaxCommand = 0xC008001A, 33 IocSyncptReadMaxCommand = 0xC008001A,
33 IocGetConfigCommand = 0xC183001B, 34 IocGetConfigCommand = 0xC183001B,
34 IocCtrlEventSignalCommand = 0xC004001C, 35 IocCtrlClearEventWaitCommand = 0xC004001C,
35 IocCtrlEventWaitCommand = 0xC010001D, 36 IocCtrlEventWaitCommand = 0xC010001D,
36 IocCtrlEventWaitAsyncCommand = 0xC010001E, 37 IocCtrlEventWaitAsyncCommand = 0xC010001E,
37 IocCtrlEventRegisterCommand = 0xC004001F, 38 IocCtrlEventRegisterCommand = 0xC004001F,
@@ -94,7 +95,7 @@ private:
94 static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size"); 95 static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size");
95 96
96 struct IocCtrlEventSignalParams { 97 struct IocCtrlEventSignalParams {
97 u32_le user_event_id; 98 u32_le event_id;
98 }; 99 };
99 static_assert(sizeof(IocCtrlEventSignalParams) == 4, 100 static_assert(sizeof(IocCtrlEventSignalParams) == 4,
100 "IocCtrlEventSignalParams is incorrect size"); 101 "IocCtrlEventSignalParams is incorrect size");
@@ -142,9 +143,10 @@ private:
142 143
143 u32 IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output); 144 u32 IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
144 145
145 u32 IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8>& output); 146 u32 IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output);
146 147
147 EventInterface& events_interface; 148 EventInterface& events_interface;
149 SyncpointManager& syncpoint_manager;
148}; 150};
149 151
150} // namespace Service::Nvidia::Devices 152} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index f1966ac0e..152019548 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -7,14 +7,20 @@
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/hle/service/nvdrv/devices/nvhost_gpu.h" 9#include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
10#include "core/hle/service/nvdrv/syncpoint_manager.h"
10#include "core/memory.h" 11#include "core/memory.h"
11#include "video_core/gpu.h" 12#include "video_core/gpu.h"
12#include "video_core/memory_manager.h" 13#include "video_core/memory_manager.h"
13 14
14namespace Service::Nvidia::Devices { 15namespace Service::Nvidia::Devices {
15 16
16nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) 17nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
17 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} 18 SyncpointManager& syncpoint_manager)
19 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)), syncpoint_manager{syncpoint_manager} {
20 channel_fence.id = syncpoint_manager.AllocateSyncpoint();
21 channel_fence.value = system.GPU().GetSyncpointValue(channel_fence.id);
22}
23
18nvhost_gpu::~nvhost_gpu() = default; 24nvhost_gpu::~nvhost_gpu() = default;
19 25
20u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 26u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
@@ -126,10 +132,10 @@ u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& ou
126 params.num_entries, params.flags, params.unk0, params.unk1, params.unk2, 132 params.num_entries, params.flags, params.unk0, params.unk1, params.unk2,
127 params.unk3); 133 params.unk3);
128 134
129 auto& gpu = system.GPU(); 135 channel_fence.value = system.GPU().GetSyncpointValue(channel_fence.id);
130 params.fence_out.id = assigned_syncpoints; 136
131 params.fence_out.value = gpu.GetSyncpointValue(assigned_syncpoints); 137 params.fence_out = channel_fence;
132 assigned_syncpoints++; 138
133 std::memcpy(output.data(), &params, output.size()); 139 std::memcpy(output.data(), &params, output.size());
134 return 0; 140 return 0;
135} 141}
@@ -145,39 +151,100 @@ u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<
145 return 0; 151 return 0;
146} 152}
147 153
148u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) { 154static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) {
149 if (input.size() < sizeof(IoctlSubmitGpfifo)) { 155 return {
150 UNIMPLEMENTED(); 156 Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1,
157 Tegra::SubmissionMode::Increasing),
158 {fence.value},
159 Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1,
160 Tegra::SubmissionMode::Increasing),
161 Tegra::GPU::FenceAction::Build(Tegra::GPU::FenceOperation::Acquire, fence.id),
162 };
163}
164
165static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(Fence fence, u32 add_increment) {
166 std::vector<Tegra::CommandHeader> result{
167 Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1,
168 Tegra::SubmissionMode::Increasing),
169 {}};
170
171 for (u32 count = 0; count < add_increment; ++count) {
172 result.emplace_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1,
173 Tegra::SubmissionMode::Increasing));
174 result.emplace_back(
175 Tegra::GPU::FenceAction::Build(Tegra::GPU::FenceOperation::Increment, fence.id));
151 } 176 }
152 IoctlSubmitGpfifo params{}; 177
153 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo)); 178 return result;
179}
180
181static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(Fence fence,
182 u32 add_increment) {
183 std::vector<Tegra::CommandHeader> result{
184 Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForInterrupt, 1,
185 Tegra::SubmissionMode::Increasing),
186 {}};
187 const std::vector<Tegra::CommandHeader> increment{
188 BuildIncrementCommandList(fence, add_increment)};
189
190 result.insert(result.end(), increment.begin(), increment.end());
191
192 return result;
193}
194
195u32 nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output,
196 Tegra::CommandList&& entries) {
154 LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address, 197 LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
155 params.num_entries, params.flags.raw); 198 params.num_entries, params.flags.raw);
156 199
157 ASSERT_MSG(input.size() == sizeof(IoctlSubmitGpfifo) + 200 auto& gpu = system.GPU();
158 params.num_entries * sizeof(Tegra::CommandListHeader),
159 "Incorrect input size");
160 201
161 Tegra::CommandList entries(params.num_entries); 202 params.fence_out.id = channel_fence.id;
162 std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)],
163 params.num_entries * sizeof(Tegra::CommandListHeader));
164 203
165 UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0); 204 if (params.flags.add_wait.Value() &&
166 UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0); 205 !syncpoint_manager.IsSyncpointExpired(params.fence_out.id, params.fence_out.value)) {
206 gpu.PushGPUEntries(Tegra::CommandList{BuildWaitCommandList(params.fence_out)});
207 }
167 208
168 auto& gpu = system.GPU(); 209 if (params.flags.add_increment.Value() || params.flags.increment.Value()) {
169 u32 current_syncpoint_value = gpu.GetSyncpointValue(params.fence_out.id); 210 const u32 increment_value = params.flags.increment.Value() ? params.fence_out.value : 0;
170 if (params.flags.increment.Value()) { 211 params.fence_out.value = syncpoint_manager.IncreaseSyncpoint(
171 params.fence_out.value += current_syncpoint_value; 212 params.fence_out.id, params.AddIncrementValue() + increment_value);
172 } else { 213 } else {
173 params.fence_out.value = current_syncpoint_value; 214 params.fence_out.value = syncpoint_manager.GetSyncpointMax(params.fence_out.id);
174 } 215 }
216
217 entries.RefreshIntegrityChecks(gpu);
175 gpu.PushGPUEntries(std::move(entries)); 218 gpu.PushGPUEntries(std::move(entries));
176 219
220 if (params.flags.add_increment.Value()) {
221 if (params.flags.suppress_wfi) {
222 gpu.PushGPUEntries(Tegra::CommandList{
223 BuildIncrementCommandList(params.fence_out, params.AddIncrementValue())});
224 } else {
225 gpu.PushGPUEntries(Tegra::CommandList{
226 BuildIncrementWithWfiCommandList(params.fence_out, params.AddIncrementValue())});
227 }
228 }
229
177 std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo)); 230 std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo));
178 return 0; 231 return 0;
179} 232}
180 233
234u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) {
235 if (input.size() < sizeof(IoctlSubmitGpfifo)) {
236 UNIMPLEMENTED();
237 }
238 IoctlSubmitGpfifo params{};
239 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
240
241 Tegra::CommandList entries(params.num_entries);
242 std::memcpy(entries.command_lists.data(), &input[sizeof(IoctlSubmitGpfifo)],
243 params.num_entries * sizeof(Tegra::CommandListHeader));
244
245 return SubmitGPFIFOImpl(params, output, std::move(entries));
246}
247
181u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output, 248u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output,
182 const std::vector<u8>& input2, IoctlVersion version) { 249 const std::vector<u8>& input2, IoctlVersion version) {
183 if (input.size() < sizeof(IoctlSubmitGpfifo)) { 250 if (input.size() < sizeof(IoctlSubmitGpfifo)) {
@@ -185,31 +252,17 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output,
185 } 252 }
186 IoctlSubmitGpfifo params{}; 253 IoctlSubmitGpfifo params{};
187 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo)); 254 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
188 LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
189 params.num_entries, params.flags.raw);
190 255
191 Tegra::CommandList entries(params.num_entries); 256 Tegra::CommandList entries(params.num_entries);
192 if (version == IoctlVersion::Version2) { 257 if (version == IoctlVersion::Version2) {
193 std::memcpy(entries.data(), input2.data(), 258 std::memcpy(entries.command_lists.data(), input2.data(),
194 params.num_entries * sizeof(Tegra::CommandListHeader)); 259 params.num_entries * sizeof(Tegra::CommandListHeader));
195 } else { 260 } else {
196 system.Memory().ReadBlock(params.address, entries.data(), 261 system.Memory().ReadBlock(params.address, entries.command_lists.data(),
197 params.num_entries * sizeof(Tegra::CommandListHeader)); 262 params.num_entries * sizeof(Tegra::CommandListHeader));
198 } 263 }
199 UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0);
200 UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0);
201
202 auto& gpu = system.GPU();
203 u32 current_syncpoint_value = gpu.GetSyncpointValue(params.fence_out.id);
204 if (params.flags.increment.Value()) {
205 params.fence_out.value += current_syncpoint_value;
206 } else {
207 params.fence_out.value = current_syncpoint_value;
208 }
209 gpu.PushGPUEntries(std::move(entries));
210 264
211 std::memcpy(output.data(), &params, output.size()); 265 return SubmitGPFIFOImpl(params, output, std::move(entries));
212 return 0;
213} 266}
214 267
215u32 nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { 268u32 nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index 2ac74743f..a252fc06d 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -11,6 +11,11 @@
11#include "common/swap.h" 11#include "common/swap.h"
12#include "core/hle/service/nvdrv/devices/nvdevice.h" 12#include "core/hle/service/nvdrv/devices/nvdevice.h"
13#include "core/hle/service/nvdrv/nvdata.h" 13#include "core/hle/service/nvdrv/nvdata.h"
14#include "video_core/dma_pusher.h"
15
16namespace Service::Nvidia {
17class SyncpointManager;
18}
14 19
15namespace Service::Nvidia::Devices { 20namespace Service::Nvidia::Devices {
16 21
@@ -21,7 +26,8 @@ constexpr u32 NVGPU_IOCTL_CHANNEL_KICKOFF_PB(0x1b);
21 26
22class nvhost_gpu final : public nvdevice { 27class nvhost_gpu final : public nvdevice {
23public: 28public:
24 explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); 29 explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
30 SyncpointManager& syncpoint_manager);
25 ~nvhost_gpu() override; 31 ~nvhost_gpu() override;
26 32
27 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 33 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
@@ -162,10 +168,15 @@ private:
162 u32_le raw; 168 u32_le raw;
163 BitField<0, 1, u32_le> add_wait; // append a wait sync_point to the list 169 BitField<0, 1, u32_le> add_wait; // append a wait sync_point to the list
164 BitField<1, 1, u32_le> add_increment; // append an increment to the list 170 BitField<1, 1, u32_le> add_increment; // append an increment to the list
165 BitField<2, 1, u32_le> new_hw_format; // Mostly ignored 171 BitField<2, 1, u32_le> new_hw_format; // mostly ignored
172 BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt
166 BitField<8, 1, u32_le> increment; // increment the returned fence 173 BitField<8, 1, u32_le> increment; // increment the returned fence
167 } flags; 174 } flags;
168 Fence fence_out; // returned new fence object for others to wait on 175 Fence fence_out; // returned new fence object for others to wait on
176
177 u32 AddIncrementValue() const {
178 return flags.add_increment.Value() << 1;
179 }
169 }; 180 };
170 static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(Fence), 181 static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(Fence),
171 "IoctlSubmitGpfifo is incorrect size"); 182 "IoctlSubmitGpfifo is incorrect size");
@@ -190,6 +201,8 @@ private:
190 u32 SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output); 201 u32 SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output);
191 u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output); 202 u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output);
192 u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output); 203 u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output);
204 u32 SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output,
205 Tegra::CommandList&& entries);
193 u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output); 206 u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output);
194 u32 KickoffPB(const std::vector<u8>& input, std::vector<u8>& output, 207 u32 KickoffPB(const std::vector<u8>& input, std::vector<u8>& output,
195 const std::vector<u8>& input2, IoctlVersion version); 208 const std::vector<u8>& input2, IoctlVersion version);
@@ -198,7 +211,8 @@ private:
198 u32 ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output); 211 u32 ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output);
199 212
200 std::shared_ptr<nvmap> nvmap_dev; 213 std::shared_ptr<nvmap> nvmap_dev;
201 u32 assigned_syncpoints{}; 214 SyncpointManager& syncpoint_manager;
215 Fence channel_fence;
202}; 216};
203 217
204} // namespace Service::Nvidia::Devices 218} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
index 85792495f..30f03f845 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
@@ -38,7 +38,7 @@ std::size_t WriteVectors(std::vector<u8>& dst, const std::vector<T>& src, std::s
38 38
39namespace NvErrCodes { 39namespace NvErrCodes {
40constexpr u32 Success{}; 40constexpr u32 Success{};
41constexpr u32 OutOfMemory{static_cast<u32>(-12)}; 41[[maybe_unused]] constexpr u32 OutOfMemory{static_cast<u32>(-12)};
42constexpr u32 InvalidInput{static_cast<u32>(-22)}; 42constexpr u32 InvalidInput{static_cast<u32>(-22)};
43} // namespace NvErrCodes 43} // namespace NvErrCodes
44 44
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 803c1a984..a46755cdc 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -21,6 +21,7 @@
21#include "core/hle/service/nvdrv/interface.h" 21#include "core/hle/service/nvdrv/interface.h"
22#include "core/hle/service/nvdrv/nvdrv.h" 22#include "core/hle/service/nvdrv/nvdrv.h"
23#include "core/hle/service/nvdrv/nvmemp.h" 23#include "core/hle/service/nvdrv/nvmemp.h"
24#include "core/hle/service/nvdrv/syncpoint_manager.h"
24#include "core/hle/service/nvflinger/nvflinger.h" 25#include "core/hle/service/nvflinger/nvflinger.h"
25 26
26namespace Service::Nvidia { 27namespace Service::Nvidia {
@@ -36,21 +37,23 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger
36 nvflinger.SetNVDrvInstance(module_); 37 nvflinger.SetNVDrvInstance(module_);
37} 38}
38 39
39Module::Module(Core::System& system) { 40Module::Module(Core::System& system) : syncpoint_manager{system.GPU()} {
40 auto& kernel = system.Kernel(); 41 auto& kernel = system.Kernel();
41 for (u32 i = 0; i < MaxNvEvents; i++) { 42 for (u32 i = 0; i < MaxNvEvents; i++) {
42 std::string event_label = fmt::format("NVDRV::NvEvent_{}", i); 43 std::string event_label = fmt::format("NVDRV::NvEvent_{}", i);
43 events_interface.events[i] = Kernel::WritableEvent::CreateEventPair(kernel, event_label); 44 events_interface.events[i] = {Kernel::WritableEvent::CreateEventPair(kernel, event_label)};
44 events_interface.status[i] = EventState::Free; 45 events_interface.status[i] = EventState::Free;
45 events_interface.registered[i] = false; 46 events_interface.registered[i] = false;
46 } 47 }
47 auto nvmap_dev = std::make_shared<Devices::nvmap>(system); 48 auto nvmap_dev = std::make_shared<Devices::nvmap>(system);
48 devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(system, nvmap_dev); 49 devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(system, nvmap_dev);
49 devices["/dev/nvhost-gpu"] = std::make_shared<Devices::nvhost_gpu>(system, nvmap_dev); 50 devices["/dev/nvhost-gpu"] =
51 std::make_shared<Devices::nvhost_gpu>(system, nvmap_dev, syncpoint_manager);
50 devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>(system); 52 devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>(system);
51 devices["/dev/nvmap"] = nvmap_dev; 53 devices["/dev/nvmap"] = nvmap_dev;
52 devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(system, nvmap_dev); 54 devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(system, nvmap_dev);
53 devices["/dev/nvhost-ctrl"] = std::make_shared<Devices::nvhost_ctrl>(system, events_interface); 55 devices["/dev/nvhost-ctrl"] =
56 std::make_shared<Devices::nvhost_ctrl>(system, events_interface, syncpoint_manager);
54 devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>(system, nvmap_dev); 57 devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>(system, nvmap_dev);
55 devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(system); 58 devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(system);
56 devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(system, nvmap_dev); 59 devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(system, nvmap_dev);
@@ -95,17 +98,17 @@ void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) {
95 if (events_interface.assigned_syncpt[i] == syncpoint_id && 98 if (events_interface.assigned_syncpt[i] == syncpoint_id &&
96 events_interface.assigned_value[i] == value) { 99 events_interface.assigned_value[i] == value) {
97 events_interface.LiberateEvent(i); 100 events_interface.LiberateEvent(i);
98 events_interface.events[i].writable->Signal(); 101 events_interface.events[i].event.writable->Signal();
99 } 102 }
100 } 103 }
101} 104}
102 105
103std::shared_ptr<Kernel::ReadableEvent> Module::GetEvent(const u32 event_id) const { 106std::shared_ptr<Kernel::ReadableEvent> Module::GetEvent(const u32 event_id) const {
104 return events_interface.events[event_id].readable; 107 return events_interface.events[event_id].event.readable;
105} 108}
106 109
107std::shared_ptr<Kernel::WritableEvent> Module::GetEventWriteable(const u32 event_id) const { 110std::shared_ptr<Kernel::WritableEvent> Module::GetEventWriteable(const u32 event_id) const {
108 return events_interface.events[event_id].writable; 111 return events_interface.events[event_id].event.writable;
109} 112}
110 113
111} // namespace Service::Nvidia 114} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index 7706a5590..f3d863dac 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -10,6 +10,7 @@
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/hle/kernel/writable_event.h" 11#include "core/hle/kernel/writable_event.h"
12#include "core/hle/service/nvdrv/nvdata.h" 12#include "core/hle/service/nvdrv/nvdata.h"
13#include "core/hle/service/nvdrv/syncpoint_manager.h"
13#include "core/hle/service/service.h" 14#include "core/hle/service/service.h"
14 15
15namespace Core { 16namespace Core {
@@ -22,15 +23,23 @@ class NVFlinger;
22 23
23namespace Service::Nvidia { 24namespace Service::Nvidia {
24 25
26class SyncpointManager;
27
25namespace Devices { 28namespace Devices {
26class nvdevice; 29class nvdevice;
27} 30}
28 31
32/// Represents an Nvidia event
33struct NvEvent {
34 Kernel::EventPair event;
35 Fence fence{};
36};
37
29struct EventInterface { 38struct EventInterface {
30 // Mask representing currently busy events 39 // Mask representing currently busy events
31 u64 events_mask{}; 40 u64 events_mask{};
32 // Each kernel event associated to an NV event 41 // Each kernel event associated to an NV event
33 std::array<Kernel::EventPair, MaxNvEvents> events; 42 std::array<NvEvent, MaxNvEvents> events;
34 // The status of the current NVEvent 43 // The status of the current NVEvent
35 std::array<EventState, MaxNvEvents> status{}; 44 std::array<EventState, MaxNvEvents> status{};
36 // Tells if an NVEvent is registered or not 45 // Tells if an NVEvent is registered or not
@@ -119,6 +128,9 @@ public:
119 std::shared_ptr<Kernel::WritableEvent> GetEventWriteable(u32 event_id) const; 128 std::shared_ptr<Kernel::WritableEvent> GetEventWriteable(u32 event_id) const;
120 129
121private: 130private:
131 /// Manages syncpoints on the host
132 SyncpointManager syncpoint_manager;
133
122 /// Id to use for the next open file descriptor. 134 /// Id to use for the next open file descriptor.
123 u32 next_fd = 1; 135 u32 next_fd = 1;
124 136
diff --git a/src/core/hle/service/nvdrv/syncpoint_manager.cpp b/src/core/hle/service/nvdrv/syncpoint_manager.cpp
new file mode 100644
index 000000000..0151a03b7
--- /dev/null
+++ b/src/core/hle/service/nvdrv/syncpoint_manager.cpp
@@ -0,0 +1,39 @@
1// Copyright 2020 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "core/hle/service/nvdrv/syncpoint_manager.h"
7#include "video_core/gpu.h"
8
9namespace Service::Nvidia {
10
11SyncpointManager::SyncpointManager(Tegra::GPU& gpu) : gpu{gpu} {}
12
13SyncpointManager::~SyncpointManager() = default;
14
15u32 SyncpointManager::RefreshSyncpoint(u32 syncpoint_id) {
16 syncpoints[syncpoint_id].min = gpu.GetSyncpointValue(syncpoint_id);
17 return GetSyncpointMin(syncpoint_id);
18}
19
20u32 SyncpointManager::AllocateSyncpoint() {
21 for (u32 syncpoint_id = 1; syncpoint_id < MaxSyncPoints; syncpoint_id++) {
22 if (!syncpoints[syncpoint_id].is_allocated) {
23 syncpoints[syncpoint_id].is_allocated = true;
24 return syncpoint_id;
25 }
26 }
27 UNREACHABLE_MSG("No more available syncpoints!");
28 return {};
29}
30
31u32 SyncpointManager::IncreaseSyncpoint(u32 syncpoint_id, u32 value) {
32 for (u32 index = 0; index < value; ++index) {
33 syncpoints[syncpoint_id].max.fetch_add(1, std::memory_order_relaxed);
34 }
35
36 return GetSyncpointMax(syncpoint_id);
37}
38
39} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/syncpoint_manager.h b/src/core/hle/service/nvdrv/syncpoint_manager.h
new file mode 100644
index 000000000..4168b6c7e
--- /dev/null
+++ b/src/core/hle/service/nvdrv/syncpoint_manager.h
@@ -0,0 +1,85 @@
1// Copyright 2020 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <atomic>
9
10#include "common/common_types.h"
11#include "core/hle/service/nvdrv/nvdata.h"
12
13namespace Tegra {
14class GPU;
15}
16
17namespace Service::Nvidia {
18
19class SyncpointManager final {
20public:
21 explicit SyncpointManager(Tegra::GPU& gpu);
22 ~SyncpointManager();
23
24 /**
25 * Returns true if the specified syncpoint is expired for the given value.
26 * @param syncpoint_id Syncpoint ID to check.
27 * @param value Value to check against the specified syncpoint.
28 * @returns True if the specified syncpoint is expired for the given value, otherwise False.
29 */
30 bool IsSyncpointExpired(u32 syncpoint_id, u32 value) const {
31 return (GetSyncpointMax(syncpoint_id) - value) >= (GetSyncpointMin(syncpoint_id) - value);
32 }
33
34 /**
35 * Gets the lower bound for the specified syncpoint.
36 * @param syncpoint_id Syncpoint ID to get the lower bound for.
37 * @returns The lower bound for the specified syncpoint.
38 */
39 u32 GetSyncpointMin(u32 syncpoint_id) const {
40 return syncpoints[syncpoint_id].min.load(std::memory_order_relaxed);
41 }
42
43 /**
44 * Gets the uper bound for the specified syncpoint.
45 * @param syncpoint_id Syncpoint ID to get the upper bound for.
46 * @returns The upper bound for the specified syncpoint.
47 */
48 u32 GetSyncpointMax(u32 syncpoint_id) const {
49 return syncpoints[syncpoint_id].max.load(std::memory_order_relaxed);
50 }
51
52 /**
53 * Refreshes the minimum value for the specified syncpoint.
54 * @param syncpoint_id Syncpoint ID to be refreshed.
55 * @returns The new syncpoint minimum value.
56 */
57 u32 RefreshSyncpoint(u32 syncpoint_id);
58
59 /**
60 * Allocates a new syncoint.
61 * @returns The syncpoint ID for the newly allocated syncpoint.
62 */
63 u32 AllocateSyncpoint();
64
65 /**
66 * Increases the maximum value for the specified syncpoint.
67 * @param syncpoint_id Syncpoint ID to be increased.
68 * @param value Value to increase the specified syncpoint by.
69 * @returns The new syncpoint maximum value.
70 */
71 u32 IncreaseSyncpoint(u32 syncpoint_id, u32 value);
72
73private:
74 struct Syncpoint {
75 std::atomic<u32> min;
76 std::atomic<u32> max;
77 std::atomic<bool> is_allocated;
78 };
79
80 std::array<Syncpoint, MaxSyncPoints> syncpoints{};
81
82 Tegra::GPU& gpu;
83};
84
85} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index 4f1e210b1..b89a2d41b 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -29,6 +29,10 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
29 .slot = slot, 29 .slot = slot,
30 .status = Buffer::Status::Free, 30 .status = Buffer::Status::Free,
31 .igbp_buffer = igbp_buffer, 31 .igbp_buffer = igbp_buffer,
32 .transform = {},
33 .crop_rect = {},
34 .swap_interval = 0,
35 .multi_fence = {},
32 }); 36 });
33 37
34 buffer_wait_event.writable->Signal(); 38 buffer_wait_event.writable->Signal();
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index c64673dba..44aa2bdae 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -242,6 +242,10 @@ void NVFlinger::Compose() {
242 242
243 const auto& igbp_buffer = buffer->get().igbp_buffer; 243 const auto& igbp_buffer = buffer->get().igbp_buffer;
244 244
245 if (!system.IsPoweredOn()) {
246 return; // We are likely shutting down
247 }
248
245 auto& gpu = system.GPU(); 249 auto& gpu = system.GPU();
246 const auto& multi_fence = buffer->get().multi_fence; 250 const auto& multi_fence = buffer->get().multi_fence;
247 guard->unlock(); 251 guard->unlock();
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp
index e64777668..ffbf90b00 100644
--- a/src/core/hle/service/set/set.cpp
+++ b/src/core/hle/service/set/set.cpp
@@ -202,6 +202,7 @@ SET::SET() : ServiceFramework("set") {
202 {8, &SET::GetQuestFlag, "GetQuestFlag"}, 202 {8, &SET::GetQuestFlag, "GetQuestFlag"},
203 {9, &SET::GetKeyCodeMap2, "GetKeyCodeMap2"}, 203 {9, &SET::GetKeyCodeMap2, "GetKeyCodeMap2"},
204 {10, nullptr, "GetFirmwareVersionForDebug"}, 204 {10, nullptr, "GetFirmwareVersionForDebug"},
205 {11, nullptr, "GetDeviceNickName"},
205 }; 206 };
206 // clang-format on 207 // clang-format on
207 208
diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp
index 8bd4c7e79..080b5743e 100644
--- a/src/core/hle/service/set/set_sys.cpp
+++ b/src/core/hle/service/set/set_sys.cpp
@@ -300,6 +300,8 @@ SET_SYS::SET_SYS() : ServiceFramework("set:sys") {
300 {198, nullptr, "SetButtonConfigRegisteredSettingsEmbedded"}, 300 {198, nullptr, "SetButtonConfigRegisteredSettingsEmbedded"},
301 {199, nullptr, "GetButtonConfigRegisteredSettings"}, 301 {199, nullptr, "GetButtonConfigRegisteredSettings"},
302 {200, nullptr, "SetButtonConfigRegisteredSettings"}, 302 {200, nullptr, "SetButtonConfigRegisteredSettings"},
303 {201, nullptr, "GetFieldTestingFlag"},
304 {202, nullptr, "SetFieldTestingFlag"},
303 }; 305 };
304 // clang-format on 306 // clang-format on
305 307
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index e14c02045..a99d3cf5a 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -14,7 +14,7 @@
14namespace Settings { 14namespace Settings {
15 15
16Values values = {}; 16Values values = {};
17bool configuring_global = true; 17static bool configuring_global = true;
18 18
19std::string GetTimeZoneString() { 19std::string GetTimeZoneString() {
20 static constexpr std::array timezones{ 20 static constexpr std::array timezones{
@@ -81,11 +81,12 @@ void LogSettings() {
81 log_setting("Services_BCATBoxcatLocal", values.bcat_boxcat_local); 81 log_setting("Services_BCATBoxcatLocal", values.bcat_boxcat_local);
82} 82}
83 83
84float Volume() { 84bool IsConfiguringGlobal() {
85 if (values.audio_muted) { 85 return configuring_global;
86 return 0.0f; 86}
87 } 87
88 return values.volume.GetValue(); 88void SetConfiguringGlobal(bool is_global) {
89 configuring_global = is_global;
89} 90}
90 91
91bool IsGPULevelExtreme() { 92bool IsGPULevelExtreme() {
@@ -97,6 +98,13 @@ bool IsGPULevelHigh() {
97 values.gpu_accuracy.GetValue() == GPUAccuracy::High; 98 values.gpu_accuracy.GetValue() == GPUAccuracy::High;
98} 99}
99 100
101float Volume() {
102 if (values.audio_muted) {
103 return 0.0f;
104 }
105 return values.volume.GetValue();
106}
107
100void RestoreGlobalState() { 108void RestoreGlobalState() {
101 // If a game is running, DO NOT restore the global settings state 109 // If a game is running, DO NOT restore the global settings state
102 if (Core::System::GetInstance().IsPoweredOn()) { 110 if (Core::System::GetInstance().IsPoweredOn()) {
diff --git a/src/core/settings.h b/src/core/settings.h
index 604805615..dcb1dbb31 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -33,8 +33,6 @@ enum class CPUAccuracy {
33 DebugMode = 2, 33 DebugMode = 2,
34}; 34};
35 35
36extern bool configuring_global;
37
38template <typename Type> 36template <typename Type>
39class Setting final { 37class Setting final {
40public: 38public:
@@ -198,13 +196,18 @@ struct Values {
198 196
199 // Add-Ons 197 // Add-Ons
200 std::map<u64, std::vector<std::string>> disabled_addons; 198 std::map<u64, std::vector<std::string>> disabled_addons;
201} extern values; 199};
202 200
203float Volume(); 201extern Values values;
202
203bool IsConfiguringGlobal();
204void SetConfiguringGlobal(bool is_global);
204 205
205bool IsGPULevelExtreme(); 206bool IsGPULevelExtreme();
206bool IsGPULevelHigh(); 207bool IsGPULevelHigh();
207 208
209float Volume();
210
208std::string GetTimeZoneString(); 211std::string GetTimeZoneString();
209 212
210void Apply(); 213void Apply();
diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp
index c95feb0d7..b912188b6 100644
--- a/src/input_common/gcadapter/gc_adapter.cpp
+++ b/src/input_common/gcadapter/gc_adapter.cpp
@@ -21,26 +21,6 @@
21 21
22namespace GCAdapter { 22namespace GCAdapter {
23 23
24// Used to loop through and assign button in poller
25constexpr std::array<PadButton, 12> PadButtonArray{
26 PadButton::PAD_BUTTON_LEFT, PadButton::PAD_BUTTON_RIGHT, PadButton::PAD_BUTTON_DOWN,
27 PadButton::PAD_BUTTON_UP, PadButton::PAD_TRIGGER_Z, PadButton::PAD_TRIGGER_R,
28 PadButton::PAD_TRIGGER_L, PadButton::PAD_BUTTON_A, PadButton::PAD_BUTTON_B,
29 PadButton::PAD_BUTTON_X, PadButton::PAD_BUTTON_Y, PadButton::PAD_BUTTON_START,
30};
31
32static void PadToState(const GCPadStatus& pad, GCState& out_state) {
33 for (const auto& button : PadButtonArray) {
34 const auto button_key = static_cast<u16>(button);
35 const auto button_value = (pad.button & button_key) != 0;
36 out_state.buttons.insert_or_assign(static_cast<s32>(button_key), button_value);
37 }
38
39 for (std::size_t i = 0; i < pad.axis_values.size(); ++i) {
40 out_state.axes.insert_or_assign(static_cast<u32>(i), pad.axis_values[i]);
41 }
42}
43
44Adapter::Adapter() { 24Adapter::Adapter() {
45 if (usb_adapter_handle != nullptr) { 25 if (usb_adapter_handle != nullptr) {
46 return; 26 return;
@@ -49,168 +29,263 @@ Adapter::Adapter() {
49 29
50 const int init_res = libusb_init(&libusb_ctx); 30 const int init_res = libusb_init(&libusb_ctx);
51 if (init_res == LIBUSB_SUCCESS) { 31 if (init_res == LIBUSB_SUCCESS) {
52 Setup(); 32 adapter_scan_thread = std::thread(&Adapter::AdapterScanThread, this);
53 } else { 33 } else {
54 LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res); 34 LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res);
55 } 35 }
56} 36}
57 37
58GCPadStatus Adapter::GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload) { 38Adapter::~Adapter() {
59 GCPadStatus pad = {}; 39 Reset();
60 const std::size_t offset = 1 + (9 * port); 40}
41
42void Adapter::AdapterInputThread() {
43 LOG_DEBUG(Input, "GC Adapter input thread started");
44 s32 payload_size{};
45 AdapterPayload adapter_payload{};
46
47 if (adapter_scan_thread.joinable()) {
48 adapter_scan_thread.join();
49 }
50
51 while (adapter_input_thread_running) {
52 libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(),
53 static_cast<s32>(adapter_payload.size()), &payload_size, 16);
54 if (IsPayloadCorrect(adapter_payload, payload_size)) {
55 UpdateControllers(adapter_payload);
56 UpdateVibrations();
57 }
58 std::this_thread::yield();
59 }
61 60
62 adapter_controllers_status[port] = static_cast<ControllerTypes>(adapter_payload[offset] >> 4); 61 if (restart_scan_thread) {
62 adapter_scan_thread = std::thread(&Adapter::AdapterScanThread, this);
63 restart_scan_thread = false;
64 }
65}
66
67bool Adapter::IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size) {
68 if (payload_size != static_cast<s32>(adapter_payload.size()) ||
69 adapter_payload[0] != LIBUSB_DT_HID) {
70 LOG_DEBUG(Input, "Error reading payload (size: {}, type: {:02x})", payload_size,
71 adapter_payload[0]);
72 if (input_error_counter++ > 20) {
73 LOG_ERROR(Input, "GC adapter timeout, Is the adapter connected?");
74 adapter_input_thread_running = false;
75 restart_scan_thread = true;
76 }
77 return false;
78 }
79
80 input_error_counter = 0;
81 return true;
82}
83
84void Adapter::UpdateControllers(const AdapterPayload& adapter_payload) {
85 for (std::size_t port = 0; port < pads.size(); ++port) {
86 const std::size_t offset = 1 + (9 * port);
87 const auto type = static_cast<ControllerTypes>(adapter_payload[offset] >> 4);
88 UpdatePadType(port, type);
89 if (DeviceConnected(port)) {
90 const u8 b1 = adapter_payload[offset + 1];
91 const u8 b2 = adapter_payload[offset + 2];
92 UpdateStateButtons(port, b1, b2);
93 UpdateStateAxes(port, adapter_payload);
94 if (configuring) {
95 UpdateYuzuSettings(port);
96 }
97 }
98 }
99}
100
101void Adapter::UpdatePadType(std::size_t port, ControllerTypes pad_type) {
102 if (pads[port].type == pad_type) {
103 return;
104 }
105 // Device changed reset device and set new type
106 ResetDevice(port);
107 pads[port].type = pad_type;
108}
109
110void Adapter::UpdateStateButtons(std::size_t port, u8 b1, u8 b2) {
111 if (port >= pads.size()) {
112 return;
113 }
63 114
64 static constexpr std::array<PadButton, 8> b1_buttons{ 115 static constexpr std::array<PadButton, 8> b1_buttons{
65 PadButton::PAD_BUTTON_A, PadButton::PAD_BUTTON_B, PadButton::PAD_BUTTON_X, 116 PadButton::ButtonA, PadButton::ButtonB, PadButton::ButtonX, PadButton::ButtonY,
66 PadButton::PAD_BUTTON_Y, PadButton::PAD_BUTTON_LEFT, PadButton::PAD_BUTTON_RIGHT, 117 PadButton::ButtonLeft, PadButton::ButtonRight, PadButton::ButtonDown, PadButton::ButtonUp,
67 PadButton::PAD_BUTTON_DOWN, PadButton::PAD_BUTTON_UP,
68 }; 118 };
69 119
70 static constexpr std::array<PadButton, 4> b2_buttons{ 120 static constexpr std::array<PadButton, 4> b2_buttons{
71 PadButton::PAD_BUTTON_START, 121 PadButton::ButtonStart,
72 PadButton::PAD_TRIGGER_Z, 122 PadButton::TriggerZ,
73 PadButton::PAD_TRIGGER_R, 123 PadButton::TriggerR,
74 PadButton::PAD_TRIGGER_L, 124 PadButton::TriggerL,
75 }; 125 };
126 pads[port].buttons = 0;
127 for (std::size_t i = 0; i < b1_buttons.size(); ++i) {
128 if ((b1 & (1U << i)) != 0) {
129 pads[port].buttons =
130 static_cast<u16>(pads[port].buttons | static_cast<u16>(b1_buttons[i]));
131 pads[port].last_button = b1_buttons[i];
132 }
133 }
76 134
135 for (std::size_t j = 0; j < b2_buttons.size(); ++j) {
136 if ((b2 & (1U << j)) != 0) {
137 pads[port].buttons =
138 static_cast<u16>(pads[port].buttons | static_cast<u16>(b2_buttons[j]));
139 pads[port].last_button = b2_buttons[j];
140 }
141 }
142}
143
144void Adapter::UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload) {
145 if (port >= pads.size()) {
146 return;
147 }
148
149 const std::size_t offset = 1 + (9 * port);
77 static constexpr std::array<PadAxes, 6> axes{ 150 static constexpr std::array<PadAxes, 6> axes{
78 PadAxes::StickX, PadAxes::StickY, PadAxes::SubstickX, 151 PadAxes::StickX, PadAxes::StickY, PadAxes::SubstickX,
79 PadAxes::SubstickY, PadAxes::TriggerLeft, PadAxes::TriggerRight, 152 PadAxes::SubstickY, PadAxes::TriggerLeft, PadAxes::TriggerRight,
80 }; 153 };
81 154
82 if (adapter_controllers_status[port] == ControllerTypes::None && !get_origin[port]) { 155 for (const PadAxes axis : axes) {
83 // Controller may have been disconnected, recalibrate if reconnected. 156 const auto index = static_cast<std::size_t>(axis);
84 get_origin[port] = true; 157 const u8 axis_value = adapter_payload[offset + 3 + index];
158 if (pads[port].axis_origin[index] == 255) {
159 pads[port].axis_origin[index] = axis_value;
160 }
161 pads[port].axis_values[index] =
162 static_cast<s16>(axis_value - pads[port].axis_origin[index]);
85 } 163 }
164}
86 165
87 if (adapter_controllers_status[port] != ControllerTypes::None) { 166void Adapter::UpdateYuzuSettings(std::size_t port) {
88 const u8 b1 = adapter_payload[offset + 1]; 167 if (port >= pads.size()) {
89 const u8 b2 = adapter_payload[offset + 2]; 168 return;
169 }
90 170
91 for (std::size_t i = 0; i < b1_buttons.size(); ++i) { 171 constexpr u8 axis_threshold = 50;
92 if ((b1 & (1U << i)) != 0) { 172 GCPadStatus pad_status = {.port = port};
93 pad.button = static_cast<u16>(pad.button | static_cast<u16>(b1_buttons[i]));
94 }
95 }
96 173
97 for (std::size_t j = 0; j < b2_buttons.size(); ++j) { 174 if (pads[port].buttons != 0) {
98 if ((b2 & (1U << j)) != 0) { 175 pad_status.button = pads[port].last_button;
99 pad.button = static_cast<u16>(pad.button | static_cast<u16>(b2_buttons[j])); 176 pad_queue.Push(pad_status);
100 } 177 }
101 } 178
102 for (PadAxes axis : axes) { 179 // Accounting for a threshold here to ensure an intentional press
103 const auto index = static_cast<std::size_t>(axis); 180 for (std::size_t i = 0; i < pads[port].axis_values.size(); ++i) {
104 pad.axis_values[index] = adapter_payload[offset + 3 + index]; 181 const s16 value = pads[port].axis_values[i];
105 }
106 182
107 if (get_origin[port]) { 183 if (value > axis_threshold || value < -axis_threshold) {
108 origin_status[port].axis_values = pad.axis_values; 184 pad_status.axis = static_cast<PadAxes>(i);
109 get_origin[port] = false; 185 pad_status.axis_value = value;
186 pad_status.axis_threshold = axis_threshold;
187 pad_queue.Push(pad_status);
110 } 188 }
111 } 189 }
112 return pad;
113} 190}
114 191
115void Adapter::Read() { 192void Adapter::UpdateVibrations() {
116 LOG_DEBUG(Input, "GC Adapter Read() thread started"); 193 // Use 8 states to keep the switching between on/off fast enough for
194 // a human to not notice the difference between switching from on/off
195 // More states = more rumble strengths = slower update time
196 constexpr u8 vibration_states = 8;
117 197
118 int payload_size; 198 vibration_counter = (vibration_counter + 1) % vibration_states;
119 std::array<u8, 37> adapter_payload;
120 std::array<GCPadStatus, 4> pads;
121
122 while (adapter_thread_running) {
123 libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(),
124 sizeof(adapter_payload), &payload_size, 16);
125
126 if (payload_size != sizeof(adapter_payload) || adapter_payload[0] != LIBUSB_DT_HID) {
127 LOG_ERROR(Input,
128 "Error reading payload (size: {}, type: {:02x}) Is the adapter connected?",
129 payload_size, adapter_payload[0]);
130 adapter_thread_running = false; // error reading from adapter, stop reading.
131 break;
132 }
133 for (std::size_t port = 0; port < pads.size(); ++port) {
134 pads[port] = GetPadStatus(port, adapter_payload);
135 if (DeviceConnected(port) && configuring) {
136 if (pads[port].button != 0) {
137 pad_queue[port].Push(pads[port]);
138 }
139 199
140 // Accounting for a threshold here to ensure an intentional press 200 for (GCController& pad : pads) {
141 for (size_t i = 0; i < pads[port].axis_values.size(); ++i) { 201 const bool vibrate = pad.rumble_amplitude > vibration_counter;
142 const u8 value = pads[port].axis_values[i]; 202 vibration_changed |= vibrate != pad.enable_vibration;
143 const u8 origin = origin_status[port].axis_values[i]; 203 pad.enable_vibration = vibrate;
144
145 if (value > origin + pads[port].THRESHOLD ||
146 value < origin - pads[port].THRESHOLD) {
147 pads[port].axis = static_cast<PadAxes>(i);
148 pads[port].axis_value = pads[port].axis_values[i];
149 pad_queue[port].Push(pads[port]);
150 }
151 }
152 }
153 PadToState(pads[port], state[port]);
154 }
155 std::this_thread::yield();
156 } 204 }
205 SendVibrations();
157} 206}
158 207
159void Adapter::Setup() { 208void Adapter::SendVibrations() {
160 // Initialize all controllers as unplugged 209 if (!rumble_enabled || !vibration_changed) {
161 adapter_controllers_status.fill(ControllerTypes::None);
162 // Initialize all ports to store axis origin values
163 get_origin.fill(true);
164
165 // pointer to list of connected usb devices
166 libusb_device** devices{};
167
168 // populate the list of devices, get the count
169 const ssize_t device_count = libusb_get_device_list(libusb_ctx, &devices);
170 if (device_count < 0) {
171 LOG_ERROR(Input, "libusb_get_device_list failed with error: {}", device_count);
172 return; 210 return;
173 } 211 }
174 212 s32 size{};
175 if (devices != nullptr) { 213 constexpr u8 rumble_command = 0x11;
176 for (std::size_t index = 0; index < static_cast<std::size_t>(device_count); ++index) { 214 const u8 p1 = pads[0].enable_vibration;
177 if (CheckDeviceAccess(devices[index])) { 215 const u8 p2 = pads[1].enable_vibration;
178 // GC Adapter found and accessible, registering it 216 const u8 p3 = pads[2].enable_vibration;
179 GetGCEndpoint(devices[index]); 217 const u8 p4 = pads[3].enable_vibration;
180 break; 218 std::array<u8, 5> payload = {rumble_command, p1, p2, p3, p4};
181 } 219 const int err = libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, payload.data(),
220 static_cast<s32>(payload.size()), &size, 16);
221 if (err) {
222 LOG_DEBUG(Input, "Adapter libusb write failed: {}", libusb_error_name(err));
223 if (output_error_counter++ > 5) {
224 LOG_ERROR(Input, "GC adapter output timeout, Rumble disabled");
225 rumble_enabled = false;
182 } 226 }
183 libusb_free_device_list(devices, 1); 227 return;
184 } 228 }
229 output_error_counter = 0;
230 vibration_changed = false;
185} 231}
186 232
187bool Adapter::CheckDeviceAccess(libusb_device* device) { 233bool Adapter::RumblePlay(std::size_t port, f32 amplitude) {
188 libusb_device_descriptor desc; 234 amplitude = std::clamp(amplitude, 0.0f, 1.0f);
189 const int get_descriptor_error = libusb_get_device_descriptor(device, &desc); 235 const auto raw_amp = static_cast<u8>(amplitude * 0x8);
190 if (get_descriptor_error) { 236 pads[port].rumble_amplitude = raw_amp;
191 // could not acquire the descriptor, no point in trying to use it. 237
192 LOG_ERROR(Input, "libusb_get_device_descriptor failed with error: {}", 238 return rumble_enabled;
193 get_descriptor_error); 239}
194 return false; 240
241void Adapter::AdapterScanThread() {
242 adapter_scan_thread_running = true;
243 adapter_input_thread_running = false;
244 if (adapter_input_thread.joinable()) {
245 adapter_input_thread.join();
246 }
247 ClearLibusbHandle();
248 ResetDevices();
249 while (adapter_scan_thread_running && !adapter_input_thread_running) {
250 Setup();
251 std::this_thread::sleep_for(std::chrono::seconds(1));
195 } 252 }
253}
196 254
197 if (desc.idVendor != 0x057e || desc.idProduct != 0x0337) { 255void Adapter::Setup() {
198 // This isn't the device we are looking for. 256 usb_adapter_handle = libusb_open_device_with_vid_pid(libusb_ctx, 0x057e, 0x0337);
199 return false; 257
258 if (usb_adapter_handle == NULL) {
259 return;
260 }
261 if (!CheckDeviceAccess()) {
262 ClearLibusbHandle();
263 return;
200 } 264 }
201 const int open_error = libusb_open(device, &usb_adapter_handle);
202 265
203 if (open_error == LIBUSB_ERROR_ACCESS) { 266 libusb_device* device = libusb_get_device(usb_adapter_handle);
204 LOG_ERROR(Input, "Yuzu can not gain access to this device: ID {:04X}:{:04X}.", 267
205 desc.idVendor, desc.idProduct); 268 LOG_INFO(Input, "GC adapter is now connected");
206 return false; 269 // GC Adapter found and accessible, registering it
270 if (GetGCEndpoint(device)) {
271 adapter_scan_thread_running = false;
272 adapter_input_thread_running = true;
273 rumble_enabled = true;
274 input_error_counter = 0;
275 output_error_counter = 0;
276 adapter_input_thread = std::thread(&Adapter::AdapterInputThread, this);
207 } 277 }
208 if (open_error) { 278}
209 LOG_ERROR(Input, "libusb_open failed to open device with error = {}", open_error); 279
210 return false; 280bool Adapter::CheckDeviceAccess() {
281 // This fixes payload problems from offbrand GCAdapters
282 const s32 control_transfer_error =
283 libusb_control_transfer(usb_adapter_handle, 0x21, 11, 0x0001, 0, nullptr, 0, 1000);
284 if (control_transfer_error < 0) {
285 LOG_ERROR(Input, "libusb_control_transfer failed with error= {}", control_transfer_error);
211 } 286 }
212 287
213 int kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle, 0); 288 s32 kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle, 0);
214 if (kernel_driver_error == 1) { 289 if (kernel_driver_error == 1) {
215 kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle, 0); 290 kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle, 0);
216 if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) { 291 if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) {
@@ -236,13 +311,13 @@ bool Adapter::CheckDeviceAccess(libusb_device* device) {
236 return true; 311 return true;
237} 312}
238 313
239void Adapter::GetGCEndpoint(libusb_device* device) { 314bool Adapter::GetGCEndpoint(libusb_device* device) {
240 libusb_config_descriptor* config = nullptr; 315 libusb_config_descriptor* config = nullptr;
241 const int config_descriptor_return = libusb_get_config_descriptor(device, 0, &config); 316 const int config_descriptor_return = libusb_get_config_descriptor(device, 0, &config);
242 if (config_descriptor_return != LIBUSB_SUCCESS) { 317 if (config_descriptor_return != LIBUSB_SUCCESS) {
243 LOG_ERROR(Input, "libusb_get_config_descriptor failed with error = {}", 318 LOG_ERROR(Input, "libusb_get_config_descriptor failed with error = {}",
244 config_descriptor_return); 319 config_descriptor_return);
245 return; 320 return false;
246 } 321 }
247 322
248 for (u8 ic = 0; ic < config->bNumInterfaces; ic++) { 323 for (u8 ic = 0; ic < config->bNumInterfaces; ic++) {
@@ -264,31 +339,51 @@ void Adapter::GetGCEndpoint(libusb_device* device) {
264 unsigned char clear_payload = 0x13; 339 unsigned char clear_payload = 0x13;
265 libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, &clear_payload, 340 libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, &clear_payload,
266 sizeof(clear_payload), nullptr, 16); 341 sizeof(clear_payload), nullptr, 16);
267 342 return true;
268 adapter_thread_running = true;
269 adapter_input_thread = std::thread(&Adapter::Read, this);
270} 343}
271 344
272Adapter::~Adapter() { 345void Adapter::JoinThreads() {
273 Reset(); 346 restart_scan_thread = false;
274} 347 adapter_input_thread_running = false;
348 adapter_scan_thread_running = false;
275 349
276void Adapter::Reset() { 350 if (adapter_scan_thread.joinable()) {
277 if (adapter_thread_running) { 351 adapter_scan_thread.join();
278 adapter_thread_running = false;
279 } 352 }
353
280 if (adapter_input_thread.joinable()) { 354 if (adapter_input_thread.joinable()) {
281 adapter_input_thread.join(); 355 adapter_input_thread.join();
282 } 356 }
357}
283 358
284 adapter_controllers_status.fill(ControllerTypes::None); 359void Adapter::ClearLibusbHandle() {
285 get_origin.fill(true);
286
287 if (usb_adapter_handle) { 360 if (usb_adapter_handle) {
288 libusb_release_interface(usb_adapter_handle, 1); 361 libusb_release_interface(usb_adapter_handle, 1);
289 libusb_close(usb_adapter_handle); 362 libusb_close(usb_adapter_handle);
290 usb_adapter_handle = nullptr; 363 usb_adapter_handle = nullptr;
291 } 364 }
365}
366
367void Adapter::ResetDevices() {
368 for (std::size_t i = 0; i < pads.size(); ++i) {
369 ResetDevice(i);
370 }
371}
372
373void Adapter::ResetDevice(std::size_t port) {
374 pads[port].type = ControllerTypes::None;
375 pads[port].enable_vibration = false;
376 pads[port].rumble_amplitude = 0;
377 pads[port].buttons = 0;
378 pads[port].last_button = PadButton::Undefined;
379 pads[port].axis_values.fill(0);
380 pads[port].axis_origin.fill(255);
381}
382
383void Adapter::Reset() {
384 JoinThreads();
385 ClearLibusbHandle();
386 ResetDevices();
292 387
293 if (libusb_ctx) { 388 if (libusb_ctx) {
294 libusb_exit(libusb_ctx); 389 libusb_exit(libusb_ctx);
@@ -297,11 +392,11 @@ void Adapter::Reset() {
297 392
298std::vector<Common::ParamPackage> Adapter::GetInputDevices() const { 393std::vector<Common::ParamPackage> Adapter::GetInputDevices() const {
299 std::vector<Common::ParamPackage> devices; 394 std::vector<Common::ParamPackage> devices;
300 for (std::size_t port = 0; port < state.size(); ++port) { 395 for (std::size_t port = 0; port < pads.size(); ++port) {
301 if (!DeviceConnected(port)) { 396 if (!DeviceConnected(port)) {
302 continue; 397 continue;
303 } 398 }
304 std::string name = fmt::format("Gamecube Controller {}", port); 399 std::string name = fmt::format("Gamecube Controller {}", port + 1);
305 devices.emplace_back(Common::ParamPackage{ 400 devices.emplace_back(Common::ParamPackage{
306 {"class", "gcpad"}, 401 {"class", "gcpad"},
307 {"display", std::move(name)}, 402 {"display", std::move(name)},
@@ -318,18 +413,18 @@ InputCommon::ButtonMapping Adapter::GetButtonMappingForDevice(
318 // This list also excludes any button that can't be really mapped 413 // This list also excludes any button that can't be really mapped
319 static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 12> 414 static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 12>
320 switch_to_gcadapter_button = { 415 switch_to_gcadapter_button = {
321 std::pair{Settings::NativeButton::A, PadButton::PAD_BUTTON_A}, 416 std::pair{Settings::NativeButton::A, PadButton::ButtonA},
322 {Settings::NativeButton::B, PadButton::PAD_BUTTON_B}, 417 {Settings::NativeButton::B, PadButton::ButtonB},
323 {Settings::NativeButton::X, PadButton::PAD_BUTTON_X}, 418 {Settings::NativeButton::X, PadButton::ButtonX},
324 {Settings::NativeButton::Y, PadButton::PAD_BUTTON_Y}, 419 {Settings::NativeButton::Y, PadButton::ButtonY},
325 {Settings::NativeButton::Plus, PadButton::PAD_BUTTON_START}, 420 {Settings::NativeButton::Plus, PadButton::ButtonStart},
326 {Settings::NativeButton::DLeft, PadButton::PAD_BUTTON_LEFT}, 421 {Settings::NativeButton::DLeft, PadButton::ButtonLeft},
327 {Settings::NativeButton::DUp, PadButton::PAD_BUTTON_UP}, 422 {Settings::NativeButton::DUp, PadButton::ButtonUp},
328 {Settings::NativeButton::DRight, PadButton::PAD_BUTTON_RIGHT}, 423 {Settings::NativeButton::DRight, PadButton::ButtonRight},
329 {Settings::NativeButton::DDown, PadButton::PAD_BUTTON_DOWN}, 424 {Settings::NativeButton::DDown, PadButton::ButtonDown},
330 {Settings::NativeButton::SL, PadButton::PAD_TRIGGER_L}, 425 {Settings::NativeButton::SL, PadButton::TriggerL},
331 {Settings::NativeButton::SR, PadButton::PAD_TRIGGER_R}, 426 {Settings::NativeButton::SR, PadButton::TriggerR},
332 {Settings::NativeButton::R, PadButton::PAD_TRIGGER_Z}, 427 {Settings::NativeButton::R, PadButton::TriggerZ},
333 }; 428 };
334 if (!params.Has("port")) { 429 if (!params.Has("port")) {
335 return {}; 430 return {};
@@ -352,8 +447,10 @@ InputCommon::ButtonMapping Adapter::GetButtonMappingForDevice(
352 for (const auto& [switch_button, gcadapter_axis] : switch_to_gcadapter_axis) { 447 for (const auto& [switch_button, gcadapter_axis] : switch_to_gcadapter_axis) {
353 Common::ParamPackage button_params({{"engine", "gcpad"}}); 448 Common::ParamPackage button_params({{"engine", "gcpad"}});
354 button_params.Set("port", params.Get("port", 0)); 449 button_params.Set("port", params.Get("port", 0));
355 button_params.Set("button", static_cast<int>(PadButton::PAD_STICK)); 450 button_params.Set("button", static_cast<s32>(PadButton::Stick));
356 button_params.Set("axis", static_cast<int>(gcadapter_axis)); 451 button_params.Set("axis", static_cast<s32>(gcadapter_axis));
452 button_params.Set("threshold", 0.5f);
453 button_params.Set("direction", "+");
357 mapping.insert_or_assign(switch_button, std::move(button_params)); 454 mapping.insert_or_assign(switch_button, std::move(button_params));
358 } 455 }
359 return mapping; 456 return mapping;
@@ -382,46 +479,33 @@ InputCommon::AnalogMapping Adapter::GetAnalogMappingForDevice(
382} 479}
383 480
384bool Adapter::DeviceConnected(std::size_t port) const { 481bool Adapter::DeviceConnected(std::size_t port) const {
385 return adapter_controllers_status[port] != ControllerTypes::None; 482 return pads[port].type != ControllerTypes::None;
386}
387
388void Adapter::ResetDeviceType(std::size_t port) {
389 adapter_controllers_status[port] = ControllerTypes::None;
390} 483}
391 484
392void Adapter::BeginConfiguration() { 485void Adapter::BeginConfiguration() {
393 get_origin.fill(true); 486 pad_queue.Clear();
394 for (auto& pq : pad_queue) {
395 pq.Clear();
396 }
397 configuring = true; 487 configuring = true;
398} 488}
399 489
400void Adapter::EndConfiguration() { 490void Adapter::EndConfiguration() {
401 for (auto& pq : pad_queue) { 491 pad_queue.Clear();
402 pq.Clear();
403 }
404 configuring = false; 492 configuring = false;
405} 493}
406 494
407std::array<Common::SPSCQueue<GCPadStatus>, 4>& Adapter::GetPadQueue() { 495Common::SPSCQueue<GCPadStatus>& Adapter::GetPadQueue() {
408 return pad_queue; 496 return pad_queue;
409} 497}
410 498
411const std::array<Common::SPSCQueue<GCPadStatus>, 4>& Adapter::GetPadQueue() const { 499const Common::SPSCQueue<GCPadStatus>& Adapter::GetPadQueue() const {
412 return pad_queue; 500 return pad_queue;
413} 501}
414 502
415std::array<GCState, 4>& Adapter::GetPadState() { 503GCController& Adapter::GetPadState(std::size_t port) {
416 return state; 504 return pads.at(port);
417}
418
419const std::array<GCState, 4>& Adapter::GetPadState() const {
420 return state;
421} 505}
422 506
423int Adapter::GetOriginValue(u32 port, u32 axis) const { 507const GCController& Adapter::GetPadState(std::size_t port) const {
424 return origin_status[port].axis_values[axis]; 508 return pads.at(port);
425} 509}
426 510
427} // namespace GCAdapter 511} // namespace GCAdapter
diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h
index 4f5f3de8e..d28dcfad3 100644
--- a/src/input_common/gcadapter/gc_adapter.h
+++ b/src/input_common/gcadapter/gc_adapter.h
@@ -19,24 +19,23 @@ struct libusb_device_handle;
19namespace GCAdapter { 19namespace GCAdapter {
20 20
21enum class PadButton { 21enum class PadButton {
22 PAD_BUTTON_LEFT = 0x0001, 22 Undefined = 0x0000,
23 PAD_BUTTON_RIGHT = 0x0002, 23 ButtonLeft = 0x0001,
24 PAD_BUTTON_DOWN = 0x0004, 24 ButtonRight = 0x0002,
25 PAD_BUTTON_UP = 0x0008, 25 ButtonDown = 0x0004,
26 PAD_TRIGGER_Z = 0x0010, 26 ButtonUp = 0x0008,
27 PAD_TRIGGER_R = 0x0020, 27 TriggerZ = 0x0010,
28 PAD_TRIGGER_L = 0x0040, 28 TriggerR = 0x0020,
29 PAD_BUTTON_A = 0x0100, 29 TriggerL = 0x0040,
30 PAD_BUTTON_B = 0x0200, 30 ButtonA = 0x0100,
31 PAD_BUTTON_X = 0x0400, 31 ButtonB = 0x0200,
32 PAD_BUTTON_Y = 0x0800, 32 ButtonX = 0x0400,
33 PAD_BUTTON_START = 0x1000, 33 ButtonY = 0x0800,
34 ButtonStart = 0x1000,
34 // Below is for compatibility with "AxisButton" type 35 // Below is for compatibility with "AxisButton" type
35 PAD_STICK = 0x2000, 36 Stick = 0x2000,
36}; 37};
37 38
38extern const std::array<PadButton, 12> PadButtonArray;
39
40enum class PadAxes : u8 { 39enum class PadAxes : u8 {
41 StickX, 40 StickX,
42 StickY, 41 StickY,
@@ -47,87 +46,122 @@ enum class PadAxes : u8 {
47 Undefined, 46 Undefined,
48}; 47};
49 48
49enum class ControllerTypes {
50 None,
51 Wired,
52 Wireless,
53};
54
50struct GCPadStatus { 55struct GCPadStatus {
51 u16 button{}; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits 56 std::size_t port{};
52 57
53 std::array<u8, 6> axis_values{}; // Triggers and sticks, following indices defined in PadAxes 58 PadButton button{PadButton::Undefined}; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits
54 static constexpr u8 THRESHOLD = 50; // Threshold for axis press for polling
55 59
56 u8 port{};
57 PadAxes axis{PadAxes::Undefined}; 60 PadAxes axis{PadAxes::Undefined};
58 u8 axis_value{255}; 61 s16 axis_value{};
62 u8 axis_threshold{50};
59}; 63};
60 64
61struct GCState { 65struct GCController {
62 std::unordered_map<int, bool> buttons; 66 ControllerTypes type{};
63 std::unordered_map<u32, u16> axes; 67 bool enable_vibration{};
68 u8 rumble_amplitude{};
69 u16 buttons{};
70 PadButton last_button{};
71 std::array<s16, 6> axis_values{};
72 std::array<u8, 6> axis_origin{};
64}; 73};
65 74
66enum class ControllerTypes { None, Wired, Wireless };
67
68class Adapter { 75class Adapter {
69public: 76public:
70 /// Initialize the GC Adapter capture and read sequence
71 Adapter(); 77 Adapter();
72
73 /// Close the adapter read thread and release the adapter
74 ~Adapter(); 78 ~Adapter();
79
80 /// Request a vibration for a controlelr
81 bool RumblePlay(std::size_t port, f32 amplitude);
82
75 /// Used for polling 83 /// Used for polling
76 void BeginConfiguration(); 84 void BeginConfiguration();
77 void EndConfiguration(); 85 void EndConfiguration();
78 86
87 Common::SPSCQueue<GCPadStatus>& GetPadQueue();
88 const Common::SPSCQueue<GCPadStatus>& GetPadQueue() const;
89
90 GCController& GetPadState(std::size_t port);
91 const GCController& GetPadState(std::size_t port) const;
92
93 /// Returns true if there is a device connected to port
94 bool DeviceConnected(std::size_t port) const;
95
96 /// Used for automapping features
79 std::vector<Common::ParamPackage> GetInputDevices() const; 97 std::vector<Common::ParamPackage> GetInputDevices() const;
80 InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const; 98 InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const;
81 InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const; 99 InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const;
82 100
83 /// Returns true if there is a device connected to port 101private:
84 bool DeviceConnected(std::size_t port) const; 102 using AdapterPayload = std::array<u8, 37>;
85 103
86 std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue(); 104 void UpdatePadType(std::size_t port, ControllerTypes pad_type);
87 const std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue() const; 105 void UpdateControllers(const AdapterPayload& adapter_payload);
106 void UpdateYuzuSettings(std::size_t port);
107 void UpdateStateButtons(std::size_t port, u8 b1, u8 b2);
108 void UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload);
109 void UpdateVibrations();
88 110
89 std::array<GCState, 4>& GetPadState(); 111 void AdapterInputThread();
90 const std::array<GCState, 4>& GetPadState() const;
91 112
92 int GetOriginValue(u32 port, u32 axis) const; 113 void AdapterScanThread();
93 114
94private: 115 bool IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size);
95 GCPadStatus GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload); 116
117 // Updates vibration state of all controllers
118 void SendVibrations();
119
120 /// For use in initialization, querying devices to find the adapter
121 void Setup();
96 122
97 void Read(); 123 /// Resets status of all GC controller devices to a disconected state
124 void ResetDevices();
98 125
99 /// Resets status of device connected to port 126 /// Resets status of device connected to a disconected state
100 void ResetDeviceType(std::size_t port); 127 void ResetDevice(std::size_t port);
101 128
102 /// Returns true if we successfully gain access to GC Adapter 129 /// Returns true if we successfully gain access to GC Adapter
103 bool CheckDeviceAccess(libusb_device* device); 130 bool CheckDeviceAccess();
104 131
105 /// Captures GC Adapter endpoint address, 132 /// Captures GC Adapter endpoint address
106 void GetGCEndpoint(libusb_device* device); 133 /// Returns true if the endpoind was set correctly
134 bool GetGCEndpoint(libusb_device* device);
107 135
108 /// For shutting down, clear all data, join all threads, release usb 136 /// For shutting down, clear all data, join all threads, release usb
109 void Reset(); 137 void Reset();
110 138
111 /// For use in initialization, querying devices to find the adapter 139 // Join all threads
112 void Setup(); 140 void JoinThreads();
141
142 // Release usb handles
143 void ClearLibusbHandle();
113 144
114 libusb_device_handle* usb_adapter_handle = nullptr; 145 libusb_device_handle* usb_adapter_handle = nullptr;
146 std::array<GCController, 4> pads;
147 Common::SPSCQueue<GCPadStatus> pad_queue;
115 148
116 std::thread adapter_input_thread; 149 std::thread adapter_input_thread;
117 bool adapter_thread_running; 150 std::thread adapter_scan_thread;
151 bool adapter_input_thread_running;
152 bool adapter_scan_thread_running;
153 bool restart_scan_thread;
118 154
119 libusb_context* libusb_ctx; 155 libusb_context* libusb_ctx;
120 156
121 u8 input_endpoint = 0; 157 u8 input_endpoint{0};
122 u8 output_endpoint = 0; 158 u8 output_endpoint{0};
123 159 u8 input_error_counter{0};
124 bool configuring = false; 160 u8 output_error_counter{0};
161 int vibration_counter{0};
125 162
126 std::array<GCState, 4> state; 163 bool configuring{false};
127 std::array<bool, 4> get_origin; 164 bool rumble_enabled{true};
128 std::array<GCPadStatus, 4> origin_status; 165 bool vibration_changed{true};
129 std::array<Common::SPSCQueue<GCPadStatus>, 4> pad_queue;
130 std::array<ControllerTypes, 4> adapter_controllers_status{};
131}; 166};
132
133} // namespace GCAdapter 167} // namespace GCAdapter
diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp
index 893556916..6bd6f57fc 100644
--- a/src/input_common/gcadapter/gc_poller.cpp
+++ b/src/input_common/gcadapter/gc_poller.cpp
@@ -15,22 +15,30 @@ namespace InputCommon {
15 15
16class GCButton final : public Input::ButtonDevice { 16class GCButton final : public Input::ButtonDevice {
17public: 17public:
18 explicit GCButton(u32 port_, int button_, const GCAdapter::Adapter* adapter) 18 explicit GCButton(u32 port_, s32 button_, GCAdapter::Adapter* adapter)
19 : port(port_), button(button_), gcadapter(adapter) {} 19 : port(port_), button(button_), gcadapter(adapter) {}
20 20
21 ~GCButton() override; 21 ~GCButton() override;
22 22
23 bool GetStatus() const override { 23 bool GetStatus() const override {
24 if (gcadapter->DeviceConnected(port)) { 24 if (gcadapter->DeviceConnected(port)) {
25 return gcadapter->GetPadState()[port].buttons.at(button); 25 return (gcadapter->GetPadState(port).buttons & button) != 0;
26 } 26 }
27 return false; 27 return false;
28 } 28 }
29 29
30 bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const override {
31 const float amplitude = amp_high + amp_low > 2.0f ? 1.0f : (amp_high + amp_low) * 0.5f;
32 const auto new_amp =
33 static_cast<f32>(pow(amplitude, 0.5f) * (3.0f - 2.0f * pow(amplitude, 0.15f)));
34
35 return gcadapter->RumblePlay(port, new_amp);
36 }
37
30private: 38private:
31 const u32 port; 39 const u32 port;
32 const int button; 40 const s32 button;
33 const GCAdapter::Adapter* gcadapter; 41 GCAdapter::Adapter* gcadapter;
34}; 42};
35 43
36class GCAxisButton final : public Input::ButtonDevice { 44class GCAxisButton final : public Input::ButtonDevice {
@@ -38,13 +46,12 @@ public:
38 explicit GCAxisButton(u32 port_, u32 axis_, float threshold_, bool trigger_if_greater_, 46 explicit GCAxisButton(u32 port_, u32 axis_, float threshold_, bool trigger_if_greater_,
39 const GCAdapter::Adapter* adapter) 47 const GCAdapter::Adapter* adapter)
40 : port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_), 48 : port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_),
41 gcadapter(adapter), 49 gcadapter(adapter) {}
42 origin_value(static_cast<float>(adapter->GetOriginValue(port_, axis_))) {}
43 50
44 bool GetStatus() const override { 51 bool GetStatus() const override {
45 if (gcadapter->DeviceConnected(port)) { 52 if (gcadapter->DeviceConnected(port)) {
46 const float current_axis_value = gcadapter->GetPadState()[port].axes.at(axis); 53 const float current_axis_value = gcadapter->GetPadState(port).axis_values.at(axis);
47 const float axis_value = (current_axis_value - origin_value) / 128.0f; 54 const float axis_value = current_axis_value / 128.0f;
48 if (trigger_if_greater) { 55 if (trigger_if_greater) {
49 // TODO: Might be worthwile to set a slider for the trigger threshold. It is 56 // TODO: Might be worthwile to set a slider for the trigger threshold. It is
50 // currently always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick 57 // currently always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick
@@ -61,7 +68,6 @@ private:
61 float threshold; 68 float threshold;
62 bool trigger_if_greater; 69 bool trigger_if_greater;
63 const GCAdapter::Adapter* gcadapter; 70 const GCAdapter::Adapter* gcadapter;
64 const float origin_value;
65}; 71};
66 72
67GCButtonFactory::GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_) 73GCButtonFactory::GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
@@ -73,7 +79,7 @@ std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::Param
73 const auto button_id = params.Get("button", 0); 79 const auto button_id = params.Get("button", 0);
74 const auto port = static_cast<u32>(params.Get("port", 0)); 80 const auto port = static_cast<u32>(params.Get("port", 0));
75 81
76 constexpr int PAD_STICK_ID = static_cast<u16>(GCAdapter::PadButton::PAD_STICK); 82 constexpr s32 PAD_STICK_ID = static_cast<s32>(GCAdapter::PadButton::Stick);
77 83
78 // button is not an axis/stick button 84 // button is not an axis/stick button
79 if (button_id != PAD_STICK_ID) { 85 if (button_id != PAD_STICK_ID) {
@@ -106,32 +112,25 @@ Common::ParamPackage GCButtonFactory::GetNextInput() const {
106 Common::ParamPackage params; 112 Common::ParamPackage params;
107 GCAdapter::GCPadStatus pad; 113 GCAdapter::GCPadStatus pad;
108 auto& queue = adapter->GetPadQueue(); 114 auto& queue = adapter->GetPadQueue();
109 for (std::size_t port = 0; port < queue.size(); ++port) { 115 while (queue.Pop(pad)) {
110 while (queue[port].Pop(pad)) { 116 // This while loop will break on the earliest detected button
111 // This while loop will break on the earliest detected button 117 params.Set("engine", "gcpad");
112 params.Set("engine", "gcpad"); 118 params.Set("port", static_cast<s32>(pad.port));
113 params.Set("port", static_cast<int>(port)); 119 if (pad.button != GCAdapter::PadButton::Undefined) {
114 for (const auto& button : GCAdapter::PadButtonArray) { 120 params.Set("button", static_cast<u16>(pad.button));
115 const u16 button_value = static_cast<u16>(button); 121 }
116 if (pad.button & button_value) {
117 params.Set("button", button_value);
118 break;
119 }
120 }
121 122
122 // For Axis button implementation 123 // For Axis button implementation
123 if (pad.axis != GCAdapter::PadAxes::Undefined) { 124 if (pad.axis != GCAdapter::PadAxes::Undefined) {
124 params.Set("axis", static_cast<u8>(pad.axis)); 125 params.Set("axis", static_cast<u8>(pad.axis));
125 params.Set("button", static_cast<u16>(GCAdapter::PadButton::PAD_STICK)); 126 params.Set("button", static_cast<u16>(GCAdapter::PadButton::Stick));
126 if (pad.axis_value > 128) { 127 params.Set("threshold", "0.25");
127 params.Set("direction", "+"); 128 if (pad.axis_value > 0) {
128 params.Set("threshold", "0.25"); 129 params.Set("direction", "+");
129 } else { 130 } else {
130 params.Set("direction", "-"); 131 params.Set("direction", "-");
131 params.Set("threshold", "-0.25");
132 }
133 break;
134 } 132 }
133 break;
135 } 134 }
136 } 135 }
137 return params; 136 return params;
@@ -152,17 +151,14 @@ public:
152 explicit GCAnalog(u32 port_, u32 axis_x_, u32 axis_y_, float deadzone_, 151 explicit GCAnalog(u32 port_, u32 axis_x_, u32 axis_y_, float deadzone_,
153 const GCAdapter::Adapter* adapter, float range_) 152 const GCAdapter::Adapter* adapter, float range_)
154 : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter), 153 : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter),
155 origin_value_x(static_cast<float>(adapter->GetOriginValue(port_, axis_x_))),
156 origin_value_y(static_cast<float>(adapter->GetOriginValue(port_, axis_y_))),
157 range(range_) {} 154 range(range_) {}
158 155
159 float GetAxis(u32 axis) const { 156 float GetAxis(u32 axis) const {
160 if (gcadapter->DeviceConnected(port)) { 157 if (gcadapter->DeviceConnected(port)) {
161 std::lock_guard lock{mutex}; 158 std::lock_guard lock{mutex};
162 const auto origin_value = axis % 2 == 0 ? origin_value_x : origin_value_y;
163 const auto axis_value = 159 const auto axis_value =
164 static_cast<float>(gcadapter->GetPadState()[port].axes.at(axis)); 160 static_cast<float>(gcadapter->GetPadState(port).axis_values.at(axis));
165 return (axis_value - origin_value) / (100.0f * range); 161 return (axis_value) / (100.0f * range);
166 } 162 }
167 return 0.0f; 163 return 0.0f;
168 } 164 }
@@ -215,8 +211,6 @@ private:
215 const u32 axis_y; 211 const u32 axis_y;
216 const float deadzone; 212 const float deadzone;
217 const GCAdapter::Adapter* gcadapter; 213 const GCAdapter::Adapter* gcadapter;
218 const float origin_value_x;
219 const float origin_value_y;
220 const float range; 214 const float range;
221 mutable std::mutex mutex; 215 mutable std::mutex mutex;
222}; 216};
@@ -254,26 +248,44 @@ void GCAnalogFactory::EndConfiguration() {
254 248
255Common::ParamPackage GCAnalogFactory::GetNextInput() { 249Common::ParamPackage GCAnalogFactory::GetNextInput() {
256 GCAdapter::GCPadStatus pad; 250 GCAdapter::GCPadStatus pad;
251 Common::ParamPackage params;
257 auto& queue = adapter->GetPadQueue(); 252 auto& queue = adapter->GetPadQueue();
258 for (std::size_t port = 0; port < queue.size(); ++port) { 253 while (queue.Pop(pad)) {
259 while (queue[port].Pop(pad)) { 254 if (pad.button != GCAdapter::PadButton::Undefined) {
260 if (pad.axis == GCAdapter::PadAxes::Undefined || 255 params.Set("engine", "gcpad");
261 std::abs((static_cast<float>(pad.axis_value) - 128.0f) / 128.0f) < 0.1f) { 256 params.Set("port", static_cast<s32>(pad.port));
262 continue; 257 params.Set("button", static_cast<u16>(pad.button));
263 } 258 return params;
264 // An analog device needs two axes, so we need to store the axis for later and wait for 259 }
265 // a second input event. The axes also must be from the same joystick. 260 if (pad.axis == GCAdapter::PadAxes::Undefined ||
266 const u8 axis = static_cast<u8>(pad.axis); 261 std::abs(static_cast<float>(pad.axis_value) / 128.0f) < 0.1f) {
267 if (analog_x_axis == -1) { 262 continue;
268 analog_x_axis = axis; 263 }
269 controller_number = static_cast<int>(port); 264 // An analog device needs two axes, so we need to store the axis for later and wait for
270 } else if (analog_y_axis == -1 && analog_x_axis != axis && 265 // a second input event. The axes also must be from the same joystick.
271 controller_number == static_cast<int>(port)) { 266 const u8 axis = static_cast<u8>(pad.axis);
272 analog_y_axis = axis; 267 if (axis == 0 || axis == 1) {
273 } 268 analog_x_axis = 0;
269 analog_y_axis = 1;
270 controller_number = static_cast<s32>(pad.port);
271 break;
272 }
273 if (axis == 2 || axis == 3) {
274 analog_x_axis = 2;
275 analog_y_axis = 3;
276 controller_number = static_cast<s32>(pad.port);
277 break;
278 }
279
280 if (analog_x_axis == -1) {
281 analog_x_axis = axis;
282 controller_number = static_cast<s32>(pad.port);
283 } else if (analog_y_axis == -1 && analog_x_axis != axis &&
284 controller_number == static_cast<s32>(pad.port)) {
285 analog_y_axis = axis;
286 break;
274 } 287 }
275 } 288 }
276 Common::ParamPackage params;
277 if (analog_x_axis != -1 && analog_y_axis != -1) { 289 if (analog_x_axis != -1 && analog_y_axis != -1) {
278 params.Set("engine", "gcpad"); 290 params.Set("engine", "gcpad");
279 params.Set("port", controller_number); 291 params.Set("port", controller_number);
diff --git a/src/video_core/cdma_pusher.cpp b/src/video_core/cdma_pusher.cpp
index d774db107..b60f86260 100644
--- a/src/video_core/cdma_pusher.cpp
+++ b/src/video_core/cdma_pusher.cpp
@@ -144,7 +144,7 @@ void CDmaPusher::ExecuteCommand(u32 offset, u32 data) {
144 } 144 }
145 case ThiMethod::SetMethod1: 145 case ThiMethod::SetMethod1:
146 LOG_DEBUG(Service_NVDRV, "VIC method 0x{:X}, Args=({})", 146 LOG_DEBUG(Service_NVDRV, "VIC method 0x{:X}, Args=({})",
147 static_cast<u32>(vic_thi_state.method_0)); 147 static_cast<u32>(vic_thi_state.method_0), data);
148 vic_processor->ProcessMethod(static_cast<Tegra::Vic::Method>(vic_thi_state.method_0), 148 vic_processor->ProcessMethod(static_cast<Tegra::Vic::Method>(vic_thi_state.method_0),
149 {data}); 149 {data});
150 break; 150 break;
diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp
index 2df410be8..1adf3cd13 100644
--- a/src/video_core/command_classes/codecs/codec.cpp
+++ b/src/video_core/command_classes/codecs/codec.cpp
@@ -4,6 +4,7 @@
4 4
5#include <cstring> 5#include <cstring>
6#include <fstream> 6#include <fstream>
7#include <vector>
7#include "common/assert.h" 8#include "common/assert.h"
8#include "video_core/command_classes/codecs/codec.h" 9#include "video_core/command_classes/codecs/codec.h"
9#include "video_core/command_classes/codecs/h264.h" 10#include "video_core/command_classes/codecs/h264.h"
diff --git a/src/video_core/command_classes/codecs/codec.h b/src/video_core/command_classes/codecs/codec.h
index 2e56daf29..5bbe6a332 100644
--- a/src/video_core/command_classes/codecs/codec.h
+++ b/src/video_core/command_classes/codecs/codec.h
@@ -5,8 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <vector>
9#include "common/common_funcs.h"
10#include "common/common_types.h" 8#include "common/common_types.h"
11#include "video_core/command_classes/nvdec_common.h" 9#include "video_core/command_classes/nvdec_common.h"
12 10
@@ -44,11 +42,11 @@ public:
44 void Decode(); 42 void Decode();
45 43
46 /// Returns most recently decoded frame 44 /// Returns most recently decoded frame
47 AVFrame* GetCurrentFrame(); 45 [[nodiscard]] AVFrame* GetCurrentFrame();
48 const AVFrame* GetCurrentFrame() const; 46 [[nodiscard]] const AVFrame* GetCurrentFrame() const;
49 47
50 /// Returns the value of current_codec 48 /// Returns the value of current_codec
51 NvdecCommon::VideoCodec GetCurrentCodec() const; 49 [[nodiscard]] NvdecCommon::VideoCodec GetCurrentCodec() const;
52 50
53private: 51private:
54 bool initialized{}; 52 bool initialized{};
diff --git a/src/video_core/command_classes/codecs/h264.cpp b/src/video_core/command_classes/codecs/h264.cpp
index 1a39f7b23..33e063e20 100644
--- a/src/video_core/command_classes/codecs/h264.cpp
+++ b/src/video_core/command_classes/codecs/h264.cpp
@@ -18,17 +18,33 @@
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19// 19//
20 20
21#include <array>
21#include "common/bit_util.h" 22#include "common/bit_util.h"
22#include "video_core/command_classes/codecs/h264.h" 23#include "video_core/command_classes/codecs/h264.h"
23#include "video_core/gpu.h" 24#include "video_core/gpu.h"
24#include "video_core/memory_manager.h" 25#include "video_core/memory_manager.h"
25 26
26namespace Tegra::Decoder { 27namespace Tegra::Decoder {
28namespace {
29// ZigZag LUTs from libavcodec.
30constexpr std::array<u8, 64> zig_zag_direct{
31 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48,
32 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23,
33 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63,
34};
35
36constexpr std::array<u8, 16> zig_zag_scan{
37 0 + 0 * 4, 1 + 0 * 4, 0 + 1 * 4, 0 + 2 * 4, 1 + 1 * 4, 2 + 0 * 4, 3 + 0 * 4, 2 + 1 * 4,
38 1 + 2 * 4, 0 + 3 * 4, 1 + 3 * 4, 2 + 2 * 4, 3 + 1 * 4, 3 + 2 * 4, 2 + 3 * 4, 3 + 3 * 4,
39};
40} // Anonymous namespace
41
27H264::H264(GPU& gpu_) : gpu(gpu_) {} 42H264::H264(GPU& gpu_) : gpu(gpu_) {}
28 43
29H264::~H264() = default; 44H264::~H264() = default;
30 45
31std::vector<u8>& H264::ComposeFrameHeader(NvdecCommon::NvdecRegisters& state, bool is_first_frame) { 46const std::vector<u8>& H264::ComposeFrameHeader(NvdecCommon::NvdecRegisters& state,
47 bool is_first_frame) {
32 H264DecoderContext context{}; 48 H264DecoderContext context{};
33 gpu.MemoryManager().ReadBlock(state.picture_info_offset, &context, sizeof(H264DecoderContext)); 49 gpu.MemoryManager().ReadBlock(state.picture_info_offset, &context, sizeof(H264DecoderContext));
34 50
@@ -48,7 +64,8 @@ std::vector<u8>& H264::ComposeFrameHeader(NvdecCommon::NvdecRegisters& state, bo
48 writer.WriteU(0, 8); 64 writer.WriteU(0, 8);
49 writer.WriteU(31, 8); 65 writer.WriteU(31, 8);
50 writer.WriteUe(0); 66 writer.WriteUe(0);
51 const s32 chroma_format_idc = (context.h264_parameter_set.flags >> 12) & 0x3; 67 const auto chroma_format_idc =
68 static_cast<u32>((context.h264_parameter_set.flags >> 12) & 3);
52 writer.WriteUe(chroma_format_idc); 69 writer.WriteUe(chroma_format_idc);
53 if (chroma_format_idc == 3) { 70 if (chroma_format_idc == 3) {
54 writer.WriteBit(false); 71 writer.WriteBit(false);
@@ -59,8 +76,8 @@ std::vector<u8>& H264::ComposeFrameHeader(NvdecCommon::NvdecRegisters& state, bo
59 writer.WriteBit(false); // QpprimeYZeroTransformBypassFlag 76 writer.WriteBit(false); // QpprimeYZeroTransformBypassFlag
60 writer.WriteBit(false); // Scaling matrix present flag 77 writer.WriteBit(false); // Scaling matrix present flag
61 78
62 const s32 order_cnt_type = static_cast<s32>((context.h264_parameter_set.flags >> 14) & 3); 79 const auto order_cnt_type = static_cast<u32>((context.h264_parameter_set.flags >> 14) & 3);
63 writer.WriteUe(static_cast<s32>((context.h264_parameter_set.flags >> 8) & 0xf)); 80 writer.WriteUe(static_cast<u32>((context.h264_parameter_set.flags >> 8) & 0xf));
64 writer.WriteUe(order_cnt_type); 81 writer.WriteUe(order_cnt_type);
65 if (order_cnt_type == 0) { 82 if (order_cnt_type == 0) {
66 writer.WriteUe(context.h264_parameter_set.log2_max_pic_order_cnt); 83 writer.WriteUe(context.h264_parameter_set.log2_max_pic_order_cnt);
@@ -100,7 +117,7 @@ std::vector<u8>& H264::ComposeFrameHeader(NvdecCommon::NvdecRegisters& state, bo
100 writer.WriteUe(0); 117 writer.WriteUe(0);
101 writer.WriteUe(0); 118 writer.WriteUe(0);
102 119
103 writer.WriteBit(context.h264_parameter_set.entropy_coding_mode_flag); 120 writer.WriteBit(context.h264_parameter_set.entropy_coding_mode_flag != 0);
104 writer.WriteBit(false); 121 writer.WriteBit(false);
105 writer.WriteUe(0); 122 writer.WriteUe(0);
106 writer.WriteUe(context.h264_parameter_set.num_refidx_l0_default_active); 123 writer.WriteUe(context.h264_parameter_set.num_refidx_l0_default_active);
@@ -172,8 +189,8 @@ void H264BitWriter::WriteSe(s32 value) {
172 WriteExpGolombCodedInt(value); 189 WriteExpGolombCodedInt(value);
173} 190}
174 191
175void H264BitWriter::WriteUe(s32 value) { 192void H264BitWriter::WriteUe(u32 value) {
176 WriteExpGolombCodedUInt((u32)value); 193 WriteExpGolombCodedUInt(value);
177} 194}
178 195
179void H264BitWriter::End() { 196void H264BitWriter::End() {
diff --git a/src/video_core/command_classes/codecs/h264.h b/src/video_core/command_classes/codecs/h264.h
index 21752dd90..273449495 100644
--- a/src/video_core/command_classes/codecs/h264.h
+++ b/src/video_core/command_classes/codecs/h264.h
@@ -38,7 +38,7 @@ public:
38 /// WriteSe and WriteUe write in the Exp-Golomb-coded syntax 38 /// WriteSe and WriteUe write in the Exp-Golomb-coded syntax
39 void WriteU(s32 value, s32 value_sz); 39 void WriteU(s32 value, s32 value_sz);
40 void WriteSe(s32 value); 40 void WriteSe(s32 value);
41 void WriteUe(s32 value); 41 void WriteUe(u32 value);
42 42
43 /// Finalize the bitstream 43 /// Finalize the bitstream
44 void End(); 44 void End();
@@ -51,26 +51,14 @@ public:
51 void WriteScalingList(const std::vector<u8>& list, s32 start, s32 count); 51 void WriteScalingList(const std::vector<u8>& list, s32 start, s32 count);
52 52
53 /// Return the bitstream as a vector. 53 /// Return the bitstream as a vector.
54 std::vector<u8>& GetByteArray(); 54 [[nodiscard]] std::vector<u8>& GetByteArray();
55 const std::vector<u8>& GetByteArray() const; 55 [[nodiscard]] const std::vector<u8>& GetByteArray() const;
56 56
57private: 57private:
58 // ZigZag LUTs from libavcodec.
59 static constexpr std::array<u8, 64> zig_zag_direct{
60 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48,
61 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23,
62 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63,
63 };
64
65 static constexpr std::array<u8, 16> zig_zag_scan{
66 0 + 0 * 4, 1 + 0 * 4, 0 + 1 * 4, 0 + 2 * 4, 1 + 1 * 4, 2 + 0 * 4, 3 + 0 * 4, 2 + 1 * 4,
67 1 + 2 * 4, 0 + 3 * 4, 1 + 3 * 4, 2 + 2 * 4, 3 + 1 * 4, 3 + 2 * 4, 2 + 3 * 4, 3 + 3 * 4,
68 };
69
70 void WriteBits(s32 value, s32 bit_count); 58 void WriteBits(s32 value, s32 bit_count);
71 void WriteExpGolombCodedInt(s32 value); 59 void WriteExpGolombCodedInt(s32 value);
72 void WriteExpGolombCodedUInt(u32 value); 60 void WriteExpGolombCodedUInt(u32 value);
73 s32 GetFreeBufferBits(); 61 [[nodiscard]] s32 GetFreeBufferBits();
74 void Flush(); 62 void Flush();
75 63
76 s32 buffer_size{8}; 64 s32 buffer_size{8};
@@ -86,8 +74,8 @@ public:
86 ~H264(); 74 ~H264();
87 75
88 /// Compose the H264 header of the frame for FFmpeg decoding 76 /// Compose the H264 header of the frame for FFmpeg decoding
89 std::vector<u8>& ComposeFrameHeader(NvdecCommon::NvdecRegisters& state, 77 [[nodiscard]] const std::vector<u8>& ComposeFrameHeader(NvdecCommon::NvdecRegisters& state,
90 bool is_first_frame = false); 78 bool is_first_frame = false);
91 79
92private: 80private:
93 struct H264ParameterSet { 81 struct H264ParameterSet {
diff --git a/src/video_core/command_classes/codecs/vp9.cpp b/src/video_core/command_classes/codecs/vp9.cpp
index d205a8f5d..ab44fdc9e 100644
--- a/src/video_core/command_classes/codecs/vp9.cpp
+++ b/src/video_core/command_classes/codecs/vp9.cpp
@@ -9,7 +9,7 @@
9#include "video_core/memory_manager.h" 9#include "video_core/memory_manager.h"
10 10
11namespace Tegra::Decoder { 11namespace Tegra::Decoder {
12 12namespace {
13// Default compressed header probabilities once frame context resets 13// Default compressed header probabilities once frame context resets
14constexpr Vp9EntropyProbs default_probs{ 14constexpr Vp9EntropyProbs default_probs{
15 .y_mode_prob{ 15 .y_mode_prob{
@@ -170,6 +170,89 @@ constexpr Vp9EntropyProbs default_probs{
170 .high_precision{128, 128}, 170 .high_precision{128, 128},
171}; 171};
172 172
173constexpr std::array<s32, 256> norm_lut{
174 0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
175 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
176 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
177 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
178 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
179 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
180 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
181 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
182};
183
184constexpr std::array<s32, 254> map_lut{
185 20, 21, 22, 23, 24, 25, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
186 1, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 2, 50, 51, 52, 53, 54,
187 55, 56, 57, 58, 59, 60, 61, 3, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
188 73, 4, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 5, 86, 87, 88, 89,
189 90, 91, 92, 93, 94, 95, 96, 97, 6, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
190 108, 109, 7, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 8, 122, 123, 124,
191 125, 126, 127, 128, 129, 130, 131, 132, 133, 9, 134, 135, 136, 137, 138, 139, 140, 141, 142,
192 143, 144, 145, 10, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 11, 158, 159,
193 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 12, 170, 171, 172, 173, 174, 175, 176, 177,
194 178, 179, 180, 181, 13, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 14, 194,
195 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 15, 206, 207, 208, 209, 210, 211, 212,
196 213, 214, 215, 216, 217, 16, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 17,
197 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 18, 242, 243, 244, 245, 246, 247,
198 248, 249, 250, 251, 252, 253, 19,
199};
200
201// 6.2.14 Tile size calculation
202
203[[nodiscard]] s32 CalcMinLog2TileCols(s32 frame_width) {
204 const s32 sb64_cols = (frame_width + 63) / 64;
205 s32 min_log2 = 0;
206
207 while ((64 << min_log2) < sb64_cols) {
208 min_log2++;
209 }
210
211 return min_log2;
212}
213
214[[nodiscard]] s32 CalcMaxLog2TileCols(s32 frame_width) {
215 const s32 sb64_cols = (frame_width + 63) / 64;
216 s32 max_log2 = 1;
217
218 while ((sb64_cols >> max_log2) >= 4) {
219 max_log2++;
220 }
221
222 return max_log2 - 1;
223}
224
225// Recenters probability. Based on section 6.3.6 of VP9 Specification
226[[nodiscard]] s32 RecenterNonNeg(s32 new_prob, s32 old_prob) {
227 if (new_prob > old_prob * 2) {
228 return new_prob;
229 }
230
231 if (new_prob >= old_prob) {
232 return (new_prob - old_prob) * 2;
233 }
234
235 return (old_prob - new_prob) * 2 - 1;
236}
237
238// Adjusts old_prob depending on new_prob. Based on section 6.3.5 of VP9 Specification
239[[nodiscard]] s32 RemapProbability(s32 new_prob, s32 old_prob) {
240 new_prob--;
241 old_prob--;
242
243 std::size_t index{};
244
245 if (old_prob * 2 <= 0xff) {
246 index = static_cast<std::size_t>(std::max(0, RecenterNonNeg(new_prob, old_prob) - 1));
247 } else {
248 index = static_cast<std::size_t>(
249 std::max(0, RecenterNonNeg(0xff - 1 - new_prob, 0xff - 1 - old_prob) - 1));
250 }
251
252 return map_lut[index];
253}
254} // Anonymous namespace
255
173VP9::VP9(GPU& gpu) : gpu(gpu) {} 256VP9::VP9(GPU& gpu) : gpu(gpu) {}
174 257
175VP9::~VP9() = default; 258VP9::~VP9() = default;
@@ -207,32 +290,6 @@ void VP9::WriteProbabilityDelta(VpxRangeEncoder& writer, u8 new_prob, u8 old_pro
207 EncodeTermSubExp(writer, delta); 290 EncodeTermSubExp(writer, delta);
208} 291}
209 292
210s32 VP9::RemapProbability(s32 new_prob, s32 old_prob) {
211 new_prob--;
212 old_prob--;
213
214 std::size_t index{};
215
216 if (old_prob * 2 <= 0xff) {
217 index = static_cast<std::size_t>(std::max(0, RecenterNonNeg(new_prob, old_prob) - 1));
218 } else {
219 index = static_cast<std::size_t>(
220 std::max(0, RecenterNonNeg(0xff - 1 - new_prob, 0xff - 1 - old_prob) - 1));
221 }
222
223 return map_lut[index];
224}
225
226s32 VP9::RecenterNonNeg(s32 new_prob, s32 old_prob) {
227 if (new_prob > old_prob * 2) {
228 return new_prob;
229 } else if (new_prob >= old_prob) {
230 return (new_prob - old_prob) * 2;
231 } else {
232 return (old_prob - new_prob) * 2 - 1;
233 }
234}
235
236void VP9::EncodeTermSubExp(VpxRangeEncoder& writer, s32 value) { 293void VP9::EncodeTermSubExp(VpxRangeEncoder& writer, s32 value) {
237 if (WriteLessThan(writer, value, 16)) { 294 if (WriteLessThan(writer, value, 16)) {
238 writer.Write(value, 4); 295 writer.Write(value, 4);
@@ -332,28 +389,6 @@ void VP9::WriteMvProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_
332 } 389 }
333} 390}
334 391
335s32 VP9::CalcMinLog2TileCols(s32 frame_width) {
336 const s32 sb64_cols = (frame_width + 63) / 64;
337 s32 min_log2 = 0;
338
339 while ((64 << min_log2) < sb64_cols) {
340 min_log2++;
341 }
342
343 return min_log2;
344}
345
346s32 VP9::CalcMaxLog2TileCols(s32 frameWidth) {
347 const s32 sb64_cols = (frameWidth + 63) / 64;
348 s32 max_log2 = 1;
349
350 while ((sb64_cols >> max_log2) >= 4) {
351 max_log2++;
352 }
353
354 return max_log2 - 1;
355}
356
357Vp9PictureInfo VP9::GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state) { 392Vp9PictureInfo VP9::GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state) {
358 PictureInfo picture_info{}; 393 PictureInfo picture_info{};
359 gpu.MemoryManager().ReadBlock(state.picture_info_offset, &picture_info, sizeof(PictureInfo)); 394 gpu.MemoryManager().ReadBlock(state.picture_info_offset, &picture_info, sizeof(PictureInfo));
@@ -379,14 +414,14 @@ Vp9FrameContainer VP9::GetCurrentFrame(const NvdecCommon::NvdecRegisters& state)
379 Vp9FrameContainer frame{}; 414 Vp9FrameContainer frame{};
380 { 415 {
381 gpu.SyncGuestHost(); 416 gpu.SyncGuestHost();
382 frame.info = std::move(GetVp9PictureInfo(state)); 417 frame.info = GetVp9PictureInfo(state);
383 418
384 frame.bit_stream.resize(frame.info.bitstream_size); 419 frame.bit_stream.resize(frame.info.bitstream_size);
385 gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.bit_stream.data(), 420 gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.bit_stream.data(),
386 frame.info.bitstream_size); 421 frame.info.bitstream_size);
387 } 422 }
388 // Buffer two frames, saving the last show frame info 423 // Buffer two frames, saving the last show frame info
389 if (next_next_frame.bit_stream.size() != 0) { 424 if (!next_next_frame.bit_stream.empty()) {
390 Vp9FrameContainer temp{ 425 Vp9FrameContainer temp{
391 .info = frame.info, 426 .info = frame.info,
392 .bit_stream = frame.bit_stream, 427 .bit_stream = frame.bit_stream,
@@ -396,15 +431,15 @@ Vp9FrameContainer VP9::GetCurrentFrame(const NvdecCommon::NvdecRegisters& state)
396 frame.bit_stream = next_next_frame.bit_stream; 431 frame.bit_stream = next_next_frame.bit_stream;
397 next_next_frame = std::move(temp); 432 next_next_frame = std::move(temp);
398 433
399 if (next_frame.bit_stream.size() != 0) { 434 if (!next_frame.bit_stream.empty()) {
400 Vp9FrameContainer temp{ 435 Vp9FrameContainer temp2{
401 .info = frame.info, 436 .info = frame.info,
402 .bit_stream = frame.bit_stream, 437 .bit_stream = frame.bit_stream,
403 }; 438 };
404 next_frame.info.show_frame = frame.info.last_frame_shown; 439 next_frame.info.show_frame = frame.info.last_frame_shown;
405 frame.info = next_frame.info; 440 frame.info = next_frame.info;
406 frame.bit_stream = next_frame.bit_stream; 441 frame.bit_stream = next_frame.bit_stream;
407 next_frame = std::move(temp); 442 next_frame = std::move(temp2);
408 } else { 443 } else {
409 next_frame.info = frame.info; 444 next_frame.info = frame.info;
410 next_frame.bit_stream = frame.bit_stream; 445 next_frame.bit_stream = frame.bit_stream;
@@ -605,12 +640,6 @@ std::vector<u8> VP9::ComposeCompressedHeader() {
605 640
606 writer.End(); 641 writer.End();
607 return writer.GetBuffer(); 642 return writer.GetBuffer();
608
609 const auto writer_bytearray = writer.GetBuffer();
610
611 std::vector<u8> compressed_header(writer_bytearray.size());
612 std::memcpy(compressed_header.data(), writer_bytearray.data(), writer_bytearray.size());
613 return compressed_header;
614} 643}
615 644
616VpxBitStreamWriter VP9::ComposeUncompressedHeader() { 645VpxBitStreamWriter VP9::ComposeUncompressedHeader() {
@@ -648,7 +677,6 @@ VpxBitStreamWriter VP9::ComposeUncompressedHeader() {
648 current_frame_info.intra_only = true; 677 current_frame_info.intra_only = true;
649 678
650 } else { 679 } else {
651 std::array<s32, 3> ref_frame_index;
652 680
653 if (!current_frame_info.show_frame) { 681 if (!current_frame_info.show_frame) {
654 uncomp_writer.WriteBit(current_frame_info.intra_only); 682 uncomp_writer.WriteBit(current_frame_info.intra_only);
@@ -663,9 +691,9 @@ VpxBitStreamWriter VP9::ComposeUncompressedHeader() {
663 } 691 }
664 692
665 // Last, Golden, Altref frames 693 // Last, Golden, Altref frames
666 ref_frame_index = std::array<s32, 3>{0, 1, 2}; 694 std::array<s32, 3> ref_frame_index{0, 1, 2};
667 695
668 // set when next frame is hidden 696 // Set when next frame is hidden
669 // altref and golden references are swapped 697 // altref and golden references are swapped
670 if (swap_next_golden) { 698 if (swap_next_golden) {
671 ref_frame_index = std::array<s32, 3>{0, 2, 1}; 699 ref_frame_index = std::array<s32, 3>{0, 2, 1};
@@ -754,17 +782,19 @@ VpxBitStreamWriter VP9::ComposeUncompressedHeader() {
754 for (std::size_t index = 0; index < current_frame_info.ref_deltas.size(); index++) { 782 for (std::size_t index = 0; index < current_frame_info.ref_deltas.size(); index++) {
755 const s8 old_deltas = loop_filter_ref_deltas[index]; 783 const s8 old_deltas = loop_filter_ref_deltas[index];
756 const s8 new_deltas = current_frame_info.ref_deltas[index]; 784 const s8 new_deltas = current_frame_info.ref_deltas[index];
785 const bool differing_delta = old_deltas != new_deltas;
757 786
758 loop_filter_delta_update |= 787 update_loop_filter_ref_deltas[index] = differing_delta;
759 (update_loop_filter_ref_deltas[index] = old_deltas != new_deltas); 788 loop_filter_delta_update |= differing_delta;
760 } 789 }
761 790
762 for (std::size_t index = 0; index < current_frame_info.mode_deltas.size(); index++) { 791 for (std::size_t index = 0; index < current_frame_info.mode_deltas.size(); index++) {
763 const s8 old_deltas = loop_filter_mode_deltas[index]; 792 const s8 old_deltas = loop_filter_mode_deltas[index];
764 const s8 new_deltas = current_frame_info.mode_deltas[index]; 793 const s8 new_deltas = current_frame_info.mode_deltas[index];
794 const bool differing_delta = old_deltas != new_deltas;
765 795
766 loop_filter_delta_update |= 796 update_loop_filter_mode_deltas[index] = differing_delta;
767 (update_loop_filter_mode_deltas[index] = old_deltas != new_deltas); 797 loop_filter_delta_update |= differing_delta;
768 } 798 }
769 799
770 uncomp_writer.WriteBit(loop_filter_delta_update); 800 uncomp_writer.WriteBit(loop_filter_delta_update);
@@ -824,12 +854,12 @@ VpxBitStreamWriter VP9::ComposeUncompressedHeader() {
824 return uncomp_writer; 854 return uncomp_writer;
825} 855}
826 856
827std::vector<u8>& VP9::ComposeFrameHeader(NvdecCommon::NvdecRegisters& state) { 857const std::vector<u8>& VP9::ComposeFrameHeader(NvdecCommon::NvdecRegisters& state) {
828 std::vector<u8> bitstream; 858 std::vector<u8> bitstream;
829 { 859 {
830 Vp9FrameContainer curr_frame = GetCurrentFrame(state); 860 Vp9FrameContainer curr_frame = GetCurrentFrame(state);
831 current_frame_info = curr_frame.info; 861 current_frame_info = curr_frame.info;
832 bitstream = curr_frame.bit_stream; 862 bitstream = std::move(curr_frame.bit_stream);
833 } 863 }
834 864
835 // The uncompressed header routine sets PrevProb parameters needed for the compressed header 865 // The uncompressed header routine sets PrevProb parameters needed for the compressed header
diff --git a/src/video_core/command_classes/codecs/vp9.h b/src/video_core/command_classes/codecs/vp9.h
index 748e11bae..e2504512c 100644
--- a/src/video_core/command_classes/codecs/vp9.h
+++ b/src/video_core/command_classes/codecs/vp9.h
@@ -4,9 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <unordered_map> 7#include <array>
8#include <vector> 8#include <vector>
9#include "common/common_funcs.h" 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/stream.h" 11#include "common/stream.h"
12#include "video_core/command_classes/codecs/vp9_types.h" 12#include "video_core/command_classes/codecs/vp9_types.h"
@@ -25,6 +25,12 @@ public:
25 VpxRangeEncoder(); 25 VpxRangeEncoder();
26 ~VpxRangeEncoder(); 26 ~VpxRangeEncoder();
27 27
28 VpxRangeEncoder(const VpxRangeEncoder&) = delete;
29 VpxRangeEncoder& operator=(const VpxRangeEncoder&) = delete;
30
31 VpxRangeEncoder(VpxRangeEncoder&&) = default;
32 VpxRangeEncoder& operator=(VpxRangeEncoder&&) = default;
33
28 /// Writes the rightmost value_size bits from value into the stream 34 /// Writes the rightmost value_size bits from value into the stream
29 void Write(s32 value, s32 value_size); 35 void Write(s32 value, s32 value_size);
30 36
@@ -37,11 +43,11 @@ public:
37 /// Signal the end of the bitstream 43 /// Signal the end of the bitstream
38 void End(); 44 void End();
39 45
40 std::vector<u8>& GetBuffer() { 46 [[nodiscard]] std::vector<u8>& GetBuffer() {
41 return base_stream.GetBuffer(); 47 return base_stream.GetBuffer();
42 } 48 }
43 49
44 const std::vector<u8>& GetBuffer() const { 50 [[nodiscard]] const std::vector<u8>& GetBuffer() const {
45 return base_stream.GetBuffer(); 51 return base_stream.GetBuffer();
46 } 52 }
47 53
@@ -52,17 +58,6 @@ private:
52 u32 range{0xff}; 58 u32 range{0xff};
53 s32 count{-24}; 59 s32 count{-24};
54 s32 half_probability{128}; 60 s32 half_probability{128};
55 static constexpr std::array<s32, 256> norm_lut{
56 0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
57 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
58 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
59 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
60 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
61 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
62 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
63 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
64 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
65 };
66}; 61};
67 62
68class VpxBitStreamWriter { 63class VpxBitStreamWriter {
@@ -70,6 +65,12 @@ public:
70 VpxBitStreamWriter(); 65 VpxBitStreamWriter();
71 ~VpxBitStreamWriter(); 66 ~VpxBitStreamWriter();
72 67
68 VpxBitStreamWriter(const VpxBitStreamWriter&) = delete;
69 VpxBitStreamWriter& operator=(const VpxBitStreamWriter&) = delete;
70
71 VpxBitStreamWriter(VpxBitStreamWriter&&) = default;
72 VpxBitStreamWriter& operator=(VpxBitStreamWriter&&) = default;
73
73 /// Write an unsigned integer value 74 /// Write an unsigned integer value
74 void WriteU(u32 value, u32 value_size); 75 void WriteU(u32 value, u32 value_size);
75 76
@@ -86,10 +87,10 @@ public:
86 void Flush(); 87 void Flush();
87 88
88 /// Returns byte_array 89 /// Returns byte_array
89 std::vector<u8>& GetByteArray(); 90 [[nodiscard]] std::vector<u8>& GetByteArray();
90 91
91 /// Returns const byte_array 92 /// Returns const byte_array
92 const std::vector<u8>& GetByteArray() const; 93 [[nodiscard]] const std::vector<u8>& GetByteArray() const;
93 94
94private: 95private:
95 /// Write bit_count bits from value into buffer 96 /// Write bit_count bits from value into buffer
@@ -110,12 +111,18 @@ public:
110 explicit VP9(GPU& gpu); 111 explicit VP9(GPU& gpu);
111 ~VP9(); 112 ~VP9();
112 113
114 VP9(const VP9&) = delete;
115 VP9& operator=(const VP9&) = delete;
116
117 VP9(VP9&&) = default;
118 VP9& operator=(VP9&&) = delete;
119
113 /// Composes the VP9 frame from the GPU state information. Based on the official VP9 spec 120 /// Composes the VP9 frame from the GPU state information. Based on the official VP9 spec
114 /// documentation 121 /// documentation
115 std::vector<u8>& ComposeFrameHeader(NvdecCommon::NvdecRegisters& state); 122 [[nodiscard]] const std::vector<u8>& ComposeFrameHeader(NvdecCommon::NvdecRegisters& state);
116 123
117 /// Returns true if the most recent frame was a hidden frame. 124 /// Returns true if the most recent frame was a hidden frame.
118 bool WasFrameHidden() const { 125 [[nodiscard]] bool WasFrameHidden() const {
119 return hidden; 126 return hidden;
120 } 127 }
121 128
@@ -132,12 +139,6 @@ private:
132 /// Generates compressed header probability deltas in the bitstream writer 139 /// Generates compressed header probability deltas in the bitstream writer
133 void WriteProbabilityDelta(VpxRangeEncoder& writer, u8 new_prob, u8 old_prob); 140 void WriteProbabilityDelta(VpxRangeEncoder& writer, u8 new_prob, u8 old_prob);
134 141
135 /// Adjusts old_prob depending on new_prob. Based on section 6.3.5 of VP9 Specification
136 s32 RemapProbability(s32 new_prob, s32 old_prob);
137
138 /// Recenters probability. Based on section 6.3.6 of VP9 Specification
139 s32 RecenterNonNeg(s32 new_prob, s32 old_prob);
140
141 /// Inverse of 6.3.4 Decode term subexp 142 /// Inverse of 6.3.4 Decode term subexp
142 void EncodeTermSubExp(VpxRangeEncoder& writer, s32 value); 143 void EncodeTermSubExp(VpxRangeEncoder& writer, s32 value);
143 144
@@ -157,22 +158,18 @@ private:
157 /// Write motion vector probability updates. 6.3.17 in the spec 158 /// Write motion vector probability updates. 6.3.17 in the spec
158 void WriteMvProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_prob); 159 void WriteMvProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_prob);
159 160
160 /// 6.2.14 Tile size calculation
161 s32 CalcMinLog2TileCols(s32 frame_width);
162 s32 CalcMaxLog2TileCols(s32 frame_width);
163
164 /// Returns VP9 information from NVDEC provided offset and size 161 /// Returns VP9 information from NVDEC provided offset and size
165 Vp9PictureInfo GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state); 162 [[nodiscard]] Vp9PictureInfo GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state);
166 163
167 /// Read and convert NVDEC provided entropy probs to Vp9EntropyProbs struct 164 /// Read and convert NVDEC provided entropy probs to Vp9EntropyProbs struct
168 void InsertEntropy(u64 offset, Vp9EntropyProbs& dst); 165 void InsertEntropy(u64 offset, Vp9EntropyProbs& dst);
169 166
170 /// Returns frame to be decoded after buffering 167 /// Returns frame to be decoded after buffering
171 Vp9FrameContainer GetCurrentFrame(const NvdecCommon::NvdecRegisters& state); 168 [[nodiscard]] Vp9FrameContainer GetCurrentFrame(const NvdecCommon::NvdecRegisters& state);
172 169
173 /// Use NVDEC providied information to compose the headers for the current frame 170 /// Use NVDEC providied information to compose the headers for the current frame
174 std::vector<u8> ComposeCompressedHeader(); 171 [[nodiscard]] std::vector<u8> ComposeCompressedHeader();
175 VpxBitStreamWriter ComposeUncompressedHeader(); 172 [[nodiscard]] VpxBitStreamWriter ComposeUncompressedHeader();
176 173
177 GPU& gpu; 174 GPU& gpu;
178 std::vector<u8> frame; 175 std::vector<u8> frame;
@@ -180,7 +177,7 @@ private:
180 std::array<s8, 4> loop_filter_ref_deltas{}; 177 std::array<s8, 4> loop_filter_ref_deltas{};
181 std::array<s8, 2> loop_filter_mode_deltas{}; 178 std::array<s8, 2> loop_filter_mode_deltas{};
182 179
183 bool hidden; 180 bool hidden = false;
184 s64 current_frame_number = -2; // since we buffer 2 frames 181 s64 current_frame_number = -2; // since we buffer 2 frames
185 s32 grace_period = 6; // frame offsets need to stabilize 182 s32 grace_period = 6; // frame offsets need to stabilize
186 std::array<FrameContexts, 4> frame_ctxs{}; 183 std::array<FrameContexts, 4> frame_ctxs{};
@@ -193,23 +190,6 @@ private:
193 190
194 s32 diff_update_probability = 252; 191 s32 diff_update_probability = 252;
195 s32 frame_sync_code = 0x498342; 192 s32 frame_sync_code = 0x498342;
196 static constexpr std::array<s32, 254> map_lut = {
197 20, 21, 22, 23, 24, 25, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
198 36, 37, 1, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 2, 50,
199 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 3, 62, 63, 64, 65, 66,
200 67, 68, 69, 70, 71, 72, 73, 4, 74, 75, 76, 77, 78, 79, 80, 81, 82,
201 83, 84, 85, 5, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 6,
202 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 7, 110, 111, 112, 113,
203 114, 115, 116, 117, 118, 119, 120, 121, 8, 122, 123, 124, 125, 126, 127, 128, 129,
204 130, 131, 132, 133, 9, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145,
205 10, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 11, 158, 159, 160,
206 161, 162, 163, 164, 165, 166, 167, 168, 169, 12, 170, 171, 172, 173, 174, 175, 176,
207 177, 178, 179, 180, 181, 13, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192,
208 193, 14, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 15, 206, 207,
209 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 16, 218, 219, 220, 221, 222, 223,
210 224, 225, 226, 227, 228, 229, 17, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
211 240, 241, 18, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 19,
212 };
213}; 193};
214 194
215} // namespace Decoder 195} // namespace Decoder
diff --git a/src/video_core/command_classes/codecs/vp9_types.h b/src/video_core/command_classes/codecs/vp9_types.h
index 8688fdac0..4f0b05d22 100644
--- a/src/video_core/command_classes/codecs/vp9_types.h
+++ b/src/video_core/command_classes/codecs/vp9_types.h
@@ -4,13 +4,11 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <algorithm> 7#include <array>
8#include <list> 8#include <cstring>
9#include <vector> 9#include <vector>
10#include "common/cityhash.h"
11#include "common/common_funcs.h" 10#include "common/common_funcs.h"
12#include "common/common_types.h" 11#include "common/common_types.h"
13#include "video_core/command_classes/nvdec_common.h"
14 12
15namespace Tegra { 13namespace Tegra {
16class GPU; 14class GPU;
@@ -233,9 +231,8 @@ struct PictureInfo {
233 u32 surface_params{}; 231 u32 surface_params{};
234 INSERT_PADDING_WORDS(3); 232 INSERT_PADDING_WORDS(3);
235 233
236 Vp9PictureInfo Convert() const { 234 [[nodiscard]] Vp9PictureInfo Convert() const {
237 235 return {
238 return Vp9PictureInfo{
239 .is_key_frame = (vp9_flags & FrameFlags::IsKeyFrame) != 0, 236 .is_key_frame = (vp9_flags & FrameFlags::IsKeyFrame) != 0,
240 .intra_only = (vp9_flags & FrameFlags::IntraOnly) != 0, 237 .intra_only = (vp9_flags & FrameFlags::IntraOnly) != 0,
241 .last_frame_was_key = (vp9_flags & FrameFlags::LastFrameIsKeyFrame) != 0, 238 .last_frame_was_key = (vp9_flags & FrameFlags::LastFrameIsKeyFrame) != 0,
diff --git a/src/video_core/command_classes/host1x.cpp b/src/video_core/command_classes/host1x.cpp
index a5234ee47..c4dd4881a 100644
--- a/src/video_core/command_classes/host1x.cpp
+++ b/src/video_core/command_classes/host1x.cpp
@@ -15,7 +15,7 @@ void Tegra::Host1x::StateWrite(u32 offset, u32 arguments) {
15 std::memcpy(state_offset, &arguments, sizeof(u32)); 15 std::memcpy(state_offset, &arguments, sizeof(u32));
16} 16}
17 17
18void Tegra::Host1x::ProcessMethod(Host1x::Method method, const std::vector<u32>& arguments) { 18void Tegra::Host1x::ProcessMethod(Method method, const std::vector<u32>& arguments) {
19 StateWrite(static_cast<u32>(method), arguments[0]); 19 StateWrite(static_cast<u32>(method), arguments[0]);
20 switch (method) { 20 switch (method) {
21 case Method::WaitSyncpt: 21 case Method::WaitSyncpt:
diff --git a/src/video_core/command_classes/host1x.h b/src/video_core/command_classes/host1x.h
index 501a5ed2e..013eaa0c1 100644
--- a/src/video_core/command_classes/host1x.h
+++ b/src/video_core/command_classes/host1x.h
@@ -61,7 +61,7 @@ public:
61 ~Host1x(); 61 ~Host1x();
62 62
63 /// Writes the method into the state, Invoke Execute() if encountered 63 /// Writes the method into the state, Invoke Execute() if encountered
64 void ProcessMethod(Host1x::Method method, const std::vector<u32>& arguments); 64 void ProcessMethod(Method method, const std::vector<u32>& arguments);
65 65
66private: 66private:
67 /// For Host1x, execute is waiting on a syncpoint previously written into the state 67 /// For Host1x, execute is waiting on a syncpoint previously written into the state
diff --git a/src/video_core/command_classes/nvdec.cpp b/src/video_core/command_classes/nvdec.cpp
index ede9466eb..8ca7a7b06 100644
--- a/src/video_core/command_classes/nvdec.cpp
+++ b/src/video_core/command_classes/nvdec.cpp
@@ -2,13 +2,9 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <bitset>
6#include "common/assert.h" 5#include "common/assert.h"
7#include "common/bit_util.h"
8#include "core/memory.h"
9#include "video_core/command_classes/nvdec.h" 6#include "video_core/command_classes/nvdec.h"
10#include "video_core/gpu.h" 7#include "video_core/gpu.h"
11#include "video_core/memory_manager.h"
12 8
13namespace Tegra { 9namespace Tegra {
14 10
@@ -16,7 +12,7 @@ Nvdec::Nvdec(GPU& gpu_) : gpu(gpu_), codec(std::make_unique<Codec>(gpu)) {}
16 12
17Nvdec::~Nvdec() = default; 13Nvdec::~Nvdec() = default;
18 14
19void Nvdec::ProcessMethod(Nvdec::Method method, const std::vector<u32>& arguments) { 15void Nvdec::ProcessMethod(Method method, const std::vector<u32>& arguments) {
20 if (method == Method::SetVideoCodec) { 16 if (method == Method::SetVideoCodec) {
21 codec->StateWrite(static_cast<u32>(method), arguments[0]); 17 codec->StateWrite(static_cast<u32>(method), arguments[0]);
22 } else { 18 } else {
diff --git a/src/video_core/command_classes/nvdec.h b/src/video_core/command_classes/nvdec.h
index c1a9d843e..eec4443f9 100644
--- a/src/video_core/command_classes/nvdec.h
+++ b/src/video_core/command_classes/nvdec.h
@@ -4,8 +4,8 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <memory>
7#include <vector> 8#include <vector>
8#include "common/common_funcs.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "video_core/command_classes/codecs/codec.h" 10#include "video_core/command_classes/codecs/codec.h"
11 11
@@ -23,17 +23,17 @@ public:
23 ~Nvdec(); 23 ~Nvdec();
24 24
25 /// Writes the method into the state, Invoke Execute() if encountered 25 /// Writes the method into the state, Invoke Execute() if encountered
26 void ProcessMethod(Nvdec::Method method, const std::vector<u32>& arguments); 26 void ProcessMethod(Method method, const std::vector<u32>& arguments);
27 27
28 /// Return most recently decoded frame 28 /// Return most recently decoded frame
29 AVFrame* GetFrame(); 29 [[nodiscard]] AVFrame* GetFrame();
30 const AVFrame* GetFrame() const; 30 [[nodiscard]] const AVFrame* GetFrame() const;
31 31
32private: 32private:
33 /// Invoke codec to decode a frame 33 /// Invoke codec to decode a frame
34 void Execute(); 34 void Execute();
35 35
36 GPU& gpu; 36 GPU& gpu;
37 std::unique_ptr<Tegra::Codec> codec; 37 std::unique_ptr<Codec> codec;
38}; 38};
39} // namespace Tegra 39} // namespace Tegra
diff --git a/src/video_core/command_classes/sync_manager.cpp b/src/video_core/command_classes/sync_manager.cpp
index a0ab44855..19dc9e0ab 100644
--- a/src/video_core/command_classes/sync_manager.cpp
+++ b/src/video_core/command_classes/sync_manager.cpp
@@ -27,22 +27,22 @@ SyncptIncrManager::SyncptIncrManager(GPU& gpu_) : gpu(gpu_) {}
27SyncptIncrManager::~SyncptIncrManager() = default; 27SyncptIncrManager::~SyncptIncrManager() = default;
28 28
29void SyncptIncrManager::Increment(u32 id) { 29void SyncptIncrManager::Increment(u32 id) {
30 increments.push_back(SyncptIncr{0, id, true}); 30 increments.emplace_back(0, 0, id, true);
31 IncrementAllDone(); 31 IncrementAllDone();
32} 32}
33 33
34u32 SyncptIncrManager::IncrementWhenDone(u32 class_id, u32 id) { 34u32 SyncptIncrManager::IncrementWhenDone(u32 class_id, u32 id) {
35 const u32 handle = current_id++; 35 const u32 handle = current_id++;
36 increments.push_back(SyncptIncr{handle, class_id, id}); 36 increments.emplace_back(handle, class_id, id);
37 return handle; 37 return handle;
38} 38}
39 39
40void SyncptIncrManager::SignalDone(u32 handle) { 40void SyncptIncrManager::SignalDone(u32 handle) {
41 auto done_incr = std::find_if(increments.begin(), increments.end(), 41 const auto done_incr =
42 [handle](SyncptIncr incr) { return incr.id == handle; }); 42 std::find_if(increments.begin(), increments.end(),
43 if (done_incr != increments.end()) { 43 [handle](const SyncptIncr& incr) { return incr.id == handle; });
44 const SyncptIncr incr = *done_incr; 44 if (done_incr != increments.cend()) {
45 *done_incr = SyncptIncr{incr.id, incr.class_id, incr.syncpt_id, true}; 45 done_incr->complete = true;
46 } 46 }
47 IncrementAllDone(); 47 IncrementAllDone();
48} 48}
diff --git a/src/video_core/command_classes/sync_manager.h b/src/video_core/command_classes/sync_manager.h
index 353b67573..2c321ec58 100644
--- a/src/video_core/command_classes/sync_manager.h
+++ b/src/video_core/command_classes/sync_manager.h
@@ -32,8 +32,8 @@ struct SyncptIncr {
32 u32 syncpt_id; 32 u32 syncpt_id;
33 bool complete; 33 bool complete;
34 34
35 SyncptIncr(u32 id, u32 syncpt_id_, u32 class_id_, bool done = false) 35 SyncptIncr(u32 id_, u32 class_id_, u32 syncpt_id_, bool done = false)
36 : id(id), class_id(class_id_), syncpt_id(syncpt_id_), complete(done) {} 36 : id(id_), class_id(class_id_), syncpt_id(syncpt_id_), complete(done) {}
37}; 37};
38 38
39class SyncptIncrManager { 39class SyncptIncrManager {
diff --git a/src/video_core/command_classes/vic.cpp b/src/video_core/command_classes/vic.cpp
index 66e15a1a8..5b52da277 100644
--- a/src/video_core/command_classes/vic.cpp
+++ b/src/video_core/command_classes/vic.cpp
@@ -26,7 +26,7 @@ void Vic::VicStateWrite(u32 offset, u32 arguments) {
26 std::memcpy(state_offset, &arguments, sizeof(u32)); 26 std::memcpy(state_offset, &arguments, sizeof(u32));
27} 27}
28 28
29void Vic::ProcessMethod(Vic::Method method, const std::vector<u32>& arguments) { 29void Vic::ProcessMethod(Method method, const std::vector<u32>& arguments) {
30 LOG_DEBUG(HW_GPU, "Vic method 0x{:X}", static_cast<u32>(method)); 30 LOG_DEBUG(HW_GPU, "Vic method 0x{:X}", static_cast<u32>(method));
31 VicStateWrite(static_cast<u32>(method), arguments[0]); 31 VicStateWrite(static_cast<u32>(method), arguments[0]);
32 const u64 arg = static_cast<u64>(arguments[0]) << 8; 32 const u64 arg = static_cast<u64>(arguments[0]) << 8;
diff --git a/src/video_core/command_classes/vic.h b/src/video_core/command_classes/vic.h
index dd0a2aed8..8c4e284a1 100644
--- a/src/video_core/command_classes/vic.h
+++ b/src/video_core/command_classes/vic.h
@@ -63,11 +63,11 @@ public:
63 SetOutputSurfaceChromaVOffset = 0x1ca 63 SetOutputSurfaceChromaVOffset = 0x1ca
64 }; 64 };
65 65
66 explicit Vic(GPU& gpu, std::shared_ptr<Tegra::Nvdec> nvdec_processor); 66 explicit Vic(GPU& gpu, std::shared_ptr<Nvdec> nvdec_processor);
67 ~Vic(); 67 ~Vic();
68 68
69 /// Write to the device state. 69 /// Write to the device state.
70 void ProcessMethod(Vic::Method method, const std::vector<u32>& arguments); 70 void ProcessMethod(Method method, const std::vector<u32>& arguments);
71 71
72private: 72private:
73 void Execute(); 73 void Execute();
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp
index f2f96ac33..105b85a92 100644
--- a/src/video_core/dma_pusher.cpp
+++ b/src/video_core/dma_pusher.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/cityhash.h"
5#include "common/microprofile.h" 6#include "common/microprofile.h"
6#include "core/core.h" 7#include "core/core.h"
7#include "core/memory.h" 8#include "core/memory.h"
@@ -12,6 +13,20 @@
12 13
13namespace Tegra { 14namespace Tegra {
14 15
16void CommandList::RefreshIntegrityChecks(GPU& gpu) {
17 command_list_hashes.resize(command_lists.size());
18
19 for (std::size_t index = 0; index < command_lists.size(); ++index) {
20 const CommandListHeader command_list_header = command_lists[index];
21 std::vector<CommandHeader> command_headers(command_list_header.size);
22 gpu.MemoryManager().ReadBlockUnsafe(command_list_header.addr, command_headers.data(),
23 command_list_header.size * sizeof(u32));
24 command_list_hashes[index] =
25 Common::CityHash64(reinterpret_cast<char*>(command_headers.data()),
26 command_list_header.size * sizeof(u32));
27 }
28}
29
15DmaPusher::DmaPusher(Core::System& system, GPU& gpu) : gpu{gpu}, system{system} {} 30DmaPusher::DmaPusher(Core::System& system, GPU& gpu) : gpu{gpu}, system{system} {}
16 31
17DmaPusher::~DmaPusher() = default; 32DmaPusher::~DmaPusher() = default;
@@ -45,32 +60,51 @@ bool DmaPusher::Step() {
45 return false; 60 return false;
46 } 61 }
47 62
48 const CommandList& command_list{dma_pushbuffer.front()}; 63 CommandList& command_list{dma_pushbuffer.front()};
49 ASSERT_OR_EXECUTE(!command_list.empty(), {
50 // Somehow the command_list is empty, in order to avoid a crash
51 // We ignore it and assume its size is 0.
52 dma_pushbuffer.pop();
53 dma_pushbuffer_subindex = 0;
54 return true;
55 });
56 const CommandListHeader command_list_header{command_list[dma_pushbuffer_subindex++]};
57 const GPUVAddr dma_get = command_list_header.addr;
58
59 if (dma_pushbuffer_subindex >= command_list.size()) {
60 // We've gone through the current list, remove it from the queue
61 dma_pushbuffer.pop();
62 dma_pushbuffer_subindex = 0;
63 }
64 64
65 if (command_list_header.size == 0) { 65 ASSERT_OR_EXECUTE(
66 return true; 66 command_list.command_lists.size() || command_list.prefetch_command_list.size(), {
67 } 67 // Somehow the command_list is empty, in order to avoid a crash
68 // We ignore it and assume its size is 0.
69 dma_pushbuffer.pop();
70 dma_pushbuffer_subindex = 0;
71 return true;
72 });
68 73
69 // Push buffer non-empty, read a word 74 if (command_list.prefetch_command_list.size()) {
70 command_headers.resize(command_list_header.size); 75 // Prefetched command list from nvdrv, used for things like synchronization
71 gpu.MemoryManager().ReadBlockUnsafe(dma_get, command_headers.data(), 76 command_headers = std::move(command_list.prefetch_command_list);
72 command_list_header.size * sizeof(u32)); 77 dma_pushbuffer.pop();
78 } else {
79 const CommandListHeader command_list_header{
80 command_list.command_lists[dma_pushbuffer_subindex]};
81 const u64 next_hash = command_list.command_list_hashes[dma_pushbuffer_subindex++];
82 const GPUVAddr dma_get = command_list_header.addr;
83
84 if (dma_pushbuffer_subindex >= command_list.command_lists.size()) {
85 // We've gone through the current list, remove it from the queue
86 dma_pushbuffer.pop();
87 dma_pushbuffer_subindex = 0;
88 }
73 89
90 if (command_list_header.size == 0) {
91 return true;
92 }
93
94 // Push buffer non-empty, read a word
95 command_headers.resize(command_list_header.size);
96 gpu.MemoryManager().ReadBlockUnsafe(dma_get, command_headers.data(),
97 command_list_header.size * sizeof(u32));
98
99 // Integrity check
100 const u64 new_hash = Common::CityHash64(reinterpret_cast<char*>(command_headers.data()),
101 command_list_header.size * sizeof(u32));
102 if (new_hash != next_hash) {
103 LOG_CRITICAL(HW_GPU, "CommandList at addr=0x{:X} is corrupt, skipping!", dma_get);
104 dma_pushbuffer.pop();
105 return true;
106 }
107 }
74 for (std::size_t index = 0; index < command_headers.size();) { 108 for (std::size_t index = 0; index < command_headers.size();) {
75 const CommandHeader& command_header = command_headers[index]; 109 const CommandHeader& command_header = command_headers[index];
76 110
diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h
index efa90d170..8496ba2da 100644
--- a/src/video_core/dma_pusher.h
+++ b/src/video_core/dma_pusher.h
@@ -27,6 +27,31 @@ enum class SubmissionMode : u32 {
27 IncreaseOnce = 5 27 IncreaseOnce = 5
28}; 28};
29 29
30// Note that, traditionally, methods are treated as 4-byte addressable locations, and hence
31// their numbers are written down multiplied by 4 in Docs. Here we are not multiply by 4.
32// So the values you see in docs might be multiplied by 4.
33enum class BufferMethods : u32 {
34 BindObject = 0x0,
35 Nop = 0x2,
36 SemaphoreAddressHigh = 0x4,
37 SemaphoreAddressLow = 0x5,
38 SemaphoreSequence = 0x6,
39 SemaphoreTrigger = 0x7,
40 NotifyIntr = 0x8,
41 WrcacheFlush = 0x9,
42 Unk28 = 0xA,
43 UnkCacheFlush = 0xB,
44 RefCnt = 0x14,
45 SemaphoreAcquire = 0x1A,
46 SemaphoreRelease = 0x1B,
47 FenceValue = 0x1C,
48 FenceAction = 0x1D,
49 WaitForInterrupt = 0x1E,
50 Unk7c = 0x1F,
51 Yield = 0x20,
52 NonPullerMethods = 0x40,
53};
54
30struct CommandListHeader { 55struct CommandListHeader {
31 union { 56 union {
32 u64 raw; 57 u64 raw;
@@ -49,9 +74,29 @@ union CommandHeader {
49static_assert(std::is_standard_layout_v<CommandHeader>, "CommandHeader is not standard layout"); 74static_assert(std::is_standard_layout_v<CommandHeader>, "CommandHeader is not standard layout");
50static_assert(sizeof(CommandHeader) == sizeof(u32), "CommandHeader has incorrect size!"); 75static_assert(sizeof(CommandHeader) == sizeof(u32), "CommandHeader has incorrect size!");
51 76
77static constexpr CommandHeader BuildCommandHeader(BufferMethods method, u32 arg_count,
78 SubmissionMode mode) {
79 CommandHeader result{};
80 result.method.Assign(static_cast<u32>(method));
81 result.arg_count.Assign(arg_count);
82 result.mode.Assign(mode);
83 return result;
84}
85
52class GPU; 86class GPU;
53 87
54using CommandList = std::vector<Tegra::CommandListHeader>; 88struct CommandList final {
89 CommandList() = default;
90 explicit CommandList(std::size_t size) : command_lists(size) {}
91 explicit CommandList(std::vector<Tegra::CommandHeader>&& prefetch_command_list)
92 : prefetch_command_list{std::move(prefetch_command_list)} {}
93
94 void RefreshIntegrityChecks(GPU& gpu);
95
96 std::vector<Tegra::CommandListHeader> command_lists;
97 std::vector<u64> command_list_hashes;
98 std::vector<Tegra::CommandHeader> prefetch_command_list;
99};
55 100
56/** 101/**
57 * The DmaPusher class implements DMA submission to FIFOs, providing an area of memory that the 102 * The DmaPusher class implements DMA submission to FIFOs, providing an area of memory that the
@@ -60,7 +105,7 @@ using CommandList = std::vector<Tegra::CommandListHeader>;
60 * See https://envytools.readthedocs.io/en/latest/hw/fifo/dma-pusher.html#fifo-dma-pusher for 105 * See https://envytools.readthedocs.io/en/latest/hw/fifo/dma-pusher.html#fifo-dma-pusher for
61 * details on this implementation. 106 * details on this implementation.
62 */ 107 */
63class DmaPusher { 108class DmaPusher final {
64public: 109public:
65 explicit DmaPusher(Core::System& system, GPU& gpu); 110 explicit DmaPusher(Core::System& system, GPU& gpu);
66 ~DmaPusher(); 111 ~DmaPusher();
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index d374b73cf..a3c05d1b0 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -1893,6 +1893,7 @@ public:
1893 ICMP_IMM, 1893 ICMP_IMM,
1894 FCMP_RR, 1894 FCMP_RR,
1895 FCMP_RC, 1895 FCMP_RC,
1896 FCMP_IMMR,
1896 MUFU, // Multi-Function Operator 1897 MUFU, // Multi-Function Operator
1897 RRO_C, // Range Reduction Operator 1898 RRO_C, // Range Reduction Operator
1898 RRO_R, 1899 RRO_R,
@@ -2205,6 +2206,7 @@ private:
2205 INST("0111110-0-------", Id::HSET2_IMM, Type::HalfSet, "HSET2_IMM"), 2206 INST("0111110-0-------", Id::HSET2_IMM, Type::HalfSet, "HSET2_IMM"),
2206 INST("010110111010----", Id::FCMP_RR, Type::Arithmetic, "FCMP_RR"), 2207 INST("010110111010----", Id::FCMP_RR, Type::Arithmetic, "FCMP_RR"),
2207 INST("010010111010----", Id::FCMP_RC, Type::Arithmetic, "FCMP_RC"), 2208 INST("010010111010----", Id::FCMP_RC, Type::Arithmetic, "FCMP_RC"),
2209 INST("0011011-1010----", Id::FCMP_IMMR, Type::Arithmetic, "FCMP_IMMR"),
2208 INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"), 2210 INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"),
2209 INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"), 2211 INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"),
2210 INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"), 2212 INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"),
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 171f78183..ebd149c3a 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -194,30 +194,6 @@ void GPU::SyncGuestHost() {
194void GPU::OnCommandListEnd() { 194void GPU::OnCommandListEnd() {
195 renderer->Rasterizer().ReleaseFences(); 195 renderer->Rasterizer().ReleaseFences();
196} 196}
197// Note that, traditionally, methods are treated as 4-byte addressable locations, and hence
198// their numbers are written down multiplied by 4 in Docs. Here we are not multiply by 4.
199// So the values you see in docs might be multiplied by 4.
200enum class BufferMethods {
201 BindObject = 0x0,
202 Nop = 0x2,
203 SemaphoreAddressHigh = 0x4,
204 SemaphoreAddressLow = 0x5,
205 SemaphoreSequence = 0x6,
206 SemaphoreTrigger = 0x7,
207 NotifyIntr = 0x8,
208 WrcacheFlush = 0x9,
209 Unk28 = 0xA,
210 UnkCacheFlush = 0xB,
211 RefCnt = 0x14,
212 SemaphoreAcquire = 0x1A,
213 SemaphoreRelease = 0x1B,
214 FenceValue = 0x1C,
215 FenceAction = 0x1D,
216 Unk78 = 0x1E,
217 Unk7c = 0x1F,
218 Yield = 0x20,
219 NonPullerMethods = 0x40,
220};
221 197
222enum class GpuSemaphoreOperation { 198enum class GpuSemaphoreOperation {
223 AcquireEqual = 0x1, 199 AcquireEqual = 0x1,
@@ -277,7 +253,12 @@ void GPU::CallPullerMethod(const MethodCall& method_call) {
277 case BufferMethods::UnkCacheFlush: 253 case BufferMethods::UnkCacheFlush:
278 case BufferMethods::WrcacheFlush: 254 case BufferMethods::WrcacheFlush:
279 case BufferMethods::FenceValue: 255 case BufferMethods::FenceValue:
256 break;
280 case BufferMethods::FenceAction: 257 case BufferMethods::FenceAction:
258 ProcessFenceActionMethod();
259 break;
260 case BufferMethods::WaitForInterrupt:
261 ProcessWaitForInterruptMethod();
281 break; 262 break;
282 case BufferMethods::SemaphoreTrigger: { 263 case BufferMethods::SemaphoreTrigger: {
283 ProcessSemaphoreTriggerMethod(); 264 ProcessSemaphoreTriggerMethod();
@@ -391,6 +372,25 @@ void GPU::ProcessBindMethod(const MethodCall& method_call) {
391 } 372 }
392} 373}
393 374
375void GPU::ProcessFenceActionMethod() {
376 switch (regs.fence_action.op) {
377 case FenceOperation::Acquire:
378 WaitFence(regs.fence_action.syncpoint_id, regs.fence_value);
379 break;
380 case FenceOperation::Increment:
381 IncrementSyncPoint(regs.fence_action.syncpoint_id);
382 break;
383 default:
384 UNIMPLEMENTED_MSG("Unimplemented operation {}",
385 static_cast<u32>(regs.fence_action.op.Value()));
386 }
387}
388
389void GPU::ProcessWaitForInterruptMethod() {
390 // TODO(bunnei) ImplementMe
391 LOG_WARNING(HW_GPU, "(STUBBED) called");
392}
393
394void GPU::ProcessSemaphoreTriggerMethod() { 394void GPU::ProcessSemaphoreTriggerMethod() {
395 const auto semaphoreOperationMask = 0xF; 395 const auto semaphoreOperationMask = 0xF;
396 const auto op = 396 const auto op =
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index b8c613b11..5444b49f3 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -263,6 +263,24 @@ public:
263 return use_nvdec; 263 return use_nvdec;
264 } 264 }
265 265
266 enum class FenceOperation : u32 {
267 Acquire = 0,
268 Increment = 1,
269 };
270
271 union FenceAction {
272 u32 raw;
273 BitField<0, 1, FenceOperation> op;
274 BitField<8, 24, u32> syncpoint_id;
275
276 static constexpr CommandHeader Build(FenceOperation op, u32 syncpoint_id) {
277 FenceAction result{};
278 result.op.Assign(op);
279 result.syncpoint_id.Assign(syncpoint_id);
280 return {result.raw};
281 }
282 };
283
266 struct Regs { 284 struct Regs {
267 static constexpr size_t NUM_REGS = 0x40; 285 static constexpr size_t NUM_REGS = 0x40;
268 286
@@ -291,10 +309,7 @@ public:
291 u32 semaphore_acquire; 309 u32 semaphore_acquire;
292 u32 semaphore_release; 310 u32 semaphore_release;
293 u32 fence_value; 311 u32 fence_value;
294 union { 312 FenceAction fence_action;
295 BitField<4, 4, u32> operation;
296 BitField<8, 8, u32> id;
297 } fence_action;
298 INSERT_UNION_PADDING_WORDS(0xE2); 313 INSERT_UNION_PADDING_WORDS(0xE2);
299 314
300 // Puller state 315 // Puller state
@@ -342,6 +357,8 @@ protected:
342 357
343private: 358private:
344 void ProcessBindMethod(const MethodCall& method_call); 359 void ProcessBindMethod(const MethodCall& method_call);
360 void ProcessFenceActionMethod();
361 void ProcessWaitForInterruptMethod();
345 void ProcessSemaphoreTriggerMethod(); 362 void ProcessSemaphoreTriggerMethod();
346 void ProcessSemaphoreRelease(); 363 void ProcessSemaphoreRelease();
347 void ProcessSemaphoreAcquire(); 364 void ProcessSemaphoreAcquire();
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp
index e1217ca83..f34ed6735 100644
--- a/src/video_core/renderer_vulkan/vk_device.cpp
+++ b/src/video_core/renderer_vulkan/vk_device.cpp
@@ -771,13 +771,18 @@ void VKDevice::CollectTelemetryParameters() {
771 VkPhysicalDeviceDriverPropertiesKHR driver{ 771 VkPhysicalDeviceDriverPropertiesKHR driver{
772 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR, 772 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR,
773 .pNext = nullptr, 773 .pNext = nullptr,
774 .driverID = {},
775 .driverName = {},
776 .driverInfo = {},
777 .conformanceVersion = {},
774 }; 778 };
775 779
776 VkPhysicalDeviceProperties2KHR properties{ 780 VkPhysicalDeviceProperties2KHR device_properties{
777 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR, 781 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
778 .pNext = &driver, 782 .pNext = &driver,
783 .properties = {},
779 }; 784 };
780 physical.GetProperties2KHR(properties); 785 physical.GetProperties2KHR(device_properties);
781 786
782 driver_id = driver.driverID; 787 driver_id = driver.driverID;
783 vendor_name = driver.driverName; 788 vendor_name = driver.driverName;
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 696eaeb5f..0e8f9c352 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -159,6 +159,7 @@ std::vector<vk::ShaderModule> VKGraphicsPipeline::CreateShaderModules(
159 .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, 159 .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
160 .pNext = nullptr, 160 .pNext = nullptr,
161 .flags = 0, 161 .flags = 0,
162 .codeSize = 0,
162 }; 163 };
163 164
164 std::vector<vk::ShaderModule> modules; 165 std::vector<vk::ShaderModule> modules;
@@ -388,6 +389,7 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
388 .logicOp = VK_LOGIC_OP_COPY, 389 .logicOp = VK_LOGIC_OP_COPY,
389 .attachmentCount = static_cast<u32>(num_attachments), 390 .attachmentCount = static_cast<u32>(num_attachments),
390 .pAttachments = cb_attachments.data(), 391 .pAttachments = cb_attachments.data(),
392 .blendConstants = {},
391 }; 393 };
392 394
393 std::vector dynamic_states{ 395 std::vector dynamic_states{
diff --git a/src/video_core/shader/async_shaders.cpp b/src/video_core/shader/async_shaders.cpp
index aabd62c5c..39cc3b869 100644
--- a/src/video_core/shader/async_shaders.cpp
+++ b/src/video_core/shader/async_shaders.cpp
@@ -20,14 +20,15 @@ AsyncShaders::~AsyncShaders() {
20} 20}
21 21
22void AsyncShaders::AllocateWorkers() { 22void AsyncShaders::AllocateWorkers() {
23 // Max worker threads we should allow 23 // Use at least one thread
24 constexpr u32 MAX_THREADS = 4; 24 u32 num_workers = 1;
25 // Deduce how many threads we can use 25
26 const u32 threads_used = std::thread::hardware_concurrency() / 4; 26 // Deduce how many more threads we can use
27 // Always allow at least 1 thread regardless of our settings 27 const u32 thread_count = std::thread::hardware_concurrency();
28 const auto max_worker_count = std::max(1U, threads_used); 28 if (thread_count >= 8) {
29 // Don't use more than MAX_THREADS 29 // Increase async workers by 1 for every 2 threads >= 8
30 const auto num_workers = std::min(max_worker_count, MAX_THREADS); 30 num_workers += 1 + (thread_count - 8) / 2;
31 }
31 32
32 // If we already have workers queued, ignore 33 // If we already have workers queued, ignore
33 if (num_workers == worker_threads.size()) { 34 if (num_workers == worker_threads.size()) {
diff --git a/src/video_core/shader/decode/arithmetic.cpp b/src/video_core/shader/decode/arithmetic.cpp
index 4db329fa5..afef5948d 100644
--- a/src/video_core/shader/decode/arithmetic.cpp
+++ b/src/video_core/shader/decode/arithmetic.cpp
@@ -137,7 +137,8 @@ u32 ShaderIR::DecodeArithmetic(NodeBlock& bb, u32 pc) {
137 break; 137 break;
138 } 138 }
139 case OpCode::Id::FCMP_RR: 139 case OpCode::Id::FCMP_RR:
140 case OpCode::Id::FCMP_RC: { 140 case OpCode::Id::FCMP_RC:
141 case OpCode::Id::FCMP_IMMR: {
141 UNIMPLEMENTED_IF(instr.fcmp.ftz == 0); 142 UNIMPLEMENTED_IF(instr.fcmp.ftz == 0);
142 Node op_c = GetRegister(instr.gpr39); 143 Node op_c = GetRegister(instr.gpr39);
143 Node comp = GetPredicateComparisonFloat(instr.fcmp.cond, std::move(op_c), Immediate(0.0f)); 144 Node comp = GetPredicateComparisonFloat(instr.fcmp.cond, std::move(op_c), Immediate(0.0f));
diff --git a/src/video_core/texture_cache/surface_params.cpp b/src/video_core/texture_cache/surface_params.cpp
index e8515321b..13dd16356 100644
--- a/src/video_core/texture_cache/surface_params.cpp
+++ b/src/video_core/texture_cache/surface_params.cpp
@@ -240,6 +240,7 @@ SurfaceParams SurfaceParams::CreateForFermiCopySurface(
240 .is_tiled = is_tiled, 240 .is_tiled = is_tiled,
241 .srgb_conversion = config.format == Tegra::RenderTargetFormat::B8G8R8A8_SRGB || 241 .srgb_conversion = config.format == Tegra::RenderTargetFormat::B8G8R8A8_SRGB ||
242 config.format == Tegra::RenderTargetFormat::A8B8G8R8_SRGB, 242 config.format == Tegra::RenderTargetFormat::A8B8G8R8_SRGB,
243 .is_layered = false,
243 .block_width = is_tiled ? std::min(config.BlockWidth(), 5U) : 0U, 244 .block_width = is_tiled ? std::min(config.BlockWidth(), 5U) : 0U,
244 .block_height = is_tiled ? std::min(config.BlockHeight(), 5U) : 0U, 245 .block_height = is_tiled ? std::min(config.BlockHeight(), 5U) : 0U,
245 .block_depth = is_tiled ? std::min(config.BlockDepth(), 5U) : 0U, 246 .block_depth = is_tiled ? std::min(config.BlockDepth(), 5U) : 0U,
diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp
index c56cd7c71..67183e64c 100644
--- a/src/web_service/web_backend.cpp
+++ b/src/web_service/web_backend.cpp
@@ -65,6 +65,12 @@ struct Client::Impl {
65 if (cli == nullptr) { 65 if (cli == nullptr) {
66 cli = std::make_unique<httplib::Client>(host.c_str()); 66 cli = std::make_unique<httplib::Client>(host.c_str());
67 } 67 }
68
69 if (!cli->is_valid()) {
70 LOG_ERROR(WebService, "Client is invalid, skipping request!");
71 return {};
72 }
73
68 cli->set_connection_timeout(TIMEOUT_SECONDS); 74 cli->set_connection_timeout(TIMEOUT_SECONDS);
69 cli->set_read_timeout(TIMEOUT_SECONDS); 75 cli->set_read_timeout(TIMEOUT_SECONDS);
70 cli->set_write_timeout(TIMEOUT_SECONDS); 76 cli->set_write_timeout(TIMEOUT_SECONDS);
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp
index fa9124ecf..db9518798 100644
--- a/src/yuzu/configuration/configure_audio.cpp
+++ b/src/yuzu/configuration/configure_audio.cpp
@@ -25,8 +25,8 @@ ConfigureAudio::ConfigureAudio(QWidget* parent)
25 connect(ui->output_sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this, 25 connect(ui->output_sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
26 &ConfigureAudio::UpdateAudioDevices); 26 &ConfigureAudio::UpdateAudioDevices);
27 27
28 ui->volume_label->setVisible(Settings::configuring_global); 28 ui->volume_label->setVisible(Settings::IsConfiguringGlobal());
29 ui->volume_combo_box->setVisible(!Settings::configuring_global); 29 ui->volume_combo_box->setVisible(!Settings::IsConfiguringGlobal());
30 30
31 SetupPerGameUI(); 31 SetupPerGameUI();
32 32
@@ -51,7 +51,7 @@ void ConfigureAudio::SetConfiguration() {
51 51
52 ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching.GetValue()); 52 ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching.GetValue());
53 53
54 if (!Settings::configuring_global) { 54 if (!Settings::IsConfiguringGlobal()) {
55 if (Settings::values.volume.UsingGlobal()) { 55 if (Settings::values.volume.UsingGlobal()) {
56 ui->volume_combo_box->setCurrentIndex(0); 56 ui->volume_combo_box->setCurrentIndex(0);
57 ui->volume_slider->setEnabled(false); 57 ui->volume_slider->setEnabled(false);
@@ -99,7 +99,7 @@ void ConfigureAudio::SetVolumeIndicatorText(int percentage) {
99} 99}
100 100
101void ConfigureAudio::ApplyConfiguration() { 101void ConfigureAudio::ApplyConfiguration() {
102 if (Settings::configuring_global) { 102 if (Settings::IsConfiguringGlobal()) {
103 Settings::values.sink_id = 103 Settings::values.sink_id =
104 ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex()) 104 ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex())
105 .toStdString(); 105 .toStdString();
@@ -165,7 +165,7 @@ void ConfigureAudio::RetranslateUI() {
165} 165}
166 166
167void ConfigureAudio::SetupPerGameUI() { 167void ConfigureAudio::SetupPerGameUI() {
168 if (Settings::configuring_global) { 168 if (Settings::IsConfiguringGlobal()) {
169 ui->volume_slider->setEnabled(Settings::values.volume.UsingGlobal()); 169 ui->volume_slider->setEnabled(Settings::values.volume.UsingGlobal());
170 ui->toggle_audio_stretching->setEnabled( 170 ui->toggle_audio_stretching->setEnabled(
171 Settings::values.enable_audio_stretching.UsingGlobal()); 171 Settings::values.enable_audio_stretching.UsingGlobal());
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 8186929a6..5041e0bf8 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -15,7 +15,7 @@
15ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, 15ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry,
16 InputCommon::InputSubsystem* input_subsystem) 16 InputCommon::InputSubsystem* input_subsystem)
17 : QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) { 17 : QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) {
18 Settings::configuring_global = true; 18 Settings::SetConfiguringGlobal(true);
19 19
20 ui->setupUi(this); 20 ui->setupUi(this);
21 ui->hotkeysTab->Populate(registry); 21 ui->hotkeysTab->Populate(registry);
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index 830096ea0..d4d29d422 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -19,7 +19,7 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
19 19
20 SetConfiguration(); 20 SetConfiguration();
21 21
22 if (Settings::configuring_global) { 22 if (Settings::IsConfiguringGlobal()) {
23 connect(ui->toggle_frame_limit, &QCheckBox::clicked, ui->frame_limit, 23 connect(ui->toggle_frame_limit, &QCheckBox::clicked, ui->frame_limit,
24 [this]() { ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked()); }); 24 [this]() { ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked()); });
25 } 25 }
@@ -41,7 +41,7 @@ void ConfigureGeneral::SetConfiguration() {
41 ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit.GetValue()); 41 ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit.GetValue());
42 ui->frame_limit->setValue(Settings::values.frame_limit.GetValue()); 42 ui->frame_limit->setValue(Settings::values.frame_limit.GetValue());
43 43
44 if (Settings::configuring_global) { 44 if (Settings::IsConfiguringGlobal()) {
45 ui->frame_limit->setEnabled(Settings::values.use_frame_limit.GetValue()); 45 ui->frame_limit->setEnabled(Settings::values.use_frame_limit.GetValue());
46 } else { 46 } else {
47 ui->frame_limit->setEnabled(Settings::values.use_frame_limit.GetValue() && 47 ui->frame_limit->setEnabled(Settings::values.use_frame_limit.GetValue() &&
@@ -50,7 +50,7 @@ void ConfigureGeneral::SetConfiguration() {
50} 50}
51 51
52void ConfigureGeneral::ApplyConfiguration() { 52void ConfigureGeneral::ApplyConfiguration() {
53 if (Settings::configuring_global) { 53 if (Settings::IsConfiguringGlobal()) {
54 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); 54 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
55 UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); 55 UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked();
56 UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked(); 56 UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked();
@@ -93,7 +93,7 @@ void ConfigureGeneral::RetranslateUI() {
93} 93}
94 94
95void ConfigureGeneral::SetupPerGameUI() { 95void ConfigureGeneral::SetupPerGameUI() {
96 if (Settings::configuring_global) { 96 if (Settings::IsConfiguringGlobal()) {
97 ui->toggle_frame_limit->setEnabled(Settings::values.use_frame_limit.UsingGlobal()); 97 ui->toggle_frame_limit->setEnabled(Settings::values.use_frame_limit.UsingGlobal());
98 ui->frame_limit->setEnabled(Settings::values.frame_limit.UsingGlobal()); 98 ui->frame_limit->setEnabled(Settings::values.frame_limit.UsingGlobal());
99 99
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 4f083ecda..6fda0ce35 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -33,7 +33,7 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
33 33
34 connect(ui->api, qOverload<int>(&QComboBox::currentIndexChanged), this, [this] { 34 connect(ui->api, qOverload<int>(&QComboBox::currentIndexChanged), this, [this] {
35 UpdateDeviceComboBox(); 35 UpdateDeviceComboBox();
36 if (!Settings::configuring_global) { 36 if (!Settings::IsConfiguringGlobal()) {
37 ConfigurationShared::SetHighlight( 37 ConfigurationShared::SetHighlight(
38 ui->api_layout, ui->api->currentIndex() != ConfigurationShared::USE_GLOBAL_INDEX); 38 ui->api_layout, ui->api->currentIndex() != ConfigurationShared::USE_GLOBAL_INDEX);
39 } 39 }
@@ -49,8 +49,8 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
49 UpdateBackgroundColorButton(new_bg_color); 49 UpdateBackgroundColorButton(new_bg_color);
50 }); 50 });
51 51
52 ui->bg_label->setVisible(Settings::configuring_global); 52 ui->bg_label->setVisible(Settings::IsConfiguringGlobal());
53 ui->bg_combobox->setVisible(!Settings::configuring_global); 53 ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal());
54} 54}
55 55
56void ConfigureGraphics::UpdateDeviceSelection(int device) { 56void ConfigureGraphics::UpdateDeviceSelection(int device) {
@@ -76,7 +76,7 @@ void ConfigureGraphics::SetConfiguration() {
76 Settings::values.use_asynchronous_gpu_emulation.GetValue()); 76 Settings::values.use_asynchronous_gpu_emulation.GetValue());
77 ui->use_nvdec_emulation->setChecked(Settings::values.use_nvdec_emulation.GetValue()); 77 ui->use_nvdec_emulation->setChecked(Settings::values.use_nvdec_emulation.GetValue());
78 78
79 if (Settings::configuring_global) { 79 if (Settings::IsConfiguringGlobal()) {
80 ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue())); 80 ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue()));
81 ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue()); 81 ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue());
82 } else { 82 } else {
@@ -100,7 +100,7 @@ void ConfigureGraphics::SetConfiguration() {
100} 100}
101 101
102void ConfigureGraphics::ApplyConfiguration() { 102void ConfigureGraphics::ApplyConfiguration() {
103 if (Settings::configuring_global) { 103 if (Settings::IsConfiguringGlobal()) {
104 // Guard if during game and set to game-specific value 104 // Guard if during game and set to game-specific value
105 if (Settings::values.renderer_backend.UsingGlobal()) { 105 if (Settings::values.renderer_backend.UsingGlobal()) {
106 Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend()); 106 Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend());
@@ -194,7 +194,7 @@ void ConfigureGraphics::UpdateDeviceComboBox() {
194 194
195 bool enabled = false; 195 bool enabled = false;
196 196
197 if (!Settings::configuring_global && 197 if (!Settings::IsConfiguringGlobal() &&
198 ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { 198 ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
199 vulkan_device = Settings::values.vulkan_device.GetValue(); 199 vulkan_device = Settings::values.vulkan_device.GetValue();
200 } 200 }
@@ -212,7 +212,7 @@ void ConfigureGraphics::UpdateDeviceComboBox() {
212 break; 212 break;
213 } 213 }
214 // If in per-game config and use global is selected, don't enable. 214 // If in per-game config and use global is selected, don't enable.
215 enabled &= !(!Settings::configuring_global && 215 enabled &= !(!Settings::IsConfiguringGlobal() &&
216 ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX); 216 ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX);
217 ui->device->setEnabled(enabled && !Core::System::GetInstance().IsPoweredOn()); 217 ui->device->setEnabled(enabled && !Core::System::GetInstance().IsPoweredOn());
218} 218}
@@ -227,7 +227,7 @@ void ConfigureGraphics::RetrieveVulkanDevices() {
227} 227}
228 228
229Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { 229Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
230 if (Settings::configuring_global) { 230 if (Settings::IsConfiguringGlobal()) {
231 return static_cast<Settings::RendererBackend>(ui->api->currentIndex()); 231 return static_cast<Settings::RendererBackend>(ui->api->currentIndex());
232 } 232 }
233 233
@@ -241,7 +241,7 @@ Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
241} 241}
242 242
243void ConfigureGraphics::SetupPerGameUI() { 243void ConfigureGraphics::SetupPerGameUI() {
244 if (Settings::configuring_global) { 244 if (Settings::IsConfiguringGlobal()) {
245 ui->api->setEnabled(Settings::values.renderer_backend.UsingGlobal()); 245 ui->api->setEnabled(Settings::values.renderer_backend.UsingGlobal());
246 ui->device->setEnabled(Settings::values.renderer_backend.UsingGlobal()); 246 ui->device->setEnabled(Settings::values.renderer_backend.UsingGlobal());
247 ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal()); 247 ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal());
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp
index 73f276949..383c7bac8 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.cpp
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -32,7 +32,7 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
32 ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue()); 32 ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue());
33 ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue()); 33 ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue());
34 34
35 if (Settings::configuring_global) { 35 if (Settings::IsConfiguringGlobal()) {
36 ui->gpu_accuracy->setCurrentIndex( 36 ui->gpu_accuracy->setCurrentIndex(
37 static_cast<int>(Settings::values.gpu_accuracy.GetValue())); 37 static_cast<int>(Settings::values.gpu_accuracy.GetValue()));
38 ui->anisotropic_filtering_combobox->setCurrentIndex( 38 ui->anisotropic_filtering_combobox->setCurrentIndex(
@@ -52,9 +52,9 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
52 // Subtract 2 if configuring per-game (separator and "use global configuration" take 2 slots) 52 // Subtract 2 if configuring per-game (separator and "use global configuration" take 2 slots)
53 const auto gpu_accuracy = static_cast<Settings::GPUAccuracy>( 53 const auto gpu_accuracy = static_cast<Settings::GPUAccuracy>(
54 ui->gpu_accuracy->currentIndex() - 54 ui->gpu_accuracy->currentIndex() -
55 ((Settings::configuring_global) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET)); 55 ((Settings::IsConfiguringGlobal()) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET));
56 56
57 if (Settings::configuring_global) { 57 if (Settings::IsConfiguringGlobal()) {
58 // Must guard in case of a during-game configuration when set to be game-specific. 58 // Must guard in case of a during-game configuration when set to be game-specific.
59 if (Settings::values.gpu_accuracy.UsingGlobal()) { 59 if (Settings::values.gpu_accuracy.UsingGlobal()) {
60 Settings::values.gpu_accuracy.SetValue(gpu_accuracy); 60 Settings::values.gpu_accuracy.SetValue(gpu_accuracy);
@@ -118,7 +118,7 @@ void ConfigureGraphicsAdvanced::RetranslateUI() {
118 118
119void ConfigureGraphicsAdvanced::SetupPerGameUI() { 119void ConfigureGraphicsAdvanced::SetupPerGameUI() {
120 // Disable if not global (only happens during game) 120 // Disable if not global (only happens during game)
121 if (Settings::configuring_global) { 121 if (Settings::IsConfiguringGlobal()) {
122 ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal()); 122 ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal());
123 ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal()); 123 ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal());
124 ui->use_assembly_shaders->setEnabled(Settings::values.use_assembly_shaders.UsingGlobal()); 124 ui->use_assembly_shaders->setEnabled(Settings::values.use_assembly_shaders.UsingGlobal());
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
index 1e49f0787..002db3f93 100644
--- a/src/yuzu/configuration/configure_per_game.cpp
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -31,7 +31,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id)
31 : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()), title_id(title_id) { 31 : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()), title_id(title_id) {
32 game_config = std::make_unique<Config>(fmt::format("{:016X}.ini", title_id), false); 32 game_config = std::make_unique<Config>(fmt::format("{:016X}.ini", title_id), false);
33 33
34 Settings::configuring_global = false; 34 Settings::SetConfiguringGlobal(false);
35 35
36 ui->setupUi(this); 36 ui->setupUi(this);
37 setFocusPolicy(Qt::ClickFocus); 37 setFocusPolicy(Qt::ClickFocus);
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 5e8e201dc..59a58d92c 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -37,8 +37,8 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::
37 } 37 }
38 }); 38 });
39 39
40 ui->label_console_id->setVisible(Settings::configuring_global); 40 ui->label_console_id->setVisible(Settings::IsConfiguringGlobal());
41 ui->button_regenerate_console_id->setVisible(Settings::configuring_global); 41 ui->button_regenerate_console_id->setVisible(Settings::IsConfiguringGlobal());
42 42
43 SetupPerGameUI(); 43 SetupPerGameUI();
44 44
@@ -78,7 +78,7 @@ void ConfigureSystem::SetConfiguration() {
78 Settings::values.rng_seed.UsingGlobal()); 78 Settings::values.rng_seed.UsingGlobal());
79 ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time.count())); 79 ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time.count()));
80 80
81 if (Settings::configuring_global) { 81 if (Settings::IsConfiguringGlobal()) {
82 ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue()); 82 ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue());
83 ui->combo_region->setCurrentIndex(Settings::values.region_index.GetValue()); 83 ui->combo_region->setCurrentIndex(Settings::values.region_index.GetValue());
84 ui->combo_time_zone->setCurrentIndex(Settings::values.time_zone_index.GetValue()); 84 ui->combo_time_zone->setCurrentIndex(Settings::values.time_zone_index.GetValue());
@@ -125,7 +125,7 @@ void ConfigureSystem::ApplyConfiguration() {
125 return; 125 return;
126 } 126 }
127 127
128 if (Settings::configuring_global) { 128 if (Settings::IsConfiguringGlobal()) {
129 // Guard if during game and set to game-specific value 129 // Guard if during game and set to game-specific value
130 if (Settings::values.language_index.UsingGlobal()) { 130 if (Settings::values.language_index.UsingGlobal()) {
131 Settings::values.language_index.SetValue(ui->combo_language->currentIndex()); 131 Settings::values.language_index.SetValue(ui->combo_language->currentIndex());
@@ -218,7 +218,7 @@ void ConfigureSystem::RefreshConsoleID() {
218} 218}
219 219
220void ConfigureSystem::SetupPerGameUI() { 220void ConfigureSystem::SetupPerGameUI() {
221 if (Settings::configuring_global) { 221 if (Settings::IsConfiguringGlobal()) {
222 ui->combo_language->setEnabled(Settings::values.language_index.UsingGlobal()); 222 ui->combo_language->setEnabled(Settings::values.language_index.UsingGlobal());
223 ui->combo_region->setEnabled(Settings::values.region_index.UsingGlobal()); 223 ui->combo_region->setEnabled(Settings::values.region_index.UsingGlobal());
224 ui->combo_time_zone->setEnabled(Settings::values.time_zone_index.UsingGlobal()); 224 ui->combo_time_zone->setEnabled(Settings::values.time_zone_index.UsingGlobal());