Compare commits

...

21 commits

Author SHA1 Message Date
lizzie
42e967eb34 fixups 2026-06-04 01:09:46 +00:00
lizzie
bfa237220c fix license 2026-06-03 17:50:40 +00:00
lizzie
1a82215976 case stupidity in windows 2026-06-03 17:49:15 +00:00
lizzie
9cff7b8337 correct defines for d3d9 2026-06-03 17:49:15 +00:00
lizzie
2e857190dc fs 2026-06-03 17:49:15 +00:00
lizzie
e03f3dd25c fix msvc 2026-06-03 17:49:15 +00:00
lizzie
cd079ed97f public 2026-06-03 17:49:15 +00:00
lizzie
3a875e50d6 Trigger Build 2026-06-03 17:49:15 +00:00
lizzie
736fa0540e better inlining 2026-06-03 17:49:15 +00:00
lizzie
2c2d69887f maxwell macros 2026-06-03 17:49:15 +00:00
lizzie
9ab941fc23 remove implicit system saved in struct, pass as first param 2026-06-03 17:48:58 +00:00
lizzie
9702a87277 ffs 2026-06-03 17:48:29 +00:00
lizzie
7ab6c88ad9 extra 2026-06-03 17:48:29 +00:00
lizzie
09b9210739 extra fixups 2026-06-03 17:48:29 +00:00
lizzie
e4d16aadab less load, fix nv01 timer being kepler 2026-06-03 17:48:29 +00:00
lizzie
8f991ecf79 fx 2026-06-03 17:48:11 +00:00
lizzie
cd23ec0b07 [video_core] Remove redundant references in GPU engine structs
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-06-03 17:48:11 +00:00
maufeat
2f89c65c83 [kernel, hle] Initial 22.0.0 kernel changes and cmd stubs (#3761)
- KProcess::Run() and CreateThread() SVC now write the current thread handle to TLS+0x110
- KPageTableBase::LockForMapDeviceAddressSpace now checks for a new KPageTableBase boolean, m_allowed_exec_device_mapping
- Stub `am` + `acc` + `settings` cmd module that needs to work for qlaunch

Thanks to: @alula and @yellows8
Source for changes: https://github.com/Atmosphere-NX/Atmosphere/pull/2744, https://switchbrew.org/, https://yls8.mtheall.com/

Co-authored-by: PavelBARABANOV <pavelbarabanov94@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3761
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-06-03 17:46:56 +00:00
lizzie
ecc82cf0b7 llicense fix 2026-06-03 17:46:56 +00:00
lizzie
4baf6aa160 fx 2026-06-03 17:46:56 +00:00
lizzie
65f6dd2654 [audio_core, hle/services, video_core/compute] Inline std::unique_ptr<> allocs into std::optional<>
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-06-03 17:46:56 +00:00
68 changed files with 1025 additions and 1226 deletions

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -8,10 +11,11 @@
namespace AudioCore { namespace AudioCore {
AudioCore::AudioCore(Core::System& system) : audio_manager{std::make_unique<AudioManager>()} { AudioCore::AudioCore(Core::System& system) {
audio_manager.emplace();
CreateSinks(); CreateSinks();
// Must be created after the sinks // Must be created after the sinks
adsp = std::make_unique<ADSP::ADSP>(system, *output_sink); adsp.emplace(system, *output_sink);
} }
AudioCore ::~AudioCore() { AudioCore ::~AudioCore() {

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -15,10 +18,7 @@ class System;
namespace AudioCore { namespace AudioCore {
class AudioManager; /// @brief Main audio class, stored inside the core, and holding the audio manager, all sinks, and the ADSP.
/**
* Main audio class, stored inside the core, and holding the audio manager, all sinks, and the ADSP.
*/
class AudioCore { class AudioCore {
public: public:
explicit AudioCore(Core::System& system); explicit AudioCore(Core::System& system);
@ -50,27 +50,22 @@ public:
*/ */
Sink::Sink& GetInputSink(); Sink::Sink& GetInputSink();
/** /// @brief Get the ADSP.
* Get the ADSP. /// @return Ref to the ADSP.
*
* @return Ref to the ADSP.
*/
ADSP::ADSP& ADSP(); ADSP::ADSP& ADSP();
private: private:
/** /// @brief Create the sinks on startup.
* Create the sinks on startup.
*/
void CreateSinks(); void CreateSinks();
/// Main audio manager for audio in/out /// Main audio manager for audio in/out
std::unique_ptr<AudioManager> audio_manager; std::optional<AudioManager> audio_manager;
/// Sink used for audio renderer and audio out /// Sink used for audio renderer and audio out
std::unique_ptr<Sink::Sink> output_sink; std::unique_ptr<Sink::Sink> output_sink;
/// Sink used for audio input /// Sink used for audio input
std::unique_ptr<Sink::Sink> input_sink; std::unique_ptr<Sink::Sink> input_sink;
/// The ADSP in the sysmodule /// The ADSP in the sysmodule
std::unique_ptr<ADSP::ADSP> adsp; std::optional<ADSP::ADSP> adsp;
}; };
} // namespace AudioCore } // namespace AudioCore

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -41,8 +44,7 @@ void Manager::ReleaseSessionId(const size_t session_id) {
Result Manager::LinkToManager() { Result Manager::LinkToManager() {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
if (!linked_to_manager) { if (!linked_to_manager) {
AudioManager& manager{system.AudioCore().GetAudioManager()}; system.AudioCore().GetAudioManager().SetInManager(std::bind(&Manager::BufferReleaseAndRegister, this));
manager.SetInManager(std::bind(&Manager::BufferReleaseAndRegister, this));
linked_to_manager = true; linked_to_manager = true;
} }

View file

@ -1,81 +1,77 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/audio_manager.h" #include "audio_core/audio_manager.h"
#include "common/thread.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/service/audio/errors.h" #include "core/hle/service/audio/errors.h"
namespace AudioCore { namespace AudioCore {
AudioManager::AudioManager() { AudioManager::AudioManager() {
thread = std::jthread([this]() { ThreadFunc(); }); thread = std::jthread([this](std::stop_token stop_token) {
Common::SetCurrentThreadName("AudioManager");
std::unique_lock l{events.GetAudioEventLock()};
events.ClearEvents();
while (!stop_token.stop_requested()) {
const auto timed_out{events.Wait(l, std::chrono::seconds(2))};
if (events.CheckAudioEventSet(Event::Type::Max)) {
break;
}
for (size_t i = 0; i < buffer_events.size(); i++) {
const auto event_type = Event::Type(i);
if (events.CheckAudioEventSet(event_type) || timed_out) {
if (buffer_events[i]) {
buffer_events[i]();
}
}
events.SetAudioEvent(event_type, false);
}
}
});
} }
void AudioManager::Shutdown() { void AudioManager::Shutdown() {
running = false;
events.SetAudioEvent(Event::Type::Max, true); events.SetAudioEvent(Event::Type::Max, true);
thread.join(); if (thread.joinable()) {
thread.request_stop();
thread.join();
}
} }
Result AudioManager::SetOutManager(BufferEventFunc buffer_func) { Result AudioManager::SetOutManager(BufferEventFunc buffer_func) {
if (!running) { if (thread.joinable()) {
return Service::Audio::ResultOperationFailed; std::scoped_lock l{lock};
const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)};
if (buffer_events[index] == nullptr) {
buffer_events[index] = std::move(buffer_func);
needs_update = true;
events.SetAudioEvent(Event::Type::AudioOutManager, true);
}
return ResultSuccess;
} }
return Service::Audio::ResultOperationFailed;
std::scoped_lock l{lock};
const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)};
if (buffer_events[index] == nullptr) {
buffer_events[index] = std::move(buffer_func);
needs_update = true;
events.SetAudioEvent(Event::Type::AudioOutManager, true);
}
return ResultSuccess;
} }
Result AudioManager::SetInManager(BufferEventFunc buffer_func) { Result AudioManager::SetInManager(BufferEventFunc buffer_func) {
if (!running) { if (thread.joinable()) {
return Service::Audio::ResultOperationFailed; std::scoped_lock l{lock};
const auto index{events.GetManagerIndex(Event::Type::AudioInManager)};
if (buffer_events[index] == nullptr) {
buffer_events[index] = std::move(buffer_func);
needs_update = true;
events.SetAudioEvent(Event::Type::AudioInManager, true);
}
return ResultSuccess;
} }
return Service::Audio::ResultOperationFailed;
std::scoped_lock l{lock};
const auto index{events.GetManagerIndex(Event::Type::AudioInManager)};
if (buffer_events[index] == nullptr) {
buffer_events[index] = std::move(buffer_func);
needs_update = true;
events.SetAudioEvent(Event::Type::AudioInManager, true);
}
return ResultSuccess;
} }
void AudioManager::SetEvent(const Event::Type type, const bool signalled) { void AudioManager::SetEvent(const Event::Type type, const bool signalled) {
events.SetAudioEvent(type, signalled); events.SetAudioEvent(type, signalled);
} }
void AudioManager::ThreadFunc() {
std::unique_lock l{events.GetAudioEventLock()};
events.ClearEvents();
running = true;
while (running) {
const auto timed_out{events.Wait(l, std::chrono::seconds(2))};
if (events.CheckAudioEventSet(Event::Type::Max)) {
break;
}
for (size_t i = 0; i < buffer_events.size(); i++) {
const auto event_type = static_cast<Event::Type>(i);
if (events.CheckAudioEventSet(event_type) || timed_out) {
if (buffer_events[i]) {
buffer_events[i]();
}
}
events.SetAudioEvent(event_type, false);
}
}
}
} // namespace AudioCore } // namespace AudioCore

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -66,13 +69,6 @@ public:
void SetEvent(Event::Type type, bool signalled); void SetEvent(Event::Type type, bool signalled);
private: private:
/**
* Main thread, waiting on a manager signal and calling the registered function.
*/
void ThreadFunc();
/// Is the main thread running?
std::atomic<bool> running{};
/// Unused /// Unused
bool needs_update{}; bool needs_update{};
/// Events to be set and signalled /// Events to be set and signalled

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -40,8 +43,7 @@ void Manager::ReleaseSessionId(const size_t session_id) {
Result Manager::LinkToManager() { Result Manager::LinkToManager() {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
if (!linked_to_manager) { if (!linked_to_manager) {
AudioManager& manager{system.AudioCore().GetAudioManager()}; system.AudioCore().GetAudioManager().SetOutManager(std::bind(&Manager::BufferReleaseAndRegister, this));
manager.SetOutManager(std::bind(&Manager::BufferReleaseAndRegister, this));
linked_to_manager = true; linked_to_manager = true;
} }

View file

@ -271,7 +271,7 @@ struct System::Impl {
SystemResultStatus SetupForApplicationProcess(System& system, Frontend::EmuWindow& emu_window) { SystemResultStatus SetupForApplicationProcess(System& system, Frontend::EmuWindow& emu_window) {
host1x_core.emplace(system); host1x_core.emplace(system);
gpu_core = VideoCore::CreateGPU(emu_window, system); VideoCore::CreateGPU(gpu_core, emu_window, system);
if (!gpu_core) if (!gpu_core)
return SystemResultStatus::ErrorVideoCore; return SystemResultStatus::ErrorVideoCore;
@ -347,7 +347,7 @@ struct System::Impl {
// Register with applet manager // Register with applet manager
// All threads are started, begin main process execution, now that we're in the clear // All threads are started, begin main process execution, now that we're in the clear
applet_manager.CreateAndInsertByFrontendAppletParameters(std::move(process), params); applet_manager.CreateAndInsertByFrontendAppletParameters(std::make_unique<Service::Process>(*std::move(process)), params);
if (Settings::values.gamecard_inserted) { if (Settings::values.gamecard_inserted) {
if (Settings::values.gamecard_current_game) { if (Settings::values.gamecard_current_game) {
@ -391,10 +391,8 @@ struct System::Impl {
is_powered_on = false; is_powered_on = false;
exit_locked = false; exit_locked = false;
exit_requested = false; exit_requested = false;
if (gpu_core)
if (gpu_core != nullptr) {
gpu_core->NotifyShutdown(); gpu_core->NotifyShutdown();
}
stop_event.request_stop(); stop_event.request_stop();
core_timing.SyncPause(false); core_timing.SyncPause(false);
@ -478,6 +476,7 @@ struct System::Impl {
std::optional<Memory::CheatEngine> cheat_engine; std::optional<Memory::CheatEngine> cheat_engine;
std::optional<Tools::Freezer> memory_freezer; std::optional<Tools::Freezer> memory_freezer;
std::optional<Tools::RenderdocAPI> renderdoc_api; std::optional<Tools::RenderdocAPI> renderdoc_api;
std::optional<Tegra::GPU> gpu_core;
std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES> gpu_dirty_memory_managers; std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES> gpu_dirty_memory_managers;
std::vector<std::vector<u8>> user_channel; std::vector<std::vector<u8>> user_channel;
@ -492,7 +491,6 @@ struct System::Impl {
std::unique_ptr<FileSys::ContentProviderUnion> content_provider; std::unique_ptr<FileSys::ContentProviderUnion> content_provider;
/// AppLoader used to load the current executing application /// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader; std::unique_ptr<Loader::AppLoader> app_loader;
std::unique_ptr<Tegra::GPU> gpu_core;
std::stop_source stop_event; std::stop_source stop_event;
mutable std::mutex suspend_guard; mutable std::mutex suspend_guard;

View file

@ -25,9 +25,11 @@ CpuManager::~CpuManager() = default;
void CpuManager::Initialize() { void CpuManager::Initialize() {
num_cores = is_multicore ? Core::Hardware::NUM_CPU_CORES : 1; num_cores = is_multicore ? Core::Hardware::NUM_CPU_CORES : 1;
gpu_barrier = std::make_unique<Common::Barrier>(num_cores + 1); gpu_barrier.emplace(num_cores + 1);
for (std::size_t core = 0; core < num_cores; core++) for (std::size_t core = 0; core < num_cores; core++)
core_data[core].host_thread = std::jthread([this, core](std::stop_token token) { RunThread(token, core); }); core_data[core].host_thread = std::jthread([this, core](std::stop_token token) {
RunThread(token, core);
});
} }
void CpuManager::Shutdown() { void CpuManager::Shutdown() {
@ -63,7 +65,7 @@ void CpuManager::HandleInterrupt() {
auto& kernel = system.Kernel(); auto& kernel = system.Kernel();
auto core_index = kernel.CurrentPhysicalCoreIndex(); auto core_index = kernel.CurrentPhysicalCoreIndex();
Kernel::KInterruptManager::HandleInterrupt(kernel, static_cast<s32>(core_index)); Kernel::KInterruptManager::HandleInterrupt(kernel, s32(core_index));
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -86,22 +89,20 @@ private:
void ShutdownThread(); void ShutdownThread();
void RunThread(std::stop_token stop_token, std::size_t core); void RunThread(std::stop_token stop_token, std::size_t core);
static constexpr std::size_t max_cycle_runs = 5;
std::optional<Common::Barrier> gpu_barrier{};
struct CoreData { struct CoreData {
std::shared_ptr<Common::Fiber> host_context; std::shared_ptr<Common::Fiber> host_context;
std::jthread host_thread; std::jthread host_thread;
}; };
std::unique_ptr<Common::Barrier> gpu_barrier{};
std::array<CoreData, Core::Hardware::NUM_CPU_CORES> core_data{}; std::array<CoreData, Core::Hardware::NUM_CPU_CORES> core_data{};
System& system;
bool is_async_gpu{};
bool is_multicore{};
std::atomic<std::size_t> current_core{}; std::atomic<std::size_t> current_core{};
std::size_t idle_count{}; std::size_t idle_count{};
std::size_t num_cores{}; std::size_t num_cores{};
static constexpr std::size_t max_cycle_runs = 5; bool is_async_gpu{};
bool is_multicore{};
System& system;
}; };
} // namespace Core } // namespace Core

View file

@ -268,7 +268,7 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) {
if (Settings::values.enable_overlay && m_window_system->GetOverlayDisplayApplet() == nullptr) { if (Settings::values.enable_overlay && m_window_system->GetOverlayDisplayApplet() == nullptr) {
if (auto overlay_process = CreateProcess(m_system, static_cast<u64>(AppletProgramId::OverlayDisplay), 0, 0)) { if (auto overlay_process = CreateProcess(m_system, static_cast<u64>(AppletProgramId::OverlayDisplay), 0, 0)) {
auto overlay_applet = std::make_shared<Applet>(m_system, std::move(overlay_process), false); auto overlay_applet = std::make_shared<Applet>(m_system, std::make_unique<Service::Process>(*std::move(overlay_process)), false);
overlay_applet->program_id = static_cast<u64>(AppletProgramId::OverlayDisplay); overlay_applet->program_id = static_cast<u64>(AppletProgramId::OverlayDisplay);
overlay_applet->applet_id = AppletId::OverlayDisplay; overlay_applet->applet_id = AppletId::OverlayDisplay;
overlay_applet->type = AppletType::OverlayApplet; overlay_applet->type = AppletType::OverlayApplet;

View file

@ -1,9 +1,11 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <thread>
#include "common/thread.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/service/am/am_types.h" #include "core/hle/service/am/am_types.h"
#include "core/hle/service/am/button_poller.h" #include "core/hle/service/am/button_poller.h"
@ -34,16 +36,15 @@ ButtonPressDuration ClassifyPressDuration(std::chrono::steady_clock::time_point
} // namespace } // namespace
ButtonPoller::ButtonPoller(Core::System& system, WindowSystem& window_system) ButtonPoller::ButtonPoller(Core::System& system, WindowSystem& window_system) {
: m_window_system(window_system) {
// TODO: am reads this from the home button state in hid, which is controller-agnostic. // TODO: am reads this from the home button state in hid, which is controller-agnostic.
Core::HID::ControllerUpdateCallback engine_callback{ Core::HID::ControllerUpdateCallback engine_callback{
.on_change = .on_change = [this, &window_system](Core::HID::ControllerTriggerType type) {
[this](Core::HID::ControllerTriggerType type) { if (type == Core::HID::ControllerTriggerType::Button) {
if (type == Core::HID::ControllerTriggerType::Button) { std::unique_lock lk{m_mutex};
this->OnButtonStateChanged(); OnButtonStateChanged(window_system);
} }
}, },
.is_npad_service = true, .is_npad_service = true,
}; };
@ -52,25 +53,35 @@ ButtonPoller::ButtonPoller(Core::System& system, WindowSystem& window_system)
m_player1 = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); m_player1 = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
m_player1_key = m_player1->SetCallback(engine_callback); m_player1_key = m_player1->SetCallback(engine_callback);
m_thread = std::thread([this] { this->ThreadLoop(); }); m_thread = std::jthread([this, &window_system](std::stop_token stop_token) {
Common::SetCurrentThreadName("ButtonPoller");
while (!stop_token.stop_requested()) {
using namespace std::chrono_literals;
std::unique_lock lk{m_mutex};
m_cv.wait_for(lk, 50ms);
if (stop_token.stop_requested())
break;
OnButtonStateChanged(window_system);
std::this_thread::sleep_for(5ms);
}
});
} }
ButtonPoller::~ButtonPoller() { ButtonPoller::~ButtonPoller() {
m_handheld->DeleteCallback(m_handheld_key); m_handheld->DeleteCallback(m_handheld_key);
m_player1->DeleteCallback(m_player1_key); m_player1->DeleteCallback(m_player1_key);
m_stop = true;
m_cv.notify_all(); m_cv.notify_all();
if (m_thread.joinable()) { if (m_thread.joinable()) {
m_thread.request_stop();
m_thread.join(); m_thread.join();
} }
} }
void ButtonPoller::OnButtonStateChanged() { void ButtonPoller::OnButtonStateChanged(WindowSystem& window_system) {
std::lock_guard lk{m_mutex}; auto const home_button = m_handheld->GetHomeButtons().home.Value()
const bool home_button = || m_player1->GetHomeButtons().home.Value();
m_handheld->GetHomeButtons().home.Value() || m_player1->GetHomeButtons().home.Value(); auto const capture_button = m_handheld->GetCaptureButtons().capture.Value()
const bool capture_button = m_handheld->GetCaptureButtons().capture.Value() || || m_player1->GetCaptureButtons().capture.Value();
m_player1->GetCaptureButtons().capture.Value();
// Buttons pressed which were not previously pressed // Buttons pressed which were not previously pressed
if (home_button && !m_home_button_press_start) { if (home_button && !m_home_button_press_start) {
@ -90,7 +101,7 @@ void ButtonPoller::OnButtonStateChanged() {
if (home_button && m_home_button_press_start && !m_home_button_long_sent) { if (home_button && m_home_button_press_start && !m_home_button_long_sent) {
const auto duration = ClassifyPressDuration(*m_home_button_press_start); const auto duration = ClassifyPressDuration(*m_home_button_press_start);
if (duration != ButtonPressDuration::ShortPressing) { if (duration != ButtonPressDuration::ShortPressing) {
m_window_system.OnSystemButtonPress(SystemButtonType::HomeButtonLongPressing); window_system.OnSystemButtonPress(SystemButtonType::HomeButtonLongPressing);
m_home_button_long_sent = true; m_home_button_long_sent = true;
} }
} }
@ -98,7 +109,7 @@ void ButtonPoller::OnButtonStateChanged() {
if (capture_button && m_capture_button_press_start && !m_capture_button_long_sent) { if (capture_button && m_capture_button_press_start && !m_capture_button_long_sent) {
const auto duration = ClassifyPressDuration(*m_capture_button_press_start); const auto duration = ClassifyPressDuration(*m_capture_button_press_start);
if (duration != ButtonPressDuration::ShortPressing) { if (duration != ButtonPressDuration::ShortPressing) {
m_window_system.OnSystemButtonPress(SystemButtonType::CaptureButtonLongPressing); window_system.OnSystemButtonPress(SystemButtonType::CaptureButtonLongPressing);
m_capture_button_long_sent = true; m_capture_button_long_sent = true;
} }
} }
@ -107,9 +118,8 @@ void ButtonPoller::OnButtonStateChanged() {
if (!home_button && m_home_button_press_start) { if (!home_button && m_home_button_press_start) {
if(!m_home_button_long_sent) { if(!m_home_button_long_sent) {
const auto duration = ClassifyPressDuration(*m_home_button_press_start); const auto duration = ClassifyPressDuration(*m_home_button_press_start);
m_window_system.OnSystemButtonPress( window_system.OnSystemButtonPress(duration == ButtonPressDuration::ShortPressing
duration == ButtonPressDuration::ShortPressing ? SystemButtonType::HomeButtonShortPressing ? SystemButtonType::HomeButtonShortPressing : SystemButtonType::HomeButtonLongPressing);
: SystemButtonType::HomeButtonLongPressing);
} }
m_home_button_press_start = std::nullopt; m_home_button_press_start = std::nullopt;
m_home_button_long_sent = false; m_home_button_long_sent = false;
@ -117,9 +127,8 @@ void ButtonPoller::OnButtonStateChanged() {
if (!capture_button && m_capture_button_press_start) { if (!capture_button && m_capture_button_press_start) {
if (!m_capture_button_long_sent) { if (!m_capture_button_long_sent) {
const auto duration = ClassifyPressDuration(*m_capture_button_press_start); const auto duration = ClassifyPressDuration(*m_capture_button_press_start);
m_window_system.OnSystemButtonPress( window_system.OnSystemButtonPress(duration == ButtonPressDuration::ShortPressing
duration == ButtonPressDuration::ShortPressing ? SystemButtonType::CaptureButtonShortPressing ? SystemButtonType::CaptureButtonShortPressing : SystemButtonType::CaptureButtonLongPressing);
: SystemButtonType::CaptureButtonLongPressing);
} }
m_capture_button_press_start = std::nullopt; m_capture_button_press_start = std::nullopt;
m_capture_button_long_sent = false; m_capture_button_long_sent = false;
@ -130,16 +139,4 @@ void ButtonPoller::OnButtonStateChanged() {
// } // }
} }
void ButtonPoller::ThreadLoop() {
using namespace std::chrono_literals;
std::unique_lock lk{m_mutex};
while (!m_stop) {
m_cv.wait_for(lk, 50ms);
if (m_stop) break;
lk.unlock();
OnButtonStateChanged();
lk.lock();
}
}
} // namespace Service::AM } // namespace Service::AM

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@ -30,31 +30,23 @@ class ButtonPoller {
public: public:
explicit ButtonPoller(Core::System& system, WindowSystem& window_system); explicit ButtonPoller(Core::System& system, WindowSystem& window_system);
~ButtonPoller(); ~ButtonPoller();
void OnButtonStateChanged(WindowSystem& window_system);
private: private:
void OnButtonStateChanged(); std::mutex m_mutex;
void ThreadLoop(); std::condition_variable m_cv;
std::jthread m_thread;
private:
WindowSystem& m_window_system;
Core::HID::EmulatedController* m_handheld{};
int m_handheld_key{};
Core::HID::EmulatedController* m_player1{};
int m_player1_key{};
std::optional<std::chrono::steady_clock::time_point> m_home_button_press_start{}; std::optional<std::chrono::steady_clock::time_point> m_home_button_press_start{};
std::optional<std::chrono::steady_clock::time_point> m_capture_button_press_start{}; std::optional<std::chrono::steady_clock::time_point> m_capture_button_press_start{};
std::optional<std::chrono::steady_clock::time_point> m_power_button_press_start{}; std::optional<std::chrono::steady_clock::time_point> m_power_button_press_start{};
bool m_home_button_long_sent{}; Core::HID::EmulatedController* m_handheld{};
bool m_capture_button_long_sent{}; Core::HID::EmulatedController* m_player1{};
bool m_power_button_long_sent{}; int32_t m_handheld_key{};
int32_t m_player1_key{};
std::thread m_thread; bool m_home_button_long_sent : 1 = false;
std::atomic<bool> m_stop{false}; bool m_capture_button_long_sent : 1 = false;
std::condition_variable m_cv; bool m_power_button_long_sent : 1 = false;
std::mutex m_mutex;
}; };
} // namespace Service::AM } // namespace Service::AM

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -15,12 +18,24 @@ enum class UserDataTag : u32 {
}; };
EventObserver::EventObserver(Core::System& system, WindowSystem& window_system) EventObserver::EventObserver(Core::System& system, WindowSystem& window_system)
: m_system(system), m_context(system, "am:EventObserver"), m_window_system(window_system), : m_system(system), m_context(system, "am:EventObserver")
m_wakeup_event(m_context), m_wakeup_holder(m_wakeup_event.GetHandle()) { , m_window_system(window_system)
, m_wakeup_event(m_context)
, m_wakeup_holder(m_wakeup_event.GetHandle())
{
m_window_system.SetEventObserver(this); m_window_system.SetEventObserver(this);
m_wakeup_holder.SetUserData(static_cast<uintptr_t>(UserDataTag::WakeupEvent)); m_wakeup_holder.SetUserData(static_cast<uintptr_t>(UserDataTag::WakeupEvent));
m_wakeup_holder.LinkToMultiWait(std::addressof(m_multi_wait)); m_wakeup_holder.LinkToMultiWait(std::addressof(m_multi_wait));
m_thread = std::thread([&] { this->ThreadFunc(); }); m_thread = std::thread([this] {
Common::SetCurrentThreadName("am:EventObserver");
while (true) {
auto* signaled_holder = this->WaitSignaled();
if (!signaled_holder) {
break;
}
this->Process(signaled_holder);
}
});
} }
EventObserver::~EventObserver() { EventObserver::~EventObserver() {
@ -146,17 +161,4 @@ void EventObserver::DestroyAppletProcessHolderLocked(ProcessHolder* holder) {
delete holder; delete holder;
} }
void EventObserver::ThreadFunc() {
Common::SetCurrentThreadName("am:EventObserver");
while (true) {
auto* signaled_holder = this->WaitSignaled();
if (!signaled_holder) {
break;
}
this->Process(signaled_holder);
}
}
} // namespace Service::AM } // namespace Service::AM

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -41,9 +44,6 @@ private:
private: private:
void DestroyAppletProcessHolderLocked(ProcessHolder* holder); void DestroyAppletProcessHolderLocked(ProcessHolder* holder);
private:
void ThreadFunc();
private: private:
// System reference and context. // System reference and context.
Core::System& m_system; Core::System& m_system;

View file

@ -1,6 +1,10 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <optional>
#include "core/core.h" #include "core/core.h"
#include "core/file_sys/content_archive.h" #include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h" #include "core/file_sys/nca_metadata.h"
@ -16,7 +20,7 @@ namespace Service::AM {
namespace { namespace {
FileSys::StorageId GetStorageIdForFrontendSlot( [[nodiscard]] FileSys::StorageId GetStorageIdForFrontendSlot(
std::optional<FileSys::ContentProviderUnionSlot> slot) { std::optional<FileSys::ContentProviderUnionSlot> slot) {
if (!slot.has_value()) { if (!slot.has_value()) {
return FileSys::StorageId::None; return FileSys::StorageId::None;
@ -36,31 +40,23 @@ FileSys::StorageId GetStorageIdForFrontendSlot(
} }
} }
std::unique_ptr<Process> CreateProcessImpl(std::unique_ptr<Loader::AppLoader>& out_loader, [[nodiscard]] inline std::optional<Process> CreateProcessImpl(std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index) {
Loader::ResultStatus& out_load_result,
Core::System& system, FileSys::VirtualFile file,
u64 program_id, u64 program_index) {
// Get the appropriate loader to parse this NCA. // Get the appropriate loader to parse this NCA.
out_loader = Loader::GetLoader(system, file, program_id, program_index); out_loader = Loader::GetLoader(system, file, program_id, program_index);
// Ensure we have a loader which can parse the NCA. // Ensure we have a loader which can parse the NCA.
if (!out_loader) { if (out_loader) {
return nullptr; // Try to load the process.
auto process = std::make_optional<Process>(system);
if (process->Initialize(*out_loader, out_load_result)) {
return process;
}
} }
return std::nullopt;
// Try to load the process.
auto process = std::make_unique<Process>(system);
if (process->Initialize(*out_loader, out_load_result)) {
return process;
}
return nullptr;
} }
} // Anonymous namespace } // Anonymous namespace
std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id, std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation) {
u8 minimum_key_generation, u8 maximum_key_generation) {
// Attempt to load program NCA. // Attempt to load program NCA.
FileSys::VirtualFile nca_raw{}; FileSys::VirtualFile nca_raw{};
@ -70,7 +66,7 @@ std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id,
// Ensure we retrieved a program NCA. // Ensure we retrieved a program NCA.
if (!nca_raw) { if (!nca_raw) {
return nullptr; return std::nullopt;
} }
// Ensure we have a suitable version. // Ensure we have a suitable version.
@ -79,9 +75,8 @@ std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id,
if (nca.GetStatus() == Loader::ResultStatus::Success && if (nca.GetStatus() == Loader::ResultStatus::Success &&
(nca.GetKeyGeneration() < minimum_key_generation || (nca.GetKeyGeneration() < minimum_key_generation ||
nca.GetKeyGeneration() > maximum_key_generation)) { nca.GetKeyGeneration() > maximum_key_generation)) {
LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id, LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id, nca.GetKeyGeneration());
nca.GetKeyGeneration()); return std::nullopt;
return nullptr;
} }
} }
@ -90,42 +85,32 @@ std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id,
return CreateProcessImpl(loader, status, system, nca_raw, program_id, 0); return CreateProcessImpl(loader, status, system, nca_raw, program_id, 0);
} }
std::unique_ptr<Process> CreateApplicationProcess(std::vector<u8>& out_control, std::optional<Process> CreateApplicationProcess(std::vector<u8>& out_control, std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index) {
std::unique_ptr<Loader::AppLoader>& out_loader, if (auto process = CreateProcessImpl(out_loader, out_load_result, system, file, program_id, program_index); process) {
Loader::ResultStatus& out_load_result, FileSys::NACP nacp;
Core::System& system, FileSys::VirtualFile file, if (out_loader->ReadControlData(nacp) == Loader::ResultStatus::Success) {
u64 program_id, u64 program_index) { out_control = nacp.GetRawBytes();
auto process = } else {
CreateProcessImpl(out_loader, out_load_result, system, file, program_id, program_index); out_control.resize(sizeof(FileSys::RawNACP));
if (!process) { std::fill(out_control.begin(), out_control.end(), (u8) 0);
return nullptr; }
auto& storage = system.GetContentProviderUnion();
Service::Glue::ApplicationLaunchProperty launch{};
launch.title_id = process->GetProgramId();
FileSys::PatchManager pm{launch.title_id, system.GetFileSystemController(), storage};
launch.version = pm.GetGameVersion().value_or(0);
// TODO(DarkLordZach): When FSController/Game Card Support is added, if
// current_process_game_card use correct StorageId
launch.base_game_storage_id = GetStorageIdForFrontendSlot(storage.GetSlotForEntry(launch.title_id, FileSys::ContentRecordType::Program));
launch.update_storage_id = GetStorageIdForFrontendSlot(storage.GetSlotForEntry(FileSys::GetUpdateTitleID(launch.title_id), FileSys::ContentRecordType::Program));
system.GetARPManager().Register(launch.title_id, launch, out_control);
return process;
} }
return std::nullopt;
FileSys::NACP nacp;
if (out_loader->ReadControlData(nacp) == Loader::ResultStatus::Success) {
out_control = nacp.GetRawBytes();
} else {
out_control.resize(sizeof(FileSys::RawNACP));
std::fill(out_control.begin(), out_control.end(), (u8) 0);
}
auto& storage = system.GetContentProviderUnion();
Service::Glue::ApplicationLaunchProperty launch{};
launch.title_id = process->GetProgramId();
FileSys::PatchManager pm{launch.title_id, system.GetFileSystemController(), storage};
launch.version = pm.GetGameVersion().value_or(0);
// TODO(DarkLordZach): When FSController/Game Card Support is added, if
// current_process_game_card use correct StorageId
launch.base_game_storage_id = GetStorageIdForFrontendSlot(
storage.GetSlotForEntry(launch.title_id, FileSys::ContentRecordType::Program));
launch.update_storage_id = GetStorageIdForFrontendSlot(storage.GetSlotForEntry(
FileSys::GetUpdateTitleID(launch.title_id), FileSys::ContentRecordType::Program));
system.GetARPManager().Register(launch.title_id, launch, out_control);
return process;
} }
} // namespace Service::AM } // namespace Service::AM

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -24,12 +27,7 @@ class Process;
namespace Service::AM { namespace Service::AM {
std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id, std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation);
u8 minimum_key_generation, u8 maximum_key_generation); std::optional<Process> CreateApplicationProcess(std::vector<u8>& out_control, std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index);
std::unique_ptr<Process> CreateApplicationProcess(std::vector<u8>& out_control,
std::unique_ptr<Loader::AppLoader>& out_loader,
Loader::ResultStatus& out_load_result,
Core::System& system, FileSys::VirtualFile file,
u64 program_id, u64 program_index);
} // namespace Service::AM } // namespace Service::AM

View file

@ -21,8 +21,7 @@ namespace Service::AM {
namespace { namespace {
Result CreateGuestApplication(SharedPointer<IApplicationAccessor>* out_application_accessor, Result CreateGuestApplication(SharedPointer<IApplicationAccessor>* out_application_accessor, Core::System& system, WindowSystem& window_system, u64 program_id) {
Core::System& system, WindowSystem& window_system, u64 program_id) {
FileSys::VirtualFile nca_raw{}; FileSys::VirtualFile nca_raw{};
// Get the program NCA from storage. // Get the program NCA from storage.
@ -35,11 +34,10 @@ Result CreateGuestApplication(SharedPointer<IApplicationAccessor>* out_applicati
std::vector<u8> control; std::vector<u8> control;
std::unique_ptr<Loader::AppLoader> loader; std::unique_ptr<Loader::AppLoader> loader;
Loader::ResultStatus result; Loader::ResultStatus result;
auto process = auto process = CreateApplicationProcess(control, loader, result, system, nca_raw, program_id, 0);
CreateApplicationProcess(control, loader, result, system, nca_raw, program_id, 0); R_UNLESS(process != std::nullopt, ResultUnknown);
R_UNLESS(process != nullptr, ResultUnknown);
const auto applet = std::make_shared<Applet>(system, std::move(process), true); const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), true);
applet->program_id = program_id; applet->program_id = program_id;
applet->applet_id = AppletId::Application; applet->applet_id = AppletId::Application;
applet->type = AppletType::Application; applet->type = AppletType::Application;
@ -47,8 +45,7 @@ Result CreateGuestApplication(SharedPointer<IApplicationAccessor>* out_applicati
window_system.TrackApplet(applet, true); window_system.TrackApplet(applet, true);
*out_application_accessor = *out_application_accessor = std::make_shared<IApplicationAccessor>(system, applet, window_system);
std::make_shared<IApplicationAccessor>(system, applet, window_system);
R_SUCCEED(); R_SUCCEED();
} }
@ -90,12 +87,10 @@ Result IApplicationCreator::CreateSystemApplication(
std::vector<u8> control; std::vector<u8> control;
std::unique_ptr<Loader::AppLoader> loader; std::unique_ptr<Loader::AppLoader> loader;
auto process = CreateProcess(system, application_id, 1, 22);
R_UNLESS(process != std::nullopt, ResultUnknown);
auto process = const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), true);
CreateProcess(system, application_id, 1, 22);
R_UNLESS(process != nullptr, ResultUnknown);
const auto applet = std::make_shared<Applet>(system, std::move(process), true);
applet->program_id = application_id; applet->program_id = application_id;
applet->applet_id = AppletId::Starter; applet->applet_id = AppletId::Starter;
applet->type = AppletType::LibraryApplet; applet->type = AppletType::LibraryApplet;
@ -103,8 +98,7 @@ Result IApplicationCreator::CreateSystemApplication(
m_window_system.TrackApplet(applet, true); m_window_system.TrackApplet(applet, true);
*out_application_accessor = *out_application_accessor = std::make_shared<IApplicationAccessor>(system, applet, m_window_system);
std::make_shared<IApplicationAccessor>(system, applet, m_window_system);
Core::LaunchTimestampCache::SaveLaunchTimestamp(application_id); Core::LaunchTimestampCache::SaveLaunchTimestamp(application_id);
R_SUCCEED(); R_SUCCEED();
} }

View file

@ -122,26 +122,23 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
}; };
auto process = CreateProcess(system, program_id, Firmware1400, Firmware2200); auto process = CreateProcess(system, program_id, Firmware1400, Firmware2200);
if (!process) { if (process) {
// Couldn't initialize the guest process const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), false);
return {}; applet->program_id = program_id;
applet->applet_id = applet_id;
applet->type = AppletType::LibraryApplet;
applet->library_applet_mode = mode;
applet->window_visible = mode != LibraryAppletMode::AllForegroundInitiallyHidden;
auto broker = std::make_shared<AppletDataBroker>(system);
applet->caller_applet = caller_applet;
applet->caller_applet_broker = broker;
caller_applet->child_applets.push_back(applet);
window_system.TrackApplet(applet, false);
return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
} }
// Couldn't initialize the guest process
const auto applet = std::make_shared<Applet>(system, std::move(process), false); return {};
applet->program_id = program_id;
applet->applet_id = applet_id;
applet->type = AppletType::LibraryApplet;
applet->library_applet_mode = mode;
applet->window_visible = mode != LibraryAppletMode::AllForegroundInitiallyHidden;
auto broker = std::make_shared<AppletDataBroker>(system);
applet->caller_applet = caller_applet;
applet->caller_applet_broker = broker;
caller_applet->child_applets.push_back(applet);
window_system.TrackApplet(applet, false);
return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
} }
std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(Core::System& system, std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(Core::System& system,

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@ -14,7 +14,9 @@ namespace Service::Audio {
using namespace AudioCore::AudioOut; using namespace AudioCore::AudioOut;
IAudioOutManager::IAudioOutManager(Core::System& system_) IAudioOutManager::IAudioOutManager(Core::System& system_)
: ServiceFramework{system_, "audout:u"}, impl{std::make_unique<Manager>(system_)} { : ServiceFramework{system_, "audout:u"}
, impl(system_)
{
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, D<&IAudioOutManager::ListAudioOuts>, "ListAudioOuts"}, {0, D<&IAudioOutManager::ListAudioOuts>, "ListAudioOuts"},

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@ -43,7 +43,7 @@ private:
AudioCore::AudioOut::AudioOutParameter parameter, AudioCore::AudioOut::AudioOutParameter parameter,
InCopyHandle<Kernel::KProcess> process_handle, ClientAppletResourceUserId aruid); InCopyHandle<Kernel::KProcess> process_handle, ClientAppletResourceUserId aruid);
std::unique_ptr<AudioCore::AudioOut::Manager> impl; std::optional<AudioCore::AudioOut::Manager> impl;
}; };
} // namespace Service::Audio } // namespace Service::Audio

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -15,7 +18,9 @@ namespace Service::Audio {
using namespace AudioCore::Renderer; using namespace AudioCore::Renderer;
IAudioRendererManager::IAudioRendererManager(Core::System& system_) IAudioRendererManager::IAudioRendererManager(Core::System& system_)
: ServiceFramework{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} { : ServiceFramework{system_, "audren:u"}
, impl(system_)
{
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, D<&IAudioRendererManager::OpenAudioRenderer>, "OpenAudioRenderer"}, {0, D<&IAudioRendererManager::OpenAudioRenderer>, "OpenAudioRenderer"},

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -30,7 +33,7 @@ private:
Result GetAudioDeviceServiceWithRevisionInfo(Out<SharedPointer<IAudioDevice>> out_audio_device, Result GetAudioDeviceServiceWithRevisionInfo(Out<SharedPointer<IAudioDevice>> out_audio_device,
u32 revision, ClientAppletResourceUserId aruid); u32 revision, ClientAppletResourceUserId aruid);
std::unique_ptr<AudioCore::Renderer::Manager> impl; std::optional<AudioCore::Renderer::Manager> impl;
u32 num_audio_devices{0}; u32 num_audio_devices{0};
}; };

View file

@ -138,8 +138,7 @@ NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) {
static_cast<u32>((vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits)}; static_cast<u32>((vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits)};
vm.big_page_allocator.emplace(start_big_pages, end_big_pages); vm.big_page_allocator.emplace(start_big_pages, end_big_pages);
gmmu = std::make_shared<Tegra::MemoryManager>(system, max_big_page_bits, vm.va_range_split, gmmu = std::make_unique<Tegra::MemoryManager>(system, max_big_page_bits, vm.va_range_split, vm.big_page_size_bits, VM::PAGE_SIZE_BITS);
vm.big_page_size_bits, VM::PAGE_SIZE_BITS);
system.GPU().InitAddressSpace(*gmmu); system.GPU().InitAddressSpace(*gmmu);
vm.initialised = true; vm.initialised = true;
@ -416,7 +415,7 @@ NvResult nvhost_as_gpu::BindChannel(IoctlBindChannel& params) {
LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd); LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd);
auto gpu_channel_device = module.GetDevice<nvhost_gpu>(params.fd); auto gpu_channel_device = module.GetDevice<nvhost_gpu>(params.fd);
gpu_channel_device->channel_state->memory_manager = gmmu; gpu_channel_device->channel_state->memory_manager = gmmu.get();
return NvResult::Success; return NvResult::Success;
} }

View file

@ -219,7 +219,7 @@ private:
bool initialised{}; bool initialised{};
} vm; } vm;
std::shared_ptr<Tegra::MemoryManager> gmmu; std::unique_ptr<Tegra::MemoryManager> gmmu;
}; };
} // namespace Service::Nvidia::Devices } // namespace Service::Nvidia::Devices

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -10,14 +13,6 @@
namespace Service { namespace Service {
Process::Process(Core::System& system)
: m_system(system), m_process(), m_main_thread_priority(), m_main_thread_stack_size(),
m_process_started() {}
Process::~Process() {
this->Finalize();
}
bool Process::Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_load_result) { bool Process::Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_load_result) {
// First, ensure we are not holding another process. // First, ensure we are not holding another process.
this->Finalize(); this->Finalize();

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -22,8 +25,8 @@ namespace Service {
class Process { class Process {
public: public:
explicit Process(Core::System& system); inline explicit Process(Core::System& system) noexcept : m_system(system) {}
~Process(); inline ~Process() { this->Finalize(); }
bool Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_load_result); bool Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_load_result);
void Finalize(); void Finalize();
@ -50,8 +53,8 @@ public:
private: private:
Core::System& m_system; Core::System& m_system;
Kernel::KProcess* m_process{}; Kernel::KProcess* m_process{};
s32 m_main_thread_priority{};
u64 m_main_thread_stack_size{}; u64 m_main_thread_stack_size{};
s32 m_main_thread_priority{};
bool m_process_started{}; bool m_process_started{};
}; };

View file

@ -13,6 +13,7 @@
#include "common/param_package.h" #include "common/param_package.h"
#include "common/random.h" #include "common/random.h"
#include "common/settings.h" #include "common/settings.h"
#include "common/thread.h"
#include "input_common/drivers/udp_client.h" #include "input_common/drivers/udp_client.h"
#include "input_common/helpers/udp_protocol.h" #include "input_common/helpers/udp_protocol.h"
@ -131,6 +132,7 @@ private:
}; };
static void SocketLoop(Socket* socket) { static void SocketLoop(Socket* socket) {
Common::SetCurrentThreadName("cemuhookWorker");
socket->StartReceive(); socket->StartReceive();
socket->StartSend(Socket::clock::now()); socket->StartSend(Socket::clock::now());
socket->Loop(); socket->Loop();
@ -326,9 +328,11 @@ void UDPClient::OnPadData(Response::PadData data, std::size_t client) {
} }
void UDPClient::StartCommunication(std::size_t client, const std::string& host, u16 port) { void UDPClient::StartCommunication(std::size_t client, const std::string& host, u16 port) {
SocketCallback callback{[this](Response::Version version) { OnVersion(version); }, SocketCallback callback{
[this](Response::PortInfo info) { OnPortInfo(info); }, [this](Response::Version version) { OnVersion(version); },
[this, client](Response::PadData data) { OnPadData(data, client); }}; [this](Response::PortInfo info) { OnPortInfo(info); },
[this, client](Response::PadData data) { OnPadData(data, client); }
};
LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port); LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port);
clients[client].uuid = GetHostUUID(host); clients[client].uuid = GetHostUUID(host);
clients[client].host = host; clients[client].host = host;
@ -566,9 +570,7 @@ bool UDPClient::IsStickInverted(const Common::ParamPackage& params) {
return true; return true;
} }
void TestCommunication(const std::string& host, u16 port, void TestCommunication(const std::string& host, u16 port, const std::function<void()>& success_callback, const std::function<void()>& failure_callback) {
const std::function<void()>& success_callback,
const std::function<void()>& failure_callback) {
std::thread([=] { std::thread([=] {
Common::Event success_event; Common::Event success_event;
SocketCallback callback{ SocketCallback callback{
@ -601,40 +603,38 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
u16 max_y{}; u16 max_y{};
Status current_status{Status::Initialized}; Status current_status{Status::Initialized};
SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {}, SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {}, [&](Response::PadData data) {
[&](Response::PadData data) { constexpr u16 CALIBRATION_THRESHOLD = 100;
constexpr u16 CALIBRATION_THRESHOLD = 100;
if (current_status == Status::Initialized) { if (current_status == Status::Initialized) {
// Receiving data means the communication is ready now // Receiving data means the communication is ready now
current_status = Status::Ready; current_status = Status::Ready;
status_callback(current_status); status_callback(current_status);
} }
if (data.touch[0].is_active == 0) { if (data.touch[0].is_active == 0) {
return; return;
} }
LOG_DEBUG(Input, "Current touch: {} {}", data.touch[0].x, LOG_DEBUG(Input, "Current touch: {} {}", data.touch[0].x, data.touch[0].y);
data.touch[0].y); min_x = (std::min)(min_x, u16(data.touch[0].x));
min_x = (std::min)(min_x, static_cast<u16>(data.touch[0].x)); min_y = (std::min)(min_y, u16(data.touch[0].y));
min_y = (std::min)(min_y, static_cast<u16>(data.touch[0].y)); if (current_status == Status::Ready) {
if (current_status == Status::Ready) { // First touch - min data (min_x/min_y)
// First touch - min data (min_x/min_y) current_status = Status::Stage1Completed;
current_status = Status::Stage1Completed; status_callback(current_status);
status_callback(current_status); }
} if (data.touch[0].x - min_x > CALIBRATION_THRESHOLD &&
if (data.touch[0].x - min_x > CALIBRATION_THRESHOLD && data.touch[0].y - min_y > CALIBRATION_THRESHOLD) {
data.touch[0].y - min_y > CALIBRATION_THRESHOLD) { // Set the current position as max value and finishes
// Set the current position as max value and finishes // configuration
// configuration max_x = data.touch[0].x;
max_x = data.touch[0].x; max_y = data.touch[0].y;
max_y = data.touch[0].y; current_status = Status::Completed;
current_status = Status::Completed; data_callback(min_x, min_y, max_x, max_y);
data_callback(min_x, min_y, max_x, max_y); status_callback(current_status);
status_callback(current_status);
complete_event.Set(); complete_event.Set();
} }
}}; }};
Socket socket{host, port, std::move(callback)}; Socket socket{host, port, std::move(callback)};
std::thread worker_thread{SocketLoop, &socket}; std::thread worker_thread{SocketLoop, &socket};
complete_event.Wait(); complete_event.Wait();

View file

@ -334,7 +334,7 @@ if (YUZU_USE_EXTERNAL_FFMPEG)
add_dependencies(video_core ffmpeg-build) add_dependencies(video_core ffmpeg-build)
endif() endif()
target_include_directories(video_core PRIVATE ${FFmpeg_INCLUDE_DIR}) target_include_directories(video_core PUBLIC ${FFmpeg_INCLUDE_DIR})
target_link_libraries(video_core PRIVATE ${FFmpeg_LIBRARIES}) target_link_libraries(video_core PRIVATE ${FFmpeg_LIBRARIES})
target_link_options(video_core PRIVATE ${FFmpeg_LDFLAGS}) target_link_options(video_core PRIVATE ${FFmpeg_LDFLAGS})

View file

@ -20,8 +20,7 @@
namespace Tegra { namespace Tegra {
CDmaPusher::CDmaPusher(Host1x::Host1x& host1x_, s32 id) CDmaPusher::CDmaPusher(Host1x::Host1x& host1x_, s32 id)
: host_processor(host1x_) : host1x{host1x_}
, host1x{host1x_}
, current_class{ChClassId(id)} , current_class{ChClassId(id)}
{ {
thread = std::jthread([this](std::stop_token stop_token) { thread = std::jthread([this](std::stop_token stop_token) {
@ -99,7 +98,7 @@ void CDmaPusher::ExecuteCommand(u32 method, u32 arg) {
switch (current_class) { switch (current_class) {
case ChClassId::Control: case ChClassId::Control:
LOG_TRACE(Service_NVDRV, "Class {} method {:#X} arg 0x{:X}", u32(current_class), method, arg); LOG_TRACE(Service_NVDRV, "Class {} method {:#X} arg 0x{:X}", u32(current_class), method, arg);
host_processor.ProcessMethod(Host1x::Control::Method(method), arg); host_processor.ProcessMethod(host1x, Host1x::Control::Method(method), arg);
break; break;
default: default:
thi_regs.reg_array[method] = arg; thi_regs.reg_array[method] = arg;

View file

@ -17,28 +17,36 @@
namespace Tegra::Control { namespace Tegra::Control {
ChannelState::ChannelState(s32 bind_id_) : bind_id{bind_id_}, initialized{} {} ChannelState::Payload::Payload(Core::System& system, MemoryManager& memory_manager, ChannelState& channel_state)
: maxwell_3d(memory_manager)
, fermi_2d(memory_manager)
, kepler_compute(memory_manager)
, maxwell_dma(memory_manager)
, kepler_memory(memory_manager)
, nv01_timer(memory_manager)
, dma_pusher(system, memory_manager, channel_state)
{}
void ChannelState::Init(Core::System& system, GPU& gpu, u64 program_id_) { ChannelState::ChannelState(s32 bind_id_)
: bind_id{bind_id_}
{}
void ChannelState::Init(Core::System& system, u64 program_id_) {
ASSERT(memory_manager); ASSERT(memory_manager);
program_id = program_id_; program_id = program_id_;
dma_pusher.emplace(system, gpu, *memory_manager, *this); payload.emplace(system, *memory_manager, *this);
maxwell_3d.emplace(system, *memory_manager);
fermi_2d.emplace(*memory_manager);
kepler_compute.emplace(system, *memory_manager);
maxwell_dma.emplace(system, *memory_manager);
kepler_memory.emplace(system, *memory_manager);
initialized = true; initialized = true;
} }
void ChannelState::BindRasterizer(VideoCore::RasterizerInterface* rasterizer) { void ChannelState::BindRasterizer(VideoCore::RasterizerInterface* rasterizer) {
dma_pusher->BindRasterizer(rasterizer); payload->dma_pusher.BindRasterizer(rasterizer);
memory_manager->BindRasterizer(rasterizer); memory_manager->BindRasterizer(rasterizer);
maxwell_3d->BindRasterizer(rasterizer); payload->maxwell_3d.BindRasterizer(rasterizer);
fermi_2d->BindRasterizer(rasterizer); payload->fermi_2d.BindRasterizer(rasterizer);
kepler_memory->BindRasterizer(rasterizer); payload->kepler_memory.BindRasterizer(rasterizer);
kepler_compute->BindRasterizer(rasterizer); payload->kepler_compute.BindRasterizer(rasterizer);
maxwell_dma->BindRasterizer(rasterizer); payload->maxwell_dma.BindRasterizer(rasterizer);
//payload->nv01_timer.BindRasterizer(rasterizer);
} }
} // namespace Tegra::Control } // namespace Tegra::Control

View file

@ -14,6 +14,7 @@
#include "video_core/engines/kepler_compute.h" #include "video_core/engines/kepler_compute.h"
#include "video_core/engines/maxwell_3d.h" #include "video_core/engines/maxwell_3d.h"
#include "video_core/engines/maxwell_dma.h" #include "video_core/engines/maxwell_dma.h"
#include "video_core/engines/nv01_timer.h"
#include "video_core/dma_pusher.h" #include "video_core/dma_pusher.h"
namespace Core { namespace Core {
@ -34,28 +35,33 @@ namespace Control {
struct ChannelState { struct ChannelState {
explicit ChannelState(s32 bind_id); explicit ChannelState(s32 bind_id);
void Init(Core::System& system, GPU& gpu, u64 program_id); void Init(Core::System& system, u64 program_id);
void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
/// 3D engine struct Payload {
std::optional<Engines::Maxwell3D> maxwell_3d; explicit Payload(Core::System& system, MemoryManager& memory_manager, ChannelState& channel_state);
/// 2D engine
std::optional<Engines::Fermi2D> fermi_2d; /// 3D engine
/// Compute engine Engines::Maxwell3D maxwell_3d;
std::optional<Engines::KeplerCompute> kepler_compute; /// 2D engine
/// DMA engine Engines::Fermi2D fermi_2d;
std::optional<Engines::MaxwellDMA> maxwell_dma; /// Compute engine
/// Inline memory engine Engines::KeplerCompute kepler_compute;
std::optional<Engines::KeplerMemory> kepler_memory; /// DMA engine
/// NV01 Timer Engines::MaxwellDMA maxwell_dma;
std::optional<Engines::KeplerMemory> nv01_timer; /// Inline memory engine
std::optional<DmaPusher> dma_pusher; Engines::KeplerMemory kepler_memory;
std::shared_ptr<MemoryManager> memory_manager; /// NV01 Timer
Engines::Nv01Timer nv01_timer;
DmaPusher dma_pusher;
};
std::optional<Payload> payload;
MemoryManager* memory_manager = nullptr;
s32 bind_id = -1; s32 bind_id = -1;
u64 program_id = 0; u64 program_id = 0;
bool initialized{}; bool initialized = false;
}; };
} // namespace Control } // namespace Control

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project // SPDX-FileCopyrightText: 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
@ -6,8 +9,11 @@
namespace VideoCommon { namespace VideoCommon {
ChannelInfo::ChannelInfo(Tegra::Control::ChannelState& channel_state) ChannelInfo::ChannelInfo(Tegra::Control::ChannelState& channel_state)
: maxwell3d{*channel_state.maxwell_3d}, kepler_compute{*channel_state.kepler_compute}, : maxwell3d{channel_state.payload->maxwell_3d}
gpu_memory{*channel_state.memory_manager}, program_id{channel_state.program_id} {} , kepler_compute{channel_state.payload->kepler_compute}
, gpu_memory{*channel_state.memory_manager}
, program_id{channel_state.program_id}
{}
template class VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo>; template class VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo>;

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project // SPDX-FileCopyrightText: 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
@ -41,7 +44,7 @@ void ChannelSetupCaches<P>::CreateChannel(struct Tegra::Control::ChannelState& c
AddressSpaceRef new_gpu_mem_ref{ AddressSpaceRef new_gpu_mem_ref{
.ref_count = 1, .ref_count = 1,
.storage_id = address_spaces.size(), .storage_id = address_spaces.size(),
.gpu_memory = channel.memory_manager.get(), .gpu_memory = channel.memory_manager,
}; };
address_spaces.emplace(channel.memory_manager->GetID(), new_gpu_mem_ref); address_spaces.emplace(channel.memory_manager->GetID(), new_gpu_mem_ref);
OnGPUASRegister(channel.memory_manager->GetID()); OnGPUASRegister(channel.memory_manager->GetID());

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project // SPDX-FileCopyrightText: 2021 yuzu Emulator Project
@ -12,11 +12,8 @@
#include "video_core/gpu.h" #include "video_core/gpu.h"
namespace Tegra::Control { namespace Tegra::Control {
Scheduler::Scheduler(GPU& gpu_) : gpu{gpu_} {}
Scheduler::~Scheduler() = default; void Scheduler::Push(GPU& gpu, s32 channel, CommandList&& entries) {
void Scheduler::Push(s32 channel, CommandList&& entries) {
std::shared_ptr<ChannelState> channel_state; std::shared_ptr<ChannelState> channel_state;
{ {
std::unique_lock lk(scheduling_guard); std::unique_lock lk(scheduling_guard);
@ -27,8 +24,8 @@ void Scheduler::Push(s32 channel, CommandList&& entries) {
} }
// Process commands outside the lock to reduce contention. // Process commands outside the lock to reduce contention.
// Multiple channels can prepare their commands in parallel. // Multiple channels can prepare their commands in parallel.
channel_state->dma_pusher->Push(std::move(entries)); channel_state->payload->dma_pusher.Push(std::move(entries));
channel_state->dma_pusher->DispatchCalls(); channel_state->payload->dma_pusher.DispatchCalls();
} }
void Scheduler::DeclareChannel(std::shared_ptr<ChannelState> new_channel) { void Scheduler::DeclareChannel(std::shared_ptr<ChannelState> new_channel) {

View file

@ -22,17 +22,13 @@ struct ChannelState;
class Scheduler { class Scheduler {
public: public:
explicit Scheduler(GPU& gpu_); void Push(GPU& gpu, s32 channel, CommandList&& entries);
~Scheduler();
void Push(s32 channel, CommandList&& entries);
void DeclareChannel(std::shared_ptr<ChannelState> new_channel); void DeclareChannel(std::shared_ptr<ChannelState> new_channel);
private: private:
ankerl::unordered_dense::map<s32, std::shared_ptr<ChannelState>> channels; ankerl::unordered_dense::map<s32, std::shared_ptr<ChannelState>> channels;
std::mutex scheduling_guard; std::mutex scheduling_guard;
GPU& gpu;
}; };
} // namespace Control } // namespace Control

View file

@ -12,37 +12,32 @@
#include "video_core/guest_memory.h" #include "video_core/guest_memory.h"
#include "video_core/memory_manager.h" #include "video_core/memory_manager.h"
#include "video_core/rasterizer_interface.h" #include "video_core/rasterizer_interface.h"
#include "video_core/texture_cache/util.h"
#ifdef _MSC_VER
#include <intrin.h>
#endif
namespace Tegra { namespace Tegra {
constexpr u32 MacroRegistersStart = 0xE00; constexpr u32 MacroRegistersStart = 0xE00;
[[maybe_unused]] constexpr u32 ComputeInline = 0x6D; [[maybe_unused]] constexpr u32 ComputeInline = 0x6D;
DmaPusher::DmaPusher(Core::System& system_, GPU& gpu_, MemoryManager& memory_manager_, DmaPusher::DmaPusher(Core::System& system_, MemoryManager& memory_manager_, Control::ChannelState& channel_state_)
Control::ChannelState& channel_state_) : system{system_}
: gpu{gpu_}, system{system_}, memory_manager{memory_manager_}, puller{gpu_, memory_manager_, , memory_manager{memory_manager_}
*this, channel_state_}, signal_sync{false}, synced{true} {} , channel_state{channel_state_}
, signal_sync{false}
, synced{false}
{}
DmaPusher::~DmaPusher() = default; DmaPusher::~DmaPusher() = default;
void DmaPusher::DispatchCalls() { void DmaPusher::DispatchCalls() {
dma_pushbuffer_subindex = 0; dma_pushbuffer_subindex = 0;
dma_state.is_last_call = true; dma_state.is_last_call = true;
while (system.IsPoweredOn()) { while (system.IsPoweredOn()) {
if (!Step()) { if (!Step()) {
break; break;
} }
} }
gpu.FlushCommands(); system.GPU().FlushCommands();
gpu.OnCommandListEnd(); system.GPU().OnCommandListEnd();
} }
bool DmaPusher::Step() { bool DmaPusher::Step() {
@ -171,9 +166,9 @@ void DmaPusher::SetState(const CommandHeader& command_header) {
dma_state.method_count = command_header.method_count; dma_state.method_count = command_header.method_count;
} }
void DmaPusher::CallMethod(u32 argument) const { void DmaPusher::CallMethod(u32 argument) {
if (dma_state.method < non_puller_methods) { if (dma_state.method < non_puller_methods) {
puller.CallPullerMethod(Engines::Puller::MethodCall{ puller.CallPullerMethod(*this, Engines::Puller::MethodCall{
dma_state.method, dma_state.method,
argument, argument,
dma_state.subchannel, dma_state.subchannel,
@ -181,30 +176,29 @@ void DmaPusher::CallMethod(u32 argument) const {
}); });
} else { } else {
auto subchannel = subchannels[dma_state.subchannel]; auto subchannel = subchannels[dma_state.subchannel];
if (subchannel->execution_mask[dma_state.method]) { if (!subchannel->execution_mask[dma_state.method]) {
subchannel->ConsumeSink();
subchannel->current_dma_segment = dma_state.dma_get + dma_state.dma_word_offset;
subchannel->CallMethod(dma_state.method, argument, dma_state.is_last_call);
} else {
subchannel->method_sink.emplace_back(dma_state.method, argument); subchannel->method_sink.emplace_back(dma_state.method, argument);
} else {
subchannel->ConsumeSink(system);
subchannel->current_dma_segment = dma_state.dma_get + dma_state.dma_word_offset;
subchannel->CallMethod(system, dma_state.method, argument, dma_state.is_last_call);
} }
} }
} }
void DmaPusher::CallMultiMethod(const u32* base_start, u32 num_methods) const { void DmaPusher::CallMultiMethod(const u32* base_start, u32 num_methods) {
if (dma_state.method < non_puller_methods) { if (dma_state.method < non_puller_methods) {
puller.CallMultiMethod(dma_state.method, dma_state.subchannel, base_start, num_methods, dma_state.method_count); puller.CallMultiMethod(*this, dma_state.method, dma_state.subchannel, base_start, num_methods, dma_state.method_count);
} else { } else {
auto subchannel = subchannels[dma_state.subchannel]; auto subchannel = subchannels[dma_state.subchannel];
subchannel->ConsumeSink(); subchannel->ConsumeSink(system);
subchannel->current_dma_segment = dma_state.dma_get + dma_state.dma_word_offset; subchannel->current_dma_segment = dma_state.dma_get + dma_state.dma_word_offset;
subchannel->CallMultiMethod(dma_state.method, base_start, num_methods, dma_state.method_count); subchannel->CallMultiMethod(system, dma_state.method, base_start, num_methods, dma_state.method_count);
} }
} }
void DmaPusher::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) { void DmaPusher::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
rasterizer = rasterizer_; rasterizer = rasterizer_;
puller.BindRasterizer(rasterizer);
} }
} // namespace Tegra } // namespace Tegra

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@ -109,25 +109,21 @@ inline CommandHeader BuildCommandHeader(BufferMethods method, u32 arg_count, Sub
struct CommandList final { struct CommandList final {
CommandList() = default; CommandList() = default;
explicit CommandList(std::size_t size) : command_lists(size) {} explicit CommandList(std::size_t size) : command_lists(size) {}
explicit CommandList( explicit CommandList(boost::container::small_vector<CommandHeader, 512>&& prefetch_command_list_)
boost::container::small_vector<CommandHeader, 512>&& prefetch_command_list_)
: prefetch_command_list{std::move(prefetch_command_list_)} {} : prefetch_command_list{std::move(prefetch_command_list_)} {}
boost::container::small_vector<CommandListHeader, 512> command_lists; boost::container::small_vector<CommandListHeader, 512> command_lists;
boost::container::small_vector<CommandHeader, 512> prefetch_command_list; boost::container::small_vector<CommandHeader, 512> prefetch_command_list;
}; };
/** /// @brief The DmaPusher class implements DMA submission to FIFOs, providing an area of memory that the
* The DmaPusher class implements DMA submission to FIFOs, providing an area of memory that the /// emulated app fills with commands and tells PFIFO to process. The pushbuffers are then assembled
* emulated app fills with commands and tells PFIFO to process. The pushbuffers are then assembled /// into a "command stream" consisting of 32-bit words that make up "commands".
* into a "command stream" consisting of 32-bit words that make up "commands". /// See https://envytools.readthedocs.io/en/latest/hw/fifo/dma-pusher.html#fifo-dma-pusher for
* See https://envytools.readthedocs.io/en/latest/hw/fifo/dma-pusher.html#fifo-dma-pusher for /// details on this implementation.
* details on this implementation.
*/
class DmaPusher final { class DmaPusher final {
public: public:
explicit DmaPusher(Core::System& system_, GPU& gpu_, MemoryManager& memory_manager_, explicit DmaPusher(Core::System& system_, MemoryManager& memory_manager_, Control::ChannelState& channel_state_);
Control::ChannelState& channel_state_);
~DmaPusher(); ~DmaPusher();
void Push(CommandList&& entries) { void Push(CommandList&& entries) {
@ -136,8 +132,7 @@ public:
void DispatchCalls(); void DispatchCalls();
void BindSubchannel(Engines::EngineInterface* engine, u32 subchannel_id, void BindSubchannel(Engines::EngineInterface* engine, u32 subchannel_id, Engines::EngineTypes engine_type) {
Engines::EngineTypes engine_type) {
subchannels[subchannel_id] = engine; subchannels[subchannel_id] = engine;
subchannel_type[subchannel_id] = engine_type; subchannel_type[subchannel_id] = engine_type;
} }
@ -152,11 +147,11 @@ private:
void SetState(const CommandHeader& command_header); void SetState(const CommandHeader& command_header);
void CallMethod(u32 argument) const; void CallMethod(u32 argument);
void CallMultiMethod(const u32* base_start, u32 num_methods) const; void CallMultiMethod(const u32* base_start, u32 num_methods);
Common::ScratchBuffer<CommandHeader> public:
command_headers; ///< Buffer for list of commands fetched at once Common::ScratchBuffer<CommandHeader> command_headers; ///< Buffer for list of commands fetched at once
std::queue<CommandList> dma_pushbuffer; ///< Queue of command lists to be processed std::queue<CommandList> dma_pushbuffer; ///< Queue of command lists to be processed
std::size_t dma_pushbuffer_subindex{}; ///< Index within a command list within the pushbuffer std::size_t dma_pushbuffer_subindex{}; ///< Index within a command list within the pushbuffer
@ -172,24 +167,24 @@ private:
bool is_last_call; bool is_last_call;
}; };
Core::System& system;
MemoryManager& memory_manager;
Control::ChannelState& channel_state;
DmaState dma_state{}; DmaState dma_state{};
bool dma_increment_once{};
const bool ib_enable{true}; ///< IB mode enabled
std::array<Engines::EngineInterface*, max_subchannels> subchannels{}; std::array<Engines::EngineInterface*, max_subchannels> subchannels{};
std::array<Engines::EngineTypes, max_subchannels> subchannel_type; std::array<Engines::EngineTypes, max_subchannels> subchannel_type;
GPU& gpu; Engines::Puller puller;
Core::System& system;
MemoryManager& memory_manager;
mutable Engines::Puller puller;
VideoCore::RasterizerInterface* rasterizer;
bool signal_sync;
bool synced;
std::mutex sync_mutex; std::mutex sync_mutex;
std::condition_variable sync_cv; std::condition_variable sync_cv;
VideoCore::RasterizerInterface* rasterizer = nullptr;
const bool ib_enable : 1 = true; ///< IB mode enabled
bool dma_increment_once : 1 = false;
bool signal_sync : 1 = false;
bool synced : 1 = false;
}; };
} // namespace Tegra } // namespace Tegra

View file

@ -12,6 +12,10 @@
#include "common/common_types.h" #include "common/common_types.h"
namespace Core {
class System;
}
namespace Tegra::Engines { namespace Tegra::Engines {
enum class EngineTypes : u32 { enum class EngineTypes : u32 {
@ -28,28 +32,25 @@ public:
virtual ~EngineInterface() = default; virtual ~EngineInterface() = default;
/// Write the value to the register identified by method. /// Write the value to the register identified by method.
virtual void CallMethod(u32 method, u32 method_argument, bool is_last_call) = 0; virtual void CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) = 0;
/// Write multiple values to the register identified by method. /// Write multiple values to the register identified by method.
virtual void CallMultiMethod(u32 method, const u32* base_start, u32 amount, virtual void CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) = 0;
u32 methods_pending) = 0;
void ConsumeSink() { void ConsumeSink(Core::System& system) {
if (method_sink.empty()) { if (!method_sink.empty()) {
return; ConsumeSinkImpl(system);
} }
ConsumeSinkImpl();
} }
std::bitset<(std::numeric_limits<u16>::max)()> execution_mask{}; std::bitset<(std::numeric_limits<u16>::max)()> execution_mask{};
std::vector<std::pair<u32, u32>> method_sink{}; std::vector<std::pair<u32, u32>> method_sink{};
bool current_dirty{};
GPUVAddr current_dma_segment; GPUVAddr current_dma_segment;
bool current_dirty{};
protected: protected:
virtual void ConsumeSinkImpl() { virtual void ConsumeSinkImpl(Core::System& system) {
for (auto [method, value] : method_sink) { for (auto [method, value] : method_sink) {
CallMethod(method, value, true); CallMethod(system, method, value, true);
} }
method_sink.clear(); method_sink.clear();
} }

View file

@ -36,9 +36,8 @@ void Fermi2D::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
rasterizer = rasterizer_; rasterizer = rasterizer_;
} }
void Fermi2D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { void Fermi2D::CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) {
ASSERT_MSG(method < Regs::NUM_REGS, ASSERT_MSG(method < Regs::NUM_REGS, "Invalid Fermi2D register, increase the size of the Regs structure");
"Invalid Fermi2D register, increase the size of the Regs structure");
regs.reg_array[method] = method_argument; regs.reg_array[method] = method_argument;
if (method == FERMI2D_REG_INDEX(pixels_from_memory.src_y0) + 1) { if (method == FERMI2D_REG_INDEX(pixels_from_memory.src_y0) + 1) {
@ -46,13 +45,13 @@ void Fermi2D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
} }
} }
void Fermi2D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 methods_pending) { void Fermi2D::CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) {
for (u32 i = 0; i < amount; ++i) { for (u32 i = 0; i < amount; ++i) {
CallMethod(method, base_start[i], methods_pending - i <= 1); CallMethod(system, method, base_start[i], methods_pending - i <= 1);
} }
} }
void Fermi2D::ConsumeSinkImpl() { void Fermi2D::ConsumeSinkImpl(Core::System& system) {
for (auto [method, value] : method_sink) { for (auto [method, value] : method_sink) {
regs.reg_array[method] = value; regs.reg_array[method] = value;
} }
@ -60,8 +59,7 @@ void Fermi2D::ConsumeSinkImpl() {
} }
void Fermi2D::Blit() { void Fermi2D::Blit() {
LOG_DEBUG(HW_GPU, "called. source address=0x{:x}, destination address=0x{:x}", LOG_DEBUG(HW_GPU, "called. source address=0x{:x}, destination address=0x{:x}", regs.src.Address(), regs.dst.Address());
regs.src.Address(), regs.dst.Address());
UNIMPLEMENTED_IF_MSG(regs.operation != Operation::SrcCopy, "Operation is not copy"); UNIMPLEMENTED_IF_MSG(regs.operation != Operation::SrcCopy, "Operation is not copy");
UNIMPLEMENTED_IF_MSG(regs.src.layer != 0, "Source layer is not zero"); UNIMPLEMENTED_IF_MSG(regs.src.layer != 0, "Source layer is not zero");

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -44,11 +47,10 @@ public:
void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
/// Write the value to the register identified by method. /// Write the value to the register identified by method.
void CallMethod(u32 method, u32 method_argument, bool is_last_call) override; void CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) override;
/// Write multiple values to the register identified by method. /// Write multiple values to the register identified by method.
void CallMultiMethod(u32 method, const u32* base_start, u32 amount, void CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) override;
u32 methods_pending) override;
enum class Origin : u32 { enum class Origin : u32 {
Center = 0, Center = 0,
@ -311,7 +313,7 @@ private:
/// registers. /// registers.
void Blit(); void Blit();
void ConsumeSinkImpl() override; void ConsumeSinkImpl(Core::System& system) override;
}; };
#define ASSERT_REG_POSITION(field_name, position) \ #define ASSERT_REG_POSITION(field_name, position) \

View file

@ -16,8 +16,10 @@
namespace Tegra::Engines { namespace Tegra::Engines {
KeplerCompute::KeplerCompute(Core::System& system_, MemoryManager& memory_manager_) KeplerCompute::KeplerCompute(MemoryManager& memory_manager_)
: system{system_}, memory_manager{memory_manager_}, upload_state{memory_manager, regs.upload} { : memory_manager{memory_manager_}
, upload_state{memory_manager, regs.upload}
{
execution_mask.reset(); execution_mask.reset();
execution_mask[KEPLER_COMPUTE_REG_INDEX(exec_upload)] = true; execution_mask[KEPLER_COMPUTE_REG_INDEX(exec_upload)] = true;
execution_mask[KEPLER_COMPUTE_REG_INDEX(data_upload)] = true; execution_mask[KEPLER_COMPUTE_REG_INDEX(data_upload)] = true;
@ -31,16 +33,15 @@ void KeplerCompute::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_)
upload_state.BindRasterizer(rasterizer); upload_state.BindRasterizer(rasterizer);
} }
void KeplerCompute::ConsumeSinkImpl() { void KeplerCompute::ConsumeSinkImpl(Core::System& system) {
for (auto [method, value] : method_sink) { for (auto [method, value] : method_sink) {
regs.reg_array[method] = value; regs.reg_array[method] = value;
} }
method_sink.clear(); method_sink.clear();
} }
void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_call) { void KeplerCompute::CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) {
ASSERT_MSG(method < Regs::NUM_REGS, ASSERT_MSG(method < Regs::NUM_REGS, "Invalid KeplerCompute register, increase the size of the Regs structure");
"Invalid KeplerCompute register, increase the size of the Regs structure");
regs.reg_array[method] = method_argument; regs.reg_array[method] = method_argument;
@ -78,8 +79,7 @@ void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_cal
} }
} }
void KeplerCompute::CallMultiMethod(u32 method, const u32* base_start, u32 amount, void KeplerCompute::CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) {
u32 methods_pending) {
switch (method) { switch (method) {
case KEPLER_COMPUTE_REG_INDEX(data_upload): case KEPLER_COMPUTE_REG_INDEX(data_upload):
upload_address = current_dma_segment; upload_address = current_dma_segment;
@ -87,7 +87,7 @@ void KeplerCompute::CallMultiMethod(u32 method, const u32* base_start, u32 amoun
return; return;
default: default:
for (u32 i = 0; i < amount; i++) { for (u32 i = 0; i < amount; i++) {
CallMethod(method, base_start[i], methods_pending - i <= 1); CallMethod(system, method, base_start[i], methods_pending - i <= 1);
} }
break; break;
} }

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -42,7 +45,7 @@ namespace Tegra::Engines {
class KeplerCompute final : public EngineInterface { class KeplerCompute final : public EngineInterface {
public: public:
explicit KeplerCompute(Core::System& system, MemoryManager& memory_manager); explicit KeplerCompute(MemoryManager& memory_manager);
~KeplerCompute(); ~KeplerCompute();
/// Binds a rasterizer to this engine. /// Binds a rasterizer to this engine.
@ -199,11 +202,10 @@ public:
"KeplerCompute LaunchParams has wrong size"); "KeplerCompute LaunchParams has wrong size");
/// Write the value to the register identified by method. /// Write the value to the register identified by method.
void CallMethod(u32 method, u32 method_argument, bool is_last_call) override; void CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) override;
/// Write multiple values to the register identified by method. /// Write multiple values to the register identified by method.
void CallMultiMethod(u32 method, const u32* base_start, u32 amount, void CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) override;
u32 methods_pending) override;
std::optional<GPUVAddr> GetIndirectComputeAddress() const { std::optional<GPUVAddr> GetIndirectComputeAddress() const {
return indirect_compute; return indirect_compute;
@ -212,7 +214,7 @@ public:
private: private:
void ProcessLaunch(); void ProcessLaunch();
void ConsumeSinkImpl() override; void ConsumeSinkImpl(Core::System& system) override;
/// Retrieves information about a specific TIC entry from the TIC buffer. /// Retrieves information about a specific TIC entry from the TIC buffer.
Texture::TICEntry GetTICEntry(u32 tic_index) const; Texture::TICEntry GetTICEntry(u32 tic_index) const;
@ -220,7 +222,6 @@ private:
/// Retrieves information about a specific TSC entry from the TSC buffer. /// Retrieves information about a specific TSC entry from the TSC buffer.
Texture::TSCEntry GetTSCEntry(u32 tsc_index) const; Texture::TSCEntry GetTSCEntry(u32 tsc_index) const;
Core::System& system;
MemoryManager& memory_manager; MemoryManager& memory_manager;
VideoCore::RasterizerInterface* rasterizer = nullptr; VideoCore::RasterizerInterface* rasterizer = nullptr;
Upload::State upload_state; Upload::State upload_state;

View file

@ -14,8 +14,9 @@
namespace Tegra::Engines { namespace Tegra::Engines {
KeplerMemory::KeplerMemory(Core::System& system_, MemoryManager& memory_manager) KeplerMemory::KeplerMemory(MemoryManager& memory_manager)
: system{system_}, upload_state{memory_manager, regs.upload} {} : upload_state{memory_manager, regs.upload}
{}
KeplerMemory::~KeplerMemory() = default; KeplerMemory::~KeplerMemory() = default;
@ -27,16 +28,15 @@ void KeplerMemory::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
execution_mask[KEPLERMEMORY_REG_INDEX(data)] = true; execution_mask[KEPLERMEMORY_REG_INDEX(data)] = true;
} }
void KeplerMemory::ConsumeSinkImpl() { void KeplerMemory::ConsumeSinkImpl(Core::System& system) {
for (auto [method, value] : method_sink) { for (auto [method, value] : method_sink) {
regs.reg_array[method] = value; regs.reg_array[method] = value;
} }
method_sink.clear(); method_sink.clear();
} }
void KeplerMemory::CallMethod(u32 method, u32 method_argument, bool is_last_call) { void KeplerMemory::CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) {
ASSERT_MSG(method < Regs::NUM_REGS, ASSERT_MSG(method < Regs::NUM_REGS, "Invalid KeplerMemory register, increase the size of the Regs structure");
"Invalid KeplerMemory register, increase the size of the Regs structure");
regs.reg_array[method] = method_argument; regs.reg_array[method] = method_argument;
@ -52,15 +52,14 @@ void KeplerMemory::CallMethod(u32 method, u32 method_argument, bool is_last_call
} }
} }
void KeplerMemory::CallMultiMethod(u32 method, const u32* base_start, u32 amount, void KeplerMemory::CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) {
u32 methods_pending) {
switch (method) { switch (method) {
case KEPLERMEMORY_REG_INDEX(data): case KEPLERMEMORY_REG_INDEX(data):
upload_state.ProcessData(base_start, amount); upload_state.ProcessData(base_start, amount);
return; return;
default: default:
for (u32 i = 0; i < amount; i++) { for (u32 i = 0; i < amount; i++) {
CallMethod(method, base_start[i], methods_pending - i <= 1); CallMethod(system, method, base_start[i], methods_pending - i <= 1);
} }
break; break;
} }

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -36,18 +39,17 @@ namespace Tegra::Engines {
class KeplerMemory final : public EngineInterface { class KeplerMemory final : public EngineInterface {
public: public:
explicit KeplerMemory(Core::System& system_, MemoryManager& memory_manager); explicit KeplerMemory(MemoryManager& memory_manager);
~KeplerMemory() override; ~KeplerMemory() override;
/// Binds a rasterizer to this engine. /// Binds a rasterizer to this engine.
void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
/// Write the value to the register identified by method. /// Write the value to the register identified by method.
void CallMethod(u32 method, u32 method_argument, bool is_last_call) override; void CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) override;
/// Write multiple values to the register identified by method. /// Write multiple values to the register identified by method.
void CallMultiMethod(u32 method, const u32* base_start, u32 amount, void CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) override;
u32 methods_pending) override;
struct Regs { struct Regs {
static constexpr size_t NUM_REGS = 0x7F; static constexpr size_t NUM_REGS = 0x7F;
@ -73,9 +75,7 @@ public:
} regs{}; } regs{};
private: private:
void ConsumeSinkImpl() override; void ConsumeSinkImpl(Core::System& system) override;
Core::System& system;
Upload::State upload_state; Upload::State upload_state;
}; };

View file

@ -25,9 +25,8 @@ namespace Tegra::Engines {
/// First register id that is actually a Macro call. /// First register id that is actually a Macro call.
constexpr u32 MacroRegistersStart = 0xE00; constexpr u32 MacroRegistersStart = 0xE00;
Maxwell3D::Maxwell3D(Core::System& system_, MemoryManager& memory_manager_) Maxwell3D::Maxwell3D(MemoryManager& memory_manager_)
: draw_manager() : draw_manager()
, system{system_}
, memory_manager{memory_manager_} , memory_manager{memory_manager_}
#ifdef ARCHITECTURE_x86_64 #ifdef ARCHITECTURE_x86_64
, macro_engine(bool(Settings::values.disable_macro_jit)) , macro_engine(bool(Settings::values.disable_macro_jit))
@ -201,11 +200,10 @@ bool Maxwell3D::IsMethodExecutable(u32 method) {
} }
} }
void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) { void Maxwell3D::ProcessMacro(Core::System& system, u32 method, const u32* base_start, u32 amount, bool is_last_call) {
if (executing_macro == 0) { if (executing_macro == 0) {
// A macro call must begin by writing the macro method's register, not its argument. // A macro call must begin by writing the macro method's register, not its argument.
ASSERT_MSG((method % 2) == 0, ASSERT((method % 2) == 0 && "Can't start macro execution by writing to the ARGS register");
"Can't start macro execution by writing to the ARGS register");
executing_macro = method; executing_macro = method;
} }
@ -219,8 +217,8 @@ void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool
// Call the macro when there are no more parameters in the command buffer // Call the macro when there are no more parameters in the command buffer
if (is_last_call) { if (is_last_call) {
ConsumeSink(); ConsumeSink(system);
CallMacroMethod(executing_macro, macro_params); CallMacroMethod(system, executing_macro, macro_params);
macro_params.clear(); macro_params.clear();
macro_addresses.clear(); macro_addresses.clear();
macro_segments.clear(); macro_segments.clear();
@ -287,7 +285,7 @@ u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) {
return argument; return argument;
} }
void Maxwell3D::ConsumeSinkImpl() { void Maxwell3D::ConsumeSinkImpl(Core::System& system) {
const auto control = shadow_state.shadow_ram_control; const auto control = shadow_state.shadow_ram_control;
if (control == Regs::ShadowRamControl::Track || control == Regs::ShadowRamControl::TrackWithFilter) { if (control == Regs::ShadowRamControl::Track || control == Regs::ShadowRamControl::TrackWithFilter) {
for (auto [method, value] : method_sink) { for (auto [method, value] : method_sink) {
@ -378,7 +376,7 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
} }
} }
void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters) { void Maxwell3D::CallMacroMethod(Core::System& system, u32 method, const std::vector<u32>& parameters) {
// Reset the current macro. // Reset the current macro.
executing_macro = 0; executing_macro = 0;
@ -387,11 +385,11 @@ void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters)
((method - MacroRegistersStart) >> 1) % static_cast<u32>(macro_positions.size()); ((method - MacroRegistersStart) >> 1) % static_cast<u32>(macro_positions.size());
// Execute the current macro. // Execute the current macro.
macro_engine.Execute(*this, macro_positions[entry], parameters); macro_engine.Execute(system, *this, macro_positions[entry], parameters);
draw_manager.DrawDeferred(*this); draw_manager.DrawDeferred(*this);
} }
void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { void Maxwell3D::CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) {
// It is an error to write to a register other than the current macro's ARG register before // It is an error to write to a register other than the current macro's ARG register before
// it has finished execution. // it has finished execution.
if (executing_macro != 0) { if (executing_macro != 0) {
@ -401,7 +399,7 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
// Methods after 0xE00 are special, they're actually triggers for some microcode that was // Methods after 0xE00 are special, they're actually triggers for some microcode that was
// uploaded to the GPU during initialization. // uploaded to the GPU during initialization.
if (method >= MacroRegistersStart) { if (method >= MacroRegistersStart) {
ProcessMacro(method, &method_argument, 1, is_last_call); ProcessMacro(system, method, &method_argument, 1, is_last_call);
return; return;
} }
@ -411,12 +409,11 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
ProcessMethodCall(method, argument, method_argument, is_last_call); ProcessMethodCall(method, argument, method_argument, is_last_call);
} }
void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, void Maxwell3D::CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) {
u32 methods_pending) {
// Methods after 0xE00 are special, they're actually triggers for some microcode that was // Methods after 0xE00 are special, they're actually triggers for some microcode that was
// uploaded to the GPU during initialization. // uploaded to the GPU during initialization.
if (method >= MacroRegistersStart) { if (method >= MacroRegistersStart) {
ProcessMacro(method, base_start, amount, amount == methods_pending); ProcessMacro(system, method, base_start, amount, amount == methods_pending);
return; return;
} }
switch (method) { switch (method) {
@ -445,7 +442,7 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
} }
default: default:
for (u32 i = 0; i < amount; i++) { for (u32 i = 0; i < amount; i++) {
CallMethod(method, base_start[i], methods_pending - i <= 1); CallMethod(system, method, base_start[i], methods_pending - i <= 1);
} }
break; break;
} }
@ -467,7 +464,7 @@ void Maxwell3D::ProcessFirmwareCall4() {
regs.shadow_scratch[0] = 1; regs.shadow_scratch[0] = 1;
} }
void Maxwell3D::StampQueryResult(u64 payload, bool long_query) { void Maxwell3D::StampQueryResult(Core::System& system, u64 payload, bool long_query) {
const GPUVAddr sequence_address{regs.report_semaphore.Address()}; const GPUVAddr sequence_address{regs.report_semaphore.Address()};
if (long_query) { if (long_query) {
memory_manager.Write<u64>(sequence_address + sizeof(u64), system.GPU().GetTicks()); memory_manager.Write<u64>(sequence_address + sizeof(u64), system.GPU().GetTicks());

View file

@ -55,7 +55,7 @@ namespace Tegra::Engines {
class Maxwell3D final : public EngineInterface { class Maxwell3D final : public EngineInterface {
public: public:
explicit Maxwell3D(Core::System& system, MemoryManager& memory_manager); explicit Maxwell3D(MemoryManager& memory_manager);
~Maxwell3D(); ~Maxwell3D();
/// Binds a rasterizer to this engine. /// Binds a rasterizer to this engine.
@ -3129,11 +3129,10 @@ public:
u32 GetRegisterValue(u32 method) const; u32 GetRegisterValue(u32 method) const;
/// Write the value to the register identified by method. /// Write the value to the register identified by method.
void CallMethod(u32 method, u32 method_argument, bool is_last_call) override; void CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) override;
/// Write multiple values to the register identified by method. /// Write multiple values to the register identified by method.
void CallMultiMethod(u32 method, const u32* base_start, u32 amount, void CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) override;
u32 methods_pending) override;
bool ShouldExecute() const { bool ShouldExecute() const {
return execute_on; return execute_on;
@ -3190,13 +3189,13 @@ public:
private: private:
void InitializeRegisterDefaults(); void InitializeRegisterDefaults();
void ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call); void ProcessMacro(Core::System& system, u32 method, const u32* base_start, u32 amount, bool is_last_call);
u32 ProcessShadowRam(u32 method, u32 argument); u32 ProcessShadowRam(u32 method, u32 argument);
void ProcessDirtyRegisters(u32 method, u32 argument); void ProcessDirtyRegisters(u32 method, u32 argument);
void ConsumeSinkImpl() override; void ConsumeSinkImpl(Core::System& system) override;
void ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argument, bool is_last_call); void ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argument, bool is_last_call);
@ -3212,7 +3211,7 @@ private:
* @param method Method to call * @param method Method to call
* @param parameters Arguments to the method call * @param parameters Arguments to the method call
*/ */
void CallMacroMethod(u32 method, const std::vector<u32>& parameters); void CallMacroMethod(Core::System& system, u32 method, const std::vector<u32>& parameters);
/// Handles writes to the macro uploading register. /// Handles writes to the macro uploading register.
void ProcessMacroUpload(u32 data); void ProcessMacroUpload(u32 data);
@ -3227,7 +3226,7 @@ private:
void ProcessQueryGet(); void ProcessQueryGet();
/// Writes the query result accordingly. /// Writes the query result accordingly.
void StampQueryResult(u64 payload, bool long_query); void StampQueryResult(Core::System& system, u64 payload, bool long_query);
/// Handles conditional rendering. /// Handles conditional rendering.
void ProcessQueryCondition(); void ProcessQueryCondition();
@ -3242,7 +3241,6 @@ private:
bool IsMethodExecutable(u32 method); bool IsMethodExecutable(u32 method);
Core::System& system;
MemoryManager& memory_manager; MemoryManager& memory_manager;
VideoCore::RasterizerInterface* rasterizer = nullptr; VideoCore::RasterizerInterface* rasterizer = nullptr;

View file

@ -21,8 +21,9 @@ namespace Tegra::Engines {
using namespace Texture; using namespace Texture;
MaxwellDMA::MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_) MaxwellDMA::MaxwellDMA(MemoryManager& memory_manager_)
: system{system_}, memory_manager{memory_manager_} { : memory_manager{memory_manager_}
{
execution_mask.reset(); execution_mask.reset();
execution_mask[offsetof(Regs, launch_dma) / sizeof(u32)] = true; execution_mask[offsetof(Regs, launch_dma) / sizeof(u32)] = true;
} }
@ -33,14 +34,14 @@ void MaxwellDMA::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
rasterizer = rasterizer_; rasterizer = rasterizer_;
} }
void MaxwellDMA::ConsumeSinkImpl() { void MaxwellDMA::ConsumeSinkImpl(Core::System& system) {
for (auto [method, value] : method_sink) { for (auto [method, value] : method_sink) {
regs.reg_array[method] = value; regs.reg_array[method] = value;
} }
method_sink.clear(); method_sink.clear();
} }
void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call) { void MaxwellDMA::CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) {
ASSERT_MSG(method < NUM_REGS, "Invalid MaxwellDMA register"); ASSERT_MSG(method < NUM_REGS, "Invalid MaxwellDMA register");
regs.reg_array[method] = method_argument; regs.reg_array[method] = method_argument;
@ -50,16 +51,14 @@ void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call)
} }
} }
void MaxwellDMA::CallMultiMethod(u32 method, const u32* base_start, u32 amount, void MaxwellDMA::CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) {
u32 methods_pending) {
for (u32 i = 0; i < amount; ++i) { for (u32 i = 0; i < amount; ++i) {
CallMethod(method, base_start[i], methods_pending - i <= 1); CallMethod(system, method, base_start[i], methods_pending - i <= 1);
} }
} }
void MaxwellDMA::Launch() { void MaxwellDMA::Launch() {
LOG_TRACE(Render_OpenGL, "DMA copy 0x{:x} -> 0x{:x}", static_cast<GPUVAddr>(regs.offset_in), LOG_TRACE(Render_OpenGL, "DMA copy 0x{:x} -> 0x{:x}", static_cast<GPUVAddr>(regs.offset_in), GPUVAddr(regs.offset_out));
static_cast<GPUVAddr>(regs.offset_out));
// TODO(Subv): Perform more research and implement all features of this engine. // TODO(Subv): Perform more research and implement all features of this engine.
const LaunchDMA& launch = regs.launch_dma; const LaunchDMA& launch = regs.launch_dma;

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -238,15 +241,14 @@ public:
void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
explicit MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_); explicit MaxwellDMA(MemoryManager& memory_manager_);
~MaxwellDMA() override; ~MaxwellDMA() override;
/// Write the value to the register identified by method. /// Write the value to the register identified by method.
void CallMethod(u32 method, u32 method_argument, bool is_last_call) override; void CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) override;
/// Write multiple values to the register identified by method. /// Write multiple values to the register identified by method.
void CallMultiMethod(u32 method, const u32* base_start, u32 amount, void CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) override;
u32 methods_pending) override;
private: private:
/// Performs the copy from the source buffer to the destination buffer as configured in the /// Performs the copy from the source buffer to the destination buffer as configured in the
@ -261,9 +263,7 @@ private:
void ReleaseSemaphore(); void ReleaseSemaphore();
void ConsumeSinkImpl() override; void ConsumeSinkImpl(Core::System& system) override;
Core::System& system;
MemoryManager& memory_manager; MemoryManager& memory_manager;
VideoCore::RasterizerInterface* rasterizer = nullptr; VideoCore::RasterizerInterface* rasterizer = nullptr;

View file

@ -26,18 +26,16 @@ class MemoryManager;
namespace Tegra::Engines { namespace Tegra::Engines {
class Nv01Timer final : public EngineInterface { class Nv01Timer final : public EngineInterface {
public: public:
explicit Nv01Timer(Core::System& system_, MemoryManager& memory_manager) explicit Nv01Timer(MemoryManager& memory_manager) noexcept {}
: system{system_} ~Nv01Timer() noexcept override {}
{}
~Nv01Timer() override;
/// Write the value to the register identified by method. /// Write the value to the register identified by method.
void CallMethod(u32 method, u32 method_argument, bool is_last_call) override { void CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) override {
LOG_DEBUG(HW_GPU, "method={}, argument={}, is_last_call={}", method, method_argument, is_last_call); LOG_DEBUG(HW_GPU, "method={}, argument={}, is_last_call={}", method, method_argument, is_last_call);
} }
/// Write multiple values to the register identified by method. /// Write multiple values to the register identified by method.
void CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 methods_pending) override { void CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) override {
LOG_DEBUG(HW_GPU, "method={}, base_start={}, amount={}, pending={}", method, fmt::ptr(base_start), amount, methods_pending); LOG_DEBUG(HW_GPU, "method={}, base_start={}, amount={}, pending={}", method, fmt::ptr(base_start), amount, methods_pending);
} }
@ -46,7 +44,6 @@ public:
INSERT_PADDING_BYTES_NOINIT(0x48); INSERT_PADDING_BYTES_NOINIT(0x48);
} regs{}; } regs{};
private: private:
void ConsumeSinkImpl() override {} void ConsumeSinkImpl(Core::System& system) override {}
Core::System& system;
}; };
} }

View file

@ -22,37 +22,29 @@
namespace Tegra::Engines { namespace Tegra::Engines {
Puller::Puller(GPU& gpu_, MemoryManager& memory_manager_, DmaPusher& dma_pusher_, void Puller::ProcessBindMethod(DmaPusher& dma_pusher, const MethodCall& method_call) {
Control::ChannelState& channel_state_)
: gpu{gpu_}, memory_manager{memory_manager_}, dma_pusher{dma_pusher_}, channel_state{
channel_state_} {}
Puller::~Puller() = default;
void Puller::ProcessBindMethod(const MethodCall& method_call) {
// Bind the current subchannel to the desired engine id. // Bind the current subchannel to the desired engine id.
LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel, LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel, method_call.argument);
method_call.argument);
const auto engine_id = static_cast<EngineID>(method_call.argument); const auto engine_id = static_cast<EngineID>(method_call.argument);
bound_engines[method_call.subchannel] = engine_id; bound_engines[method_call.subchannel] = engine_id;
switch (engine_id) { switch (engine_id) {
case EngineID::FERMI_TWOD_A: case EngineID::FERMI_TWOD_A:
dma_pusher.BindSubchannel(&*channel_state.fermi_2d, method_call.subchannel, EngineTypes::Fermi2D); dma_pusher.BindSubchannel(&dma_pusher.channel_state.payload->fermi_2d, method_call.subchannel, EngineTypes::Fermi2D);
break; break;
case EngineID::MAXWELL_B: case EngineID::MAXWELL_B:
dma_pusher.BindSubchannel(&*channel_state.maxwell_3d, method_call.subchannel, EngineTypes::Maxwell3D); dma_pusher.BindSubchannel(&dma_pusher.channel_state.payload->maxwell_3d, method_call.subchannel, EngineTypes::Maxwell3D);
break; break;
case EngineID::KEPLER_COMPUTE_B: case EngineID::KEPLER_COMPUTE_B:
dma_pusher.BindSubchannel(&*channel_state.kepler_compute, method_call.subchannel, EngineTypes::KeplerCompute); dma_pusher.BindSubchannel(&dma_pusher.channel_state.payload->kepler_compute, method_call.subchannel, EngineTypes::KeplerCompute);
break; break;
case EngineID::MAXWELL_DMA_COPY_A: case EngineID::MAXWELL_DMA_COPY_A:
dma_pusher.BindSubchannel(&*channel_state.maxwell_dma, method_call.subchannel, EngineTypes::MaxwellDMA); dma_pusher.BindSubchannel(&dma_pusher.channel_state.payload->maxwell_dma, method_call.subchannel, EngineTypes::MaxwellDMA);
break; break;
case EngineID::KEPLER_INLINE_TO_MEMORY_B: case EngineID::KEPLER_INLINE_TO_MEMORY_B:
dma_pusher.BindSubchannel(&*channel_state.kepler_memory, method_call.subchannel, EngineTypes::KeplerMemory); dma_pusher.BindSubchannel(&dma_pusher.channel_state.payload->kepler_memory, method_call.subchannel, EngineTypes::KeplerMemory);
break; break;
case EngineID::NV01_TIMER: case EngineID::NV01_TIMER:
dma_pusher.BindSubchannel(&*channel_state.nv01_timer, method_call.subchannel, EngineTypes::Nv01Timer); dma_pusher.BindSubchannel(&dma_pusher.channel_state.payload->nv01_timer, method_call.subchannel, EngineTypes::Nv01Timer);
break; break;
default: default:
UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id); UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id);
@ -60,15 +52,15 @@ void Puller::ProcessBindMethod(const MethodCall& method_call) {
} }
} }
void Puller::ProcessFenceActionMethod() { void Puller::ProcessFenceActionMethod(DmaPusher& dma_pusher) {
switch (regs.fence_action.op) { switch (regs.fence_action.op) {
case Puller::FenceOperation::Acquire: case Puller::FenceOperation::Acquire:
// UNIMPLEMENTED_MSG("Channel Scheduling pending."); // UNIMPLEMENTED_MSG("Channel Scheduling pending.");
// WaitFence(regs.fence_action.syncpoint_id, regs.fence_value); // WaitFence(regs.fence_action.syncpoint_id, regs.fence_value);
rasterizer->ReleaseFences(); dma_pusher.rasterizer->ReleaseFences();
break; break;
case Puller::FenceOperation::Increment: case Puller::FenceOperation::Increment:
rasterizer->SignalSyncPoint(regs.fence_action.syncpoint_id); dma_pusher.rasterizer->SignalSyncPoint(regs.fence_action.syncpoint_id);
break; break;
default: default:
UNIMPLEMENTED_MSG("Unimplemented operation {}", regs.fence_action.op.Value()); UNIMPLEMENTED_MSG("Unimplemented operation {}", regs.fence_action.op.Value());
@ -76,37 +68,35 @@ void Puller::ProcessFenceActionMethod() {
} }
} }
void Puller::ProcessSemaphoreTriggerMethod() { void Puller::ProcessSemaphoreTriggerMethod(DmaPusher& dma_pusher) {
const auto semaphoreOperationMask = 0xF; const auto semaphoreOperationMask = 0xF;
const auto op = const auto op = GpuSemaphoreOperation(regs.semaphore_trigger & semaphoreOperationMask);
static_cast<GpuSemaphoreOperation>(regs.semaphore_trigger & semaphoreOperationMask);
if (op == GpuSemaphoreOperation::WriteLong) { if (op == GpuSemaphoreOperation::WriteLong) {
const GPUVAddr sequence_address{regs.semaphore_address.SemaphoreAddress()}; const GPUVAddr sequence_address{regs.semaphore_address.SemaphoreAddress()};
const u32 payload = regs.semaphore_sequence; const u32 payload = regs.semaphore_sequence;
rasterizer->Query(sequence_address, VideoCommon::QueryType::Payload, dma_pusher.rasterizer->Query(sequence_address, VideoCommon::QueryType::Payload, VideoCommon::QueryPropertiesFlags::HasTimeout, payload, 0);
VideoCommon::QueryPropertiesFlags::HasTimeout, payload, 0);
} else { } else {
do { do {
const u32 word{memory_manager.Read<u32>(regs.semaphore_address.SemaphoreAddress())}; const u32 word = dma_pusher.memory_manager.Read<u32>(regs.semaphore_address.SemaphoreAddress());
regs.acquire_source = true; regs.acquire_source = true;
regs.acquire_value = regs.semaphore_sequence; regs.acquire_value = regs.semaphore_sequence;
if (op == GpuSemaphoreOperation::AcquireEqual) { if (op == GpuSemaphoreOperation::AcquireEqual) {
regs.acquire_active = true; regs.acquire_active = true;
regs.acquire_mode = false; regs.acquire_mode = false;
if (word != regs.acquire_value) { if (word != regs.acquire_value) {
rasterizer->ReleaseFences(); dma_pusher.rasterizer->ReleaseFences();
continue; continue;
} }
} else if (op == GpuSemaphoreOperation::AcquireGequal) { } else if (op == GpuSemaphoreOperation::AcquireGequal) {
regs.acquire_active = true; regs.acquire_active = true;
regs.acquire_mode = true; regs.acquire_mode = true;
if (word < regs.acquire_value) { if (word < regs.acquire_value) {
rasterizer->ReleaseFences(); dma_pusher.rasterizer->ReleaseFences();
continue; continue;
} }
} else if (op == GpuSemaphoreOperation::AcquireMask) { } else if (op == GpuSemaphoreOperation::AcquireMask) {
if (word && regs.semaphore_sequence == 0) { if (word && regs.semaphore_sequence == 0) {
rasterizer->ReleaseFences(); dma_pusher.rasterizer->ReleaseFences();
continue; continue;
} }
} else { } else {
@ -116,21 +106,20 @@ void Puller::ProcessSemaphoreTriggerMethod() {
} }
} }
void Puller::ProcessSemaphoreRelease() { void Puller::ProcessSemaphoreRelease(DmaPusher& dma_pusher) {
const GPUVAddr sequence_address{regs.semaphore_address.SemaphoreAddress()}; const GPUVAddr sequence_address{regs.semaphore_address.SemaphoreAddress()};
const u32 payload = regs.semaphore_release; const u32 payload = regs.semaphore_release;
rasterizer->Query(sequence_address, VideoCommon::QueryType::Payload, dma_pusher.rasterizer->Query(sequence_address, VideoCommon::QueryType::Payload, VideoCommon::QueryPropertiesFlags::IsAFence, payload, 0);
VideoCommon::QueryPropertiesFlags::IsAFence, payload, 0);
} }
void Puller::ProcessSemaphoreAcquire() { void Puller::ProcessSemaphoreAcquire(DmaPusher& dma_pusher) {
u32 word = memory_manager.Read<u32>(regs.semaphore_address.SemaphoreAddress()); u32 word = dma_pusher.memory_manager.Read<u32>(regs.semaphore_address.SemaphoreAddress());
const auto value = regs.semaphore_acquire; const auto value = regs.semaphore_acquire;
while (word != value) { while (word != value) {
regs.acquire_active = true; regs.acquire_active = true;
regs.acquire_value = value; regs.acquire_value = value;
rasterizer->ReleaseFences(); dma_pusher.rasterizer->ReleaseFences();
word = memory_manager.Read<u32>(regs.semaphore_address.SemaphoreAddress()); word = dma_pusher.memory_manager.Read<u32>(regs.semaphore_address.SemaphoreAddress());
// TODO(kemathe73) figure out how to do the acquire_timeout // TODO(kemathe73) figure out how to do the acquire_timeout
regs.acquire_mode = false; regs.acquire_mode = false;
regs.acquire_source = false; regs.acquire_source = false;
@ -138,13 +127,13 @@ void Puller::ProcessSemaphoreAcquire() {
} }
/// Calls a GPU puller method. /// Calls a GPU puller method.
void Puller::CallPullerMethod(const MethodCall& method_call) { void Puller::CallPullerMethod(DmaPusher& dma_pusher, const MethodCall& method_call) {
regs.reg_array[method_call.method] = method_call.argument; regs.reg_array[method_call.method] = method_call.argument;
const auto method = static_cast<BufferMethods>(method_call.method); const auto method = static_cast<BufferMethods>(method_call.method);
switch (method) { switch (method) {
case BufferMethods::BindObject: { case BufferMethods::BindObject: {
ProcessBindMethod(method_call); ProcessBindMethod(dma_pusher, method_call);
break; break;
} }
case BufferMethods::Nop: case BufferMethods::Nop:
@ -155,16 +144,16 @@ void Puller::CallPullerMethod(const MethodCall& method_call) {
case BufferMethods::WrcacheFlush: case BufferMethods::WrcacheFlush:
break; break;
case BufferMethods::RefCnt: case BufferMethods::RefCnt:
rasterizer->SignalReference(); dma_pusher.rasterizer->SignalReference();
break; break;
case BufferMethods::SyncpointOperation: case BufferMethods::SyncpointOperation:
ProcessFenceActionMethod(); ProcessFenceActionMethod(dma_pusher);
break; break;
case BufferMethods::WaitForIdle: case BufferMethods::WaitForIdle:
rasterizer->WaitForIdle(); dma_pusher.rasterizer->WaitForIdle();
break; break;
case BufferMethods::SemaphoreOperation: { case BufferMethods::SemaphoreOperation: {
ProcessSemaphoreTriggerMethod(); ProcessSemaphoreTriggerMethod(dma_pusher);
break; break;
} }
case BufferMethods::NonStallInterrupt: { case BufferMethods::NonStallInterrupt: {
@ -177,7 +166,7 @@ void Puller::CallPullerMethod(const MethodCall& method_call) {
} }
case BufferMethods::MemOpB: { case BufferMethods::MemOpB: {
// Implement this better. // Implement this better.
rasterizer->InvalidateGPUCache(); dma_pusher.rasterizer->InvalidateGPUCache();
break; break;
} }
case BufferMethods::MemOpC: case BufferMethods::MemOpC:
@ -186,11 +175,11 @@ void Puller::CallPullerMethod(const MethodCall& method_call) {
break; break;
} }
case BufferMethods::SemaphoreAcquire: { case BufferMethods::SemaphoreAcquire: {
ProcessSemaphoreAcquire(); ProcessSemaphoreAcquire(dma_pusher);
break; break;
} }
case BufferMethods::SemaphoreRelease: { case BufferMethods::SemaphoreRelease: {
ProcessSemaphoreRelease(); ProcessSemaphoreRelease(dma_pusher);
break; break;
} }
case BufferMethods::Yield: { case BufferMethods::Yield: {
@ -205,27 +194,26 @@ void Puller::CallPullerMethod(const MethodCall& method_call) {
} }
/// Calls a GPU engine method. /// Calls a GPU engine method.
void Puller::CallEngineMethod(const MethodCall& method_call) { void Puller::CallEngineMethod(DmaPusher& dma_pusher, const MethodCall& method_call) {
const EngineID engine = bound_engines[method_call.subchannel]; const EngineID engine = bound_engines[method_call.subchannel];
switch (engine) { switch (engine) {
case EngineID::FERMI_TWOD_A: case EngineID::FERMI_TWOD_A:
channel_state.fermi_2d->CallMethod(method_call.method, method_call.argument, method_call.IsLastCall()); dma_pusher.channel_state.payload->fermi_2d.CallMethod(dma_pusher.system, method_call.method, method_call.argument, method_call.IsLastCall());
break; break;
case EngineID::MAXWELL_B: case EngineID::MAXWELL_B:
channel_state.maxwell_3d->CallMethod(method_call.method, method_call.argument, method_call.IsLastCall()); dma_pusher.channel_state.payload->maxwell_3d.CallMethod(dma_pusher.system, method_call.method, method_call.argument, method_call.IsLastCall());
break; break;
case EngineID::KEPLER_COMPUTE_B: case EngineID::KEPLER_COMPUTE_B:
channel_state.kepler_compute->CallMethod(method_call.method, method_call.argument, method_call.IsLastCall()); dma_pusher.channel_state.payload->kepler_compute.CallMethod(dma_pusher.system, method_call.method, method_call.argument, method_call.IsLastCall());
break; break;
case EngineID::MAXWELL_DMA_COPY_A: case EngineID::MAXWELL_DMA_COPY_A:
channel_state.maxwell_dma->CallMethod(method_call.method, method_call.argument, method_call.IsLastCall()); dma_pusher.channel_state.payload->maxwell_dma.CallMethod(dma_pusher.system, method_call.method, method_call.argument, method_call.IsLastCall());
break; break;
case EngineID::KEPLER_INLINE_TO_MEMORY_B: case EngineID::KEPLER_INLINE_TO_MEMORY_B:
channel_state.kepler_memory->CallMethod(method_call.method, method_call.argument, method_call.IsLastCall()); dma_pusher.channel_state.payload->kepler_memory.CallMethod(dma_pusher.system, method_call.method, method_call.argument, method_call.IsLastCall());
break; break;
case EngineID::NV01_TIMER: case EngineID::NV01_TIMER:
channel_state.nv01_timer->CallMethod(method_call.method, method_call.argument, method_call.IsLastCall()); dma_pusher.channel_state.payload->nv01_timer.CallMethod(dma_pusher.system, method_call.method, method_call.argument, method_call.IsLastCall());
break; break;
default: default:
UNIMPLEMENTED_MSG("Unimplemented engine"); UNIMPLEMENTED_MSG("Unimplemented engine");
@ -234,28 +222,26 @@ void Puller::CallEngineMethod(const MethodCall& method_call) {
} }
/// Calls a GPU engine multivalue method. /// Calls a GPU engine multivalue method.
void Puller::CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, void Puller::CallEngineMultiMethod(DmaPusher& dma_pusher, u32 method, u32 subchannel, const u32* base_start, u32 amount, u32 methods_pending) {
u32 methods_pending) {
const EngineID engine = bound_engines[subchannel]; const EngineID engine = bound_engines[subchannel];
switch (engine) { switch (engine) {
case EngineID::FERMI_TWOD_A: case EngineID::FERMI_TWOD_A:
channel_state.fermi_2d->CallMultiMethod(method, base_start, amount, methods_pending); dma_pusher.channel_state.payload->fermi_2d.CallMultiMethod(dma_pusher.system, method, base_start, amount, methods_pending);
break; break;
case EngineID::MAXWELL_B: case EngineID::MAXWELL_B:
channel_state.maxwell_3d->CallMultiMethod(method, base_start, amount, methods_pending); dma_pusher.channel_state.payload->maxwell_3d.CallMultiMethod(dma_pusher.system, method, base_start, amount, methods_pending);
break; break;
case EngineID::KEPLER_COMPUTE_B: case EngineID::KEPLER_COMPUTE_B:
channel_state.kepler_compute->CallMultiMethod(method, base_start, amount, methods_pending); dma_pusher.channel_state.payload->kepler_compute.CallMultiMethod(dma_pusher.system, method, base_start, amount, methods_pending);
break; break;
case EngineID::MAXWELL_DMA_COPY_A: case EngineID::MAXWELL_DMA_COPY_A:
channel_state.maxwell_dma->CallMultiMethod(method, base_start, amount, methods_pending); dma_pusher.channel_state.payload->maxwell_dma.CallMultiMethod(dma_pusher.system, method, base_start, amount, methods_pending);
break; break;
case EngineID::KEPLER_INLINE_TO_MEMORY_B: case EngineID::KEPLER_INLINE_TO_MEMORY_B:
channel_state.kepler_memory->CallMultiMethod(method, base_start, amount, methods_pending); dma_pusher.channel_state.payload->kepler_memory.CallMultiMethod(dma_pusher.system, method, base_start, amount, methods_pending);
break; break;
case EngineID::NV01_TIMER: case EngineID::NV01_TIMER:
channel_state.nv01_timer->CallMultiMethod(method, base_start, amount, methods_pending); dma_pusher.channel_state.payload->nv01_timer.CallMultiMethod(dma_pusher.system, method, base_start, amount, methods_pending);
break; break;
default: default:
UNIMPLEMENTED_MSG("Unimplemented engine"); UNIMPLEMENTED_MSG("Unimplemented engine");
@ -264,31 +250,26 @@ void Puller::CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_s
} }
/// Calls a GPU method. /// Calls a GPU method.
void Puller::CallMethod(const MethodCall& method_call) { void Puller::CallMethod(DmaPusher& dma_pusher, const MethodCall& method_call) {
LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method_call.method, LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method_call.method, method_call.subchannel);
method_call.subchannel);
ASSERT(method_call.subchannel < bound_engines.size()); ASSERT(method_call.subchannel < bound_engines.size());
if (ExecuteMethodOnEngine(method_call.method)) { if (ExecuteMethodOnEngine(dma_pusher, method_call.method)) {
CallEngineMethod(method_call); CallEngineMethod(dma_pusher, method_call);
} else { } else {
CallPullerMethod(method_call); CallPullerMethod(dma_pusher, method_call);
} }
} }
/// Calls a GPU multivalue method. /// Calls a GPU multivalue method.
void Puller::CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, void Puller::CallMultiMethod(DmaPusher& dma_pusher, u32 method, u32 subchannel, const u32* base_start, u32 amount, u32 methods_pending) {
u32 methods_pending) {
LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method, subchannel); LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method, subchannel);
ASSERT(subchannel < bound_engines.size()); ASSERT(subchannel < bound_engines.size());
if (ExecuteMethodOnEngine(dma_pusher, method)) {
if (ExecuteMethodOnEngine(method)) { CallEngineMultiMethod(dma_pusher, method, subchannel, base_start, amount, methods_pending);
CallEngineMultiMethod(method, subchannel, base_start, amount, methods_pending);
} else { } else {
for (u32 i = 0; i < amount; i++) { for (u32 i = 0; i < amount; i++) {
CallPullerMethod(MethodCall{ CallPullerMethod(dma_pusher, MethodCall{
method, method,
base_start[i], base_start[i],
subchannel, subchannel,
@ -298,13 +279,9 @@ void Puller::CallMultiMethod(u32 method, u32 subchannel, const u32* base_start,
} }
} }
void Puller::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
rasterizer = rasterizer_;
}
/// Determines where the method should be executed. /// Determines where the method should be executed.
[[nodiscard]] bool Puller::ExecuteMethodOnEngine(u32 method) { [[nodiscard]] bool Puller::ExecuteMethodOnEngine(DmaPusher& dma_pusher, u32 method) {
const auto buffer_method = static_cast<BufferMethods>(method); const auto buffer_method = BufferMethods(method);
return buffer_method >= BufferMethods::NonPullerMethods; return buffer_method >= BufferMethods::NonPullerMethods;
} }

View file

@ -70,32 +70,13 @@ public:
BitField<8, 24, u32> syncpoint_id; BitField<8, 24, u32> syncpoint_id;
}; };
explicit Puller(GPU& gpu_, MemoryManager& memory_manager_, DmaPusher& dma_pusher, void CallMethod(DmaPusher& dma_pusher, const MethodCall& method_call);
Control::ChannelState& channel_state); void CallMultiMethod(DmaPusher& dma_pusher, u32 method, u32 subchannel, const u32* base_start, u32 amount, u32 methods_pending);
~Puller(); void BindRasterizer(DmaPusher& dma_pusher, VideoCore::RasterizerInterface* rasterizer);
void CallPullerMethod(DmaPusher& dma_pusher, const MethodCall& method_call);
void CallMethod(const MethodCall& method_call); void CallEngineMethod(DmaPusher& dma_pusher, const MethodCall& method_call);
void CallEngineMultiMethod(DmaPusher& dma_pusher, u32 method, u32 subchannel, const u32* base_start, u32 amount, u32 methods_pending);
void CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount,
u32 methods_pending);
void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
void CallPullerMethod(const MethodCall& method_call);
void CallEngineMethod(const MethodCall& method_call);
void CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount,
u32 methods_pending);
private: private:
Tegra::GPU& gpu;
MemoryManager& memory_manager;
DmaPusher& dma_pusher;
Control::ChannelState& channel_state;
VideoCore::RasterizerInterface* rasterizer = nullptr;
static constexpr std::size_t NUM_REGS = 0x800; static constexpr std::size_t NUM_REGS = 0x800;
struct Regs { struct Regs {
static constexpr size_t NUM_REGS = 0x40; static constexpr size_t NUM_REGS = 0x40;
@ -139,12 +120,12 @@ private:
}; };
} regs{}; } regs{};
void ProcessBindMethod(const MethodCall& method_call); void ProcessBindMethod(DmaPusher& dma_pusher, const MethodCall& method_call);
void ProcessFenceActionMethod(); void ProcessFenceActionMethod(DmaPusher& dma_pusher);
void ProcessSemaphoreAcquire(); void ProcessSemaphoreAcquire(DmaPusher& dma_pusher);
void ProcessSemaphoreRelease(); void ProcessSemaphoreRelease(DmaPusher& dma_pusher);
void ProcessSemaphoreTriggerMethod(); void ProcessSemaphoreTriggerMethod(DmaPusher& dma_pusher);
[[nodiscard]] bool ExecuteMethodOnEngine(u32 method); [[nodiscard]] bool ExecuteMethodOnEngine(DmaPusher& dma_pusher, u32 method);
/// Mapping of command subchannels to their bound engine ids /// Mapping of command subchannels to their bound engine ids
std::array<EngineID, 8> bound_engines{}; std::array<EngineID, 8> bound_engines{};
@ -157,8 +138,7 @@ private:
}; };
#define ASSERT_REG_POSITION(field_name, position) \ #define ASSERT_REG_POSITION(field_name, position) \
static_assert(offsetof(Regs, field_name) == position * 4, \ static_assert(offsetof(Regs, field_name) == position * 4, "Field " #field_name " has invalid position")
"Field " #field_name " has invalid position")
ASSERT_REG_POSITION(semaphore_address, 0x4); ASSERT_REG_POSITION(semaphore_address, 0x4);
ASSERT_REG_POSITION(semaphore_sequence, 0x6); ASSERT_REG_POSITION(semaphore_sequence, 0x6);

View file

@ -40,30 +40,31 @@
namespace Tegra { namespace Tegra {
struct GPU::Impl { struct GPU::Impl {
explicit Impl(GPU& gpu_, Core::System& system_, bool is_async_, bool use_nvdec_) explicit Impl(Core::System& system_, bool is_async_, bool use_nvdec_)
: gpu{gpu_}, system{system_}, host1x{system.Host1x()}, use_nvdec{use_nvdec_}, : system{system_}
shader_notify{std::make_unique<VideoCore::ShaderNotify>()}, is_async{is_async_}, , use_nvdec{use_nvdec_}
gpu_thread{system_, is_async_}, scheduler{std::make_unique<Control::Scheduler>(gpu)} {} , shader_notify()
, is_async{is_async_}
, gpu_thread{system_}
{}
~Impl() = default; ~Impl() = default;
std::shared_ptr<Control::ChannelState> CreateChannel(s32 channel_id) { std::shared_ptr<Control::ChannelState> CreateChannel(s32 channel_id) {
auto channel_state = std::make_shared<Tegra::Control::ChannelState>(channel_id); auto channel_state = std::make_shared<Tegra::Control::ChannelState>(channel_id);
channels.emplace(channel_id, channel_state); channels.emplace(channel_id, channel_state);
scheduler->DeclareChannel(channel_state); scheduler.DeclareChannel(channel_state);
return channel_state; return channel_state;
} }
void BindChannel(s32 channel_id) { void BindChannel(s32 channel_id) {
if (bound_channel == channel_id) { if (bound_channel != channel_id) {
return; auto it = channels.find(channel_id);
ASSERT(it != channels.end());
bound_channel = channel_id;
current_channel = it->second.get();
renderer->ReadRasterizer()->BindChannel(*current_channel);
} }
auto it = channels.find(channel_id);
ASSERT(it != channels.end());
bound_channel = channel_id;
current_channel = it->second.get();
rasterizer->BindChannel(*current_channel);
} }
std::shared_ptr<Control::ChannelState> AllocateChannel() { std::shared_ptr<Control::ChannelState> AllocateChannel() {
@ -71,13 +72,13 @@ struct GPU::Impl {
} }
void InitChannel(Control::ChannelState& to_init, u64 program_id) { void InitChannel(Control::ChannelState& to_init, u64 program_id) {
to_init.Init(system, gpu, program_id); to_init.Init(system, program_id);
to_init.BindRasterizer(rasterizer); to_init.BindRasterizer(renderer->ReadRasterizer());
rasterizer->InitializeChannel(to_init); renderer->ReadRasterizer()->InitializeChannel(to_init);
} }
void InitAddressSpace(Tegra::MemoryManager& memory_manager) { void InitAddressSpace(Tegra::MemoryManager& memory_manager) {
memory_manager.BindRasterizer(rasterizer); memory_manager.BindRasterizer(renderer->ReadRasterizer());
} }
void ReleaseChannel(Control::ChannelState& to_release) { void ReleaseChannel(Control::ChannelState& to_release) {
@ -87,26 +88,26 @@ struct GPU::Impl {
/// Binds a renderer to the GPU. /// Binds a renderer to the GPU.
void BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer_) { void BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer_) {
renderer = std::move(renderer_); renderer = std::move(renderer_);
rasterizer = renderer->ReadRasterizer(); system.Host1x().memory_manager.BindInterface(renderer->ReadRasterizer());
host1x.MemoryManager().BindInterface(rasterizer); system.Host1x().gmmu_manager.BindRasterizer(renderer->ReadRasterizer());
host1x.gmmu_manager.BindRasterizer(rasterizer);
} }
/// Flush all current written commands into the host GPU for execution. /// Flush all current written commands into the host GPU for execution.
void FlushCommands() { void FlushCommands() {
rasterizer->FlushCommands(); renderer->ReadRasterizer()->FlushCommands();
} }
/// Synchronizes CPU writes with Host GPU memory. /// Synchronizes CPU writes with Host GPU memory.
void InvalidateGPUCache() { void InvalidateGPUCache() {
std::function<void(PAddr, size_t)> callback_writes( std::function<void(PAddr, size_t)> callback_writes([this](PAddr address, size_t size) {
[this](PAddr address, size_t size) { rasterizer->OnCacheInvalidation(address, size); }); renderer->ReadRasterizer()->OnCacheInvalidation(address, size);
});
system.GatherGPUDirtyMemory(callback_writes); system.GatherGPUDirtyMemory(callback_writes);
} }
/// Signal the ending of command list. /// Signal the ending of command list.
void OnCommandListEnd() { void OnCommandListEnd() {
rasterizer->ReleaseFences(false); renderer->ReadRasterizer()->ReleaseFences(false);
Settings::UpdateGPUAccuracy(); Settings::UpdateGPUAccuracy();
} }
@ -143,62 +144,6 @@ struct GPU::Impl {
} }
} }
/// Returns a reference to the Maxwell3D GPU engine.
[[nodiscard]] Engines::Maxwell3D& Maxwell3D() {
ASSERT(current_channel);
return *current_channel->maxwell_3d;
}
/// Returns a const reference to the Maxwell3D GPU engine.
[[nodiscard]] const Engines::Maxwell3D& Maxwell3D() const {
ASSERT(current_channel);
return *current_channel->maxwell_3d;
}
/// Returns a reference to the KeplerCompute GPU engine.
[[nodiscard]] Engines::KeplerCompute& KeplerCompute() {
ASSERT(current_channel);
return *current_channel->kepler_compute;
}
/// Returns a reference to the KeplerCompute GPU engine.
[[nodiscard]] const Engines::KeplerCompute& KeplerCompute() const {
ASSERT(current_channel);
return *current_channel->kepler_compute;
}
/// Returns a reference to the GPU DMA pusher.
[[nodiscard]] Tegra::DmaPusher& DmaPusher() {
ASSERT(current_channel);
return *current_channel->dma_pusher;
}
/// Returns a const reference to the GPU DMA pusher.
[[nodiscard]] const Tegra::DmaPusher& DmaPusher() const {
ASSERT(current_channel);
return *current_channel->dma_pusher;
}
/// Returns a reference to the underlying renderer.
[[nodiscard]] VideoCore::RendererBase& Renderer() {
return *renderer;
}
/// Returns a const reference to the underlying renderer.
[[nodiscard]] const VideoCore::RendererBase& Renderer() const {
return *renderer;
}
/// Returns a reference to the shader notifier.
[[nodiscard]] VideoCore::ShaderNotify& ShaderNotify() {
return *shader_notify;
}
/// Returns a const reference to the shader notifier.
[[nodiscard]] const VideoCore::ShaderNotify& ShaderNotify() const {
return *shader_notify;
}
[[nodiscard]] u64 GetTicks() const { [[nodiscard]] u64 GetTicks() const {
u64 gpu_tick = system.CoreTiming().GetGPUTicks(); u64 gpu_tick = system.CoreTiming().GetGPUTicks();
Settings::GpuOverclock overclock = Settings::values.fast_gpu_time.GetValue(); Settings::GpuOverclock overclock = Settings::values.fast_gpu_time.GetValue();
@ -210,14 +155,6 @@ struct GPU::Impl {
return gpu_tick; return gpu_tick;
} }
[[nodiscard]] bool IsAsync() const {
return is_async;
}
[[nodiscard]] bool UseNvdec() const {
return use_nvdec;
}
void RendererFrameEndNotify() { void RendererFrameEndNotify() {
system.GetPerfStats().EndGameFrame(); system.GetPerfStats().EndGameFrame();
} }
@ -227,7 +164,7 @@ struct GPU::Impl {
/// core timing events. /// core timing events.
void Start() { void Start() {
Settings::UpdateGPUAccuracy(); Settings::UpdateGPUAccuracy();
gpu_thread.StartThread(*renderer, renderer->Context(), *scheduler); gpu_thread.StartThread(*renderer, renderer->Context(), scheduler);
} }
void NotifyShutdown() { void NotifyShutdown() {
@ -251,25 +188,24 @@ struct GPU::Impl {
/// Push GPU command entries to be processed /// Push GPU command entries to be processed
void PushGPUEntries(s32 channel, Tegra::CommandList&& entries) { void PushGPUEntries(s32 channel, Tegra::CommandList&& entries) {
gpu_thread.SubmitList(channel, std::move(entries)); gpu_thread.SubmitList(channel, std::move(entries), is_async);
} }
/// Notify rasterizer that any caches of the specified region should be flushed to Switch memory /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
void FlushRegion(DAddr addr, u64 size) { void FlushRegion(DAddr addr, u64 size) {
gpu_thread.FlushRegion(addr, size); gpu_thread.FlushRegion(addr, size, is_async);
} }
VideoCore::RasterizerDownloadArea OnCPURead(DAddr addr, u64 size) { VideoCore::RasterizerDownloadArea OnCPURead(DAddr addr, u64 size) {
auto raster_area = rasterizer->GetFlushArea(addr, size); auto raster_area = renderer->ReadRasterizer()->GetFlushArea(addr, size);
if (raster_area.preemtive) { if (raster_area.preemtive) {
return raster_area; return raster_area;
} }
raster_area.preemtive = true; raster_area.preemtive = true;
const u64 fence = RequestSyncOperation([this, &raster_area]() { const u64 fence = RequestSyncOperation([this, &raster_area]() {
rasterizer->FlushRegion(raster_area.start_address, renderer->ReadRasterizer()->FlushRegion(raster_area.start_address, raster_area.end_address - raster_area.start_address);
raster_area.end_address - raster_area.start_address);
}); });
gpu_thread.TickGPU(); gpu_thread.TickGPU(is_async);
WaitForSyncOperation(fence); WaitForSyncOperation(fence);
return raster_area; return raster_area;
} }
@ -280,16 +216,15 @@ struct GPU::Impl {
} }
bool OnCPUWrite(DAddr addr, u64 size) { bool OnCPUWrite(DAddr addr, u64 size) {
return rasterizer->OnCPUWrite(addr, size); return renderer->ReadRasterizer()->OnCPUWrite(addr, size);
} }
/// Notify rasterizer that any caches of the specified region should be flushed and invalidated /// Notify rasterizer that any caches of the specified region should be flushed and invalidated
void FlushAndInvalidateRegion(DAddr addr, u64 size) { void FlushAndInvalidateRegion(DAddr addr, u64 size) {
gpu_thread.FlushAndInvalidateRegion(addr, size); gpu_thread.FlushAndInvalidateRegion(addr, size, is_async);
} }
void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers, void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers, std::vector<Service::Nvidia::NvFence>&& fences) {
std::vector<Service::Nvidia::NvFence>&& fences) {
size_t num_fences{fences.size()}; size_t num_fences{fences.size()};
size_t current_request_counter{}; size_t current_request_counter{};
{ {
@ -304,7 +239,7 @@ struct GPU::Impl {
} }
} }
const auto wait_fence = RequestSyncOperation([this, current_request_counter, &layers, &fences, num_fences] { const auto wait_fence = RequestSyncOperation([this, current_request_counter, &layers, &fences, num_fences] {
auto& syncpoint_manager = host1x.GetSyncpointManager(); auto& syncpoint_manager = system.Host1x().GetSyncpointManager();
if (num_fences == 0) { if (num_fences == 0) {
renderer->Composite(layers); renderer->Composite(layers);
} }
@ -322,7 +257,7 @@ struct GPU::Impl {
syncpoint_manager.RegisterGuestAction(fences[i].id, fences[i].value, executer); syncpoint_manager.RegisterGuestAction(fences[i].id, fences[i].value, executer);
} }
}); });
gpu_thread.TickGPU(); gpu_thread.TickGPU(is_async);
WaitForSyncOperation(wait_fence); WaitForSyncOperation(wait_fence);
} }
@ -331,23 +266,20 @@ struct GPU::Impl {
const auto wait_fence = const auto wait_fence =
RequestSyncOperation([&] { out = renderer->GetAppletCaptureBuffer(); }); RequestSyncOperation([&] { out = renderer->GetAppletCaptureBuffer(); });
gpu_thread.TickGPU(); gpu_thread.TickGPU(is_async);
WaitForSyncOperation(wait_fence); WaitForSyncOperation(wait_fence);
return out; return out;
} }
GPU& gpu;
Core::System& system; Core::System& system;
Host1x::Host1x& host1x;
std::unique_ptr<VideoCore::RendererBase> renderer; std::unique_ptr<VideoCore::RendererBase> renderer;
VideoCore::RasterizerInterface* rasterizer = nullptr;
const bool use_nvdec; const bool use_nvdec;
s32 new_channel_id{1}; s32 new_channel_id{1};
/// Shader build notifier /// Shader build notifier
std::unique_ptr<VideoCore::ShaderNotify> shader_notify; VideoCore::ShaderNotify shader_notify;
/// When true, we are about to shut down emulation session, so terminate outstanding tasks /// When true, we are about to shut down emulation session, so terminate outstanding tasks
std::atomic_bool shutting_down{}; std::atomic_bool shutting_down{};
@ -371,7 +303,7 @@ struct GPU::Impl {
VideoCommon::GPUThread::ThreadManager gpu_thread; VideoCommon::GPUThread::ThreadManager gpu_thread;
std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context; std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context;
std::unique_ptr<Tegra::Control::Scheduler> scheduler; Tegra::Control::Scheduler scheduler;
ankerl::unordered_dense::map<s32, std::shared_ptr<Tegra::Control::ChannelState>> channels; ankerl::unordered_dense::map<s32, std::shared_ptr<Tegra::Control::ChannelState>> channels;
Tegra::Control::ChannelState* current_channel; Tegra::Control::ChannelState* current_channel;
s32 bound_channel{-1}; s32 bound_channel{-1};
@ -382,7 +314,8 @@ struct GPU::Impl {
}; };
GPU::GPU(Core::System& system, bool is_async, bool use_nvdec) GPU::GPU(Core::System& system, bool is_async, bool use_nvdec)
: impl{std::make_unique<Impl>(*this, system, is_async, use_nvdec)} {} : impl{std::make_unique<Impl>(system, is_async, use_nvdec)}
{}
GPU::~GPU() = default; GPU::~GPU() = default;
@ -423,8 +356,9 @@ void GPU::OnCommandListEnd() {
} }
u64 GPU::RequestFlush(DAddr addr, std::size_t size) { u64 GPU::RequestFlush(DAddr addr, std::size_t size) {
return impl->RequestSyncOperation( return impl->RequestSyncOperation([this, addr, size]() {
[this, addr, size]() { impl->rasterizer->FlushRegion(addr, size); }); impl->renderer->ReadRasterizer()->FlushRegion(addr, size);
});
} }
u64 GPU::CurrentSyncRequestFence() const { u64 GPU::CurrentSyncRequestFence() const {
@ -441,52 +375,52 @@ void GPU::TickWork() {
/// Gets a mutable reference to the Host1x interface /// Gets a mutable reference to the Host1x interface
Host1x::Host1x& GPU::Host1x() { Host1x::Host1x& GPU::Host1x() {
return impl->host1x; return impl->system.Host1x();
} }
/// Gets an immutable reference to the Host1x interface. /// Gets an immutable reference to the Host1x interface.
const Host1x::Host1x& GPU::Host1x() const { const Host1x::Host1x& GPU::Host1x() const {
return impl->host1x; return impl->system.Host1x();
} }
Engines::Maxwell3D& GPU::Maxwell3D() { Engines::Maxwell3D& GPU::Maxwell3D() {
return impl->Maxwell3D(); return impl->current_channel->payload->maxwell_3d;
} }
const Engines::Maxwell3D& GPU::Maxwell3D() const { const Engines::Maxwell3D& GPU::Maxwell3D() const {
return impl->Maxwell3D(); return impl->current_channel->payload->maxwell_3d;
} }
Engines::KeplerCompute& GPU::KeplerCompute() { Engines::KeplerCompute& GPU::KeplerCompute() {
return impl->KeplerCompute(); return impl->current_channel->payload->kepler_compute;
} }
const Engines::KeplerCompute& GPU::KeplerCompute() const { const Engines::KeplerCompute& GPU::KeplerCompute() const {
return impl->KeplerCompute(); return impl->current_channel->payload->kepler_compute;
} }
Tegra::DmaPusher& GPU::DmaPusher() { Tegra::DmaPusher& GPU::DmaPusher() {
return impl->DmaPusher(); return impl->current_channel->payload->dma_pusher;
} }
const Tegra::DmaPusher& GPU::DmaPusher() const { const Tegra::DmaPusher& GPU::DmaPusher() const {
return impl->DmaPusher(); return impl->current_channel->payload->dma_pusher;
} }
VideoCore::RendererBase& GPU::Renderer() { VideoCore::RendererBase& GPU::Renderer() {
return impl->Renderer(); return *impl->renderer;
} }
const VideoCore::RendererBase& GPU::Renderer() const { const VideoCore::RendererBase& GPU::Renderer() const {
return impl->Renderer(); return *impl->renderer;
} }
VideoCore::ShaderNotify& GPU::ShaderNotify() { VideoCore::ShaderNotify& GPU::ShaderNotify() {
return impl->ShaderNotify(); return impl->shader_notify;
} }
const VideoCore::ShaderNotify& GPU::ShaderNotify() const { const VideoCore::ShaderNotify& GPU::ShaderNotify() const {
return impl->ShaderNotify(); return impl->shader_notify;
} }
void GPU::RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers, void GPU::RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers,
@ -503,11 +437,11 @@ u64 GPU::GetTicks() const {
} }
bool GPU::IsAsync() const { bool GPU::IsAsync() const {
return impl->IsAsync(); return impl->is_async;
} }
bool GPU::UseNvdec() const { bool GPU::UseNvdec() const {
return impl->UseNvdec(); return impl->use_nvdec;
} }
void GPU::RendererFrameEndNotify() { void GPU::RendererFrameEndNotify() {

View file

@ -19,8 +19,9 @@
namespace VideoCommon::GPUThread { namespace VideoCommon::GPUThread {
ThreadManager::ThreadManager(Core::System& system_, bool is_async_) ThreadManager::ThreadManager(Core::System& system_)
: system{system_}, is_async{is_async_} {} : system{system_}
{}
ThreadManager::~ThreadManager() = default; ThreadManager::~ThreadManager() = default;
@ -39,7 +40,7 @@ void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Core::Fronten
break; break;
} }
if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) { if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) {
scheduler.Push(submit_list->channel, std::move(submit_list->entries)); scheduler.Push(system.GPU(), submit_list->channel, std::move(submit_list->entries));
} else if (std::holds_alternative<GPUTickCommand>(next.data)) { } else if (std::holds_alternative<GPUTickCommand>(next.data)) {
system.GPU().TickWork(); system.GPU().TickWork();
} else if (const auto* flush = std::get_if<FlushRegionCommand>(&next.data)) { } else if (const auto* flush = std::get_if<FlushRegionCommand>(&next.data)) {
@ -60,41 +61,40 @@ void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Core::Fronten
}); });
} }
void ThreadManager::SubmitList(s32 channel, Tegra::CommandList&& entries) { void ThreadManager::SubmitList(s32 channel, Tegra::CommandList&& entries, bool is_async) {
PushCommand(SubmitListCommand(channel, std::move(entries))); PushCommand(SubmitListCommand(channel, std::move(entries)), false, is_async);
} }
void ThreadManager::FlushRegion(DAddr addr, u64 size) { void ThreadManager::FlushRegion(DAddr addr, u64 size, bool is_async) {
if (!is_async) { if (!is_async) {
// Always flush with synchronous GPU mode // Always flush with synchronous GPU mode
PushCommand(FlushRegionCommand(addr, size)); PushCommand(FlushRegionCommand(addr, size), false, is_async);
} }
return;
} }
void ThreadManager::TickGPU() { void ThreadManager::TickGPU(bool is_async) {
PushCommand(GPUTickCommand()); PushCommand(GPUTickCommand(), false, is_async);
} }
void ThreadManager::InvalidateRegion(DAddr addr, u64 size) { void ThreadManager::InvalidateRegion(DAddr addr, u64 size) {
rasterizer->OnCacheInvalidation(addr, size); rasterizer->OnCacheInvalidation(addr, size);
} }
void ThreadManager::FlushAndInvalidateRegion(DAddr addr, u64 size) { void ThreadManager::FlushAndInvalidateRegion(DAddr addr, u64 size, bool is_async) {
if (Settings::IsGPULevelHigh()) { if (Settings::IsGPULevelHigh()) {
if (!is_async) { if (!is_async) {
PushCommand(FlushRegionCommand(addr, size)); PushCommand(FlushRegionCommand(addr, size), false, is_async);
} else { } else {
auto& gpu = system.GPU(); auto& gpu = system.GPU();
const u64 fence = gpu.RequestFlush(addr, size); const u64 fence = gpu.RequestFlush(addr, size);
TickGPU(); TickGPU(is_async);
gpu.WaitForSyncOperation(fence); gpu.WaitForSyncOperation(fence);
} }
} }
rasterizer->OnCacheInvalidation(addr, size); rasterizer->OnCacheInvalidation(addr, size);
} }
u64 ThreadManager::PushCommand(CommandData&& command_data, bool block) { u64 ThreadManager::PushCommand(CommandData&& command_data, bool block, bool is_async) {
if (!is_async) { if (!is_async) {
// In synchronous GPU mode, block the caller until the command has executed // In synchronous GPU mode, block the caller until the command has executed
block = true; block = true;

View file

@ -15,6 +15,7 @@
#include "common/bounded_threadsafe_queue.h" #include "common/bounded_threadsafe_queue.h"
#include "common/polyfill_thread.h" #include "common/polyfill_thread.h"
#include "video_core/dma_pusher.h"
#include "video_core/framebuffer_config.h" #include "video_core/framebuffer_config.h"
namespace Tegra { namespace Tegra {
@ -103,7 +104,7 @@ struct SynchState final {
/// Class used to manage the GPU thread /// Class used to manage the GPU thread
class ThreadManager final { class ThreadManager final {
public: public:
explicit ThreadManager(Core::System& system_, bool is_async_); explicit ThreadManager(Core::System& system_);
~ThreadManager(); ~ThreadManager();
/// Creates and starts the GPU thread. /// Creates and starts the GPU thread.
@ -111,27 +112,25 @@ public:
Tegra::Control::Scheduler& scheduler); Tegra::Control::Scheduler& scheduler);
/// Push GPU command entries to be processed /// Push GPU command entries to be processed
void SubmitList(s32 channel, Tegra::CommandList&& entries); void SubmitList(s32 channel, Tegra::CommandList&& entries, bool is_async);
/// Notify rasterizer that any caches of the specified region should be flushed to Switch memory /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
void FlushRegion(DAddr addr, u64 size); void FlushRegion(DAddr addr, u64 size, bool is_async);
/// Notify rasterizer that any caches of the specified region should be invalidated /// Notify rasterizer that any caches of the specified region should be invalidated
void InvalidateRegion(DAddr addr, u64 size); void InvalidateRegion(DAddr addr, u64 size);
/// Notify rasterizer that any caches of the specified region should be flushed and invalidated /// Notify rasterizer that any caches of the specified region should be flushed and invalidated
void FlushAndInvalidateRegion(DAddr addr, u64 size); void FlushAndInvalidateRegion(DAddr addr, u64 size, bool is_async);
void TickGPU(); void TickGPU(bool is_async);
private: private:
/// Pushes a command to be executed by the GPU thread /// Pushes a command to be executed by the GPU thread
u64 PushCommand(CommandData&& command_data, bool block = false); u64 PushCommand(CommandData&& command_data, bool block, bool is_async);
Core::System& system; Core::System& system;
const bool is_async;
VideoCore::RasterizerInterface* rasterizer = nullptr; VideoCore::RasterizerInterface* rasterizer = nullptr;
SynchState state; SynchState state;
std::jthread thread; std::jthread thread;
}; };

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project // SPDX-FileCopyrightText: 2021 yuzu Emulator Project
@ -10,26 +10,22 @@
namespace Tegra::Host1x { namespace Tegra::Host1x {
Control::Control(Host1x& host1x_) : host1x(host1x_) {} void Control::ProcessMethod(Host1x& host1x, Method method, u32 argument) {
Control::~Control() = default;
void Control::ProcessMethod(Method method, u32 argument) {
switch (method) { switch (method) {
case Method::LoadSyncptPayload32: case Method::LoadSyncptPayload32:
syncpoint_value = argument; syncpoint_value = argument;
break; break;
case Method::WaitSyncpt: case Method::WaitSyncpt:
case Method::WaitSyncpt32: case Method::WaitSyncpt32:
Execute(argument); Execute(host1x, argument);
break; break;
default: default:
UNIMPLEMENTED_MSG("Control method {:#X}", static_cast<u32>(method)); UNIMPLEMENTED_MSG("Control method {:#X}", u32(method));
break; break;
} }
} }
void Control::Execute(u32 data) { void Control::Execute(Host1x& host1x, u32 data) {
LOG_TRACE(Service_NVDRV, "Control wait syncpt {} value {}", data, syncpoint_value); LOG_TRACE(Service_NVDRV, "Control wait syncpt {} value {}", data, syncpoint_value);
host1x.GetSyncpointManager().WaitHost(data, syncpoint_value); host1x.GetSyncpointManager().WaitHost(data, syncpoint_value);
} }

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project // SPDX-FileCopyrightText: 2021 yuzu Emulator Project
// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors // SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
@ -19,17 +22,11 @@ public:
WaitSyncpt32 = 0x50, WaitSyncpt32 = 0x50,
}; };
explicit Control(Host1x& host1x);
~Control();
/// Writes the method into the state, Invoke Execute() if encountered /// Writes the method into the state, Invoke Execute() if encountered
void ProcessMethod(Method method, u32 argument); void ProcessMethod(Host1x& host1x, Method method, u32 argument);
private:
/// For Host1x, execute is waiting on a syncpoint previously written into the state /// For Host1x, execute is waiting on a syncpoint previously written into the state
void Execute(u32 data); void Execute(Host1x& host1x, u32 data);
Host1x& host1x;
u32 syncpoint_value{}; u32 syncpoint_value{};
}; };

View file

@ -27,22 +27,22 @@ void Host1x::StartDevice(s32 fd, ChannelType type, u32 syncpt) {
#ifdef YUZU_LEGACY #ifdef YUZU_LEGACY
std::call_once(nvdec_first_init, []() {std::this_thread::sleep_for(std::chrono::milliseconds{500});}); // HACK: For Astroneer std::call_once(nvdec_first_init, []() {std::this_thread::sleep_for(std::chrono::milliseconds{500});}); // HACK: For Astroneer
#endif #endif
devices[fd] = std::make_unique<Tegra::Host1x::Nvdec>(*this, fd, syncpt); devices[fd].emplace<Tegra::Host1x::Nvdec>(*this, fd, syncpt);
break; break;
case ChannelType::VIC: case ChannelType::VIC:
#ifdef YUZU_LEGACY #ifdef YUZU_LEGACY
std::call_once(vic_first_init, []() {std::this_thread::sleep_for(std::chrono::milliseconds{500});}); // HACK: For Astroneer std::call_once(vic_first_init, []() {std::this_thread::sleep_for(std::chrono::milliseconds{500});}); // HACK: For Astroneer
#endif #endif
devices[fd] = std::make_unique<Tegra::Host1x::Vic>(*this, fd, syncpt); devices[fd].emplace<Tegra::Host1x::Vic>(*this, fd, syncpt);
break; break;
default: default:
LOG_ERROR(HW_GPU, "Unimplemented host1x device {}", static_cast<u32>(type)); LOG_ERROR(HW_GPU, "Unimplemented host1x device {}", u32(type));
break; break;
} }
} }
void Host1x::StopDevice(s32 fd, ChannelType type) { void Host1x::StopDevice(s32 fd, ChannelType type) {
devices.erase(fd); devices[fd].emplace<std::monostate>();
} }
} // namespace Tegra::Host1x } // namespace Tegra::Host1x

View file

@ -8,10 +8,14 @@
#include <ankerl/unordered_dense.h> #include <ankerl/unordered_dense.h>
#include <unordered_map> #include <unordered_map>
#include <queue> #include <variant>
#include "common/common_types.h" #include "common/common_types.h"
// fd types?
#include "video_core/host1x/nvdec.h"
#include "video_core/host1x/vic.h"
#include "common/address_space.h" #include "common/address_space.h"
#include "video_core/cdma_pusher.h" #include "video_core/cdma_pusher.h"
#include "video_core/host1x/gpu_device_memory_manager.h" #include "video_core/host1x/gpu_device_memory_manager.h"
@ -31,118 +35,90 @@ class Nvdec;
class FrameQueue { class FrameQueue {
public: public:
struct FrameDevice {
std::deque<std::pair<u64, std::shared_ptr<FFmpeg::Frame>>> m_presentation_order;
std::unordered_map<u64, std::shared_ptr<FFmpeg::Frame>> m_decode_order;
};
void Open(s32 fd) { void Open(s32 fd) {
std::scoped_lock l{m_mutex}; std::scoped_lock l{m_mutex};
m_presentation_order.insert({fd, {}}); m_frame_devices.insert_or_assign(fd, FrameDevice{});
m_decode_order.insert({fd, {}});
} }
void Close(s32 fd) { void Close(s32 fd) {
std::scoped_lock l{m_mutex}; std::scoped_lock l{m_mutex};
m_presentation_order.erase(fd); m_frame_devices.erase(fd);
m_decode_order.erase(fd);
} }
s32 VicFindNvdecFdFromOffset(u64 search_offset) { s32 VicFindNvdecFdFromOffset(u64 search_offset) {
std::scoped_lock l{m_mutex}; std::scoped_lock l{m_mutex};
for (auto& map : m_presentation_order) { for (auto const& [fd, dev] : m_frame_devices) {
for (auto& [offset, frame] : map.second) { for (auto const& [offset, frame] : dev.m_presentation_order)
if (offset == search_offset) { if (offset == search_offset)
return map.first; return fd;
} for (auto const& [offset, frame] : dev.m_decode_order)
} if (offset == search_offset)
} return fd;
for (auto& map : m_decode_order) {
for (auto& [offset, frame] : map.second) {
if (offset == search_offset) {
return map.first;
}
}
} }
return -1; return -1;
} }
void PushPresentOrder(s32 fd, u64 offset, std::shared_ptr<FFmpeg::Frame>&& frame) { void PushPresentOrder(s32 fd, u64 offset, std::shared_ptr<FFmpeg::Frame>&& frame) {
std::scoped_lock l{m_mutex}; std::scoped_lock l{m_mutex};
auto map = m_presentation_order.find(fd); if (auto const it = m_frame_devices.find(fd); it != m_frame_devices.end()) {
if (map == m_presentation_order.end()) { if (it->second.m_presentation_order.size() >= MAX_PRESENT_QUEUE)
return; it->second.m_presentation_order.pop_front();
it->second.m_presentation_order.emplace_back(offset, std::move(frame));
} }
if (map->second.size() >= MAX_PRESENT_QUEUE) {
map->second.pop_front();
}
map->second.emplace_back(offset, std::move(frame));
} }
void PushDecodeOrder(s32 fd, u64 offset, std::shared_ptr<FFmpeg::Frame>&& frame) { void PushDecodeOrder(s32 fd, u64 offset, std::shared_ptr<FFmpeg::Frame>&& frame) {
std::scoped_lock l{m_mutex}; std::scoped_lock l{m_mutex};
auto map = m_decode_order.find(fd); if (auto const it = m_frame_devices.find(fd); it != m_frame_devices.end()) {
if (map == m_decode_order.end()) { it->second.m_decode_order.insert_or_assign(offset, std::move(frame));
return; if (it->second.m_decode_order.size() > MAX_DECODE_MAP) {
} auto it2 = it->second.m_decode_order.begin();
std::advance(it2, it->second.m_decode_order.size() - MAX_DECODE_MAP);
map->second.insert_or_assign(offset, std::move(frame)); it->second.m_decode_order.erase(it->second.m_decode_order.begin(), it2);
}
if (map->second.size() > MAX_DECODE_MAP) {
auto it = map->second.begin();
std::advance(it, map->second.size() - MAX_DECODE_MAP);
map->second.erase(map->second.begin(), it);
} }
} }
std::shared_ptr<FFmpeg::Frame> GetFrame(s32 fd, u64 offset) { std::shared_ptr<FFmpeg::Frame> GetFrame(s32 fd, u64 offset) {
if (fd == -1) { if (fd != -1) {
return {}; std::scoped_lock l{m_mutex};
if (auto const it = m_frame_devices.find(fd); it != m_frame_devices.end()) {
if (it->second.m_presentation_order.size() > 0)
return GetPresentOrderLocked(fd);
if (it->second.m_decode_order.size() > 0)
return GetDecodeOrderLocked(fd, offset);
}
} }
std::scoped_lock l{m_mutex};
auto present_map = m_presentation_order.find(fd);
if (present_map != m_presentation_order.end() && !present_map->second.empty()) {
return GetPresentOrderLocked(fd);
}
auto decode_map = m_decode_order.find(fd);
if (decode_map != m_decode_order.end() && !decode_map->second.empty()) {
return GetDecodeOrderLocked(fd, offset);
}
return {}; return {};
} }
private: private:
std::shared_ptr<FFmpeg::Frame> GetPresentOrderLocked(s32 fd) { std::shared_ptr<FFmpeg::Frame> GetPresentOrderLocked(s32 fd) {
auto map = m_presentation_order.find(fd); if (auto const it = m_frame_devices.find(fd); it != m_frame_devices.end()) {
if (map == m_presentation_order.end() || map->second.empty()) { auto frame = std::move(it->second.m_presentation_order.front().second);
return {}; it->second.m_presentation_order.pop_front();
return frame;
} }
return {};
auto frame = std::move(map->second.front().second);
map->second.pop_front();
return frame;
} }
std::shared_ptr<FFmpeg::Frame> GetDecodeOrderLocked(s32 fd, u64 offset) { std::shared_ptr<FFmpeg::Frame> GetDecodeOrderLocked(s32 fd, u64 offset) {
auto map = m_decode_order.find(fd); if (auto const it = m_frame_devices.find(fd); it != m_frame_devices.end()) {
if (map == m_decode_order.end() || map->second.empty()) { if (auto const it2 = it->second.m_decode_order.find(offset); it2 != it->second.m_decode_order.end()) {
return {}; // TODO: this "mapped" prevents us from fully embracing ankerl
return std::move(it->second.m_decode_order.extract(it2).mapped());
}
} }
return {};
auto it = map->second.find(offset);
if (it == map->second.end()) {
return {};
}
// TODO: this "mapped" prevents us from fully embracing ankerl
return std::move(map->second.extract(it).mapped());
} }
using FramePtr = std::shared_ptr<FFmpeg::Frame>;
std::mutex m_mutex{}; std::mutex m_mutex{};
ankerl::unordered_dense::map<s32, std::deque<std::pair<u64, FramePtr>>> m_presentation_order; ankerl::unordered_dense::map<s32, FrameDevice> m_frame_devices;
ankerl::unordered_dense::map<s32, std::unordered_map<u64, FramePtr>> m_decode_order;
static constexpr size_t MAX_PRESENT_QUEUE = 100; static constexpr size_t MAX_PRESENT_QUEUE = 100;
static constexpr size_t MAX_DECODE_MAP = 200; static constexpr size_t MAX_DECODE_MAP = 200;
@ -196,11 +172,11 @@ public:
void StopDevice(s32 fd, ChannelType type); void StopDevice(s32 fd, ChannelType type);
void PushEntries(s32 fd, ChCommandHeaderList&& entries) { void PushEntries(s32 fd, ChCommandHeaderList&& entries) {
auto it = devices.find(fd); if (auto const nvdec = std::get_if<Tegra::Host1x::Nvdec>(&devices[fd])) {
if (it == devices.end()) { nvdec->PushEntries(std::move(entries));
return; } else if (auto const vic = std::get_if<Tegra::Host1x::Vic>(&devices[fd])) {
vic->PushEntries(std::move(entries));
} }
it->second->PushEntries(std::move(entries));
} }
Core::System& system; Core::System& system;
@ -209,7 +185,11 @@ public:
Tegra::MemoryManager gmmu_manager; Tegra::MemoryManager gmmu_manager;
Common::FlatAllocator<u32, 0, 32> allocator; Common::FlatAllocator<u32, 0, 32> allocator;
FrameQueue frame_queue; FrameQueue frame_queue;
ankerl::unordered_dense::map<s32, std::unique_ptr<CDmaPusher>> devices; std::array<std::variant<
std::monostate,
Tegra::Host1x::Nvdec,
Tegra::Host1x::Vic
>, 1024> devices;
#ifdef YUZU_LEGACY #ifdef YUZU_LEGACY
std::once_flag nvdec_first_init; std::once_flag nvdec_first_init;
std::once_flag vic_first_init; std::once_flag vic_first_init;

View file

@ -407,15 +407,15 @@ void Vic::ReadInterlacedY8__V8U8_N420(const SlotStruct& slot, std::span<const Pl
}; };
switch (slot.config.deinterlace_mode) { switch (slot.config.deinterlace_mode) {
case DXVAHD_DEINTERLACE_MODE_PRIVATE::WEAVE: case DxvhadDeinterlaceModePrivate::Weave:
// Due to the fact that we do not write to memory in nvdec, we cannot use Weave as it // Due to the fact that we do not write to memory in nvdec, we cannot use Weave as it
// relies on the previous frame. // relies on the previous frame.
DecodeBobField(); DecodeBobField();
break; break;
case DXVAHD_DEINTERLACE_MODE_PRIVATE::BOB_FIELD: case DxvhadDeinterlaceModePrivate::BobField:
DecodeBobField(); DecodeBobField();
break; break;
case DXVAHD_DEINTERLACE_MODE_PRIVATE::DISI1: case DxvhadDeinterlaceModePrivate::Disi1:
// Due to the fact that we do not write to memory in nvdec, we cannot use DISI1 as it // Due to the fact that we do not write to memory in nvdec, we cannot use DISI1 as it
// relies on previous/next frames. // relies on previous/next frames.
DecodeBobField(); DecodeBobField();
@ -431,13 +431,13 @@ void Vic::ReadInterlacedY8__V8U8_N420(const SlotStruct& slot, std::span<const Pl
void Vic::ReadY8__V8U8_N420(const SlotStruct& slot, std::span<const PlaneOffsets> offsets, std::shared_ptr<const FFmpeg::Frame> frame, bool planar) noexcept { void Vic::ReadY8__V8U8_N420(const SlotStruct& slot, std::span<const PlaneOffsets> offsets, std::shared_ptr<const FFmpeg::Frame> frame, bool planar) noexcept {
switch (slot.config.frame_format) { switch (slot.config.frame_format) {
case DXVAHD_FRAME_FORMAT::PROGRESSIVE: case DxvhadFrameFormat::Progressive:
ReadProgressiveY8__V8U8_N420(slot, offsets, std::move(frame), planar, false); ReadProgressiveY8__V8U8_N420(slot, offsets, std::move(frame), planar, false);
break; break;
case DXVAHD_FRAME_FORMAT::TOP_FIELD: case DxvhadFrameFormat::TopField:
ReadInterlacedY8__V8U8_N420(slot, offsets, std::move(frame), planar, true); ReadInterlacedY8__V8U8_N420(slot, offsets, std::move(frame), planar, true);
break; break;
case DXVAHD_FRAME_FORMAT::BOTTOM_FIELD: case DxvhadFrameFormat::BottomField:
ReadInterlacedY8__V8U8_N420(slot, offsets, std::move(frame), planar, false); ReadInterlacedY8__V8U8_N420(slot, offsets, std::move(frame), planar, false);
break; break;
default: default:
@ -860,7 +860,7 @@ void Vic::WriteY8__V8U8_N420(const OutputSurfaceConfig& output_surface_config) n
}; };
switch (output_surface_config.out_block_kind) { switch (output_surface_config.out_block_kind) {
case BLK_KIND::GENERIC_16Bx2: { case BlkKind::Generic_16Bx2: {
u32 const block_height = u32(output_surface_config.out_block_height); u32 const block_height = u32(output_surface_config.out_block_height);
auto const out_luma_swizzle_size = Texture::CalculateSize(true, BytesPerPixel, out_luma_width, out_luma_height, 1, block_height, 0); auto const out_luma_swizzle_size = Texture::CalculateSize(true, BytesPerPixel, out_luma_width, out_luma_height, 1, block_height, 0);
auto const out_chroma_swizzle_size = Texture::CalculateSize(true, BytesPerPixel * 2, out_chroma_width, out_chroma_height, 1, block_height, 0); auto const out_chroma_swizzle_size = Texture::CalculateSize(true, BytesPerPixel * 2, out_chroma_width, out_chroma_height, 1, block_height, 0);
@ -889,7 +889,7 @@ void Vic::WriteY8__V8U8_N420(const OutputSurfaceConfig& output_surface_config) n
Texture::SwizzleTexture(out_chroma, chroma_scratch, BytesPerPixel, out_chroma_width, out_chroma_height, 1, block_height, 0, 1); Texture::SwizzleTexture(out_chroma, chroma_scratch, BytesPerPixel, out_chroma_width, out_chroma_height, 1, block_height, 0, 1);
} }
} break; } break;
case BLK_KIND::PITCH: { case BlkKind::Pitch: {
LOG_TRACE(HW_GPU, "Writing Y8__V8U8_N420 swizzled frame\n" LOG_TRACE(HW_GPU, "Writing Y8__V8U8_N420 swizzled frame\n"
"\tinput surface {}x{} stride {} size {:#X}\n" "\tinput surface {}x{} stride {} size {:#X}\n"
"\toutput luma {}x{} stride {} size {:#X} block height {} swizzled size 0x{:X}\n", "\toutput luma {}x{} stride {} size {:#X} block height {} swizzled size 0x{:X}\n",
@ -1032,7 +1032,7 @@ void Vic::WriteABGR(const OutputSurfaceConfig& output_surface_config, VideoPixel
}; };
switch (output_surface_config.out_block_kind) { switch (output_surface_config.out_block_kind) {
case BLK_KIND::GENERIC_16Bx2: { case BlkKind::Generic_16Bx2: {
const u32 block_height = u32(output_surface_config.out_block_height); const u32 block_height = u32(output_surface_config.out_block_height);
auto const out_swizzle_size = Texture::CalculateSize(true, BytesPerPixel, out_luma_width, out_luma_height, 1, block_height, 0); auto const out_swizzle_size = Texture::CalculateSize(true, BytesPerPixel, out_luma_width, out_luma_height, 1, block_height, 0);
LOG_TRACE(HW_GPU, "Writing ABGR swizzled frame\n" LOG_TRACE(HW_GPU, "Writing ABGR swizzled frame\n"
@ -1051,7 +1051,7 @@ void Vic::WriteABGR(const OutputSurfaceConfig& output_surface_config, VideoPixel
Texture::SwizzleTexture(out_luma, luma_scratch, BytesPerPixel, out_luma_width, out_luma_height, 1, block_height, 0, 1); Texture::SwizzleTexture(out_luma, luma_scratch, BytesPerPixel, out_luma_width, out_luma_height, 1, block_height, 0, 1);
} }
} break; } break;
case BLK_KIND::PITCH: { case BlkKind::Pitch: {
LOG_TRACE(HW_GPU, "Writing ABGR pitch frame\n" LOG_TRACE(HW_GPU, "Writing ABGR pitch frame\n"
"\tinput surface {}x{} stride {} size {:#X}" "\tinput surface {}x{} stride {} size {:#X}"
"\toutput surface {}x{} stride {} size {:#X}", "\toutput surface {}x{} stride {} size {:#X}",

View file

@ -6,16 +6,12 @@
#pragma once #pragma once
#include <condition_variable>
#include <functional>
#include <memory> #include <memory>
#include <mutex>
#include <thread>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/scratch_buffer.h" #include "common/scratch_buffer.h"
#include "video_core/cdma_pusher.h" #include "video_core/cdma_pusher.h"
#include "video_core/host1x/host1x.h" #include "video_core/host1x/ffmpeg.h"
namespace Tegra::Host1x { namespace Tegra::Host1x {
class Host1x; class Host1x;
@ -138,52 +134,53 @@ enum SurfaceIndex : u32 {
CombinedMotion = 7, CombinedMotion = 7,
}; };
enum class DXVAHD_ALPHA_FILL_MODE : u32 { // Note: these will inevitably collide with Win32 defines if you use their UPPER_SNAKE_CASE naming
OPAQUE = 0, enum class DxvhadAlphaFillMode : u32 {
BACKGROUND = 1, Opaque = 0,
DESTINATION = 2, Background = 1,
SOURCE_STREAM = 3, Destination = 2,
COMPOSITED = 4, SourceStream = 3,
SOURCE_ALPHA = 5, Composited = 4,
SourceAlpha = 5,
}; };
enum class DXVAHD_FRAME_FORMAT : u64 { enum class DxvhadFrameFormat : u64 {
PROGRESSIVE = 0, Progressive = 0,
INTERLACED_TOP_FIELD_FIRST = 1, InterlacedTopFieldFirst = 1,
INTERLACED_BOTTOM_FIELD_FIRST = 2, InterlacedBottomFieldFirst = 2,
TOP_FIELD = 3, TopField = 3,
BOTTOM_FIELD = 4, BottomField = 4,
SUBPIC_PROGRESSIVE = 5, SubpicProgressive = 5,
SUBPIC_INTERLACED_TOP_FIELD_FIRST = 6, SubpicInterlacedTopFieldFirst = 6,
SUBPIC_INTERLACED_BOTTOM_FIELD_FIRST = 7, SubpicInterlacedBottomFieldFirst = 7,
SUBPIC_TOP_FIELD = 8, SubpicTopField = 8,
SUBPIC_BOTTOM_FIELD = 9, SubpicBottomField = 9,
TOP_FIELD_CHROMA_BOTTOM = 10, TopFieldChromaBottom = 10,
BOTTOM_FIELD_CHROMA_TOP = 11, BottomFieldChromaTop = 11,
SUBPIC_TOP_FIELD_CHROMA_BOTTOM = 12, SubpicTopFieldChromaBottom = 12,
SUBPIC_BOTTOM_FIELD_CHROMA_TOP = 13, SubpicBottomFieldChromaTop = 13,
}; };
enum class DXVAHD_DEINTERLACE_MODE_PRIVATE : u64 { enum class DxvhadDeinterlaceModePrivate : u64 {
WEAVE = 0, Weave = 0,
BOB_FIELD = 1, BobField = 1,
BOB = 2, Bob = 2,
NEWBOB = 3, Newbob = 3,
DISI1 = 4, Disi1 = 4,
WEAVE_LUMA_BOB_FIELD_CHROMA = 5, WeaveLumaBobFieldChroma = 5,
MAX = 0xF, Max = 0xF,
}; };
enum class BLK_KIND { enum class BlkKind {
PITCH = 0, Pitch = 0,
GENERIC_16Bx2 = 1, Generic_16Bx2 = 1,
// These are unsupported in the vic // These are unsupported in the vic
BL_NAIVE = 2, BlNaive = 2,
BL_KEPLER_XBAR_RAW = 3, BlKeplerXbarRaw = 3,
VP2_TILED = 15, Vp2Tiled = 15,
}; };
enum class BLEND_SRCFACTC : u32 { enum class BlendSrcFactC : u32 {
K1 = 0, K1 = 0,
K1_TIMES_DST = 1, K1_TIMES_DST = 1,
NEG_K1_TIMES_DST = 2, NEG_K1_TIMES_DST = 2,
@ -191,7 +188,7 @@ enum class BLEND_SRCFACTC : u32 {
ZERO = 4, ZERO = 4,
}; };
enum class BLEND_DSTFACTC : u32 { enum class BlendDstFactC : u32 {
K1 = 0, K1 = 0,
K2 = 1, K2 = 1,
K1_TIMES_DST = 2, K1_TIMES_DST = 2,
@ -201,7 +198,7 @@ enum class BLEND_DSTFACTC : u32 {
ONE = 6, ONE = 6,
}; };
enum class BLEND_SRCFACTA : u32 { enum class BlendSrcFactA : u32 {
K1 = 0, K1 = 0,
K2 = 1, K2 = 1,
NEG_K1_TIMES_DST = 2, NEG_K1_TIMES_DST = 2,
@ -209,7 +206,7 @@ enum class BLEND_SRCFACTA : u32 {
MAX = 7, MAX = 7,
}; };
enum class BLEND_DSTFACTA : u32 { enum class BlendDstFactA : u32 {
K2 = 0, K2 = 0,
NEG_K1_TIMES_SRC = 1, NEG_K1_TIMES_SRC = 1,
ZERO = 2, ZERO = 2,
@ -232,7 +229,7 @@ static_assert(sizeof(PipeConfig) == 0x10, "PipeConfig has the wrong size!");
struct OutputConfig { struct OutputConfig {
union { union {
BitField<0, 3, DXVAHD_ALPHA_FILL_MODE> alpha_fill_mode; BitField<0, 3, DxvhadAlphaFillMode> alpha_fill_mode;
BitField<3, 3, u64> alpha_fill_slot; BitField<3, 3, u64> alpha_fill_slot;
BitField<6, 10, u64> background_a; BitField<6, 10, u64> background_a;
BitField<16, 10, u64> background_r; BitField<16, 10, u64> background_r;
@ -265,7 +262,7 @@ struct OutputSurfaceConfig {
BitField<0, 7, VideoPixelFormat> out_pixel_format; BitField<0, 7, VideoPixelFormat> out_pixel_format;
BitField<7, 2, u32> out_chroma_loc_horiz; BitField<7, 2, u32> out_chroma_loc_horiz;
BitField<9, 2, u32> out_chroma_loc_vert; BitField<9, 2, u32> out_chroma_loc_vert;
BitField<11, 4, BLK_KIND> out_block_kind; BitField<11, 4, BlkKind> out_block_kind;
BitField<15, 4, u32> out_block_height; // in gobs, log2 BitField<15, 4, u32> out_block_height; // in gobs, log2
BitField<19, 3, u32> reserved0; BitField<19, 3, u32> reserved0;
BitField<22, 10, u32> reserved1; BitField<22, 10, u32> reserved1;
@ -365,7 +362,7 @@ struct SlotConfig {
BitField<14, 1, u64> prev_prev_motion_field_enable; BitField<14, 1, u64> prev_prev_motion_field_enable;
BitField<15, 1, u64> combined_motion_field_enable; BitField<15, 1, u64> combined_motion_field_enable;
BitField<16, 4, DXVAHD_FRAME_FORMAT> frame_format; BitField<16, 4, DxvhadFrameFormat> frame_format;
BitField<20, 2, u64> filter_length_y; // 0: 1-tap, 1: 2-tap, 2: 5-tap, 3: 10-tap BitField<20, 2, u64> filter_length_y; // 0: 1-tap, 1: 2-tap, 2: 5-tap, 3: 10-tap
BitField<22, 2, u64> filter_length_x; BitField<22, 2, u64> filter_length_x;
BitField<24, 12, u64> panoramic; BitField<24, 12, u64> panoramic;
@ -377,7 +374,7 @@ struct SlotConfig {
BitField<10, 10, u64> filter_detail; BitField<10, 10, u64> filter_detail;
BitField<20, 10, u64> chroma_noise; BitField<20, 10, u64> chroma_noise;
BitField<30, 10, u64> chroma_detail; BitField<30, 10, u64> chroma_detail;
BitField<40, 4, DXVAHD_DEINTERLACE_MODE_PRIVATE> deinterlace_mode; BitField<40, 4, DxvhadDeinterlaceModePrivate> deinterlace_mode;
BitField<44, 3, u64> motion_accumulation_weight; BitField<44, 3, u64> motion_accumulation_weight;
BitField<47, 11, u64> noise_iir; BitField<47, 11, u64> noise_iir;
BitField<58, 4, u64> light_level; BitField<58, 4, u64> light_level;
@ -484,13 +481,13 @@ struct BlendingSlotStruct {
BitField<26, 6, u32> reserved1; BitField<26, 6, u32> reserved1;
}; };
union { union {
BitField<0, 3, BLEND_SRCFACTC> src_factor_color_match_select; BitField<0, 3, BlendSrcFactC> src_factor_color_match_select;
BitField<3, 1, u32> reserved2; BitField<3, 1, u32> reserved2;
BitField<4, 3, BLEND_DSTFACTC> dst_factor_color_match_select; BitField<4, 3, BlendDstFactC> dst_factor_color_match_select;
BitField<7, 1, u32> reserved3; BitField<7, 1, u32> reserved3;
BitField<8, 3, BLEND_SRCFACTA> src_factor_a_match_select; BitField<8, 3, BlendSrcFactA> src_factor_a_match_select;
BitField<11, 1, u32> reserved4; BitField<11, 1, u32> reserved4;
BitField<12, 3, BLEND_DSTFACTA> dst_factor_a_match_select; BitField<12, 3, BlendDstFactA> dst_factor_a_match_select;
BitField<15, 1, u32> reserved5; BitField<15, 1, u32> reserved5;
BitField<16, 4, u32> reserved6; BitField<16, 4, u32> reserved6;
BitField<20, 4, u32> reserved7; BitField<20, 4, u32> reserved7;
@ -624,8 +621,8 @@ private:
VicRegisters regs{}; VicRegisters regs{};
Common::ScratchBuffer<u8> swizzle_scratch; Common::ScratchBuffer<u8> swizzle_scratch;
Common::ScratchBuffer<Pixel> output_surface; Common::ScratchBuffer<Tegra::Host1x::Pixel> output_surface;
Common::ScratchBuffer<Pixel> slot_surface; Common::ScratchBuffer<Tegra::Host1x::Pixel> slot_surface;
Common::ScratchBuffer<u8> luma_scratch; Common::ScratchBuffer<u8> luma_scratch;
Common::ScratchBuffer<u8> chroma_scratch; Common::ScratchBuffer<u8> chroma_scratch;

View file

@ -64,10 +64,10 @@ bool IsTopologySafe(Maxwell3D::Regs::PrimitiveTopology topology) {
} // Anonymous namespace } // Anonymous namespace
void HLE_DrawArraysIndirect::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) { void HLE_DrawArraysIndirect::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[0]); auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[0]);
if (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology)) { if (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology)) {
Fallback(maxwell3d, parameters); Fallback(system, maxwell3d, parameters);
return; return;
} }
@ -93,7 +93,7 @@ void HLE_DrawArraysIndirect::Execute(Engines::Maxwell3D& maxwell3d, std::span<co
maxwell3d.replace_table.clear(); maxwell3d.replace_table.clear();
} }
} }
void HLE_DrawArraysIndirect::Fallback(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters) { void HLE_DrawArraysIndirect::Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters) {
SCOPE_EXIT { SCOPE_EXIT {
if (extended) { if (extended) {
maxwell3d.engine_state = Maxwell3D::EngineHint::None; maxwell3d.engine_state = Maxwell3D::EngineHint::None;
@ -123,10 +123,10 @@ void HLE_DrawArraysIndirect::Fallback(Engines::Maxwell3D& maxwell3d, std::span<c
} }
} }
void HLE_DrawIndexedIndirect::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) { void HLE_DrawIndexedIndirect::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[0]); auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[0]);
if (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology)) { if (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology)) {
Fallback(maxwell3d, parameters); Fallback(system, maxwell3d, parameters);
return; return;
} }
@ -161,7 +161,7 @@ void HLE_DrawIndexedIndirect::Execute(Engines::Maxwell3D& maxwell3d, std::span<c
maxwell3d.replace_table.clear(); maxwell3d.replace_table.clear();
} }
} }
void HLE_DrawIndexedIndirect::Fallback(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters) { void HLE_DrawIndexedIndirect::Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters) {
maxwell3d.RefreshParameters(); maxwell3d.RefreshParameters();
const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]);
const u32 element_base = parameters[4]; const u32 element_base = parameters[4];
@ -184,7 +184,7 @@ void HLE_DrawIndexedIndirect::Fallback(Engines::Maxwell3D& maxwell3d, std::span<
maxwell3d.replace_table.clear(); maxwell3d.replace_table.clear();
} }
} }
void HLE_MultiLayerClear::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) { void HLE_MultiLayerClear::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
maxwell3d.RefreshParameters(); maxwell3d.RefreshParameters();
ASSERT(parameters.size() == 1); ASSERT(parameters.size() == 1);
@ -196,47 +196,44 @@ void HLE_MultiLayerClear::Execute(Engines::Maxwell3D& maxwell3d, std::span<const
maxwell3d.regs.clear_surface.raw = clear_params.raw; maxwell3d.regs.clear_surface.raw = clear_params.raw;
maxwell3d.draw_manager.Clear(maxwell3d, num_layers); maxwell3d.draw_manager.Clear(maxwell3d, num_layers);
} }
void HLE_MultiDrawIndexedIndirectCount::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) { void HLE_MultiDrawIndexedIndirectCount::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
const auto topology = Maxwell3D::Regs::PrimitiveTopology(parameters[2]); const auto topology = Maxwell3D::Regs::PrimitiveTopology(parameters[2]);
if (!IsTopologySafe(topology)) { if (IsTopologySafe(topology)) {
Fallback(maxwell3d, parameters); const u32 start_indirect = parameters[0];
return; const u32 end_indirect = parameters[1];
if (start_indirect >= end_indirect) {
// Nothing to do.
return;
}
const u32 padding = parameters[3]; // padding is in words
// size of each indirect segment
const u32 indirect_words = 5 + padding;
const u32 stride = indirect_words * sizeof(u32);
const std::size_t draw_count = end_indirect - start_indirect;
const u32 estimate = u32(maxwell3d.EstimateIndexBufferSize());
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
auto& params = maxwell3d.draw_manager.indirect_state;
params.is_byte_count = false;
params.is_indexed = true;
params.include_count = true;
params.count_start_address = maxwell3d.GetMacroAddress(4);
params.indirect_start_address = maxwell3d.GetMacroAddress(5);
params.buffer_size = stride * draw_count;
params.max_draw_counts = draw_count;
params.stride = stride;
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro;
maxwell3d.SetHLEReplacementAttributeType(0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseVertex);
maxwell3d.SetHLEReplacementAttributeType(0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance);
maxwell3d.SetHLEReplacementAttributeType(0, 0x648, Maxwell3D::HLEReplacementAttributeType::DrawID);
maxwell3d.draw_manager.DrawIndexedIndirect(maxwell3d, topology, 0, estimate);
maxwell3d.engine_state = Maxwell3D::EngineHint::None;
maxwell3d.replace_table.clear();
} else {
Fallback(system, maxwell3d, parameters);
} }
const u32 start_indirect = parameters[0];
const u32 end_indirect = parameters[1];
if (start_indirect >= end_indirect) {
// Nothing to do.
return;
}
const u32 padding = parameters[3]; // padding is in words
// size of each indirect segment
const u32 indirect_words = 5 + padding;
const u32 stride = indirect_words * sizeof(u32);
const std::size_t draw_count = end_indirect - start_indirect;
const u32 estimate = static_cast<u32>(maxwell3d.EstimateIndexBufferSize());
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
auto& params = maxwell3d.draw_manager.indirect_state;
params.is_byte_count = false;
params.is_indexed = true;
params.include_count = true;
params.count_start_address = maxwell3d.GetMacroAddress(4);
params.indirect_start_address = maxwell3d.GetMacroAddress(5);
params.buffer_size = stride * draw_count;
params.max_draw_counts = draw_count;
params.stride = stride;
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro;
maxwell3d.SetHLEReplacementAttributeType(0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseVertex);
maxwell3d.SetHLEReplacementAttributeType(0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance);
maxwell3d.SetHLEReplacementAttributeType(0, 0x648, Maxwell3D::HLEReplacementAttributeType::DrawID);
maxwell3d.draw_manager.DrawIndexedIndirect(maxwell3d, topology, 0, estimate);
maxwell3d.engine_state = Maxwell3D::EngineHint::None;
maxwell3d.replace_table.clear();
} }
void HLE_MultiDrawIndexedIndirectCount::Fallback(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters) { void HLE_MultiDrawIndexedIndirectCount::Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters) {
SCOPE_EXIT { SCOPE_EXIT {
// Clean everything. // Clean everything.
maxwell3d.regs.vertex_id_base = 0x0; maxwell3d.regs.vertex_id_base = 0x0;
@ -250,7 +247,7 @@ void HLE_MultiDrawIndexedIndirectCount::Fallback(Engines::Maxwell3D& maxwell3d,
// Nothing to do. // Nothing to do.
return; return;
} }
const auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[2]); const auto topology = Maxwell3D::Regs::PrimitiveTopology(parameters[2]);
const u32 padding = parameters[3]; const u32 padding = parameters[3];
const std::size_t max_draws = parameters[4]; const std::size_t max_draws = parameters[4];
const u32 indirect_words = 5 + padding; const u32 indirect_words = 5 + padding;
@ -265,41 +262,41 @@ void HLE_MultiDrawIndexedIndirectCount::Fallback(Engines::Maxwell3D& maxwell3d,
maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro; maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro;
maxwell3d.SetHLEReplacementAttributeType(0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseVertex); maxwell3d.SetHLEReplacementAttributeType(0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseVertex);
maxwell3d.SetHLEReplacementAttributeType(0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance); maxwell3d.SetHLEReplacementAttributeType(0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance);
maxwell3d.CallMethod(0x8e3, 0x648, true); maxwell3d.CallMethod(system, 0x8e3, 0x648, true);
maxwell3d.CallMethod(0x8e4, static_cast<u32>(index), true); maxwell3d.CallMethod(system, 0x8e4, u32(index), true);
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
maxwell3d.draw_manager.DrawIndex(maxwell3d, topology, parameters[base + 2], parameters[base], base_vertex, base_instance, parameters[base + 1]); maxwell3d.draw_manager.DrawIndex(maxwell3d, topology, parameters[base + 2], parameters[base], base_vertex, base_instance, parameters[base + 1]);
} }
} }
void HLE_DrawIndirectByteCount::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) { void HLE_DrawIndirectByteCount::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
const bool force = maxwell3d.Rasterizer().HasDrawTransformFeedback(); const bool force = maxwell3d.Rasterizer().HasDrawTransformFeedback();
if (!force) { if (force) {
Fallback(maxwell3d, parameters); auto topology = Maxwell3D::Regs::PrimitiveTopology(parameters[0] & 0xFFFFU);
return; auto& params = maxwell3d.draw_manager.indirect_state;
params.is_byte_count = true;
params.is_indexed = false;
params.include_count = false;
params.count_start_address = 0;
params.indirect_start_address = maxwell3d.GetMacroAddress(2);
params.buffer_size = 4;
params.max_draw_counts = 1;
params.stride = parameters[1];
maxwell3d.regs.draw.begin = parameters[0];
maxwell3d.regs.draw_auto_stride = parameters[1];
maxwell3d.regs.draw_auto_byte_count = parameters[2];
maxwell3d.draw_manager.DrawArrayIndirect(maxwell3d, topology);
} else {
Fallback(system, maxwell3d, parameters);
} }
auto topology = Maxwell3D::Regs::PrimitiveTopology(parameters[0] & 0xFFFFU);
auto& params = maxwell3d.draw_manager.indirect_state;
params.is_byte_count = true;
params.is_indexed = false;
params.include_count = false;
params.count_start_address = 0;
params.indirect_start_address = maxwell3d.GetMacroAddress(2);
params.buffer_size = 4;
params.max_draw_counts = 1;
params.stride = parameters[1];
maxwell3d.regs.draw.begin = parameters[0];
maxwell3d.regs.draw_auto_stride = parameters[1];
maxwell3d.regs.draw_auto_byte_count = parameters[2];
maxwell3d.draw_manager.DrawArrayIndirect(maxwell3d, topology);
} }
void HLE_DrawIndirectByteCount::Fallback(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters) { void HLE_DrawIndirectByteCount::Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters) {
maxwell3d.RefreshParameters(); maxwell3d.RefreshParameters();
maxwell3d.regs.draw.begin = parameters[0]; maxwell3d.regs.draw.begin = parameters[0];
maxwell3d.regs.draw_auto_stride = parameters[1]; maxwell3d.regs.draw_auto_stride = parameters[1];
maxwell3d.regs.draw_auto_byte_count = parameters[2]; maxwell3d.regs.draw_auto_byte_count = parameters[2];
maxwell3d.draw_manager.DrawArray(maxwell3d, maxwell3d.regs.draw.topology, 0, maxwell3d.regs.draw_auto_byte_count / maxwell3d.regs.draw_auto_stride, 0, 1); maxwell3d.draw_manager.DrawArray(maxwell3d, maxwell3d.regs.draw.topology, 0, maxwell3d.regs.draw_auto_byte_count / maxwell3d.regs.draw_auto_stride, 0, 1);
} }
void HLE_C713C83D8F63CCF3::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) { void HLE_C713C83D8F63CCF3::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
maxwell3d.RefreshParameters(); maxwell3d.RefreshParameters();
const u32 offset = (parameters[0] & 0x3FFFFFFF) << 2; const u32 offset = (parameters[0] & 0x3FFFFFFF) << 2;
const u32 address = maxwell3d.regs.shadow_scratch[24]; const u32 address = maxwell3d.regs.shadow_scratch[24];
@ -309,7 +306,7 @@ void HLE_C713C83D8F63CCF3::Execute(Engines::Maxwell3D& maxwell3d, std::span<cons
const_buffer.address_low = address << 8; const_buffer.address_low = address << 8;
const_buffer.offset = offset; const_buffer.offset = offset;
} }
void HLE_D7333D26E0A93EDE::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) { void HLE_D7333D26E0A93EDE::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
maxwell3d.RefreshParameters(); maxwell3d.RefreshParameters();
const size_t index = parameters[0]; const size_t index = parameters[0];
const u32 address = maxwell3d.regs.shadow_scratch[42 + index]; const u32 address = maxwell3d.regs.shadow_scratch[42 + index];
@ -319,7 +316,7 @@ void HLE_D7333D26E0A93EDE::Execute(Engines::Maxwell3D& maxwell3d, std::span<cons
const_buffer.address_high = (address >> 24) & 0xFF; const_buffer.address_high = (address >> 24) & 0xFF;
const_buffer.address_low = address << 8; const_buffer.address_low = address << 8;
} }
void HLE_BindShader::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) { void HLE_BindShader::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
maxwell3d.RefreshParameters(); maxwell3d.RefreshParameters();
auto& regs = maxwell3d.regs; auto& regs = maxwell3d.regs;
const u32 index = parameters[0]; const u32 index = parameters[0];
@ -343,7 +340,7 @@ void HLE_BindShader::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32>
bind_group.raw_config = 0x11; bind_group.raw_config = 0x11;
maxwell3d.ProcessCBBind(bind_group_id); maxwell3d.ProcessCBBind(bind_group_id);
} }
void HLE_SetRasterBoundingBox::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) { void HLE_SetRasterBoundingBox::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
maxwell3d.RefreshParameters(); maxwell3d.RefreshParameters();
const u32 raster_mode = parameters[0]; const u32 raster_mode = parameters[0];
auto& regs = maxwell3d.regs; auto& regs = maxwell3d.regs;
@ -352,7 +349,7 @@ void HLE_SetRasterBoundingBox::Execute(Engines::Maxwell3D& maxwell3d, std::span<
regs.raster_bounding_box.raw = raster_mode & 0xFFFFF00F; regs.raster_bounding_box.raw = raster_mode & 0xFFFFF00F;
regs.raster_bounding_box.pad.Assign(scratch_data & raster_enabled); regs.raster_bounding_box.pad.Assign(scratch_data & raster_enabled);
} }
void HLE_ClearConstBuffer::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) { void HLE_ClearConstBuffer::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
static constexpr std::array<u32, 0x7000> zeroes{}; //must be bigger than either 7000 or 5F00 static constexpr std::array<u32, 0x7000> zeroes{}; //must be bigger than either 7000 or 5F00
maxwell3d.RefreshParameters(); maxwell3d.RefreshParameters();
auto& regs = maxwell3d.regs; auto& regs = maxwell3d.regs;
@ -362,7 +359,7 @@ void HLE_ClearConstBuffer::Execute(Engines::Maxwell3D& maxwell3d, std::span<cons
regs.const_buffer.offset = 0; regs.const_buffer.offset = 0;
maxwell3d.ProcessCBMultiData(zeroes.data(), parameters[2] * 4); maxwell3d.ProcessCBMultiData(zeroes.data(), parameters[2] * 4);
} }
void HLE_ClearMemory::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) { void HLE_ClearMemory::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
maxwell3d.RefreshParameters(); maxwell3d.RefreshParameters();
const u32 needed_memory = parameters[2] / sizeof(u32); const u32 needed_memory = parameters[2] / sizeof(u32);
if (needed_memory > zero_memory.size()) { if (needed_memory > zero_memory.size()) {
@ -373,10 +370,10 @@ void HLE_ClearMemory::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32
regs.upload.line_count = 1; regs.upload.line_count = 1;
regs.upload.dest.address_high = parameters[0]; regs.upload.dest.address_high = parameters[0];
regs.upload.dest.address_low = parameters[1]; regs.upload.dest.address_low = parameters[1];
maxwell3d.CallMethod(size_t(MAXWELL3D_REG_INDEX(launch_dma)), 0x1011, true); maxwell3d.CallMethod(system, size_t(MAXWELL3D_REG_INDEX(launch_dma)), 0x1011, true);
maxwell3d.CallMultiMethod(size_t(MAXWELL3D_REG_INDEX(inline_data)), zero_memory.data(), needed_memory, needed_memory); maxwell3d.CallMultiMethod(system, size_t(MAXWELL3D_REG_INDEX(inline_data)), zero_memory.data(), needed_memory, needed_memory);
} }
void HLE_TransformFeedbackSetup::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) { void HLE_TransformFeedbackSetup::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
maxwell3d.RefreshParameters(); maxwell3d.RefreshParameters();
auto& regs = maxwell3d.regs; auto& regs = maxwell3d.regs;
regs.transform_feedback_enabled = 1; regs.transform_feedback_enabled = 1;
@ -388,8 +385,8 @@ void HLE_TransformFeedbackSetup::Execute(Engines::Maxwell3D& maxwell3d, std::spa
regs.upload.line_count = 1; regs.upload.line_count = 1;
regs.upload.dest.address_high = parameters[0]; regs.upload.dest.address_high = parameters[0];
regs.upload.dest.address_low = parameters[1]; regs.upload.dest.address_low = parameters[1];
maxwell3d.CallMethod(size_t(MAXWELL3D_REG_INDEX(launch_dma)), 0x1011, true); maxwell3d.CallMethod(system, size_t(MAXWELL3D_REG_INDEX(launch_dma)), 0x1011, true);
maxwell3d.CallMethod(size_t(MAXWELL3D_REG_INDEX(inline_data)), regs.transform_feedback.controls[0].stride, true); maxwell3d.CallMethod(system, size_t(MAXWELL3D_REG_INDEX(inline_data)), regs.transform_feedback.controls[0].stride, true);
maxwell3d.Rasterizer().RegisterTransformFeedback(regs.upload.dest.Address()); maxwell3d.Rasterizer().RegisterTransformFeedback(regs.upload.dest.Address());
} }
@ -429,7 +426,7 @@ void HLE_TransformFeedbackSetup::Execute(Engines::Maxwell3D& maxwell3d, std::spa
} }
} }
void MacroInterpreterImpl::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> params, u32 method) { void MacroInterpreterImpl::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> params, u32 method) {
Reset(); Reset();
registers[1] = params[0]; registers[1] = params[0];
@ -439,7 +436,7 @@ void MacroInterpreterImpl::Execute(Engines::Maxwell3D& maxwell3d, std::span<cons
// Execute the code until we hit an exit condition. // Execute the code until we hit an exit condition.
bool keep_executing = true; bool keep_executing = true;
while (keep_executing) { while (keep_executing) {
keep_executing = Step(maxwell3d, false); keep_executing = Step(system, maxwell3d, false);
} }
// Assert the the macro used all the input parameters // Assert the the macro used all the input parameters
@ -462,7 +459,7 @@ void MacroInterpreterImpl::Reset() {
/// @brief Executes a single macro instruction located at the current program counter. Returns whether /// @brief Executes a single macro instruction located at the current program counter. Returns whether
/// the interpreter should keep running. /// the interpreter should keep running.
/// @param is_delay_slot Whether the current step is being executed due to a delay slot in a previous instruction. /// @param is_delay_slot Whether the current step is being executed due to a delay slot in a previous instruction.
bool MacroInterpreterImpl::Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slot) { bool MacroInterpreterImpl::Step(Core::System& system, Engines::Maxwell3D& maxwell3d, bool is_delay_slot) {
u32 base_address = pc; u32 base_address = pc;
Macro::Opcode opcode = GetOpcode(); Macro::Opcode opcode = GetOpcode();
@ -478,11 +475,11 @@ bool MacroInterpreterImpl::Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slo
switch (opcode.operation) { switch (opcode.operation) {
case Macro::Operation::ALU: { case Macro::Operation::ALU: {
u32 result = GetALUResult(opcode.alu_operation, GetRegister(opcode.src_a), GetRegister(opcode.src_b)); u32 result = GetALUResult(opcode.alu_operation, GetRegister(opcode.src_a), GetRegister(opcode.src_b));
ProcessResult(maxwell3d, opcode.result_operation, opcode.dst, result); ProcessResult(system, maxwell3d, opcode.result_operation, opcode.dst, result);
break; break;
} }
case Macro::Operation::AddImmediate: { case Macro::Operation::AddImmediate: {
ProcessResult(maxwell3d, opcode.result_operation, opcode.dst, GetRegister(opcode.src_a) + opcode.immediate); ProcessResult(system, maxwell3d, opcode.result_operation, opcode.dst, GetRegister(opcode.src_a) + opcode.immediate);
break; break;
} }
case Macro::Operation::ExtractInsert: { case Macro::Operation::ExtractInsert: {
@ -492,7 +489,7 @@ bool MacroInterpreterImpl::Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slo
src = (src >> opcode.bf_src_bit) & opcode.GetBitfieldMask(); src = (src >> opcode.bf_src_bit) & opcode.GetBitfieldMask();
dst &= ~(opcode.GetBitfieldMask() << opcode.bf_dst_bit); dst &= ~(opcode.GetBitfieldMask() << opcode.bf_dst_bit);
dst |= src << opcode.bf_dst_bit; dst |= src << opcode.bf_dst_bit;
ProcessResult(maxwell3d, opcode.result_operation, opcode.dst, dst); ProcessResult(system, maxwell3d, opcode.result_operation, opcode.dst, dst);
break; break;
} }
case Macro::Operation::ExtractShiftLeftImmediate: { case Macro::Operation::ExtractShiftLeftImmediate: {
@ -501,7 +498,7 @@ bool MacroInterpreterImpl::Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slo
u32 result = ((src >> dst) & opcode.GetBitfieldMask()) << opcode.bf_dst_bit; u32 result = ((src >> dst) & opcode.GetBitfieldMask()) << opcode.bf_dst_bit;
ProcessResult(maxwell3d, opcode.result_operation, opcode.dst, result); ProcessResult(system, maxwell3d, opcode.result_operation, opcode.dst, result);
break; break;
} }
case Macro::Operation::ExtractShiftLeftRegister: { case Macro::Operation::ExtractShiftLeftRegister: {
@ -510,12 +507,12 @@ bool MacroInterpreterImpl::Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slo
u32 result = ((src >> opcode.bf_src_bit) & opcode.GetBitfieldMask()) << dst; u32 result = ((src >> opcode.bf_src_bit) & opcode.GetBitfieldMask()) << dst;
ProcessResult(maxwell3d, opcode.result_operation, opcode.dst, result); ProcessResult(system, maxwell3d, opcode.result_operation, opcode.dst, result);
break; break;
} }
case Macro::Operation::Read: { case Macro::Operation::Read: {
u32 result = Read(maxwell3d, GetRegister(opcode.src_a) + opcode.immediate); u32 result = Read(maxwell3d, GetRegister(opcode.src_a) + opcode.immediate);
ProcessResult(maxwell3d, opcode.result_operation, opcode.dst, result); ProcessResult(system, maxwell3d, opcode.result_operation, opcode.dst, result);
break; break;
} }
case Macro::Operation::Branch: { case Macro::Operation::Branch: {
@ -531,7 +528,7 @@ bool MacroInterpreterImpl::Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slo
delayed_pc = base_address + opcode.GetBranchTarget(); delayed_pc = base_address + opcode.GetBranchTarget();
// Execute one more instruction due to the delay slot. // Execute one more instruction due to the delay slot.
return Step(maxwell3d, true); return Step(system, maxwell3d, true);
} }
break; break;
} }
@ -544,7 +541,7 @@ bool MacroInterpreterImpl::Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slo
// cause an exit if it's executed inside a delay slot. // cause an exit if it's executed inside a delay slot.
if (opcode.is_exit && !is_delay_slot) { if (opcode.is_exit && !is_delay_slot) {
// Exit has a delay slot, execute the next instruction // Exit has a delay slot, execute the next instruction
Step(maxwell3d, true); Step(system, maxwell3d, true);
return false; return false;
} }
return true; return true;
@ -591,7 +588,7 @@ u32 MacroInterpreterImpl::GetALUResult(Macro::ALUOperation operation, u32 src_a,
} }
/// Performs the result operation on the input result and stores it in the specified register (if necessary). /// Performs the result operation on the input result and stores it in the specified register (if necessary).
void MacroInterpreterImpl::ProcessResult(Engines::Maxwell3D& maxwell3d, Macro::ResultOperation operation, u32 reg, u32 result) { void MacroInterpreterImpl::ProcessResult(Core::System& system, Engines::Maxwell3D& maxwell3d, Macro::ResultOperation operation, u32 reg, u32 result) {
switch (operation) { switch (operation) {
case Macro::ResultOperation::IgnoreAndFetch: case Macro::ResultOperation::IgnoreAndFetch:
// Fetch parameter and ignore result. // Fetch parameter and ignore result.
@ -609,12 +606,12 @@ void MacroInterpreterImpl::ProcessResult(Engines::Maxwell3D& maxwell3d, Macro::R
case Macro::ResultOperation::FetchAndSend: case Macro::ResultOperation::FetchAndSend:
// Fetch parameter and send result. // Fetch parameter and send result.
SetRegister(reg, FetchParameter()); SetRegister(reg, FetchParameter());
Send(maxwell3d, result); Send(system, maxwell3d, result);
break; break;
case Macro::ResultOperation::MoveAndSend: case Macro::ResultOperation::MoveAndSend:
// Move and send result. // Move and send result.
SetRegister(reg, result); SetRegister(reg, result);
Send(maxwell3d, result); Send(system, maxwell3d, result);
break; break;
case Macro::ResultOperation::FetchAndSetMethod: case Macro::ResultOperation::FetchAndSetMethod:
// Fetch parameter and use result as Method Address. // Fetch parameter and use result as Method Address.
@ -625,13 +622,13 @@ void MacroInterpreterImpl::ProcessResult(Engines::Maxwell3D& maxwell3d, Macro::R
// Move result and use as Method Address, then fetch and send parameter. // Move result and use as Method Address, then fetch and send parameter.
SetRegister(reg, result); SetRegister(reg, result);
SetMethodAddress(result); SetMethodAddress(result);
Send(maxwell3d, FetchParameter()); Send(system, maxwell3d, FetchParameter());
break; break;
case Macro::ResultOperation::MoveAndSetMethodSend: case Macro::ResultOperation::MoveAndSetMethodSend:
// Move result and use as Method Address, then send bits 12:17 of result. // Move result and use as Method Address, then send bits 12:17 of result.
SetRegister(reg, result); SetRegister(reg, result);
SetMethodAddress(result); SetMethodAddress(result);
Send(maxwell3d, (result >> 12) & 0b111111); Send(system, maxwell3d, (result >> 12) & 0b111111);
break; break;
default: default:
UNIMPLEMENTED_MSG("Unimplemented result operation {}", operation); UNIMPLEMENTED_MSG("Unimplemented result operation {}", operation);
@ -672,8 +669,8 @@ void MacroInterpreterImpl::SetRegister(u32 register_id, u32 value) {
} }
/// Calls a GPU Engine method with the input parameter. /// Calls a GPU Engine method with the input parameter.
void MacroInterpreterImpl::Send(Engines::Maxwell3D& maxwell3d, u32 value) { void MacroInterpreterImpl::Send(Core::System& system, Engines::Maxwell3D& maxwell3d, u32 value) {
maxwell3d.CallMethod(method_address.address, value, true); maxwell3d.CallMethod(system, method_address.address, value, true);
// Increment the method address by the method increment. // Increment the method address by the method increment.
method_address.address.Assign(method_address.address.Value() + method_address.increment.Value()); method_address.address.Assign(method_address.address.Value() + method_address.increment.Value());
} }
@ -724,34 +721,35 @@ static const auto default_cg_mode = nullptr; //Allow RWE
#endif #endif
struct MacroJITx64Impl final : public Xbyak::CodeGenerator, public DynamicCachedMacro { struct MacroJITx64Impl final : public Xbyak::CodeGenerator, public DynamicCachedMacro {
explicit MacroJITx64Impl(std::span<const u32> code_) explicit MacroJITx64Impl(Core::System& system, std::span<const u32> code_)
: Xbyak::CodeGenerator(MAX_CODE_SIZE, default_cg_mode) : Xbyak::CodeGenerator(MAX_CODE_SIZE, default_cg_mode)
, code{code_} , code{code_}
{ {
Compile(); Compile(system);
} }
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, u32 method) override; void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, u32 method) override;
void Compile_ALU(Macro::Opcode opcode); void Compile_ALU(Core::System& system, Macro::Opcode opcode);
void Compile_AddImmediate(Macro::Opcode opcode); void Compile_AddImmediate(Core::System& system, Macro::Opcode opcode);
void Compile_ExtractInsert(Macro::Opcode opcode); void Compile_ExtractInsert(Core::System& system, Macro::Opcode opcode);
void Compile_ExtractShiftLeftImmediate(Macro::Opcode opcode); void Compile_ExtractShiftLeftImmediate(Core::System& system, Macro::Opcode opcode);
void Compile_ExtractShiftLeftRegister(Macro::Opcode opcode); void Compile_ExtractShiftLeftRegister(Core::System& system, Macro::Opcode opcode);
void Compile_Read(Macro::Opcode opcode); void Compile_Read(Core::System& system, Macro::Opcode opcode);
void Compile_Branch(Macro::Opcode opcode); void Compile_Branch(Macro::Opcode opcode);
void Optimizer_ScanFlags(); void Optimizer_ScanFlags();
void Compile(); void Compile(Core::System& system);
bool Compile_NextInstruction(); bool Compile_NextInstruction(Core::System& system);
Xbyak::Reg32 Compile_FetchParameter(); Xbyak::Reg32 Compile_FetchParameter();
Xbyak::Reg32 Compile_GetRegister(u32 index, Xbyak::Reg32 dst); Xbyak::Reg32 Compile_GetRegister(u32 index, Xbyak::Reg32 dst);
void Compile_ProcessResult(Macro::ResultOperation operation, u32 reg); void Compile_ProcessResult(Core::System& system, Macro::ResultOperation operation, u32 reg);
void Compile_Send(Xbyak::Reg32 value); void Compile_Send(Core::System& system, Xbyak::Reg32 value);
Macro::Opcode GetOpCode() const; Macro::Opcode GetOpCode() const;
struct JITState { struct JITState {
Engines::Maxwell3D* maxwell3d{}; Engines::Maxwell3D* maxwell3d = nullptr;
Core::System* system = nullptr;
std::array<u32, Macro::NUM_MACRO_REGISTERS> registers{}; std::array<u32, Macro::NUM_MACRO_REGISTERS> registers{};
u32 carry_flag{}; u32 carry_flag{};
}; };
@ -777,15 +775,16 @@ struct MacroJITx64Impl final : public Xbyak::CodeGenerator, public DynamicCached
std::span<const u32> code; std::span<const u32> code;
}; };
void MacroJITx64Impl::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, u32 method) { void MacroJITx64Impl::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, u32 method) {
ASSERT_OR_EXECUTE(program != nullptr, { return; }); ASSERT_OR_EXECUTE(program != nullptr, { return; });
JITState state{}; JITState state{};
state.maxwell3d = &maxwell3d; state.maxwell3d = &maxwell3d;
state.system = &system;
state.registers = {}; state.registers = {};
program(&state, parameters.data(), parameters.data() + parameters.size()); program(&state, parameters.data(), parameters.data() + parameters.size());
} }
void MacroJITx64Impl::Compile_ALU(Macro::Opcode opcode) { void MacroJITx64Impl::Compile_ALU(Core::System& system, Macro::Opcode opcode) {
const bool is_a_zero = opcode.src_a == 0; const bool is_a_zero = opcode.src_a == 0;
const bool is_b_zero = opcode.src_b == 0; const bool is_b_zero = opcode.src_b == 0;
const bool valid_operation = !is_a_zero && !is_b_zero; const bool valid_operation = !is_a_zero && !is_b_zero;
@ -902,10 +901,10 @@ void MacroJITx64Impl::Compile_ALU(Macro::Opcode opcode) {
UNIMPLEMENTED_MSG("Unimplemented ALU operation {}", opcode.alu_operation.Value()); UNIMPLEMENTED_MSG("Unimplemented ALU operation {}", opcode.alu_operation.Value());
break; break;
} }
Compile_ProcessResult(opcode.result_operation, opcode.dst); Compile_ProcessResult(system, opcode.result_operation, opcode.dst);
} }
void MacroJITx64Impl::Compile_AddImmediate(Macro::Opcode opcode) { void MacroJITx64Impl::Compile_AddImmediate(Core::System& system, Macro::Opcode opcode) {
if (optimizer.skip_dummy_addimmediate) { if (optimizer.skip_dummy_addimmediate) {
// Games tend to use this as an exit instruction placeholder. It's to encode an instruction // Games tend to use this as an exit instruction placeholder. It's to encode an instruction
// without doing anything. In our case we can just not emit anything. // without doing anything. In our case we can just not emit anything.
@ -940,10 +939,10 @@ void MacroJITx64Impl::Compile_AddImmediate(Macro::Opcode opcode) {
sub(result, opcode.immediate * -1); sub(result, opcode.immediate * -1);
} }
} }
Compile_ProcessResult(opcode.result_operation, opcode.dst); Compile_ProcessResult(system, opcode.result_operation, opcode.dst);
} }
void MacroJITx64Impl::Compile_ExtractInsert(Macro::Opcode opcode) { void MacroJITx64Impl::Compile_ExtractInsert(Core::System& system, Macro::Opcode opcode) {
auto dst = Compile_GetRegister(opcode.src_a, RESULT); auto dst = Compile_GetRegister(opcode.src_a, RESULT);
auto src = Compile_GetRegister(opcode.src_b, eax); auto src = Compile_GetRegister(opcode.src_b, eax);
@ -954,10 +953,10 @@ void MacroJITx64Impl::Compile_ExtractInsert(Macro::Opcode opcode) {
shl(src, opcode.bf_dst_bit); shl(src, opcode.bf_dst_bit);
or_(dst, src); or_(dst, src);
Compile_ProcessResult(opcode.result_operation, opcode.dst); Compile_ProcessResult(system, opcode.result_operation, opcode.dst);
} }
void MacroJITx64Impl::Compile_ExtractShiftLeftImmediate(Macro::Opcode opcode) { void MacroJITx64Impl::Compile_ExtractShiftLeftImmediate(Core::System& system, Macro::Opcode opcode) {
const auto dst = Compile_GetRegister(opcode.src_a, ecx); const auto dst = Compile_GetRegister(opcode.src_a, ecx);
const auto src = Compile_GetRegister(opcode.src_b, RESULT); const auto src = Compile_GetRegister(opcode.src_b, RESULT);
@ -965,10 +964,10 @@ void MacroJITx64Impl::Compile_ExtractShiftLeftImmediate(Macro::Opcode opcode) {
and_(src, opcode.GetBitfieldMask()); and_(src, opcode.GetBitfieldMask());
shl(src, opcode.bf_dst_bit); shl(src, opcode.bf_dst_bit);
Compile_ProcessResult(opcode.result_operation, opcode.dst); Compile_ProcessResult(system, opcode.result_operation, opcode.dst);
} }
void MacroJITx64Impl::Compile_ExtractShiftLeftRegister(Macro::Opcode opcode) { void MacroJITx64Impl::Compile_ExtractShiftLeftRegister(Core::System& system, Macro::Opcode opcode) {
const auto dst = Compile_GetRegister(opcode.src_a, ecx); const auto dst = Compile_GetRegister(opcode.src_a, ecx);
const auto src = Compile_GetRegister(opcode.src_b, RESULT); const auto src = Compile_GetRegister(opcode.src_b, RESULT);
@ -976,10 +975,10 @@ void MacroJITx64Impl::Compile_ExtractShiftLeftRegister(Macro::Opcode opcode) {
and_(src, opcode.GetBitfieldMask()); and_(src, opcode.GetBitfieldMask());
shl(src, dst.cvt8()); shl(src, dst.cvt8());
Compile_ProcessResult(opcode.result_operation, opcode.dst); Compile_ProcessResult(system, opcode.result_operation, opcode.dst);
} }
void MacroJITx64Impl::Compile_Read(Macro::Opcode opcode) { void MacroJITx64Impl::Compile_Read(Core::System& system, Macro::Opcode opcode) {
if (optimizer.zero_reg_skip && opcode.src_a == 0) { if (optimizer.zero_reg_skip && opcode.src_a == 0) {
if (opcode.immediate == 0) { if (opcode.immediate == 0) {
xor_(RESULT, RESULT); xor_(RESULT, RESULT);
@ -1005,23 +1004,21 @@ void MacroJITx64Impl::Compile_Read(Macro::Opcode opcode) {
int3(); int3();
L(pass_range_check); L(pass_range_check);
} }
mov(rax, qword[STATE]); mov(rax, qword[STATE + offsetof(JITState, maxwell3d)]);
mov(RESULT, mov(RESULT, dword[rax + offsetof(Engines::Maxwell3D, regs) + offsetof(Engines::Maxwell3D::Regs, reg_array) + RESULT.cvt64() * sizeof(u32)]);
dword[rax + offsetof(Engines::Maxwell3D, regs) + Compile_ProcessResult(system, opcode.result_operation, opcode.dst);
offsetof(Engines::Maxwell3D::Regs, reg_array) + RESULT.cvt64() * sizeof(u32)]);
Compile_ProcessResult(opcode.result_operation, opcode.dst);
} }
static void MacroJIT_SendThunk(Engines::Maxwell3D* maxwell3d, Macro::MethodAddress method_address, u32 value) { static void MacroJIT_SendThunk(Core::System* system, Engines::Maxwell3D* maxwell3d, Macro::MethodAddress method_address, u32 value) {
maxwell3d->CallMethod(method_address.address, value, true); maxwell3d->CallMethod(*system, method_address.address, value, true);
} }
void MacroJITx64Impl::Compile_Send(Xbyak::Reg32 value) { void MacroJITx64Impl::Compile_Send(Core::System& system, Xbyak::Reg32 value) {
Common::X64::ABI_PushRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0); Common::X64::ABI_PushRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0);
mov(Common::X64::ABI_PARAM1, qword[STATE]); mov(Common::X64::ABI_PARAM1, qword[STATE + offsetof(JITState, system)]);
mov(Common::X64::ABI_PARAM2.cvt32(), METHOD_ADDRESS); mov(Common::X64::ABI_PARAM2, qword[STATE + offsetof(JITState, maxwell3d)]);
mov(Common::X64::ABI_PARAM3.cvt32(), value); mov(Common::X64::ABI_PARAM3.cvt32(), METHOD_ADDRESS);
mov(Common::X64::ABI_PARAM4.cvt32(), value);
Common::X64::CallFarFunction(*this, &MacroJIT_SendThunk); Common::X64::CallFarFunction(*this, &MacroJIT_SendThunk);
Common::X64::ABI_PopRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0); Common::X64::ABI_PopRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0);
@ -1045,9 +1042,8 @@ void MacroJITx64Impl::Compile_Send(Xbyak::Reg32 value) {
} }
void MacroJITx64Impl::Compile_Branch(Macro::Opcode opcode) { void MacroJITx64Impl::Compile_Branch(Macro::Opcode opcode) {
ASSERT_MSG(!is_delay_slot, "Executing a branch in a delay slot is not valid"); ASSERT(!is_delay_slot && "Executing a branch in a delay slot is not valid");
const s32 jump_address = const s32 jump_address = s32(pc) + s32(opcode.GetBranchTarget() / sizeof(s32));
static_cast<s32>(pc) + static_cast<s32>(opcode.GetBranchTarget() / sizeof(s32));
Xbyak::Label end; Xbyak::Label end;
auto value = Compile_GetRegister(opcode.src_a, eax); auto value = Compile_GetRegister(opcode.src_a, eax);
@ -1116,7 +1112,7 @@ void MacroJITx64Impl::Optimizer_ScanFlags() {
} }
} }
void MacroJITx64Impl::Compile() { void MacroJITx64Impl::Compile(Core::System& system) {
labels.fill(Xbyak::Label()); labels.fill(Xbyak::Label());
Common::X64::ABI_PushRegistersAndAdjustStack(*this, Common::X64::ABI_ALL_CALLEE_SAVED, 8); Common::X64::ABI_PushRegistersAndAdjustStack(*this, Common::X64::ABI_ALL_CALLEE_SAVED, 8);
@ -1156,7 +1152,7 @@ void MacroJITx64Impl::Compile() {
next_opcode = {}; next_opcode = {};
} }
pc = i; pc = i;
Compile_NextInstruction(); Compile_NextInstruction(system);
} }
L(end_of_code); L(end_of_code);
@ -1167,7 +1163,7 @@ void MacroJITx64Impl::Compile() {
program = getCode<ProgramType>(); program = getCode<ProgramType>();
} }
bool MacroJITx64Impl::Compile_NextInstruction() { bool MacroJITx64Impl::Compile_NextInstruction(Core::System& system) {
const auto opcode = GetOpCode(); const auto opcode = GetOpCode();
if (labels[pc].getAddress()) { if (labels[pc].getAddress()) {
return false; return false;
@ -1177,22 +1173,22 @@ bool MacroJITx64Impl::Compile_NextInstruction() {
switch (opcode.operation) { switch (opcode.operation) {
case Macro::Operation::ALU: case Macro::Operation::ALU:
Compile_ALU(opcode); Compile_ALU(system, opcode);
break; break;
case Macro::Operation::AddImmediate: case Macro::Operation::AddImmediate:
Compile_AddImmediate(opcode); Compile_AddImmediate(system, opcode);
break; break;
case Macro::Operation::ExtractInsert: case Macro::Operation::ExtractInsert:
Compile_ExtractInsert(opcode); Compile_ExtractInsert(system, opcode);
break; break;
case Macro::Operation::ExtractShiftLeftImmediate: case Macro::Operation::ExtractShiftLeftImmediate:
Compile_ExtractShiftLeftImmediate(opcode); Compile_ExtractShiftLeftImmediate(system, opcode);
break; break;
case Macro::Operation::ExtractShiftLeftRegister: case Macro::Operation::ExtractShiftLeftRegister:
Compile_ExtractShiftLeftRegister(opcode); Compile_ExtractShiftLeftRegister(system, opcode);
break; break;
case Macro::Operation::Read: case Macro::Operation::Read:
Compile_Read(opcode); Compile_Read(system, opcode);
break; break;
case Macro::Operation::Branch: case Macro::Operation::Branch:
Compile_Branch(opcode); Compile_Branch(opcode);
@ -1264,7 +1260,7 @@ Xbyak::Reg32 MacroJITx64Impl::Compile_GetRegister(u32 index, Xbyak::Reg32 dst) {
return dst; return dst;
} }
void MacroJITx64Impl::Compile_ProcessResult(Macro::ResultOperation operation, u32 reg) { void MacroJITx64Impl::Compile_ProcessResult(Core::System& system, Macro::ResultOperation operation, u32 reg) {
const auto SetRegister = [this](u32 reg_index, const Xbyak::Reg32& result) { const auto SetRegister = [this](u32 reg_index, const Xbyak::Reg32& result) {
// Register 0 is supposed to always return 0. NOP is implemented as a store to the zero // Register 0 is supposed to always return 0. NOP is implemented as a store to the zero
// register. // register.
@ -1289,12 +1285,12 @@ void MacroJITx64Impl::Compile_ProcessResult(Macro::ResultOperation operation, u3
case Macro::ResultOperation::FetchAndSend: case Macro::ResultOperation::FetchAndSend:
// Fetch parameter and send result. // Fetch parameter and send result.
SetRegister(reg, Compile_FetchParameter()); SetRegister(reg, Compile_FetchParameter());
Compile_Send(RESULT); Compile_Send(system, RESULT);
break; break;
case Macro::ResultOperation::MoveAndSend: case Macro::ResultOperation::MoveAndSend:
// Move and send result. // Move and send result.
SetRegister(reg, RESULT); SetRegister(reg, RESULT);
Compile_Send(RESULT); Compile_Send(system, RESULT);
break; break;
case Macro::ResultOperation::FetchAndSetMethod: case Macro::ResultOperation::FetchAndSetMethod:
// Fetch parameter and use result as Method Address. // Fetch parameter and use result as Method Address.
@ -1305,7 +1301,7 @@ void MacroJITx64Impl::Compile_ProcessResult(Macro::ResultOperation operation, u3
// Move result and use as Method Address, then fetch and send parameter. // Move result and use as Method Address, then fetch and send parameter.
SetRegister(reg, RESULT); SetRegister(reg, RESULT);
SetMethodAddress(RESULT); SetMethodAddress(RESULT);
Compile_Send(Compile_FetchParameter()); Compile_Send(system, Compile_FetchParameter());
break; break;
case Macro::ResultOperation::MoveAndSetMethodSend: case Macro::ResultOperation::MoveAndSetMethodSend:
// Move result and use as Method Address, then send bits 12:17 of result. // Move result and use as Method Address, then send bits 12:17 of result.
@ -1313,7 +1309,7 @@ void MacroJITx64Impl::Compile_ProcessResult(Macro::ResultOperation operation, u3
SetMethodAddress(RESULT); SetMethodAddress(RESULT);
shr(RESULT, 12); shr(RESULT, 12);
and_(RESULT, 0b111111); and_(RESULT, 0b111111);
Compile_Send(RESULT); Compile_Send(system, RESULT);
break; break;
default: default:
UNIMPLEMENTED_MSG("Unimplemented macro operation {}", operation); UNIMPLEMENTED_MSG("Unimplemented macro operation {}", operation);
@ -1354,36 +1350,36 @@ static void Dump(u64 hash, std::span<const u32> code, bool decompiled = false) {
macro_file.write(reinterpret_cast<const char*>(code.data()), code.size_bytes()); macro_file.write(reinterpret_cast<const char*>(code.data()), code.size_bytes());
} }
void MacroEngine::Execute(Engines::Maxwell3D& maxwell3d, u32 method, std::span<const u32> parameters) { void MacroEngine::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, u32 method, std::span<const u32> parameters) {
auto const execute_variant = [&maxwell3d, &parameters, method](AnyCachedMacro& acm) { auto const execute_variant = [&system, &maxwell3d, &parameters, method](AnyCachedMacro& acm) {
if (auto a = std::get_if<HLE_DrawArraysIndirect>(&acm)) if (auto a = std::get_if<HLE_DrawArraysIndirect>(&acm))
a->Execute(maxwell3d, parameters, method); a->Execute(system, maxwell3d, parameters, method);
if (auto a = std::get_if<HLE_DrawIndexedIndirect>(&acm)) if (auto a = std::get_if<HLE_DrawIndexedIndirect>(&acm))
a->Execute(maxwell3d, parameters, method); a->Execute(system, maxwell3d, parameters, method);
if (auto a = std::get_if<HLE_MultiDrawIndexedIndirectCount>(&acm)) if (auto a = std::get_if<HLE_MultiDrawIndexedIndirectCount>(&acm))
a->Execute(maxwell3d, parameters, method); a->Execute(system, maxwell3d, parameters, method);
if (auto a = std::get_if<HLE_MultiLayerClear>(&acm)) if (auto a = std::get_if<HLE_MultiLayerClear>(&acm))
a->Execute(maxwell3d, parameters, method); a->Execute(system, maxwell3d, parameters, method);
if (auto a = std::get_if<HLE_C713C83D8F63CCF3>(&acm)) if (auto a = std::get_if<HLE_C713C83D8F63CCF3>(&acm))
a->Execute(maxwell3d, parameters, method); a->Execute(system, maxwell3d, parameters, method);
if (auto a = std::get_if<HLE_D7333D26E0A93EDE>(&acm)) if (auto a = std::get_if<HLE_D7333D26E0A93EDE>(&acm))
a->Execute(maxwell3d, parameters, method); a->Execute(system, maxwell3d, parameters, method);
if (auto a = std::get_if<HLE_BindShader>(&acm)) if (auto a = std::get_if<HLE_BindShader>(&acm))
a->Execute(maxwell3d, parameters, method); a->Execute(system, maxwell3d, parameters, method);
if (auto a = std::get_if<HLE_SetRasterBoundingBox>(&acm)) if (auto a = std::get_if<HLE_SetRasterBoundingBox>(&acm))
a->Execute(maxwell3d, parameters, method); a->Execute(system, maxwell3d, parameters, method);
if (auto a = std::get_if<HLE_ClearConstBuffer>(&acm)) if (auto a = std::get_if<HLE_ClearConstBuffer>(&acm))
a->Execute(maxwell3d, parameters, method); a->Execute(system, maxwell3d, parameters, method);
if (auto a = std::get_if<HLE_ClearMemory>(&acm)) if (auto a = std::get_if<HLE_ClearMemory>(&acm))
a->Execute(maxwell3d, parameters, method); a->Execute(system, maxwell3d, parameters, method);
if (auto a = std::get_if<HLE_TransformFeedbackSetup>(&acm)) if (auto a = std::get_if<HLE_TransformFeedbackSetup>(&acm))
a->Execute(maxwell3d, parameters, method); a->Execute(system, maxwell3d, parameters, method);
if (auto a = std::get_if<HLE_DrawIndirectByteCount>(&acm)) if (auto a = std::get_if<HLE_DrawIndirectByteCount>(&acm))
a->Execute(maxwell3d, parameters, method); a->Execute(system, maxwell3d, parameters, method);
if (auto a = std::get_if<MacroInterpreterImpl>(&acm)) if (auto a = std::get_if<MacroInterpreterImpl>(&acm))
a->Execute(maxwell3d, parameters, method); a->Execute(system, maxwell3d, parameters, method);
if (auto a = std::get_if<std::unique_ptr<DynamicCachedMacro>>(&acm)) if (auto a = std::get_if<std::unique_ptr<DynamicCachedMacro>>(&acm))
a->get()->Execute(maxwell3d, parameters, method); a->get()->Execute(system, maxwell3d, parameters, method);
}; };
if (auto const it = macro_cache.find(method); it != macro_cache.end()) { if (auto const it = macro_cache.find(method); it != macro_cache.end()) {
auto& ci = it->second; auto& ci = it->second;
@ -1414,9 +1410,9 @@ void MacroEngine::Execute(Engines::Maxwell3D& maxwell3d, u32 method, std::span<c
code.resize(macro_cached.size() - rebased_method); code.resize(macro_cached.size() - rebased_method);
std::memcpy(code.data(), macro_cached.data() + rebased_method, code.size() * sizeof(u32)); std::memcpy(code.data(), macro_cached.data() + rebased_method, code.size() * sizeof(u32));
ci.hash = Common::HashValue(code); ci.hash = Common::HashValue(code);
ci.program = Compile(maxwell3d, code); ci.program = Compile(system, maxwell3d, code);
} else { } else {
ci.program = Compile(maxwell3d, macro_code->second); ci.program = Compile(system, maxwell3d, macro_code->second);
ci.hash = Common::HashValue(macro_code->second); ci.hash = Common::HashValue(macro_code->second);
} }
if (CanBeHLEProgram(ci.hash) && !Settings::values.disable_macro_hle) { if (CanBeHLEProgram(ci.hash) && !Settings::values.disable_macro_hle) {
@ -1431,10 +1427,10 @@ void MacroEngine::Execute(Engines::Maxwell3D& maxwell3d, u32 method, std::span<c
} }
} }
AnyCachedMacro MacroEngine::Compile(Engines::Maxwell3D& maxwell3d, std::span<const u32> code) { AnyCachedMacro MacroEngine::Compile(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> code) {
#ifdef ARCHITECTURE_x86_64 #ifdef ARCHITECTURE_x86_64
if (!is_interpreted) if (!is_interpreted)
return std::make_unique<MacroJITx64Impl>(code); return std::make_unique<MacroJITx64Impl>(system, code);
#endif #endif
return MacroInterpreterImpl(code); return MacroInterpreterImpl(code);
} }

View file

@ -14,6 +14,10 @@
#include "common/bit_field.h" #include "common/bit_field.h"
#include "common/common_types.h" #include "common/common_types.h"
namespace Core {
class System;
}
namespace Tegra { namespace Tegra {
namespace Engines { namespace Engines {
@ -106,61 +110,61 @@ struct HLEMacro {
/// also assigning the base vertex/instance. /// also assigning the base vertex/instance.
struct HLE_DrawArraysIndirect final { struct HLE_DrawArraysIndirect final {
HLE_DrawArraysIndirect(bool extended_) noexcept : extended{extended_} {} HLE_DrawArraysIndirect(bool extended_) noexcept : extended{extended_} {}
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method); void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
void Fallback(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters); void Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters);
bool extended; bool extended;
}; };
/// @note: these macros have two versions, a normal and extended version, with the extended version /// @note: these macros have two versions, a normal and extended version, with the extended version
/// also assigning the base vertex/instance. /// also assigning the base vertex/instance.
struct HLE_DrawIndexedIndirect final { struct HLE_DrawIndexedIndirect final {
explicit HLE_DrawIndexedIndirect(bool extended_) noexcept : extended{extended_} {} explicit HLE_DrawIndexedIndirect(bool extended_) noexcept : extended{extended_} {}
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method); void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
void Fallback(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters); void Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters);
bool extended; bool extended;
}; };
struct HLE_MultiLayerClear final { struct HLE_MultiLayerClear final {
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method); void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
}; };
struct HLE_MultiDrawIndexedIndirectCount final { struct HLE_MultiDrawIndexedIndirectCount final {
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method); void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
void Fallback(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters); void Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters);
}; };
struct HLE_DrawIndirectByteCount final { struct HLE_DrawIndirectByteCount final {
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method); void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
void Fallback(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters); void Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters);
}; };
struct HLE_C713C83D8F63CCF3 final { struct HLE_C713C83D8F63CCF3 final {
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method); void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
}; };
struct HLE_D7333D26E0A93EDE final { struct HLE_D7333D26E0A93EDE final {
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method); void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
}; };
struct HLE_BindShader final { struct HLE_BindShader final {
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method); void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
}; };
struct HLE_SetRasterBoundingBox final { struct HLE_SetRasterBoundingBox final {
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method); void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
}; };
struct HLE_ClearConstBuffer final { struct HLE_ClearConstBuffer final {
HLE_ClearConstBuffer(size_t base_size_) noexcept : base_size{base_size_} {} HLE_ClearConstBuffer(size_t base_size_) noexcept : base_size{base_size_} {}
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method); void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
size_t base_size; size_t base_size;
}; };
struct HLE_ClearMemory final { struct HLE_ClearMemory final {
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method); void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
std::vector<u32> zero_memory; std::vector<u32> zero_memory;
}; };
struct HLE_TransformFeedbackSetup final { struct HLE_TransformFeedbackSetup final {
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method); void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
}; };
struct MacroInterpreterImpl final { struct MacroInterpreterImpl final {
MacroInterpreterImpl() {} MacroInterpreterImpl() {}
MacroInterpreterImpl(std::span<const u32> code_) : code{code_} {} MacroInterpreterImpl(std::span<const u32> code_) : code{code_} {}
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> params, u32 method); void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> params, u32 method);
void Reset(); void Reset();
bool Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slot); bool Step(Core::System& system, Engines::Maxwell3D& maxwell3d, bool is_delay_slot);
u32 GetALUResult(Macro::ALUOperation operation, u32 src_a, u32 src_b); u32 GetALUResult(Macro::ALUOperation operation, u32 src_a, u32 src_b);
void ProcessResult(Engines::Maxwell3D& maxwell3d, Macro::ResultOperation operation, u32 reg, u32 result); void ProcessResult(Core::System& system, Engines::Maxwell3D& maxwell3d, Macro::ResultOperation operation, u32 reg, u32 result);
bool EvaluateBranchCondition(Macro::BranchCondition cond, u32 value) const; bool EvaluateBranchCondition(Macro::BranchCondition cond, u32 value) const;
Macro::Opcode GetOpcode() const; Macro::Opcode GetOpcode() const;
u32 GetRegister(u32 register_id) const; u32 GetRegister(u32 register_id) const;
@ -169,7 +173,7 @@ struct MacroInterpreterImpl final {
[[nodiscard]] inline void SetMethodAddress(u32 address) noexcept { [[nodiscard]] inline void SetMethodAddress(u32 address) noexcept {
method_address.raw = address; method_address.raw = address;
} }
void Send(Engines::Maxwell3D& maxwell3d, u32 value); void Send(Core::System& system, Engines::Maxwell3D& maxwell3d, u32 value);
u32 Read(Engines::Maxwell3D& maxwell3d, u32 method) const; u32 Read(Engines::Maxwell3D& maxwell3d, u32 method) const;
u32 FetchParameter(); u32 FetchParameter();
/// General purpose macro registers. /// General purpose macro registers.
@ -192,7 +196,7 @@ struct DynamicCachedMacro {
/// Executes the macro code with the specified input parameters. /// Executes the macro code with the specified input parameters.
/// @param parameters The parameters of the macro /// @param parameters The parameters of the macro
/// @param method The method to execute /// @param method The method to execute
virtual void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, u32 method) = 0; virtual void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, u32 method) = 0;
}; };
using AnyCachedMacro = std::variant< using AnyCachedMacro = std::variant<
@ -227,8 +231,8 @@ struct MacroEngine {
uploaded_macro_code.erase(method); uploaded_macro_code.erase(method);
} }
// Compiles the macro if its not in the cache, and executes the compiled macro // Compiles the macro if its not in the cache, and executes the compiled macro
void Execute(Engines::Maxwell3D& maxwell3d, u32 method, std::span<const u32> parameters); void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, u32 method, std::span<const u32> parameters);
AnyCachedMacro Compile(Engines::Maxwell3D& maxwell3d, std::span<const u32> code); AnyCachedMacro Compile(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> code);
struct CacheInfo { struct CacheInfo {
AnyCachedMacro program; AnyCachedMacro program;
u64 hash{}; u64 hash{};

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -204,7 +207,7 @@ void SetupDirtyMisc(Tables& tables) {
} // Anonymous namespace } // Anonymous namespace
void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) { void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) {
auto& tables{channel_state.maxwell_3d->dirty.tables}; auto& tables{channel_state.payload->maxwell_3d.dirty.tables};
SetupDirtyFlags(tables); SetupDirtyFlags(tables);
SetupDirtyColorMasks(tables); SetupDirtyColorMasks(tables);
SetupDirtyViewports(tables); SetupDirtyViewports(tables);
@ -231,7 +234,7 @@ void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) {
} }
void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) { void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) {
flags = &channel_state.maxwell_3d->dirty.flags; flags = &channel_state.payload->maxwell_3d.dirty.flags;
} }
void StateTracker::InvalidateState() { void StateTracker::InvalidateState() {

View file

@ -126,16 +126,14 @@ public:
current_query = nullptr; current_query = nullptr;
amend_value = 0; amend_value = 0;
accumulation_value = 0; accumulation_value = 0;
queries_prefix_scan_pass = std::make_unique<QueriesPrefixScanPass>( queries_prefix_scan_pass.emplace(device, scheduler, descriptor_pool, compute_pass_descriptor_queue);
device, scheduler, descriptor_pool, compute_pass_descriptor_queue);
const VkBufferCreateInfo buffer_ci = { const VkBufferCreateInfo buffer_ci = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
.flags = 0, .flags = 0,
.size = 8, .size = 8,
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE, .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0, .queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr, .pQueueFamilyIndices = nullptr,
@ -592,8 +590,7 @@ private:
VideoCommon::HostQueryBase* current_query; VideoCommon::HostQueryBase* current_query;
bool has_started{}; bool has_started{};
std::mutex flush_guard; std::mutex flush_guard;
std::optional<QueriesPrefixScanPass> queries_prefix_scan_pass;
std::unique_ptr<QueriesPrefixScanPass> queries_prefix_scan_pass;
}; };
// Transform feedback queries // Transform feedback queries
@ -1266,41 +1263,23 @@ private:
} // namespace } // namespace
struct QueryCacheRuntimeImpl { struct QueryCacheRuntimeImpl {
QueryCacheRuntimeImpl(QueryCacheRuntime& runtime, VideoCore::RasterizerInterface* rasterizer_, QueryCacheRuntimeImpl(QueryCacheRuntime& runtime, VideoCore::RasterizerInterface* rasterizer_, Tegra::MaxwellDeviceMemoryManager& device_memory_, Vulkan::BufferCache& buffer_cache_, const Device& device_, const MemoryAllocator& memory_allocator_, Scheduler& scheduler_, StagingBufferPool& staging_pool_, ComputePassDescriptorQueue& compute_pass_descriptor_queue, DescriptorPool& descriptor_pool, TextureCache& texture_cache_)
Tegra::MaxwellDeviceMemoryManager& device_memory_, : rasterizer{rasterizer_}, device_memory{device_memory_}, buffer_cache{buffer_cache_}
Vulkan::BufferCache& buffer_cache_, const Device& device_, , device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_}
const MemoryAllocator& memory_allocator_, Scheduler& scheduler_, , staging_pool{staging_pool_}, guest_streamer(0, runtime)
StagingBufferPool& staging_pool_, , sample_streamer(size_t(QueryType::ZPassPixelCount64), runtime, rasterizer, texture_cache_, device, scheduler, memory_allocator, compute_pass_descriptor_queue, descriptor_pool)
ComputePassDescriptorQueue& compute_pass_descriptor_queue, , tfb_streamer(size_t(QueryType::StreamingByteCount), runtime, device, scheduler, memory_allocator, staging_pool)
DescriptorPool& descriptor_pool, TextureCache& texture_cache_) , primitives_succeeded_streamer(size_t(QueryType::StreamingPrimitivesSucceeded), runtime, tfb_streamer, device_memory_)
: rasterizer{rasterizer_}, device_memory{device_memory_}, buffer_cache{buffer_cache_}, , primitives_needed_minus_succeeded_streamer(size_t(QueryType::StreamingPrimitivesNeededMinusSucceeded), runtime, 0u)
device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_}, , hcr_setup{}, hcr_is_set{}, is_hcr_running{}, maxwell3d{} {
staging_pool{staging_pool_}, guest_streamer(0, runtime),
sample_streamer(static_cast<size_t>(QueryType::ZPassPixelCount64), runtime, rasterizer,
texture_cache_, device, scheduler, memory_allocator,
compute_pass_descriptor_queue, descriptor_pool),
tfb_streamer(static_cast<size_t>(QueryType::StreamingByteCount), runtime, device,
scheduler, memory_allocator, staging_pool),
primitives_succeeded_streamer(
static_cast<size_t>(QueryType::StreamingPrimitivesSucceeded), runtime, tfb_streamer,
device_memory_),
primitives_needed_minus_succeeded_streamer(
static_cast<size_t>(QueryType::StreamingPrimitivesNeededMinusSucceeded), runtime, 0u),
hcr_setup{}, hcr_is_set{}, is_hcr_running{}, maxwell3d{} {
hcr_setup.sType = VK_STRUCTURE_TYPE_CONDITIONAL_RENDERING_BEGIN_INFO_EXT; hcr_setup.sType = VK_STRUCTURE_TYPE_CONDITIONAL_RENDERING_BEGIN_INFO_EXT;
hcr_setup.pNext = nullptr; hcr_setup.pNext = nullptr;
hcr_setup.flags = 0; hcr_setup.flags = 0;
const bool has_conditional_rendering = device.IsExtConditionalRendering(); VkBufferUsageFlags hcr_buffer_usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
if (has_conditional_rendering) { if (device.IsExtConditionalRendering()) {
conditional_resolve_pass = std::make_unique<ConditionalRenderingResolvePass>( conditional_resolve_pass.emplace(device, scheduler, descriptor_pool, compute_pass_descriptor_queue);
device, scheduler, descriptor_pool, compute_pass_descriptor_queue);
}
VkBufferUsageFlags hcr_buffer_usage =
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
if (has_conditional_rendering) {
hcr_buffer_usage |= VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT; hcr_buffer_usage |= VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT;
} }
@ -1339,7 +1318,7 @@ struct QueryCacheRuntimeImpl {
std::vector<std::vector<VkBufferCopy>> copies_setup; std::vector<std::vector<VkBufferCopy>> copies_setup;
// Host conditional rendering data // Host conditional rendering data
std::unique_ptr<ConditionalRenderingResolvePass> conditional_resolve_pass; std::optional<ConditionalRenderingResolvePass> conditional_resolve_pass;
vk::Buffer hcr_resolve_buffer; vk::Buffer hcr_resolve_buffer;
VkConditionalRenderingBeginInfoEXT hcr_setup; VkConditionalRenderingBeginInfoEXT hcr_setup;
VkBuffer hcr_buffer; VkBuffer hcr_buffer;
@ -1351,13 +1330,7 @@ struct QueryCacheRuntimeImpl {
Maxwell3D* maxwell3d; Maxwell3D* maxwell3d;
}; };
QueryCacheRuntime::QueryCacheRuntime(VideoCore::RasterizerInterface* rasterizer, QueryCacheRuntime::QueryCacheRuntime(VideoCore::RasterizerInterface* rasterizer, Tegra::MaxwellDeviceMemoryManager& device_memory_, Vulkan::BufferCache& buffer_cache_, const Device& device_, const MemoryAllocator& memory_allocator_, Scheduler& scheduler_, StagingBufferPool& staging_pool_, ComputePassDescriptorQueue& compute_pass_descriptor_queue, DescriptorPool& descriptor_pool, TextureCache& texture_cache_) {
Tegra::MaxwellDeviceMemoryManager& device_memory_,
Vulkan::BufferCache& buffer_cache_, const Device& device_,
const MemoryAllocator& memory_allocator_,
Scheduler& scheduler_, StagingBufferPool& staging_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue,
DescriptorPool& descriptor_pool, TextureCache& texture_cache_) {
impl = std::make_unique<QueryCacheRuntimeImpl>( impl = std::make_unique<QueryCacheRuntimeImpl>(
*this, rasterizer, device_memory_, buffer_cache_, device_, memory_allocator_, scheduler_, *this, rasterizer, device_memory_, buffer_cache_, device_, memory_allocator_, scheduler_,
staging_pool_, compute_pass_descriptor_queue, descriptor_pool, texture_cache_); staging_pool_, compute_pass_descriptor_queue, descriptor_pool, texture_cache_);
@ -1686,8 +1659,7 @@ void QueryCacheRuntime::SyncValues(std::span<SyncValuesType> values, VkBuffer ba
} }
impl->scheduler.RequestOutsideRenderPassOperationContext(); impl->scheduler.RequestOutsideRenderPassOperationContext();
impl->scheduler.Record([src_buffer, dst_buffers = std::move(impl->buffers_to_upload_to), impl->scheduler.Record([src_buffer, dst_buffers = std::move(impl->buffers_to_upload_to), vk_copies = std::move(impl->copies_setup)](vk::CommandBuffer cmdbuf) {
vk_copies = std::move(impl->copies_setup)](vk::CommandBuffer cmdbuf) {
size_t size = dst_buffers.size(); size_t size = dst_buffers.size();
for (size_t i = 0; i < size; i++) { for (size_t i = 0; i < size; i++) {
cmdbuf.CopyBuffer(src_buffer, dst_buffers[i].first, vk_copies[i]); cmdbuf.CopyBuffer(src_buffer, dst_buffers[i].first, vk_copies[i]);

View file

@ -235,7 +235,7 @@ void SetupRasterModes(Maxwell3D::DirtyState::Tables &tables) {
} // Anonymous namespace } // Anonymous namespace
void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) { void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) {
auto& tables{channel_state.maxwell_3d->dirty.tables}; auto& tables{channel_state.payload->maxwell_3d.dirty.tables};
SetupDirtyFlags(tables); SetupDirtyFlags(tables);
SetupDirtyViewports(tables); SetupDirtyViewports(tables);
SetupDirtyScissors(tables); SetupDirtyScissors(tables);
@ -258,7 +258,7 @@ void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) {
} }
void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) { void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) {
flags = &channel_state.maxwell_3d->dirty.flags; flags = &channel_state.payload->maxwell_3d.dirty.flags;
} }
void StateTracker::InvalidateState() { void StateTracker::InvalidateState() {

View file

@ -1,10 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include <array> #include <array>
#include <variant>
#include "video_core/vulkan_common/vulkan_wrapper.h" #include "video_core/vulkan_common/vulkan_wrapper.h"
namespace Vulkan { namespace Vulkan {
@ -12,20 +15,15 @@ namespace Vulkan {
class Device; class Device;
class Scheduler; class Scheduler;
struct DescriptorUpdateEntry { union DescriptorUpdateEntry {
struct Empty {};
DescriptorUpdateEntry() = default; DescriptorUpdateEntry() = default;
DescriptorUpdateEntry(VkDescriptorImageInfo image_) : image{image_} {} DescriptorUpdateEntry(VkDescriptorImageInfo image_) : image{image_} {}
DescriptorUpdateEntry(VkDescriptorBufferInfo buffer_) : buffer{buffer_} {} DescriptorUpdateEntry(VkDescriptorBufferInfo buffer_) : buffer{buffer_} {}
DescriptorUpdateEntry(VkBufferView texel_buffer_) : texel_buffer{texel_buffer_} {} DescriptorUpdateEntry(VkBufferView texel_buffer_) : texel_buffer{texel_buffer_} {}
std::monostate empty{};
union { VkDescriptorImageInfo image;
Empty empty{}; VkDescriptorBufferInfo buffer;
VkDescriptorImageInfo image; VkBufferView texel_buffer;
VkDescriptorBufferInfo buffer;
VkBufferView texel_buffer;
};
}; };
class UpdateDescriptorQueue final { class UpdateDescriptorQueue final {

View file

@ -45,23 +45,23 @@ std::unique_ptr<VideoCore::RendererBase> CreateRenderer(Core::System& system, Co
namespace VideoCore { namespace VideoCore {
std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) { /// @brief Creates an emulated GPU instance using the given system context.
void CreateGPU(std::optional<Tegra::GPU>& gpu, Core::Frontend::EmuWindow& emu_window, Core::System& system) {
Settings::UpdateRescalingInfo(); Settings::UpdateRescalingInfo();
const auto nvdec_value = Settings::values.nvdec_emulation.GetValue(); const auto nvdec_value = Settings::values.nvdec_emulation.GetValue();
const bool use_nvdec = nvdec_value != Settings::NvdecEmulation::Off; const bool use_nvdec = nvdec_value != Settings::NvdecEmulation::Off;
const bool use_async = Settings::values.use_asynchronous_gpu_emulation.GetValue(); const bool use_async = Settings::values.use_asynchronous_gpu_emulation.GetValue();
auto gpu = std::make_unique<Tegra::GPU>(system, use_async, use_nvdec); gpu.emplace(system, use_async, use_nvdec);
auto context = emu_window.CreateSharedContext(); auto context = emu_window.CreateSharedContext();
auto scope = context->Acquire(); auto scope = context->Acquire();
try { try {
auto renderer = CreateRenderer(system, emu_window, *gpu, std::move(context)); auto renderer = CreateRenderer(system, emu_window, *gpu, std::move(context));
gpu->BindRenderer(std::move(renderer)); gpu->BindRenderer(std::move(renderer));
return gpu;
} catch (const std::runtime_error& exception) { } catch (const std::runtime_error& exception) {
scope.Cancel(); scope.Cancel();
LOG_ERROR(HW_GPU, "Failed to initialize GPU: {}", exception.what()); LOG_ERROR(HW_GPU, "Failed to initialize GPU: {}", exception.what());
return nullptr; gpu.reset();
} }
} }

View file

@ -1,9 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2014 Citra Emulator Project // SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include <memory> #include <memory>
#include <optional>
namespace Core { namespace Core {
class System; class System;
@ -20,8 +24,6 @@ class GPU;
namespace VideoCore { namespace VideoCore {
class RendererBase; class RendererBase;
void CreateGPU(std::optional<Tegra::GPU>& gpu, Core::Frontend::EmuWindow& emu_window, Core::System& system);
/// Creates an emulated GPU instance using the given system context.
std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system);
} // namespace VideoCore } // namespace VideoCore