summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules2
-rw-r--r--CMakeLists.txt14
-rw-r--r--CMakeModules/CopyYuzuFFmpegDeps.cmake10
-rw-r--r--externals/CMakeLists.txt25
-rw-r--r--externals/find-modules/FindFFmpeg.cmake100
-rw-r--r--externals/httplib/README.md2
-rw-r--r--externals/httplib/httplib.h651
m---------externals/inih/inih0
m---------externals/libressl0
-rw-r--r--externals/lurlparser/CMakeLists.txt8
-rw-r--r--externals/lurlparser/LUrlParser.cpp265
-rw-r--r--externals/lurlparser/LUrlParser.h78
-rw-r--r--externals/lurlparser/README.md19
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/common/fiber.cpp4
-rw-r--r--src/common/fiber.h2
-rw-r--r--src/common/stream.cpp47
-rw-r--r--src/common/stream.h50
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/core.cpp15
-rw-r--r--src/core/core.h8
-rw-r--r--src/core/cpu_manager.cpp2
-rw-r--r--src/core/frontend/applets/controller.cpp2
-rw-r--r--src/core/frontend/applets/controller.h4
-rw-r--r--src/core/hle/kernel/kernel.cpp5
-rw-r--r--src/core/hle/kernel/process.cpp3
-rw-r--r--src/core/hle/service/am/am.cpp2
-rw-r--r--src/core/hle/service/hid/hid.cpp2
-rw-r--r--src/core/hle/service/mii/mii.cpp1
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp100
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.h71
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp234
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h168
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.cpp90
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.h88
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.h1
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp4
-rw-r--r--src/core/hle/service/set/set.cpp1
-rw-r--r--src/core/hle/service/set/set_sys.cpp2
-rw-r--r--src/core/hle/service/time/time.cpp28
-rw-r--r--src/core/hle/service/time/time.h9
-rw-r--r--src/core/hle/service/time/time_manager.cpp359
-rw-r--r--src/core/hle/service/time/time_manager.h85
-rw-r--r--src/core/hle/service/time/time_zone_content_manager.cpp5
-rw-r--r--src/core/hle/service/time/time_zone_content_manager.h4
-rw-r--r--src/core/settings.cpp2
-rw-r--r--src/core/settings.h1
-rw-r--r--src/core/telemetry_session.cpp2
-rw-r--r--src/input_common/gcadapter/gc_adapter.cpp462
-rw-r--r--src/input_common/gcadapter/gc_adapter.h146
-rw-r--r--src/input_common/gcadapter/gc_poller.cpp128
-rw-r--r--src/input_common/sdl/sdl_impl.cpp169
-rw-r--r--src/tests/common/fibers.cpp71
-rw-r--r--src/video_core/CMakeLists.txt29
-rw-r--r--src/video_core/cdma_pusher.cpp171
-rw-r--r--src/video_core/cdma_pusher.h138
-rw-r--r--src/video_core/command_classes/codecs/codec.cpp115
-rw-r--r--src/video_core/command_classes/codecs/codec.h66
-rw-r--r--src/video_core/command_classes/codecs/h264.cpp292
-rw-r--r--src/video_core/command_classes/codecs/h264.h118
-rw-r--r--src/video_core/command_classes/codecs/vp9.cpp1034
-rw-r--r--src/video_core/command_classes/codecs/vp9.h188
-rw-r--r--src/video_core/command_classes/codecs/vp9_types.h367
-rw-r--r--src/video_core/command_classes/host1x.cpp39
-rw-r--r--src/video_core/command_classes/host1x.h78
-rw-r--r--src/video_core/command_classes/nvdec.cpp52
-rw-r--r--src/video_core/command_classes/nvdec.h39
-rw-r--r--src/video_core/command_classes/nvdec_common.h48
-rw-r--r--src/video_core/command_classes/sync_manager.cpp60
-rw-r--r--src/video_core/command_classes/sync_manager.h64
-rw-r--r--src/video_core/command_classes/vic.cpp180
-rw-r--r--src/video_core/command_classes/vic.h110
-rw-r--r--src/video_core/gpu.cpp11
-rw-r--r--src/video_core/gpu.h23
-rw-r--r--src/video_core/gpu_asynch.cpp26
-rw-r--r--src/video_core/gpu_asynch.h3
-rw-r--r--src/video_core/gpu_synch.cpp18
-rw-r--r--src/video_core/gpu_synch.h3
-rw-r--r--src/video_core/gpu_thread.cpp16
-rw-r--r--src/video_core/gpu_thread.h19
-rw-r--r--src/video_core/host_shaders/CMakeLists.txt17
-rw-r--r--src/video_core/host_shaders/StringShaderHeader.cmake2
-rw-r--r--src/video_core/memory_manager.cpp12
-rw-r--r--src/video_core/memory_manager.h5
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.cpp3
-rw-r--r--src/video_core/renderer_vulkan/wrapper.cpp2
-rw-r--r--src/video_core/video_core.cpp5
-rw-r--r--src/web_service/CMakeLists.txt2
-rw-r--r--src/web_service/web_backend.cpp26
-rw-r--r--src/yuzu/CMakeLists.txt2
-rw-r--r--src/yuzu/applets/controller.cpp20
-rw-r--r--src/yuzu/applets/controller.h8
-rw-r--r--src/yuzu/applets/profile_select.cpp9
-rw-r--r--src/yuzu/applets/profile_select.h1
-rw-r--r--src/yuzu/configuration/config.cpp16
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp10
-rw-r--r--src/yuzu/configuration/configure_graphics.h1
-rw-r--r--src/yuzu/configuration/configure_graphics.ui7
-rw-r--r--src/yuzu/configuration/configure_system.cpp26
-rw-r--r--src/yuzu/main.cpp26
-rw-r--r--src/yuzu_cmd/config.cpp6
-rw-r--r--src/yuzu_cmd/default_ini.h4
102 files changed, 5463 insertions, 1609 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 0479efaab..1bd9d2aa7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -263,6 +263,7 @@ if (CONAN_REQUIRED_LIBS)
263 libzip:with_openssl=False 263 libzip:with_openssl=False
264 libzip:enable_windows_crypto=False 264 libzip:enable_windows_crypto=False
265 ) 265 )
266
266 conan_check(VERSION 1.24.0 REQUIRED) 267 conan_check(VERSION 1.24.0 REQUIRED)
267 # Add the bincrafters remote 268 # Add the bincrafters remote
268 conan_add_remote(NAME bincrafters 269 conan_add_remote(NAME bincrafters
@@ -354,6 +355,19 @@ if (NOT LIBUSB_FOUND)
354 set(LIBUSB_LIBRARIES usb) 355 set(LIBUSB_LIBRARIES usb)
355endif() 356endif()
356 357
358# Use system installed ffmpeg.
359if (NOT MSVC)
360 find_package(FFmpeg REQUIRED)
361else()
362 set(FFMPEG_EXT_NAME "ffmpeg-4.2.1")
363 set(FFMPEG_PATH "${CMAKE_BINARY_DIR}/externals/${FFMPEG_EXT_NAME}")
364 download_bundled_external("ffmpeg/" ${FFMPEG_EXT_NAME} "")
365 set(FFMPEG_FOUND YES)
366 set(FFMPEG_INCLUDE_DIR "${FFMPEG_PATH}/include" CACHE PATH "Path to FFmpeg headers" FORCE)
367 set(FFMPEG_LIBRARY_DIR "${FFMPEG_PATH}/bin" CACHE PATH "Path to FFmpeg library" FORCE)
368 set(FFMPEG_DLL_DIR "${FFMPEG_PATH}/bin" CACHE PATH "Path to FFmpeg dll's" FORCE)
369endif()
370
357# Prefer the -pthread flag on Linux. 371# Prefer the -pthread flag on Linux.
358set(THREADS_PREFER_PTHREAD_FLAG ON) 372set(THREADS_PREFER_PTHREAD_FLAG ON)
359find_package(Threads REQUIRED) 373find_package(Threads REQUIRED)
diff --git a/CMakeModules/CopyYuzuFFmpegDeps.cmake b/CMakeModules/CopyYuzuFFmpegDeps.cmake
new file mode 100644
index 000000000..cca1eeeab
--- /dev/null
+++ b/CMakeModules/CopyYuzuFFmpegDeps.cmake
@@ -0,0 +1,10 @@
1function(copy_yuzu_FFmpeg_deps target_dir)
2 include(WindowsCopyFiles)
3 set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/")
4 windows_copy_files(${target_dir} ${FFMPEG_DLL_DIR} ${DLL_DEST}
5 avcodec-58.dll
6 avutil-56.dll
7 swresample-3.dll
8 swscale-5.dll
9 )
10endfunction(copy_yuzu_FFmpeg_deps)
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index e01ff6930..421b35890 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -73,17 +73,20 @@ if (NOT LIBZIP_FOUND)
73endif() 73endif()
74 74
75if (ENABLE_WEB_SERVICE) 75if (ENABLE_WEB_SERVICE)
76 # LibreSSL 76 find_package(OpenSSL 1.1)
77 set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "") 77 if (OPENSSL_FOUND)
78 add_subdirectory(libressl EXCLUDE_FROM_ALL) 78 set(OPENSSL_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
79 target_include_directories(ssl INTERFACE ./libressl/include) 79 else()
80 target_compile_definitions(ssl PRIVATE -DHAVE_INET_NTOP) 80 # LibreSSL
81 get_directory_property(OPENSSL_LIBRARIES 81 set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
82 DIRECTORY libressl 82 set(OPENSSLDIR "/etc/ssl/")
83 DEFINITION OPENSSL_LIBS) 83 add_subdirectory(libressl EXCLUDE_FROM_ALL)
84 84 target_include_directories(ssl INTERFACE ./libressl/include)
85 # lurlparser 85 target_compile_definitions(ssl PRIVATE -DHAVE_INET_NTOP)
86 add_subdirectory(lurlparser EXCLUDE_FROM_ALL) 86 get_directory_property(OPENSSL_LIBRARIES
87 DIRECTORY libressl
88 DEFINITION OPENSSL_LIBS)
89 endif()
87 90
88 # httplib 91 # httplib
89 add_library(httplib INTERFACE) 92 add_library(httplib INTERFACE)
diff --git a/externals/find-modules/FindFFmpeg.cmake b/externals/find-modules/FindFFmpeg.cmake
new file mode 100644
index 000000000..77b331e00
--- /dev/null
+++ b/externals/find-modules/FindFFmpeg.cmake
@@ -0,0 +1,100 @@
1# - Try to find ffmpeg libraries (libavcodec, libavformat and libavutil)
2# Once done this will define
3#
4# FFMPEG_FOUND - system has ffmpeg or libav
5# FFMPEG_INCLUDE_DIR - the ffmpeg include directory
6# FFMPEG_LIBRARIES - Link these to use ffmpeg
7# FFMPEG_LIBAVCODEC
8# FFMPEG_LIBAVFORMAT
9# FFMPEG_LIBAVUTIL
10#
11# Copyright (c) 2008 Andreas Schneider <mail@cynapses.org>
12# Modified for other libraries by Lasse Kärkkäinen <tronic>
13# Modified for Hedgewars by Stepik777
14# Modified for FFmpeg-example Tuukka Pasanen 2018
15# Modified for yuzu toastUnlimted 2020
16#
17# Redistribution and use is allowed according to the terms of the New
18# BSD license.
19#
20
21include(FindPackageHandleStandardArgs)
22
23find_package_handle_standard_args(FFMPEG
24 FOUND_VAR FFMPEG_FOUND
25 REQUIRED_VARS
26 FFMPEG_LIBRARY
27 FFMPEG_INCLUDE_DIR
28 VERSION_VAR FFMPEG_VERSION
29)
30
31if(FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR)
32 # in cache already
33 set(FFMPEG_FOUND TRUE)
34else()
35 # use pkg-config to get the directories and then use these values
36 # in the FIND_PATH() and FIND_LIBRARY() calls
37 find_package(PkgConfig)
38 if(PKG_CONFIG_FOUND)
39 pkg_check_modules(_FFMPEG_AVCODEC libavcodec)
40 pkg_check_modules(_FFMPEG_AVUTIL libavutil)
41 pkg_check_modules(_FFMPEG_SWSCALE libswscale)
42 endif()
43
44 find_path(FFMPEG_AVCODEC_INCLUDE_DIR
45 NAMES libavcodec/avcodec.h
46 PATHS ${_FFMPEG_AVCODEC_INCLUDE_DIRS}
47 /usr/include
48 /usr/local/include
49 /opt/local/include
50 /sw/include
51 PATH_SUFFIXES ffmpeg libav)
52
53 find_library(FFMPEG_LIBAVCODEC
54 NAMES avcodec
55 PATHS ${_FFMPEG_AVCODEC_LIBRARY_DIRS}
56 /usr/lib
57 /usr/local/lib
58 /opt/local/lib
59 /sw/lib)
60
61 find_library(FFMPEG_LIBAVUTIL
62 NAMES avutil
63 PATHS ${_FFMPEG_AVUTIL_LIBRARY_DIRS}
64 /usr/lib
65 /usr/local/lib
66 /opt/local/lib
67 /sw/lib)
68
69 find_library(FFMPEG_LIBSWSCALE
70 NAMES swscale
71 PATHS ${_FFMPEG_SWSCALE_LIBRARY_DIRS}
72 /usr/lib
73 /usr/local/lib
74 /opt/local/lib
75 /sw/lib)
76
77 if(FFMPEG_LIBAVCODEC AND FFMPEG_LIBAVUTIL AND FFMPEG_LIBSWSCALE)
78 set(FFMPEG_FOUND TRUE)
79 endif()
80
81 if(FFMPEG_FOUND)
82 set(FFMPEG_INCLUDE_DIR ${FFMPEG_AVCODEC_INCLUDE_DIR})
83 set(FFMPEG_LIBRARIES
84 ${FFMPEG_LIBAVCODEC}
85 ${FFMPEG_LIBAVUTIL}
86 ${FFMPEG_LIBSWSCALE})
87 endif()
88
89 if(FFMPEG_FOUND)
90 if(NOT FFMPEG_FIND_QUIETLY)
91 message(STATUS
92 "Found FFMPEG or Libav: ${FFMPEG_LIBRARIES}, ${FFMPEG_INCLUDE_DIR}")
93 endif()
94 else()
95 if(FFMPEG_FIND_REQUIRED)
96 message(FATAL_ERROR
97 "Could not find libavcodec or libavutil or libswscale")
98 endif()
99 endif()
100endif()
diff --git a/externals/httplib/README.md b/externals/httplib/README.md
index 73037d297..1940e446c 100644
--- a/externals/httplib/README.md
+++ b/externals/httplib/README.md
@@ -1,4 +1,4 @@
1From https://github.com/yhirose/cpp-httplib/tree/fce8e6fefdab4ad48bc5b25c98e5ebfda4f3cf53 1From https://github.com/yhirose/cpp-httplib/tree/ff5677ad197947177c158fe857caff4f0e242045 with https://github.com/yhirose/cpp-httplib/pull/701
2 2
3MIT License 3MIT License
4 4
diff --git a/externals/httplib/httplib.h b/externals/httplib/httplib.h
index 5139b7f05..8982054e2 100644
--- a/externals/httplib/httplib.h
+++ b/externals/httplib/httplib.h
@@ -173,6 +173,7 @@ using socket_t = int;
173#define INVALID_SOCKET (-1) 173#define INVALID_SOCKET (-1)
174#endif //_WIN32 174#endif //_WIN32
175 175
176#include <algorithm>
176#include <array> 177#include <array>
177#include <atomic> 178#include <atomic>
178#include <cassert> 179#include <cassert>
@@ -237,6 +238,27 @@ namespace httplib {
237 238
238namespace detail { 239namespace detail {
239 240
241/*
242 * Backport std::make_unique from C++14.
243 *
244 * NOTE: This code came up with the following stackoverflow post:
245 * https://stackoverflow.com/questions/10149840/c-arrays-and-make-unique
246 *
247 */
248
249template <class T, class... Args>
250typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
251make_unique(Args &&... args) {
252 return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
253}
254
255template <class T>
256typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
257make_unique(std::size_t n) {
258 typedef typename std::remove_extent<T>::type RT;
259 return std::unique_ptr<T>(new RT[n]);
260}
261
240struct ci { 262struct ci {
241 bool operator()(const std::string &s1, const std::string &s2) const { 263 bool operator()(const std::string &s1, const std::string &s2) const {
242 return std::lexicographical_compare( 264 return std::lexicographical_compare(
@@ -304,6 +326,10 @@ using ContentProvider =
304using ContentProviderWithoutLength = 326using ContentProviderWithoutLength =
305 std::function<bool(size_t offset, DataSink &sink)>; 327 std::function<bool(size_t offset, DataSink &sink)>;
306 328
329using ContentReceiverWithProgress =
330 std::function<bool(const char *data, size_t data_length, uint64_t offset,
331 uint64_t total_length)>;
332
307using ContentReceiver = 333using ContentReceiver =
308 std::function<bool(const char *data, size_t data_length)>; 334 std::function<bool(const char *data, size_t data_length)>;
309 335
@@ -317,14 +343,17 @@ public:
317 ContentReceiver receiver)>; 343 ContentReceiver receiver)>;
318 344
319 ContentReader(Reader reader, MultipartReader multipart_reader) 345 ContentReader(Reader reader, MultipartReader multipart_reader)
320 : reader_(reader), multipart_reader_(multipart_reader) {} 346 : reader_(std::move(reader)),
347 multipart_reader_(std::move(multipart_reader)) {}
321 348
322 bool operator()(MultipartContentHeader header, 349 bool operator()(MultipartContentHeader header,
323 ContentReceiver receiver) const { 350 ContentReceiver receiver) const {
324 return multipart_reader_(header, receiver); 351 return multipart_reader_(std::move(header), std::move(receiver));
325 } 352 }
326 353
327 bool operator()(ContentReceiver receiver) const { return reader_(receiver); } 354 bool operator()(ContentReceiver receiver) const {
355 return reader_(std::move(receiver));
356 }
328 357
329 Reader reader_; 358 Reader reader_;
330 MultipartReader multipart_reader_; 359 MultipartReader multipart_reader_;
@@ -353,7 +382,7 @@ struct Request {
353 // for client 382 // for client
354 size_t redirect_count = CPPHTTPLIB_REDIRECT_MAX_COUNT; 383 size_t redirect_count = CPPHTTPLIB_REDIRECT_MAX_COUNT;
355 ResponseHandler response_handler; 384 ResponseHandler response_handler;
356 ContentReceiver content_receiver; 385 ContentReceiverWithProgress content_receiver;
357 size_t content_length = 0; 386 size_t content_length = 0;
358 ContentProvider content_provider; 387 ContentProvider content_provider;
359 Progress progress; 388 Progress progress;
@@ -475,7 +504,7 @@ public:
475 504
476 void enqueue(std::function<void()> fn) override { 505 void enqueue(std::function<void()> fn) override {
477 std::unique_lock<std::mutex> lock(mutex_); 506 std::unique_lock<std::mutex> lock(mutex_);
478 jobs_.push_back(fn); 507 jobs_.push_back(std::move(fn));
479 cond_.notify_one(); 508 cond_.notify_one();
480 } 509 }
481 510
@@ -664,8 +693,8 @@ private:
664 ContentReceiver multipart_receiver); 693 ContentReceiver multipart_receiver);
665 694
666 virtual bool process_and_close_socket(socket_t sock); 695 virtual bool process_and_close_socket(socket_t sock);
667 696
668 struct MountPointEntry { 697 struct MountPointEntry {
669 std::string mount_point; 698 std::string mount_point;
670 std::string base_dir; 699 std::string base_dir;
671 Headers headers; 700 Headers headers;
@@ -704,23 +733,27 @@ enum Error {
704 Canceled, 733 Canceled,
705 SSLConnection, 734 SSLConnection,
706 SSLLoadingCerts, 735 SSLLoadingCerts,
707 SSLServerVerification 736 SSLServerVerification,
737 UnsupportedMultipartBoundaryChars
708}; 738};
709 739
710class Result { 740class Result {
711public: 741public:
712 Result(const std::shared_ptr<Response> &res, Error err) 742 Result(std::unique_ptr<Response> res, Error err)
713 : res_(res), err_(err) {} 743 : res_(std::move(res)), err_(err) {}
714 operator bool() const { return res_ != nullptr; } 744 operator bool() const { return res_ != nullptr; }
715 bool operator==(std::nullptr_t) const { return res_ == nullptr; } 745 bool operator==(std::nullptr_t) const { return res_ == nullptr; }
716 bool operator!=(std::nullptr_t) const { return res_ != nullptr; } 746 bool operator!=(std::nullptr_t) const { return res_ != nullptr; }
717 const Response &value() const { return *res_; } 747 const Response &value() const { return *res_; }
748 Response &value() { return *res_; }
718 const Response &operator*() const { return *res_; } 749 const Response &operator*() const { return *res_; }
750 Response &operator*() { return *res_; }
719 const Response *operator->() const { return res_.get(); } 751 const Response *operator->() const { return res_.get(); }
752 Response *operator->() { return res_.get(); }
720 Error error() const { return err_; } 753 Error error() const { return err_; }
721 754
722private: 755private:
723 std::shared_ptr<Response> res_; 756 std::unique_ptr<Response> res_;
724 Error err_; 757 Error err_;
725}; 758};
726 759
@@ -777,6 +810,8 @@ public:
777 Result Post(const char *path, const MultipartFormDataItems &items); 810 Result Post(const char *path, const MultipartFormDataItems &items);
778 Result Post(const char *path, const Headers &headers, 811 Result Post(const char *path, const Headers &headers,
779 const MultipartFormDataItems &items); 812 const MultipartFormDataItems &items);
813 Result Post(const char *path, const Headers &headers,
814 const MultipartFormDataItems &items, const std::string &boundary);
780 815
781 Result Put(const char *path); 816 Result Put(const char *path);
782 Result Put(const char *path, const std::string &body, 817 Result Put(const char *path, const std::string &body,
@@ -863,7 +898,21 @@ protected:
863 }; 898 };
864 899
865 virtual bool create_and_connect_socket(Socket &socket); 900 virtual bool create_and_connect_socket(Socket &socket);
866 virtual void close_socket(Socket &socket, bool process_socket_ret); 901
902 // All of:
903 // shutdown_ssl
904 // shutdown_socket
905 // close_socket
906 // should ONLY be called when socket_mutex_ is locked.
907 // Also, shutdown_ssl and close_socket should also NOT be called concurrently
908 // with a DIFFERENT thread sending requests using that socket.
909 virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully);
910 void shutdown_socket(Socket &socket);
911 void close_socket(Socket &socket);
912
913 // Similar to shutdown_ssl and close_socket, this should NOT be called
914 // concurrently with a DIFFERENT thread sending requests from the socket
915 void lock_socket_and_shutdown_and_close();
867 916
868 bool process_request(Stream &strm, const Request &req, Response &res, 917 bool process_request(Stream &strm, const Request &req, Response &res,
869 bool close_connection); 918 bool close_connection);
@@ -873,7 +922,7 @@ protected:
873 void copy_settings(const ClientImpl &rhs); 922 void copy_settings(const ClientImpl &rhs);
874 923
875 // Error state 924 // Error state
876 mutable Error error_ = Error::Success; 925 mutable std::atomic<Error> error_;
877 926
878 // Socket endoint information 927 // Socket endoint information
879 const std::string host_; 928 const std::string host_;
@@ -885,6 +934,11 @@ protected:
885 mutable std::mutex socket_mutex_; 934 mutable std::mutex socket_mutex_;
886 std::recursive_mutex request_mutex_; 935 std::recursive_mutex request_mutex_;
887 936
937 // These are all protected under socket_mutex
938 int socket_requests_in_flight_ = 0;
939 std::thread::id socket_requests_are_from_thread_ = std::thread::id();
940 bool socket_should_be_closed_when_request_is_done_ = false;
941
888 // Default headers 942 // Default headers
889 Headers default_headers_; 943 Headers default_headers_;
890 944
@@ -942,13 +996,13 @@ private:
942 bool redirect(const Request &req, Response &res); 996 bool redirect(const Request &req, Response &res);
943 bool handle_request(Stream &strm, const Request &req, Response &res, 997 bool handle_request(Stream &strm, const Request &req, Response &res,
944 bool close_connection); 998 bool close_connection);
945 void stop_core(); 999 std::unique_ptr<Response> send_with_content_provider(
946 std::shared_ptr<Response> send_with_content_provider(
947 const char *method, const char *path, const Headers &headers, 1000 const char *method, const char *path, const Headers &headers,
948 const std::string &body, size_t content_length, 1001 const std::string &body, size_t content_length,
949 ContentProvider content_provider, const char *content_type); 1002 ContentProvider content_provider, const char *content_type);
950 1003
951 virtual bool process_socket(Socket &socket, 1004 // socket is const because this function is called when socket_mutex_ is not locked
1005 virtual bool process_socket(const Socket &socket,
952 std::function<bool(Stream &strm)> callback); 1006 std::function<bool(Stream &strm)> callback);
953 virtual bool is_ssl() const; 1007 virtual bool is_ssl() const;
954}; 1008};
@@ -1012,6 +1066,8 @@ public:
1012 Result Post(const char *path, const MultipartFormDataItems &items); 1066 Result Post(const char *path, const MultipartFormDataItems &items);
1013 Result Post(const char *path, const Headers &headers, 1067 Result Post(const char *path, const Headers &headers,
1014 const MultipartFormDataItems &items); 1068 const MultipartFormDataItems &items);
1069 Result Post(const char *path, const Headers &headers,
1070 const MultipartFormDataItems &items, const std::string &boundary);
1015 Result Put(const char *path); 1071 Result Put(const char *path);
1016 Result Put(const char *path, const std::string &body, 1072 Result Put(const char *path, const std::string &body,
1017 const char *content_type); 1073 const char *content_type);
@@ -1098,7 +1154,7 @@ public:
1098#endif 1154#endif
1099 1155
1100private: 1156private:
1101 std::shared_ptr<ClientImpl> cli_; 1157 std::unique_ptr<ClientImpl> cli_;
1102 1158
1103#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 1159#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1104 bool is_ssl_ = false; 1160 bool is_ssl_ = false;
@@ -1154,9 +1210,9 @@ public:
1154 1210
1155private: 1211private:
1156 bool create_and_connect_socket(Socket &socket) override; 1212 bool create_and_connect_socket(Socket &socket) override;
1157 void close_socket(Socket &socket, bool process_socket_ret) override; 1213 void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override;
1158 1214
1159 bool process_socket(Socket &socket, 1215 bool process_socket(const Socket &socket,
1160 std::function<bool(Stream &strm)> callback) override; 1216 std::function<bool(Stream &strm)> callback) override;
1161 bool is_ssl() const override; 1217 bool is_ssl() const override;
1162 1218
@@ -1942,9 +1998,9 @@ inline socket_t create_client_socket(const char *host, int port,
1942 bool tcp_nodelay, 1998 bool tcp_nodelay,
1943 SocketOptions socket_options, 1999 SocketOptions socket_options,
1944 time_t timeout_sec, time_t timeout_usec, 2000 time_t timeout_sec, time_t timeout_usec,
1945 const std::string &intf, Error &error) { 2001 const std::string &intf, std::atomic<Error> &error) {
1946 auto sock = create_socket( 2002 auto sock = create_socket(
1947 host, port, 0, tcp_nodelay, socket_options, 2003 host, port, 0, tcp_nodelay, std::move(socket_options),
1948 [&](socket_t sock, struct addrinfo &ai) -> bool { 2004 [&](socket_t sock, struct addrinfo &ai) -> bool {
1949 if (!intf.empty()) { 2005 if (!intf.empty()) {
1950#ifdef USE_IF2IP 2006#ifdef USE_IF2IP
@@ -2478,7 +2534,8 @@ inline bool read_headers(Stream &strm, Headers &headers) {
2478} 2534}
2479 2535
2480inline bool read_content_with_length(Stream &strm, uint64_t len, 2536inline bool read_content_with_length(Stream &strm, uint64_t len,
2481 Progress progress, ContentReceiver out) { 2537 Progress progress,
2538 ContentReceiverWithProgress out) {
2482 char buf[CPPHTTPLIB_RECV_BUFSIZ]; 2539 char buf[CPPHTTPLIB_RECV_BUFSIZ];
2483 2540
2484 uint64_t r = 0; 2541 uint64_t r = 0;
@@ -2487,8 +2544,7 @@ inline bool read_content_with_length(Stream &strm, uint64_t len,
2487 auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ)); 2544 auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
2488 if (n <= 0) { return false; } 2545 if (n <= 0) { return false; }
2489 2546
2490 if (!out(buf, static_cast<size_t>(n))) { return false; } 2547 if (!out(buf, static_cast<size_t>(n), r, len)) { return false; }
2491
2492 r += static_cast<uint64_t>(n); 2548 r += static_cast<uint64_t>(n);
2493 2549
2494 if (progress) { 2550 if (progress) {
@@ -2510,8 +2566,10 @@ inline void skip_content_with_length(Stream &strm, uint64_t len) {
2510 } 2566 }
2511} 2567}
2512 2568
2513inline bool read_content_without_length(Stream &strm, ContentReceiver out) { 2569inline bool read_content_without_length(Stream &strm,
2570 ContentReceiverWithProgress out) {
2514 char buf[CPPHTTPLIB_RECV_BUFSIZ]; 2571 char buf[CPPHTTPLIB_RECV_BUFSIZ];
2572 uint64_t r = 0;
2515 for (;;) { 2573 for (;;) {
2516 auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ); 2574 auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);
2517 if (n < 0) { 2575 if (n < 0) {
@@ -2519,13 +2577,16 @@ inline bool read_content_without_length(Stream &strm, ContentReceiver out) {
2519 } else if (n == 0) { 2577 } else if (n == 0) {
2520 return true; 2578 return true;
2521 } 2579 }
2522 if (!out(buf, static_cast<size_t>(n))) { return false; } 2580
2581 if (!out(buf, static_cast<size_t>(n), r, 0)) { return false; }
2582 r += static_cast<uint64_t>(n);
2523 } 2583 }
2524 2584
2525 return true; 2585 return true;
2526} 2586}
2527 2587
2528inline bool read_content_chunked(Stream &strm, ContentReceiver out) { 2588inline bool read_content_chunked(Stream &strm,
2589 ContentReceiverWithProgress out) {
2529 const auto bufsiz = 16; 2590 const auto bufsiz = 16;
2530 char buf[bufsiz]; 2591 char buf[bufsiz];
2531 2592
@@ -2570,23 +2631,24 @@ inline bool is_chunked_transfer_encoding(const Headers &headers) {
2570} 2631}
2571 2632
2572template <typename T, typename U> 2633template <typename T, typename U>
2573bool prepare_content_receiver(T &x, int &status, ContentReceiver receiver, 2634bool prepare_content_receiver(T &x, int &status,
2635 ContentReceiverWithProgress receiver,
2574 bool decompress, U callback) { 2636 bool decompress, U callback) {
2575 if (decompress) { 2637 if (decompress) {
2576 std::string encoding = x.get_header_value("Content-Encoding"); 2638 std::string encoding = x.get_header_value("Content-Encoding");
2577 std::shared_ptr<decompressor> decompressor; 2639 std::unique_ptr<decompressor> decompressor;
2578 2640
2579 if (encoding.find("gzip") != std::string::npos || 2641 if (encoding.find("gzip") != std::string::npos ||
2580 encoding.find("deflate") != std::string::npos) { 2642 encoding.find("deflate") != std::string::npos) {
2581#ifdef CPPHTTPLIB_ZLIB_SUPPORT 2643#ifdef CPPHTTPLIB_ZLIB_SUPPORT
2582 decompressor = std::make_shared<gzip_decompressor>(); 2644 decompressor = detail::make_unique<gzip_decompressor>();
2583#else 2645#else
2584 status = 415; 2646 status = 415;
2585 return false; 2647 return false;
2586#endif 2648#endif
2587 } else if (encoding.find("br") != std::string::npos) { 2649 } else if (encoding.find("br") != std::string::npos) {
2588#ifdef CPPHTTPLIB_BROTLI_SUPPORT 2650#ifdef CPPHTTPLIB_BROTLI_SUPPORT
2589 decompressor = std::make_shared<brotli_decompressor>(); 2651 decompressor = detail::make_unique<brotli_decompressor>();
2590#else 2652#else
2591 status = 415; 2653 status = 415;
2592 return false; 2654 return false;
@@ -2595,12 +2657,14 @@ bool prepare_content_receiver(T &x, int &status, ContentReceiver receiver,
2595 2657
2596 if (decompressor) { 2658 if (decompressor) {
2597 if (decompressor->is_valid()) { 2659 if (decompressor->is_valid()) {
2598 ContentReceiver out = [&](const char *buf, size_t n) { 2660 ContentReceiverWithProgress out = [&](const char *buf, size_t n,
2599 return decompressor->decompress( 2661 uint64_t off, uint64_t len) {
2600 buf, n, 2662 return decompressor->decompress(buf, n,
2601 [&](const char *buf, size_t n) { return receiver(buf, n); }); 2663 [&](const char *buf, size_t n) {
2664 return receiver(buf, n, off, len);
2665 });
2602 }; 2666 };
2603 return callback(out); 2667 return callback(std::move(out));
2604 } else { 2668 } else {
2605 status = 500; 2669 status = 500;
2606 return false; 2670 return false;
@@ -2608,18 +2672,20 @@ bool prepare_content_receiver(T &x, int &status, ContentReceiver receiver,
2608 } 2672 }
2609 } 2673 }
2610 2674
2611 ContentReceiver out = [&](const char *buf, size_t n) { 2675 ContentReceiverWithProgress out = [&](const char *buf, size_t n, uint64_t off,
2612 return receiver(buf, n); 2676 uint64_t len) {
2677 return receiver(buf, n, off, len);
2613 }; 2678 };
2614 return callback(out); 2679 return callback(std::move(out));
2615} 2680}
2616 2681
2617template <typename T> 2682template <typename T>
2618bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status, 2683bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
2619 Progress progress, ContentReceiver receiver, 2684 Progress progress, ContentReceiverWithProgress receiver,
2620 bool decompress) { 2685 bool decompress) {
2621 return prepare_content_receiver( 2686 return prepare_content_receiver(
2622 x, status, receiver, decompress, [&](const ContentReceiver &out) { 2687 x, status, std::move(receiver), decompress,
2688 [&](const ContentReceiverWithProgress &out) {
2623 auto ret = true; 2689 auto ret = true;
2624 auto exceed_payload_max_length = false; 2690 auto exceed_payload_max_length = false;
2625 2691
@@ -2634,7 +2700,7 @@ bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
2634 skip_content_with_length(strm, len); 2700 skip_content_with_length(strm, len);
2635 ret = false; 2701 ret = false;
2636 } else if (len > 0) { 2702 } else if (len > 0) {
2637 ret = read_content_with_length(strm, len, progress, out); 2703 ret = read_content_with_length(strm, len, std::move(progress), out);
2638 } 2704 }
2639 } 2705 }
2640 2706
@@ -2875,7 +2941,7 @@ inline bool parse_multipart_boundary(const std::string &content_type,
2875 return !boundary.empty(); 2941 return !boundary.empty();
2876} 2942}
2877 2943
2878inline bool parse_range_header(const std::string &s, Ranges &ranges) { 2944inline bool parse_range_header(const std::string &s, Ranges &ranges) try {
2879 static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))"); 2945 static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))");
2880 std::smatch m; 2946 std::smatch m;
2881 if (std::regex_match(s, m, re_first_range)) { 2947 if (std::regex_match(s, m, re_first_range)) {
@@ -2907,7 +2973,7 @@ inline bool parse_range_header(const std::string &s, Ranges &ranges) {
2907 return all_valid_ranges; 2973 return all_valid_ranges;
2908 } 2974 }
2909 return false; 2975 return false;
2910} 2976} catch (...) { return false; }
2911 2977
2912class MultipartFormDataParser { 2978class MultipartFormDataParser {
2913public: 2979public:
@@ -2918,7 +2984,8 @@ public:
2918 bool is_valid() const { return is_valid_; } 2984 bool is_valid() const { return is_valid_; }
2919 2985
2920 template <typename T, typename U> 2986 template <typename T, typename U>
2921 bool parse(const char *buf, size_t n, T content_callback, U header_callback) { 2987 bool parse(const char *buf, size_t n, const T &content_callback,
2988 const U &header_callback) {
2922 2989
2923 static const std::regex re_content_disposition( 2990 static const std::regex re_content_disposition(
2924 "^Content-Disposition:\\s*form-data;\\s*name=\"(.*?)\"(?:;\\s*filename=" 2991 "^Content-Disposition:\\s*form-data;\\s*name=\"(.*?)\"(?:;\\s*filename="
@@ -3090,8 +3157,13 @@ inline std::string make_multipart_data_boundary() {
3090 static const char data[] = 3157 static const char data[] =
3091 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 3158 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
3092 3159
3160 // std::random_device might actually be deterministic on some
3161 // platforms, but due to lack of support in the c++ standard library,
3162 // doing better requires either some ugly hacks or breaking portability.
3093 std::random_device seed_gen; 3163 std::random_device seed_gen;
3094 std::mt19937 engine(seed_gen()); 3164 // Request 128 bits of entropy for initialization
3165 std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()};
3166 std::mt19937 engine(seed_sequence);
3095 3167
3096 std::string result = "--cpp-httplib-multipart-data-"; 3168 std::string result = "--cpp-httplib-multipart-data-";
3097 3169
@@ -3114,7 +3186,7 @@ get_range_offset_and_length(const Request &req, size_t content_length,
3114 auto slen = static_cast<ssize_t>(content_length); 3186 auto slen = static_cast<ssize_t>(content_length);
3115 3187
3116 if (r.first == -1) { 3188 if (r.first == -1) {
3117 r.first = slen - r.second; 3189 r.first = (std::max)(static_cast<ssize_t>(0), slen - r.second);
3118 r.second = slen - 1; 3190 r.second = slen - 1;
3119 } 3191 }
3120 3192
@@ -3451,7 +3523,7 @@ inline std::pair<std::string, std::string> make_range_header(Ranges ranges) {
3451 if (r.second != -1) { field += std::to_string(r.second); } 3523 if (r.second != -1) { field += std::to_string(r.second); }
3452 i++; 3524 i++;
3453 } 3525 }
3454 return std::make_pair("Range", field); 3526 return std::make_pair("Range", std::move(field));
3455} 3527}
3456 3528
3457inline std::pair<std::string, std::string> 3529inline std::pair<std::string, std::string>
@@ -3460,7 +3532,7 @@ make_basic_authentication_header(const std::string &username,
3460 bool is_proxy = false) { 3532 bool is_proxy = false) {
3461 auto field = "Basic " + detail::base64_encode(username + ":" + password); 3533 auto field = "Basic " + detail::base64_encode(username + ":" + password);
3462 auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; 3534 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
3463 return std::make_pair(key, field); 3535 return std::make_pair(key, std::move(field));
3464} 3536}
3465 3537
3466inline std::pair<std::string, std::string> 3538inline std::pair<std::string, std::string>
@@ -3468,7 +3540,7 @@ make_bearer_token_authentication_header(const std::string &token,
3468 bool is_proxy = false) { 3540 bool is_proxy = false) {
3469 auto field = "Bearer " + token; 3541 auto field = "Bearer " + token;
3470 auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; 3542 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
3471 return std::make_pair(key, field); 3543 return std::make_pair(key, std::move(field));
3472} 3544}
3473 3545
3474// Request implementation 3546// Request implementation
@@ -3761,60 +3833,66 @@ inline Server::Server()
3761inline Server::~Server() {} 3833inline Server::~Server() {}
3762 3834
3763inline Server &Server::Get(const char *pattern, Handler handler) { 3835inline Server &Server::Get(const char *pattern, Handler handler) {
3764 get_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); 3836 get_handlers_.push_back(
3837 std::make_pair(std::regex(pattern), std::move(handler)));
3765 return *this; 3838 return *this;
3766} 3839}
3767 3840
3768inline Server &Server::Post(const char *pattern, Handler handler) { 3841inline Server &Server::Post(const char *pattern, Handler handler) {
3769 post_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); 3842 post_handlers_.push_back(
3843 std::make_pair(std::regex(pattern), std::move(handler)));
3770 return *this; 3844 return *this;
3771} 3845}
3772 3846
3773inline Server &Server::Post(const char *pattern, 3847inline Server &Server::Post(const char *pattern,
3774 HandlerWithContentReader handler) { 3848 HandlerWithContentReader handler) {
3775 post_handlers_for_content_reader_.push_back( 3849 post_handlers_for_content_reader_.push_back(
3776 std::make_pair(std::regex(pattern), handler)); 3850 std::make_pair(std::regex(pattern), std::move(handler)));
3777 return *this; 3851 return *this;
3778} 3852}
3779 3853
3780inline Server &Server::Put(const char *pattern, Handler handler) { 3854inline Server &Server::Put(const char *pattern, Handler handler) {
3781 put_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); 3855 put_handlers_.push_back(
3856 std::make_pair(std::regex(pattern), std::move(handler)));
3782 return *this; 3857 return *this;
3783} 3858}
3784 3859
3785inline Server &Server::Put(const char *pattern, 3860inline Server &Server::Put(const char *pattern,
3786 HandlerWithContentReader handler) { 3861 HandlerWithContentReader handler) {
3787 put_handlers_for_content_reader_.push_back( 3862 put_handlers_for_content_reader_.push_back(
3788 std::make_pair(std::regex(pattern), handler)); 3863 std::make_pair(std::regex(pattern), std::move(handler)));
3789 return *this; 3864 return *this;
3790} 3865}
3791 3866
3792inline Server &Server::Patch(const char *pattern, Handler handler) { 3867inline Server &Server::Patch(const char *pattern, Handler handler) {
3793 patch_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); 3868 patch_handlers_.push_back(
3869 std::make_pair(std::regex(pattern), std::move(handler)));
3794 return *this; 3870 return *this;
3795} 3871}
3796 3872
3797inline Server &Server::Patch(const char *pattern, 3873inline Server &Server::Patch(const char *pattern,
3798 HandlerWithContentReader handler) { 3874 HandlerWithContentReader handler) {
3799 patch_handlers_for_content_reader_.push_back( 3875 patch_handlers_for_content_reader_.push_back(
3800 std::make_pair(std::regex(pattern), handler)); 3876 std::make_pair(std::regex(pattern), std::move(handler)));
3801 return *this; 3877 return *this;
3802} 3878}
3803 3879
3804inline Server &Server::Delete(const char *pattern, Handler handler) { 3880inline Server &Server::Delete(const char *pattern, Handler handler) {
3805 delete_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); 3881 delete_handlers_.push_back(
3882 std::make_pair(std::regex(pattern), std::move(handler)));
3806 return *this; 3883 return *this;
3807} 3884}
3808 3885
3809inline Server &Server::Delete(const char *pattern, 3886inline Server &Server::Delete(const char *pattern,
3810 HandlerWithContentReader handler) { 3887 HandlerWithContentReader handler) {
3811 delete_handlers_for_content_reader_.push_back( 3888 delete_handlers_for_content_reader_.push_back(
3812 std::make_pair(std::regex(pattern), handler)); 3889 std::make_pair(std::regex(pattern), std::move(handler)));
3813 return *this; 3890 return *this;
3814} 3891}
3815 3892
3816inline Server &Server::Options(const char *pattern, Handler handler) { 3893inline Server &Server::Options(const char *pattern, Handler handler) {
3817 options_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); 3894 options_handlers_.push_back(
3895 std::make_pair(std::regex(pattern), std::move(handler)));
3818 return *this; 3896 return *this;
3819} 3897}
3820 3898
@@ -3860,7 +3938,7 @@ inline void Server::set_error_handler(Handler handler) {
3860inline void Server::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; } 3938inline void Server::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
3861 3939
3862inline void Server::set_socket_options(SocketOptions socket_options) { 3940inline void Server::set_socket_options(SocketOptions socket_options) {
3863 socket_options_ = socket_options; 3941 socket_options_ = std::move(socket_options);
3864} 3942}
3865 3943
3866inline void Server::set_logger(Logger logger) { logger_ = std::move(logger); } 3944inline void Server::set_logger(Logger logger) { logger_ = std::move(logger); }
@@ -4045,16 +4123,16 @@ inline bool Server::write_response(Stream &strm, bool close_connection,
4045 } 4123 }
4046 4124
4047 if (type != detail::EncodingType::None) { 4125 if (type != detail::EncodingType::None) {
4048 std::shared_ptr<detail::compressor> compressor; 4126 std::unique_ptr<detail::compressor> compressor;
4049 4127
4050 if (type == detail::EncodingType::Gzip) { 4128 if (type == detail::EncodingType::Gzip) {
4051#ifdef CPPHTTPLIB_ZLIB_SUPPORT 4129#ifdef CPPHTTPLIB_ZLIB_SUPPORT
4052 compressor = std::make_shared<detail::gzip_compressor>(); 4130 compressor = detail::make_unique<detail::gzip_compressor>();
4053 res.set_header("Content-Encoding", "gzip"); 4131 res.set_header("Content-Encoding", "gzip");
4054#endif 4132#endif
4055 } else if (type == detail::EncodingType::Brotli) { 4133 } else if (type == detail::EncodingType::Brotli) {
4056#ifdef CPPHTTPLIB_BROTLI_SUPPORT 4134#ifdef CPPHTTPLIB_BROTLI_SUPPORT
4057 compressor = std::make_shared<detail::brotli_compressor>(); 4135 compressor = detail::make_unique<detail::brotli_compressor>();
4058 res.set_header("Content-Encoding", "brotli"); 4136 res.set_header("Content-Encoding", "brotli");
4059#endif 4137#endif
4060 } 4138 }
@@ -4136,17 +4214,17 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
4136 if (res.is_chunked_content_provider) { 4214 if (res.is_chunked_content_provider) {
4137 auto type = detail::encoding_type(req, res); 4215 auto type = detail::encoding_type(req, res);
4138 4216
4139 std::shared_ptr<detail::compressor> compressor; 4217 std::unique_ptr<detail::compressor> compressor;
4140 if (type == detail::EncodingType::Gzip) { 4218 if (type == detail::EncodingType::Gzip) {
4141#ifdef CPPHTTPLIB_ZLIB_SUPPORT 4219#ifdef CPPHTTPLIB_ZLIB_SUPPORT
4142 compressor = std::make_shared<detail::gzip_compressor>(); 4220 compressor = detail::make_unique<detail::gzip_compressor>();
4143#endif 4221#endif
4144 } else if (type == detail::EncodingType::Brotli) { 4222 } else if (type == detail::EncodingType::Brotli) {
4145#ifdef CPPHTTPLIB_BROTLI_SUPPORT 4223#ifdef CPPHTTPLIB_BROTLI_SUPPORT
4146 compressor = std::make_shared<detail::brotli_compressor>(); 4224 compressor = detail::make_unique<detail::brotli_compressor>();
4147#endif 4225#endif
4148 } else { 4226 } else {
4149 compressor = std::make_shared<detail::nocompressor>(); 4227 compressor = detail::make_unique<detail::nocompressor>();
4150 } 4228 }
4151 assert(compressor != nullptr); 4229 assert(compressor != nullptr);
4152 4230
@@ -4198,8 +4276,9 @@ inline bool Server::read_content_with_content_receiver(
4198 Stream &strm, Request &req, Response &res, ContentReceiver receiver, 4276 Stream &strm, Request &req, Response &res, ContentReceiver receiver,
4199 MultipartContentHeader multipart_header, 4277 MultipartContentHeader multipart_header,
4200 ContentReceiver multipart_receiver) { 4278 ContentReceiver multipart_receiver) {
4201 return read_content_core(strm, req, res, receiver, multipart_header, 4279 return read_content_core(strm, req, res, std::move(receiver),
4202 multipart_receiver); 4280 std::move(multipart_header),
4281 std::move(multipart_receiver));
4203} 4282}
4204 4283
4205inline bool Server::read_content_core(Stream &strm, Request &req, Response &res, 4284inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
@@ -4207,7 +4286,7 @@ inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
4207 MultipartContentHeader mulitpart_header, 4286 MultipartContentHeader mulitpart_header,
4208 ContentReceiver multipart_receiver) { 4287 ContentReceiver multipart_receiver) {
4209 detail::MultipartFormDataParser multipart_form_data_parser; 4288 detail::MultipartFormDataParser multipart_form_data_parser;
4210 ContentReceiver out; 4289 ContentReceiverWithProgress out;
4211 4290
4212 if (req.is_multipart_form_data()) { 4291 if (req.is_multipart_form_data()) {
4213 const auto &content_type = req.get_header_value("Content-Type"); 4292 const auto &content_type = req.get_header_value("Content-Type");
@@ -4218,7 +4297,7 @@ inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
4218 } 4297 }
4219 4298
4220 multipart_form_data_parser.set_boundary(std::move(boundary)); 4299 multipart_form_data_parser.set_boundary(std::move(boundary));
4221 out = [&](const char *buf, size_t n) { 4300 out = [&](const char *buf, size_t n, uint64_t /*off*/, uint64_t /*len*/) {
4222 /* For debug 4301 /* For debug
4223 size_t pos = 0; 4302 size_t pos = 0;
4224 while (pos < n) { 4303 while (pos < n) {
@@ -4234,7 +4313,8 @@ inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
4234 mulitpart_header); 4313 mulitpart_header);
4235 }; 4314 };
4236 } else { 4315 } else {
4237 out = receiver; 4316 out = [receiver](const char *buf, size_t n, uint64_t /*off*/,
4317 uint64_t /*len*/) { return receiver(buf, n); };
4238 } 4318 }
4239 4319
4240 if (req.method == "DELETE" && !req.has_header("Content-Length")) { 4320 if (req.method == "DELETE" && !req.has_header("Content-Length")) {
@@ -4271,7 +4351,7 @@ inline bool Server::handle_file_request(Request &req, Response &res,
4271 auto type = 4351 auto type =
4272 detail::find_content_type(path, file_extension_and_mimetype_map_); 4352 detail::find_content_type(path, file_extension_and_mimetype_map_);
4273 if (type) { res.set_header("Content-Type", type); } 4353 if (type) { res.set_header("Content-Type", type); }
4274 for (const auto& kv : entry.headers) { 4354 for (const auto &kv : entry.headers) {
4275 res.set_header(kv.first.c_str(), kv.second); 4355 res.set_header(kv.first.c_str(), kv.second);
4276 } 4356 }
4277 res.status = 200; 4357 res.status = 200;
@@ -4290,7 +4370,7 @@ inline socket_t
4290Server::create_server_socket(const char *host, int port, int socket_flags, 4370Server::create_server_socket(const char *host, int port, int socket_flags,
4291 SocketOptions socket_options) const { 4371 SocketOptions socket_options) const {
4292 return detail::create_socket( 4372 return detail::create_socket(
4293 host, port, socket_flags, tcp_nodelay_, socket_options, 4373 host, port, socket_flags, tcp_nodelay_, std::move(socket_options),
4294 [](socket_t sock, struct addrinfo &ai) -> bool { 4374 [](socket_t sock, struct addrinfo &ai) -> bool {
4295 if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) { 4375 if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
4296 return false; 4376 return false;
@@ -4392,32 +4472,37 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm) {
4392 { 4472 {
4393 ContentReader reader( 4473 ContentReader reader(
4394 [&](ContentReceiver receiver) { 4474 [&](ContentReceiver receiver) {
4395 return read_content_with_content_receiver(strm, req, res, receiver, 4475 return read_content_with_content_receiver(
4396 nullptr, nullptr); 4476 strm, req, res, std::move(receiver), nullptr, nullptr);
4397 }, 4477 },
4398 [&](MultipartContentHeader header, ContentReceiver receiver) { 4478 [&](MultipartContentHeader header, ContentReceiver receiver) {
4399 return read_content_with_content_receiver(strm, req, res, nullptr, 4479 return read_content_with_content_receiver(strm, req, res, nullptr,
4400 header, receiver); 4480 std::move(header),
4481 std::move(receiver));
4401 }); 4482 });
4402 4483
4403 if (req.method == "POST") { 4484 if (req.method == "POST") {
4404 if (dispatch_request_for_content_reader( 4485 if (dispatch_request_for_content_reader(
4405 req, res, reader, post_handlers_for_content_reader_)) { 4486 req, res, std::move(reader),
4487 post_handlers_for_content_reader_)) {
4406 return true; 4488 return true;
4407 } 4489 }
4408 } else if (req.method == "PUT") { 4490 } else if (req.method == "PUT") {
4409 if (dispatch_request_for_content_reader( 4491 if (dispatch_request_for_content_reader(
4410 req, res, reader, put_handlers_for_content_reader_)) { 4492 req, res, std::move(reader),
4493 put_handlers_for_content_reader_)) {
4411 return true; 4494 return true;
4412 } 4495 }
4413 } else if (req.method == "PATCH") { 4496 } else if (req.method == "PATCH") {
4414 if (dispatch_request_for_content_reader( 4497 if (dispatch_request_for_content_reader(
4415 req, res, reader, patch_handlers_for_content_reader_)) { 4498 req, res, std::move(reader),
4499 patch_handlers_for_content_reader_)) {
4416 return true; 4500 return true;
4417 } 4501 }
4418 } else if (req.method == "DELETE") { 4502 } else if (req.method == "DELETE") {
4419 if (dispatch_request_for_content_reader( 4503 if (dispatch_request_for_content_reader(
4420 req, res, reader, delete_handlers_for_content_reader_)) { 4504 req, res, std::move(reader),
4505 delete_handlers_for_content_reader_)) {
4421 return true; 4506 return true;
4422 } 4507 }
4423 } 4508 }
@@ -4448,7 +4533,6 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm) {
4448 4533
4449inline bool Server::dispatch_request(Request &req, Response &res, 4534inline bool Server::dispatch_request(Request &req, Response &res,
4450 const Handlers &handlers) { 4535 const Handlers &handlers) {
4451
4452 try { 4536 try {
4453 for (const auto &x : handlers) { 4537 for (const auto &x : handlers) {
4454 const auto &pattern = x.first; 4538 const auto &pattern = x.first;
@@ -4531,7 +4615,8 @@ Server::process_request(Stream &strm, bool close_connection,
4531 if (req.has_header("Range")) { 4615 if (req.has_header("Range")) {
4532 const auto &range_header_value = req.get_header_value("Range"); 4616 const auto &range_header_value = req.get_header_value("Range");
4533 if (!detail::parse_range_header(range_header_value, req.ranges)) { 4617 if (!detail::parse_range_header(range_header_value, req.ranges)) {
4534 // TODO: error 4618 res.status = 416;
4619 return write_response(strm, close_connection, req, res);
4535 } 4620 }
4536 } 4621 }
4537 4622
@@ -4588,11 +4673,11 @@ inline ClientImpl::ClientImpl(const std::string &host, int port)
4588inline ClientImpl::ClientImpl(const std::string &host, int port, 4673inline ClientImpl::ClientImpl(const std::string &host, int port,
4589 const std::string &client_cert_path, 4674 const std::string &client_cert_path,
4590 const std::string &client_key_path) 4675 const std::string &client_key_path)
4591 : host_(host), port_(port), 4676 : error_(Error::Success), host_(host), port_(port),
4592 host_and_port_(host_ + ":" + std::to_string(port_)), 4677 host_and_port_(host_ + ":" + std::to_string(port_)),
4593 client_cert_path_(client_cert_path), client_key_path_(client_key_path) {} 4678 client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
4594 4679
4595inline ClientImpl::~ClientImpl() { stop_core(); } 4680inline ClientImpl::~ClientImpl() { lock_socket_and_shutdown_and_close(); }
4596 4681
4597inline bool ClientImpl::is_valid() const { return true; } 4682inline bool ClientImpl::is_valid() const { return true; }
4598 4683
@@ -4653,15 +4738,47 @@ inline bool ClientImpl::create_and_connect_socket(Socket &socket) {
4653 return true; 4738 return true;
4654} 4739}
4655 4740
4656inline void ClientImpl::close_socket(Socket &socket, 4741inline void ClientImpl::shutdown_ssl(Socket &socket, bool shutdown_gracefully) {
4657 bool /*process_socket_ret*/) { 4742 (void)socket;
4658 detail::close_socket(socket.sock); 4743 (void)shutdown_gracefully;
4659 socket_.sock = INVALID_SOCKET; 4744 //If there are any requests in flight from threads other than us, then it's
4745 //a thread-unsafe race because individual ssl* objects are not thread-safe.
4746 assert(socket_requests_in_flight_ == 0 ||
4747 socket_requests_are_from_thread_ == std::this_thread::get_id());
4748}
4749
4750inline void ClientImpl::shutdown_socket(Socket &socket) {
4751 if (socket.sock == INVALID_SOCKET)
4752 return;
4753 detail::shutdown_socket(socket.sock);
4754}
4755
4756inline void ClientImpl::close_socket(Socket &socket) {
4757 // If there are requests in flight in another thread, usually closing
4758 // the socket will be fine and they will simply receive an error when
4759 // using the closed socket, but it is still a bug since rarely the OS
4760 // may reassign the socket id to be used for a new socket, and then
4761 // suddenly they will be operating on a live socket that is different
4762 // than the one they intended!
4763 assert(socket_requests_in_flight_ == 0 ||
4764 socket_requests_are_from_thread_ == std::this_thread::get_id());
4765 // It is also a bug if this happens while SSL is still active
4660#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 4766#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4661 socket_.ssl = nullptr; 4767 assert(socket.ssl == nullptr);
4662#endif 4768#endif
4769 if (socket.sock == INVALID_SOCKET)
4770 return;
4771 detail::close_socket(socket.sock);
4772 socket.sock = INVALID_SOCKET;
4663} 4773}
4664 4774
4775inline void ClientImpl::lock_socket_and_shutdown_and_close() {
4776 std::lock_guard<std::mutex> guard(socket_mutex_);
4777 shutdown_ssl(socket_, true);
4778 shutdown_socket(socket_);
4779 close_socket(socket_);
4780}
4781
4665inline bool ClientImpl::read_response_line(Stream &strm, Response &res) { 4782inline bool ClientImpl::read_response_line(Stream &strm, Response &res) {
4666 std::array<char, 2048> buf; 4783 std::array<char, 2048> buf;
4667 4784
@@ -4696,11 +4813,23 @@ inline bool ClientImpl::send(const Request &req, Response &res) {
4696 4813
4697 { 4814 {
4698 std::lock_guard<std::mutex> guard(socket_mutex_); 4815 std::lock_guard<std::mutex> guard(socket_mutex_);
4816 // Set this to false immediately - if it ever gets set to true by the end of the
4817 // request, we know another thread instructed us to close the socket.
4818 socket_should_be_closed_when_request_is_done_ = false;
4699 4819
4700 auto is_alive = false; 4820 auto is_alive = false;
4701 if (socket_.is_open()) { 4821 if (socket_.is_open()) {
4702 is_alive = detail::select_write(socket_.sock, 0, 0) > 0; 4822 is_alive = detail::select_write(socket_.sock, 0, 0) > 0;
4703 if (!is_alive) { close_socket(socket_, false); } 4823 if (!is_alive) {
4824 // Attempt to avoid sigpipe by shutting down nongracefully if it seems like
4825 // the other side has already closed the connection
4826 // Also, there cannot be any requests in flight from other threads since we locked
4827 // request_mutex_, so safe to close everything immediately
4828 const bool shutdown_gracefully = false;
4829 shutdown_ssl(socket_, shutdown_gracefully);
4830 shutdown_socket(socket_);
4831 close_socket(socket_);
4832 }
4704 } 4833 }
4705 4834
4706 if (!is_alive) { 4835 if (!is_alive) {
@@ -4721,15 +4850,38 @@ inline bool ClientImpl::send(const Request &req, Response &res) {
4721 } 4850 }
4722#endif 4851#endif
4723 } 4852 }
4853
4854 // Mark the current socket as being in use so that it cannot be closed by anyone
4855 // else while this request is ongoing, even though we will be releasing the mutex.
4856 if (socket_requests_in_flight_ > 1) {
4857 assert(socket_requests_are_from_thread_ == std::this_thread::get_id());
4858 }
4859 socket_requests_in_flight_ += 1;
4860 socket_requests_are_from_thread_ = std::this_thread::get_id();
4724 } 4861 }
4725 4862
4726 auto close_connection = !keep_alive_; 4863 auto close_connection = !keep_alive_;
4727
4728 auto ret = process_socket(socket_, [&](Stream &strm) { 4864 auto ret = process_socket(socket_, [&](Stream &strm) {
4729 return handle_request(strm, req, res, close_connection); 4865 return handle_request(strm, req, res, close_connection);
4730 }); 4866 });
4731 4867
4732 if (close_connection || !ret) { stop_core(); } 4868 //Briefly lock mutex in order to mark that a request is no longer ongoing
4869 {
4870 std::lock_guard<std::mutex> guard(socket_mutex_);
4871 socket_requests_in_flight_ -= 1;
4872 if (socket_requests_in_flight_ <= 0) {
4873 assert(socket_requests_in_flight_ == 0);
4874 socket_requests_are_from_thread_ = std::thread::id();
4875 }
4876
4877 if (socket_should_be_closed_when_request_is_done_ ||
4878 close_connection ||
4879 !ret ) {
4880 shutdown_ssl(socket_, true);
4881 shutdown_socket(socket_);
4882 close_socket(socket_);
4883 }
4884 }
4733 4885
4734 if (!ret) { 4886 if (!ret) {
4735 if (error_ == Error::Success) { error_ = Error::Unknown; } 4887 if (error_ == Error::Success) { error_ = Error::Unknown; }
@@ -4973,7 +5125,7 @@ inline bool ClientImpl::write_request(Stream &strm, const Request &req,
4973 return true; 5125 return true;
4974} 5126}
4975 5127
4976inline std::shared_ptr<Response> ClientImpl::send_with_content_provider( 5128inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
4977 const char *method, const char *path, const Headers &headers, 5129 const char *method, const char *path, const Headers &headers,
4978 const std::string &body, size_t content_length, 5130 const std::string &body, size_t content_length,
4979 ContentProvider content_provider, const char *content_type) { 5131 ContentProvider content_provider, const char *content_type) {
@@ -5036,15 +5188,15 @@ inline std::shared_ptr<Response> ClientImpl::send_with_content_provider(
5036 { 5188 {
5037 if (content_provider) { 5189 if (content_provider) {
5038 req.content_length = content_length; 5190 req.content_length = content_length;
5039 req.content_provider = content_provider; 5191 req.content_provider = std::move(content_provider);
5040 } else { 5192 } else {
5041 req.body = body; 5193 req.body = body;
5042 } 5194 }
5043 } 5195 }
5044 5196
5045 auto res = std::make_shared<Response>(); 5197 auto res = detail::make_unique<Response>();
5046 5198
5047 return send(req, *res) ? res : nullptr; 5199 return send(req, *res) ? std::move(res) : nullptr;
5048} 5200}
5049 5201
5050inline bool ClientImpl::process_request(Stream &strm, const Request &req, 5202inline bool ClientImpl::process_request(Stream &strm, const Request &req,
@@ -5070,16 +5222,21 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
5070 if (req.method != "HEAD" && req.method != "CONNECT") { 5222 if (req.method != "HEAD" && req.method != "CONNECT") {
5071 auto out = 5223 auto out =
5072 req.content_receiver 5224 req.content_receiver
5073 ? static_cast<ContentReceiver>([&](const char *buf, size_t n) { 5225 ? static_cast<ContentReceiverWithProgress>(
5074 auto ret = req.content_receiver(buf, n); 5226 [&](const char *buf, size_t n, uint64_t off, uint64_t len) {
5075 if (!ret) { error_ = Error::Canceled; } 5227 auto ret = req.content_receiver(buf, n, off, len);
5076 return ret; 5228 if (!ret) { error_ = Error::Canceled; }
5077 }) 5229 return ret;
5078 : static_cast<ContentReceiver>([&](const char *buf, size_t n) { 5230 })
5079 if (res.body.size() + n > res.body.max_size()) { return false; } 5231 : static_cast<ContentReceiverWithProgress>(
5080 res.body.append(buf, n); 5232 [&](const char *buf, size_t n, uint64_t /*off*/,
5081 return true; 5233 uint64_t /*len*/) {
5082 }); 5234 if (res.body.size() + n > res.body.max_size()) {
5235 return false;
5236 }
5237 res.body.append(buf, n);
5238 return true;
5239 });
5083 5240
5084 auto progress = [&](uint64_t current, uint64_t total) { 5241 auto progress = [&](uint64_t current, uint64_t total) {
5085 if (!req.progress) { return true; } 5242 if (!req.progress) { return true; }
@@ -5090,7 +5247,8 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
5090 5247
5091 int dummy_status; 5248 int dummy_status;
5092 if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(), 5249 if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(),
5093 dummy_status, progress, out, decompress_)) { 5250 dummy_status, std::move(progress), std::move(out),
5251 decompress_)) {
5094 if (error_ != Error::Canceled) { error_ = Error::Read; } 5252 if (error_ != Error::Canceled) { error_ = Error::Read; }
5095 return false; 5253 return false;
5096 } 5254 }
@@ -5098,7 +5256,16 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
5098 5256
5099 if (res.get_header_value("Connection") == "close" || 5257 if (res.get_header_value("Connection") == "close" ||
5100 (res.version == "HTTP/1.0" && res.reason != "Connection established")) { 5258 (res.version == "HTTP/1.0" && res.reason != "Connection established")) {
5101 stop_core(); 5259 // TODO this requires a not-entirely-obvious chain of calls to be correct
5260 // for this to be safe. Maybe a code refactor (such as moving this out to
5261 // the send function and getting rid of the recursiveness of the mutex)
5262 // could make this more obvious.
5263
5264 // This is safe to call because process_request is only called by handle_request
5265 // which is only called by send, which locks the request mutex during the process.
5266 // It would be a bug to call it from a different thread since it's a thread-safety
5267 // issue to do these things to the socket if another thread is using the socket.
5268 lock_socket_and_shutdown_and_close();
5102 } 5269 }
5103 5270
5104 // Log 5271 // Log
@@ -5108,11 +5275,11 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
5108} 5275}
5109 5276
5110inline bool 5277inline bool
5111ClientImpl::process_socket(Socket &socket, 5278ClientImpl::process_socket(const Socket &socket,
5112 std::function<bool(Stream &strm)> callback) { 5279 std::function<bool(Stream &strm)> callback) {
5113 return detail::process_client_socket(socket.sock, read_timeout_sec_, 5280 return detail::process_client_socket(
5114 read_timeout_usec_, write_timeout_sec_, 5281 socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
5115 write_timeout_usec_, callback); 5282 write_timeout_usec_, std::move(callback));
5116} 5283}
5117 5284
5118inline bool ClientImpl::is_ssl() const { return false; } 5285inline bool ClientImpl::is_ssl() const { return false; }
@@ -5138,9 +5305,9 @@ inline Result ClientImpl::Get(const char *path, const Headers &headers,
5138 req.headers.insert(headers.begin(), headers.end()); 5305 req.headers.insert(headers.begin(), headers.end());
5139 req.progress = std::move(progress); 5306 req.progress = std::move(progress);
5140 5307
5141 auto res = std::make_shared<Response>(); 5308 auto res = detail::make_unique<Response>();
5142 auto ret = send(req, *res); 5309 auto ret = send(req, *res);
5143 return Result{ret ? res : nullptr, get_last_error()}; 5310 return Result{ret ? std::move(res) : nullptr, get_last_error()};
5144} 5311}
5145 5312
5146inline Result ClientImpl::Get(const char *path, 5313inline Result ClientImpl::Get(const char *path,
@@ -5170,23 +5337,23 @@ inline Result ClientImpl::Get(const char *path, const Headers &headers,
5170inline Result ClientImpl::Get(const char *path, 5337inline Result ClientImpl::Get(const char *path,
5171 ResponseHandler response_handler, 5338 ResponseHandler response_handler,
5172 ContentReceiver content_receiver) { 5339 ContentReceiver content_receiver) {
5173 return Get(path, Headers(), std::move(response_handler), content_receiver, 5340 return Get(path, Headers(), std::move(response_handler),
5174 nullptr); 5341 std::move(content_receiver), nullptr);
5175} 5342}
5176 5343
5177inline Result ClientImpl::Get(const char *path, const Headers &headers, 5344inline Result ClientImpl::Get(const char *path, const Headers &headers,
5178 ResponseHandler response_handler, 5345 ResponseHandler response_handler,
5179 ContentReceiver content_receiver) { 5346 ContentReceiver content_receiver) {
5180 return Get(path, headers, std::move(response_handler), content_receiver, 5347 return Get(path, headers, std::move(response_handler),
5181 nullptr); 5348 std::move(content_receiver), nullptr);
5182} 5349}
5183 5350
5184inline Result ClientImpl::Get(const char *path, 5351inline Result ClientImpl::Get(const char *path,
5185 ResponseHandler response_handler, 5352 ResponseHandler response_handler,
5186 ContentReceiver content_receiver, 5353 ContentReceiver content_receiver,
5187 Progress progress) { 5354 Progress progress) {
5188 return Get(path, Headers(), std::move(response_handler), content_receiver, 5355 return Get(path, Headers(), std::move(response_handler),
5189 progress); 5356 std::move(content_receiver), std::move(progress));
5190} 5357}
5191 5358
5192inline Result ClientImpl::Get(const char *path, const Headers &headers, 5359inline Result ClientImpl::Get(const char *path, const Headers &headers,
@@ -5199,12 +5366,16 @@ inline Result ClientImpl::Get(const char *path, const Headers &headers,
5199 req.headers = default_headers_; 5366 req.headers = default_headers_;
5200 req.headers.insert(headers.begin(), headers.end()); 5367 req.headers.insert(headers.begin(), headers.end());
5201 req.response_handler = std::move(response_handler); 5368 req.response_handler = std::move(response_handler);
5202 req.content_receiver = std::move(content_receiver); 5369 req.content_receiver =
5370 [content_receiver](const char *data, size_t data_length,
5371 uint64_t /*offset*/, uint64_t /*total_length*/) {
5372 return content_receiver(data, data_length);
5373 };
5203 req.progress = std::move(progress); 5374 req.progress = std::move(progress);
5204 5375
5205 auto res = std::make_shared<Response>(); 5376 auto res = detail::make_unique<Response>();
5206 auto ret = send(req, *res); 5377 auto ret = send(req, *res);
5207 return Result{ret ? res : nullptr, get_last_error()}; 5378 return Result{ret ? std::move(res) : nullptr, get_last_error()};
5208} 5379}
5209 5380
5210inline Result ClientImpl::Head(const char *path) { 5381inline Result ClientImpl::Head(const char *path) {
@@ -5218,9 +5389,9 @@ inline Result ClientImpl::Head(const char *path, const Headers &headers) {
5218 req.headers.insert(headers.begin(), headers.end()); 5389 req.headers.insert(headers.begin(), headers.end());
5219 req.path = path; 5390 req.path = path;
5220 5391
5221 auto res = std::make_shared<Response>(); 5392 auto res = detail::make_unique<Response>();
5222 auto ret = send(req, *res); 5393 auto ret = send(req, *res);
5223 return Result{ret ? res : nullptr, get_last_error()}; 5394 return Result{ret ? std::move(res) : nullptr, get_last_error()};
5224} 5395}
5225 5396
5226inline Result ClientImpl::Post(const char *path) { 5397inline Result ClientImpl::Post(const char *path) {
@@ -5237,7 +5408,7 @@ inline Result ClientImpl::Post(const char *path, const Headers &headers,
5237 const char *content_type) { 5408 const char *content_type) {
5238 auto ret = send_with_content_provider("POST", path, headers, body, 0, nullptr, 5409 auto ret = send_with_content_provider("POST", path, headers, body, 0, nullptr,
5239 content_type); 5410 content_type);
5240 return Result{ret, get_last_error()}; 5411 return Result{std::move(ret), get_last_error()};
5241} 5412}
5242 5413
5243inline Result ClientImpl::Post(const char *path, const Params &params) { 5414inline Result ClientImpl::Post(const char *path, const Params &params) {
@@ -5247,17 +5418,18 @@ inline Result ClientImpl::Post(const char *path, const Params &params) {
5247inline Result ClientImpl::Post(const char *path, size_t content_length, 5418inline Result ClientImpl::Post(const char *path, size_t content_length,
5248 ContentProvider content_provider, 5419 ContentProvider content_provider,
5249 const char *content_type) { 5420 const char *content_type) {
5250 return Post(path, Headers(), content_length, content_provider, content_type); 5421 return Post(path, Headers(), content_length, std::move(content_provider),
5422 content_type);
5251} 5423}
5252 5424
5253inline Result ClientImpl::Post(const char *path, const Headers &headers, 5425inline Result ClientImpl::Post(const char *path, const Headers &headers,
5254 size_t content_length, 5426 size_t content_length,
5255 ContentProvider content_provider, 5427 ContentProvider content_provider,
5256 const char *content_type) { 5428 const char *content_type) {
5257 auto ret = send_with_content_provider("POST", path, headers, std::string(), 5429 auto ret = send_with_content_provider(
5258 content_length, content_provider, 5430 "POST", path, headers, std::string(), content_length,
5259 content_type); 5431 std::move(content_provider), content_type);
5260 return Result{ret, get_last_error()}; 5432 return Result{std::move(ret), get_last_error()};
5261} 5433}
5262 5434
5263inline Result ClientImpl::Post(const char *path, const Headers &headers, 5435inline Result ClientImpl::Post(const char *path, const Headers &headers,
@@ -5273,7 +5445,18 @@ inline Result ClientImpl::Post(const char *path,
5273 5445
5274inline Result ClientImpl::Post(const char *path, const Headers &headers, 5446inline Result ClientImpl::Post(const char *path, const Headers &headers,
5275 const MultipartFormDataItems &items) { 5447 const MultipartFormDataItems &items) {
5276 auto boundary = detail::make_multipart_data_boundary(); 5448 return Post(path, headers, items, detail::make_multipart_data_boundary());
5449}
5450inline Result ClientImpl::Post(const char *path, const Headers &headers,
5451 const MultipartFormDataItems &items,
5452 const std::string &boundary) {
5453 for (size_t i = 0; i < boundary.size(); i++) {
5454 char c = boundary[i];
5455 if (!std::isalnum(c) && c != '-' && c != '_') {
5456 error_ = Error::UnsupportedMultipartBoundaryChars;
5457 return Result{nullptr, error_};
5458 }
5459 }
5277 5460
5278 std::string body; 5461 std::string body;
5279 5462
@@ -5311,23 +5494,24 @@ inline Result ClientImpl::Put(const char *path, const Headers &headers,
5311 const char *content_type) { 5494 const char *content_type) {
5312 auto ret = send_with_content_provider("PUT", path, headers, body, 0, nullptr, 5495 auto ret = send_with_content_provider("PUT", path, headers, body, 0, nullptr,
5313 content_type); 5496 content_type);
5314 return Result{ret, get_last_error()}; 5497 return Result{std::move(ret), get_last_error()};
5315} 5498}
5316 5499
5317inline Result ClientImpl::Put(const char *path, size_t content_length, 5500inline Result ClientImpl::Put(const char *path, size_t content_length,
5318 ContentProvider content_provider, 5501 ContentProvider content_provider,
5319 const char *content_type) { 5502 const char *content_type) {
5320 return Put(path, Headers(), content_length, content_provider, content_type); 5503 return Put(path, Headers(), content_length, std::move(content_provider),
5504 content_type);
5321} 5505}
5322 5506
5323inline Result ClientImpl::Put(const char *path, const Headers &headers, 5507inline Result ClientImpl::Put(const char *path, const Headers &headers,
5324 size_t content_length, 5508 size_t content_length,
5325 ContentProvider content_provider, 5509 ContentProvider content_provider,
5326 const char *content_type) { 5510 const char *content_type) {
5327 auto ret = send_with_content_provider("PUT", path, headers, std::string(), 5511 auto ret = send_with_content_provider(
5328 content_length, content_provider, 5512 "PUT", path, headers, std::string(), content_length,
5329 content_type); 5513 std::move(content_provider), content_type);
5330 return Result{ret, get_last_error()}; 5514 return Result{std::move(ret), get_last_error()};
5331} 5515}
5332 5516
5333inline Result ClientImpl::Put(const char *path, const Params &params) { 5517inline Result ClientImpl::Put(const char *path, const Params &params) {
@@ -5350,23 +5534,24 @@ inline Result ClientImpl::Patch(const char *path, const Headers &headers,
5350 const char *content_type) { 5534 const char *content_type) {
5351 auto ret = send_with_content_provider("PATCH", path, headers, body, 0, 5535 auto ret = send_with_content_provider("PATCH", path, headers, body, 0,
5352 nullptr, content_type); 5536 nullptr, content_type);
5353 return Result{ret, get_last_error()}; 5537 return Result{std::move(ret), get_last_error()};
5354} 5538}
5355 5539
5356inline Result ClientImpl::Patch(const char *path, size_t content_length, 5540inline Result ClientImpl::Patch(const char *path, size_t content_length,
5357 ContentProvider content_provider, 5541 ContentProvider content_provider,
5358 const char *content_type) { 5542 const char *content_type) {
5359 return Patch(path, Headers(), content_length, content_provider, content_type); 5543 return Patch(path, Headers(), content_length, std::move(content_provider),
5544 content_type);
5360} 5545}
5361 5546
5362inline Result ClientImpl::Patch(const char *path, const Headers &headers, 5547inline Result ClientImpl::Patch(const char *path, const Headers &headers,
5363 size_t content_length, 5548 size_t content_length,
5364 ContentProvider content_provider, 5549 ContentProvider content_provider,
5365 const char *content_type) { 5550 const char *content_type) {
5366 auto ret = send_with_content_provider("PATCH", path, headers, std::string(), 5551 auto ret = send_with_content_provider(
5367 content_length, content_provider, 5552 "PATCH", path, headers, std::string(), content_length,
5368 content_type); 5553 std::move(content_provider), content_type);
5369 return Result{ret, get_last_error()}; 5554 return Result{std::move(ret), get_last_error()};
5370} 5555}
5371 5556
5372inline Result ClientImpl::Delete(const char *path) { 5557inline Result ClientImpl::Delete(const char *path) {
@@ -5394,9 +5579,9 @@ inline Result ClientImpl::Delete(const char *path, const Headers &headers,
5394 if (content_type) { req.headers.emplace("Content-Type", content_type); } 5579 if (content_type) { req.headers.emplace("Content-Type", content_type); }
5395 req.body = body; 5580 req.body = body;
5396 5581
5397 auto res = std::make_shared<Response>(); 5582 auto res = detail::make_unique<Response>();
5398 auto ret = send(req, *res); 5583 auto ret = send(req, *res);
5399 return Result{ret ? res : nullptr, get_last_error()}; 5584 return Result{ret ? std::move(res) : nullptr, get_last_error()};
5400} 5585}
5401 5586
5402inline Result ClientImpl::Options(const char *path) { 5587inline Result ClientImpl::Options(const char *path) {
@@ -5410,9 +5595,9 @@ inline Result ClientImpl::Options(const char *path, const Headers &headers) {
5410 req.headers.insert(headers.begin(), headers.end()); 5595 req.headers.insert(headers.begin(), headers.end());
5411 req.path = path; 5596 req.path = path;
5412 5597
5413 auto res = std::make_shared<Response>(); 5598 auto res = detail::make_unique<Response>();
5414 auto ret = send(req, *res); 5599 auto ret = send(req, *res);
5415 return Result{ret ? res : nullptr, get_last_error()}; 5600 return Result{ret ? std::move(res) : nullptr, get_last_error()};
5416} 5601}
5417 5602
5418inline size_t ClientImpl::is_socket_open() const { 5603inline size_t ClientImpl::is_socket_open() const {
@@ -5421,18 +5606,27 @@ inline size_t ClientImpl::is_socket_open() const {
5421} 5606}
5422 5607
5423inline void ClientImpl::stop() { 5608inline void ClientImpl::stop() {
5424 stop_core();
5425 error_ = Error::Canceled;
5426}
5427
5428inline void ClientImpl::stop_core() {
5429 std::lock_guard<std::mutex> guard(socket_mutex_); 5609 std::lock_guard<std::mutex> guard(socket_mutex_);
5430 if (socket_.is_open()) { 5610 // There is no guarantee that this doesn't get overwritten later, but set it so that
5431 detail::shutdown_socket(socket_.sock); 5611 // there is a good chance that any threads stopping as a result pick up this error.
5432 std::this_thread::sleep_for(std::chrono::milliseconds(1)); 5612 error_ = Error::Canceled;
5433 close_socket(socket_, true); 5613
5434 std::this_thread::sleep_for(std::chrono::milliseconds(1)); 5614 // If there is anything ongoing right now, the ONLY thread-safe thing we can do
5615 // is to shutdown_socket, so that threads using this socket suddenly discover
5616 // they can't read/write any more and error out.
5617 // Everything else (closing the socket, shutting ssl down) is unsafe because these
5618 // actions are not thread-safe.
5619 if (socket_requests_in_flight_ > 0) {
5620 shutdown_socket(socket_);
5621 // Aside from that, we set a flag for the socket to be closed when we're done.
5622 socket_should_be_closed_when_request_is_done_ = true;
5623 return;
5435 } 5624 }
5625
5626 //Otherwise, sitll holding the mutex, we can shut everything down ourselves
5627 shutdown_ssl(socket_, true);
5628 shutdown_socket(socket_);
5629 close_socket(socket_);
5436} 5630}
5437 5631
5438inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) { 5632inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) {
@@ -5479,7 +5673,7 @@ inline void ClientImpl::set_default_headers(Headers headers) {
5479inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; } 5673inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
5480 5674
5481inline void ClientImpl::set_socket_options(SocketOptions socket_options) { 5675inline void ClientImpl::set_socket_options(SocketOptions socket_options) {
5482 socket_options_ = socket_options; 5676 socket_options_ = std::move(socket_options);
5483} 5677}
5484 5678
5485inline void ClientImpl::set_compress(bool on) { compress_ = on; } 5679inline void ClientImpl::set_compress(bool on) { compress_ = on; }
@@ -5554,9 +5748,12 @@ inline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex,
5554} 5748}
5555 5749
5556inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, 5750inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl,
5557 bool process_socket_ret) { 5751 bool shutdown_gracefully) {
5558 if (process_socket_ret) { 5752 // sometimes we may want to skip this to try to avoid SIGPIPE if we know
5559 SSL_shutdown(ssl); // shutdown only if not already closed by remote 5753 // the remote has closed the network connection
5754 // Note that it is not always possible to avoid SIGPIPE, this is merely a best-efforts.
5755 if (shutdown_gracefully) {
5756 SSL_shutdown(ssl);
5560 } 5757 }
5561 5758
5562 std::lock_guard<std::mutex> guard(ctx_mutex); 5759 std::lock_guard<std::mutex> guard(ctx_mutex);
@@ -5650,22 +5847,7 @@ inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl,
5650 read_timeout_usec_(read_timeout_usec), 5847 read_timeout_usec_(read_timeout_usec),
5651 write_timeout_sec_(write_timeout_sec), 5848 write_timeout_sec_(write_timeout_sec),
5652 write_timeout_usec_(write_timeout_usec) { 5849 write_timeout_usec_(write_timeout_usec) {
5653 { 5850 SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
5654 timeval tv;
5655 tv.tv_sec = static_cast<long>(read_timeout_sec);
5656 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(read_timeout_usec);
5657
5658 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<char *>(&tv),
5659 sizeof(tv));
5660 }
5661 {
5662 timeval tv;
5663 tv.tv_sec = static_cast<long>(write_timeout_sec);
5664 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(write_timeout_usec);
5665
5666 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<char *>(&tv),
5667 sizeof(tv));
5668 }
5669} 5851}
5670 5852
5671inline SSLSocketStream::~SSLSocketStream() {} 5853inline SSLSocketStream::~SSLSocketStream() {}
@@ -5680,8 +5862,27 @@ inline bool SSLSocketStream::is_writable() const {
5680} 5862}
5681 5863
5682inline ssize_t SSLSocketStream::read(char *ptr, size_t size) { 5864inline ssize_t SSLSocketStream::read(char *ptr, size_t size) {
5683 if (SSL_pending(ssl_) > 0 || is_readable()) { 5865 if (SSL_pending(ssl_) > 0) {
5684 return SSL_read(ssl_, ptr, static_cast<int>(size)); 5866 return SSL_read(ssl_, ptr, static_cast<int>(size));
5867 } else if (is_readable()) {
5868 auto ret = SSL_read(ssl_, ptr, static_cast<int>(size));
5869 if (ret < 0) {
5870 auto err = SSL_get_error(ssl_, ret);
5871 while (err == SSL_ERROR_WANT_READ) {
5872 if (SSL_pending(ssl_) > 0) {
5873 return SSL_read(ssl_, ptr, static_cast<int>(size));
5874 } else if (is_readable()) {
5875 ret = SSL_read(ssl_, ptr, static_cast<int>(size));
5876 if (ret >= 0) {
5877 return ret;
5878 }
5879 err = SSL_get_error(ssl_, ret);
5880 } else {
5881 return -1;
5882 }
5883 }
5884 }
5885 return ret;
5685 } 5886 }
5686 return -1; 5887 return -1;
5687} 5888}
@@ -5788,9 +5989,12 @@ inline bool SSLServer::process_and_close_socket(socket_t sock) {
5788 }); 5989 });
5789 5990
5790 detail::ssl_delete(ctx_mutex_, ssl, ret); 5991 detail::ssl_delete(ctx_mutex_, ssl, ret);
5992 detail::shutdown_socket(sock);
5993 detail::close_socket(sock);
5791 return ret; 5994 return ret;
5792 } 5995 }
5793 5996
5997 detail::shutdown_socket(sock);
5794 detail::close_socket(sock); 5998 detail::close_socket(sock);
5795 return false; 5999 return false;
5796} 6000}
@@ -5843,6 +6047,10 @@ inline SSLClient::SSLClient(const std::string &host, int port,
5843 6047
5844inline SSLClient::~SSLClient() { 6048inline SSLClient::~SSLClient() {
5845 if (ctx_) { SSL_CTX_free(ctx_); } 6049 if (ctx_) { SSL_CTX_free(ctx_); }
6050 // Make sure to shut down SSL since shutdown_ssl will resolve to the
6051 // base function rather than the derived function once we get to the
6052 // base class destructor, and won't free the SSL (causing a leak).
6053 SSLClient::shutdown_ssl(socket_, true);
5846} 6054}
5847 6055
5848inline bool SSLClient::is_valid() const { return ctx_; } 6056inline bool SSLClient::is_valid() const { return ctx_; }
@@ -5876,11 +6084,11 @@ inline bool SSLClient::create_and_connect_socket(Socket &socket) {
5876 return is_valid() && ClientImpl::create_and_connect_socket(socket); 6084 return is_valid() && ClientImpl::create_and_connect_socket(socket);
5877} 6085}
5878 6086
6087// Assumes that socket_mutex_ is locked and that there are no requests in flight
5879inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res, 6088inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,
5880 bool &success) { 6089 bool &success) {
5881 success = true; 6090 success = true;
5882 Response res2; 6091 Response res2;
5883
5884 if (!detail::process_client_socket( 6092 if (!detail::process_client_socket(
5885 socket.sock, read_timeout_sec_, read_timeout_usec_, 6093 socket.sock, read_timeout_sec_, read_timeout_usec_,
5886 write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) { 6094 write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) {
@@ -5889,7 +6097,10 @@ inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,
5889 req2.path = host_and_port_; 6097 req2.path = host_and_port_;
5890 return process_request(strm, req2, res2, false); 6098 return process_request(strm, req2, res2, false);
5891 })) { 6099 })) {
5892 close_socket(socket, true); 6100 // Thread-safe to close everything because we are assuming there are no requests in flight
6101 shutdown_ssl(socket, true);
6102 shutdown_socket(socket);
6103 close_socket(socket);
5893 success = false; 6104 success = false;
5894 return false; 6105 return false;
5895 } 6106 }
@@ -5912,7 +6123,10 @@ inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,
5912 true)); 6123 true));
5913 return process_request(strm, req3, res3, false); 6124 return process_request(strm, req3, res3, false);
5914 })) { 6125 })) {
5915 close_socket(socket, true); 6126 // Thread-safe to close everything because we are assuming there are no requests in flight
6127 shutdown_ssl(socket, true);
6128 shutdown_socket(socket);
6129 close_socket(socket);
5916 success = false; 6130 success = false;
5917 return false; 6131 return false;
5918 } 6132 }
@@ -6005,26 +6219,30 @@ inline bool SSLClient::initialize_ssl(Socket &socket) {
6005 return true; 6219 return true;
6006 } 6220 }
6007 6221
6008 close_socket(socket, false); 6222 shutdown_socket(socket);
6223 close_socket(socket);
6009 return false; 6224 return false;
6010} 6225}
6011 6226
6012inline void SSLClient::close_socket(Socket &socket, bool process_socket_ret) { 6227inline void SSLClient::shutdown_ssl(Socket &socket, bool shutdown_gracefully) {
6013 detail::close_socket(socket.sock); 6228 if (socket.sock == INVALID_SOCKET) {
6014 socket_.sock = INVALID_SOCKET; 6229 assert(socket.ssl == nullptr);
6230 return;
6231 }
6015 if (socket.ssl) { 6232 if (socket.ssl) {
6016 detail::ssl_delete(ctx_mutex_, socket.ssl, process_socket_ret); 6233 detail::ssl_delete(ctx_mutex_, socket.ssl, shutdown_gracefully);
6017 socket_.ssl = nullptr; 6234 socket.ssl = nullptr;
6018 } 6235 }
6236 assert(socket.ssl == nullptr);
6019} 6237}
6020 6238
6021inline bool 6239inline bool
6022SSLClient::process_socket(Socket &socket, 6240SSLClient::process_socket(const Socket &socket,
6023 std::function<bool(Stream &strm)> callback) { 6241 std::function<bool(Stream &strm)> callback) {
6024 assert(socket.ssl); 6242 assert(socket.ssl);
6025 return detail::process_client_socket_ssl( 6243 return detail::process_client_socket_ssl(
6026 socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_, 6244 socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_,
6027 write_timeout_sec_, write_timeout_usec_, callback); 6245 write_timeout_sec_, write_timeout_usec_, std::move(callback));
6028} 6246}
6029 6247
6030inline bool SSLClient::is_ssl() const { return true; } 6248inline bool SSLClient::is_ssl() const { return true; }
@@ -6175,6 +6393,8 @@ inline Client::Client(const char *scheme_host_port,
6175#else 6393#else
6176 if (!scheme.empty() && scheme != "http") { 6394 if (!scheme.empty() && scheme != "http") {
6177#endif 6395#endif
6396 std::string msg = "'" + scheme + "' scheme is not supported.";
6397 throw std::invalid_argument(msg);
6178 return; 6398 return;
6179 } 6399 }
6180 6400
@@ -6187,28 +6407,28 @@ inline Client::Client(const char *scheme_host_port,
6187 6407
6188 if (is_ssl) { 6408 if (is_ssl) {
6189#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 6409#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
6190 cli_ = std::make_shared<SSLClient>(host.c_str(), port, client_cert_path, 6410 cli_ = detail::make_unique<SSLClient>(host.c_str(), port,
6191 client_key_path); 6411 client_cert_path, client_key_path);
6192 is_ssl_ = is_ssl; 6412 is_ssl_ = is_ssl;
6193#endif 6413#endif
6194 } else { 6414 } else {
6195 cli_ = std::make_shared<ClientImpl>(host.c_str(), port, client_cert_path, 6415 cli_ = detail::make_unique<ClientImpl>(host.c_str(), port,
6196 client_key_path); 6416 client_cert_path, client_key_path);
6197 } 6417 }
6198 } else { 6418 } else {
6199 cli_ = std::make_shared<ClientImpl>(scheme_host_port, 80, client_cert_path, 6419 cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80,
6200 client_key_path); 6420 client_cert_path, client_key_path);
6201 } 6421 }
6202} 6422}
6203 6423
6204inline Client::Client(const std::string &host, int port) 6424inline Client::Client(const std::string &host, int port)
6205 : cli_(std::make_shared<ClientImpl>(host, port)) {} 6425 : cli_(detail::make_unique<ClientImpl>(host, port)) {}
6206 6426
6207inline Client::Client(const std::string &host, int port, 6427inline Client::Client(const std::string &host, int port,
6208 const std::string &client_cert_path, 6428 const std::string &client_cert_path,
6209 const std::string &client_key_path) 6429 const std::string &client_key_path)
6210 : cli_(std::make_shared<ClientImpl>(host, port, client_cert_path, 6430 : cli_(detail::make_unique<ClientImpl>(host, port, client_cert_path,
6211 client_key_path)) {} 6431 client_key_path)) {}
6212 6432
6213inline Client::~Client() {} 6433inline Client::~Client() {}
6214 6434
@@ -6221,11 +6441,11 @@ inline Result Client::Get(const char *path, const Headers &headers) {
6221 return cli_->Get(path, headers); 6441 return cli_->Get(path, headers);
6222} 6442}
6223inline Result Client::Get(const char *path, Progress progress) { 6443inline Result Client::Get(const char *path, Progress progress) {
6224 return cli_->Get(path, progress); 6444 return cli_->Get(path, std::move(progress));
6225} 6445}
6226inline Result Client::Get(const char *path, const Headers &headers, 6446inline Result Client::Get(const char *path, const Headers &headers,
6227 Progress progress) { 6447 Progress progress) {
6228 return cli_->Get(path, headers, progress); 6448 return cli_->Get(path, headers, std::move(progress));
6229} 6449}
6230inline Result Client::Get(const char *path, ContentReceiver content_receiver) { 6450inline Result Client::Get(const char *path, ContentReceiver content_receiver) {
6231 return cli_->Get(path, std::move(content_receiver)); 6451 return cli_->Get(path, std::move(content_receiver));
@@ -6262,7 +6482,8 @@ inline Result Client::Get(const char *path, ResponseHandler response_handler,
6262inline Result Client::Get(const char *path, const Headers &headers, 6482inline Result Client::Get(const char *path, const Headers &headers,
6263 ResponseHandler response_handler, 6483 ResponseHandler response_handler,
6264 ContentReceiver content_receiver, Progress progress) { 6484 ContentReceiver content_receiver, Progress progress) {
6265 return cli_->Get(path, headers, response_handler, content_receiver, progress); 6485 return cli_->Get(path, headers, std::move(response_handler),
6486 std::move(content_receiver), std::move(progress));
6266} 6487}
6267 6488
6268inline Result Client::Head(const char *path) { return cli_->Head(path); } 6489inline Result Client::Head(const char *path) { return cli_->Head(path); }
@@ -6282,13 +6503,14 @@ inline Result Client::Post(const char *path, const Headers &headers,
6282inline Result Client::Post(const char *path, size_t content_length, 6503inline Result Client::Post(const char *path, size_t content_length,
6283 ContentProvider content_provider, 6504 ContentProvider content_provider,
6284 const char *content_type) { 6505 const char *content_type) {
6285 return cli_->Post(path, content_length, content_provider, content_type); 6506 return cli_->Post(path, content_length, std::move(content_provider),
6507 content_type);
6286} 6508}
6287inline Result Client::Post(const char *path, const Headers &headers, 6509inline Result Client::Post(const char *path, const Headers &headers,
6288 size_t content_length, 6510 size_t content_length,
6289 ContentProvider content_provider, 6511 ContentProvider content_provider,
6290 const char *content_type) { 6512 const char *content_type) {
6291 return cli_->Post(path, headers, content_length, content_provider, 6513 return cli_->Post(path, headers, content_length, std::move(content_provider),
6292 content_type); 6514 content_type);
6293} 6515}
6294inline Result Client::Post(const char *path, const Params &params) { 6516inline Result Client::Post(const char *path, const Params &params) {
@@ -6306,6 +6528,11 @@ inline Result Client::Post(const char *path, const Headers &headers,
6306 const MultipartFormDataItems &items) { 6528 const MultipartFormDataItems &items) {
6307 return cli_->Post(path, headers, items); 6529 return cli_->Post(path, headers, items);
6308} 6530}
6531inline Result Client::Post(const char *path, const Headers &headers,
6532 const MultipartFormDataItems &items,
6533 const std::string &boundary) {
6534 return cli_->Post(path, headers, items, boundary);
6535}
6309inline Result Client::Put(const char *path) { return cli_->Put(path); } 6536inline Result Client::Put(const char *path) { return cli_->Put(path); }
6310inline Result Client::Put(const char *path, const std::string &body, 6537inline Result Client::Put(const char *path, const std::string &body,
6311 const char *content_type) { 6538 const char *content_type) {
@@ -6318,13 +6545,14 @@ inline Result Client::Put(const char *path, const Headers &headers,
6318inline Result Client::Put(const char *path, size_t content_length, 6545inline Result Client::Put(const char *path, size_t content_length,
6319 ContentProvider content_provider, 6546 ContentProvider content_provider,
6320 const char *content_type) { 6547 const char *content_type) {
6321 return cli_->Put(path, content_length, content_provider, content_type); 6548 return cli_->Put(path, content_length, std::move(content_provider),
6549 content_type);
6322} 6550}
6323inline Result Client::Put(const char *path, const Headers &headers, 6551inline Result Client::Put(const char *path, const Headers &headers,
6324 size_t content_length, 6552 size_t content_length,
6325 ContentProvider content_provider, 6553 ContentProvider content_provider,
6326 const char *content_type) { 6554 const char *content_type) {
6327 return cli_->Put(path, headers, content_length, content_provider, 6555 return cli_->Put(path, headers, content_length, std::move(content_provider),
6328 content_type); 6556 content_type);
6329} 6557}
6330inline Result Client::Put(const char *path, const Params &params) { 6558inline Result Client::Put(const char *path, const Params &params) {
@@ -6345,13 +6573,14 @@ inline Result Client::Patch(const char *path, const Headers &headers,
6345inline Result Client::Patch(const char *path, size_t content_length, 6573inline Result Client::Patch(const char *path, size_t content_length,
6346 ContentProvider content_provider, 6574 ContentProvider content_provider,
6347 const char *content_type) { 6575 const char *content_type) {
6348 return cli_->Patch(path, content_length, content_provider, content_type); 6576 return cli_->Patch(path, content_length, std::move(content_provider),
6577 content_type);
6349} 6578}
6350inline Result Client::Patch(const char *path, const Headers &headers, 6579inline Result Client::Patch(const char *path, const Headers &headers,
6351 size_t content_length, 6580 size_t content_length,
6352 ContentProvider content_provider, 6581 ContentProvider content_provider,
6353 const char *content_type) { 6582 const char *content_type) {
6354 return cli_->Patch(path, headers, content_length, content_provider, 6583 return cli_->Patch(path, headers, content_length, std::move(content_provider),
6355 content_type); 6584 content_type);
6356} 6585}
6357inline Result Client::Delete(const char *path) { return cli_->Delete(path); } 6586inline Result Client::Delete(const char *path) { return cli_->Delete(path); }
@@ -6386,7 +6615,7 @@ inline void Client::set_default_headers(Headers headers) {
6386 6615
6387inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); } 6616inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); }
6388inline void Client::set_socket_options(SocketOptions socket_options) { 6617inline void Client::set_socket_options(SocketOptions socket_options) {
6389 cli_->set_socket_options(socket_options); 6618 cli_->set_socket_options(std::move(socket_options));
6390} 6619}
6391 6620
6392inline void Client::set_connection_timeout(time_t sec, time_t usec) { 6621inline void Client::set_connection_timeout(time_t sec, time_t usec) {
diff --git a/externals/inih/inih b/externals/inih/inih
Subproject 603729dec89aaca42d7bd08f08bc333165b7d5d Subproject 1e80a47dffbda813604f0913e2ad68c7054c14e
diff --git a/externals/libressl b/externals/libressl
Subproject 7d01cb01cb1a926ecb4c9c98b107ef3c26f59df Subproject 8289d0d07de6553bf4b900bf60e808ea3f7f59d
diff --git a/externals/lurlparser/CMakeLists.txt b/externals/lurlparser/CMakeLists.txt
deleted file mode 100644
index 45046ffd3..000000000
--- a/externals/lurlparser/CMakeLists.txt
+++ /dev/null
@@ -1,8 +0,0 @@
1add_library(lurlparser
2 LUrlParser.cpp
3 LUrlParser.h
4)
5
6create_target_directory_groups(lurlparser)
7
8target_include_directories(lurlparser INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/externals/lurlparser/LUrlParser.cpp b/externals/lurlparser/LUrlParser.cpp
deleted file mode 100644
index 9c134e330..000000000
--- a/externals/lurlparser/LUrlParser.cpp
+++ /dev/null
@@ -1,265 +0,0 @@
1/*
2 * Lightweight URL & URI parser (RFC 1738, RFC 3986)
3 * https://github.com/corporateshark/LUrlParser
4 *
5 * The MIT License (MIT)
6 *
7 * Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com)
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a copy
10 * of this software and associated documentation files (the "Software"), to deal
11 * in the Software without restriction, including without limitation the rights
12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in all
17 * copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * SOFTWARE.
26 */
27
28#include "LUrlParser.h"
29
30#include <algorithm>
31#include <cstring>
32#include <stdlib.h>
33
34// check if the scheme name is valid
35static bool IsSchemeValid( const std::string& SchemeName )
36{
37 for ( auto c : SchemeName )
38 {
39 if ( !isalpha( c ) && c != '+' && c != '-' && c != '.' ) return false;
40 }
41
42 return true;
43}
44
45bool LUrlParser::clParseURL::GetPort( int* OutPort ) const
46{
47 if ( !IsValid() ) { return false; }
48
49 int Port = atoi( m_Port.c_str() );
50
51 if ( Port <= 0 || Port > 65535 ) { return false; }
52
53 if ( OutPort ) { *OutPort = Port; }
54
55 return true;
56}
57
58// based on RFC 1738 and RFC 3986
59LUrlParser::clParseURL LUrlParser::clParseURL::ParseURL( const std::string& URL )
60{
61 LUrlParser::clParseURL Result;
62
63 const char* CurrentString = URL.c_str();
64
65 /*
66 * <scheme>:<scheme-specific-part>
67 * <scheme> := [a-z\+\-\.]+
68 * For resiliency, programs interpreting URLs should treat upper case letters as equivalent to lower case in scheme names
69 */
70
71 // try to read scheme
72 {
73 const char* LocalString = strchr( CurrentString, ':' );
74
75 if ( !LocalString )
76 {
77 return clParseURL( LUrlParserError_NoUrlCharacter );
78 }
79
80 // save the scheme name
81 Result.m_Scheme = std::string( CurrentString, LocalString - CurrentString );
82
83 if ( !IsSchemeValid( Result.m_Scheme ) )
84 {
85 return clParseURL( LUrlParserError_InvalidSchemeName );
86 }
87
88 // scheme should be lowercase
89 std::transform( Result.m_Scheme.begin(), Result.m_Scheme.end(), Result.m_Scheme.begin(), ::tolower );
90
91 // skip ':'
92 CurrentString = LocalString+1;
93 }
94
95 /*
96 * //<user>:<password>@<host>:<port>/<url-path>
97 * any ":", "@" and "/" must be normalized
98 */
99
100 // skip "//"
101 if ( *CurrentString++ != '/' ) return clParseURL( LUrlParserError_NoDoubleSlash );
102 if ( *CurrentString++ != '/' ) return clParseURL( LUrlParserError_NoDoubleSlash );
103
104 // check if the user name and password are specified
105 bool bHasUserName = false;
106
107 const char* LocalString = CurrentString;
108
109 while ( *LocalString )
110 {
111 if ( *LocalString == '@' )
112 {
113 // user name and password are specified
114 bHasUserName = true;
115 break;
116 }
117 else if ( *LocalString == '/' )
118 {
119 // end of <host>:<port> specification
120 bHasUserName = false;
121 break;
122 }
123
124 LocalString++;
125 }
126
127 // user name and password
128 LocalString = CurrentString;
129
130 if ( bHasUserName )
131 {
132 // read user name
133 while ( *LocalString && *LocalString != ':' && *LocalString != '@' ) LocalString++;
134
135 Result.m_UserName = std::string( CurrentString, LocalString - CurrentString );
136
137 // proceed with the current pointer
138 CurrentString = LocalString;
139
140 if ( *CurrentString == ':' )
141 {
142 // skip ':'
143 CurrentString++;
144
145 // read password
146 LocalString = CurrentString;
147
148 while ( *LocalString && *LocalString != '@' ) LocalString++;
149
150 Result.m_Password = std::string( CurrentString, LocalString - CurrentString );
151
152 CurrentString = LocalString;
153 }
154
155 // skip '@'
156 if ( *CurrentString != '@' )
157 {
158 return clParseURL( LUrlParserError_NoAtSign );
159 }
160
161 CurrentString++;
162 }
163
164 bool bHasBracket = ( *CurrentString == '[' );
165
166 // go ahead, read the host name
167 LocalString = CurrentString;
168
169 while ( *LocalString )
170 {
171 if ( bHasBracket && *LocalString == ']' )
172 {
173 // end of IPv6 address
174 LocalString++;
175 break;
176 }
177 else if ( !bHasBracket && ( *LocalString == ':' || *LocalString == '/' ) )
178 {
179 // port number is specified
180 break;
181 }
182
183 LocalString++;
184 }
185
186 Result.m_Host = std::string( CurrentString, LocalString - CurrentString );
187
188 CurrentString = LocalString;
189
190 // is port number specified?
191 if ( *CurrentString == ':' )
192 {
193 CurrentString++;
194
195 // read port number
196 LocalString = CurrentString;
197
198 while ( *LocalString && *LocalString != '/' ) LocalString++;
199
200 Result.m_Port = std::string( CurrentString, LocalString - CurrentString );
201
202 CurrentString = LocalString;
203 }
204
205 // end of string
206 if ( !*CurrentString )
207 {
208 Result.m_ErrorCode = LUrlParserError_Ok;
209
210 return Result;
211 }
212
213 // skip '/'
214 if ( *CurrentString != '/' )
215 {
216 return clParseURL( LUrlParserError_NoSlash );
217 }
218
219 CurrentString++;
220
221 // parse the path
222 LocalString = CurrentString;
223
224 while ( *LocalString && *LocalString != '#' && *LocalString != '?' ) LocalString++;
225
226 Result.m_Path = std::string( CurrentString, LocalString - CurrentString );
227
228 CurrentString = LocalString;
229
230 // check for query
231 if ( *CurrentString == '?' )
232 {
233 // skip '?'
234 CurrentString++;
235
236 // read query
237 LocalString = CurrentString;
238
239 while ( *LocalString && *LocalString != '#' ) LocalString++;
240
241 Result.m_Query = std::string( CurrentString, LocalString - CurrentString );
242
243 CurrentString = LocalString;
244 }
245
246 // check for fragment
247 if ( *CurrentString == '#' )
248 {
249 // skip '#'
250 CurrentString++;
251
252 // read fragment
253 LocalString = CurrentString;
254
255 while ( *LocalString ) LocalString++;
256
257 Result.m_Fragment = std::string( CurrentString, LocalString - CurrentString );
258
259 CurrentString = LocalString;
260 }
261
262 Result.m_ErrorCode = LUrlParserError_Ok;
263
264 return Result;
265}
diff --git a/externals/lurlparser/LUrlParser.h b/externals/lurlparser/LUrlParser.h
deleted file mode 100644
index 25d210981..000000000
--- a/externals/lurlparser/LUrlParser.h
+++ /dev/null
@@ -1,78 +0,0 @@
1/*
2 * Lightweight URL & URI parser (RFC 1738, RFC 3986)
3 * https://github.com/corporateshark/LUrlParser
4 *
5 * The MIT License (MIT)
6 *
7 * Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com)
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a copy
10 * of this software and associated documentation files (the "Software"), to deal
11 * in the Software without restriction, including without limitation the rights
12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in all
17 * copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * SOFTWARE.
26 */
27
28#pragma once
29
30#include <string>
31
32namespace LUrlParser
33{
34enum LUrlParserError
35{
36 LUrlParserError_Ok = 0,
37 LUrlParserError_Uninitialized = 1,
38 LUrlParserError_NoUrlCharacter = 2,
39 LUrlParserError_InvalidSchemeName = 3,
40 LUrlParserError_NoDoubleSlash = 4,
41 LUrlParserError_NoAtSign = 5,
42 LUrlParserError_UnexpectedEndOfLine = 6,
43 LUrlParserError_NoSlash = 7,
44};
45
46class clParseURL
47{
48public:
49 LUrlParserError m_ErrorCode;
50 std::string m_Scheme;
51 std::string m_Host;
52 std::string m_Port;
53 std::string m_Path;
54 std::string m_Query;
55 std::string m_Fragment;
56 std::string m_UserName;
57 std::string m_Password;
58
59 clParseURL()
60 : m_ErrorCode( LUrlParserError_Uninitialized )
61 {}
62
63 /// return 'true' if the parsing was successful
64 bool IsValid() const { return m_ErrorCode == LUrlParserError_Ok; }
65
66 /// helper to convert the port number to int, return 'true' if the port is valid (within the 0..65535 range)
67 bool GetPort( int* OutPort ) const;
68
69 /// parse the URL
70 static clParseURL ParseURL( const std::string& URL );
71
72private:
73 explicit clParseURL( LUrlParserError ErrorCode )
74 : m_ErrorCode( ErrorCode )
75 {}
76};
77
78} // namespace LUrlParser
diff --git a/externals/lurlparser/README.md b/externals/lurlparser/README.md
deleted file mode 100644
index be7f0135a..000000000
--- a/externals/lurlparser/README.md
+++ /dev/null
@@ -1,19 +0,0 @@
1From https://github.com/corporateshark/LUrlParser/commit/455d5e2d27e3946f11ad0328fee9ee2628e6a8e2
2
3MIT License
4
5===
6
7Lightweight URL & URI parser (RFC 1738, RFC 3986)
8
9(C) Sergey Kosarevsky, 2015
10
11@corporateshark sk@linderdaum.com
12
13http://www.linderdaum.com
14
15http://blog.linderdaum.com
16
17=============================
18
19A tiny and lightweight URL & URI parser (RFC 1738, RFC 3986) written in C++.
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 0fb5d9708..e50ab2922 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -150,6 +150,8 @@ add_library(common STATIC
150 scope_exit.h 150 scope_exit.h
151 spin_lock.cpp 151 spin_lock.cpp
152 spin_lock.h 152 spin_lock.h
153 stream.cpp
154 stream.h
153 string_util.cpp 155 string_util.cpp
154 string_util.h 156 string_util.h
155 swap.h 157 swap.h
diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp
index 1c1d09ccb..e186ed880 100644
--- a/src/common/fiber.cpp
+++ b/src/common/fiber.cpp
@@ -91,7 +91,7 @@ void Fiber::Rewind() {
91 SwitchToFiber(impl->rewind_handle); 91 SwitchToFiber(impl->rewind_handle);
92} 92}
93 93
94void Fiber::YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to) { 94void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
95 ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); 95 ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
96 ASSERT_MSG(to != nullptr, "Next fiber is null!"); 96 ASSERT_MSG(to != nullptr, "Next fiber is null!");
97 to->guard.lock(); 97 to->guard.lock();
@@ -199,7 +199,7 @@ void Fiber::Rewind() {
199 boost::context::detail::jump_fcontext(impl->rewind_context, this); 199 boost::context::detail::jump_fcontext(impl->rewind_context, this);
200} 200}
201 201
202void Fiber::YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to) { 202void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
203 ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); 203 ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
204 ASSERT_MSG(to != nullptr, "Next fiber is null!"); 204 ASSERT_MSG(to != nullptr, "Next fiber is null!");
205 to->guard.lock(); 205 to->guard.lock();
diff --git a/src/common/fiber.h b/src/common/fiber.h
index 89dde5e36..cefd61df9 100644
--- a/src/common/fiber.h
+++ b/src/common/fiber.h
@@ -46,7 +46,7 @@ public:
46 46
47 /// Yields control from Fiber 'from' to Fiber 'to' 47 /// Yields control from Fiber 'from' to Fiber 'to'
48 /// Fiber 'from' must be the currently running fiber. 48 /// Fiber 'from' must be the currently running fiber.
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* start_parameter);
diff --git a/src/common/stream.cpp b/src/common/stream.cpp
new file mode 100644
index 000000000..bf0496c26
--- /dev/null
+++ b/src/common/stream.cpp
@@ -0,0 +1,47 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <stdexcept>
6#include "common/common_types.h"
7#include "common/stream.h"
8
9namespace Common {
10
11Stream::Stream() = default;
12Stream::~Stream() = default;
13
14void Stream::Seek(s32 offset, SeekOrigin origin) {
15 if (origin == SeekOrigin::SetOrigin) {
16 if (offset < 0) {
17 position = 0;
18 } else if (position >= buffer.size()) {
19 position = buffer.size();
20 } else {
21 position = offset;
22 }
23 } else if (origin == SeekOrigin::FromCurrentPos) {
24 Seek(static_cast<s32>(position) + offset, SeekOrigin::SetOrigin);
25 } else if (origin == SeekOrigin::FromEnd) {
26 Seek(static_cast<s32>(buffer.size()) - offset, SeekOrigin::SetOrigin);
27 }
28}
29
30u8 Stream::ReadByte() {
31 if (position < buffer.size()) {
32 return buffer[position++];
33 } else {
34 throw std::out_of_range("Attempting to read a byte not within the buffer range");
35 }
36}
37
38void Stream::WriteByte(u8 byte) {
39 if (position == buffer.size()) {
40 buffer.push_back(byte);
41 position++;
42 } else {
43 buffer.insert(buffer.begin() + position, byte);
44 }
45}
46
47} // namespace Common
diff --git a/src/common/stream.h b/src/common/stream.h
new file mode 100644
index 000000000..2585c16af
--- /dev/null
+++ b/src/common/stream.h
@@ -0,0 +1,50 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <vector>
8#include "common/common_types.h"
9
10namespace Common {
11
12enum class SeekOrigin {
13 SetOrigin,
14 FromCurrentPos,
15 FromEnd,
16};
17
18class Stream {
19public:
20 /// Stream creates a bitstream and provides common functionality on the stream.
21 explicit Stream();
22 ~Stream();
23
24 /// Reposition bitstream "cursor" to the specified offset from origin
25 void Seek(s32 offset, SeekOrigin origin);
26
27 /// Reads next byte in the stream buffer and increments position
28 u8 ReadByte();
29
30 /// Writes byte at current position
31 void WriteByte(u8 byte);
32
33 std::size_t GetPosition() const {
34 return position;
35 }
36
37 std::vector<u8>& GetBuffer() {
38 return buffer;
39 }
40
41 const std::vector<u8>& GetBuffer() const {
42 return buffer;
43 }
44
45private:
46 std::vector<u8> buffer;
47 std::size_t position{0};
48};
49
50} // namespace Common
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index db1c9fdef..e0f207f3e 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -439,6 +439,8 @@ add_library(core STATIC
439 hle/service/nvdrv/devices/nvhost_gpu.h 439 hle/service/nvdrv/devices/nvhost_gpu.h
440 hle/service/nvdrv/devices/nvhost_nvdec.cpp 440 hle/service/nvdrv/devices/nvhost_nvdec.cpp
441 hle/service/nvdrv/devices/nvhost_nvdec.h 441 hle/service/nvdrv/devices/nvhost_nvdec.h
442 hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
443 hle/service/nvdrv/devices/nvhost_nvdec_common.h
442 hle/service/nvdrv/devices/nvhost_nvjpg.cpp 444 hle/service/nvdrv/devices/nvhost_nvjpg.cpp
443 hle/service/nvdrv/devices/nvhost_nvjpg.h 445 hle/service/nvdrv/devices/nvhost_nvjpg.h
444 hle/service/nvdrv/devices/nvhost_vic.cpp 446 hle/service/nvdrv/devices/nvhost_vic.cpp
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 81e8cc338..fde2ccc09 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -40,6 +40,7 @@
40#include "core/hle/service/lm/manager.h" 40#include "core/hle/service/lm/manager.h"
41#include "core/hle/service/service.h" 41#include "core/hle/service/service.h"
42#include "core/hle/service/sm/sm.h" 42#include "core/hle/service/sm/sm.h"
43#include "core/hle/service/time/time_manager.h"
43#include "core/loader/loader.h" 44#include "core/loader/loader.h"
44#include "core/memory.h" 45#include "core/memory.h"
45#include "core/memory/cheat_engine.h" 46#include "core/memory/cheat_engine.h"
@@ -121,7 +122,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
121struct System::Impl { 122struct System::Impl {
122 explicit Impl(System& system) 123 explicit Impl(System& system)
123 : kernel{system}, fs_controller{system}, memory{system}, 124 : kernel{system}, fs_controller{system}, memory{system},
124 cpu_manager{system}, reporter{system}, applet_manager{system} {} 125 cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {}
125 126
126 ResultStatus Run() { 127 ResultStatus Run() {
127 status = ResultStatus::Success; 128 status = ResultStatus::Success;
@@ -189,6 +190,9 @@ struct System::Impl {
189 return ResultStatus::ErrorVideoCore; 190 return ResultStatus::ErrorVideoCore;
190 } 191 }
191 192
193 // Initialize time manager, which must happen after kernel is created
194 time_manager.Initialize();
195
192 is_powered_on = true; 196 is_powered_on = true;
193 exit_lock = false; 197 exit_lock = false;
194 198
@@ -387,6 +391,7 @@ struct System::Impl {
387 /// Service State 391 /// Service State
388 Service::Glue::ARPManager arp_manager; 392 Service::Glue::ARPManager arp_manager;
389 Service::LM::Manager lm_manager{reporter}; 393 Service::LM::Manager lm_manager{reporter};
394 Service::Time::TimeManager time_manager;
390 395
391 /// Service manager 396 /// Service manager
392 std::shared_ptr<Service::SM::ServiceManager> service_manager; 397 std::shared_ptr<Service::SM::ServiceManager> service_manager;
@@ -717,6 +722,14 @@ const Service::LM::Manager& System::GetLogManager() const {
717 return impl->lm_manager; 722 return impl->lm_manager;
718} 723}
719 724
725Service::Time::TimeManager& System::GetTimeManager() {
726 return impl->time_manager;
727}
728
729const Service::Time::TimeManager& System::GetTimeManager() const {
730 return impl->time_manager;
731}
732
720void System::SetExitLock(bool locked) { 733void System::SetExitLock(bool locked) {
721 impl->exit_lock = locked; 734 impl->exit_lock = locked;
722} 735}
diff --git a/src/core/core.h b/src/core/core.h
index 27efe30bb..6db896bae 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -69,6 +69,10 @@ namespace SM {
69class ServiceManager; 69class ServiceManager;
70} // namespace SM 70} // namespace SM
71 71
72namespace Time {
73class TimeManager;
74} // namespace Time
75
72} // namespace Service 76} // namespace Service
73 77
74namespace Tegra { 78namespace Tegra {
@@ -361,6 +365,10 @@ public:
361 365
362 const Service::LM::Manager& GetLogManager() const; 366 const Service::LM::Manager& GetLogManager() const;
363 367
368 Service::Time::TimeManager& GetTimeManager();
369
370 const Service::Time::TimeManager& GetTimeManager() const;
371
364 void SetExitLock(bool locked); 372 void SetExitLock(bool locked);
365 373
366 bool GetExitLock() const; 374 bool GetExitLock() const;
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 688b99eba..983210197 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -365,6 +365,8 @@ void CpuManager::RunThread(std::size_t core) {
365 data.enter_barrier.reset(); 365 data.enter_barrier.reset();
366 data.exit_barrier.reset(); 366 data.exit_barrier.reset();
367 data.initialized = false; 367 data.initialized = false;
368
369 MicroProfileOnThreadExit();
368} 370}
369 371
370} // namespace Core 372} // namespace Core
diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp
index c5d65f2d0..5582091f4 100644
--- a/src/core/frontend/applets/controller.cpp
+++ b/src/core/frontend/applets/controller.cpp
@@ -19,7 +19,7 @@ DefaultControllerApplet::DefaultControllerApplet(Service::SM::ServiceManager& se
19DefaultControllerApplet::~DefaultControllerApplet() = default; 19DefaultControllerApplet::~DefaultControllerApplet() = default;
20 20
21void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callback, 21void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callback,
22 ControllerParameters parameters) const { 22 const ControllerParameters& parameters) const {
23 LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!"); 23 LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!");
24 24
25 auto& npad = 25 auto& npad =
diff --git a/src/core/frontend/applets/controller.h b/src/core/frontend/applets/controller.h
index 3e49cdbb9..dff71d8d9 100644
--- a/src/core/frontend/applets/controller.h
+++ b/src/core/frontend/applets/controller.h
@@ -38,7 +38,7 @@ public:
38 virtual ~ControllerApplet(); 38 virtual ~ControllerApplet();
39 39
40 virtual void ReconfigureControllers(std::function<void()> callback, 40 virtual void ReconfigureControllers(std::function<void()> callback,
41 ControllerParameters parameters) const = 0; 41 const ControllerParameters& parameters) const = 0;
42}; 42};
43 43
44class DefaultControllerApplet final : public ControllerApplet { 44class DefaultControllerApplet final : public ControllerApplet {
@@ -47,7 +47,7 @@ public:
47 ~DefaultControllerApplet() override; 47 ~DefaultControllerApplet() override;
48 48
49 void ReconfigureControllers(std::function<void()> callback, 49 void ReconfigureControllers(std::function<void()> callback,
50 ControllerParameters parameters) const override; 50 const ControllerParameters& parameters) const override;
51 51
52private: 52private:
53 Service::SM::ServiceManager& service_manager; 53 Service::SM::ServiceManager& service_manager;
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index b2b5b8adf..bb3e312a7 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -86,8 +86,6 @@ struct KernelCore::Impl {
86 } 86 }
87 cores.clear(); 87 cores.clear();
88 88
89 registered_core_threads.reset();
90
91 process_list.clear(); 89 process_list.clear();
92 current_process = nullptr; 90 current_process = nullptr;
93 91
@@ -199,9 +197,7 @@ struct KernelCore::Impl {
199 const auto it = std::find(register_host_thread_keys.begin(), end, this_id); 197 const auto it = std::find(register_host_thread_keys.begin(), end, this_id);
200 ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); 198 ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
201 ASSERT(it == end); 199 ASSERT(it == end);
202 ASSERT(!registered_core_threads[core_id]);
203 InsertHostThread(static_cast<u32>(core_id)); 200 InsertHostThread(static_cast<u32>(core_id));
204 registered_core_threads.set(core_id);
205 } 201 }
206 202
207 void RegisterHostThread() { 203 void RegisterHostThread() {
@@ -332,7 +328,6 @@ struct KernelCore::Impl {
332 328
333 // 0-3 IDs represent core threads, >3 represent others 329 // 0-3 IDs represent core threads, >3 represent others
334 std::atomic<u32> registered_thread_ids{Core::Hardware::NUM_CPU_CORES}; 330 std::atomic<u32> registered_thread_ids{Core::Hardware::NUM_CPU_CORES};
335 std::bitset<Core::Hardware::NUM_CPU_CORES> registered_core_threads;
336 331
337 // Number of host threads is a relatively high number to avoid overflowing 332 // Number of host threads is a relatively high number to avoid overflowing
338 static constexpr size_t NUM_REGISTRABLE_HOST_THREADS = 64; 333 static constexpr size_t NUM_REGISTRABLE_HOST_THREADS = 64;
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/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_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index fcb612864..b6df48360 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -2,15 +2,17 @@
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 <cstring>
6
7#include "common/assert.h" 5#include "common/assert.h"
8#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "core/core.h"
9#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" 8#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
9#include "video_core/memory_manager.h"
10#include "video_core/renderer_base.h"
10 11
11namespace Service::Nvidia::Devices { 12namespace Service::Nvidia::Devices {
12 13
13nvhost_nvdec::nvhost_nvdec(Core::System& system) : nvdevice(system) {} 14nvhost_nvdec::nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
15 : nvhost_nvdec_common(system, std::move(nvmap_dev)) {}
14nvhost_nvdec::~nvhost_nvdec() = default; 16nvhost_nvdec::~nvhost_nvdec() = default;
15 17
16u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 18u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
@@ -21,7 +23,7 @@ u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std::
21 23
22 switch (static_cast<IoctlCommand>(command.raw)) { 24 switch (static_cast<IoctlCommand>(command.raw)) {
23 case IoctlCommand::IocSetNVMAPfdCommand: 25 case IoctlCommand::IocSetNVMAPfdCommand:
24 return SetNVMAPfd(input, output); 26 return SetNVMAPfd(input);
25 case IoctlCommand::IocSubmit: 27 case IoctlCommand::IocSubmit:
26 return Submit(input, output); 28 return Submit(input, output);
27 case IoctlCommand::IocGetSyncpoint: 29 case IoctlCommand::IocGetSyncpoint:
@@ -29,79 +31,29 @@ u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std::
29 case IoctlCommand::IocGetWaitbase: 31 case IoctlCommand::IocGetWaitbase:
30 return GetWaitbase(input, output); 32 return GetWaitbase(input, output);
31 case IoctlCommand::IocMapBuffer: 33 case IoctlCommand::IocMapBuffer:
32 return MapBuffer(input, output); 34 case IoctlCommand::IocMapBuffer2:
35 case IoctlCommand::IocMapBuffer3:
33 case IoctlCommand::IocMapBufferEx: 36 case IoctlCommand::IocMapBufferEx:
34 return MapBufferEx(input, output); 37 return MapBuffer(input, output);
35 case IoctlCommand::IocUnmapBufferEx: 38 case IoctlCommand::IocUnmapBufferEx: {
36 return UnmapBufferEx(input, output); 39 // This command is sent when the video stream has ended, flush all video contexts
40 // This is usually sent in the folowing order: vic, nvdec, vic.
41 // Inform the GPU to clear any remaining nvdec buffers when this is detected.
42 LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
43 Tegra::ChCommandHeaderList cmdlist(1);
44 cmdlist[0] = Tegra::ChCommandHeader{0xDEADB33F};
45 system.GPU().PushCommandBuffer(cmdlist);
46 [[fallthrough]]; // fallthrough to unmap buffers
47 };
48 case IoctlCommand::IocUnmapBuffer:
49 case IoctlCommand::IocUnmapBuffer2:
50 case IoctlCommand::IocUnmapBuffer3:
51 return UnmapBuffer(input, output);
52 case IoctlCommand::IocSetSubmitTimeout:
53 return SetSubmitTimeout(input, output);
37 } 54 }
38 55
39 UNIMPLEMENTED_MSG("Unimplemented ioctl"); 56 UNIMPLEMENTED_MSG("Unimplemented ioctl 0x{:X}", command.raw);
40 return 0;
41}
42
43u32 nvhost_nvdec::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
44 IoctlSetNvmapFD params{};
45 std::memcpy(&params, input.data(), sizeof(IoctlSetNvmapFD));
46 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
47
48 nvmap_fd = params.nvmap_fd;
49 return 0;
50}
51
52u32 nvhost_nvdec::Submit(const std::vector<u8>& input, std::vector<u8>& output) {
53 IoctlSubmit params{};
54 std::memcpy(&params, input.data(), sizeof(IoctlSubmit));
55 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
56 std::memcpy(output.data(), &params, sizeof(IoctlSubmit));
57 return 0;
58}
59
60u32 nvhost_nvdec::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) {
61 IoctlGetSyncpoint params{};
62 std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint));
63 LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
64 params.value = 0; // Seems to be hard coded at 0
65 std::memcpy(output.data(), &params, sizeof(IoctlGetSyncpoint));
66 return 0;
67}
68
69u32 nvhost_nvdec::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
70 IoctlGetWaitbase params{};
71 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
72 LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
73 params.value = 0; // Seems to be hard coded at 0
74 std::memcpy(output.data(), &params, sizeof(IoctlGetWaitbase));
75 return 0;
76}
77
78u32 nvhost_nvdec::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
79 IoctlMapBuffer params{};
80 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
81 LOG_WARNING(Service_NVDRV, "(STUBBED) called with address={:08X}{:08X}", params.address_2,
82 params.address_1);
83 params.address_1 = 0;
84 params.address_2 = 0;
85 std::memcpy(output.data(), &params, sizeof(IoctlMapBuffer));
86 return 0;
87}
88
89u32 nvhost_nvdec::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
90 IoctlMapBufferEx params{};
91 std::memcpy(&params, input.data(), sizeof(IoctlMapBufferEx));
92 LOG_WARNING(Service_NVDRV, "(STUBBED) called with address={:08X}{:08X}", params.address_2,
93 params.address_1);
94 params.address_1 = 0;
95 params.address_2 = 0;
96 std::memcpy(output.data(), &params, sizeof(IoctlMapBufferEx));
97 return 0;
98}
99
100u32 nvhost_nvdec::UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
101 IoctlUnmapBufferEx params{};
102 std::memcpy(&params, input.data(), sizeof(IoctlUnmapBufferEx));
103 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
104 std::memcpy(output.data(), &params, sizeof(IoctlUnmapBufferEx));
105 return 0; 57 return 0;
106} 58}
107 59
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index 4332db118..102777ddd 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -4,16 +4,14 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <vector> 7#include <memory>
8#include "common/common_types.h" 8#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h"
9#include "common/swap.h"
10#include "core/hle/service/nvdrv/devices/nvdevice.h"
11 9
12namespace Service::Nvidia::Devices { 10namespace Service::Nvidia::Devices {
13 11
14class nvhost_nvdec final : public nvdevice { 12class nvhost_nvdec final : public nvhost_nvdec_common {
15public: 13public:
16 explicit nvhost_nvdec(Core::System& system); 14 explicit nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
17 ~nvhost_nvdec() override; 15 ~nvhost_nvdec() override;
18 16
19 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 17 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
@@ -27,62 +25,15 @@ private:
27 IocGetSyncpoint = 0xC0080002, 25 IocGetSyncpoint = 0xC0080002,
28 IocGetWaitbase = 0xC0080003, 26 IocGetWaitbase = 0xC0080003,
29 IocMapBuffer = 0xC01C0009, 27 IocMapBuffer = 0xC01C0009,
28 IocMapBuffer2 = 0xC16C0009,
29 IocMapBuffer3 = 0xC15C0009,
30 IocMapBufferEx = 0xC0A40009, 30 IocMapBufferEx = 0xC0A40009,
31 IocUnmapBufferEx = 0xC0A4000A, 31 IocUnmapBuffer = 0xC0A4000A,
32 IocUnmapBuffer2 = 0xC16C000A,
33 IocUnmapBufferEx = 0xC01C000A,
34 IocUnmapBuffer3 = 0xC15C000A,
35 IocSetSubmitTimeout = 0x40040007,
32 }; 36 };
33
34 struct IoctlSetNvmapFD {
35 u32_le nvmap_fd;
36 };
37 static_assert(sizeof(IoctlSetNvmapFD) == 0x4, "IoctlSetNvmapFD is incorrect size");
38
39 struct IoctlSubmit {
40 INSERT_PADDING_BYTES(0x40); // TODO(DarkLordZach): RE this structure
41 };
42 static_assert(sizeof(IoctlSubmit) == 0x40, "IoctlSubmit has incorrect size");
43
44 struct IoctlGetSyncpoint {
45 u32 unknown; // seems to be ignored? Nintendo added this
46 u32 value;
47 };
48 static_assert(sizeof(IoctlGetSyncpoint) == 0x08, "IoctlGetSyncpoint has incorrect size");
49
50 struct IoctlGetWaitbase {
51 u32 unknown; // seems to be ignored? Nintendo added this
52 u32 value;
53 };
54 static_assert(sizeof(IoctlGetWaitbase) == 0x08, "IoctlGetWaitbase has incorrect size");
55
56 struct IoctlMapBuffer {
57 u32 unknown;
58 u32 address_1;
59 u32 address_2;
60 INSERT_PADDING_BYTES(0x10); // TODO(DarkLordZach): RE this structure
61 };
62 static_assert(sizeof(IoctlMapBuffer) == 0x1C, "IoctlMapBuffer is incorrect size");
63
64 struct IoctlMapBufferEx {
65 u32 unknown;
66 u32 address_1;
67 u32 address_2;
68 INSERT_PADDING_BYTES(0x98); // TODO(DarkLordZach): RE this structure
69 };
70 static_assert(sizeof(IoctlMapBufferEx) == 0xA4, "IoctlMapBufferEx has incorrect size");
71
72 struct IoctlUnmapBufferEx {
73 INSERT_PADDING_BYTES(0xA4); // TODO(DarkLordZach): RE this structure
74 };
75 static_assert(sizeof(IoctlUnmapBufferEx) == 0xA4, "IoctlUnmapBufferEx has incorrect size");
76
77 u32_le nvmap_fd{};
78
79 u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
80 u32 Submit(const std::vector<u8>& input, std::vector<u8>& output);
81 u32 GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output);
82 u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
83 u32 MapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
84 u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
85 u32 UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
86}; 37};
87 38
88} // namespace Service::Nvidia::Devices 39} // 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
new file mode 100644
index 000000000..85792495f
--- /dev/null
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
@@ -0,0 +1,234 @@
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 <algorithm>
6#include <cstring>
7
8#include "common/assert.h"
9#include "common/common_types.h"
10#include "common/logging/log.h"
11#include "core/core.h"
12#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h"
13#include "core/hle/service/nvdrv/devices/nvmap.h"
14#include "core/memory.h"
15#include "video_core/memory_manager.h"
16#include "video_core/renderer_base.h"
17
18namespace Service::Nvidia::Devices {
19
20namespace {
21// Splice vectors will copy count amount of type T from the input vector into the dst vector.
22template <typename T>
23std::size_t SpliceVectors(const std::vector<u8>& input, std::vector<T>& dst, std::size_t count,
24 std::size_t offset) {
25 std::memcpy(dst.data(), input.data() + offset, count * sizeof(T));
26 offset += count * sizeof(T);
27 return offset;
28}
29
30// Write vectors will write data to the output buffer
31template <typename T>
32std::size_t WriteVectors(std::vector<u8>& dst, const std::vector<T>& src, std::size_t offset) {
33 std::memcpy(dst.data() + offset, src.data(), src.size() * sizeof(T));
34 offset += src.size() * sizeof(T);
35 return offset;
36}
37} // Anonymous namespace
38
39namespace NvErrCodes {
40constexpr u32 Success{};
41constexpr u32 OutOfMemory{static_cast<u32>(-12)};
42constexpr u32 InvalidInput{static_cast<u32>(-22)};
43} // namespace NvErrCodes
44
45nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
46 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
47nvhost_nvdec_common::~nvhost_nvdec_common() = default;
48
49u32 nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) {
50 IoctlSetNvmapFD params{};
51 std::memcpy(&params, input.data(), sizeof(IoctlSetNvmapFD));
52 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
53
54 nvmap_fd = params.nvmap_fd;
55 return 0;
56}
57
58u32 nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& output) {
59 IoctlSubmit params{};
60 std::memcpy(&params, input.data(), sizeof(IoctlSubmit));
61 LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count);
62
63 // Instantiate param buffers
64 std::size_t offset = sizeof(IoctlSubmit);
65 std::vector<CommandBuffer> command_buffers(params.cmd_buffer_count);
66 std::vector<Reloc> relocs(params.relocation_count);
67 std::vector<u32> reloc_shifts(params.relocation_count);
68 std::vector<SyncptIncr> syncpt_increments(params.syncpoint_count);
69 std::vector<SyncptIncr> wait_checks(params.syncpoint_count);
70 std::vector<Fence> fences(params.fence_count);
71
72 // Splice input into their respective buffers
73 offset = SpliceVectors(input, command_buffers, params.cmd_buffer_count, offset);
74 offset = SpliceVectors(input, relocs, params.relocation_count, offset);
75 offset = SpliceVectors(input, reloc_shifts, params.relocation_count, offset);
76 offset = SpliceVectors(input, syncpt_increments, params.syncpoint_count, offset);
77 offset = SpliceVectors(input, wait_checks, params.syncpoint_count, offset);
78 offset = SpliceVectors(input, fences, params.fence_count, offset);
79
80 // TODO(ameerj): For async gpu, utilize fences for syncpoint 'max' increment
81
82 auto& gpu = system.GPU();
83
84 for (const auto& cmd_buffer : command_buffers) {
85 auto object = nvmap_dev->GetObject(cmd_buffer.memory_id);
86 ASSERT_OR_EXECUTE(object, return NvErrCodes::InvalidInput;);
87 const auto map = FindBufferMap(object->dma_map_addr);
88 if (!map) {
89 LOG_ERROR(Service_NVDRV, "Tried to submit an invalid offset 0x{:X} dma 0x{:X}",
90 object->addr, object->dma_map_addr);
91 return 0;
92 }
93 Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count);
94 gpu.MemoryManager().ReadBlock(map->StartAddr() + cmd_buffer.offset, cmdlist.data(),
95 cmdlist.size() * sizeof(u32));
96 gpu.PushCommandBuffer(cmdlist);
97 }
98
99 std::memcpy(output.data(), &params, sizeof(IoctlSubmit));
100 // Some games expect command_buffers to be written back
101 offset = sizeof(IoctlSubmit);
102 offset = WriteVectors(output, command_buffers, offset);
103 offset = WriteVectors(output, relocs, offset);
104 offset = WriteVectors(output, reloc_shifts, offset);
105 offset = WriteVectors(output, syncpt_increments, offset);
106 offset = WriteVectors(output, wait_checks, offset);
107
108 return NvErrCodes::Success;
109}
110
111u32 nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) {
112 IoctlGetSyncpoint params{};
113 std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint));
114 LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param);
115
116 // We found that implementing this causes deadlocks with async gpu, along with degraded
117 // performance. TODO: RE the nvdec async implementation
118 params.value = 0;
119 std::memcpy(output.data(), &params, sizeof(IoctlGetSyncpoint));
120
121 return NvErrCodes::Success;
122}
123
124u32 nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
125 IoctlGetWaitbase params{};
126 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
127 params.value = 0; // Seems to be hard coded at 0
128 std::memcpy(output.data(), &params, sizeof(IoctlGetWaitbase));
129 return 0;
130}
131
132u32 nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
133 IoctlMapBuffer params{};
134 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
135 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
136
137 SpliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer));
138
139 auto& gpu = system.GPU();
140
141 for (auto& cmf_buff : cmd_buffer_handles) {
142 auto object{nvmap_dev->GetObject(cmf_buff.map_handle)};
143 if (!object) {
144 LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmf_buff.map_handle);
145 std::memcpy(output.data(), &params, output.size());
146 return NvErrCodes::InvalidInput;
147 }
148 if (object->dma_map_addr == 0) {
149 // NVDEC and VIC memory is in the 32-bit address space
150 // MapAllocate32 will attempt to map a lower 32-bit value in the shared gpu memory space
151 const GPUVAddr low_addr = gpu.MemoryManager().MapAllocate32(object->addr, object->size);
152 object->dma_map_addr = static_cast<u32>(low_addr);
153 // Ensure that the dma_map_addr is indeed in the lower 32-bit address space.
154 ASSERT(object->dma_map_addr == low_addr);
155 }
156 if (!object->dma_map_addr) {
157 LOG_ERROR(Service_NVDRV, "failed to map size={}", object->size);
158 } else {
159 cmf_buff.map_address = object->dma_map_addr;
160 AddBufferMap(object->dma_map_addr, object->size, object->addr,
161 object->status == nvmap::Object::Status::Allocated);
162 }
163 }
164 std::memcpy(output.data(), &params, sizeof(IoctlMapBuffer));
165 std::memcpy(output.data() + sizeof(IoctlMapBuffer), cmd_buffer_handles.data(),
166 cmd_buffer_handles.size() * sizeof(MapBufferEntry));
167
168 return NvErrCodes::Success;
169}
170
171u32 nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
172 IoctlMapBuffer params{};
173 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
174 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
175 SpliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer));
176
177 auto& gpu = system.GPU();
178
179 for (auto& cmf_buff : cmd_buffer_handles) {
180 const auto object{nvmap_dev->GetObject(cmf_buff.map_handle)};
181 if (!object) {
182 LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmf_buff.map_handle);
183 std::memcpy(output.data(), &params, output.size());
184 return NvErrCodes::InvalidInput;
185 }
186 if (const auto size{RemoveBufferMap(object->dma_map_addr)}; size) {
187 gpu.MemoryManager().Unmap(object->dma_map_addr, *size);
188 } else {
189 // This occurs quite frequently, however does not seem to impact functionality
190 LOG_DEBUG(Service_NVDRV, "invalid offset=0x{:X} dma=0x{:X}", object->addr,
191 object->dma_map_addr);
192 }
193 object->dma_map_addr = 0;
194 }
195 std::memset(output.data(), 0, output.size());
196 return NvErrCodes::Success;
197}
198
199u32 nvhost_nvdec_common::SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output) {
200 std::memcpy(&submit_timeout, input.data(), input.size());
201 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
202 return NvErrCodes::Success;
203}
204
205std::optional<nvhost_nvdec_common::BufferMap> nvhost_nvdec_common::FindBufferMap(
206 GPUVAddr gpu_addr) const {
207 const auto it = std::find_if(
208 buffer_mappings.begin(), buffer_mappings.upper_bound(gpu_addr), [&](const auto& entry) {
209 return (gpu_addr >= entry.second.StartAddr() && gpu_addr < entry.second.EndAddr());
210 });
211
212 ASSERT(it != buffer_mappings.end());
213 return it->second;
214}
215
216void nvhost_nvdec_common::AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr,
217 bool is_allocated) {
218 buffer_mappings.insert_or_assign(gpu_addr, BufferMap{gpu_addr, size, cpu_addr, is_allocated});
219}
220
221std::optional<std::size_t> nvhost_nvdec_common::RemoveBufferMap(GPUVAddr gpu_addr) {
222 const auto iter{buffer_mappings.find(gpu_addr)};
223 if (iter == buffer_mappings.end()) {
224 return std::nullopt;
225 }
226 std::size_t size = 0;
227 if (iter->second.IsAllocated()) {
228 size = iter->second.Size();
229 }
230 buffer_mappings.erase(iter);
231 return size;
232}
233
234} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
new file mode 100644
index 000000000..c249c5349
--- /dev/null
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
@@ -0,0 +1,168 @@
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 <map>
8#include <vector>
9#include "common/common_types.h"
10#include "common/swap.h"
11#include "core/hle/service/nvdrv/devices/nvdevice.h"
12
13namespace Service::Nvidia::Devices {
14class nvmap;
15
16class nvhost_nvdec_common : public nvdevice {
17public:
18 explicit nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
19 ~nvhost_nvdec_common() override;
20
21 virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
22 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
23 IoctlVersion version) = 0;
24
25protected:
26 class BufferMap final {
27 public:
28 constexpr BufferMap() = default;
29
30 constexpr BufferMap(GPUVAddr start_addr, std::size_t size)
31 : start_addr{start_addr}, end_addr{start_addr + size} {}
32
33 constexpr BufferMap(GPUVAddr start_addr, std::size_t size, VAddr cpu_addr,
34 bool is_allocated)
35 : start_addr{start_addr}, end_addr{start_addr + size}, cpu_addr{cpu_addr},
36 is_allocated{is_allocated} {}
37
38 constexpr VAddr StartAddr() const {
39 return start_addr;
40 }
41
42 constexpr VAddr EndAddr() const {
43 return end_addr;
44 }
45
46 constexpr std::size_t Size() const {
47 return end_addr - start_addr;
48 }
49
50 constexpr VAddr CpuAddr() const {
51 return cpu_addr;
52 }
53
54 constexpr bool IsAllocated() const {
55 return is_allocated;
56 }
57
58 private:
59 GPUVAddr start_addr{};
60 GPUVAddr end_addr{};
61 VAddr cpu_addr{};
62 bool is_allocated{};
63 };
64
65 struct IoctlSetNvmapFD {
66 u32_le nvmap_fd;
67 };
68 static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
69
70 struct IoctlSubmitCommandBuffer {
71 u32_le id;
72 u32_le offset;
73 u32_le count;
74 };
75 static_assert(sizeof(IoctlSubmitCommandBuffer) == 0xC,
76 "IoctlSubmitCommandBuffer is incorrect size");
77 struct IoctlSubmit {
78 u32_le cmd_buffer_count;
79 u32_le relocation_count;
80 u32_le syncpoint_count;
81 u32_le fence_count;
82 };
83 static_assert(sizeof(IoctlSubmit) == 0x10, "IoctlSubmit has incorrect size");
84
85 struct CommandBuffer {
86 s32 memory_id;
87 u32 offset;
88 s32 word_count;
89 };
90 static_assert(sizeof(CommandBuffer) == 0xC, "CommandBuffer has incorrect size");
91
92 struct Reloc {
93 s32 cmdbuffer_memory;
94 s32 cmdbuffer_offset;
95 s32 target;
96 s32 target_offset;
97 };
98 static_assert(sizeof(Reloc) == 0x10, "CommandBuffer has incorrect size");
99
100 struct SyncptIncr {
101 u32 id;
102 u32 increments;
103 };
104 static_assert(sizeof(SyncptIncr) == 0x8, "CommandBuffer has incorrect size");
105
106 struct Fence {
107 u32 id;
108 u32 value;
109 };
110 static_assert(sizeof(Fence) == 0x8, "CommandBuffer has incorrect size");
111
112 struct IoctlGetSyncpoint {
113 // Input
114 u32_le param;
115 // Output
116 u32_le value;
117 };
118 static_assert(sizeof(IoctlGetSyncpoint) == 8, "IocGetIdParams has wrong size");
119
120 struct IoctlGetWaitbase {
121 u32_le unknown; // seems to be ignored? Nintendo added this
122 u32_le value;
123 };
124 static_assert(sizeof(IoctlGetWaitbase) == 0x8, "IoctlGetWaitbase is incorrect size");
125
126 struct IoctlMapBuffer {
127 u32_le num_entries;
128 u32_le data_address; // Ignored by the driver.
129 u32_le attach_host_ch_das;
130 };
131 static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size");
132
133 struct IocGetIdParams {
134 // Input
135 u32_le param;
136 // Output
137 u32_le value;
138 };
139 static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
140
141 // Used for mapping and unmapping command buffers
142 struct MapBufferEntry {
143 u32_le map_handle;
144 u32_le map_address;
145 };
146 static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size");
147
148 /// Ioctl command implementations
149 u32 SetNVMAPfd(const std::vector<u8>& input);
150 u32 Submit(const std::vector<u8>& input, std::vector<u8>& output);
151 u32 GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output);
152 u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
153 u32 MapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
154 u32 UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
155 u32 SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output);
156
157 std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const;
158 void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated);
159 std::optional<std::size_t> RemoveBufferMap(GPUVAddr gpu_addr);
160
161 u32_le nvmap_fd{};
162 u32_le submit_timeout{};
163 std::shared_ptr<nvmap> nvmap_dev;
164
165 // This is expected to be ordered, therefore we must use a map, not unordered_map
166 std::map<GPUVAddr, BufferMap> buffer_mappings;
167};
168}; // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index 9da19ad56..60db54d00 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -2,15 +2,17 @@
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 <cstring>
6
7#include "common/assert.h" 5#include "common/assert.h"
8#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "core/core.h"
9#include "core/hle/service/nvdrv/devices/nvhost_vic.h" 8#include "core/hle/service/nvdrv/devices/nvhost_vic.h"
9#include "video_core/memory_manager.h"
10#include "video_core/renderer_base.h"
10 11
11namespace Service::Nvidia::Devices { 12namespace Service::Nvidia::Devices {
13nvhost_vic::nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
14 : nvhost_nvdec_common(system, std::move(nvmap_dev)) {}
12 15
13nvhost_vic::nvhost_vic(Core::System& system) : nvdevice(system) {}
14nvhost_vic::~nvhost_vic() = default; 16nvhost_vic::~nvhost_vic() = default;
15 17
16u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 18u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
@@ -21,7 +23,7 @@ u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, const std::ve
21 23
22 switch (static_cast<IoctlCommand>(command.raw)) { 24 switch (static_cast<IoctlCommand>(command.raw)) {
23 case IoctlCommand::IocSetNVMAPfdCommand: 25 case IoctlCommand::IocSetNVMAPfdCommand:
24 return SetNVMAPfd(input, output); 26 return SetNVMAPfd(input);
25 case IoctlCommand::IocSubmit: 27 case IoctlCommand::IocSubmit:
26 return Submit(input, output); 28 return Submit(input, output);
27 case IoctlCommand::IocGetSyncpoint: 29 case IoctlCommand::IocGetSyncpoint:
@@ -29,83 +31,19 @@ u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, const std::ve
29 case IoctlCommand::IocGetWaitbase: 31 case IoctlCommand::IocGetWaitbase:
30 return GetWaitbase(input, output); 32 return GetWaitbase(input, output);
31 case IoctlCommand::IocMapBuffer: 33 case IoctlCommand::IocMapBuffer:
32 return MapBuffer(input, output); 34 case IoctlCommand::IocMapBuffer2:
35 case IoctlCommand::IocMapBuffer3:
36 case IoctlCommand::IocMapBuffer4:
33 case IoctlCommand::IocMapBufferEx: 37 case IoctlCommand::IocMapBufferEx:
34 return MapBuffer(input, output); 38 return MapBuffer(input, output);
39 case IoctlCommand::IocUnmapBuffer:
40 case IoctlCommand::IocUnmapBuffer2:
41 case IoctlCommand::IocUnmapBuffer3:
35 case IoctlCommand::IocUnmapBufferEx: 42 case IoctlCommand::IocUnmapBufferEx:
36 return UnmapBufferEx(input, output); 43 return UnmapBuffer(input, output);
37 } 44 }
38 45
39 UNIMPLEMENTED_MSG("Unimplemented ioctl"); 46 UNIMPLEMENTED_MSG("Unimplemented ioctl 0x{:X}", command.raw);
40 return 0;
41}
42
43u32 nvhost_vic::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
44 IoctlSetNvmapFD params{};
45 std::memcpy(&params, input.data(), sizeof(IoctlSetNvmapFD));
46 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
47
48 nvmap_fd = params.nvmap_fd;
49 return 0;
50}
51
52u32 nvhost_vic::Submit(const std::vector<u8>& input, std::vector<u8>& output) {
53 IoctlSubmit params{};
54 std::memcpy(&params, input.data(), sizeof(IoctlSubmit));
55 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
56
57 // Workaround for Luigi's Mansion 3, as nvhost_vic is not implemented for asynch GPU
58 params.command_buffer = {};
59
60 std::memcpy(output.data(), &params, sizeof(IoctlSubmit));
61 return 0;
62}
63
64u32 nvhost_vic::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) {
65 IoctlGetSyncpoint params{};
66 std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint));
67 LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
68 params.value = 0; // Seems to be hard coded at 0
69 std::memcpy(output.data(), &params, sizeof(IoctlGetSyncpoint));
70 return 0;
71}
72
73u32 nvhost_vic::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
74 IoctlGetWaitbase params{};
75 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
76 LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
77 params.value = 0; // Seems to be hard coded at 0
78 std::memcpy(output.data(), &params, sizeof(IoctlGetWaitbase));
79 return 0;
80}
81
82u32 nvhost_vic::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
83 IoctlMapBuffer params{};
84 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
85 LOG_WARNING(Service_NVDRV, "(STUBBED) called with address={:08X}{:08X}", params.address_2,
86 params.address_1);
87 params.address_1 = 0;
88 params.address_2 = 0;
89 std::memcpy(output.data(), &params, sizeof(IoctlMapBuffer));
90 return 0;
91}
92
93u32 nvhost_vic::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
94 IoctlMapBufferEx params{};
95 std::memcpy(&params, input.data(), sizeof(IoctlMapBufferEx));
96 LOG_WARNING(Service_NVDRV, "(STUBBED) called with address={:08X}{:08X}", params.address_2,
97 params.address_1);
98 params.address_1 = 0;
99 params.address_2 = 0;
100 std::memcpy(output.data(), &params, sizeof(IoctlMapBufferEx));
101 return 0;
102}
103
104u32 nvhost_vic::UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
105 IoctlUnmapBufferEx params{};
106 std::memcpy(&params, input.data(), sizeof(IoctlUnmapBufferEx));
107 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
108 std::memcpy(output.data(), &params, sizeof(IoctlUnmapBufferEx));
109 return 0; 47 return 0;
110} 48}
111 49
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
index a7bb7bbd5..f975b190c 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
@@ -4,19 +4,15 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array> 7#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h"
8#include <vector>
9#include "common/common_types.h"
10#include "common/swap.h"
11#include "core/hle/service/nvdrv/devices/nvdevice.h"
12 8
13namespace Service::Nvidia::Devices { 9namespace Service::Nvidia::Devices {
10class nvmap;
14 11
15class nvhost_vic final : public nvdevice { 12class nvhost_vic final : public nvhost_nvdec_common {
16public: 13public:
17 explicit nvhost_vic(Core::System& system); 14 explicit nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
18 ~nvhost_vic() override; 15 ~nvhost_vic();
19
20 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 16 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
21 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 17 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
22 IoctlVersion version) override; 18 IoctlVersion version) override;
@@ -28,74 +24,14 @@ private:
28 IocGetSyncpoint = 0xC0080002, 24 IocGetSyncpoint = 0xC0080002,
29 IocGetWaitbase = 0xC0080003, 25 IocGetWaitbase = 0xC0080003,
30 IocMapBuffer = 0xC01C0009, 26 IocMapBuffer = 0xC01C0009,
27 IocMapBuffer2 = 0xC0340009,
28 IocMapBuffer3 = 0xC0140009,
29 IocMapBuffer4 = 0xC00C0009,
31 IocMapBufferEx = 0xC03C0009, 30 IocMapBufferEx = 0xC03C0009,
32 IocUnmapBufferEx = 0xC03C000A, 31 IocUnmapBuffer = 0xC03C000A,
33 }; 32 IocUnmapBuffer2 = 0xC034000A,
34 33 IocUnmapBuffer3 = 0xC00C000A,
35 struct IoctlSetNvmapFD { 34 IocUnmapBufferEx = 0xC01C000A,
36 u32_le nvmap_fd;
37 };
38 static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
39
40 struct IoctlSubmitCommandBuffer {
41 u32 id;
42 u32 offset;
43 u32 count;
44 };
45 static_assert(sizeof(IoctlSubmitCommandBuffer) == 0xC,
46 "IoctlSubmitCommandBuffer is incorrect size");
47
48 struct IoctlSubmit {
49 u32 command_buffer_count;
50 u32 relocations_count;
51 u32 syncpt_count;
52 u32 wait_count;
53 std::array<IoctlSubmitCommandBuffer, 4> command_buffer;
54 };
55 static_assert(sizeof(IoctlSubmit) == 0x40, "IoctlSubmit is incorrect size");
56
57 struct IoctlGetSyncpoint {
58 u32 unknown; // seems to be ignored? Nintendo added this
59 u32 value;
60 };
61 static_assert(sizeof(IoctlGetSyncpoint) == 0x8, "IoctlGetSyncpoint is incorrect size");
62
63 struct IoctlGetWaitbase {
64 u32 unknown; // seems to be ignored? Nintendo added this
65 u32 value;
66 };
67 static_assert(sizeof(IoctlGetWaitbase) == 0x8, "IoctlGetWaitbase is incorrect size");
68
69 struct IoctlMapBuffer {
70 u32 unknown;
71 u32 address_1;
72 u32 address_2;
73 INSERT_PADDING_BYTES(0x10); // TODO(DarkLordZach): RE this structure
74 };
75 static_assert(sizeof(IoctlMapBuffer) == 0x1C, "IoctlMapBuffer is incorrect size");
76
77 struct IoctlMapBufferEx {
78 u32 unknown;
79 u32 address_1;
80 u32 address_2;
81 INSERT_PADDING_BYTES(0x30); // TODO(DarkLordZach): RE this structure
82 }; 35 };
83 static_assert(sizeof(IoctlMapBufferEx) == 0x3C, "IoctlMapBufferEx is incorrect size");
84
85 struct IoctlUnmapBufferEx {
86 INSERT_PADDING_BYTES(0x3C); // TODO(DarkLordZach): RE this structure
87 };
88 static_assert(sizeof(IoctlUnmapBufferEx) == 0x3C, "IoctlUnmapBufferEx is incorrect size");
89
90 u32_le nvmap_fd{};
91
92 u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
93 u32 Submit(const std::vector<u8>& input, std::vector<u8>& output);
94 u32 GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output);
95 u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
96 u32 MapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
97 u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
98 u32 UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
99}; 36};
100
101} // namespace Service::Nvidia::Devices 37} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h
index 84624be00..04b9ef540 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.h
+++ b/src/core/hle/service/nvdrv/devices/nvmap.h
@@ -37,6 +37,7 @@ public:
37 VAddr addr; 37 VAddr addr;
38 Status status; 38 Status status;
39 u32 refcount; 39 u32 refcount;
40 u32 dma_map_addr;
40 }; 41 };
41 42
42 std::shared_ptr<Object> GetObject(u32 handle) const { 43 std::shared_ptr<Object> GetObject(u32 handle) const {
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 197c77db0..803c1a984 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -51,9 +51,9 @@ Module::Module(Core::System& system) {
51 devices["/dev/nvmap"] = nvmap_dev; 51 devices["/dev/nvmap"] = nvmap_dev;
52 devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(system, nvmap_dev); 52 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); 53 devices["/dev/nvhost-ctrl"] = std::make_shared<Devices::nvhost_ctrl>(system, events_interface);
54 devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>(system); 54 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); 55 devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(system);
56 devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(system); 56 devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(system, nvmap_dev);
57} 57}
58 58
59Module::~Module() = default; 59Module::~Module() = default;
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/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index ee4fa4b48..7d0474e0b 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -10,6 +10,7 @@
10#include "core/hle/ipc_helpers.h" 10#include "core/hle/ipc_helpers.h"
11#include "core/hle/kernel/client_port.h" 11#include "core/hle/kernel/client_port.h"
12#include "core/hle/kernel/client_session.h" 12#include "core/hle/kernel/client_session.h"
13#include "core/hle/kernel/kernel.h"
13#include "core/hle/kernel/scheduler.h" 14#include "core/hle/kernel/scheduler.h"
14#include "core/hle/service/time/interface.h" 15#include "core/hle/service/time/interface.h"
15#include "core/hle/service/time/time.h" 16#include "core/hle/service/time/time.h"
@@ -125,7 +126,7 @@ ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal(
125 Kernel::Thread* thread, Clock::SystemClockContext user_context, 126 Kernel::Thread* thread, Clock::SystemClockContext user_context,
126 Clock::SystemClockContext network_context, u8 type, Clock::ClockSnapshot& clock_snapshot) { 127 Clock::SystemClockContext network_context, u8 type, Clock::ClockSnapshot& clock_snapshot) {
127 128
128 auto& time_manager{module->GetTimeManager()}; 129 auto& time_manager{system.GetTimeManager()};
129 130
130 clock_snapshot.is_automatic_correction_enabled = 131 clock_snapshot.is_automatic_correction_enabled =
131 time_manager.GetStandardUserSystemClockCore().IsAutomaticCorrectionEnabled(); 132 time_manager.GetStandardUserSystemClockCore().IsAutomaticCorrectionEnabled();
@@ -182,7 +183,7 @@ void Module::Interface::GetStandardUserSystemClock(Kernel::HLERequestContext& ct
182 LOG_DEBUG(Service_Time, "called"); 183 LOG_DEBUG(Service_Time, "called");
183 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 184 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
184 rb.Push(RESULT_SUCCESS); 185 rb.Push(RESULT_SUCCESS);
185 rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardUserSystemClockCore(), 186 rb.PushIpcInterface<ISystemClock>(system.GetTimeManager().GetStandardUserSystemClockCore(),
186 system); 187 system);
187} 188}
188 189
@@ -190,7 +191,7 @@ void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext&
190 LOG_DEBUG(Service_Time, "called"); 191 LOG_DEBUG(Service_Time, "called");
191 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 192 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
192 rb.Push(RESULT_SUCCESS); 193 rb.Push(RESULT_SUCCESS);
193 rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardNetworkSystemClockCore(), 194 rb.PushIpcInterface<ISystemClock>(system.GetTimeManager().GetStandardNetworkSystemClockCore(),
194 system); 195 system);
195} 196}
196 197
@@ -198,29 +199,28 @@ void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) {
198 LOG_DEBUG(Service_Time, "called"); 199 LOG_DEBUG(Service_Time, "called");
199 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 200 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
200 rb.Push(RESULT_SUCCESS); 201 rb.Push(RESULT_SUCCESS);
201 rb.PushIpcInterface<ISteadyClock>(module->GetTimeManager().GetStandardSteadyClockCore(), 202 rb.PushIpcInterface<ISteadyClock>(system.GetTimeManager().GetStandardSteadyClockCore(), system);
202 system);
203} 203}
204 204
205void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) { 205void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) {
206 LOG_DEBUG(Service_Time, "called"); 206 LOG_DEBUG(Service_Time, "called");
207 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 207 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
208 rb.Push(RESULT_SUCCESS); 208 rb.Push(RESULT_SUCCESS);
209 rb.PushIpcInterface<ITimeZoneService>(module->GetTimeManager().GetTimeZoneContentManager()); 209 rb.PushIpcInterface<ITimeZoneService>(system.GetTimeManager().GetTimeZoneContentManager());
210} 210}
211 211
212void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx) { 212void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx) {
213 LOG_DEBUG(Service_Time, "called"); 213 LOG_DEBUG(Service_Time, "called");
214 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 214 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
215 rb.Push(RESULT_SUCCESS); 215 rb.Push(RESULT_SUCCESS);
216 rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardLocalSystemClockCore(), 216 rb.PushIpcInterface<ISystemClock>(system.GetTimeManager().GetStandardLocalSystemClockCore(),
217 system); 217 system);
218} 218}
219 219
220void Module::Interface::IsStandardNetworkSystemClockAccuracySufficient( 220void Module::Interface::IsStandardNetworkSystemClockAccuracySufficient(
221 Kernel::HLERequestContext& ctx) { 221 Kernel::HLERequestContext& ctx) {
222 LOG_DEBUG(Service_Time, "called"); 222 LOG_DEBUG(Service_Time, "called");
223 auto& clock_core{module->GetTimeManager().GetStandardNetworkSystemClockCore()}; 223 auto& clock_core{system.GetTimeManager().GetStandardNetworkSystemClockCore()};
224 IPC::ResponseBuilder rb{ctx, 3}; 224 IPC::ResponseBuilder rb{ctx, 3};
225 rb.Push(RESULT_SUCCESS); 225 rb.Push(RESULT_SUCCESS);
226 rb.Push<u32>(clock_core.IsStandardNetworkSystemClockAccuracySufficient(system)); 226 rb.Push<u32>(clock_core.IsStandardNetworkSystemClockAccuracySufficient(system));
@@ -229,7 +229,7 @@ void Module::Interface::IsStandardNetworkSystemClockAccuracySufficient(
229void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERequestContext& ctx) { 229void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERequestContext& ctx) {
230 LOG_DEBUG(Service_Time, "called"); 230 LOG_DEBUG(Service_Time, "called");
231 231
232 auto& steady_clock_core{module->GetTimeManager().GetStandardSteadyClockCore()}; 232 auto& steady_clock_core{system.GetTimeManager().GetStandardSteadyClockCore()};
233 if (!steady_clock_core.IsInitialized()) { 233 if (!steady_clock_core.IsInitialized()) {
234 IPC::ResponseBuilder rb{ctx, 2}; 234 IPC::ResponseBuilder rb{ctx, 2};
235 rb.Push(ERROR_UNINITIALIZED_CLOCK); 235 rb.Push(ERROR_UNINITIALIZED_CLOCK);
@@ -262,8 +262,8 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
262 262
263 Clock::SystemClockContext user_context{}; 263 Clock::SystemClockContext user_context{};
264 if (const ResultCode result{ 264 if (const ResultCode result{
265 module->GetTimeManager().GetStandardUserSystemClockCore().GetClockContext( 265 system.GetTimeManager().GetStandardUserSystemClockCore().GetClockContext(system,
266 system, user_context)}; 266 user_context)};
267 result.IsError()) { 267 result.IsError()) {
268 IPC::ResponseBuilder rb{ctx, 2}; 268 IPC::ResponseBuilder rb{ctx, 2};
269 rb.Push(result); 269 rb.Push(result);
@@ -271,7 +271,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
271 } 271 }
272 Clock::SystemClockContext network_context{}; 272 Clock::SystemClockContext network_context{};
273 if (const ResultCode result{ 273 if (const ResultCode result{
274 module->GetTimeManager().GetStandardNetworkSystemClockCore().GetClockContext( 274 system.GetTimeManager().GetStandardNetworkSystemClockCore().GetClockContext(
275 system, network_context)}; 275 system, network_context)};
276 result.IsError()) { 276 result.IsError()) {
277 IPC::ResponseBuilder rb{ctx, 2}; 277 IPC::ResponseBuilder rb{ctx, 2};
@@ -372,7 +372,7 @@ void Module::Interface::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& c
372 LOG_DEBUG(Service_Time, "called"); 372 LOG_DEBUG(Service_Time, "called");
373 IPC::ResponseBuilder rb{ctx, 2, 1}; 373 IPC::ResponseBuilder rb{ctx, 2, 1};
374 rb.Push(RESULT_SUCCESS); 374 rb.Push(RESULT_SUCCESS);
375 rb.PushCopyObjects(module->GetTimeManager().GetSharedMemory().GetSharedMemoryHolder()); 375 rb.PushCopyObjects(SharedFrom(&system.Kernel().GetTimeSharedMem()));
376} 376}
377 377
378Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name) 378Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name)
@@ -381,7 +381,7 @@ Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& syste
381Module::Interface::~Interface() = default; 381Module::Interface::~Interface() = default;
382 382
383void InstallInterfaces(Core::System& system) { 383void InstallInterfaces(Core::System& system) {
384 auto module{std::make_shared<Module>(system)}; 384 auto module{std::make_shared<Module>()};
385 std::make_shared<Time>(module, system, "time:a")->InstallAsService(system.ServiceManager()); 385 std::make_shared<Time>(module, system, "time:a")->InstallAsService(system.ServiceManager());
386 std::make_shared<Time>(module, system, "time:s")->InstallAsService(system.ServiceManager()); 386 std::make_shared<Time>(module, system, "time:s")->InstallAsService(system.ServiceManager());
387 std::make_shared<Time>(module, system, "time:u")->InstallAsService(system.ServiceManager()); 387 std::make_shared<Time>(module, system, "time:u")->InstallAsService(system.ServiceManager());
diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h
index 41f3002e9..49f4aac0a 100644
--- a/src/core/hle/service/time/time.h
+++ b/src/core/hle/service/time/time.h
@@ -16,7 +16,7 @@ namespace Service::Time {
16 16
17class Module final { 17class Module final {
18public: 18public:
19 Module(Core::System& system) : time_manager{system} {} 19 Module() = default;
20 20
21 class Interface : public ServiceFramework<Interface> { 21 class Interface : public ServiceFramework<Interface> {
22 public: 22 public:
@@ -46,13 +46,6 @@ public:
46 std::shared_ptr<Module> module; 46 std::shared_ptr<Module> module;
47 Core::System& system; 47 Core::System& system;
48 }; 48 };
49
50 TimeManager& GetTimeManager() {
51 return time_manager;
52 }
53
54private:
55 TimeManager time_manager;
56}; 49};
57 50
58/// Registers all Time services with the specified service manager. 51/// Registers all Time services with the specified service manager.
diff --git a/src/core/hle/service/time/time_manager.cpp b/src/core/hle/service/time/time_manager.cpp
index b4dfe45e5..858623e2b 100644
--- a/src/core/hle/service/time/time_manager.cpp
+++ b/src/core/hle/service/time/time_manager.cpp
@@ -22,125 +22,282 @@ static std::chrono::seconds GetSecondsSinceEpoch() {
22 Settings::values.custom_rtc_differential; 22 Settings::values.custom_rtc_differential;
23} 23}
24 24
25static s64 GetExternalTimeZoneOffset() {
26 // With "auto" timezone setting, we use the external system's timezone offset
27 if (Settings::GetTimeZoneString() == "auto") {
28 return Common::TimeZone::GetCurrentOffsetSeconds().count();
29 }
30 return 0;
31}
32
33static s64 GetExternalRtcValue() { 25static s64 GetExternalRtcValue() {
34 return GetSecondsSinceEpoch().count() + GetExternalTimeZoneOffset(); 26 return GetSecondsSinceEpoch().count() + TimeManager::GetExternalTimeZoneOffset();
35}
36
37TimeManager::TimeManager(Core::System& system)
38 : shared_memory{system}, standard_local_system_clock_core{standard_steady_clock_core},
39 standard_network_system_clock_core{standard_steady_clock_core},
40 standard_user_system_clock_core{standard_local_system_clock_core,
41 standard_network_system_clock_core, system},
42 ephemeral_network_system_clock_core{tick_based_steady_clock_core},
43 local_system_clock_context_writer{
44 std::make_shared<Clock::LocalSystemClockContextWriter>(shared_memory)},
45 network_system_clock_context_writer{
46 std::make_shared<Clock::NetworkSystemClockContextWriter>(shared_memory)},
47 ephemeral_network_system_clock_context_writer{
48 std::make_shared<Clock::EphemeralNetworkSystemClockContextWriter>()},
49 time_zone_content_manager{*this, system} {
50
51 const auto system_time{Clock::TimeSpanType::FromSeconds(GetExternalRtcValue())};
52 SetupStandardSteadyClock(system, Common::UUID::Generate(), system_time, {}, {});
53 SetupStandardLocalSystemClock(system, {}, system_time.ToSeconds());
54 SetupStandardNetworkSystemClock({}, standard_network_clock_accuracy);
55 SetupStandardUserSystemClock(system, {}, Clock::SteadyClockTimePoint::GetRandom());
56 SetupEphemeralNetworkSystemClock();
57} 27}
58 28
59TimeManager::~TimeManager() = default; 29struct TimeManager::Impl final {
30 explicit Impl(Core::System& system)
31 : shared_memory{system}, standard_local_system_clock_core{standard_steady_clock_core},
32 standard_network_system_clock_core{standard_steady_clock_core},
33 standard_user_system_clock_core{standard_local_system_clock_core,
34 standard_network_system_clock_core, system},
35 ephemeral_network_system_clock_core{tick_based_steady_clock_core},
36 local_system_clock_context_writer{
37 std::make_shared<Clock::LocalSystemClockContextWriter>(shared_memory)},
38 network_system_clock_context_writer{
39 std::make_shared<Clock::NetworkSystemClockContextWriter>(shared_memory)},
40 ephemeral_network_system_clock_context_writer{
41 std::make_shared<Clock::EphemeralNetworkSystemClockContextWriter>()},
42 time_zone_content_manager{system} {
60 43
61void TimeManager::SetupTimeZoneManager(std::string location_name, 44 const auto system_time{Clock::TimeSpanType::FromSeconds(GetExternalRtcValue())};
62 Clock::SteadyClockTimePoint time_zone_updated_time_point, 45 SetupStandardSteadyClock(system, Common::UUID::Generate(), system_time, {}, {});
63 std::size_t total_location_name_count, 46 SetupStandardLocalSystemClock(system, {}, system_time.ToSeconds());
64 u128 time_zone_rule_version, 47 SetupStandardNetworkSystemClock({}, standard_network_clock_accuracy);
65 FileSys::VirtualFile& vfs_file) { 48 SetupStandardUserSystemClock(system, {}, Clock::SteadyClockTimePoint::GetRandom());
66 if (time_zone_content_manager.GetTimeZoneManager().SetDeviceLocationNameWithTimeZoneRule( 49 SetupEphemeralNetworkSystemClock();
67 location_name, vfs_file) != RESULT_SUCCESS) { 50 }
68 UNREACHABLE(); 51
69 return; 52 ~Impl() = default;
70 } 53
71 54 Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() {
72 time_zone_content_manager.GetTimeZoneManager().SetUpdatedTime(time_zone_updated_time_point); 55 return standard_steady_clock_core;
73 time_zone_content_manager.GetTimeZoneManager().SetTotalLocationNameCount( 56 }
74 total_location_name_count); 57
75 time_zone_content_manager.GetTimeZoneManager().SetTimeZoneRuleVersion(time_zone_rule_version); 58 const Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() const {
76 time_zone_content_manager.GetTimeZoneManager().MarkAsInitialized(); 59 return standard_steady_clock_core;
77} 60 }
78 61
79void TimeManager::SetupStandardSteadyClock(Core::System& system, Common::UUID clock_source_id, 62 Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() {
80 Clock::TimeSpanType setup_value, 63 return standard_local_system_clock_core;
81 Clock::TimeSpanType internal_offset, 64 }
82 bool is_rtc_reset_detected) { 65
83 standard_steady_clock_core.SetClockSourceId(clock_source_id); 66 const Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() const {
84 standard_steady_clock_core.SetSetupValue(setup_value); 67 return standard_local_system_clock_core;
85 standard_steady_clock_core.SetInternalOffset(internal_offset); 68 }
86 standard_steady_clock_core.MarkAsInitialized(); 69
87 70 Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() {
88 const auto current_time_point{standard_steady_clock_core.GetCurrentRawTimePoint(system)}; 71 return standard_network_system_clock_core;
89 shared_memory.SetupStandardSteadyClock(system, clock_source_id, current_time_point); 72 }
90} 73
91 74 const Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() const {
92void TimeManager::SetupStandardLocalSystemClock(Core::System& system, 75 return standard_network_system_clock_core;
93 Clock::SystemClockContext clock_context, 76 }
94 s64 posix_time) { 77
95 standard_local_system_clock_core.SetUpdateCallbackInstance(local_system_clock_context_writer); 78 Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() {
96 79 return standard_user_system_clock_core;
97 const auto current_time_point{ 80 }
98 standard_local_system_clock_core.GetSteadyClockCore().GetCurrentTimePoint(system)}; 81
99 if (current_time_point.clock_source_id == clock_context.steady_time_point.clock_source_id) { 82 const Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() const {
100 standard_local_system_clock_core.SetSystemClockContext(clock_context); 83 return standard_user_system_clock_core;
101 } else { 84 }
102 if (standard_local_system_clock_core.SetCurrentTime(system, posix_time) != RESULT_SUCCESS) { 85
86 TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() {
87 return time_zone_content_manager;
88 }
89
90 const TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() const {
91 return time_zone_content_manager;
92 }
93
94 SharedMemory& GetSharedMemory() {
95 return shared_memory;
96 }
97
98 const SharedMemory& GetSharedMemory() const {
99 return shared_memory;
100 }
101
102 void SetupTimeZoneManager(std::string location_name,
103 Clock::SteadyClockTimePoint time_zone_updated_time_point,
104 std::size_t total_location_name_count, u128 time_zone_rule_version,
105 FileSys::VirtualFile& vfs_file) {
106 if (time_zone_content_manager.GetTimeZoneManager().SetDeviceLocationNameWithTimeZoneRule(
107 location_name, vfs_file) != RESULT_SUCCESS) {
103 UNREACHABLE(); 108 UNREACHABLE();
104 return; 109 return;
105 } 110 }
111
112 time_zone_content_manager.GetTimeZoneManager().SetUpdatedTime(time_zone_updated_time_point);
113 time_zone_content_manager.GetTimeZoneManager().SetTotalLocationNameCount(
114 total_location_name_count);
115 time_zone_content_manager.GetTimeZoneManager().SetTimeZoneRuleVersion(
116 time_zone_rule_version);
117 time_zone_content_manager.GetTimeZoneManager().MarkAsInitialized();
106 } 118 }
107 119
108 standard_local_system_clock_core.MarkAsInitialized(); 120 static s64 GetExternalTimeZoneOffset() {
109} 121 // With "auto" timezone setting, we use the external system's timezone offset
122 if (Settings::GetTimeZoneString() == "auto") {
123 return Common::TimeZone::GetCurrentOffsetSeconds().count();
124 }
125 return 0;
126 }
110 127
111void TimeManager::SetupStandardNetworkSystemClock(Clock::SystemClockContext clock_context, 128 void SetupStandardSteadyClock(Core::System& system, Common::UUID clock_source_id,
112 Clock::TimeSpanType sufficient_accuracy) { 129 Clock::TimeSpanType setup_value,
113 standard_network_system_clock_core.SetUpdateCallbackInstance( 130 Clock::TimeSpanType internal_offset, bool is_rtc_reset_detected) {
114 network_system_clock_context_writer); 131 standard_steady_clock_core.SetClockSourceId(clock_source_id);
132 standard_steady_clock_core.SetSetupValue(setup_value);
133 standard_steady_clock_core.SetInternalOffset(internal_offset);
134 standard_steady_clock_core.MarkAsInitialized();
115 135
116 if (standard_network_system_clock_core.SetSystemClockContext(clock_context) != RESULT_SUCCESS) { 136 const auto current_time_point{standard_steady_clock_core.GetCurrentRawTimePoint(system)};
117 UNREACHABLE(); 137 shared_memory.SetupStandardSteadyClock(system, clock_source_id, current_time_point);
118 return;
119 } 138 }
120 139
121 standard_network_system_clock_core.SetStandardNetworkClockSufficientAccuracy( 140 void SetupStandardLocalSystemClock(Core::System& system,
122 sufficient_accuracy); 141 Clock::SystemClockContext clock_context, s64 posix_time) {
123 standard_network_system_clock_core.MarkAsInitialized(); 142 standard_local_system_clock_core.SetUpdateCallbackInstance(
124} 143 local_system_clock_context_writer);
144
145 const auto current_time_point{
146 standard_local_system_clock_core.GetSteadyClockCore().GetCurrentTimePoint(system)};
147 if (current_time_point.clock_source_id == clock_context.steady_time_point.clock_source_id) {
148 standard_local_system_clock_core.SetSystemClockContext(clock_context);
149 } else {
150 if (standard_local_system_clock_core.SetCurrentTime(system, posix_time) !=
151 RESULT_SUCCESS) {
152 UNREACHABLE();
153 return;
154 }
155 }
156
157 standard_local_system_clock_core.MarkAsInitialized();
158 }
159
160 void SetupStandardNetworkSystemClock(Clock::SystemClockContext clock_context,
161 Clock::TimeSpanType sufficient_accuracy) {
162 standard_network_system_clock_core.SetUpdateCallbackInstance(
163 network_system_clock_context_writer);
125 164
126void TimeManager::SetupStandardUserSystemClock( 165 if (standard_network_system_clock_core.SetSystemClockContext(clock_context) !=
127 Core::System& system, bool is_automatic_correction_enabled, 166 RESULT_SUCCESS) {
128 Clock::SteadyClockTimePoint steady_clock_time_point) { 167 UNREACHABLE();
129 if (standard_user_system_clock_core.SetAutomaticCorrectionEnabled( 168 return;
130 system, is_automatic_correction_enabled) != RESULT_SUCCESS) { 169 }
131 UNREACHABLE(); 170
132 return; 171 standard_network_system_clock_core.SetStandardNetworkClockSufficientAccuracy(
172 sufficient_accuracy);
173 standard_network_system_clock_core.MarkAsInitialized();
133 } 174 }
134 175
135 standard_user_system_clock_core.SetAutomaticCorrectionUpdatedTime(steady_clock_time_point); 176 void SetupStandardUserSystemClock(Core::System& system, bool is_automatic_correction_enabled,
136 standard_user_system_clock_core.MarkAsInitialized(); 177 Clock::SteadyClockTimePoint steady_clock_time_point) {
137 shared_memory.SetAutomaticCorrectionEnabled(is_automatic_correction_enabled); 178 if (standard_user_system_clock_core.SetAutomaticCorrectionEnabled(
179 system, is_automatic_correction_enabled) != RESULT_SUCCESS) {
180 UNREACHABLE();
181 return;
182 }
183
184 standard_user_system_clock_core.SetAutomaticCorrectionUpdatedTime(steady_clock_time_point);
185 standard_user_system_clock_core.MarkAsInitialized();
186 shared_memory.SetAutomaticCorrectionEnabled(is_automatic_correction_enabled);
187 }
188
189 void SetupEphemeralNetworkSystemClock() {
190 ephemeral_network_system_clock_core.SetUpdateCallbackInstance(
191 ephemeral_network_system_clock_context_writer);
192 ephemeral_network_system_clock_core.MarkAsInitialized();
193 }
194
195 void UpdateLocalSystemClockTime(Core::System& system, s64 posix_time) {
196 const auto timespan{Service::Time::Clock::TimeSpanType::FromSeconds(posix_time)};
197 if (GetStandardLocalSystemClockCore()
198 .SetCurrentTime(system, timespan.ToSeconds())
199 .IsError()) {
200 UNREACHABLE();
201 return;
202 }
203 }
204
205 SharedMemory shared_memory;
206
207 Clock::StandardSteadyClockCore standard_steady_clock_core;
208 Clock::TickBasedSteadyClockCore tick_based_steady_clock_core;
209 Clock::StandardLocalSystemClockCore standard_local_system_clock_core;
210 Clock::StandardNetworkSystemClockCore standard_network_system_clock_core;
211 Clock::StandardUserSystemClockCore standard_user_system_clock_core;
212 Clock::EphemeralNetworkSystemClockCore ephemeral_network_system_clock_core;
213
214 std::shared_ptr<Clock::LocalSystemClockContextWriter> local_system_clock_context_writer;
215 std::shared_ptr<Clock::NetworkSystemClockContextWriter> network_system_clock_context_writer;
216 std::shared_ptr<Clock::EphemeralNetworkSystemClockContextWriter>
217 ephemeral_network_system_clock_context_writer;
218
219 TimeZone::TimeZoneContentManager time_zone_content_manager;
220};
221
222TimeManager::TimeManager(Core::System& system) : system{system} {}
223
224TimeManager::~TimeManager() = default;
225
226void TimeManager::Initialize() {
227 impl = std::make_unique<Impl>(system);
228
229 // Time zones can only be initialized after impl is valid
230 impl->time_zone_content_manager.Initialize(*this);
231}
232
233Clock::StandardSteadyClockCore& TimeManager::GetStandardSteadyClockCore() {
234 return impl->standard_steady_clock_core;
235}
236
237const Clock::StandardSteadyClockCore& TimeManager::GetStandardSteadyClockCore() const {
238 return impl->standard_steady_clock_core;
239}
240
241Clock::StandardLocalSystemClockCore& TimeManager::GetStandardLocalSystemClockCore() {
242 return impl->standard_local_system_clock_core;
243}
244
245const Clock::StandardLocalSystemClockCore& TimeManager::GetStandardLocalSystemClockCore() const {
246 return impl->standard_local_system_clock_core;
247}
248
249Clock::StandardNetworkSystemClockCore& TimeManager::GetStandardNetworkSystemClockCore() {
250 return impl->standard_network_system_clock_core;
138} 251}
139 252
140void TimeManager::SetupEphemeralNetworkSystemClock() { 253const Clock::StandardNetworkSystemClockCore& TimeManager::GetStandardNetworkSystemClockCore()
141 ephemeral_network_system_clock_core.SetUpdateCallbackInstance( 254 const {
142 ephemeral_network_system_clock_context_writer); 255 return impl->standard_network_system_clock_core;
143 ephemeral_network_system_clock_core.MarkAsInitialized(); 256}
257
258Clock::StandardUserSystemClockCore& TimeManager::GetStandardUserSystemClockCore() {
259 return impl->standard_user_system_clock_core;
260}
261
262const Clock::StandardUserSystemClockCore& TimeManager::GetStandardUserSystemClockCore() const {
263 return impl->standard_user_system_clock_core;
264}
265
266TimeZone::TimeZoneContentManager& TimeManager::GetTimeZoneContentManager() {
267 return impl->time_zone_content_manager;
268}
269
270const TimeZone::TimeZoneContentManager& TimeManager::GetTimeZoneContentManager() const {
271 return impl->time_zone_content_manager;
272}
273
274SharedMemory& TimeManager::GetSharedMemory() {
275 return impl->shared_memory;
276}
277
278const SharedMemory& TimeManager::GetSharedMemory() const {
279 return impl->shared_memory;
280}
281
282void TimeManager::UpdateLocalSystemClockTime(s64 posix_time) {
283 impl->UpdateLocalSystemClockTime(system, posix_time);
284}
285
286void TimeManager::SetupTimeZoneManager(std::string location_name,
287 Clock::SteadyClockTimePoint time_zone_updated_time_point,
288 std::size_t total_location_name_count,
289 u128 time_zone_rule_version,
290 FileSys::VirtualFile& vfs_file) {
291 impl->SetupTimeZoneManager(location_name, time_zone_updated_time_point,
292 total_location_name_count, time_zone_rule_version, vfs_file);
293}
294
295/*static*/ s64 TimeManager::GetExternalTimeZoneOffset() {
296 // With "auto" timezone setting, we use the external system's timezone offset
297 if (Settings::GetTimeZoneString() == "auto") {
298 return Common::TimeZone::GetCurrentOffsetSeconds().count();
299 }
300 return 0;
144} 301}
145 302
146} // namespace Service::Time 303} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_manager.h b/src/core/hle/service/time/time_manager.h
index 8e65f0d22..993c7c288 100644
--- a/src/core/hle/service/time/time_manager.h
+++ b/src/core/hle/service/time/time_manager.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "common/time_zone.h"
8#include "core/file_sys/vfs_types.h" 9#include "core/file_sys/vfs_types.h"
9#include "core/hle/service/time/clock_types.h" 10#include "core/hle/service/time/clock_types.h"
10#include "core/hle/service/time/ephemeral_network_system_clock_core.h" 11#include "core/hle/service/time/ephemeral_network_system_clock_core.h"
@@ -32,86 +33,46 @@ public:
32 explicit TimeManager(Core::System& system); 33 explicit TimeManager(Core::System& system);
33 ~TimeManager(); 34 ~TimeManager();
34 35
35 Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() { 36 void Initialize();
36 return standard_steady_clock_core;
37 }
38 37
39 const Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() const { 38 Clock::StandardSteadyClockCore& GetStandardSteadyClockCore();
40 return standard_steady_clock_core;
41 }
42 39
43 Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() { 40 const Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() const;
44 return standard_local_system_clock_core;
45 }
46 41
47 const Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() const { 42 Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore();
48 return standard_local_system_clock_core;
49 }
50 43
51 Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() { 44 const Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() const;
52 return standard_network_system_clock_core;
53 }
54 45
55 const Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() const { 46 Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore();
56 return standard_network_system_clock_core;
57 }
58 47
59 Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() { 48 const Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() const;
60 return standard_user_system_clock_core;
61 }
62 49
63 const Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() const { 50 Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore();
64 return standard_user_system_clock_core;
65 }
66 51
67 TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() { 52 const Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() const;
68 return time_zone_content_manager;
69 }
70 53
71 const TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() const { 54 TimeZone::TimeZoneContentManager& GetTimeZoneContentManager();
72 return time_zone_content_manager;
73 }
74 55
75 SharedMemory& GetSharedMemory() { 56 const TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() const;
76 return shared_memory;
77 }
78 57
79 const SharedMemory& GetSharedMemory() const { 58 void UpdateLocalSystemClockTime(s64 posix_time);
80 return shared_memory; 59
81 } 60 SharedMemory& GetSharedMemory();
61
62 const SharedMemory& GetSharedMemory() const;
82 63
83 void SetupTimeZoneManager(std::string location_name, 64 void SetupTimeZoneManager(std::string location_name,
84 Clock::SteadyClockTimePoint time_zone_updated_time_point, 65 Clock::SteadyClockTimePoint time_zone_updated_time_point,
85 std::size_t total_location_name_count, u128 time_zone_rule_version, 66 std::size_t total_location_name_count, u128 time_zone_rule_version,
86 FileSys::VirtualFile& vfs_file); 67 FileSys::VirtualFile& vfs_file);
87 68
69 static s64 GetExternalTimeZoneOffset();
70
88private: 71private:
89 void SetupStandardSteadyClock(Core::System& system, Common::UUID clock_source_id, 72 Core::System& system;
90 Clock::TimeSpanType setup_value, 73
91 Clock::TimeSpanType internal_offset, bool is_rtc_reset_detected); 74 struct Impl;
92 void SetupStandardLocalSystemClock(Core::System& system, 75 std::unique_ptr<Impl> impl;
93 Clock::SystemClockContext clock_context, s64 posix_time);
94 void SetupStandardNetworkSystemClock(Clock::SystemClockContext clock_context,
95 Clock::TimeSpanType sufficient_accuracy);
96 void SetupStandardUserSystemClock(Core::System& system, bool is_automatic_correction_enabled,
97 Clock::SteadyClockTimePoint steady_clock_time_point);
98 void SetupEphemeralNetworkSystemClock();
99
100 SharedMemory shared_memory;
101
102 Clock::StandardSteadyClockCore standard_steady_clock_core;
103 Clock::TickBasedSteadyClockCore tick_based_steady_clock_core;
104 Clock::StandardLocalSystemClockCore standard_local_system_clock_core;
105 Clock::StandardNetworkSystemClockCore standard_network_system_clock_core;
106 Clock::StandardUserSystemClockCore standard_user_system_clock_core;
107 Clock::EphemeralNetworkSystemClockCore ephemeral_network_system_clock_core;
108
109 std::shared_ptr<Clock::LocalSystemClockContextWriter> local_system_clock_context_writer;
110 std::shared_ptr<Clock::NetworkSystemClockContextWriter> network_system_clock_context_writer;
111 std::shared_ptr<Clock::EphemeralNetworkSystemClockContextWriter>
112 ephemeral_network_system_clock_context_writer;
113
114 TimeZone::TimeZoneContentManager time_zone_content_manager;
115}; 76};
116 77
117} // namespace Service::Time 78} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_zone_content_manager.cpp b/src/core/hle/service/time/time_zone_content_manager.cpp
index 320672add..4177d0a41 100644
--- a/src/core/hle/service/time/time_zone_content_manager.cpp
+++ b/src/core/hle/service/time/time_zone_content_manager.cpp
@@ -68,9 +68,10 @@ static std::vector<std::string> BuildLocationNameCache(Core::System& system) {
68 return location_name_cache; 68 return location_name_cache;
69} 69}
70 70
71TimeZoneContentManager::TimeZoneContentManager(TimeManager& time_manager, Core::System& system) 71TimeZoneContentManager::TimeZoneContentManager(Core::System& system)
72 : system{system}, location_name_cache{BuildLocationNameCache(system)} { 72 : system{system}, location_name_cache{BuildLocationNameCache(system)} {}
73 73
74void TimeZoneContentManager::Initialize(TimeManager& time_manager) {
74 std::string location_name; 75 std::string location_name;
75 const auto timezone_setting = Settings::GetTimeZoneString(); 76 const auto timezone_setting = Settings::GetTimeZoneString();
76 if (timezone_setting == "auto" || timezone_setting == "default") { 77 if (timezone_setting == "auto" || timezone_setting == "default") {
diff --git a/src/core/hle/service/time/time_zone_content_manager.h b/src/core/hle/service/time/time_zone_content_manager.h
index 4f302c3b9..52dd1a020 100644
--- a/src/core/hle/service/time/time_zone_content_manager.h
+++ b/src/core/hle/service/time/time_zone_content_manager.h
@@ -21,7 +21,9 @@ namespace Service::Time::TimeZone {
21 21
22class TimeZoneContentManager final { 22class TimeZoneContentManager final {
23public: 23public:
24 TimeZoneContentManager(TimeManager& time_manager, Core::System& system); 24 explicit TimeZoneContentManager(Core::System& system);
25
26 void Initialize(TimeManager& time_manager);
25 27
26 TimeZoneManager& GetTimeZoneManager() { 28 TimeZoneManager& GetTimeZoneManager() {
27 return time_zone_manager; 29 return time_zone_manager;
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 28d3f9099..e14c02045 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -63,6 +63,7 @@ void LogSettings() {
63 log_setting("Renderer_GPUAccuracyLevel", values.gpu_accuracy.GetValue()); 63 log_setting("Renderer_GPUAccuracyLevel", values.gpu_accuracy.GetValue());
64 log_setting("Renderer_UseAsynchronousGpuEmulation", 64 log_setting("Renderer_UseAsynchronousGpuEmulation",
65 values.use_asynchronous_gpu_emulation.GetValue()); 65 values.use_asynchronous_gpu_emulation.GetValue());
66 log_setting("Renderer_UseNvdecEmulation", values.use_nvdec_emulation.GetValue());
66 log_setting("Renderer_UseVsync", values.use_vsync.GetValue()); 67 log_setting("Renderer_UseVsync", values.use_vsync.GetValue());
67 log_setting("Renderer_UseAssemblyShaders", values.use_assembly_shaders.GetValue()); 68 log_setting("Renderer_UseAssemblyShaders", values.use_assembly_shaders.GetValue());
68 log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue()); 69 log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
@@ -119,6 +120,7 @@ void RestoreGlobalState() {
119 values.use_disk_shader_cache.SetGlobal(true); 120 values.use_disk_shader_cache.SetGlobal(true);
120 values.gpu_accuracy.SetGlobal(true); 121 values.gpu_accuracy.SetGlobal(true);
121 values.use_asynchronous_gpu_emulation.SetGlobal(true); 122 values.use_asynchronous_gpu_emulation.SetGlobal(true);
123 values.use_nvdec_emulation.SetGlobal(true);
122 values.use_vsync.SetGlobal(true); 124 values.use_vsync.SetGlobal(true);
123 values.use_assembly_shaders.SetGlobal(true); 125 values.use_assembly_shaders.SetGlobal(true);
124 values.use_asynchronous_shaders.SetGlobal(true); 126 values.use_asynchronous_shaders.SetGlobal(true);
diff --git a/src/core/settings.h b/src/core/settings.h
index 9834f44bb..604805615 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -111,6 +111,7 @@ struct Values {
111 Setting<bool> use_disk_shader_cache; 111 Setting<bool> use_disk_shader_cache;
112 Setting<GPUAccuracy> gpu_accuracy; 112 Setting<GPUAccuracy> gpu_accuracy;
113 Setting<bool> use_asynchronous_gpu_emulation; 113 Setting<bool> use_asynchronous_gpu_emulation;
114 Setting<bool> use_nvdec_emulation;
114 Setting<bool> use_vsync; 115 Setting<bool> use_vsync;
115 Setting<bool> use_assembly_shaders; 116 Setting<bool> use_assembly_shaders;
116 Setting<bool> use_asynchronous_shaders; 117 Setting<bool> use_asynchronous_shaders;
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index da09c0dbc..ebc19e18a 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -206,6 +206,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
206 TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy.GetValue())); 206 TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy.GetValue()));
207 AddField(field_type, "Renderer_UseAsynchronousGpuEmulation", 207 AddField(field_type, "Renderer_UseAsynchronousGpuEmulation",
208 Settings::values.use_asynchronous_gpu_emulation.GetValue()); 208 Settings::values.use_asynchronous_gpu_emulation.GetValue());
209 AddField(field_type, "Renderer_UseNvdecEmulation",
210 Settings::values.use_nvdec_emulation.GetValue());
209 AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue()); 211 AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue());
210 AddField(field_type, "Renderer_UseAssemblyShaders", 212 AddField(field_type, "Renderer_UseAssemblyShaders",
211 Settings::values.use_assembly_shaders.GetValue()); 213 Settings::values.use_assembly_shaders.GetValue());
diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp
index c95feb0d7..b912188b6 100644
--- a/src/input_common/gcadapter/gc_adapter.cpp
+++ b/src/input_common/gcadapter/gc_adapter.cpp
@@ -21,26 +21,6 @@
21 21
22namespace GCAdapter { 22namespace GCAdapter {
23 23
24// Used to loop through and assign button in poller
25constexpr std::array<PadButton, 12> PadButtonArray{
26 PadButton::PAD_BUTTON_LEFT, PadButton::PAD_BUTTON_RIGHT, PadButton::PAD_BUTTON_DOWN,
27 PadButton::PAD_BUTTON_UP, PadButton::PAD_TRIGGER_Z, PadButton::PAD_TRIGGER_R,
28 PadButton::PAD_TRIGGER_L, PadButton::PAD_BUTTON_A, PadButton::PAD_BUTTON_B,
29 PadButton::PAD_BUTTON_X, PadButton::PAD_BUTTON_Y, PadButton::PAD_BUTTON_START,
30};
31
32static void PadToState(const GCPadStatus& pad, GCState& out_state) {
33 for (const auto& button : PadButtonArray) {
34 const auto button_key = static_cast<u16>(button);
35 const auto button_value = (pad.button & button_key) != 0;
36 out_state.buttons.insert_or_assign(static_cast<s32>(button_key), button_value);
37 }
38
39 for (std::size_t i = 0; i < pad.axis_values.size(); ++i) {
40 out_state.axes.insert_or_assign(static_cast<u32>(i), pad.axis_values[i]);
41 }
42}
43
44Adapter::Adapter() { 24Adapter::Adapter() {
45 if (usb_adapter_handle != nullptr) { 25 if (usb_adapter_handle != nullptr) {
46 return; 26 return;
@@ -49,168 +29,263 @@ Adapter::Adapter() {
49 29
50 const int init_res = libusb_init(&libusb_ctx); 30 const int init_res = libusb_init(&libusb_ctx);
51 if (init_res == LIBUSB_SUCCESS) { 31 if (init_res == LIBUSB_SUCCESS) {
52 Setup(); 32 adapter_scan_thread = std::thread(&Adapter::AdapterScanThread, this);
53 } else { 33 } else {
54 LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res); 34 LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res);
55 } 35 }
56} 36}
57 37
58GCPadStatus Adapter::GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload) { 38Adapter::~Adapter() {
59 GCPadStatus pad = {}; 39 Reset();
60 const std::size_t offset = 1 + (9 * port); 40}
41
42void Adapter::AdapterInputThread() {
43 LOG_DEBUG(Input, "GC Adapter input thread started");
44 s32 payload_size{};
45 AdapterPayload adapter_payload{};
46
47 if (adapter_scan_thread.joinable()) {
48 adapter_scan_thread.join();
49 }
50
51 while (adapter_input_thread_running) {
52 libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(),
53 static_cast<s32>(adapter_payload.size()), &payload_size, 16);
54 if (IsPayloadCorrect(adapter_payload, payload_size)) {
55 UpdateControllers(adapter_payload);
56 UpdateVibrations();
57 }
58 std::this_thread::yield();
59 }
61 60
62 adapter_controllers_status[port] = static_cast<ControllerTypes>(adapter_payload[offset] >> 4); 61 if (restart_scan_thread) {
62 adapter_scan_thread = std::thread(&Adapter::AdapterScanThread, this);
63 restart_scan_thread = false;
64 }
65}
66
67bool Adapter::IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size) {
68 if (payload_size != static_cast<s32>(adapter_payload.size()) ||
69 adapter_payload[0] != LIBUSB_DT_HID) {
70 LOG_DEBUG(Input, "Error reading payload (size: {}, type: {:02x})", payload_size,
71 adapter_payload[0]);
72 if (input_error_counter++ > 20) {
73 LOG_ERROR(Input, "GC adapter timeout, Is the adapter connected?");
74 adapter_input_thread_running = false;
75 restart_scan_thread = true;
76 }
77 return false;
78 }
79
80 input_error_counter = 0;
81 return true;
82}
83
84void Adapter::UpdateControllers(const AdapterPayload& adapter_payload) {
85 for (std::size_t port = 0; port < pads.size(); ++port) {
86 const std::size_t offset = 1 + (9 * port);
87 const auto type = static_cast<ControllerTypes>(adapter_payload[offset] >> 4);
88 UpdatePadType(port, type);
89 if (DeviceConnected(port)) {
90 const u8 b1 = adapter_payload[offset + 1];
91 const u8 b2 = adapter_payload[offset + 2];
92 UpdateStateButtons(port, b1, b2);
93 UpdateStateAxes(port, adapter_payload);
94 if (configuring) {
95 UpdateYuzuSettings(port);
96 }
97 }
98 }
99}
100
101void Adapter::UpdatePadType(std::size_t port, ControllerTypes pad_type) {
102 if (pads[port].type == pad_type) {
103 return;
104 }
105 // Device changed reset device and set new type
106 ResetDevice(port);
107 pads[port].type = pad_type;
108}
109
110void Adapter::UpdateStateButtons(std::size_t port, u8 b1, u8 b2) {
111 if (port >= pads.size()) {
112 return;
113 }
63 114
64 static constexpr std::array<PadButton, 8> b1_buttons{ 115 static constexpr std::array<PadButton, 8> b1_buttons{
65 PadButton::PAD_BUTTON_A, PadButton::PAD_BUTTON_B, PadButton::PAD_BUTTON_X, 116 PadButton::ButtonA, PadButton::ButtonB, PadButton::ButtonX, PadButton::ButtonY,
66 PadButton::PAD_BUTTON_Y, PadButton::PAD_BUTTON_LEFT, PadButton::PAD_BUTTON_RIGHT, 117 PadButton::ButtonLeft, PadButton::ButtonRight, PadButton::ButtonDown, PadButton::ButtonUp,
67 PadButton::PAD_BUTTON_DOWN, PadButton::PAD_BUTTON_UP,
68 }; 118 };
69 119
70 static constexpr std::array<PadButton, 4> b2_buttons{ 120 static constexpr std::array<PadButton, 4> b2_buttons{
71 PadButton::PAD_BUTTON_START, 121 PadButton::ButtonStart,
72 PadButton::PAD_TRIGGER_Z, 122 PadButton::TriggerZ,
73 PadButton::PAD_TRIGGER_R, 123 PadButton::TriggerR,
74 PadButton::PAD_TRIGGER_L, 124 PadButton::TriggerL,
75 }; 125 };
126 pads[port].buttons = 0;
127 for (std::size_t i = 0; i < b1_buttons.size(); ++i) {
128 if ((b1 & (1U << i)) != 0) {
129 pads[port].buttons =
130 static_cast<u16>(pads[port].buttons | static_cast<u16>(b1_buttons[i]));
131 pads[port].last_button = b1_buttons[i];
132 }
133 }
76 134
135 for (std::size_t j = 0; j < b2_buttons.size(); ++j) {
136 if ((b2 & (1U << j)) != 0) {
137 pads[port].buttons =
138 static_cast<u16>(pads[port].buttons | static_cast<u16>(b2_buttons[j]));
139 pads[port].last_button = b2_buttons[j];
140 }
141 }
142}
143
144void Adapter::UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload) {
145 if (port >= pads.size()) {
146 return;
147 }
148
149 const std::size_t offset = 1 + (9 * port);
77 static constexpr std::array<PadAxes, 6> axes{ 150 static constexpr std::array<PadAxes, 6> axes{
78 PadAxes::StickX, PadAxes::StickY, PadAxes::SubstickX, 151 PadAxes::StickX, PadAxes::StickY, PadAxes::SubstickX,
79 PadAxes::SubstickY, PadAxes::TriggerLeft, PadAxes::TriggerRight, 152 PadAxes::SubstickY, PadAxes::TriggerLeft, PadAxes::TriggerRight,
80 }; 153 };
81 154
82 if (adapter_controllers_status[port] == ControllerTypes::None && !get_origin[port]) { 155 for (const PadAxes axis : axes) {
83 // Controller may have been disconnected, recalibrate if reconnected. 156 const auto index = static_cast<std::size_t>(axis);
84 get_origin[port] = true; 157 const u8 axis_value = adapter_payload[offset + 3 + index];
158 if (pads[port].axis_origin[index] == 255) {
159 pads[port].axis_origin[index] = axis_value;
160 }
161 pads[port].axis_values[index] =
162 static_cast<s16>(axis_value - pads[port].axis_origin[index]);
85 } 163 }
164}
86 165
87 if (adapter_controllers_status[port] != ControllerTypes::None) { 166void Adapter::UpdateYuzuSettings(std::size_t port) {
88 const u8 b1 = adapter_payload[offset + 1]; 167 if (port >= pads.size()) {
89 const u8 b2 = adapter_payload[offset + 2]; 168 return;
169 }
90 170
91 for (std::size_t i = 0; i < b1_buttons.size(); ++i) { 171 constexpr u8 axis_threshold = 50;
92 if ((b1 & (1U << i)) != 0) { 172 GCPadStatus pad_status = {.port = port};
93 pad.button = static_cast<u16>(pad.button | static_cast<u16>(b1_buttons[i]));
94 }
95 }
96 173
97 for (std::size_t j = 0; j < b2_buttons.size(); ++j) { 174 if (pads[port].buttons != 0) {
98 if ((b2 & (1U << j)) != 0) { 175 pad_status.button = pads[port].last_button;
99 pad.button = static_cast<u16>(pad.button | static_cast<u16>(b2_buttons[j])); 176 pad_queue.Push(pad_status);
100 } 177 }
101 } 178
102 for (PadAxes axis : axes) { 179 // Accounting for a threshold here to ensure an intentional press
103 const auto index = static_cast<std::size_t>(axis); 180 for (std::size_t i = 0; i < pads[port].axis_values.size(); ++i) {
104 pad.axis_values[index] = adapter_payload[offset + 3 + index]; 181 const s16 value = pads[port].axis_values[i];
105 }
106 182
107 if (get_origin[port]) { 183 if (value > axis_threshold || value < -axis_threshold) {
108 origin_status[port].axis_values = pad.axis_values; 184 pad_status.axis = static_cast<PadAxes>(i);
109 get_origin[port] = false; 185 pad_status.axis_value = value;
186 pad_status.axis_threshold = axis_threshold;
187 pad_queue.Push(pad_status);
110 } 188 }
111 } 189 }
112 return pad;
113} 190}
114 191
115void Adapter::Read() { 192void Adapter::UpdateVibrations() {
116 LOG_DEBUG(Input, "GC Adapter Read() thread started"); 193 // Use 8 states to keep the switching between on/off fast enough for
194 // a human to not notice the difference between switching from on/off
195 // More states = more rumble strengths = slower update time
196 constexpr u8 vibration_states = 8;
117 197
118 int payload_size; 198 vibration_counter = (vibration_counter + 1) % vibration_states;
119 std::array<u8, 37> adapter_payload;
120 std::array<GCPadStatus, 4> pads;
121
122 while (adapter_thread_running) {
123 libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(),
124 sizeof(adapter_payload), &payload_size, 16);
125
126 if (payload_size != sizeof(adapter_payload) || adapter_payload[0] != LIBUSB_DT_HID) {
127 LOG_ERROR(Input,
128 "Error reading payload (size: {}, type: {:02x}) Is the adapter connected?",
129 payload_size, adapter_payload[0]);
130 adapter_thread_running = false; // error reading from adapter, stop reading.
131 break;
132 }
133 for (std::size_t port = 0; port < pads.size(); ++port) {
134 pads[port] = GetPadStatus(port, adapter_payload);
135 if (DeviceConnected(port) && configuring) {
136 if (pads[port].button != 0) {
137 pad_queue[port].Push(pads[port]);
138 }
139 199
140 // Accounting for a threshold here to ensure an intentional press 200 for (GCController& pad : pads) {
141 for (size_t i = 0; i < pads[port].axis_values.size(); ++i) { 201 const bool vibrate = pad.rumble_amplitude > vibration_counter;
142 const u8 value = pads[port].axis_values[i]; 202 vibration_changed |= vibrate != pad.enable_vibration;
143 const u8 origin = origin_status[port].axis_values[i]; 203 pad.enable_vibration = vibrate;
144
145 if (value > origin + pads[port].THRESHOLD ||
146 value < origin - pads[port].THRESHOLD) {
147 pads[port].axis = static_cast<PadAxes>(i);
148 pads[port].axis_value = pads[port].axis_values[i];
149 pad_queue[port].Push(pads[port]);
150 }
151 }
152 }
153 PadToState(pads[port], state[port]);
154 }
155 std::this_thread::yield();
156 } 204 }
205 SendVibrations();
157} 206}
158 207
159void Adapter::Setup() { 208void Adapter::SendVibrations() {
160 // Initialize all controllers as unplugged 209 if (!rumble_enabled || !vibration_changed) {
161 adapter_controllers_status.fill(ControllerTypes::None);
162 // Initialize all ports to store axis origin values
163 get_origin.fill(true);
164
165 // pointer to list of connected usb devices
166 libusb_device** devices{};
167
168 // populate the list of devices, get the count
169 const ssize_t device_count = libusb_get_device_list(libusb_ctx, &devices);
170 if (device_count < 0) {
171 LOG_ERROR(Input, "libusb_get_device_list failed with error: {}", device_count);
172 return; 210 return;
173 } 211 }
174 212 s32 size{};
175 if (devices != nullptr) { 213 constexpr u8 rumble_command = 0x11;
176 for (std::size_t index = 0; index < static_cast<std::size_t>(device_count); ++index) { 214 const u8 p1 = pads[0].enable_vibration;
177 if (CheckDeviceAccess(devices[index])) { 215 const u8 p2 = pads[1].enable_vibration;
178 // GC Adapter found and accessible, registering it 216 const u8 p3 = pads[2].enable_vibration;
179 GetGCEndpoint(devices[index]); 217 const u8 p4 = pads[3].enable_vibration;
180 break; 218 std::array<u8, 5> payload = {rumble_command, p1, p2, p3, p4};
181 } 219 const int err = libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, payload.data(),
220 static_cast<s32>(payload.size()), &size, 16);
221 if (err) {
222 LOG_DEBUG(Input, "Adapter libusb write failed: {}", libusb_error_name(err));
223 if (output_error_counter++ > 5) {
224 LOG_ERROR(Input, "GC adapter output timeout, Rumble disabled");
225 rumble_enabled = false;
182 } 226 }
183 libusb_free_device_list(devices, 1); 227 return;
184 } 228 }
229 output_error_counter = 0;
230 vibration_changed = false;
185} 231}
186 232
187bool Adapter::CheckDeviceAccess(libusb_device* device) { 233bool Adapter::RumblePlay(std::size_t port, f32 amplitude) {
188 libusb_device_descriptor desc; 234 amplitude = std::clamp(amplitude, 0.0f, 1.0f);
189 const int get_descriptor_error = libusb_get_device_descriptor(device, &desc); 235 const auto raw_amp = static_cast<u8>(amplitude * 0x8);
190 if (get_descriptor_error) { 236 pads[port].rumble_amplitude = raw_amp;
191 // could not acquire the descriptor, no point in trying to use it. 237
192 LOG_ERROR(Input, "libusb_get_device_descriptor failed with error: {}", 238 return rumble_enabled;
193 get_descriptor_error); 239}
194 return false; 240
241void Adapter::AdapterScanThread() {
242 adapter_scan_thread_running = true;
243 adapter_input_thread_running = false;
244 if (adapter_input_thread.joinable()) {
245 adapter_input_thread.join();
246 }
247 ClearLibusbHandle();
248 ResetDevices();
249 while (adapter_scan_thread_running && !adapter_input_thread_running) {
250 Setup();
251 std::this_thread::sleep_for(std::chrono::seconds(1));
195 } 252 }
253}
196 254
197 if (desc.idVendor != 0x057e || desc.idProduct != 0x0337) { 255void Adapter::Setup() {
198 // This isn't the device we are looking for. 256 usb_adapter_handle = libusb_open_device_with_vid_pid(libusb_ctx, 0x057e, 0x0337);
199 return false; 257
258 if (usb_adapter_handle == NULL) {
259 return;
260 }
261 if (!CheckDeviceAccess()) {
262 ClearLibusbHandle();
263 return;
200 } 264 }
201 const int open_error = libusb_open(device, &usb_adapter_handle);
202 265
203 if (open_error == LIBUSB_ERROR_ACCESS) { 266 libusb_device* device = libusb_get_device(usb_adapter_handle);
204 LOG_ERROR(Input, "Yuzu can not gain access to this device: ID {:04X}:{:04X}.", 267
205 desc.idVendor, desc.idProduct); 268 LOG_INFO(Input, "GC adapter is now connected");
206 return false; 269 // GC Adapter found and accessible, registering it
270 if (GetGCEndpoint(device)) {
271 adapter_scan_thread_running = false;
272 adapter_input_thread_running = true;
273 rumble_enabled = true;
274 input_error_counter = 0;
275 output_error_counter = 0;
276 adapter_input_thread = std::thread(&Adapter::AdapterInputThread, this);
207 } 277 }
208 if (open_error) { 278}
209 LOG_ERROR(Input, "libusb_open failed to open device with error = {}", open_error); 279
210 return false; 280bool Adapter::CheckDeviceAccess() {
281 // This fixes payload problems from offbrand GCAdapters
282 const s32 control_transfer_error =
283 libusb_control_transfer(usb_adapter_handle, 0x21, 11, 0x0001, 0, nullptr, 0, 1000);
284 if (control_transfer_error < 0) {
285 LOG_ERROR(Input, "libusb_control_transfer failed with error= {}", control_transfer_error);
211 } 286 }
212 287
213 int kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle, 0); 288 s32 kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle, 0);
214 if (kernel_driver_error == 1) { 289 if (kernel_driver_error == 1) {
215 kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle, 0); 290 kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle, 0);
216 if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) { 291 if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) {
@@ -236,13 +311,13 @@ bool Adapter::CheckDeviceAccess(libusb_device* device) {
236 return true; 311 return true;
237} 312}
238 313
239void Adapter::GetGCEndpoint(libusb_device* device) { 314bool Adapter::GetGCEndpoint(libusb_device* device) {
240 libusb_config_descriptor* config = nullptr; 315 libusb_config_descriptor* config = nullptr;
241 const int config_descriptor_return = libusb_get_config_descriptor(device, 0, &config); 316 const int config_descriptor_return = libusb_get_config_descriptor(device, 0, &config);
242 if (config_descriptor_return != LIBUSB_SUCCESS) { 317 if (config_descriptor_return != LIBUSB_SUCCESS) {
243 LOG_ERROR(Input, "libusb_get_config_descriptor failed with error = {}", 318 LOG_ERROR(Input, "libusb_get_config_descriptor failed with error = {}",
244 config_descriptor_return); 319 config_descriptor_return);
245 return; 320 return false;
246 } 321 }
247 322
248 for (u8 ic = 0; ic < config->bNumInterfaces; ic++) { 323 for (u8 ic = 0; ic < config->bNumInterfaces; ic++) {
@@ -264,31 +339,51 @@ void Adapter::GetGCEndpoint(libusb_device* device) {
264 unsigned char clear_payload = 0x13; 339 unsigned char clear_payload = 0x13;
265 libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, &clear_payload, 340 libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, &clear_payload,
266 sizeof(clear_payload), nullptr, 16); 341 sizeof(clear_payload), nullptr, 16);
267 342 return true;
268 adapter_thread_running = true;
269 adapter_input_thread = std::thread(&Adapter::Read, this);
270} 343}
271 344
272Adapter::~Adapter() { 345void Adapter::JoinThreads() {
273 Reset(); 346 restart_scan_thread = false;
274} 347 adapter_input_thread_running = false;
348 adapter_scan_thread_running = false;
275 349
276void Adapter::Reset() { 350 if (adapter_scan_thread.joinable()) {
277 if (adapter_thread_running) { 351 adapter_scan_thread.join();
278 adapter_thread_running = false;
279 } 352 }
353
280 if (adapter_input_thread.joinable()) { 354 if (adapter_input_thread.joinable()) {
281 adapter_input_thread.join(); 355 adapter_input_thread.join();
282 } 356 }
357}
283 358
284 adapter_controllers_status.fill(ControllerTypes::None); 359void Adapter::ClearLibusbHandle() {
285 get_origin.fill(true);
286
287 if (usb_adapter_handle) { 360 if (usb_adapter_handle) {
288 libusb_release_interface(usb_adapter_handle, 1); 361 libusb_release_interface(usb_adapter_handle, 1);
289 libusb_close(usb_adapter_handle); 362 libusb_close(usb_adapter_handle);
290 usb_adapter_handle = nullptr; 363 usb_adapter_handle = nullptr;
291 } 364 }
365}
366
367void Adapter::ResetDevices() {
368 for (std::size_t i = 0; i < pads.size(); ++i) {
369 ResetDevice(i);
370 }
371}
372
373void Adapter::ResetDevice(std::size_t port) {
374 pads[port].type = ControllerTypes::None;
375 pads[port].enable_vibration = false;
376 pads[port].rumble_amplitude = 0;
377 pads[port].buttons = 0;
378 pads[port].last_button = PadButton::Undefined;
379 pads[port].axis_values.fill(0);
380 pads[port].axis_origin.fill(255);
381}
382
383void Adapter::Reset() {
384 JoinThreads();
385 ClearLibusbHandle();
386 ResetDevices();
292 387
293 if (libusb_ctx) { 388 if (libusb_ctx) {
294 libusb_exit(libusb_ctx); 389 libusb_exit(libusb_ctx);
@@ -297,11 +392,11 @@ void Adapter::Reset() {
297 392
298std::vector<Common::ParamPackage> Adapter::GetInputDevices() const { 393std::vector<Common::ParamPackage> Adapter::GetInputDevices() const {
299 std::vector<Common::ParamPackage> devices; 394 std::vector<Common::ParamPackage> devices;
300 for (std::size_t port = 0; port < state.size(); ++port) { 395 for (std::size_t port = 0; port < pads.size(); ++port) {
301 if (!DeviceConnected(port)) { 396 if (!DeviceConnected(port)) {
302 continue; 397 continue;
303 } 398 }
304 std::string name = fmt::format("Gamecube Controller {}", port); 399 std::string name = fmt::format("Gamecube Controller {}", port + 1);
305 devices.emplace_back(Common::ParamPackage{ 400 devices.emplace_back(Common::ParamPackage{
306 {"class", "gcpad"}, 401 {"class", "gcpad"},
307 {"display", std::move(name)}, 402 {"display", std::move(name)},
@@ -318,18 +413,18 @@ InputCommon::ButtonMapping Adapter::GetButtonMappingForDevice(
318 // This list also excludes any button that can't be really mapped 413 // This list also excludes any button that can't be really mapped
319 static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 12> 414 static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 12>
320 switch_to_gcadapter_button = { 415 switch_to_gcadapter_button = {
321 std::pair{Settings::NativeButton::A, PadButton::PAD_BUTTON_A}, 416 std::pair{Settings::NativeButton::A, PadButton::ButtonA},
322 {Settings::NativeButton::B, PadButton::PAD_BUTTON_B}, 417 {Settings::NativeButton::B, PadButton::ButtonB},
323 {Settings::NativeButton::X, PadButton::PAD_BUTTON_X}, 418 {Settings::NativeButton::X, PadButton::ButtonX},
324 {Settings::NativeButton::Y, PadButton::PAD_BUTTON_Y}, 419 {Settings::NativeButton::Y, PadButton::ButtonY},
325 {Settings::NativeButton::Plus, PadButton::PAD_BUTTON_START}, 420 {Settings::NativeButton::Plus, PadButton::ButtonStart},
326 {Settings::NativeButton::DLeft, PadButton::PAD_BUTTON_LEFT}, 421 {Settings::NativeButton::DLeft, PadButton::ButtonLeft},
327 {Settings::NativeButton::DUp, PadButton::PAD_BUTTON_UP}, 422 {Settings::NativeButton::DUp, PadButton::ButtonUp},
328 {Settings::NativeButton::DRight, PadButton::PAD_BUTTON_RIGHT}, 423 {Settings::NativeButton::DRight, PadButton::ButtonRight},
329 {Settings::NativeButton::DDown, PadButton::PAD_BUTTON_DOWN}, 424 {Settings::NativeButton::DDown, PadButton::ButtonDown},
330 {Settings::NativeButton::SL, PadButton::PAD_TRIGGER_L}, 425 {Settings::NativeButton::SL, PadButton::TriggerL},
331 {Settings::NativeButton::SR, PadButton::PAD_TRIGGER_R}, 426 {Settings::NativeButton::SR, PadButton::TriggerR},
332 {Settings::NativeButton::R, PadButton::PAD_TRIGGER_Z}, 427 {Settings::NativeButton::R, PadButton::TriggerZ},
333 }; 428 };
334 if (!params.Has("port")) { 429 if (!params.Has("port")) {
335 return {}; 430 return {};
@@ -352,8 +447,10 @@ InputCommon::ButtonMapping Adapter::GetButtonMappingForDevice(
352 for (const auto& [switch_button, gcadapter_axis] : switch_to_gcadapter_axis) { 447 for (const auto& [switch_button, gcadapter_axis] : switch_to_gcadapter_axis) {
353 Common::ParamPackage button_params({{"engine", "gcpad"}}); 448 Common::ParamPackage button_params({{"engine", "gcpad"}});
354 button_params.Set("port", params.Get("port", 0)); 449 button_params.Set("port", params.Get("port", 0));
355 button_params.Set("button", static_cast<int>(PadButton::PAD_STICK)); 450 button_params.Set("button", static_cast<s32>(PadButton::Stick));
356 button_params.Set("axis", static_cast<int>(gcadapter_axis)); 451 button_params.Set("axis", static_cast<s32>(gcadapter_axis));
452 button_params.Set("threshold", 0.5f);
453 button_params.Set("direction", "+");
357 mapping.insert_or_assign(switch_button, std::move(button_params)); 454 mapping.insert_or_assign(switch_button, std::move(button_params));
358 } 455 }
359 return mapping; 456 return mapping;
@@ -382,46 +479,33 @@ InputCommon::AnalogMapping Adapter::GetAnalogMappingForDevice(
382} 479}
383 480
384bool Adapter::DeviceConnected(std::size_t port) const { 481bool Adapter::DeviceConnected(std::size_t port) const {
385 return adapter_controllers_status[port] != ControllerTypes::None; 482 return pads[port].type != ControllerTypes::None;
386}
387
388void Adapter::ResetDeviceType(std::size_t port) {
389 adapter_controllers_status[port] = ControllerTypes::None;
390} 483}
391 484
392void Adapter::BeginConfiguration() { 485void Adapter::BeginConfiguration() {
393 get_origin.fill(true); 486 pad_queue.Clear();
394 for (auto& pq : pad_queue) {
395 pq.Clear();
396 }
397 configuring = true; 487 configuring = true;
398} 488}
399 489
400void Adapter::EndConfiguration() { 490void Adapter::EndConfiguration() {
401 for (auto& pq : pad_queue) { 491 pad_queue.Clear();
402 pq.Clear();
403 }
404 configuring = false; 492 configuring = false;
405} 493}
406 494
407std::array<Common::SPSCQueue<GCPadStatus>, 4>& Adapter::GetPadQueue() { 495Common::SPSCQueue<GCPadStatus>& Adapter::GetPadQueue() {
408 return pad_queue; 496 return pad_queue;
409} 497}
410 498
411const std::array<Common::SPSCQueue<GCPadStatus>, 4>& Adapter::GetPadQueue() const { 499const Common::SPSCQueue<GCPadStatus>& Adapter::GetPadQueue() const {
412 return pad_queue; 500 return pad_queue;
413} 501}
414 502
415std::array<GCState, 4>& Adapter::GetPadState() { 503GCController& Adapter::GetPadState(std::size_t port) {
416 return state; 504 return pads.at(port);
417}
418
419const std::array<GCState, 4>& Adapter::GetPadState() const {
420 return state;
421} 505}
422 506
423int Adapter::GetOriginValue(u32 port, u32 axis) const { 507const GCController& Adapter::GetPadState(std::size_t port) const {
424 return origin_status[port].axis_values[axis]; 508 return pads.at(port);
425} 509}
426 510
427} // namespace GCAdapter 511} // namespace GCAdapter
diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h
index 4f5f3de8e..d28dcfad3 100644
--- a/src/input_common/gcadapter/gc_adapter.h
+++ b/src/input_common/gcadapter/gc_adapter.h
@@ -19,24 +19,23 @@ struct libusb_device_handle;
19namespace GCAdapter { 19namespace GCAdapter {
20 20
21enum class PadButton { 21enum class PadButton {
22 PAD_BUTTON_LEFT = 0x0001, 22 Undefined = 0x0000,
23 PAD_BUTTON_RIGHT = 0x0002, 23 ButtonLeft = 0x0001,
24 PAD_BUTTON_DOWN = 0x0004, 24 ButtonRight = 0x0002,
25 PAD_BUTTON_UP = 0x0008, 25 ButtonDown = 0x0004,
26 PAD_TRIGGER_Z = 0x0010, 26 ButtonUp = 0x0008,
27 PAD_TRIGGER_R = 0x0020, 27 TriggerZ = 0x0010,
28 PAD_TRIGGER_L = 0x0040, 28 TriggerR = 0x0020,
29 PAD_BUTTON_A = 0x0100, 29 TriggerL = 0x0040,
30 PAD_BUTTON_B = 0x0200, 30 ButtonA = 0x0100,
31 PAD_BUTTON_X = 0x0400, 31 ButtonB = 0x0200,
32 PAD_BUTTON_Y = 0x0800, 32 ButtonX = 0x0400,
33 PAD_BUTTON_START = 0x1000, 33 ButtonY = 0x0800,
34 ButtonStart = 0x1000,
34 // Below is for compatibility with "AxisButton" type 35 // Below is for compatibility with "AxisButton" type
35 PAD_STICK = 0x2000, 36 Stick = 0x2000,
36}; 37};
37 38
38extern const std::array<PadButton, 12> PadButtonArray;
39
40enum class PadAxes : u8 { 39enum class PadAxes : u8 {
41 StickX, 40 StickX,
42 StickY, 41 StickY,
@@ -47,87 +46,122 @@ enum class PadAxes : u8 {
47 Undefined, 46 Undefined,
48}; 47};
49 48
49enum class ControllerTypes {
50 None,
51 Wired,
52 Wireless,
53};
54
50struct GCPadStatus { 55struct GCPadStatus {
51 u16 button{}; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits 56 std::size_t port{};
52 57
53 std::array<u8, 6> axis_values{}; // Triggers and sticks, following indices defined in PadAxes 58 PadButton button{PadButton::Undefined}; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits
54 static constexpr u8 THRESHOLD = 50; // Threshold for axis press for polling
55 59
56 u8 port{};
57 PadAxes axis{PadAxes::Undefined}; 60 PadAxes axis{PadAxes::Undefined};
58 u8 axis_value{255}; 61 s16 axis_value{};
62 u8 axis_threshold{50};
59}; 63};
60 64
61struct GCState { 65struct GCController {
62 std::unordered_map<int, bool> buttons; 66 ControllerTypes type{};
63 std::unordered_map<u32, u16> axes; 67 bool enable_vibration{};
68 u8 rumble_amplitude{};
69 u16 buttons{};
70 PadButton last_button{};
71 std::array<s16, 6> axis_values{};
72 std::array<u8, 6> axis_origin{};
64}; 73};
65 74
66enum class ControllerTypes { None, Wired, Wireless };
67
68class Adapter { 75class Adapter {
69public: 76public:
70 /// Initialize the GC Adapter capture and read sequence
71 Adapter(); 77 Adapter();
72
73 /// Close the adapter read thread and release the adapter
74 ~Adapter(); 78 ~Adapter();
79
80 /// Request a vibration for a controlelr
81 bool RumblePlay(std::size_t port, f32 amplitude);
82
75 /// Used for polling 83 /// Used for polling
76 void BeginConfiguration(); 84 void BeginConfiguration();
77 void EndConfiguration(); 85 void EndConfiguration();
78 86
87 Common::SPSCQueue<GCPadStatus>& GetPadQueue();
88 const Common::SPSCQueue<GCPadStatus>& GetPadQueue() const;
89
90 GCController& GetPadState(std::size_t port);
91 const GCController& GetPadState(std::size_t port) const;
92
93 /// Returns true if there is a device connected to port
94 bool DeviceConnected(std::size_t port) const;
95
96 /// Used for automapping features
79 std::vector<Common::ParamPackage> GetInputDevices() const; 97 std::vector<Common::ParamPackage> GetInputDevices() const;
80 InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const; 98 InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const;
81 InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const; 99 InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const;
82 100
83 /// Returns true if there is a device connected to port 101private:
84 bool DeviceConnected(std::size_t port) const; 102 using AdapterPayload = std::array<u8, 37>;
85 103
86 std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue(); 104 void UpdatePadType(std::size_t port, ControllerTypes pad_type);
87 const std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue() const; 105 void UpdateControllers(const AdapterPayload& adapter_payload);
106 void UpdateYuzuSettings(std::size_t port);
107 void UpdateStateButtons(std::size_t port, u8 b1, u8 b2);
108 void UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload);
109 void UpdateVibrations();
88 110
89 std::array<GCState, 4>& GetPadState(); 111 void AdapterInputThread();
90 const std::array<GCState, 4>& GetPadState() const;
91 112
92 int GetOriginValue(u32 port, u32 axis) const; 113 void AdapterScanThread();
93 114
94private: 115 bool IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size);
95 GCPadStatus GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload); 116
117 // Updates vibration state of all controllers
118 void SendVibrations();
119
120 /// For use in initialization, querying devices to find the adapter
121 void Setup();
96 122
97 void Read(); 123 /// Resets status of all GC controller devices to a disconected state
124 void ResetDevices();
98 125
99 /// Resets status of device connected to port 126 /// Resets status of device connected to a disconected state
100 void ResetDeviceType(std::size_t port); 127 void ResetDevice(std::size_t port);
101 128
102 /// Returns true if we successfully gain access to GC Adapter 129 /// Returns true if we successfully gain access to GC Adapter
103 bool CheckDeviceAccess(libusb_device* device); 130 bool CheckDeviceAccess();
104 131
105 /// Captures GC Adapter endpoint address, 132 /// Captures GC Adapter endpoint address
106 void GetGCEndpoint(libusb_device* device); 133 /// Returns true if the endpoind was set correctly
134 bool GetGCEndpoint(libusb_device* device);
107 135
108 /// For shutting down, clear all data, join all threads, release usb 136 /// For shutting down, clear all data, join all threads, release usb
109 void Reset(); 137 void Reset();
110 138
111 /// For use in initialization, querying devices to find the adapter 139 // Join all threads
112 void Setup(); 140 void JoinThreads();
141
142 // Release usb handles
143 void ClearLibusbHandle();
113 144
114 libusb_device_handle* usb_adapter_handle = nullptr; 145 libusb_device_handle* usb_adapter_handle = nullptr;
146 std::array<GCController, 4> pads;
147 Common::SPSCQueue<GCPadStatus> pad_queue;
115 148
116 std::thread adapter_input_thread; 149 std::thread adapter_input_thread;
117 bool adapter_thread_running; 150 std::thread adapter_scan_thread;
151 bool adapter_input_thread_running;
152 bool adapter_scan_thread_running;
153 bool restart_scan_thread;
118 154
119 libusb_context* libusb_ctx; 155 libusb_context* libusb_ctx;
120 156
121 u8 input_endpoint = 0; 157 u8 input_endpoint{0};
122 u8 output_endpoint = 0; 158 u8 output_endpoint{0};
123 159 u8 input_error_counter{0};
124 bool configuring = false; 160 u8 output_error_counter{0};
161 int vibration_counter{0};
125 162
126 std::array<GCState, 4> state; 163 bool configuring{false};
127 std::array<bool, 4> get_origin; 164 bool rumble_enabled{true};
128 std::array<GCPadStatus, 4> origin_status; 165 bool vibration_changed{true};
129 std::array<Common::SPSCQueue<GCPadStatus>, 4> pad_queue;
130 std::array<ControllerTypes, 4> adapter_controllers_status{};
131}; 166};
132
133} // namespace GCAdapter 167} // namespace GCAdapter
diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp
index 893556916..6bd6f57fc 100644
--- a/src/input_common/gcadapter/gc_poller.cpp
+++ b/src/input_common/gcadapter/gc_poller.cpp
@@ -15,22 +15,30 @@ namespace InputCommon {
15 15
16class GCButton final : public Input::ButtonDevice { 16class GCButton final : public Input::ButtonDevice {
17public: 17public:
18 explicit GCButton(u32 port_, int button_, const GCAdapter::Adapter* adapter) 18 explicit GCButton(u32 port_, s32 button_, GCAdapter::Adapter* adapter)
19 : port(port_), button(button_), gcadapter(adapter) {} 19 : port(port_), button(button_), gcadapter(adapter) {}
20 20
21 ~GCButton() override; 21 ~GCButton() override;
22 22
23 bool GetStatus() const override { 23 bool GetStatus() const override {
24 if (gcadapter->DeviceConnected(port)) { 24 if (gcadapter->DeviceConnected(port)) {
25 return gcadapter->GetPadState()[port].buttons.at(button); 25 return (gcadapter->GetPadState(port).buttons & button) != 0;
26 } 26 }
27 return false; 27 return false;
28 } 28 }
29 29
30 bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const override {
31 const float amplitude = amp_high + amp_low > 2.0f ? 1.0f : (amp_high + amp_low) * 0.5f;
32 const auto new_amp =
33 static_cast<f32>(pow(amplitude, 0.5f) * (3.0f - 2.0f * pow(amplitude, 0.15f)));
34
35 return gcadapter->RumblePlay(port, new_amp);
36 }
37
30private: 38private:
31 const u32 port; 39 const u32 port;
32 const int button; 40 const s32 button;
33 const GCAdapter::Adapter* gcadapter; 41 GCAdapter::Adapter* gcadapter;
34}; 42};
35 43
36class GCAxisButton final : public Input::ButtonDevice { 44class GCAxisButton final : public Input::ButtonDevice {
@@ -38,13 +46,12 @@ public:
38 explicit GCAxisButton(u32 port_, u32 axis_, float threshold_, bool trigger_if_greater_, 46 explicit GCAxisButton(u32 port_, u32 axis_, float threshold_, bool trigger_if_greater_,
39 const GCAdapter::Adapter* adapter) 47 const GCAdapter::Adapter* adapter)
40 : port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_), 48 : port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_),
41 gcadapter(adapter), 49 gcadapter(adapter) {}
42 origin_value(static_cast<float>(adapter->GetOriginValue(port_, axis_))) {}
43 50
44 bool GetStatus() const override { 51 bool GetStatus() const override {
45 if (gcadapter->DeviceConnected(port)) { 52 if (gcadapter->DeviceConnected(port)) {
46 const float current_axis_value = gcadapter->GetPadState()[port].axes.at(axis); 53 const float current_axis_value = gcadapter->GetPadState(port).axis_values.at(axis);
47 const float axis_value = (current_axis_value - origin_value) / 128.0f; 54 const float axis_value = current_axis_value / 128.0f;
48 if (trigger_if_greater) { 55 if (trigger_if_greater) {
49 // TODO: Might be worthwile to set a slider for the trigger threshold. It is 56 // TODO: Might be worthwile to set a slider for the trigger threshold. It is
50 // currently always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick 57 // currently always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick
@@ -61,7 +68,6 @@ private:
61 float threshold; 68 float threshold;
62 bool trigger_if_greater; 69 bool trigger_if_greater;
63 const GCAdapter::Adapter* gcadapter; 70 const GCAdapter::Adapter* gcadapter;
64 const float origin_value;
65}; 71};
66 72
67GCButtonFactory::GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_) 73GCButtonFactory::GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
@@ -73,7 +79,7 @@ std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::Param
73 const auto button_id = params.Get("button", 0); 79 const auto button_id = params.Get("button", 0);
74 const auto port = static_cast<u32>(params.Get("port", 0)); 80 const auto port = static_cast<u32>(params.Get("port", 0));
75 81
76 constexpr int PAD_STICK_ID = static_cast<u16>(GCAdapter::PadButton::PAD_STICK); 82 constexpr s32 PAD_STICK_ID = static_cast<s32>(GCAdapter::PadButton::Stick);
77 83
78 // button is not an axis/stick button 84 // button is not an axis/stick button
79 if (button_id != PAD_STICK_ID) { 85 if (button_id != PAD_STICK_ID) {
@@ -106,32 +112,25 @@ Common::ParamPackage GCButtonFactory::GetNextInput() const {
106 Common::ParamPackage params; 112 Common::ParamPackage params;
107 GCAdapter::GCPadStatus pad; 113 GCAdapter::GCPadStatus pad;
108 auto& queue = adapter->GetPadQueue(); 114 auto& queue = adapter->GetPadQueue();
109 for (std::size_t port = 0; port < queue.size(); ++port) { 115 while (queue.Pop(pad)) {
110 while (queue[port].Pop(pad)) { 116 // This while loop will break on the earliest detected button
111 // This while loop will break on the earliest detected button 117 params.Set("engine", "gcpad");
112 params.Set("engine", "gcpad"); 118 params.Set("port", static_cast<s32>(pad.port));
113 params.Set("port", static_cast<int>(port)); 119 if (pad.button != GCAdapter::PadButton::Undefined) {
114 for (const auto& button : GCAdapter::PadButtonArray) { 120 params.Set("button", static_cast<u16>(pad.button));
115 const u16 button_value = static_cast<u16>(button); 121 }
116 if (pad.button & button_value) {
117 params.Set("button", button_value);
118 break;
119 }
120 }
121 122
122 // For Axis button implementation 123 // For Axis button implementation
123 if (pad.axis != GCAdapter::PadAxes::Undefined) { 124 if (pad.axis != GCAdapter::PadAxes::Undefined) {
124 params.Set("axis", static_cast<u8>(pad.axis)); 125 params.Set("axis", static_cast<u8>(pad.axis));
125 params.Set("button", static_cast<u16>(GCAdapter::PadButton::PAD_STICK)); 126 params.Set("button", static_cast<u16>(GCAdapter::PadButton::Stick));
126 if (pad.axis_value > 128) { 127 params.Set("threshold", "0.25");
127 params.Set("direction", "+"); 128 if (pad.axis_value > 0) {
128 params.Set("threshold", "0.25"); 129 params.Set("direction", "+");
129 } else { 130 } else {
130 params.Set("direction", "-"); 131 params.Set("direction", "-");
131 params.Set("threshold", "-0.25");
132 }
133 break;
134 } 132 }
133 break;
135 } 134 }
136 } 135 }
137 return params; 136 return params;
@@ -152,17 +151,14 @@ public:
152 explicit GCAnalog(u32 port_, u32 axis_x_, u32 axis_y_, float deadzone_, 151 explicit GCAnalog(u32 port_, u32 axis_x_, u32 axis_y_, float deadzone_,
153 const GCAdapter::Adapter* adapter, float range_) 152 const GCAdapter::Adapter* adapter, float range_)
154 : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter), 153 : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter),
155 origin_value_x(static_cast<float>(adapter->GetOriginValue(port_, axis_x_))),
156 origin_value_y(static_cast<float>(adapter->GetOriginValue(port_, axis_y_))),
157 range(range_) {} 154 range(range_) {}
158 155
159 float GetAxis(u32 axis) const { 156 float GetAxis(u32 axis) const {
160 if (gcadapter->DeviceConnected(port)) { 157 if (gcadapter->DeviceConnected(port)) {
161 std::lock_guard lock{mutex}; 158 std::lock_guard lock{mutex};
162 const auto origin_value = axis % 2 == 0 ? origin_value_x : origin_value_y;
163 const auto axis_value = 159 const auto axis_value =
164 static_cast<float>(gcadapter->GetPadState()[port].axes.at(axis)); 160 static_cast<float>(gcadapter->GetPadState(port).axis_values.at(axis));
165 return (axis_value - origin_value) / (100.0f * range); 161 return (axis_value) / (100.0f * range);
166 } 162 }
167 return 0.0f; 163 return 0.0f;
168 } 164 }
@@ -215,8 +211,6 @@ private:
215 const u32 axis_y; 211 const u32 axis_y;
216 const float deadzone; 212 const float deadzone;
217 const GCAdapter::Adapter* gcadapter; 213 const GCAdapter::Adapter* gcadapter;
218 const float origin_value_x;
219 const float origin_value_y;
220 const float range; 214 const float range;
221 mutable std::mutex mutex; 215 mutable std::mutex mutex;
222}; 216};
@@ -254,26 +248,44 @@ void GCAnalogFactory::EndConfiguration() {
254 248
255Common::ParamPackage GCAnalogFactory::GetNextInput() { 249Common::ParamPackage GCAnalogFactory::GetNextInput() {
256 GCAdapter::GCPadStatus pad; 250 GCAdapter::GCPadStatus pad;
251 Common::ParamPackage params;
257 auto& queue = adapter->GetPadQueue(); 252 auto& queue = adapter->GetPadQueue();
258 for (std::size_t port = 0; port < queue.size(); ++port) { 253 while (queue.Pop(pad)) {
259 while (queue[port].Pop(pad)) { 254 if (pad.button != GCAdapter::PadButton::Undefined) {
260 if (pad.axis == GCAdapter::PadAxes::Undefined || 255 params.Set("engine", "gcpad");
261 std::abs((static_cast<float>(pad.axis_value) - 128.0f) / 128.0f) < 0.1f) { 256 params.Set("port", static_cast<s32>(pad.port));
262 continue; 257 params.Set("button", static_cast<u16>(pad.button));
263 } 258 return params;
264 // An analog device needs two axes, so we need to store the axis for later and wait for 259 }
265 // a second input event. The axes also must be from the same joystick. 260 if (pad.axis == GCAdapter::PadAxes::Undefined ||
266 const u8 axis = static_cast<u8>(pad.axis); 261 std::abs(static_cast<float>(pad.axis_value) / 128.0f) < 0.1f) {
267 if (analog_x_axis == -1) { 262 continue;
268 analog_x_axis = axis; 263 }
269 controller_number = static_cast<int>(port); 264 // An analog device needs two axes, so we need to store the axis for later and wait for
270 } else if (analog_y_axis == -1 && analog_x_axis != axis && 265 // a second input event. The axes also must be from the same joystick.
271 controller_number == static_cast<int>(port)) { 266 const u8 axis = static_cast<u8>(pad.axis);
272 analog_y_axis = axis; 267 if (axis == 0 || axis == 1) {
273 } 268 analog_x_axis = 0;
269 analog_y_axis = 1;
270 controller_number = static_cast<s32>(pad.port);
271 break;
272 }
273 if (axis == 2 || axis == 3) {
274 analog_x_axis = 2;
275 analog_y_axis = 3;
276 controller_number = static_cast<s32>(pad.port);
277 break;
278 }
279
280 if (analog_x_axis == -1) {
281 analog_x_axis = axis;
282 controller_number = static_cast<s32>(pad.port);
283 } else if (analog_y_axis == -1 && analog_x_axis != axis &&
284 controller_number == static_cast<s32>(pad.port)) {
285 analog_y_axis = axis;
286 break;
274 } 287 }
275 } 288 }
276 Common::ParamPackage params;
277 if (analog_x_axis != -1 && analog_y_axis != -1) { 289 if (analog_x_axis != -1 && analog_y_axis != -1) {
278 params.Set("engine", "gcpad"); 290 params.Set("engine", "gcpad");
279 params.Set("port", controller_number); 291 params.Set("port", controller_number);
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index 9c3035920..10883e2d9 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -155,15 +155,15 @@ public:
155 return sdl_joystick.get(); 155 return sdl_joystick.get();
156 } 156 }
157 157
158 void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) {
159 sdl_controller.reset(controller);
160 sdl_joystick.reset(joystick);
161 }
162
163 SDL_GameController* GetSDLGameController() const { 158 SDL_GameController* GetSDLGameController() const {
164 return sdl_controller.get(); 159 return sdl_controller.get();
165 } 160 }
166 161
162 void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) {
163 sdl_joystick.reset(joystick);
164 sdl_controller.reset(controller);
165 }
166
167private: 167private:
168 struct State { 168 struct State {
169 std::unordered_map<int, bool> buttons; 169 std::unordered_map<int, bool> buttons;
@@ -186,69 +186,58 @@ private:
186std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) { 186std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) {
187 std::lock_guard lock{joystick_map_mutex}; 187 std::lock_guard lock{joystick_map_mutex};
188 const auto it = joystick_map.find(guid); 188 const auto it = joystick_map.find(guid);
189
189 if (it != joystick_map.end()) { 190 if (it != joystick_map.end()) {
190 while (it->second.size() <= static_cast<std::size_t>(port)) { 191 while (it->second.size() <= static_cast<std::size_t>(port)) {
191 auto joystick = std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()), 192 auto joystick = std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()),
192 nullptr, nullptr); 193 nullptr, nullptr);
193 it->second.emplace_back(std::move(joystick)); 194 it->second.emplace_back(std::move(joystick));
194 } 195 }
196
195 return it->second[static_cast<std::size_t>(port)]; 197 return it->second[static_cast<std::size_t>(port)];
196 } 198 }
199
197 auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, nullptr); 200 auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, nullptr);
201
198 return joystick_map[guid].emplace_back(std::move(joystick)); 202 return joystick_map[guid].emplace_back(std::move(joystick));
199} 203}
200 204
201std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) { 205std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
202 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id); 206 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
203 auto sdl_controller = SDL_GameControllerFromInstanceID(sdl_id);
204 const std::string guid = GetGUID(sdl_joystick); 207 const std::string guid = GetGUID(sdl_joystick);
205 208
206 std::lock_guard lock{joystick_map_mutex}; 209 std::lock_guard lock{joystick_map_mutex};
207 const auto map_it = joystick_map.find(guid); 210 const auto map_it = joystick_map.find(guid);
208 if (map_it != joystick_map.end()) {
209 const auto vec_it =
210 std::find_if(map_it->second.begin(), map_it->second.end(),
211 [&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) {
212 return sdl_joystick == joystick->GetSDLJoystick();
213 });
214 if (vec_it != map_it->second.end()) {
215 // This is the common case: There is already an existing SDL_Joystick mapped to a
216 // SDLJoystick. return the SDLJoystick
217 return *vec_it;
218 }
219 211
220 // Search for a SDLJoystick without a mapped SDL_Joystick... 212 if (map_it == joystick_map.end()) {
221 const auto nullptr_it = std::find_if(map_it->second.begin(), map_it->second.end(), 213 return nullptr;
222 [](const std::shared_ptr<SDLJoystick>& joystick) { 214 }
223 return joystick->GetSDLJoystick() == nullptr;
224 });
225 if (nullptr_it != map_it->second.end()) {
226 // ... and map it
227 (*nullptr_it)->SetSDLJoystick(sdl_joystick, sdl_controller);
228 return *nullptr_it;
229 }
230 215
231 // There is no SDLJoystick without a mapped SDL_Joystick 216 const auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(),
232 // Create a new SDLJoystick 217 [&sdl_joystick](const auto& joystick) {
233 const int port = static_cast<int>(map_it->second.size()); 218 return joystick->GetSDLJoystick() == sdl_joystick;
234 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_controller); 219 });
235 return map_it->second.emplace_back(std::move(joystick)); 220
221 if (vec_it == map_it->second.end()) {
222 return nullptr;
236 } 223 }
237 224
238 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_controller); 225 return *vec_it;
239 return joystick_map[guid].emplace_back(std::move(joystick));
240} 226}
241 227
242void SDLState::InitJoystick(int joystick_index) { 228void SDLState::InitJoystick(int joystick_index) {
243 SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index); 229 SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index);
244 SDL_GameController* sdl_gamecontroller = nullptr; 230 SDL_GameController* sdl_gamecontroller = nullptr;
231
245 if (SDL_IsGameController(joystick_index)) { 232 if (SDL_IsGameController(joystick_index)) {
246 sdl_gamecontroller = SDL_GameControllerOpen(joystick_index); 233 sdl_gamecontroller = SDL_GameControllerOpen(joystick_index);
247 } 234 }
235
248 if (!sdl_joystick) { 236 if (!sdl_joystick) {
249 LOG_ERROR(Input, "Failed to open joystick {}", joystick_index); 237 LOG_ERROR(Input, "Failed to open joystick {}", joystick_index);
250 return; 238 return;
251 } 239 }
240
252 const std::string guid = GetGUID(sdl_joystick); 241 const std::string guid = GetGUID(sdl_joystick);
253 242
254 std::lock_guard lock{joystick_map_mutex}; 243 std::lock_guard lock{joystick_map_mutex};
@@ -257,14 +246,17 @@ void SDLState::InitJoystick(int joystick_index) {
257 joystick_map[guid].emplace_back(std::move(joystick)); 246 joystick_map[guid].emplace_back(std::move(joystick));
258 return; 247 return;
259 } 248 }
249
260 auto& joystick_guid_list = joystick_map[guid]; 250 auto& joystick_guid_list = joystick_map[guid];
261 const auto it = std::find_if( 251 const auto joystick_it =
262 joystick_guid_list.begin(), joystick_guid_list.end(), 252 std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
263 [](const std::shared_ptr<SDLJoystick>& joystick) { return !joystick->GetSDLJoystick(); }); 253 [](const auto& joystick) { return !joystick->GetSDLJoystick(); });
264 if (it != joystick_guid_list.end()) { 254
265 (*it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller); 255 if (joystick_it != joystick_guid_list.end()) {
256 (*joystick_it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller);
266 return; 257 return;
267 } 258 }
259
268 const int port = static_cast<int>(joystick_guid_list.size()); 260 const int port = static_cast<int>(joystick_guid_list.size());
269 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller); 261 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller);
270 joystick_guid_list.emplace_back(std::move(joystick)); 262 joystick_guid_list.emplace_back(std::move(joystick));
@@ -274,18 +266,14 @@ void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) {
274 const std::string guid = GetGUID(sdl_joystick); 266 const std::string guid = GetGUID(sdl_joystick);
275 267
276 std::lock_guard lock{joystick_map_mutex}; 268 std::lock_guard lock{joystick_map_mutex};
277 auto& joystick_guid_list = joystick_map[guid]; 269 // This call to guid is safe since the joystick is guaranteed to be in the map
278 auto joystick_it = std::find_if( 270 const auto& joystick_guid_list = joystick_map[guid];
279 joystick_guid_list.begin(), joystick_guid_list.end(), 271 const auto joystick_it = std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
280 [&sdl_joystick](auto& joystick) { return joystick->GetSDLJoystick() == sdl_joystick; }); 272 [&sdl_joystick](const auto& joystick) {
281 273 return joystick->GetSDLJoystick() == sdl_joystick;
282 if (joystick_it != joystick_guid_list.end()) { 274 });
283 (*joystick_it)->SetSDLJoystick(nullptr, nullptr); 275
284 joystick_guid_list.erase(joystick_it); 276 (*joystick_it)->SetSDLJoystick(nullptr, nullptr);
285 if (joystick_guid_list.empty()) {
286 joystick_map.erase(guid);
287 }
288 }
289} 277}
290 278
291void SDLState::HandleGameControllerEvent(const SDL_Event& event) { 279void SDLState::HandleGameControllerEvent(const SDL_Event& event) {
@@ -720,8 +708,7 @@ std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
720 std::vector<Common::ParamPackage> devices; 708 std::vector<Common::ParamPackage> devices;
721 for (const auto& [key, value] : joystick_map) { 709 for (const auto& [key, value] : joystick_map) {
722 for (const auto& joystick : value) { 710 for (const auto& joystick : value) {
723 auto* joy = joystick->GetSDLJoystick(); 711 if (auto* const controller = joystick->GetSDLGameController()) {
724 if (auto* controller = joystick->GetSDLGameController()) {
725 std::string name = 712 std::string name =
726 fmt::format("{} {}", SDL_GameControllerName(controller), joystick->GetPort()); 713 fmt::format("{} {}", SDL_GameControllerName(controller), joystick->GetPort());
727 devices.emplace_back(Common::ParamPackage{ 714 devices.emplace_back(Common::ParamPackage{
@@ -730,7 +717,7 @@ std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
730 {"guid", joystick->GetGUID()}, 717 {"guid", joystick->GetGUID()},
731 {"port", std::to_string(joystick->GetPort())}, 718 {"port", std::to_string(joystick->GetPort())},
732 }); 719 });
733 } else if (joy) { 720 } else if (auto* const joy = joystick->GetSDLJoystick()) {
734 std::string name = fmt::format("{} {}", SDL_JoystickName(joy), joystick->GetPort()); 721 std::string name = fmt::format("{} {}", SDL_JoystickName(joy), joystick->GetPort());
735 devices.emplace_back(Common::ParamPackage{ 722 devices.emplace_back(Common::ParamPackage{
736 {"class", "sdl"}, 723 {"class", "sdl"},
@@ -797,21 +784,27 @@ Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, s
797Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) { 784Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
798 switch (event.type) { 785 switch (event.type) {
799 case SDL_JOYAXISMOTION: { 786 case SDL_JOYAXISMOTION: {
800 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); 787 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
801 return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), 788 return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
802 static_cast<s32>(event.jaxis.axis), 789 static_cast<s32>(event.jaxis.axis),
803 event.jaxis.value); 790 event.jaxis.value);
791 }
792 break;
804 } 793 }
805 case SDL_JOYBUTTONUP: { 794 case SDL_JOYBUTTONUP: {
806 const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which); 795 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which)) {
807 return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), 796 return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
808 static_cast<s32>(event.jbutton.button)); 797 static_cast<s32>(event.jbutton.button));
798 }
799 break;
809 } 800 }
810 case SDL_JOYHATMOTION: { 801 case SDL_JOYHATMOTION: {
811 const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which); 802 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which)) {
812 return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), 803 return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
813 static_cast<s32>(event.jhat.hat), 804 static_cast<s32>(event.jhat.hat),
814 static_cast<s32>(event.jhat.value)); 805 static_cast<s32>(event.jhat.value));
806 }
807 break;
815 } 808 }
816 } 809 }
817 return {}; 810 return {};
@@ -820,21 +813,27 @@ Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Eve
820Common::ParamPackage SDLEventToMotionParamPackage(SDLState& state, const SDL_Event& event) { 813Common::ParamPackage SDLEventToMotionParamPackage(SDLState& state, const SDL_Event& event) {
821 switch (event.type) { 814 switch (event.type) {
822 case SDL_JOYAXISMOTION: { 815 case SDL_JOYAXISMOTION: {
823 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); 816 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
824 return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), 817 return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
825 static_cast<s32>(event.jaxis.axis), 818 static_cast<s32>(event.jaxis.axis),
826 event.jaxis.value); 819 event.jaxis.value);
820 }
821 break;
827 } 822 }
828 case SDL_JOYBUTTONUP: { 823 case SDL_JOYBUTTONUP: {
829 const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which); 824 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which)) {
830 return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), 825 return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
831 static_cast<s32>(event.jbutton.button)); 826 static_cast<s32>(event.jbutton.button));
827 }
828 break;
832 } 829 }
833 case SDL_JOYHATMOTION: { 830 case SDL_JOYHATMOTION: {
834 const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which); 831 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which)) {
835 return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), 832 return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
836 static_cast<s32>(event.jhat.hat), 833 static_cast<s32>(event.jhat.hat),
837 static_cast<s32>(event.jhat.value)); 834 static_cast<s32>(event.jhat.value));
835 }
836 break;
838 } 837 }
839 } 838 }
840 return {}; 839 return {};
@@ -1062,9 +1061,8 @@ public:
1062 // Simplify controller config by testing if game controller support is enabled. 1061 // Simplify controller config by testing if game controller support is enabled.
1063 if (event.type == SDL_JOYAXISMOTION) { 1062 if (event.type == SDL_JOYAXISMOTION) {
1064 const auto axis = event.jaxis.axis; 1063 const auto axis = event.jaxis.axis;
1065 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); 1064 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
1066 auto* const controller = joystick->GetSDLGameController(); 1065 auto* const controller = joystick->GetSDLGameController()) {
1067 if (controller) {
1068 const auto axis_left_x = 1066 const auto axis_left_x =
1069 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX) 1067 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX)
1070 .value.axis; 1068 .value.axis;
@@ -1098,12 +1096,13 @@ public:
1098 } 1096 }
1099 1097
1100 if (analog_x_axis != -1 && analog_y_axis != -1) { 1098 if (analog_x_axis != -1 && analog_y_axis != -1) {
1101 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); 1099 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
1102 auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), 1100 auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
1103 analog_x_axis, analog_y_axis); 1101 analog_x_axis, analog_y_axis);
1104 analog_x_axis = -1; 1102 analog_x_axis = -1;
1105 analog_y_axis = -1; 1103 analog_y_axis = -1;
1106 return params; 1104 return params;
1105 }
1107 } 1106 }
1108 return {}; 1107 return {};
1109 } 1108 }
diff --git a/src/tests/common/fibers.cpp b/src/tests/common/fibers.cpp
index 4fd92428f..4757dd2b4 100644
--- a/src/tests/common/fibers.cpp
+++ b/src/tests/common/fibers.cpp
@@ -6,18 +6,40 @@
6#include <cstdlib> 6#include <cstdlib>
7#include <functional> 7#include <functional>
8#include <memory> 8#include <memory>
9#include <mutex>
10#include <stdexcept>
9#include <thread> 11#include <thread>
10#include <unordered_map> 12#include <unordered_map>
11#include <vector> 13#include <vector>
12 14
13#include <catch2/catch.hpp> 15#include <catch2/catch.hpp>
14#include <math.h> 16
15#include "common/common_types.h" 17#include "common/common_types.h"
16#include "common/fiber.h" 18#include "common/fiber.h"
17#include "common/spin_lock.h"
18 19
19namespace Common { 20namespace Common {
20 21
22class ThreadIds {
23public:
24 void Register(u32 id) {
25 const auto thread_id = std::this_thread::get_id();
26 std::scoped_lock lock{mutex};
27 if (ids.contains(thread_id)) {
28 throw std::logic_error{"Registering the same thread twice"};
29 }
30 ids.emplace(thread_id, id);
31 }
32
33 [[nodiscard]] u32 Get() const {
34 std::scoped_lock lock{mutex};
35 return ids.at(std::this_thread::get_id());
36 }
37
38private:
39 mutable std::mutex mutex;
40 std::unordered_map<std::thread::id, u32> ids;
41};
42
21class TestControl1 { 43class TestControl1 {
22public: 44public:
23 TestControl1() = default; 45 TestControl1() = default;
@@ -26,7 +48,7 @@ public:
26 48
27 void ExecuteThread(u32 id); 49 void ExecuteThread(u32 id);
28 50
29 std::unordered_map<std::thread::id, u32> ids; 51 ThreadIds thread_ids;
30 std::vector<std::shared_ptr<Common::Fiber>> thread_fibers; 52 std::vector<std::shared_ptr<Common::Fiber>> thread_fibers;
31 std::vector<std::shared_ptr<Common::Fiber>> work_fibers; 53 std::vector<std::shared_ptr<Common::Fiber>> work_fibers;
32 std::vector<u32> items; 54 std::vector<u32> items;
@@ -39,8 +61,7 @@ static void WorkControl1(void* control) {
39} 61}
40 62
41void TestControl1::DoWork() { 63void TestControl1::DoWork() {
42 std::thread::id this_id = std::this_thread::get_id(); 64 const u32 id = thread_ids.Get();
43 u32 id = ids[this_id];
44 u32 value = items[id]; 65 u32 value = items[id];
45 for (u32 i = 0; i < id; i++) { 66 for (u32 i = 0; i < id; i++) {
46 value++; 67 value++;
@@ -50,8 +71,7 @@ void TestControl1::DoWork() {
50} 71}
51 72
52void TestControl1::ExecuteThread(u32 id) { 73void TestControl1::ExecuteThread(u32 id) {
53 std::thread::id this_id = std::this_thread::get_id(); 74 thread_ids.Register(id);
54 ids[this_id] = id;
55 auto thread_fiber = Fiber::ThreadToFiber(); 75 auto thread_fiber = Fiber::ThreadToFiber();
56 thread_fibers[id] = thread_fiber; 76 thread_fibers[id] = thread_fiber;
57 work_fibers[id] = std::make_shared<Fiber>(std::function<void(void*)>{WorkControl1}, this); 77 work_fibers[id] = std::make_shared<Fiber>(std::function<void(void*)>{WorkControl1}, this);
@@ -98,8 +118,7 @@ public:
98 value1 += i; 118 value1 += i;
99 } 119 }
100 Fiber::YieldTo(fiber1, fiber3); 120 Fiber::YieldTo(fiber1, fiber3);
101 std::thread::id this_id = std::this_thread::get_id(); 121 const u32 id = thread_ids.Get();
102 u32 id = ids[this_id];
103 assert1 = id == 1; 122 assert1 = id == 1;
104 value2 += 5000; 123 value2 += 5000;
105 Fiber::YieldTo(fiber1, thread_fibers[id]); 124 Fiber::YieldTo(fiber1, thread_fibers[id]);
@@ -115,8 +134,7 @@ public:
115 } 134 }
116 135
117 void DoWork3() { 136 void DoWork3() {
118 std::thread::id this_id = std::this_thread::get_id(); 137 const u32 id = thread_ids.Get();
119 u32 id = ids[this_id];
120 assert2 = id == 0; 138 assert2 = id == 0;
121 value1 += 1000; 139 value1 += 1000;
122 Fiber::YieldTo(fiber3, thread_fibers[id]); 140 Fiber::YieldTo(fiber3, thread_fibers[id]);
@@ -125,14 +143,12 @@ public:
125 void ExecuteThread(u32 id); 143 void ExecuteThread(u32 id);
126 144
127 void CallFiber1() { 145 void CallFiber1() {
128 std::thread::id this_id = std::this_thread::get_id(); 146 const u32 id = thread_ids.Get();
129 u32 id = ids[this_id];
130 Fiber::YieldTo(thread_fibers[id], fiber1); 147 Fiber::YieldTo(thread_fibers[id], fiber1);
131 } 148 }
132 149
133 void CallFiber2() { 150 void CallFiber2() {
134 std::thread::id this_id = std::this_thread::get_id(); 151 const u32 id = thread_ids.Get();
135 u32 id = ids[this_id];
136 Fiber::YieldTo(thread_fibers[id], fiber2); 152 Fiber::YieldTo(thread_fibers[id], fiber2);
137 } 153 }
138 154
@@ -145,7 +161,7 @@ public:
145 u32 value2{}; 161 u32 value2{};
146 std::atomic<bool> trap{true}; 162 std::atomic<bool> trap{true};
147 std::atomic<bool> trap2{true}; 163 std::atomic<bool> trap2{true};
148 std::unordered_map<std::thread::id, u32> ids; 164 ThreadIds thread_ids;
149 std::vector<std::shared_ptr<Common::Fiber>> thread_fibers; 165 std::vector<std::shared_ptr<Common::Fiber>> thread_fibers;
150 std::shared_ptr<Common::Fiber> fiber1; 166 std::shared_ptr<Common::Fiber> fiber1;
151 std::shared_ptr<Common::Fiber> fiber2; 167 std::shared_ptr<Common::Fiber> fiber2;
@@ -168,15 +184,13 @@ static void WorkControl2_3(void* control) {
168} 184}
169 185
170void TestControl2::ExecuteThread(u32 id) { 186void TestControl2::ExecuteThread(u32 id) {
171 std::thread::id this_id = std::this_thread::get_id(); 187 thread_ids.Register(id);
172 ids[this_id] = id;
173 auto thread_fiber = Fiber::ThreadToFiber(); 188 auto thread_fiber = Fiber::ThreadToFiber();
174 thread_fibers[id] = thread_fiber; 189 thread_fibers[id] = thread_fiber;
175} 190}
176 191
177void TestControl2::Exit() { 192void TestControl2::Exit() {
178 std::thread::id this_id = std::this_thread::get_id(); 193 const u32 id = thread_ids.Get();
179 u32 id = ids[this_id];
180 thread_fibers[id]->Exit(); 194 thread_fibers[id]->Exit();
181} 195}
182 196
@@ -228,24 +242,21 @@ public:
228 void DoWork1() { 242 void DoWork1() {
229 value1 += 1; 243 value1 += 1;
230 Fiber::YieldTo(fiber1, fiber2); 244 Fiber::YieldTo(fiber1, fiber2);
231 std::thread::id this_id = std::this_thread::get_id(); 245 const u32 id = thread_ids.Get();
232 u32 id = ids[this_id];
233 value3 += 1; 246 value3 += 1;
234 Fiber::YieldTo(fiber1, thread_fibers[id]); 247 Fiber::YieldTo(fiber1, thread_fibers[id]);
235 } 248 }
236 249
237 void DoWork2() { 250 void DoWork2() {
238 value2 += 1; 251 value2 += 1;
239 std::thread::id this_id = std::this_thread::get_id(); 252 const u32 id = thread_ids.Get();
240 u32 id = ids[this_id];
241 Fiber::YieldTo(fiber2, thread_fibers[id]); 253 Fiber::YieldTo(fiber2, thread_fibers[id]);
242 } 254 }
243 255
244 void ExecuteThread(u32 id); 256 void ExecuteThread(u32 id);
245 257
246 void CallFiber1() { 258 void CallFiber1() {
247 std::thread::id this_id = std::this_thread::get_id(); 259 const u32 id = thread_ids.Get();
248 u32 id = ids[this_id];
249 Fiber::YieldTo(thread_fibers[id], fiber1); 260 Fiber::YieldTo(thread_fibers[id], fiber1);
250 } 261 }
251 262
@@ -254,7 +265,7 @@ public:
254 u32 value1{}; 265 u32 value1{};
255 u32 value2{}; 266 u32 value2{};
256 u32 value3{}; 267 u32 value3{};
257 std::unordered_map<std::thread::id, u32> ids; 268 ThreadIds thread_ids;
258 std::vector<std::shared_ptr<Common::Fiber>> thread_fibers; 269 std::vector<std::shared_ptr<Common::Fiber>> thread_fibers;
259 std::shared_ptr<Common::Fiber> fiber1; 270 std::shared_ptr<Common::Fiber> fiber1;
260 std::shared_ptr<Common::Fiber> fiber2; 271 std::shared_ptr<Common::Fiber> fiber2;
@@ -271,15 +282,13 @@ static void WorkControl3_2(void* control) {
271} 282}
272 283
273void TestControl3::ExecuteThread(u32 id) { 284void TestControl3::ExecuteThread(u32 id) {
274 std::thread::id this_id = std::this_thread::get_id(); 285 thread_ids.Register(id);
275 ids[this_id] = id;
276 auto thread_fiber = Fiber::ThreadToFiber(); 286 auto thread_fiber = Fiber::ThreadToFiber();
277 thread_fibers[id] = thread_fiber; 287 thread_fibers[id] = thread_fiber;
278} 288}
279 289
280void TestControl3::Exit() { 290void TestControl3::Exit() {
281 std::thread::id this_id = std::this_thread::get_id(); 291 const u32 id = thread_ids.Get();
282 u32 id = ids[this_id];
283 thread_fibers[id]->Exit(); 292 thread_fibers[id]->Exit();
284} 293}
285 294
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 77ebac19f..abcee2a1c 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -5,6 +5,24 @@ add_library(video_core STATIC
5 buffer_cache/buffer_cache.h 5 buffer_cache/buffer_cache.h
6 buffer_cache/map_interval.cpp 6 buffer_cache/map_interval.cpp
7 buffer_cache/map_interval.h 7 buffer_cache/map_interval.h
8 cdma_pusher.cpp
9 cdma_pusher.h
10 command_classes/codecs/codec.cpp
11 command_classes/codecs/codec.h
12 command_classes/codecs/h264.cpp
13 command_classes/codecs/h264.h
14 command_classes/codecs/vp9.cpp
15 command_classes/codecs/vp9.h
16 command_classes/codecs/vp9_types.h
17 command_classes/host1x.cpp
18 command_classes/host1x.h
19 command_classes/nvdec.cpp
20 command_classes/nvdec.h
21 command_classes/nvdec_common.h
22 command_classes/sync_manager.cpp
23 command_classes/sync_manager.h
24 command_classes/vic.cpp
25 command_classes/vic.h
8 compatible_formats.cpp 26 compatible_formats.cpp
9 compatible_formats.h 27 compatible_formats.h
10 dirty_flags.cpp 28 dirty_flags.cpp
@@ -250,6 +268,14 @@ create_target_directory_groups(video_core)
250target_link_libraries(video_core PUBLIC common core) 268target_link_libraries(video_core PUBLIC common core)
251target_link_libraries(video_core PRIVATE glad xbyak) 269target_link_libraries(video_core PRIVATE glad xbyak)
252 270
271if (MSVC)
272 target_include_directories(video_core PRIVATE ${FFMPEG_INCLUDE_DIR})
273 target_link_libraries(video_core PUBLIC ${FFMPEG_LIBRARY_DIR}/swscale.lib ${FFMPEG_LIBRARY_DIR}/avcodec.lib ${FFMPEG_LIBRARY_DIR}/avutil.lib)
274else()
275 target_include_directories(video_core PRIVATE ${FFMPEG_INCLUDE_DIR})
276 target_link_libraries(video_core PRIVATE ${FFMPEG_LIBRARIES})
277endif()
278
253add_dependencies(video_core host_shaders) 279add_dependencies(video_core host_shaders)
254target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE}) 280target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE})
255 281
@@ -276,7 +302,10 @@ else()
276 target_compile_options(video_core PRIVATE 302 target_compile_options(video_core PRIVATE
277 -Werror=conversion 303 -Werror=conversion
278 -Wno-error=sign-conversion 304 -Wno-error=sign-conversion
305 -Werror=pessimizing-move
306 -Werror=redundant-move
279 -Werror=switch 307 -Werror=switch
308 -Werror=type-limits
280 -Werror=unused-variable 309 -Werror=unused-variable
281 310
282 $<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess> 311 $<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess>
diff --git a/src/video_core/cdma_pusher.cpp b/src/video_core/cdma_pusher.cpp
new file mode 100644
index 000000000..b60f86260
--- /dev/null
+++ b/src/video_core/cdma_pusher.cpp
@@ -0,0 +1,171 @@
1// MIT License
2//
3// Copyright (c) Ryujinx Team and Contributors
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
6// associated documentation files (the "Software"), to deal in the Software without restriction,
7// including without limitation the rights to use, copy, modify, merge, publish, distribute,
8// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
9// furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in all copies or
12// substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
15// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19//
20
21#include "command_classes/host1x.h"
22#include "command_classes/nvdec.h"
23#include "command_classes/vic.h"
24#include "common/bit_util.h"
25#include "video_core/cdma_pusher.h"
26#include "video_core/command_classes/nvdec_common.h"
27#include "video_core/engines/maxwell_3d.h"
28#include "video_core/gpu.h"
29#include "video_core/memory_manager.h"
30
31namespace Tegra {
32CDmaPusher::CDmaPusher(GPU& gpu)
33 : gpu(gpu), nvdec_processor(std::make_shared<Nvdec>(gpu)),
34 vic_processor(std::make_unique<Vic>(gpu, nvdec_processor)),
35 host1x_processor(std::make_unique<Host1x>(gpu)),
36 nvdec_sync(std::make_unique<SyncptIncrManager>(gpu)),
37 vic_sync(std::make_unique<SyncptIncrManager>(gpu)) {}
38
39CDmaPusher::~CDmaPusher() = default;
40
41void CDmaPusher::Push(ChCommandHeaderList&& entries) {
42 cdma_queue.push(std::move(entries));
43}
44
45void CDmaPusher::DispatchCalls() {
46 while (!cdma_queue.empty()) {
47 Step();
48 }
49}
50
51void CDmaPusher::Step() {
52 const auto entries{cdma_queue.front()};
53 cdma_queue.pop();
54
55 std::vector<u32> values(entries.size());
56 std::memcpy(values.data(), entries.data(), entries.size() * sizeof(u32));
57
58 for (const u32 value : values) {
59 if (mask != 0) {
60 const u32 lbs = Common::CountTrailingZeroes32(mask);
61 mask &= ~(1U << lbs);
62 ExecuteCommand(static_cast<u32>(offset + lbs), value);
63 continue;
64 } else if (count != 0) {
65 --count;
66 ExecuteCommand(static_cast<u32>(offset), value);
67 if (incrementing) {
68 ++offset;
69 }
70 continue;
71 }
72 const auto mode = static_cast<ChSubmissionMode>((value >> 28) & 0xf);
73 switch (mode) {
74 case ChSubmissionMode::SetClass: {
75 mask = value & 0x3f;
76 offset = (value >> 16) & 0xfff;
77 current_class = static_cast<ChClassId>((value >> 6) & 0x3ff);
78 break;
79 }
80 case ChSubmissionMode::Incrementing:
81 case ChSubmissionMode::NonIncrementing:
82 count = value & 0xffff;
83 offset = (value >> 16) & 0xfff;
84 incrementing = mode == ChSubmissionMode::Incrementing;
85 break;
86 case ChSubmissionMode::Mask:
87 mask = value & 0xffff;
88 offset = (value >> 16) & 0xfff;
89 break;
90 case ChSubmissionMode::Immediate: {
91 const u32 data = value & 0xfff;
92 offset = (value >> 16) & 0xfff;
93 ExecuteCommand(static_cast<u32>(offset), data);
94 break;
95 }
96 default:
97 UNIMPLEMENTED_MSG("ChSubmission mode {} is not implemented!", static_cast<u32>(mode));
98 break;
99 }
100 }
101}
102
103void CDmaPusher::ExecuteCommand(u32 offset, u32 data) {
104 switch (current_class) {
105 case ChClassId::NvDec:
106 ThiStateWrite(nvdec_thi_state, offset, {data});
107 switch (static_cast<ThiMethod>(offset)) {
108 case ThiMethod::IncSyncpt: {
109 LOG_DEBUG(Service_NVDRV, "NVDEC Class IncSyncpt Method");
110 const auto syncpoint_id = static_cast<u32>(data & 0xFF);
111 const auto cond = static_cast<u32>((data >> 8) & 0xFF);
112 if (cond == 0) {
113 nvdec_sync->Increment(syncpoint_id);
114 } else {
115 nvdec_sync->IncrementWhenDone(static_cast<u32>(current_class), syncpoint_id);
116 nvdec_sync->SignalDone(syncpoint_id);
117 }
118 break;
119 }
120 case ThiMethod::SetMethod1:
121 LOG_DEBUG(Service_NVDRV, "NVDEC method 0x{:X}",
122 static_cast<u32>(nvdec_thi_state.method_0));
123 nvdec_processor->ProcessMethod(
124 static_cast<Tegra::Nvdec::Method>(nvdec_thi_state.method_0), {data});
125 break;
126 default:
127 break;
128 }
129 break;
130 case ChClassId::GraphicsVic:
131 ThiStateWrite(vic_thi_state, static_cast<u32>(offset), {data});
132 switch (static_cast<ThiMethod>(offset)) {
133 case ThiMethod::IncSyncpt: {
134 LOG_DEBUG(Service_NVDRV, "VIC Class IncSyncpt Method");
135 const auto syncpoint_id = static_cast<u32>(data & 0xFF);
136 const auto cond = static_cast<u32>((data >> 8) & 0xFF);
137 if (cond == 0) {
138 vic_sync->Increment(syncpoint_id);
139 } else {
140 vic_sync->IncrementWhenDone(static_cast<u32>(current_class), syncpoint_id);
141 vic_sync->SignalDone(syncpoint_id);
142 }
143 break;
144 }
145 case ThiMethod::SetMethod1:
146 LOG_DEBUG(Service_NVDRV, "VIC method 0x{:X}, Args=({})",
147 static_cast<u32>(vic_thi_state.method_0), data);
148 vic_processor->ProcessMethod(static_cast<Tegra::Vic::Method>(vic_thi_state.method_0),
149 {data});
150 break;
151 default:
152 break;
153 }
154 break;
155 case ChClassId::Host1x:
156 // This device is mainly for syncpoint synchronization
157 LOG_DEBUG(Service_NVDRV, "Host1X Class Method");
158 host1x_processor->ProcessMethod(static_cast<Tegra::Host1x::Method>(offset), {data});
159 break;
160 default:
161 UNIMPLEMENTED_MSG("Current class not implemented {:X}", static_cast<u32>(current_class));
162 break;
163 }
164}
165
166void CDmaPusher::ThiStateWrite(ThiRegisters& state, u32 offset, const std::vector<u32>& arguments) {
167 u8* const state_offset = reinterpret_cast<u8*>(&state) + sizeof(u32) * offset;
168 std::memcpy(state_offset, arguments.data(), sizeof(u32) * arguments.size());
169}
170
171} // namespace Tegra
diff --git a/src/video_core/cdma_pusher.h b/src/video_core/cdma_pusher.h
new file mode 100644
index 000000000..982f309c5
--- /dev/null
+++ b/src/video_core/cdma_pusher.h
@@ -0,0 +1,138 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <unordered_map>
9#include <vector>
10#include <queue>
11
12#include "common/bit_field.h"
13#include "common/common_types.h"
14#include "video_core/command_classes/sync_manager.h"
15
16namespace Tegra {
17
18class GPU;
19class Nvdec;
20class Vic;
21class Host1x;
22
23enum class ChSubmissionMode : u32 {
24 SetClass = 0,
25 Incrementing = 1,
26 NonIncrementing = 2,
27 Mask = 3,
28 Immediate = 4,
29 Restart = 5,
30 Gather = 6,
31};
32
33enum class ChClassId : u32 {
34 NoClass = 0x0,
35 Host1x = 0x1,
36 VideoEncodeMpeg = 0x20,
37 VideoEncodeNvEnc = 0x21,
38 VideoStreamingVi = 0x30,
39 VideoStreamingIsp = 0x32,
40 VideoStreamingIspB = 0x34,
41 VideoStreamingViI2c = 0x36,
42 GraphicsVic = 0x5d,
43 Graphics3D = 0x60,
44 GraphicsGpu = 0x61,
45 Tsec = 0xe0,
46 TsecB = 0xe1,
47 NvJpg = 0xc0,
48 NvDec = 0xf0
49};
50
51enum class ChMethod : u32 {
52 Empty = 0,
53 SetMethod = 0x10,
54 SetData = 0x11,
55};
56
57union ChCommandHeader {
58 u32 raw;
59 BitField<0, 16, u32> value;
60 BitField<16, 12, ChMethod> method_offset;
61 BitField<28, 4, ChSubmissionMode> submission_mode;
62};
63static_assert(sizeof(ChCommandHeader) == sizeof(u32), "ChCommand header is an invalid size");
64
65struct ChCommand {
66 ChClassId class_id{};
67 int method_offset{};
68 std::vector<u32> arguments;
69};
70
71using ChCommandHeaderList = std::vector<Tegra::ChCommandHeader>;
72using ChCommandList = std::vector<Tegra::ChCommand>;
73
74struct ThiRegisters {
75 u32_le increment_syncpt{};
76 INSERT_PADDING_WORDS(1);
77 u32_le increment_syncpt_error{};
78 u32_le ctx_switch_incremement_syncpt{};
79 INSERT_PADDING_WORDS(4);
80 u32_le ctx_switch{};
81 INSERT_PADDING_WORDS(1);
82 u32_le ctx_syncpt_eof{};
83 INSERT_PADDING_WORDS(5);
84 u32_le method_0{};
85 u32_le method_1{};
86 INSERT_PADDING_WORDS(12);
87 u32_le int_status{};
88 u32_le int_mask{};
89};
90
91enum class ThiMethod : u32 {
92 IncSyncpt = offsetof(ThiRegisters, increment_syncpt) / sizeof(u32),
93 SetMethod0 = offsetof(ThiRegisters, method_0) / sizeof(u32),
94 SetMethod1 = offsetof(ThiRegisters, method_1) / sizeof(u32),
95};
96
97class CDmaPusher {
98public:
99 explicit CDmaPusher(GPU& gpu);
100 ~CDmaPusher();
101
102 /// Push NVDEC command buffer entries into queue
103 void Push(ChCommandHeaderList&& entries);
104
105 /// Process queued command buffer entries
106 void DispatchCalls();
107
108 /// Process one queue element
109 void Step();
110
111 /// Invoke command class devices to execute the command based on the current state
112 void ExecuteCommand(u32 offset, u32 data);
113
114private:
115 /// Write arguments value to the ThiRegisters member at the specified offset
116 void ThiStateWrite(ThiRegisters& state, u32 offset, const std::vector<u32>& arguments);
117
118 GPU& gpu;
119
120 std::shared_ptr<Tegra::Nvdec> nvdec_processor;
121 std::unique_ptr<Tegra::Vic> vic_processor;
122 std::unique_ptr<Tegra::Host1x> host1x_processor;
123 std::unique_ptr<SyncptIncrManager> nvdec_sync;
124 std::unique_ptr<SyncptIncrManager> vic_sync;
125 ChClassId current_class{};
126 ThiRegisters vic_thi_state{};
127 ThiRegisters nvdec_thi_state{};
128
129 s32 count{};
130 s32 offset{};
131 s32 mask{};
132 bool incrementing{};
133
134 // Queue of command lists to be processed
135 std::queue<ChCommandHeaderList> cdma_queue;
136};
137
138} // namespace Tegra
diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp
new file mode 100644
index 000000000..1adf3cd13
--- /dev/null
+++ b/src/video_core/command_classes/codecs/codec.cpp
@@ -0,0 +1,115 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include <fstream>
7#include <vector>
8#include "common/assert.h"
9#include "video_core/command_classes/codecs/codec.h"
10#include "video_core/command_classes/codecs/h264.h"
11#include "video_core/command_classes/codecs/vp9.h"
12#include "video_core/gpu.h"
13#include "video_core/memory_manager.h"
14
15extern "C" {
16#include <libavutil/opt.h>
17}
18
19namespace Tegra {
20
21Codec::Codec(GPU& gpu_)
22 : gpu(gpu_), h264_decoder(std::make_unique<Decoder::H264>(gpu)),
23 vp9_decoder(std::make_unique<Decoder::VP9>(gpu)) {}
24
25Codec::~Codec() {
26 if (!initialized) {
27 return;
28 }
29 // Free libav memory
30 avcodec_send_packet(av_codec_ctx, nullptr);
31 avcodec_receive_frame(av_codec_ctx, av_frame);
32 avcodec_flush_buffers(av_codec_ctx);
33
34 av_frame_unref(av_frame);
35 av_free(av_frame);
36 avcodec_close(av_codec_ctx);
37}
38
39void Codec::SetTargetCodec(NvdecCommon::VideoCodec codec) {
40 LOG_INFO(Service_NVDRV, "NVDEC video codec initialized to {}", static_cast<u32>(codec));
41 current_codec = codec;
42}
43
44void Codec::StateWrite(u32 offset, u64 arguments) {
45 u8* const state_offset = reinterpret_cast<u8*>(&state) + offset * sizeof(u64);
46 std::memcpy(state_offset, &arguments, sizeof(u64));
47}
48
49void Codec::Decode() {
50 bool is_first_frame = false;
51
52 if (!initialized) {
53 if (current_codec == NvdecCommon::VideoCodec::H264) {
54 av_codec = avcodec_find_decoder(AV_CODEC_ID_H264);
55 } else if (current_codec == NvdecCommon::VideoCodec::Vp9) {
56 av_codec = avcodec_find_decoder(AV_CODEC_ID_VP9);
57 } else {
58 LOG_ERROR(Service_NVDRV, "Unknown video codec {}", static_cast<u32>(current_codec));
59 return;
60 }
61
62 av_codec_ctx = avcodec_alloc_context3(av_codec);
63 av_frame = av_frame_alloc();
64 av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0);
65
66 // TODO(ameerj): libavcodec gpu hw acceleration
67
68 const auto av_error = avcodec_open2(av_codec_ctx, av_codec, nullptr);
69 if (av_error < 0) {
70 LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed.");
71 av_frame_unref(av_frame);
72 av_free(av_frame);
73 avcodec_close(av_codec_ctx);
74 return;
75 }
76 initialized = true;
77 is_first_frame = true;
78 }
79 bool vp9_hidden_frame = false;
80
81 AVPacket packet{};
82 av_init_packet(&packet);
83 std::vector<u8> frame_data;
84
85 if (current_codec == NvdecCommon::VideoCodec::H264) {
86 frame_data = h264_decoder->ComposeFrameHeader(state, is_first_frame);
87 } else if (current_codec == NvdecCommon::VideoCodec::Vp9) {
88 frame_data = vp9_decoder->ComposeFrameHeader(state);
89 vp9_hidden_frame = vp9_decoder->WasFrameHidden();
90 }
91
92 packet.data = frame_data.data();
93 packet.size = static_cast<int>(frame_data.size());
94
95 avcodec_send_packet(av_codec_ctx, &packet);
96
97 if (!vp9_hidden_frame) {
98 // Only receive/store visible frames
99 avcodec_receive_frame(av_codec_ctx, av_frame);
100 }
101}
102
103AVFrame* Codec::GetCurrentFrame() {
104 return av_frame;
105}
106
107const AVFrame* Codec::GetCurrentFrame() const {
108 return av_frame;
109}
110
111NvdecCommon::VideoCodec Codec::GetCurrentCodec() const {
112 return current_codec;
113}
114
115} // namespace Tegra
diff --git a/src/video_core/command_classes/codecs/codec.h b/src/video_core/command_classes/codecs/codec.h
new file mode 100644
index 000000000..cb67094f6
--- /dev/null
+++ b/src/video_core/command_classes/codecs/codec.h
@@ -0,0 +1,66 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include "common/common_types.h"
9#include "video_core/command_classes/nvdec_common.h"
10
11extern "C" {
12#if defined(__GNUC__) || defined(__clang__)
13#pragma GCC diagnostic ignored "-Wconversion"
14#endif
15#include <libavcodec/avcodec.h>
16#if defined(__GNUC__) || defined(__clang__)
17#pragma GCC diagnostic pop
18#endif
19}
20
21namespace Tegra {
22class GPU;
23struct VicRegisters;
24
25namespace Decoder {
26class H264;
27class VP9;
28} // namespace Decoder
29
30class Codec {
31public:
32 explicit Codec(GPU& gpu);
33 ~Codec();
34
35 /// Sets NVDEC video stream codec
36 void SetTargetCodec(NvdecCommon::VideoCodec codec);
37
38 /// Populate NvdecRegisters state with argument value at the provided offset
39 void StateWrite(u32 offset, u64 arguments);
40
41 /// Call decoders to construct headers, decode AVFrame with ffmpeg
42 void Decode();
43
44 /// Returns most recently decoded frame
45 AVFrame* GetCurrentFrame();
46 const AVFrame* GetCurrentFrame() const;
47
48 /// Returns the value of current_codec
49 NvdecCommon::VideoCodec GetCurrentCodec() const;
50
51private:
52 bool initialized{};
53 NvdecCommon::VideoCodec current_codec{NvdecCommon::VideoCodec::None};
54
55 AVCodec* av_codec{nullptr};
56 AVCodecContext* av_codec_ctx{nullptr};
57 AVFrame* av_frame{nullptr};
58
59 GPU& gpu;
60 std::unique_ptr<Decoder::H264> h264_decoder;
61 std::unique_ptr<Decoder::VP9> vp9_decoder;
62
63 NvdecCommon::NvdecRegisters state{};
64};
65
66} // namespace Tegra
diff --git a/src/video_core/command_classes/codecs/h264.cpp b/src/video_core/command_classes/codecs/h264.cpp
new file mode 100644
index 000000000..549a40f52
--- /dev/null
+++ b/src/video_core/command_classes/codecs/h264.cpp
@@ -0,0 +1,292 @@
1// MIT License
2//
3// Copyright (c) Ryujinx Team and Contributors
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
6// associated documentation files (the "Software"), to deal in the Software without restriction,
7// including without limitation the rights to use, copy, modify, merge, publish, distribute,
8// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
9// furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in all copies or
12// substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
15// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19//
20
21#include <array>
22#include "common/bit_util.h"
23#include "video_core/command_classes/codecs/h264.h"
24#include "video_core/gpu.h"
25#include "video_core/memory_manager.h"
26
27namespace Tegra::Decoder {
28namespace {
29// ZigZag LUTs from libavcodec.
30constexpr std::array<u8, 64> zig_zag_direct{
31 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48,
32 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23,
33 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63,
34};
35
36constexpr std::array<u8, 16> zig_zag_scan{
37 0 + 0 * 4, 1 + 0 * 4, 0 + 1 * 4, 0 + 2 * 4, 1 + 1 * 4, 2 + 0 * 4, 3 + 0 * 4, 2 + 1 * 4,
38 1 + 2 * 4, 0 + 3 * 4, 1 + 3 * 4, 2 + 2 * 4, 3 + 1 * 4, 3 + 2 * 4, 2 + 3 * 4, 3 + 3 * 4,
39};
40} // Anonymous namespace
41
42H264::H264(GPU& gpu_) : gpu(gpu_) {}
43
44H264::~H264() = default;
45
46std::vector<u8>& H264::ComposeFrameHeader(NvdecCommon::NvdecRegisters& state, bool is_first_frame) {
47 H264DecoderContext context{};
48 gpu.MemoryManager().ReadBlock(state.picture_info_offset, &context, sizeof(H264DecoderContext));
49
50 const s32 frame_number = static_cast<s32>((context.h264_parameter_set.flags >> 46) & 0x1ffff);
51 if (!is_first_frame && frame_number != 0) {
52 frame.resize(context.frame_data_size);
53
54 gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size());
55 } else {
56 /// Encode header
57 H264BitWriter writer{};
58 writer.WriteU(1, 24);
59 writer.WriteU(0, 1);
60 writer.WriteU(3, 2);
61 writer.WriteU(7, 5);
62 writer.WriteU(100, 8);
63 writer.WriteU(0, 8);
64 writer.WriteU(31, 8);
65 writer.WriteUe(0);
66 const auto chroma_format_idc =
67 static_cast<u32>((context.h264_parameter_set.flags >> 12) & 3);
68 writer.WriteUe(chroma_format_idc);
69 if (chroma_format_idc == 3) {
70 writer.WriteBit(false);
71 }
72
73 writer.WriteUe(0);
74 writer.WriteUe(0);
75 writer.WriteBit(false); // QpprimeYZeroTransformBypassFlag
76 writer.WriteBit(false); // Scaling matrix present flag
77
78 const auto order_cnt_type = static_cast<u32>((context.h264_parameter_set.flags >> 14) & 3);
79 writer.WriteUe(static_cast<u32>((context.h264_parameter_set.flags >> 8) & 0xf));
80 writer.WriteUe(order_cnt_type);
81 if (order_cnt_type == 0) {
82 writer.WriteUe(context.h264_parameter_set.log2_max_pic_order_cnt);
83 } else if (order_cnt_type == 1) {
84 writer.WriteBit(context.h264_parameter_set.delta_pic_order_always_zero_flag != 0);
85
86 writer.WriteSe(0);
87 writer.WriteSe(0);
88 writer.WriteUe(0);
89 }
90
91 const s32 pic_height = context.h264_parameter_set.pic_height_in_map_units /
92 (context.h264_parameter_set.frame_mbs_only_flag ? 1 : 2);
93
94 writer.WriteUe(16);
95 writer.WriteBit(false);
96 writer.WriteUe(context.h264_parameter_set.pic_width_in_mbs - 1);
97 writer.WriteUe(pic_height - 1);
98 writer.WriteBit(context.h264_parameter_set.frame_mbs_only_flag != 0);
99
100 if (!context.h264_parameter_set.frame_mbs_only_flag) {
101 writer.WriteBit(((context.h264_parameter_set.flags >> 0) & 1) != 0);
102 }
103
104 writer.WriteBit(((context.h264_parameter_set.flags >> 1) & 1) != 0);
105 writer.WriteBit(false); // Frame cropping flag
106 writer.WriteBit(false); // VUI parameter present flag
107
108 writer.End();
109
110 // H264 PPS
111 writer.WriteU(1, 24);
112 writer.WriteU(0, 1);
113 writer.WriteU(3, 2);
114 writer.WriteU(8, 5);
115
116 writer.WriteUe(0);
117 writer.WriteUe(0);
118
119 writer.WriteBit(context.h264_parameter_set.entropy_coding_mode_flag != 0);
120 writer.WriteBit(false);
121 writer.WriteUe(0);
122 writer.WriteUe(context.h264_parameter_set.num_refidx_l0_default_active);
123 writer.WriteUe(context.h264_parameter_set.num_refidx_l1_default_active);
124 writer.WriteBit(((context.h264_parameter_set.flags >> 2) & 1) != 0);
125 writer.WriteU(static_cast<s32>((context.h264_parameter_set.flags >> 32) & 0x3), 2);
126 s32 pic_init_qp = static_cast<s32>((context.h264_parameter_set.flags >> 16) & 0x3f);
127 pic_init_qp = (pic_init_qp << 26) >> 26;
128 writer.WriteSe(pic_init_qp);
129 writer.WriteSe(0);
130 s32 chroma_qp_index_offset =
131 static_cast<s32>((context.h264_parameter_set.flags >> 22) & 0x1f);
132 chroma_qp_index_offset = (chroma_qp_index_offset << 27) >> 27;
133
134 writer.WriteSe(chroma_qp_index_offset);
135 writer.WriteBit(context.h264_parameter_set.deblocking_filter_control_flag != 0);
136 writer.WriteBit(((context.h264_parameter_set.flags >> 3) & 1) != 0);
137 writer.WriteBit(context.h264_parameter_set.redundant_pic_count_flag != 0);
138 writer.WriteBit(context.h264_parameter_set.transform_8x8_mode_flag != 0);
139
140 writer.WriteBit(true);
141
142 for (s32 index = 0; index < 6; index++) {
143 writer.WriteBit(true);
144 const auto matrix_x4 =
145 std::vector<u8>(context.scaling_matrix_4.begin(), context.scaling_matrix_4.end());
146 writer.WriteScalingList(matrix_x4, index * 16, 16);
147 }
148
149 if (context.h264_parameter_set.transform_8x8_mode_flag) {
150 for (s32 index = 0; index < 2; index++) {
151 writer.WriteBit(true);
152 const auto matrix_x8 = std::vector<u8>(context.scaling_matrix_8.begin(),
153 context.scaling_matrix_8.end());
154
155 writer.WriteScalingList(matrix_x8, index * 64, 64);
156 }
157 }
158
159 s32 chroma_qp_index_offset2 =
160 static_cast<s32>((context.h264_parameter_set.flags >> 27) & 0x1f);
161 chroma_qp_index_offset2 = (chroma_qp_index_offset2 << 27) >> 27;
162
163 writer.WriteSe(chroma_qp_index_offset2);
164
165 writer.End();
166
167 const auto& encoded_header = writer.GetByteArray();
168 frame.resize(encoded_header.size() + context.frame_data_size);
169 std::memcpy(frame.data(), encoded_header.data(), encoded_header.size());
170
171 gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset,
172 frame.data() + encoded_header.size(),
173 context.frame_data_size);
174 }
175
176 return frame;
177}
178
179H264BitWriter::H264BitWriter() = default;
180
181H264BitWriter::~H264BitWriter() = default;
182
183void H264BitWriter::WriteU(s32 value, s32 value_sz) {
184 WriteBits(value, value_sz);
185}
186
187void H264BitWriter::WriteSe(s32 value) {
188 WriteExpGolombCodedInt(value);
189}
190
191void H264BitWriter::WriteUe(u32 value) {
192 WriteExpGolombCodedUInt(value);
193}
194
195void H264BitWriter::End() {
196 WriteBit(true);
197 Flush();
198}
199
200void H264BitWriter::WriteBit(bool state) {
201 WriteBits(state ? 1 : 0, 1);
202}
203
204void H264BitWriter::WriteScalingList(const std::vector<u8>& list, s32 start, s32 count) {
205 std::vector<u8> scan(count);
206 if (count == 16) {
207 std::memcpy(scan.data(), zig_zag_scan.data(), scan.size());
208 } else {
209 std::memcpy(scan.data(), zig_zag_direct.data(), scan.size());
210 }
211 u8 last_scale = 8;
212
213 for (s32 index = 0; index < count; index++) {
214 const u8 value = list[start + scan[index]];
215 const s32 delta_scale = static_cast<s32>(value - last_scale);
216
217 WriteSe(delta_scale);
218
219 last_scale = value;
220 }
221}
222
223std::vector<u8>& H264BitWriter::GetByteArray() {
224 return byte_array;
225}
226
227const std::vector<u8>& H264BitWriter::GetByteArray() const {
228 return byte_array;
229}
230
231void H264BitWriter::WriteBits(s32 value, s32 bit_count) {
232 s32 value_pos = 0;
233
234 s32 remaining = bit_count;
235
236 while (remaining > 0) {
237 s32 copy_size = remaining;
238
239 const s32 free_bits = GetFreeBufferBits();
240
241 if (copy_size > free_bits) {
242 copy_size = free_bits;
243 }
244
245 const s32 mask = (1 << copy_size) - 1;
246
247 const s32 src_shift = (bit_count - value_pos) - copy_size;
248 const s32 dst_shift = (buffer_size - buffer_pos) - copy_size;
249
250 buffer |= ((value >> src_shift) & mask) << dst_shift;
251
252 value_pos += copy_size;
253 buffer_pos += copy_size;
254 remaining -= copy_size;
255 }
256}
257
258void H264BitWriter::WriteExpGolombCodedInt(s32 value) {
259 const s32 sign = value <= 0 ? 0 : 1;
260 if (value < 0) {
261 value = -value;
262 }
263 value = (value << 1) - sign;
264 WriteExpGolombCodedUInt(value);
265}
266
267void H264BitWriter::WriteExpGolombCodedUInt(u32 value) {
268 const s32 size = 32 - Common::CountLeadingZeroes32(static_cast<s32>(value + 1));
269 WriteBits(1, size);
270
271 value -= (1U << (size - 1)) - 1;
272 WriteBits(static_cast<s32>(value), size - 1);
273}
274
275s32 H264BitWriter::GetFreeBufferBits() {
276 if (buffer_pos == buffer_size) {
277 Flush();
278 }
279
280 return buffer_size - buffer_pos;
281}
282
283void H264BitWriter::Flush() {
284 if (buffer_pos == 0) {
285 return;
286 }
287 byte_array.push_back(static_cast<u8>(buffer));
288
289 buffer = 0;
290 buffer_pos = 0;
291}
292} // namespace Tegra::Decoder
diff --git a/src/video_core/command_classes/codecs/h264.h b/src/video_core/command_classes/codecs/h264.h
new file mode 100644
index 000000000..f2292fd2f
--- /dev/null
+++ b/src/video_core/command_classes/codecs/h264.h
@@ -0,0 +1,118 @@
1// MIT License
2//
3// Copyright (c) Ryujinx Team and Contributors
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
6// associated documentation files (the "Software"), to deal in the Software without restriction,
7// including without limitation the rights to use, copy, modify, merge, publish, distribute,
8// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
9// furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in all copies or
12// substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
15// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19//
20
21#pragma once
22
23#include <vector>
24#include "common/common_funcs.h"
25#include "common/common_types.h"
26#include "video_core/command_classes/nvdec_common.h"
27
28namespace Tegra {
29class GPU;
30namespace Decoder {
31
32class H264BitWriter {
33public:
34 H264BitWriter();
35 ~H264BitWriter();
36
37 /// The following Write methods are based on clause 9.1 in the H.264 specification.
38 /// WriteSe and WriteUe write in the Exp-Golomb-coded syntax
39 void WriteU(s32 value, s32 value_sz);
40 void WriteSe(s32 value);
41 void WriteUe(u32 value);
42
43 /// Finalize the bitstream
44 void End();
45
46 /// append a bit to the stream, equivalent value to the state parameter
47 void WriteBit(bool state);
48
49 /// Based on section 7.3.2.1.1.1 and Table 7-4 in the H.264 specification
50 /// Writes the scaling matrices of the sream
51 void WriteScalingList(const std::vector<u8>& list, s32 start, s32 count);
52
53 /// Return the bitstream as a vector.
54 std::vector<u8>& GetByteArray();
55 const std::vector<u8>& GetByteArray() const;
56
57private:
58 void WriteBits(s32 value, s32 bit_count);
59 void WriteExpGolombCodedInt(s32 value);
60 void WriteExpGolombCodedUInt(u32 value);
61 s32 GetFreeBufferBits();
62 void Flush();
63
64 s32 buffer_size{8};
65
66 s32 buffer{};
67 s32 buffer_pos{};
68 std::vector<u8> byte_array;
69};
70
71class H264 {
72public:
73 explicit H264(GPU& gpu);
74 ~H264();
75
76 /// Compose the H264 header of the frame for FFmpeg decoding
77 std::vector<u8>& ComposeFrameHeader(NvdecCommon::NvdecRegisters& state,
78 bool is_first_frame = false);
79
80private:
81 struct H264ParameterSet {
82 u32 log2_max_pic_order_cnt{};
83 u32 delta_pic_order_always_zero_flag{};
84 u32 frame_mbs_only_flag{};
85 u32 pic_width_in_mbs{};
86 u32 pic_height_in_map_units{};
87 INSERT_PADDING_WORDS(1);
88 u32 entropy_coding_mode_flag{};
89 u32 bottom_field_pic_order_flag{};
90 u32 num_refidx_l0_default_active{};
91 u32 num_refidx_l1_default_active{};
92 u32 deblocking_filter_control_flag{};
93 u32 redundant_pic_count_flag{};
94 u32 transform_8x8_mode_flag{};
95 INSERT_PADDING_WORDS(9);
96 u64 flags{};
97 u32 frame_number{};
98 u32 frame_number2{};
99 };
100 static_assert(sizeof(H264ParameterSet) == 0x68, "H264ParameterSet is an invalid size");
101
102 struct H264DecoderContext {
103 INSERT_PADDING_BYTES(0x48);
104 u32 frame_data_size{};
105 INSERT_PADDING_BYTES(0xc);
106 H264ParameterSet h264_parameter_set{};
107 INSERT_PADDING_BYTES(0x100);
108 std::array<u8, 0x60> scaling_matrix_4;
109 std::array<u8, 0x80> scaling_matrix_8;
110 };
111 static_assert(sizeof(H264DecoderContext) == 0x2a0, "H264DecoderContext is an invalid size");
112
113 std::vector<u8> frame;
114 GPU& gpu;
115};
116
117} // namespace Decoder
118} // namespace Tegra
diff --git a/src/video_core/command_classes/codecs/vp9.cpp b/src/video_core/command_classes/codecs/vp9.cpp
new file mode 100644
index 000000000..b3e98aa9f
--- /dev/null
+++ b/src/video_core/command_classes/codecs/vp9.cpp
@@ -0,0 +1,1034 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring> // for std::memcpy
6#include <numeric>
7#include "video_core/command_classes/codecs/vp9.h"
8#include "video_core/gpu.h"
9#include "video_core/memory_manager.h"
10
11namespace Tegra::Decoder {
12namespace {
13// Default compressed header probabilities once frame context resets
14constexpr Vp9EntropyProbs default_probs{
15 .y_mode_prob{
16 65, 32, 18, 144, 162, 194, 41, 51, 98, 132, 68, 18, 165, 217, 196, 45, 40, 78,
17 173, 80, 19, 176, 240, 193, 64, 35, 46, 221, 135, 38, 194, 248, 121, 96, 85, 29,
18 },
19 .partition_prob{
20 199, 122, 141, 0, 147, 63, 159, 0, 148, 133, 118, 0, 121, 104, 114, 0,
21 174, 73, 87, 0, 92, 41, 83, 0, 82, 99, 50, 0, 53, 39, 39, 0,
22 177, 58, 59, 0, 68, 26, 63, 0, 52, 79, 25, 0, 17, 14, 12, 0,
23 222, 34, 30, 0, 72, 16, 44, 0, 58, 32, 12, 0, 10, 7, 6, 0,
24 },
25 .coef_probs{
26 195, 29, 183, 0, 84, 49, 136, 0, 8, 42, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0,
27 0, 0, 0, 0, 31, 107, 169, 0, 35, 99, 159, 0, 17, 82, 140, 0, 8, 66, 114, 0,
28 2, 44, 76, 0, 1, 19, 32, 0, 40, 132, 201, 0, 29, 114, 187, 0, 13, 91, 157, 0,
29 7, 75, 127, 0, 3, 58, 95, 0, 1, 28, 47, 0, 69, 142, 221, 0, 42, 122, 201, 0,
30 15, 91, 159, 0, 6, 67, 121, 0, 1, 42, 77, 0, 1, 17, 31, 0, 102, 148, 228, 0,
31 67, 117, 204, 0, 17, 82, 154, 0, 6, 59, 114, 0, 2, 39, 75, 0, 1, 15, 29, 0,
32 156, 57, 233, 0, 119, 57, 212, 0, 58, 48, 163, 0, 29, 40, 124, 0, 12, 30, 81, 0,
33 3, 12, 31, 0, 191, 107, 226, 0, 124, 117, 204, 0, 25, 99, 155, 0, 0, 0, 0, 0,
34 0, 0, 0, 0, 0, 0, 0, 0, 29, 148, 210, 0, 37, 126, 194, 0, 8, 93, 157, 0,
35 2, 68, 118, 0, 1, 39, 69, 0, 1, 17, 33, 0, 41, 151, 213, 0, 27, 123, 193, 0,
36 3, 82, 144, 0, 1, 58, 105, 0, 1, 32, 60, 0, 1, 13, 26, 0, 59, 159, 220, 0,
37 23, 126, 198, 0, 4, 88, 151, 0, 1, 66, 114, 0, 1, 38, 71, 0, 1, 18, 34, 0,
38 114, 136, 232, 0, 51, 114, 207, 0, 11, 83, 155, 0, 3, 56, 105, 0, 1, 33, 65, 0,
39 1, 17, 34, 0, 149, 65, 234, 0, 121, 57, 215, 0, 61, 49, 166, 0, 28, 36, 114, 0,
40 12, 25, 76, 0, 3, 16, 42, 0, 214, 49, 220, 0, 132, 63, 188, 0, 42, 65, 137, 0,
41 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 137, 221, 0, 104, 131, 216, 0,
42 49, 111, 192, 0, 21, 87, 155, 0, 2, 49, 87, 0, 1, 16, 28, 0, 89, 163, 230, 0,
43 90, 137, 220, 0, 29, 100, 183, 0, 10, 70, 135, 0, 2, 42, 81, 0, 1, 17, 33, 0,
44 108, 167, 237, 0, 55, 133, 222, 0, 15, 97, 179, 0, 4, 72, 135, 0, 1, 45, 85, 0,
45 1, 19, 38, 0, 124, 146, 240, 0, 66, 124, 224, 0, 17, 88, 175, 0, 4, 58, 122, 0,
46 1, 36, 75, 0, 1, 18, 37, 0, 141, 79, 241, 0, 126, 70, 227, 0, 66, 58, 182, 0,
47 30, 44, 136, 0, 12, 34, 96, 0, 2, 20, 47, 0, 229, 99, 249, 0, 143, 111, 235, 0,
48 46, 109, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 158, 236, 0,
49 94, 146, 224, 0, 25, 117, 191, 0, 9, 87, 149, 0, 3, 56, 99, 0, 1, 33, 57, 0,
50 83, 167, 237, 0, 68, 145, 222, 0, 10, 103, 177, 0, 2, 72, 131, 0, 1, 41, 79, 0,
51 1, 20, 39, 0, 99, 167, 239, 0, 47, 141, 224, 0, 10, 104, 178, 0, 2, 73, 133, 0,
52 1, 44, 85, 0, 1, 22, 47, 0, 127, 145, 243, 0, 71, 129, 228, 0, 17, 93, 177, 0,
53 3, 61, 124, 0, 1, 41, 84, 0, 1, 21, 52, 0, 157, 78, 244, 0, 140, 72, 231, 0,
54 69, 58, 184, 0, 31, 44, 137, 0, 14, 38, 105, 0, 8, 23, 61, 0, 125, 34, 187, 0,
55 52, 41, 133, 0, 6, 31, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
56 37, 109, 153, 0, 51, 102, 147, 0, 23, 87, 128, 0, 8, 67, 101, 0, 1, 41, 63, 0,
57 1, 19, 29, 0, 31, 154, 185, 0, 17, 127, 175, 0, 6, 96, 145, 0, 2, 73, 114, 0,
58 1, 51, 82, 0, 1, 28, 45, 0, 23, 163, 200, 0, 10, 131, 185, 0, 2, 93, 148, 0,
59 1, 67, 111, 0, 1, 41, 69, 0, 1, 14, 24, 0, 29, 176, 217, 0, 12, 145, 201, 0,
60 3, 101, 156, 0, 1, 69, 111, 0, 1, 39, 63, 0, 1, 14, 23, 0, 57, 192, 233, 0,
61 25, 154, 215, 0, 6, 109, 167, 0, 3, 78, 118, 0, 1, 48, 69, 0, 1, 21, 29, 0,
62 202, 105, 245, 0, 108, 106, 216, 0, 18, 90, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0,
63 0, 0, 0, 0, 33, 172, 219, 0, 64, 149, 206, 0, 14, 117, 177, 0, 5, 90, 141, 0,
64 2, 61, 95, 0, 1, 37, 57, 0, 33, 179, 220, 0, 11, 140, 198, 0, 1, 89, 148, 0,
65 1, 60, 104, 0, 1, 33, 57, 0, 1, 12, 21, 0, 30, 181, 221, 0, 8, 141, 198, 0,
66 1, 87, 145, 0, 1, 58, 100, 0, 1, 31, 55, 0, 1, 12, 20, 0, 32, 186, 224, 0,
67 7, 142, 198, 0, 1, 86, 143, 0, 1, 58, 100, 0, 1, 31, 55, 0, 1, 12, 22, 0,
68 57, 192, 227, 0, 20, 143, 204, 0, 3, 96, 154, 0, 1, 68, 112, 0, 1, 42, 69, 0,
69 1, 19, 32, 0, 212, 35, 215, 0, 113, 47, 169, 0, 29, 48, 105, 0, 0, 0, 0, 0,
70 0, 0, 0, 0, 0, 0, 0, 0, 74, 129, 203, 0, 106, 120, 203, 0, 49, 107, 178, 0,
71 19, 84, 144, 0, 4, 50, 84, 0, 1, 15, 25, 0, 71, 172, 217, 0, 44, 141, 209, 0,
72 15, 102, 173, 0, 6, 76, 133, 0, 2, 51, 89, 0, 1, 24, 42, 0, 64, 185, 231, 0,
73 31, 148, 216, 0, 8, 103, 175, 0, 3, 74, 131, 0, 1, 46, 81, 0, 1, 18, 30, 0,
74 65, 196, 235, 0, 25, 157, 221, 0, 5, 105, 174, 0, 1, 67, 120, 0, 1, 38, 69, 0,
75 1, 15, 30, 0, 65, 204, 238, 0, 30, 156, 224, 0, 7, 107, 177, 0, 2, 70, 124, 0,
76 1, 42, 73, 0, 1, 18, 34, 0, 225, 86, 251, 0, 144, 104, 235, 0, 42, 99, 181, 0,
77 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 175, 239, 0, 112, 165, 229, 0,
78 29, 136, 200, 0, 12, 103, 162, 0, 6, 77, 123, 0, 2, 53, 84, 0, 75, 183, 239, 0,
79 30, 155, 221, 0, 3, 106, 171, 0, 1, 74, 128, 0, 1, 44, 76, 0, 1, 17, 28, 0,
80 73, 185, 240, 0, 27, 159, 222, 0, 2, 107, 172, 0, 1, 75, 127, 0, 1, 42, 73, 0,
81 1, 17, 29, 0, 62, 190, 238, 0, 21, 159, 222, 0, 2, 107, 172, 0, 1, 72, 122, 0,
82 1, 40, 71, 0, 1, 18, 32, 0, 61, 199, 240, 0, 27, 161, 226, 0, 4, 113, 180, 0,
83 1, 76, 129, 0, 1, 46, 80, 0, 1, 23, 41, 0, 7, 27, 153, 0, 5, 30, 95, 0,
84 1, 16, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 75, 127, 0,
85 57, 75, 124, 0, 27, 67, 108, 0, 10, 54, 86, 0, 1, 33, 52, 0, 1, 12, 18, 0,
86 43, 125, 151, 0, 26, 108, 148, 0, 7, 83, 122, 0, 2, 59, 89, 0, 1, 38, 60, 0,
87 1, 17, 27, 0, 23, 144, 163, 0, 13, 112, 154, 0, 2, 75, 117, 0, 1, 50, 81, 0,
88 1, 31, 51, 0, 1, 14, 23, 0, 18, 162, 185, 0, 6, 123, 171, 0, 1, 78, 125, 0,
89 1, 51, 86, 0, 1, 31, 54, 0, 1, 14, 23, 0, 15, 199, 227, 0, 3, 150, 204, 0,
90 1, 91, 146, 0, 1, 55, 95, 0, 1, 30, 53, 0, 1, 11, 20, 0, 19, 55, 240, 0,
91 19, 59, 196, 0, 3, 52, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
92 41, 166, 207, 0, 104, 153, 199, 0, 31, 123, 181, 0, 14, 101, 152, 0, 5, 72, 106, 0,
93 1, 36, 52, 0, 35, 176, 211, 0, 12, 131, 190, 0, 2, 88, 144, 0, 1, 60, 101, 0,
94 1, 36, 60, 0, 1, 16, 28, 0, 28, 183, 213, 0, 8, 134, 191, 0, 1, 86, 142, 0,
95 1, 56, 96, 0, 1, 30, 53, 0, 1, 12, 20, 0, 20, 190, 215, 0, 4, 135, 192, 0,
96 1, 84, 139, 0, 1, 53, 91, 0, 1, 28, 49, 0, 1, 11, 20, 0, 13, 196, 216, 0,
97 2, 137, 192, 0, 1, 86, 143, 0, 1, 57, 99, 0, 1, 32, 56, 0, 1, 13, 24, 0,
98 211, 29, 217, 0, 96, 47, 156, 0, 22, 43, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0,
99 0, 0, 0, 0, 78, 120, 193, 0, 111, 116, 186, 0, 46, 102, 164, 0, 15, 80, 128, 0,
100 2, 49, 76, 0, 1, 18, 28, 0, 71, 161, 203, 0, 42, 132, 192, 0, 10, 98, 150, 0,
101 3, 69, 109, 0, 1, 44, 70, 0, 1, 18, 29, 0, 57, 186, 211, 0, 30, 140, 196, 0,
102 4, 93, 146, 0, 1, 62, 102, 0, 1, 38, 65, 0, 1, 16, 27, 0, 47, 199, 217, 0,
103 14, 145, 196, 0, 1, 88, 142, 0, 1, 57, 98, 0, 1, 36, 62, 0, 1, 15, 26, 0,
104 26, 219, 229, 0, 5, 155, 207, 0, 1, 94, 151, 0, 1, 60, 104, 0, 1, 36, 62, 0,
105 1, 16, 28, 0, 233, 29, 248, 0, 146, 47, 220, 0, 43, 52, 140, 0, 0, 0, 0, 0,
106 0, 0, 0, 0, 0, 0, 0, 0, 100, 163, 232, 0, 179, 161, 222, 0, 63, 142, 204, 0,
107 37, 113, 174, 0, 26, 89, 137, 0, 18, 68, 97, 0, 85, 181, 230, 0, 32, 146, 209, 0,
108 7, 100, 164, 0, 3, 71, 121, 0, 1, 45, 77, 0, 1, 18, 30, 0, 65, 187, 230, 0,
109 20, 148, 207, 0, 2, 97, 159, 0, 1, 68, 116, 0, 1, 40, 70, 0, 1, 14, 29, 0,
110 40, 194, 227, 0, 8, 147, 204, 0, 1, 94, 155, 0, 1, 65, 112, 0, 1, 39, 66, 0,
111 1, 14, 26, 0, 16, 208, 228, 0, 3, 151, 207, 0, 1, 98, 160, 0, 1, 67, 117, 0,
112 1, 41, 74, 0, 1, 17, 31, 0, 17, 38, 140, 0, 7, 34, 80, 0, 1, 17, 29, 0,
113 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 75, 128, 0, 41, 76, 128, 0,
114 26, 66, 116, 0, 12, 52, 94, 0, 2, 32, 55, 0, 1, 10, 16, 0, 50, 127, 154, 0,
115 37, 109, 152, 0, 16, 82, 121, 0, 5, 59, 85, 0, 1, 35, 54, 0, 1, 13, 20, 0,
116 40, 142, 167, 0, 17, 110, 157, 0, 2, 71, 112, 0, 1, 44, 72, 0, 1, 27, 45, 0,
117 1, 11, 17, 0, 30, 175, 188, 0, 9, 124, 169, 0, 1, 74, 116, 0, 1, 48, 78, 0,
118 1, 30, 49, 0, 1, 11, 18, 0, 10, 222, 223, 0, 2, 150, 194, 0, 1, 83, 128, 0,
119 1, 48, 79, 0, 1, 27, 45, 0, 1, 11, 17, 0, 36, 41, 235, 0, 29, 36, 193, 0,
120 10, 27, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 165, 222, 0,
121 177, 162, 215, 0, 110, 135, 195, 0, 57, 113, 168, 0, 23, 83, 120, 0, 10, 49, 61, 0,
122 85, 190, 223, 0, 36, 139, 200, 0, 5, 90, 146, 0, 1, 60, 103, 0, 1, 38, 65, 0,
123 1, 18, 30, 0, 72, 202, 223, 0, 23, 141, 199, 0, 2, 86, 140, 0, 1, 56, 97, 0,
124 1, 36, 61, 0, 1, 16, 27, 0, 55, 218, 225, 0, 13, 145, 200, 0, 1, 86, 141, 0,
125 1, 57, 99, 0, 1, 35, 61, 0, 1, 13, 22, 0, 15, 235, 212, 0, 1, 132, 184, 0,
126 1, 84, 139, 0, 1, 57, 97, 0, 1, 34, 56, 0, 1, 14, 23, 0, 181, 21, 201, 0,
127 61, 37, 123, 0, 10, 38, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
128 47, 106, 172, 0, 95, 104, 173, 0, 42, 93, 159, 0, 18, 77, 131, 0, 4, 50, 81, 0,
129 1, 17, 23, 0, 62, 147, 199, 0, 44, 130, 189, 0, 28, 102, 154, 0, 18, 75, 115, 0,
130 2, 44, 65, 0, 1, 12, 19, 0, 55, 153, 210, 0, 24, 130, 194, 0, 3, 93, 146, 0,
131 1, 61, 97, 0, 1, 31, 50, 0, 1, 10, 16, 0, 49, 186, 223, 0, 17, 148, 204, 0,
132 1, 96, 142, 0, 1, 53, 83, 0, 1, 26, 44, 0, 1, 11, 17, 0, 13, 217, 212, 0,
133 2, 136, 180, 0, 1, 78, 124, 0, 1, 50, 83, 0, 1, 29, 49, 0, 1, 14, 23, 0,
134 197, 13, 247, 0, 82, 17, 222, 0, 25, 17, 162, 0, 0, 0, 0, 0, 0, 0, 0, 0,
135 0, 0, 0, 0, 126, 186, 247, 0, 234, 191, 243, 0, 176, 177, 234, 0, 104, 158, 220, 0,
136 66, 128, 186, 0, 55, 90, 137, 0, 111, 197, 242, 0, 46, 158, 219, 0, 9, 104, 171, 0,
137 2, 65, 125, 0, 1, 44, 80, 0, 1, 17, 91, 0, 104, 208, 245, 0, 39, 168, 224, 0,
138 3, 109, 162, 0, 1, 79, 124, 0, 1, 50, 102, 0, 1, 43, 102, 0, 84, 220, 246, 0,
139 31, 177, 231, 0, 2, 115, 180, 0, 1, 79, 134, 0, 1, 55, 77, 0, 1, 60, 79, 0,
140 43, 243, 240, 0, 8, 180, 217, 0, 1, 115, 166, 0, 1, 84, 121, 0, 1, 51, 67, 0,
141 1, 16, 6, 0,
142 },
143 .switchable_interp_prob{235, 162, 36, 255, 34, 3, 149, 144},
144 .inter_mode_prob{
145 2, 173, 34, 0, 7, 145, 85, 0, 7, 166, 63, 0, 7, 94,
146 66, 0, 8, 64, 46, 0, 17, 81, 31, 0, 25, 29, 30, 0,
147 },
148 .intra_inter_prob{9, 102, 187, 225},
149 .comp_inter_prob{9, 102, 187, 225, 0},
150 .single_ref_prob{33, 16, 77, 74, 142, 142, 172, 170, 238, 247},
151 .comp_ref_prob{50, 126, 123, 221, 226},
152 .tx_32x32_prob{3, 136, 37, 5, 52, 13},
153 .tx_16x16_prob{20, 152, 15, 101},
154 .tx_8x8_prob{100, 66},
155 .skip_probs{192, 128, 64},
156 .joints{32, 64, 96},
157 .sign{128, 128},
158 .classes{
159 224, 144, 192, 168, 192, 176, 192, 198, 198, 245,
160 216, 128, 176, 160, 176, 176, 192, 198, 198, 208,
161 },
162 .class_0{216, 208},
163 .prob_bits{
164 136, 140, 148, 160, 176, 192, 224, 234, 234, 240,
165 136, 140, 148, 160, 176, 192, 224, 234, 234, 240,
166 },
167 .class_0_fr{128, 128, 64, 96, 112, 64, 128, 128, 64, 96, 112, 64},
168 .fr{64, 96, 64, 64, 96, 64},
169 .class_0_hp{160, 160},
170 .high_precision{128, 128},
171};
172
173constexpr std::array<s32, 256> norm_lut{
174 0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
175 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
176 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
177 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
178 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
179 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
180 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
181 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
182};
183
184constexpr std::array<s32, 254> map_lut{
185 20, 21, 22, 23, 24, 25, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
186 1, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 2, 50, 51, 52, 53, 54,
187 55, 56, 57, 58, 59, 60, 61, 3, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
188 73, 4, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 5, 86, 87, 88, 89,
189 90, 91, 92, 93, 94, 95, 96, 97, 6, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
190 108, 109, 7, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 8, 122, 123, 124,
191 125, 126, 127, 128, 129, 130, 131, 132, 133, 9, 134, 135, 136, 137, 138, 139, 140, 141, 142,
192 143, 144, 145, 10, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 11, 158, 159,
193 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 12, 170, 171, 172, 173, 174, 175, 176, 177,
194 178, 179, 180, 181, 13, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 14, 194,
195 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 15, 206, 207, 208, 209, 210, 211, 212,
196 213, 214, 215, 216, 217, 16, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 17,
197 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 18, 242, 243, 244, 245, 246, 247,
198 248, 249, 250, 251, 252, 253, 19,
199};
200} // Anonymous namespace
201
202VP9::VP9(GPU& gpu) : gpu(gpu) {}
203
204VP9::~VP9() = default;
205
206void VP9::WriteProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_prob) {
207 const bool update = new_prob != old_prob;
208
209 writer.Write(update, diff_update_probability);
210
211 if (update) {
212 WriteProbabilityDelta(writer, new_prob, old_prob);
213 }
214}
215template <typename T, std::size_t N>
216void VP9::WriteProbabilityUpdate(VpxRangeEncoder& writer, const std::array<T, N>& new_prob,
217 const std::array<T, N>& old_prob) {
218 for (std::size_t offset = 0; offset < new_prob.size(); ++offset) {
219 WriteProbabilityUpdate(writer, new_prob[offset], old_prob[offset]);
220 }
221}
222
223template <typename T, std::size_t N>
224void VP9::WriteProbabilityUpdateAligned4(VpxRangeEncoder& writer, const std::array<T, N>& new_prob,
225 const std::array<T, N>& old_prob) {
226 for (std::size_t offset = 0; offset < new_prob.size(); offset += 4) {
227 WriteProbabilityUpdate(writer, new_prob[offset + 0], old_prob[offset + 0]);
228 WriteProbabilityUpdate(writer, new_prob[offset + 1], old_prob[offset + 1]);
229 WriteProbabilityUpdate(writer, new_prob[offset + 2], old_prob[offset + 2]);
230 }
231}
232
233void VP9::WriteProbabilityDelta(VpxRangeEncoder& writer, u8 new_prob, u8 old_prob) {
234 const int delta = RemapProbability(new_prob, old_prob);
235
236 EncodeTermSubExp(writer, delta);
237}
238
239s32 VP9::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
255s32 VP9::RecenterNonNeg(s32 new_prob, s32 old_prob) {
256 if (new_prob > old_prob * 2) {
257 return new_prob;
258 } else if (new_prob >= old_prob) {
259 return (new_prob - old_prob) * 2;
260 } else {
261 return (old_prob - new_prob) * 2 - 1;
262 }
263}
264
265void VP9::EncodeTermSubExp(VpxRangeEncoder& writer, s32 value) {
266 if (WriteLessThan(writer, value, 16)) {
267 writer.Write(value, 4);
268 } else if (WriteLessThan(writer, value, 32)) {
269 writer.Write(value - 16, 4);
270 } else if (WriteLessThan(writer, value, 64)) {
271 writer.Write(value - 32, 5);
272 } else {
273 value -= 64;
274
275 constexpr s32 size = 8;
276
277 const s32 mask = (1 << size) - 191;
278
279 const s32 delta = value - mask;
280
281 if (delta < 0) {
282 writer.Write(value, size - 1);
283 } else {
284 writer.Write(delta / 2 + mask, size - 1);
285 writer.Write(delta & 1, 1);
286 }
287 }
288}
289
290bool VP9::WriteLessThan(VpxRangeEncoder& writer, s32 value, s32 test) {
291 const bool is_lt = value < test;
292 writer.Write(!is_lt);
293 return is_lt;
294}
295
296void VP9::WriteCoefProbabilityUpdate(VpxRangeEncoder& writer, s32 tx_mode,
297 const std::array<u8, 2304>& new_prob,
298 const std::array<u8, 2304>& old_prob) {
299 // Note: There's 1 byte added on each packet for alignment,
300 // this byte is ignored when doing updates.
301 constexpr s32 block_bytes = 2 * 2 * 6 * 6 * 4;
302
303 const auto needs_update = [&](s32 base_index) -> bool {
304 s32 index = base_index;
305 for (s32 i = 0; i < 2; i++) {
306 for (s32 j = 0; j < 2; j++) {
307 for (s32 k = 0; k < 6; k++) {
308 for (s32 l = 0; l < 6; l++) {
309 if (new_prob[index + 0] != old_prob[index + 0] ||
310 new_prob[index + 1] != old_prob[index + 1] ||
311 new_prob[index + 2] != old_prob[index + 2]) {
312 return true;
313 }
314
315 index += 4;
316 }
317 }
318 }
319 }
320 return false;
321 };
322
323 for (s32 block_index = 0; block_index < 4; block_index++) {
324 const s32 base_index = block_index * block_bytes;
325 const bool update = needs_update(base_index);
326 writer.Write(update);
327
328 if (update) {
329 s32 index = base_index;
330 for (s32 i = 0; i < 2; i++) {
331 for (s32 j = 0; j < 2; j++) {
332 for (s32 k = 0; k < 6; k++) {
333 for (s32 l = 0; l < 6; l++) {
334 if (k != 0 || l < 3) {
335 WriteProbabilityUpdate(writer, new_prob[index + 0],
336 old_prob[index + 0]);
337 WriteProbabilityUpdate(writer, new_prob[index + 1],
338 old_prob[index + 1]);
339 WriteProbabilityUpdate(writer, new_prob[index + 2],
340 old_prob[index + 2]);
341 }
342 index += 4;
343 }
344 }
345 }
346 }
347 }
348
349 if (block_index == tx_mode) {
350 break;
351 }
352 }
353}
354
355void VP9::WriteMvProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_prob) {
356 const bool update = new_prob != old_prob;
357 writer.Write(update, diff_update_probability);
358
359 if (update) {
360 writer.Write(new_prob >> 1, 7);
361 }
362}
363
364s32 VP9::CalcMinLog2TileCols(s32 frame_width) {
365 const s32 sb64_cols = (frame_width + 63) / 64;
366 s32 min_log2 = 0;
367
368 while ((64 << min_log2) < sb64_cols) {
369 min_log2++;
370 }
371
372 return min_log2;
373}
374
375s32 VP9::CalcMaxLog2TileCols(s32 frameWidth) {
376 const s32 sb64_cols = (frameWidth + 63) / 64;
377 s32 max_log2 = 1;
378
379 while ((sb64_cols >> max_log2) >= 4) {
380 max_log2++;
381 }
382
383 return max_log2 - 1;
384}
385
386Vp9PictureInfo VP9::GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state) {
387 PictureInfo picture_info{};
388 gpu.MemoryManager().ReadBlock(state.picture_info_offset, &picture_info, sizeof(PictureInfo));
389 Vp9PictureInfo vp9_info = picture_info.Convert();
390
391 InsertEntropy(state.vp9_entropy_probs_offset, vp9_info.entropy);
392
393 // surface_luma_offset[0:3] contains the address of the reference frame offsets in the following
394 // order: last, golden, altref, current. It may be worthwhile to track the updates done here
395 // to avoid buffering frame data needed for reference frame updating in the header composition.
396 std::memcpy(vp9_info.frame_offsets.data(), state.surface_luma_offset.data(), 4 * sizeof(u64));
397
398 return vp9_info;
399}
400
401void VP9::InsertEntropy(u64 offset, Vp9EntropyProbs& dst) {
402 EntropyProbs entropy{};
403 gpu.MemoryManager().ReadBlock(offset, &entropy, sizeof(EntropyProbs));
404 entropy.Convert(dst);
405}
406
407Vp9FrameContainer VP9::GetCurrentFrame(const NvdecCommon::NvdecRegisters& state) {
408 Vp9FrameContainer frame{};
409 {
410 gpu.SyncGuestHost();
411 frame.info = GetVp9PictureInfo(state);
412
413 frame.bit_stream.resize(frame.info.bitstream_size);
414 gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.bit_stream.data(),
415 frame.info.bitstream_size);
416 }
417 // Buffer two frames, saving the last show frame info
418 if (!next_next_frame.bit_stream.empty()) {
419 Vp9FrameContainer temp{
420 .info = frame.info,
421 .bit_stream = frame.bit_stream,
422 };
423 next_next_frame.info.show_frame = frame.info.last_frame_shown;
424 frame.info = next_next_frame.info;
425 frame.bit_stream = next_next_frame.bit_stream;
426 next_next_frame = std::move(temp);
427
428 if (!next_frame.bit_stream.empty()) {
429 Vp9FrameContainer temp2{
430 .info = frame.info,
431 .bit_stream = frame.bit_stream,
432 };
433 next_frame.info.show_frame = frame.info.last_frame_shown;
434 frame.info = next_frame.info;
435 frame.bit_stream = next_frame.bit_stream;
436 next_frame = std::move(temp2);
437 } else {
438 next_frame.info = frame.info;
439 next_frame.bit_stream = frame.bit_stream;
440 }
441 } else {
442 next_next_frame.info = frame.info;
443 next_next_frame.bit_stream = frame.bit_stream;
444 }
445 return frame;
446}
447
448std::vector<u8> VP9::ComposeCompressedHeader() {
449 VpxRangeEncoder writer{};
450
451 if (!current_frame_info.lossless) {
452 if (static_cast<u32>(current_frame_info.transform_mode) >= 3) {
453 writer.Write(3, 2);
454 writer.Write(current_frame_info.transform_mode == 4);
455 } else {
456 writer.Write(current_frame_info.transform_mode, 2);
457 }
458 }
459
460 if (current_frame_info.transform_mode == 4) {
461 // tx_mode_probs() in the spec
462 WriteProbabilityUpdate(writer, current_frame_info.entropy.tx_8x8_prob,
463 prev_frame_probs.tx_8x8_prob);
464 WriteProbabilityUpdate(writer, current_frame_info.entropy.tx_16x16_prob,
465 prev_frame_probs.tx_16x16_prob);
466 WriteProbabilityUpdate(writer, current_frame_info.entropy.tx_32x32_prob,
467 prev_frame_probs.tx_32x32_prob);
468 if (current_frame_info.show_frame && !current_frame_info.is_key_frame) {
469 prev_frame_probs.tx_8x8_prob = current_frame_info.entropy.tx_8x8_prob;
470 prev_frame_probs.tx_16x16_prob = current_frame_info.entropy.tx_16x16_prob;
471 prev_frame_probs.tx_32x32_prob = current_frame_info.entropy.tx_32x32_prob;
472 }
473 }
474 // read_coef_probs() in the spec
475 WriteCoefProbabilityUpdate(writer, current_frame_info.transform_mode,
476 current_frame_info.entropy.coef_probs, prev_frame_probs.coef_probs);
477 // read_skip_probs() in the spec
478 WriteProbabilityUpdate(writer, current_frame_info.entropy.skip_probs,
479 prev_frame_probs.skip_probs);
480
481 if (current_frame_info.show_frame && !current_frame_info.is_key_frame) {
482 prev_frame_probs.coef_probs = current_frame_info.entropy.coef_probs;
483 prev_frame_probs.skip_probs = current_frame_info.entropy.skip_probs;
484 }
485
486 if (!current_frame_info.intra_only) {
487 // read_inter_probs() in the spec
488 WriteProbabilityUpdateAligned4(writer, current_frame_info.entropy.inter_mode_prob,
489 prev_frame_probs.inter_mode_prob);
490 if (current_frame_info.show_frame && !current_frame_info.is_key_frame) {
491 prev_frame_probs.inter_mode_prob = current_frame_info.entropy.inter_mode_prob;
492 }
493
494 if (current_frame_info.interp_filter == 4) {
495 // read_interp_filter_probs() in the spec
496 WriteProbabilityUpdate(writer, current_frame_info.entropy.switchable_interp_prob,
497 prev_frame_probs.switchable_interp_prob);
498 if (current_frame_info.show_frame && !current_frame_info.is_key_frame) {
499 prev_frame_probs.switchable_interp_prob =
500 current_frame_info.entropy.switchable_interp_prob;
501 }
502 }
503
504 // read_is_inter_probs() in the spec
505 WriteProbabilityUpdate(writer, current_frame_info.entropy.intra_inter_prob,
506 prev_frame_probs.intra_inter_prob);
507 if (current_frame_info.show_frame && !current_frame_info.is_key_frame) {
508 prev_frame_probs.intra_inter_prob = current_frame_info.entropy.intra_inter_prob;
509 }
510 // frame_reference_mode() in the spec
511 if ((current_frame_info.ref_frame_sign_bias[1] & 1) !=
512 (current_frame_info.ref_frame_sign_bias[2] & 1) ||
513 (current_frame_info.ref_frame_sign_bias[1] & 1) !=
514 (current_frame_info.ref_frame_sign_bias[3] & 1)) {
515 if (current_frame_info.reference_mode >= 1) {
516 writer.Write(1, 1);
517 writer.Write(current_frame_info.reference_mode == 2);
518 } else {
519 writer.Write(0, 1);
520 }
521 }
522
523 // frame_reference_mode_probs() in the spec
524 if (current_frame_info.reference_mode == 2) {
525 WriteProbabilityUpdate(writer, current_frame_info.entropy.comp_inter_prob,
526 prev_frame_probs.comp_inter_prob);
527 if (current_frame_info.show_frame && !current_frame_info.is_key_frame) {
528 prev_frame_probs.comp_inter_prob = current_frame_info.entropy.comp_inter_prob;
529 }
530 }
531
532 if (current_frame_info.reference_mode != 1) {
533 WriteProbabilityUpdate(writer, current_frame_info.entropy.single_ref_prob,
534 prev_frame_probs.single_ref_prob);
535 if (current_frame_info.show_frame && !current_frame_info.is_key_frame) {
536 prev_frame_probs.single_ref_prob = current_frame_info.entropy.single_ref_prob;
537 }
538 }
539
540 if (current_frame_info.reference_mode != 0) {
541 WriteProbabilityUpdate(writer, current_frame_info.entropy.comp_ref_prob,
542 prev_frame_probs.comp_ref_prob);
543 if (current_frame_info.show_frame && !current_frame_info.is_key_frame) {
544 prev_frame_probs.comp_ref_prob = current_frame_info.entropy.comp_ref_prob;
545 }
546 }
547
548 // read_y_mode_probs
549 for (std::size_t index = 0; index < current_frame_info.entropy.y_mode_prob.size();
550 ++index) {
551 WriteProbabilityUpdate(writer, current_frame_info.entropy.y_mode_prob[index],
552 prev_frame_probs.y_mode_prob[index]);
553 }
554 if (current_frame_info.show_frame && !current_frame_info.is_key_frame) {
555 prev_frame_probs.y_mode_prob = current_frame_info.entropy.y_mode_prob;
556 }
557 // read_partition_probs
558 WriteProbabilityUpdateAligned4(writer, current_frame_info.entropy.partition_prob,
559 prev_frame_probs.partition_prob);
560 if (current_frame_info.show_frame && !current_frame_info.is_key_frame) {
561 prev_frame_probs.partition_prob = current_frame_info.entropy.partition_prob;
562 }
563
564 // mv_probs
565 for (s32 i = 0; i < 3; i++) {
566 WriteMvProbabilityUpdate(writer, current_frame_info.entropy.joints[i],
567 prev_frame_probs.joints[i]);
568 }
569 if (current_frame_info.show_frame && !current_frame_info.is_key_frame) {
570 prev_frame_probs.joints = current_frame_info.entropy.joints;
571 }
572
573 for (s32 i = 0; i < 2; i++) {
574 WriteMvProbabilityUpdate(writer, current_frame_info.entropy.sign[i],
575 prev_frame_probs.sign[i]);
576
577 for (s32 j = 0; j < 10; j++) {
578 const int index = i * 10 + j;
579
580 WriteMvProbabilityUpdate(writer, current_frame_info.entropy.classes[index],
581 prev_frame_probs.classes[index]);
582 }
583
584 WriteMvProbabilityUpdate(writer, current_frame_info.entropy.class_0[i],
585 prev_frame_probs.class_0[i]);
586
587 for (s32 j = 0; j < 10; j++) {
588 const int index = i * 10 + j;
589
590 WriteMvProbabilityUpdate(writer, current_frame_info.entropy.prob_bits[index],
591 prev_frame_probs.prob_bits[index]);
592 }
593 }
594
595 for (s32 i = 0; i < 2; i++) {
596 for (s32 j = 0; j < 2; j++) {
597 for (s32 k = 0; k < 3; k++) {
598 const int index = i * 2 * 3 + j * 3 + k;
599
600 WriteMvProbabilityUpdate(writer, current_frame_info.entropy.class_0_fr[index],
601 prev_frame_probs.class_0_fr[index]);
602 }
603 }
604
605 for (s32 j = 0; j < 3; j++) {
606 const int index = i * 3 + j;
607
608 WriteMvProbabilityUpdate(writer, current_frame_info.entropy.fr[index],
609 prev_frame_probs.fr[index]);
610 }
611 }
612
613 if (current_frame_info.allow_high_precision_mv) {
614 for (s32 index = 0; index < 2; index++) {
615 WriteMvProbabilityUpdate(writer, current_frame_info.entropy.class_0_hp[index],
616 prev_frame_probs.class_0_hp[index]);
617 WriteMvProbabilityUpdate(writer, current_frame_info.entropy.high_precision[index],
618 prev_frame_probs.high_precision[index]);
619 }
620 }
621
622 // save previous probs
623 if (current_frame_info.show_frame && !current_frame_info.is_key_frame) {
624 prev_frame_probs.sign = current_frame_info.entropy.sign;
625 prev_frame_probs.classes = current_frame_info.entropy.classes;
626 prev_frame_probs.class_0 = current_frame_info.entropy.class_0;
627 prev_frame_probs.prob_bits = current_frame_info.entropy.prob_bits;
628 prev_frame_probs.class_0_fr = current_frame_info.entropy.class_0_fr;
629 prev_frame_probs.fr = current_frame_info.entropy.fr;
630 prev_frame_probs.class_0_hp = current_frame_info.entropy.class_0_hp;
631 prev_frame_probs.high_precision = current_frame_info.entropy.high_precision;
632 }
633 }
634
635 writer.End();
636 return writer.GetBuffer();
637}
638
639VpxBitStreamWriter VP9::ComposeUncompressedHeader() {
640 VpxBitStreamWriter uncomp_writer{};
641
642 uncomp_writer.WriteU(2, 2); // Frame marker.
643 uncomp_writer.WriteU(0, 2); // Profile.
644 uncomp_writer.WriteBit(false); // Show existing frame.
645 uncomp_writer.WriteBit(!current_frame_info.is_key_frame); // is key frame?
646 uncomp_writer.WriteBit(current_frame_info.show_frame); // show frame?
647 uncomp_writer.WriteBit(current_frame_info.error_resilient_mode); // error reslience
648
649 if (current_frame_info.is_key_frame) {
650 uncomp_writer.WriteU(frame_sync_code, 24);
651 uncomp_writer.WriteU(0, 3); // Color space.
652 uncomp_writer.WriteU(0, 1); // Color range.
653 uncomp_writer.WriteU(current_frame_info.frame_size.width - 1, 16);
654 uncomp_writer.WriteU(current_frame_info.frame_size.height - 1, 16);
655 uncomp_writer.WriteBit(false); // Render and frame size different.
656
657 // Reset context
658 prev_frame_probs = default_probs;
659 swap_next_golden = false;
660 loop_filter_ref_deltas.fill(0);
661 loop_filter_mode_deltas.fill(0);
662
663 // allow frames offsets to stabilize before checking for golden frames
664 grace_period = 4;
665
666 // On key frames, all frame slots are set to the current frame,
667 // so the value of the selected slot doesn't really matter.
668 frame_ctxs.fill({current_frame_number, false, default_probs});
669
670 // intra only, meaning the frame can be recreated with no other references
671 current_frame_info.intra_only = true;
672
673 } else {
674
675 if (!current_frame_info.show_frame) {
676 uncomp_writer.WriteBit(current_frame_info.intra_only);
677 if (!current_frame_info.last_frame_was_key) {
678 swap_next_golden = !swap_next_golden;
679 }
680 } else {
681 current_frame_info.intra_only = false;
682 }
683 if (!current_frame_info.error_resilient_mode) {
684 uncomp_writer.WriteU(0, 2); // Reset frame context.
685 }
686
687 // Last, Golden, Altref frames
688 std::array<s32, 3> ref_frame_index{0, 1, 2};
689
690 // Set when next frame is hidden
691 // altref and golden references are swapped
692 if (swap_next_golden) {
693 ref_frame_index = std::array<s32, 3>{0, 2, 1};
694 }
695
696 // update Last Frame
697 u64 refresh_frame_flags = 1;
698
699 // golden frame may refresh, determined if the next golden frame offset is changed
700 bool golden_refresh = false;
701 if (grace_period <= 0) {
702 for (s32 index = 1; index < 3; ++index) {
703 if (current_frame_info.frame_offsets[index] !=
704 next_frame.info.frame_offsets[index]) {
705 current_frame_info.refresh_frame[index] = true;
706 golden_refresh = true;
707 grace_period = 3;
708 }
709 }
710 }
711
712 if (current_frame_info.show_frame &&
713 (!next_frame.info.show_frame || next_frame.info.is_key_frame)) {
714 // Update golden frame
715 refresh_frame_flags = swap_next_golden ? 2 : 4;
716 }
717
718 if (!current_frame_info.show_frame) {
719 // Update altref
720 refresh_frame_flags = swap_next_golden ? 2 : 4;
721 } else if (golden_refresh) {
722 refresh_frame_flags = 3;
723 }
724
725 if (current_frame_info.intra_only) {
726 uncomp_writer.WriteU(frame_sync_code, 24);
727 uncomp_writer.WriteU(static_cast<s32>(refresh_frame_flags), 8);
728 uncomp_writer.WriteU(current_frame_info.frame_size.width - 1, 16);
729 uncomp_writer.WriteU(current_frame_info.frame_size.height - 1, 16);
730 uncomp_writer.WriteBit(false); // Render and frame size different.
731 } else {
732 uncomp_writer.WriteU(static_cast<s32>(refresh_frame_flags), 8);
733
734 for (s32 index = 1; index < 4; index++) {
735 uncomp_writer.WriteU(ref_frame_index[index - 1], 3);
736 uncomp_writer.WriteU(current_frame_info.ref_frame_sign_bias[index], 1);
737 }
738
739 uncomp_writer.WriteBit(true); // Frame size with refs.
740 uncomp_writer.WriteBit(false); // Render and frame size different.
741 uncomp_writer.WriteBit(current_frame_info.allow_high_precision_mv);
742 uncomp_writer.WriteBit(current_frame_info.interp_filter == 4);
743
744 if (current_frame_info.interp_filter != 4) {
745 uncomp_writer.WriteU(current_frame_info.interp_filter, 2);
746 }
747 }
748 }
749
750 if (!current_frame_info.error_resilient_mode) {
751 uncomp_writer.WriteBit(true); // Refresh frame context. where do i get this info from?
752 uncomp_writer.WriteBit(true); // Frame parallel decoding mode.
753 }
754
755 int frame_ctx_idx = 0;
756 if (!current_frame_info.show_frame) {
757 frame_ctx_idx = 1;
758 }
759
760 uncomp_writer.WriteU(frame_ctx_idx, 2); // Frame context index.
761 prev_frame_probs =
762 frame_ctxs[frame_ctx_idx].probs; // reference probabilities for compressed header
763 frame_ctxs[frame_ctx_idx] = {current_frame_number, false, current_frame_info.entropy};
764
765 uncomp_writer.WriteU(current_frame_info.first_level, 6);
766 uncomp_writer.WriteU(current_frame_info.sharpness_level, 3);
767 uncomp_writer.WriteBit(current_frame_info.mode_ref_delta_enabled);
768
769 if (current_frame_info.mode_ref_delta_enabled) {
770 // check if ref deltas are different, update accordingly
771 std::array<bool, 4> update_loop_filter_ref_deltas;
772 std::array<bool, 2> update_loop_filter_mode_deltas;
773
774 bool loop_filter_delta_update = false;
775
776 for (std::size_t index = 0; index < current_frame_info.ref_deltas.size(); index++) {
777 const s8 old_deltas = loop_filter_ref_deltas[index];
778 const s8 new_deltas = current_frame_info.ref_deltas[index];
779 const bool differing_delta = old_deltas != new_deltas;
780
781 update_loop_filter_ref_deltas[index] = differing_delta;
782 loop_filter_delta_update |= differing_delta;
783 }
784
785 for (std::size_t index = 0; index < current_frame_info.mode_deltas.size(); index++) {
786 const s8 old_deltas = loop_filter_mode_deltas[index];
787 const s8 new_deltas = current_frame_info.mode_deltas[index];
788 const bool differing_delta = old_deltas != new_deltas;
789
790 update_loop_filter_mode_deltas[index] = differing_delta;
791 loop_filter_delta_update |= differing_delta;
792 }
793
794 uncomp_writer.WriteBit(loop_filter_delta_update);
795
796 if (loop_filter_delta_update) {
797 for (std::size_t index = 0; index < current_frame_info.ref_deltas.size(); index++) {
798 uncomp_writer.WriteBit(update_loop_filter_ref_deltas[index]);
799
800 if (update_loop_filter_ref_deltas[index]) {
801 uncomp_writer.WriteS(current_frame_info.ref_deltas[index], 6);
802 }
803 }
804
805 for (std::size_t index = 0; index < current_frame_info.mode_deltas.size(); index++) {
806 uncomp_writer.WriteBit(update_loop_filter_mode_deltas[index]);
807
808 if (update_loop_filter_mode_deltas[index]) {
809 uncomp_writer.WriteS(current_frame_info.mode_deltas[index], 6);
810 }
811 }
812 // save new deltas
813 loop_filter_ref_deltas = current_frame_info.ref_deltas;
814 loop_filter_mode_deltas = current_frame_info.mode_deltas;
815 }
816 }
817
818 uncomp_writer.WriteU(current_frame_info.base_q_index, 8);
819
820 uncomp_writer.WriteDeltaQ(current_frame_info.y_dc_delta_q);
821 uncomp_writer.WriteDeltaQ(current_frame_info.uv_dc_delta_q);
822 uncomp_writer.WriteDeltaQ(current_frame_info.uv_ac_delta_q);
823
824 uncomp_writer.WriteBit(false); // Segmentation enabled (TODO).
825
826 const s32 min_tile_cols_log2 = CalcMinLog2TileCols(current_frame_info.frame_size.width);
827 const s32 max_tile_cols_log2 = CalcMaxLog2TileCols(current_frame_info.frame_size.width);
828
829 const s32 tile_cols_log2_diff = current_frame_info.log2_tile_cols - min_tile_cols_log2;
830 const s32 tile_cols_log2_inc_mask = (1 << tile_cols_log2_diff) - 1;
831
832 // If it's less than the maximum, we need to add an extra 0 on the bitstream
833 // to indicate that it should stop reading.
834 if (current_frame_info.log2_tile_cols < max_tile_cols_log2) {
835 uncomp_writer.WriteU(tile_cols_log2_inc_mask << 1, tile_cols_log2_diff + 1);
836 } else {
837 uncomp_writer.WriteU(tile_cols_log2_inc_mask, tile_cols_log2_diff);
838 }
839
840 const bool tile_rows_log2_is_nonzero = current_frame_info.log2_tile_rows != 0;
841
842 uncomp_writer.WriteBit(tile_rows_log2_is_nonzero);
843
844 if (tile_rows_log2_is_nonzero) {
845 uncomp_writer.WriteBit(current_frame_info.log2_tile_rows > 1);
846 }
847
848 return uncomp_writer;
849}
850
851std::vector<u8>& VP9::ComposeFrameHeader(NvdecCommon::NvdecRegisters& state) {
852 std::vector<u8> bitstream;
853 {
854 Vp9FrameContainer curr_frame = GetCurrentFrame(state);
855 current_frame_info = curr_frame.info;
856 bitstream = std::move(curr_frame.bit_stream);
857 }
858
859 // The uncompressed header routine sets PrevProb parameters needed for the compressed header
860 auto uncomp_writer = ComposeUncompressedHeader();
861 std::vector<u8> compressed_header = ComposeCompressedHeader();
862
863 uncomp_writer.WriteU(static_cast<s32>(compressed_header.size()), 16);
864 uncomp_writer.Flush();
865 std::vector<u8> uncompressed_header = uncomp_writer.GetByteArray();
866
867 // Write headers and frame to buffer
868 frame.resize(uncompressed_header.size() + compressed_header.size() + bitstream.size());
869 std::memcpy(frame.data(), uncompressed_header.data(), uncompressed_header.size());
870 std::memcpy(frame.data() + uncompressed_header.size(), compressed_header.data(),
871 compressed_header.size());
872 std::memcpy(frame.data() + uncompressed_header.size() + compressed_header.size(),
873 bitstream.data(), bitstream.size());
874
875 // keep track of frame number
876 current_frame_number++;
877 grace_period--;
878
879 // don't display hidden frames
880 hidden = !current_frame_info.show_frame;
881 return frame;
882}
883
884VpxRangeEncoder::VpxRangeEncoder() {
885 Write(false);
886}
887
888VpxRangeEncoder::~VpxRangeEncoder() = default;
889
890void VpxRangeEncoder::Write(s32 value, s32 value_size) {
891 for (s32 bit = value_size - 1; bit >= 0; bit--) {
892 Write(((value >> bit) & 1) != 0);
893 }
894}
895
896void VpxRangeEncoder::Write(bool bit) {
897 Write(bit, half_probability);
898}
899
900void VpxRangeEncoder::Write(bool bit, s32 probability) {
901 u32 local_range = range;
902 const u32 split = 1 + (((local_range - 1) * static_cast<u32>(probability)) >> 8);
903 local_range = split;
904
905 if (bit) {
906 low_value += split;
907 local_range = range - split;
908 }
909
910 s32 shift = norm_lut[local_range];
911 local_range <<= shift;
912 count += shift;
913
914 if (count >= 0) {
915 const s32 offset = shift - count;
916
917 if (((low_value << (offset - 1)) >> 31) != 0) {
918 const s32 current_pos = static_cast<s32>(base_stream.GetPosition());
919 base_stream.Seek(-1, Common::SeekOrigin::FromCurrentPos);
920 while (PeekByte() == 0xff) {
921 base_stream.WriteByte(0);
922
923 base_stream.Seek(-2, Common::SeekOrigin::FromCurrentPos);
924 }
925 base_stream.WriteByte(static_cast<u8>((PeekByte() + 1)));
926 base_stream.Seek(current_pos, Common::SeekOrigin::SetOrigin);
927 }
928 base_stream.WriteByte(static_cast<u8>((low_value >> (24 - offset))));
929
930 low_value <<= offset;
931 shift = count;
932 low_value &= 0xffffff;
933 count -= 8;
934 }
935
936 low_value <<= shift;
937 range = local_range;
938}
939
940void VpxRangeEncoder::End() {
941 for (std::size_t index = 0; index < 32; ++index) {
942 Write(false);
943 }
944}
945
946u8 VpxRangeEncoder::PeekByte() {
947 const u8 value = base_stream.ReadByte();
948 base_stream.Seek(-1, Common::SeekOrigin::FromCurrentPos);
949
950 return value;
951}
952
953VpxBitStreamWriter::VpxBitStreamWriter() = default;
954
955VpxBitStreamWriter::~VpxBitStreamWriter() = default;
956
957void VpxBitStreamWriter::WriteU(u32 value, u32 value_size) {
958 WriteBits(value, value_size);
959}
960
961void VpxBitStreamWriter::WriteS(s32 value, u32 value_size) {
962 const bool sign = value < 0;
963 if (sign) {
964 value = -value;
965 }
966
967 WriteBits(static_cast<u32>(value << 1) | (sign ? 1 : 0), value_size + 1);
968}
969
970void VpxBitStreamWriter::WriteDeltaQ(u32 value) {
971 const bool delta_coded = value != 0;
972 WriteBit(delta_coded);
973
974 if (delta_coded) {
975 WriteBits(value, 4);
976 }
977}
978
979void VpxBitStreamWriter::WriteBits(u32 value, u32 bit_count) {
980 s32 value_pos = 0;
981 s32 remaining = bit_count;
982
983 while (remaining > 0) {
984 s32 copy_size = remaining;
985
986 const s32 free = GetFreeBufferBits();
987
988 if (copy_size > free) {
989 copy_size = free;
990 }
991
992 const s32 mask = (1 << copy_size) - 1;
993
994 const s32 src_shift = (bit_count - value_pos) - copy_size;
995 const s32 dst_shift = (buffer_size - buffer_pos) - copy_size;
996
997 buffer |= ((value >> src_shift) & mask) << dst_shift;
998
999 value_pos += copy_size;
1000 buffer_pos += copy_size;
1001 remaining -= copy_size;
1002 }
1003}
1004
1005void VpxBitStreamWriter::WriteBit(bool state) {
1006 WriteBits(state ? 1 : 0, 1);
1007}
1008
1009s32 VpxBitStreamWriter::GetFreeBufferBits() {
1010 if (buffer_pos == buffer_size) {
1011 Flush();
1012 }
1013
1014 return buffer_size - buffer_pos;
1015}
1016
1017void VpxBitStreamWriter::Flush() {
1018 if (buffer_pos == 0) {
1019 return;
1020 }
1021 byte_array.push_back(static_cast<u8>(buffer));
1022 buffer = 0;
1023 buffer_pos = 0;
1024}
1025
1026std::vector<u8>& VpxBitStreamWriter::GetByteArray() {
1027 return byte_array;
1028}
1029
1030const std::vector<u8>& VpxBitStreamWriter::GetByteArray() const {
1031 return byte_array;
1032}
1033
1034} // namespace Tegra::Decoder
diff --git a/src/video_core/command_classes/codecs/vp9.h b/src/video_core/command_classes/codecs/vp9.h
new file mode 100644
index 000000000..dc52ddbde
--- /dev/null
+++ b/src/video_core/command_classes/codecs/vp9.h
@@ -0,0 +1,188 @@
1// Copyright 2020 yuzu Emulator Project
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 <vector>
9
10#include "common/common_types.h"
11#include "common/stream.h"
12#include "video_core/command_classes/codecs/vp9_types.h"
13#include "video_core/command_classes/nvdec_common.h"
14
15namespace Tegra {
16class GPU;
17enum class FrameType { KeyFrame = 0, InterFrame = 1 };
18namespace Decoder {
19
20/// The VpxRangeEncoder, and VpxBitStreamWriter classes are used to compose the
21/// VP9 header bitstreams.
22
23class VpxRangeEncoder {
24public:
25 VpxRangeEncoder();
26 ~VpxRangeEncoder();
27
28 /// Writes the rightmost value_size bits from value into the stream
29 void Write(s32 value, s32 value_size);
30
31 /// Writes a single bit with half probability
32 void Write(bool bit);
33
34 /// Writes a bit to the base_stream encoded with probability
35 void Write(bool bit, s32 probability);
36
37 /// Signal the end of the bitstream
38 void End();
39
40 std::vector<u8>& GetBuffer() {
41 return base_stream.GetBuffer();
42 }
43
44 const std::vector<u8>& GetBuffer() const {
45 return base_stream.GetBuffer();
46 }
47
48private:
49 u8 PeekByte();
50 Common::Stream base_stream{};
51 u32 low_value{};
52 u32 range{0xff};
53 s32 count{-24};
54 s32 half_probability{128};
55};
56
57class VpxBitStreamWriter {
58public:
59 VpxBitStreamWriter();
60 ~VpxBitStreamWriter();
61
62 /// Write an unsigned integer value
63 void WriteU(u32 value, u32 value_size);
64
65 /// Write a signed integer value
66 void WriteS(s32 value, u32 value_size);
67
68 /// Based on 6.2.10 of VP9 Spec, writes a delta coded value
69 void WriteDeltaQ(u32 value);
70
71 /// Write a single bit.
72 void WriteBit(bool state);
73
74 /// Pushes current buffer into buffer_array, resets buffer
75 void Flush();
76
77 /// Returns byte_array
78 std::vector<u8>& GetByteArray();
79
80 /// Returns const byte_array
81 const std::vector<u8>& GetByteArray() const;
82
83private:
84 /// Write bit_count bits from value into buffer
85 void WriteBits(u32 value, u32 bit_count);
86
87 /// Gets next available position in buffer, invokes Flush() if buffer is full
88 s32 GetFreeBufferBits();
89
90 s32 buffer_size{8};
91
92 s32 buffer{};
93 s32 buffer_pos{};
94 std::vector<u8> byte_array;
95};
96
97class VP9 {
98public:
99 explicit VP9(GPU& gpu);
100 ~VP9();
101
102 /// Composes the VP9 frame from the GPU state information. Based on the official VP9 spec
103 /// documentation
104 std::vector<u8>& ComposeFrameHeader(NvdecCommon::NvdecRegisters& state);
105
106 /// Returns true if the most recent frame was a hidden frame.
107 bool WasFrameHidden() const {
108 return hidden;
109 }
110
111private:
112 /// Generates compressed header probability updates in the bitstream writer
113 template <typename T, std::size_t N>
114 void WriteProbabilityUpdate(VpxRangeEncoder& writer, const std::array<T, N>& new_prob,
115 const std::array<T, N>& old_prob);
116
117 /// Generates compressed header probability updates in the bitstream writer
118 /// If probs are not equal, WriteProbabilityDelta is invoked
119 void WriteProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_prob);
120
121 /// Generates compressed header probability deltas in the bitstream writer
122 void WriteProbabilityDelta(VpxRangeEncoder& writer, u8 new_prob, u8 old_prob);
123
124 /// Adjusts old_prob depending on new_prob. Based on section 6.3.5 of VP9 Specification
125 s32 RemapProbability(s32 new_prob, s32 old_prob);
126
127 /// Recenters probability. Based on section 6.3.6 of VP9 Specification
128 s32 RecenterNonNeg(s32 new_prob, s32 old_prob);
129
130 /// Inverse of 6.3.4 Decode term subexp
131 void EncodeTermSubExp(VpxRangeEncoder& writer, s32 value);
132
133 /// Writes if the value is less than the test value
134 bool WriteLessThan(VpxRangeEncoder& writer, s32 value, s32 test);
135
136 /// Writes probability updates for the Coef probabilities
137 void WriteCoefProbabilityUpdate(VpxRangeEncoder& writer, s32 tx_mode,
138 const std::array<u8, 2304>& new_prob,
139 const std::array<u8, 2304>& old_prob);
140
141 /// Write probabilities for 4-byte aligned structures
142 template <typename T, std::size_t N>
143 void WriteProbabilityUpdateAligned4(VpxRangeEncoder& writer, const std::array<T, N>& new_prob,
144 const std::array<T, N>& old_prob);
145
146 /// Write motion vector probability updates. 6.3.17 in the spec
147 void WriteMvProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_prob);
148
149 /// 6.2.14 Tile size calculation
150 s32 CalcMinLog2TileCols(s32 frame_width);
151 s32 CalcMaxLog2TileCols(s32 frame_width);
152
153 /// Returns VP9 information from NVDEC provided offset and size
154 Vp9PictureInfo GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state);
155
156 /// Read and convert NVDEC provided entropy probs to Vp9EntropyProbs struct
157 void InsertEntropy(u64 offset, Vp9EntropyProbs& dst);
158
159 /// Returns frame to be decoded after buffering
160 Vp9FrameContainer GetCurrentFrame(const NvdecCommon::NvdecRegisters& state);
161
162 /// Use NVDEC providied information to compose the headers for the current frame
163 std::vector<u8> ComposeCompressedHeader();
164 VpxBitStreamWriter ComposeUncompressedHeader();
165
166 GPU& gpu;
167 std::vector<u8> frame;
168
169 std::array<s8, 4> loop_filter_ref_deltas{};
170 std::array<s8, 2> loop_filter_mode_deltas{};
171
172 bool hidden;
173 s64 current_frame_number = -2; // since we buffer 2 frames
174 s32 grace_period = 6; // frame offsets need to stabilize
175 std::array<FrameContexts, 4> frame_ctxs{};
176 Vp9FrameContainer next_frame{};
177 Vp9FrameContainer next_next_frame{};
178 bool swap_next_golden{};
179
180 Vp9PictureInfo current_frame_info{};
181 Vp9EntropyProbs prev_frame_probs{};
182
183 s32 diff_update_probability = 252;
184 s32 frame_sync_code = 0x498342;
185};
186
187} // namespace Decoder
188} // namespace Tegra
diff --git a/src/video_core/command_classes/codecs/vp9_types.h b/src/video_core/command_classes/codecs/vp9_types.h
new file mode 100644
index 000000000..a50acf6e8
--- /dev/null
+++ b/src/video_core/command_classes/codecs/vp9_types.h
@@ -0,0 +1,367 @@
1// Copyright 2020 yuzu Emulator Project
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 <cstring>
9#include <vector>
10#include "common/common_funcs.h"
11#include "common/common_types.h"
12
13namespace Tegra {
14class GPU;
15
16namespace Decoder {
17struct Vp9FrameDimensions {
18 s16 width{};
19 s16 height{};
20 s16 luma_pitch{};
21 s16 chroma_pitch{};
22};
23static_assert(sizeof(Vp9FrameDimensions) == 0x8, "Vp9 Vp9FrameDimensions is an invalid size");
24
25enum FrameFlags : u32 {
26 IsKeyFrame = 1 << 0,
27 LastFrameIsKeyFrame = 1 << 1,
28 FrameSizeChanged = 1 << 2,
29 ErrorResilientMode = 1 << 3,
30 LastShowFrame = 1 << 4,
31 IntraOnly = 1 << 5,
32};
33
34enum class MvJointType {
35 MvJointZero = 0, /* Zero vector */
36 MvJointHnzvz = 1, /* Vert zero, hor nonzero */
37 MvJointHzvnz = 2, /* Hor zero, vert nonzero */
38 MvJointHnzvnz = 3, /* Both components nonzero */
39};
40enum class MvClassType {
41 MvClass0 = 0, /* (0, 2] integer pel */
42 MvClass1 = 1, /* (2, 4] integer pel */
43 MvClass2 = 2, /* (4, 8] integer pel */
44 MvClass3 = 3, /* (8, 16] integer pel */
45 MvClass4 = 4, /* (16, 32] integer pel */
46 MvClass5 = 5, /* (32, 64] integer pel */
47 MvClass6 = 6, /* (64, 128] integer pel */
48 MvClass7 = 7, /* (128, 256] integer pel */
49 MvClass8 = 8, /* (256, 512] integer pel */
50 MvClass9 = 9, /* (512, 1024] integer pel */
51 MvClass10 = 10, /* (1024,2048] integer pel */
52};
53
54enum class BlockSize {
55 Block4x4 = 0,
56 Block4x8 = 1,
57 Block8x4 = 2,
58 Block8x8 = 3,
59 Block8x16 = 4,
60 Block16x8 = 5,
61 Block16x16 = 6,
62 Block16x32 = 7,
63 Block32x16 = 8,
64 Block32x32 = 9,
65 Block32x64 = 10,
66 Block64x32 = 11,
67 Block64x64 = 12,
68 BlockSizes = 13,
69 BlockInvalid = BlockSizes
70};
71
72enum class PredictionMode {
73 DcPred = 0, // Average of above and left pixels
74 VPred = 1, // Vertical
75 HPred = 2, // Horizontal
76 D45Pred = 3, // Directional 45 deg = round(arctan(1 / 1) * 180 / pi)
77 D135Pred = 4, // Directional 135 deg = 180 - 45
78 D117Pred = 5, // Directional 117 deg = 180 - 63
79 D153Pred = 6, // Directional 153 deg = 180 - 27
80 D207Pred = 7, // Directional 207 deg = 180 + 27
81 D63Pred = 8, // Directional 63 deg = round(arctan(2 / 1) * 180 / pi)
82 TmPred = 9, // True-motion
83 NearestMv = 10,
84 NearMv = 11,
85 ZeroMv = 12,
86 NewMv = 13,
87 MbModeCount = 14
88};
89
90enum class TxSize {
91 Tx4x4 = 0, // 4x4 transform
92 Tx8x8 = 1, // 8x8 transform
93 Tx16x16 = 2, // 16x16 transform
94 Tx32x32 = 3, // 32x32 transform
95 TxSizes = 4
96};
97
98enum class TxMode {
99 Only4X4 = 0, // Only 4x4 transform used
100 Allow8X8 = 1, // Allow block transform size up to 8x8
101 Allow16X16 = 2, // Allow block transform size up to 16x16
102 Allow32X32 = 3, // Allow block transform size up to 32x32
103 TxModeSelect = 4, // Transform specified for each block
104 TxModes = 5
105};
106
107enum class reference_mode {
108 SingleReference = 0,
109 CompoundReference = 1,
110 ReferenceModeSelect = 2,
111 ReferenceModes = 3
112};
113
114struct Segmentation {
115 u8 enabled{};
116 u8 update_map{};
117 u8 temporal_update{};
118 u8 abs_delta{};
119 std::array<u32, 8> feature_mask{};
120 std::array<std::array<s16, 4>, 8> feature_data{};
121};
122static_assert(sizeof(Segmentation) == 0x64, "Segmentation is an invalid size");
123
124struct LoopFilter {
125 u8 mode_ref_delta_enabled{};
126 std::array<s8, 4> ref_deltas{};
127 std::array<s8, 2> mode_deltas{};
128};
129static_assert(sizeof(LoopFilter) == 0x7, "LoopFilter is an invalid size");
130
131struct Vp9EntropyProbs {
132 std::array<u8, 36> y_mode_prob{};
133 std::array<u8, 64> partition_prob{};
134 std::array<u8, 2304> coef_probs{};
135 std::array<u8, 8> switchable_interp_prob{};
136 std::array<u8, 28> inter_mode_prob{};
137 std::array<u8, 4> intra_inter_prob{};
138 std::array<u8, 5> comp_inter_prob{};
139 std::array<u8, 10> single_ref_prob{};
140 std::array<u8, 5> comp_ref_prob{};
141 std::array<u8, 6> tx_32x32_prob{};
142 std::array<u8, 4> tx_16x16_prob{};
143 std::array<u8, 2> tx_8x8_prob{};
144 std::array<u8, 3> skip_probs{};
145 std::array<u8, 3> joints{};
146 std::array<u8, 2> sign{};
147 std::array<u8, 20> classes{};
148 std::array<u8, 2> class_0{};
149 std::array<u8, 20> prob_bits{};
150 std::array<u8, 12> class_0_fr{};
151 std::array<u8, 6> fr{};
152 std::array<u8, 2> class_0_hp{};
153 std::array<u8, 2> high_precision{};
154};
155static_assert(sizeof(Vp9EntropyProbs) == 0x9F4, "Vp9EntropyProbs is an invalid size");
156
157struct Vp9PictureInfo {
158 bool is_key_frame{};
159 bool intra_only{};
160 bool last_frame_was_key{};
161 bool frame_size_changed{};
162 bool error_resilient_mode{};
163 bool last_frame_shown{};
164 bool show_frame{};
165 std::array<s8, 4> ref_frame_sign_bias{};
166 s32 base_q_index{};
167 s32 y_dc_delta_q{};
168 s32 uv_dc_delta_q{};
169 s32 uv_ac_delta_q{};
170 bool lossless{};
171 s32 transform_mode{};
172 bool allow_high_precision_mv{};
173 s32 interp_filter{};
174 s32 reference_mode{};
175 s8 comp_fixed_ref{};
176 std::array<s8, 2> comp_var_ref{};
177 s32 log2_tile_cols{};
178 s32 log2_tile_rows{};
179 bool segment_enabled{};
180 bool segment_map_update{};
181 bool segment_map_temporal_update{};
182 s32 segment_abs_delta{};
183 std::array<u32, 8> segment_feature_enable{};
184 std::array<std::array<s16, 4>, 8> segment_feature_data{};
185 bool mode_ref_delta_enabled{};
186 bool use_prev_in_find_mv_refs{};
187 std::array<s8, 4> ref_deltas{};
188 std::array<s8, 2> mode_deltas{};
189 Vp9EntropyProbs entropy{};
190 Vp9FrameDimensions frame_size{};
191 u8 first_level{};
192 u8 sharpness_level{};
193 u32 bitstream_size{};
194 std::array<u64, 4> frame_offsets{};
195 std::array<bool, 4> refresh_frame{};
196};
197
198struct Vp9FrameContainer {
199 Vp9PictureInfo info{};
200 std::vector<u8> bit_stream;
201};
202
203struct PictureInfo {
204 INSERT_PADDING_WORDS(12);
205 u32 bitstream_size{};
206 INSERT_PADDING_WORDS(5);
207 Vp9FrameDimensions last_frame_size{};
208 Vp9FrameDimensions golden_frame_size{};
209 Vp9FrameDimensions alt_frame_size{};
210 Vp9FrameDimensions current_frame_size{};
211 u32 vp9_flags{};
212 std::array<s8, 4> ref_frame_sign_bias{};
213 u8 first_level{};
214 u8 sharpness_level{};
215 u8 base_q_index{};
216 u8 y_dc_delta_q{};
217 u8 uv_ac_delta_q{};
218 u8 uv_dc_delta_q{};
219 u8 lossless{};
220 u8 tx_mode{};
221 u8 allow_high_precision_mv{};
222 u8 interp_filter{};
223 u8 reference_mode{};
224 s8 comp_fixed_ref{};
225 std::array<s8, 2> comp_var_ref{};
226 u8 log2_tile_cols{};
227 u8 log2_tile_rows{};
228 Segmentation segmentation{};
229 LoopFilter loop_filter{};
230 INSERT_PADDING_BYTES(5);
231 u32 surface_params{};
232 INSERT_PADDING_WORDS(3);
233
234 Vp9PictureInfo Convert() const {
235
236 return Vp9PictureInfo{
237 .is_key_frame = (vp9_flags & FrameFlags::IsKeyFrame) != 0,
238 .intra_only = (vp9_flags & FrameFlags::IntraOnly) != 0,
239 .last_frame_was_key = (vp9_flags & FrameFlags::LastFrameIsKeyFrame) != 0,
240 .frame_size_changed = (vp9_flags & FrameFlags::FrameSizeChanged) != 0,
241 .error_resilient_mode = (vp9_flags & FrameFlags::ErrorResilientMode) != 0,
242 .last_frame_shown = (vp9_flags & FrameFlags::LastShowFrame) != 0,
243 .ref_frame_sign_bias = ref_frame_sign_bias,
244 .base_q_index = base_q_index,
245 .y_dc_delta_q = y_dc_delta_q,
246 .uv_dc_delta_q = uv_dc_delta_q,
247 .uv_ac_delta_q = uv_ac_delta_q,
248 .lossless = lossless != 0,
249 .transform_mode = tx_mode,
250 .allow_high_precision_mv = allow_high_precision_mv != 0,
251 .interp_filter = interp_filter,
252 .reference_mode = reference_mode,
253 .comp_fixed_ref = comp_fixed_ref,
254 .comp_var_ref = comp_var_ref,
255 .log2_tile_cols = log2_tile_cols,
256 .log2_tile_rows = log2_tile_rows,
257 .segment_enabled = segmentation.enabled != 0,
258 .segment_map_update = segmentation.update_map != 0,
259 .segment_map_temporal_update = segmentation.temporal_update != 0,
260 .segment_abs_delta = segmentation.abs_delta,
261 .segment_feature_enable = segmentation.feature_mask,
262 .segment_feature_data = segmentation.feature_data,
263 .mode_ref_delta_enabled = loop_filter.mode_ref_delta_enabled != 0,
264 .use_prev_in_find_mv_refs = !(vp9_flags == (FrameFlags::ErrorResilientMode)) &&
265 !(vp9_flags == (FrameFlags::FrameSizeChanged)) &&
266 !(vp9_flags == (FrameFlags::IntraOnly)) &&
267 (vp9_flags == (FrameFlags::LastShowFrame)) &&
268 !(vp9_flags == (FrameFlags::LastFrameIsKeyFrame)),
269 .ref_deltas = loop_filter.ref_deltas,
270 .mode_deltas = loop_filter.mode_deltas,
271 .frame_size = current_frame_size,
272 .first_level = first_level,
273 .sharpness_level = sharpness_level,
274 .bitstream_size = bitstream_size,
275 };
276 }
277};
278static_assert(sizeof(PictureInfo) == 0x100, "PictureInfo is an invalid size");
279
280struct EntropyProbs {
281 INSERT_PADDING_BYTES(1024);
282 std::array<std::array<u8, 4>, 7> inter_mode_prob{};
283 std::array<u8, 4> intra_inter_prob{};
284 INSERT_PADDING_BYTES(80);
285 std::array<std::array<u8, 1>, 2> tx_8x8_prob{};
286 std::array<std::array<u8, 2>, 2> tx_16x16_prob{};
287 std::array<std::array<u8, 3>, 2> tx_32x32_prob{};
288 std::array<u8, 4> y_mode_prob_e8{};
289 std::array<std::array<u8, 8>, 4> y_mode_prob_e0e7{};
290 INSERT_PADDING_BYTES(64);
291 std::array<std::array<u8, 4>, 16> partition_prob{};
292 INSERT_PADDING_BYTES(10);
293 std::array<std::array<u8, 2>, 4> switchable_interp_prob{};
294 std::array<u8, 5> comp_inter_prob{};
295 std::array<u8, 4> skip_probs{};
296 std::array<u8, 3> joints{};
297 std::array<u8, 2> sign{};
298 std::array<std::array<u8, 1>, 2> class_0{};
299 std::array<std::array<u8, 3>, 2> fr{};
300 std::array<u8, 2> class_0_hp{};
301 std::array<u8, 2> high_precision{};
302 std::array<std::array<u8, 10>, 2> classes{};
303 std::array<std::array<std::array<u8, 3>, 2>, 2> class_0_fr{};
304 std::array<std::array<u8, 10>, 2> pred_bits{};
305 std::array<std::array<u8, 2>, 5> single_ref_prob{};
306 std::array<u8, 5> comp_ref_prob{};
307 INSERT_PADDING_BYTES(17);
308 std::array<std::array<std::array<std::array<std::array<std::array<u8, 4>, 6>, 6>, 2>, 2>, 4>
309 coef_probs{};
310
311 void Convert(Vp9EntropyProbs& fc) {
312 std::memcpy(fc.inter_mode_prob.data(), inter_mode_prob.data(), fc.inter_mode_prob.size());
313
314 std::memcpy(fc.intra_inter_prob.data(), intra_inter_prob.data(),
315 fc.intra_inter_prob.size());
316
317 std::memcpy(fc.tx_8x8_prob.data(), tx_8x8_prob.data(), fc.tx_8x8_prob.size());
318 std::memcpy(fc.tx_16x16_prob.data(), tx_16x16_prob.data(), fc.tx_16x16_prob.size());
319 std::memcpy(fc.tx_32x32_prob.data(), tx_32x32_prob.data(), fc.tx_32x32_prob.size());
320
321 for (s32 i = 0; i < 4; i++) {
322 for (s32 j = 0; j < 9; j++) {
323 fc.y_mode_prob[j + 9 * i] = j < 8 ? y_mode_prob_e0e7[i][j] : y_mode_prob_e8[i];
324 }
325 }
326
327 std::memcpy(fc.partition_prob.data(), partition_prob.data(), fc.partition_prob.size());
328
329 std::memcpy(fc.switchable_interp_prob.data(), switchable_interp_prob.data(),
330 fc.switchable_interp_prob.size());
331 std::memcpy(fc.comp_inter_prob.data(), comp_inter_prob.data(), fc.comp_inter_prob.size());
332 std::memcpy(fc.skip_probs.data(), skip_probs.data(), fc.skip_probs.size());
333
334 std::memcpy(fc.joints.data(), joints.data(), fc.joints.size());
335
336 std::memcpy(fc.sign.data(), sign.data(), fc.sign.size());
337 std::memcpy(fc.class_0.data(), class_0.data(), fc.class_0.size());
338 std::memcpy(fc.fr.data(), fr.data(), fc.fr.size());
339 std::memcpy(fc.class_0_hp.data(), class_0_hp.data(), fc.class_0_hp.size());
340 std::memcpy(fc.high_precision.data(), high_precision.data(), fc.high_precision.size());
341 std::memcpy(fc.classes.data(), classes.data(), fc.classes.size());
342 std::memcpy(fc.class_0_fr.data(), class_0_fr.data(), fc.class_0_fr.size());
343 std::memcpy(fc.prob_bits.data(), pred_bits.data(), fc.prob_bits.size());
344 std::memcpy(fc.single_ref_prob.data(), single_ref_prob.data(), fc.single_ref_prob.size());
345 std::memcpy(fc.comp_ref_prob.data(), comp_ref_prob.data(), fc.comp_ref_prob.size());
346
347 std::memcpy(fc.coef_probs.data(), coef_probs.data(), fc.coef_probs.size());
348 }
349};
350static_assert(sizeof(EntropyProbs) == 0xEA0, "EntropyProbs is an invalid size");
351
352enum class Ref { Last, Golden, AltRef };
353
354struct RefPoolElement {
355 s64 frame{};
356 Ref ref{};
357 bool refresh{};
358};
359
360struct FrameContexts {
361 s64 from{};
362 bool adapted{};
363 Vp9EntropyProbs probs{};
364};
365
366}; // namespace Decoder
367}; // namespace Tegra
diff --git a/src/video_core/command_classes/host1x.cpp b/src/video_core/command_classes/host1x.cpp
new file mode 100644
index 000000000..c4dd4881a
--- /dev/null
+++ b/src/video_core/command_classes/host1x.cpp
@@ -0,0 +1,39 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "video_core/command_classes/host1x.h"
7#include "video_core/gpu.h"
8
9Tegra::Host1x::Host1x(GPU& gpu_) : gpu(gpu_) {}
10
11Tegra::Host1x::~Host1x() = default;
12
13void Tegra::Host1x::StateWrite(u32 offset, u32 arguments) {
14 u8* const state_offset = reinterpret_cast<u8*>(&state) + offset * sizeof(u32);
15 std::memcpy(state_offset, &arguments, sizeof(u32));
16}
17
18void Tegra::Host1x::ProcessMethod(Method method, const std::vector<u32>& arguments) {
19 StateWrite(static_cast<u32>(method), arguments[0]);
20 switch (method) {
21 case Method::WaitSyncpt:
22 Execute(arguments[0]);
23 break;
24 case Method::LoadSyncptPayload32:
25 syncpoint_value = arguments[0];
26 break;
27 case Method::WaitSyncpt32:
28 Execute(arguments[0]);
29 break;
30 default:
31 UNIMPLEMENTED_MSG("Host1x method 0x{:X}", static_cast<u32>(method));
32 break;
33 }
34}
35
36void Tegra::Host1x::Execute(u32 data) {
37 // This method waits on a valid syncpoint.
38 // TODO: Implement when proper Async is in place
39}
diff --git a/src/video_core/command_classes/host1x.h b/src/video_core/command_classes/host1x.h
new file mode 100644
index 000000000..013eaa0c1
--- /dev/null
+++ b/src/video_core/command_classes/host1x.h
@@ -0,0 +1,78 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <vector>
8#include "common/common_funcs.h"
9#include "common/common_types.h"
10
11namespace Tegra {
12class GPU;
13class Nvdec;
14
15class Host1x {
16public:
17 struct Host1xClassRegisters {
18 u32 incr_syncpt{};
19 u32 incr_syncpt_ctrl{};
20 u32 incr_syncpt_error{};
21 INSERT_PADDING_WORDS(5);
22 u32 wait_syncpt{};
23 u32 wait_syncpt_base{};
24 u32 wait_syncpt_incr{};
25 u32 load_syncpt_base{};
26 u32 incr_syncpt_base{};
27 u32 clear{};
28 u32 wait{};
29 u32 wait_with_interrupt{};
30 u32 delay_use{};
31 u32 tick_count_high{};
32 u32 tick_count_low{};
33 u32 tick_ctrl{};
34 INSERT_PADDING_WORDS(23);
35 u32 ind_ctrl{};
36 u32 ind_off2{};
37 u32 ind_off{};
38 std::array<u32, 31> ind_data{};
39 INSERT_PADDING_WORDS(1);
40 u32 load_syncpoint_payload32{};
41 u32 stall_ctrl{};
42 u32 wait_syncpt32{};
43 u32 wait_syncpt_base32{};
44 u32 load_syncpt_base32{};
45 u32 incr_syncpt_base32{};
46 u32 stall_count_high{};
47 u32 stall_count_low{};
48 u32 xref_ctrl{};
49 u32 channel_xref_high{};
50 u32 channel_xref_low{};
51 };
52 static_assert(sizeof(Host1xClassRegisters) == 0x164, "Host1xClassRegisters is an invalid size");
53
54 enum class Method : u32 {
55 WaitSyncpt = offsetof(Host1xClassRegisters, wait_syncpt) / 4,
56 LoadSyncptPayload32 = offsetof(Host1xClassRegisters, load_syncpoint_payload32) / 4,
57 WaitSyncpt32 = offsetof(Host1xClassRegisters, wait_syncpt32) / 4,
58 };
59
60 explicit Host1x(GPU& gpu);
61 ~Host1x();
62
63 /// Writes the method into the state, Invoke Execute() if encountered
64 void ProcessMethod(Method method, const std::vector<u32>& arguments);
65
66private:
67 /// For Host1x, execute is waiting on a syncpoint previously written into the state
68 void Execute(u32 data);
69
70 /// Write argument into the provided offset
71 void StateWrite(u32 offset, u32 arguments);
72
73 u32 syncpoint_value{};
74 Host1xClassRegisters state{};
75 GPU& gpu;
76};
77
78} // namespace Tegra
diff --git a/src/video_core/command_classes/nvdec.cpp b/src/video_core/command_classes/nvdec.cpp
new file mode 100644
index 000000000..8ca7a7b06
--- /dev/null
+++ b/src/video_core/command_classes/nvdec.cpp
@@ -0,0 +1,52 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "video_core/command_classes/nvdec.h"
7#include "video_core/gpu.h"
8
9namespace Tegra {
10
11Nvdec::Nvdec(GPU& gpu_) : gpu(gpu_), codec(std::make_unique<Codec>(gpu)) {}
12
13Nvdec::~Nvdec() = default;
14
15void Nvdec::ProcessMethod(Method method, const std::vector<u32>& arguments) {
16 if (method == Method::SetVideoCodec) {
17 codec->StateWrite(static_cast<u32>(method), arguments[0]);
18 } else {
19 codec->StateWrite(static_cast<u32>(method), static_cast<u64>(arguments[0]) << 8);
20 }
21
22 switch (method) {
23 case Method::SetVideoCodec:
24 codec->SetTargetCodec(static_cast<NvdecCommon::VideoCodec>(arguments[0]));
25 break;
26 case Method::Execute:
27 Execute();
28 break;
29 }
30}
31
32AVFrame* Nvdec::GetFrame() {
33 return codec->GetCurrentFrame();
34}
35
36const AVFrame* Nvdec::GetFrame() const {
37 return codec->GetCurrentFrame();
38}
39
40void Nvdec::Execute() {
41 switch (codec->GetCurrentCodec()) {
42 case NvdecCommon::VideoCodec::H264:
43 case NvdecCommon::VideoCodec::Vp9:
44 codec->Decode();
45 break;
46 default:
47 UNIMPLEMENTED_MSG("Unknown codec {}", static_cast<u32>(codec->GetCurrentCodec()));
48 break;
49 }
50}
51
52} // namespace Tegra
diff --git a/src/video_core/command_classes/nvdec.h b/src/video_core/command_classes/nvdec.h
new file mode 100644
index 000000000..af14f9857
--- /dev/null
+++ b/src/video_core/command_classes/nvdec.h
@@ -0,0 +1,39 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <vector>
9#include "common/common_types.h"
10#include "video_core/command_classes/codecs/codec.h"
11
12namespace Tegra {
13class GPU;
14
15class Nvdec {
16public:
17 enum class Method : u32 {
18 SetVideoCodec = 0x80,
19 Execute = 0xc0,
20 };
21
22 explicit Nvdec(GPU& gpu);
23 ~Nvdec();
24
25 /// Writes the method into the state, Invoke Execute() if encountered
26 void ProcessMethod(Method method, const std::vector<u32>& arguments);
27
28 /// Return most recently decoded frame
29 AVFrame* GetFrame();
30 const AVFrame* GetFrame() const;
31
32private:
33 /// Invoke codec to decode a frame
34 void Execute();
35
36 GPU& gpu;
37 std::unique_ptr<Codec> codec;
38};
39} // namespace Tegra
diff --git a/src/video_core/command_classes/nvdec_common.h b/src/video_core/command_classes/nvdec_common.h
new file mode 100644
index 000000000..01b5e086d
--- /dev/null
+++ b/src/video_core/command_classes/nvdec_common.h
@@ -0,0 +1,48 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_funcs.h"
8#include "common/common_types.h"
9
10namespace Tegra::NvdecCommon {
11
12struct NvdecRegisters {
13 INSERT_PADDING_WORDS(256);
14 u64 set_codec_id{};
15 INSERT_PADDING_WORDS(254);
16 u64 set_platform_id{};
17 u64 picture_info_offset{};
18 u64 frame_bitstream_offset{};
19 u64 frame_number{};
20 u64 h264_slice_data_offsets{};
21 u64 h264_mv_dump_offset{};
22 INSERT_PADDING_WORDS(6);
23 u64 frame_stats_offset{};
24 u64 h264_last_surface_luma_offset{};
25 u64 h264_last_surface_chroma_offset{};
26 std::array<u64, 17> surface_luma_offset{};
27 std::array<u64, 17> surface_chroma_offset{};
28 INSERT_PADDING_WORDS(132);
29 u64 vp9_entropy_probs_offset{};
30 u64 vp9_backward_updates_offset{};
31 u64 vp9_last_frame_segmap_offset{};
32 u64 vp9_curr_frame_segmap_offset{};
33 INSERT_PADDING_WORDS(2);
34 u64 vp9_last_frame_mvs_offset{};
35 u64 vp9_curr_frame_mvs_offset{};
36 INSERT_PADDING_WORDS(2);
37};
38static_assert(sizeof(NvdecRegisters) == (0xBC0), "NvdecRegisters is incorrect size");
39
40enum class VideoCodec : u32 {
41 None = 0x0,
42 H264 = 0x3,
43 Vp8 = 0x5,
44 H265 = 0x7,
45 Vp9 = 0x9,
46};
47
48} // namespace Tegra::NvdecCommon
diff --git a/src/video_core/command_classes/sync_manager.cpp b/src/video_core/command_classes/sync_manager.cpp
new file mode 100644
index 000000000..19dc9e0ab
--- /dev/null
+++ b/src/video_core/command_classes/sync_manager.cpp
@@ -0,0 +1,60 @@
1// MIT License
2//
3// Copyright (c) Ryujinx Team and Contributors
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
6// associated documentation files (the "Software"), to deal in the Software without restriction,
7// including without limitation the rights to use, copy, modify, merge, publish, distribute,
8// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
9// furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in all copies or
12// substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
15// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19//
20
21#include <algorithm>
22#include "sync_manager.h"
23#include "video_core/gpu.h"
24
25namespace Tegra {
26SyncptIncrManager::SyncptIncrManager(GPU& gpu_) : gpu(gpu_) {}
27SyncptIncrManager::~SyncptIncrManager() = default;
28
29void SyncptIncrManager::Increment(u32 id) {
30 increments.emplace_back(0, 0, id, true);
31 IncrementAllDone();
32}
33
34u32 SyncptIncrManager::IncrementWhenDone(u32 class_id, u32 id) {
35 const u32 handle = current_id++;
36 increments.emplace_back(handle, class_id, id);
37 return handle;
38}
39
40void SyncptIncrManager::SignalDone(u32 handle) {
41 const auto done_incr =
42 std::find_if(increments.begin(), increments.end(),
43 [handle](const SyncptIncr& incr) { return incr.id == handle; });
44 if (done_incr != increments.cend()) {
45 done_incr->complete = true;
46 }
47 IncrementAllDone();
48}
49
50void SyncptIncrManager::IncrementAllDone() {
51 std::size_t done_count = 0;
52 for (; done_count < increments.size(); ++done_count) {
53 if (!increments[done_count].complete) {
54 break;
55 }
56 gpu.IncrementSyncPoint(increments[done_count].syncpt_id);
57 }
58 increments.erase(increments.begin(), increments.begin() + done_count);
59}
60} // namespace Tegra
diff --git a/src/video_core/command_classes/sync_manager.h b/src/video_core/command_classes/sync_manager.h
new file mode 100644
index 000000000..2c321ec58
--- /dev/null
+++ b/src/video_core/command_classes/sync_manager.h
@@ -0,0 +1,64 @@
1// MIT License
2//
3// Copyright (c) Ryujinx Team and Contributors
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
6// associated documentation files (the "Software"), to deal in the Software without restriction,
7// including without limitation the rights to use, copy, modify, merge, publish, distribute,
8// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
9// furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in all copies or
12// substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
15// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19//
20
21#pragma once
22
23#include <mutex>
24#include <vector>
25#include "common/common_types.h"
26
27namespace Tegra {
28class GPU;
29struct SyncptIncr {
30 u32 id;
31 u32 class_id;
32 u32 syncpt_id;
33 bool complete;
34
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) {}
37};
38
39class SyncptIncrManager {
40public:
41 explicit SyncptIncrManager(GPU& gpu);
42 ~SyncptIncrManager();
43
44 /// Add syncpoint id and increment all
45 void Increment(u32 id);
46
47 /// Returns a handle to increment later
48 u32 IncrementWhenDone(u32 class_id, u32 id);
49
50 /// IncrememntAllDone, including handle
51 void SignalDone(u32 handle);
52
53 /// Increment all sequential pending increments that are already done.
54 void IncrementAllDone();
55
56private:
57 std::vector<SyncptIncr> increments;
58 std::mutex increment_lock;
59 u32 current_id{};
60
61 GPU& gpu;
62};
63
64} // namespace Tegra
diff --git a/src/video_core/command_classes/vic.cpp b/src/video_core/command_classes/vic.cpp
new file mode 100644
index 000000000..5b52da277
--- /dev/null
+++ b/src/video_core/command_classes/vic.cpp
@@ -0,0 +1,180 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <array>
6#include "common/assert.h"
7#include "video_core/command_classes/nvdec.h"
8#include "video_core/command_classes/vic.h"
9#include "video_core/engines/maxwell_3d.h"
10#include "video_core/gpu.h"
11#include "video_core/memory_manager.h"
12#include "video_core/texture_cache/surface_params.h"
13
14extern "C" {
15#include <libswscale/swscale.h>
16}
17
18namespace Tegra {
19
20Vic::Vic(GPU& gpu_, std::shared_ptr<Nvdec> nvdec_processor_)
21 : gpu(gpu_), nvdec_processor(std::move(nvdec_processor_)) {}
22Vic::~Vic() = default;
23
24void Vic::VicStateWrite(u32 offset, u32 arguments) {
25 u8* const state_offset = reinterpret_cast<u8*>(&vic_state) + offset * sizeof(u32);
26 std::memcpy(state_offset, &arguments, sizeof(u32));
27}
28
29void Vic::ProcessMethod(Method method, const std::vector<u32>& arguments) {
30 LOG_DEBUG(HW_GPU, "Vic method 0x{:X}", static_cast<u32>(method));
31 VicStateWrite(static_cast<u32>(method), arguments[0]);
32 const u64 arg = static_cast<u64>(arguments[0]) << 8;
33 switch (method) {
34 case Method::Execute:
35 Execute();
36 break;
37 case Method::SetConfigStructOffset:
38 config_struct_address = arg;
39 break;
40 case Method::SetOutputSurfaceLumaOffset:
41 output_surface_luma_address = arg;
42 break;
43 case Method::SetOutputSurfaceChromaUOffset:
44 output_surface_chroma_u_address = arg;
45 break;
46 case Method::SetOutputSurfaceChromaVOffset:
47 output_surface_chroma_v_address = arg;
48 break;
49 default:
50 break;
51 }
52}
53
54void Vic::Execute() {
55 if (output_surface_luma_address == 0) {
56 LOG_ERROR(Service_NVDRV, "VIC Luma address not set. Recieved 0x{:X}",
57 vic_state.output_surface.luma_offset);
58 return;
59 }
60 const VicConfig config{gpu.MemoryManager().Read<u64>(config_struct_address + 0x20)};
61 const VideoPixelFormat pixel_format =
62 static_cast<VideoPixelFormat>(config.pixel_format.Value());
63 switch (pixel_format) {
64 case VideoPixelFormat::BGRA8:
65 case VideoPixelFormat::RGBA8: {
66 LOG_TRACE(Service_NVDRV, "Writing RGB Frame");
67 const auto* frame = nvdec_processor->GetFrame();
68
69 if (!frame || frame->width == 0 || frame->height == 0) {
70 return;
71 }
72 if (scaler_ctx == nullptr || frame->width != scaler_width ||
73 frame->height != scaler_height) {
74 const AVPixelFormat target_format =
75 (pixel_format == VideoPixelFormat::RGBA8) ? AV_PIX_FMT_RGBA : AV_PIX_FMT_BGRA;
76
77 sws_freeContext(scaler_ctx);
78 scaler_ctx = nullptr;
79
80 // FFmpeg returns all frames in YUV420, convert it into expected format
81 scaler_ctx =
82 sws_getContext(frame->width, frame->height, AV_PIX_FMT_YUV420P, frame->width,
83 frame->height, target_format, 0, nullptr, nullptr, nullptr);
84
85 scaler_width = frame->width;
86 scaler_height = frame->height;
87 }
88 // Get Converted frame
89 const std::size_t linear_size = frame->width * frame->height * 4;
90
91 using AVMallocPtr = std::unique_ptr<u8, decltype(&av_free)>;
92 AVMallocPtr converted_frame_buffer{static_cast<u8*>(av_malloc(linear_size)), av_free};
93
94 const int converted_stride{frame->width * 4};
95 u8* const converted_frame_buf_addr{converted_frame_buffer.get()};
96
97 sws_scale(scaler_ctx, frame->data, frame->linesize, 0, frame->height,
98 &converted_frame_buf_addr, &converted_stride);
99
100 const u32 blk_kind = static_cast<u32>(config.block_linear_kind);
101 if (blk_kind != 0) {
102 // swizzle pitch linear to block linear
103 const u32 block_height = static_cast<u32>(config.block_linear_height_log2);
104 const auto size = Tegra::Texture::CalculateSize(true, 4, frame->width, frame->height, 1,
105 block_height, 0);
106 std::vector<u8> swizzled_data(size);
107 Tegra::Texture::CopySwizzledData(frame->width, frame->height, 1, 4, 4,
108 swizzled_data.data(), converted_frame_buffer.get(),
109 false, block_height, 0, 1);
110
111 gpu.MemoryManager().WriteBlock(output_surface_luma_address, swizzled_data.data(), size);
112 gpu.Maxwell3D().OnMemoryWrite();
113 } else {
114 // send pitch linear frame
115 gpu.MemoryManager().WriteBlock(output_surface_luma_address, converted_frame_buf_addr,
116 linear_size);
117 gpu.Maxwell3D().OnMemoryWrite();
118 }
119 break;
120 }
121 case VideoPixelFormat::Yuv420: {
122 LOG_TRACE(Service_NVDRV, "Writing YUV420 Frame");
123
124 const auto* frame = nvdec_processor->GetFrame();
125
126 if (!frame || frame->width == 0 || frame->height == 0) {
127 return;
128 }
129
130 const std::size_t surface_width = config.surface_width_minus1 + 1;
131 const std::size_t surface_height = config.surface_height_minus1 + 1;
132 const std::size_t half_width = surface_width / 2;
133 const std::size_t half_height = config.surface_height_minus1 / 2;
134 const std::size_t aligned_width = (surface_width + 0xff) & ~0xff;
135
136 const auto* luma_ptr = frame->data[0];
137 const auto* chroma_b_ptr = frame->data[1];
138 const auto* chroma_r_ptr = frame->data[2];
139 const auto stride = frame->linesize[0];
140 const auto half_stride = frame->linesize[1];
141
142 std::vector<u8> luma_buffer(aligned_width * surface_height);
143 std::vector<u8> chroma_buffer(aligned_width * half_height);
144
145 // Populate luma buffer
146 for (std::size_t y = 0; y < surface_height - 1; ++y) {
147 std::size_t src = y * stride;
148 std::size_t dst = y * aligned_width;
149
150 std::size_t size = surface_width;
151
152 for (std::size_t offset = 0; offset < size; ++offset) {
153 luma_buffer[dst + offset] = luma_ptr[src + offset];
154 }
155 }
156 gpu.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(),
157 luma_buffer.size());
158
159 // Populate chroma buffer from both channels with interleaving.
160 for (std::size_t y = 0; y < half_height; ++y) {
161 std::size_t src = y * half_stride;
162 std::size_t dst = y * aligned_width;
163
164 for (std::size_t x = 0; x < half_width; ++x) {
165 chroma_buffer[dst + x * 2] = chroma_b_ptr[src + x];
166 chroma_buffer[dst + x * 2 + 1] = chroma_r_ptr[src + x];
167 }
168 }
169 gpu.MemoryManager().WriteBlock(output_surface_chroma_u_address, chroma_buffer.data(),
170 chroma_buffer.size());
171 gpu.Maxwell3D().OnMemoryWrite();
172 break;
173 }
174 default:
175 UNIMPLEMENTED_MSG("Unknown video pixel format {}", config.pixel_format.Value());
176 break;
177 }
178}
179
180} // namespace Tegra
diff --git a/src/video_core/command_classes/vic.h b/src/video_core/command_classes/vic.h
new file mode 100644
index 000000000..8c4e284a1
--- /dev/null
+++ b/src/video_core/command_classes/vic.h
@@ -0,0 +1,110 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <vector>
9#include "common/bit_field.h"
10#include "common/common_types.h"
11
12struct SwsContext;
13
14namespace Tegra {
15class GPU;
16class Nvdec;
17
18struct PlaneOffsets {
19 u32 luma_offset{};
20 u32 chroma_u_offset{};
21 u32 chroma_v_offset{};
22};
23
24struct VicRegisters {
25 INSERT_PADDING_WORDS(64);
26 u32 nop{};
27 INSERT_PADDING_WORDS(15);
28 u32 pm_trigger{};
29 INSERT_PADDING_WORDS(47);
30 u32 set_application_id{};
31 u32 set_watchdog_timer{};
32 INSERT_PADDING_WORDS(17);
33 u32 context_save_area{};
34 u32 context_switch{};
35 INSERT_PADDING_WORDS(43);
36 u32 execute{};
37 INSERT_PADDING_WORDS(63);
38 std::array<std::array<PlaneOffsets, 8>, 8> surfacex_slots{};
39 u32 picture_index{};
40 u32 control_params{};
41 u32 config_struct_offset{};
42 u32 filter_struct_offset{};
43 u32 palette_offset{};
44 u32 hist_offset{};
45 u32 context_id{};
46 u32 fce_ucode_size{};
47 PlaneOffsets output_surface{};
48 u32 fce_ucode_offset{};
49 INSERT_PADDING_WORDS(4);
50 std::array<u32, 8> slot_context_id{};
51 INSERT_PADDING_WORDS(16);
52};
53static_assert(sizeof(VicRegisters) == 0x7A0, "VicRegisters is an invalid size");
54
55class Vic {
56public:
57 enum class Method : u32 {
58 Execute = 0xc0,
59 SetControlParams = 0x1c1,
60 SetConfigStructOffset = 0x1c2,
61 SetOutputSurfaceLumaOffset = 0x1c8,
62 SetOutputSurfaceChromaUOffset = 0x1c9,
63 SetOutputSurfaceChromaVOffset = 0x1ca
64 };
65
66 explicit Vic(GPU& gpu, std::shared_ptr<Nvdec> nvdec_processor);
67 ~Vic();
68
69 /// Write to the device state.
70 void ProcessMethod(Method method, const std::vector<u32>& arguments);
71
72private:
73 void Execute();
74
75 void VicStateWrite(u32 offset, u32 arguments);
76 VicRegisters vic_state{};
77
78 enum class VideoPixelFormat : u64_le {
79 RGBA8 = 0x1f,
80 BGRA8 = 0x20,
81 Yuv420 = 0x44,
82 };
83
84 union VicConfig {
85 u64_le raw{};
86 BitField<0, 7, u64_le> pixel_format;
87 BitField<7, 2, u64_le> chroma_loc_horiz;
88 BitField<9, 2, u64_le> chroma_loc_vert;
89 BitField<11, 4, u64_le> block_linear_kind;
90 BitField<15, 4, u64_le> block_linear_height_log2;
91 BitField<19, 3, u64_le> reserved0;
92 BitField<22, 10, u64_le> reserved1;
93 BitField<32, 14, u64_le> surface_width_minus1;
94 BitField<46, 14, u64_le> surface_height_minus1;
95 };
96
97 GPU& gpu;
98 std::shared_ptr<Tegra::Nvdec> nvdec_processor;
99
100 GPUVAddr config_struct_address{};
101 GPUVAddr output_surface_luma_address{};
102 GPUVAddr output_surface_chroma_u_address{};
103 GPUVAddr output_surface_chroma_v_address{};
104
105 SwsContext* scaler_ctx{};
106 s32 scaler_width{};
107 s32 scaler_height{};
108};
109
110} // namespace Tegra
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 4bb9256e9..171f78183 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -27,9 +27,10 @@ namespace Tegra {
27 27
28MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192)); 28MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192));
29 29
30GPU::GPU(Core::System& system_, bool is_async_) 30GPU::GPU(Core::System& system_, bool is_async_, bool use_nvdec_)
31 : system{system_}, memory_manager{std::make_unique<Tegra::MemoryManager>(system)}, 31 : system{system_}, memory_manager{std::make_unique<Tegra::MemoryManager>(system)},
32 dma_pusher{std::make_unique<Tegra::DmaPusher>(system, *this)}, 32 dma_pusher{std::make_unique<Tegra::DmaPusher>(system, *this)},
33 cdma_pusher{std::make_unique<Tegra::CDmaPusher>(*this)}, use_nvdec{use_nvdec_},
33 maxwell_3d{std::make_unique<Engines::Maxwell3D>(system, *memory_manager)}, 34 maxwell_3d{std::make_unique<Engines::Maxwell3D>(system, *memory_manager)},
34 fermi_2d{std::make_unique<Engines::Fermi2D>()}, 35 fermi_2d{std::make_unique<Engines::Fermi2D>()},
35 kepler_compute{std::make_unique<Engines::KeplerCompute>(system, *memory_manager)}, 36 kepler_compute{std::make_unique<Engines::KeplerCompute>(system, *memory_manager)},
@@ -77,10 +78,18 @@ DmaPusher& GPU::DmaPusher() {
77 return *dma_pusher; 78 return *dma_pusher;
78} 79}
79 80
81Tegra::CDmaPusher& GPU::CDmaPusher() {
82 return *cdma_pusher;
83}
84
80const DmaPusher& GPU::DmaPusher() const { 85const DmaPusher& GPU::DmaPusher() const {
81 return *dma_pusher; 86 return *dma_pusher;
82} 87}
83 88
89const Tegra::CDmaPusher& GPU::CDmaPusher() const {
90 return *cdma_pusher;
91}
92
84void GPU::WaitFence(u32 syncpoint_id, u32 value) { 93void GPU::WaitFence(u32 syncpoint_id, u32 value) {
85 // Synced GPU, is always in sync 94 // Synced GPU, is always in sync
86 if (!is_async) { 95 if (!is_async) {
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 2d15d1c6f..b8c613b11 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -13,6 +13,7 @@
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "core/hle/service/nvdrv/nvdata.h" 14#include "core/hle/service/nvdrv/nvdata.h"
15#include "core/hle/service/nvflinger/buffer_queue.h" 15#include "core/hle/service/nvflinger/buffer_queue.h"
16#include "video_core/cdma_pusher.h"
16#include "video_core/dma_pusher.h" 17#include "video_core/dma_pusher.h"
17 18
18using CacheAddr = std::uintptr_t; 19using CacheAddr = std::uintptr_t;
@@ -157,7 +158,7 @@ public:
157 method_count(method_count) {} 158 method_count(method_count) {}
158 }; 159 };
159 160
160 explicit GPU(Core::System& system, bool is_async); 161 explicit GPU(Core::System& system, bool is_async, bool use_nvdec);
161 virtual ~GPU(); 162 virtual ~GPU();
162 163
163 /// Binds a renderer to the GPU. 164 /// Binds a renderer to the GPU.
@@ -209,6 +210,15 @@ public:
209 /// Returns a reference to the GPU DMA pusher. 210 /// Returns a reference to the GPU DMA pusher.
210 Tegra::DmaPusher& DmaPusher(); 211 Tegra::DmaPusher& DmaPusher();
211 212
213 /// Returns a const reference to the GPU DMA pusher.
214 const Tegra::DmaPusher& DmaPusher() const;
215
216 /// Returns a reference to the GPU CDMA pusher.
217 Tegra::CDmaPusher& CDmaPusher();
218
219 /// Returns a const reference to the GPU CDMA pusher.
220 const Tegra::CDmaPusher& CDmaPusher() const;
221
212 VideoCore::RendererBase& Renderer() { 222 VideoCore::RendererBase& Renderer() {
213 return *renderer; 223 return *renderer;
214 } 224 }
@@ -249,8 +259,9 @@ public:
249 return is_async; 259 return is_async;
250 } 260 }
251 261
252 /// Returns a const reference to the GPU DMA pusher. 262 bool UseNvdec() const {
253 const Tegra::DmaPusher& DmaPusher() const; 263 return use_nvdec;
264 }
254 265
255 struct Regs { 266 struct Regs {
256 static constexpr size_t NUM_REGS = 0x40; 267 static constexpr size_t NUM_REGS = 0x40;
@@ -311,6 +322,9 @@ public:
311 /// Push GPU command entries to be processed 322 /// Push GPU command entries to be processed
312 virtual void PushGPUEntries(Tegra::CommandList&& entries) = 0; 323 virtual void PushGPUEntries(Tegra::CommandList&& entries) = 0;
313 324
325 /// Push GPU command buffer entries to be processed
326 virtual void PushCommandBuffer(Tegra::ChCommandHeaderList& entries) = 0;
327
314 /// Swap buffers (render frame) 328 /// Swap buffers (render frame)
315 virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0; 329 virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0;
316 330
@@ -349,7 +363,9 @@ protected:
349 Core::System& system; 363 Core::System& system;
350 std::unique_ptr<Tegra::MemoryManager> memory_manager; 364 std::unique_ptr<Tegra::MemoryManager> memory_manager;
351 std::unique_ptr<Tegra::DmaPusher> dma_pusher; 365 std::unique_ptr<Tegra::DmaPusher> dma_pusher;
366 std::unique_ptr<Tegra::CDmaPusher> cdma_pusher;
352 std::unique_ptr<VideoCore::RendererBase> renderer; 367 std::unique_ptr<VideoCore::RendererBase> renderer;
368 const bool use_nvdec;
353 369
354private: 370private:
355 /// Mapping of command subchannels to their bound engine ids 371 /// Mapping of command subchannels to their bound engine ids
@@ -372,6 +388,7 @@ private:
372 std::array<std::list<u32>, Service::Nvidia::MaxSyncPoints> syncpt_interrupts; 388 std::array<std::list<u32>, Service::Nvidia::MaxSyncPoints> syncpt_interrupts;
373 389
374 std::mutex sync_mutex; 390 std::mutex sync_mutex;
391 std::mutex device_mutex;
375 392
376 std::condition_variable sync_cv; 393 std::condition_variable sync_cv;
377 394
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp
index 70a3d5738..a9baaf7ef 100644
--- a/src/video_core/gpu_asynch.cpp
+++ b/src/video_core/gpu_asynch.cpp
@@ -10,12 +10,13 @@
10 10
11namespace VideoCommon { 11namespace VideoCommon {
12 12
13GPUAsynch::GPUAsynch(Core::System& system) : GPU{system, true}, gpu_thread{system} {} 13GPUAsynch::GPUAsynch(Core::System& system, bool use_nvdec)
14 : GPU{system, true, use_nvdec}, gpu_thread{system} {}
14 15
15GPUAsynch::~GPUAsynch() = default; 16GPUAsynch::~GPUAsynch() = default;
16 17
17void GPUAsynch::Start() { 18void GPUAsynch::Start() {
18 gpu_thread.StartThread(*renderer, renderer->Context(), *dma_pusher); 19 gpu_thread.StartThread(*renderer, renderer->Context(), *dma_pusher, *cdma_pusher);
19 cpu_context = renderer->GetRenderWindow().CreateSharedContext(); 20 cpu_context = renderer->GetRenderWindow().CreateSharedContext();
20 cpu_context->MakeCurrent(); 21 cpu_context->MakeCurrent();
21} 22}
@@ -32,6 +33,27 @@ void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) {
32 gpu_thread.SubmitList(std::move(entries)); 33 gpu_thread.SubmitList(std::move(entries));
33} 34}
34 35
36void GPUAsynch::PushCommandBuffer(Tegra::ChCommandHeaderList& entries) {
37 if (!use_nvdec) {
38 return;
39 }
40 // This condition fires when a video stream ends, clear all intermediary data
41 if (entries[0].raw == 0xDEADB33F) {
42 cdma_pusher.reset();
43 return;
44 }
45 if (!cdma_pusher) {
46 cdma_pusher = std::make_unique<Tegra::CDmaPusher>(*this);
47 }
48
49 // SubmitCommandBuffer would make the nvdec operations async, this is not currently working
50 // TODO(ameerj): RE proper async nvdec operation
51 // gpu_thread.SubmitCommandBuffer(std::move(entries));
52
53 cdma_pusher->Push(std::move(entries));
54 cdma_pusher->DispatchCalls();
55}
56
35void GPUAsynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { 57void GPUAsynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
36 gpu_thread.SwapBuffers(framebuffer); 58 gpu_thread.SwapBuffers(framebuffer);
37} 59}
diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h
index f89c855a5..0c0872e73 100644
--- a/src/video_core/gpu_asynch.h
+++ b/src/video_core/gpu_asynch.h
@@ -20,13 +20,14 @@ namespace VideoCommon {
20/// Implementation of GPU interface that runs the GPU asynchronously 20/// Implementation of GPU interface that runs the GPU asynchronously
21class GPUAsynch final : public Tegra::GPU { 21class GPUAsynch final : public Tegra::GPU {
22public: 22public:
23 explicit GPUAsynch(Core::System& system); 23 explicit GPUAsynch(Core::System& system, bool use_nvdec);
24 ~GPUAsynch() override; 24 ~GPUAsynch() override;
25 25
26 void Start() override; 26 void Start() override;
27 void ObtainContext() override; 27 void ObtainContext() override;
28 void ReleaseContext() override; 28 void ReleaseContext() override;
29 void PushGPUEntries(Tegra::CommandList&& entries) override; 29 void PushGPUEntries(Tegra::CommandList&& entries) override;
30 void PushCommandBuffer(Tegra::ChCommandHeaderList& entries) override;
30 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; 31 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
31 void FlushRegion(VAddr addr, u64 size) override; 32 void FlushRegion(VAddr addr, u64 size) override;
32 void InvalidateRegion(VAddr addr, u64 size) override; 33 void InvalidateRegion(VAddr addr, u64 size) override;
diff --git a/src/video_core/gpu_synch.cpp b/src/video_core/gpu_synch.cpp
index 1ca47ddef..ecf7bbdf3 100644
--- a/src/video_core/gpu_synch.cpp
+++ b/src/video_core/gpu_synch.cpp
@@ -7,7 +7,7 @@
7 7
8namespace VideoCommon { 8namespace VideoCommon {
9 9
10GPUSynch::GPUSynch(Core::System& system) : GPU{system, false} {} 10GPUSynch::GPUSynch(Core::System& system, bool use_nvdec) : GPU{system, false, use_nvdec} {}
11 11
12GPUSynch::~GPUSynch() = default; 12GPUSynch::~GPUSynch() = default;
13 13
@@ -26,6 +26,22 @@ void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {
26 dma_pusher->DispatchCalls(); 26 dma_pusher->DispatchCalls();
27} 27}
28 28
29void GPUSynch::PushCommandBuffer(Tegra::ChCommandHeaderList& entries) {
30 if (!use_nvdec) {
31 return;
32 }
33 // This condition fires when a video stream ends, clears all intermediary data
34 if (entries[0].raw == 0xDEADB33F) {
35 cdma_pusher.reset();
36 return;
37 }
38 if (!cdma_pusher) {
39 cdma_pusher = std::make_unique<Tegra::CDmaPusher>(*this);
40 }
41 cdma_pusher->Push(std::move(entries));
42 cdma_pusher->DispatchCalls();
43}
44
29void GPUSynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { 45void GPUSynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
30 renderer->SwapBuffers(framebuffer); 46 renderer->SwapBuffers(framebuffer);
31} 47}
diff --git a/src/video_core/gpu_synch.h b/src/video_core/gpu_synch.h
index 297258cb1..9d778c71a 100644
--- a/src/video_core/gpu_synch.h
+++ b/src/video_core/gpu_synch.h
@@ -19,13 +19,14 @@ namespace VideoCommon {
19/// Implementation of GPU interface that runs the GPU synchronously 19/// Implementation of GPU interface that runs the GPU synchronously
20class GPUSynch final : public Tegra::GPU { 20class GPUSynch final : public Tegra::GPU {
21public: 21public:
22 explicit GPUSynch(Core::System& system); 22 explicit GPUSynch(Core::System& system, bool use_nvdec);
23 ~GPUSynch() override; 23 ~GPUSynch() override;
24 24
25 void Start() override; 25 void Start() override;
26 void ObtainContext() override; 26 void ObtainContext() override;
27 void ReleaseContext() override; 27 void ReleaseContext() override;
28 void PushGPUEntries(Tegra::CommandList&& entries) override; 28 void PushGPUEntries(Tegra::CommandList&& entries) override;
29 void PushCommandBuffer(Tegra::ChCommandHeaderList& entries) override;
29 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; 30 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
30 void FlushRegion(VAddr addr, u64 size) override; 31 void FlushRegion(VAddr addr, u64 size) override;
31 void InvalidateRegion(VAddr addr, u64 size) override; 32 void InvalidateRegion(VAddr addr, u64 size) override;
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index bf761abf2..4b8f58283 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -18,7 +18,7 @@ namespace VideoCommon::GPUThread {
18/// Runs the GPU thread 18/// Runs the GPU thread
19static void RunThread(Core::System& system, VideoCore::RendererBase& renderer, 19static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
20 Core::Frontend::GraphicsContext& context, Tegra::DmaPusher& dma_pusher, 20 Core::Frontend::GraphicsContext& context, Tegra::DmaPusher& dma_pusher,
21 SynchState& state) { 21 SynchState& state, Tegra::CDmaPusher& cdma_pusher) {
22 std::string name = "yuzu:GPU"; 22 std::string name = "yuzu:GPU";
23 MicroProfileOnThreadCreate(name.c_str()); 23 MicroProfileOnThreadCreate(name.c_str());
24 Common::SetCurrentThreadName(name.c_str()); 24 Common::SetCurrentThreadName(name.c_str());
@@ -42,6 +42,10 @@ static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
42 if (const auto submit_list = std::get_if<SubmitListCommand>(&next.data)) { 42 if (const auto submit_list = std::get_if<SubmitListCommand>(&next.data)) {
43 dma_pusher.Push(std::move(submit_list->entries)); 43 dma_pusher.Push(std::move(submit_list->entries));
44 dma_pusher.DispatchCalls(); 44 dma_pusher.DispatchCalls();
45 } else if (const auto command_list = std::get_if<SubmitChCommandEntries>(&next.data)) {
46 // NVDEC
47 cdma_pusher.Push(std::move(command_list->entries));
48 cdma_pusher.DispatchCalls();
45 } else if (const auto data = std::get_if<SwapBuffersCommand>(&next.data)) { 49 } else if (const auto data = std::get_if<SwapBuffersCommand>(&next.data)) {
46 renderer.SwapBuffers(data->framebuffer ? &*data->framebuffer : nullptr); 50 renderer.SwapBuffers(data->framebuffer ? &*data->framebuffer : nullptr);
47 } else if (std::holds_alternative<OnCommandListEndCommand>(next.data)) { 51 } else if (std::holds_alternative<OnCommandListEndCommand>(next.data)) {
@@ -75,15 +79,19 @@ ThreadManager::~ThreadManager() {
75 79
76void ThreadManager::StartThread(VideoCore::RendererBase& renderer, 80void ThreadManager::StartThread(VideoCore::RendererBase& renderer,
77 Core::Frontend::GraphicsContext& context, 81 Core::Frontend::GraphicsContext& context,
78 Tegra::DmaPusher& dma_pusher) { 82 Tegra::DmaPusher& dma_pusher, Tegra::CDmaPusher& cdma_pusher) {
79 thread = std::thread{RunThread, std::ref(system), std::ref(renderer), 83 thread = std::thread(RunThread, std::ref(system), std::ref(renderer), std::ref(context),
80 std::ref(context), std::ref(dma_pusher), std::ref(state)}; 84 std::ref(dma_pusher), std::ref(state), std::ref(cdma_pusher));
81} 85}
82 86
83void ThreadManager::SubmitList(Tegra::CommandList&& entries) { 87void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
84 PushCommand(SubmitListCommand(std::move(entries))); 88 PushCommand(SubmitListCommand(std::move(entries)));
85} 89}
86 90
91void ThreadManager::SubmitCommandBuffer(Tegra::ChCommandHeaderList&& entries) {
92 PushCommand(SubmitChCommandEntries(std::move(entries)));
93}
94
87void ThreadManager::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { 95void ThreadManager::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
88 PushCommand(SwapBuffersCommand(framebuffer ? std::make_optional(*framebuffer) : std::nullopt)); 96 PushCommand(SwapBuffersCommand(framebuffer ? std::make_optional(*framebuffer) : std::nullopt));
89} 97}
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
index 5a28335d6..32a34e3a7 100644
--- a/src/video_core/gpu_thread.h
+++ b/src/video_core/gpu_thread.h
@@ -37,6 +37,14 @@ struct SubmitListCommand final {
37 Tegra::CommandList entries; 37 Tegra::CommandList entries;
38}; 38};
39 39
40/// Command to signal to the GPU thread that a cdma command list is ready for processing
41struct SubmitChCommandEntries final {
42 explicit SubmitChCommandEntries(Tegra::ChCommandHeaderList&& entries)
43 : entries{std::move(entries)} {}
44
45 Tegra::ChCommandHeaderList entries;
46};
47
40/// Command to signal to the GPU thread that a swap buffers is pending 48/// Command to signal to the GPU thread that a swap buffers is pending
41struct SwapBuffersCommand final { 49struct SwapBuffersCommand final {
42 explicit SwapBuffersCommand(std::optional<const Tegra::FramebufferConfig> framebuffer) 50 explicit SwapBuffersCommand(std::optional<const Tegra::FramebufferConfig> framebuffer)
@@ -77,9 +85,9 @@ struct OnCommandListEndCommand final {};
77struct GPUTickCommand final {}; 85struct GPUTickCommand final {};
78 86
79using CommandData = 87using CommandData =
80 std::variant<EndProcessingCommand, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand, 88 std::variant<EndProcessingCommand, SubmitListCommand, SubmitChCommandEntries,
81 InvalidateRegionCommand, FlushAndInvalidateRegionCommand, OnCommandListEndCommand, 89 SwapBuffersCommand, FlushRegionCommand, InvalidateRegionCommand,
82 GPUTickCommand>; 90 FlushAndInvalidateRegionCommand, OnCommandListEndCommand, GPUTickCommand>;
83 91
84struct CommandDataContainer { 92struct CommandDataContainer {
85 CommandDataContainer() = default; 93 CommandDataContainer() = default;
@@ -109,11 +117,14 @@ public:
109 117
110 /// Creates and starts the GPU thread. 118 /// Creates and starts the GPU thread.
111 void StartThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context, 119 void StartThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context,
112 Tegra::DmaPusher& dma_pusher); 120 Tegra::DmaPusher& dma_pusher, Tegra::CDmaPusher& cdma_pusher);
113 121
114 /// Push GPU command entries to be processed 122 /// Push GPU command entries to be processed
115 void SubmitList(Tegra::CommandList&& entries); 123 void SubmitList(Tegra::CommandList&& entries);
116 124
125 /// Push GPU CDMA command buffer entries to be processed
126 void SubmitCommandBuffer(Tegra::ChCommandHeaderList&& entries);
127
117 /// Swap buffers (render frame) 128 /// Swap buffers (render frame)
118 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer); 129 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer);
119 130
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt
index aa62363a7..c157724a9 100644
--- a/src/video_core/host_shaders/CMakeLists.txt
+++ b/src/video_core/host_shaders/CMakeLists.txt
@@ -1,23 +1,16 @@
1set(SHADER_FILES 1set(SHADER_SOURCES
2 opengl_present.frag 2 opengl_present.frag
3 opengl_present.vert 3 opengl_present.vert
4) 4)
5 5
6set(SHADER_INCLUDE ${CMAKE_CURRENT_BINARY_DIR}/include) 6set(SHADER_INCLUDE ${CMAKE_CURRENT_BINARY_DIR}/include)
7set(HOST_SHADERS_INCLUDE ${SHADER_INCLUDE} PARENT_SCOPE)
8
9set(SHADER_DIR ${SHADER_INCLUDE}/video_core/host_shaders) 7set(SHADER_DIR ${SHADER_INCLUDE}/video_core/host_shaders)
10add_custom_command( 8set(HOST_SHADERS_INCLUDE ${SHADER_INCLUDE} PARENT_SCOPE)
11 OUTPUT
12 ${SHADER_DIR}
13 COMMAND
14 ${CMAKE_COMMAND} -E make_directory ${SHADER_DIR}
15)
16 9
17set(INPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/source_shader.h.in) 10set(INPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/source_shader.h.in)
18set(HEADER_GENERATOR ${CMAKE_CURRENT_SOURCE_DIR}/StringShaderHeader.cmake) 11set(HEADER_GENERATOR ${CMAKE_CURRENT_SOURCE_DIR}/StringShaderHeader.cmake)
19 12
20foreach(FILENAME IN ITEMS ${SHADER_FILES}) 13foreach(FILENAME IN ITEMS ${SHADER_SOURCES})
21 string(REPLACE "." "_" SHADER_NAME ${FILENAME}) 14 string(REPLACE "." "_" SHADER_NAME ${FILENAME})
22 set(SOURCE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME}) 15 set(SOURCE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME})
23 set(HEADER_FILE ${SHADER_DIR}/${SHADER_NAME}.h) 16 set(HEADER_FILE ${SHADER_DIR}/${SHADER_NAME}.h)
@@ -29,8 +22,8 @@ foreach(FILENAME IN ITEMS ${SHADER_FILES})
29 MAIN_DEPENDENCY 22 MAIN_DEPENDENCY
30 ${SOURCE_FILE} 23 ${SOURCE_FILE}
31 DEPENDS 24 DEPENDS
32 ${HEADER_GENERATOR}
33 ${INPUT_FILE} 25 ${INPUT_FILE}
26 # HEADER_GENERATOR should be included here but msbuild seems to assume it's always modified
34 ) 27 )
35 set(SHADER_HEADERS ${SHADER_HEADERS} ${HEADER_FILE}) 28 set(SHADER_HEADERS ${SHADER_HEADERS} ${HEADER_FILE})
36endforeach() 29endforeach()
@@ -39,5 +32,5 @@ add_custom_target(host_shaders
39 DEPENDS 32 DEPENDS
40 ${SHADER_HEADERS} 33 ${SHADER_HEADERS}
41 SOURCES 34 SOURCES
42 ${SHADER_FILES} 35 ${SHADER_SOURCES}
43) 36)
diff --git a/src/video_core/host_shaders/StringShaderHeader.cmake b/src/video_core/host_shaders/StringShaderHeader.cmake
index 368bce0ed..c0fc49768 100644
--- a/src/video_core/host_shaders/StringShaderHeader.cmake
+++ b/src/video_core/host_shaders/StringShaderHeader.cmake
@@ -8,4 +8,6 @@ string(TOUPPER ${CONTENTS_NAME} CONTENTS_NAME)
8 8
9file(READ ${SOURCE_FILE} CONTENTS) 9file(READ ${SOURCE_FILE} CONTENTS)
10 10
11get_filename_component(OUTPUT_DIR ${HEADER_FILE} DIRECTORY)
12make_directory(${OUTPUT_DIR})
11configure_file(${INPUT_FILE} ${HEADER_FILE} @ONLY) 13configure_file(${INPUT_FILE} ${HEADER_FILE} @ONLY)
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 02cf53d15..6e70bd362 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -11,6 +11,7 @@
11#include "video_core/gpu.h" 11#include "video_core/gpu.h"
12#include "video_core/memory_manager.h" 12#include "video_core/memory_manager.h"
13#include "video_core/rasterizer_interface.h" 13#include "video_core/rasterizer_interface.h"
14#include "video_core/renderer_base.h"
14 15
15namespace Tegra { 16namespace Tegra {
16 17
@@ -44,6 +45,12 @@ GPUVAddr MemoryManager::MapAllocate(VAddr cpu_addr, std::size_t size, std::size_
44 return Map(cpu_addr, *FindFreeRange(size, align), size); 45 return Map(cpu_addr, *FindFreeRange(size, align), size);
45} 46}
46 47
48GPUVAddr MemoryManager::MapAllocate32(VAddr cpu_addr, std::size_t size) {
49 const std::optional<GPUVAddr> gpu_addr = FindFreeRange(size, 1, true);
50 ASSERT(gpu_addr);
51 return Map(cpu_addr, *gpu_addr, size);
52}
53
47void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) { 54void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) {
48 if (!size) { 55 if (!size) {
49 return; 56 return;
@@ -108,7 +115,8 @@ void MemoryManager::SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::s
108 page_table[PageEntryIndex(gpu_addr)] = page_entry; 115 page_table[PageEntryIndex(gpu_addr)] = page_entry;
109} 116}
110 117
111std::optional<GPUVAddr> MemoryManager::FindFreeRange(std::size_t size, std::size_t align) const { 118std::optional<GPUVAddr> MemoryManager::FindFreeRange(std::size_t size, std::size_t align,
119 bool start_32bit_address) const {
112 if (!align) { 120 if (!align) {
113 align = page_size; 121 align = page_size;
114 } else { 122 } else {
@@ -116,7 +124,7 @@ std::optional<GPUVAddr> MemoryManager::FindFreeRange(std::size_t size, std::size
116 } 124 }
117 125
118 u64 available_size{}; 126 u64 available_size{};
119 GPUVAddr gpu_addr{address_space_start}; 127 GPUVAddr gpu_addr{start_32bit_address ? address_space_start_low : address_space_start};
120 while (gpu_addr + available_size < address_space_size) { 128 while (gpu_addr + available_size < address_space_size) {
121 if (GetPageEntry(gpu_addr + available_size).IsUnmapped()) { 129 if (GetPageEntry(gpu_addr + available_size).IsUnmapped()) {
122 available_size += page_size; 130 available_size += page_size;
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index 53c8d122a..c078193d9 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -116,6 +116,7 @@ public:
116 116
117 [[nodiscard]] GPUVAddr Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size); 117 [[nodiscard]] GPUVAddr Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size);
118 [[nodiscard]] GPUVAddr MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align); 118 [[nodiscard]] GPUVAddr MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align);
119 [[nodiscard]] GPUVAddr MapAllocate32(VAddr cpu_addr, std::size_t size);
119 [[nodiscard]] std::optional<GPUVAddr> AllocateFixed(GPUVAddr gpu_addr, std::size_t size); 120 [[nodiscard]] std::optional<GPUVAddr> AllocateFixed(GPUVAddr gpu_addr, std::size_t size);
120 [[nodiscard]] GPUVAddr Allocate(std::size_t size, std::size_t align); 121 [[nodiscard]] GPUVAddr Allocate(std::size_t size, std::size_t align);
121 void Unmap(GPUVAddr gpu_addr, std::size_t size); 122 void Unmap(GPUVAddr gpu_addr, std::size_t size);
@@ -124,7 +125,8 @@ private:
124 [[nodiscard]] PageEntry GetPageEntry(GPUVAddr gpu_addr) const; 125 [[nodiscard]] PageEntry GetPageEntry(GPUVAddr gpu_addr) const;
125 void SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size = page_size); 126 void SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size = page_size);
126 GPUVAddr UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size); 127 GPUVAddr UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size);
127 [[nodiscard]] std::optional<GPUVAddr> FindFreeRange(std::size_t size, std::size_t align) const; 128 [[nodiscard]] std::optional<GPUVAddr> FindFreeRange(std::size_t size, std::size_t align,
129 bool start_32bit_address = false) const;
128 130
129 void TryLockPage(PageEntry page_entry, std::size_t size); 131 void TryLockPage(PageEntry page_entry, std::size_t size);
130 void TryUnlockPage(PageEntry page_entry, std::size_t size); 132 void TryUnlockPage(PageEntry page_entry, std::size_t size);
@@ -135,6 +137,7 @@ private:
135 137
136 static constexpr u64 address_space_size = 1ULL << 40; 138 static constexpr u64 address_space_size = 1ULL << 40;
137 static constexpr u64 address_space_start = 1ULL << 32; 139 static constexpr u64 address_space_start = 1ULL << 32;
140 static constexpr u64 address_space_start_low = 1ULL << 16;
138 static constexpr u64 page_bits{16}; 141 static constexpr u64 page_bits{16};
139 static constexpr u64 page_size{1 << page_bits}; 142 static constexpr u64 page_size{1 << page_bits};
140 static constexpr u64 page_mask{page_size - 1}; 143 static constexpr u64 page_mask{page_size - 1};
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
index 166ee34e1..70dd0c3c6 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
@@ -317,8 +317,7 @@ std::optional<std::vector<ShaderDiskCachePrecompiled>> ShaderDiskCacheOpenGL::Lo
317 return std::nullopt; 317 return std::nullopt;
318 } 318 }
319 } 319 }
320 320 return entries;
321 return std::move(entries);
322} 321}
323 322
324void ShaderDiskCacheOpenGL::InvalidateTransferable() { 323void ShaderDiskCacheOpenGL::InvalidateTransferable() {
diff --git a/src/video_core/renderer_vulkan/wrapper.cpp b/src/video_core/renderer_vulkan/wrapper.cpp
index c034558a3..4e83303d8 100644
--- a/src/video_core/renderer_vulkan/wrapper.cpp
+++ b/src/video_core/renderer_vulkan/wrapper.cpp
@@ -844,7 +844,7 @@ std::optional<std::vector<VkExtensionProperties>> EnumerateInstanceExtensionProp
844 VK_SUCCESS) { 844 VK_SUCCESS) {
845 return std::nullopt; 845 return std::nullopt;
846 } 846 }
847 return std::move(properties); 847 return properties;
848} 848}
849 849
850std::optional<std::vector<VkLayerProperties>> EnumerateInstanceLayerProperties( 850std::optional<std::vector<VkLayerProperties>> EnumerateInstanceLayerProperties(
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index a14df06a3..dd5cee4a1 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -44,10 +44,11 @@ namespace VideoCore {
44 44
45std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) { 45std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) {
46 std::unique_ptr<Tegra::GPU> gpu; 46 std::unique_ptr<Tegra::GPU> gpu;
47 const bool use_nvdec = Settings::values.use_nvdec_emulation.GetValue();
47 if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) { 48 if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
48 gpu = std::make_unique<VideoCommon::GPUAsynch>(system); 49 gpu = std::make_unique<VideoCommon::GPUAsynch>(system, use_nvdec);
49 } else { 50 } else {
50 gpu = std::make_unique<VideoCommon::GPUSynch>(system); 51 gpu = std::make_unique<VideoCommon::GPUSynch>(system, use_nvdec);
51 } 52 }
52 53
53 auto context = emu_window.CreateSharedContext(); 54 auto context = emu_window.CreateSharedContext();
diff --git a/src/web_service/CMakeLists.txt b/src/web_service/CMakeLists.txt
index 7e484b906..ae85a72ea 100644
--- a/src/web_service/CMakeLists.txt
+++ b/src/web_service/CMakeLists.txt
@@ -9,4 +9,4 @@ add_library(web_service STATIC
9) 9)
10 10
11create_target_directory_groups(web_service) 11create_target_directory_groups(web_service)
12target_link_libraries(web_service PRIVATE common nlohmann_json::nlohmann_json httplib lurlparser) 12target_link_libraries(web_service PRIVATE common nlohmann_json::nlohmann_json httplib)
diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp
index 534960d09..67183e64c 100644
--- a/src/web_service/web_backend.cpp
+++ b/src/web_service/web_backend.cpp
@@ -7,7 +7,6 @@
7#include <mutex> 7#include <mutex>
8#include <string> 8#include <string>
9 9
10#include <LUrlParser.h>
11#include <fmt/format.h> 10#include <fmt/format.h>
12#include <httplib.h> 11#include <httplib.h>
13 12
@@ -19,9 +18,6 @@ namespace WebService {
19 18
20constexpr std::array<const char, 1> API_VERSION{'1'}; 19constexpr std::array<const char, 1> API_VERSION{'1'};
21 20
22constexpr int HTTP_PORT = 80;
23constexpr int HTTPS_PORT = 443;
24
25constexpr std::size_t TIMEOUT_SECONDS = 30; 21constexpr std::size_t TIMEOUT_SECONDS = 30;
26 22
27struct Client::Impl { 23struct Client::Impl {
@@ -67,22 +63,14 @@ struct Client::Impl {
67 const std::string& jwt = "", const std::string& username = "", 63 const std::string& jwt = "", const std::string& username = "",
68 const std::string& token = "") { 64 const std::string& token = "") {
69 if (cli == nullptr) { 65 if (cli == nullptr) {
70 const auto parsedUrl = LUrlParser::clParseURL::ParseURL(host); 66 cli = std::make_unique<httplib::Client>(host.c_str());
71 int port{}; 67 }
72 if (parsedUrl.m_Scheme == "http") { 68
73 if (!parsedUrl.GetPort(&port)) { 69 if (!cli->is_valid()) {
74 port = HTTP_PORT; 70 LOG_ERROR(WebService, "Client is invalid, skipping request!");
75 } 71 return {};
76 } else if (parsedUrl.m_Scheme == "https") {
77 if (!parsedUrl.GetPort(&port)) {
78 port = HTTPS_PORT;
79 }
80 } else {
81 LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme);
82 return WebResult{WebResult::Code::InvalidURL, "Bad URL scheme", ""};
83 }
84 cli = std::make_unique<httplib::Client>(parsedUrl.m_Host.c_str(), port);
85 } 72 }
73
86 cli->set_connection_timeout(TIMEOUT_SECONDS); 74 cli->set_connection_timeout(TIMEOUT_SECONDS);
87 cli->set_read_timeout(TIMEOUT_SECONDS); 75 cli->set_read_timeout(TIMEOUT_SECONDS);
88 cli->set_write_timeout(TIMEOUT_SECONDS); 76 cli->set_write_timeout(TIMEOUT_SECONDS);
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index cc0291b15..4659e1f89 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -265,9 +265,11 @@ if (MSVC)
265 include(CopyYuzuQt5Deps) 265 include(CopyYuzuQt5Deps)
266 include(CopyYuzuSDLDeps) 266 include(CopyYuzuSDLDeps)
267 include(CopyYuzuUnicornDeps) 267 include(CopyYuzuUnicornDeps)
268 include(CopyYuzuFFmpegDeps)
268 copy_yuzu_Qt5_deps(yuzu) 269 copy_yuzu_Qt5_deps(yuzu)
269 copy_yuzu_SDL_deps(yuzu) 270 copy_yuzu_SDL_deps(yuzu)
270 copy_yuzu_unicorn_deps(yuzu) 271 copy_yuzu_unicorn_deps(yuzu)
272 copy_yuzu_FFmpeg_deps(yuzu)
271endif() 273endif()
272 274
273if (NOT APPLE) 275if (NOT APPLE)
diff --git a/src/yuzu/applets/controller.cpp b/src/yuzu/applets/controller.cpp
index 2760487a3..c6fa3e4f6 100644
--- a/src/yuzu/applets/controller.cpp
+++ b/src/yuzu/applets/controller.cpp
@@ -18,15 +18,15 @@
18 18
19namespace { 19namespace {
20 20
21constexpr std::array<std::array<bool, 4>, 8> led_patterns = {{ 21constexpr std::array<std::array<bool, 4>, 8> led_patterns{{
22 {1, 0, 0, 0}, 22 {true, false, false, false},
23 {1, 1, 0, 0}, 23 {true, true, false, false},
24 {1, 1, 1, 0}, 24 {true, true, true, false},
25 {1, 1, 1, 1}, 25 {true, true, true, true},
26 {1, 0, 0, 1}, 26 {true, false, false, true},
27 {1, 0, 1, 0}, 27 {true, false, true, false},
28 {1, 0, 1, 1}, 28 {true, false, true, true},
29 {0, 1, 1, 0}, 29 {false, true, true, false},
30}}; 30}};
31 31
32void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index, 32void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index,
@@ -589,7 +589,7 @@ QtControllerSelector::QtControllerSelector(GMainWindow& parent) {
589QtControllerSelector::~QtControllerSelector() = default; 589QtControllerSelector::~QtControllerSelector() = default;
590 590
591void QtControllerSelector::ReconfigureControllers( 591void QtControllerSelector::ReconfigureControllers(
592 std::function<void()> callback, Core::Frontend::ControllerParameters parameters) const { 592 std::function<void()> callback, const Core::Frontend::ControllerParameters& parameters) const {
593 this->callback = std::move(callback); 593 this->callback = std::move(callback);
594 emit MainWindowReconfigureControllers(parameters); 594 emit MainWindowReconfigureControllers(parameters);
595} 595}
diff --git a/src/yuzu/applets/controller.h b/src/yuzu/applets/controller.h
index 2d6d588c6..729ecc831 100644
--- a/src/yuzu/applets/controller.h
+++ b/src/yuzu/applets/controller.h
@@ -120,11 +120,13 @@ public:
120 explicit QtControllerSelector(GMainWindow& parent); 120 explicit QtControllerSelector(GMainWindow& parent);
121 ~QtControllerSelector() override; 121 ~QtControllerSelector() override;
122 122
123 void ReconfigureControllers(std::function<void()> callback, 123 void ReconfigureControllers(
124 Core::Frontend::ControllerParameters parameters) const override; 124 std::function<void()> callback,
125 const Core::Frontend::ControllerParameters& parameters) const override;
125 126
126signals: 127signals:
127 void MainWindowReconfigureControllers(Core::Frontend::ControllerParameters parameters) const; 128 void MainWindowReconfigureControllers(
129 const Core::Frontend::ControllerParameters& parameters) const;
128 130
129private: 131private:
130 void MainWindowReconfigureFinished(); 132 void MainWindowReconfigureFinished();
diff --git a/src/yuzu/applets/profile_select.cpp b/src/yuzu/applets/profile_select.cpp
index dca8835ed..c9a2f8601 100644
--- a/src/yuzu/applets/profile_select.cpp
+++ b/src/yuzu/applets/profile_select.cpp
@@ -114,6 +114,15 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent)
114 114
115QtProfileSelectionDialog::~QtProfileSelectionDialog() = default; 115QtProfileSelectionDialog::~QtProfileSelectionDialog() = default;
116 116
117int QtProfileSelectionDialog::exec() {
118 // Skip profile selection when there's only one.
119 if (profile_manager->GetUserCount() == 1) {
120 user_index = 0;
121 return QDialog::Accepted;
122 }
123 return QDialog::exec();
124}
125
117void QtProfileSelectionDialog::accept() { 126void QtProfileSelectionDialog::accept() {
118 QDialog::accept(); 127 QDialog::accept();
119} 128}
diff --git a/src/yuzu/applets/profile_select.h b/src/yuzu/applets/profile_select.h
index cee886a77..29c33cca0 100644
--- a/src/yuzu/applets/profile_select.h
+++ b/src/yuzu/applets/profile_select.h
@@ -27,6 +27,7 @@ public:
27 explicit QtProfileSelectionDialog(QWidget* parent); 27 explicit QtProfileSelectionDialog(QWidget* parent);
28 ~QtProfileSelectionDialog() override; 28 ~QtProfileSelectionDialog() override;
29 29
30 int exec() override;
30 void accept() override; 31 void accept() override;
31 void reject() override; 32 void reject() override;
32 33
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index d2913d613..1ce62e4a6 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -515,7 +515,7 @@ void Config::ReadMotionTouchValues() {
515void Config::ReadCoreValues() { 515void Config::ReadCoreValues() {
516 qt_config->beginGroup(QStringLiteral("Core")); 516 qt_config->beginGroup(QStringLiteral("Core"));
517 517
518 ReadSettingGlobal(Settings::values.use_multi_core, QStringLiteral("use_multi_core"), false); 518 ReadSettingGlobal(Settings::values.use_multi_core, QStringLiteral("use_multi_core"), true);
519 519
520 qt_config->endGroup(); 520 qt_config->endGroup();
521} 521}
@@ -716,10 +716,12 @@ void Config::ReadRendererValues() {
716 QStringLiteral("use_disk_shader_cache"), true); 716 QStringLiteral("use_disk_shader_cache"), true);
717 ReadSettingGlobal(Settings::values.gpu_accuracy, QStringLiteral("gpu_accuracy"), 0); 717 ReadSettingGlobal(Settings::values.gpu_accuracy, QStringLiteral("gpu_accuracy"), 0);
718 ReadSettingGlobal(Settings::values.use_asynchronous_gpu_emulation, 718 ReadSettingGlobal(Settings::values.use_asynchronous_gpu_emulation,
719 QStringLiteral("use_asynchronous_gpu_emulation"), false); 719 QStringLiteral("use_asynchronous_gpu_emulation"), true);
720 ReadSettingGlobal(Settings::values.use_nvdec_emulation, QStringLiteral("use_nvdec_emulation"),
721 true);
720 ReadSettingGlobal(Settings::values.use_vsync, QStringLiteral("use_vsync"), true); 722 ReadSettingGlobal(Settings::values.use_vsync, QStringLiteral("use_vsync"), true);
721 ReadSettingGlobal(Settings::values.use_assembly_shaders, QStringLiteral("use_assembly_shaders"), 723 ReadSettingGlobal(Settings::values.use_assembly_shaders, QStringLiteral("use_assembly_shaders"),
722 false); 724 true);
723 ReadSettingGlobal(Settings::values.use_asynchronous_shaders, 725 ReadSettingGlobal(Settings::values.use_asynchronous_shaders,
724 QStringLiteral("use_asynchronous_shaders"), false); 726 QStringLiteral("use_asynchronous_shaders"), false);
725 ReadSettingGlobal(Settings::values.use_fast_gpu_time, QStringLiteral("use_fast_gpu_time"), 727 ReadSettingGlobal(Settings::values.use_fast_gpu_time, QStringLiteral("use_fast_gpu_time"),
@@ -1108,7 +1110,7 @@ void Config::SaveControlValues() {
1108void Config::SaveCoreValues() { 1110void Config::SaveCoreValues() {
1109 qt_config->beginGroup(QStringLiteral("Core")); 1111 qt_config->beginGroup(QStringLiteral("Core"));
1110 1112
1111 WriteSettingGlobal(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, false); 1113 WriteSettingGlobal(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, true);
1112 1114
1113 qt_config->endGroup(); 1115 qt_config->endGroup();
1114} 1116}
@@ -1264,10 +1266,12 @@ void Config::SaveRendererValues() {
1264 static_cast<int>(Settings::values.gpu_accuracy.GetValue(global)), 1266 static_cast<int>(Settings::values.gpu_accuracy.GetValue(global)),
1265 Settings::values.gpu_accuracy.UsingGlobal(), 0); 1267 Settings::values.gpu_accuracy.UsingGlobal(), 0);
1266 WriteSettingGlobal(QStringLiteral("use_asynchronous_gpu_emulation"), 1268 WriteSettingGlobal(QStringLiteral("use_asynchronous_gpu_emulation"),
1267 Settings::values.use_asynchronous_gpu_emulation, false); 1269 Settings::values.use_asynchronous_gpu_emulation, true);
1270 WriteSettingGlobal(QStringLiteral("use_nvdec_emulation"), Settings::values.use_nvdec_emulation,
1271 true);
1268 WriteSettingGlobal(QStringLiteral("use_vsync"), Settings::values.use_vsync, true); 1272 WriteSettingGlobal(QStringLiteral("use_vsync"), Settings::values.use_vsync, true);
1269 WriteSettingGlobal(QStringLiteral("use_assembly_shaders"), 1273 WriteSettingGlobal(QStringLiteral("use_assembly_shaders"),
1270 Settings::values.use_assembly_shaders, false); 1274 Settings::values.use_assembly_shaders, true);
1271 WriteSettingGlobal(QStringLiteral("use_asynchronous_shaders"), 1275 WriteSettingGlobal(QStringLiteral("use_asynchronous_shaders"),
1272 Settings::values.use_asynchronous_shaders, false); 1276 Settings::values.use_asynchronous_shaders, false);
1273 WriteSettingGlobal(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time, 1277 WriteSettingGlobal(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time,
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 07d818548..4f083ecda 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -70,9 +70,11 @@ void ConfigureGraphics::SetConfiguration() {
70 ui->api->setEnabled(runtime_lock); 70 ui->api->setEnabled(runtime_lock);
71 ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock); 71 ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock);
72 ui->use_disk_shader_cache->setEnabled(runtime_lock); 72 ui->use_disk_shader_cache->setEnabled(runtime_lock);
73 ui->use_nvdec_emulation->setEnabled(runtime_lock);
73 ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue()); 74 ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue());
74 ui->use_asynchronous_gpu_emulation->setChecked( 75 ui->use_asynchronous_gpu_emulation->setChecked(
75 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());
76 78
77 if (Settings::configuring_global) { 79 if (Settings::configuring_global) {
78 ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue())); 80 ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue()));
@@ -116,6 +118,9 @@ void ConfigureGraphics::ApplyConfiguration() {
116 Settings::values.use_asynchronous_gpu_emulation.SetValue( 118 Settings::values.use_asynchronous_gpu_emulation.SetValue(
117 ui->use_asynchronous_gpu_emulation->isChecked()); 119 ui->use_asynchronous_gpu_emulation->isChecked());
118 } 120 }
121 if (Settings::values.use_nvdec_emulation.UsingGlobal()) {
122 Settings::values.use_nvdec_emulation.SetValue(ui->use_nvdec_emulation->isChecked());
123 }
119 if (Settings::values.bg_red.UsingGlobal()) { 124 if (Settings::values.bg_red.UsingGlobal()) {
120 Settings::values.bg_red.SetValue(static_cast<float>(bg_color.redF())); 125 Settings::values.bg_red.SetValue(static_cast<float>(bg_color.redF()));
121 Settings::values.bg_green.SetValue(static_cast<float>(bg_color.greenF())); 126 Settings::values.bg_green.SetValue(static_cast<float>(bg_color.greenF()));
@@ -144,6 +149,8 @@ void ConfigureGraphics::ApplyConfiguration() {
144 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_gpu_emulation, 149 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_gpu_emulation,
145 ui->use_asynchronous_gpu_emulation, 150 ui->use_asynchronous_gpu_emulation,
146 use_asynchronous_gpu_emulation); 151 use_asynchronous_gpu_emulation);
152 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_nvdec_emulation,
153 ui->use_nvdec_emulation, use_nvdec_emulation);
147 154
148 if (ui->bg_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { 155 if (ui->bg_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
149 Settings::values.bg_red.SetGlobal(true); 156 Settings::values.bg_red.SetGlobal(true);
@@ -240,6 +247,7 @@ void ConfigureGraphics::SetupPerGameUI() {
240 ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal()); 247 ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal());
241 ui->use_asynchronous_gpu_emulation->setEnabled( 248 ui->use_asynchronous_gpu_emulation->setEnabled(
242 Settings::values.use_asynchronous_gpu_emulation.UsingGlobal()); 249 Settings::values.use_asynchronous_gpu_emulation.UsingGlobal());
250 ui->use_nvdec_emulation->setEnabled(Settings::values.use_nvdec_emulation.UsingGlobal());
243 ui->use_disk_shader_cache->setEnabled(Settings::values.use_disk_shader_cache.UsingGlobal()); 251 ui->use_disk_shader_cache->setEnabled(Settings::values.use_disk_shader_cache.UsingGlobal());
244 ui->bg_button->setEnabled(Settings::values.bg_red.UsingGlobal()); 252 ui->bg_button->setEnabled(Settings::values.bg_red.UsingGlobal());
245 253
@@ -253,6 +261,8 @@ void ConfigureGraphics::SetupPerGameUI() {
253 261
254 ConfigurationShared::SetColoredTristate( 262 ConfigurationShared::SetColoredTristate(
255 ui->use_disk_shader_cache, Settings::values.use_disk_shader_cache, use_disk_shader_cache); 263 ui->use_disk_shader_cache, Settings::values.use_disk_shader_cache, use_disk_shader_cache);
264 ConfigurationShared::SetColoredTristate(
265 ui->use_nvdec_emulation, Settings::values.use_nvdec_emulation, use_nvdec_emulation);
256 ConfigurationShared::SetColoredTristate(ui->use_asynchronous_gpu_emulation, 266 ConfigurationShared::SetColoredTristate(ui->use_asynchronous_gpu_emulation,
257 Settings::values.use_asynchronous_gpu_emulation, 267 Settings::values.use_asynchronous_gpu_emulation,
258 use_asynchronous_gpu_emulation); 268 use_asynchronous_gpu_emulation);
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h
index b4961f719..1fefc88eb 100644
--- a/src/yuzu/configuration/configure_graphics.h
+++ b/src/yuzu/configuration/configure_graphics.h
@@ -46,6 +46,7 @@ private:
46 std::unique_ptr<Ui::ConfigureGraphics> ui; 46 std::unique_ptr<Ui::ConfigureGraphics> ui;
47 QColor bg_color; 47 QColor bg_color;
48 48
49 ConfigurationShared::CheckState use_nvdec_emulation;
49 ConfigurationShared::CheckState use_disk_shader_cache; 50 ConfigurationShared::CheckState use_disk_shader_cache;
50 ConfigurationShared::CheckState use_asynchronous_gpu_emulation; 51 ConfigurationShared::CheckState use_asynchronous_gpu_emulation;
51 52
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 62aa337e7..58486eb1e 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -98,6 +98,13 @@
98 </widget> 98 </widget>
99 </item> 99 </item>
100 <item> 100 <item>
101 <widget class="QCheckBox" name="use_nvdec_emulation">
102 <property name="text">
103 <string>Use NVDEC emulation</string>
104 </property>
105 </widget>
106 </item>
107 <item>
101 <widget class="QWidget" name="aspect_ratio_layout" native="true"> 108 <widget class="QWidget" name="aspect_ratio_layout" native="true">
102 <layout class="QHBoxLayout" name="horizontalLayout_6"> 109 <layout class="QHBoxLayout" name="horizontalLayout_6">
103 <property name="leftMargin"> 110 <property name="leftMargin">
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 9ad43ed8f..5e8e201dc 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -12,6 +12,7 @@
12#include "common/assert.h" 12#include "common/assert.h"
13#include "common/file_util.h" 13#include "common/file_util.h"
14#include "core/core.h" 14#include "core/core.h"
15#include "core/hle/service/time/time.h"
15#include "core/settings.h" 16#include "core/settings.h"
16#include "ui_configure_system.h" 17#include "ui_configure_system.h"
17#include "yuzu/configuration/configuration_shared.h" 18#include "yuzu/configuration/configuration_shared.h"
@@ -104,6 +105,22 @@ void ConfigureSystem::SetConfiguration() {
104void ConfigureSystem::ReadSystemSettings() {} 105void ConfigureSystem::ReadSystemSettings() {}
105 106
106void ConfigureSystem::ApplyConfiguration() { 107void ConfigureSystem::ApplyConfiguration() {
108 // Allow setting custom RTC even if system is powered on, to allow in-game time to be fast
109 // forwared
110 if (Settings::values.custom_rtc.UsingGlobal()) {
111 if (ui->custom_rtc_checkbox->isChecked()) {
112 Settings::values.custom_rtc.SetValue(
113 std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch()));
114 if (Core::System::GetInstance().IsPoweredOn()) {
115 const s64 posix_time{Settings::values.custom_rtc.GetValue()->count() +
116 Service::Time::TimeManager::GetExternalTimeZoneOffset()};
117 Core::System::GetInstance().GetTimeManager().UpdateLocalSystemClockTime(posix_time);
118 }
119 } else {
120 Settings::values.custom_rtc.SetValue(std::nullopt);
121 }
122 }
123
107 if (!enabled) { 124 if (!enabled) {
108 return; 125 return;
109 } 126 }
@@ -131,15 +148,6 @@ void ConfigureSystem::ApplyConfiguration() {
131 Settings::values.rng_seed.SetValue(std::nullopt); 148 Settings::values.rng_seed.SetValue(std::nullopt);
132 } 149 }
133 } 150 }
134
135 if (Settings::values.custom_rtc.UsingGlobal()) {
136 if (ui->custom_rtc_checkbox->isChecked()) {
137 Settings::values.custom_rtc.SetValue(
138 std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch()));
139 } else {
140 Settings::values.custom_rtc.SetValue(std::nullopt);
141 }
142 }
143 } else { 151 } else {
144 ConfigurationShared::ApplyPerGameSetting(&Settings::values.language_index, 152 ConfigurationShared::ApplyPerGameSetting(&Settings::values.language_index,
145 ui->combo_language); 153 ui->combo_language);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index e3de0f0e1..18e68e590 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -303,24 +303,18 @@ void GMainWindow::ControllerSelectorReconfigureControllers(
303} 303}
304 304
305void GMainWindow::ProfileSelectorSelectProfile() { 305void GMainWindow::ProfileSelectorSelectProfile() {
306 const Service::Account::ProfileManager manager; 306 QtProfileSelectionDialog dialog(this);
307 int index = 0; 307 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint |
308 if (manager.GetUserCount() != 1) { 308 Qt::WindowTitleHint | Qt::WindowSystemMenuHint |
309 QtProfileSelectionDialog dialog(this); 309 Qt::WindowCloseButtonHint);
310 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint | 310 dialog.setWindowModality(Qt::WindowModal);
311 Qt::WindowTitleHint | Qt::WindowSystemMenuHint | 311 if (dialog.exec() == QDialog::Rejected) {
312 Qt::WindowCloseButtonHint); 312 emit ProfileSelectorFinishedSelection(std::nullopt);
313 dialog.setWindowModality(Qt::WindowModal); 313 return;
314
315 if (dialog.exec() == QDialog::Rejected) {
316 emit ProfileSelectorFinishedSelection(std::nullopt);
317 return;
318 }
319
320 index = dialog.GetIndex();
321 } 314 }
322 315
323 const auto uuid = manager.GetUser(static_cast<std::size_t>(index)); 316 const Service::Account::ProfileManager manager;
317 const auto uuid = manager.GetUser(static_cast<std::size_t>(dialog.GetIndex()));
324 if (!uuid.has_value()) { 318 if (!uuid.has_value()) {
325 emit ProfileSelectorFinishedSelection(std::nullopt); 319 emit ProfileSelectorFinishedSelection(std::nullopt);
326 return; 320 return;
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 23448e747..334038ef9 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -371,7 +371,7 @@ void Config::ReadValues() {
371 371
372 // Core 372 // Core
373 Settings::values.use_multi_core.SetValue( 373 Settings::values.use_multi_core.SetValue(
374 sdl2_config->GetBoolean("Core", "use_multi_core", false)); 374 sdl2_config->GetBoolean("Core", "use_multi_core", true));
375 375
376 // Renderer 376 // Renderer
377 const int renderer_backend = sdl2_config->GetInteger( 377 const int renderer_backend = sdl2_config->GetInteger(
@@ -395,11 +395,11 @@ void Config::ReadValues() {
395 const int gpu_accuracy_level = sdl2_config->GetInteger("Renderer", "gpu_accuracy", 0); 395 const int gpu_accuracy_level = sdl2_config->GetInteger("Renderer", "gpu_accuracy", 0);
396 Settings::values.gpu_accuracy.SetValue(static_cast<Settings::GPUAccuracy>(gpu_accuracy_level)); 396 Settings::values.gpu_accuracy.SetValue(static_cast<Settings::GPUAccuracy>(gpu_accuracy_level));
397 Settings::values.use_asynchronous_gpu_emulation.SetValue( 397 Settings::values.use_asynchronous_gpu_emulation.SetValue(
398 sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false)); 398 sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", true));
399 Settings::values.use_vsync.SetValue( 399 Settings::values.use_vsync.SetValue(
400 static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync", 1))); 400 static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync", 1)));
401 Settings::values.use_assembly_shaders.SetValue( 401 Settings::values.use_assembly_shaders.SetValue(
402 sdl2_config->GetBoolean("Renderer", "use_assembly_shaders", false)); 402 sdl2_config->GetBoolean("Renderer", "use_assembly_shaders", true));
403 Settings::values.use_asynchronous_shaders.SetValue( 403 Settings::values.use_asynchronous_shaders.SetValue(
404 sdl2_config->GetBoolean("Renderer", "use_asynchronous_shaders", false)); 404 sdl2_config->GetBoolean("Renderer", "use_asynchronous_shaders", false));
405 Settings::values.use_asynchronous_shaders.SetValue( 405 Settings::values.use_asynchronous_shaders.SetValue(
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index aa9e40380..796e27df4 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -94,7 +94,7 @@ udp_pad_index=
94 94
95[Core] 95[Core]
96# Whether to use multi-core for CPU emulation 96# Whether to use multi-core for CPU emulation
97# 0 (default): Disabled, 1: Enabled 97# 0: Disabled, 1 (default): Enabled
98use_multi_core= 98use_multi_core=
99 99
100[Cpu] 100[Cpu]
@@ -163,7 +163,7 @@ max_anisotropy =
163use_vsync = 163use_vsync =
164 164
165# Whether to use OpenGL assembly shaders or not. NV_gpu_program5 is required. 165# Whether to use OpenGL assembly shaders or not. NV_gpu_program5 is required.
166# 0 (default): Off, 1: On 166# 0: Off, 1 (default): On
167use_assembly_shaders = 167use_assembly_shaders =
168 168
169# Whether to allow asynchronous shader building. 169# Whether to allow asynchronous shader building.