mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-06-06 01:13:45 -04:00
Compare commits
21 commits
331348da3d
...
42e967eb34
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42e967eb34 | ||
|
|
bfa237220c | ||
|
|
1a82215976 | ||
|
|
9cff7b8337 | ||
|
|
2e857190dc | ||
|
|
e03f3dd25c | ||
|
|
cd079ed97f | ||
|
|
3a875e50d6 | ||
|
|
736fa0540e | ||
|
|
2c2d69887f | ||
|
|
9ab941fc23 | ||
|
|
9702a87277 | ||
|
|
7ab6c88ad9 | ||
|
|
09b9210739 | ||
|
|
e4d16aadab | ||
|
|
8f991ecf79 | ||
|
|
cd23ec0b07 | ||
|
|
2f89c65c83 | ||
|
|
ecc82cf0b7 | ||
|
|
4baf6aa160 | ||
|
|
65f6dd2654 |
68 changed files with 1025 additions and 1226 deletions
|
|
@ -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-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -8,10 +11,11 @@
|
|||
|
||||
namespace AudioCore {
|
||||
|
||||
AudioCore::AudioCore(Core::System& system) : audio_manager{std::make_unique<AudioManager>()} {
|
||||
AudioCore::AudioCore(Core::System& system) {
|
||||
audio_manager.emplace();
|
||||
CreateSinks();
|
||||
// Must be created after the sinks
|
||||
adsp = std::make_unique<ADSP::ADSP>(system, *output_sink);
|
||||
adsp.emplace(system, *output_sink);
|
||||
}
|
||||
|
||||
AudioCore ::~AudioCore() {
|
||||
|
|
|
|||
|
|
@ -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-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -15,10 +18,7 @@ class System;
|
|||
|
||||
namespace AudioCore {
|
||||
|
||||
class AudioManager;
|
||||
/**
|
||||
* Main audio class, stored inside the core, and holding the audio manager, all sinks, and the ADSP.
|
||||
*/
|
||||
/// @brief Main audio class, stored inside the core, and holding the audio manager, all sinks, and the ADSP.
|
||||
class AudioCore {
|
||||
public:
|
||||
explicit AudioCore(Core::System& system);
|
||||
|
|
@ -50,27 +50,22 @@ public:
|
|||
*/
|
||||
Sink::Sink& GetInputSink();
|
||||
|
||||
/**
|
||||
* Get the ADSP.
|
||||
*
|
||||
* @return Ref to the ADSP.
|
||||
*/
|
||||
/// @brief Get the ADSP.
|
||||
/// @return Ref to the ADSP.
|
||||
ADSP::ADSP& ADSP();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Create the sinks on startup.
|
||||
*/
|
||||
/// @brief Create the sinks on startup.
|
||||
void CreateSinks();
|
||||
|
||||
/// 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
|
||||
std::unique_ptr<Sink::Sink> output_sink;
|
||||
/// Sink used for audio input
|
||||
std::unique_ptr<Sink::Sink> input_sink;
|
||||
/// The ADSP in the sysmodule
|
||||
std::unique_ptr<ADSP::ADSP> adsp;
|
||||
std::optional<ADSP::ADSP> adsp;
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
||||
|
|
|
|||
|
|
@ -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-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -41,8 +44,7 @@ void Manager::ReleaseSessionId(const size_t session_id) {
|
|||
Result Manager::LinkToManager() {
|
||||
std::scoped_lock l{mutex};
|
||||
if (!linked_to_manager) {
|
||||
AudioManager& manager{system.AudioCore().GetAudioManager()};
|
||||
manager.SetInManager(std::bind(&Manager::BufferReleaseAndRegister, this));
|
||||
system.AudioCore().GetAudioManager().SetInManager(std::bind(&Manager::BufferReleaseAndRegister, this));
|
||||
linked_to_manager = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,73 +1,28 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "audio_core/audio_manager.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/audio/errors.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
AudioManager::AudioManager() {
|
||||
thread = std::jthread([this]() { ThreadFunc(); });
|
||||
}
|
||||
|
||||
void AudioManager::Shutdown() {
|
||||
running = false;
|
||||
events.SetAudioEvent(Event::Type::Max, true);
|
||||
thread.join();
|
||||
}
|
||||
|
||||
Result AudioManager::SetOutManager(BufferEventFunc buffer_func) {
|
||||
if (!running) {
|
||||
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) {
|
||||
if (!running) {
|
||||
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) {
|
||||
events.SetAudioEvent(type, signalled);
|
||||
}
|
||||
|
||||
void AudioManager::ThreadFunc() {
|
||||
thread = std::jthread([this](std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("AudioManager");
|
||||
std::unique_lock l{events.GetAudioEventLock()};
|
||||
events.ClearEvents();
|
||||
running = true;
|
||||
|
||||
while (running) {
|
||||
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 = static_cast<Event::Type>(i);
|
||||
|
||||
const auto event_type = Event::Type(i);
|
||||
if (events.CheckAudioEventSet(event_type) || timed_out) {
|
||||
if (buffer_events[i]) {
|
||||
buffer_events[i]();
|
||||
|
|
@ -76,6 +31,47 @@ void AudioManager::ThreadFunc() {
|
|||
events.SetAudioEvent(event_type, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void AudioManager::Shutdown() {
|
||||
events.SetAudioEvent(Event::Type::Max, true);
|
||||
if (thread.joinable()) {
|
||||
thread.request_stop();
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
Result AudioManager::SetOutManager(BufferEventFunc buffer_func) {
|
||||
if (thread.joinable()) {
|
||||
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;
|
||||
}
|
||||
|
||||
Result AudioManager::SetInManager(BufferEventFunc buffer_func) {
|
||||
if (thread.joinable()) {
|
||||
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;
|
||||
}
|
||||
|
||||
void AudioManager::SetEvent(const Event::Type type, const bool signalled) {
|
||||
events.SetAudioEvent(type, signalled);
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
|
|
|
|||
|
|
@ -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-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -66,13 +69,6 @@ public:
|
|||
void SetEvent(Event::Type type, bool signalled);
|
||||
|
||||
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
|
||||
bool needs_update{};
|
||||
/// Events to be set and signalled
|
||||
|
|
|
|||
|
|
@ -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-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -40,8 +43,7 @@ void Manager::ReleaseSessionId(const size_t session_id) {
|
|||
Result Manager::LinkToManager() {
|
||||
std::scoped_lock l{mutex};
|
||||
if (!linked_to_manager) {
|
||||
AudioManager& manager{system.AudioCore().GetAudioManager()};
|
||||
manager.SetOutManager(std::bind(&Manager::BufferReleaseAndRegister, this));
|
||||
system.AudioCore().GetAudioManager().SetOutManager(std::bind(&Manager::BufferReleaseAndRegister, this));
|
||||
linked_to_manager = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -271,7 +271,7 @@ struct System::Impl {
|
|||
|
||||
SystemResultStatus SetupForApplicationProcess(System& system, Frontend::EmuWindow& emu_window) {
|
||||
host1x_core.emplace(system);
|
||||
gpu_core = VideoCore::CreateGPU(emu_window, system);
|
||||
VideoCore::CreateGPU(gpu_core, emu_window, system);
|
||||
if (!gpu_core)
|
||||
return SystemResultStatus::ErrorVideoCore;
|
||||
|
||||
|
|
@ -347,7 +347,7 @@ struct System::Impl {
|
|||
|
||||
// Register with applet manager
|
||||
// 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_current_game) {
|
||||
|
|
@ -391,10 +391,8 @@ struct System::Impl {
|
|||
is_powered_on = false;
|
||||
exit_locked = false;
|
||||
exit_requested = false;
|
||||
|
||||
if (gpu_core != nullptr) {
|
||||
if (gpu_core)
|
||||
gpu_core->NotifyShutdown();
|
||||
}
|
||||
|
||||
stop_event.request_stop();
|
||||
core_timing.SyncPause(false);
|
||||
|
|
@ -478,6 +476,7 @@ struct System::Impl {
|
|||
std::optional<Memory::CheatEngine> cheat_engine;
|
||||
std::optional<Tools::Freezer> memory_freezer;
|
||||
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::vector<std::vector<u8>> user_channel;
|
||||
|
|
@ -492,7 +491,6 @@ struct System::Impl {
|
|||
std::unique_ptr<FileSys::ContentProviderUnion> content_provider;
|
||||
/// AppLoader used to load the current executing application
|
||||
std::unique_ptr<Loader::AppLoader> app_loader;
|
||||
std::unique_ptr<Tegra::GPU> gpu_core;
|
||||
std::stop_source stop_event;
|
||||
|
||||
mutable std::mutex suspend_guard;
|
||||
|
|
|
|||
|
|
@ -25,9 +25,11 @@ CpuManager::~CpuManager() = default;
|
|||
|
||||
void CpuManager::Initialize() {
|
||||
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++)
|
||||
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() {
|
||||
|
|
@ -63,7 +65,7 @@ void CpuManager::HandleInterrupt() {
|
|||
auto& kernel = system.Kernel();
|
||||
auto core_index = kernel.CurrentPhysicalCoreIndex();
|
||||
|
||||
Kernel::KInterruptManager::HandleInterrupt(kernel, static_cast<s32>(core_index));
|
||||
Kernel::KInterruptManager::HandleInterrupt(kernel, s32(core_index));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -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-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -86,22 +89,20 @@ private:
|
|||
void ShutdownThread();
|
||||
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 {
|
||||
std::shared_ptr<Common::Fiber> host_context;
|
||||
std::jthread host_thread;
|
||||
};
|
||||
|
||||
std::unique_ptr<Common::Barrier> gpu_barrier{};
|
||||
std::array<CoreData, Core::Hardware::NUM_CPU_CORES> core_data{};
|
||||
|
||||
bool is_async_gpu{};
|
||||
bool is_multicore{};
|
||||
System& system;
|
||||
std::atomic<std::size_t> current_core{};
|
||||
std::size_t idle_count{};
|
||||
std::size_t num_cores{};
|
||||
static constexpr std::size_t max_cycle_runs = 5;
|
||||
|
||||
System& system;
|
||||
bool is_async_gpu{};
|
||||
bool is_multicore{};
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
|
|
|||
|
|
@ -268,7 +268,7 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) {
|
|||
|
||||
if (Settings::values.enable_overlay && m_window_system->GetOverlayDisplayApplet() == nullptr) {
|
||||
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->applet_id = AppletId::OverlayDisplay;
|
||||
overlay_applet->type = AppletType::OverlayApplet;
|
||||
|
|
|
|||
|
|
@ -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-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <thread>
|
||||
#include "common/thread.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/am/am_types.h"
|
||||
#include "core/hle/service/am/button_poller.h"
|
||||
|
|
@ -34,14 +36,13 @@ ButtonPressDuration ClassifyPressDuration(std::chrono::steady_clock::time_point
|
|||
|
||||
} // namespace
|
||||
|
||||
ButtonPoller::ButtonPoller(Core::System& system, WindowSystem& window_system)
|
||||
: m_window_system(window_system) {
|
||||
ButtonPoller::ButtonPoller(Core::System& system, WindowSystem& window_system) {
|
||||
// TODO: am reads this from the home button state in hid, which is controller-agnostic.
|
||||
Core::HID::ControllerUpdateCallback engine_callback{
|
||||
.on_change =
|
||||
[this](Core::HID::ControllerTriggerType type) {
|
||||
.on_change = [this, &window_system](Core::HID::ControllerTriggerType type) {
|
||||
if (type == Core::HID::ControllerTriggerType::Button) {
|
||||
this->OnButtonStateChanged();
|
||||
std::unique_lock lk{m_mutex};
|
||||
OnButtonStateChanged(window_system);
|
||||
}
|
||||
},
|
||||
.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_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() {
|
||||
m_handheld->DeleteCallback(m_handheld_key);
|
||||
m_player1->DeleteCallback(m_player1_key);
|
||||
m_stop = true;
|
||||
m_cv.notify_all();
|
||||
if (m_thread.joinable()) {
|
||||
m_thread.request_stop();
|
||||
m_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void ButtonPoller::OnButtonStateChanged() {
|
||||
std::lock_guard lk{m_mutex};
|
||||
const bool home_button =
|
||||
m_handheld->GetHomeButtons().home.Value() || m_player1->GetHomeButtons().home.Value();
|
||||
const bool capture_button = m_handheld->GetCaptureButtons().capture.Value() ||
|
||||
m_player1->GetCaptureButtons().capture.Value();
|
||||
void ButtonPoller::OnButtonStateChanged(WindowSystem& window_system) {
|
||||
auto const home_button = m_handheld->GetHomeButtons().home.Value()
|
||||
|| m_player1->GetHomeButtons().home.Value();
|
||||
auto const capture_button = m_handheld->GetCaptureButtons().capture.Value()
|
||||
|| m_player1->GetCaptureButtons().capture.Value();
|
||||
|
||||
// Buttons pressed which were not previously pressed
|
||||
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) {
|
||||
const auto duration = ClassifyPressDuration(*m_home_button_press_start);
|
||||
if (duration != ButtonPressDuration::ShortPressing) {
|
||||
m_window_system.OnSystemButtonPress(SystemButtonType::HomeButtonLongPressing);
|
||||
window_system.OnSystemButtonPress(SystemButtonType::HomeButtonLongPressing);
|
||||
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) {
|
||||
const auto duration = ClassifyPressDuration(*m_capture_button_press_start);
|
||||
if (duration != ButtonPressDuration::ShortPressing) {
|
||||
m_window_system.OnSystemButtonPress(SystemButtonType::CaptureButtonLongPressing);
|
||||
window_system.OnSystemButtonPress(SystemButtonType::CaptureButtonLongPressing);
|
||||
m_capture_button_long_sent = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -107,9 +118,8 @@ void ButtonPoller::OnButtonStateChanged() {
|
|||
if (!home_button && m_home_button_press_start) {
|
||||
if(!m_home_button_long_sent) {
|
||||
const auto duration = ClassifyPressDuration(*m_home_button_press_start);
|
||||
m_window_system.OnSystemButtonPress(
|
||||
duration == ButtonPressDuration::ShortPressing ? SystemButtonType::HomeButtonShortPressing
|
||||
: SystemButtonType::HomeButtonLongPressing);
|
||||
window_system.OnSystemButtonPress(duration == ButtonPressDuration::ShortPressing
|
||||
? SystemButtonType::HomeButtonShortPressing : SystemButtonType::HomeButtonLongPressing);
|
||||
}
|
||||
m_home_button_press_start = std::nullopt;
|
||||
m_home_button_long_sent = false;
|
||||
|
|
@ -117,9 +127,8 @@ void ButtonPoller::OnButtonStateChanged() {
|
|||
if (!capture_button && m_capture_button_press_start) {
|
||||
if (!m_capture_button_long_sent) {
|
||||
const auto duration = ClassifyPressDuration(*m_capture_button_press_start);
|
||||
m_window_system.OnSystemButtonPress(
|
||||
duration == ButtonPressDuration::ShortPressing ? SystemButtonType::CaptureButtonShortPressing
|
||||
: SystemButtonType::CaptureButtonLongPressing);
|
||||
window_system.OnSystemButtonPress(duration == ButtonPressDuration::ShortPressing
|
||||
? SystemButtonType::CaptureButtonShortPressing : SystemButtonType::CaptureButtonLongPressing);
|
||||
}
|
||||
m_capture_button_press_start = std::nullopt;
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
|
|
@ -30,31 +30,23 @@ class ButtonPoller {
|
|||
public:
|
||||
explicit ButtonPoller(Core::System& system, WindowSystem& window_system);
|
||||
~ButtonPoller();
|
||||
void OnButtonStateChanged(WindowSystem& window_system);
|
||||
|
||||
private:
|
||||
void OnButtonStateChanged();
|
||||
void ThreadLoop();
|
||||
|
||||
private:
|
||||
WindowSystem& m_window_system;
|
||||
|
||||
Core::HID::EmulatedController* m_handheld{};
|
||||
int m_handheld_key{};
|
||||
Core::HID::EmulatedController* m_player1{};
|
||||
int m_player1_key{};
|
||||
|
||||
std::mutex m_mutex;
|
||||
std::condition_variable m_cv;
|
||||
std::jthread m_thread;
|
||||
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_power_button_press_start{};
|
||||
|
||||
bool m_home_button_long_sent{};
|
||||
bool m_capture_button_long_sent{};
|
||||
bool m_power_button_long_sent{};
|
||||
|
||||
std::thread m_thread;
|
||||
std::atomic<bool> m_stop{false};
|
||||
std::condition_variable m_cv;
|
||||
std::mutex m_mutex;
|
||||
Core::HID::EmulatedController* m_handheld{};
|
||||
Core::HID::EmulatedController* m_player1{};
|
||||
int32_t m_handheld_key{};
|
||||
int32_t m_player1_key{};
|
||||
bool m_home_button_long_sent : 1 = false;
|
||||
bool m_capture_button_long_sent : 1 = false;
|
||||
bool m_power_button_long_sent : 1 = false;
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
||||
|
|
|
|||
|
|
@ -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-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -15,12 +18,24 @@ enum class UserDataTag : u32 {
|
|||
};
|
||||
|
||||
EventObserver::EventObserver(Core::System& system, WindowSystem& window_system)
|
||||
: m_system(system), m_context(system, "am:EventObserver"), m_window_system(window_system),
|
||||
m_wakeup_event(m_context), m_wakeup_holder(m_wakeup_event.GetHandle()) {
|
||||
: m_system(system), m_context(system, "am:EventObserver")
|
||||
, m_window_system(window_system)
|
||||
, m_wakeup_event(m_context)
|
||||
, m_wakeup_holder(m_wakeup_event.GetHandle())
|
||||
{
|
||||
m_window_system.SetEventObserver(this);
|
||||
m_wakeup_holder.SetUserData(static_cast<uintptr_t>(UserDataTag::WakeupEvent));
|
||||
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() {
|
||||
|
|
@ -146,17 +161,4 @@ void EventObserver::DestroyAppletProcessHolderLocked(ProcessHolder* 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
|
||||
|
|
|
|||
|
|
@ -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-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -41,9 +44,6 @@ private:
|
|||
private:
|
||||
void DestroyAppletProcessHolderLocked(ProcessHolder* holder);
|
||||
|
||||
private:
|
||||
void ThreadFunc();
|
||||
|
||||
private:
|
||||
// System reference and context.
|
||||
Core::System& m_system;
|
||||
|
|
|
|||
|
|
@ -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-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <optional>
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
|
|
@ -16,7 +20,7 @@ namespace Service::AM {
|
|||
|
||||
namespace {
|
||||
|
||||
FileSys::StorageId GetStorageIdForFrontendSlot(
|
||||
[[nodiscard]] FileSys::StorageId GetStorageIdForFrontendSlot(
|
||||
std::optional<FileSys::ContentProviderUnionSlot> slot) {
|
||||
if (!slot.has_value()) {
|
||||
return FileSys::StorageId::None;
|
||||
|
|
@ -36,31 +40,23 @@ FileSys::StorageId GetStorageIdForFrontendSlot(
|
|||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<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) {
|
||||
[[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) {
|
||||
// Get the appropriate loader to parse this NCA.
|
||||
out_loader = Loader::GetLoader(system, file, program_id, program_index);
|
||||
|
||||
// Ensure we have a loader which can parse the NCA.
|
||||
if (!out_loader) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (out_loader) {
|
||||
// Try to load the process.
|
||||
auto process = std::make_unique<Process>(system);
|
||||
auto process = std::make_optional<Process>(system);
|
||||
if (process->Initialize(*out_loader, out_load_result)) {
|
||||
return process;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id,
|
||||
u8 minimum_key_generation, u8 maximum_key_generation) {
|
||||
std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation) {
|
||||
// Attempt to load program NCA.
|
||||
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.
|
||||
if (!nca_raw) {
|
||||
return nullptr;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// 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 &&
|
||||
(nca.GetKeyGeneration() < minimum_key_generation ||
|
||||
nca.GetKeyGeneration() > maximum_key_generation)) {
|
||||
LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id,
|
||||
nca.GetKeyGeneration());
|
||||
return nullptr;
|
||||
LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id, nca.GetKeyGeneration());
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -90,17 +85,8 @@ std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id,
|
|||
return CreateProcessImpl(loader, status, system, nca_raw, program_id, 0);
|
||||
}
|
||||
|
||||
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) {
|
||||
auto process =
|
||||
CreateProcessImpl(out_loader, out_load_result, system, file, program_id, program_index);
|
||||
if (!process) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (auto process = CreateProcessImpl(out_loader, out_load_result, system, file, program_id, program_index); process) {
|
||||
FileSys::NACP nacp;
|
||||
if (out_loader->ReadControlData(nacp) == Loader::ResultStatus::Success) {
|
||||
out_control = nacp.GetRawBytes();
|
||||
|
|
@ -118,14 +104,13 @@ std::unique_ptr<Process> CreateApplicationProcess(std::vector<u8>& out_control,
|
|||
|
||||
// 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));
|
||||
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;
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
|
|
|||
|
|
@ -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-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -24,12 +27,7 @@ class Process;
|
|||
|
||||
namespace Service::AM {
|
||||
|
||||
std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id,
|
||||
u8 minimum_key_generation, u8 maximum_key_generation);
|
||||
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);
|
||||
std::optional<Process> CreateProcess(Core::System& system, u64 program_id, 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);
|
||||
|
||||
} // namespace Service::AM
|
||||
|
|
|
|||
|
|
@ -21,8 +21,7 @@ namespace Service::AM {
|
|||
|
||||
namespace {
|
||||
|
||||
Result CreateGuestApplication(SharedPointer<IApplicationAccessor>* out_application_accessor,
|
||||
Core::System& system, WindowSystem& window_system, u64 program_id) {
|
||||
Result CreateGuestApplication(SharedPointer<IApplicationAccessor>* out_application_accessor, Core::System& system, WindowSystem& window_system, u64 program_id) {
|
||||
FileSys::VirtualFile nca_raw{};
|
||||
|
||||
// Get the program NCA from storage.
|
||||
|
|
@ -35,11 +34,10 @@ Result CreateGuestApplication(SharedPointer<IApplicationAccessor>* out_applicati
|
|||
std::vector<u8> control;
|
||||
std::unique_ptr<Loader::AppLoader> loader;
|
||||
Loader::ResultStatus result;
|
||||
auto process =
|
||||
CreateApplicationProcess(control, loader, result, system, nca_raw, program_id, 0);
|
||||
R_UNLESS(process != nullptr, ResultUnknown);
|
||||
auto process = CreateApplicationProcess(control, loader, result, system, nca_raw, program_id, 0);
|
||||
R_UNLESS(process != std::nullopt, 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->applet_id = AppletId::Application;
|
||||
applet->type = AppletType::Application;
|
||||
|
|
@ -47,8 +45,7 @@ Result CreateGuestApplication(SharedPointer<IApplicationAccessor>* out_applicati
|
|||
|
||||
window_system.TrackApplet(applet, true);
|
||||
|
||||
*out_application_accessor =
|
||||
std::make_shared<IApplicationAccessor>(system, applet, window_system);
|
||||
*out_application_accessor = std::make_shared<IApplicationAccessor>(system, applet, window_system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
|
@ -90,12 +87,10 @@ Result IApplicationCreator::CreateSystemApplication(
|
|||
|
||||
std::vector<u8> control;
|
||||
std::unique_ptr<Loader::AppLoader> loader;
|
||||
auto process = CreateProcess(system, application_id, 1, 22);
|
||||
R_UNLESS(process != std::nullopt, ResultUnknown);
|
||||
|
||||
auto process =
|
||||
CreateProcess(system, application_id, 1, 22);
|
||||
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 = application_id;
|
||||
applet->applet_id = AppletId::Starter;
|
||||
applet->type = AppletType::LibraryApplet;
|
||||
|
|
@ -103,8 +98,7 @@ Result IApplicationCreator::CreateSystemApplication(
|
|||
|
||||
m_window_system.TrackApplet(applet, true);
|
||||
|
||||
*out_application_accessor =
|
||||
std::make_shared<IApplicationAccessor>(system, applet, m_window_system);
|
||||
*out_application_accessor = std::make_shared<IApplicationAccessor>(system, applet, m_window_system);
|
||||
Core::LaunchTimestampCache::SaveLaunchTimestamp(application_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,12 +122,8 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
|
|||
};
|
||||
|
||||
auto process = CreateProcess(system, program_id, Firmware1400, Firmware2200);
|
||||
if (!process) {
|
||||
// Couldn't initialize the guest process
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto applet = std::make_shared<Applet>(system, std::move(process), false);
|
||||
if (process) {
|
||||
const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), false);
|
||||
applet->program_id = program_id;
|
||||
applet->applet_id = applet_id;
|
||||
applet->type = AppletType::LibraryApplet;
|
||||
|
|
@ -138,10 +134,11 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& 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
|
||||
return {};
|
||||
}
|
||||
|
||||
std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(Core::System& system,
|
||||
|
|
|
|||
|
|
@ -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-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
|
|
@ -14,7 +14,9 @@ namespace Service::Audio {
|
|||
using namespace AudioCore::AudioOut;
|
||||
|
||||
IAudioOutManager::IAudioOutManager(Core::System& system_)
|
||||
: ServiceFramework{system_, "audout:u"}, impl{std::make_unique<Manager>(system_)} {
|
||||
: ServiceFramework{system_, "audout:u"}
|
||||
, impl(system_)
|
||||
{
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IAudioOutManager::ListAudioOuts>, "ListAudioOuts"},
|
||||
|
|
|
|||
|
|
@ -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-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
|
|
@ -43,7 +43,7 @@ private:
|
|||
AudioCore::AudioOut::AudioOutParameter parameter,
|
||||
InCopyHandle<Kernel::KProcess> process_handle, ClientAppletResourceUserId aruid);
|
||||
|
||||
std::unique_ptr<AudioCore::AudioOut::Manager> impl;
|
||||
std::optional<AudioCore::AudioOut::Manager> impl;
|
||||
};
|
||||
|
||||
} // namespace Service::Audio
|
||||
|
|
|
|||
|
|
@ -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-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -15,7 +18,9 @@ namespace Service::Audio {
|
|||
using namespace AudioCore::Renderer;
|
||||
|
||||
IAudioRendererManager::IAudioRendererManager(Core::System& system_)
|
||||
: ServiceFramework{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} {
|
||||
: ServiceFramework{system_, "audren:u"}
|
||||
, impl(system_)
|
||||
{
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IAudioRendererManager::OpenAudioRenderer>, "OpenAudioRenderer"},
|
||||
|
|
|
|||
|
|
@ -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-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -30,7 +33,7 @@ private:
|
|||
Result GetAudioDeviceServiceWithRevisionInfo(Out<SharedPointer<IAudioDevice>> out_audio_device,
|
||||
u32 revision, ClientAppletResourceUserId aruid);
|
||||
|
||||
std::unique_ptr<AudioCore::Renderer::Manager> impl;
|
||||
std::optional<AudioCore::Renderer::Manager> impl;
|
||||
u32 num_audio_devices{0};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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)};
|
||||
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,
|
||||
vm.big_page_size_bits, VM::PAGE_SIZE_BITS);
|
||||
gmmu = std::make_unique<Tegra::MemoryManager>(system, max_big_page_bits, vm.va_range_split, vm.big_page_size_bits, VM::PAGE_SIZE_BITS);
|
||||
system.GPU().InitAddressSpace(*gmmu);
|
||||
vm.initialised = true;
|
||||
|
||||
|
|
@ -416,7 +415,7 @@ NvResult nvhost_as_gpu::BindChannel(IoctlBindChannel& params) {
|
|||
LOG_DEBUG(Service_NVDRV, "called, fd={:X}", 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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -219,7 +219,7 @@ private:
|
|||
|
||||
bool initialised{};
|
||||
} vm;
|
||||
std::shared_ptr<Tegra::MemoryManager> gmmu;
|
||||
std::unique_ptr<Tegra::MemoryManager> gmmu;
|
||||
};
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
|
|
|||
|
|
@ -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-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -10,14 +13,6 @@
|
|||
|
||||
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) {
|
||||
// First, ensure we are not holding another process.
|
||||
this->Finalize();
|
||||
|
|
|
|||
|
|
@ -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-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -22,8 +25,8 @@ namespace Service {
|
|||
|
||||
class Process {
|
||||
public:
|
||||
explicit Process(Core::System& system);
|
||||
~Process();
|
||||
inline explicit Process(Core::System& system) noexcept : m_system(system) {}
|
||||
inline ~Process() { this->Finalize(); }
|
||||
|
||||
bool Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_load_result);
|
||||
void Finalize();
|
||||
|
|
@ -50,8 +53,8 @@ public:
|
|||
private:
|
||||
Core::System& m_system;
|
||||
Kernel::KProcess* m_process{};
|
||||
s32 m_main_thread_priority{};
|
||||
u64 m_main_thread_stack_size{};
|
||||
s32 m_main_thread_priority{};
|
||||
bool m_process_started{};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include "common/param_package.h"
|
||||
#include "common/random.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/thread.h"
|
||||
#include "input_common/drivers/udp_client.h"
|
||||
#include "input_common/helpers/udp_protocol.h"
|
||||
|
||||
|
|
@ -131,6 +132,7 @@ private:
|
|||
};
|
||||
|
||||
static void SocketLoop(Socket* socket) {
|
||||
Common::SetCurrentThreadName("cemuhookWorker");
|
||||
socket->StartReceive();
|
||||
socket->StartSend(Socket::clock::now());
|
||||
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) {
|
||||
SocketCallback callback{[this](Response::Version version) { OnVersion(version); },
|
||||
SocketCallback callback{
|
||||
[this](Response::Version version) { OnVersion(version); },
|
||||
[this](Response::PortInfo info) { OnPortInfo(info); },
|
||||
[this, client](Response::PadData data) { OnPadData(data, client); }};
|
||||
[this, client](Response::PadData data) { OnPadData(data, client); }
|
||||
};
|
||||
LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port);
|
||||
clients[client].uuid = GetHostUUID(host);
|
||||
clients[client].host = host;
|
||||
|
|
@ -566,9 +570,7 @@ bool UDPClient::IsStickInverted(const Common::ParamPackage& params) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void TestCommunication(const std::string& host, u16 port,
|
||||
const std::function<void()>& success_callback,
|
||||
const std::function<void()>& failure_callback) {
|
||||
void TestCommunication(const std::string& host, u16 port, const std::function<void()>& success_callback, const std::function<void()>& failure_callback) {
|
||||
std::thread([=] {
|
||||
Common::Event success_event;
|
||||
SocketCallback callback{
|
||||
|
|
@ -601,8 +603,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
|
|||
u16 max_y{};
|
||||
|
||||
Status current_status{Status::Initialized};
|
||||
SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {},
|
||||
[&](Response::PadData data) {
|
||||
SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {}, [&](Response::PadData data) {
|
||||
constexpr u16 CALIBRATION_THRESHOLD = 100;
|
||||
|
||||
if (current_status == Status::Initialized) {
|
||||
|
|
@ -613,10 +614,9 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
|
|||
if (data.touch[0].is_active == 0) {
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG(Input, "Current touch: {} {}", data.touch[0].x,
|
||||
data.touch[0].y);
|
||||
min_x = (std::min)(min_x, static_cast<u16>(data.touch[0].x));
|
||||
min_y = (std::min)(min_y, static_cast<u16>(data.touch[0].y));
|
||||
LOG_DEBUG(Input, "Current touch: {} {}", data.touch[0].x, data.touch[0].y);
|
||||
min_x = (std::min)(min_x, u16(data.touch[0].x));
|
||||
min_y = (std::min)(min_y, u16(data.touch[0].y));
|
||||
if (current_status == Status::Ready) {
|
||||
// First touch - min data (min_x/min_y)
|
||||
current_status = Status::Stage1Completed;
|
||||
|
|
|
|||
|
|
@ -334,7 +334,7 @@ if (YUZU_USE_EXTERNAL_FFMPEG)
|
|||
add_dependencies(video_core ffmpeg-build)
|
||||
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_options(video_core PRIVATE ${FFmpeg_LDFLAGS})
|
||||
|
|
|
|||
|
|
@ -20,8 +20,7 @@
|
|||
namespace Tegra {
|
||||
|
||||
CDmaPusher::CDmaPusher(Host1x::Host1x& host1x_, s32 id)
|
||||
: host_processor(host1x_)
|
||||
, host1x{host1x_}
|
||||
: host1x{host1x_}
|
||||
, current_class{ChClassId(id)}
|
||||
{
|
||||
thread = std::jthread([this](std::stop_token stop_token) {
|
||||
|
|
@ -99,7 +98,7 @@ void CDmaPusher::ExecuteCommand(u32 method, u32 arg) {
|
|||
switch (current_class) {
|
||||
case ChClassId::Control:
|
||||
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;
|
||||
default:
|
||||
thi_regs.reg_array[method] = arg;
|
||||
|
|
|
|||
|
|
@ -17,28 +17,36 @@
|
|||
|
||||
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);
|
||||
program_id = program_id_;
|
||||
dma_pusher.emplace(system, gpu, *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);
|
||||
payload.emplace(system, *memory_manager, *this);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
void ChannelState::BindRasterizer(VideoCore::RasterizerInterface* rasterizer) {
|
||||
dma_pusher->BindRasterizer(rasterizer);
|
||||
payload->dma_pusher.BindRasterizer(rasterizer);
|
||||
memory_manager->BindRasterizer(rasterizer);
|
||||
maxwell_3d->BindRasterizer(rasterizer);
|
||||
fermi_2d->BindRasterizer(rasterizer);
|
||||
kepler_memory->BindRasterizer(rasterizer);
|
||||
kepler_compute->BindRasterizer(rasterizer);
|
||||
maxwell_dma->BindRasterizer(rasterizer);
|
||||
payload->maxwell_3d.BindRasterizer(rasterizer);
|
||||
payload->fermi_2d.BindRasterizer(rasterizer);
|
||||
payload->kepler_memory.BindRasterizer(rasterizer);
|
||||
payload->kepler_compute.BindRasterizer(rasterizer);
|
||||
payload->maxwell_dma.BindRasterizer(rasterizer);
|
||||
//payload->nv01_timer.BindRasterizer(rasterizer);
|
||||
}
|
||||
|
||||
} // namespace Tegra::Control
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include "video_core/engines/kepler_compute.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/engines/maxwell_dma.h"
|
||||
#include "video_core/engines/nv01_timer.h"
|
||||
#include "video_core/dma_pusher.h"
|
||||
|
||||
namespace Core {
|
||||
|
|
@ -34,28 +35,33 @@ namespace Control {
|
|||
struct ChannelState {
|
||||
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);
|
||||
|
||||
struct Payload {
|
||||
explicit Payload(Core::System& system, MemoryManager& memory_manager, ChannelState& channel_state);
|
||||
|
||||
/// 3D engine
|
||||
std::optional<Engines::Maxwell3D> maxwell_3d;
|
||||
Engines::Maxwell3D maxwell_3d;
|
||||
/// 2D engine
|
||||
std::optional<Engines::Fermi2D> fermi_2d;
|
||||
Engines::Fermi2D fermi_2d;
|
||||
/// Compute engine
|
||||
std::optional<Engines::KeplerCompute> kepler_compute;
|
||||
Engines::KeplerCompute kepler_compute;
|
||||
/// DMA engine
|
||||
std::optional<Engines::MaxwellDMA> maxwell_dma;
|
||||
Engines::MaxwellDMA maxwell_dma;
|
||||
/// Inline memory engine
|
||||
std::optional<Engines::KeplerMemory> kepler_memory;
|
||||
Engines::KeplerMemory kepler_memory;
|
||||
/// NV01 Timer
|
||||
std::optional<Engines::KeplerMemory> nv01_timer;
|
||||
std::optional<DmaPusher> dma_pusher;
|
||||
std::shared_ptr<MemoryManager> memory_manager;
|
||||
Engines::Nv01Timer nv01_timer;
|
||||
DmaPusher dma_pusher;
|
||||
};
|
||||
std::optional<Payload> payload;
|
||||
MemoryManager* memory_manager = nullptr;
|
||||
|
||||
s32 bind_id = -1;
|
||||
u64 program_id = 0;
|
||||
bool initialized{};
|
||||
bool initialized = false;
|
||||
};
|
||||
|
||||
} // namespace Control
|
||||
|
|
|
|||
|
|
@ -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-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
|
|
@ -6,8 +9,11 @@
|
|||
namespace VideoCommon {
|
||||
|
||||
ChannelInfo::ChannelInfo(Tegra::Control::ChannelState& channel_state)
|
||||
: maxwell3d{*channel_state.maxwell_3d}, kepler_compute{*channel_state.kepler_compute},
|
||||
gpu_memory{*channel_state.memory_manager}, program_id{channel_state.program_id} {}
|
||||
: maxwell3d{channel_state.payload->maxwell_3d}
|
||||
, 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>;
|
||||
|
||||
|
|
|
|||
|
|
@ -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-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{
|
||||
.ref_count = 1,
|
||||
.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);
|
||||
OnGPUASRegister(channel.memory_manager->GetID());
|
||||
|
|
|
|||
|
|
@ -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-FileCopyrightText: 2021 yuzu Emulator Project
|
||||
|
|
@ -12,11 +12,8 @@
|
|||
#include "video_core/gpu.h"
|
||||
|
||||
namespace Tegra::Control {
|
||||
Scheduler::Scheduler(GPU& gpu_) : gpu{gpu_} {}
|
||||
|
||||
Scheduler::~Scheduler() = default;
|
||||
|
||||
void Scheduler::Push(s32 channel, CommandList&& entries) {
|
||||
void Scheduler::Push(GPU& gpu, s32 channel, CommandList&& entries) {
|
||||
std::shared_ptr<ChannelState> channel_state;
|
||||
{
|
||||
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.
|
||||
// Multiple channels can prepare their commands in parallel.
|
||||
channel_state->dma_pusher->Push(std::move(entries));
|
||||
channel_state->dma_pusher->DispatchCalls();
|
||||
channel_state->payload->dma_pusher.Push(std::move(entries));
|
||||
channel_state->payload->dma_pusher.DispatchCalls();
|
||||
}
|
||||
|
||||
void Scheduler::DeclareChannel(std::shared_ptr<ChannelState> new_channel) {
|
||||
|
|
|
|||
|
|
@ -22,17 +22,13 @@ struct ChannelState;
|
|||
|
||||
class Scheduler {
|
||||
public:
|
||||
explicit Scheduler(GPU& gpu_);
|
||||
~Scheduler();
|
||||
|
||||
void Push(s32 channel, CommandList&& entries);
|
||||
void Push(GPU& gpu, s32 channel, CommandList&& entries);
|
||||
|
||||
void DeclareChannel(std::shared_ptr<ChannelState> new_channel);
|
||||
|
||||
private:
|
||||
ankerl::unordered_dense::map<s32, std::shared_ptr<ChannelState>> channels;
|
||||
std::mutex scheduling_guard;
|
||||
GPU& gpu;
|
||||
};
|
||||
|
||||
} // namespace Control
|
||||
|
|
|
|||
|
|
@ -12,37 +12,32 @@
|
|||
#include "video_core/guest_memory.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
#include "video_core/texture_cache/util.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
constexpr u32 MacroRegistersStart = 0xE00;
|
||||
[[maybe_unused]] constexpr u32 ComputeInline = 0x6D;
|
||||
|
||||
DmaPusher::DmaPusher(Core::System& system_, GPU& gpu_, MemoryManager& memory_manager_,
|
||||
Control::ChannelState& channel_state_)
|
||||
: gpu{gpu_}, system{system_}, memory_manager{memory_manager_}, puller{gpu_, memory_manager_,
|
||||
*this, channel_state_}, signal_sync{false}, synced{true} {}
|
||||
DmaPusher::DmaPusher(Core::System& system_, MemoryManager& memory_manager_, Control::ChannelState& channel_state_)
|
||||
: system{system_}
|
||||
, memory_manager{memory_manager_}
|
||||
, channel_state{channel_state_}
|
||||
, signal_sync{false}
|
||||
, synced{false}
|
||||
{}
|
||||
|
||||
DmaPusher::~DmaPusher() = default;
|
||||
|
||||
void DmaPusher::DispatchCalls() {
|
||||
|
||||
dma_pushbuffer_subindex = 0;
|
||||
|
||||
dma_state.is_last_call = true;
|
||||
|
||||
while (system.IsPoweredOn()) {
|
||||
if (!Step()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
gpu.FlushCommands();
|
||||
gpu.OnCommandListEnd();
|
||||
system.GPU().FlushCommands();
|
||||
system.GPU().OnCommandListEnd();
|
||||
}
|
||||
|
||||
bool DmaPusher::Step() {
|
||||
|
|
@ -171,9 +166,9 @@ void DmaPusher::SetState(const CommandHeader& command_header) {
|
|||
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) {
|
||||
puller.CallPullerMethod(Engines::Puller::MethodCall{
|
||||
puller.CallPullerMethod(*this, Engines::Puller::MethodCall{
|
||||
dma_state.method,
|
||||
argument,
|
||||
dma_state.subchannel,
|
||||
|
|
@ -181,30 +176,29 @@ void DmaPusher::CallMethod(u32 argument) const {
|
|||
});
|
||||
} else {
|
||||
auto subchannel = subchannels[dma_state.subchannel];
|
||||
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 {
|
||||
if (!subchannel->execution_mask[dma_state.method]) {
|
||||
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) {
|
||||
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 {
|
||||
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->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_) {
|
||||
rasterizer = rasterizer_;
|
||||
puller.BindRasterizer(rasterizer);
|
||||
}
|
||||
|
||||
} // namespace Tegra
|
||||
|
|
|
|||
|
|
@ -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-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
|
|
@ -109,25 +109,21 @@ inline CommandHeader BuildCommandHeader(BufferMethods method, u32 arg_count, Sub
|
|||
struct CommandList final {
|
||||
CommandList() = default;
|
||||
explicit CommandList(std::size_t size) : command_lists(size) {}
|
||||
explicit CommandList(
|
||||
boost::container::small_vector<CommandHeader, 512>&& prefetch_command_list_)
|
||||
explicit CommandList(boost::container::small_vector<CommandHeader, 512>&& prefetch_command_list_)
|
||||
: prefetch_command_list{std::move(prefetch_command_list_)} {}
|
||||
|
||||
boost::container::small_vector<CommandListHeader, 512> command_lists;
|
||||
boost::container::small_vector<CommandHeader, 512> prefetch_command_list;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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
|
||||
* details on this implementation.
|
||||
*/
|
||||
/// @brief 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
|
||||
/// 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
|
||||
/// details on this implementation.
|
||||
class DmaPusher final {
|
||||
public:
|
||||
explicit DmaPusher(Core::System& system_, GPU& gpu_, MemoryManager& memory_manager_,
|
||||
Control::ChannelState& channel_state_);
|
||||
explicit DmaPusher(Core::System& system_, MemoryManager& memory_manager_, Control::ChannelState& channel_state_);
|
||||
~DmaPusher();
|
||||
|
||||
void Push(CommandList&& entries) {
|
||||
|
|
@ -136,8 +132,7 @@ public:
|
|||
|
||||
void DispatchCalls();
|
||||
|
||||
void BindSubchannel(Engines::EngineInterface* engine, u32 subchannel_id,
|
||||
Engines::EngineTypes engine_type) {
|
||||
void BindSubchannel(Engines::EngineInterface* engine, u32 subchannel_id, Engines::EngineTypes engine_type) {
|
||||
subchannels[subchannel_id] = engine;
|
||||
subchannel_type[subchannel_id] = engine_type;
|
||||
}
|
||||
|
|
@ -152,11 +147,11 @@ private:
|
|||
|
||||
void SetState(const CommandHeader& command_header);
|
||||
|
||||
void CallMethod(u32 argument) const;
|
||||
void CallMultiMethod(const u32* base_start, u32 num_methods) const;
|
||||
void CallMethod(u32 argument);
|
||||
void CallMultiMethod(const u32* base_start, u32 num_methods);
|
||||
|
||||
Common::ScratchBuffer<CommandHeader>
|
||||
command_headers; ///< Buffer for list of commands fetched at once
|
||||
public:
|
||||
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::size_t dma_pushbuffer_subindex{}; ///< Index within a command list within the pushbuffer
|
||||
|
|
@ -172,24 +167,24 @@ private:
|
|||
bool is_last_call;
|
||||
};
|
||||
|
||||
Core::System& system;
|
||||
MemoryManager& memory_manager;
|
||||
Control::ChannelState& channel_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::EngineTypes, max_subchannels> subchannel_type;
|
||||
|
||||
GPU& gpu;
|
||||
Core::System& system;
|
||||
MemoryManager& memory_manager;
|
||||
mutable Engines::Puller puller;
|
||||
|
||||
VideoCore::RasterizerInterface* rasterizer;
|
||||
bool signal_sync;
|
||||
bool synced;
|
||||
Engines::Puller puller;
|
||||
std::mutex sync_mutex;
|
||||
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
|
||||
|
|
|
|||
|
|
@ -12,6 +12,10 @@
|
|||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Tegra::Engines {
|
||||
|
||||
enum class EngineTypes : u32 {
|
||||
|
|
@ -28,28 +32,25 @@ public:
|
|||
virtual ~EngineInterface() = default;
|
||||
|
||||
/// 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.
|
||||
virtual void CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
||||
u32 methods_pending) = 0;
|
||||
virtual void CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) = 0;
|
||||
|
||||
void ConsumeSink() {
|
||||
if (method_sink.empty()) {
|
||||
return;
|
||||
void ConsumeSink(Core::System& system) {
|
||||
if (!method_sink.empty()) {
|
||||
ConsumeSinkImpl(system);
|
||||
}
|
||||
ConsumeSinkImpl();
|
||||
}
|
||||
|
||||
std::bitset<(std::numeric_limits<u16>::max)()> execution_mask{};
|
||||
std::vector<std::pair<u32, u32>> method_sink{};
|
||||
bool current_dirty{};
|
||||
GPUVAddr current_dma_segment;
|
||||
|
||||
bool current_dirty{};
|
||||
protected:
|
||||
virtual void ConsumeSinkImpl() {
|
||||
virtual void ConsumeSinkImpl(Core::System& system) {
|
||||
for (auto [method, value] : method_sink) {
|
||||
CallMethod(method, value, true);
|
||||
CallMethod(system, method, value, true);
|
||||
}
|
||||
method_sink.clear();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,9 +36,8 @@ void Fermi2D::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
|
|||
rasterizer = rasterizer_;
|
||||
}
|
||||
|
||||
void Fermi2D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
|
||||
ASSERT_MSG(method < Regs::NUM_REGS,
|
||||
"Invalid Fermi2D register, increase the size of the Regs structure");
|
||||
void Fermi2D::CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) {
|
||||
ASSERT_MSG(method < Regs::NUM_REGS, "Invalid Fermi2D register, increase the size of the Regs structure");
|
||||
regs.reg_array[method] = method_argument;
|
||||
|
||||
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) {
|
||||
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) {
|
||||
regs.reg_array[method] = value;
|
||||
}
|
||||
|
|
@ -60,8 +59,7 @@ void Fermi2D::ConsumeSinkImpl() {
|
|||
}
|
||||
|
||||
void Fermi2D::Blit() {
|
||||
LOG_DEBUG(HW_GPU, "called. source address=0x{:x}, destination address=0x{:x}",
|
||||
regs.src.Address(), regs.dst.Address());
|
||||
LOG_DEBUG(HW_GPU, "called. source address=0x{:x}, destination address=0x{:x}", regs.src.Address(), regs.dst.Address());
|
||||
|
||||
UNIMPLEMENTED_IF_MSG(regs.operation != Operation::SrcCopy, "Operation is not copy");
|
||||
UNIMPLEMENTED_IF_MSG(regs.src.layer != 0, "Source layer is not zero");
|
||||
|
|
|
|||
|
|
@ -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-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -44,11 +47,10 @@ public:
|
|||
void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
|
||||
|
||||
/// 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.
|
||||
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;
|
||||
|
||||
enum class Origin : u32 {
|
||||
Center = 0,
|
||||
|
|
@ -311,7 +313,7 @@ private:
|
|||
/// registers.
|
||||
void Blit();
|
||||
|
||||
void ConsumeSinkImpl() override;
|
||||
void ConsumeSinkImpl(Core::System& system) override;
|
||||
};
|
||||
|
||||
#define ASSERT_REG_POSITION(field_name, position) \
|
||||
|
|
|
|||
|
|
@ -16,8 +16,10 @@
|
|||
|
||||
namespace Tegra::Engines {
|
||||
|
||||
KeplerCompute::KeplerCompute(Core::System& system_, MemoryManager& memory_manager_)
|
||||
: system{system_}, memory_manager{memory_manager_}, upload_state{memory_manager, regs.upload} {
|
||||
KeplerCompute::KeplerCompute(MemoryManager& memory_manager_)
|
||||
: memory_manager{memory_manager_}
|
||||
, upload_state{memory_manager, regs.upload}
|
||||
{
|
||||
execution_mask.reset();
|
||||
execution_mask[KEPLER_COMPUTE_REG_INDEX(exec_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);
|
||||
}
|
||||
|
||||
void KeplerCompute::ConsumeSinkImpl() {
|
||||
void KeplerCompute::ConsumeSinkImpl(Core::System& system) {
|
||||
for (auto [method, value] : method_sink) {
|
||||
regs.reg_array[method] = value;
|
||||
}
|
||||
method_sink.clear();
|
||||
}
|
||||
|
||||
void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
|
||||
ASSERT_MSG(method < Regs::NUM_REGS,
|
||||
"Invalid KeplerCompute register, increase the size of the Regs structure");
|
||||
void KeplerCompute::CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) {
|
||||
ASSERT_MSG(method < Regs::NUM_REGS, "Invalid KeplerCompute register, increase the size of the Regs structure");
|
||||
|
||||
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,
|
||||
u32 methods_pending) {
|
||||
void KeplerCompute::CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) {
|
||||
switch (method) {
|
||||
case KEPLER_COMPUTE_REG_INDEX(data_upload):
|
||||
upload_address = current_dma_segment;
|
||||
|
|
@ -87,7 +87,7 @@ void KeplerCompute::CallMultiMethod(u32 method, const u32* base_start, u32 amoun
|
|||
return;
|
||||
default:
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -42,7 +45,7 @@ namespace Tegra::Engines {
|
|||
|
||||
class KeplerCompute final : public EngineInterface {
|
||||
public:
|
||||
explicit KeplerCompute(Core::System& system, MemoryManager& memory_manager);
|
||||
explicit KeplerCompute(MemoryManager& memory_manager);
|
||||
~KeplerCompute();
|
||||
|
||||
/// Binds a rasterizer to this engine.
|
||||
|
|
@ -199,11 +202,10 @@ public:
|
|||
"KeplerCompute LaunchParams has wrong size");
|
||||
|
||||
/// 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.
|
||||
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;
|
||||
|
||||
std::optional<GPUVAddr> GetIndirectComputeAddress() const {
|
||||
return indirect_compute;
|
||||
|
|
@ -212,7 +214,7 @@ public:
|
|||
private:
|
||||
void ProcessLaunch();
|
||||
|
||||
void ConsumeSinkImpl() override;
|
||||
void ConsumeSinkImpl(Core::System& system) override;
|
||||
|
||||
/// Retrieves information about a specific TIC entry from the TIC buffer.
|
||||
Texture::TICEntry GetTICEntry(u32 tic_index) const;
|
||||
|
|
@ -220,7 +222,6 @@ private:
|
|||
/// Retrieves information about a specific TSC entry from the TSC buffer.
|
||||
Texture::TSCEntry GetTSCEntry(u32 tsc_index) const;
|
||||
|
||||
Core::System& system;
|
||||
MemoryManager& memory_manager;
|
||||
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
||||
Upload::State upload_state;
|
||||
|
|
|
|||
|
|
@ -14,8 +14,9 @@
|
|||
|
||||
namespace Tegra::Engines {
|
||||
|
||||
KeplerMemory::KeplerMemory(Core::System& system_, MemoryManager& memory_manager)
|
||||
: system{system_}, upload_state{memory_manager, regs.upload} {}
|
||||
KeplerMemory::KeplerMemory(MemoryManager& memory_manager)
|
||||
: upload_state{memory_manager, regs.upload}
|
||||
{}
|
||||
|
||||
KeplerMemory::~KeplerMemory() = default;
|
||||
|
||||
|
|
@ -27,16 +28,15 @@ void KeplerMemory::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
|
|||
execution_mask[KEPLERMEMORY_REG_INDEX(data)] = true;
|
||||
}
|
||||
|
||||
void KeplerMemory::ConsumeSinkImpl() {
|
||||
void KeplerMemory::ConsumeSinkImpl(Core::System& system) {
|
||||
for (auto [method, value] : method_sink) {
|
||||
regs.reg_array[method] = value;
|
||||
}
|
||||
method_sink.clear();
|
||||
}
|
||||
|
||||
void KeplerMemory::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
|
||||
ASSERT_MSG(method < Regs::NUM_REGS,
|
||||
"Invalid KeplerMemory register, increase the size of the Regs structure");
|
||||
void KeplerMemory::CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) {
|
||||
ASSERT_MSG(method < Regs::NUM_REGS, "Invalid KeplerMemory register, increase the size of the Regs structure");
|
||||
|
||||
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,
|
||||
u32 methods_pending) {
|
||||
void KeplerMemory::CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) {
|
||||
switch (method) {
|
||||
case KEPLERMEMORY_REG_INDEX(data):
|
||||
upload_state.ProcessData(base_start, amount);
|
||||
return;
|
||||
default:
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -36,18 +39,17 @@ namespace Tegra::Engines {
|
|||
|
||||
class KeplerMemory final : public EngineInterface {
|
||||
public:
|
||||
explicit KeplerMemory(Core::System& system_, MemoryManager& memory_manager);
|
||||
explicit KeplerMemory(MemoryManager& memory_manager);
|
||||
~KeplerMemory() override;
|
||||
|
||||
/// Binds a rasterizer to this engine.
|
||||
void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
|
||||
|
||||
/// 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.
|
||||
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;
|
||||
|
||||
struct Regs {
|
||||
static constexpr size_t NUM_REGS = 0x7F;
|
||||
|
|
@ -73,9 +75,7 @@ public:
|
|||
} regs{};
|
||||
|
||||
private:
|
||||
void ConsumeSinkImpl() override;
|
||||
|
||||
Core::System& system;
|
||||
void ConsumeSinkImpl(Core::System& system) override;
|
||||
Upload::State upload_state;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -25,9 +25,8 @@ namespace Tegra::Engines {
|
|||
/// First register id that is actually a Macro call.
|
||||
constexpr u32 MacroRegistersStart = 0xE00;
|
||||
|
||||
Maxwell3D::Maxwell3D(Core::System& system_, MemoryManager& memory_manager_)
|
||||
Maxwell3D::Maxwell3D(MemoryManager& memory_manager_)
|
||||
: draw_manager()
|
||||
, system{system_}
|
||||
, memory_manager{memory_manager_}
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
, 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) {
|
||||
// A macro call must begin by writing the macro method's register, not its argument.
|
||||
ASSERT_MSG((method % 2) == 0,
|
||||
"Can't start macro execution by writing to the ARGS register");
|
||||
ASSERT((method % 2) == 0 && "Can't start macro execution by writing to the ARGS register");
|
||||
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
|
||||
if (is_last_call) {
|
||||
ConsumeSink();
|
||||
CallMacroMethod(executing_macro, macro_params);
|
||||
ConsumeSink(system);
|
||||
CallMacroMethod(system, executing_macro, macro_params);
|
||||
macro_params.clear();
|
||||
macro_addresses.clear();
|
||||
macro_segments.clear();
|
||||
|
|
@ -287,7 +285,7 @@ u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) {
|
|||
return argument;
|
||||
}
|
||||
|
||||
void Maxwell3D::ConsumeSinkImpl() {
|
||||
void Maxwell3D::ConsumeSinkImpl(Core::System& system) {
|
||||
const auto control = shadow_state.shadow_ram_control;
|
||||
if (control == Regs::ShadowRamControl::Track || control == Regs::ShadowRamControl::TrackWithFilter) {
|
||||
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.
|
||||
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());
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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 has finished execution.
|
||||
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
|
||||
// uploaded to the GPU during initialization.
|
||||
if (method >= MacroRegistersStart) {
|
||||
ProcessMacro(method, &method_argument, 1, is_last_call);
|
||||
ProcessMacro(system, method, &method_argument, 1, is_last_call);
|
||||
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);
|
||||
}
|
||||
|
||||
void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
||||
u32 methods_pending) {
|
||||
void Maxwell3D::CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) {
|
||||
// Methods after 0xE00 are special, they're actually triggers for some microcode that was
|
||||
// uploaded to the GPU during initialization.
|
||||
if (method >= MacroRegistersStart) {
|
||||
ProcessMacro(method, base_start, amount, amount == methods_pending);
|
||||
ProcessMacro(system, method, base_start, amount, amount == methods_pending);
|
||||
return;
|
||||
}
|
||||
switch (method) {
|
||||
|
|
@ -445,7 +442,7 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
|||
}
|
||||
default:
|
||||
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;
|
||||
}
|
||||
|
|
@ -467,7 +464,7 @@ void Maxwell3D::ProcessFirmwareCall4() {
|
|||
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()};
|
||||
if (long_query) {
|
||||
memory_manager.Write<u64>(sequence_address + sizeof(u64), system.GPU().GetTicks());
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ namespace Tegra::Engines {
|
|||
|
||||
class Maxwell3D final : public EngineInterface {
|
||||
public:
|
||||
explicit Maxwell3D(Core::System& system, MemoryManager& memory_manager);
|
||||
explicit Maxwell3D(MemoryManager& memory_manager);
|
||||
~Maxwell3D();
|
||||
|
||||
/// Binds a rasterizer to this engine.
|
||||
|
|
@ -3129,11 +3129,10 @@ public:
|
|||
u32 GetRegisterValue(u32 method) const;
|
||||
|
||||
/// 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.
|
||||
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;
|
||||
|
||||
bool ShouldExecute() const {
|
||||
return execute_on;
|
||||
|
|
@ -3190,13 +3189,13 @@ public:
|
|||
private:
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
|
|
@ -3212,7 +3211,7 @@ private:
|
|||
* @param method Method to 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.
|
||||
void ProcessMacroUpload(u32 data);
|
||||
|
|
@ -3227,7 +3226,7 @@ private:
|
|||
void ProcessQueryGet();
|
||||
|
||||
/// 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.
|
||||
void ProcessQueryCondition();
|
||||
|
|
@ -3242,7 +3241,6 @@ private:
|
|||
|
||||
bool IsMethodExecutable(u32 method);
|
||||
|
||||
Core::System& system;
|
||||
MemoryManager& memory_manager;
|
||||
|
||||
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
||||
|
|
|
|||
|
|
@ -21,8 +21,9 @@ namespace Tegra::Engines {
|
|||
|
||||
using namespace Texture;
|
||||
|
||||
MaxwellDMA::MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_)
|
||||
: system{system_}, memory_manager{memory_manager_} {
|
||||
MaxwellDMA::MaxwellDMA(MemoryManager& memory_manager_)
|
||||
: memory_manager{memory_manager_}
|
||||
{
|
||||
execution_mask.reset();
|
||||
execution_mask[offsetof(Regs, launch_dma) / sizeof(u32)] = true;
|
||||
}
|
||||
|
|
@ -33,14 +34,14 @@ void MaxwellDMA::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
|
|||
rasterizer = rasterizer_;
|
||||
}
|
||||
|
||||
void MaxwellDMA::ConsumeSinkImpl() {
|
||||
void MaxwellDMA::ConsumeSinkImpl(Core::System& system) {
|
||||
for (auto [method, value] : method_sink) {
|
||||
regs.reg_array[method] = value;
|
||||
}
|
||||
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");
|
||||
|
||||
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,
|
||||
u32 methods_pending) {
|
||||
void MaxwellDMA::CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) {
|
||||
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() {
|
||||
LOG_TRACE(Render_OpenGL, "DMA copy 0x{:x} -> 0x{:x}", static_cast<GPUVAddr>(regs.offset_in),
|
||||
static_cast<GPUVAddr>(regs.offset_out));
|
||||
LOG_TRACE(Render_OpenGL, "DMA copy 0x{:x} -> 0x{:x}", static_cast<GPUVAddr>(regs.offset_in), GPUVAddr(regs.offset_out));
|
||||
|
||||
// TODO(Subv): Perform more research and implement all features of this engine.
|
||||
const LaunchDMA& launch = regs.launch_dma;
|
||||
|
|
|
|||
|
|
@ -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-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -238,15 +241,14 @@ public:
|
|||
|
||||
void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
|
||||
|
||||
explicit MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_);
|
||||
explicit MaxwellDMA(MemoryManager& memory_manager_);
|
||||
~MaxwellDMA() override;
|
||||
|
||||
/// 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.
|
||||
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;
|
||||
|
||||
private:
|
||||
/// Performs the copy from the source buffer to the destination buffer as configured in the
|
||||
|
|
@ -261,9 +263,7 @@ private:
|
|||
|
||||
void ReleaseSemaphore();
|
||||
|
||||
void ConsumeSinkImpl() override;
|
||||
|
||||
Core::System& system;
|
||||
void ConsumeSinkImpl(Core::System& system) override;
|
||||
|
||||
MemoryManager& memory_manager;
|
||||
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
||||
|
|
|
|||
|
|
@ -26,18 +26,16 @@ class MemoryManager;
|
|||
namespace Tegra::Engines {
|
||||
class Nv01Timer final : public EngineInterface {
|
||||
public:
|
||||
explicit Nv01Timer(Core::System& system_, MemoryManager& memory_manager)
|
||||
: system{system_}
|
||||
{}
|
||||
~Nv01Timer() override;
|
||||
explicit Nv01Timer(MemoryManager& memory_manager) noexcept {}
|
||||
~Nv01Timer() noexcept override {}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
|
||||
|
|
@ -46,7 +44,6 @@ public:
|
|||
INSERT_PADDING_BYTES_NOINIT(0x48);
|
||||
} regs{};
|
||||
private:
|
||||
void ConsumeSinkImpl() override {}
|
||||
Core::System& system;
|
||||
void ConsumeSinkImpl(Core::System& system) override {}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,37 +22,29 @@
|
|||
|
||||
namespace Tegra::Engines {
|
||||
|
||||
Puller::Puller(GPU& gpu_, MemoryManager& memory_manager_, DmaPusher& dma_pusher_,
|
||||
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) {
|
||||
void Puller::ProcessBindMethod(DmaPusher& dma_pusher, const MethodCall& method_call) {
|
||||
// Bind the current subchannel to the desired engine id.
|
||||
LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel,
|
||||
method_call.argument);
|
||||
LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel, method_call.argument);
|
||||
const auto engine_id = static_cast<EngineID>(method_call.argument);
|
||||
bound_engines[method_call.subchannel] = engine_id;
|
||||
switch (engine_id) {
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
default:
|
||||
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) {
|
||||
case Puller::FenceOperation::Acquire:
|
||||
// UNIMPLEMENTED_MSG("Channel Scheduling pending.");
|
||||
// WaitFence(regs.fence_action.syncpoint_id, regs.fence_value);
|
||||
rasterizer->ReleaseFences();
|
||||
dma_pusher.rasterizer->ReleaseFences();
|
||||
break;
|
||||
case Puller::FenceOperation::Increment:
|
||||
rasterizer->SignalSyncPoint(regs.fence_action.syncpoint_id);
|
||||
dma_pusher.rasterizer->SignalSyncPoint(regs.fence_action.syncpoint_id);
|
||||
break;
|
||||
default:
|
||||
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 op =
|
||||
static_cast<GpuSemaphoreOperation>(regs.semaphore_trigger & semaphoreOperationMask);
|
||||
const auto op = GpuSemaphoreOperation(regs.semaphore_trigger & semaphoreOperationMask);
|
||||
if (op == GpuSemaphoreOperation::WriteLong) {
|
||||
const GPUVAddr sequence_address{regs.semaphore_address.SemaphoreAddress()};
|
||||
const u32 payload = regs.semaphore_sequence;
|
||||
rasterizer->Query(sequence_address, VideoCommon::QueryType::Payload,
|
||||
VideoCommon::QueryPropertiesFlags::HasTimeout, payload, 0);
|
||||
dma_pusher.rasterizer->Query(sequence_address, VideoCommon::QueryType::Payload, VideoCommon::QueryPropertiesFlags::HasTimeout, payload, 0);
|
||||
} else {
|
||||
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_value = regs.semaphore_sequence;
|
||||
if (op == GpuSemaphoreOperation::AcquireEqual) {
|
||||
regs.acquire_active = true;
|
||||
regs.acquire_mode = false;
|
||||
if (word != regs.acquire_value) {
|
||||
rasterizer->ReleaseFences();
|
||||
dma_pusher.rasterizer->ReleaseFences();
|
||||
continue;
|
||||
}
|
||||
} else if (op == GpuSemaphoreOperation::AcquireGequal) {
|
||||
regs.acquire_active = true;
|
||||
regs.acquire_mode = true;
|
||||
if (word < regs.acquire_value) {
|
||||
rasterizer->ReleaseFences();
|
||||
dma_pusher.rasterizer->ReleaseFences();
|
||||
continue;
|
||||
}
|
||||
} else if (op == GpuSemaphoreOperation::AcquireMask) {
|
||||
if (word && regs.semaphore_sequence == 0) {
|
||||
rasterizer->ReleaseFences();
|
||||
dma_pusher.rasterizer->ReleaseFences();
|
||||
continue;
|
||||
}
|
||||
} 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 u32 payload = regs.semaphore_release;
|
||||
rasterizer->Query(sequence_address, VideoCommon::QueryType::Payload,
|
||||
VideoCommon::QueryPropertiesFlags::IsAFence, payload, 0);
|
||||
dma_pusher.rasterizer->Query(sequence_address, VideoCommon::QueryType::Payload, VideoCommon::QueryPropertiesFlags::IsAFence, payload, 0);
|
||||
}
|
||||
|
||||
void Puller::ProcessSemaphoreAcquire() {
|
||||
u32 word = memory_manager.Read<u32>(regs.semaphore_address.SemaphoreAddress());
|
||||
void Puller::ProcessSemaphoreAcquire(DmaPusher& dma_pusher) {
|
||||
u32 word = dma_pusher.memory_manager.Read<u32>(regs.semaphore_address.SemaphoreAddress());
|
||||
const auto value = regs.semaphore_acquire;
|
||||
while (word != value) {
|
||||
regs.acquire_active = true;
|
||||
regs.acquire_value = value;
|
||||
rasterizer->ReleaseFences();
|
||||
word = memory_manager.Read<u32>(regs.semaphore_address.SemaphoreAddress());
|
||||
dma_pusher.rasterizer->ReleaseFences();
|
||||
word = dma_pusher.memory_manager.Read<u32>(regs.semaphore_address.SemaphoreAddress());
|
||||
// TODO(kemathe73) figure out how to do the acquire_timeout
|
||||
regs.acquire_mode = false;
|
||||
regs.acquire_source = false;
|
||||
|
|
@ -138,13 +127,13 @@ void Puller::ProcessSemaphoreAcquire() {
|
|||
}
|
||||
|
||||
/// 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;
|
||||
const auto method = static_cast<BufferMethods>(method_call.method);
|
||||
|
||||
switch (method) {
|
||||
case BufferMethods::BindObject: {
|
||||
ProcessBindMethod(method_call);
|
||||
ProcessBindMethod(dma_pusher, method_call);
|
||||
break;
|
||||
}
|
||||
case BufferMethods::Nop:
|
||||
|
|
@ -155,16 +144,16 @@ void Puller::CallPullerMethod(const MethodCall& method_call) {
|
|||
case BufferMethods::WrcacheFlush:
|
||||
break;
|
||||
case BufferMethods::RefCnt:
|
||||
rasterizer->SignalReference();
|
||||
dma_pusher.rasterizer->SignalReference();
|
||||
break;
|
||||
case BufferMethods::SyncpointOperation:
|
||||
ProcessFenceActionMethod();
|
||||
ProcessFenceActionMethod(dma_pusher);
|
||||
break;
|
||||
case BufferMethods::WaitForIdle:
|
||||
rasterizer->WaitForIdle();
|
||||
dma_pusher.rasterizer->WaitForIdle();
|
||||
break;
|
||||
case BufferMethods::SemaphoreOperation: {
|
||||
ProcessSemaphoreTriggerMethod();
|
||||
ProcessSemaphoreTriggerMethod(dma_pusher);
|
||||
break;
|
||||
}
|
||||
case BufferMethods::NonStallInterrupt: {
|
||||
|
|
@ -177,7 +166,7 @@ void Puller::CallPullerMethod(const MethodCall& method_call) {
|
|||
}
|
||||
case BufferMethods::MemOpB: {
|
||||
// Implement this better.
|
||||
rasterizer->InvalidateGPUCache();
|
||||
dma_pusher.rasterizer->InvalidateGPUCache();
|
||||
break;
|
||||
}
|
||||
case BufferMethods::MemOpC:
|
||||
|
|
@ -186,11 +175,11 @@ void Puller::CallPullerMethod(const MethodCall& method_call) {
|
|||
break;
|
||||
}
|
||||
case BufferMethods::SemaphoreAcquire: {
|
||||
ProcessSemaphoreAcquire();
|
||||
ProcessSemaphoreAcquire(dma_pusher);
|
||||
break;
|
||||
}
|
||||
case BufferMethods::SemaphoreRelease: {
|
||||
ProcessSemaphoreRelease();
|
||||
ProcessSemaphoreRelease(dma_pusher);
|
||||
break;
|
||||
}
|
||||
case BufferMethods::Yield: {
|
||||
|
|
@ -205,27 +194,26 @@ void Puller::CallPullerMethod(const MethodCall& method_call) {
|
|||
}
|
||||
|
||||
/// 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];
|
||||
|
||||
switch (engine) {
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented engine");
|
||||
|
|
@ -234,28 +222,26 @@ void Puller::CallEngineMethod(const MethodCall& method_call) {
|
|||
}
|
||||
|
||||
/// Calls a GPU engine multivalue method.
|
||||
void Puller::CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount,
|
||||
u32 methods_pending) {
|
||||
void Puller::CallEngineMultiMethod(DmaPusher& dma_pusher, u32 method, u32 subchannel, const u32* base_start, u32 amount, u32 methods_pending) {
|
||||
const EngineID engine = bound_engines[subchannel];
|
||||
|
||||
switch (engine) {
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented engine");
|
||||
|
|
@ -264,31 +250,26 @@ void Puller::CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_s
|
|||
}
|
||||
|
||||
/// Calls a GPU method.
|
||||
void Puller::CallMethod(const MethodCall& method_call) {
|
||||
LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method_call.method,
|
||||
method_call.subchannel);
|
||||
|
||||
void Puller::CallMethod(DmaPusher& dma_pusher, const MethodCall& method_call) {
|
||||
LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method_call.method, method_call.subchannel);
|
||||
ASSERT(method_call.subchannel < bound_engines.size());
|
||||
|
||||
if (ExecuteMethodOnEngine(method_call.method)) {
|
||||
CallEngineMethod(method_call);
|
||||
if (ExecuteMethodOnEngine(dma_pusher, method_call.method)) {
|
||||
CallEngineMethod(dma_pusher, method_call);
|
||||
} else {
|
||||
CallPullerMethod(method_call);
|
||||
CallPullerMethod(dma_pusher, method_call);
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls a GPU multivalue method.
|
||||
void Puller::CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount,
|
||||
u32 methods_pending) {
|
||||
void Puller::CallMultiMethod(DmaPusher& dma_pusher, u32 method, u32 subchannel, const u32* base_start, u32 amount, u32 methods_pending) {
|
||||
LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method, subchannel);
|
||||
|
||||
ASSERT(subchannel < bound_engines.size());
|
||||
|
||||
if (ExecuteMethodOnEngine(method)) {
|
||||
CallEngineMultiMethod(method, subchannel, base_start, amount, methods_pending);
|
||||
if (ExecuteMethodOnEngine(dma_pusher, method)) {
|
||||
CallEngineMultiMethod(dma_pusher, method, subchannel, base_start, amount, methods_pending);
|
||||
} else {
|
||||
for (u32 i = 0; i < amount; i++) {
|
||||
CallPullerMethod(MethodCall{
|
||||
CallPullerMethod(dma_pusher, MethodCall{
|
||||
method,
|
||||
base_start[i],
|
||||
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.
|
||||
[[nodiscard]] bool Puller::ExecuteMethodOnEngine(u32 method) {
|
||||
const auto buffer_method = static_cast<BufferMethods>(method);
|
||||
[[nodiscard]] bool Puller::ExecuteMethodOnEngine(DmaPusher& dma_pusher, u32 method) {
|
||||
const auto buffer_method = BufferMethods(method);
|
||||
return buffer_method >= BufferMethods::NonPullerMethods;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -70,32 +70,13 @@ public:
|
|||
BitField<8, 24, u32> syncpoint_id;
|
||||
};
|
||||
|
||||
explicit Puller(GPU& gpu_, MemoryManager& memory_manager_, DmaPusher& dma_pusher,
|
||||
Control::ChannelState& channel_state);
|
||||
~Puller();
|
||||
|
||||
void CallMethod(const MethodCall& method_call);
|
||||
|
||||
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);
|
||||
|
||||
void CallMethod(DmaPusher& dma_pusher, const MethodCall& method_call);
|
||||
void CallMultiMethod(DmaPusher& dma_pusher, u32 method, u32 subchannel, const u32* base_start, u32 amount, u32 methods_pending);
|
||||
void BindRasterizer(DmaPusher& dma_pusher, VideoCore::RasterizerInterface* rasterizer);
|
||||
void CallPullerMethod(DmaPusher& dma_pusher, 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);
|
||||
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;
|
||||
struct Regs {
|
||||
static constexpr size_t NUM_REGS = 0x40;
|
||||
|
|
@ -139,12 +120,12 @@ private:
|
|||
};
|
||||
} regs{};
|
||||
|
||||
void ProcessBindMethod(const MethodCall& method_call);
|
||||
void ProcessFenceActionMethod();
|
||||
void ProcessSemaphoreAcquire();
|
||||
void ProcessSemaphoreRelease();
|
||||
void ProcessSemaphoreTriggerMethod();
|
||||
[[nodiscard]] bool ExecuteMethodOnEngine(u32 method);
|
||||
void ProcessBindMethod(DmaPusher& dma_pusher, const MethodCall& method_call);
|
||||
void ProcessFenceActionMethod(DmaPusher& dma_pusher);
|
||||
void ProcessSemaphoreAcquire(DmaPusher& dma_pusher);
|
||||
void ProcessSemaphoreRelease(DmaPusher& dma_pusher);
|
||||
void ProcessSemaphoreTriggerMethod(DmaPusher& dma_pusher);
|
||||
[[nodiscard]] bool ExecuteMethodOnEngine(DmaPusher& dma_pusher, u32 method);
|
||||
|
||||
/// Mapping of command subchannels to their bound engine ids
|
||||
std::array<EngineID, 8> bound_engines{};
|
||||
|
|
@ -157,8 +138,7 @@ private:
|
|||
};
|
||||
|
||||
#define ASSERT_REG_POSITION(field_name, position) \
|
||||
static_assert(offsetof(Regs, field_name) == position * 4, \
|
||||
"Field " #field_name " has invalid position")
|
||||
static_assert(offsetof(Regs, field_name) == position * 4, "Field " #field_name " has invalid position")
|
||||
|
||||
ASSERT_REG_POSITION(semaphore_address, 0x4);
|
||||
ASSERT_REG_POSITION(semaphore_sequence, 0x6);
|
||||
|
|
|
|||
|
|
@ -40,30 +40,31 @@
|
|||
namespace Tegra {
|
||||
|
||||
struct GPU::Impl {
|
||||
explicit Impl(GPU& gpu_, Core::System& system_, bool is_async_, bool use_nvdec_)
|
||||
: gpu{gpu_}, system{system_}, host1x{system.Host1x()}, use_nvdec{use_nvdec_},
|
||||
shader_notify{std::make_unique<VideoCore::ShaderNotify>()}, is_async{is_async_},
|
||||
gpu_thread{system_, is_async_}, scheduler{std::make_unique<Control::Scheduler>(gpu)} {}
|
||||
explicit Impl(Core::System& system_, bool is_async_, bool use_nvdec_)
|
||||
: system{system_}
|
||||
, use_nvdec{use_nvdec_}
|
||||
, shader_notify()
|
||||
, is_async{is_async_}
|
||||
, gpu_thread{system_}
|
||||
{}
|
||||
|
||||
~Impl() = default;
|
||||
|
||||
std::shared_ptr<Control::ChannelState> CreateChannel(s32 channel_id) {
|
||||
auto channel_state = std::make_shared<Tegra::Control::ChannelState>(channel_id);
|
||||
channels.emplace(channel_id, channel_state);
|
||||
scheduler->DeclareChannel(channel_state);
|
||||
scheduler.DeclareChannel(channel_state);
|
||||
return channel_state;
|
||||
}
|
||||
|
||||
void BindChannel(s32 channel_id) {
|
||||
if (bound_channel == channel_id) {
|
||||
return;
|
||||
}
|
||||
if (bound_channel != channel_id) {
|
||||
auto it = channels.find(channel_id);
|
||||
ASSERT(it != channels.end());
|
||||
bound_channel = channel_id;
|
||||
current_channel = it->second.get();
|
||||
|
||||
rasterizer->BindChannel(*current_channel);
|
||||
renderer->ReadRasterizer()->BindChannel(*current_channel);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Control::ChannelState> AllocateChannel() {
|
||||
|
|
@ -71,13 +72,13 @@ struct GPU::Impl {
|
|||
}
|
||||
|
||||
void InitChannel(Control::ChannelState& to_init, u64 program_id) {
|
||||
to_init.Init(system, gpu, program_id);
|
||||
to_init.BindRasterizer(rasterizer);
|
||||
rasterizer->InitializeChannel(to_init);
|
||||
to_init.Init(system, program_id);
|
||||
to_init.BindRasterizer(renderer->ReadRasterizer());
|
||||
renderer->ReadRasterizer()->InitializeChannel(to_init);
|
||||
}
|
||||
|
||||
void InitAddressSpace(Tegra::MemoryManager& memory_manager) {
|
||||
memory_manager.BindRasterizer(rasterizer);
|
||||
memory_manager.BindRasterizer(renderer->ReadRasterizer());
|
||||
}
|
||||
|
||||
void ReleaseChannel(Control::ChannelState& to_release) {
|
||||
|
|
@ -87,26 +88,26 @@ struct GPU::Impl {
|
|||
/// Binds a renderer to the GPU.
|
||||
void BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer_) {
|
||||
renderer = std::move(renderer_);
|
||||
rasterizer = renderer->ReadRasterizer();
|
||||
host1x.MemoryManager().BindInterface(rasterizer);
|
||||
host1x.gmmu_manager.BindRasterizer(rasterizer);
|
||||
system.Host1x().memory_manager.BindInterface(renderer->ReadRasterizer());
|
||||
system.Host1x().gmmu_manager.BindRasterizer(renderer->ReadRasterizer());
|
||||
}
|
||||
|
||||
/// Flush all current written commands into the host GPU for execution.
|
||||
void FlushCommands() {
|
||||
rasterizer->FlushCommands();
|
||||
renderer->ReadRasterizer()->FlushCommands();
|
||||
}
|
||||
|
||||
/// Synchronizes CPU writes with Host GPU memory.
|
||||
void InvalidateGPUCache() {
|
||||
std::function<void(PAddr, size_t)> callback_writes(
|
||||
[this](PAddr address, size_t size) { rasterizer->OnCacheInvalidation(address, size); });
|
||||
std::function<void(PAddr, size_t)> callback_writes([this](PAddr address, size_t size) {
|
||||
renderer->ReadRasterizer()->OnCacheInvalidation(address, size);
|
||||
});
|
||||
system.GatherGPUDirtyMemory(callback_writes);
|
||||
}
|
||||
|
||||
/// Signal the ending of command list.
|
||||
void OnCommandListEnd() {
|
||||
rasterizer->ReleaseFences(false);
|
||||
renderer->ReadRasterizer()->ReleaseFences(false);
|
||||
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 {
|
||||
u64 gpu_tick = system.CoreTiming().GetGPUTicks();
|
||||
Settings::GpuOverclock overclock = Settings::values.fast_gpu_time.GetValue();
|
||||
|
|
@ -210,14 +155,6 @@ struct GPU::Impl {
|
|||
return gpu_tick;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsAsync() const {
|
||||
return is_async;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool UseNvdec() const {
|
||||
return use_nvdec;
|
||||
}
|
||||
|
||||
void RendererFrameEndNotify() {
|
||||
system.GetPerfStats().EndGameFrame();
|
||||
}
|
||||
|
|
@ -227,7 +164,7 @@ struct GPU::Impl {
|
|||
/// core timing events.
|
||||
void Start() {
|
||||
Settings::UpdateGPUAccuracy();
|
||||
gpu_thread.StartThread(*renderer, renderer->Context(), *scheduler);
|
||||
gpu_thread.StartThread(*renderer, renderer->Context(), scheduler);
|
||||
}
|
||||
|
||||
void NotifyShutdown() {
|
||||
|
|
@ -251,25 +188,24 @@ struct GPU::Impl {
|
|||
|
||||
/// Push GPU command entries to be processed
|
||||
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
|
||||
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) {
|
||||
auto raster_area = rasterizer->GetFlushArea(addr, size);
|
||||
auto raster_area = renderer->ReadRasterizer()->GetFlushArea(addr, size);
|
||||
if (raster_area.preemtive) {
|
||||
return raster_area;
|
||||
}
|
||||
raster_area.preemtive = true;
|
||||
const u64 fence = RequestSyncOperation([this, &raster_area]() {
|
||||
rasterizer->FlushRegion(raster_area.start_address,
|
||||
raster_area.end_address - raster_area.start_address);
|
||||
renderer->ReadRasterizer()->FlushRegion(raster_area.start_address, raster_area.end_address - raster_area.start_address);
|
||||
});
|
||||
gpu_thread.TickGPU();
|
||||
gpu_thread.TickGPU(is_async);
|
||||
WaitForSyncOperation(fence);
|
||||
return raster_area;
|
||||
}
|
||||
|
|
@ -280,16 +216,15 @@ struct GPU::Impl {
|
|||
}
|
||||
|
||||
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
|
||||
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,
|
||||
std::vector<Service::Nvidia::NvFence>&& fences) {
|
||||
void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers, std::vector<Service::Nvidia::NvFence>&& fences) {
|
||||
size_t num_fences{fences.size()};
|
||||
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] {
|
||||
auto& syncpoint_manager = host1x.GetSyncpointManager();
|
||||
auto& syncpoint_manager = system.Host1x().GetSyncpointManager();
|
||||
if (num_fences == 0) {
|
||||
renderer->Composite(layers);
|
||||
}
|
||||
|
|
@ -322,7 +257,7 @@ struct GPU::Impl {
|
|||
syncpoint_manager.RegisterGuestAction(fences[i].id, fences[i].value, executer);
|
||||
}
|
||||
});
|
||||
gpu_thread.TickGPU();
|
||||
gpu_thread.TickGPU(is_async);
|
||||
WaitForSyncOperation(wait_fence);
|
||||
}
|
||||
|
||||
|
|
@ -331,23 +266,20 @@ struct GPU::Impl {
|
|||
|
||||
const auto wait_fence =
|
||||
RequestSyncOperation([&] { out = renderer->GetAppletCaptureBuffer(); });
|
||||
gpu_thread.TickGPU();
|
||||
gpu_thread.TickGPU(is_async);
|
||||
WaitForSyncOperation(wait_fence);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
GPU& gpu;
|
||||
Core::System& system;
|
||||
Host1x::Host1x& host1x;
|
||||
|
||||
std::unique_ptr<VideoCore::RendererBase> renderer;
|
||||
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
||||
const bool use_nvdec;
|
||||
|
||||
s32 new_channel_id{1};
|
||||
/// 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
|
||||
std::atomic_bool shutting_down{};
|
||||
|
||||
|
|
@ -371,7 +303,7 @@ struct GPU::Impl {
|
|||
VideoCommon::GPUThread::ThreadManager gpu_thread;
|
||||
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;
|
||||
Tegra::Control::ChannelState* current_channel;
|
||||
s32 bound_channel{-1};
|
||||
|
|
@ -382,7 +314,8 @@ struct GPU::Impl {
|
|||
};
|
||||
|
||||
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;
|
||||
|
||||
|
|
@ -423,8 +356,9 @@ void GPU::OnCommandListEnd() {
|
|||
}
|
||||
|
||||
u64 GPU::RequestFlush(DAddr addr, std::size_t size) {
|
||||
return impl->RequestSyncOperation(
|
||||
[this, addr, size]() { impl->rasterizer->FlushRegion(addr, size); });
|
||||
return impl->RequestSyncOperation([this, addr, size]() {
|
||||
impl->renderer->ReadRasterizer()->FlushRegion(addr, size);
|
||||
});
|
||||
}
|
||||
|
||||
u64 GPU::CurrentSyncRequestFence() const {
|
||||
|
|
@ -441,52 +375,52 @@ void GPU::TickWork() {
|
|||
|
||||
/// Gets a mutable reference to the Host1x interface
|
||||
Host1x::Host1x& GPU::Host1x() {
|
||||
return impl->host1x;
|
||||
return impl->system.Host1x();
|
||||
}
|
||||
|
||||
/// Gets an immutable reference to the Host1x interface.
|
||||
const Host1x::Host1x& GPU::Host1x() const {
|
||||
return impl->host1x;
|
||||
return impl->system.Host1x();
|
||||
}
|
||||
|
||||
Engines::Maxwell3D& GPU::Maxwell3D() {
|
||||
return impl->Maxwell3D();
|
||||
return impl->current_channel->payload->maxwell_3d;
|
||||
}
|
||||
|
||||
const Engines::Maxwell3D& GPU::Maxwell3D() const {
|
||||
return impl->Maxwell3D();
|
||||
return impl->current_channel->payload->maxwell_3d;
|
||||
}
|
||||
|
||||
Engines::KeplerCompute& GPU::KeplerCompute() {
|
||||
return impl->KeplerCompute();
|
||||
return impl->current_channel->payload->kepler_compute;
|
||||
}
|
||||
|
||||
const Engines::KeplerCompute& GPU::KeplerCompute() const {
|
||||
return impl->KeplerCompute();
|
||||
return impl->current_channel->payload->kepler_compute;
|
||||
}
|
||||
|
||||
Tegra::DmaPusher& GPU::DmaPusher() {
|
||||
return impl->DmaPusher();
|
||||
return impl->current_channel->payload->dma_pusher;
|
||||
}
|
||||
|
||||
const Tegra::DmaPusher& GPU::DmaPusher() const {
|
||||
return impl->DmaPusher();
|
||||
return impl->current_channel->payload->dma_pusher;
|
||||
}
|
||||
|
||||
VideoCore::RendererBase& GPU::Renderer() {
|
||||
return impl->Renderer();
|
||||
return *impl->renderer;
|
||||
}
|
||||
|
||||
const VideoCore::RendererBase& GPU::Renderer() const {
|
||||
return impl->Renderer();
|
||||
return *impl->renderer;
|
||||
}
|
||||
|
||||
VideoCore::ShaderNotify& GPU::ShaderNotify() {
|
||||
return impl->ShaderNotify();
|
||||
return impl->shader_notify;
|
||||
}
|
||||
|
||||
const VideoCore::ShaderNotify& GPU::ShaderNotify() const {
|
||||
return impl->ShaderNotify();
|
||||
return impl->shader_notify;
|
||||
}
|
||||
|
||||
void GPU::RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers,
|
||||
|
|
@ -503,11 +437,11 @@ u64 GPU::GetTicks() const {
|
|||
}
|
||||
|
||||
bool GPU::IsAsync() const {
|
||||
return impl->IsAsync();
|
||||
return impl->is_async;
|
||||
}
|
||||
|
||||
bool GPU::UseNvdec() const {
|
||||
return impl->UseNvdec();
|
||||
return impl->use_nvdec;
|
||||
}
|
||||
|
||||
void GPU::RendererFrameEndNotify() {
|
||||
|
|
|
|||
|
|
@ -19,8 +19,9 @@
|
|||
|
||||
namespace VideoCommon::GPUThread {
|
||||
|
||||
ThreadManager::ThreadManager(Core::System& system_, bool is_async_)
|
||||
: system{system_}, is_async{is_async_} {}
|
||||
ThreadManager::ThreadManager(Core::System& system_)
|
||||
: system{system_}
|
||||
{}
|
||||
|
||||
ThreadManager::~ThreadManager() = default;
|
||||
|
||||
|
|
@ -39,7 +40,7 @@ void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Core::Fronten
|
|||
break;
|
||||
}
|
||||
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)) {
|
||||
system.GPU().TickWork();
|
||||
} 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) {
|
||||
PushCommand(SubmitListCommand(channel, std::move(entries)));
|
||||
void ThreadManager::SubmitList(s32 channel, Tegra::CommandList&& entries, bool is_async) {
|
||||
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) {
|
||||
// Always flush with synchronous GPU mode
|
||||
PushCommand(FlushRegionCommand(addr, size));
|
||||
PushCommand(FlushRegionCommand(addr, size), false, is_async);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void ThreadManager::TickGPU() {
|
||||
PushCommand(GPUTickCommand());
|
||||
void ThreadManager::TickGPU(bool is_async) {
|
||||
PushCommand(GPUTickCommand(), false, is_async);
|
||||
}
|
||||
|
||||
void ThreadManager::InvalidateRegion(DAddr addr, u64 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 (!is_async) {
|
||||
PushCommand(FlushRegionCommand(addr, size));
|
||||
PushCommand(FlushRegionCommand(addr, size), false, is_async);
|
||||
} else {
|
||||
auto& gpu = system.GPU();
|
||||
const u64 fence = gpu.RequestFlush(addr, size);
|
||||
TickGPU();
|
||||
TickGPU(is_async);
|
||||
gpu.WaitForSyncOperation(fence);
|
||||
}
|
||||
}
|
||||
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) {
|
||||
// In synchronous GPU mode, block the caller until the command has executed
|
||||
block = true;
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include "common/bounded_threadsafe_queue.h"
|
||||
#include "common/polyfill_thread.h"
|
||||
#include "video_core/dma_pusher.h"
|
||||
#include "video_core/framebuffer_config.h"
|
||||
|
||||
namespace Tegra {
|
||||
|
|
@ -103,7 +104,7 @@ struct SynchState final {
|
|||
/// Class used to manage the GPU thread
|
||||
class ThreadManager final {
|
||||
public:
|
||||
explicit ThreadManager(Core::System& system_, bool is_async_);
|
||||
explicit ThreadManager(Core::System& system_);
|
||||
~ThreadManager();
|
||||
|
||||
/// Creates and starts the GPU thread.
|
||||
|
|
@ -111,27 +112,25 @@ public:
|
|||
Tegra::Control::Scheduler& scheduler);
|
||||
|
||||
/// 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
|
||||
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
|
||||
void InvalidateRegion(DAddr addr, u64 size);
|
||||
|
||||
/// 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:
|
||||
/// 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;
|
||||
const bool is_async;
|
||||
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
||||
|
||||
SynchState state;
|
||||
std::jthread thread;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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-FileCopyrightText: 2021 yuzu Emulator Project
|
||||
|
|
@ -10,26 +10,22 @@
|
|||
|
||||
namespace Tegra::Host1x {
|
||||
|
||||
Control::Control(Host1x& host1x_) : host1x(host1x_) {}
|
||||
|
||||
Control::~Control() = default;
|
||||
|
||||
void Control::ProcessMethod(Method method, u32 argument) {
|
||||
void Control::ProcessMethod(Host1x& host1x, Method method, u32 argument) {
|
||||
switch (method) {
|
||||
case Method::LoadSyncptPayload32:
|
||||
syncpoint_value = argument;
|
||||
break;
|
||||
case Method::WaitSyncpt:
|
||||
case Method::WaitSyncpt32:
|
||||
Execute(argument);
|
||||
Execute(host1x, argument);
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Control method {:#X}", static_cast<u32>(method));
|
||||
UNIMPLEMENTED_MSG("Control method {:#X}", u32(method));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Control::Execute(u32 data) {
|
||||
void Control::Execute(Host1x& host1x, u32 data) {
|
||||
LOG_TRACE(Service_NVDRV, "Control wait syncpt {} value {}", data, syncpoint_value);
|
||||
host1x.GetSyncpointManager().WaitHost(data, syncpoint_value);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 Skyline Team and Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
|
@ -19,17 +22,11 @@ public:
|
|||
WaitSyncpt32 = 0x50,
|
||||
};
|
||||
|
||||
explicit Control(Host1x& host1x);
|
||||
~Control();
|
||||
|
||||
/// Writes the method into the state, Invoke Execute() if encountered
|
||||
void ProcessMethod(Method method, u32 argument);
|
||||
|
||||
private:
|
||||
void ProcessMethod(Host1x& host1x, Method method, u32 argument);
|
||||
/// 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{};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -27,22 +27,22 @@ void Host1x::StartDevice(s32 fd, ChannelType type, u32 syncpt) {
|
|||
#ifdef YUZU_LEGACY
|
||||
std::call_once(nvdec_first_init, []() {std::this_thread::sleep_for(std::chrono::milliseconds{500});}); // HACK: For Astroneer
|
||||
#endif
|
||||
devices[fd] = std::make_unique<Tegra::Host1x::Nvdec>(*this, fd, syncpt);
|
||||
devices[fd].emplace<Tegra::Host1x::Nvdec>(*this, fd, syncpt);
|
||||
break;
|
||||
case ChannelType::VIC:
|
||||
#ifdef YUZU_LEGACY
|
||||
std::call_once(vic_first_init, []() {std::this_thread::sleep_for(std::chrono::milliseconds{500});}); // HACK: For Astroneer
|
||||
#endif
|
||||
devices[fd] = std::make_unique<Tegra::Host1x::Vic>(*this, fd, syncpt);
|
||||
devices[fd].emplace<Tegra::Host1x::Vic>(*this, fd, syncpt);
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(HW_GPU, "Unimplemented host1x device {}", static_cast<u32>(type));
|
||||
LOG_ERROR(HW_GPU, "Unimplemented host1x device {}", u32(type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Host1x::StopDevice(s32 fd, ChannelType type) {
|
||||
devices.erase(fd);
|
||||
devices[fd].emplace<std::monostate>();
|
||||
}
|
||||
|
||||
} // namespace Tegra::Host1x
|
||||
|
|
|
|||
|
|
@ -8,10 +8,14 @@
|
|||
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <unordered_map>
|
||||
#include <queue>
|
||||
#include <variant>
|
||||
|
||||
#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 "video_core/cdma_pusher.h"
|
||||
#include "video_core/host1x/gpu_device_memory_manager.h"
|
||||
|
|
@ -31,118 +35,90 @@ class Nvdec;
|
|||
|
||||
class FrameQueue {
|
||||
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) {
|
||||
std::scoped_lock l{m_mutex};
|
||||
m_presentation_order.insert({fd, {}});
|
||||
m_decode_order.insert({fd, {}});
|
||||
m_frame_devices.insert_or_assign(fd, FrameDevice{});
|
||||
}
|
||||
|
||||
void Close(s32 fd) {
|
||||
std::scoped_lock l{m_mutex};
|
||||
m_presentation_order.erase(fd);
|
||||
m_decode_order.erase(fd);
|
||||
m_frame_devices.erase(fd);
|
||||
}
|
||||
|
||||
s32 VicFindNvdecFdFromOffset(u64 search_offset) {
|
||||
std::scoped_lock l{m_mutex};
|
||||
for (auto& map : m_presentation_order) {
|
||||
for (auto& [offset, frame] : map.second) {
|
||||
if (offset == search_offset) {
|
||||
return map.first;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto& map : m_decode_order) {
|
||||
for (auto& [offset, frame] : map.second) {
|
||||
if (offset == search_offset) {
|
||||
return map.first;
|
||||
}
|
||||
}
|
||||
for (auto const& [fd, dev] : m_frame_devices) {
|
||||
for (auto const& [offset, frame] : dev.m_presentation_order)
|
||||
if (offset == search_offset)
|
||||
return fd;
|
||||
for (auto const& [offset, frame] : dev.m_decode_order)
|
||||
if (offset == search_offset)
|
||||
return fd;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void PushPresentOrder(s32 fd, u64 offset, std::shared_ptr<FFmpeg::Frame>&& frame) {
|
||||
std::scoped_lock l{m_mutex};
|
||||
auto map = m_presentation_order.find(fd);
|
||||
if (map == m_presentation_order.end()) {
|
||||
return;
|
||||
if (auto const it = m_frame_devices.find(fd); it != m_frame_devices.end()) {
|
||||
if (it->second.m_presentation_order.size() >= MAX_PRESENT_QUEUE)
|
||||
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) {
|
||||
std::scoped_lock l{m_mutex};
|
||||
auto map = m_decode_order.find(fd);
|
||||
if (map == m_decode_order.end()) {
|
||||
return;
|
||||
if (auto const it = m_frame_devices.find(fd); it != m_frame_devices.end()) {
|
||||
it->second.m_decode_order.insert_or_assign(offset, std::move(frame));
|
||||
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);
|
||||
it->second.m_decode_order.erase(it->second.m_decode_order.begin(), it2);
|
||||
}
|
||||
|
||||
map->second.insert_or_assign(offset, std::move(frame));
|
||||
|
||||
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) {
|
||||
if (fd == -1) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (fd != -1) {
|
||||
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()) {
|
||||
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);
|
||||
}
|
||||
|
||||
auto decode_map = m_decode_order.find(fd);
|
||||
if (decode_map != m_decode_order.end() && !decode_map->second.empty()) {
|
||||
if (it->second.m_decode_order.size() > 0)
|
||||
return GetDecodeOrderLocked(fd, offset);
|
||||
}
|
||||
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<FFmpeg::Frame> GetPresentOrderLocked(s32 fd) {
|
||||
auto map = m_presentation_order.find(fd);
|
||||
if (map == m_presentation_order.end() || map->second.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto frame = std::move(map->second.front().second);
|
||||
map->second.pop_front();
|
||||
if (auto const it = m_frame_devices.find(fd); it != m_frame_devices.end()) {
|
||||
auto frame = std::move(it->second.m_presentation_order.front().second);
|
||||
it->second.m_presentation_order.pop_front();
|
||||
return frame;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::shared_ptr<FFmpeg::Frame> GetDecodeOrderLocked(s32 fd, u64 offset) {
|
||||
auto map = m_decode_order.find(fd);
|
||||
if (map == m_decode_order.end() || map->second.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto it = map->second.find(offset);
|
||||
if (it == map->second.end()) {
|
||||
return {};
|
||||
}
|
||||
if (auto const it = m_frame_devices.find(fd); it != m_frame_devices.end()) {
|
||||
if (auto const it2 = it->second.m_decode_order.find(offset); it2 != it->second.m_decode_order.end()) {
|
||||
// TODO: this "mapped" prevents us from fully embracing ankerl
|
||||
return std::move(map->second.extract(it).mapped());
|
||||
return std::move(it->second.m_decode_order.extract(it2).mapped());
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
using FramePtr = std::shared_ptr<FFmpeg::Frame>;
|
||||
|
||||
std::mutex m_mutex{};
|
||||
ankerl::unordered_dense::map<s32, std::deque<std::pair<u64, FramePtr>>> m_presentation_order;
|
||||
ankerl::unordered_dense::map<s32, std::unordered_map<u64, FramePtr>> m_decode_order;
|
||||
ankerl::unordered_dense::map<s32, FrameDevice> m_frame_devices;
|
||||
|
||||
static constexpr size_t MAX_PRESENT_QUEUE = 100;
|
||||
static constexpr size_t MAX_DECODE_MAP = 200;
|
||||
|
|
@ -196,11 +172,11 @@ public:
|
|||
void StopDevice(s32 fd, ChannelType type);
|
||||
|
||||
void PushEntries(s32 fd, ChCommandHeaderList&& entries) {
|
||||
auto it = devices.find(fd);
|
||||
if (it == devices.end()) {
|
||||
return;
|
||||
if (auto const nvdec = std::get_if<Tegra::Host1x::Nvdec>(&devices[fd])) {
|
||||
nvdec->PushEntries(std::move(entries));
|
||||
} 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;
|
||||
|
|
@ -209,7 +185,11 @@ public:
|
|||
Tegra::MemoryManager gmmu_manager;
|
||||
Common::FlatAllocator<u32, 0, 32> allocator;
|
||||
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
|
||||
std::once_flag nvdec_first_init;
|
||||
std::once_flag vic_first_init;
|
||||
|
|
|
|||
|
|
@ -407,15 +407,15 @@ void Vic::ReadInterlacedY8__V8U8_N420(const SlotStruct& slot, std::span<const Pl
|
|||
};
|
||||
|
||||
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
|
||||
// relies on the previous frame.
|
||||
DecodeBobField();
|
||||
break;
|
||||
case DXVAHD_DEINTERLACE_MODE_PRIVATE::BOB_FIELD:
|
||||
case DxvhadDeinterlaceModePrivate::BobField:
|
||||
DecodeBobField();
|
||||
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
|
||||
// relies on previous/next frames.
|
||||
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 {
|
||||
switch (slot.config.frame_format) {
|
||||
case DXVAHD_FRAME_FORMAT::PROGRESSIVE:
|
||||
case DxvhadFrameFormat::Progressive:
|
||||
ReadProgressiveY8__V8U8_N420(slot, offsets, std::move(frame), planar, false);
|
||||
break;
|
||||
case DXVAHD_FRAME_FORMAT::TOP_FIELD:
|
||||
case DxvhadFrameFormat::TopField:
|
||||
ReadInterlacedY8__V8U8_N420(slot, offsets, std::move(frame), planar, true);
|
||||
break;
|
||||
case DXVAHD_FRAME_FORMAT::BOTTOM_FIELD:
|
||||
case DxvhadFrameFormat::BottomField:
|
||||
ReadInterlacedY8__V8U8_N420(slot, offsets, std::move(frame), planar, false);
|
||||
break;
|
||||
default:
|
||||
|
|
@ -860,7 +860,7 @@ void Vic::WriteY8__V8U8_N420(const OutputSurfaceConfig& output_surface_config) n
|
|||
};
|
||||
|
||||
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);
|
||||
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);
|
||||
|
|
@ -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);
|
||||
}
|
||||
} break;
|
||||
case BLK_KIND::PITCH: {
|
||||
case BlkKind::Pitch: {
|
||||
LOG_TRACE(HW_GPU, "Writing Y8__V8U8_N420 swizzled frame\n"
|
||||
"\tinput surface {}x{} stride {} size {:#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) {
|
||||
case BLK_KIND::GENERIC_16Bx2: {
|
||||
case BlkKind::Generic_16Bx2: {
|
||||
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);
|
||||
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);
|
||||
}
|
||||
} break;
|
||||
case BLK_KIND::PITCH: {
|
||||
case BlkKind::Pitch: {
|
||||
LOG_TRACE(HW_GPU, "Writing ABGR pitch frame\n"
|
||||
"\tinput surface {}x{} stride {} size {:#X}"
|
||||
"\toutput surface {}x{} stride {} size {:#X}",
|
||||
|
|
|
|||
|
|
@ -6,16 +6,12 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/scratch_buffer.h"
|
||||
#include "video_core/cdma_pusher.h"
|
||||
#include "video_core/host1x/host1x.h"
|
||||
#include "video_core/host1x/ffmpeg.h"
|
||||
|
||||
namespace Tegra::Host1x {
|
||||
class Host1x;
|
||||
|
|
@ -138,52 +134,53 @@ enum SurfaceIndex : u32 {
|
|||
CombinedMotion = 7,
|
||||
};
|
||||
|
||||
enum class DXVAHD_ALPHA_FILL_MODE : u32 {
|
||||
OPAQUE = 0,
|
||||
BACKGROUND = 1,
|
||||
DESTINATION = 2,
|
||||
SOURCE_STREAM = 3,
|
||||
COMPOSITED = 4,
|
||||
SOURCE_ALPHA = 5,
|
||||
// Note: these will inevitably collide with Win32 defines if you use their UPPER_SNAKE_CASE naming
|
||||
enum class DxvhadAlphaFillMode : u32 {
|
||||
Opaque = 0,
|
||||
Background = 1,
|
||||
Destination = 2,
|
||||
SourceStream = 3,
|
||||
Composited = 4,
|
||||
SourceAlpha = 5,
|
||||
};
|
||||
|
||||
enum class DXVAHD_FRAME_FORMAT : u64 {
|
||||
PROGRESSIVE = 0,
|
||||
INTERLACED_TOP_FIELD_FIRST = 1,
|
||||
INTERLACED_BOTTOM_FIELD_FIRST = 2,
|
||||
TOP_FIELD = 3,
|
||||
BOTTOM_FIELD = 4,
|
||||
SUBPIC_PROGRESSIVE = 5,
|
||||
SUBPIC_INTERLACED_TOP_FIELD_FIRST = 6,
|
||||
SUBPIC_INTERLACED_BOTTOM_FIELD_FIRST = 7,
|
||||
SUBPIC_TOP_FIELD = 8,
|
||||
SUBPIC_BOTTOM_FIELD = 9,
|
||||
TOP_FIELD_CHROMA_BOTTOM = 10,
|
||||
BOTTOM_FIELD_CHROMA_TOP = 11,
|
||||
SUBPIC_TOP_FIELD_CHROMA_BOTTOM = 12,
|
||||
SUBPIC_BOTTOM_FIELD_CHROMA_TOP = 13,
|
||||
enum class DxvhadFrameFormat : u64 {
|
||||
Progressive = 0,
|
||||
InterlacedTopFieldFirst = 1,
|
||||
InterlacedBottomFieldFirst = 2,
|
||||
TopField = 3,
|
||||
BottomField = 4,
|
||||
SubpicProgressive = 5,
|
||||
SubpicInterlacedTopFieldFirst = 6,
|
||||
SubpicInterlacedBottomFieldFirst = 7,
|
||||
SubpicTopField = 8,
|
||||
SubpicBottomField = 9,
|
||||
TopFieldChromaBottom = 10,
|
||||
BottomFieldChromaTop = 11,
|
||||
SubpicTopFieldChromaBottom = 12,
|
||||
SubpicBottomFieldChromaTop = 13,
|
||||
};
|
||||
|
||||
enum class DXVAHD_DEINTERLACE_MODE_PRIVATE : u64 {
|
||||
WEAVE = 0,
|
||||
BOB_FIELD = 1,
|
||||
BOB = 2,
|
||||
NEWBOB = 3,
|
||||
DISI1 = 4,
|
||||
WEAVE_LUMA_BOB_FIELD_CHROMA = 5,
|
||||
MAX = 0xF,
|
||||
enum class DxvhadDeinterlaceModePrivate : u64 {
|
||||
Weave = 0,
|
||||
BobField = 1,
|
||||
Bob = 2,
|
||||
Newbob = 3,
|
||||
Disi1 = 4,
|
||||
WeaveLumaBobFieldChroma = 5,
|
||||
Max = 0xF,
|
||||
};
|
||||
|
||||
enum class BLK_KIND {
|
||||
PITCH = 0,
|
||||
GENERIC_16Bx2 = 1,
|
||||
enum class BlkKind {
|
||||
Pitch = 0,
|
||||
Generic_16Bx2 = 1,
|
||||
// These are unsupported in the vic
|
||||
BL_NAIVE = 2,
|
||||
BL_KEPLER_XBAR_RAW = 3,
|
||||
VP2_TILED = 15,
|
||||
BlNaive = 2,
|
||||
BlKeplerXbarRaw = 3,
|
||||
Vp2Tiled = 15,
|
||||
};
|
||||
|
||||
enum class BLEND_SRCFACTC : u32 {
|
||||
enum class BlendSrcFactC : u32 {
|
||||
K1 = 0,
|
||||
K1_TIMES_DST = 1,
|
||||
NEG_K1_TIMES_DST = 2,
|
||||
|
|
@ -191,7 +188,7 @@ enum class BLEND_SRCFACTC : u32 {
|
|||
ZERO = 4,
|
||||
};
|
||||
|
||||
enum class BLEND_DSTFACTC : u32 {
|
||||
enum class BlendDstFactC : u32 {
|
||||
K1 = 0,
|
||||
K2 = 1,
|
||||
K1_TIMES_DST = 2,
|
||||
|
|
@ -201,7 +198,7 @@ enum class BLEND_DSTFACTC : u32 {
|
|||
ONE = 6,
|
||||
};
|
||||
|
||||
enum class BLEND_SRCFACTA : u32 {
|
||||
enum class BlendSrcFactA : u32 {
|
||||
K1 = 0,
|
||||
K2 = 1,
|
||||
NEG_K1_TIMES_DST = 2,
|
||||
|
|
@ -209,7 +206,7 @@ enum class BLEND_SRCFACTA : u32 {
|
|||
MAX = 7,
|
||||
};
|
||||
|
||||
enum class BLEND_DSTFACTA : u32 {
|
||||
enum class BlendDstFactA : u32 {
|
||||
K2 = 0,
|
||||
NEG_K1_TIMES_SRC = 1,
|
||||
ZERO = 2,
|
||||
|
|
@ -232,7 +229,7 @@ static_assert(sizeof(PipeConfig) == 0x10, "PipeConfig has the wrong size!");
|
|||
|
||||
struct OutputConfig {
|
||||
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<6, 10, u64> background_a;
|
||||
BitField<16, 10, u64> background_r;
|
||||
|
|
@ -265,7 +262,7 @@ struct OutputSurfaceConfig {
|
|||
BitField<0, 7, VideoPixelFormat> out_pixel_format;
|
||||
BitField<7, 2, u32> out_chroma_loc_horiz;
|
||||
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<19, 3, u32> reserved0;
|
||||
BitField<22, 10, u32> reserved1;
|
||||
|
|
@ -365,7 +362,7 @@ struct SlotConfig {
|
|||
BitField<14, 1, u64> prev_prev_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<22, 2, u64> filter_length_x;
|
||||
BitField<24, 12, u64> panoramic;
|
||||
|
|
@ -377,7 +374,7 @@ struct SlotConfig {
|
|||
BitField<10, 10, u64> filter_detail;
|
||||
BitField<20, 10, u64> chroma_noise;
|
||||
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<47, 11, u64> noise_iir;
|
||||
BitField<58, 4, u64> light_level;
|
||||
|
|
@ -484,13 +481,13 @@ struct BlendingSlotStruct {
|
|||
BitField<26, 6, u32> reserved1;
|
||||
};
|
||||
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<4, 3, BLEND_DSTFACTC> dst_factor_color_match_select;
|
||||
BitField<4, 3, BlendDstFactC> dst_factor_color_match_select;
|
||||
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<12, 3, BLEND_DSTFACTA> dst_factor_a_match_select;
|
||||
BitField<12, 3, BlendDstFactA> dst_factor_a_match_select;
|
||||
BitField<15, 1, u32> reserved5;
|
||||
BitField<16, 4, u32> reserved6;
|
||||
BitField<20, 4, u32> reserved7;
|
||||
|
|
@ -624,8 +621,8 @@ private:
|
|||
VicRegisters regs{};
|
||||
|
||||
Common::ScratchBuffer<u8> swizzle_scratch;
|
||||
Common::ScratchBuffer<Pixel> output_surface;
|
||||
Common::ScratchBuffer<Pixel> slot_surface;
|
||||
Common::ScratchBuffer<Tegra::Host1x::Pixel> output_surface;
|
||||
Common::ScratchBuffer<Tegra::Host1x::Pixel> slot_surface;
|
||||
Common::ScratchBuffer<u8> luma_scratch;
|
||||
Common::ScratchBuffer<u8> chroma_scratch;
|
||||
|
||||
|
|
|
|||
|
|
@ -64,10 +64,10 @@ bool IsTopologySafe(Maxwell3D::Regs::PrimitiveTopology topology) {
|
|||
|
||||
} // 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]);
|
||||
if (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology)) {
|
||||
Fallback(maxwell3d, parameters);
|
||||
Fallback(system, maxwell3d, parameters);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -93,7 +93,7 @@ void HLE_DrawArraysIndirect::Execute(Engines::Maxwell3D& maxwell3d, std::span<co
|
|||
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 {
|
||||
if (extended) {
|
||||
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]);
|
||||
if (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology)) {
|
||||
Fallback(maxwell3d, parameters);
|
||||
Fallback(system, maxwell3d, parameters);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -161,7 +161,7 @@ void HLE_DrawIndexedIndirect::Execute(Engines::Maxwell3D& maxwell3d, std::span<c
|
|||
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();
|
||||
const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]);
|
||||
const u32 element_base = parameters[4];
|
||||
|
|
@ -184,7 +184,7 @@ void HLE_DrawIndexedIndirect::Fallback(Engines::Maxwell3D& maxwell3d, std::span<
|
|||
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();
|
||||
ASSERT(parameters.size() == 1);
|
||||
|
||||
|
|
@ -196,27 +196,21 @@ void HLE_MultiLayerClear::Execute(Engines::Maxwell3D& maxwell3d, std::span<const
|
|||
maxwell3d.regs.clear_surface.raw = clear_params.raw;
|
||||
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]);
|
||||
if (!IsTopologySafe(topology)) {
|
||||
Fallback(maxwell3d, parameters);
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsTopologySafe(topology)) {
|
||||
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());
|
||||
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;
|
||||
|
|
@ -235,8 +229,11 @@ void HLE_MultiDrawIndexedIndirectCount::Execute(Engines::Maxwell3D& maxwell3d, s
|
|||
maxwell3d.draw_manager.DrawIndexedIndirect(maxwell3d, topology, 0, estimate);
|
||||
maxwell3d.engine_state = Maxwell3D::EngineHint::None;
|
||||
maxwell3d.replace_table.clear();
|
||||
} else {
|
||||
Fallback(system, maxwell3d, parameters);
|
||||
}
|
||||
}
|
||||
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 {
|
||||
// Clean everything.
|
||||
maxwell3d.regs.vertex_id_base = 0x0;
|
||||
|
|
@ -250,7 +247,7 @@ void HLE_MultiDrawIndexedIndirectCount::Fallback(Engines::Maxwell3D& maxwell3d,
|
|||
// Nothing to do.
|
||||
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 std::size_t max_draws = parameters[4];
|
||||
const u32 indirect_words = 5 + padding;
|
||||
|
|
@ -265,18 +262,15 @@ void HLE_MultiDrawIndexedIndirectCount::Fallback(Engines::Maxwell3D& maxwell3d,
|
|||
maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro;
|
||||
maxwell3d.SetHLEReplacementAttributeType(0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseVertex);
|
||||
maxwell3d.SetHLEReplacementAttributeType(0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance);
|
||||
maxwell3d.CallMethod(0x8e3, 0x648, true);
|
||||
maxwell3d.CallMethod(0x8e4, static_cast<u32>(index), true);
|
||||
maxwell3d.CallMethod(system, 0x8e3, 0x648, true);
|
||||
maxwell3d.CallMethod(system, 0x8e4, u32(index), 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]);
|
||||
}
|
||||
}
|
||||
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();
|
||||
if (!force) {
|
||||
Fallback(maxwell3d, parameters);
|
||||
return;
|
||||
}
|
||||
if (force) {
|
||||
auto topology = Maxwell3D::Regs::PrimitiveTopology(parameters[0] & 0xFFFFU);
|
||||
auto& params = maxwell3d.draw_manager.indirect_state;
|
||||
params.is_byte_count = true;
|
||||
|
|
@ -291,15 +285,18 @@ void HLE_DrawIndirectByteCount::Execute(Engines::Maxwell3D& maxwell3d, std::span
|
|||
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);
|
||||
}
|
||||
}
|
||||
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.regs.draw.begin = parameters[0];
|
||||
maxwell3d.regs.draw_auto_stride = parameters[1];
|
||||
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);
|
||||
}
|
||||
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();
|
||||
const u32 offset = (parameters[0] & 0x3FFFFFFF) << 2;
|
||||
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.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();
|
||||
const size_t index = parameters[0];
|
||||
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_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();
|
||||
auto& regs = maxwell3d.regs;
|
||||
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;
|
||||
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();
|
||||
const u32 raster_mode = parameters[0];
|
||||
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.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
|
||||
maxwell3d.RefreshParameters();
|
||||
auto& regs = maxwell3d.regs;
|
||||
|
|
@ -362,7 +359,7 @@ void HLE_ClearConstBuffer::Execute(Engines::Maxwell3D& maxwell3d, std::span<cons
|
|||
regs.const_buffer.offset = 0;
|
||||
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();
|
||||
const u32 needed_memory = parameters[2] / sizeof(u32);
|
||||
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.dest.address_high = parameters[0];
|
||||
regs.upload.dest.address_low = parameters[1];
|
||||
maxwell3d.CallMethod(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.CallMethod(system, size_t(MAXWELL3D_REG_INDEX(launch_dma)), 0x1011, true);
|
||||
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();
|
||||
auto& regs = maxwell3d.regs;
|
||||
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.dest.address_high = parameters[0];
|
||||
regs.upload.dest.address_low = parameters[1];
|
||||
maxwell3d.CallMethod(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(launch_dma)), 0x1011, 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());
|
||||
}
|
||||
|
||||
|
|
@ -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();
|
||||
|
||||
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.
|
||||
bool keep_executing = true;
|
||||
while (keep_executing) {
|
||||
keep_executing = Step(maxwell3d, false);
|
||||
keep_executing = Step(system, maxwell3d, false);
|
||||
}
|
||||
|
||||
// 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
|
||||
/// 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.
|
||||
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;
|
||||
|
||||
Macro::Opcode opcode = GetOpcode();
|
||||
|
|
@ -478,11 +475,11 @@ bool MacroInterpreterImpl::Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slo
|
|||
switch (opcode.operation) {
|
||||
case Macro::Operation::ALU: {
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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();
|
||||
dst &= ~(opcode.GetBitfieldMask() << 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;
|
||||
}
|
||||
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;
|
||||
|
||||
ProcessResult(maxwell3d, opcode.result_operation, opcode.dst, result);
|
||||
ProcessResult(system, maxwell3d, opcode.result_operation, opcode.dst, result);
|
||||
break;
|
||||
}
|
||||
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;
|
||||
|
||||
ProcessResult(maxwell3d, opcode.result_operation, opcode.dst, result);
|
||||
ProcessResult(system, maxwell3d, opcode.result_operation, opcode.dst, result);
|
||||
break;
|
||||
}
|
||||
case Macro::Operation::Read: {
|
||||
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;
|
||||
}
|
||||
case Macro::Operation::Branch: {
|
||||
|
|
@ -531,7 +528,7 @@ bool MacroInterpreterImpl::Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slo
|
|||
|
||||
delayed_pc = base_address + opcode.GetBranchTarget();
|
||||
// Execute one more instruction due to the delay slot.
|
||||
return Step(maxwell3d, true);
|
||||
return Step(system, maxwell3d, true);
|
||||
}
|
||||
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.
|
||||
if (opcode.is_exit && !is_delay_slot) {
|
||||
// Exit has a delay slot, execute the next instruction
|
||||
Step(maxwell3d, true);
|
||||
Step(system, maxwell3d, true);
|
||||
return false;
|
||||
}
|
||||
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).
|
||||
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) {
|
||||
case Macro::ResultOperation::IgnoreAndFetch:
|
||||
// Fetch parameter and ignore result.
|
||||
|
|
@ -609,12 +606,12 @@ void MacroInterpreterImpl::ProcessResult(Engines::Maxwell3D& maxwell3d, Macro::R
|
|||
case Macro::ResultOperation::FetchAndSend:
|
||||
// Fetch parameter and send result.
|
||||
SetRegister(reg, FetchParameter());
|
||||
Send(maxwell3d, result);
|
||||
Send(system, maxwell3d, result);
|
||||
break;
|
||||
case Macro::ResultOperation::MoveAndSend:
|
||||
// Move and send result.
|
||||
SetRegister(reg, result);
|
||||
Send(maxwell3d, result);
|
||||
Send(system, maxwell3d, result);
|
||||
break;
|
||||
case Macro::ResultOperation::FetchAndSetMethod:
|
||||
// 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.
|
||||
SetRegister(reg, result);
|
||||
SetMethodAddress(result);
|
||||
Send(maxwell3d, FetchParameter());
|
||||
Send(system, maxwell3d, FetchParameter());
|
||||
break;
|
||||
case Macro::ResultOperation::MoveAndSetMethodSend:
|
||||
// Move result and use as Method Address, then send bits 12:17 of result.
|
||||
SetRegister(reg, result);
|
||||
SetMethodAddress(result);
|
||||
Send(maxwell3d, (result >> 12) & 0b111111);
|
||||
Send(system, maxwell3d, (result >> 12) & 0b111111);
|
||||
break;
|
||||
default:
|
||||
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.
|
||||
void MacroInterpreterImpl::Send(Engines::Maxwell3D& maxwell3d, u32 value) {
|
||||
maxwell3d.CallMethod(method_address.address, value, true);
|
||||
void MacroInterpreterImpl::Send(Core::System& system, Engines::Maxwell3D& maxwell3d, u32 value) {
|
||||
maxwell3d.CallMethod(system, method_address.address, value, true);
|
||||
// Increment the method address by the method increment.
|
||||
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
|
||||
|
||||
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)
|
||||
, 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_AddImmediate(Macro::Opcode opcode);
|
||||
void Compile_ExtractInsert(Macro::Opcode opcode);
|
||||
void Compile_ExtractShiftLeftImmediate(Macro::Opcode opcode);
|
||||
void Compile_ExtractShiftLeftRegister(Macro::Opcode opcode);
|
||||
void Compile_Read(Macro::Opcode opcode);
|
||||
void Compile_ALU(Core::System& system, Macro::Opcode opcode);
|
||||
void Compile_AddImmediate(Core::System& system, Macro::Opcode opcode);
|
||||
void Compile_ExtractInsert(Core::System& system, Macro::Opcode opcode);
|
||||
void Compile_ExtractShiftLeftImmediate(Core::System& system, Macro::Opcode opcode);
|
||||
void Compile_ExtractShiftLeftRegister(Core::System& system, Macro::Opcode opcode);
|
||||
void Compile_Read(Core::System& system, Macro::Opcode opcode);
|
||||
void Compile_Branch(Macro::Opcode opcode);
|
||||
|
||||
void Optimizer_ScanFlags();
|
||||
void Compile();
|
||||
bool Compile_NextInstruction();
|
||||
void Compile(Core::System& system);
|
||||
bool Compile_NextInstruction(Core::System& system);
|
||||
Xbyak::Reg32 Compile_FetchParameter();
|
||||
Xbyak::Reg32 Compile_GetRegister(u32 index, Xbyak::Reg32 dst);
|
||||
void Compile_ProcessResult(Macro::ResultOperation operation, u32 reg);
|
||||
void Compile_Send(Xbyak::Reg32 value);
|
||||
void Compile_ProcessResult(Core::System& system, Macro::ResultOperation operation, u32 reg);
|
||||
void Compile_Send(Core::System& system, Xbyak::Reg32 value);
|
||||
Macro::Opcode GetOpCode() const;
|
||||
|
||||
struct JITState {
|
||||
Engines::Maxwell3D* maxwell3d{};
|
||||
Engines::Maxwell3D* maxwell3d = nullptr;
|
||||
Core::System* system = nullptr;
|
||||
std::array<u32, Macro::NUM_MACRO_REGISTERS> registers{};
|
||||
u32 carry_flag{};
|
||||
};
|
||||
|
|
@ -777,15 +775,16 @@ struct MacroJITx64Impl final : public Xbyak::CodeGenerator, public DynamicCached
|
|||
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; });
|
||||
JITState state{};
|
||||
state.maxwell3d = &maxwell3d;
|
||||
state.system = &system;
|
||||
state.registers = {};
|
||||
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_b_zero = opcode.src_b == 0;
|
||||
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());
|
||||
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) {
|
||||
// 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.
|
||||
|
|
@ -940,10 +939,10 @@ void MacroJITx64Impl::Compile_AddImmediate(Macro::Opcode opcode) {
|
|||
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 src = Compile_GetRegister(opcode.src_b, eax);
|
||||
|
||||
|
|
@ -954,10 +953,10 @@ void MacroJITx64Impl::Compile_ExtractInsert(Macro::Opcode opcode) {
|
|||
shl(src, opcode.bf_dst_bit);
|
||||
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 src = Compile_GetRegister(opcode.src_b, RESULT);
|
||||
|
||||
|
|
@ -965,10 +964,10 @@ void MacroJITx64Impl::Compile_ExtractShiftLeftImmediate(Macro::Opcode opcode) {
|
|||
and_(src, opcode.GetBitfieldMask());
|
||||
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 src = Compile_GetRegister(opcode.src_b, RESULT);
|
||||
|
||||
|
|
@ -976,10 +975,10 @@ void MacroJITx64Impl::Compile_ExtractShiftLeftRegister(Macro::Opcode opcode) {
|
|||
and_(src, opcode.GetBitfieldMask());
|
||||
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 (opcode.immediate == 0) {
|
||||
xor_(RESULT, RESULT);
|
||||
|
|
@ -1005,23 +1004,21 @@ void MacroJITx64Impl::Compile_Read(Macro::Opcode opcode) {
|
|||
int3();
|
||||
L(pass_range_check);
|
||||
}
|
||||
mov(rax, qword[STATE]);
|
||||
mov(RESULT,
|
||||
dword[rax + offsetof(Engines::Maxwell3D, regs) +
|
||||
offsetof(Engines::Maxwell3D::Regs, reg_array) + RESULT.cvt64() * sizeof(u32)]);
|
||||
|
||||
Compile_ProcessResult(opcode.result_operation, opcode.dst);
|
||||
mov(rax, qword[STATE + offsetof(JITState, maxwell3d)]);
|
||||
mov(RESULT, dword[rax + offsetof(Engines::Maxwell3D, regs) + offsetof(Engines::Maxwell3D::Regs, reg_array) + RESULT.cvt64() * sizeof(u32)]);
|
||||
Compile_ProcessResult(system, opcode.result_operation, opcode.dst);
|
||||
}
|
||||
|
||||
static void MacroJIT_SendThunk(Engines::Maxwell3D* maxwell3d, Macro::MethodAddress method_address, u32 value) {
|
||||
maxwell3d->CallMethod(method_address.address, value, true);
|
||||
static void MacroJIT_SendThunk(Core::System* system, Engines::Maxwell3D* maxwell3d, Macro::MethodAddress method_address, u32 value) {
|
||||
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);
|
||||
mov(Common::X64::ABI_PARAM1, qword[STATE]);
|
||||
mov(Common::X64::ABI_PARAM2.cvt32(), METHOD_ADDRESS);
|
||||
mov(Common::X64::ABI_PARAM3.cvt32(), value);
|
||||
mov(Common::X64::ABI_PARAM1, qword[STATE + offsetof(JITState, system)]);
|
||||
mov(Common::X64::ABI_PARAM2, qword[STATE + offsetof(JITState, maxwell3d)]);
|
||||
mov(Common::X64::ABI_PARAM3.cvt32(), METHOD_ADDRESS);
|
||||
mov(Common::X64::ABI_PARAM4.cvt32(), value);
|
||||
Common::X64::CallFarFunction(*this, &MacroJIT_SendThunk);
|
||||
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) {
|
||||
ASSERT_MSG(!is_delay_slot, "Executing a branch in a delay slot is not valid");
|
||||
const s32 jump_address =
|
||||
static_cast<s32>(pc) + static_cast<s32>(opcode.GetBranchTarget() / sizeof(s32));
|
||||
ASSERT(!is_delay_slot && "Executing a branch in a delay slot is not valid");
|
||||
const s32 jump_address = s32(pc) + s32(opcode.GetBranchTarget() / sizeof(s32));
|
||||
|
||||
Xbyak::Label end;
|
||||
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());
|
||||
|
||||
Common::X64::ABI_PushRegistersAndAdjustStack(*this, Common::X64::ABI_ALL_CALLEE_SAVED, 8);
|
||||
|
|
@ -1156,7 +1152,7 @@ void MacroJITx64Impl::Compile() {
|
|||
next_opcode = {};
|
||||
}
|
||||
pc = i;
|
||||
Compile_NextInstruction();
|
||||
Compile_NextInstruction(system);
|
||||
}
|
||||
|
||||
L(end_of_code);
|
||||
|
|
@ -1167,7 +1163,7 @@ void MacroJITx64Impl::Compile() {
|
|||
program = getCode<ProgramType>();
|
||||
}
|
||||
|
||||
bool MacroJITx64Impl::Compile_NextInstruction() {
|
||||
bool MacroJITx64Impl::Compile_NextInstruction(Core::System& system) {
|
||||
const auto opcode = GetOpCode();
|
||||
if (labels[pc].getAddress()) {
|
||||
return false;
|
||||
|
|
@ -1177,22 +1173,22 @@ bool MacroJITx64Impl::Compile_NextInstruction() {
|
|||
|
||||
switch (opcode.operation) {
|
||||
case Macro::Operation::ALU:
|
||||
Compile_ALU(opcode);
|
||||
Compile_ALU(system, opcode);
|
||||
break;
|
||||
case Macro::Operation::AddImmediate:
|
||||
Compile_AddImmediate(opcode);
|
||||
Compile_AddImmediate(system, opcode);
|
||||
break;
|
||||
case Macro::Operation::ExtractInsert:
|
||||
Compile_ExtractInsert(opcode);
|
||||
Compile_ExtractInsert(system, opcode);
|
||||
break;
|
||||
case Macro::Operation::ExtractShiftLeftImmediate:
|
||||
Compile_ExtractShiftLeftImmediate(opcode);
|
||||
Compile_ExtractShiftLeftImmediate(system, opcode);
|
||||
break;
|
||||
case Macro::Operation::ExtractShiftLeftRegister:
|
||||
Compile_ExtractShiftLeftRegister(opcode);
|
||||
Compile_ExtractShiftLeftRegister(system, opcode);
|
||||
break;
|
||||
case Macro::Operation::Read:
|
||||
Compile_Read(opcode);
|
||||
Compile_Read(system, opcode);
|
||||
break;
|
||||
case Macro::Operation::Branch:
|
||||
Compile_Branch(opcode);
|
||||
|
|
@ -1264,7 +1260,7 @@ Xbyak::Reg32 MacroJITx64Impl::Compile_GetRegister(u32 index, Xbyak::Reg32 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) {
|
||||
// Register 0 is supposed to always return 0. NOP is implemented as a store to the zero
|
||||
// register.
|
||||
|
|
@ -1289,12 +1285,12 @@ void MacroJITx64Impl::Compile_ProcessResult(Macro::ResultOperation operation, u3
|
|||
case Macro::ResultOperation::FetchAndSend:
|
||||
// Fetch parameter and send result.
|
||||
SetRegister(reg, Compile_FetchParameter());
|
||||
Compile_Send(RESULT);
|
||||
Compile_Send(system, RESULT);
|
||||
break;
|
||||
case Macro::ResultOperation::MoveAndSend:
|
||||
// Move and send result.
|
||||
SetRegister(reg, RESULT);
|
||||
Compile_Send(RESULT);
|
||||
Compile_Send(system, RESULT);
|
||||
break;
|
||||
case Macro::ResultOperation::FetchAndSetMethod:
|
||||
// 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.
|
||||
SetRegister(reg, RESULT);
|
||||
SetMethodAddress(RESULT);
|
||||
Compile_Send(Compile_FetchParameter());
|
||||
Compile_Send(system, Compile_FetchParameter());
|
||||
break;
|
||||
case Macro::ResultOperation::MoveAndSetMethodSend:
|
||||
// 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);
|
||||
shr(RESULT, 12);
|
||||
and_(RESULT, 0b111111);
|
||||
Compile_Send(RESULT);
|
||||
Compile_Send(system, RESULT);
|
||||
break;
|
||||
default:
|
||||
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());
|
||||
}
|
||||
|
||||
void MacroEngine::Execute(Engines::Maxwell3D& maxwell3d, u32 method, std::span<const u32> parameters) {
|
||||
auto const execute_variant = [&maxwell3d, ¶meters, method](AnyCachedMacro& acm) {
|
||||
void MacroEngine::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, u32 method, std::span<const u32> parameters) {
|
||||
auto const execute_variant = [&system, &maxwell3d, ¶meters, method](AnyCachedMacro& 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))
|
||||
a->Execute(maxwell3d, parameters, method);
|
||||
a->Execute(system, maxwell3d, parameters, method);
|
||||
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))
|
||||
a->Execute(maxwell3d, parameters, method);
|
||||
a->Execute(system, maxwell3d, parameters, method);
|
||||
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))
|
||||
a->Execute(maxwell3d, parameters, method);
|
||||
a->Execute(system, maxwell3d, parameters, method);
|
||||
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))
|
||||
a->Execute(maxwell3d, parameters, method);
|
||||
a->Execute(system, maxwell3d, parameters, method);
|
||||
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))
|
||||
a->Execute(maxwell3d, parameters, method);
|
||||
a->Execute(system, maxwell3d, parameters, method);
|
||||
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))
|
||||
a->Execute(maxwell3d, parameters, method);
|
||||
a->Execute(system, maxwell3d, parameters, method);
|
||||
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))
|
||||
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()) {
|
||||
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);
|
||||
std::memcpy(code.data(), macro_cached.data() + rebased_method, code.size() * sizeof(u32));
|
||||
ci.hash = Common::HashValue(code);
|
||||
ci.program = Compile(maxwell3d, code);
|
||||
ci.program = Compile(system, maxwell3d, code);
|
||||
} else {
|
||||
ci.program = Compile(maxwell3d, macro_code->second);
|
||||
ci.program = Compile(system, maxwell3d, macro_code->second);
|
||||
ci.hash = Common::HashValue(macro_code->second);
|
||||
}
|
||||
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
|
||||
if (!is_interpreted)
|
||||
return std::make_unique<MacroJITx64Impl>(code);
|
||||
return std::make_unique<MacroJITx64Impl>(system, code);
|
||||
#endif
|
||||
return MacroInterpreterImpl(code);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,10 @@
|
|||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
namespace Engines {
|
||||
|
|
@ -106,61 +110,61 @@ struct HLEMacro {
|
|||
/// also assigning the base vertex/instance.
|
||||
struct HLE_DrawArraysIndirect final {
|
||||
HLE_DrawArraysIndirect(bool extended_) noexcept : extended{extended_} {}
|
||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
void Fallback(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters);
|
||||
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
void Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters);
|
||||
bool extended;
|
||||
};
|
||||
/// @note: these macros have two versions, a normal and extended version, with the extended version
|
||||
/// also assigning the base vertex/instance.
|
||||
struct HLE_DrawIndexedIndirect final {
|
||||
explicit HLE_DrawIndexedIndirect(bool extended_) noexcept : extended{extended_} {}
|
||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
void Fallback(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters);
|
||||
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
void Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters);
|
||||
bool extended;
|
||||
};
|
||||
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 {
|
||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
void Fallback(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters);
|
||||
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
void Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters);
|
||||
};
|
||||
struct HLE_DrawIndirectByteCount final {
|
||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
void Fallback(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters);
|
||||
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
void Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters);
|
||||
};
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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;
|
||||
};
|
||||
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;
|
||||
};
|
||||
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 {
|
||||
MacroInterpreterImpl() {}
|
||||
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();
|
||||
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);
|
||||
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;
|
||||
Macro::Opcode GetOpcode() const;
|
||||
u32 GetRegister(u32 register_id) const;
|
||||
|
|
@ -169,7 +173,7 @@ struct MacroInterpreterImpl final {
|
|||
[[nodiscard]] inline void SetMethodAddress(u32 address) noexcept {
|
||||
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 FetchParameter();
|
||||
/// General purpose macro registers.
|
||||
|
|
@ -192,7 +196,7 @@ struct DynamicCachedMacro {
|
|||
/// Executes the macro code with the specified input parameters.
|
||||
/// @param parameters The parameters of the macro
|
||||
/// @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<
|
||||
|
|
@ -227,8 +231,8 @@ struct MacroEngine {
|
|||
uploaded_macro_code.erase(method);
|
||||
}
|
||||
// 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);
|
||||
AnyCachedMacro Compile(Engines::Maxwell3D& maxwell3d, std::span<const u32> code);
|
||||
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, u32 method, std::span<const u32> parameters);
|
||||
AnyCachedMacro Compile(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> code);
|
||||
struct CacheInfo {
|
||||
AnyCachedMacro program;
|
||||
u64 hash{};
|
||||
|
|
|
|||
|
|
@ -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-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -204,7 +207,7 @@ void SetupDirtyMisc(Tables& tables) {
|
|||
} // Anonymous namespace
|
||||
|
||||
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);
|
||||
SetupDirtyColorMasks(tables);
|
||||
SetupDirtyViewports(tables);
|
||||
|
|
@ -231,7 +234,7 @@ void StateTracker::SetupTables(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() {
|
||||
|
|
|
|||
|
|
@ -126,16 +126,14 @@ public:
|
|||
current_query = nullptr;
|
||||
amend_value = 0;
|
||||
accumulation_value = 0;
|
||||
queries_prefix_scan_pass = std::make_unique<QueriesPrefixScanPass>(
|
||||
device, scheduler, descriptor_pool, compute_pass_descriptor_queue);
|
||||
queries_prefix_scan_pass.emplace(device, scheduler, descriptor_pool, compute_pass_descriptor_queue);
|
||||
|
||||
const VkBufferCreateInfo buffer_ci = {
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.size = 8,
|
||||
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
|
||||
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
|
||||
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.queueFamilyIndexCount = 0,
|
||||
.pQueueFamilyIndices = nullptr,
|
||||
|
|
@ -592,8 +590,7 @@ private:
|
|||
VideoCommon::HostQueryBase* current_query;
|
||||
bool has_started{};
|
||||
std::mutex flush_guard;
|
||||
|
||||
std::unique_ptr<QueriesPrefixScanPass> queries_prefix_scan_pass;
|
||||
std::optional<QueriesPrefixScanPass> queries_prefix_scan_pass;
|
||||
};
|
||||
|
||||
// Transform feedback queries
|
||||
|
|
@ -1266,41 +1263,23 @@ private:
|
|||
} // namespace
|
||||
|
||||
struct QueryCacheRuntimeImpl {
|
||||
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_)
|
||||
: rasterizer{rasterizer_}, device_memory{device_memory_}, buffer_cache{buffer_cache_},
|
||||
device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_},
|
||||
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{} {
|
||||
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_)
|
||||
: rasterizer{rasterizer_}, device_memory{device_memory_}, buffer_cache{buffer_cache_}
|
||||
, device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_}
|
||||
, staging_pool{staging_pool_}, guest_streamer(0, runtime)
|
||||
, sample_streamer(size_t(QueryType::ZPassPixelCount64), runtime, rasterizer, texture_cache_, device, scheduler, memory_allocator, compute_pass_descriptor_queue, descriptor_pool)
|
||||
, tfb_streamer(size_t(QueryType::StreamingByteCount), runtime, device, scheduler, memory_allocator, staging_pool)
|
||||
, primitives_succeeded_streamer(size_t(QueryType::StreamingPrimitivesSucceeded), runtime, tfb_streamer, device_memory_)
|
||||
, primitives_needed_minus_succeeded_streamer(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.pNext = nullptr;
|
||||
hcr_setup.flags = 0;
|
||||
|
||||
const bool has_conditional_rendering = device.IsExtConditionalRendering();
|
||||
if (has_conditional_rendering) {
|
||||
conditional_resolve_pass = std::make_unique<ConditionalRenderingResolvePass>(
|
||||
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) {
|
||||
VkBufferUsageFlags hcr_buffer_usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
||||
if (device.IsExtConditionalRendering()) {
|
||||
conditional_resolve_pass.emplace(device, scheduler, descriptor_pool, compute_pass_descriptor_queue);
|
||||
hcr_buffer_usage |= VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT;
|
||||
}
|
||||
|
||||
|
|
@ -1339,7 +1318,7 @@ struct QueryCacheRuntimeImpl {
|
|||
std::vector<std::vector<VkBufferCopy>> copies_setup;
|
||||
|
||||
// Host conditional rendering data
|
||||
std::unique_ptr<ConditionalRenderingResolvePass> conditional_resolve_pass;
|
||||
std::optional<ConditionalRenderingResolvePass> conditional_resolve_pass;
|
||||
vk::Buffer hcr_resolve_buffer;
|
||||
VkConditionalRenderingBeginInfoEXT hcr_setup;
|
||||
VkBuffer hcr_buffer;
|
||||
|
|
@ -1351,13 +1330,7 @@ struct QueryCacheRuntimeImpl {
|
|||
Maxwell3D* maxwell3d;
|
||||
};
|
||||
|
||||
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_) {
|
||||
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_) {
|
||||
impl = std::make_unique<QueryCacheRuntimeImpl>(
|
||||
*this, rasterizer, device_memory_, buffer_cache_, device_, memory_allocator_, scheduler_,
|
||||
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.Record([src_buffer, dst_buffers = std::move(impl->buffers_to_upload_to),
|
||||
vk_copies = std::move(impl->copies_setup)](vk::CommandBuffer cmdbuf) {
|
||||
impl->scheduler.Record([src_buffer, dst_buffers = std::move(impl->buffers_to_upload_to), vk_copies = std::move(impl->copies_setup)](vk::CommandBuffer cmdbuf) {
|
||||
size_t size = dst_buffers.size();
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
cmdbuf.CopyBuffer(src_buffer, dst_buffers[i].first, vk_copies[i]);
|
||||
|
|
|
|||
|
|
@ -235,7 +235,7 @@ void SetupRasterModes(Maxwell3D::DirtyState::Tables &tables) {
|
|||
} // Anonymous namespace
|
||||
|
||||
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);
|
||||
SetupDirtyViewports(tables);
|
||||
SetupDirtyScissors(tables);
|
||||
|
|
@ -258,7 +258,7 @@ void StateTracker::SetupTables(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() {
|
||||
|
|
|
|||
|
|
@ -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-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <variant>
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
|
@ -12,20 +15,15 @@ namespace Vulkan {
|
|||
class Device;
|
||||
class Scheduler;
|
||||
|
||||
struct DescriptorUpdateEntry {
|
||||
struct Empty {};
|
||||
|
||||
union DescriptorUpdateEntry {
|
||||
DescriptorUpdateEntry() = default;
|
||||
DescriptorUpdateEntry(VkDescriptorImageInfo image_) : image{image_} {}
|
||||
DescriptorUpdateEntry(VkDescriptorBufferInfo buffer_) : buffer{buffer_} {}
|
||||
DescriptorUpdateEntry(VkBufferView texel_buffer_) : texel_buffer{texel_buffer_} {}
|
||||
|
||||
union {
|
||||
Empty empty{};
|
||||
std::monostate empty{};
|
||||
VkDescriptorImageInfo image;
|
||||
VkDescriptorBufferInfo buffer;
|
||||
VkBufferView texel_buffer;
|
||||
};
|
||||
};
|
||||
|
||||
class UpdateDescriptorQueue final {
|
||||
|
|
|
|||
|
|
@ -45,23 +45,23 @@ std::unique_ptr<VideoCore::RendererBase> CreateRenderer(Core::System& system, Co
|
|||
|
||||
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();
|
||||
|
||||
const auto nvdec_value = Settings::values.nvdec_emulation.GetValue();
|
||||
const bool use_nvdec = nvdec_value != Settings::NvdecEmulation::Off;
|
||||
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 scope = context->Acquire();
|
||||
try {
|
||||
auto renderer = CreateRenderer(system, emu_window, *gpu, std::move(context));
|
||||
gpu->BindRenderer(std::move(renderer));
|
||||
return gpu;
|
||||
} catch (const std::runtime_error& exception) {
|
||||
scope.Cancel();
|
||||
LOG_ERROR(HW_GPU, "Failed to initialize GPU: {}", exception.what());
|
||||
return nullptr;
|
||||
gpu.reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
|
|
@ -20,8 +24,6 @@ class GPU;
|
|||
namespace VideoCore {
|
||||
|
||||
class RendererBase;
|
||||
|
||||
/// Creates an emulated GPU instance using the given system context.
|
||||
std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system);
|
||||
void CreateGPU(std::optional<Tegra::GPU>& gpu, Core::Frontend::EmuWindow& emu_window, Core::System& system);
|
||||
|
||||
} // namespace VideoCore
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue