summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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
89 files changed, 4883 insertions, 1015 deletions
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.