diff options
Diffstat (limited to 'src')
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 | ||
| 94 | void Fiber::YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to) { | 94 | void 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 | ||
| 202 | void Fiber::YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to) { | 202 | void 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 | |||
| 9 | namespace Common { | ||
| 10 | |||
| 11 | Stream::Stream() = default; | ||
| 12 | Stream::~Stream() = default; | ||
| 13 | |||
| 14 | void 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 | |||
| 30 | u8 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 | |||
| 38 | void 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 | |||
| 10 | namespace Common { | ||
| 11 | |||
| 12 | enum class SeekOrigin { | ||
| 13 | SetOrigin, | ||
| 14 | FromCurrentPos, | ||
| 15 | FromEnd, | ||
| 16 | }; | ||
| 17 | |||
| 18 | class Stream { | ||
| 19 | public: | ||
| 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 | |||
| 45 | private: | ||
| 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, | |||
| 121 | struct System::Impl { | 122 | struct 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 | ||
| 725 | Service::Time::TimeManager& System::GetTimeManager() { | ||
| 726 | return impl->time_manager; | ||
| 727 | } | ||
| 728 | |||
| 729 | const Service::Time::TimeManager& System::GetTimeManager() const { | ||
| 730 | return impl->time_manager; | ||
| 731 | } | ||
| 732 | |||
| 720 | void System::SetExitLock(bool locked) { | 733 | void 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 { | |||
| 69 | class ServiceManager; | 69 | class ServiceManager; |
| 70 | } // namespace SM | 70 | } // namespace SM |
| 71 | 71 | ||
| 72 | namespace Time { | ||
| 73 | class TimeManager; | ||
| 74 | } // namespace Time | ||
| 75 | |||
| 72 | } // namespace Service | 76 | } // namespace Service |
| 73 | 77 | ||
| 74 | namespace Tegra { | 78 | namespace 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 | |||
| 19 | DefaultControllerApplet::~DefaultControllerApplet() = default; | 19 | DefaultControllerApplet::~DefaultControllerApplet() = default; |
| 20 | 20 | ||
| 21 | void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callback, | 21 | void 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 | ||
| 44 | class DefaultControllerApplet final : public ControllerApplet { | 44 | class 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 | ||
| 52 | private: | 52 | private: |
| 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 | ||
| 11 | namespace Service::Nvidia::Devices { | 12 | namespace Service::Nvidia::Devices { |
| 12 | 13 | ||
| 13 | nvhost_nvdec::nvhost_nvdec(Core::System& system) : nvdevice(system) {} | 14 | nvhost_nvdec::nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) |
| 15 | : nvhost_nvdec_common(system, std::move(nvmap_dev)) {} | ||
| 14 | nvhost_nvdec::~nvhost_nvdec() = default; | 16 | nvhost_nvdec::~nvhost_nvdec() = default; |
| 15 | 17 | ||
| 16 | u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, | 18 | u32 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 | |||
| 43 | u32 nvhost_nvdec::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 44 | IoctlSetNvmapFD params{}; | ||
| 45 | std::memcpy(¶ms, 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 | |||
| 52 | u32 nvhost_nvdec::Submit(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 53 | IoctlSubmit params{}; | ||
| 54 | std::memcpy(¶ms, input.data(), sizeof(IoctlSubmit)); | ||
| 55 | LOG_WARNING(Service_NVDRV, "(STUBBED) called"); | ||
| 56 | std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmit)); | ||
| 57 | return 0; | ||
| 58 | } | ||
| 59 | |||
| 60 | u32 nvhost_nvdec::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 61 | IoctlGetSyncpoint params{}; | ||
| 62 | std::memcpy(¶ms, 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(), ¶ms, sizeof(IoctlGetSyncpoint)); | ||
| 66 | return 0; | ||
| 67 | } | ||
| 68 | |||
| 69 | u32 nvhost_nvdec::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 70 | IoctlGetWaitbase params{}; | ||
| 71 | std::memcpy(¶ms, 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(), ¶ms, sizeof(IoctlGetWaitbase)); | ||
| 75 | return 0; | ||
| 76 | } | ||
| 77 | |||
| 78 | u32 nvhost_nvdec::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 79 | IoctlMapBuffer params{}; | ||
| 80 | std::memcpy(¶ms, 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(), ¶ms, sizeof(IoctlMapBuffer)); | ||
| 86 | return 0; | ||
| 87 | } | ||
| 88 | |||
| 89 | u32 nvhost_nvdec::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 90 | IoctlMapBufferEx params{}; | ||
| 91 | std::memcpy(¶ms, 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(), ¶ms, sizeof(IoctlMapBufferEx)); | ||
| 97 | return 0; | ||
| 98 | } | ||
| 99 | |||
| 100 | u32 nvhost_nvdec::UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 101 | IoctlUnmapBufferEx params{}; | ||
| 102 | std::memcpy(¶ms, input.data(), sizeof(IoctlUnmapBufferEx)); | ||
| 103 | LOG_WARNING(Service_NVDRV, "(STUBBED) called"); | ||
| 104 | std::memcpy(output.data(), ¶ms, 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 | ||
| 12 | namespace Service::Nvidia::Devices { | 10 | namespace Service::Nvidia::Devices { |
| 13 | 11 | ||
| 14 | class nvhost_nvdec final : public nvdevice { | 12 | class nvhost_nvdec final : public nvhost_nvdec_common { |
| 15 | public: | 13 | public: |
| 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 | |||
| 18 | namespace Service::Nvidia::Devices { | ||
| 19 | |||
| 20 | namespace { | ||
| 21 | // Splice vectors will copy count amount of type T from the input vector into the dst vector. | ||
| 22 | template <typename T> | ||
| 23 | std::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 | ||
| 31 | template <typename T> | ||
| 32 | std::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 | |||
| 39 | namespace NvErrCodes { | ||
| 40 | constexpr u32 Success{}; | ||
| 41 | constexpr u32 OutOfMemory{static_cast<u32>(-12)}; | ||
| 42 | constexpr u32 InvalidInput{static_cast<u32>(-22)}; | ||
| 43 | } // namespace NvErrCodes | ||
| 44 | |||
| 45 | nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) | ||
| 46 | : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} | ||
| 47 | nvhost_nvdec_common::~nvhost_nvdec_common() = default; | ||
| 48 | |||
| 49 | u32 nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) { | ||
| 50 | IoctlSetNvmapFD params{}; | ||
| 51 | std::memcpy(¶ms, 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 | |||
| 58 | u32 nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 59 | IoctlSubmit params{}; | ||
| 60 | std::memcpy(¶ms, 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(), ¶ms, 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 | |||
| 111 | u32 nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 112 | IoctlGetSyncpoint params{}; | ||
| 113 | std::memcpy(¶ms, 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(), ¶ms, sizeof(IoctlGetSyncpoint)); | ||
| 120 | |||
| 121 | return NvErrCodes::Success; | ||
| 122 | } | ||
| 123 | |||
| 124 | u32 nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 125 | IoctlGetWaitbase params{}; | ||
| 126 | std::memcpy(¶ms, input.data(), sizeof(IoctlGetWaitbase)); | ||
| 127 | params.value = 0; // Seems to be hard coded at 0 | ||
| 128 | std::memcpy(output.data(), ¶ms, sizeof(IoctlGetWaitbase)); | ||
| 129 | return 0; | ||
| 130 | } | ||
| 131 | |||
| 132 | u32 nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 133 | IoctlMapBuffer params{}; | ||
| 134 | std::memcpy(¶ms, 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(), ¶ms, 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(), ¶ms, 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 | |||
| 171 | u32 nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 172 | IoctlMapBuffer params{}; | ||
| 173 | std::memcpy(¶ms, 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(), ¶ms, 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 | |||
| 199 | u32 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 | |||
| 205 | std::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 | |||
| 216 | void 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 | |||
| 221 | std::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 | |||
| 13 | namespace Service::Nvidia::Devices { | ||
| 14 | class nvmap; | ||
| 15 | |||
| 16 | class nvhost_nvdec_common : public nvdevice { | ||
| 17 | public: | ||
| 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 | |||
| 25 | protected: | ||
| 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 | ||
| 11 | namespace Service::Nvidia::Devices { | 12 | namespace Service::Nvidia::Devices { |
| 13 | nvhost_vic::nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) | ||
| 14 | : nvhost_nvdec_common(system, std::move(nvmap_dev)) {} | ||
| 12 | 15 | ||
| 13 | nvhost_vic::nvhost_vic(Core::System& system) : nvdevice(system) {} | ||
| 14 | nvhost_vic::~nvhost_vic() = default; | 16 | nvhost_vic::~nvhost_vic() = default; |
| 15 | 17 | ||
| 16 | u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, | 18 | u32 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 | |||
| 43 | u32 nvhost_vic::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 44 | IoctlSetNvmapFD params{}; | ||
| 45 | std::memcpy(¶ms, 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 | |||
| 52 | u32 nvhost_vic::Submit(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 53 | IoctlSubmit params{}; | ||
| 54 | std::memcpy(¶ms, 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(), ¶ms, sizeof(IoctlSubmit)); | ||
| 61 | return 0; | ||
| 62 | } | ||
| 63 | |||
| 64 | u32 nvhost_vic::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 65 | IoctlGetSyncpoint params{}; | ||
| 66 | std::memcpy(¶ms, 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(), ¶ms, sizeof(IoctlGetSyncpoint)); | ||
| 70 | return 0; | ||
| 71 | } | ||
| 72 | |||
| 73 | u32 nvhost_vic::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 74 | IoctlGetWaitbase params{}; | ||
| 75 | std::memcpy(¶ms, 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(), ¶ms, sizeof(IoctlGetWaitbase)); | ||
| 79 | return 0; | ||
| 80 | } | ||
| 81 | |||
| 82 | u32 nvhost_vic::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 83 | IoctlMapBuffer params{}; | ||
| 84 | std::memcpy(¶ms, 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(), ¶ms, sizeof(IoctlMapBuffer)); | ||
| 90 | return 0; | ||
| 91 | } | ||
| 92 | |||
| 93 | u32 nvhost_vic::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 94 | IoctlMapBufferEx params{}; | ||
| 95 | std::memcpy(¶ms, 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(), ¶ms, sizeof(IoctlMapBufferEx)); | ||
| 101 | return 0; | ||
| 102 | } | ||
| 103 | |||
| 104 | u32 nvhost_vic::UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 105 | IoctlUnmapBufferEx params{}; | ||
| 106 | std::memcpy(¶ms, input.data(), sizeof(IoctlUnmapBufferEx)); | ||
| 107 | LOG_WARNING(Service_NVDRV, "(STUBBED) called"); | ||
| 108 | std::memcpy(output.data(), ¶ms, 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 | ||
| 13 | namespace Service::Nvidia::Devices { | 9 | namespace Service::Nvidia::Devices { |
| 10 | class nvmap; | ||
| 14 | 11 | ||
| 15 | class nvhost_vic final : public nvdevice { | 12 | class nvhost_vic final : public nvhost_nvdec_common { |
| 16 | public: | 13 | public: |
| 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 | ||
| 59 | Module::~Module() = default; | 59 | Module::~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 | ||
| 205 | void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) { | 205 | void 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 | ||
| 212 | void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx) { | 212 | void 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 | ||
| 220 | void Module::Interface::IsStandardNetworkSystemClockAccuracySufficient( | 220 | void 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( | |||
| 229 | void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERequestContext& ctx) { | 229 | void 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 | ||
| 378 | Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name) | 378 | Module::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 | |||
| 381 | Module::Interface::~Interface() = default; | 381 | Module::Interface::~Interface() = default; |
| 382 | 382 | ||
| 383 | void InstallInterfaces(Core::System& system) { | 383 | void 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 | ||
| 17 | class Module final { | 17 | class Module final { |
| 18 | public: | 18 | public: |
| 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 | |||
| 54 | private: | ||
| 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 | ||
| 25 | static 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 | |||
| 33 | static s64 GetExternalRtcValue() { | 25 | static s64 GetExternalRtcValue() { |
| 34 | return GetSecondsSinceEpoch().count() + GetExternalTimeZoneOffset(); | 26 | return GetSecondsSinceEpoch().count() + TimeManager::GetExternalTimeZoneOffset(); |
| 35 | } | ||
| 36 | |||
| 37 | TimeManager::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 | ||
| 59 | TimeManager::~TimeManager() = default; | 29 | struct 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 | ||
| 61 | void 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 | ||
| 79 | void 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 { | |
| 92 | void 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 | ||
| 111 | void 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 | ||
| 126 | void 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 | |||
| 222 | TimeManager::TimeManager(Core::System& system) : system{system} {} | ||
| 223 | |||
| 224 | TimeManager::~TimeManager() = default; | ||
| 225 | |||
| 226 | void 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 | |||
| 233 | Clock::StandardSteadyClockCore& TimeManager::GetStandardSteadyClockCore() { | ||
| 234 | return impl->standard_steady_clock_core; | ||
| 235 | } | ||
| 236 | |||
| 237 | const Clock::StandardSteadyClockCore& TimeManager::GetStandardSteadyClockCore() const { | ||
| 238 | return impl->standard_steady_clock_core; | ||
| 239 | } | ||
| 240 | |||
| 241 | Clock::StandardLocalSystemClockCore& TimeManager::GetStandardLocalSystemClockCore() { | ||
| 242 | return impl->standard_local_system_clock_core; | ||
| 243 | } | ||
| 244 | |||
| 245 | const Clock::StandardLocalSystemClockCore& TimeManager::GetStandardLocalSystemClockCore() const { | ||
| 246 | return impl->standard_local_system_clock_core; | ||
| 247 | } | ||
| 248 | |||
| 249 | Clock::StandardNetworkSystemClockCore& TimeManager::GetStandardNetworkSystemClockCore() { | ||
| 250 | return impl->standard_network_system_clock_core; | ||
| 138 | } | 251 | } |
| 139 | 252 | ||
| 140 | void TimeManager::SetupEphemeralNetworkSystemClock() { | 253 | const 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 | |||
| 258 | Clock::StandardUserSystemClockCore& TimeManager::GetStandardUserSystemClockCore() { | ||
| 259 | return impl->standard_user_system_clock_core; | ||
| 260 | } | ||
| 261 | |||
| 262 | const Clock::StandardUserSystemClockCore& TimeManager::GetStandardUserSystemClockCore() const { | ||
| 263 | return impl->standard_user_system_clock_core; | ||
| 264 | } | ||
| 265 | |||
| 266 | TimeZone::TimeZoneContentManager& TimeManager::GetTimeZoneContentManager() { | ||
| 267 | return impl->time_zone_content_manager; | ||
| 268 | } | ||
| 269 | |||
| 270 | const TimeZone::TimeZoneContentManager& TimeManager::GetTimeZoneContentManager() const { | ||
| 271 | return impl->time_zone_content_manager; | ||
| 272 | } | ||
| 273 | |||
| 274 | SharedMemory& TimeManager::GetSharedMemory() { | ||
| 275 | return impl->shared_memory; | ||
| 276 | } | ||
| 277 | |||
| 278 | const SharedMemory& TimeManager::GetSharedMemory() const { | ||
| 279 | return impl->shared_memory; | ||
| 280 | } | ||
| 281 | |||
| 282 | void TimeManager::UpdateLocalSystemClockTime(s64 posix_time) { | ||
| 283 | impl->UpdateLocalSystemClockTime(system, posix_time); | ||
| 284 | } | ||
| 285 | |||
| 286 | void 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 | |||
| 88 | private: | 71 | private: |
| 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 | ||
| 71 | TimeZoneContentManager::TimeZoneContentManager(TimeManager& time_manager, Core::System& system) | 71 | TimeZoneContentManager::TimeZoneContentManager(Core::System& system) |
| 72 | : system{system}, location_name_cache{BuildLocationNameCache(system)} { | 72 | : system{system}, location_name_cache{BuildLocationNameCache(system)} {} |
| 73 | 73 | ||
| 74 | void 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 | ||
| 22 | class TimeZoneContentManager final { | 22 | class TimeZoneContentManager final { |
| 23 | public: | 23 | public: |
| 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 | ||
| 22 | namespace GCAdapter { | 22 | namespace GCAdapter { |
| 23 | 23 | ||
| 24 | // Used to loop through and assign button in poller | ||
| 25 | constexpr std::array<PadButton, 12> PadButtonArray{ | ||
| 26 | PadButton::PAD_BUTTON_LEFT, PadButton::PAD_BUTTON_RIGHT, PadButton::PAD_BUTTON_DOWN, | ||
| 27 | PadButton::PAD_BUTTON_UP, PadButton::PAD_TRIGGER_Z, PadButton::PAD_TRIGGER_R, | ||
| 28 | PadButton::PAD_TRIGGER_L, PadButton::PAD_BUTTON_A, PadButton::PAD_BUTTON_B, | ||
| 29 | PadButton::PAD_BUTTON_X, PadButton::PAD_BUTTON_Y, PadButton::PAD_BUTTON_START, | ||
| 30 | }; | ||
| 31 | |||
| 32 | static void PadToState(const GCPadStatus& pad, GCState& out_state) { | ||
| 33 | for (const auto& button : PadButtonArray) { | ||
| 34 | const auto button_key = static_cast<u16>(button); | ||
| 35 | const auto button_value = (pad.button & button_key) != 0; | ||
| 36 | out_state.buttons.insert_or_assign(static_cast<s32>(button_key), button_value); | ||
| 37 | } | ||
| 38 | |||
| 39 | for (std::size_t i = 0; i < pad.axis_values.size(); ++i) { | ||
| 40 | out_state.axes.insert_or_assign(static_cast<u32>(i), pad.axis_values[i]); | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | Adapter::Adapter() { | 24 | Adapter::Adapter() { |
| 45 | if (usb_adapter_handle != nullptr) { | 25 | if (usb_adapter_handle != nullptr) { |
| 46 | return; | 26 | return; |
| @@ -49,168 +29,263 @@ Adapter::Adapter() { | |||
| 49 | 29 | ||
| 50 | const int init_res = libusb_init(&libusb_ctx); | 30 | const int init_res = libusb_init(&libusb_ctx); |
| 51 | if (init_res == LIBUSB_SUCCESS) { | 31 | if (init_res == LIBUSB_SUCCESS) { |
| 52 | Setup(); | 32 | adapter_scan_thread = std::thread(&Adapter::AdapterScanThread, this); |
| 53 | } else { | 33 | } else { |
| 54 | LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res); | 34 | LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res); |
| 55 | } | 35 | } |
| 56 | } | 36 | } |
| 57 | 37 | ||
| 58 | GCPadStatus Adapter::GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload) { | 38 | Adapter::~Adapter() { |
| 59 | GCPadStatus pad = {}; | 39 | Reset(); |
| 60 | const std::size_t offset = 1 + (9 * port); | 40 | } |
| 41 | |||
| 42 | void Adapter::AdapterInputThread() { | ||
| 43 | LOG_DEBUG(Input, "GC Adapter input thread started"); | ||
| 44 | s32 payload_size{}; | ||
| 45 | AdapterPayload adapter_payload{}; | ||
| 46 | |||
| 47 | if (adapter_scan_thread.joinable()) { | ||
| 48 | adapter_scan_thread.join(); | ||
| 49 | } | ||
| 50 | |||
| 51 | while (adapter_input_thread_running) { | ||
| 52 | libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(), | ||
| 53 | static_cast<s32>(adapter_payload.size()), &payload_size, 16); | ||
| 54 | if (IsPayloadCorrect(adapter_payload, payload_size)) { | ||
| 55 | UpdateControllers(adapter_payload); | ||
| 56 | UpdateVibrations(); | ||
| 57 | } | ||
| 58 | std::this_thread::yield(); | ||
| 59 | } | ||
| 61 | 60 | ||
| 62 | adapter_controllers_status[port] = static_cast<ControllerTypes>(adapter_payload[offset] >> 4); | 61 | if (restart_scan_thread) { |
| 62 | adapter_scan_thread = std::thread(&Adapter::AdapterScanThread, this); | ||
| 63 | restart_scan_thread = false; | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | bool Adapter::IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size) { | ||
| 68 | if (payload_size != static_cast<s32>(adapter_payload.size()) || | ||
| 69 | adapter_payload[0] != LIBUSB_DT_HID) { | ||
| 70 | LOG_DEBUG(Input, "Error reading payload (size: {}, type: {:02x})", payload_size, | ||
| 71 | adapter_payload[0]); | ||
| 72 | if (input_error_counter++ > 20) { | ||
| 73 | LOG_ERROR(Input, "GC adapter timeout, Is the adapter connected?"); | ||
| 74 | adapter_input_thread_running = false; | ||
| 75 | restart_scan_thread = true; | ||
| 76 | } | ||
| 77 | return false; | ||
| 78 | } | ||
| 79 | |||
| 80 | input_error_counter = 0; | ||
| 81 | return true; | ||
| 82 | } | ||
| 83 | |||
| 84 | void Adapter::UpdateControllers(const AdapterPayload& adapter_payload) { | ||
| 85 | for (std::size_t port = 0; port < pads.size(); ++port) { | ||
| 86 | const std::size_t offset = 1 + (9 * port); | ||
| 87 | const auto type = static_cast<ControllerTypes>(adapter_payload[offset] >> 4); | ||
| 88 | UpdatePadType(port, type); | ||
| 89 | if (DeviceConnected(port)) { | ||
| 90 | const u8 b1 = adapter_payload[offset + 1]; | ||
| 91 | const u8 b2 = adapter_payload[offset + 2]; | ||
| 92 | UpdateStateButtons(port, b1, b2); | ||
| 93 | UpdateStateAxes(port, adapter_payload); | ||
| 94 | if (configuring) { | ||
| 95 | UpdateYuzuSettings(port); | ||
| 96 | } | ||
| 97 | } | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | void Adapter::UpdatePadType(std::size_t port, ControllerTypes pad_type) { | ||
| 102 | if (pads[port].type == pad_type) { | ||
| 103 | return; | ||
| 104 | } | ||
| 105 | // Device changed reset device and set new type | ||
| 106 | ResetDevice(port); | ||
| 107 | pads[port].type = pad_type; | ||
| 108 | } | ||
| 109 | |||
| 110 | void Adapter::UpdateStateButtons(std::size_t port, u8 b1, u8 b2) { | ||
| 111 | if (port >= pads.size()) { | ||
| 112 | return; | ||
| 113 | } | ||
| 63 | 114 | ||
| 64 | static constexpr std::array<PadButton, 8> b1_buttons{ | 115 | static constexpr std::array<PadButton, 8> b1_buttons{ |
| 65 | PadButton::PAD_BUTTON_A, PadButton::PAD_BUTTON_B, PadButton::PAD_BUTTON_X, | 116 | PadButton::ButtonA, PadButton::ButtonB, PadButton::ButtonX, PadButton::ButtonY, |
| 66 | PadButton::PAD_BUTTON_Y, PadButton::PAD_BUTTON_LEFT, PadButton::PAD_BUTTON_RIGHT, | 117 | PadButton::ButtonLeft, PadButton::ButtonRight, PadButton::ButtonDown, PadButton::ButtonUp, |
| 67 | PadButton::PAD_BUTTON_DOWN, PadButton::PAD_BUTTON_UP, | ||
| 68 | }; | 118 | }; |
| 69 | 119 | ||
| 70 | static constexpr std::array<PadButton, 4> b2_buttons{ | 120 | static constexpr std::array<PadButton, 4> b2_buttons{ |
| 71 | PadButton::PAD_BUTTON_START, | 121 | PadButton::ButtonStart, |
| 72 | PadButton::PAD_TRIGGER_Z, | 122 | PadButton::TriggerZ, |
| 73 | PadButton::PAD_TRIGGER_R, | 123 | PadButton::TriggerR, |
| 74 | PadButton::PAD_TRIGGER_L, | 124 | PadButton::TriggerL, |
| 75 | }; | 125 | }; |
| 126 | pads[port].buttons = 0; | ||
| 127 | for (std::size_t i = 0; i < b1_buttons.size(); ++i) { | ||
| 128 | if ((b1 & (1U << i)) != 0) { | ||
| 129 | pads[port].buttons = | ||
| 130 | static_cast<u16>(pads[port].buttons | static_cast<u16>(b1_buttons[i])); | ||
| 131 | pads[port].last_button = b1_buttons[i]; | ||
| 132 | } | ||
| 133 | } | ||
| 76 | 134 | ||
| 135 | for (std::size_t j = 0; j < b2_buttons.size(); ++j) { | ||
| 136 | if ((b2 & (1U << j)) != 0) { | ||
| 137 | pads[port].buttons = | ||
| 138 | static_cast<u16>(pads[port].buttons | static_cast<u16>(b2_buttons[j])); | ||
| 139 | pads[port].last_button = b2_buttons[j]; | ||
| 140 | } | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | void Adapter::UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload) { | ||
| 145 | if (port >= pads.size()) { | ||
| 146 | return; | ||
| 147 | } | ||
| 148 | |||
| 149 | const std::size_t offset = 1 + (9 * port); | ||
| 77 | static constexpr std::array<PadAxes, 6> axes{ | 150 | static constexpr std::array<PadAxes, 6> axes{ |
| 78 | PadAxes::StickX, PadAxes::StickY, PadAxes::SubstickX, | 151 | PadAxes::StickX, PadAxes::StickY, PadAxes::SubstickX, |
| 79 | PadAxes::SubstickY, PadAxes::TriggerLeft, PadAxes::TriggerRight, | 152 | PadAxes::SubstickY, PadAxes::TriggerLeft, PadAxes::TriggerRight, |
| 80 | }; | 153 | }; |
| 81 | 154 | ||
| 82 | if (adapter_controllers_status[port] == ControllerTypes::None && !get_origin[port]) { | 155 | for (const PadAxes axis : axes) { |
| 83 | // Controller may have been disconnected, recalibrate if reconnected. | 156 | const auto index = static_cast<std::size_t>(axis); |
| 84 | get_origin[port] = true; | 157 | const u8 axis_value = adapter_payload[offset + 3 + index]; |
| 158 | if (pads[port].axis_origin[index] == 255) { | ||
| 159 | pads[port].axis_origin[index] = axis_value; | ||
| 160 | } | ||
| 161 | pads[port].axis_values[index] = | ||
| 162 | static_cast<s16>(axis_value - pads[port].axis_origin[index]); | ||
| 85 | } | 163 | } |
| 164 | } | ||
| 86 | 165 | ||
| 87 | if (adapter_controllers_status[port] != ControllerTypes::None) { | 166 | void Adapter::UpdateYuzuSettings(std::size_t port) { |
| 88 | const u8 b1 = adapter_payload[offset + 1]; | 167 | if (port >= pads.size()) { |
| 89 | const u8 b2 = adapter_payload[offset + 2]; | 168 | return; |
| 169 | } | ||
| 90 | 170 | ||
| 91 | for (std::size_t i = 0; i < b1_buttons.size(); ++i) { | 171 | constexpr u8 axis_threshold = 50; |
| 92 | if ((b1 & (1U << i)) != 0) { | 172 | GCPadStatus pad_status = {.port = port}; |
| 93 | pad.button = static_cast<u16>(pad.button | static_cast<u16>(b1_buttons[i])); | ||
| 94 | } | ||
| 95 | } | ||
| 96 | 173 | ||
| 97 | for (std::size_t j = 0; j < b2_buttons.size(); ++j) { | 174 | if (pads[port].buttons != 0) { |
| 98 | if ((b2 & (1U << j)) != 0) { | 175 | pad_status.button = pads[port].last_button; |
| 99 | pad.button = static_cast<u16>(pad.button | static_cast<u16>(b2_buttons[j])); | 176 | pad_queue.Push(pad_status); |
| 100 | } | 177 | } |
| 101 | } | 178 | |
| 102 | for (PadAxes axis : axes) { | 179 | // Accounting for a threshold here to ensure an intentional press |
| 103 | const auto index = static_cast<std::size_t>(axis); | 180 | for (std::size_t i = 0; i < pads[port].axis_values.size(); ++i) { |
| 104 | pad.axis_values[index] = adapter_payload[offset + 3 + index]; | 181 | const s16 value = pads[port].axis_values[i]; |
| 105 | } | ||
| 106 | 182 | ||
| 107 | if (get_origin[port]) { | 183 | if (value > axis_threshold || value < -axis_threshold) { |
| 108 | origin_status[port].axis_values = pad.axis_values; | 184 | pad_status.axis = static_cast<PadAxes>(i); |
| 109 | get_origin[port] = false; | 185 | pad_status.axis_value = value; |
| 186 | pad_status.axis_threshold = axis_threshold; | ||
| 187 | pad_queue.Push(pad_status); | ||
| 110 | } | 188 | } |
| 111 | } | 189 | } |
| 112 | return pad; | ||
| 113 | } | 190 | } |
| 114 | 191 | ||
| 115 | void Adapter::Read() { | 192 | void Adapter::UpdateVibrations() { |
| 116 | LOG_DEBUG(Input, "GC Adapter Read() thread started"); | 193 | // Use 8 states to keep the switching between on/off fast enough for |
| 194 | // a human to not notice the difference between switching from on/off | ||
| 195 | // More states = more rumble strengths = slower update time | ||
| 196 | constexpr u8 vibration_states = 8; | ||
| 117 | 197 | ||
| 118 | int payload_size; | 198 | vibration_counter = (vibration_counter + 1) % vibration_states; |
| 119 | std::array<u8, 37> adapter_payload; | ||
| 120 | std::array<GCPadStatus, 4> pads; | ||
| 121 | |||
| 122 | while (adapter_thread_running) { | ||
| 123 | libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(), | ||
| 124 | sizeof(adapter_payload), &payload_size, 16); | ||
| 125 | |||
| 126 | if (payload_size != sizeof(adapter_payload) || adapter_payload[0] != LIBUSB_DT_HID) { | ||
| 127 | LOG_ERROR(Input, | ||
| 128 | "Error reading payload (size: {}, type: {:02x}) Is the adapter connected?", | ||
| 129 | payload_size, adapter_payload[0]); | ||
| 130 | adapter_thread_running = false; // error reading from adapter, stop reading. | ||
| 131 | break; | ||
| 132 | } | ||
| 133 | for (std::size_t port = 0; port < pads.size(); ++port) { | ||
| 134 | pads[port] = GetPadStatus(port, adapter_payload); | ||
| 135 | if (DeviceConnected(port) && configuring) { | ||
| 136 | if (pads[port].button != 0) { | ||
| 137 | pad_queue[port].Push(pads[port]); | ||
| 138 | } | ||
| 139 | 199 | ||
| 140 | // Accounting for a threshold here to ensure an intentional press | 200 | for (GCController& pad : pads) { |
| 141 | for (size_t i = 0; i < pads[port].axis_values.size(); ++i) { | 201 | const bool vibrate = pad.rumble_amplitude > vibration_counter; |
| 142 | const u8 value = pads[port].axis_values[i]; | 202 | vibration_changed |= vibrate != pad.enable_vibration; |
| 143 | const u8 origin = origin_status[port].axis_values[i]; | 203 | pad.enable_vibration = vibrate; |
| 144 | |||
| 145 | if (value > origin + pads[port].THRESHOLD || | ||
| 146 | value < origin - pads[port].THRESHOLD) { | ||
| 147 | pads[port].axis = static_cast<PadAxes>(i); | ||
| 148 | pads[port].axis_value = pads[port].axis_values[i]; | ||
| 149 | pad_queue[port].Push(pads[port]); | ||
| 150 | } | ||
| 151 | } | ||
| 152 | } | ||
| 153 | PadToState(pads[port], state[port]); | ||
| 154 | } | ||
| 155 | std::this_thread::yield(); | ||
| 156 | } | 204 | } |
| 205 | SendVibrations(); | ||
| 157 | } | 206 | } |
| 158 | 207 | ||
| 159 | void Adapter::Setup() { | 208 | void Adapter::SendVibrations() { |
| 160 | // Initialize all controllers as unplugged | 209 | if (!rumble_enabled || !vibration_changed) { |
| 161 | adapter_controllers_status.fill(ControllerTypes::None); | ||
| 162 | // Initialize all ports to store axis origin values | ||
| 163 | get_origin.fill(true); | ||
| 164 | |||
| 165 | // pointer to list of connected usb devices | ||
| 166 | libusb_device** devices{}; | ||
| 167 | |||
| 168 | // populate the list of devices, get the count | ||
| 169 | const ssize_t device_count = libusb_get_device_list(libusb_ctx, &devices); | ||
| 170 | if (device_count < 0) { | ||
| 171 | LOG_ERROR(Input, "libusb_get_device_list failed with error: {}", device_count); | ||
| 172 | return; | 210 | return; |
| 173 | } | 211 | } |
| 174 | 212 | s32 size{}; | |
| 175 | if (devices != nullptr) { | 213 | constexpr u8 rumble_command = 0x11; |
| 176 | for (std::size_t index = 0; index < static_cast<std::size_t>(device_count); ++index) { | 214 | const u8 p1 = pads[0].enable_vibration; |
| 177 | if (CheckDeviceAccess(devices[index])) { | 215 | const u8 p2 = pads[1].enable_vibration; |
| 178 | // GC Adapter found and accessible, registering it | 216 | const u8 p3 = pads[2].enable_vibration; |
| 179 | GetGCEndpoint(devices[index]); | 217 | const u8 p4 = pads[3].enable_vibration; |
| 180 | break; | 218 | std::array<u8, 5> payload = {rumble_command, p1, p2, p3, p4}; |
| 181 | } | 219 | const int err = libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, payload.data(), |
| 220 | static_cast<s32>(payload.size()), &size, 16); | ||
| 221 | if (err) { | ||
| 222 | LOG_DEBUG(Input, "Adapter libusb write failed: {}", libusb_error_name(err)); | ||
| 223 | if (output_error_counter++ > 5) { | ||
| 224 | LOG_ERROR(Input, "GC adapter output timeout, Rumble disabled"); | ||
| 225 | rumble_enabled = false; | ||
| 182 | } | 226 | } |
| 183 | libusb_free_device_list(devices, 1); | 227 | return; |
| 184 | } | 228 | } |
| 229 | output_error_counter = 0; | ||
| 230 | vibration_changed = false; | ||
| 185 | } | 231 | } |
| 186 | 232 | ||
| 187 | bool Adapter::CheckDeviceAccess(libusb_device* device) { | 233 | bool Adapter::RumblePlay(std::size_t port, f32 amplitude) { |
| 188 | libusb_device_descriptor desc; | 234 | amplitude = std::clamp(amplitude, 0.0f, 1.0f); |
| 189 | const int get_descriptor_error = libusb_get_device_descriptor(device, &desc); | 235 | const auto raw_amp = static_cast<u8>(amplitude * 0x8); |
| 190 | if (get_descriptor_error) { | 236 | pads[port].rumble_amplitude = raw_amp; |
| 191 | // could not acquire the descriptor, no point in trying to use it. | 237 | |
| 192 | LOG_ERROR(Input, "libusb_get_device_descriptor failed with error: {}", | 238 | return rumble_enabled; |
| 193 | get_descriptor_error); | 239 | } |
| 194 | return false; | 240 | |
| 241 | void Adapter::AdapterScanThread() { | ||
| 242 | adapter_scan_thread_running = true; | ||
| 243 | adapter_input_thread_running = false; | ||
| 244 | if (adapter_input_thread.joinable()) { | ||
| 245 | adapter_input_thread.join(); | ||
| 246 | } | ||
| 247 | ClearLibusbHandle(); | ||
| 248 | ResetDevices(); | ||
| 249 | while (adapter_scan_thread_running && !adapter_input_thread_running) { | ||
| 250 | Setup(); | ||
| 251 | std::this_thread::sleep_for(std::chrono::seconds(1)); | ||
| 195 | } | 252 | } |
| 253 | } | ||
| 196 | 254 | ||
| 197 | if (desc.idVendor != 0x057e || desc.idProduct != 0x0337) { | 255 | void Adapter::Setup() { |
| 198 | // This isn't the device we are looking for. | 256 | usb_adapter_handle = libusb_open_device_with_vid_pid(libusb_ctx, 0x057e, 0x0337); |
| 199 | return false; | 257 | |
| 258 | if (usb_adapter_handle == NULL) { | ||
| 259 | return; | ||
| 260 | } | ||
| 261 | if (!CheckDeviceAccess()) { | ||
| 262 | ClearLibusbHandle(); | ||
| 263 | return; | ||
| 200 | } | 264 | } |
| 201 | const int open_error = libusb_open(device, &usb_adapter_handle); | ||
| 202 | 265 | ||
| 203 | if (open_error == LIBUSB_ERROR_ACCESS) { | 266 | libusb_device* device = libusb_get_device(usb_adapter_handle); |
| 204 | LOG_ERROR(Input, "Yuzu can not gain access to this device: ID {:04X}:{:04X}.", | 267 | |
| 205 | desc.idVendor, desc.idProduct); | 268 | LOG_INFO(Input, "GC adapter is now connected"); |
| 206 | return false; | 269 | // GC Adapter found and accessible, registering it |
| 270 | if (GetGCEndpoint(device)) { | ||
| 271 | adapter_scan_thread_running = false; | ||
| 272 | adapter_input_thread_running = true; | ||
| 273 | rumble_enabled = true; | ||
| 274 | input_error_counter = 0; | ||
| 275 | output_error_counter = 0; | ||
| 276 | adapter_input_thread = std::thread(&Adapter::AdapterInputThread, this); | ||
| 207 | } | 277 | } |
| 208 | if (open_error) { | 278 | } |
| 209 | LOG_ERROR(Input, "libusb_open failed to open device with error = {}", open_error); | 279 | |
| 210 | return false; | 280 | bool Adapter::CheckDeviceAccess() { |
| 281 | // This fixes payload problems from offbrand GCAdapters | ||
| 282 | const s32 control_transfer_error = | ||
| 283 | libusb_control_transfer(usb_adapter_handle, 0x21, 11, 0x0001, 0, nullptr, 0, 1000); | ||
| 284 | if (control_transfer_error < 0) { | ||
| 285 | LOG_ERROR(Input, "libusb_control_transfer failed with error= {}", control_transfer_error); | ||
| 211 | } | 286 | } |
| 212 | 287 | ||
| 213 | int kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle, 0); | 288 | s32 kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle, 0); |
| 214 | if (kernel_driver_error == 1) { | 289 | if (kernel_driver_error == 1) { |
| 215 | kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle, 0); | 290 | kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle, 0); |
| 216 | if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) { | 291 | if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) { |
| @@ -236,13 +311,13 @@ bool Adapter::CheckDeviceAccess(libusb_device* device) { | |||
| 236 | return true; | 311 | return true; |
| 237 | } | 312 | } |
| 238 | 313 | ||
| 239 | void Adapter::GetGCEndpoint(libusb_device* device) { | 314 | bool Adapter::GetGCEndpoint(libusb_device* device) { |
| 240 | libusb_config_descriptor* config = nullptr; | 315 | libusb_config_descriptor* config = nullptr; |
| 241 | const int config_descriptor_return = libusb_get_config_descriptor(device, 0, &config); | 316 | const int config_descriptor_return = libusb_get_config_descriptor(device, 0, &config); |
| 242 | if (config_descriptor_return != LIBUSB_SUCCESS) { | 317 | if (config_descriptor_return != LIBUSB_SUCCESS) { |
| 243 | LOG_ERROR(Input, "libusb_get_config_descriptor failed with error = {}", | 318 | LOG_ERROR(Input, "libusb_get_config_descriptor failed with error = {}", |
| 244 | config_descriptor_return); | 319 | config_descriptor_return); |
| 245 | return; | 320 | return false; |
| 246 | } | 321 | } |
| 247 | 322 | ||
| 248 | for (u8 ic = 0; ic < config->bNumInterfaces; ic++) { | 323 | for (u8 ic = 0; ic < config->bNumInterfaces; ic++) { |
| @@ -264,31 +339,51 @@ void Adapter::GetGCEndpoint(libusb_device* device) { | |||
| 264 | unsigned char clear_payload = 0x13; | 339 | unsigned char clear_payload = 0x13; |
| 265 | libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, &clear_payload, | 340 | libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, &clear_payload, |
| 266 | sizeof(clear_payload), nullptr, 16); | 341 | sizeof(clear_payload), nullptr, 16); |
| 267 | 342 | return true; | |
| 268 | adapter_thread_running = true; | ||
| 269 | adapter_input_thread = std::thread(&Adapter::Read, this); | ||
| 270 | } | 343 | } |
| 271 | 344 | ||
| 272 | Adapter::~Adapter() { | 345 | void Adapter::JoinThreads() { |
| 273 | Reset(); | 346 | restart_scan_thread = false; |
| 274 | } | 347 | adapter_input_thread_running = false; |
| 348 | adapter_scan_thread_running = false; | ||
| 275 | 349 | ||
| 276 | void Adapter::Reset() { | 350 | if (adapter_scan_thread.joinable()) { |
| 277 | if (adapter_thread_running) { | 351 | adapter_scan_thread.join(); |
| 278 | adapter_thread_running = false; | ||
| 279 | } | 352 | } |
| 353 | |||
| 280 | if (adapter_input_thread.joinable()) { | 354 | if (adapter_input_thread.joinable()) { |
| 281 | adapter_input_thread.join(); | 355 | adapter_input_thread.join(); |
| 282 | } | 356 | } |
| 357 | } | ||
| 283 | 358 | ||
| 284 | adapter_controllers_status.fill(ControllerTypes::None); | 359 | void Adapter::ClearLibusbHandle() { |
| 285 | get_origin.fill(true); | ||
| 286 | |||
| 287 | if (usb_adapter_handle) { | 360 | if (usb_adapter_handle) { |
| 288 | libusb_release_interface(usb_adapter_handle, 1); | 361 | libusb_release_interface(usb_adapter_handle, 1); |
| 289 | libusb_close(usb_adapter_handle); | 362 | libusb_close(usb_adapter_handle); |
| 290 | usb_adapter_handle = nullptr; | 363 | usb_adapter_handle = nullptr; |
| 291 | } | 364 | } |
| 365 | } | ||
| 366 | |||
| 367 | void Adapter::ResetDevices() { | ||
| 368 | for (std::size_t i = 0; i < pads.size(); ++i) { | ||
| 369 | ResetDevice(i); | ||
| 370 | } | ||
| 371 | } | ||
| 372 | |||
| 373 | void Adapter::ResetDevice(std::size_t port) { | ||
| 374 | pads[port].type = ControllerTypes::None; | ||
| 375 | pads[port].enable_vibration = false; | ||
| 376 | pads[port].rumble_amplitude = 0; | ||
| 377 | pads[port].buttons = 0; | ||
| 378 | pads[port].last_button = PadButton::Undefined; | ||
| 379 | pads[port].axis_values.fill(0); | ||
| 380 | pads[port].axis_origin.fill(255); | ||
| 381 | } | ||
| 382 | |||
| 383 | void Adapter::Reset() { | ||
| 384 | JoinThreads(); | ||
| 385 | ClearLibusbHandle(); | ||
| 386 | ResetDevices(); | ||
| 292 | 387 | ||
| 293 | if (libusb_ctx) { | 388 | if (libusb_ctx) { |
| 294 | libusb_exit(libusb_ctx); | 389 | libusb_exit(libusb_ctx); |
| @@ -297,11 +392,11 @@ void Adapter::Reset() { | |||
| 297 | 392 | ||
| 298 | std::vector<Common::ParamPackage> Adapter::GetInputDevices() const { | 393 | std::vector<Common::ParamPackage> Adapter::GetInputDevices() const { |
| 299 | std::vector<Common::ParamPackage> devices; | 394 | std::vector<Common::ParamPackage> devices; |
| 300 | for (std::size_t port = 0; port < state.size(); ++port) { | 395 | for (std::size_t port = 0; port < pads.size(); ++port) { |
| 301 | if (!DeviceConnected(port)) { | 396 | if (!DeviceConnected(port)) { |
| 302 | continue; | 397 | continue; |
| 303 | } | 398 | } |
| 304 | std::string name = fmt::format("Gamecube Controller {}", port); | 399 | std::string name = fmt::format("Gamecube Controller {}", port + 1); |
| 305 | devices.emplace_back(Common::ParamPackage{ | 400 | devices.emplace_back(Common::ParamPackage{ |
| 306 | {"class", "gcpad"}, | 401 | {"class", "gcpad"}, |
| 307 | {"display", std::move(name)}, | 402 | {"display", std::move(name)}, |
| @@ -318,18 +413,18 @@ InputCommon::ButtonMapping Adapter::GetButtonMappingForDevice( | |||
| 318 | // This list also excludes any button that can't be really mapped | 413 | // This list also excludes any button that can't be really mapped |
| 319 | static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 12> | 414 | static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 12> |
| 320 | switch_to_gcadapter_button = { | 415 | switch_to_gcadapter_button = { |
| 321 | std::pair{Settings::NativeButton::A, PadButton::PAD_BUTTON_A}, | 416 | std::pair{Settings::NativeButton::A, PadButton::ButtonA}, |
| 322 | {Settings::NativeButton::B, PadButton::PAD_BUTTON_B}, | 417 | {Settings::NativeButton::B, PadButton::ButtonB}, |
| 323 | {Settings::NativeButton::X, PadButton::PAD_BUTTON_X}, | 418 | {Settings::NativeButton::X, PadButton::ButtonX}, |
| 324 | {Settings::NativeButton::Y, PadButton::PAD_BUTTON_Y}, | 419 | {Settings::NativeButton::Y, PadButton::ButtonY}, |
| 325 | {Settings::NativeButton::Plus, PadButton::PAD_BUTTON_START}, | 420 | {Settings::NativeButton::Plus, PadButton::ButtonStart}, |
| 326 | {Settings::NativeButton::DLeft, PadButton::PAD_BUTTON_LEFT}, | 421 | {Settings::NativeButton::DLeft, PadButton::ButtonLeft}, |
| 327 | {Settings::NativeButton::DUp, PadButton::PAD_BUTTON_UP}, | 422 | {Settings::NativeButton::DUp, PadButton::ButtonUp}, |
| 328 | {Settings::NativeButton::DRight, PadButton::PAD_BUTTON_RIGHT}, | 423 | {Settings::NativeButton::DRight, PadButton::ButtonRight}, |
| 329 | {Settings::NativeButton::DDown, PadButton::PAD_BUTTON_DOWN}, | 424 | {Settings::NativeButton::DDown, PadButton::ButtonDown}, |
| 330 | {Settings::NativeButton::SL, PadButton::PAD_TRIGGER_L}, | 425 | {Settings::NativeButton::SL, PadButton::TriggerL}, |
| 331 | {Settings::NativeButton::SR, PadButton::PAD_TRIGGER_R}, | 426 | {Settings::NativeButton::SR, PadButton::TriggerR}, |
| 332 | {Settings::NativeButton::R, PadButton::PAD_TRIGGER_Z}, | 427 | {Settings::NativeButton::R, PadButton::TriggerZ}, |
| 333 | }; | 428 | }; |
| 334 | if (!params.Has("port")) { | 429 | if (!params.Has("port")) { |
| 335 | return {}; | 430 | return {}; |
| @@ -352,8 +447,10 @@ InputCommon::ButtonMapping Adapter::GetButtonMappingForDevice( | |||
| 352 | for (const auto& [switch_button, gcadapter_axis] : switch_to_gcadapter_axis) { | 447 | for (const auto& [switch_button, gcadapter_axis] : switch_to_gcadapter_axis) { |
| 353 | Common::ParamPackage button_params({{"engine", "gcpad"}}); | 448 | Common::ParamPackage button_params({{"engine", "gcpad"}}); |
| 354 | button_params.Set("port", params.Get("port", 0)); | 449 | button_params.Set("port", params.Get("port", 0)); |
| 355 | button_params.Set("button", static_cast<int>(PadButton::PAD_STICK)); | 450 | button_params.Set("button", static_cast<s32>(PadButton::Stick)); |
| 356 | button_params.Set("axis", static_cast<int>(gcadapter_axis)); | 451 | button_params.Set("axis", static_cast<s32>(gcadapter_axis)); |
| 452 | button_params.Set("threshold", 0.5f); | ||
| 453 | button_params.Set("direction", "+"); | ||
| 357 | mapping.insert_or_assign(switch_button, std::move(button_params)); | 454 | mapping.insert_or_assign(switch_button, std::move(button_params)); |
| 358 | } | 455 | } |
| 359 | return mapping; | 456 | return mapping; |
| @@ -382,46 +479,33 @@ InputCommon::AnalogMapping Adapter::GetAnalogMappingForDevice( | |||
| 382 | } | 479 | } |
| 383 | 480 | ||
| 384 | bool Adapter::DeviceConnected(std::size_t port) const { | 481 | bool Adapter::DeviceConnected(std::size_t port) const { |
| 385 | return adapter_controllers_status[port] != ControllerTypes::None; | 482 | return pads[port].type != ControllerTypes::None; |
| 386 | } | ||
| 387 | |||
| 388 | void Adapter::ResetDeviceType(std::size_t port) { | ||
| 389 | adapter_controllers_status[port] = ControllerTypes::None; | ||
| 390 | } | 483 | } |
| 391 | 484 | ||
| 392 | void Adapter::BeginConfiguration() { | 485 | void Adapter::BeginConfiguration() { |
| 393 | get_origin.fill(true); | 486 | pad_queue.Clear(); |
| 394 | for (auto& pq : pad_queue) { | ||
| 395 | pq.Clear(); | ||
| 396 | } | ||
| 397 | configuring = true; | 487 | configuring = true; |
| 398 | } | 488 | } |
| 399 | 489 | ||
| 400 | void Adapter::EndConfiguration() { | 490 | void Adapter::EndConfiguration() { |
| 401 | for (auto& pq : pad_queue) { | 491 | pad_queue.Clear(); |
| 402 | pq.Clear(); | ||
| 403 | } | ||
| 404 | configuring = false; | 492 | configuring = false; |
| 405 | } | 493 | } |
| 406 | 494 | ||
| 407 | std::array<Common::SPSCQueue<GCPadStatus>, 4>& Adapter::GetPadQueue() { | 495 | Common::SPSCQueue<GCPadStatus>& Adapter::GetPadQueue() { |
| 408 | return pad_queue; | 496 | return pad_queue; |
| 409 | } | 497 | } |
| 410 | 498 | ||
| 411 | const std::array<Common::SPSCQueue<GCPadStatus>, 4>& Adapter::GetPadQueue() const { | 499 | const Common::SPSCQueue<GCPadStatus>& Adapter::GetPadQueue() const { |
| 412 | return pad_queue; | 500 | return pad_queue; |
| 413 | } | 501 | } |
| 414 | 502 | ||
| 415 | std::array<GCState, 4>& Adapter::GetPadState() { | 503 | GCController& Adapter::GetPadState(std::size_t port) { |
| 416 | return state; | 504 | return pads.at(port); |
| 417 | } | ||
| 418 | |||
| 419 | const std::array<GCState, 4>& Adapter::GetPadState() const { | ||
| 420 | return state; | ||
| 421 | } | 505 | } |
| 422 | 506 | ||
| 423 | int Adapter::GetOriginValue(u32 port, u32 axis) const { | 507 | const GCController& Adapter::GetPadState(std::size_t port) const { |
| 424 | return origin_status[port].axis_values[axis]; | 508 | return pads.at(port); |
| 425 | } | 509 | } |
| 426 | 510 | ||
| 427 | } // namespace GCAdapter | 511 | } // namespace GCAdapter |
diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h index 4f5f3de8e..d28dcfad3 100644 --- a/src/input_common/gcadapter/gc_adapter.h +++ b/src/input_common/gcadapter/gc_adapter.h | |||
| @@ -19,24 +19,23 @@ struct libusb_device_handle; | |||
| 19 | namespace GCAdapter { | 19 | namespace GCAdapter { |
| 20 | 20 | ||
| 21 | enum class PadButton { | 21 | enum class PadButton { |
| 22 | PAD_BUTTON_LEFT = 0x0001, | 22 | Undefined = 0x0000, |
| 23 | PAD_BUTTON_RIGHT = 0x0002, | 23 | ButtonLeft = 0x0001, |
| 24 | PAD_BUTTON_DOWN = 0x0004, | 24 | ButtonRight = 0x0002, |
| 25 | PAD_BUTTON_UP = 0x0008, | 25 | ButtonDown = 0x0004, |
| 26 | PAD_TRIGGER_Z = 0x0010, | 26 | ButtonUp = 0x0008, |
| 27 | PAD_TRIGGER_R = 0x0020, | 27 | TriggerZ = 0x0010, |
| 28 | PAD_TRIGGER_L = 0x0040, | 28 | TriggerR = 0x0020, |
| 29 | PAD_BUTTON_A = 0x0100, | 29 | TriggerL = 0x0040, |
| 30 | PAD_BUTTON_B = 0x0200, | 30 | ButtonA = 0x0100, |
| 31 | PAD_BUTTON_X = 0x0400, | 31 | ButtonB = 0x0200, |
| 32 | PAD_BUTTON_Y = 0x0800, | 32 | ButtonX = 0x0400, |
| 33 | PAD_BUTTON_START = 0x1000, | 33 | ButtonY = 0x0800, |
| 34 | ButtonStart = 0x1000, | ||
| 34 | // Below is for compatibility with "AxisButton" type | 35 | // Below is for compatibility with "AxisButton" type |
| 35 | PAD_STICK = 0x2000, | 36 | Stick = 0x2000, |
| 36 | }; | 37 | }; |
| 37 | 38 | ||
| 38 | extern const std::array<PadButton, 12> PadButtonArray; | ||
| 39 | |||
| 40 | enum class PadAxes : u8 { | 39 | enum class PadAxes : u8 { |
| 41 | StickX, | 40 | StickX, |
| 42 | StickY, | 41 | StickY, |
| @@ -47,87 +46,122 @@ enum class PadAxes : u8 { | |||
| 47 | Undefined, | 46 | Undefined, |
| 48 | }; | 47 | }; |
| 49 | 48 | ||
| 49 | enum class ControllerTypes { | ||
| 50 | None, | ||
| 51 | Wired, | ||
| 52 | Wireless, | ||
| 53 | }; | ||
| 54 | |||
| 50 | struct GCPadStatus { | 55 | struct GCPadStatus { |
| 51 | u16 button{}; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits | 56 | std::size_t port{}; |
| 52 | 57 | ||
| 53 | std::array<u8, 6> axis_values{}; // Triggers and sticks, following indices defined in PadAxes | 58 | PadButton button{PadButton::Undefined}; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits |
| 54 | static constexpr u8 THRESHOLD = 50; // Threshold for axis press for polling | ||
| 55 | 59 | ||
| 56 | u8 port{}; | ||
| 57 | PadAxes axis{PadAxes::Undefined}; | 60 | PadAxes axis{PadAxes::Undefined}; |
| 58 | u8 axis_value{255}; | 61 | s16 axis_value{}; |
| 62 | u8 axis_threshold{50}; | ||
| 59 | }; | 63 | }; |
| 60 | 64 | ||
| 61 | struct GCState { | 65 | struct GCController { |
| 62 | std::unordered_map<int, bool> buttons; | 66 | ControllerTypes type{}; |
| 63 | std::unordered_map<u32, u16> axes; | 67 | bool enable_vibration{}; |
| 68 | u8 rumble_amplitude{}; | ||
| 69 | u16 buttons{}; | ||
| 70 | PadButton last_button{}; | ||
| 71 | std::array<s16, 6> axis_values{}; | ||
| 72 | std::array<u8, 6> axis_origin{}; | ||
| 64 | }; | 73 | }; |
| 65 | 74 | ||
| 66 | enum class ControllerTypes { None, Wired, Wireless }; | ||
| 67 | |||
| 68 | class Adapter { | 75 | class Adapter { |
| 69 | public: | 76 | public: |
| 70 | /// Initialize the GC Adapter capture and read sequence | ||
| 71 | Adapter(); | 77 | Adapter(); |
| 72 | |||
| 73 | /// Close the adapter read thread and release the adapter | ||
| 74 | ~Adapter(); | 78 | ~Adapter(); |
| 79 | |||
| 80 | /// Request a vibration for a controlelr | ||
| 81 | bool RumblePlay(std::size_t port, f32 amplitude); | ||
| 82 | |||
| 75 | /// Used for polling | 83 | /// Used for polling |
| 76 | void BeginConfiguration(); | 84 | void BeginConfiguration(); |
| 77 | void EndConfiguration(); | 85 | void EndConfiguration(); |
| 78 | 86 | ||
| 87 | Common::SPSCQueue<GCPadStatus>& GetPadQueue(); | ||
| 88 | const Common::SPSCQueue<GCPadStatus>& GetPadQueue() const; | ||
| 89 | |||
| 90 | GCController& GetPadState(std::size_t port); | ||
| 91 | const GCController& GetPadState(std::size_t port) const; | ||
| 92 | |||
| 93 | /// Returns true if there is a device connected to port | ||
| 94 | bool DeviceConnected(std::size_t port) const; | ||
| 95 | |||
| 96 | /// Used for automapping features | ||
| 79 | std::vector<Common::ParamPackage> GetInputDevices() const; | 97 | std::vector<Common::ParamPackage> GetInputDevices() const; |
| 80 | InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const; | 98 | InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const; |
| 81 | InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const; | 99 | InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const; |
| 82 | 100 | ||
| 83 | /// Returns true if there is a device connected to port | 101 | private: |
| 84 | bool DeviceConnected(std::size_t port) const; | 102 | using AdapterPayload = std::array<u8, 37>; |
| 85 | 103 | ||
| 86 | std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue(); | 104 | void UpdatePadType(std::size_t port, ControllerTypes pad_type); |
| 87 | const std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue() const; | 105 | void UpdateControllers(const AdapterPayload& adapter_payload); |
| 106 | void UpdateYuzuSettings(std::size_t port); | ||
| 107 | void UpdateStateButtons(std::size_t port, u8 b1, u8 b2); | ||
| 108 | void UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload); | ||
| 109 | void UpdateVibrations(); | ||
| 88 | 110 | ||
| 89 | std::array<GCState, 4>& GetPadState(); | 111 | void AdapterInputThread(); |
| 90 | const std::array<GCState, 4>& GetPadState() const; | ||
| 91 | 112 | ||
| 92 | int GetOriginValue(u32 port, u32 axis) const; | 113 | void AdapterScanThread(); |
| 93 | 114 | ||
| 94 | private: | 115 | bool IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size); |
| 95 | GCPadStatus GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload); | 116 | |
| 117 | // Updates vibration state of all controllers | ||
| 118 | void SendVibrations(); | ||
| 119 | |||
| 120 | /// For use in initialization, querying devices to find the adapter | ||
| 121 | void Setup(); | ||
| 96 | 122 | ||
| 97 | void Read(); | 123 | /// Resets status of all GC controller devices to a disconected state |
| 124 | void ResetDevices(); | ||
| 98 | 125 | ||
| 99 | /// Resets status of device connected to port | 126 | /// Resets status of device connected to a disconected state |
| 100 | void ResetDeviceType(std::size_t port); | 127 | void ResetDevice(std::size_t port); |
| 101 | 128 | ||
| 102 | /// Returns true if we successfully gain access to GC Adapter | 129 | /// Returns true if we successfully gain access to GC Adapter |
| 103 | bool CheckDeviceAccess(libusb_device* device); | 130 | bool CheckDeviceAccess(); |
| 104 | 131 | ||
| 105 | /// Captures GC Adapter endpoint address, | 132 | /// Captures GC Adapter endpoint address |
| 106 | void GetGCEndpoint(libusb_device* device); | 133 | /// Returns true if the endpoind was set correctly |
| 134 | bool GetGCEndpoint(libusb_device* device); | ||
| 107 | 135 | ||
| 108 | /// For shutting down, clear all data, join all threads, release usb | 136 | /// For shutting down, clear all data, join all threads, release usb |
| 109 | void Reset(); | 137 | void Reset(); |
| 110 | 138 | ||
| 111 | /// For use in initialization, querying devices to find the adapter | 139 | // Join all threads |
| 112 | void Setup(); | 140 | void JoinThreads(); |
| 141 | |||
| 142 | // Release usb handles | ||
| 143 | void ClearLibusbHandle(); | ||
| 113 | 144 | ||
| 114 | libusb_device_handle* usb_adapter_handle = nullptr; | 145 | libusb_device_handle* usb_adapter_handle = nullptr; |
| 146 | std::array<GCController, 4> pads; | ||
| 147 | Common::SPSCQueue<GCPadStatus> pad_queue; | ||
| 115 | 148 | ||
| 116 | std::thread adapter_input_thread; | 149 | std::thread adapter_input_thread; |
| 117 | bool adapter_thread_running; | 150 | std::thread adapter_scan_thread; |
| 151 | bool adapter_input_thread_running; | ||
| 152 | bool adapter_scan_thread_running; | ||
| 153 | bool restart_scan_thread; | ||
| 118 | 154 | ||
| 119 | libusb_context* libusb_ctx; | 155 | libusb_context* libusb_ctx; |
| 120 | 156 | ||
| 121 | u8 input_endpoint = 0; | 157 | u8 input_endpoint{0}; |
| 122 | u8 output_endpoint = 0; | 158 | u8 output_endpoint{0}; |
| 123 | 159 | u8 input_error_counter{0}; | |
| 124 | bool configuring = false; | 160 | u8 output_error_counter{0}; |
| 161 | int vibration_counter{0}; | ||
| 125 | 162 | ||
| 126 | std::array<GCState, 4> state; | 163 | bool configuring{false}; |
| 127 | std::array<bool, 4> get_origin; | 164 | bool rumble_enabled{true}; |
| 128 | std::array<GCPadStatus, 4> origin_status; | 165 | bool vibration_changed{true}; |
| 129 | std::array<Common::SPSCQueue<GCPadStatus>, 4> pad_queue; | ||
| 130 | std::array<ControllerTypes, 4> adapter_controllers_status{}; | ||
| 131 | }; | 166 | }; |
| 132 | |||
| 133 | } // namespace GCAdapter | 167 | } // namespace GCAdapter |
diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp index 893556916..6bd6f57fc 100644 --- a/src/input_common/gcadapter/gc_poller.cpp +++ b/src/input_common/gcadapter/gc_poller.cpp | |||
| @@ -15,22 +15,30 @@ namespace InputCommon { | |||
| 15 | 15 | ||
| 16 | class GCButton final : public Input::ButtonDevice { | 16 | class GCButton final : public Input::ButtonDevice { |
| 17 | public: | 17 | public: |
| 18 | explicit GCButton(u32 port_, int button_, const GCAdapter::Adapter* adapter) | 18 | explicit GCButton(u32 port_, s32 button_, GCAdapter::Adapter* adapter) |
| 19 | : port(port_), button(button_), gcadapter(adapter) {} | 19 | : port(port_), button(button_), gcadapter(adapter) {} |
| 20 | 20 | ||
| 21 | ~GCButton() override; | 21 | ~GCButton() override; |
| 22 | 22 | ||
| 23 | bool GetStatus() const override { | 23 | bool GetStatus() const override { |
| 24 | if (gcadapter->DeviceConnected(port)) { | 24 | if (gcadapter->DeviceConnected(port)) { |
| 25 | return gcadapter->GetPadState()[port].buttons.at(button); | 25 | return (gcadapter->GetPadState(port).buttons & button) != 0; |
| 26 | } | 26 | } |
| 27 | return false; | 27 | return false; |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const override { | ||
| 31 | const float amplitude = amp_high + amp_low > 2.0f ? 1.0f : (amp_high + amp_low) * 0.5f; | ||
| 32 | const auto new_amp = | ||
| 33 | static_cast<f32>(pow(amplitude, 0.5f) * (3.0f - 2.0f * pow(amplitude, 0.15f))); | ||
| 34 | |||
| 35 | return gcadapter->RumblePlay(port, new_amp); | ||
| 36 | } | ||
| 37 | |||
| 30 | private: | 38 | private: |
| 31 | const u32 port; | 39 | const u32 port; |
| 32 | const int button; | 40 | const s32 button; |
| 33 | const GCAdapter::Adapter* gcadapter; | 41 | GCAdapter::Adapter* gcadapter; |
| 34 | }; | 42 | }; |
| 35 | 43 | ||
| 36 | class GCAxisButton final : public Input::ButtonDevice { | 44 | class GCAxisButton final : public Input::ButtonDevice { |
| @@ -38,13 +46,12 @@ public: | |||
| 38 | explicit GCAxisButton(u32 port_, u32 axis_, float threshold_, bool trigger_if_greater_, | 46 | explicit GCAxisButton(u32 port_, u32 axis_, float threshold_, bool trigger_if_greater_, |
| 39 | const GCAdapter::Adapter* adapter) | 47 | const GCAdapter::Adapter* adapter) |
| 40 | : port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_), | 48 | : port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_), |
| 41 | gcadapter(adapter), | 49 | gcadapter(adapter) {} |
| 42 | origin_value(static_cast<float>(adapter->GetOriginValue(port_, axis_))) {} | ||
| 43 | 50 | ||
| 44 | bool GetStatus() const override { | 51 | bool GetStatus() const override { |
| 45 | if (gcadapter->DeviceConnected(port)) { | 52 | if (gcadapter->DeviceConnected(port)) { |
| 46 | const float current_axis_value = gcadapter->GetPadState()[port].axes.at(axis); | 53 | const float current_axis_value = gcadapter->GetPadState(port).axis_values.at(axis); |
| 47 | const float axis_value = (current_axis_value - origin_value) / 128.0f; | 54 | const float axis_value = current_axis_value / 128.0f; |
| 48 | if (trigger_if_greater) { | 55 | if (trigger_if_greater) { |
| 49 | // TODO: Might be worthwile to set a slider for the trigger threshold. It is | 56 | // TODO: Might be worthwile to set a slider for the trigger threshold. It is |
| 50 | // currently always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick | 57 | // currently always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick |
| @@ -61,7 +68,6 @@ private: | |||
| 61 | float threshold; | 68 | float threshold; |
| 62 | bool trigger_if_greater; | 69 | bool trigger_if_greater; |
| 63 | const GCAdapter::Adapter* gcadapter; | 70 | const GCAdapter::Adapter* gcadapter; |
| 64 | const float origin_value; | ||
| 65 | }; | 71 | }; |
| 66 | 72 | ||
| 67 | GCButtonFactory::GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_) | 73 | GCButtonFactory::GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_) |
| @@ -73,7 +79,7 @@ std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::Param | |||
| 73 | const auto button_id = params.Get("button", 0); | 79 | const auto button_id = params.Get("button", 0); |
| 74 | const auto port = static_cast<u32>(params.Get("port", 0)); | 80 | const auto port = static_cast<u32>(params.Get("port", 0)); |
| 75 | 81 | ||
| 76 | constexpr int PAD_STICK_ID = static_cast<u16>(GCAdapter::PadButton::PAD_STICK); | 82 | constexpr s32 PAD_STICK_ID = static_cast<s32>(GCAdapter::PadButton::Stick); |
| 77 | 83 | ||
| 78 | // button is not an axis/stick button | 84 | // button is not an axis/stick button |
| 79 | if (button_id != PAD_STICK_ID) { | 85 | if (button_id != PAD_STICK_ID) { |
| @@ -106,32 +112,25 @@ Common::ParamPackage GCButtonFactory::GetNextInput() const { | |||
| 106 | Common::ParamPackage params; | 112 | Common::ParamPackage params; |
| 107 | GCAdapter::GCPadStatus pad; | 113 | GCAdapter::GCPadStatus pad; |
| 108 | auto& queue = adapter->GetPadQueue(); | 114 | auto& queue = adapter->GetPadQueue(); |
| 109 | for (std::size_t port = 0; port < queue.size(); ++port) { | 115 | while (queue.Pop(pad)) { |
| 110 | while (queue[port].Pop(pad)) { | 116 | // This while loop will break on the earliest detected button |
| 111 | // This while loop will break on the earliest detected button | 117 | params.Set("engine", "gcpad"); |
| 112 | params.Set("engine", "gcpad"); | 118 | params.Set("port", static_cast<s32>(pad.port)); |
| 113 | params.Set("port", static_cast<int>(port)); | 119 | if (pad.button != GCAdapter::PadButton::Undefined) { |
| 114 | for (const auto& button : GCAdapter::PadButtonArray) { | 120 | params.Set("button", static_cast<u16>(pad.button)); |
| 115 | const u16 button_value = static_cast<u16>(button); | 121 | } |
| 116 | if (pad.button & button_value) { | ||
| 117 | params.Set("button", button_value); | ||
| 118 | break; | ||
| 119 | } | ||
| 120 | } | ||
| 121 | 122 | ||
| 122 | // For Axis button implementation | 123 | // For Axis button implementation |
| 123 | if (pad.axis != GCAdapter::PadAxes::Undefined) { | 124 | if (pad.axis != GCAdapter::PadAxes::Undefined) { |
| 124 | params.Set("axis", static_cast<u8>(pad.axis)); | 125 | params.Set("axis", static_cast<u8>(pad.axis)); |
| 125 | params.Set("button", static_cast<u16>(GCAdapter::PadButton::PAD_STICK)); | 126 | params.Set("button", static_cast<u16>(GCAdapter::PadButton::Stick)); |
| 126 | if (pad.axis_value > 128) { | 127 | params.Set("threshold", "0.25"); |
| 127 | params.Set("direction", "+"); | 128 | if (pad.axis_value > 0) { |
| 128 | params.Set("threshold", "0.25"); | 129 | params.Set("direction", "+"); |
| 129 | } else { | 130 | } else { |
| 130 | params.Set("direction", "-"); | 131 | params.Set("direction", "-"); |
| 131 | params.Set("threshold", "-0.25"); | ||
| 132 | } | ||
| 133 | break; | ||
| 134 | } | 132 | } |
| 133 | break; | ||
| 135 | } | 134 | } |
| 136 | } | 135 | } |
| 137 | return params; | 136 | return params; |
| @@ -152,17 +151,14 @@ public: | |||
| 152 | explicit GCAnalog(u32 port_, u32 axis_x_, u32 axis_y_, float deadzone_, | 151 | explicit GCAnalog(u32 port_, u32 axis_x_, u32 axis_y_, float deadzone_, |
| 153 | const GCAdapter::Adapter* adapter, float range_) | 152 | const GCAdapter::Adapter* adapter, float range_) |
| 154 | : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter), | 153 | : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter), |
| 155 | origin_value_x(static_cast<float>(adapter->GetOriginValue(port_, axis_x_))), | ||
| 156 | origin_value_y(static_cast<float>(adapter->GetOriginValue(port_, axis_y_))), | ||
| 157 | range(range_) {} | 154 | range(range_) {} |
| 158 | 155 | ||
| 159 | float GetAxis(u32 axis) const { | 156 | float GetAxis(u32 axis) const { |
| 160 | if (gcadapter->DeviceConnected(port)) { | 157 | if (gcadapter->DeviceConnected(port)) { |
| 161 | std::lock_guard lock{mutex}; | 158 | std::lock_guard lock{mutex}; |
| 162 | const auto origin_value = axis % 2 == 0 ? origin_value_x : origin_value_y; | ||
| 163 | const auto axis_value = | 159 | const auto axis_value = |
| 164 | static_cast<float>(gcadapter->GetPadState()[port].axes.at(axis)); | 160 | static_cast<float>(gcadapter->GetPadState(port).axis_values.at(axis)); |
| 165 | return (axis_value - origin_value) / (100.0f * range); | 161 | return (axis_value) / (100.0f * range); |
| 166 | } | 162 | } |
| 167 | return 0.0f; | 163 | return 0.0f; |
| 168 | } | 164 | } |
| @@ -215,8 +211,6 @@ private: | |||
| 215 | const u32 axis_y; | 211 | const u32 axis_y; |
| 216 | const float deadzone; | 212 | const float deadzone; |
| 217 | const GCAdapter::Adapter* gcadapter; | 213 | const GCAdapter::Adapter* gcadapter; |
| 218 | const float origin_value_x; | ||
| 219 | const float origin_value_y; | ||
| 220 | const float range; | 214 | const float range; |
| 221 | mutable std::mutex mutex; | 215 | mutable std::mutex mutex; |
| 222 | }; | 216 | }; |
| @@ -254,26 +248,44 @@ void GCAnalogFactory::EndConfiguration() { | |||
| 254 | 248 | ||
| 255 | Common::ParamPackage GCAnalogFactory::GetNextInput() { | 249 | Common::ParamPackage GCAnalogFactory::GetNextInput() { |
| 256 | GCAdapter::GCPadStatus pad; | 250 | GCAdapter::GCPadStatus pad; |
| 251 | Common::ParamPackage params; | ||
| 257 | auto& queue = adapter->GetPadQueue(); | 252 | auto& queue = adapter->GetPadQueue(); |
| 258 | for (std::size_t port = 0; port < queue.size(); ++port) { | 253 | while (queue.Pop(pad)) { |
| 259 | while (queue[port].Pop(pad)) { | 254 | if (pad.button != GCAdapter::PadButton::Undefined) { |
| 260 | if (pad.axis == GCAdapter::PadAxes::Undefined || | 255 | params.Set("engine", "gcpad"); |
| 261 | std::abs((static_cast<float>(pad.axis_value) - 128.0f) / 128.0f) < 0.1f) { | 256 | params.Set("port", static_cast<s32>(pad.port)); |
| 262 | continue; | 257 | params.Set("button", static_cast<u16>(pad.button)); |
| 263 | } | 258 | return params; |
| 264 | // An analog device needs two axes, so we need to store the axis for later and wait for | 259 | } |
| 265 | // a second input event. The axes also must be from the same joystick. | 260 | if (pad.axis == GCAdapter::PadAxes::Undefined || |
| 266 | const u8 axis = static_cast<u8>(pad.axis); | 261 | std::abs(static_cast<float>(pad.axis_value) / 128.0f) < 0.1f) { |
| 267 | if (analog_x_axis == -1) { | 262 | continue; |
| 268 | analog_x_axis = axis; | 263 | } |
| 269 | controller_number = static_cast<int>(port); | 264 | // An analog device needs two axes, so we need to store the axis for later and wait for |
| 270 | } else if (analog_y_axis == -1 && analog_x_axis != axis && | 265 | // a second input event. The axes also must be from the same joystick. |
| 271 | controller_number == static_cast<int>(port)) { | 266 | const u8 axis = static_cast<u8>(pad.axis); |
| 272 | analog_y_axis = axis; | 267 | if (axis == 0 || axis == 1) { |
| 273 | } | 268 | analog_x_axis = 0; |
| 269 | analog_y_axis = 1; | ||
| 270 | controller_number = static_cast<s32>(pad.port); | ||
| 271 | break; | ||
| 272 | } | ||
| 273 | if (axis == 2 || axis == 3) { | ||
| 274 | analog_x_axis = 2; | ||
| 275 | analog_y_axis = 3; | ||
| 276 | controller_number = static_cast<s32>(pad.port); | ||
| 277 | break; | ||
| 278 | } | ||
| 279 | |||
| 280 | if (analog_x_axis == -1) { | ||
| 281 | analog_x_axis = axis; | ||
| 282 | controller_number = static_cast<s32>(pad.port); | ||
| 283 | } else if (analog_y_axis == -1 && analog_x_axis != axis && | ||
| 284 | controller_number == static_cast<s32>(pad.port)) { | ||
| 285 | analog_y_axis = axis; | ||
| 286 | break; | ||
| 274 | } | 287 | } |
| 275 | } | 288 | } |
| 276 | Common::ParamPackage params; | ||
| 277 | if (analog_x_axis != -1 && analog_y_axis != -1) { | 289 | if (analog_x_axis != -1 && analog_y_axis != -1) { |
| 278 | params.Set("engine", "gcpad"); | 290 | params.Set("engine", "gcpad"); |
| 279 | params.Set("port", controller_number); | 291 | params.Set("port", controller_number); |
diff --git a/src/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 | |||
| 167 | private: | 167 | private: |
| 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: | |||
| 186 | std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) { | 186 | std::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 | ||
| 201 | std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) { | 205 | std::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 | ||
| 242 | void SDLState::InitJoystick(int joystick_index) { | 228 | void 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 | ||
| 291 | void SDLState::HandleGameControllerEvent(const SDL_Event& event) { | 279 | void 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 | |||
| 797 | Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) { | 784 | Common::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 | |||
| 820 | Common::ParamPackage SDLEventToMotionParamPackage(SDLState& state, const SDL_Event& event) { | 813 | Common::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 | ||
| 19 | namespace Common { | 20 | namespace Common { |
| 20 | 21 | ||
| 22 | class ThreadIds { | ||
| 23 | public: | ||
| 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 | |||
| 38 | private: | ||
| 39 | mutable std::mutex mutex; | ||
| 40 | std::unordered_map<std::thread::id, u32> ids; | ||
| 41 | }; | ||
| 42 | |||
| 21 | class TestControl1 { | 43 | class TestControl1 { |
| 22 | public: | 44 | public: |
| 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 | ||
| 41 | void TestControl1::DoWork() { | 63 | void 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 | ||
| 52 | void TestControl1::ExecuteThread(u32 id) { | 73 | void 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 | ||
| 170 | void TestControl2::ExecuteThread(u32 id) { | 186 | void 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 | ||
| 177 | void TestControl2::Exit() { | 192 | void 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 | ||
| 273 | void TestControl3::ExecuteThread(u32 id) { | 284 | void 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 | ||
| 280 | void TestControl3::Exit() { | 290 | void 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) | |||
| 250 | target_link_libraries(video_core PUBLIC common core) | 268 | target_link_libraries(video_core PUBLIC common core) |
| 251 | target_link_libraries(video_core PRIVATE glad xbyak) | 269 | target_link_libraries(video_core PRIVATE glad xbyak) |
| 252 | 270 | ||
| 271 | if (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) | ||
| 274 | else() | ||
| 275 | target_include_directories(video_core PRIVATE ${FFMPEG_INCLUDE_DIR}) | ||
| 276 | target_link_libraries(video_core PRIVATE ${FFMPEG_LIBRARIES}) | ||
| 277 | endif() | ||
| 278 | |||
| 253 | add_dependencies(video_core host_shaders) | 279 | add_dependencies(video_core host_shaders) |
| 254 | target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE}) | 280 | target_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 | |||
| 31 | namespace Tegra { | ||
| 32 | CDmaPusher::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 | |||
| 39 | CDmaPusher::~CDmaPusher() = default; | ||
| 40 | |||
| 41 | void CDmaPusher::Push(ChCommandHeaderList&& entries) { | ||
| 42 | cdma_queue.push(std::move(entries)); | ||
| 43 | } | ||
| 44 | |||
| 45 | void CDmaPusher::DispatchCalls() { | ||
| 46 | while (!cdma_queue.empty()) { | ||
| 47 | Step(); | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | void 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 | |||
| 103 | void 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 | |||
| 166 | void 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 | |||
| 16 | namespace Tegra { | ||
| 17 | |||
| 18 | class GPU; | ||
| 19 | class Nvdec; | ||
| 20 | class Vic; | ||
| 21 | class Host1x; | ||
| 22 | |||
| 23 | enum 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 | |||
| 33 | enum 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 | |||
| 51 | enum class ChMethod : u32 { | ||
| 52 | Empty = 0, | ||
| 53 | SetMethod = 0x10, | ||
| 54 | SetData = 0x11, | ||
| 55 | }; | ||
| 56 | |||
| 57 | union 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 | }; | ||
| 63 | static_assert(sizeof(ChCommandHeader) == sizeof(u32), "ChCommand header is an invalid size"); | ||
| 64 | |||
| 65 | struct ChCommand { | ||
| 66 | ChClassId class_id{}; | ||
| 67 | int method_offset{}; | ||
| 68 | std::vector<u32> arguments; | ||
| 69 | }; | ||
| 70 | |||
| 71 | using ChCommandHeaderList = std::vector<Tegra::ChCommandHeader>; | ||
| 72 | using ChCommandList = std::vector<Tegra::ChCommand>; | ||
| 73 | |||
| 74 | struct 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 | |||
| 91 | enum 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 | |||
| 97 | class CDmaPusher { | ||
| 98 | public: | ||
| 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 | |||
| 114 | private: | ||
| 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 | |||
| 15 | extern "C" { | ||
| 16 | #include <libavutil/opt.h> | ||
| 17 | } | ||
| 18 | |||
| 19 | namespace Tegra { | ||
| 20 | |||
| 21 | Codec::Codec(GPU& gpu_) | ||
| 22 | : gpu(gpu_), h264_decoder(std::make_unique<Decoder::H264>(gpu)), | ||
| 23 | vp9_decoder(std::make_unique<Decoder::VP9>(gpu)) {} | ||
| 24 | |||
| 25 | Codec::~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 | |||
| 39 | void 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 | |||
| 44 | void 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 | |||
| 49 | void 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 | |||
| 103 | AVFrame* Codec::GetCurrentFrame() { | ||
| 104 | return av_frame; | ||
| 105 | } | ||
| 106 | |||
| 107 | const AVFrame* Codec::GetCurrentFrame() const { | ||
| 108 | return av_frame; | ||
| 109 | } | ||
| 110 | |||
| 111 | NvdecCommon::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 | |||
| 11 | extern "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 | |||
| 21 | namespace Tegra { | ||
| 22 | class GPU; | ||
| 23 | struct VicRegisters; | ||
| 24 | |||
| 25 | namespace Decoder { | ||
| 26 | class H264; | ||
| 27 | class VP9; | ||
| 28 | } // namespace Decoder | ||
| 29 | |||
| 30 | class Codec { | ||
| 31 | public: | ||
| 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 | |||
| 51 | private: | ||
| 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 | |||
| 27 | namespace Tegra::Decoder { | ||
| 28 | namespace { | ||
| 29 | // ZigZag LUTs from libavcodec. | ||
| 30 | constexpr std::array<u8, 64> zig_zag_direct{ | ||
| 31 | 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, | ||
| 32 | 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, | ||
| 33 | 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63, | ||
| 34 | }; | ||
| 35 | |||
| 36 | constexpr std::array<u8, 16> zig_zag_scan{ | ||
| 37 | 0 + 0 * 4, 1 + 0 * 4, 0 + 1 * 4, 0 + 2 * 4, 1 + 1 * 4, 2 + 0 * 4, 3 + 0 * 4, 2 + 1 * 4, | ||
| 38 | 1 + 2 * 4, 0 + 3 * 4, 1 + 3 * 4, 2 + 2 * 4, 3 + 1 * 4, 3 + 2 * 4, 2 + 3 * 4, 3 + 3 * 4, | ||
| 39 | }; | ||
| 40 | } // Anonymous namespace | ||
| 41 | |||
| 42 | H264::H264(GPU& gpu_) : gpu(gpu_) {} | ||
| 43 | |||
| 44 | H264::~H264() = default; | ||
| 45 | |||
| 46 | std::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 | |||
| 179 | H264BitWriter::H264BitWriter() = default; | ||
| 180 | |||
| 181 | H264BitWriter::~H264BitWriter() = default; | ||
| 182 | |||
| 183 | void H264BitWriter::WriteU(s32 value, s32 value_sz) { | ||
| 184 | WriteBits(value, value_sz); | ||
| 185 | } | ||
| 186 | |||
| 187 | void H264BitWriter::WriteSe(s32 value) { | ||
| 188 | WriteExpGolombCodedInt(value); | ||
| 189 | } | ||
| 190 | |||
| 191 | void H264BitWriter::WriteUe(u32 value) { | ||
| 192 | WriteExpGolombCodedUInt(value); | ||
| 193 | } | ||
| 194 | |||
| 195 | void H264BitWriter::End() { | ||
| 196 | WriteBit(true); | ||
| 197 | Flush(); | ||
| 198 | } | ||
| 199 | |||
| 200 | void H264BitWriter::WriteBit(bool state) { | ||
| 201 | WriteBits(state ? 1 : 0, 1); | ||
| 202 | } | ||
| 203 | |||
| 204 | void 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 | |||
| 223 | std::vector<u8>& H264BitWriter::GetByteArray() { | ||
| 224 | return byte_array; | ||
| 225 | } | ||
| 226 | |||
| 227 | const std::vector<u8>& H264BitWriter::GetByteArray() const { | ||
| 228 | return byte_array; | ||
| 229 | } | ||
| 230 | |||
| 231 | void 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 | |||
| 258 | void 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 | |||
| 267 | void 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 | |||
| 275 | s32 H264BitWriter::GetFreeBufferBits() { | ||
| 276 | if (buffer_pos == buffer_size) { | ||
| 277 | Flush(); | ||
| 278 | } | ||
| 279 | |||
| 280 | return buffer_size - buffer_pos; | ||
| 281 | } | ||
| 282 | |||
| 283 | void 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 | |||
| 28 | namespace Tegra { | ||
| 29 | class GPU; | ||
| 30 | namespace Decoder { | ||
| 31 | |||
| 32 | class H264BitWriter { | ||
| 33 | public: | ||
| 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 | |||
| 57 | private: | ||
| 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 | |||
| 71 | class H264 { | ||
| 72 | public: | ||
| 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 | |||
| 80 | private: | ||
| 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 | |||
| 11 | namespace Tegra::Decoder { | ||
| 12 | namespace { | ||
| 13 | // Default compressed header probabilities once frame context resets | ||
| 14 | constexpr 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 | |||
| 173 | constexpr std::array<s32, 256> norm_lut{ | ||
| 174 | 0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, | ||
| 175 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, | ||
| 176 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||
| 177 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||
| 178 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 179 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 180 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 181 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 182 | }; | ||
| 183 | |||
| 184 | constexpr std::array<s32, 254> map_lut{ | ||
| 185 | 20, 21, 22, 23, 24, 25, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, | ||
| 186 | 1, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 2, 50, 51, 52, 53, 54, | ||
| 187 | 55, 56, 57, 58, 59, 60, 61, 3, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, | ||
| 188 | 73, 4, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 5, 86, 87, 88, 89, | ||
| 189 | 90, 91, 92, 93, 94, 95, 96, 97, 6, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, | ||
| 190 | 108, 109, 7, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 8, 122, 123, 124, | ||
| 191 | 125, 126, 127, 128, 129, 130, 131, 132, 133, 9, 134, 135, 136, 137, 138, 139, 140, 141, 142, | ||
| 192 | 143, 144, 145, 10, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 11, 158, 159, | ||
| 193 | 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 12, 170, 171, 172, 173, 174, 175, 176, 177, | ||
| 194 | 178, 179, 180, 181, 13, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 14, 194, | ||
| 195 | 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 15, 206, 207, 208, 209, 210, 211, 212, | ||
| 196 | 213, 214, 215, 216, 217, 16, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 17, | ||
| 197 | 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 18, 242, 243, 244, 245, 246, 247, | ||
| 198 | 248, 249, 250, 251, 252, 253, 19, | ||
| 199 | }; | ||
| 200 | } // Anonymous namespace | ||
| 201 | |||
| 202 | VP9::VP9(GPU& gpu) : gpu(gpu) {} | ||
| 203 | |||
| 204 | VP9::~VP9() = default; | ||
| 205 | |||
| 206 | void 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 | } | ||
| 215 | template <typename T, std::size_t N> | ||
| 216 | void 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 | |||
| 223 | template <typename T, std::size_t N> | ||
| 224 | void 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 | |||
| 233 | void 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 | |||
| 239 | s32 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 | |||
| 255 | s32 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 | |||
| 265 | void 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 | |||
| 290 | bool 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 | |||
| 296 | void 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 | |||
| 355 | void 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 | |||
| 364 | s32 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 | |||
| 375 | s32 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 | |||
| 386 | Vp9PictureInfo 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 | |||
| 401 | void VP9::InsertEntropy(u64 offset, Vp9EntropyProbs& dst) { | ||
| 402 | EntropyProbs entropy{}; | ||
| 403 | gpu.MemoryManager().ReadBlock(offset, &entropy, sizeof(EntropyProbs)); | ||
| 404 | entropy.Convert(dst); | ||
| 405 | } | ||
| 406 | |||
| 407 | Vp9FrameContainer 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 | |||
| 448 | std::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 | |||
| 639 | VpxBitStreamWriter 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 | |||
| 851 | std::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 | |||
| 884 | VpxRangeEncoder::VpxRangeEncoder() { | ||
| 885 | Write(false); | ||
| 886 | } | ||
| 887 | |||
| 888 | VpxRangeEncoder::~VpxRangeEncoder() = default; | ||
| 889 | |||
| 890 | void 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 | |||
| 896 | void VpxRangeEncoder::Write(bool bit) { | ||
| 897 | Write(bit, half_probability); | ||
| 898 | } | ||
| 899 | |||
| 900 | void 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 | |||
| 940 | void VpxRangeEncoder::End() { | ||
| 941 | for (std::size_t index = 0; index < 32; ++index) { | ||
| 942 | Write(false); | ||
| 943 | } | ||
| 944 | } | ||
| 945 | |||
| 946 | u8 VpxRangeEncoder::PeekByte() { | ||
| 947 | const u8 value = base_stream.ReadByte(); | ||
| 948 | base_stream.Seek(-1, Common::SeekOrigin::FromCurrentPos); | ||
| 949 | |||
| 950 | return value; | ||
| 951 | } | ||
| 952 | |||
| 953 | VpxBitStreamWriter::VpxBitStreamWriter() = default; | ||
| 954 | |||
| 955 | VpxBitStreamWriter::~VpxBitStreamWriter() = default; | ||
| 956 | |||
| 957 | void VpxBitStreamWriter::WriteU(u32 value, u32 value_size) { | ||
| 958 | WriteBits(value, value_size); | ||
| 959 | } | ||
| 960 | |||
| 961 | void 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 | |||
| 970 | void 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 | |||
| 979 | void 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 | |||
| 1005 | void VpxBitStreamWriter::WriteBit(bool state) { | ||
| 1006 | WriteBits(state ? 1 : 0, 1); | ||
| 1007 | } | ||
| 1008 | |||
| 1009 | s32 VpxBitStreamWriter::GetFreeBufferBits() { | ||
| 1010 | if (buffer_pos == buffer_size) { | ||
| 1011 | Flush(); | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | return buffer_size - buffer_pos; | ||
| 1015 | } | ||
| 1016 | |||
| 1017 | void 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 | |||
| 1026 | std::vector<u8>& VpxBitStreamWriter::GetByteArray() { | ||
| 1027 | return byte_array; | ||
| 1028 | } | ||
| 1029 | |||
| 1030 | const 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 | |||
| 15 | namespace Tegra { | ||
| 16 | class GPU; | ||
| 17 | enum class FrameType { KeyFrame = 0, InterFrame = 1 }; | ||
| 18 | namespace Decoder { | ||
| 19 | |||
| 20 | /// The VpxRangeEncoder, and VpxBitStreamWriter classes are used to compose the | ||
| 21 | /// VP9 header bitstreams. | ||
| 22 | |||
| 23 | class VpxRangeEncoder { | ||
| 24 | public: | ||
| 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 | |||
| 48 | private: | ||
| 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 | |||
| 57 | class VpxBitStreamWriter { | ||
| 58 | public: | ||
| 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 | |||
| 83 | private: | ||
| 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 | |||
| 97 | class VP9 { | ||
| 98 | public: | ||
| 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 | |||
| 111 | private: | ||
| 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 | |||
| 13 | namespace Tegra { | ||
| 14 | class GPU; | ||
| 15 | |||
| 16 | namespace Decoder { | ||
| 17 | struct Vp9FrameDimensions { | ||
| 18 | s16 width{}; | ||
| 19 | s16 height{}; | ||
| 20 | s16 luma_pitch{}; | ||
| 21 | s16 chroma_pitch{}; | ||
| 22 | }; | ||
| 23 | static_assert(sizeof(Vp9FrameDimensions) == 0x8, "Vp9 Vp9FrameDimensions is an invalid size"); | ||
| 24 | |||
| 25 | enum 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 | |||
| 34 | enum 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 | }; | ||
| 40 | enum 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 | |||
| 54 | enum 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 | |||
| 72 | enum 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 | |||
| 90 | enum 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 | |||
| 98 | enum 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 | |||
| 107 | enum class reference_mode { | ||
| 108 | SingleReference = 0, | ||
| 109 | CompoundReference = 1, | ||
| 110 | ReferenceModeSelect = 2, | ||
| 111 | ReferenceModes = 3 | ||
| 112 | }; | ||
| 113 | |||
| 114 | struct 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 | }; | ||
| 122 | static_assert(sizeof(Segmentation) == 0x64, "Segmentation is an invalid size"); | ||
| 123 | |||
| 124 | struct LoopFilter { | ||
| 125 | u8 mode_ref_delta_enabled{}; | ||
| 126 | std::array<s8, 4> ref_deltas{}; | ||
| 127 | std::array<s8, 2> mode_deltas{}; | ||
| 128 | }; | ||
| 129 | static_assert(sizeof(LoopFilter) == 0x7, "LoopFilter is an invalid size"); | ||
| 130 | |||
| 131 | struct 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 | }; | ||
| 155 | static_assert(sizeof(Vp9EntropyProbs) == 0x9F4, "Vp9EntropyProbs is an invalid size"); | ||
| 156 | |||
| 157 | struct 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 | |||
| 198 | struct Vp9FrameContainer { | ||
| 199 | Vp9PictureInfo info{}; | ||
| 200 | std::vector<u8> bit_stream; | ||
| 201 | }; | ||
| 202 | |||
| 203 | struct 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 | }; | ||
| 278 | static_assert(sizeof(PictureInfo) == 0x100, "PictureInfo is an invalid size"); | ||
| 279 | |||
| 280 | struct 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 | }; | ||
| 350 | static_assert(sizeof(EntropyProbs) == 0xEA0, "EntropyProbs is an invalid size"); | ||
| 351 | |||
| 352 | enum class Ref { Last, Golden, AltRef }; | ||
| 353 | |||
| 354 | struct RefPoolElement { | ||
| 355 | s64 frame{}; | ||
| 356 | Ref ref{}; | ||
| 357 | bool refresh{}; | ||
| 358 | }; | ||
| 359 | |||
| 360 | struct 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 | |||
| 9 | Tegra::Host1x::Host1x(GPU& gpu_) : gpu(gpu_) {} | ||
| 10 | |||
| 11 | Tegra::Host1x::~Host1x() = default; | ||
| 12 | |||
| 13 | void 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 | |||
| 18 | void 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 | |||
| 36 | void 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 | |||
| 11 | namespace Tegra { | ||
| 12 | class GPU; | ||
| 13 | class Nvdec; | ||
| 14 | |||
| 15 | class Host1x { | ||
| 16 | public: | ||
| 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 | |||
| 66 | private: | ||
| 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 | |||
| 9 | namespace Tegra { | ||
| 10 | |||
| 11 | Nvdec::Nvdec(GPU& gpu_) : gpu(gpu_), codec(std::make_unique<Codec>(gpu)) {} | ||
| 12 | |||
| 13 | Nvdec::~Nvdec() = default; | ||
| 14 | |||
| 15 | void 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 | |||
| 32 | AVFrame* Nvdec::GetFrame() { | ||
| 33 | return codec->GetCurrentFrame(); | ||
| 34 | } | ||
| 35 | |||
| 36 | const AVFrame* Nvdec::GetFrame() const { | ||
| 37 | return codec->GetCurrentFrame(); | ||
| 38 | } | ||
| 39 | |||
| 40 | void 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 | |||
| 12 | namespace Tegra { | ||
| 13 | class GPU; | ||
| 14 | |||
| 15 | class Nvdec { | ||
| 16 | public: | ||
| 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 | |||
| 32 | private: | ||
| 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 | |||
| 10 | namespace Tegra::NvdecCommon { | ||
| 11 | |||
| 12 | struct 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 | }; | ||
| 38 | static_assert(sizeof(NvdecRegisters) == (0xBC0), "NvdecRegisters is incorrect size"); | ||
| 39 | |||
| 40 | enum 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 | |||
| 25 | namespace Tegra { | ||
| 26 | SyncptIncrManager::SyncptIncrManager(GPU& gpu_) : gpu(gpu_) {} | ||
| 27 | SyncptIncrManager::~SyncptIncrManager() = default; | ||
| 28 | |||
| 29 | void SyncptIncrManager::Increment(u32 id) { | ||
| 30 | increments.emplace_back(0, 0, id, true); | ||
| 31 | IncrementAllDone(); | ||
| 32 | } | ||
| 33 | |||
| 34 | u32 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 | |||
| 40 | void 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 | |||
| 50 | void 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 | |||
| 27 | namespace Tegra { | ||
| 28 | class GPU; | ||
| 29 | struct 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 | |||
| 39 | class SyncptIncrManager { | ||
| 40 | public: | ||
| 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 | |||
| 56 | private: | ||
| 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 | |||
| 14 | extern "C" { | ||
| 15 | #include <libswscale/swscale.h> | ||
| 16 | } | ||
| 17 | |||
| 18 | namespace Tegra { | ||
| 19 | |||
| 20 | Vic::Vic(GPU& gpu_, std::shared_ptr<Nvdec> nvdec_processor_) | ||
| 21 | : gpu(gpu_), nvdec_processor(std::move(nvdec_processor_)) {} | ||
| 22 | Vic::~Vic() = default; | ||
| 23 | |||
| 24 | void 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 | |||
| 29 | void 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 | |||
| 54 | void 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 | |||
| 12 | struct SwsContext; | ||
| 13 | |||
| 14 | namespace Tegra { | ||
| 15 | class GPU; | ||
| 16 | class Nvdec; | ||
| 17 | |||
| 18 | struct PlaneOffsets { | ||
| 19 | u32 luma_offset{}; | ||
| 20 | u32 chroma_u_offset{}; | ||
| 21 | u32 chroma_v_offset{}; | ||
| 22 | }; | ||
| 23 | |||
| 24 | struct 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 | }; | ||
| 53 | static_assert(sizeof(VicRegisters) == 0x7A0, "VicRegisters is an invalid size"); | ||
| 54 | |||
| 55 | class Vic { | ||
| 56 | public: | ||
| 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 | |||
| 72 | private: | ||
| 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 | ||
| 28 | MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192)); | 28 | MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192)); |
| 29 | 29 | ||
| 30 | GPU::GPU(Core::System& system_, bool is_async_) | 30 | GPU::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 | ||
| 81 | Tegra::CDmaPusher& GPU::CDmaPusher() { | ||
| 82 | return *cdma_pusher; | ||
| 83 | } | ||
| 84 | |||
| 80 | const DmaPusher& GPU::DmaPusher() const { | 85 | const DmaPusher& GPU::DmaPusher() const { |
| 81 | return *dma_pusher; | 86 | return *dma_pusher; |
| 82 | } | 87 | } |
| 83 | 88 | ||
| 89 | const Tegra::CDmaPusher& GPU::CDmaPusher() const { | ||
| 90 | return *cdma_pusher; | ||
| 91 | } | ||
| 92 | |||
| 84 | void GPU::WaitFence(u32 syncpoint_id, u32 value) { | 93 | void 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 | ||
| 18 | using CacheAddr = std::uintptr_t; | 19 | using 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 | ||
| 354 | private: | 370 | private: |
| 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 | ||
| 11 | namespace VideoCommon { | 11 | namespace VideoCommon { |
| 12 | 12 | ||
| 13 | GPUAsynch::GPUAsynch(Core::System& system) : GPU{system, true}, gpu_thread{system} {} | 13 | GPUAsynch::GPUAsynch(Core::System& system, bool use_nvdec) |
| 14 | : GPU{system, true, use_nvdec}, gpu_thread{system} {} | ||
| 14 | 15 | ||
| 15 | GPUAsynch::~GPUAsynch() = default; | 16 | GPUAsynch::~GPUAsynch() = default; |
| 16 | 17 | ||
| 17 | void GPUAsynch::Start() { | 18 | void 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 | ||
| 36 | void 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 | |||
| 35 | void GPUAsynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | 57 | void 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 |
| 21 | class GPUAsynch final : public Tegra::GPU { | 21 | class GPUAsynch final : public Tegra::GPU { |
| 22 | public: | 22 | public: |
| 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 | ||
| 8 | namespace VideoCommon { | 8 | namespace VideoCommon { |
| 9 | 9 | ||
| 10 | GPUSynch::GPUSynch(Core::System& system) : GPU{system, false} {} | 10 | GPUSynch::GPUSynch(Core::System& system, bool use_nvdec) : GPU{system, false, use_nvdec} {} |
| 11 | 11 | ||
| 12 | GPUSynch::~GPUSynch() = default; | 12 | GPUSynch::~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 | ||
| 29 | void 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 | |||
| 29 | void GPUSynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | 45 | void 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 |
| 20 | class GPUSynch final : public Tegra::GPU { | 20 | class GPUSynch final : public Tegra::GPU { |
| 21 | public: | 21 | public: |
| 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 |
| 19 | static void RunThread(Core::System& system, VideoCore::RendererBase& renderer, | 19 | static 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 | ||
| 76 | void ThreadManager::StartThread(VideoCore::RendererBase& renderer, | 80 | void 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 | ||
| 83 | void ThreadManager::SubmitList(Tegra::CommandList&& entries) { | 87 | void ThreadManager::SubmitList(Tegra::CommandList&& entries) { |
| 84 | PushCommand(SubmitListCommand(std::move(entries))); | 88 | PushCommand(SubmitListCommand(std::move(entries))); |
| 85 | } | 89 | } |
| 86 | 90 | ||
| 91 | void ThreadManager::SubmitCommandBuffer(Tegra::ChCommandHeaderList&& entries) { | ||
| 92 | PushCommand(SubmitChCommandEntries(std::move(entries))); | ||
| 93 | } | ||
| 94 | |||
| 87 | void ThreadManager::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | 95 | void 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 | ||
| 41 | struct 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 |
| 41 | struct SwapBuffersCommand final { | 49 | struct 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 {}; | |||
| 77 | struct GPUTickCommand final {}; | 85 | struct GPUTickCommand final {}; |
| 78 | 86 | ||
| 79 | using CommandData = | 87 | using 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 | ||
| 84 | struct CommandDataContainer { | 92 | struct 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 @@ | |||
| 1 | set(SHADER_FILES | 1 | set(SHADER_SOURCES |
| 2 | opengl_present.frag | 2 | opengl_present.frag |
| 3 | opengl_present.vert | 3 | opengl_present.vert |
| 4 | ) | 4 | ) |
| 5 | 5 | ||
| 6 | set(SHADER_INCLUDE ${CMAKE_CURRENT_BINARY_DIR}/include) | 6 | set(SHADER_INCLUDE ${CMAKE_CURRENT_BINARY_DIR}/include) |
| 7 | set(HOST_SHADERS_INCLUDE ${SHADER_INCLUDE} PARENT_SCOPE) | ||
| 8 | |||
| 9 | set(SHADER_DIR ${SHADER_INCLUDE}/video_core/host_shaders) | 7 | set(SHADER_DIR ${SHADER_INCLUDE}/video_core/host_shaders) |
| 10 | add_custom_command( | 8 | set(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 | ||
| 17 | set(INPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/source_shader.h.in) | 10 | set(INPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/source_shader.h.in) |
| 18 | set(HEADER_GENERATOR ${CMAKE_CURRENT_SOURCE_DIR}/StringShaderHeader.cmake) | 11 | set(HEADER_GENERATOR ${CMAKE_CURRENT_SOURCE_DIR}/StringShaderHeader.cmake) |
| 19 | 12 | ||
| 20 | foreach(FILENAME IN ITEMS ${SHADER_FILES}) | 13 | foreach(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}) |
| 36 | endforeach() | 29 | endforeach() |
| @@ -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 | ||
| 9 | file(READ ${SOURCE_FILE} CONTENTS) | 9 | file(READ ${SOURCE_FILE} CONTENTS) |
| 10 | 10 | ||
| 11 | get_filename_component(OUTPUT_DIR ${HEADER_FILE} DIRECTORY) | ||
| 12 | make_directory(${OUTPUT_DIR}) | ||
| 11 | configure_file(${INPUT_FILE} ${HEADER_FILE} @ONLY) | 13 | configure_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 | ||
| 15 | namespace Tegra { | 16 | namespace 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 | ||
| 48 | GPUVAddr 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 | |||
| 47 | void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) { | 54 | void 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 | ||
| 111 | std::optional<GPUVAddr> MemoryManager::FindFreeRange(std::size_t size, std::size_t align) const { | 118 | std::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 | ||
| 324 | void ShaderDiskCacheOpenGL::InvalidateTransferable() { | 323 | void 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 | ||
| 850 | std::optional<std::vector<VkLayerProperties>> EnumerateInstanceLayerProperties( | 850 | std::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 | ||
| 45 | std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) { | 45 | std::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 | ||
| 11 | create_target_directory_groups(web_service) | 11 | create_target_directory_groups(web_service) |
| 12 | target_link_libraries(web_service PRIVATE common nlohmann_json::nlohmann_json httplib lurlparser) | 12 | target_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 | ||
| 20 | constexpr std::array<const char, 1> API_VERSION{'1'}; | 19 | constexpr std::array<const char, 1> API_VERSION{'1'}; |
| 21 | 20 | ||
| 22 | constexpr int HTTP_PORT = 80; | ||
| 23 | constexpr int HTTPS_PORT = 443; | ||
| 24 | |||
| 25 | constexpr std::size_t TIMEOUT_SECONDS = 30; | 21 | constexpr std::size_t TIMEOUT_SECONDS = 30; |
| 26 | 22 | ||
| 27 | struct Client::Impl { | 23 | struct 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) | ||
| 271 | endif() | 273 | endif() |
| 272 | 274 | ||
| 273 | if (NOT APPLE) | 275 | if (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 | ||
| 19 | namespace { | 19 | namespace { |
| 20 | 20 | ||
| 21 | constexpr std::array<std::array<bool, 4>, 8> led_patterns = {{ | 21 | constexpr 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 | ||
| 32 | void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index, | 32 | void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index, |
| @@ -589,7 +589,7 @@ QtControllerSelector::QtControllerSelector(GMainWindow& parent) { | |||
| 589 | QtControllerSelector::~QtControllerSelector() = default; | 589 | QtControllerSelector::~QtControllerSelector() = default; |
| 590 | 590 | ||
| 591 | void QtControllerSelector::ReconfigureControllers( | 591 | void 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 | ||
| 126 | signals: | 127 | signals: |
| 127 | void MainWindowReconfigureControllers(Core::Frontend::ControllerParameters parameters) const; | 128 | void MainWindowReconfigureControllers( |
| 129 | const Core::Frontend::ControllerParameters& parameters) const; | ||
| 128 | 130 | ||
| 129 | private: | 131 | private: |
| 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 | ||
| 115 | QtProfileSelectionDialog::~QtProfileSelectionDialog() = default; | 115 | QtProfileSelectionDialog::~QtProfileSelectionDialog() = default; |
| 116 | 116 | ||
| 117 | int 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 | |||
| 117 | void QtProfileSelectionDialog::accept() { | 126 | void 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() { | |||
| 515 | void Config::ReadCoreValues() { | 515 | void 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() { | |||
| 1108 | void Config::SaveCoreValues() { | 1110 | void 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() { | |||
| 104 | void ConfigureSystem::ReadSystemSettings() {} | 105 | void ConfigureSystem::ReadSystemSettings() {} |
| 105 | 106 | ||
| 106 | void ConfigureSystem::ApplyConfiguration() { | 107 | void 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 | ||
| 305 | void GMainWindow::ProfileSelectorSelectProfile() { | 305 | void 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 |
| 98 | use_multi_core= | 98 | use_multi_core= |
| 99 | 99 | ||
| 100 | [Cpu] | 100 | [Cpu] |
| @@ -163,7 +163,7 @@ max_anisotropy = | |||
| 163 | use_vsync = | 163 | use_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 |
| 167 | use_assembly_shaders = | 167 | use_assembly_shaders = |
| 168 | 168 | ||
| 169 | # Whether to allow asynchronous shader building. | 169 | # Whether to allow asynchronous shader building. |