diff options
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 @@ | |||
| 1 | From https://github.com/yhirose/cpp-httplib/tree/fce8e6fefdab4ad48bc5b25c98e5ebfda4f3cf53 | 1 | From https://github.com/yhirose/cpp-httplib/tree/ff5677ad197947177c158fe857caff4f0e242045 with https://github.com/yhirose/cpp-httplib/pull/701 |
| 2 | 2 | ||
| 3 | MIT License | 3 | MIT 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 | ||
| 238 | namespace detail { | 239 | namespace 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 | |||
| 249 | template <class T, class... Args> | ||
| 250 | typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type | ||
| 251 | make_unique(Args &&... args) { | ||
| 252 | return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); | ||
| 253 | } | ||
| 254 | |||
| 255 | template <class T> | ||
| 256 | typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type | ||
| 257 | make_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 | |||
| 240 | struct ci { | 262 | struct 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 = | |||
| 304 | using ContentProviderWithoutLength = | 326 | using ContentProviderWithoutLength = |
| 305 | std::function<bool(size_t offset, DataSink &sink)>; | 327 | std::function<bool(size_t offset, DataSink &sink)>; |
| 306 | 328 | ||
| 329 | using ContentReceiverWithProgress = | ||
| 330 | std::function<bool(const char *data, size_t data_length, uint64_t offset, | ||
| 331 | uint64_t total_length)>; | ||
| 332 | |||
| 307 | using ContentReceiver = | 333 | using 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 | ||
| 710 | class Result { | 740 | class Result { |
| 711 | public: | 741 | public: |
| 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 | ||
| 722 | private: | 755 | private: |
| 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 | ||
| 1100 | private: | 1156 | private: |
| 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 | ||
| 1155 | private: | 1211 | private: |
| 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 | ||
| 2480 | inline bool read_content_with_length(Stream &strm, uint64_t len, | 2536 | inline 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 | ||
| 2513 | inline bool read_content_without_length(Stream &strm, ContentReceiver out) { | 2569 | inline 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 | ||
| 2528 | inline bool read_content_chunked(Stream &strm, ContentReceiver out) { | 2588 | inline 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 | ||
| 2572 | template <typename T, typename U> | 2633 | template <typename T, typename U> |
| 2573 | bool prepare_content_receiver(T &x, int &status, ContentReceiver receiver, | 2634 | bool 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 | ||
| 2617 | template <typename T> | 2682 | template <typename T> |
| 2618 | bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status, | 2683 | bool 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 | ||
| 2878 | inline bool parse_range_header(const std::string &s, Ranges &ranges) { | 2944 | inline 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 | ||
| 2912 | class MultipartFormDataParser { | 2978 | class MultipartFormDataParser { |
| 2913 | public: | 2979 | public: |
| @@ -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 | ||
| 3457 | inline std::pair<std::string, std::string> | 3529 | inline 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 | ||
| 3466 | inline std::pair<std::string, std::string> | 3538 | inline 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() | |||
| 3761 | inline Server::~Server() {} | 3833 | inline Server::~Server() {} |
| 3762 | 3834 | ||
| 3763 | inline Server &Server::Get(const char *pattern, Handler handler) { | 3835 | inline 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 | ||
| 3768 | inline Server &Server::Post(const char *pattern, Handler handler) { | 3841 | inline 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 | ||
| 3773 | inline Server &Server::Post(const char *pattern, | 3847 | inline 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 | ||
| 3780 | inline Server &Server::Put(const char *pattern, Handler handler) { | 3854 | inline 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 | ||
| 3785 | inline Server &Server::Put(const char *pattern, | 3860 | inline 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 | ||
| 3792 | inline Server &Server::Patch(const char *pattern, Handler handler) { | 3867 | inline 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 | ||
| 3797 | inline Server &Server::Patch(const char *pattern, | 3873 | inline 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 | ||
| 3804 | inline Server &Server::Delete(const char *pattern, Handler handler) { | 3880 | inline 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 | ||
| 3809 | inline Server &Server::Delete(const char *pattern, | 3886 | inline 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 | ||
| 3816 | inline Server &Server::Options(const char *pattern, Handler handler) { | 3893 | inline 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) { | |||
| 3860 | inline void Server::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; } | 3938 | inline void Server::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; } |
| 3861 | 3939 | ||
| 3862 | inline void Server::set_socket_options(SocketOptions socket_options) { | 3940 | inline void Server::set_socket_options(SocketOptions socket_options) { |
| 3863 | socket_options_ = socket_options; | 3941 | socket_options_ = std::move(socket_options); |
| 3864 | } | 3942 | } |
| 3865 | 3943 | ||
| 3866 | inline void Server::set_logger(Logger logger) { logger_ = std::move(logger); } | 3944 | inline 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 | ||
| 4205 | inline bool Server::read_content_core(Stream &strm, Request &req, Response &res, | 4284 | inline 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 | |||
| 4290 | Server::create_server_socket(const char *host, int port, int socket_flags, | 4370 | Server::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 | ||
| 4449 | inline bool Server::dispatch_request(Request &req, Response &res, | 4534 | inline 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) | |||
| 4588 | inline ClientImpl::ClientImpl(const std::string &host, int port, | 4673 | inline 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 | ||
| 4595 | inline ClientImpl::~ClientImpl() { stop_core(); } | 4680 | inline ClientImpl::~ClientImpl() { lock_socket_and_shutdown_and_close(); } |
| 4596 | 4681 | ||
| 4597 | inline bool ClientImpl::is_valid() const { return true; } | 4682 | inline 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 | ||
| 4656 | inline void ClientImpl::close_socket(Socket &socket, | 4741 | inline 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 | |||
| 4750 | inline void ClientImpl::shutdown_socket(Socket &socket) { | ||
| 4751 | if (socket.sock == INVALID_SOCKET) | ||
| 4752 | return; | ||
| 4753 | detail::shutdown_socket(socket.sock); | ||
| 4754 | } | ||
| 4755 | |||
| 4756 | inline 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 | ||
| 4775 | inline 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 | |||
| 4665 | inline bool ClientImpl::read_response_line(Stream &strm, Response &res) { | 4782 | inline 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 | ||
| 4976 | inline std::shared_ptr<Response> ClientImpl::send_with_content_provider( | 5128 | inline 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 | ||
| 5050 | inline bool ClientImpl::process_request(Stream &strm, const Request &req, | 5202 | inline 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 | ||
| 5110 | inline bool | 5277 | inline bool |
| 5111 | ClientImpl::process_socket(Socket &socket, | 5278 | ClientImpl::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 | ||
| 5118 | inline bool ClientImpl::is_ssl() const { return false; } | 5285 | inline 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 | ||
| 5146 | inline Result ClientImpl::Get(const char *path, | 5313 | inline Result ClientImpl::Get(const char *path, |
| @@ -5170,23 +5337,23 @@ inline Result ClientImpl::Get(const char *path, const Headers &headers, | |||
| 5170 | inline Result ClientImpl::Get(const char *path, | 5337 | inline 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 | ||
| 5177 | inline Result ClientImpl::Get(const char *path, const Headers &headers, | 5344 | inline 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 | ||
| 5184 | inline Result ClientImpl::Get(const char *path, | 5351 | inline 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 | ||
| 5192 | inline Result ClientImpl::Get(const char *path, const Headers &headers, | 5359 | inline 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 | ||
| 5210 | inline Result ClientImpl::Head(const char *path) { | 5381 | inline 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 | ||
| 5226 | inline Result ClientImpl::Post(const char *path) { | 5397 | inline 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 | ||
| 5243 | inline Result ClientImpl::Post(const char *path, const Params ¶ms) { | 5414 | inline Result ClientImpl::Post(const char *path, const Params ¶ms) { |
| @@ -5247,17 +5418,18 @@ inline Result ClientImpl::Post(const char *path, const Params ¶ms) { | |||
| 5247 | inline Result ClientImpl::Post(const char *path, size_t content_length, | 5418 | inline 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 | ||
| 5253 | inline Result ClientImpl::Post(const char *path, const Headers &headers, | 5425 | inline 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 | ||
| 5263 | inline Result ClientImpl::Post(const char *path, const Headers &headers, | 5435 | inline Result ClientImpl::Post(const char *path, const Headers &headers, |
| @@ -5273,7 +5445,18 @@ inline Result ClientImpl::Post(const char *path, | |||
| 5273 | 5445 | ||
| 5274 | inline Result ClientImpl::Post(const char *path, const Headers &headers, | 5446 | inline 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 | } | ||
| 5450 | inline 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 | ||
| 5317 | inline Result ClientImpl::Put(const char *path, size_t content_length, | 5500 | inline 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 | ||
| 5323 | inline Result ClientImpl::Put(const char *path, const Headers &headers, | 5507 | inline 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 | ||
| 5333 | inline Result ClientImpl::Put(const char *path, const Params ¶ms) { | 5517 | inline Result ClientImpl::Put(const char *path, const Params ¶ms) { |
| @@ -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 | ||
| 5356 | inline Result ClientImpl::Patch(const char *path, size_t content_length, | 5540 | inline 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 | ||
| 5362 | inline Result ClientImpl::Patch(const char *path, const Headers &headers, | 5547 | inline 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 | ||
| 5372 | inline Result ClientImpl::Delete(const char *path) { | 5557 | inline 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 | ||
| 5402 | inline Result ClientImpl::Options(const char *path) { | 5587 | inline 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 | ||
| 5418 | inline size_t ClientImpl::is_socket_open() const { | 5603 | inline size_t ClientImpl::is_socket_open() const { |
| @@ -5421,18 +5606,27 @@ inline size_t ClientImpl::is_socket_open() const { | |||
| 5421 | } | 5606 | } |
| 5422 | 5607 | ||
| 5423 | inline void ClientImpl::stop() { | 5608 | inline void ClientImpl::stop() { |
| 5424 | stop_core(); | ||
| 5425 | error_ = Error::Canceled; | ||
| 5426 | } | ||
| 5427 | |||
| 5428 | inline 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 | ||
| 5438 | inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) { | 5632 | inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) { |
| @@ -5479,7 +5673,7 @@ inline void ClientImpl::set_default_headers(Headers headers) { | |||
| 5479 | inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; } | 5673 | inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; } |
| 5480 | 5674 | ||
| 5481 | inline void ClientImpl::set_socket_options(SocketOptions socket_options) { | 5675 | inline void ClientImpl::set_socket_options(SocketOptions socket_options) { |
| 5482 | socket_options_ = socket_options; | 5676 | socket_options_ = std::move(socket_options); |
| 5483 | } | 5677 | } |
| 5484 | 5678 | ||
| 5485 | inline void ClientImpl::set_compress(bool on) { compress_ = on; } | 5679 | inline 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 | ||
| 5556 | inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, | 5750 | inline 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 | ||
| 5671 | inline SSLSocketStream::~SSLSocketStream() {} | 5853 | inline SSLSocketStream::~SSLSocketStream() {} |
| @@ -5680,8 +5862,27 @@ inline bool SSLSocketStream::is_writable() const { | |||
| 5680 | } | 5862 | } |
| 5681 | 5863 | ||
| 5682 | inline ssize_t SSLSocketStream::read(char *ptr, size_t size) { | 5864 | inline 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 | ||
| 5844 | inline SSLClient::~SSLClient() { | 6048 | inline 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 | ||
| 5848 | inline bool SSLClient::is_valid() const { return ctx_; } | 6056 | inline 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 | ||
| 5879 | inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res, | 6088 | inline 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 | ||
| 6012 | inline void SSLClient::close_socket(Socket &socket, bool process_socket_ret) { | 6227 | inline 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 | ||
| 6021 | inline bool | 6239 | inline bool |
| 6022 | SSLClient::process_socket(Socket &socket, | 6240 | SSLClient::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 | ||
| 6030 | inline bool SSLClient::is_ssl() const { return true; } | 6248 | inline 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 | ||
| 6204 | inline Client::Client(const std::string &host, int port) | 6424 | inline 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 | ||
| 6207 | inline Client::Client(const std::string &host, int port, | 6427 | inline 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 | ||
| 6213 | inline Client::~Client() {} | 6433 | inline 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 | } |
| 6223 | inline Result Client::Get(const char *path, Progress progress) { | 6443 | inline Result Client::Get(const char *path, Progress progress) { |
| 6224 | return cli_->Get(path, progress); | 6444 | return cli_->Get(path, std::move(progress)); |
| 6225 | } | 6445 | } |
| 6226 | inline Result Client::Get(const char *path, const Headers &headers, | 6446 | inline 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 | } |
| 6230 | inline Result Client::Get(const char *path, ContentReceiver content_receiver) { | 6450 | inline 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, | |||
| 6262 | inline Result Client::Get(const char *path, const Headers &headers, | 6482 | inline 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 | ||
| 6268 | inline Result Client::Head(const char *path) { return cli_->Head(path); } | 6489 | inline Result Client::Head(const char *path) { return cli_->Head(path); } |
| @@ -6282,13 +6503,14 @@ inline Result Client::Post(const char *path, const Headers &headers, | |||
| 6282 | inline Result Client::Post(const char *path, size_t content_length, | 6503 | inline 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 | } |
| 6287 | inline Result Client::Post(const char *path, const Headers &headers, | 6509 | inline 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 | } |
| 6294 | inline Result Client::Post(const char *path, const Params ¶ms) { | 6516 | inline Result Client::Post(const char *path, const Params ¶ms) { |
| @@ -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 | } |
| 6531 | inline 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 | } | ||
| 6309 | inline Result Client::Put(const char *path) { return cli_->Put(path); } | 6536 | inline Result Client::Put(const char *path) { return cli_->Put(path); } |
| 6310 | inline Result Client::Put(const char *path, const std::string &body, | 6537 | inline 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, | |||
| 6318 | inline Result Client::Put(const char *path, size_t content_length, | 6545 | inline 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 | } |
| 6323 | inline Result Client::Put(const char *path, const Headers &headers, | 6551 | inline 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 | } |
| 6330 | inline Result Client::Put(const char *path, const Params ¶ms) { | 6558 | inline Result Client::Put(const char *path, const Params ¶ms) { |
| @@ -6345,13 +6573,14 @@ inline Result Client::Patch(const char *path, const Headers &headers, | |||
| 6345 | inline Result Client::Patch(const char *path, size_t content_length, | 6573 | inline 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 | } |
| 6350 | inline Result Client::Patch(const char *path, const Headers &headers, | 6579 | inline 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 | } |
| 6357 | inline Result Client::Delete(const char *path) { return cli_->Delete(path); } | 6586 | inline Result Client::Delete(const char *path) { return cli_->Delete(path); } |
| @@ -6386,7 +6615,7 @@ inline void Client::set_default_headers(Headers headers) { | |||
| 6386 | 6615 | ||
| 6387 | inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); } | 6616 | inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); } |
| 6388 | inline void Client::set_socket_options(SocketOptions socket_options) { | 6617 | inline 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 | ||
| 6392 | inline void Client::set_connection_timeout(time_t sec, time_t usec) { | 6621 | inline 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 | ||
| 907 | int64_t MicroProfileTicksPerSecondCpu() | 909 | int64_t MicroProfileTicksPerSecondCpu() |
| 908 | { | 910 | { |
| 909 | static int64_t nTicksPerSecond = 0; | 911 | static int64_t nTicksPerSecond = 0; |
| @@ -946,7 +948,11 @@ typedef HANDLE MicroProfileThread; | |||
| 946 | DWORD _stdcall ThreadTrampoline(void* pFunc) | 948 | DWORD _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 | ||
| 952 | inline void MicroProfileThreadStart(MicroProfileThread* pThread, MicroProfileThreadFunc Func) | 958 | inline 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 | ||
| 3331 | bool MicroProfileIsLocalThread(uint32_t nThreadId){return false;} | 3337 | bool MicroProfileIsLocalThread([[maybe_unused]] uint32_t nThreadId) { return false; } |
| 3332 | void MicroProfileStopContextSwitchTrace(){} | 3338 | void MicroProfileStopContextSwitchTrace(){} |
| 3333 | void MicroProfileStartContextSwitchTrace(){} | 3339 | void 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 | ) |
| 191 | endif() | 191 | endif() |
| 192 | 192 | ||
| 193 | if (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 | ) | ||
| 203 | else() | ||
| 204 | target_compile_options(common PRIVATE | ||
| 205 | -Werror | ||
| 206 | ) | ||
| 207 | endif() | ||
| 208 | |||
| 193 | create_target_directory_groups(common) | 209 | create_target_directory_groups(common) |
| 194 | find_package(Boost 1.71 COMPONENTS context headers REQUIRED) | 210 | find_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 | ||
| 82 | void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter) { | 82 | void 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 | ||
| 87 | void Fiber::Rewind() { | 87 | void 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 | ||
| 164 | void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter) { | 164 | void 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 | ||
| 169 | Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {} | 169 | Fiber::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 | ||
| 474 | bool DeleteDirRecursively(const std::string& directory, unsigned int recursion) { | 474 | bool 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 | ||
| 495 | void CopyDir(const std::string& source_path, const std::string& dest_path) { | 496 | void 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 | ||
| 559 | bool SetCurrentDir(const std::string& directory) { | 561 | bool SetCurrentDir(const std::string& directory) { |
| @@ -772,21 +774,23 @@ std::size_t ReadFileToString(bool text_file, const std::string& filename, std::s | |||
| 772 | 774 | ||
| 773 | void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name, | 775 | void 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. |
| 18 | std::string GetLastErrorMsg() { | 18 | std::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 |
| 22 | std::string ToLower(std::string str) { | 23 | std::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 |
| 29 | std::string ToUpper(std::string str) { | 30 | std::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 | // ---------------- |
| 143 | double Timer::GetDoubleTime() { | 143 | double 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 | ||
| 16 | namespace Service::Nvidia::Devices { | 16 | namespace Service::Nvidia::Devices { |
| 17 | 17 | ||
| 18 | nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface) | 18 | nvhost_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} {} | ||
| 20 | nvhost_ctrl::~nvhost_ctrl() = default; | 21 | nvhost_ctrl::~nvhost_ctrl() = default; |
| 21 | 22 | ||
| 22 | u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, | 23 | u32 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(), ¶ms, 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(), ¶ms, 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(), ¶ms, sizeof(params)); | 102 | std::memcpy(output.data(), ¶ms, 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 | ||
| 157 | u32 nvhost_ctrl::IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8>& output) { | 172 | u32 nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) { |
| 158 | IocCtrlEventSignalParams params{}; | 173 | IocCtrlEventSignalParams params{}; |
| 159 | std::memcpy(¶ms, input.data(), sizeof(params)); | 174 | std::memcpy(¶ms, 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 | ||
| 15 | class nvhost_ctrl final : public nvdevice { | 15 | class nvhost_ctrl final : public nvdevice { |
| 16 | public: | 16 | public: |
| 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 | ||
| 14 | namespace Service::Nvidia::Devices { | 15 | namespace Service::Nvidia::Devices { |
| 15 | 16 | ||
| 16 | nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) | 17 | nvhost_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 | |||
| 18 | nvhost_gpu::~nvhost_gpu() = default; | 24 | nvhost_gpu::~nvhost_gpu() = default; |
| 19 | 25 | ||
| 20 | u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, | 26 | u32 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(), ¶ms, output.size()); | 139 | std::memcpy(output.data(), ¶ms, 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 | ||
| 148 | u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) { | 154 | static 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 | |||
| 165 | static 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(¶ms, input.data(), sizeof(IoctlSubmitGpfifo)); | 178 | return result; |
| 179 | } | ||
| 180 | |||
| 181 | static 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 | |||
| 195 | u32 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(), ¶ms, sizeof(IoctlSubmitGpfifo)); | 230 | std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmitGpfifo)); |
| 178 | return 0; | 231 | return 0; |
| 179 | } | 232 | } |
| 180 | 233 | ||
| 234 | u32 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(¶ms, 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 | |||
| 181 | u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output, | 248 | u32 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(¶ms, input.data(), sizeof(IoctlSubmitGpfifo)); | 254 | std::memcpy(¶ms, 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(), ¶ms, output.size()); | 265 | return SubmitGPFIFOImpl(params, output, std::move(entries)); |
| 212 | return 0; | ||
| 213 | } | 266 | } |
| 214 | 267 | ||
| 215 | u32 nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { | 268 | u32 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 | |||
| 16 | namespace Service::Nvidia { | ||
| 17 | class SyncpointManager; | ||
| 18 | } | ||
| 14 | 19 | ||
| 15 | namespace Service::Nvidia::Devices { | 20 | namespace Service::Nvidia::Devices { |
| 16 | 21 | ||
| @@ -21,7 +26,8 @@ constexpr u32 NVGPU_IOCTL_CHANNEL_KICKOFF_PB(0x1b); | |||
| 21 | 26 | ||
| 22 | class nvhost_gpu final : public nvdevice { | 27 | class nvhost_gpu final : public nvdevice { |
| 23 | public: | 28 | public: |
| 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 | ||
| 39 | namespace NvErrCodes { | 39 | namespace NvErrCodes { |
| 40 | constexpr u32 Success{}; | 40 | constexpr u32 Success{}; |
| 41 | constexpr u32 OutOfMemory{static_cast<u32>(-12)}; | 41 | [[maybe_unused]] constexpr u32 OutOfMemory{static_cast<u32>(-12)}; |
| 42 | constexpr u32 InvalidInput{static_cast<u32>(-22)}; | 42 | constexpr 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 | ||
| 26 | namespace Service::Nvidia { | 27 | namespace 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 | ||
| 39 | Module::Module(Core::System& system) { | 40 | Module::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 | ||
| 103 | std::shared_ptr<Kernel::ReadableEvent> Module::GetEvent(const u32 event_id) const { | 106 | std::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 | ||
| 107 | std::shared_ptr<Kernel::WritableEvent> Module::GetEventWriteable(const u32 event_id) const { | 110 | std::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 | ||
| 15 | namespace Core { | 16 | namespace Core { |
| @@ -22,15 +23,23 @@ class NVFlinger; | |||
| 22 | 23 | ||
| 23 | namespace Service::Nvidia { | 24 | namespace Service::Nvidia { |
| 24 | 25 | ||
| 26 | class SyncpointManager; | ||
| 27 | |||
| 25 | namespace Devices { | 28 | namespace Devices { |
| 26 | class nvdevice; | 29 | class nvdevice; |
| 27 | } | 30 | } |
| 28 | 31 | ||
| 32 | /// Represents an Nvidia event | ||
| 33 | struct NvEvent { | ||
| 34 | Kernel::EventPair event; | ||
| 35 | Fence fence{}; | ||
| 36 | }; | ||
| 37 | |||
| 29 | struct EventInterface { | 38 | struct 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 | ||
| 121 | private: | 130 | private: |
| 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 | |||
| 9 | namespace Service::Nvidia { | ||
| 10 | |||
| 11 | SyncpointManager::SyncpointManager(Tegra::GPU& gpu) : gpu{gpu} {} | ||
| 12 | |||
| 13 | SyncpointManager::~SyncpointManager() = default; | ||
| 14 | |||
| 15 | u32 SyncpointManager::RefreshSyncpoint(u32 syncpoint_id) { | ||
| 16 | syncpoints[syncpoint_id].min = gpu.GetSyncpointValue(syncpoint_id); | ||
| 17 | return GetSyncpointMin(syncpoint_id); | ||
| 18 | } | ||
| 19 | |||
| 20 | u32 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 | |||
| 31 | u32 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 | |||
| 13 | namespace Tegra { | ||
| 14 | class GPU; | ||
| 15 | } | ||
| 16 | |||
| 17 | namespace Service::Nvidia { | ||
| 18 | |||
| 19 | class SyncpointManager final { | ||
| 20 | public: | ||
| 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 | |||
| 73 | private: | ||
| 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 @@ | |||
| 14 | namespace Settings { | 14 | namespace Settings { |
| 15 | 15 | ||
| 16 | Values values = {}; | 16 | Values values = {}; |
| 17 | bool configuring_global = true; | 17 | static bool configuring_global = true; |
| 18 | 18 | ||
| 19 | std::string GetTimeZoneString() { | 19 | std::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 | ||
| 84 | float Volume() { | 84 | bool IsConfiguringGlobal() { |
| 85 | if (values.audio_muted) { | 85 | return configuring_global; |
| 86 | return 0.0f; | 86 | } |
| 87 | } | 87 | |
| 88 | return values.volume.GetValue(); | 88 | void SetConfiguringGlobal(bool is_global) { |
| 89 | configuring_global = is_global; | ||
| 89 | } | 90 | } |
| 90 | 91 | ||
| 91 | bool IsGPULevelExtreme() { | 92 | bool 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 | ||
| 101 | float Volume() { | ||
| 102 | if (values.audio_muted) { | ||
| 103 | return 0.0f; | ||
| 104 | } | ||
| 105 | return values.volume.GetValue(); | ||
| 106 | } | ||
| 107 | |||
| 100 | void RestoreGlobalState() { | 108 | void 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 | ||
| 36 | extern bool configuring_global; | ||
| 37 | |||
| 38 | template <typename Type> | 36 | template <typename Type> |
| 39 | class Setting final { | 37 | class Setting final { |
| 40 | public: | 38 | public: |
| @@ -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 | ||
| 203 | float Volume(); | 201 | extern Values values; |
| 202 | |||
| 203 | bool IsConfiguringGlobal(); | ||
| 204 | void SetConfiguringGlobal(bool is_global); | ||
| 204 | 205 | ||
| 205 | bool IsGPULevelExtreme(); | 206 | bool IsGPULevelExtreme(); |
| 206 | bool IsGPULevelHigh(); | 207 | bool IsGPULevelHigh(); |
| 207 | 208 | ||
| 209 | float Volume(); | ||
| 210 | |||
| 208 | std::string GetTimeZoneString(); | 211 | std::string GetTimeZoneString(); |
| 209 | 212 | ||
| 210 | void Apply(); | 213 | void 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 | ||
| 22 | namespace GCAdapter { | 22 | namespace GCAdapter { |
| 23 | 23 | ||
| 24 | // Used to loop through and assign button in poller | ||
| 25 | constexpr 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 | |||
| 32 | static 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 | |||
| 44 | Adapter::Adapter() { | 24 | Adapter::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 | ||
| 58 | GCPadStatus Adapter::GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload) { | 38 | Adapter::~Adapter() { |
| 59 | GCPadStatus pad = {}; | 39 | Reset(); |
| 60 | const std::size_t offset = 1 + (9 * port); | 40 | } |
| 41 | |||
| 42 | void 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 | |||
| 67 | bool 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 | |||
| 84 | void 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 | |||
| 101 | void 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 | |||
| 110 | void 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 | |||
| 144 | void 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) { | 166 | void 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 | ||
| 115 | void Adapter::Read() { | 192 | void 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 | ||
| 159 | void Adapter::Setup() { | 208 | void 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 | ||
| 187 | bool Adapter::CheckDeviceAccess(libusb_device* device) { | 233 | bool 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 | |
| 241 | void 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) { | 255 | void 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; | 280 | bool 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 | ||
| 239 | void Adapter::GetGCEndpoint(libusb_device* device) { | 314 | bool 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 | ||
| 272 | Adapter::~Adapter() { | 345 | void Adapter::JoinThreads() { |
| 273 | Reset(); | 346 | restart_scan_thread = false; |
| 274 | } | 347 | adapter_input_thread_running = false; |
| 348 | adapter_scan_thread_running = false; | ||
| 275 | 349 | ||
| 276 | void 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); | 359 | void 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 | |||
| 367 | void Adapter::ResetDevices() { | ||
| 368 | for (std::size_t i = 0; i < pads.size(); ++i) { | ||
| 369 | ResetDevice(i); | ||
| 370 | } | ||
| 371 | } | ||
| 372 | |||
| 373 | void 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 | |||
| 383 | void 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 | ||
| 298 | std::vector<Common::ParamPackage> Adapter::GetInputDevices() const { | 393 | std::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 | ||
| 384 | bool Adapter::DeviceConnected(std::size_t port) const { | 481 | bool Adapter::DeviceConnected(std::size_t port) const { |
| 385 | return adapter_controllers_status[port] != ControllerTypes::None; | 482 | return pads[port].type != ControllerTypes::None; |
| 386 | } | ||
| 387 | |||
| 388 | void Adapter::ResetDeviceType(std::size_t port) { | ||
| 389 | adapter_controllers_status[port] = ControllerTypes::None; | ||
| 390 | } | 483 | } |
| 391 | 484 | ||
| 392 | void Adapter::BeginConfiguration() { | 485 | void 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 | ||
| 400 | void Adapter::EndConfiguration() { | 490 | void 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 | ||
| 407 | std::array<Common::SPSCQueue<GCPadStatus>, 4>& Adapter::GetPadQueue() { | 495 | Common::SPSCQueue<GCPadStatus>& Adapter::GetPadQueue() { |
| 408 | return pad_queue; | 496 | return pad_queue; |
| 409 | } | 497 | } |
| 410 | 498 | ||
| 411 | const std::array<Common::SPSCQueue<GCPadStatus>, 4>& Adapter::GetPadQueue() const { | 499 | const Common::SPSCQueue<GCPadStatus>& Adapter::GetPadQueue() const { |
| 412 | return pad_queue; | 500 | return pad_queue; |
| 413 | } | 501 | } |
| 414 | 502 | ||
| 415 | std::array<GCState, 4>& Adapter::GetPadState() { | 503 | GCController& Adapter::GetPadState(std::size_t port) { |
| 416 | return state; | 504 | return pads.at(port); |
| 417 | } | ||
| 418 | |||
| 419 | const std::array<GCState, 4>& Adapter::GetPadState() const { | ||
| 420 | return state; | ||
| 421 | } | 505 | } |
| 422 | 506 | ||
| 423 | int Adapter::GetOriginValue(u32 port, u32 axis) const { | 507 | const 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; | |||
| 19 | namespace GCAdapter { | 19 | namespace GCAdapter { |
| 20 | 20 | ||
| 21 | enum class PadButton { | 21 | enum 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 | ||
| 38 | extern const std::array<PadButton, 12> PadButtonArray; | ||
| 39 | |||
| 40 | enum class PadAxes : u8 { | 39 | enum 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 | ||
| 49 | enum class ControllerTypes { | ||
| 50 | None, | ||
| 51 | Wired, | ||
| 52 | Wireless, | ||
| 53 | }; | ||
| 54 | |||
| 50 | struct GCPadStatus { | 55 | struct 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 | ||
| 61 | struct GCState { | 65 | struct 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 | ||
| 66 | enum class ControllerTypes { None, Wired, Wireless }; | ||
| 67 | |||
| 68 | class Adapter { | 75 | class Adapter { |
| 69 | public: | 76 | public: |
| 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 | 101 | private: |
| 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 | ||
| 94 | private: | 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 | ||
| 16 | class GCButton final : public Input::ButtonDevice { | 16 | class GCButton final : public Input::ButtonDevice { |
| 17 | public: | 17 | public: |
| 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 | |||
| 30 | private: | 38 | private: |
| 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 | ||
| 36 | class GCAxisButton final : public Input::ButtonDevice { | 44 | class 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 | ||
| 67 | GCButtonFactory::GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_) | 73 | GCButtonFactory::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 | ||
| 255 | Common::ParamPackage GCAnalogFactory::GetNextInput() { | 249 | Common::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 | ||
| 53 | private: | 51 | private: |
| 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 | ||
| 26 | namespace Tegra::Decoder { | 27 | namespace Tegra::Decoder { |
| 28 | namespace { | ||
| 29 | // ZigZag LUTs from libavcodec. | ||
| 30 | constexpr 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 | |||
| 36 | constexpr 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 | |||
| 27 | H264::H264(GPU& gpu_) : gpu(gpu_) {} | 42 | H264::H264(GPU& gpu_) : gpu(gpu_) {} |
| 28 | 43 | ||
| 29 | H264::~H264() = default; | 44 | H264::~H264() = default; |
| 30 | 45 | ||
| 31 | std::vector<u8>& H264::ComposeFrameHeader(NvdecCommon::NvdecRegisters& state, bool is_first_frame) { | 46 | const 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 | ||
| 175 | void H264BitWriter::WriteUe(s32 value) { | 192 | void H264BitWriter::WriteUe(u32 value) { |
| 176 | WriteExpGolombCodedUInt((u32)value); | 193 | WriteExpGolombCodedUInt(value); |
| 177 | } | 194 | } |
| 178 | 195 | ||
| 179 | void H264BitWriter::End() { | 196 | void 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 | ||
| 57 | private: | 57 | private: |
| 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 | ||
| 92 | private: | 80 | private: |
| 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 | ||
| 11 | namespace Tegra::Decoder { | 11 | namespace Tegra::Decoder { |
| 12 | 12 | namespace { | |
| 13 | // Default compressed header probabilities once frame context resets | 13 | // Default compressed header probabilities once frame context resets |
| 14 | constexpr Vp9EntropyProbs default_probs{ | 14 | constexpr 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 | ||
| 173 | constexpr 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 | |||
| 184 | constexpr 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 | |||
| 173 | VP9::VP9(GPU& gpu) : gpu(gpu) {} | 256 | VP9::VP9(GPU& gpu) : gpu(gpu) {} |
| 174 | 257 | ||
| 175 | VP9::~VP9() = default; | 258 | VP9::~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 | ||
| 210 | s32 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 | |||
| 226 | s32 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 | |||
| 236 | void VP9::EncodeTermSubExp(VpxRangeEncoder& writer, s32 value) { | 293 | void 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 | ||
| 335 | s32 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 | |||
| 346 | s32 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 | |||
| 357 | Vp9PictureInfo VP9::GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state) { | 392 | Vp9PictureInfo 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 | ||
| 616 | VpxBitStreamWriter VP9::ComposeUncompressedHeader() { | 645 | VpxBitStreamWriter 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 | ||
| 827 | std::vector<u8>& VP9::ComposeFrameHeader(NvdecCommon::NvdecRegisters& state) { | 857 | const 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 | ||
| 68 | class VpxBitStreamWriter { | 63 | class 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 | ||
| 94 | private: | 95 | private: |
| 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 | ||
| 15 | namespace Tegra { | 13 | namespace Tegra { |
| 16 | class GPU; | 14 | class 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 | ||
| 18 | void Tegra::Host1x::ProcessMethod(Host1x::Method method, const std::vector<u32>& arguments) { | 18 | void 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 | ||
| 66 | private: | 66 | private: |
| 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 | ||
| 13 | namespace Tegra { | 9 | namespace Tegra { |
| 14 | 10 | ||
| @@ -16,7 +12,7 @@ Nvdec::Nvdec(GPU& gpu_) : gpu(gpu_), codec(std::make_unique<Codec>(gpu)) {} | |||
| 16 | 12 | ||
| 17 | Nvdec::~Nvdec() = default; | 13 | Nvdec::~Nvdec() = default; |
| 18 | 14 | ||
| 19 | void Nvdec::ProcessMethod(Nvdec::Method method, const std::vector<u32>& arguments) { | 15 | void 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 | ||
| 32 | private: | 32 | private: |
| 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_) {} | |||
| 27 | SyncptIncrManager::~SyncptIncrManager() = default; | 27 | SyncptIncrManager::~SyncptIncrManager() = default; |
| 28 | 28 | ||
| 29 | void SyncptIncrManager::Increment(u32 id) { | 29 | void 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 | ||
| 34 | u32 SyncptIncrManager::IncrementWhenDone(u32 class_id, u32 id) { | 34 | u32 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 | ||
| 40 | void SyncptIncrManager::SignalDone(u32 handle) { | 40 | void 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 | ||
| 39 | class SyncptIncrManager { | 39 | class 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 | ||
| 29 | void Vic::ProcessMethod(Vic::Method method, const std::vector<u32>& arguments) { | 29 | void 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 | ||
| 72 | private: | 72 | private: |
| 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 | ||
| 13 | namespace Tegra { | 14 | namespace Tegra { |
| 14 | 15 | ||
| 16 | void 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 | |||
| 15 | DmaPusher::DmaPusher(Core::System& system, GPU& gpu) : gpu{gpu}, system{system} {} | 30 | DmaPusher::DmaPusher(Core::System& system, GPU& gpu) : gpu{gpu}, system{system} {} |
| 16 | 31 | ||
| 17 | DmaPusher::~DmaPusher() = default; | 32 | DmaPusher::~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. | ||
| 33 | enum 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 | |||
| 30 | struct CommandListHeader { | 55 | struct CommandListHeader { |
| 31 | union { | 56 | union { |
| 32 | u64 raw; | 57 | u64 raw; |
| @@ -49,9 +74,29 @@ union CommandHeader { | |||
| 49 | static_assert(std::is_standard_layout_v<CommandHeader>, "CommandHeader is not standard layout"); | 74 | static_assert(std::is_standard_layout_v<CommandHeader>, "CommandHeader is not standard layout"); |
| 50 | static_assert(sizeof(CommandHeader) == sizeof(u32), "CommandHeader has incorrect size!"); | 75 | static_assert(sizeof(CommandHeader) == sizeof(u32), "CommandHeader has incorrect size!"); |
| 51 | 76 | ||
| 77 | static 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 | |||
| 52 | class GPU; | 86 | class GPU; |
| 53 | 87 | ||
| 54 | using CommandList = std::vector<Tegra::CommandListHeader>; | 88 | struct 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 | */ |
| 63 | class DmaPusher { | 108 | class DmaPusher final { |
| 64 | public: | 109 | public: |
| 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() { | |||
| 194 | void GPU::OnCommandListEnd() { | 194 | void 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. | ||
| 200 | enum 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 | ||
| 222 | enum class GpuSemaphoreOperation { | 198 | enum 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 | ||
| 375 | void 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 | |||
| 389 | void GPU::ProcessWaitForInterruptMethod() { | ||
| 390 | // TODO(bunnei) ImplementMe | ||
| 391 | LOG_WARNING(HW_GPU, "(STUBBED) called"); | ||
| 392 | } | ||
| 393 | |||
| 394 | void GPU::ProcessSemaphoreTriggerMethod() { | 394 | void 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 | ||
| 343 | private: | 358 | private: |
| 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 | ||
| 22 | void AsyncShaders::AllocateWorkers() { | 22 | void 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 | ||
| 101 | void ConfigureAudio::ApplyConfiguration() { | 101 | void 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 | ||
| 167 | void ConfigureAudio::SetupPerGameUI() { | 167 | void 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 @@ | |||
| 15 | ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, | 15 | ConfigureDialog::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 | ||
| 52 | void ConfigureGeneral::ApplyConfiguration() { | 52 | void 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 | ||
| 95 | void ConfigureGeneral::SetupPerGameUI() { | 95 | void 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 | ||
| 56 | void ConfigureGraphics::UpdateDeviceSelection(int device) { | 56 | void 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 | ||
| 102 | void ConfigureGraphics::ApplyConfiguration() { | 102 | void 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 | ||
| 229 | Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { | 229 | Settings::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 | ||
| 243 | void ConfigureGraphics::SetupPerGameUI() { | 243 | void 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 | ||
| 119 | void ConfigureGraphicsAdvanced::SetupPerGameUI() { | 119 | void 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 | ||
| 220 | void ConfigureSystem::SetupPerGameUI() { | 220 | void 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()); |