summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules2
-rw-r--r--CMakeLists.txt2
-rw-r--r--externals/httplib/README.md2
-rw-r--r--externals/httplib/httplib.h651
m---------externals/inih/inih0
-rw-r--r--externals/microprofile/microprofile.h18
-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
79 files changed, 1730 insertions, 933 deletions
diff --git a/.gitmodules b/.gitmodules
index 9d9356151..6931a641d 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,6 @@
1[submodule "inih"] 1[submodule "inih"]
2 path = externals/inih/inih 2 path = externals/inih/inih
3 url = https://github.com/svn2github/inih 3 url = https://github.com/benhoyt/inih.git
4[submodule "cubeb"] 4[submodule "cubeb"]
5 path = externals/cubeb 5 path = externals/cubeb
6 url = https://github.com/kinetiknz/cubeb.git 6 url = https://github.com/kinetiknz/cubeb.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8e9502a97..1bd9d2aa7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -161,7 +161,7 @@ macro(yuzu_find_packages)
161 # Cmake Pkg Prefix Version Conan Pkg 161 # Cmake Pkg Prefix Version Conan Pkg
162 "Boost 1.73 boost/1.73.0" 162 "Boost 1.73 boost/1.73.0"
163 "Catch2 2.13 catch2/2.13.0" 163 "Catch2 2.13 catch2/2.13.0"
164 "fmt 7.0 fmt/7.0.3" 164 "fmt 7.1 fmt/7.1.0"
165 # can't use until https://github.com/bincrafters/community/issues/1173 165 # can't use until https://github.com/bincrafters/community/issues/1173
166 #"libzip 1.5 libzip/1.5.2@bincrafters/stable" 166 #"libzip 1.5 libzip/1.5.2@bincrafters/stable"
167 "lz4 1.8 lz4/1.9.2" 167 "lz4 1.8 lz4/1.9.2"
diff --git a/externals/httplib/README.md b/externals/httplib/README.md
index 73037d297..1940e446c 100644
--- a/externals/httplib/README.md
+++ b/externals/httplib/README.md
@@ -1,4 +1,4 @@
1From https://github.com/yhirose/cpp-httplib/tree/fce8e6fefdab4ad48bc5b25c98e5ebfda4f3cf53 1From https://github.com/yhirose/cpp-httplib/tree/ff5677ad197947177c158fe857caff4f0e242045 with https://github.com/yhirose/cpp-httplib/pull/701
2 2
3MIT License 3MIT License
4 4
diff --git a/externals/httplib/httplib.h b/externals/httplib/httplib.h
index 5139b7f05..8982054e2 100644
--- a/externals/httplib/httplib.h
+++ b/externals/httplib/httplib.h
@@ -173,6 +173,7 @@ using socket_t = int;
173#define INVALID_SOCKET (-1) 173#define INVALID_SOCKET (-1)
174#endif //_WIN32 174#endif //_WIN32
175 175
176#include <algorithm>
176#include <array> 177#include <array>
177#include <atomic> 178#include <atomic>
178#include <cassert> 179#include <cassert>
@@ -237,6 +238,27 @@ namespace httplib {
237 238
238namespace detail { 239namespace detail {
239 240
241/*
242 * Backport std::make_unique from C++14.
243 *
244 * NOTE: This code came up with the following stackoverflow post:
245 * https://stackoverflow.com/questions/10149840/c-arrays-and-make-unique
246 *
247 */
248
249template <class T, class... Args>
250typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
251make_unique(Args &&... args) {
252 return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
253}
254
255template <class T>
256typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
257make_unique(std::size_t n) {
258 typedef typename std::remove_extent<T>::type RT;
259 return std::unique_ptr<T>(new RT[n]);
260}
261
240struct ci { 262struct ci {
241 bool operator()(const std::string &s1, const std::string &s2) const { 263 bool operator()(const std::string &s1, const std::string &s2) const {
242 return std::lexicographical_compare( 264 return std::lexicographical_compare(
@@ -304,6 +326,10 @@ using ContentProvider =
304using ContentProviderWithoutLength = 326using ContentProviderWithoutLength =
305 std::function<bool(size_t offset, DataSink &sink)>; 327 std::function<bool(size_t offset, DataSink &sink)>;
306 328
329using ContentReceiverWithProgress =
330 std::function<bool(const char *data, size_t data_length, uint64_t offset,
331 uint64_t total_length)>;
332
307using ContentReceiver = 333using ContentReceiver =
308 std::function<bool(const char *data, size_t data_length)>; 334 std::function<bool(const char *data, size_t data_length)>;
309 335
@@ -317,14 +343,17 @@ public:
317 ContentReceiver receiver)>; 343 ContentReceiver receiver)>;
318 344
319 ContentReader(Reader reader, MultipartReader multipart_reader) 345 ContentReader(Reader reader, MultipartReader multipart_reader)
320 : reader_(reader), multipart_reader_(multipart_reader) {} 346 : reader_(std::move(reader)),
347 multipart_reader_(std::move(multipart_reader)) {}
321 348
322 bool operator()(MultipartContentHeader header, 349 bool operator()(MultipartContentHeader header,
323 ContentReceiver receiver) const { 350 ContentReceiver receiver) const {
324 return multipart_reader_(header, receiver); 351 return multipart_reader_(std::move(header), std::move(receiver));
325 } 352 }
326 353
327 bool operator()(ContentReceiver receiver) const { return reader_(receiver); } 354 bool operator()(ContentReceiver receiver) const {
355 return reader_(std::move(receiver));
356 }
328 357
329 Reader reader_; 358 Reader reader_;
330 MultipartReader multipart_reader_; 359 MultipartReader multipart_reader_;
@@ -353,7 +382,7 @@ struct Request {
353 // for client 382 // for client
354 size_t redirect_count = CPPHTTPLIB_REDIRECT_MAX_COUNT; 383 size_t redirect_count = CPPHTTPLIB_REDIRECT_MAX_COUNT;
355 ResponseHandler response_handler; 384 ResponseHandler response_handler;
356 ContentReceiver content_receiver; 385 ContentReceiverWithProgress content_receiver;
357 size_t content_length = 0; 386 size_t content_length = 0;
358 ContentProvider content_provider; 387 ContentProvider content_provider;
359 Progress progress; 388 Progress progress;
@@ -475,7 +504,7 @@ public:
475 504
476 void enqueue(std::function<void()> fn) override { 505 void enqueue(std::function<void()> fn) override {
477 std::unique_lock<std::mutex> lock(mutex_); 506 std::unique_lock<std::mutex> lock(mutex_);
478 jobs_.push_back(fn); 507 jobs_.push_back(std::move(fn));
479 cond_.notify_one(); 508 cond_.notify_one();
480 } 509 }
481 510
@@ -664,8 +693,8 @@ private:
664 ContentReceiver multipart_receiver); 693 ContentReceiver multipart_receiver);
665 694
666 virtual bool process_and_close_socket(socket_t sock); 695 virtual bool process_and_close_socket(socket_t sock);
667 696
668 struct MountPointEntry { 697 struct MountPointEntry {
669 std::string mount_point; 698 std::string mount_point;
670 std::string base_dir; 699 std::string base_dir;
671 Headers headers; 700 Headers headers;
@@ -704,23 +733,27 @@ enum Error {
704 Canceled, 733 Canceled,
705 SSLConnection, 734 SSLConnection,
706 SSLLoadingCerts, 735 SSLLoadingCerts,
707 SSLServerVerification 736 SSLServerVerification,
737 UnsupportedMultipartBoundaryChars
708}; 738};
709 739
710class Result { 740class Result {
711public: 741public:
712 Result(const std::shared_ptr<Response> &res, Error err) 742 Result(std::unique_ptr<Response> res, Error err)
713 : res_(res), err_(err) {} 743 : res_(std::move(res)), err_(err) {}
714 operator bool() const { return res_ != nullptr; } 744 operator bool() const { return res_ != nullptr; }
715 bool operator==(std::nullptr_t) const { return res_ == nullptr; } 745 bool operator==(std::nullptr_t) const { return res_ == nullptr; }
716 bool operator!=(std::nullptr_t) const { return res_ != nullptr; } 746 bool operator!=(std::nullptr_t) const { return res_ != nullptr; }
717 const Response &value() const { return *res_; } 747 const Response &value() const { return *res_; }
748 Response &value() { return *res_; }
718 const Response &operator*() const { return *res_; } 749 const Response &operator*() const { return *res_; }
750 Response &operator*() { return *res_; }
719 const Response *operator->() const { return res_.get(); } 751 const Response *operator->() const { return res_.get(); }
752 Response *operator->() { return res_.get(); }
720 Error error() const { return err_; } 753 Error error() const { return err_; }
721 754
722private: 755private:
723 std::shared_ptr<Response> res_; 756 std::unique_ptr<Response> res_;
724 Error err_; 757 Error err_;
725}; 758};
726 759
@@ -777,6 +810,8 @@ public:
777 Result Post(const char *path, const MultipartFormDataItems &items); 810 Result Post(const char *path, const MultipartFormDataItems &items);
778 Result Post(const char *path, const Headers &headers, 811 Result Post(const char *path, const Headers &headers,
779 const MultipartFormDataItems &items); 812 const MultipartFormDataItems &items);
813 Result Post(const char *path, const Headers &headers,
814 const MultipartFormDataItems &items, const std::string &boundary);
780 815
781 Result Put(const char *path); 816 Result Put(const char *path);
782 Result Put(const char *path, const std::string &body, 817 Result Put(const char *path, const std::string &body,
@@ -863,7 +898,21 @@ protected:
863 }; 898 };
864 899
865 virtual bool create_and_connect_socket(Socket &socket); 900 virtual bool create_and_connect_socket(Socket &socket);
866 virtual void close_socket(Socket &socket, bool process_socket_ret); 901
902 // All of:
903 // shutdown_ssl
904 // shutdown_socket
905 // close_socket
906 // should ONLY be called when socket_mutex_ is locked.
907 // Also, shutdown_ssl and close_socket should also NOT be called concurrently
908 // with a DIFFERENT thread sending requests using that socket.
909 virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully);
910 void shutdown_socket(Socket &socket);
911 void close_socket(Socket &socket);
912
913 // Similar to shutdown_ssl and close_socket, this should NOT be called
914 // concurrently with a DIFFERENT thread sending requests from the socket
915 void lock_socket_and_shutdown_and_close();
867 916
868 bool process_request(Stream &strm, const Request &req, Response &res, 917 bool process_request(Stream &strm, const Request &req, Response &res,
869 bool close_connection); 918 bool close_connection);
@@ -873,7 +922,7 @@ protected:
873 void copy_settings(const ClientImpl &rhs); 922 void copy_settings(const ClientImpl &rhs);
874 923
875 // Error state 924 // Error state
876 mutable Error error_ = Error::Success; 925 mutable std::atomic<Error> error_;
877 926
878 // Socket endoint information 927 // Socket endoint information
879 const std::string host_; 928 const std::string host_;
@@ -885,6 +934,11 @@ protected:
885 mutable std::mutex socket_mutex_; 934 mutable std::mutex socket_mutex_;
886 std::recursive_mutex request_mutex_; 935 std::recursive_mutex request_mutex_;
887 936
937 // These are all protected under socket_mutex
938 int socket_requests_in_flight_ = 0;
939 std::thread::id socket_requests_are_from_thread_ = std::thread::id();
940 bool socket_should_be_closed_when_request_is_done_ = false;
941
888 // Default headers 942 // Default headers
889 Headers default_headers_; 943 Headers default_headers_;
890 944
@@ -942,13 +996,13 @@ private:
942 bool redirect(const Request &req, Response &res); 996 bool redirect(const Request &req, Response &res);
943 bool handle_request(Stream &strm, const Request &req, Response &res, 997 bool handle_request(Stream &strm, const Request &req, Response &res,
944 bool close_connection); 998 bool close_connection);
945 void stop_core(); 999 std::unique_ptr<Response> send_with_content_provider(
946 std::shared_ptr<Response> send_with_content_provider(
947 const char *method, const char *path, const Headers &headers, 1000 const char *method, const char *path, const Headers &headers,
948 const std::string &body, size_t content_length, 1001 const std::string &body, size_t content_length,
949 ContentProvider content_provider, const char *content_type); 1002 ContentProvider content_provider, const char *content_type);
950 1003
951 virtual bool process_socket(Socket &socket, 1004 // socket is const because this function is called when socket_mutex_ is not locked
1005 virtual bool process_socket(const Socket &socket,
952 std::function<bool(Stream &strm)> callback); 1006 std::function<bool(Stream &strm)> callback);
953 virtual bool is_ssl() const; 1007 virtual bool is_ssl() const;
954}; 1008};
@@ -1012,6 +1066,8 @@ public:
1012 Result Post(const char *path, const MultipartFormDataItems &items); 1066 Result Post(const char *path, const MultipartFormDataItems &items);
1013 Result Post(const char *path, const Headers &headers, 1067 Result Post(const char *path, const Headers &headers,
1014 const MultipartFormDataItems &items); 1068 const MultipartFormDataItems &items);
1069 Result Post(const char *path, const Headers &headers,
1070 const MultipartFormDataItems &items, const std::string &boundary);
1015 Result Put(const char *path); 1071 Result Put(const char *path);
1016 Result Put(const char *path, const std::string &body, 1072 Result Put(const char *path, const std::string &body,
1017 const char *content_type); 1073 const char *content_type);
@@ -1098,7 +1154,7 @@ public:
1098#endif 1154#endif
1099 1155
1100private: 1156private:
1101 std::shared_ptr<ClientImpl> cli_; 1157 std::unique_ptr<ClientImpl> cli_;
1102 1158
1103#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 1159#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1104 bool is_ssl_ = false; 1160 bool is_ssl_ = false;
@@ -1154,9 +1210,9 @@ public:
1154 1210
1155private: 1211private:
1156 bool create_and_connect_socket(Socket &socket) override; 1212 bool create_and_connect_socket(Socket &socket) override;
1157 void close_socket(Socket &socket, bool process_socket_ret) override; 1213 void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override;
1158 1214
1159 bool process_socket(Socket &socket, 1215 bool process_socket(const Socket &socket,
1160 std::function<bool(Stream &strm)> callback) override; 1216 std::function<bool(Stream &strm)> callback) override;
1161 bool is_ssl() const override; 1217 bool is_ssl() const override;
1162 1218
@@ -1942,9 +1998,9 @@ inline socket_t create_client_socket(const char *host, int port,
1942 bool tcp_nodelay, 1998 bool tcp_nodelay,
1943 SocketOptions socket_options, 1999 SocketOptions socket_options,
1944 time_t timeout_sec, time_t timeout_usec, 2000 time_t timeout_sec, time_t timeout_usec,
1945 const std::string &intf, Error &error) { 2001 const std::string &intf, std::atomic<Error> &error) {
1946 auto sock = create_socket( 2002 auto sock = create_socket(
1947 host, port, 0, tcp_nodelay, socket_options, 2003 host, port, 0, tcp_nodelay, std::move(socket_options),
1948 [&](socket_t sock, struct addrinfo &ai) -> bool { 2004 [&](socket_t sock, struct addrinfo &ai) -> bool {
1949 if (!intf.empty()) { 2005 if (!intf.empty()) {
1950#ifdef USE_IF2IP 2006#ifdef USE_IF2IP
@@ -2478,7 +2534,8 @@ inline bool read_headers(Stream &strm, Headers &headers) {
2478} 2534}
2479 2535
2480inline bool read_content_with_length(Stream &strm, uint64_t len, 2536inline bool read_content_with_length(Stream &strm, uint64_t len,
2481 Progress progress, ContentReceiver out) { 2537 Progress progress,
2538 ContentReceiverWithProgress out) {
2482 char buf[CPPHTTPLIB_RECV_BUFSIZ]; 2539 char buf[CPPHTTPLIB_RECV_BUFSIZ];
2483 2540
2484 uint64_t r = 0; 2541 uint64_t r = 0;
@@ -2487,8 +2544,7 @@ inline bool read_content_with_length(Stream &strm, uint64_t len,
2487 auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ)); 2544 auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
2488 if (n <= 0) { return false; } 2545 if (n <= 0) { return false; }
2489 2546
2490 if (!out(buf, static_cast<size_t>(n))) { return false; } 2547 if (!out(buf, static_cast<size_t>(n), r, len)) { return false; }
2491
2492 r += static_cast<uint64_t>(n); 2548 r += static_cast<uint64_t>(n);
2493 2549
2494 if (progress) { 2550 if (progress) {
@@ -2510,8 +2566,10 @@ inline void skip_content_with_length(Stream &strm, uint64_t len) {
2510 } 2566 }
2511} 2567}
2512 2568
2513inline bool read_content_without_length(Stream &strm, ContentReceiver out) { 2569inline bool read_content_without_length(Stream &strm,
2570 ContentReceiverWithProgress out) {
2514 char buf[CPPHTTPLIB_RECV_BUFSIZ]; 2571 char buf[CPPHTTPLIB_RECV_BUFSIZ];
2572 uint64_t r = 0;
2515 for (;;) { 2573 for (;;) {
2516 auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ); 2574 auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);
2517 if (n < 0) { 2575 if (n < 0) {
@@ -2519,13 +2577,16 @@ inline bool read_content_without_length(Stream &strm, ContentReceiver out) {
2519 } else if (n == 0) { 2577 } else if (n == 0) {
2520 return true; 2578 return true;
2521 } 2579 }
2522 if (!out(buf, static_cast<size_t>(n))) { return false; } 2580
2581 if (!out(buf, static_cast<size_t>(n), r, 0)) { return false; }
2582 r += static_cast<uint64_t>(n);
2523 } 2583 }
2524 2584
2525 return true; 2585 return true;
2526} 2586}
2527 2587
2528inline bool read_content_chunked(Stream &strm, ContentReceiver out) { 2588inline bool read_content_chunked(Stream &strm,
2589 ContentReceiverWithProgress out) {
2529 const auto bufsiz = 16; 2590 const auto bufsiz = 16;
2530 char buf[bufsiz]; 2591 char buf[bufsiz];
2531 2592
@@ -2570,23 +2631,24 @@ inline bool is_chunked_transfer_encoding(const Headers &headers) {
2570} 2631}
2571 2632
2572template <typename T, typename U> 2633template <typename T, typename U>
2573bool prepare_content_receiver(T &x, int &status, ContentReceiver receiver, 2634bool prepare_content_receiver(T &x, int &status,
2635 ContentReceiverWithProgress receiver,
2574 bool decompress, U callback) { 2636 bool decompress, U callback) {
2575 if (decompress) { 2637 if (decompress) {
2576 std::string encoding = x.get_header_value("Content-Encoding"); 2638 std::string encoding = x.get_header_value("Content-Encoding");
2577 std::shared_ptr<decompressor> decompressor; 2639 std::unique_ptr<decompressor> decompressor;
2578 2640
2579 if (encoding.find("gzip") != std::string::npos || 2641 if (encoding.find("gzip") != std::string::npos ||
2580 encoding.find("deflate") != std::string::npos) { 2642 encoding.find("deflate") != std::string::npos) {
2581#ifdef CPPHTTPLIB_ZLIB_SUPPORT 2643#ifdef CPPHTTPLIB_ZLIB_SUPPORT
2582 decompressor = std::make_shared<gzip_decompressor>(); 2644 decompressor = detail::make_unique<gzip_decompressor>();
2583#else 2645#else
2584 status = 415; 2646 status = 415;
2585 return false; 2647 return false;
2586#endif 2648#endif
2587 } else if (encoding.find("br") != std::string::npos) { 2649 } else if (encoding.find("br") != std::string::npos) {
2588#ifdef CPPHTTPLIB_BROTLI_SUPPORT 2650#ifdef CPPHTTPLIB_BROTLI_SUPPORT
2589 decompressor = std::make_shared<brotli_decompressor>(); 2651 decompressor = detail::make_unique<brotli_decompressor>();
2590#else 2652#else
2591 status = 415; 2653 status = 415;
2592 return false; 2654 return false;
@@ -2595,12 +2657,14 @@ bool prepare_content_receiver(T &x, int &status, ContentReceiver receiver,
2595 2657
2596 if (decompressor) { 2658 if (decompressor) {
2597 if (decompressor->is_valid()) { 2659 if (decompressor->is_valid()) {
2598 ContentReceiver out = [&](const char *buf, size_t n) { 2660 ContentReceiverWithProgress out = [&](const char *buf, size_t n,
2599 return decompressor->decompress( 2661 uint64_t off, uint64_t len) {
2600 buf, n, 2662 return decompressor->decompress(buf, n,
2601 [&](const char *buf, size_t n) { return receiver(buf, n); }); 2663 [&](const char *buf, size_t n) {
2664 return receiver(buf, n, off, len);
2665 });
2602 }; 2666 };
2603 return callback(out); 2667 return callback(std::move(out));
2604 } else { 2668 } else {
2605 status = 500; 2669 status = 500;
2606 return false; 2670 return false;
@@ -2608,18 +2672,20 @@ bool prepare_content_receiver(T &x, int &status, ContentReceiver receiver,
2608 } 2672 }
2609 } 2673 }
2610 2674
2611 ContentReceiver out = [&](const char *buf, size_t n) { 2675 ContentReceiverWithProgress out = [&](const char *buf, size_t n, uint64_t off,
2612 return receiver(buf, n); 2676 uint64_t len) {
2677 return receiver(buf, n, off, len);
2613 }; 2678 };
2614 return callback(out); 2679 return callback(std::move(out));
2615} 2680}
2616 2681
2617template <typename T> 2682template <typename T>
2618bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status, 2683bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
2619 Progress progress, ContentReceiver receiver, 2684 Progress progress, ContentReceiverWithProgress receiver,
2620 bool decompress) { 2685 bool decompress) {
2621 return prepare_content_receiver( 2686 return prepare_content_receiver(
2622 x, status, receiver, decompress, [&](const ContentReceiver &out) { 2687 x, status, std::move(receiver), decompress,
2688 [&](const ContentReceiverWithProgress &out) {
2623 auto ret = true; 2689 auto ret = true;
2624 auto exceed_payload_max_length = false; 2690 auto exceed_payload_max_length = false;
2625 2691
@@ -2634,7 +2700,7 @@ bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
2634 skip_content_with_length(strm, len); 2700 skip_content_with_length(strm, len);
2635 ret = false; 2701 ret = false;
2636 } else if (len > 0) { 2702 } else if (len > 0) {
2637 ret = read_content_with_length(strm, len, progress, out); 2703 ret = read_content_with_length(strm, len, std::move(progress), out);
2638 } 2704 }
2639 } 2705 }
2640 2706
@@ -2875,7 +2941,7 @@ inline bool parse_multipart_boundary(const std::string &content_type,
2875 return !boundary.empty(); 2941 return !boundary.empty();
2876} 2942}
2877 2943
2878inline bool parse_range_header(const std::string &s, Ranges &ranges) { 2944inline bool parse_range_header(const std::string &s, Ranges &ranges) try {
2879 static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))"); 2945 static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))");
2880 std::smatch m; 2946 std::smatch m;
2881 if (std::regex_match(s, m, re_first_range)) { 2947 if (std::regex_match(s, m, re_first_range)) {
@@ -2907,7 +2973,7 @@ inline bool parse_range_header(const std::string &s, Ranges &ranges) {
2907 return all_valid_ranges; 2973 return all_valid_ranges;
2908 } 2974 }
2909 return false; 2975 return false;
2910} 2976} catch (...) { return false; }
2911 2977
2912class MultipartFormDataParser { 2978class MultipartFormDataParser {
2913public: 2979public:
@@ -2918,7 +2984,8 @@ public:
2918 bool is_valid() const { return is_valid_; } 2984 bool is_valid() const { return is_valid_; }
2919 2985
2920 template <typename T, typename U> 2986 template <typename T, typename U>
2921 bool parse(const char *buf, size_t n, T content_callback, U header_callback) { 2987 bool parse(const char *buf, size_t n, const T &content_callback,
2988 const U &header_callback) {
2922 2989
2923 static const std::regex re_content_disposition( 2990 static const std::regex re_content_disposition(
2924 "^Content-Disposition:\\s*form-data;\\s*name=\"(.*?)\"(?:;\\s*filename=" 2991 "^Content-Disposition:\\s*form-data;\\s*name=\"(.*?)\"(?:;\\s*filename="
@@ -3090,8 +3157,13 @@ inline std::string make_multipart_data_boundary() {
3090 static const char data[] = 3157 static const char data[] =
3091 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 3158 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
3092 3159
3160 // std::random_device might actually be deterministic on some
3161 // platforms, but due to lack of support in the c++ standard library,
3162 // doing better requires either some ugly hacks or breaking portability.
3093 std::random_device seed_gen; 3163 std::random_device seed_gen;
3094 std::mt19937 engine(seed_gen()); 3164 // Request 128 bits of entropy for initialization
3165 std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()};
3166 std::mt19937 engine(seed_sequence);
3095 3167
3096 std::string result = "--cpp-httplib-multipart-data-"; 3168 std::string result = "--cpp-httplib-multipart-data-";
3097 3169
@@ -3114,7 +3186,7 @@ get_range_offset_and_length(const Request &req, size_t content_length,
3114 auto slen = static_cast<ssize_t>(content_length); 3186 auto slen = static_cast<ssize_t>(content_length);
3115 3187
3116 if (r.first == -1) { 3188 if (r.first == -1) {
3117 r.first = slen - r.second; 3189 r.first = (std::max)(static_cast<ssize_t>(0), slen - r.second);
3118 r.second = slen - 1; 3190 r.second = slen - 1;
3119 } 3191 }
3120 3192
@@ -3451,7 +3523,7 @@ inline std::pair<std::string, std::string> make_range_header(Ranges ranges) {
3451 if (r.second != -1) { field += std::to_string(r.second); } 3523 if (r.second != -1) { field += std::to_string(r.second); }
3452 i++; 3524 i++;
3453 } 3525 }
3454 return std::make_pair("Range", field); 3526 return std::make_pair("Range", std::move(field));
3455} 3527}
3456 3528
3457inline std::pair<std::string, std::string> 3529inline std::pair<std::string, std::string>
@@ -3460,7 +3532,7 @@ make_basic_authentication_header(const std::string &username,
3460 bool is_proxy = false) { 3532 bool is_proxy = false) {
3461 auto field = "Basic " + detail::base64_encode(username + ":" + password); 3533 auto field = "Basic " + detail::base64_encode(username + ":" + password);
3462 auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; 3534 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
3463 return std::make_pair(key, field); 3535 return std::make_pair(key, std::move(field));
3464} 3536}
3465 3537
3466inline std::pair<std::string, std::string> 3538inline std::pair<std::string, std::string>
@@ -3468,7 +3540,7 @@ make_bearer_token_authentication_header(const std::string &token,
3468 bool is_proxy = false) { 3540 bool is_proxy = false) {
3469 auto field = "Bearer " + token; 3541 auto field = "Bearer " + token;
3470 auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; 3542 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
3471 return std::make_pair(key, field); 3543 return std::make_pair(key, std::move(field));
3472} 3544}
3473 3545
3474// Request implementation 3546// Request implementation
@@ -3761,60 +3833,66 @@ inline Server::Server()
3761inline Server::~Server() {} 3833inline Server::~Server() {}
3762 3834
3763inline Server &Server::Get(const char *pattern, Handler handler) { 3835inline Server &Server::Get(const char *pattern, Handler handler) {
3764 get_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); 3836 get_handlers_.push_back(
3837 std::make_pair(std::regex(pattern), std::move(handler)));
3765 return *this; 3838 return *this;
3766} 3839}
3767 3840
3768inline Server &Server::Post(const char *pattern, Handler handler) { 3841inline Server &Server::Post(const char *pattern, Handler handler) {
3769 post_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); 3842 post_handlers_.push_back(
3843 std::make_pair(std::regex(pattern), std::move(handler)));
3770 return *this; 3844 return *this;
3771} 3845}
3772 3846
3773inline Server &Server::Post(const char *pattern, 3847inline Server &Server::Post(const char *pattern,
3774 HandlerWithContentReader handler) { 3848 HandlerWithContentReader handler) {
3775 post_handlers_for_content_reader_.push_back( 3849 post_handlers_for_content_reader_.push_back(
3776 std::make_pair(std::regex(pattern), handler)); 3850 std::make_pair(std::regex(pattern), std::move(handler)));
3777 return *this; 3851 return *this;
3778} 3852}
3779 3853
3780inline Server &Server::Put(const char *pattern, Handler handler) { 3854inline Server &Server::Put(const char *pattern, Handler handler) {
3781 put_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); 3855 put_handlers_.push_back(
3856 std::make_pair(std::regex(pattern), std::move(handler)));
3782 return *this; 3857 return *this;
3783} 3858}
3784 3859
3785inline Server &Server::Put(const char *pattern, 3860inline Server &Server::Put(const char *pattern,
3786 HandlerWithContentReader handler) { 3861 HandlerWithContentReader handler) {
3787 put_handlers_for_content_reader_.push_back( 3862 put_handlers_for_content_reader_.push_back(
3788 std::make_pair(std::regex(pattern), handler)); 3863 std::make_pair(std::regex(pattern), std::move(handler)));
3789 return *this; 3864 return *this;
3790} 3865}
3791 3866
3792inline Server &Server::Patch(const char *pattern, Handler handler) { 3867inline Server &Server::Patch(const char *pattern, Handler handler) {
3793 patch_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); 3868 patch_handlers_.push_back(
3869 std::make_pair(std::regex(pattern), std::move(handler)));
3794 return *this; 3870 return *this;
3795} 3871}
3796 3872
3797inline Server &Server::Patch(const char *pattern, 3873inline Server &Server::Patch(const char *pattern,
3798 HandlerWithContentReader handler) { 3874 HandlerWithContentReader handler) {
3799 patch_handlers_for_content_reader_.push_back( 3875 patch_handlers_for_content_reader_.push_back(
3800 std::make_pair(std::regex(pattern), handler)); 3876 std::make_pair(std::regex(pattern), std::move(handler)));
3801 return *this; 3877 return *this;
3802} 3878}
3803 3879
3804inline Server &Server::Delete(const char *pattern, Handler handler) { 3880inline Server &Server::Delete(const char *pattern, Handler handler) {
3805 delete_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); 3881 delete_handlers_.push_back(
3882 std::make_pair(std::regex(pattern), std::move(handler)));
3806 return *this; 3883 return *this;
3807} 3884}
3808 3885
3809inline Server &Server::Delete(const char *pattern, 3886inline Server &Server::Delete(const char *pattern,
3810 HandlerWithContentReader handler) { 3887 HandlerWithContentReader handler) {
3811 delete_handlers_for_content_reader_.push_back( 3888 delete_handlers_for_content_reader_.push_back(
3812 std::make_pair(std::regex(pattern), handler)); 3889 std::make_pair(std::regex(pattern), std::move(handler)));
3813 return *this; 3890 return *this;
3814} 3891}
3815 3892
3816inline Server &Server::Options(const char *pattern, Handler handler) { 3893inline Server &Server::Options(const char *pattern, Handler handler) {
3817 options_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); 3894 options_handlers_.push_back(
3895 std::make_pair(std::regex(pattern), std::move(handler)));
3818 return *this; 3896 return *this;
3819} 3897}
3820 3898
@@ -3860,7 +3938,7 @@ inline void Server::set_error_handler(Handler handler) {
3860inline void Server::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; } 3938inline void Server::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
3861 3939
3862inline void Server::set_socket_options(SocketOptions socket_options) { 3940inline void Server::set_socket_options(SocketOptions socket_options) {
3863 socket_options_ = socket_options; 3941 socket_options_ = std::move(socket_options);
3864} 3942}
3865 3943
3866inline void Server::set_logger(Logger logger) { logger_ = std::move(logger); } 3944inline void Server::set_logger(Logger logger) { logger_ = std::move(logger); }
@@ -4045,16 +4123,16 @@ inline bool Server::write_response(Stream &strm, bool close_connection,
4045 } 4123 }
4046 4124
4047 if (type != detail::EncodingType::None) { 4125 if (type != detail::EncodingType::None) {
4048 std::shared_ptr<detail::compressor> compressor; 4126 std::unique_ptr<detail::compressor> compressor;
4049 4127
4050 if (type == detail::EncodingType::Gzip) { 4128 if (type == detail::EncodingType::Gzip) {
4051#ifdef CPPHTTPLIB_ZLIB_SUPPORT 4129#ifdef CPPHTTPLIB_ZLIB_SUPPORT
4052 compressor = std::make_shared<detail::gzip_compressor>(); 4130 compressor = detail::make_unique<detail::gzip_compressor>();
4053 res.set_header("Content-Encoding", "gzip"); 4131 res.set_header("Content-Encoding", "gzip");
4054#endif 4132#endif
4055 } else if (type == detail::EncodingType::Brotli) { 4133 } else if (type == detail::EncodingType::Brotli) {
4056#ifdef CPPHTTPLIB_BROTLI_SUPPORT 4134#ifdef CPPHTTPLIB_BROTLI_SUPPORT
4057 compressor = std::make_shared<detail::brotli_compressor>(); 4135 compressor = detail::make_unique<detail::brotli_compressor>();
4058 res.set_header("Content-Encoding", "brotli"); 4136 res.set_header("Content-Encoding", "brotli");
4059#endif 4137#endif
4060 } 4138 }
@@ -4136,17 +4214,17 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
4136 if (res.is_chunked_content_provider) { 4214 if (res.is_chunked_content_provider) {
4137 auto type = detail::encoding_type(req, res); 4215 auto type = detail::encoding_type(req, res);
4138 4216
4139 std::shared_ptr<detail::compressor> compressor; 4217 std::unique_ptr<detail::compressor> compressor;
4140 if (type == detail::EncodingType::Gzip) { 4218 if (type == detail::EncodingType::Gzip) {
4141#ifdef CPPHTTPLIB_ZLIB_SUPPORT 4219#ifdef CPPHTTPLIB_ZLIB_SUPPORT
4142 compressor = std::make_shared<detail::gzip_compressor>(); 4220 compressor = detail::make_unique<detail::gzip_compressor>();
4143#endif 4221#endif
4144 } else if (type == detail::EncodingType::Brotli) { 4222 } else if (type == detail::EncodingType::Brotli) {
4145#ifdef CPPHTTPLIB_BROTLI_SUPPORT 4223#ifdef CPPHTTPLIB_BROTLI_SUPPORT
4146 compressor = std::make_shared<detail::brotli_compressor>(); 4224 compressor = detail::make_unique<detail::brotli_compressor>();
4147#endif 4225#endif
4148 } else { 4226 } else {
4149 compressor = std::make_shared<detail::nocompressor>(); 4227 compressor = detail::make_unique<detail::nocompressor>();
4150 } 4228 }
4151 assert(compressor != nullptr); 4229 assert(compressor != nullptr);
4152 4230
@@ -4198,8 +4276,9 @@ inline bool Server::read_content_with_content_receiver(
4198 Stream &strm, Request &req, Response &res, ContentReceiver receiver, 4276 Stream &strm, Request &req, Response &res, ContentReceiver receiver,
4199 MultipartContentHeader multipart_header, 4277 MultipartContentHeader multipart_header,
4200 ContentReceiver multipart_receiver) { 4278 ContentReceiver multipart_receiver) {
4201 return read_content_core(strm, req, res, receiver, multipart_header, 4279 return read_content_core(strm, req, res, std::move(receiver),
4202 multipart_receiver); 4280 std::move(multipart_header),
4281 std::move(multipart_receiver));
4203} 4282}
4204 4283
4205inline bool Server::read_content_core(Stream &strm, Request &req, Response &res, 4284inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
@@ -4207,7 +4286,7 @@ inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
4207 MultipartContentHeader mulitpart_header, 4286 MultipartContentHeader mulitpart_header,
4208 ContentReceiver multipart_receiver) { 4287 ContentReceiver multipart_receiver) {
4209 detail::MultipartFormDataParser multipart_form_data_parser; 4288 detail::MultipartFormDataParser multipart_form_data_parser;
4210 ContentReceiver out; 4289 ContentReceiverWithProgress out;
4211 4290
4212 if (req.is_multipart_form_data()) { 4291 if (req.is_multipart_form_data()) {
4213 const auto &content_type = req.get_header_value("Content-Type"); 4292 const auto &content_type = req.get_header_value("Content-Type");
@@ -4218,7 +4297,7 @@ inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
4218 } 4297 }
4219 4298
4220 multipart_form_data_parser.set_boundary(std::move(boundary)); 4299 multipart_form_data_parser.set_boundary(std::move(boundary));
4221 out = [&](const char *buf, size_t n) { 4300 out = [&](const char *buf, size_t n, uint64_t /*off*/, uint64_t /*len*/) {
4222 /* For debug 4301 /* For debug
4223 size_t pos = 0; 4302 size_t pos = 0;
4224 while (pos < n) { 4303 while (pos < n) {
@@ -4234,7 +4313,8 @@ inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
4234 mulitpart_header); 4313 mulitpart_header);
4235 }; 4314 };
4236 } else { 4315 } else {
4237 out = receiver; 4316 out = [receiver](const char *buf, size_t n, uint64_t /*off*/,
4317 uint64_t /*len*/) { return receiver(buf, n); };
4238 } 4318 }
4239 4319
4240 if (req.method == "DELETE" && !req.has_header("Content-Length")) { 4320 if (req.method == "DELETE" && !req.has_header("Content-Length")) {
@@ -4271,7 +4351,7 @@ inline bool Server::handle_file_request(Request &req, Response &res,
4271 auto type = 4351 auto type =
4272 detail::find_content_type(path, file_extension_and_mimetype_map_); 4352 detail::find_content_type(path, file_extension_and_mimetype_map_);
4273 if (type) { res.set_header("Content-Type", type); } 4353 if (type) { res.set_header("Content-Type", type); }
4274 for (const auto& kv : entry.headers) { 4354 for (const auto &kv : entry.headers) {
4275 res.set_header(kv.first.c_str(), kv.second); 4355 res.set_header(kv.first.c_str(), kv.second);
4276 } 4356 }
4277 res.status = 200; 4357 res.status = 200;
@@ -4290,7 +4370,7 @@ inline socket_t
4290Server::create_server_socket(const char *host, int port, int socket_flags, 4370Server::create_server_socket(const char *host, int port, int socket_flags,
4291 SocketOptions socket_options) const { 4371 SocketOptions socket_options) const {
4292 return detail::create_socket( 4372 return detail::create_socket(
4293 host, port, socket_flags, tcp_nodelay_, socket_options, 4373 host, port, socket_flags, tcp_nodelay_, std::move(socket_options),
4294 [](socket_t sock, struct addrinfo &ai) -> bool { 4374 [](socket_t sock, struct addrinfo &ai) -> bool {
4295 if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) { 4375 if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
4296 return false; 4376 return false;
@@ -4392,32 +4472,37 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm) {
4392 { 4472 {
4393 ContentReader reader( 4473 ContentReader reader(
4394 [&](ContentReceiver receiver) { 4474 [&](ContentReceiver receiver) {
4395 return read_content_with_content_receiver(strm, req, res, receiver, 4475 return read_content_with_content_receiver(
4396 nullptr, nullptr); 4476 strm, req, res, std::move(receiver), nullptr, nullptr);
4397 }, 4477 },
4398 [&](MultipartContentHeader header, ContentReceiver receiver) { 4478 [&](MultipartContentHeader header, ContentReceiver receiver) {
4399 return read_content_with_content_receiver(strm, req, res, nullptr, 4479 return read_content_with_content_receiver(strm, req, res, nullptr,
4400 header, receiver); 4480 std::move(header),
4481 std::move(receiver));
4401 }); 4482 });
4402 4483
4403 if (req.method == "POST") { 4484 if (req.method == "POST") {
4404 if (dispatch_request_for_content_reader( 4485 if (dispatch_request_for_content_reader(
4405 req, res, reader, post_handlers_for_content_reader_)) { 4486 req, res, std::move(reader),
4487 post_handlers_for_content_reader_)) {
4406 return true; 4488 return true;
4407 } 4489 }
4408 } else if (req.method == "PUT") { 4490 } else if (req.method == "PUT") {
4409 if (dispatch_request_for_content_reader( 4491 if (dispatch_request_for_content_reader(
4410 req, res, reader, put_handlers_for_content_reader_)) { 4492 req, res, std::move(reader),
4493 put_handlers_for_content_reader_)) {
4411 return true; 4494 return true;
4412 } 4495 }
4413 } else if (req.method == "PATCH") { 4496 } else if (req.method == "PATCH") {
4414 if (dispatch_request_for_content_reader( 4497 if (dispatch_request_for_content_reader(
4415 req, res, reader, patch_handlers_for_content_reader_)) { 4498 req, res, std::move(reader),
4499 patch_handlers_for_content_reader_)) {
4416 return true; 4500 return true;
4417 } 4501 }
4418 } else if (req.method == "DELETE") { 4502 } else if (req.method == "DELETE") {
4419 if (dispatch_request_for_content_reader( 4503 if (dispatch_request_for_content_reader(
4420 req, res, reader, delete_handlers_for_content_reader_)) { 4504 req, res, std::move(reader),
4505 delete_handlers_for_content_reader_)) {
4421 return true; 4506 return true;
4422 } 4507 }
4423 } 4508 }
@@ -4448,7 +4533,6 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm) {
4448 4533
4449inline bool Server::dispatch_request(Request &req, Response &res, 4534inline bool Server::dispatch_request(Request &req, Response &res,
4450 const Handlers &handlers) { 4535 const Handlers &handlers) {
4451
4452 try { 4536 try {
4453 for (const auto &x : handlers) { 4537 for (const auto &x : handlers) {
4454 const auto &pattern = x.first; 4538 const auto &pattern = x.first;
@@ -4531,7 +4615,8 @@ Server::process_request(Stream &strm, bool close_connection,
4531 if (req.has_header("Range")) { 4615 if (req.has_header("Range")) {
4532 const auto &range_header_value = req.get_header_value("Range"); 4616 const auto &range_header_value = req.get_header_value("Range");
4533 if (!detail::parse_range_header(range_header_value, req.ranges)) { 4617 if (!detail::parse_range_header(range_header_value, req.ranges)) {
4534 // TODO: error 4618 res.status = 416;
4619 return write_response(strm, close_connection, req, res);
4535 } 4620 }
4536 } 4621 }
4537 4622
@@ -4588,11 +4673,11 @@ inline ClientImpl::ClientImpl(const std::string &host, int port)
4588inline ClientImpl::ClientImpl(const std::string &host, int port, 4673inline ClientImpl::ClientImpl(const std::string &host, int port,
4589 const std::string &client_cert_path, 4674 const std::string &client_cert_path,
4590 const std::string &client_key_path) 4675 const std::string &client_key_path)
4591 : host_(host), port_(port), 4676 : error_(Error::Success), host_(host), port_(port),
4592 host_and_port_(host_ + ":" + std::to_string(port_)), 4677 host_and_port_(host_ + ":" + std::to_string(port_)),
4593 client_cert_path_(client_cert_path), client_key_path_(client_key_path) {} 4678 client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
4594 4679
4595inline ClientImpl::~ClientImpl() { stop_core(); } 4680inline ClientImpl::~ClientImpl() { lock_socket_and_shutdown_and_close(); }
4596 4681
4597inline bool ClientImpl::is_valid() const { return true; } 4682inline bool ClientImpl::is_valid() const { return true; }
4598 4683
@@ -4653,15 +4738,47 @@ inline bool ClientImpl::create_and_connect_socket(Socket &socket) {
4653 return true; 4738 return true;
4654} 4739}
4655 4740
4656inline void ClientImpl::close_socket(Socket &socket, 4741inline void ClientImpl::shutdown_ssl(Socket &socket, bool shutdown_gracefully) {
4657 bool /*process_socket_ret*/) { 4742 (void)socket;
4658 detail::close_socket(socket.sock); 4743 (void)shutdown_gracefully;
4659 socket_.sock = INVALID_SOCKET; 4744 //If there are any requests in flight from threads other than us, then it's
4745 //a thread-unsafe race because individual ssl* objects are not thread-safe.
4746 assert(socket_requests_in_flight_ == 0 ||
4747 socket_requests_are_from_thread_ == std::this_thread::get_id());
4748}
4749
4750inline void ClientImpl::shutdown_socket(Socket &socket) {
4751 if (socket.sock == INVALID_SOCKET)
4752 return;
4753 detail::shutdown_socket(socket.sock);
4754}
4755
4756inline void ClientImpl::close_socket(Socket &socket) {
4757 // If there are requests in flight in another thread, usually closing
4758 // the socket will be fine and they will simply receive an error when
4759 // using the closed socket, but it is still a bug since rarely the OS
4760 // may reassign the socket id to be used for a new socket, and then
4761 // suddenly they will be operating on a live socket that is different
4762 // than the one they intended!
4763 assert(socket_requests_in_flight_ == 0 ||
4764 socket_requests_are_from_thread_ == std::this_thread::get_id());
4765 // It is also a bug if this happens while SSL is still active
4660#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 4766#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4661 socket_.ssl = nullptr; 4767 assert(socket.ssl == nullptr);
4662#endif 4768#endif
4769 if (socket.sock == INVALID_SOCKET)
4770 return;
4771 detail::close_socket(socket.sock);
4772 socket.sock = INVALID_SOCKET;
4663} 4773}
4664 4774
4775inline void ClientImpl::lock_socket_and_shutdown_and_close() {
4776 std::lock_guard<std::mutex> guard(socket_mutex_);
4777 shutdown_ssl(socket_, true);
4778 shutdown_socket(socket_);
4779 close_socket(socket_);
4780}
4781
4665inline bool ClientImpl::read_response_line(Stream &strm, Response &res) { 4782inline bool ClientImpl::read_response_line(Stream &strm, Response &res) {
4666 std::array<char, 2048> buf; 4783 std::array<char, 2048> buf;
4667 4784
@@ -4696,11 +4813,23 @@ inline bool ClientImpl::send(const Request &req, Response &res) {
4696 4813
4697 { 4814 {
4698 std::lock_guard<std::mutex> guard(socket_mutex_); 4815 std::lock_guard<std::mutex> guard(socket_mutex_);
4816 // Set this to false immediately - if it ever gets set to true by the end of the
4817 // request, we know another thread instructed us to close the socket.
4818 socket_should_be_closed_when_request_is_done_ = false;
4699 4819
4700 auto is_alive = false; 4820 auto is_alive = false;
4701 if (socket_.is_open()) { 4821 if (socket_.is_open()) {
4702 is_alive = detail::select_write(socket_.sock, 0, 0) > 0; 4822 is_alive = detail::select_write(socket_.sock, 0, 0) > 0;
4703 if (!is_alive) { close_socket(socket_, false); } 4823 if (!is_alive) {
4824 // Attempt to avoid sigpipe by shutting down nongracefully if it seems like
4825 // the other side has already closed the connection
4826 // Also, there cannot be any requests in flight from other threads since we locked
4827 // request_mutex_, so safe to close everything immediately
4828 const bool shutdown_gracefully = false;
4829 shutdown_ssl(socket_, shutdown_gracefully);
4830 shutdown_socket(socket_);
4831 close_socket(socket_);
4832 }
4704 } 4833 }
4705 4834
4706 if (!is_alive) { 4835 if (!is_alive) {
@@ -4721,15 +4850,38 @@ inline bool ClientImpl::send(const Request &req, Response &res) {
4721 } 4850 }
4722#endif 4851#endif
4723 } 4852 }
4853
4854 // Mark the current socket as being in use so that it cannot be closed by anyone
4855 // else while this request is ongoing, even though we will be releasing the mutex.
4856 if (socket_requests_in_flight_ > 1) {
4857 assert(socket_requests_are_from_thread_ == std::this_thread::get_id());
4858 }
4859 socket_requests_in_flight_ += 1;
4860 socket_requests_are_from_thread_ = std::this_thread::get_id();
4724 } 4861 }
4725 4862
4726 auto close_connection = !keep_alive_; 4863 auto close_connection = !keep_alive_;
4727
4728 auto ret = process_socket(socket_, [&](Stream &strm) { 4864 auto ret = process_socket(socket_, [&](Stream &strm) {
4729 return handle_request(strm, req, res, close_connection); 4865 return handle_request(strm, req, res, close_connection);
4730 }); 4866 });
4731 4867
4732 if (close_connection || !ret) { stop_core(); } 4868 //Briefly lock mutex in order to mark that a request is no longer ongoing
4869 {
4870 std::lock_guard<std::mutex> guard(socket_mutex_);
4871 socket_requests_in_flight_ -= 1;
4872 if (socket_requests_in_flight_ <= 0) {
4873 assert(socket_requests_in_flight_ == 0);
4874 socket_requests_are_from_thread_ = std::thread::id();
4875 }
4876
4877 if (socket_should_be_closed_when_request_is_done_ ||
4878 close_connection ||
4879 !ret ) {
4880 shutdown_ssl(socket_, true);
4881 shutdown_socket(socket_);
4882 close_socket(socket_);
4883 }
4884 }
4733 4885
4734 if (!ret) { 4886 if (!ret) {
4735 if (error_ == Error::Success) { error_ = Error::Unknown; } 4887 if (error_ == Error::Success) { error_ = Error::Unknown; }
@@ -4973,7 +5125,7 @@ inline bool ClientImpl::write_request(Stream &strm, const Request &req,
4973 return true; 5125 return true;
4974} 5126}
4975 5127
4976inline std::shared_ptr<Response> ClientImpl::send_with_content_provider( 5128inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
4977 const char *method, const char *path, const Headers &headers, 5129 const char *method, const char *path, const Headers &headers,
4978 const std::string &body, size_t content_length, 5130 const std::string &body, size_t content_length,
4979 ContentProvider content_provider, const char *content_type) { 5131 ContentProvider content_provider, const char *content_type) {
@@ -5036,15 +5188,15 @@ inline std::shared_ptr<Response> ClientImpl::send_with_content_provider(
5036 { 5188 {
5037 if (content_provider) { 5189 if (content_provider) {
5038 req.content_length = content_length; 5190 req.content_length = content_length;
5039 req.content_provider = content_provider; 5191 req.content_provider = std::move(content_provider);
5040 } else { 5192 } else {
5041 req.body = body; 5193 req.body = body;
5042 } 5194 }
5043 } 5195 }
5044 5196
5045 auto res = std::make_shared<Response>(); 5197 auto res = detail::make_unique<Response>();
5046 5198
5047 return send(req, *res) ? res : nullptr; 5199 return send(req, *res) ? std::move(res) : nullptr;
5048} 5200}
5049 5201
5050inline bool ClientImpl::process_request(Stream &strm, const Request &req, 5202inline bool ClientImpl::process_request(Stream &strm, const Request &req,
@@ -5070,16 +5222,21 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
5070 if (req.method != "HEAD" && req.method != "CONNECT") { 5222 if (req.method != "HEAD" && req.method != "CONNECT") {
5071 auto out = 5223 auto out =
5072 req.content_receiver 5224 req.content_receiver
5073 ? static_cast<ContentReceiver>([&](const char *buf, size_t n) { 5225 ? static_cast<ContentReceiverWithProgress>(
5074 auto ret = req.content_receiver(buf, n); 5226 [&](const char *buf, size_t n, uint64_t off, uint64_t len) {
5075 if (!ret) { error_ = Error::Canceled; } 5227 auto ret = req.content_receiver(buf, n, off, len);
5076 return ret; 5228 if (!ret) { error_ = Error::Canceled; }
5077 }) 5229 return ret;
5078 : static_cast<ContentReceiver>([&](const char *buf, size_t n) { 5230 })
5079 if (res.body.size() + n > res.body.max_size()) { return false; } 5231 : static_cast<ContentReceiverWithProgress>(
5080 res.body.append(buf, n); 5232 [&](const char *buf, size_t n, uint64_t /*off*/,
5081 return true; 5233 uint64_t /*len*/) {
5082 }); 5234 if (res.body.size() + n > res.body.max_size()) {
5235 return false;
5236 }
5237 res.body.append(buf, n);
5238 return true;
5239 });
5083 5240
5084 auto progress = [&](uint64_t current, uint64_t total) { 5241 auto progress = [&](uint64_t current, uint64_t total) {
5085 if (!req.progress) { return true; } 5242 if (!req.progress) { return true; }
@@ -5090,7 +5247,8 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
5090 5247
5091 int dummy_status; 5248 int dummy_status;
5092 if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(), 5249 if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(),
5093 dummy_status, progress, out, decompress_)) { 5250 dummy_status, std::move(progress), std::move(out),
5251 decompress_)) {
5094 if (error_ != Error::Canceled) { error_ = Error::Read; } 5252 if (error_ != Error::Canceled) { error_ = Error::Read; }
5095 return false; 5253 return false;
5096 } 5254 }
@@ -5098,7 +5256,16 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
5098 5256
5099 if (res.get_header_value("Connection") == "close" || 5257 if (res.get_header_value("Connection") == "close" ||
5100 (res.version == "HTTP/1.0" && res.reason != "Connection established")) { 5258 (res.version == "HTTP/1.0" && res.reason != "Connection established")) {
5101 stop_core(); 5259 // TODO this requires a not-entirely-obvious chain of calls to be correct
5260 // for this to be safe. Maybe a code refactor (such as moving this out to
5261 // the send function and getting rid of the recursiveness of the mutex)
5262 // could make this more obvious.
5263
5264 // This is safe to call because process_request is only called by handle_request
5265 // which is only called by send, which locks the request mutex during the process.
5266 // It would be a bug to call it from a different thread since it's a thread-safety
5267 // issue to do these things to the socket if another thread is using the socket.
5268 lock_socket_and_shutdown_and_close();
5102 } 5269 }
5103 5270
5104 // Log 5271 // Log
@@ -5108,11 +5275,11 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
5108} 5275}
5109 5276
5110inline bool 5277inline bool
5111ClientImpl::process_socket(Socket &socket, 5278ClientImpl::process_socket(const Socket &socket,
5112 std::function<bool(Stream &strm)> callback) { 5279 std::function<bool(Stream &strm)> callback) {
5113 return detail::process_client_socket(socket.sock, read_timeout_sec_, 5280 return detail::process_client_socket(
5114 read_timeout_usec_, write_timeout_sec_, 5281 socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
5115 write_timeout_usec_, callback); 5282 write_timeout_usec_, std::move(callback));
5116} 5283}
5117 5284
5118inline bool ClientImpl::is_ssl() const { return false; } 5285inline bool ClientImpl::is_ssl() const { return false; }
@@ -5138,9 +5305,9 @@ inline Result ClientImpl::Get(const char *path, const Headers &headers,
5138 req.headers.insert(headers.begin(), headers.end()); 5305 req.headers.insert(headers.begin(), headers.end());
5139 req.progress = std::move(progress); 5306 req.progress = std::move(progress);
5140 5307
5141 auto res = std::make_shared<Response>(); 5308 auto res = detail::make_unique<Response>();
5142 auto ret = send(req, *res); 5309 auto ret = send(req, *res);
5143 return Result{ret ? res : nullptr, get_last_error()}; 5310 return Result{ret ? std::move(res) : nullptr, get_last_error()};
5144} 5311}
5145 5312
5146inline Result ClientImpl::Get(const char *path, 5313inline Result ClientImpl::Get(const char *path,
@@ -5170,23 +5337,23 @@ inline Result ClientImpl::Get(const char *path, const Headers &headers,
5170inline Result ClientImpl::Get(const char *path, 5337inline Result ClientImpl::Get(const char *path,
5171 ResponseHandler response_handler, 5338 ResponseHandler response_handler,
5172 ContentReceiver content_receiver) { 5339 ContentReceiver content_receiver) {
5173 return Get(path, Headers(), std::move(response_handler), content_receiver, 5340 return Get(path, Headers(), std::move(response_handler),
5174 nullptr); 5341 std::move(content_receiver), nullptr);
5175} 5342}
5176 5343
5177inline Result ClientImpl::Get(const char *path, const Headers &headers, 5344inline Result ClientImpl::Get(const char *path, const Headers &headers,
5178 ResponseHandler response_handler, 5345 ResponseHandler response_handler,
5179 ContentReceiver content_receiver) { 5346 ContentReceiver content_receiver) {
5180 return Get(path, headers, std::move(response_handler), content_receiver, 5347 return Get(path, headers, std::move(response_handler),
5181 nullptr); 5348 std::move(content_receiver), nullptr);
5182} 5349}
5183 5350
5184inline Result ClientImpl::Get(const char *path, 5351inline Result ClientImpl::Get(const char *path,
5185 ResponseHandler response_handler, 5352 ResponseHandler response_handler,
5186 ContentReceiver content_receiver, 5353 ContentReceiver content_receiver,
5187 Progress progress) { 5354 Progress progress) {
5188 return Get(path, Headers(), std::move(response_handler), content_receiver, 5355 return Get(path, Headers(), std::move(response_handler),
5189 progress); 5356 std::move(content_receiver), std::move(progress));
5190} 5357}
5191 5358
5192inline Result ClientImpl::Get(const char *path, const Headers &headers, 5359inline Result ClientImpl::Get(const char *path, const Headers &headers,
@@ -5199,12 +5366,16 @@ inline Result ClientImpl::Get(const char *path, const Headers &headers,
5199 req.headers = default_headers_; 5366 req.headers = default_headers_;
5200 req.headers.insert(headers.begin(), headers.end()); 5367 req.headers.insert(headers.begin(), headers.end());
5201 req.response_handler = std::move(response_handler); 5368 req.response_handler = std::move(response_handler);
5202 req.content_receiver = std::move(content_receiver); 5369 req.content_receiver =
5370 [content_receiver](const char *data, size_t data_length,
5371 uint64_t /*offset*/, uint64_t /*total_length*/) {
5372 return content_receiver(data, data_length);
5373 };
5203 req.progress = std::move(progress); 5374 req.progress = std::move(progress);
5204 5375
5205 auto res = std::make_shared<Response>(); 5376 auto res = detail::make_unique<Response>();
5206 auto ret = send(req, *res); 5377 auto ret = send(req, *res);
5207 return Result{ret ? res : nullptr, get_last_error()}; 5378 return Result{ret ? std::move(res) : nullptr, get_last_error()};
5208} 5379}
5209 5380
5210inline Result ClientImpl::Head(const char *path) { 5381inline Result ClientImpl::Head(const char *path) {
@@ -5218,9 +5389,9 @@ inline Result ClientImpl::Head(const char *path, const Headers &headers) {
5218 req.headers.insert(headers.begin(), headers.end()); 5389 req.headers.insert(headers.begin(), headers.end());
5219 req.path = path; 5390 req.path = path;
5220 5391
5221 auto res = std::make_shared<Response>(); 5392 auto res = detail::make_unique<Response>();
5222 auto ret = send(req, *res); 5393 auto ret = send(req, *res);
5223 return Result{ret ? res : nullptr, get_last_error()}; 5394 return Result{ret ? std::move(res) : nullptr, get_last_error()};
5224} 5395}
5225 5396
5226inline Result ClientImpl::Post(const char *path) { 5397inline Result ClientImpl::Post(const char *path) {
@@ -5237,7 +5408,7 @@ inline Result ClientImpl::Post(const char *path, const Headers &headers,
5237 const char *content_type) { 5408 const char *content_type) {
5238 auto ret = send_with_content_provider("POST", path, headers, body, 0, nullptr, 5409 auto ret = send_with_content_provider("POST", path, headers, body, 0, nullptr,
5239 content_type); 5410 content_type);
5240 return Result{ret, get_last_error()}; 5411 return Result{std::move(ret), get_last_error()};
5241} 5412}
5242 5413
5243inline Result ClientImpl::Post(const char *path, const Params &params) { 5414inline Result ClientImpl::Post(const char *path, const Params &params) {
@@ -5247,17 +5418,18 @@ inline Result ClientImpl::Post(const char *path, const Params &params) {
5247inline Result ClientImpl::Post(const char *path, size_t content_length, 5418inline Result ClientImpl::Post(const char *path, size_t content_length,
5248 ContentProvider content_provider, 5419 ContentProvider content_provider,
5249 const char *content_type) { 5420 const char *content_type) {
5250 return Post(path, Headers(), content_length, content_provider, content_type); 5421 return Post(path, Headers(), content_length, std::move(content_provider),
5422 content_type);
5251} 5423}
5252 5424
5253inline Result ClientImpl::Post(const char *path, const Headers &headers, 5425inline Result ClientImpl::Post(const char *path, const Headers &headers,
5254 size_t content_length, 5426 size_t content_length,
5255 ContentProvider content_provider, 5427 ContentProvider content_provider,
5256 const char *content_type) { 5428 const char *content_type) {
5257 auto ret = send_with_content_provider("POST", path, headers, std::string(), 5429 auto ret = send_with_content_provider(
5258 content_length, content_provider, 5430 "POST", path, headers, std::string(), content_length,
5259 content_type); 5431 std::move(content_provider), content_type);
5260 return Result{ret, get_last_error()}; 5432 return Result{std::move(ret), get_last_error()};
5261} 5433}
5262 5434
5263inline Result ClientImpl::Post(const char *path, const Headers &headers, 5435inline Result ClientImpl::Post(const char *path, const Headers &headers,
@@ -5273,7 +5445,18 @@ inline Result ClientImpl::Post(const char *path,
5273 5445
5274inline Result ClientImpl::Post(const char *path, const Headers &headers, 5446inline Result ClientImpl::Post(const char *path, const Headers &headers,
5275 const MultipartFormDataItems &items) { 5447 const MultipartFormDataItems &items) {
5276 auto boundary = detail::make_multipart_data_boundary(); 5448 return Post(path, headers, items, detail::make_multipart_data_boundary());
5449}
5450inline Result ClientImpl::Post(const char *path, const Headers &headers,
5451 const MultipartFormDataItems &items,
5452 const std::string &boundary) {
5453 for (size_t i = 0; i < boundary.size(); i++) {
5454 char c = boundary[i];
5455 if (!std::isalnum(c) && c != '-' && c != '_') {
5456 error_ = Error::UnsupportedMultipartBoundaryChars;
5457 return Result{nullptr, error_};
5458 }
5459 }
5277 5460
5278 std::string body; 5461 std::string body;
5279 5462
@@ -5311,23 +5494,24 @@ inline Result ClientImpl::Put(const char *path, const Headers &headers,
5311 const char *content_type) { 5494 const char *content_type) {
5312 auto ret = send_with_content_provider("PUT", path, headers, body, 0, nullptr, 5495 auto ret = send_with_content_provider("PUT", path, headers, body, 0, nullptr,
5313 content_type); 5496 content_type);
5314 return Result{ret, get_last_error()}; 5497 return Result{std::move(ret), get_last_error()};
5315} 5498}
5316 5499
5317inline Result ClientImpl::Put(const char *path, size_t content_length, 5500inline Result ClientImpl::Put(const char *path, size_t content_length,
5318 ContentProvider content_provider, 5501 ContentProvider content_provider,
5319 const char *content_type) { 5502 const char *content_type) {
5320 return Put(path, Headers(), content_length, content_provider, content_type); 5503 return Put(path, Headers(), content_length, std::move(content_provider),
5504 content_type);
5321} 5505}
5322 5506
5323inline Result ClientImpl::Put(const char *path, const Headers &headers, 5507inline Result ClientImpl::Put(const char *path, const Headers &headers,
5324 size_t content_length, 5508 size_t content_length,
5325 ContentProvider content_provider, 5509 ContentProvider content_provider,
5326 const char *content_type) { 5510 const char *content_type) {
5327 auto ret = send_with_content_provider("PUT", path, headers, std::string(), 5511 auto ret = send_with_content_provider(
5328 content_length, content_provider, 5512 "PUT", path, headers, std::string(), content_length,
5329 content_type); 5513 std::move(content_provider), content_type);
5330 return Result{ret, get_last_error()}; 5514 return Result{std::move(ret), get_last_error()};
5331} 5515}
5332 5516
5333inline Result ClientImpl::Put(const char *path, const Params &params) { 5517inline Result ClientImpl::Put(const char *path, const Params &params) {
@@ -5350,23 +5534,24 @@ inline Result ClientImpl::Patch(const char *path, const Headers &headers,
5350 const char *content_type) { 5534 const char *content_type) {
5351 auto ret = send_with_content_provider("PATCH", path, headers, body, 0, 5535 auto ret = send_with_content_provider("PATCH", path, headers, body, 0,
5352 nullptr, content_type); 5536 nullptr, content_type);
5353 return Result{ret, get_last_error()}; 5537 return Result{std::move(ret), get_last_error()};
5354} 5538}
5355 5539
5356inline Result ClientImpl::Patch(const char *path, size_t content_length, 5540inline Result ClientImpl::Patch(const char *path, size_t content_length,
5357 ContentProvider content_provider, 5541 ContentProvider content_provider,
5358 const char *content_type) { 5542 const char *content_type) {
5359 return Patch(path, Headers(), content_length, content_provider, content_type); 5543 return Patch(path, Headers(), content_length, std::move(content_provider),
5544 content_type);
5360} 5545}
5361 5546
5362inline Result ClientImpl::Patch(const char *path, const Headers &headers, 5547inline Result ClientImpl::Patch(const char *path, const Headers &headers,
5363 size_t content_length, 5548 size_t content_length,
5364 ContentProvider content_provider, 5549 ContentProvider content_provider,
5365 const char *content_type) { 5550 const char *content_type) {
5366 auto ret = send_with_content_provider("PATCH", path, headers, std::string(), 5551 auto ret = send_with_content_provider(
5367 content_length, content_provider, 5552 "PATCH", path, headers, std::string(), content_length,
5368 content_type); 5553 std::move(content_provider), content_type);
5369 return Result{ret, get_last_error()}; 5554 return Result{std::move(ret), get_last_error()};
5370} 5555}
5371 5556
5372inline Result ClientImpl::Delete(const char *path) { 5557inline Result ClientImpl::Delete(const char *path) {
@@ -5394,9 +5579,9 @@ inline Result ClientImpl::Delete(const char *path, const Headers &headers,
5394 if (content_type) { req.headers.emplace("Content-Type", content_type); } 5579 if (content_type) { req.headers.emplace("Content-Type", content_type); }
5395 req.body = body; 5580 req.body = body;
5396 5581
5397 auto res = std::make_shared<Response>(); 5582 auto res = detail::make_unique<Response>();
5398 auto ret = send(req, *res); 5583 auto ret = send(req, *res);
5399 return Result{ret ? res : nullptr, get_last_error()}; 5584 return Result{ret ? std::move(res) : nullptr, get_last_error()};
5400} 5585}
5401 5586
5402inline Result ClientImpl::Options(const char *path) { 5587inline Result ClientImpl::Options(const char *path) {
@@ -5410,9 +5595,9 @@ inline Result ClientImpl::Options(const char *path, const Headers &headers) {
5410 req.headers.insert(headers.begin(), headers.end()); 5595 req.headers.insert(headers.begin(), headers.end());
5411 req.path = path; 5596 req.path = path;
5412 5597
5413 auto res = std::make_shared<Response>(); 5598 auto res = detail::make_unique<Response>();
5414 auto ret = send(req, *res); 5599 auto ret = send(req, *res);
5415 return Result{ret ? res : nullptr, get_last_error()}; 5600 return Result{ret ? std::move(res) : nullptr, get_last_error()};
5416} 5601}
5417 5602
5418inline size_t ClientImpl::is_socket_open() const { 5603inline size_t ClientImpl::is_socket_open() const {
@@ -5421,18 +5606,27 @@ inline size_t ClientImpl::is_socket_open() const {
5421} 5606}
5422 5607
5423inline void ClientImpl::stop() { 5608inline void ClientImpl::stop() {
5424 stop_core();
5425 error_ = Error::Canceled;
5426}
5427
5428inline void ClientImpl::stop_core() {
5429 std::lock_guard<std::mutex> guard(socket_mutex_); 5609 std::lock_guard<std::mutex> guard(socket_mutex_);
5430 if (socket_.is_open()) { 5610 // There is no guarantee that this doesn't get overwritten later, but set it so that
5431 detail::shutdown_socket(socket_.sock); 5611 // there is a good chance that any threads stopping as a result pick up this error.
5432 std::this_thread::sleep_for(std::chrono::milliseconds(1)); 5612 error_ = Error::Canceled;
5433 close_socket(socket_, true); 5613
5434 std::this_thread::sleep_for(std::chrono::milliseconds(1)); 5614 // If there is anything ongoing right now, the ONLY thread-safe thing we can do
5615 // is to shutdown_socket, so that threads using this socket suddenly discover
5616 // they can't read/write any more and error out.
5617 // Everything else (closing the socket, shutting ssl down) is unsafe because these
5618 // actions are not thread-safe.
5619 if (socket_requests_in_flight_ > 0) {
5620 shutdown_socket(socket_);
5621 // Aside from that, we set a flag for the socket to be closed when we're done.
5622 socket_should_be_closed_when_request_is_done_ = true;
5623 return;
5435 } 5624 }
5625
5626 //Otherwise, sitll holding the mutex, we can shut everything down ourselves
5627 shutdown_ssl(socket_, true);
5628 shutdown_socket(socket_);
5629 close_socket(socket_);
5436} 5630}
5437 5631
5438inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) { 5632inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) {
@@ -5479,7 +5673,7 @@ inline void ClientImpl::set_default_headers(Headers headers) {
5479inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; } 5673inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
5480 5674
5481inline void ClientImpl::set_socket_options(SocketOptions socket_options) { 5675inline void ClientImpl::set_socket_options(SocketOptions socket_options) {
5482 socket_options_ = socket_options; 5676 socket_options_ = std::move(socket_options);
5483} 5677}
5484 5678
5485inline void ClientImpl::set_compress(bool on) { compress_ = on; } 5679inline void ClientImpl::set_compress(bool on) { compress_ = on; }
@@ -5554,9 +5748,12 @@ inline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex,
5554} 5748}
5555 5749
5556inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, 5750inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl,
5557 bool process_socket_ret) { 5751 bool shutdown_gracefully) {
5558 if (process_socket_ret) { 5752 // sometimes we may want to skip this to try to avoid SIGPIPE if we know
5559 SSL_shutdown(ssl); // shutdown only if not already closed by remote 5753 // the remote has closed the network connection
5754 // Note that it is not always possible to avoid SIGPIPE, this is merely a best-efforts.
5755 if (shutdown_gracefully) {
5756 SSL_shutdown(ssl);
5560 } 5757 }
5561 5758
5562 std::lock_guard<std::mutex> guard(ctx_mutex); 5759 std::lock_guard<std::mutex> guard(ctx_mutex);
@@ -5650,22 +5847,7 @@ inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl,
5650 read_timeout_usec_(read_timeout_usec), 5847 read_timeout_usec_(read_timeout_usec),
5651 write_timeout_sec_(write_timeout_sec), 5848 write_timeout_sec_(write_timeout_sec),
5652 write_timeout_usec_(write_timeout_usec) { 5849 write_timeout_usec_(write_timeout_usec) {
5653 { 5850 SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
5654 timeval tv;
5655 tv.tv_sec = static_cast<long>(read_timeout_sec);
5656 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(read_timeout_usec);
5657
5658 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<char *>(&tv),
5659 sizeof(tv));
5660 }
5661 {
5662 timeval tv;
5663 tv.tv_sec = static_cast<long>(write_timeout_sec);
5664 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(write_timeout_usec);
5665
5666 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<char *>(&tv),
5667 sizeof(tv));
5668 }
5669} 5851}
5670 5852
5671inline SSLSocketStream::~SSLSocketStream() {} 5853inline SSLSocketStream::~SSLSocketStream() {}
@@ -5680,8 +5862,27 @@ inline bool SSLSocketStream::is_writable() const {
5680} 5862}
5681 5863
5682inline ssize_t SSLSocketStream::read(char *ptr, size_t size) { 5864inline ssize_t SSLSocketStream::read(char *ptr, size_t size) {
5683 if (SSL_pending(ssl_) > 0 || is_readable()) { 5865 if (SSL_pending(ssl_) > 0) {
5684 return SSL_read(ssl_, ptr, static_cast<int>(size)); 5866 return SSL_read(ssl_, ptr, static_cast<int>(size));
5867 } else if (is_readable()) {
5868 auto ret = SSL_read(ssl_, ptr, static_cast<int>(size));
5869 if (ret < 0) {
5870 auto err = SSL_get_error(ssl_, ret);
5871 while (err == SSL_ERROR_WANT_READ) {
5872 if (SSL_pending(ssl_) > 0) {
5873 return SSL_read(ssl_, ptr, static_cast<int>(size));
5874 } else if (is_readable()) {
5875 ret = SSL_read(ssl_, ptr, static_cast<int>(size));
5876 if (ret >= 0) {
5877 return ret;
5878 }
5879 err = SSL_get_error(ssl_, ret);
5880 } else {
5881 return -1;
5882 }
5883 }
5884 }
5885 return ret;
5685 } 5886 }
5686 return -1; 5887 return -1;
5687} 5888}
@@ -5788,9 +5989,12 @@ inline bool SSLServer::process_and_close_socket(socket_t sock) {
5788 }); 5989 });
5789 5990
5790 detail::ssl_delete(ctx_mutex_, ssl, ret); 5991 detail::ssl_delete(ctx_mutex_, ssl, ret);
5992 detail::shutdown_socket(sock);
5993 detail::close_socket(sock);
5791 return ret; 5994 return ret;
5792 } 5995 }
5793 5996
5997 detail::shutdown_socket(sock);
5794 detail::close_socket(sock); 5998 detail::close_socket(sock);
5795 return false; 5999 return false;
5796} 6000}
@@ -5843,6 +6047,10 @@ inline SSLClient::SSLClient(const std::string &host, int port,
5843 6047
5844inline SSLClient::~SSLClient() { 6048inline SSLClient::~SSLClient() {
5845 if (ctx_) { SSL_CTX_free(ctx_); } 6049 if (ctx_) { SSL_CTX_free(ctx_); }
6050 // Make sure to shut down SSL since shutdown_ssl will resolve to the
6051 // base function rather than the derived function once we get to the
6052 // base class destructor, and won't free the SSL (causing a leak).
6053 SSLClient::shutdown_ssl(socket_, true);
5846} 6054}
5847 6055
5848inline bool SSLClient::is_valid() const { return ctx_; } 6056inline bool SSLClient::is_valid() const { return ctx_; }
@@ -5876,11 +6084,11 @@ inline bool SSLClient::create_and_connect_socket(Socket &socket) {
5876 return is_valid() && ClientImpl::create_and_connect_socket(socket); 6084 return is_valid() && ClientImpl::create_and_connect_socket(socket);
5877} 6085}
5878 6086
6087// Assumes that socket_mutex_ is locked and that there are no requests in flight
5879inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res, 6088inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,
5880 bool &success) { 6089 bool &success) {
5881 success = true; 6090 success = true;
5882 Response res2; 6091 Response res2;
5883
5884 if (!detail::process_client_socket( 6092 if (!detail::process_client_socket(
5885 socket.sock, read_timeout_sec_, read_timeout_usec_, 6093 socket.sock, read_timeout_sec_, read_timeout_usec_,
5886 write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) { 6094 write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) {
@@ -5889,7 +6097,10 @@ inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,
5889 req2.path = host_and_port_; 6097 req2.path = host_and_port_;
5890 return process_request(strm, req2, res2, false); 6098 return process_request(strm, req2, res2, false);
5891 })) { 6099 })) {
5892 close_socket(socket, true); 6100 // Thread-safe to close everything because we are assuming there are no requests in flight
6101 shutdown_ssl(socket, true);
6102 shutdown_socket(socket);
6103 close_socket(socket);
5893 success = false; 6104 success = false;
5894 return false; 6105 return false;
5895 } 6106 }
@@ -5912,7 +6123,10 @@ inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,
5912 true)); 6123 true));
5913 return process_request(strm, req3, res3, false); 6124 return process_request(strm, req3, res3, false);
5914 })) { 6125 })) {
5915 close_socket(socket, true); 6126 // Thread-safe to close everything because we are assuming there are no requests in flight
6127 shutdown_ssl(socket, true);
6128 shutdown_socket(socket);
6129 close_socket(socket);
5916 success = false; 6130 success = false;
5917 return false; 6131 return false;
5918 } 6132 }
@@ -6005,26 +6219,30 @@ inline bool SSLClient::initialize_ssl(Socket &socket) {
6005 return true; 6219 return true;
6006 } 6220 }
6007 6221
6008 close_socket(socket, false); 6222 shutdown_socket(socket);
6223 close_socket(socket);
6009 return false; 6224 return false;
6010} 6225}
6011 6226
6012inline void SSLClient::close_socket(Socket &socket, bool process_socket_ret) { 6227inline void SSLClient::shutdown_ssl(Socket &socket, bool shutdown_gracefully) {
6013 detail::close_socket(socket.sock); 6228 if (socket.sock == INVALID_SOCKET) {
6014 socket_.sock = INVALID_SOCKET; 6229 assert(socket.ssl == nullptr);
6230 return;
6231 }
6015 if (socket.ssl) { 6232 if (socket.ssl) {
6016 detail::ssl_delete(ctx_mutex_, socket.ssl, process_socket_ret); 6233 detail::ssl_delete(ctx_mutex_, socket.ssl, shutdown_gracefully);
6017 socket_.ssl = nullptr; 6234 socket.ssl = nullptr;
6018 } 6235 }
6236 assert(socket.ssl == nullptr);
6019} 6237}
6020 6238
6021inline bool 6239inline bool
6022SSLClient::process_socket(Socket &socket, 6240SSLClient::process_socket(const Socket &socket,
6023 std::function<bool(Stream &strm)> callback) { 6241 std::function<bool(Stream &strm)> callback) {
6024 assert(socket.ssl); 6242 assert(socket.ssl);
6025 return detail::process_client_socket_ssl( 6243 return detail::process_client_socket_ssl(
6026 socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_, 6244 socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_,
6027 write_timeout_sec_, write_timeout_usec_, callback); 6245 write_timeout_sec_, write_timeout_usec_, std::move(callback));
6028} 6246}
6029 6247
6030inline bool SSLClient::is_ssl() const { return true; } 6248inline bool SSLClient::is_ssl() const { return true; }
@@ -6175,6 +6393,8 @@ inline Client::Client(const char *scheme_host_port,
6175#else 6393#else
6176 if (!scheme.empty() && scheme != "http") { 6394 if (!scheme.empty() && scheme != "http") {
6177#endif 6395#endif
6396 std::string msg = "'" + scheme + "' scheme is not supported.";
6397 throw std::invalid_argument(msg);
6178 return; 6398 return;
6179 } 6399 }
6180 6400
@@ -6187,28 +6407,28 @@ inline Client::Client(const char *scheme_host_port,
6187 6407
6188 if (is_ssl) { 6408 if (is_ssl) {
6189#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 6409#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
6190 cli_ = std::make_shared<SSLClient>(host.c_str(), port, client_cert_path, 6410 cli_ = detail::make_unique<SSLClient>(host.c_str(), port,
6191 client_key_path); 6411 client_cert_path, client_key_path);
6192 is_ssl_ = is_ssl; 6412 is_ssl_ = is_ssl;
6193#endif 6413#endif
6194 } else { 6414 } else {
6195 cli_ = std::make_shared<ClientImpl>(host.c_str(), port, client_cert_path, 6415 cli_ = detail::make_unique<ClientImpl>(host.c_str(), port,
6196 client_key_path); 6416 client_cert_path, client_key_path);
6197 } 6417 }
6198 } else { 6418 } else {
6199 cli_ = std::make_shared<ClientImpl>(scheme_host_port, 80, client_cert_path, 6419 cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80,
6200 client_key_path); 6420 client_cert_path, client_key_path);
6201 } 6421 }
6202} 6422}
6203 6423
6204inline Client::Client(const std::string &host, int port) 6424inline Client::Client(const std::string &host, int port)
6205 : cli_(std::make_shared<ClientImpl>(host, port)) {} 6425 : cli_(detail::make_unique<ClientImpl>(host, port)) {}
6206 6426
6207inline Client::Client(const std::string &host, int port, 6427inline Client::Client(const std::string &host, int port,
6208 const std::string &client_cert_path, 6428 const std::string &client_cert_path,
6209 const std::string &client_key_path) 6429 const std::string &client_key_path)
6210 : cli_(std::make_shared<ClientImpl>(host, port, client_cert_path, 6430 : cli_(detail::make_unique<ClientImpl>(host, port, client_cert_path,
6211 client_key_path)) {} 6431 client_key_path)) {}
6212 6432
6213inline Client::~Client() {} 6433inline Client::~Client() {}
6214 6434
@@ -6221,11 +6441,11 @@ inline Result Client::Get(const char *path, const Headers &headers) {
6221 return cli_->Get(path, headers); 6441 return cli_->Get(path, headers);
6222} 6442}
6223inline Result Client::Get(const char *path, Progress progress) { 6443inline Result Client::Get(const char *path, Progress progress) {
6224 return cli_->Get(path, progress); 6444 return cli_->Get(path, std::move(progress));
6225} 6445}
6226inline Result Client::Get(const char *path, const Headers &headers, 6446inline Result Client::Get(const char *path, const Headers &headers,
6227 Progress progress) { 6447 Progress progress) {
6228 return cli_->Get(path, headers, progress); 6448 return cli_->Get(path, headers, std::move(progress));
6229} 6449}
6230inline Result Client::Get(const char *path, ContentReceiver content_receiver) { 6450inline Result Client::Get(const char *path, ContentReceiver content_receiver) {
6231 return cli_->Get(path, std::move(content_receiver)); 6451 return cli_->Get(path, std::move(content_receiver));
@@ -6262,7 +6482,8 @@ inline Result Client::Get(const char *path, ResponseHandler response_handler,
6262inline Result Client::Get(const char *path, const Headers &headers, 6482inline Result Client::Get(const char *path, const Headers &headers,
6263 ResponseHandler response_handler, 6483 ResponseHandler response_handler,
6264 ContentReceiver content_receiver, Progress progress) { 6484 ContentReceiver content_receiver, Progress progress) {
6265 return cli_->Get(path, headers, response_handler, content_receiver, progress); 6485 return cli_->Get(path, headers, std::move(response_handler),
6486 std::move(content_receiver), std::move(progress));
6266} 6487}
6267 6488
6268inline Result Client::Head(const char *path) { return cli_->Head(path); } 6489inline Result Client::Head(const char *path) { return cli_->Head(path); }
@@ -6282,13 +6503,14 @@ inline Result Client::Post(const char *path, const Headers &headers,
6282inline Result Client::Post(const char *path, size_t content_length, 6503inline Result Client::Post(const char *path, size_t content_length,
6283 ContentProvider content_provider, 6504 ContentProvider content_provider,
6284 const char *content_type) { 6505 const char *content_type) {
6285 return cli_->Post(path, content_length, content_provider, content_type); 6506 return cli_->Post(path, content_length, std::move(content_provider),
6507 content_type);
6286} 6508}
6287inline Result Client::Post(const char *path, const Headers &headers, 6509inline Result Client::Post(const char *path, const Headers &headers,
6288 size_t content_length, 6510 size_t content_length,
6289 ContentProvider content_provider, 6511 ContentProvider content_provider,
6290 const char *content_type) { 6512 const char *content_type) {
6291 return cli_->Post(path, headers, content_length, content_provider, 6513 return cli_->Post(path, headers, content_length, std::move(content_provider),
6292 content_type); 6514 content_type);
6293} 6515}
6294inline Result Client::Post(const char *path, const Params &params) { 6516inline Result Client::Post(const char *path, const Params &params) {
@@ -6306,6 +6528,11 @@ inline Result Client::Post(const char *path, const Headers &headers,
6306 const MultipartFormDataItems &items) { 6528 const MultipartFormDataItems &items) {
6307 return cli_->Post(path, headers, items); 6529 return cli_->Post(path, headers, items);
6308} 6530}
6531inline Result Client::Post(const char *path, const Headers &headers,
6532 const MultipartFormDataItems &items,
6533 const std::string &boundary) {
6534 return cli_->Post(path, headers, items, boundary);
6535}
6309inline Result Client::Put(const char *path) { return cli_->Put(path); } 6536inline Result Client::Put(const char *path) { return cli_->Put(path); }
6310inline Result Client::Put(const char *path, const std::string &body, 6537inline Result Client::Put(const char *path, const std::string &body,
6311 const char *content_type) { 6538 const char *content_type) {
@@ -6318,13 +6545,14 @@ inline Result Client::Put(const char *path, const Headers &headers,
6318inline Result Client::Put(const char *path, size_t content_length, 6545inline Result Client::Put(const char *path, size_t content_length,
6319 ContentProvider content_provider, 6546 ContentProvider content_provider,
6320 const char *content_type) { 6547 const char *content_type) {
6321 return cli_->Put(path, content_length, content_provider, content_type); 6548 return cli_->Put(path, content_length, std::move(content_provider),
6549 content_type);
6322} 6550}
6323inline Result Client::Put(const char *path, const Headers &headers, 6551inline Result Client::Put(const char *path, const Headers &headers,
6324 size_t content_length, 6552 size_t content_length,
6325 ContentProvider content_provider, 6553 ContentProvider content_provider,
6326 const char *content_type) { 6554 const char *content_type) {
6327 return cli_->Put(path, headers, content_length, content_provider, 6555 return cli_->Put(path, headers, content_length, std::move(content_provider),
6328 content_type); 6556 content_type);
6329} 6557}
6330inline Result Client::Put(const char *path, const Params &params) { 6558inline Result Client::Put(const char *path, const Params &params) {
@@ -6345,13 +6573,14 @@ inline Result Client::Patch(const char *path, const Headers &headers,
6345inline Result Client::Patch(const char *path, size_t content_length, 6573inline Result Client::Patch(const char *path, size_t content_length,
6346 ContentProvider content_provider, 6574 ContentProvider content_provider,
6347 const char *content_type) { 6575 const char *content_type) {
6348 return cli_->Patch(path, content_length, content_provider, content_type); 6576 return cli_->Patch(path, content_length, std::move(content_provider),
6577 content_type);
6349} 6578}
6350inline Result Client::Patch(const char *path, const Headers &headers, 6579inline Result Client::Patch(const char *path, const Headers &headers,
6351 size_t content_length, 6580 size_t content_length,
6352 ContentProvider content_provider, 6581 ContentProvider content_provider,
6353 const char *content_type) { 6582 const char *content_type) {
6354 return cli_->Patch(path, headers, content_length, content_provider, 6583 return cli_->Patch(path, headers, content_length, std::move(content_provider),
6355 content_type); 6584 content_type);
6356} 6585}
6357inline Result Client::Delete(const char *path) { return cli_->Delete(path); } 6586inline Result Client::Delete(const char *path) { return cli_->Delete(path); }
@@ -6386,7 +6615,7 @@ inline void Client::set_default_headers(Headers headers) {
6386 6615
6387inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); } 6616inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); }
6388inline void Client::set_socket_options(SocketOptions socket_options) { 6617inline void Client::set_socket_options(SocketOptions socket_options) {
6389 cli_->set_socket_options(socket_options); 6618 cli_->set_socket_options(std::move(socket_options));
6390} 6619}
6391 6620
6392inline void Client::set_connection_timeout(time_t sec, time_t usec) { 6621inline void Client::set_connection_timeout(time_t sec, time_t usec) {
diff --git a/externals/inih/inih b/externals/inih/inih
Subproject 603729dec89aaca42d7bd08f08bc333165b7d5d Subproject 1e80a47dffbda813604f0913e2ad68c7054c14e
diff --git a/externals/microprofile/microprofile.h b/externals/microprofile/microprofile.h
index 85d5bd5de..a06f6457d 100644
--- a/externals/microprofile/microprofile.h
+++ b/externals/microprofile/microprofile.h
@@ -902,8 +902,10 @@ inline uint16_t MicroProfileGetGroupIndex(MicroProfileToken t)
902#include <windows.h> 902#include <windows.h>
903#define snprintf _snprintf 903#define snprintf _snprintf
904 904
905#ifdef _MSC_VER
905#pragma warning(push) 906#pragma warning(push)
906#pragma warning(disable: 4244) 907#pragma warning(disable: 4244)
908#endif
907int64_t MicroProfileTicksPerSecondCpu() 909int64_t MicroProfileTicksPerSecondCpu()
908{ 910{
909 static int64_t nTicksPerSecond = 0; 911 static int64_t nTicksPerSecond = 0;
@@ -946,7 +948,11 @@ typedef HANDLE MicroProfileThread;
946DWORD _stdcall ThreadTrampoline(void* pFunc) 948DWORD _stdcall ThreadTrampoline(void* pFunc)
947{ 949{
948 MicroProfileThreadFunc F = (MicroProfileThreadFunc)pFunc; 950 MicroProfileThreadFunc F = (MicroProfileThreadFunc)pFunc;
949 return (uint32_t)F(0); 951
952 // The return value of F will always return a void*, however, this is for
953 // compatibility with pthreads. The underlying "address" of the pointer
954 // is always a 32-bit value, so this cast is safe to perform.
955 return static_cast<DWORD>(reinterpret_cast<uint64_t>(F(0)));
950} 956}
951 957
952inline void MicroProfileThreadStart(MicroProfileThread* pThread, MicroProfileThreadFunc Func) 958inline void MicroProfileThreadStart(MicroProfileThread* pThread, MicroProfileThreadFunc Func)
@@ -1742,10 +1748,10 @@ void MicroProfileFlip()
1742 } 1748 }
1743 } 1749 }
1744 } 1750 }
1745 for(uint32_t i = 0; i < MICROPROFILE_MAX_GROUPS; ++i) 1751 for(uint32_t j = 0; j < MICROPROFILE_MAX_GROUPS; ++j)
1746 { 1752 {
1747 pLog->nGroupTicks[i] += nGroupTicks[i]; 1753 pLog->nGroupTicks[j] += nGroupTicks[j];
1748 pFrameGroup[i] += nGroupTicks[i]; 1754 pFrameGroup[j] += nGroupTicks[j];
1749 } 1755 }
1750 pLog->nStackPos = nStackPos; 1756 pLog->nStackPos = nStackPos;
1751 } 1757 }
@@ -3328,7 +3334,7 @@ bool MicroProfileIsLocalThread(uint32_t nThreadId)
3328#endif 3334#endif
3329#else 3335#else
3330 3336
3331bool MicroProfileIsLocalThread(uint32_t nThreadId){return false;} 3337bool MicroProfileIsLocalThread([[maybe_unused]] uint32_t nThreadId) { return false; }
3332void MicroProfileStopContextSwitchTrace(){} 3338void MicroProfileStopContextSwitchTrace(){}
3333void MicroProfileStartContextSwitchTrace(){} 3339void MicroProfileStartContextSwitchTrace(){}
3334 3340
@@ -3576,7 +3582,7 @@ int MicroProfileGetGpuTickReference(int64_t* pOutCpu, int64_t* pOutGpu)
3576 3582
3577#undef S 3583#undef S
3578 3584
3579#ifdef _WIN32 3585#ifdef _MSC_VER
3580#pragma warning(pop) 3586#pragma warning(pop)
3581#endif 3587#endif
3582 3588
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());