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-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|
@ -8,10 +11,11 @@
|
||||||
|
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
|
|
||||||
AudioCore::AudioCore(Core::System& system) : audio_manager{std::make_unique<AudioManager>()} {
|
AudioCore::AudioCore(Core::System& system) {
|
||||||
|
audio_manager.emplace();
|
||||||
CreateSinks();
|
CreateSinks();
|
||||||
// Must be created after the sinks
|
// Must be created after the sinks
|
||||||
adsp = std::make_unique<ADSP::ADSP>(system, *output_sink);
|
adsp.emplace(system, *output_sink);
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioCore ::~AudioCore() {
|
AudioCore ::~AudioCore() {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|
@ -15,10 +18,7 @@ class System;
|
||||||
|
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
|
|
||||||
class AudioManager;
|
/// @brief Main audio class, stored inside the core, and holding the audio manager, all sinks, and the ADSP.
|
||||||
/**
|
|
||||||
* Main audio class, stored inside the core, and holding the audio manager, all sinks, and the ADSP.
|
|
||||||
*/
|
|
||||||
class AudioCore {
|
class AudioCore {
|
||||||
public:
|
public:
|
||||||
explicit AudioCore(Core::System& system);
|
explicit AudioCore(Core::System& system);
|
||||||
|
|
@ -50,27 +50,22 @@ public:
|
||||||
*/
|
*/
|
||||||
Sink::Sink& GetInputSink();
|
Sink::Sink& GetInputSink();
|
||||||
|
|
||||||
/**
|
/// @brief Get the ADSP.
|
||||||
* Get the ADSP.
|
/// @return Ref to the ADSP.
|
||||||
*
|
|
||||||
* @return Ref to the ADSP.
|
|
||||||
*/
|
|
||||||
ADSP::ADSP& ADSP();
|
ADSP::ADSP& ADSP();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/// @brief Create the sinks on startup.
|
||||||
* Create the sinks on startup.
|
|
||||||
*/
|
|
||||||
void CreateSinks();
|
void CreateSinks();
|
||||||
|
|
||||||
/// Main audio manager for audio in/out
|
/// Main audio manager for audio in/out
|
||||||
std::unique_ptr<AudioManager> audio_manager;
|
std::optional<AudioManager> audio_manager;
|
||||||
/// Sink used for audio renderer and audio out
|
/// Sink used for audio renderer and audio out
|
||||||
std::unique_ptr<Sink::Sink> output_sink;
|
std::unique_ptr<Sink::Sink> output_sink;
|
||||||
/// Sink used for audio input
|
/// Sink used for audio input
|
||||||
std::unique_ptr<Sink::Sink> input_sink;
|
std::unique_ptr<Sink::Sink> input_sink;
|
||||||
/// The ADSP in the sysmodule
|
/// The ADSP in the sysmodule
|
||||||
std::unique_ptr<ADSP::ADSP> adsp;
|
std::optional<ADSP::ADSP> adsp;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace AudioCore
|
} // namespace AudioCore
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|
@ -41,8 +44,7 @@ void Manager::ReleaseSessionId(const size_t session_id) {
|
||||||
Result Manager::LinkToManager() {
|
Result Manager::LinkToManager() {
|
||||||
std::scoped_lock l{mutex};
|
std::scoped_lock l{mutex};
|
||||||
if (!linked_to_manager) {
|
if (!linked_to_manager) {
|
||||||
AudioManager& manager{system.AudioCore().GetAudioManager()};
|
system.AudioCore().GetAudioManager().SetInManager(std::bind(&Manager::BufferReleaseAndRegister, this));
|
||||||
manager.SetInManager(std::bind(&Manager::BufferReleaseAndRegister, this));
|
|
||||||
linked_to_manager = true;
|
linked_to_manager = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,81 +1,77 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "audio_core/audio_manager.h"
|
#include "audio_core/audio_manager.h"
|
||||||
|
#include "common/thread.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/hle/service/audio/errors.h"
|
#include "core/hle/service/audio/errors.h"
|
||||||
|
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
|
|
||||||
AudioManager::AudioManager() {
|
AudioManager::AudioManager() {
|
||||||
thread = std::jthread([this]() { ThreadFunc(); });
|
thread = std::jthread([this](std::stop_token stop_token) {
|
||||||
|
Common::SetCurrentThreadName("AudioManager");
|
||||||
|
std::unique_lock l{events.GetAudioEventLock()};
|
||||||
|
events.ClearEvents();
|
||||||
|
while (!stop_token.stop_requested()) {
|
||||||
|
const auto timed_out{events.Wait(l, std::chrono::seconds(2))};
|
||||||
|
if (events.CheckAudioEventSet(Event::Type::Max)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < buffer_events.size(); i++) {
|
||||||
|
const auto event_type = Event::Type(i);
|
||||||
|
if (events.CheckAudioEventSet(event_type) || timed_out) {
|
||||||
|
if (buffer_events[i]) {
|
||||||
|
buffer_events[i]();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
events.SetAudioEvent(event_type, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioManager::Shutdown() {
|
void AudioManager::Shutdown() {
|
||||||
running = false;
|
|
||||||
events.SetAudioEvent(Event::Type::Max, true);
|
events.SetAudioEvent(Event::Type::Max, true);
|
||||||
thread.join();
|
if (thread.joinable()) {
|
||||||
|
thread.request_stop();
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Result AudioManager::SetOutManager(BufferEventFunc buffer_func) {
|
Result AudioManager::SetOutManager(BufferEventFunc buffer_func) {
|
||||||
if (!running) {
|
if (thread.joinable()) {
|
||||||
return Service::Audio::ResultOperationFailed;
|
std::scoped_lock l{lock};
|
||||||
|
const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)};
|
||||||
|
if (buffer_events[index] == nullptr) {
|
||||||
|
buffer_events[index] = std::move(buffer_func);
|
||||||
|
needs_update = true;
|
||||||
|
events.SetAudioEvent(Event::Type::AudioOutManager, true);
|
||||||
|
}
|
||||||
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
return Service::Audio::ResultOperationFailed;
|
||||||
std::scoped_lock l{lock};
|
|
||||||
|
|
||||||
const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)};
|
|
||||||
if (buffer_events[index] == nullptr) {
|
|
||||||
buffer_events[index] = std::move(buffer_func);
|
|
||||||
needs_update = true;
|
|
||||||
events.SetAudioEvent(Event::Type::AudioOutManager, true);
|
|
||||||
}
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result AudioManager::SetInManager(BufferEventFunc buffer_func) {
|
Result AudioManager::SetInManager(BufferEventFunc buffer_func) {
|
||||||
if (!running) {
|
if (thread.joinable()) {
|
||||||
return Service::Audio::ResultOperationFailed;
|
std::scoped_lock l{lock};
|
||||||
|
const auto index{events.GetManagerIndex(Event::Type::AudioInManager)};
|
||||||
|
if (buffer_events[index] == nullptr) {
|
||||||
|
buffer_events[index] = std::move(buffer_func);
|
||||||
|
needs_update = true;
|
||||||
|
events.SetAudioEvent(Event::Type::AudioInManager, true);
|
||||||
|
}
|
||||||
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
return Service::Audio::ResultOperationFailed;
|
||||||
std::scoped_lock l{lock};
|
|
||||||
|
|
||||||
const auto index{events.GetManagerIndex(Event::Type::AudioInManager)};
|
|
||||||
if (buffer_events[index] == nullptr) {
|
|
||||||
buffer_events[index] = std::move(buffer_func);
|
|
||||||
needs_update = true;
|
|
||||||
events.SetAudioEvent(Event::Type::AudioInManager, true);
|
|
||||||
}
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioManager::SetEvent(const Event::Type type, const bool signalled) {
|
void AudioManager::SetEvent(const Event::Type type, const bool signalled) {
|
||||||
events.SetAudioEvent(type, signalled);
|
events.SetAudioEvent(type, signalled);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioManager::ThreadFunc() {
|
|
||||||
std::unique_lock l{events.GetAudioEventLock()};
|
|
||||||
events.ClearEvents();
|
|
||||||
running = true;
|
|
||||||
|
|
||||||
while (running) {
|
|
||||||
const auto timed_out{events.Wait(l, std::chrono::seconds(2))};
|
|
||||||
|
|
||||||
if (events.CheckAudioEventSet(Event::Type::Max)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < buffer_events.size(); i++) {
|
|
||||||
const auto event_type = static_cast<Event::Type>(i);
|
|
||||||
|
|
||||||
if (events.CheckAudioEventSet(event_type) || timed_out) {
|
|
||||||
if (buffer_events[i]) {
|
|
||||||
buffer_events[i]();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
events.SetAudioEvent(event_type, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace AudioCore
|
} // namespace AudioCore
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|
@ -66,13 +69,6 @@ public:
|
||||||
void SetEvent(Event::Type type, bool signalled);
|
void SetEvent(Event::Type type, bool signalled);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
|
||||||
* Main thread, waiting on a manager signal and calling the registered function.
|
|
||||||
*/
|
|
||||||
void ThreadFunc();
|
|
||||||
|
|
||||||
/// Is the main thread running?
|
|
||||||
std::atomic<bool> running{};
|
|
||||||
/// Unused
|
/// Unused
|
||||||
bool needs_update{};
|
bool needs_update{};
|
||||||
/// Events to be set and signalled
|
/// Events to be set and signalled
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|
@ -40,8 +43,7 @@ void Manager::ReleaseSessionId(const size_t session_id) {
|
||||||
Result Manager::LinkToManager() {
|
Result Manager::LinkToManager() {
|
||||||
std::scoped_lock l{mutex};
|
std::scoped_lock l{mutex};
|
||||||
if (!linked_to_manager) {
|
if (!linked_to_manager) {
|
||||||
AudioManager& manager{system.AudioCore().GetAudioManager()};
|
system.AudioCore().GetAudioManager().SetOutManager(std::bind(&Manager::BufferReleaseAndRegister, this));
|
||||||
manager.SetOutManager(std::bind(&Manager::BufferReleaseAndRegister, this));
|
|
||||||
linked_to_manager = true;
|
linked_to_manager = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -271,7 +271,7 @@ struct System::Impl {
|
||||||
|
|
||||||
SystemResultStatus SetupForApplicationProcess(System& system, Frontend::EmuWindow& emu_window) {
|
SystemResultStatus SetupForApplicationProcess(System& system, Frontend::EmuWindow& emu_window) {
|
||||||
host1x_core.emplace(system);
|
host1x_core.emplace(system);
|
||||||
gpu_core = VideoCore::CreateGPU(emu_window, system);
|
VideoCore::CreateGPU(gpu_core, emu_window, system);
|
||||||
if (!gpu_core)
|
if (!gpu_core)
|
||||||
return SystemResultStatus::ErrorVideoCore;
|
return SystemResultStatus::ErrorVideoCore;
|
||||||
|
|
||||||
|
|
@ -347,7 +347,7 @@ struct System::Impl {
|
||||||
|
|
||||||
// Register with applet manager
|
// Register with applet manager
|
||||||
// All threads are started, begin main process execution, now that we're in the clear
|
// All threads are started, begin main process execution, now that we're in the clear
|
||||||
applet_manager.CreateAndInsertByFrontendAppletParameters(std::move(process), params);
|
applet_manager.CreateAndInsertByFrontendAppletParameters(std::make_unique<Service::Process>(*std::move(process)), params);
|
||||||
|
|
||||||
if (Settings::values.gamecard_inserted) {
|
if (Settings::values.gamecard_inserted) {
|
||||||
if (Settings::values.gamecard_current_game) {
|
if (Settings::values.gamecard_current_game) {
|
||||||
|
|
@ -391,10 +391,8 @@ struct System::Impl {
|
||||||
is_powered_on = false;
|
is_powered_on = false;
|
||||||
exit_locked = false;
|
exit_locked = false;
|
||||||
exit_requested = false;
|
exit_requested = false;
|
||||||
|
if (gpu_core)
|
||||||
if (gpu_core != nullptr) {
|
|
||||||
gpu_core->NotifyShutdown();
|
gpu_core->NotifyShutdown();
|
||||||
}
|
|
||||||
|
|
||||||
stop_event.request_stop();
|
stop_event.request_stop();
|
||||||
core_timing.SyncPause(false);
|
core_timing.SyncPause(false);
|
||||||
|
|
@ -478,6 +476,7 @@ struct System::Impl {
|
||||||
std::optional<Memory::CheatEngine> cheat_engine;
|
std::optional<Memory::CheatEngine> cheat_engine;
|
||||||
std::optional<Tools::Freezer> memory_freezer;
|
std::optional<Tools::Freezer> memory_freezer;
|
||||||
std::optional<Tools::RenderdocAPI> renderdoc_api;
|
std::optional<Tools::RenderdocAPI> renderdoc_api;
|
||||||
|
std::optional<Tegra::GPU> gpu_core;
|
||||||
|
|
||||||
std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES> gpu_dirty_memory_managers;
|
std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES> gpu_dirty_memory_managers;
|
||||||
std::vector<std::vector<u8>> user_channel;
|
std::vector<std::vector<u8>> user_channel;
|
||||||
|
|
@ -492,7 +491,6 @@ struct System::Impl {
|
||||||
std::unique_ptr<FileSys::ContentProviderUnion> content_provider;
|
std::unique_ptr<FileSys::ContentProviderUnion> content_provider;
|
||||||
/// AppLoader used to load the current executing application
|
/// AppLoader used to load the current executing application
|
||||||
std::unique_ptr<Loader::AppLoader> app_loader;
|
std::unique_ptr<Loader::AppLoader> app_loader;
|
||||||
std::unique_ptr<Tegra::GPU> gpu_core;
|
|
||||||
std::stop_source stop_event;
|
std::stop_source stop_event;
|
||||||
|
|
||||||
mutable std::mutex suspend_guard;
|
mutable std::mutex suspend_guard;
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,11 @@ CpuManager::~CpuManager() = default;
|
||||||
|
|
||||||
void CpuManager::Initialize() {
|
void CpuManager::Initialize() {
|
||||||
num_cores = is_multicore ? Core::Hardware::NUM_CPU_CORES : 1;
|
num_cores = is_multicore ? Core::Hardware::NUM_CPU_CORES : 1;
|
||||||
gpu_barrier = std::make_unique<Common::Barrier>(num_cores + 1);
|
gpu_barrier.emplace(num_cores + 1);
|
||||||
for (std::size_t core = 0; core < num_cores; core++)
|
for (std::size_t core = 0; core < num_cores; core++)
|
||||||
core_data[core].host_thread = std::jthread([this, core](std::stop_token token) { RunThread(token, core); });
|
core_data[core].host_thread = std::jthread([this, core](std::stop_token token) {
|
||||||
|
RunThread(token, core);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void CpuManager::Shutdown() {
|
void CpuManager::Shutdown() {
|
||||||
|
|
@ -63,7 +65,7 @@ void CpuManager::HandleInterrupt() {
|
||||||
auto& kernel = system.Kernel();
|
auto& kernel = system.Kernel();
|
||||||
auto core_index = kernel.CurrentPhysicalCoreIndex();
|
auto core_index = kernel.CurrentPhysicalCoreIndex();
|
||||||
|
|
||||||
Kernel::KInterruptManager::HandleInterrupt(kernel, static_cast<s32>(core_index));
|
Kernel::KInterruptManager::HandleInterrupt(kernel, s32(core_index));
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|
@ -86,22 +89,20 @@ private:
|
||||||
void ShutdownThread();
|
void ShutdownThread();
|
||||||
void RunThread(std::stop_token stop_token, std::size_t core);
|
void RunThread(std::stop_token stop_token, std::size_t core);
|
||||||
|
|
||||||
|
static constexpr std::size_t max_cycle_runs = 5;
|
||||||
|
|
||||||
|
std::optional<Common::Barrier> gpu_barrier{};
|
||||||
struct CoreData {
|
struct CoreData {
|
||||||
std::shared_ptr<Common::Fiber> host_context;
|
std::shared_ptr<Common::Fiber> host_context;
|
||||||
std::jthread host_thread;
|
std::jthread host_thread;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<Common::Barrier> gpu_barrier{};
|
|
||||||
std::array<CoreData, Core::Hardware::NUM_CPU_CORES> core_data{};
|
std::array<CoreData, Core::Hardware::NUM_CPU_CORES> core_data{};
|
||||||
|
System& system;
|
||||||
bool is_async_gpu{};
|
|
||||||
bool is_multicore{};
|
|
||||||
std::atomic<std::size_t> current_core{};
|
std::atomic<std::size_t> current_core{};
|
||||||
std::size_t idle_count{};
|
std::size_t idle_count{};
|
||||||
std::size_t num_cores{};
|
std::size_t num_cores{};
|
||||||
static constexpr std::size_t max_cycle_runs = 5;
|
bool is_async_gpu{};
|
||||||
|
bool is_multicore{};
|
||||||
System& system;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
|
|
||||||
|
|
@ -268,7 +268,7 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) {
|
||||||
|
|
||||||
if (Settings::values.enable_overlay && m_window_system->GetOverlayDisplayApplet() == nullptr) {
|
if (Settings::values.enable_overlay && m_window_system->GetOverlayDisplayApplet() == nullptr) {
|
||||||
if (auto overlay_process = CreateProcess(m_system, static_cast<u64>(AppletProgramId::OverlayDisplay), 0, 0)) {
|
if (auto overlay_process = CreateProcess(m_system, static_cast<u64>(AppletProgramId::OverlayDisplay), 0, 0)) {
|
||||||
auto overlay_applet = std::make_shared<Applet>(m_system, std::move(overlay_process), false);
|
auto overlay_applet = std::make_shared<Applet>(m_system, std::make_unique<Service::Process>(*std::move(overlay_process)), false);
|
||||||
overlay_applet->program_id = static_cast<u64>(AppletProgramId::OverlayDisplay);
|
overlay_applet->program_id = static_cast<u64>(AppletProgramId::OverlayDisplay);
|
||||||
overlay_applet->applet_id = AppletId::OverlayDisplay;
|
overlay_applet->applet_id = AppletId::OverlayDisplay;
|
||||||
overlay_applet->type = AppletType::OverlayApplet;
|
overlay_applet->type = AppletType::OverlayApplet;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
#include "common/thread.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/hle/service/am/am_types.h"
|
#include "core/hle/service/am/am_types.h"
|
||||||
#include "core/hle/service/am/button_poller.h"
|
#include "core/hle/service/am/button_poller.h"
|
||||||
|
|
@ -34,16 +36,15 @@ ButtonPressDuration ClassifyPressDuration(std::chrono::steady_clock::time_point
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
ButtonPoller::ButtonPoller(Core::System& system, WindowSystem& window_system)
|
ButtonPoller::ButtonPoller(Core::System& system, WindowSystem& window_system) {
|
||||||
: m_window_system(window_system) {
|
|
||||||
// TODO: am reads this from the home button state in hid, which is controller-agnostic.
|
// TODO: am reads this from the home button state in hid, which is controller-agnostic.
|
||||||
Core::HID::ControllerUpdateCallback engine_callback{
|
Core::HID::ControllerUpdateCallback engine_callback{
|
||||||
.on_change =
|
.on_change = [this, &window_system](Core::HID::ControllerTriggerType type) {
|
||||||
[this](Core::HID::ControllerTriggerType type) {
|
if (type == Core::HID::ControllerTriggerType::Button) {
|
||||||
if (type == Core::HID::ControllerTriggerType::Button) {
|
std::unique_lock lk{m_mutex};
|
||||||
this->OnButtonStateChanged();
|
OnButtonStateChanged(window_system);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.is_npad_service = true,
|
.is_npad_service = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -52,25 +53,35 @@ ButtonPoller::ButtonPoller(Core::System& system, WindowSystem& window_system)
|
||||||
m_player1 = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
|
m_player1 = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
|
||||||
m_player1_key = m_player1->SetCallback(engine_callback);
|
m_player1_key = m_player1->SetCallback(engine_callback);
|
||||||
|
|
||||||
m_thread = std::thread([this] { this->ThreadLoop(); });
|
m_thread = std::jthread([this, &window_system](std::stop_token stop_token) {
|
||||||
|
Common::SetCurrentThreadName("ButtonPoller");
|
||||||
|
while (!stop_token.stop_requested()) {
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
std::unique_lock lk{m_mutex};
|
||||||
|
m_cv.wait_for(lk, 50ms);
|
||||||
|
if (stop_token.stop_requested())
|
||||||
|
break;
|
||||||
|
OnButtonStateChanged(window_system);
|
||||||
|
std::this_thread::sleep_for(5ms);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ButtonPoller::~ButtonPoller() {
|
ButtonPoller::~ButtonPoller() {
|
||||||
m_handheld->DeleteCallback(m_handheld_key);
|
m_handheld->DeleteCallback(m_handheld_key);
|
||||||
m_player1->DeleteCallback(m_player1_key);
|
m_player1->DeleteCallback(m_player1_key);
|
||||||
m_stop = true;
|
|
||||||
m_cv.notify_all();
|
m_cv.notify_all();
|
||||||
if (m_thread.joinable()) {
|
if (m_thread.joinable()) {
|
||||||
|
m_thread.request_stop();
|
||||||
m_thread.join();
|
m_thread.join();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ButtonPoller::OnButtonStateChanged() {
|
void ButtonPoller::OnButtonStateChanged(WindowSystem& window_system) {
|
||||||
std::lock_guard lk{m_mutex};
|
auto const home_button = m_handheld->GetHomeButtons().home.Value()
|
||||||
const bool home_button =
|
|| m_player1->GetHomeButtons().home.Value();
|
||||||
m_handheld->GetHomeButtons().home.Value() || m_player1->GetHomeButtons().home.Value();
|
auto const capture_button = m_handheld->GetCaptureButtons().capture.Value()
|
||||||
const bool capture_button = m_handheld->GetCaptureButtons().capture.Value() ||
|
|| m_player1->GetCaptureButtons().capture.Value();
|
||||||
m_player1->GetCaptureButtons().capture.Value();
|
|
||||||
|
|
||||||
// Buttons pressed which were not previously pressed
|
// Buttons pressed which were not previously pressed
|
||||||
if (home_button && !m_home_button_press_start) {
|
if (home_button && !m_home_button_press_start) {
|
||||||
|
|
@ -90,7 +101,7 @@ void ButtonPoller::OnButtonStateChanged() {
|
||||||
if (home_button && m_home_button_press_start && !m_home_button_long_sent) {
|
if (home_button && m_home_button_press_start && !m_home_button_long_sent) {
|
||||||
const auto duration = ClassifyPressDuration(*m_home_button_press_start);
|
const auto duration = ClassifyPressDuration(*m_home_button_press_start);
|
||||||
if (duration != ButtonPressDuration::ShortPressing) {
|
if (duration != ButtonPressDuration::ShortPressing) {
|
||||||
m_window_system.OnSystemButtonPress(SystemButtonType::HomeButtonLongPressing);
|
window_system.OnSystemButtonPress(SystemButtonType::HomeButtonLongPressing);
|
||||||
m_home_button_long_sent = true;
|
m_home_button_long_sent = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -98,7 +109,7 @@ void ButtonPoller::OnButtonStateChanged() {
|
||||||
if (capture_button && m_capture_button_press_start && !m_capture_button_long_sent) {
|
if (capture_button && m_capture_button_press_start && !m_capture_button_long_sent) {
|
||||||
const auto duration = ClassifyPressDuration(*m_capture_button_press_start);
|
const auto duration = ClassifyPressDuration(*m_capture_button_press_start);
|
||||||
if (duration != ButtonPressDuration::ShortPressing) {
|
if (duration != ButtonPressDuration::ShortPressing) {
|
||||||
m_window_system.OnSystemButtonPress(SystemButtonType::CaptureButtonLongPressing);
|
window_system.OnSystemButtonPress(SystemButtonType::CaptureButtonLongPressing);
|
||||||
m_capture_button_long_sent = true;
|
m_capture_button_long_sent = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -107,9 +118,8 @@ void ButtonPoller::OnButtonStateChanged() {
|
||||||
if (!home_button && m_home_button_press_start) {
|
if (!home_button && m_home_button_press_start) {
|
||||||
if(!m_home_button_long_sent) {
|
if(!m_home_button_long_sent) {
|
||||||
const auto duration = ClassifyPressDuration(*m_home_button_press_start);
|
const auto duration = ClassifyPressDuration(*m_home_button_press_start);
|
||||||
m_window_system.OnSystemButtonPress(
|
window_system.OnSystemButtonPress(duration == ButtonPressDuration::ShortPressing
|
||||||
duration == ButtonPressDuration::ShortPressing ? SystemButtonType::HomeButtonShortPressing
|
? SystemButtonType::HomeButtonShortPressing : SystemButtonType::HomeButtonLongPressing);
|
||||||
: SystemButtonType::HomeButtonLongPressing);
|
|
||||||
}
|
}
|
||||||
m_home_button_press_start = std::nullopt;
|
m_home_button_press_start = std::nullopt;
|
||||||
m_home_button_long_sent = false;
|
m_home_button_long_sent = false;
|
||||||
|
|
@ -117,9 +127,8 @@ void ButtonPoller::OnButtonStateChanged() {
|
||||||
if (!capture_button && m_capture_button_press_start) {
|
if (!capture_button && m_capture_button_press_start) {
|
||||||
if (!m_capture_button_long_sent) {
|
if (!m_capture_button_long_sent) {
|
||||||
const auto duration = ClassifyPressDuration(*m_capture_button_press_start);
|
const auto duration = ClassifyPressDuration(*m_capture_button_press_start);
|
||||||
m_window_system.OnSystemButtonPress(
|
window_system.OnSystemButtonPress(duration == ButtonPressDuration::ShortPressing
|
||||||
duration == ButtonPressDuration::ShortPressing ? SystemButtonType::CaptureButtonShortPressing
|
? SystemButtonType::CaptureButtonShortPressing : SystemButtonType::CaptureButtonLongPressing);
|
||||||
: SystemButtonType::CaptureButtonLongPressing);
|
|
||||||
}
|
}
|
||||||
m_capture_button_press_start = std::nullopt;
|
m_capture_button_press_start = std::nullopt;
|
||||||
m_capture_button_long_sent = false;
|
m_capture_button_long_sent = false;
|
||||||
|
|
@ -130,16 +139,4 @@ void ButtonPoller::OnButtonStateChanged() {
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
void ButtonPoller::ThreadLoop() {
|
|
||||||
using namespace std::chrono_literals;
|
|
||||||
std::unique_lock lk{m_mutex};
|
|
||||||
while (!m_stop) {
|
|
||||||
m_cv.wait_for(lk, 50ms);
|
|
||||||
if (m_stop) break;
|
|
||||||
lk.unlock();
|
|
||||||
OnButtonStateChanged();
|
|
||||||
lk.lock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Service::AM
|
} // namespace Service::AM
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
|
|
@ -30,31 +30,23 @@ class ButtonPoller {
|
||||||
public:
|
public:
|
||||||
explicit ButtonPoller(Core::System& system, WindowSystem& window_system);
|
explicit ButtonPoller(Core::System& system, WindowSystem& window_system);
|
||||||
~ButtonPoller();
|
~ButtonPoller();
|
||||||
|
void OnButtonStateChanged(WindowSystem& window_system);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void OnButtonStateChanged();
|
std::mutex m_mutex;
|
||||||
void ThreadLoop();
|
std::condition_variable m_cv;
|
||||||
|
std::jthread m_thread;
|
||||||
private:
|
|
||||||
WindowSystem& m_window_system;
|
|
||||||
|
|
||||||
Core::HID::EmulatedController* m_handheld{};
|
|
||||||
int m_handheld_key{};
|
|
||||||
Core::HID::EmulatedController* m_player1{};
|
|
||||||
int m_player1_key{};
|
|
||||||
|
|
||||||
std::optional<std::chrono::steady_clock::time_point> m_home_button_press_start{};
|
std::optional<std::chrono::steady_clock::time_point> m_home_button_press_start{};
|
||||||
std::optional<std::chrono::steady_clock::time_point> m_capture_button_press_start{};
|
std::optional<std::chrono::steady_clock::time_point> m_capture_button_press_start{};
|
||||||
std::optional<std::chrono::steady_clock::time_point> m_power_button_press_start{};
|
std::optional<std::chrono::steady_clock::time_point> m_power_button_press_start{};
|
||||||
|
|
||||||
bool m_home_button_long_sent{};
|
Core::HID::EmulatedController* m_handheld{};
|
||||||
bool m_capture_button_long_sent{};
|
Core::HID::EmulatedController* m_player1{};
|
||||||
bool m_power_button_long_sent{};
|
int32_t m_handheld_key{};
|
||||||
|
int32_t m_player1_key{};
|
||||||
std::thread m_thread;
|
bool m_home_button_long_sent : 1 = false;
|
||||||
std::atomic<bool> m_stop{false};
|
bool m_capture_button_long_sent : 1 = false;
|
||||||
std::condition_variable m_cv;
|
bool m_power_button_long_sent : 1 = false;
|
||||||
std::mutex m_mutex;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::AM
|
} // namespace Service::AM
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|
@ -15,12 +18,24 @@ enum class UserDataTag : u32 {
|
||||||
};
|
};
|
||||||
|
|
||||||
EventObserver::EventObserver(Core::System& system, WindowSystem& window_system)
|
EventObserver::EventObserver(Core::System& system, WindowSystem& window_system)
|
||||||
: m_system(system), m_context(system, "am:EventObserver"), m_window_system(window_system),
|
: m_system(system), m_context(system, "am:EventObserver")
|
||||||
m_wakeup_event(m_context), m_wakeup_holder(m_wakeup_event.GetHandle()) {
|
, m_window_system(window_system)
|
||||||
|
, m_wakeup_event(m_context)
|
||||||
|
, m_wakeup_holder(m_wakeup_event.GetHandle())
|
||||||
|
{
|
||||||
m_window_system.SetEventObserver(this);
|
m_window_system.SetEventObserver(this);
|
||||||
m_wakeup_holder.SetUserData(static_cast<uintptr_t>(UserDataTag::WakeupEvent));
|
m_wakeup_holder.SetUserData(static_cast<uintptr_t>(UserDataTag::WakeupEvent));
|
||||||
m_wakeup_holder.LinkToMultiWait(std::addressof(m_multi_wait));
|
m_wakeup_holder.LinkToMultiWait(std::addressof(m_multi_wait));
|
||||||
m_thread = std::thread([&] { this->ThreadFunc(); });
|
m_thread = std::thread([this] {
|
||||||
|
Common::SetCurrentThreadName("am:EventObserver");
|
||||||
|
while (true) {
|
||||||
|
auto* signaled_holder = this->WaitSignaled();
|
||||||
|
if (!signaled_holder) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this->Process(signaled_holder);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
EventObserver::~EventObserver() {
|
EventObserver::~EventObserver() {
|
||||||
|
|
@ -146,17 +161,4 @@ void EventObserver::DestroyAppletProcessHolderLocked(ProcessHolder* holder) {
|
||||||
delete holder;
|
delete holder;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventObserver::ThreadFunc() {
|
|
||||||
Common::SetCurrentThreadName("am:EventObserver");
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
auto* signaled_holder = this->WaitSignaled();
|
|
||||||
if (!signaled_holder) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->Process(signaled_holder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Service::AM
|
} // namespace Service::AM
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|
@ -41,9 +44,6 @@ private:
|
||||||
private:
|
private:
|
||||||
void DestroyAppletProcessHolderLocked(ProcessHolder* holder);
|
void DestroyAppletProcessHolderLocked(ProcessHolder* holder);
|
||||||
|
|
||||||
private:
|
|
||||||
void ThreadFunc();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// System reference and context.
|
// System reference and context.
|
||||||
Core::System& m_system;
|
Core::System& m_system;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/file_sys/content_archive.h"
|
#include "core/file_sys/content_archive.h"
|
||||||
#include "core/file_sys/nca_metadata.h"
|
#include "core/file_sys/nca_metadata.h"
|
||||||
|
|
@ -16,7 +20,7 @@ namespace Service::AM {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
FileSys::StorageId GetStorageIdForFrontendSlot(
|
[[nodiscard]] FileSys::StorageId GetStorageIdForFrontendSlot(
|
||||||
std::optional<FileSys::ContentProviderUnionSlot> slot) {
|
std::optional<FileSys::ContentProviderUnionSlot> slot) {
|
||||||
if (!slot.has_value()) {
|
if (!slot.has_value()) {
|
||||||
return FileSys::StorageId::None;
|
return FileSys::StorageId::None;
|
||||||
|
|
@ -36,31 +40,23 @@ FileSys::StorageId GetStorageIdForFrontendSlot(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Process> CreateProcessImpl(std::unique_ptr<Loader::AppLoader>& out_loader,
|
[[nodiscard]] inline std::optional<Process> CreateProcessImpl(std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index) {
|
||||||
Loader::ResultStatus& out_load_result,
|
|
||||||
Core::System& system, FileSys::VirtualFile file,
|
|
||||||
u64 program_id, u64 program_index) {
|
|
||||||
// Get the appropriate loader to parse this NCA.
|
// Get the appropriate loader to parse this NCA.
|
||||||
out_loader = Loader::GetLoader(system, file, program_id, program_index);
|
out_loader = Loader::GetLoader(system, file, program_id, program_index);
|
||||||
|
|
||||||
// Ensure we have a loader which can parse the NCA.
|
// Ensure we have a loader which can parse the NCA.
|
||||||
if (!out_loader) {
|
if (out_loader) {
|
||||||
return nullptr;
|
// Try to load the process.
|
||||||
|
auto process = std::make_optional<Process>(system);
|
||||||
|
if (process->Initialize(*out_loader, out_load_result)) {
|
||||||
|
return process;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return std::nullopt;
|
||||||
// Try to load the process.
|
|
||||||
auto process = std::make_unique<Process>(system);
|
|
||||||
if (process->Initialize(*out_loader, out_load_result)) {
|
|
||||||
return process;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id,
|
std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation) {
|
||||||
u8 minimum_key_generation, u8 maximum_key_generation) {
|
|
||||||
// Attempt to load program NCA.
|
// Attempt to load program NCA.
|
||||||
FileSys::VirtualFile nca_raw{};
|
FileSys::VirtualFile nca_raw{};
|
||||||
|
|
||||||
|
|
@ -70,7 +66,7 @@ std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id,
|
||||||
|
|
||||||
// Ensure we retrieved a program NCA.
|
// Ensure we retrieved a program NCA.
|
||||||
if (!nca_raw) {
|
if (!nca_raw) {
|
||||||
return nullptr;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure we have a suitable version.
|
// Ensure we have a suitable version.
|
||||||
|
|
@ -79,9 +75,8 @@ std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id,
|
||||||
if (nca.GetStatus() == Loader::ResultStatus::Success &&
|
if (nca.GetStatus() == Loader::ResultStatus::Success &&
|
||||||
(nca.GetKeyGeneration() < minimum_key_generation ||
|
(nca.GetKeyGeneration() < minimum_key_generation ||
|
||||||
nca.GetKeyGeneration() > maximum_key_generation)) {
|
nca.GetKeyGeneration() > maximum_key_generation)) {
|
||||||
LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id,
|
LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id, nca.GetKeyGeneration());
|
||||||
nca.GetKeyGeneration());
|
return std::nullopt;
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,42 +85,32 @@ std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id,
|
||||||
return CreateProcessImpl(loader, status, system, nca_raw, program_id, 0);
|
return CreateProcessImpl(loader, status, system, nca_raw, program_id, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Process> CreateApplicationProcess(std::vector<u8>& out_control,
|
std::optional<Process> CreateApplicationProcess(std::vector<u8>& out_control, std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index) {
|
||||||
std::unique_ptr<Loader::AppLoader>& out_loader,
|
if (auto process = CreateProcessImpl(out_loader, out_load_result, system, file, program_id, program_index); process) {
|
||||||
Loader::ResultStatus& out_load_result,
|
FileSys::NACP nacp;
|
||||||
Core::System& system, FileSys::VirtualFile file,
|
if (out_loader->ReadControlData(nacp) == Loader::ResultStatus::Success) {
|
||||||
u64 program_id, u64 program_index) {
|
out_control = nacp.GetRawBytes();
|
||||||
auto process =
|
} else {
|
||||||
CreateProcessImpl(out_loader, out_load_result, system, file, program_id, program_index);
|
out_control.resize(sizeof(FileSys::RawNACP));
|
||||||
if (!process) {
|
std::fill(out_control.begin(), out_control.end(), (u8) 0);
|
||||||
return nullptr;
|
}
|
||||||
|
|
||||||
|
auto& storage = system.GetContentProviderUnion();
|
||||||
|
Service::Glue::ApplicationLaunchProperty launch{};
|
||||||
|
launch.title_id = process->GetProgramId();
|
||||||
|
|
||||||
|
FileSys::PatchManager pm{launch.title_id, system.GetFileSystemController(), storage};
|
||||||
|
launch.version = pm.GetGameVersion().value_or(0);
|
||||||
|
|
||||||
|
// TODO(DarkLordZach): When FSController/Game Card Support is added, if
|
||||||
|
// current_process_game_card use correct StorageId
|
||||||
|
launch.base_game_storage_id = GetStorageIdForFrontendSlot(storage.GetSlotForEntry(launch.title_id, FileSys::ContentRecordType::Program));
|
||||||
|
launch.update_storage_id = GetStorageIdForFrontendSlot(storage.GetSlotForEntry(FileSys::GetUpdateTitleID(launch.title_id), FileSys::ContentRecordType::Program));
|
||||||
|
|
||||||
|
system.GetARPManager().Register(launch.title_id, launch, out_control);
|
||||||
|
return process;
|
||||||
}
|
}
|
||||||
|
return std::nullopt;
|
||||||
FileSys::NACP nacp;
|
|
||||||
if (out_loader->ReadControlData(nacp) == Loader::ResultStatus::Success) {
|
|
||||||
out_control = nacp.GetRawBytes();
|
|
||||||
} else {
|
|
||||||
out_control.resize(sizeof(FileSys::RawNACP));
|
|
||||||
std::fill(out_control.begin(), out_control.end(), (u8) 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& storage = system.GetContentProviderUnion();
|
|
||||||
Service::Glue::ApplicationLaunchProperty launch{};
|
|
||||||
launch.title_id = process->GetProgramId();
|
|
||||||
|
|
||||||
FileSys::PatchManager pm{launch.title_id, system.GetFileSystemController(), storage};
|
|
||||||
launch.version = pm.GetGameVersion().value_or(0);
|
|
||||||
|
|
||||||
// TODO(DarkLordZach): When FSController/Game Card Support is added, if
|
|
||||||
// current_process_game_card use correct StorageId
|
|
||||||
launch.base_game_storage_id = GetStorageIdForFrontendSlot(
|
|
||||||
storage.GetSlotForEntry(launch.title_id, FileSys::ContentRecordType::Program));
|
|
||||||
launch.update_storage_id = GetStorageIdForFrontendSlot(storage.GetSlotForEntry(
|
|
||||||
FileSys::GetUpdateTitleID(launch.title_id), FileSys::ContentRecordType::Program));
|
|
||||||
|
|
||||||
system.GetARPManager().Register(launch.title_id, launch, out_control);
|
|
||||||
|
|
||||||
return process;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Service::AM
|
} // namespace Service::AM
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|
@ -24,12 +27,7 @@ class Process;
|
||||||
|
|
||||||
namespace Service::AM {
|
namespace Service::AM {
|
||||||
|
|
||||||
std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id,
|
std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation);
|
||||||
u8 minimum_key_generation, u8 maximum_key_generation);
|
std::optional<Process> CreateApplicationProcess(std::vector<u8>& out_control, std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index);
|
||||||
std::unique_ptr<Process> CreateApplicationProcess(std::vector<u8>& out_control,
|
|
||||||
std::unique_ptr<Loader::AppLoader>& out_loader,
|
|
||||||
Loader::ResultStatus& out_load_result,
|
|
||||||
Core::System& system, FileSys::VirtualFile file,
|
|
||||||
u64 program_id, u64 program_index);
|
|
||||||
|
|
||||||
} // namespace Service::AM
|
} // namespace Service::AM
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,7 @@ namespace Service::AM {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
Result CreateGuestApplication(SharedPointer<IApplicationAccessor>* out_application_accessor,
|
Result CreateGuestApplication(SharedPointer<IApplicationAccessor>* out_application_accessor, Core::System& system, WindowSystem& window_system, u64 program_id) {
|
||||||
Core::System& system, WindowSystem& window_system, u64 program_id) {
|
|
||||||
FileSys::VirtualFile nca_raw{};
|
FileSys::VirtualFile nca_raw{};
|
||||||
|
|
||||||
// Get the program NCA from storage.
|
// Get the program NCA from storage.
|
||||||
|
|
@ -35,11 +34,10 @@ Result CreateGuestApplication(SharedPointer<IApplicationAccessor>* out_applicati
|
||||||
std::vector<u8> control;
|
std::vector<u8> control;
|
||||||
std::unique_ptr<Loader::AppLoader> loader;
|
std::unique_ptr<Loader::AppLoader> loader;
|
||||||
Loader::ResultStatus result;
|
Loader::ResultStatus result;
|
||||||
auto process =
|
auto process = CreateApplicationProcess(control, loader, result, system, nca_raw, program_id, 0);
|
||||||
CreateApplicationProcess(control, loader, result, system, nca_raw, program_id, 0);
|
R_UNLESS(process != std::nullopt, ResultUnknown);
|
||||||
R_UNLESS(process != nullptr, ResultUnknown);
|
|
||||||
|
|
||||||
const auto applet = std::make_shared<Applet>(system, std::move(process), true);
|
const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), true);
|
||||||
applet->program_id = program_id;
|
applet->program_id = program_id;
|
||||||
applet->applet_id = AppletId::Application;
|
applet->applet_id = AppletId::Application;
|
||||||
applet->type = AppletType::Application;
|
applet->type = AppletType::Application;
|
||||||
|
|
@ -47,8 +45,7 @@ Result CreateGuestApplication(SharedPointer<IApplicationAccessor>* out_applicati
|
||||||
|
|
||||||
window_system.TrackApplet(applet, true);
|
window_system.TrackApplet(applet, true);
|
||||||
|
|
||||||
*out_application_accessor =
|
*out_application_accessor = std::make_shared<IApplicationAccessor>(system, applet, window_system);
|
||||||
std::make_shared<IApplicationAccessor>(system, applet, window_system);
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,12 +87,10 @@ Result IApplicationCreator::CreateSystemApplication(
|
||||||
|
|
||||||
std::vector<u8> control;
|
std::vector<u8> control;
|
||||||
std::unique_ptr<Loader::AppLoader> loader;
|
std::unique_ptr<Loader::AppLoader> loader;
|
||||||
|
auto process = CreateProcess(system, application_id, 1, 22);
|
||||||
|
R_UNLESS(process != std::nullopt, ResultUnknown);
|
||||||
|
|
||||||
auto process =
|
const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), true);
|
||||||
CreateProcess(system, application_id, 1, 22);
|
|
||||||
R_UNLESS(process != nullptr, ResultUnknown);
|
|
||||||
|
|
||||||
const auto applet = std::make_shared<Applet>(system, std::move(process), true);
|
|
||||||
applet->program_id = application_id;
|
applet->program_id = application_id;
|
||||||
applet->applet_id = AppletId::Starter;
|
applet->applet_id = AppletId::Starter;
|
||||||
applet->type = AppletType::LibraryApplet;
|
applet->type = AppletType::LibraryApplet;
|
||||||
|
|
@ -103,8 +98,7 @@ Result IApplicationCreator::CreateSystemApplication(
|
||||||
|
|
||||||
m_window_system.TrackApplet(applet, true);
|
m_window_system.TrackApplet(applet, true);
|
||||||
|
|
||||||
*out_application_accessor =
|
*out_application_accessor = std::make_shared<IApplicationAccessor>(system, applet, m_window_system);
|
||||||
std::make_shared<IApplicationAccessor>(system, applet, m_window_system);
|
|
||||||
Core::LaunchTimestampCache::SaveLaunchTimestamp(application_id);
|
Core::LaunchTimestampCache::SaveLaunchTimestamp(application_id);
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -122,26 +122,23 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
|
||||||
};
|
};
|
||||||
|
|
||||||
auto process = CreateProcess(system, program_id, Firmware1400, Firmware2200);
|
auto process = CreateProcess(system, program_id, Firmware1400, Firmware2200);
|
||||||
if (!process) {
|
if (process) {
|
||||||
// Couldn't initialize the guest process
|
const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), false);
|
||||||
return {};
|
applet->program_id = program_id;
|
||||||
|
applet->applet_id = applet_id;
|
||||||
|
applet->type = AppletType::LibraryApplet;
|
||||||
|
applet->library_applet_mode = mode;
|
||||||
|
applet->window_visible = mode != LibraryAppletMode::AllForegroundInitiallyHidden;
|
||||||
|
|
||||||
|
auto broker = std::make_shared<AppletDataBroker>(system);
|
||||||
|
applet->caller_applet = caller_applet;
|
||||||
|
applet->caller_applet_broker = broker;
|
||||||
|
caller_applet->child_applets.push_back(applet);
|
||||||
|
window_system.TrackApplet(applet, false);
|
||||||
|
return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
|
||||||
}
|
}
|
||||||
|
// Couldn't initialize the guest process
|
||||||
const auto applet = std::make_shared<Applet>(system, std::move(process), false);
|
return {};
|
||||||
applet->program_id = program_id;
|
|
||||||
applet->applet_id = applet_id;
|
|
||||||
applet->type = AppletType::LibraryApplet;
|
|
||||||
applet->library_applet_mode = mode;
|
|
||||||
applet->window_visible = mode != LibraryAppletMode::AllForegroundInitiallyHidden;
|
|
||||||
|
|
||||||
auto broker = std::make_shared<AppletDataBroker>(system);
|
|
||||||
applet->caller_applet = caller_applet;
|
|
||||||
applet->caller_applet_broker = broker;
|
|
||||||
caller_applet->child_applets.push_back(applet);
|
|
||||||
|
|
||||||
window_system.TrackApplet(applet, false);
|
|
||||||
|
|
||||||
return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(Core::System& system,
|
std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(Core::System& system,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
|
|
@ -14,7 +14,9 @@ namespace Service::Audio {
|
||||||
using namespace AudioCore::AudioOut;
|
using namespace AudioCore::AudioOut;
|
||||||
|
|
||||||
IAudioOutManager::IAudioOutManager(Core::System& system_)
|
IAudioOutManager::IAudioOutManager(Core::System& system_)
|
||||||
: ServiceFramework{system_, "audout:u"}, impl{std::make_unique<Manager>(system_)} {
|
: ServiceFramework{system_, "audout:u"}
|
||||||
|
, impl(system_)
|
||||||
|
{
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, D<&IAudioOutManager::ListAudioOuts>, "ListAudioOuts"},
|
{0, D<&IAudioOutManager::ListAudioOuts>, "ListAudioOuts"},
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
|
|
@ -43,7 +43,7 @@ private:
|
||||||
AudioCore::AudioOut::AudioOutParameter parameter,
|
AudioCore::AudioOut::AudioOutParameter parameter,
|
||||||
InCopyHandle<Kernel::KProcess> process_handle, ClientAppletResourceUserId aruid);
|
InCopyHandle<Kernel::KProcess> process_handle, ClientAppletResourceUserId aruid);
|
||||||
|
|
||||||
std::unique_ptr<AudioCore::AudioOut::Manager> impl;
|
std::optional<AudioCore::AudioOut::Manager> impl;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::Audio
|
} // namespace Service::Audio
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|
@ -15,7 +18,9 @@ namespace Service::Audio {
|
||||||
using namespace AudioCore::Renderer;
|
using namespace AudioCore::Renderer;
|
||||||
|
|
||||||
IAudioRendererManager::IAudioRendererManager(Core::System& system_)
|
IAudioRendererManager::IAudioRendererManager(Core::System& system_)
|
||||||
: ServiceFramework{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} {
|
: ServiceFramework{system_, "audren:u"}
|
||||||
|
, impl(system_)
|
||||||
|
{
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, D<&IAudioRendererManager::OpenAudioRenderer>, "OpenAudioRenderer"},
|
{0, D<&IAudioRendererManager::OpenAudioRenderer>, "OpenAudioRenderer"},
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|
@ -30,7 +33,7 @@ private:
|
||||||
Result GetAudioDeviceServiceWithRevisionInfo(Out<SharedPointer<IAudioDevice>> out_audio_device,
|
Result GetAudioDeviceServiceWithRevisionInfo(Out<SharedPointer<IAudioDevice>> out_audio_device,
|
||||||
u32 revision, ClientAppletResourceUserId aruid);
|
u32 revision, ClientAppletResourceUserId aruid);
|
||||||
|
|
||||||
std::unique_ptr<AudioCore::Renderer::Manager> impl;
|
std::optional<AudioCore::Renderer::Manager> impl;
|
||||||
u32 num_audio_devices{0};
|
u32 num_audio_devices{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -138,8 +138,7 @@ NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) {
|
||||||
static_cast<u32>((vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits)};
|
static_cast<u32>((vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits)};
|
||||||
vm.big_page_allocator.emplace(start_big_pages, end_big_pages);
|
vm.big_page_allocator.emplace(start_big_pages, end_big_pages);
|
||||||
|
|
||||||
gmmu = std::make_shared<Tegra::MemoryManager>(system, max_big_page_bits, vm.va_range_split,
|
gmmu = std::make_unique<Tegra::MemoryManager>(system, max_big_page_bits, vm.va_range_split, vm.big_page_size_bits, VM::PAGE_SIZE_BITS);
|
||||||
vm.big_page_size_bits, VM::PAGE_SIZE_BITS);
|
|
||||||
system.GPU().InitAddressSpace(*gmmu);
|
system.GPU().InitAddressSpace(*gmmu);
|
||||||
vm.initialised = true;
|
vm.initialised = true;
|
||||||
|
|
||||||
|
|
@ -416,7 +415,7 @@ NvResult nvhost_as_gpu::BindChannel(IoctlBindChannel& params) {
|
||||||
LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd);
|
LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd);
|
||||||
|
|
||||||
auto gpu_channel_device = module.GetDevice<nvhost_gpu>(params.fd);
|
auto gpu_channel_device = module.GetDevice<nvhost_gpu>(params.fd);
|
||||||
gpu_channel_device->channel_state->memory_manager = gmmu;
|
gpu_channel_device->channel_state->memory_manager = gmmu.get();
|
||||||
return NvResult::Success;
|
return NvResult::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -219,7 +219,7 @@ private:
|
||||||
|
|
||||||
bool initialised{};
|
bool initialised{};
|
||||||
} vm;
|
} vm;
|
||||||
std::shared_ptr<Tegra::MemoryManager> gmmu;
|
std::unique_ptr<Tegra::MemoryManager> gmmu;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::Nvidia::Devices
|
} // namespace Service::Nvidia::Devices
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|
@ -10,14 +13,6 @@
|
||||||
|
|
||||||
namespace Service {
|
namespace Service {
|
||||||
|
|
||||||
Process::Process(Core::System& system)
|
|
||||||
: m_system(system), m_process(), m_main_thread_priority(), m_main_thread_stack_size(),
|
|
||||||
m_process_started() {}
|
|
||||||
|
|
||||||
Process::~Process() {
|
|
||||||
this->Finalize();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Process::Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_load_result) {
|
bool Process::Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_load_result) {
|
||||||
// First, ensure we are not holding another process.
|
// First, ensure we are not holding another process.
|
||||||
this->Finalize();
|
this->Finalize();
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|
@ -22,8 +25,8 @@ namespace Service {
|
||||||
|
|
||||||
class Process {
|
class Process {
|
||||||
public:
|
public:
|
||||||
explicit Process(Core::System& system);
|
inline explicit Process(Core::System& system) noexcept : m_system(system) {}
|
||||||
~Process();
|
inline ~Process() { this->Finalize(); }
|
||||||
|
|
||||||
bool Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_load_result);
|
bool Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_load_result);
|
||||||
void Finalize();
|
void Finalize();
|
||||||
|
|
@ -50,8 +53,8 @@ public:
|
||||||
private:
|
private:
|
||||||
Core::System& m_system;
|
Core::System& m_system;
|
||||||
Kernel::KProcess* m_process{};
|
Kernel::KProcess* m_process{};
|
||||||
s32 m_main_thread_priority{};
|
|
||||||
u64 m_main_thread_stack_size{};
|
u64 m_main_thread_stack_size{};
|
||||||
|
s32 m_main_thread_priority{};
|
||||||
bool m_process_started{};
|
bool m_process_started{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
#include "common/param_package.h"
|
#include "common/param_package.h"
|
||||||
#include "common/random.h"
|
#include "common/random.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
|
#include "common/thread.h"
|
||||||
#include "input_common/drivers/udp_client.h"
|
#include "input_common/drivers/udp_client.h"
|
||||||
#include "input_common/helpers/udp_protocol.h"
|
#include "input_common/helpers/udp_protocol.h"
|
||||||
|
|
||||||
|
|
@ -131,6 +132,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
static void SocketLoop(Socket* socket) {
|
static void SocketLoop(Socket* socket) {
|
||||||
|
Common::SetCurrentThreadName("cemuhookWorker");
|
||||||
socket->StartReceive();
|
socket->StartReceive();
|
||||||
socket->StartSend(Socket::clock::now());
|
socket->StartSend(Socket::clock::now());
|
||||||
socket->Loop();
|
socket->Loop();
|
||||||
|
|
@ -326,9 +328,11 @@ void UDPClient::OnPadData(Response::PadData data, std::size_t client) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDPClient::StartCommunication(std::size_t client, const std::string& host, u16 port) {
|
void UDPClient::StartCommunication(std::size_t client, const std::string& host, u16 port) {
|
||||||
SocketCallback callback{[this](Response::Version version) { OnVersion(version); },
|
SocketCallback callback{
|
||||||
[this](Response::PortInfo info) { OnPortInfo(info); },
|
[this](Response::Version version) { OnVersion(version); },
|
||||||
[this, client](Response::PadData data) { OnPadData(data, client); }};
|
[this](Response::PortInfo info) { OnPortInfo(info); },
|
||||||
|
[this, client](Response::PadData data) { OnPadData(data, client); }
|
||||||
|
};
|
||||||
LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port);
|
LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port);
|
||||||
clients[client].uuid = GetHostUUID(host);
|
clients[client].uuid = GetHostUUID(host);
|
||||||
clients[client].host = host;
|
clients[client].host = host;
|
||||||
|
|
@ -566,9 +570,7 @@ bool UDPClient::IsStickInverted(const Common::ParamPackage& params) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestCommunication(const std::string& host, u16 port,
|
void TestCommunication(const std::string& host, u16 port, const std::function<void()>& success_callback, const std::function<void()>& failure_callback) {
|
||||||
const std::function<void()>& success_callback,
|
|
||||||
const std::function<void()>& failure_callback) {
|
|
||||||
std::thread([=] {
|
std::thread([=] {
|
||||||
Common::Event success_event;
|
Common::Event success_event;
|
||||||
SocketCallback callback{
|
SocketCallback callback{
|
||||||
|
|
@ -601,40 +603,38 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
|
||||||
u16 max_y{};
|
u16 max_y{};
|
||||||
|
|
||||||
Status current_status{Status::Initialized};
|
Status current_status{Status::Initialized};
|
||||||
SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {},
|
SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {}, [&](Response::PadData data) {
|
||||||
[&](Response::PadData data) {
|
constexpr u16 CALIBRATION_THRESHOLD = 100;
|
||||||
constexpr u16 CALIBRATION_THRESHOLD = 100;
|
|
||||||
|
|
||||||
if (current_status == Status::Initialized) {
|
if (current_status == Status::Initialized) {
|
||||||
// Receiving data means the communication is ready now
|
// Receiving data means the communication is ready now
|
||||||
current_status = Status::Ready;
|
current_status = Status::Ready;
|
||||||
status_callback(current_status);
|
status_callback(current_status);
|
||||||
}
|
}
|
||||||
if (data.touch[0].is_active == 0) {
|
if (data.touch[0].is_active == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LOG_DEBUG(Input, "Current touch: {} {}", data.touch[0].x,
|
LOG_DEBUG(Input, "Current touch: {} {}", data.touch[0].x, data.touch[0].y);
|
||||||
data.touch[0].y);
|
min_x = (std::min)(min_x, u16(data.touch[0].x));
|
||||||
min_x = (std::min)(min_x, static_cast<u16>(data.touch[0].x));
|
min_y = (std::min)(min_y, u16(data.touch[0].y));
|
||||||
min_y = (std::min)(min_y, static_cast<u16>(data.touch[0].y));
|
if (current_status == Status::Ready) {
|
||||||
if (current_status == Status::Ready) {
|
// First touch - min data (min_x/min_y)
|
||||||
// First touch - min data (min_x/min_y)
|
current_status = Status::Stage1Completed;
|
||||||
current_status = Status::Stage1Completed;
|
status_callback(current_status);
|
||||||
status_callback(current_status);
|
}
|
||||||
}
|
if (data.touch[0].x - min_x > CALIBRATION_THRESHOLD &&
|
||||||
if (data.touch[0].x - min_x > CALIBRATION_THRESHOLD &&
|
data.touch[0].y - min_y > CALIBRATION_THRESHOLD) {
|
||||||
data.touch[0].y - min_y > CALIBRATION_THRESHOLD) {
|
// Set the current position as max value and finishes
|
||||||
// Set the current position as max value and finishes
|
// configuration
|
||||||
// configuration
|
max_x = data.touch[0].x;
|
||||||
max_x = data.touch[0].x;
|
max_y = data.touch[0].y;
|
||||||
max_y = data.touch[0].y;
|
current_status = Status::Completed;
|
||||||
current_status = Status::Completed;
|
data_callback(min_x, min_y, max_x, max_y);
|
||||||
data_callback(min_x, min_y, max_x, max_y);
|
status_callback(current_status);
|
||||||
status_callback(current_status);
|
|
||||||
|
|
||||||
complete_event.Set();
|
complete_event.Set();
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
Socket socket{host, port, std::move(callback)};
|
Socket socket{host, port, std::move(callback)};
|
||||||
std::thread worker_thread{SocketLoop, &socket};
|
std::thread worker_thread{SocketLoop, &socket};
|
||||||
complete_event.Wait();
|
complete_event.Wait();
|
||||||
|
|
|
||||||
|
|
@ -334,7 +334,7 @@ if (YUZU_USE_EXTERNAL_FFMPEG)
|
||||||
add_dependencies(video_core ffmpeg-build)
|
add_dependencies(video_core ffmpeg-build)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_include_directories(video_core PRIVATE ${FFmpeg_INCLUDE_DIR})
|
target_include_directories(video_core PUBLIC ${FFmpeg_INCLUDE_DIR})
|
||||||
|
|
||||||
target_link_libraries(video_core PRIVATE ${FFmpeg_LIBRARIES})
|
target_link_libraries(video_core PRIVATE ${FFmpeg_LIBRARIES})
|
||||||
target_link_options(video_core PRIVATE ${FFmpeg_LDFLAGS})
|
target_link_options(video_core PRIVATE ${FFmpeg_LDFLAGS})
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,7 @@
|
||||||
namespace Tegra {
|
namespace Tegra {
|
||||||
|
|
||||||
CDmaPusher::CDmaPusher(Host1x::Host1x& host1x_, s32 id)
|
CDmaPusher::CDmaPusher(Host1x::Host1x& host1x_, s32 id)
|
||||||
: host_processor(host1x_)
|
: host1x{host1x_}
|
||||||
, host1x{host1x_}
|
|
||||||
, current_class{ChClassId(id)}
|
, current_class{ChClassId(id)}
|
||||||
{
|
{
|
||||||
thread = std::jthread([this](std::stop_token stop_token) {
|
thread = std::jthread([this](std::stop_token stop_token) {
|
||||||
|
|
@ -99,7 +98,7 @@ void CDmaPusher::ExecuteCommand(u32 method, u32 arg) {
|
||||||
switch (current_class) {
|
switch (current_class) {
|
||||||
case ChClassId::Control:
|
case ChClassId::Control:
|
||||||
LOG_TRACE(Service_NVDRV, "Class {} method {:#X} arg 0x{:X}", u32(current_class), method, arg);
|
LOG_TRACE(Service_NVDRV, "Class {} method {:#X} arg 0x{:X}", u32(current_class), method, arg);
|
||||||
host_processor.ProcessMethod(Host1x::Control::Method(method), arg);
|
host_processor.ProcessMethod(host1x, Host1x::Control::Method(method), arg);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
thi_regs.reg_array[method] = arg;
|
thi_regs.reg_array[method] = arg;
|
||||||
|
|
|
||||||
|
|
@ -17,28 +17,36 @@
|
||||||
|
|
||||||
namespace Tegra::Control {
|
namespace Tegra::Control {
|
||||||
|
|
||||||
ChannelState::ChannelState(s32 bind_id_) : bind_id{bind_id_}, initialized{} {}
|
ChannelState::Payload::Payload(Core::System& system, MemoryManager& memory_manager, ChannelState& channel_state)
|
||||||
|
: maxwell_3d(memory_manager)
|
||||||
|
, fermi_2d(memory_manager)
|
||||||
|
, kepler_compute(memory_manager)
|
||||||
|
, maxwell_dma(memory_manager)
|
||||||
|
, kepler_memory(memory_manager)
|
||||||
|
, nv01_timer(memory_manager)
|
||||||
|
, dma_pusher(system, memory_manager, channel_state)
|
||||||
|
{}
|
||||||
|
|
||||||
void ChannelState::Init(Core::System& system, GPU& gpu, u64 program_id_) {
|
ChannelState::ChannelState(s32 bind_id_)
|
||||||
|
: bind_id{bind_id_}
|
||||||
|
{}
|
||||||
|
|
||||||
|
void ChannelState::Init(Core::System& system, u64 program_id_) {
|
||||||
ASSERT(memory_manager);
|
ASSERT(memory_manager);
|
||||||
program_id = program_id_;
|
program_id = program_id_;
|
||||||
dma_pusher.emplace(system, gpu, *memory_manager, *this);
|
payload.emplace(system, *memory_manager, *this);
|
||||||
maxwell_3d.emplace(system, *memory_manager);
|
|
||||||
fermi_2d.emplace(*memory_manager);
|
|
||||||
kepler_compute.emplace(system, *memory_manager);
|
|
||||||
maxwell_dma.emplace(system, *memory_manager);
|
|
||||||
kepler_memory.emplace(system, *memory_manager);
|
|
||||||
initialized = true;
|
initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChannelState::BindRasterizer(VideoCore::RasterizerInterface* rasterizer) {
|
void ChannelState::BindRasterizer(VideoCore::RasterizerInterface* rasterizer) {
|
||||||
dma_pusher->BindRasterizer(rasterizer);
|
payload->dma_pusher.BindRasterizer(rasterizer);
|
||||||
memory_manager->BindRasterizer(rasterizer);
|
memory_manager->BindRasterizer(rasterizer);
|
||||||
maxwell_3d->BindRasterizer(rasterizer);
|
payload->maxwell_3d.BindRasterizer(rasterizer);
|
||||||
fermi_2d->BindRasterizer(rasterizer);
|
payload->fermi_2d.BindRasterizer(rasterizer);
|
||||||
kepler_memory->BindRasterizer(rasterizer);
|
payload->kepler_memory.BindRasterizer(rasterizer);
|
||||||
kepler_compute->BindRasterizer(rasterizer);
|
payload->kepler_compute.BindRasterizer(rasterizer);
|
||||||
maxwell_dma->BindRasterizer(rasterizer);
|
payload->maxwell_dma.BindRasterizer(rasterizer);
|
||||||
|
//payload->nv01_timer.BindRasterizer(rasterizer);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Tegra::Control
|
} // namespace Tegra::Control
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
#include "video_core/engines/kepler_compute.h"
|
#include "video_core/engines/kepler_compute.h"
|
||||||
#include "video_core/engines/maxwell_3d.h"
|
#include "video_core/engines/maxwell_3d.h"
|
||||||
#include "video_core/engines/maxwell_dma.h"
|
#include "video_core/engines/maxwell_dma.h"
|
||||||
|
#include "video_core/engines/nv01_timer.h"
|
||||||
#include "video_core/dma_pusher.h"
|
#include "video_core/dma_pusher.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
@ -34,28 +35,33 @@ namespace Control {
|
||||||
struct ChannelState {
|
struct ChannelState {
|
||||||
explicit ChannelState(s32 bind_id);
|
explicit ChannelState(s32 bind_id);
|
||||||
|
|
||||||
void Init(Core::System& system, GPU& gpu, u64 program_id);
|
void Init(Core::System& system, u64 program_id);
|
||||||
|
|
||||||
void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
|
void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
|
||||||
|
|
||||||
/// 3D engine
|
struct Payload {
|
||||||
std::optional<Engines::Maxwell3D> maxwell_3d;
|
explicit Payload(Core::System& system, MemoryManager& memory_manager, ChannelState& channel_state);
|
||||||
/// 2D engine
|
|
||||||
std::optional<Engines::Fermi2D> fermi_2d;
|
/// 3D engine
|
||||||
/// Compute engine
|
Engines::Maxwell3D maxwell_3d;
|
||||||
std::optional<Engines::KeplerCompute> kepler_compute;
|
/// 2D engine
|
||||||
/// DMA engine
|
Engines::Fermi2D fermi_2d;
|
||||||
std::optional<Engines::MaxwellDMA> maxwell_dma;
|
/// Compute engine
|
||||||
/// Inline memory engine
|
Engines::KeplerCompute kepler_compute;
|
||||||
std::optional<Engines::KeplerMemory> kepler_memory;
|
/// DMA engine
|
||||||
/// NV01 Timer
|
Engines::MaxwellDMA maxwell_dma;
|
||||||
std::optional<Engines::KeplerMemory> nv01_timer;
|
/// Inline memory engine
|
||||||
std::optional<DmaPusher> dma_pusher;
|
Engines::KeplerMemory kepler_memory;
|
||||||
std::shared_ptr<MemoryManager> memory_manager;
|
/// NV01 Timer
|
||||||
|
Engines::Nv01Timer nv01_timer;
|
||||||
|
DmaPusher dma_pusher;
|
||||||
|
};
|
||||||
|
std::optional<Payload> payload;
|
||||||
|
MemoryManager* memory_manager = nullptr;
|
||||||
|
|
||||||
s32 bind_id = -1;
|
s32 bind_id = -1;
|
||||||
u64 program_id = 0;
|
u64 program_id = 0;
|
||||||
bool initialized{};
|
bool initialized = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Control
|
} // namespace Control
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
|
@ -6,8 +9,11 @@
|
||||||
namespace VideoCommon {
|
namespace VideoCommon {
|
||||||
|
|
||||||
ChannelInfo::ChannelInfo(Tegra::Control::ChannelState& channel_state)
|
ChannelInfo::ChannelInfo(Tegra::Control::ChannelState& channel_state)
|
||||||
: maxwell3d{*channel_state.maxwell_3d}, kepler_compute{*channel_state.kepler_compute},
|
: maxwell3d{channel_state.payload->maxwell_3d}
|
||||||
gpu_memory{*channel_state.memory_manager}, program_id{channel_state.program_id} {}
|
, kepler_compute{channel_state.payload->kepler_compute}
|
||||||
|
, gpu_memory{*channel_state.memory_manager}
|
||||||
|
, program_id{channel_state.program_id}
|
||||||
|
{}
|
||||||
|
|
||||||
template class VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo>;
|
template class VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo>;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
|
@ -41,7 +44,7 @@ void ChannelSetupCaches<P>::CreateChannel(struct Tegra::Control::ChannelState& c
|
||||||
AddressSpaceRef new_gpu_mem_ref{
|
AddressSpaceRef new_gpu_mem_ref{
|
||||||
.ref_count = 1,
|
.ref_count = 1,
|
||||||
.storage_id = address_spaces.size(),
|
.storage_id = address_spaces.size(),
|
||||||
.gpu_memory = channel.memory_manager.get(),
|
.gpu_memory = channel.memory_manager,
|
||||||
};
|
};
|
||||||
address_spaces.emplace(channel.memory_manager->GetID(), new_gpu_mem_ref);
|
address_spaces.emplace(channel.memory_manager->GetID(), new_gpu_mem_ref);
|
||||||
OnGPUASRegister(channel.memory_manager->GetID());
|
OnGPUASRegister(channel.memory_manager->GetID());
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
|
||||||
|
|
@ -12,11 +12,8 @@
|
||||||
#include "video_core/gpu.h"
|
#include "video_core/gpu.h"
|
||||||
|
|
||||||
namespace Tegra::Control {
|
namespace Tegra::Control {
|
||||||
Scheduler::Scheduler(GPU& gpu_) : gpu{gpu_} {}
|
|
||||||
|
|
||||||
Scheduler::~Scheduler() = default;
|
void Scheduler::Push(GPU& gpu, s32 channel, CommandList&& entries) {
|
||||||
|
|
||||||
void Scheduler::Push(s32 channel, CommandList&& entries) {
|
|
||||||
std::shared_ptr<ChannelState> channel_state;
|
std::shared_ptr<ChannelState> channel_state;
|
||||||
{
|
{
|
||||||
std::unique_lock lk(scheduling_guard);
|
std::unique_lock lk(scheduling_guard);
|
||||||
|
|
@ -27,8 +24,8 @@ void Scheduler::Push(s32 channel, CommandList&& entries) {
|
||||||
}
|
}
|
||||||
// Process commands outside the lock to reduce contention.
|
// Process commands outside the lock to reduce contention.
|
||||||
// Multiple channels can prepare their commands in parallel.
|
// Multiple channels can prepare their commands in parallel.
|
||||||
channel_state->dma_pusher->Push(std::move(entries));
|
channel_state->payload->dma_pusher.Push(std::move(entries));
|
||||||
channel_state->dma_pusher->DispatchCalls();
|
channel_state->payload->dma_pusher.DispatchCalls();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::DeclareChannel(std::shared_ptr<ChannelState> new_channel) {
|
void Scheduler::DeclareChannel(std::shared_ptr<ChannelState> new_channel) {
|
||||||
|
|
|
||||||
|
|
@ -22,17 +22,13 @@ struct ChannelState;
|
||||||
|
|
||||||
class Scheduler {
|
class Scheduler {
|
||||||
public:
|
public:
|
||||||
explicit Scheduler(GPU& gpu_);
|
void Push(GPU& gpu, s32 channel, CommandList&& entries);
|
||||||
~Scheduler();
|
|
||||||
|
|
||||||
void Push(s32 channel, CommandList&& entries);
|
|
||||||
|
|
||||||
void DeclareChannel(std::shared_ptr<ChannelState> new_channel);
|
void DeclareChannel(std::shared_ptr<ChannelState> new_channel);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ankerl::unordered_dense::map<s32, std::shared_ptr<ChannelState>> channels;
|
ankerl::unordered_dense::map<s32, std::shared_ptr<ChannelState>> channels;
|
||||||
std::mutex scheduling_guard;
|
std::mutex scheduling_guard;
|
||||||
GPU& gpu;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Control
|
} // namespace Control
|
||||||
|
|
|
||||||
|
|
@ -12,37 +12,32 @@
|
||||||
#include "video_core/guest_memory.h"
|
#include "video_core/guest_memory.h"
|
||||||
#include "video_core/memory_manager.h"
|
#include "video_core/memory_manager.h"
|
||||||
#include "video_core/rasterizer_interface.h"
|
#include "video_core/rasterizer_interface.h"
|
||||||
#include "video_core/texture_cache/util.h"
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#include <intrin.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace Tegra {
|
namespace Tegra {
|
||||||
|
|
||||||
constexpr u32 MacroRegistersStart = 0xE00;
|
constexpr u32 MacroRegistersStart = 0xE00;
|
||||||
[[maybe_unused]] constexpr u32 ComputeInline = 0x6D;
|
[[maybe_unused]] constexpr u32 ComputeInline = 0x6D;
|
||||||
|
|
||||||
DmaPusher::DmaPusher(Core::System& system_, GPU& gpu_, MemoryManager& memory_manager_,
|
DmaPusher::DmaPusher(Core::System& system_, MemoryManager& memory_manager_, Control::ChannelState& channel_state_)
|
||||||
Control::ChannelState& channel_state_)
|
: system{system_}
|
||||||
: gpu{gpu_}, system{system_}, memory_manager{memory_manager_}, puller{gpu_, memory_manager_,
|
, memory_manager{memory_manager_}
|
||||||
*this, channel_state_}, signal_sync{false}, synced{true} {}
|
, channel_state{channel_state_}
|
||||||
|
, signal_sync{false}
|
||||||
|
, synced{false}
|
||||||
|
{}
|
||||||
|
|
||||||
DmaPusher::~DmaPusher() = default;
|
DmaPusher::~DmaPusher() = default;
|
||||||
|
|
||||||
void DmaPusher::DispatchCalls() {
|
void DmaPusher::DispatchCalls() {
|
||||||
|
|
||||||
dma_pushbuffer_subindex = 0;
|
dma_pushbuffer_subindex = 0;
|
||||||
|
|
||||||
dma_state.is_last_call = true;
|
dma_state.is_last_call = true;
|
||||||
|
|
||||||
while (system.IsPoweredOn()) {
|
while (system.IsPoweredOn()) {
|
||||||
if (!Step()) {
|
if (!Step()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gpu.FlushCommands();
|
system.GPU().FlushCommands();
|
||||||
gpu.OnCommandListEnd();
|
system.GPU().OnCommandListEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DmaPusher::Step() {
|
bool DmaPusher::Step() {
|
||||||
|
|
@ -171,9 +166,9 @@ void DmaPusher::SetState(const CommandHeader& command_header) {
|
||||||
dma_state.method_count = command_header.method_count;
|
dma_state.method_count = command_header.method_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DmaPusher::CallMethod(u32 argument) const {
|
void DmaPusher::CallMethod(u32 argument) {
|
||||||
if (dma_state.method < non_puller_methods) {
|
if (dma_state.method < non_puller_methods) {
|
||||||
puller.CallPullerMethod(Engines::Puller::MethodCall{
|
puller.CallPullerMethod(*this, Engines::Puller::MethodCall{
|
||||||
dma_state.method,
|
dma_state.method,
|
||||||
argument,
|
argument,
|
||||||
dma_state.subchannel,
|
dma_state.subchannel,
|
||||||
|
|
@ -181,30 +176,29 @@ void DmaPusher::CallMethod(u32 argument) const {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
auto subchannel = subchannels[dma_state.subchannel];
|
auto subchannel = subchannels[dma_state.subchannel];
|
||||||
if (subchannel->execution_mask[dma_state.method]) {
|
if (!subchannel->execution_mask[dma_state.method]) {
|
||||||
subchannel->ConsumeSink();
|
|
||||||
subchannel->current_dma_segment = dma_state.dma_get + dma_state.dma_word_offset;
|
|
||||||
subchannel->CallMethod(dma_state.method, argument, dma_state.is_last_call);
|
|
||||||
} else {
|
|
||||||
subchannel->method_sink.emplace_back(dma_state.method, argument);
|
subchannel->method_sink.emplace_back(dma_state.method, argument);
|
||||||
|
} else {
|
||||||
|
subchannel->ConsumeSink(system);
|
||||||
|
subchannel->current_dma_segment = dma_state.dma_get + dma_state.dma_word_offset;
|
||||||
|
subchannel->CallMethod(system, dma_state.method, argument, dma_state.is_last_call);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DmaPusher::CallMultiMethod(const u32* base_start, u32 num_methods) const {
|
void DmaPusher::CallMultiMethod(const u32* base_start, u32 num_methods) {
|
||||||
if (dma_state.method < non_puller_methods) {
|
if (dma_state.method < non_puller_methods) {
|
||||||
puller.CallMultiMethod(dma_state.method, dma_state.subchannel, base_start, num_methods, dma_state.method_count);
|
puller.CallMultiMethod(*this, dma_state.method, dma_state.subchannel, base_start, num_methods, dma_state.method_count);
|
||||||
} else {
|
} else {
|
||||||
auto subchannel = subchannels[dma_state.subchannel];
|
auto subchannel = subchannels[dma_state.subchannel];
|
||||||
subchannel->ConsumeSink();
|
subchannel->ConsumeSink(system);
|
||||||
subchannel->current_dma_segment = dma_state.dma_get + dma_state.dma_word_offset;
|
subchannel->current_dma_segment = dma_state.dma_get + dma_state.dma_word_offset;
|
||||||
subchannel->CallMultiMethod(dma_state.method, base_start, num_methods, dma_state.method_count);
|
subchannel->CallMultiMethod(system, dma_state.method, base_start, num_methods, dma_state.method_count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DmaPusher::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
|
void DmaPusher::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
|
||||||
rasterizer = rasterizer_;
|
rasterizer = rasterizer_;
|
||||||
puller.BindRasterizer(rasterizer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Tegra
|
} // namespace Tegra
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
|
|
@ -109,25 +109,21 @@ inline CommandHeader BuildCommandHeader(BufferMethods method, u32 arg_count, Sub
|
||||||
struct CommandList final {
|
struct CommandList final {
|
||||||
CommandList() = default;
|
CommandList() = default;
|
||||||
explicit CommandList(std::size_t size) : command_lists(size) {}
|
explicit CommandList(std::size_t size) : command_lists(size) {}
|
||||||
explicit CommandList(
|
explicit CommandList(boost::container::small_vector<CommandHeader, 512>&& prefetch_command_list_)
|
||||||
boost::container::small_vector<CommandHeader, 512>&& prefetch_command_list_)
|
|
||||||
: prefetch_command_list{std::move(prefetch_command_list_)} {}
|
: prefetch_command_list{std::move(prefetch_command_list_)} {}
|
||||||
|
|
||||||
boost::container::small_vector<CommandListHeader, 512> command_lists;
|
boost::container::small_vector<CommandListHeader, 512> command_lists;
|
||||||
boost::container::small_vector<CommandHeader, 512> prefetch_command_list;
|
boost::container::small_vector<CommandHeader, 512> prefetch_command_list;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/// @brief The DmaPusher class implements DMA submission to FIFOs, providing an area of memory that the
|
||||||
* The DmaPusher class implements DMA submission to FIFOs, providing an area of memory that the
|
/// emulated app fills with commands and tells PFIFO to process. The pushbuffers are then assembled
|
||||||
* emulated app fills with commands and tells PFIFO to process. The pushbuffers are then assembled
|
/// into a "command stream" consisting of 32-bit words that make up "commands".
|
||||||
* into a "command stream" consisting of 32-bit words that make up "commands".
|
/// See https://envytools.readthedocs.io/en/latest/hw/fifo/dma-pusher.html#fifo-dma-pusher for
|
||||||
* See https://envytools.readthedocs.io/en/latest/hw/fifo/dma-pusher.html#fifo-dma-pusher for
|
/// details on this implementation.
|
||||||
* details on this implementation.
|
|
||||||
*/
|
|
||||||
class DmaPusher final {
|
class DmaPusher final {
|
||||||
public:
|
public:
|
||||||
explicit DmaPusher(Core::System& system_, GPU& gpu_, MemoryManager& memory_manager_,
|
explicit DmaPusher(Core::System& system_, MemoryManager& memory_manager_, Control::ChannelState& channel_state_);
|
||||||
Control::ChannelState& channel_state_);
|
|
||||||
~DmaPusher();
|
~DmaPusher();
|
||||||
|
|
||||||
void Push(CommandList&& entries) {
|
void Push(CommandList&& entries) {
|
||||||
|
|
@ -136,8 +132,7 @@ public:
|
||||||
|
|
||||||
void DispatchCalls();
|
void DispatchCalls();
|
||||||
|
|
||||||
void BindSubchannel(Engines::EngineInterface* engine, u32 subchannel_id,
|
void BindSubchannel(Engines::EngineInterface* engine, u32 subchannel_id, Engines::EngineTypes engine_type) {
|
||||||
Engines::EngineTypes engine_type) {
|
|
||||||
subchannels[subchannel_id] = engine;
|
subchannels[subchannel_id] = engine;
|
||||||
subchannel_type[subchannel_id] = engine_type;
|
subchannel_type[subchannel_id] = engine_type;
|
||||||
}
|
}
|
||||||
|
|
@ -152,11 +147,11 @@ private:
|
||||||
|
|
||||||
void SetState(const CommandHeader& command_header);
|
void SetState(const CommandHeader& command_header);
|
||||||
|
|
||||||
void CallMethod(u32 argument) const;
|
void CallMethod(u32 argument);
|
||||||
void CallMultiMethod(const u32* base_start, u32 num_methods) const;
|
void CallMultiMethod(const u32* base_start, u32 num_methods);
|
||||||
|
|
||||||
Common::ScratchBuffer<CommandHeader>
|
public:
|
||||||
command_headers; ///< Buffer for list of commands fetched at once
|
Common::ScratchBuffer<CommandHeader> command_headers; ///< Buffer for list of commands fetched at once
|
||||||
|
|
||||||
std::queue<CommandList> dma_pushbuffer; ///< Queue of command lists to be processed
|
std::queue<CommandList> dma_pushbuffer; ///< Queue of command lists to be processed
|
||||||
std::size_t dma_pushbuffer_subindex{}; ///< Index within a command list within the pushbuffer
|
std::size_t dma_pushbuffer_subindex{}; ///< Index within a command list within the pushbuffer
|
||||||
|
|
@ -172,24 +167,24 @@ private:
|
||||||
bool is_last_call;
|
bool is_last_call;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Core::System& system;
|
||||||
|
MemoryManager& memory_manager;
|
||||||
|
Control::ChannelState& channel_state;
|
||||||
|
|
||||||
DmaState dma_state{};
|
DmaState dma_state{};
|
||||||
bool dma_increment_once{};
|
|
||||||
|
|
||||||
const bool ib_enable{true}; ///< IB mode enabled
|
|
||||||
|
|
||||||
std::array<Engines::EngineInterface*, max_subchannels> subchannels{};
|
std::array<Engines::EngineInterface*, max_subchannels> subchannels{};
|
||||||
std::array<Engines::EngineTypes, max_subchannels> subchannel_type;
|
std::array<Engines::EngineTypes, max_subchannels> subchannel_type;
|
||||||
|
|
||||||
GPU& gpu;
|
Engines::Puller puller;
|
||||||
Core::System& system;
|
|
||||||
MemoryManager& memory_manager;
|
|
||||||
mutable Engines::Puller puller;
|
|
||||||
|
|
||||||
VideoCore::RasterizerInterface* rasterizer;
|
|
||||||
bool signal_sync;
|
|
||||||
bool synced;
|
|
||||||
std::mutex sync_mutex;
|
std::mutex sync_mutex;
|
||||||
std::condition_variable sync_cv;
|
std::condition_variable sync_cv;
|
||||||
|
|
||||||
|
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
||||||
|
|
||||||
|
const bool ib_enable : 1 = true; ///< IB mode enabled
|
||||||
|
bool dma_increment_once : 1 = false;
|
||||||
|
bool signal_sync : 1 = false;
|
||||||
|
bool synced : 1 = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Tegra
|
} // namespace Tegra
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,10 @@
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
class System;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Tegra::Engines {
|
namespace Tegra::Engines {
|
||||||
|
|
||||||
enum class EngineTypes : u32 {
|
enum class EngineTypes : u32 {
|
||||||
|
|
@ -28,28 +32,25 @@ public:
|
||||||
virtual ~EngineInterface() = default;
|
virtual ~EngineInterface() = default;
|
||||||
|
|
||||||
/// Write the value to the register identified by method.
|
/// Write the value to the register identified by method.
|
||||||
virtual void CallMethod(u32 method, u32 method_argument, bool is_last_call) = 0;
|
virtual void CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) = 0;
|
||||||
|
|
||||||
/// Write multiple values to the register identified by method.
|
/// Write multiple values to the register identified by method.
|
||||||
virtual void CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
virtual void CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) = 0;
|
||||||
u32 methods_pending) = 0;
|
|
||||||
|
|
||||||
void ConsumeSink() {
|
void ConsumeSink(Core::System& system) {
|
||||||
if (method_sink.empty()) {
|
if (!method_sink.empty()) {
|
||||||
return;
|
ConsumeSinkImpl(system);
|
||||||
}
|
}
|
||||||
ConsumeSinkImpl();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::bitset<(std::numeric_limits<u16>::max)()> execution_mask{};
|
std::bitset<(std::numeric_limits<u16>::max)()> execution_mask{};
|
||||||
std::vector<std::pair<u32, u32>> method_sink{};
|
std::vector<std::pair<u32, u32>> method_sink{};
|
||||||
bool current_dirty{};
|
|
||||||
GPUVAddr current_dma_segment;
|
GPUVAddr current_dma_segment;
|
||||||
|
bool current_dirty{};
|
||||||
protected:
|
protected:
|
||||||
virtual void ConsumeSinkImpl() {
|
virtual void ConsumeSinkImpl(Core::System& system) {
|
||||||
for (auto [method, value] : method_sink) {
|
for (auto [method, value] : method_sink) {
|
||||||
CallMethod(method, value, true);
|
CallMethod(system, method, value, true);
|
||||||
}
|
}
|
||||||
method_sink.clear();
|
method_sink.clear();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,9 +36,8 @@ void Fermi2D::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
|
||||||
rasterizer = rasterizer_;
|
rasterizer = rasterizer_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fermi2D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
|
void Fermi2D::CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) {
|
||||||
ASSERT_MSG(method < Regs::NUM_REGS,
|
ASSERT_MSG(method < Regs::NUM_REGS, "Invalid Fermi2D register, increase the size of the Regs structure");
|
||||||
"Invalid Fermi2D register, increase the size of the Regs structure");
|
|
||||||
regs.reg_array[method] = method_argument;
|
regs.reg_array[method] = method_argument;
|
||||||
|
|
||||||
if (method == FERMI2D_REG_INDEX(pixels_from_memory.src_y0) + 1) {
|
if (method == FERMI2D_REG_INDEX(pixels_from_memory.src_y0) + 1) {
|
||||||
|
|
@ -46,13 +45,13 @@ void Fermi2D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fermi2D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 methods_pending) {
|
void Fermi2D::CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) {
|
||||||
for (u32 i = 0; i < amount; ++i) {
|
for (u32 i = 0; i < amount; ++i) {
|
||||||
CallMethod(method, base_start[i], methods_pending - i <= 1);
|
CallMethod(system, method, base_start[i], methods_pending - i <= 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fermi2D::ConsumeSinkImpl() {
|
void Fermi2D::ConsumeSinkImpl(Core::System& system) {
|
||||||
for (auto [method, value] : method_sink) {
|
for (auto [method, value] : method_sink) {
|
||||||
regs.reg_array[method] = value;
|
regs.reg_array[method] = value;
|
||||||
}
|
}
|
||||||
|
|
@ -60,8 +59,7 @@ void Fermi2D::ConsumeSinkImpl() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fermi2D::Blit() {
|
void Fermi2D::Blit() {
|
||||||
LOG_DEBUG(HW_GPU, "called. source address=0x{:x}, destination address=0x{:x}",
|
LOG_DEBUG(HW_GPU, "called. source address=0x{:x}, destination address=0x{:x}", regs.src.Address(), regs.dst.Address());
|
||||||
regs.src.Address(), regs.dst.Address());
|
|
||||||
|
|
||||||
UNIMPLEMENTED_IF_MSG(regs.operation != Operation::SrcCopy, "Operation is not copy");
|
UNIMPLEMENTED_IF_MSG(regs.operation != Operation::SrcCopy, "Operation is not copy");
|
||||||
UNIMPLEMENTED_IF_MSG(regs.src.layer != 0, "Source layer is not zero");
|
UNIMPLEMENTED_IF_MSG(regs.src.layer != 0, "Source layer is not zero");
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|
@ -44,11 +47,10 @@ public:
|
||||||
void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
|
void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
|
||||||
|
|
||||||
/// Write the value to the register identified by method.
|
/// Write the value to the register identified by method.
|
||||||
void CallMethod(u32 method, u32 method_argument, bool is_last_call) override;
|
void CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) override;
|
||||||
|
|
||||||
/// Write multiple values to the register identified by method.
|
/// Write multiple values to the register identified by method.
|
||||||
void CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
void CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) override;
|
||||||
u32 methods_pending) override;
|
|
||||||
|
|
||||||
enum class Origin : u32 {
|
enum class Origin : u32 {
|
||||||
Center = 0,
|
Center = 0,
|
||||||
|
|
@ -311,7 +313,7 @@ private:
|
||||||
/// registers.
|
/// registers.
|
||||||
void Blit();
|
void Blit();
|
||||||
|
|
||||||
void ConsumeSinkImpl() override;
|
void ConsumeSinkImpl(Core::System& system) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define ASSERT_REG_POSITION(field_name, position) \
|
#define ASSERT_REG_POSITION(field_name, position) \
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,10 @@
|
||||||
|
|
||||||
namespace Tegra::Engines {
|
namespace Tegra::Engines {
|
||||||
|
|
||||||
KeplerCompute::KeplerCompute(Core::System& system_, MemoryManager& memory_manager_)
|
KeplerCompute::KeplerCompute(MemoryManager& memory_manager_)
|
||||||
: system{system_}, memory_manager{memory_manager_}, upload_state{memory_manager, regs.upload} {
|
: memory_manager{memory_manager_}
|
||||||
|
, upload_state{memory_manager, regs.upload}
|
||||||
|
{
|
||||||
execution_mask.reset();
|
execution_mask.reset();
|
||||||
execution_mask[KEPLER_COMPUTE_REG_INDEX(exec_upload)] = true;
|
execution_mask[KEPLER_COMPUTE_REG_INDEX(exec_upload)] = true;
|
||||||
execution_mask[KEPLER_COMPUTE_REG_INDEX(data_upload)] = true;
|
execution_mask[KEPLER_COMPUTE_REG_INDEX(data_upload)] = true;
|
||||||
|
|
@ -31,16 +33,15 @@ void KeplerCompute::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_)
|
||||||
upload_state.BindRasterizer(rasterizer);
|
upload_state.BindRasterizer(rasterizer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeplerCompute::ConsumeSinkImpl() {
|
void KeplerCompute::ConsumeSinkImpl(Core::System& system) {
|
||||||
for (auto [method, value] : method_sink) {
|
for (auto [method, value] : method_sink) {
|
||||||
regs.reg_array[method] = value;
|
regs.reg_array[method] = value;
|
||||||
}
|
}
|
||||||
method_sink.clear();
|
method_sink.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
|
void KeplerCompute::CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) {
|
||||||
ASSERT_MSG(method < Regs::NUM_REGS,
|
ASSERT_MSG(method < Regs::NUM_REGS, "Invalid KeplerCompute register, increase the size of the Regs structure");
|
||||||
"Invalid KeplerCompute register, increase the size of the Regs structure");
|
|
||||||
|
|
||||||
regs.reg_array[method] = method_argument;
|
regs.reg_array[method] = method_argument;
|
||||||
|
|
||||||
|
|
@ -78,8 +79,7 @@ void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_cal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeplerCompute::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
void KeplerCompute::CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) {
|
||||||
u32 methods_pending) {
|
|
||||||
switch (method) {
|
switch (method) {
|
||||||
case KEPLER_COMPUTE_REG_INDEX(data_upload):
|
case KEPLER_COMPUTE_REG_INDEX(data_upload):
|
||||||
upload_address = current_dma_segment;
|
upload_address = current_dma_segment;
|
||||||
|
|
@ -87,7 +87,7 @@ void KeplerCompute::CallMultiMethod(u32 method, const u32* base_start, u32 amoun
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
for (u32 i = 0; i < amount; i++) {
|
for (u32 i = 0; i < amount; i++) {
|
||||||
CallMethod(method, base_start[i], methods_pending - i <= 1);
|
CallMethod(system, method, base_start[i], methods_pending - i <= 1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|
@ -42,7 +45,7 @@ namespace Tegra::Engines {
|
||||||
|
|
||||||
class KeplerCompute final : public EngineInterface {
|
class KeplerCompute final : public EngineInterface {
|
||||||
public:
|
public:
|
||||||
explicit KeplerCompute(Core::System& system, MemoryManager& memory_manager);
|
explicit KeplerCompute(MemoryManager& memory_manager);
|
||||||
~KeplerCompute();
|
~KeplerCompute();
|
||||||
|
|
||||||
/// Binds a rasterizer to this engine.
|
/// Binds a rasterizer to this engine.
|
||||||
|
|
@ -199,11 +202,10 @@ public:
|
||||||
"KeplerCompute LaunchParams has wrong size");
|
"KeplerCompute LaunchParams has wrong size");
|
||||||
|
|
||||||
/// Write the value to the register identified by method.
|
/// Write the value to the register identified by method.
|
||||||
void CallMethod(u32 method, u32 method_argument, bool is_last_call) override;
|
void CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) override;
|
||||||
|
|
||||||
/// Write multiple values to the register identified by method.
|
/// Write multiple values to the register identified by method.
|
||||||
void CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
void CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) override;
|
||||||
u32 methods_pending) override;
|
|
||||||
|
|
||||||
std::optional<GPUVAddr> GetIndirectComputeAddress() const {
|
std::optional<GPUVAddr> GetIndirectComputeAddress() const {
|
||||||
return indirect_compute;
|
return indirect_compute;
|
||||||
|
|
@ -212,7 +214,7 @@ public:
|
||||||
private:
|
private:
|
||||||
void ProcessLaunch();
|
void ProcessLaunch();
|
||||||
|
|
||||||
void ConsumeSinkImpl() override;
|
void ConsumeSinkImpl(Core::System& system) override;
|
||||||
|
|
||||||
/// Retrieves information about a specific TIC entry from the TIC buffer.
|
/// Retrieves information about a specific TIC entry from the TIC buffer.
|
||||||
Texture::TICEntry GetTICEntry(u32 tic_index) const;
|
Texture::TICEntry GetTICEntry(u32 tic_index) const;
|
||||||
|
|
@ -220,7 +222,6 @@ private:
|
||||||
/// Retrieves information about a specific TSC entry from the TSC buffer.
|
/// Retrieves information about a specific TSC entry from the TSC buffer.
|
||||||
Texture::TSCEntry GetTSCEntry(u32 tsc_index) const;
|
Texture::TSCEntry GetTSCEntry(u32 tsc_index) const;
|
||||||
|
|
||||||
Core::System& system;
|
|
||||||
MemoryManager& memory_manager;
|
MemoryManager& memory_manager;
|
||||||
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
||||||
Upload::State upload_state;
|
Upload::State upload_state;
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,9 @@
|
||||||
|
|
||||||
namespace Tegra::Engines {
|
namespace Tegra::Engines {
|
||||||
|
|
||||||
KeplerMemory::KeplerMemory(Core::System& system_, MemoryManager& memory_manager)
|
KeplerMemory::KeplerMemory(MemoryManager& memory_manager)
|
||||||
: system{system_}, upload_state{memory_manager, regs.upload} {}
|
: upload_state{memory_manager, regs.upload}
|
||||||
|
{}
|
||||||
|
|
||||||
KeplerMemory::~KeplerMemory() = default;
|
KeplerMemory::~KeplerMemory() = default;
|
||||||
|
|
||||||
|
|
@ -27,16 +28,15 @@ void KeplerMemory::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
|
||||||
execution_mask[KEPLERMEMORY_REG_INDEX(data)] = true;
|
execution_mask[KEPLERMEMORY_REG_INDEX(data)] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeplerMemory::ConsumeSinkImpl() {
|
void KeplerMemory::ConsumeSinkImpl(Core::System& system) {
|
||||||
for (auto [method, value] : method_sink) {
|
for (auto [method, value] : method_sink) {
|
||||||
regs.reg_array[method] = value;
|
regs.reg_array[method] = value;
|
||||||
}
|
}
|
||||||
method_sink.clear();
|
method_sink.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeplerMemory::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
|
void KeplerMemory::CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) {
|
||||||
ASSERT_MSG(method < Regs::NUM_REGS,
|
ASSERT_MSG(method < Regs::NUM_REGS, "Invalid KeplerMemory register, increase the size of the Regs structure");
|
||||||
"Invalid KeplerMemory register, increase the size of the Regs structure");
|
|
||||||
|
|
||||||
regs.reg_array[method] = method_argument;
|
regs.reg_array[method] = method_argument;
|
||||||
|
|
||||||
|
|
@ -52,15 +52,14 @@ void KeplerMemory::CallMethod(u32 method, u32 method_argument, bool is_last_call
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeplerMemory::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
void KeplerMemory::CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) {
|
||||||
u32 methods_pending) {
|
|
||||||
switch (method) {
|
switch (method) {
|
||||||
case KEPLERMEMORY_REG_INDEX(data):
|
case KEPLERMEMORY_REG_INDEX(data):
|
||||||
upload_state.ProcessData(base_start, amount);
|
upload_state.ProcessData(base_start, amount);
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
for (u32 i = 0; i < amount; i++) {
|
for (u32 i = 0; i < amount; i++) {
|
||||||
CallMethod(method, base_start[i], methods_pending - i <= 1);
|
CallMethod(system, method, base_start[i], methods_pending - i <= 1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|
@ -36,18 +39,17 @@ namespace Tegra::Engines {
|
||||||
|
|
||||||
class KeplerMemory final : public EngineInterface {
|
class KeplerMemory final : public EngineInterface {
|
||||||
public:
|
public:
|
||||||
explicit KeplerMemory(Core::System& system_, MemoryManager& memory_manager);
|
explicit KeplerMemory(MemoryManager& memory_manager);
|
||||||
~KeplerMemory() override;
|
~KeplerMemory() override;
|
||||||
|
|
||||||
/// Binds a rasterizer to this engine.
|
/// Binds a rasterizer to this engine.
|
||||||
void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
|
void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
|
||||||
|
|
||||||
/// Write the value to the register identified by method.
|
/// Write the value to the register identified by method.
|
||||||
void CallMethod(u32 method, u32 method_argument, bool is_last_call) override;
|
void CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) override;
|
||||||
|
|
||||||
/// Write multiple values to the register identified by method.
|
/// Write multiple values to the register identified by method.
|
||||||
void CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
void CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) override;
|
||||||
u32 methods_pending) override;
|
|
||||||
|
|
||||||
struct Regs {
|
struct Regs {
|
||||||
static constexpr size_t NUM_REGS = 0x7F;
|
static constexpr size_t NUM_REGS = 0x7F;
|
||||||
|
|
@ -73,9 +75,7 @@ public:
|
||||||
} regs{};
|
} regs{};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void ConsumeSinkImpl() override;
|
void ConsumeSinkImpl(Core::System& system) override;
|
||||||
|
|
||||||
Core::System& system;
|
|
||||||
Upload::State upload_state;
|
Upload::State upload_state;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,8 @@ namespace Tegra::Engines {
|
||||||
/// First register id that is actually a Macro call.
|
/// First register id that is actually a Macro call.
|
||||||
constexpr u32 MacroRegistersStart = 0xE00;
|
constexpr u32 MacroRegistersStart = 0xE00;
|
||||||
|
|
||||||
Maxwell3D::Maxwell3D(Core::System& system_, MemoryManager& memory_manager_)
|
Maxwell3D::Maxwell3D(MemoryManager& memory_manager_)
|
||||||
: draw_manager()
|
: draw_manager()
|
||||||
, system{system_}
|
|
||||||
, memory_manager{memory_manager_}
|
, memory_manager{memory_manager_}
|
||||||
#ifdef ARCHITECTURE_x86_64
|
#ifdef ARCHITECTURE_x86_64
|
||||||
, macro_engine(bool(Settings::values.disable_macro_jit))
|
, macro_engine(bool(Settings::values.disable_macro_jit))
|
||||||
|
|
@ -201,11 +200,10 @@ bool Maxwell3D::IsMethodExecutable(u32 method) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) {
|
void Maxwell3D::ProcessMacro(Core::System& system, u32 method, const u32* base_start, u32 amount, bool is_last_call) {
|
||||||
if (executing_macro == 0) {
|
if (executing_macro == 0) {
|
||||||
// A macro call must begin by writing the macro method's register, not its argument.
|
// A macro call must begin by writing the macro method's register, not its argument.
|
||||||
ASSERT_MSG((method % 2) == 0,
|
ASSERT((method % 2) == 0 && "Can't start macro execution by writing to the ARGS register");
|
||||||
"Can't start macro execution by writing to the ARGS register");
|
|
||||||
executing_macro = method;
|
executing_macro = method;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -219,8 +217,8 @@ void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool
|
||||||
|
|
||||||
// Call the macro when there are no more parameters in the command buffer
|
// Call the macro when there are no more parameters in the command buffer
|
||||||
if (is_last_call) {
|
if (is_last_call) {
|
||||||
ConsumeSink();
|
ConsumeSink(system);
|
||||||
CallMacroMethod(executing_macro, macro_params);
|
CallMacroMethod(system, executing_macro, macro_params);
|
||||||
macro_params.clear();
|
macro_params.clear();
|
||||||
macro_addresses.clear();
|
macro_addresses.clear();
|
||||||
macro_segments.clear();
|
macro_segments.clear();
|
||||||
|
|
@ -287,7 +285,7 @@ u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) {
|
||||||
return argument;
|
return argument;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Maxwell3D::ConsumeSinkImpl() {
|
void Maxwell3D::ConsumeSinkImpl(Core::System& system) {
|
||||||
const auto control = shadow_state.shadow_ram_control;
|
const auto control = shadow_state.shadow_ram_control;
|
||||||
if (control == Regs::ShadowRamControl::Track || control == Regs::ShadowRamControl::TrackWithFilter) {
|
if (control == Regs::ShadowRamControl::Track || control == Regs::ShadowRamControl::TrackWithFilter) {
|
||||||
for (auto [method, value] : method_sink) {
|
for (auto [method, value] : method_sink) {
|
||||||
|
|
@ -378,7 +376,7 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters) {
|
void Maxwell3D::CallMacroMethod(Core::System& system, u32 method, const std::vector<u32>& parameters) {
|
||||||
// Reset the current macro.
|
// Reset the current macro.
|
||||||
executing_macro = 0;
|
executing_macro = 0;
|
||||||
|
|
||||||
|
|
@ -387,11 +385,11 @@ void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters)
|
||||||
((method - MacroRegistersStart) >> 1) % static_cast<u32>(macro_positions.size());
|
((method - MacroRegistersStart) >> 1) % static_cast<u32>(macro_positions.size());
|
||||||
|
|
||||||
// Execute the current macro.
|
// Execute the current macro.
|
||||||
macro_engine.Execute(*this, macro_positions[entry], parameters);
|
macro_engine.Execute(system, *this, macro_positions[entry], parameters);
|
||||||
draw_manager.DrawDeferred(*this);
|
draw_manager.DrawDeferred(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
|
void Maxwell3D::CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) {
|
||||||
// It is an error to write to a register other than the current macro's ARG register before
|
// It is an error to write to a register other than the current macro's ARG register before
|
||||||
// it has finished execution.
|
// it has finished execution.
|
||||||
if (executing_macro != 0) {
|
if (executing_macro != 0) {
|
||||||
|
|
@ -401,7 +399,7 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
|
||||||
// Methods after 0xE00 are special, they're actually triggers for some microcode that was
|
// Methods after 0xE00 are special, they're actually triggers for some microcode that was
|
||||||
// uploaded to the GPU during initialization.
|
// uploaded to the GPU during initialization.
|
||||||
if (method >= MacroRegistersStart) {
|
if (method >= MacroRegistersStart) {
|
||||||
ProcessMacro(method, &method_argument, 1, is_last_call);
|
ProcessMacro(system, method, &method_argument, 1, is_last_call);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -411,12 +409,11 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
|
||||||
ProcessMethodCall(method, argument, method_argument, is_last_call);
|
ProcessMethodCall(method, argument, method_argument, is_last_call);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
void Maxwell3D::CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) {
|
||||||
u32 methods_pending) {
|
|
||||||
// Methods after 0xE00 are special, they're actually triggers for some microcode that was
|
// Methods after 0xE00 are special, they're actually triggers for some microcode that was
|
||||||
// uploaded to the GPU during initialization.
|
// uploaded to the GPU during initialization.
|
||||||
if (method >= MacroRegistersStart) {
|
if (method >= MacroRegistersStart) {
|
||||||
ProcessMacro(method, base_start, amount, amount == methods_pending);
|
ProcessMacro(system, method, base_start, amount, amount == methods_pending);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch (method) {
|
switch (method) {
|
||||||
|
|
@ -445,7 +442,7 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
for (u32 i = 0; i < amount; i++) {
|
for (u32 i = 0; i < amount; i++) {
|
||||||
CallMethod(method, base_start[i], methods_pending - i <= 1);
|
CallMethod(system, method, base_start[i], methods_pending - i <= 1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -467,7 +464,7 @@ void Maxwell3D::ProcessFirmwareCall4() {
|
||||||
regs.shadow_scratch[0] = 1;
|
regs.shadow_scratch[0] = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Maxwell3D::StampQueryResult(u64 payload, bool long_query) {
|
void Maxwell3D::StampQueryResult(Core::System& system, u64 payload, bool long_query) {
|
||||||
const GPUVAddr sequence_address{regs.report_semaphore.Address()};
|
const GPUVAddr sequence_address{regs.report_semaphore.Address()};
|
||||||
if (long_query) {
|
if (long_query) {
|
||||||
memory_manager.Write<u64>(sequence_address + sizeof(u64), system.GPU().GetTicks());
|
memory_manager.Write<u64>(sequence_address + sizeof(u64), system.GPU().GetTicks());
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ namespace Tegra::Engines {
|
||||||
|
|
||||||
class Maxwell3D final : public EngineInterface {
|
class Maxwell3D final : public EngineInterface {
|
||||||
public:
|
public:
|
||||||
explicit Maxwell3D(Core::System& system, MemoryManager& memory_manager);
|
explicit Maxwell3D(MemoryManager& memory_manager);
|
||||||
~Maxwell3D();
|
~Maxwell3D();
|
||||||
|
|
||||||
/// Binds a rasterizer to this engine.
|
/// Binds a rasterizer to this engine.
|
||||||
|
|
@ -3129,11 +3129,10 @@ public:
|
||||||
u32 GetRegisterValue(u32 method) const;
|
u32 GetRegisterValue(u32 method) const;
|
||||||
|
|
||||||
/// Write the value to the register identified by method.
|
/// Write the value to the register identified by method.
|
||||||
void CallMethod(u32 method, u32 method_argument, bool is_last_call) override;
|
void CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) override;
|
||||||
|
|
||||||
/// Write multiple values to the register identified by method.
|
/// Write multiple values to the register identified by method.
|
||||||
void CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
void CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) override;
|
||||||
u32 methods_pending) override;
|
|
||||||
|
|
||||||
bool ShouldExecute() const {
|
bool ShouldExecute() const {
|
||||||
return execute_on;
|
return execute_on;
|
||||||
|
|
@ -3190,13 +3189,13 @@ public:
|
||||||
private:
|
private:
|
||||||
void InitializeRegisterDefaults();
|
void InitializeRegisterDefaults();
|
||||||
|
|
||||||
void ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call);
|
void ProcessMacro(Core::System& system, u32 method, const u32* base_start, u32 amount, bool is_last_call);
|
||||||
|
|
||||||
u32 ProcessShadowRam(u32 method, u32 argument);
|
u32 ProcessShadowRam(u32 method, u32 argument);
|
||||||
|
|
||||||
void ProcessDirtyRegisters(u32 method, u32 argument);
|
void ProcessDirtyRegisters(u32 method, u32 argument);
|
||||||
|
|
||||||
void ConsumeSinkImpl() override;
|
void ConsumeSinkImpl(Core::System& system) override;
|
||||||
|
|
||||||
void ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argument, bool is_last_call);
|
void ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argument, bool is_last_call);
|
||||||
|
|
||||||
|
|
@ -3212,7 +3211,7 @@ private:
|
||||||
* @param method Method to call
|
* @param method Method to call
|
||||||
* @param parameters Arguments to the method call
|
* @param parameters Arguments to the method call
|
||||||
*/
|
*/
|
||||||
void CallMacroMethod(u32 method, const std::vector<u32>& parameters);
|
void CallMacroMethod(Core::System& system, u32 method, const std::vector<u32>& parameters);
|
||||||
|
|
||||||
/// Handles writes to the macro uploading register.
|
/// Handles writes to the macro uploading register.
|
||||||
void ProcessMacroUpload(u32 data);
|
void ProcessMacroUpload(u32 data);
|
||||||
|
|
@ -3227,7 +3226,7 @@ private:
|
||||||
void ProcessQueryGet();
|
void ProcessQueryGet();
|
||||||
|
|
||||||
/// Writes the query result accordingly.
|
/// Writes the query result accordingly.
|
||||||
void StampQueryResult(u64 payload, bool long_query);
|
void StampQueryResult(Core::System& system, u64 payload, bool long_query);
|
||||||
|
|
||||||
/// Handles conditional rendering.
|
/// Handles conditional rendering.
|
||||||
void ProcessQueryCondition();
|
void ProcessQueryCondition();
|
||||||
|
|
@ -3242,7 +3241,6 @@ private:
|
||||||
|
|
||||||
bool IsMethodExecutable(u32 method);
|
bool IsMethodExecutable(u32 method);
|
||||||
|
|
||||||
Core::System& system;
|
|
||||||
MemoryManager& memory_manager;
|
MemoryManager& memory_manager;
|
||||||
|
|
||||||
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,9 @@ namespace Tegra::Engines {
|
||||||
|
|
||||||
using namespace Texture;
|
using namespace Texture;
|
||||||
|
|
||||||
MaxwellDMA::MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_)
|
MaxwellDMA::MaxwellDMA(MemoryManager& memory_manager_)
|
||||||
: system{system_}, memory_manager{memory_manager_} {
|
: memory_manager{memory_manager_}
|
||||||
|
{
|
||||||
execution_mask.reset();
|
execution_mask.reset();
|
||||||
execution_mask[offsetof(Regs, launch_dma) / sizeof(u32)] = true;
|
execution_mask[offsetof(Regs, launch_dma) / sizeof(u32)] = true;
|
||||||
}
|
}
|
||||||
|
|
@ -33,14 +34,14 @@ void MaxwellDMA::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
|
||||||
rasterizer = rasterizer_;
|
rasterizer = rasterizer_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaxwellDMA::ConsumeSinkImpl() {
|
void MaxwellDMA::ConsumeSinkImpl(Core::System& system) {
|
||||||
for (auto [method, value] : method_sink) {
|
for (auto [method, value] : method_sink) {
|
||||||
regs.reg_array[method] = value;
|
regs.reg_array[method] = value;
|
||||||
}
|
}
|
||||||
method_sink.clear();
|
method_sink.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
|
void MaxwellDMA::CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) {
|
||||||
ASSERT_MSG(method < NUM_REGS, "Invalid MaxwellDMA register");
|
ASSERT_MSG(method < NUM_REGS, "Invalid MaxwellDMA register");
|
||||||
|
|
||||||
regs.reg_array[method] = method_argument;
|
regs.reg_array[method] = method_argument;
|
||||||
|
|
@ -50,16 +51,14 @@ void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaxwellDMA::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
void MaxwellDMA::CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) {
|
||||||
u32 methods_pending) {
|
|
||||||
for (u32 i = 0; i < amount; ++i) {
|
for (u32 i = 0; i < amount; ++i) {
|
||||||
CallMethod(method, base_start[i], methods_pending - i <= 1);
|
CallMethod(system, method, base_start[i], methods_pending - i <= 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaxwellDMA::Launch() {
|
void MaxwellDMA::Launch() {
|
||||||
LOG_TRACE(Render_OpenGL, "DMA copy 0x{:x} -> 0x{:x}", static_cast<GPUVAddr>(regs.offset_in),
|
LOG_TRACE(Render_OpenGL, "DMA copy 0x{:x} -> 0x{:x}", static_cast<GPUVAddr>(regs.offset_in), GPUVAddr(regs.offset_out));
|
||||||
static_cast<GPUVAddr>(regs.offset_out));
|
|
||||||
|
|
||||||
// TODO(Subv): Perform more research and implement all features of this engine.
|
// TODO(Subv): Perform more research and implement all features of this engine.
|
||||||
const LaunchDMA& launch = regs.launch_dma;
|
const LaunchDMA& launch = regs.launch_dma;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|
@ -238,15 +241,14 @@ public:
|
||||||
|
|
||||||
void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
|
void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
|
||||||
|
|
||||||
explicit MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_);
|
explicit MaxwellDMA(MemoryManager& memory_manager_);
|
||||||
~MaxwellDMA() override;
|
~MaxwellDMA() override;
|
||||||
|
|
||||||
/// Write the value to the register identified by method.
|
/// Write the value to the register identified by method.
|
||||||
void CallMethod(u32 method, u32 method_argument, bool is_last_call) override;
|
void CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) override;
|
||||||
|
|
||||||
/// Write multiple values to the register identified by method.
|
/// Write multiple values to the register identified by method.
|
||||||
void CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
void CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) override;
|
||||||
u32 methods_pending) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Performs the copy from the source buffer to the destination buffer as configured in the
|
/// Performs the copy from the source buffer to the destination buffer as configured in the
|
||||||
|
|
@ -261,9 +263,7 @@ private:
|
||||||
|
|
||||||
void ReleaseSemaphore();
|
void ReleaseSemaphore();
|
||||||
|
|
||||||
void ConsumeSinkImpl() override;
|
void ConsumeSinkImpl(Core::System& system) override;
|
||||||
|
|
||||||
Core::System& system;
|
|
||||||
|
|
||||||
MemoryManager& memory_manager;
|
MemoryManager& memory_manager;
|
||||||
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
||||||
|
|
|
||||||
|
|
@ -26,18 +26,16 @@ class MemoryManager;
|
||||||
namespace Tegra::Engines {
|
namespace Tegra::Engines {
|
||||||
class Nv01Timer final : public EngineInterface {
|
class Nv01Timer final : public EngineInterface {
|
||||||
public:
|
public:
|
||||||
explicit Nv01Timer(Core::System& system_, MemoryManager& memory_manager)
|
explicit Nv01Timer(MemoryManager& memory_manager) noexcept {}
|
||||||
: system{system_}
|
~Nv01Timer() noexcept override {}
|
||||||
{}
|
|
||||||
~Nv01Timer() override;
|
|
||||||
|
|
||||||
/// Write the value to the register identified by method.
|
/// Write the value to the register identified by method.
|
||||||
void CallMethod(u32 method, u32 method_argument, bool is_last_call) override {
|
void CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) override {
|
||||||
LOG_DEBUG(HW_GPU, "method={}, argument={}, is_last_call={}", method, method_argument, is_last_call);
|
LOG_DEBUG(HW_GPU, "method={}, argument={}, is_last_call={}", method, method_argument, is_last_call);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write multiple values to the register identified by method.
|
/// Write multiple values to the register identified by method.
|
||||||
void CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 methods_pending) override {
|
void CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) override {
|
||||||
LOG_DEBUG(HW_GPU, "method={}, base_start={}, amount={}, pending={}", method, fmt::ptr(base_start), amount, methods_pending);
|
LOG_DEBUG(HW_GPU, "method={}, base_start={}, amount={}, pending={}", method, fmt::ptr(base_start), amount, methods_pending);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,7 +44,6 @@ public:
|
||||||
INSERT_PADDING_BYTES_NOINIT(0x48);
|
INSERT_PADDING_BYTES_NOINIT(0x48);
|
||||||
} regs{};
|
} regs{};
|
||||||
private:
|
private:
|
||||||
void ConsumeSinkImpl() override {}
|
void ConsumeSinkImpl(Core::System& system) override {}
|
||||||
Core::System& system;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,37 +22,29 @@
|
||||||
|
|
||||||
namespace Tegra::Engines {
|
namespace Tegra::Engines {
|
||||||
|
|
||||||
Puller::Puller(GPU& gpu_, MemoryManager& memory_manager_, DmaPusher& dma_pusher_,
|
void Puller::ProcessBindMethod(DmaPusher& dma_pusher, const MethodCall& method_call) {
|
||||||
Control::ChannelState& channel_state_)
|
|
||||||
: gpu{gpu_}, memory_manager{memory_manager_}, dma_pusher{dma_pusher_}, channel_state{
|
|
||||||
channel_state_} {}
|
|
||||||
|
|
||||||
Puller::~Puller() = default;
|
|
||||||
|
|
||||||
void Puller::ProcessBindMethod(const MethodCall& method_call) {
|
|
||||||
// Bind the current subchannel to the desired engine id.
|
// Bind the current subchannel to the desired engine id.
|
||||||
LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel,
|
LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel, method_call.argument);
|
||||||
method_call.argument);
|
|
||||||
const auto engine_id = static_cast<EngineID>(method_call.argument);
|
const auto engine_id = static_cast<EngineID>(method_call.argument);
|
||||||
bound_engines[method_call.subchannel] = engine_id;
|
bound_engines[method_call.subchannel] = engine_id;
|
||||||
switch (engine_id) {
|
switch (engine_id) {
|
||||||
case EngineID::FERMI_TWOD_A:
|
case EngineID::FERMI_TWOD_A:
|
||||||
dma_pusher.BindSubchannel(&*channel_state.fermi_2d, method_call.subchannel, EngineTypes::Fermi2D);
|
dma_pusher.BindSubchannel(&dma_pusher.channel_state.payload->fermi_2d, method_call.subchannel, EngineTypes::Fermi2D);
|
||||||
break;
|
break;
|
||||||
case EngineID::MAXWELL_B:
|
case EngineID::MAXWELL_B:
|
||||||
dma_pusher.BindSubchannel(&*channel_state.maxwell_3d, method_call.subchannel, EngineTypes::Maxwell3D);
|
dma_pusher.BindSubchannel(&dma_pusher.channel_state.payload->maxwell_3d, method_call.subchannel, EngineTypes::Maxwell3D);
|
||||||
break;
|
break;
|
||||||
case EngineID::KEPLER_COMPUTE_B:
|
case EngineID::KEPLER_COMPUTE_B:
|
||||||
dma_pusher.BindSubchannel(&*channel_state.kepler_compute, method_call.subchannel, EngineTypes::KeplerCompute);
|
dma_pusher.BindSubchannel(&dma_pusher.channel_state.payload->kepler_compute, method_call.subchannel, EngineTypes::KeplerCompute);
|
||||||
break;
|
break;
|
||||||
case EngineID::MAXWELL_DMA_COPY_A:
|
case EngineID::MAXWELL_DMA_COPY_A:
|
||||||
dma_pusher.BindSubchannel(&*channel_state.maxwell_dma, method_call.subchannel, EngineTypes::MaxwellDMA);
|
dma_pusher.BindSubchannel(&dma_pusher.channel_state.payload->maxwell_dma, method_call.subchannel, EngineTypes::MaxwellDMA);
|
||||||
break;
|
break;
|
||||||
case EngineID::KEPLER_INLINE_TO_MEMORY_B:
|
case EngineID::KEPLER_INLINE_TO_MEMORY_B:
|
||||||
dma_pusher.BindSubchannel(&*channel_state.kepler_memory, method_call.subchannel, EngineTypes::KeplerMemory);
|
dma_pusher.BindSubchannel(&dma_pusher.channel_state.payload->kepler_memory, method_call.subchannel, EngineTypes::KeplerMemory);
|
||||||
break;
|
break;
|
||||||
case EngineID::NV01_TIMER:
|
case EngineID::NV01_TIMER:
|
||||||
dma_pusher.BindSubchannel(&*channel_state.nv01_timer, method_call.subchannel, EngineTypes::Nv01Timer);
|
dma_pusher.BindSubchannel(&dma_pusher.channel_state.payload->nv01_timer, method_call.subchannel, EngineTypes::Nv01Timer);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id);
|
UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id);
|
||||||
|
|
@ -60,15 +52,15 @@ void Puller::ProcessBindMethod(const MethodCall& method_call) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Puller::ProcessFenceActionMethod() {
|
void Puller::ProcessFenceActionMethod(DmaPusher& dma_pusher) {
|
||||||
switch (regs.fence_action.op) {
|
switch (regs.fence_action.op) {
|
||||||
case Puller::FenceOperation::Acquire:
|
case Puller::FenceOperation::Acquire:
|
||||||
// UNIMPLEMENTED_MSG("Channel Scheduling pending.");
|
// UNIMPLEMENTED_MSG("Channel Scheduling pending.");
|
||||||
// WaitFence(regs.fence_action.syncpoint_id, regs.fence_value);
|
// WaitFence(regs.fence_action.syncpoint_id, regs.fence_value);
|
||||||
rasterizer->ReleaseFences();
|
dma_pusher.rasterizer->ReleaseFences();
|
||||||
break;
|
break;
|
||||||
case Puller::FenceOperation::Increment:
|
case Puller::FenceOperation::Increment:
|
||||||
rasterizer->SignalSyncPoint(regs.fence_action.syncpoint_id);
|
dma_pusher.rasterizer->SignalSyncPoint(regs.fence_action.syncpoint_id);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Unimplemented operation {}", regs.fence_action.op.Value());
|
UNIMPLEMENTED_MSG("Unimplemented operation {}", regs.fence_action.op.Value());
|
||||||
|
|
@ -76,37 +68,35 @@ void Puller::ProcessFenceActionMethod() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Puller::ProcessSemaphoreTriggerMethod() {
|
void Puller::ProcessSemaphoreTriggerMethod(DmaPusher& dma_pusher) {
|
||||||
const auto semaphoreOperationMask = 0xF;
|
const auto semaphoreOperationMask = 0xF;
|
||||||
const auto op =
|
const auto op = GpuSemaphoreOperation(regs.semaphore_trigger & semaphoreOperationMask);
|
||||||
static_cast<GpuSemaphoreOperation>(regs.semaphore_trigger & semaphoreOperationMask);
|
|
||||||
if (op == GpuSemaphoreOperation::WriteLong) {
|
if (op == GpuSemaphoreOperation::WriteLong) {
|
||||||
const GPUVAddr sequence_address{regs.semaphore_address.SemaphoreAddress()};
|
const GPUVAddr sequence_address{regs.semaphore_address.SemaphoreAddress()};
|
||||||
const u32 payload = regs.semaphore_sequence;
|
const u32 payload = regs.semaphore_sequence;
|
||||||
rasterizer->Query(sequence_address, VideoCommon::QueryType::Payload,
|
dma_pusher.rasterizer->Query(sequence_address, VideoCommon::QueryType::Payload, VideoCommon::QueryPropertiesFlags::HasTimeout, payload, 0);
|
||||||
VideoCommon::QueryPropertiesFlags::HasTimeout, payload, 0);
|
|
||||||
} else {
|
} else {
|
||||||
do {
|
do {
|
||||||
const u32 word{memory_manager.Read<u32>(regs.semaphore_address.SemaphoreAddress())};
|
const u32 word = dma_pusher.memory_manager.Read<u32>(regs.semaphore_address.SemaphoreAddress());
|
||||||
regs.acquire_source = true;
|
regs.acquire_source = true;
|
||||||
regs.acquire_value = regs.semaphore_sequence;
|
regs.acquire_value = regs.semaphore_sequence;
|
||||||
if (op == GpuSemaphoreOperation::AcquireEqual) {
|
if (op == GpuSemaphoreOperation::AcquireEqual) {
|
||||||
regs.acquire_active = true;
|
regs.acquire_active = true;
|
||||||
regs.acquire_mode = false;
|
regs.acquire_mode = false;
|
||||||
if (word != regs.acquire_value) {
|
if (word != regs.acquire_value) {
|
||||||
rasterizer->ReleaseFences();
|
dma_pusher.rasterizer->ReleaseFences();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else if (op == GpuSemaphoreOperation::AcquireGequal) {
|
} else if (op == GpuSemaphoreOperation::AcquireGequal) {
|
||||||
regs.acquire_active = true;
|
regs.acquire_active = true;
|
||||||
regs.acquire_mode = true;
|
regs.acquire_mode = true;
|
||||||
if (word < regs.acquire_value) {
|
if (word < regs.acquire_value) {
|
||||||
rasterizer->ReleaseFences();
|
dma_pusher.rasterizer->ReleaseFences();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else if (op == GpuSemaphoreOperation::AcquireMask) {
|
} else if (op == GpuSemaphoreOperation::AcquireMask) {
|
||||||
if (word && regs.semaphore_sequence == 0) {
|
if (word && regs.semaphore_sequence == 0) {
|
||||||
rasterizer->ReleaseFences();
|
dma_pusher.rasterizer->ReleaseFences();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -116,21 +106,20 @@ void Puller::ProcessSemaphoreTriggerMethod() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Puller::ProcessSemaphoreRelease() {
|
void Puller::ProcessSemaphoreRelease(DmaPusher& dma_pusher) {
|
||||||
const GPUVAddr sequence_address{regs.semaphore_address.SemaphoreAddress()};
|
const GPUVAddr sequence_address{regs.semaphore_address.SemaphoreAddress()};
|
||||||
const u32 payload = regs.semaphore_release;
|
const u32 payload = regs.semaphore_release;
|
||||||
rasterizer->Query(sequence_address, VideoCommon::QueryType::Payload,
|
dma_pusher.rasterizer->Query(sequence_address, VideoCommon::QueryType::Payload, VideoCommon::QueryPropertiesFlags::IsAFence, payload, 0);
|
||||||
VideoCommon::QueryPropertiesFlags::IsAFence, payload, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Puller::ProcessSemaphoreAcquire() {
|
void Puller::ProcessSemaphoreAcquire(DmaPusher& dma_pusher) {
|
||||||
u32 word = memory_manager.Read<u32>(regs.semaphore_address.SemaphoreAddress());
|
u32 word = dma_pusher.memory_manager.Read<u32>(regs.semaphore_address.SemaphoreAddress());
|
||||||
const auto value = regs.semaphore_acquire;
|
const auto value = regs.semaphore_acquire;
|
||||||
while (word != value) {
|
while (word != value) {
|
||||||
regs.acquire_active = true;
|
regs.acquire_active = true;
|
||||||
regs.acquire_value = value;
|
regs.acquire_value = value;
|
||||||
rasterizer->ReleaseFences();
|
dma_pusher.rasterizer->ReleaseFences();
|
||||||
word = memory_manager.Read<u32>(regs.semaphore_address.SemaphoreAddress());
|
word = dma_pusher.memory_manager.Read<u32>(regs.semaphore_address.SemaphoreAddress());
|
||||||
// TODO(kemathe73) figure out how to do the acquire_timeout
|
// TODO(kemathe73) figure out how to do the acquire_timeout
|
||||||
regs.acquire_mode = false;
|
regs.acquire_mode = false;
|
||||||
regs.acquire_source = false;
|
regs.acquire_source = false;
|
||||||
|
|
@ -138,13 +127,13 @@ void Puller::ProcessSemaphoreAcquire() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calls a GPU puller method.
|
/// Calls a GPU puller method.
|
||||||
void Puller::CallPullerMethod(const MethodCall& method_call) {
|
void Puller::CallPullerMethod(DmaPusher& dma_pusher, const MethodCall& method_call) {
|
||||||
regs.reg_array[method_call.method] = method_call.argument;
|
regs.reg_array[method_call.method] = method_call.argument;
|
||||||
const auto method = static_cast<BufferMethods>(method_call.method);
|
const auto method = static_cast<BufferMethods>(method_call.method);
|
||||||
|
|
||||||
switch (method) {
|
switch (method) {
|
||||||
case BufferMethods::BindObject: {
|
case BufferMethods::BindObject: {
|
||||||
ProcessBindMethod(method_call);
|
ProcessBindMethod(dma_pusher, method_call);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case BufferMethods::Nop:
|
case BufferMethods::Nop:
|
||||||
|
|
@ -155,16 +144,16 @@ void Puller::CallPullerMethod(const MethodCall& method_call) {
|
||||||
case BufferMethods::WrcacheFlush:
|
case BufferMethods::WrcacheFlush:
|
||||||
break;
|
break;
|
||||||
case BufferMethods::RefCnt:
|
case BufferMethods::RefCnt:
|
||||||
rasterizer->SignalReference();
|
dma_pusher.rasterizer->SignalReference();
|
||||||
break;
|
break;
|
||||||
case BufferMethods::SyncpointOperation:
|
case BufferMethods::SyncpointOperation:
|
||||||
ProcessFenceActionMethod();
|
ProcessFenceActionMethod(dma_pusher);
|
||||||
break;
|
break;
|
||||||
case BufferMethods::WaitForIdle:
|
case BufferMethods::WaitForIdle:
|
||||||
rasterizer->WaitForIdle();
|
dma_pusher.rasterizer->WaitForIdle();
|
||||||
break;
|
break;
|
||||||
case BufferMethods::SemaphoreOperation: {
|
case BufferMethods::SemaphoreOperation: {
|
||||||
ProcessSemaphoreTriggerMethod();
|
ProcessSemaphoreTriggerMethod(dma_pusher);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case BufferMethods::NonStallInterrupt: {
|
case BufferMethods::NonStallInterrupt: {
|
||||||
|
|
@ -177,7 +166,7 @@ void Puller::CallPullerMethod(const MethodCall& method_call) {
|
||||||
}
|
}
|
||||||
case BufferMethods::MemOpB: {
|
case BufferMethods::MemOpB: {
|
||||||
// Implement this better.
|
// Implement this better.
|
||||||
rasterizer->InvalidateGPUCache();
|
dma_pusher.rasterizer->InvalidateGPUCache();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case BufferMethods::MemOpC:
|
case BufferMethods::MemOpC:
|
||||||
|
|
@ -186,11 +175,11 @@ void Puller::CallPullerMethod(const MethodCall& method_call) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case BufferMethods::SemaphoreAcquire: {
|
case BufferMethods::SemaphoreAcquire: {
|
||||||
ProcessSemaphoreAcquire();
|
ProcessSemaphoreAcquire(dma_pusher);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case BufferMethods::SemaphoreRelease: {
|
case BufferMethods::SemaphoreRelease: {
|
||||||
ProcessSemaphoreRelease();
|
ProcessSemaphoreRelease(dma_pusher);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case BufferMethods::Yield: {
|
case BufferMethods::Yield: {
|
||||||
|
|
@ -205,27 +194,26 @@ void Puller::CallPullerMethod(const MethodCall& method_call) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calls a GPU engine method.
|
/// Calls a GPU engine method.
|
||||||
void Puller::CallEngineMethod(const MethodCall& method_call) {
|
void Puller::CallEngineMethod(DmaPusher& dma_pusher, const MethodCall& method_call) {
|
||||||
const EngineID engine = bound_engines[method_call.subchannel];
|
const EngineID engine = bound_engines[method_call.subchannel];
|
||||||
|
|
||||||
switch (engine) {
|
switch (engine) {
|
||||||
case EngineID::FERMI_TWOD_A:
|
case EngineID::FERMI_TWOD_A:
|
||||||
channel_state.fermi_2d->CallMethod(method_call.method, method_call.argument, method_call.IsLastCall());
|
dma_pusher.channel_state.payload->fermi_2d.CallMethod(dma_pusher.system, method_call.method, method_call.argument, method_call.IsLastCall());
|
||||||
break;
|
break;
|
||||||
case EngineID::MAXWELL_B:
|
case EngineID::MAXWELL_B:
|
||||||
channel_state.maxwell_3d->CallMethod(method_call.method, method_call.argument, method_call.IsLastCall());
|
dma_pusher.channel_state.payload->maxwell_3d.CallMethod(dma_pusher.system, method_call.method, method_call.argument, method_call.IsLastCall());
|
||||||
break;
|
break;
|
||||||
case EngineID::KEPLER_COMPUTE_B:
|
case EngineID::KEPLER_COMPUTE_B:
|
||||||
channel_state.kepler_compute->CallMethod(method_call.method, method_call.argument, method_call.IsLastCall());
|
dma_pusher.channel_state.payload->kepler_compute.CallMethod(dma_pusher.system, method_call.method, method_call.argument, method_call.IsLastCall());
|
||||||
break;
|
break;
|
||||||
case EngineID::MAXWELL_DMA_COPY_A:
|
case EngineID::MAXWELL_DMA_COPY_A:
|
||||||
channel_state.maxwell_dma->CallMethod(method_call.method, method_call.argument, method_call.IsLastCall());
|
dma_pusher.channel_state.payload->maxwell_dma.CallMethod(dma_pusher.system, method_call.method, method_call.argument, method_call.IsLastCall());
|
||||||
break;
|
break;
|
||||||
case EngineID::KEPLER_INLINE_TO_MEMORY_B:
|
case EngineID::KEPLER_INLINE_TO_MEMORY_B:
|
||||||
channel_state.kepler_memory->CallMethod(method_call.method, method_call.argument, method_call.IsLastCall());
|
dma_pusher.channel_state.payload->kepler_memory.CallMethod(dma_pusher.system, method_call.method, method_call.argument, method_call.IsLastCall());
|
||||||
break;
|
break;
|
||||||
case EngineID::NV01_TIMER:
|
case EngineID::NV01_TIMER:
|
||||||
channel_state.nv01_timer->CallMethod(method_call.method, method_call.argument, method_call.IsLastCall());
|
dma_pusher.channel_state.payload->nv01_timer.CallMethod(dma_pusher.system, method_call.method, method_call.argument, method_call.IsLastCall());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Unimplemented engine");
|
UNIMPLEMENTED_MSG("Unimplemented engine");
|
||||||
|
|
@ -234,28 +222,26 @@ void Puller::CallEngineMethod(const MethodCall& method_call) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calls a GPU engine multivalue method.
|
/// Calls a GPU engine multivalue method.
|
||||||
void Puller::CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount,
|
void Puller::CallEngineMultiMethod(DmaPusher& dma_pusher, u32 method, u32 subchannel, const u32* base_start, u32 amount, u32 methods_pending) {
|
||||||
u32 methods_pending) {
|
|
||||||
const EngineID engine = bound_engines[subchannel];
|
const EngineID engine = bound_engines[subchannel];
|
||||||
|
|
||||||
switch (engine) {
|
switch (engine) {
|
||||||
case EngineID::FERMI_TWOD_A:
|
case EngineID::FERMI_TWOD_A:
|
||||||
channel_state.fermi_2d->CallMultiMethod(method, base_start, amount, methods_pending);
|
dma_pusher.channel_state.payload->fermi_2d.CallMultiMethod(dma_pusher.system, method, base_start, amount, methods_pending);
|
||||||
break;
|
break;
|
||||||
case EngineID::MAXWELL_B:
|
case EngineID::MAXWELL_B:
|
||||||
channel_state.maxwell_3d->CallMultiMethod(method, base_start, amount, methods_pending);
|
dma_pusher.channel_state.payload->maxwell_3d.CallMultiMethod(dma_pusher.system, method, base_start, amount, methods_pending);
|
||||||
break;
|
break;
|
||||||
case EngineID::KEPLER_COMPUTE_B:
|
case EngineID::KEPLER_COMPUTE_B:
|
||||||
channel_state.kepler_compute->CallMultiMethod(method, base_start, amount, methods_pending);
|
dma_pusher.channel_state.payload->kepler_compute.CallMultiMethod(dma_pusher.system, method, base_start, amount, methods_pending);
|
||||||
break;
|
break;
|
||||||
case EngineID::MAXWELL_DMA_COPY_A:
|
case EngineID::MAXWELL_DMA_COPY_A:
|
||||||
channel_state.maxwell_dma->CallMultiMethod(method, base_start, amount, methods_pending);
|
dma_pusher.channel_state.payload->maxwell_dma.CallMultiMethod(dma_pusher.system, method, base_start, amount, methods_pending);
|
||||||
break;
|
break;
|
||||||
case EngineID::KEPLER_INLINE_TO_MEMORY_B:
|
case EngineID::KEPLER_INLINE_TO_MEMORY_B:
|
||||||
channel_state.kepler_memory->CallMultiMethod(method, base_start, amount, methods_pending);
|
dma_pusher.channel_state.payload->kepler_memory.CallMultiMethod(dma_pusher.system, method, base_start, amount, methods_pending);
|
||||||
break;
|
break;
|
||||||
case EngineID::NV01_TIMER:
|
case EngineID::NV01_TIMER:
|
||||||
channel_state.nv01_timer->CallMultiMethod(method, base_start, amount, methods_pending);
|
dma_pusher.channel_state.payload->nv01_timer.CallMultiMethod(dma_pusher.system, method, base_start, amount, methods_pending);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Unimplemented engine");
|
UNIMPLEMENTED_MSG("Unimplemented engine");
|
||||||
|
|
@ -264,31 +250,26 @@ void Puller::CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_s
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calls a GPU method.
|
/// Calls a GPU method.
|
||||||
void Puller::CallMethod(const MethodCall& method_call) {
|
void Puller::CallMethod(DmaPusher& dma_pusher, const MethodCall& method_call) {
|
||||||
LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method_call.method,
|
LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method_call.method, method_call.subchannel);
|
||||||
method_call.subchannel);
|
|
||||||
|
|
||||||
ASSERT(method_call.subchannel < bound_engines.size());
|
ASSERT(method_call.subchannel < bound_engines.size());
|
||||||
|
|
||||||
if (ExecuteMethodOnEngine(method_call.method)) {
|
if (ExecuteMethodOnEngine(dma_pusher, method_call.method)) {
|
||||||
CallEngineMethod(method_call);
|
CallEngineMethod(dma_pusher, method_call);
|
||||||
} else {
|
} else {
|
||||||
CallPullerMethod(method_call);
|
CallPullerMethod(dma_pusher, method_call);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calls a GPU multivalue method.
|
/// Calls a GPU multivalue method.
|
||||||
void Puller::CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount,
|
void Puller::CallMultiMethod(DmaPusher& dma_pusher, u32 method, u32 subchannel, const u32* base_start, u32 amount, u32 methods_pending) {
|
||||||
u32 methods_pending) {
|
|
||||||
LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method, subchannel);
|
LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method, subchannel);
|
||||||
|
|
||||||
ASSERT(subchannel < bound_engines.size());
|
ASSERT(subchannel < bound_engines.size());
|
||||||
|
if (ExecuteMethodOnEngine(dma_pusher, method)) {
|
||||||
if (ExecuteMethodOnEngine(method)) {
|
CallEngineMultiMethod(dma_pusher, method, subchannel, base_start, amount, methods_pending);
|
||||||
CallEngineMultiMethod(method, subchannel, base_start, amount, methods_pending);
|
|
||||||
} else {
|
} else {
|
||||||
for (u32 i = 0; i < amount; i++) {
|
for (u32 i = 0; i < amount; i++) {
|
||||||
CallPullerMethod(MethodCall{
|
CallPullerMethod(dma_pusher, MethodCall{
|
||||||
method,
|
method,
|
||||||
base_start[i],
|
base_start[i],
|
||||||
subchannel,
|
subchannel,
|
||||||
|
|
@ -298,13 +279,9 @@ void Puller::CallMultiMethod(u32 method, u32 subchannel, const u32* base_start,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Puller::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
|
|
||||||
rasterizer = rasterizer_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Determines where the method should be executed.
|
/// Determines where the method should be executed.
|
||||||
[[nodiscard]] bool Puller::ExecuteMethodOnEngine(u32 method) {
|
[[nodiscard]] bool Puller::ExecuteMethodOnEngine(DmaPusher& dma_pusher, u32 method) {
|
||||||
const auto buffer_method = static_cast<BufferMethods>(method);
|
const auto buffer_method = BufferMethods(method);
|
||||||
return buffer_method >= BufferMethods::NonPullerMethods;
|
return buffer_method >= BufferMethods::NonPullerMethods;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -70,32 +70,13 @@ public:
|
||||||
BitField<8, 24, u32> syncpoint_id;
|
BitField<8, 24, u32> syncpoint_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit Puller(GPU& gpu_, MemoryManager& memory_manager_, DmaPusher& dma_pusher,
|
void CallMethod(DmaPusher& dma_pusher, const MethodCall& method_call);
|
||||||
Control::ChannelState& channel_state);
|
void CallMultiMethod(DmaPusher& dma_pusher, u32 method, u32 subchannel, const u32* base_start, u32 amount, u32 methods_pending);
|
||||||
~Puller();
|
void BindRasterizer(DmaPusher& dma_pusher, VideoCore::RasterizerInterface* rasterizer);
|
||||||
|
void CallPullerMethod(DmaPusher& dma_pusher, const MethodCall& method_call);
|
||||||
void CallMethod(const MethodCall& method_call);
|
void CallEngineMethod(DmaPusher& dma_pusher, const MethodCall& method_call);
|
||||||
|
void CallEngineMultiMethod(DmaPusher& dma_pusher, u32 method, u32 subchannel, const u32* base_start, u32 amount, u32 methods_pending);
|
||||||
void CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount,
|
|
||||||
u32 methods_pending);
|
|
||||||
|
|
||||||
void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
|
|
||||||
|
|
||||||
void CallPullerMethod(const MethodCall& method_call);
|
|
||||||
|
|
||||||
void CallEngineMethod(const MethodCall& method_call);
|
|
||||||
|
|
||||||
void CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount,
|
|
||||||
u32 methods_pending);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Tegra::GPU& gpu;
|
|
||||||
|
|
||||||
MemoryManager& memory_manager;
|
|
||||||
DmaPusher& dma_pusher;
|
|
||||||
Control::ChannelState& channel_state;
|
|
||||||
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
|
||||||
|
|
||||||
static constexpr std::size_t NUM_REGS = 0x800;
|
static constexpr std::size_t NUM_REGS = 0x800;
|
||||||
struct Regs {
|
struct Regs {
|
||||||
static constexpr size_t NUM_REGS = 0x40;
|
static constexpr size_t NUM_REGS = 0x40;
|
||||||
|
|
@ -139,12 +120,12 @@ private:
|
||||||
};
|
};
|
||||||
} regs{};
|
} regs{};
|
||||||
|
|
||||||
void ProcessBindMethod(const MethodCall& method_call);
|
void ProcessBindMethod(DmaPusher& dma_pusher, const MethodCall& method_call);
|
||||||
void ProcessFenceActionMethod();
|
void ProcessFenceActionMethod(DmaPusher& dma_pusher);
|
||||||
void ProcessSemaphoreAcquire();
|
void ProcessSemaphoreAcquire(DmaPusher& dma_pusher);
|
||||||
void ProcessSemaphoreRelease();
|
void ProcessSemaphoreRelease(DmaPusher& dma_pusher);
|
||||||
void ProcessSemaphoreTriggerMethod();
|
void ProcessSemaphoreTriggerMethod(DmaPusher& dma_pusher);
|
||||||
[[nodiscard]] bool ExecuteMethodOnEngine(u32 method);
|
[[nodiscard]] bool ExecuteMethodOnEngine(DmaPusher& dma_pusher, u32 method);
|
||||||
|
|
||||||
/// Mapping of command subchannels to their bound engine ids
|
/// Mapping of command subchannels to their bound engine ids
|
||||||
std::array<EngineID, 8> bound_engines{};
|
std::array<EngineID, 8> bound_engines{};
|
||||||
|
|
@ -157,8 +138,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
#define ASSERT_REG_POSITION(field_name, position) \
|
#define ASSERT_REG_POSITION(field_name, position) \
|
||||||
static_assert(offsetof(Regs, field_name) == position * 4, \
|
static_assert(offsetof(Regs, field_name) == position * 4, "Field " #field_name " has invalid position")
|
||||||
"Field " #field_name " has invalid position")
|
|
||||||
|
|
||||||
ASSERT_REG_POSITION(semaphore_address, 0x4);
|
ASSERT_REG_POSITION(semaphore_address, 0x4);
|
||||||
ASSERT_REG_POSITION(semaphore_sequence, 0x6);
|
ASSERT_REG_POSITION(semaphore_sequence, 0x6);
|
||||||
|
|
|
||||||
|
|
@ -40,30 +40,31 @@
|
||||||
namespace Tegra {
|
namespace Tegra {
|
||||||
|
|
||||||
struct GPU::Impl {
|
struct GPU::Impl {
|
||||||
explicit Impl(GPU& gpu_, Core::System& system_, bool is_async_, bool use_nvdec_)
|
explicit Impl(Core::System& system_, bool is_async_, bool use_nvdec_)
|
||||||
: gpu{gpu_}, system{system_}, host1x{system.Host1x()}, use_nvdec{use_nvdec_},
|
: system{system_}
|
||||||
shader_notify{std::make_unique<VideoCore::ShaderNotify>()}, is_async{is_async_},
|
, use_nvdec{use_nvdec_}
|
||||||
gpu_thread{system_, is_async_}, scheduler{std::make_unique<Control::Scheduler>(gpu)} {}
|
, shader_notify()
|
||||||
|
, is_async{is_async_}
|
||||||
|
, gpu_thread{system_}
|
||||||
|
{}
|
||||||
|
|
||||||
~Impl() = default;
|
~Impl() = default;
|
||||||
|
|
||||||
std::shared_ptr<Control::ChannelState> CreateChannel(s32 channel_id) {
|
std::shared_ptr<Control::ChannelState> CreateChannel(s32 channel_id) {
|
||||||
auto channel_state = std::make_shared<Tegra::Control::ChannelState>(channel_id);
|
auto channel_state = std::make_shared<Tegra::Control::ChannelState>(channel_id);
|
||||||
channels.emplace(channel_id, channel_state);
|
channels.emplace(channel_id, channel_state);
|
||||||
scheduler->DeclareChannel(channel_state);
|
scheduler.DeclareChannel(channel_state);
|
||||||
return channel_state;
|
return channel_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BindChannel(s32 channel_id) {
|
void BindChannel(s32 channel_id) {
|
||||||
if (bound_channel == channel_id) {
|
if (bound_channel != channel_id) {
|
||||||
return;
|
auto it = channels.find(channel_id);
|
||||||
|
ASSERT(it != channels.end());
|
||||||
|
bound_channel = channel_id;
|
||||||
|
current_channel = it->second.get();
|
||||||
|
renderer->ReadRasterizer()->BindChannel(*current_channel);
|
||||||
}
|
}
|
||||||
auto it = channels.find(channel_id);
|
|
||||||
ASSERT(it != channels.end());
|
|
||||||
bound_channel = channel_id;
|
|
||||||
current_channel = it->second.get();
|
|
||||||
|
|
||||||
rasterizer->BindChannel(*current_channel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Control::ChannelState> AllocateChannel() {
|
std::shared_ptr<Control::ChannelState> AllocateChannel() {
|
||||||
|
|
@ -71,13 +72,13 @@ struct GPU::Impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitChannel(Control::ChannelState& to_init, u64 program_id) {
|
void InitChannel(Control::ChannelState& to_init, u64 program_id) {
|
||||||
to_init.Init(system, gpu, program_id);
|
to_init.Init(system, program_id);
|
||||||
to_init.BindRasterizer(rasterizer);
|
to_init.BindRasterizer(renderer->ReadRasterizer());
|
||||||
rasterizer->InitializeChannel(to_init);
|
renderer->ReadRasterizer()->InitializeChannel(to_init);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitAddressSpace(Tegra::MemoryManager& memory_manager) {
|
void InitAddressSpace(Tegra::MemoryManager& memory_manager) {
|
||||||
memory_manager.BindRasterizer(rasterizer);
|
memory_manager.BindRasterizer(renderer->ReadRasterizer());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReleaseChannel(Control::ChannelState& to_release) {
|
void ReleaseChannel(Control::ChannelState& to_release) {
|
||||||
|
|
@ -87,26 +88,26 @@ struct GPU::Impl {
|
||||||
/// Binds a renderer to the GPU.
|
/// Binds a renderer to the GPU.
|
||||||
void BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer_) {
|
void BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer_) {
|
||||||
renderer = std::move(renderer_);
|
renderer = std::move(renderer_);
|
||||||
rasterizer = renderer->ReadRasterizer();
|
system.Host1x().memory_manager.BindInterface(renderer->ReadRasterizer());
|
||||||
host1x.MemoryManager().BindInterface(rasterizer);
|
system.Host1x().gmmu_manager.BindRasterizer(renderer->ReadRasterizer());
|
||||||
host1x.gmmu_manager.BindRasterizer(rasterizer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Flush all current written commands into the host GPU for execution.
|
/// Flush all current written commands into the host GPU for execution.
|
||||||
void FlushCommands() {
|
void FlushCommands() {
|
||||||
rasterizer->FlushCommands();
|
renderer->ReadRasterizer()->FlushCommands();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Synchronizes CPU writes with Host GPU memory.
|
/// Synchronizes CPU writes with Host GPU memory.
|
||||||
void InvalidateGPUCache() {
|
void InvalidateGPUCache() {
|
||||||
std::function<void(PAddr, size_t)> callback_writes(
|
std::function<void(PAddr, size_t)> callback_writes([this](PAddr address, size_t size) {
|
||||||
[this](PAddr address, size_t size) { rasterizer->OnCacheInvalidation(address, size); });
|
renderer->ReadRasterizer()->OnCacheInvalidation(address, size);
|
||||||
|
});
|
||||||
system.GatherGPUDirtyMemory(callback_writes);
|
system.GatherGPUDirtyMemory(callback_writes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Signal the ending of command list.
|
/// Signal the ending of command list.
|
||||||
void OnCommandListEnd() {
|
void OnCommandListEnd() {
|
||||||
rasterizer->ReleaseFences(false);
|
renderer->ReadRasterizer()->ReleaseFences(false);
|
||||||
Settings::UpdateGPUAccuracy();
|
Settings::UpdateGPUAccuracy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,62 +144,6 @@ struct GPU::Impl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to the Maxwell3D GPU engine.
|
|
||||||
[[nodiscard]] Engines::Maxwell3D& Maxwell3D() {
|
|
||||||
ASSERT(current_channel);
|
|
||||||
return *current_channel->maxwell_3d;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a const reference to the Maxwell3D GPU engine.
|
|
||||||
[[nodiscard]] const Engines::Maxwell3D& Maxwell3D() const {
|
|
||||||
ASSERT(current_channel);
|
|
||||||
return *current_channel->maxwell_3d;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a reference to the KeplerCompute GPU engine.
|
|
||||||
[[nodiscard]] Engines::KeplerCompute& KeplerCompute() {
|
|
||||||
ASSERT(current_channel);
|
|
||||||
return *current_channel->kepler_compute;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a reference to the KeplerCompute GPU engine.
|
|
||||||
[[nodiscard]] const Engines::KeplerCompute& KeplerCompute() const {
|
|
||||||
ASSERT(current_channel);
|
|
||||||
return *current_channel->kepler_compute;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a reference to the GPU DMA pusher.
|
|
||||||
[[nodiscard]] Tegra::DmaPusher& DmaPusher() {
|
|
||||||
ASSERT(current_channel);
|
|
||||||
return *current_channel->dma_pusher;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a const reference to the GPU DMA pusher.
|
|
||||||
[[nodiscard]] const Tegra::DmaPusher& DmaPusher() const {
|
|
||||||
ASSERT(current_channel);
|
|
||||||
return *current_channel->dma_pusher;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a reference to the underlying renderer.
|
|
||||||
[[nodiscard]] VideoCore::RendererBase& Renderer() {
|
|
||||||
return *renderer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a const reference to the underlying renderer.
|
|
||||||
[[nodiscard]] const VideoCore::RendererBase& Renderer() const {
|
|
||||||
return *renderer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a reference to the shader notifier.
|
|
||||||
[[nodiscard]] VideoCore::ShaderNotify& ShaderNotify() {
|
|
||||||
return *shader_notify;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a const reference to the shader notifier.
|
|
||||||
[[nodiscard]] const VideoCore::ShaderNotify& ShaderNotify() const {
|
|
||||||
return *shader_notify;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] u64 GetTicks() const {
|
[[nodiscard]] u64 GetTicks() const {
|
||||||
u64 gpu_tick = system.CoreTiming().GetGPUTicks();
|
u64 gpu_tick = system.CoreTiming().GetGPUTicks();
|
||||||
Settings::GpuOverclock overclock = Settings::values.fast_gpu_time.GetValue();
|
Settings::GpuOverclock overclock = Settings::values.fast_gpu_time.GetValue();
|
||||||
|
|
@ -210,14 +155,6 @@ struct GPU::Impl {
|
||||||
return gpu_tick;
|
return gpu_tick;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool IsAsync() const {
|
|
||||||
return is_async;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool UseNvdec() const {
|
|
||||||
return use_nvdec;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RendererFrameEndNotify() {
|
void RendererFrameEndNotify() {
|
||||||
system.GetPerfStats().EndGameFrame();
|
system.GetPerfStats().EndGameFrame();
|
||||||
}
|
}
|
||||||
|
|
@ -227,7 +164,7 @@ struct GPU::Impl {
|
||||||
/// core timing events.
|
/// core timing events.
|
||||||
void Start() {
|
void Start() {
|
||||||
Settings::UpdateGPUAccuracy();
|
Settings::UpdateGPUAccuracy();
|
||||||
gpu_thread.StartThread(*renderer, renderer->Context(), *scheduler);
|
gpu_thread.StartThread(*renderer, renderer->Context(), scheduler);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotifyShutdown() {
|
void NotifyShutdown() {
|
||||||
|
|
@ -251,25 +188,24 @@ struct GPU::Impl {
|
||||||
|
|
||||||
/// Push GPU command entries to be processed
|
/// Push GPU command entries to be processed
|
||||||
void PushGPUEntries(s32 channel, Tegra::CommandList&& entries) {
|
void PushGPUEntries(s32 channel, Tegra::CommandList&& entries) {
|
||||||
gpu_thread.SubmitList(channel, std::move(entries));
|
gpu_thread.SubmitList(channel, std::move(entries), is_async);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
|
/// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
|
||||||
void FlushRegion(DAddr addr, u64 size) {
|
void FlushRegion(DAddr addr, u64 size) {
|
||||||
gpu_thread.FlushRegion(addr, size);
|
gpu_thread.FlushRegion(addr, size, is_async);
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoCore::RasterizerDownloadArea OnCPURead(DAddr addr, u64 size) {
|
VideoCore::RasterizerDownloadArea OnCPURead(DAddr addr, u64 size) {
|
||||||
auto raster_area = rasterizer->GetFlushArea(addr, size);
|
auto raster_area = renderer->ReadRasterizer()->GetFlushArea(addr, size);
|
||||||
if (raster_area.preemtive) {
|
if (raster_area.preemtive) {
|
||||||
return raster_area;
|
return raster_area;
|
||||||
}
|
}
|
||||||
raster_area.preemtive = true;
|
raster_area.preemtive = true;
|
||||||
const u64 fence = RequestSyncOperation([this, &raster_area]() {
|
const u64 fence = RequestSyncOperation([this, &raster_area]() {
|
||||||
rasterizer->FlushRegion(raster_area.start_address,
|
renderer->ReadRasterizer()->FlushRegion(raster_area.start_address, raster_area.end_address - raster_area.start_address);
|
||||||
raster_area.end_address - raster_area.start_address);
|
|
||||||
});
|
});
|
||||||
gpu_thread.TickGPU();
|
gpu_thread.TickGPU(is_async);
|
||||||
WaitForSyncOperation(fence);
|
WaitForSyncOperation(fence);
|
||||||
return raster_area;
|
return raster_area;
|
||||||
}
|
}
|
||||||
|
|
@ -280,16 +216,15 @@ struct GPU::Impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OnCPUWrite(DAddr addr, u64 size) {
|
bool OnCPUWrite(DAddr addr, u64 size) {
|
||||||
return rasterizer->OnCPUWrite(addr, size);
|
return renderer->ReadRasterizer()->OnCPUWrite(addr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Notify rasterizer that any caches of the specified region should be flushed and invalidated
|
/// Notify rasterizer that any caches of the specified region should be flushed and invalidated
|
||||||
void FlushAndInvalidateRegion(DAddr addr, u64 size) {
|
void FlushAndInvalidateRegion(DAddr addr, u64 size) {
|
||||||
gpu_thread.FlushAndInvalidateRegion(addr, size);
|
gpu_thread.FlushAndInvalidateRegion(addr, size, is_async);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers,
|
void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers, std::vector<Service::Nvidia::NvFence>&& fences) {
|
||||||
std::vector<Service::Nvidia::NvFence>&& fences) {
|
|
||||||
size_t num_fences{fences.size()};
|
size_t num_fences{fences.size()};
|
||||||
size_t current_request_counter{};
|
size_t current_request_counter{};
|
||||||
{
|
{
|
||||||
|
|
@ -304,7 +239,7 @@ struct GPU::Impl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const auto wait_fence = RequestSyncOperation([this, current_request_counter, &layers, &fences, num_fences] {
|
const auto wait_fence = RequestSyncOperation([this, current_request_counter, &layers, &fences, num_fences] {
|
||||||
auto& syncpoint_manager = host1x.GetSyncpointManager();
|
auto& syncpoint_manager = system.Host1x().GetSyncpointManager();
|
||||||
if (num_fences == 0) {
|
if (num_fences == 0) {
|
||||||
renderer->Composite(layers);
|
renderer->Composite(layers);
|
||||||
}
|
}
|
||||||
|
|
@ -322,7 +257,7 @@ struct GPU::Impl {
|
||||||
syncpoint_manager.RegisterGuestAction(fences[i].id, fences[i].value, executer);
|
syncpoint_manager.RegisterGuestAction(fences[i].id, fences[i].value, executer);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
gpu_thread.TickGPU();
|
gpu_thread.TickGPU(is_async);
|
||||||
WaitForSyncOperation(wait_fence);
|
WaitForSyncOperation(wait_fence);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -331,23 +266,20 @@ struct GPU::Impl {
|
||||||
|
|
||||||
const auto wait_fence =
|
const auto wait_fence =
|
||||||
RequestSyncOperation([&] { out = renderer->GetAppletCaptureBuffer(); });
|
RequestSyncOperation([&] { out = renderer->GetAppletCaptureBuffer(); });
|
||||||
gpu_thread.TickGPU();
|
gpu_thread.TickGPU(is_async);
|
||||||
WaitForSyncOperation(wait_fence);
|
WaitForSyncOperation(wait_fence);
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
GPU& gpu;
|
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
Host1x::Host1x& host1x;
|
|
||||||
|
|
||||||
std::unique_ptr<VideoCore::RendererBase> renderer;
|
std::unique_ptr<VideoCore::RendererBase> renderer;
|
||||||
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
|
||||||
const bool use_nvdec;
|
const bool use_nvdec;
|
||||||
|
|
||||||
s32 new_channel_id{1};
|
s32 new_channel_id{1};
|
||||||
/// Shader build notifier
|
/// Shader build notifier
|
||||||
std::unique_ptr<VideoCore::ShaderNotify> shader_notify;
|
VideoCore::ShaderNotify shader_notify;
|
||||||
/// When true, we are about to shut down emulation session, so terminate outstanding tasks
|
/// When true, we are about to shut down emulation session, so terminate outstanding tasks
|
||||||
std::atomic_bool shutting_down{};
|
std::atomic_bool shutting_down{};
|
||||||
|
|
||||||
|
|
@ -371,7 +303,7 @@ struct GPU::Impl {
|
||||||
VideoCommon::GPUThread::ThreadManager gpu_thread;
|
VideoCommon::GPUThread::ThreadManager gpu_thread;
|
||||||
std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context;
|
std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context;
|
||||||
|
|
||||||
std::unique_ptr<Tegra::Control::Scheduler> scheduler;
|
Tegra::Control::Scheduler scheduler;
|
||||||
ankerl::unordered_dense::map<s32, std::shared_ptr<Tegra::Control::ChannelState>> channels;
|
ankerl::unordered_dense::map<s32, std::shared_ptr<Tegra::Control::ChannelState>> channels;
|
||||||
Tegra::Control::ChannelState* current_channel;
|
Tegra::Control::ChannelState* current_channel;
|
||||||
s32 bound_channel{-1};
|
s32 bound_channel{-1};
|
||||||
|
|
@ -382,7 +314,8 @@ struct GPU::Impl {
|
||||||
};
|
};
|
||||||
|
|
||||||
GPU::GPU(Core::System& system, bool is_async, bool use_nvdec)
|
GPU::GPU(Core::System& system, bool is_async, bool use_nvdec)
|
||||||
: impl{std::make_unique<Impl>(*this, system, is_async, use_nvdec)} {}
|
: impl{std::make_unique<Impl>(system, is_async, use_nvdec)}
|
||||||
|
{}
|
||||||
|
|
||||||
GPU::~GPU() = default;
|
GPU::~GPU() = default;
|
||||||
|
|
||||||
|
|
@ -423,8 +356,9 @@ void GPU::OnCommandListEnd() {
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 GPU::RequestFlush(DAddr addr, std::size_t size) {
|
u64 GPU::RequestFlush(DAddr addr, std::size_t size) {
|
||||||
return impl->RequestSyncOperation(
|
return impl->RequestSyncOperation([this, addr, size]() {
|
||||||
[this, addr, size]() { impl->rasterizer->FlushRegion(addr, size); });
|
impl->renderer->ReadRasterizer()->FlushRegion(addr, size);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 GPU::CurrentSyncRequestFence() const {
|
u64 GPU::CurrentSyncRequestFence() const {
|
||||||
|
|
@ -441,52 +375,52 @@ void GPU::TickWork() {
|
||||||
|
|
||||||
/// Gets a mutable reference to the Host1x interface
|
/// Gets a mutable reference to the Host1x interface
|
||||||
Host1x::Host1x& GPU::Host1x() {
|
Host1x::Host1x& GPU::Host1x() {
|
||||||
return impl->host1x;
|
return impl->system.Host1x();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets an immutable reference to the Host1x interface.
|
/// Gets an immutable reference to the Host1x interface.
|
||||||
const Host1x::Host1x& GPU::Host1x() const {
|
const Host1x::Host1x& GPU::Host1x() const {
|
||||||
return impl->host1x;
|
return impl->system.Host1x();
|
||||||
}
|
}
|
||||||
|
|
||||||
Engines::Maxwell3D& GPU::Maxwell3D() {
|
Engines::Maxwell3D& GPU::Maxwell3D() {
|
||||||
return impl->Maxwell3D();
|
return impl->current_channel->payload->maxwell_3d;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Engines::Maxwell3D& GPU::Maxwell3D() const {
|
const Engines::Maxwell3D& GPU::Maxwell3D() const {
|
||||||
return impl->Maxwell3D();
|
return impl->current_channel->payload->maxwell_3d;
|
||||||
}
|
}
|
||||||
|
|
||||||
Engines::KeplerCompute& GPU::KeplerCompute() {
|
Engines::KeplerCompute& GPU::KeplerCompute() {
|
||||||
return impl->KeplerCompute();
|
return impl->current_channel->payload->kepler_compute;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Engines::KeplerCompute& GPU::KeplerCompute() const {
|
const Engines::KeplerCompute& GPU::KeplerCompute() const {
|
||||||
return impl->KeplerCompute();
|
return impl->current_channel->payload->kepler_compute;
|
||||||
}
|
}
|
||||||
|
|
||||||
Tegra::DmaPusher& GPU::DmaPusher() {
|
Tegra::DmaPusher& GPU::DmaPusher() {
|
||||||
return impl->DmaPusher();
|
return impl->current_channel->payload->dma_pusher;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Tegra::DmaPusher& GPU::DmaPusher() const {
|
const Tegra::DmaPusher& GPU::DmaPusher() const {
|
||||||
return impl->DmaPusher();
|
return impl->current_channel->payload->dma_pusher;
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoCore::RendererBase& GPU::Renderer() {
|
VideoCore::RendererBase& GPU::Renderer() {
|
||||||
return impl->Renderer();
|
return *impl->renderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
const VideoCore::RendererBase& GPU::Renderer() const {
|
const VideoCore::RendererBase& GPU::Renderer() const {
|
||||||
return impl->Renderer();
|
return *impl->renderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoCore::ShaderNotify& GPU::ShaderNotify() {
|
VideoCore::ShaderNotify& GPU::ShaderNotify() {
|
||||||
return impl->ShaderNotify();
|
return impl->shader_notify;
|
||||||
}
|
}
|
||||||
|
|
||||||
const VideoCore::ShaderNotify& GPU::ShaderNotify() const {
|
const VideoCore::ShaderNotify& GPU::ShaderNotify() const {
|
||||||
return impl->ShaderNotify();
|
return impl->shader_notify;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPU::RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers,
|
void GPU::RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers,
|
||||||
|
|
@ -503,11 +437,11 @@ u64 GPU::GetTicks() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GPU::IsAsync() const {
|
bool GPU::IsAsync() const {
|
||||||
return impl->IsAsync();
|
return impl->is_async;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GPU::UseNvdec() const {
|
bool GPU::UseNvdec() const {
|
||||||
return impl->UseNvdec();
|
return impl->use_nvdec;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPU::RendererFrameEndNotify() {
|
void GPU::RendererFrameEndNotify() {
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,9 @@
|
||||||
|
|
||||||
namespace VideoCommon::GPUThread {
|
namespace VideoCommon::GPUThread {
|
||||||
|
|
||||||
ThreadManager::ThreadManager(Core::System& system_, bool is_async_)
|
ThreadManager::ThreadManager(Core::System& system_)
|
||||||
: system{system_}, is_async{is_async_} {}
|
: system{system_}
|
||||||
|
{}
|
||||||
|
|
||||||
ThreadManager::~ThreadManager() = default;
|
ThreadManager::~ThreadManager() = default;
|
||||||
|
|
||||||
|
|
@ -39,7 +40,7 @@ void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Core::Fronten
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) {
|
if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) {
|
||||||
scheduler.Push(submit_list->channel, std::move(submit_list->entries));
|
scheduler.Push(system.GPU(), submit_list->channel, std::move(submit_list->entries));
|
||||||
} else if (std::holds_alternative<GPUTickCommand>(next.data)) {
|
} else if (std::holds_alternative<GPUTickCommand>(next.data)) {
|
||||||
system.GPU().TickWork();
|
system.GPU().TickWork();
|
||||||
} else if (const auto* flush = std::get_if<FlushRegionCommand>(&next.data)) {
|
} else if (const auto* flush = std::get_if<FlushRegionCommand>(&next.data)) {
|
||||||
|
|
@ -60,41 +61,40 @@ void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Core::Fronten
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadManager::SubmitList(s32 channel, Tegra::CommandList&& entries) {
|
void ThreadManager::SubmitList(s32 channel, Tegra::CommandList&& entries, bool is_async) {
|
||||||
PushCommand(SubmitListCommand(channel, std::move(entries)));
|
PushCommand(SubmitListCommand(channel, std::move(entries)), false, is_async);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadManager::FlushRegion(DAddr addr, u64 size) {
|
void ThreadManager::FlushRegion(DAddr addr, u64 size, bool is_async) {
|
||||||
if (!is_async) {
|
if (!is_async) {
|
||||||
// Always flush with synchronous GPU mode
|
// Always flush with synchronous GPU mode
|
||||||
PushCommand(FlushRegionCommand(addr, size));
|
PushCommand(FlushRegionCommand(addr, size), false, is_async);
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadManager::TickGPU() {
|
void ThreadManager::TickGPU(bool is_async) {
|
||||||
PushCommand(GPUTickCommand());
|
PushCommand(GPUTickCommand(), false, is_async);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadManager::InvalidateRegion(DAddr addr, u64 size) {
|
void ThreadManager::InvalidateRegion(DAddr addr, u64 size) {
|
||||||
rasterizer->OnCacheInvalidation(addr, size);
|
rasterizer->OnCacheInvalidation(addr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadManager::FlushAndInvalidateRegion(DAddr addr, u64 size) {
|
void ThreadManager::FlushAndInvalidateRegion(DAddr addr, u64 size, bool is_async) {
|
||||||
if (Settings::IsGPULevelHigh()) {
|
if (Settings::IsGPULevelHigh()) {
|
||||||
if (!is_async) {
|
if (!is_async) {
|
||||||
PushCommand(FlushRegionCommand(addr, size));
|
PushCommand(FlushRegionCommand(addr, size), false, is_async);
|
||||||
} else {
|
} else {
|
||||||
auto& gpu = system.GPU();
|
auto& gpu = system.GPU();
|
||||||
const u64 fence = gpu.RequestFlush(addr, size);
|
const u64 fence = gpu.RequestFlush(addr, size);
|
||||||
TickGPU();
|
TickGPU(is_async);
|
||||||
gpu.WaitForSyncOperation(fence);
|
gpu.WaitForSyncOperation(fence);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rasterizer->OnCacheInvalidation(addr, size);
|
rasterizer->OnCacheInvalidation(addr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 ThreadManager::PushCommand(CommandData&& command_data, bool block) {
|
u64 ThreadManager::PushCommand(CommandData&& command_data, bool block, bool is_async) {
|
||||||
if (!is_async) {
|
if (!is_async) {
|
||||||
// In synchronous GPU mode, block the caller until the command has executed
|
// In synchronous GPU mode, block the caller until the command has executed
|
||||||
block = true;
|
block = true;
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
#include "common/bounded_threadsafe_queue.h"
|
#include "common/bounded_threadsafe_queue.h"
|
||||||
#include "common/polyfill_thread.h"
|
#include "common/polyfill_thread.h"
|
||||||
|
#include "video_core/dma_pusher.h"
|
||||||
#include "video_core/framebuffer_config.h"
|
#include "video_core/framebuffer_config.h"
|
||||||
|
|
||||||
namespace Tegra {
|
namespace Tegra {
|
||||||
|
|
@ -103,7 +104,7 @@ struct SynchState final {
|
||||||
/// Class used to manage the GPU thread
|
/// Class used to manage the GPU thread
|
||||||
class ThreadManager final {
|
class ThreadManager final {
|
||||||
public:
|
public:
|
||||||
explicit ThreadManager(Core::System& system_, bool is_async_);
|
explicit ThreadManager(Core::System& system_);
|
||||||
~ThreadManager();
|
~ThreadManager();
|
||||||
|
|
||||||
/// Creates and starts the GPU thread.
|
/// Creates and starts the GPU thread.
|
||||||
|
|
@ -111,27 +112,25 @@ public:
|
||||||
Tegra::Control::Scheduler& scheduler);
|
Tegra::Control::Scheduler& scheduler);
|
||||||
|
|
||||||
/// Push GPU command entries to be processed
|
/// Push GPU command entries to be processed
|
||||||
void SubmitList(s32 channel, Tegra::CommandList&& entries);
|
void SubmitList(s32 channel, Tegra::CommandList&& entries, bool is_async);
|
||||||
|
|
||||||
/// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
|
/// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
|
||||||
void FlushRegion(DAddr addr, u64 size);
|
void FlushRegion(DAddr addr, u64 size, bool is_async);
|
||||||
|
|
||||||
/// Notify rasterizer that any caches of the specified region should be invalidated
|
/// Notify rasterizer that any caches of the specified region should be invalidated
|
||||||
void InvalidateRegion(DAddr addr, u64 size);
|
void InvalidateRegion(DAddr addr, u64 size);
|
||||||
|
|
||||||
/// Notify rasterizer that any caches of the specified region should be flushed and invalidated
|
/// Notify rasterizer that any caches of the specified region should be flushed and invalidated
|
||||||
void FlushAndInvalidateRegion(DAddr addr, u64 size);
|
void FlushAndInvalidateRegion(DAddr addr, u64 size, bool is_async);
|
||||||
|
|
||||||
void TickGPU();
|
void TickGPU(bool is_async);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Pushes a command to be executed by the GPU thread
|
/// Pushes a command to be executed by the GPU thread
|
||||||
u64 PushCommand(CommandData&& command_data, bool block = false);
|
u64 PushCommand(CommandData&& command_data, bool block, bool is_async);
|
||||||
|
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
const bool is_async;
|
|
||||||
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
||||||
|
|
||||||
SynchState state;
|
SynchState state;
|
||||||
std::jthread thread;
|
std::jthread thread;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
|
||||||
|
|
@ -10,26 +10,22 @@
|
||||||
|
|
||||||
namespace Tegra::Host1x {
|
namespace Tegra::Host1x {
|
||||||
|
|
||||||
Control::Control(Host1x& host1x_) : host1x(host1x_) {}
|
void Control::ProcessMethod(Host1x& host1x, Method method, u32 argument) {
|
||||||
|
|
||||||
Control::~Control() = default;
|
|
||||||
|
|
||||||
void Control::ProcessMethod(Method method, u32 argument) {
|
|
||||||
switch (method) {
|
switch (method) {
|
||||||
case Method::LoadSyncptPayload32:
|
case Method::LoadSyncptPayload32:
|
||||||
syncpoint_value = argument;
|
syncpoint_value = argument;
|
||||||
break;
|
break;
|
||||||
case Method::WaitSyncpt:
|
case Method::WaitSyncpt:
|
||||||
case Method::WaitSyncpt32:
|
case Method::WaitSyncpt32:
|
||||||
Execute(argument);
|
Execute(host1x, argument);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Control method {:#X}", static_cast<u32>(method));
|
UNIMPLEMENTED_MSG("Control method {:#X}", u32(method));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Control::Execute(u32 data) {
|
void Control::Execute(Host1x& host1x, u32 data) {
|
||||||
LOG_TRACE(Service_NVDRV, "Control wait syncpt {} value {}", data, syncpoint_value);
|
LOG_TRACE(Service_NVDRV, "Control wait syncpt {} value {}", data, syncpoint_value);
|
||||||
host1x.GetSyncpointManager().WaitHost(data, syncpoint_value);
|
host1x.GetSyncpointManager().WaitHost(data, syncpoint_value);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
|
||||||
// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
|
// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
@ -19,17 +22,11 @@ public:
|
||||||
WaitSyncpt32 = 0x50,
|
WaitSyncpt32 = 0x50,
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit Control(Host1x& host1x);
|
|
||||||
~Control();
|
|
||||||
|
|
||||||
/// Writes the method into the state, Invoke Execute() if encountered
|
/// Writes the method into the state, Invoke Execute() if encountered
|
||||||
void ProcessMethod(Method method, u32 argument);
|
void ProcessMethod(Host1x& host1x, Method method, u32 argument);
|
||||||
|
|
||||||
private:
|
|
||||||
/// For Host1x, execute is waiting on a syncpoint previously written into the state
|
/// For Host1x, execute is waiting on a syncpoint previously written into the state
|
||||||
void Execute(u32 data);
|
void Execute(Host1x& host1x, u32 data);
|
||||||
|
|
||||||
Host1x& host1x;
|
|
||||||
u32 syncpoint_value{};
|
u32 syncpoint_value{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,22 +27,22 @@ void Host1x::StartDevice(s32 fd, ChannelType type, u32 syncpt) {
|
||||||
#ifdef YUZU_LEGACY
|
#ifdef YUZU_LEGACY
|
||||||
std::call_once(nvdec_first_init, []() {std::this_thread::sleep_for(std::chrono::milliseconds{500});}); // HACK: For Astroneer
|
std::call_once(nvdec_first_init, []() {std::this_thread::sleep_for(std::chrono::milliseconds{500});}); // HACK: For Astroneer
|
||||||
#endif
|
#endif
|
||||||
devices[fd] = std::make_unique<Tegra::Host1x::Nvdec>(*this, fd, syncpt);
|
devices[fd].emplace<Tegra::Host1x::Nvdec>(*this, fd, syncpt);
|
||||||
break;
|
break;
|
||||||
case ChannelType::VIC:
|
case ChannelType::VIC:
|
||||||
#ifdef YUZU_LEGACY
|
#ifdef YUZU_LEGACY
|
||||||
std::call_once(vic_first_init, []() {std::this_thread::sleep_for(std::chrono::milliseconds{500});}); // HACK: For Astroneer
|
std::call_once(vic_first_init, []() {std::this_thread::sleep_for(std::chrono::milliseconds{500});}); // HACK: For Astroneer
|
||||||
#endif
|
#endif
|
||||||
devices[fd] = std::make_unique<Tegra::Host1x::Vic>(*this, fd, syncpt);
|
devices[fd].emplace<Tegra::Host1x::Vic>(*this, fd, syncpt);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG_ERROR(HW_GPU, "Unimplemented host1x device {}", static_cast<u32>(type));
|
LOG_ERROR(HW_GPU, "Unimplemented host1x device {}", u32(type));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Host1x::StopDevice(s32 fd, ChannelType type) {
|
void Host1x::StopDevice(s32 fd, ChannelType type) {
|
||||||
devices.erase(fd);
|
devices[fd].emplace<std::monostate>();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Tegra::Host1x
|
} // namespace Tegra::Host1x
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,14 @@
|
||||||
|
|
||||||
#include <ankerl/unordered_dense.h>
|
#include <ankerl/unordered_dense.h>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <queue>
|
#include <variant>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
// fd types?
|
||||||
|
#include "video_core/host1x/nvdec.h"
|
||||||
|
#include "video_core/host1x/vic.h"
|
||||||
|
|
||||||
#include "common/address_space.h"
|
#include "common/address_space.h"
|
||||||
#include "video_core/cdma_pusher.h"
|
#include "video_core/cdma_pusher.h"
|
||||||
#include "video_core/host1x/gpu_device_memory_manager.h"
|
#include "video_core/host1x/gpu_device_memory_manager.h"
|
||||||
|
|
@ -31,118 +35,90 @@ class Nvdec;
|
||||||
|
|
||||||
class FrameQueue {
|
class FrameQueue {
|
||||||
public:
|
public:
|
||||||
|
struct FrameDevice {
|
||||||
|
std::deque<std::pair<u64, std::shared_ptr<FFmpeg::Frame>>> m_presentation_order;
|
||||||
|
std::unordered_map<u64, std::shared_ptr<FFmpeg::Frame>> m_decode_order;
|
||||||
|
};
|
||||||
|
|
||||||
void Open(s32 fd) {
|
void Open(s32 fd) {
|
||||||
std::scoped_lock l{m_mutex};
|
std::scoped_lock l{m_mutex};
|
||||||
m_presentation_order.insert({fd, {}});
|
m_frame_devices.insert_or_assign(fd, FrameDevice{});
|
||||||
m_decode_order.insert({fd, {}});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Close(s32 fd) {
|
void Close(s32 fd) {
|
||||||
std::scoped_lock l{m_mutex};
|
std::scoped_lock l{m_mutex};
|
||||||
m_presentation_order.erase(fd);
|
m_frame_devices.erase(fd);
|
||||||
m_decode_order.erase(fd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 VicFindNvdecFdFromOffset(u64 search_offset) {
|
s32 VicFindNvdecFdFromOffset(u64 search_offset) {
|
||||||
std::scoped_lock l{m_mutex};
|
std::scoped_lock l{m_mutex};
|
||||||
for (auto& map : m_presentation_order) {
|
for (auto const& [fd, dev] : m_frame_devices) {
|
||||||
for (auto& [offset, frame] : map.second) {
|
for (auto const& [offset, frame] : dev.m_presentation_order)
|
||||||
if (offset == search_offset) {
|
if (offset == search_offset)
|
||||||
return map.first;
|
return fd;
|
||||||
}
|
for (auto const& [offset, frame] : dev.m_decode_order)
|
||||||
}
|
if (offset == search_offset)
|
||||||
}
|
return fd;
|
||||||
for (auto& map : m_decode_order) {
|
|
||||||
for (auto& [offset, frame] : map.second) {
|
|
||||||
if (offset == search_offset) {
|
|
||||||
return map.first;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PushPresentOrder(s32 fd, u64 offset, std::shared_ptr<FFmpeg::Frame>&& frame) {
|
void PushPresentOrder(s32 fd, u64 offset, std::shared_ptr<FFmpeg::Frame>&& frame) {
|
||||||
std::scoped_lock l{m_mutex};
|
std::scoped_lock l{m_mutex};
|
||||||
auto map = m_presentation_order.find(fd);
|
if (auto const it = m_frame_devices.find(fd); it != m_frame_devices.end()) {
|
||||||
if (map == m_presentation_order.end()) {
|
if (it->second.m_presentation_order.size() >= MAX_PRESENT_QUEUE)
|
||||||
return;
|
it->second.m_presentation_order.pop_front();
|
||||||
|
it->second.m_presentation_order.emplace_back(offset, std::move(frame));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (map->second.size() >= MAX_PRESENT_QUEUE) {
|
|
||||||
map->second.pop_front();
|
|
||||||
}
|
|
||||||
|
|
||||||
map->second.emplace_back(offset, std::move(frame));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PushDecodeOrder(s32 fd, u64 offset, std::shared_ptr<FFmpeg::Frame>&& frame) {
|
void PushDecodeOrder(s32 fd, u64 offset, std::shared_ptr<FFmpeg::Frame>&& frame) {
|
||||||
std::scoped_lock l{m_mutex};
|
std::scoped_lock l{m_mutex};
|
||||||
auto map = m_decode_order.find(fd);
|
if (auto const it = m_frame_devices.find(fd); it != m_frame_devices.end()) {
|
||||||
if (map == m_decode_order.end()) {
|
it->second.m_decode_order.insert_or_assign(offset, std::move(frame));
|
||||||
return;
|
if (it->second.m_decode_order.size() > MAX_DECODE_MAP) {
|
||||||
}
|
auto it2 = it->second.m_decode_order.begin();
|
||||||
|
std::advance(it2, it->second.m_decode_order.size() - MAX_DECODE_MAP);
|
||||||
map->second.insert_or_assign(offset, std::move(frame));
|
it->second.m_decode_order.erase(it->second.m_decode_order.begin(), it2);
|
||||||
|
}
|
||||||
if (map->second.size() > MAX_DECODE_MAP) {
|
|
||||||
auto it = map->second.begin();
|
|
||||||
std::advance(it, map->second.size() - MAX_DECODE_MAP);
|
|
||||||
map->second.erase(map->second.begin(), it);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<FFmpeg::Frame> GetFrame(s32 fd, u64 offset) {
|
std::shared_ptr<FFmpeg::Frame> GetFrame(s32 fd, u64 offset) {
|
||||||
if (fd == -1) {
|
if (fd != -1) {
|
||||||
return {};
|
std::scoped_lock l{m_mutex};
|
||||||
|
if (auto const it = m_frame_devices.find(fd); it != m_frame_devices.end()) {
|
||||||
|
if (it->second.m_presentation_order.size() > 0)
|
||||||
|
return GetPresentOrderLocked(fd);
|
||||||
|
if (it->second.m_decode_order.size() > 0)
|
||||||
|
return GetDecodeOrderLocked(fd, offset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::scoped_lock l{m_mutex};
|
|
||||||
|
|
||||||
auto present_map = m_presentation_order.find(fd);
|
|
||||||
if (present_map != m_presentation_order.end() && !present_map->second.empty()) {
|
|
||||||
return GetPresentOrderLocked(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto decode_map = m_decode_order.find(fd);
|
|
||||||
if (decode_map != m_decode_order.end() && !decode_map->second.empty()) {
|
|
||||||
return GetDecodeOrderLocked(fd, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<FFmpeg::Frame> GetPresentOrderLocked(s32 fd) {
|
std::shared_ptr<FFmpeg::Frame> GetPresentOrderLocked(s32 fd) {
|
||||||
auto map = m_presentation_order.find(fd);
|
if (auto const it = m_frame_devices.find(fd); it != m_frame_devices.end()) {
|
||||||
if (map == m_presentation_order.end() || map->second.empty()) {
|
auto frame = std::move(it->second.m_presentation_order.front().second);
|
||||||
return {};
|
it->second.m_presentation_order.pop_front();
|
||||||
|
return frame;
|
||||||
}
|
}
|
||||||
|
return {};
|
||||||
auto frame = std::move(map->second.front().second);
|
|
||||||
map->second.pop_front();
|
|
||||||
return frame;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<FFmpeg::Frame> GetDecodeOrderLocked(s32 fd, u64 offset) {
|
std::shared_ptr<FFmpeg::Frame> GetDecodeOrderLocked(s32 fd, u64 offset) {
|
||||||
auto map = m_decode_order.find(fd);
|
if (auto const it = m_frame_devices.find(fd); it != m_frame_devices.end()) {
|
||||||
if (map == m_decode_order.end() || map->second.empty()) {
|
if (auto const it2 = it->second.m_decode_order.find(offset); it2 != it->second.m_decode_order.end()) {
|
||||||
return {};
|
// TODO: this "mapped" prevents us from fully embracing ankerl
|
||||||
|
return std::move(it->second.m_decode_order.extract(it2).mapped());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return {};
|
||||||
auto it = map->second.find(offset);
|
|
||||||
if (it == map->second.end()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
// TODO: this "mapped" prevents us from fully embracing ankerl
|
|
||||||
return std::move(map->second.extract(it).mapped());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
using FramePtr = std::shared_ptr<FFmpeg::Frame>;
|
|
||||||
|
|
||||||
std::mutex m_mutex{};
|
std::mutex m_mutex{};
|
||||||
ankerl::unordered_dense::map<s32, std::deque<std::pair<u64, FramePtr>>> m_presentation_order;
|
ankerl::unordered_dense::map<s32, FrameDevice> m_frame_devices;
|
||||||
ankerl::unordered_dense::map<s32, std::unordered_map<u64, FramePtr>> m_decode_order;
|
|
||||||
|
|
||||||
static constexpr size_t MAX_PRESENT_QUEUE = 100;
|
static constexpr size_t MAX_PRESENT_QUEUE = 100;
|
||||||
static constexpr size_t MAX_DECODE_MAP = 200;
|
static constexpr size_t MAX_DECODE_MAP = 200;
|
||||||
|
|
@ -196,11 +172,11 @@ public:
|
||||||
void StopDevice(s32 fd, ChannelType type);
|
void StopDevice(s32 fd, ChannelType type);
|
||||||
|
|
||||||
void PushEntries(s32 fd, ChCommandHeaderList&& entries) {
|
void PushEntries(s32 fd, ChCommandHeaderList&& entries) {
|
||||||
auto it = devices.find(fd);
|
if (auto const nvdec = std::get_if<Tegra::Host1x::Nvdec>(&devices[fd])) {
|
||||||
if (it == devices.end()) {
|
nvdec->PushEntries(std::move(entries));
|
||||||
return;
|
} else if (auto const vic = std::get_if<Tegra::Host1x::Vic>(&devices[fd])) {
|
||||||
|
vic->PushEntries(std::move(entries));
|
||||||
}
|
}
|
||||||
it->second->PushEntries(std::move(entries));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
|
|
@ -209,7 +185,11 @@ public:
|
||||||
Tegra::MemoryManager gmmu_manager;
|
Tegra::MemoryManager gmmu_manager;
|
||||||
Common::FlatAllocator<u32, 0, 32> allocator;
|
Common::FlatAllocator<u32, 0, 32> allocator;
|
||||||
FrameQueue frame_queue;
|
FrameQueue frame_queue;
|
||||||
ankerl::unordered_dense::map<s32, std::unique_ptr<CDmaPusher>> devices;
|
std::array<std::variant<
|
||||||
|
std::monostate,
|
||||||
|
Tegra::Host1x::Nvdec,
|
||||||
|
Tegra::Host1x::Vic
|
||||||
|
>, 1024> devices;
|
||||||
#ifdef YUZU_LEGACY
|
#ifdef YUZU_LEGACY
|
||||||
std::once_flag nvdec_first_init;
|
std::once_flag nvdec_first_init;
|
||||||
std::once_flag vic_first_init;
|
std::once_flag vic_first_init;
|
||||||
|
|
|
||||||
|
|
@ -407,15 +407,15 @@ void Vic::ReadInterlacedY8__V8U8_N420(const SlotStruct& slot, std::span<const Pl
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (slot.config.deinterlace_mode) {
|
switch (slot.config.deinterlace_mode) {
|
||||||
case DXVAHD_DEINTERLACE_MODE_PRIVATE::WEAVE:
|
case DxvhadDeinterlaceModePrivate::Weave:
|
||||||
// Due to the fact that we do not write to memory in nvdec, we cannot use Weave as it
|
// Due to the fact that we do not write to memory in nvdec, we cannot use Weave as it
|
||||||
// relies on the previous frame.
|
// relies on the previous frame.
|
||||||
DecodeBobField();
|
DecodeBobField();
|
||||||
break;
|
break;
|
||||||
case DXVAHD_DEINTERLACE_MODE_PRIVATE::BOB_FIELD:
|
case DxvhadDeinterlaceModePrivate::BobField:
|
||||||
DecodeBobField();
|
DecodeBobField();
|
||||||
break;
|
break;
|
||||||
case DXVAHD_DEINTERLACE_MODE_PRIVATE::DISI1:
|
case DxvhadDeinterlaceModePrivate::Disi1:
|
||||||
// Due to the fact that we do not write to memory in nvdec, we cannot use DISI1 as it
|
// Due to the fact that we do not write to memory in nvdec, we cannot use DISI1 as it
|
||||||
// relies on previous/next frames.
|
// relies on previous/next frames.
|
||||||
DecodeBobField();
|
DecodeBobField();
|
||||||
|
|
@ -431,13 +431,13 @@ void Vic::ReadInterlacedY8__V8U8_N420(const SlotStruct& slot, std::span<const Pl
|
||||||
|
|
||||||
void Vic::ReadY8__V8U8_N420(const SlotStruct& slot, std::span<const PlaneOffsets> offsets, std::shared_ptr<const FFmpeg::Frame> frame, bool planar) noexcept {
|
void Vic::ReadY8__V8U8_N420(const SlotStruct& slot, std::span<const PlaneOffsets> offsets, std::shared_ptr<const FFmpeg::Frame> frame, bool planar) noexcept {
|
||||||
switch (slot.config.frame_format) {
|
switch (slot.config.frame_format) {
|
||||||
case DXVAHD_FRAME_FORMAT::PROGRESSIVE:
|
case DxvhadFrameFormat::Progressive:
|
||||||
ReadProgressiveY8__V8U8_N420(slot, offsets, std::move(frame), planar, false);
|
ReadProgressiveY8__V8U8_N420(slot, offsets, std::move(frame), planar, false);
|
||||||
break;
|
break;
|
||||||
case DXVAHD_FRAME_FORMAT::TOP_FIELD:
|
case DxvhadFrameFormat::TopField:
|
||||||
ReadInterlacedY8__V8U8_N420(slot, offsets, std::move(frame), planar, true);
|
ReadInterlacedY8__V8U8_N420(slot, offsets, std::move(frame), planar, true);
|
||||||
break;
|
break;
|
||||||
case DXVAHD_FRAME_FORMAT::BOTTOM_FIELD:
|
case DxvhadFrameFormat::BottomField:
|
||||||
ReadInterlacedY8__V8U8_N420(slot, offsets, std::move(frame), planar, false);
|
ReadInterlacedY8__V8U8_N420(slot, offsets, std::move(frame), planar, false);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
@ -860,7 +860,7 @@ void Vic::WriteY8__V8U8_N420(const OutputSurfaceConfig& output_surface_config) n
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (output_surface_config.out_block_kind) {
|
switch (output_surface_config.out_block_kind) {
|
||||||
case BLK_KIND::GENERIC_16Bx2: {
|
case BlkKind::Generic_16Bx2: {
|
||||||
u32 const block_height = u32(output_surface_config.out_block_height);
|
u32 const block_height = u32(output_surface_config.out_block_height);
|
||||||
auto const out_luma_swizzle_size = Texture::CalculateSize(true, BytesPerPixel, out_luma_width, out_luma_height, 1, block_height, 0);
|
auto const out_luma_swizzle_size = Texture::CalculateSize(true, BytesPerPixel, out_luma_width, out_luma_height, 1, block_height, 0);
|
||||||
auto const out_chroma_swizzle_size = Texture::CalculateSize(true, BytesPerPixel * 2, out_chroma_width, out_chroma_height, 1, block_height, 0);
|
auto const out_chroma_swizzle_size = Texture::CalculateSize(true, BytesPerPixel * 2, out_chroma_width, out_chroma_height, 1, block_height, 0);
|
||||||
|
|
@ -889,7 +889,7 @@ void Vic::WriteY8__V8U8_N420(const OutputSurfaceConfig& output_surface_config) n
|
||||||
Texture::SwizzleTexture(out_chroma, chroma_scratch, BytesPerPixel, out_chroma_width, out_chroma_height, 1, block_height, 0, 1);
|
Texture::SwizzleTexture(out_chroma, chroma_scratch, BytesPerPixel, out_chroma_width, out_chroma_height, 1, block_height, 0, 1);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case BLK_KIND::PITCH: {
|
case BlkKind::Pitch: {
|
||||||
LOG_TRACE(HW_GPU, "Writing Y8__V8U8_N420 swizzled frame\n"
|
LOG_TRACE(HW_GPU, "Writing Y8__V8U8_N420 swizzled frame\n"
|
||||||
"\tinput surface {}x{} stride {} size {:#X}\n"
|
"\tinput surface {}x{} stride {} size {:#X}\n"
|
||||||
"\toutput luma {}x{} stride {} size {:#X} block height {} swizzled size 0x{:X}\n",
|
"\toutput luma {}x{} stride {} size {:#X} block height {} swizzled size 0x{:X}\n",
|
||||||
|
|
@ -1032,7 +1032,7 @@ void Vic::WriteABGR(const OutputSurfaceConfig& output_surface_config, VideoPixel
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (output_surface_config.out_block_kind) {
|
switch (output_surface_config.out_block_kind) {
|
||||||
case BLK_KIND::GENERIC_16Bx2: {
|
case BlkKind::Generic_16Bx2: {
|
||||||
const u32 block_height = u32(output_surface_config.out_block_height);
|
const u32 block_height = u32(output_surface_config.out_block_height);
|
||||||
auto const out_swizzle_size = Texture::CalculateSize(true, BytesPerPixel, out_luma_width, out_luma_height, 1, block_height, 0);
|
auto const out_swizzle_size = Texture::CalculateSize(true, BytesPerPixel, out_luma_width, out_luma_height, 1, block_height, 0);
|
||||||
LOG_TRACE(HW_GPU, "Writing ABGR swizzled frame\n"
|
LOG_TRACE(HW_GPU, "Writing ABGR swizzled frame\n"
|
||||||
|
|
@ -1051,7 +1051,7 @@ void Vic::WriteABGR(const OutputSurfaceConfig& output_surface_config, VideoPixel
|
||||||
Texture::SwizzleTexture(out_luma, luma_scratch, BytesPerPixel, out_luma_width, out_luma_height, 1, block_height, 0, 1);
|
Texture::SwizzleTexture(out_luma, luma_scratch, BytesPerPixel, out_luma_width, out_luma_height, 1, block_height, 0, 1);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case BLK_KIND::PITCH: {
|
case BlkKind::Pitch: {
|
||||||
LOG_TRACE(HW_GPU, "Writing ABGR pitch frame\n"
|
LOG_TRACE(HW_GPU, "Writing ABGR pitch frame\n"
|
||||||
"\tinput surface {}x{} stride {} size {:#X}"
|
"\tinput surface {}x{} stride {} size {:#X}"
|
||||||
"\toutput surface {}x{} stride {} size {:#X}",
|
"\toutput surface {}x{} stride {} size {:#X}",
|
||||||
|
|
|
||||||
|
|
@ -6,16 +6,12 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/scratch_buffer.h"
|
#include "common/scratch_buffer.h"
|
||||||
#include "video_core/cdma_pusher.h"
|
#include "video_core/cdma_pusher.h"
|
||||||
#include "video_core/host1x/host1x.h"
|
#include "video_core/host1x/ffmpeg.h"
|
||||||
|
|
||||||
namespace Tegra::Host1x {
|
namespace Tegra::Host1x {
|
||||||
class Host1x;
|
class Host1x;
|
||||||
|
|
@ -138,52 +134,53 @@ enum SurfaceIndex : u32 {
|
||||||
CombinedMotion = 7,
|
CombinedMotion = 7,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class DXVAHD_ALPHA_FILL_MODE : u32 {
|
// Note: these will inevitably collide with Win32 defines if you use their UPPER_SNAKE_CASE naming
|
||||||
OPAQUE = 0,
|
enum class DxvhadAlphaFillMode : u32 {
|
||||||
BACKGROUND = 1,
|
Opaque = 0,
|
||||||
DESTINATION = 2,
|
Background = 1,
|
||||||
SOURCE_STREAM = 3,
|
Destination = 2,
|
||||||
COMPOSITED = 4,
|
SourceStream = 3,
|
||||||
SOURCE_ALPHA = 5,
|
Composited = 4,
|
||||||
|
SourceAlpha = 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class DXVAHD_FRAME_FORMAT : u64 {
|
enum class DxvhadFrameFormat : u64 {
|
||||||
PROGRESSIVE = 0,
|
Progressive = 0,
|
||||||
INTERLACED_TOP_FIELD_FIRST = 1,
|
InterlacedTopFieldFirst = 1,
|
||||||
INTERLACED_BOTTOM_FIELD_FIRST = 2,
|
InterlacedBottomFieldFirst = 2,
|
||||||
TOP_FIELD = 3,
|
TopField = 3,
|
||||||
BOTTOM_FIELD = 4,
|
BottomField = 4,
|
||||||
SUBPIC_PROGRESSIVE = 5,
|
SubpicProgressive = 5,
|
||||||
SUBPIC_INTERLACED_TOP_FIELD_FIRST = 6,
|
SubpicInterlacedTopFieldFirst = 6,
|
||||||
SUBPIC_INTERLACED_BOTTOM_FIELD_FIRST = 7,
|
SubpicInterlacedBottomFieldFirst = 7,
|
||||||
SUBPIC_TOP_FIELD = 8,
|
SubpicTopField = 8,
|
||||||
SUBPIC_BOTTOM_FIELD = 9,
|
SubpicBottomField = 9,
|
||||||
TOP_FIELD_CHROMA_BOTTOM = 10,
|
TopFieldChromaBottom = 10,
|
||||||
BOTTOM_FIELD_CHROMA_TOP = 11,
|
BottomFieldChromaTop = 11,
|
||||||
SUBPIC_TOP_FIELD_CHROMA_BOTTOM = 12,
|
SubpicTopFieldChromaBottom = 12,
|
||||||
SUBPIC_BOTTOM_FIELD_CHROMA_TOP = 13,
|
SubpicBottomFieldChromaTop = 13,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class DXVAHD_DEINTERLACE_MODE_PRIVATE : u64 {
|
enum class DxvhadDeinterlaceModePrivate : u64 {
|
||||||
WEAVE = 0,
|
Weave = 0,
|
||||||
BOB_FIELD = 1,
|
BobField = 1,
|
||||||
BOB = 2,
|
Bob = 2,
|
||||||
NEWBOB = 3,
|
Newbob = 3,
|
||||||
DISI1 = 4,
|
Disi1 = 4,
|
||||||
WEAVE_LUMA_BOB_FIELD_CHROMA = 5,
|
WeaveLumaBobFieldChroma = 5,
|
||||||
MAX = 0xF,
|
Max = 0xF,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class BLK_KIND {
|
enum class BlkKind {
|
||||||
PITCH = 0,
|
Pitch = 0,
|
||||||
GENERIC_16Bx2 = 1,
|
Generic_16Bx2 = 1,
|
||||||
// These are unsupported in the vic
|
// These are unsupported in the vic
|
||||||
BL_NAIVE = 2,
|
BlNaive = 2,
|
||||||
BL_KEPLER_XBAR_RAW = 3,
|
BlKeplerXbarRaw = 3,
|
||||||
VP2_TILED = 15,
|
Vp2Tiled = 15,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class BLEND_SRCFACTC : u32 {
|
enum class BlendSrcFactC : u32 {
|
||||||
K1 = 0,
|
K1 = 0,
|
||||||
K1_TIMES_DST = 1,
|
K1_TIMES_DST = 1,
|
||||||
NEG_K1_TIMES_DST = 2,
|
NEG_K1_TIMES_DST = 2,
|
||||||
|
|
@ -191,7 +188,7 @@ enum class BLEND_SRCFACTC : u32 {
|
||||||
ZERO = 4,
|
ZERO = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class BLEND_DSTFACTC : u32 {
|
enum class BlendDstFactC : u32 {
|
||||||
K1 = 0,
|
K1 = 0,
|
||||||
K2 = 1,
|
K2 = 1,
|
||||||
K1_TIMES_DST = 2,
|
K1_TIMES_DST = 2,
|
||||||
|
|
@ -201,7 +198,7 @@ enum class BLEND_DSTFACTC : u32 {
|
||||||
ONE = 6,
|
ONE = 6,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class BLEND_SRCFACTA : u32 {
|
enum class BlendSrcFactA : u32 {
|
||||||
K1 = 0,
|
K1 = 0,
|
||||||
K2 = 1,
|
K2 = 1,
|
||||||
NEG_K1_TIMES_DST = 2,
|
NEG_K1_TIMES_DST = 2,
|
||||||
|
|
@ -209,7 +206,7 @@ enum class BLEND_SRCFACTA : u32 {
|
||||||
MAX = 7,
|
MAX = 7,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class BLEND_DSTFACTA : u32 {
|
enum class BlendDstFactA : u32 {
|
||||||
K2 = 0,
|
K2 = 0,
|
||||||
NEG_K1_TIMES_SRC = 1,
|
NEG_K1_TIMES_SRC = 1,
|
||||||
ZERO = 2,
|
ZERO = 2,
|
||||||
|
|
@ -232,7 +229,7 @@ static_assert(sizeof(PipeConfig) == 0x10, "PipeConfig has the wrong size!");
|
||||||
|
|
||||||
struct OutputConfig {
|
struct OutputConfig {
|
||||||
union {
|
union {
|
||||||
BitField<0, 3, DXVAHD_ALPHA_FILL_MODE> alpha_fill_mode;
|
BitField<0, 3, DxvhadAlphaFillMode> alpha_fill_mode;
|
||||||
BitField<3, 3, u64> alpha_fill_slot;
|
BitField<3, 3, u64> alpha_fill_slot;
|
||||||
BitField<6, 10, u64> background_a;
|
BitField<6, 10, u64> background_a;
|
||||||
BitField<16, 10, u64> background_r;
|
BitField<16, 10, u64> background_r;
|
||||||
|
|
@ -265,7 +262,7 @@ struct OutputSurfaceConfig {
|
||||||
BitField<0, 7, VideoPixelFormat> out_pixel_format;
|
BitField<0, 7, VideoPixelFormat> out_pixel_format;
|
||||||
BitField<7, 2, u32> out_chroma_loc_horiz;
|
BitField<7, 2, u32> out_chroma_loc_horiz;
|
||||||
BitField<9, 2, u32> out_chroma_loc_vert;
|
BitField<9, 2, u32> out_chroma_loc_vert;
|
||||||
BitField<11, 4, BLK_KIND> out_block_kind;
|
BitField<11, 4, BlkKind> out_block_kind;
|
||||||
BitField<15, 4, u32> out_block_height; // in gobs, log2
|
BitField<15, 4, u32> out_block_height; // in gobs, log2
|
||||||
BitField<19, 3, u32> reserved0;
|
BitField<19, 3, u32> reserved0;
|
||||||
BitField<22, 10, u32> reserved1;
|
BitField<22, 10, u32> reserved1;
|
||||||
|
|
@ -365,7 +362,7 @@ struct SlotConfig {
|
||||||
BitField<14, 1, u64> prev_prev_motion_field_enable;
|
BitField<14, 1, u64> prev_prev_motion_field_enable;
|
||||||
BitField<15, 1, u64> combined_motion_field_enable;
|
BitField<15, 1, u64> combined_motion_field_enable;
|
||||||
|
|
||||||
BitField<16, 4, DXVAHD_FRAME_FORMAT> frame_format;
|
BitField<16, 4, DxvhadFrameFormat> frame_format;
|
||||||
BitField<20, 2, u64> filter_length_y; // 0: 1-tap, 1: 2-tap, 2: 5-tap, 3: 10-tap
|
BitField<20, 2, u64> filter_length_y; // 0: 1-tap, 1: 2-tap, 2: 5-tap, 3: 10-tap
|
||||||
BitField<22, 2, u64> filter_length_x;
|
BitField<22, 2, u64> filter_length_x;
|
||||||
BitField<24, 12, u64> panoramic;
|
BitField<24, 12, u64> panoramic;
|
||||||
|
|
@ -377,7 +374,7 @@ struct SlotConfig {
|
||||||
BitField<10, 10, u64> filter_detail;
|
BitField<10, 10, u64> filter_detail;
|
||||||
BitField<20, 10, u64> chroma_noise;
|
BitField<20, 10, u64> chroma_noise;
|
||||||
BitField<30, 10, u64> chroma_detail;
|
BitField<30, 10, u64> chroma_detail;
|
||||||
BitField<40, 4, DXVAHD_DEINTERLACE_MODE_PRIVATE> deinterlace_mode;
|
BitField<40, 4, DxvhadDeinterlaceModePrivate> deinterlace_mode;
|
||||||
BitField<44, 3, u64> motion_accumulation_weight;
|
BitField<44, 3, u64> motion_accumulation_weight;
|
||||||
BitField<47, 11, u64> noise_iir;
|
BitField<47, 11, u64> noise_iir;
|
||||||
BitField<58, 4, u64> light_level;
|
BitField<58, 4, u64> light_level;
|
||||||
|
|
@ -484,13 +481,13 @@ struct BlendingSlotStruct {
|
||||||
BitField<26, 6, u32> reserved1;
|
BitField<26, 6, u32> reserved1;
|
||||||
};
|
};
|
||||||
union {
|
union {
|
||||||
BitField<0, 3, BLEND_SRCFACTC> src_factor_color_match_select;
|
BitField<0, 3, BlendSrcFactC> src_factor_color_match_select;
|
||||||
BitField<3, 1, u32> reserved2;
|
BitField<3, 1, u32> reserved2;
|
||||||
BitField<4, 3, BLEND_DSTFACTC> dst_factor_color_match_select;
|
BitField<4, 3, BlendDstFactC> dst_factor_color_match_select;
|
||||||
BitField<7, 1, u32> reserved3;
|
BitField<7, 1, u32> reserved3;
|
||||||
BitField<8, 3, BLEND_SRCFACTA> src_factor_a_match_select;
|
BitField<8, 3, BlendSrcFactA> src_factor_a_match_select;
|
||||||
BitField<11, 1, u32> reserved4;
|
BitField<11, 1, u32> reserved4;
|
||||||
BitField<12, 3, BLEND_DSTFACTA> dst_factor_a_match_select;
|
BitField<12, 3, BlendDstFactA> dst_factor_a_match_select;
|
||||||
BitField<15, 1, u32> reserved5;
|
BitField<15, 1, u32> reserved5;
|
||||||
BitField<16, 4, u32> reserved6;
|
BitField<16, 4, u32> reserved6;
|
||||||
BitField<20, 4, u32> reserved7;
|
BitField<20, 4, u32> reserved7;
|
||||||
|
|
@ -624,8 +621,8 @@ private:
|
||||||
VicRegisters regs{};
|
VicRegisters regs{};
|
||||||
|
|
||||||
Common::ScratchBuffer<u8> swizzle_scratch;
|
Common::ScratchBuffer<u8> swizzle_scratch;
|
||||||
Common::ScratchBuffer<Pixel> output_surface;
|
Common::ScratchBuffer<Tegra::Host1x::Pixel> output_surface;
|
||||||
Common::ScratchBuffer<Pixel> slot_surface;
|
Common::ScratchBuffer<Tegra::Host1x::Pixel> slot_surface;
|
||||||
Common::ScratchBuffer<u8> luma_scratch;
|
Common::ScratchBuffer<u8> luma_scratch;
|
||||||
Common::ScratchBuffer<u8> chroma_scratch;
|
Common::ScratchBuffer<u8> chroma_scratch;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,10 +64,10 @@ bool IsTopologySafe(Maxwell3D::Regs::PrimitiveTopology topology) {
|
||||||
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
void HLE_DrawArraysIndirect::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
void HLE_DrawArraysIndirect::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||||
auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[0]);
|
auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[0]);
|
||||||
if (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology)) {
|
if (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology)) {
|
||||||
Fallback(maxwell3d, parameters);
|
Fallback(system, maxwell3d, parameters);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,7 +93,7 @@ void HLE_DrawArraysIndirect::Execute(Engines::Maxwell3D& maxwell3d, std::span<co
|
||||||
maxwell3d.replace_table.clear();
|
maxwell3d.replace_table.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void HLE_DrawArraysIndirect::Fallback(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters) {
|
void HLE_DrawArraysIndirect::Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters) {
|
||||||
SCOPE_EXIT {
|
SCOPE_EXIT {
|
||||||
if (extended) {
|
if (extended) {
|
||||||
maxwell3d.engine_state = Maxwell3D::EngineHint::None;
|
maxwell3d.engine_state = Maxwell3D::EngineHint::None;
|
||||||
|
|
@ -123,10 +123,10 @@ void HLE_DrawArraysIndirect::Fallback(Engines::Maxwell3D& maxwell3d, std::span<c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HLE_DrawIndexedIndirect::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
void HLE_DrawIndexedIndirect::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||||
auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[0]);
|
auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[0]);
|
||||||
if (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology)) {
|
if (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology)) {
|
||||||
Fallback(maxwell3d, parameters);
|
Fallback(system, maxwell3d, parameters);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -161,7 +161,7 @@ void HLE_DrawIndexedIndirect::Execute(Engines::Maxwell3D& maxwell3d, std::span<c
|
||||||
maxwell3d.replace_table.clear();
|
maxwell3d.replace_table.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void HLE_DrawIndexedIndirect::Fallback(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters) {
|
void HLE_DrawIndexedIndirect::Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters) {
|
||||||
maxwell3d.RefreshParameters();
|
maxwell3d.RefreshParameters();
|
||||||
const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]);
|
const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]);
|
||||||
const u32 element_base = parameters[4];
|
const u32 element_base = parameters[4];
|
||||||
|
|
@ -184,7 +184,7 @@ void HLE_DrawIndexedIndirect::Fallback(Engines::Maxwell3D& maxwell3d, std::span<
|
||||||
maxwell3d.replace_table.clear();
|
maxwell3d.replace_table.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void HLE_MultiLayerClear::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
void HLE_MultiLayerClear::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||||
maxwell3d.RefreshParameters();
|
maxwell3d.RefreshParameters();
|
||||||
ASSERT(parameters.size() == 1);
|
ASSERT(parameters.size() == 1);
|
||||||
|
|
||||||
|
|
@ -196,47 +196,44 @@ void HLE_MultiLayerClear::Execute(Engines::Maxwell3D& maxwell3d, std::span<const
|
||||||
maxwell3d.regs.clear_surface.raw = clear_params.raw;
|
maxwell3d.regs.clear_surface.raw = clear_params.raw;
|
||||||
maxwell3d.draw_manager.Clear(maxwell3d, num_layers);
|
maxwell3d.draw_manager.Clear(maxwell3d, num_layers);
|
||||||
}
|
}
|
||||||
void HLE_MultiDrawIndexedIndirectCount::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
void HLE_MultiDrawIndexedIndirectCount::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||||
const auto topology = Maxwell3D::Regs::PrimitiveTopology(parameters[2]);
|
const auto topology = Maxwell3D::Regs::PrimitiveTopology(parameters[2]);
|
||||||
if (!IsTopologySafe(topology)) {
|
if (IsTopologySafe(topology)) {
|
||||||
Fallback(maxwell3d, parameters);
|
const u32 start_indirect = parameters[0];
|
||||||
return;
|
const u32 end_indirect = parameters[1];
|
||||||
|
if (start_indirect >= end_indirect) {
|
||||||
|
// Nothing to do.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const u32 padding = parameters[3]; // padding is in words
|
||||||
|
// size of each indirect segment
|
||||||
|
const u32 indirect_words = 5 + padding;
|
||||||
|
const u32 stride = indirect_words * sizeof(u32);
|
||||||
|
const std::size_t draw_count = end_indirect - start_indirect;
|
||||||
|
const u32 estimate = u32(maxwell3d.EstimateIndexBufferSize());
|
||||||
|
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
|
||||||
|
auto& params = maxwell3d.draw_manager.indirect_state;
|
||||||
|
params.is_byte_count = false;
|
||||||
|
params.is_indexed = true;
|
||||||
|
params.include_count = true;
|
||||||
|
params.count_start_address = maxwell3d.GetMacroAddress(4);
|
||||||
|
params.indirect_start_address = maxwell3d.GetMacroAddress(5);
|
||||||
|
params.buffer_size = stride * draw_count;
|
||||||
|
params.max_draw_counts = draw_count;
|
||||||
|
params.stride = stride;
|
||||||
|
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
|
||||||
|
maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro;
|
||||||
|
maxwell3d.SetHLEReplacementAttributeType(0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseVertex);
|
||||||
|
maxwell3d.SetHLEReplacementAttributeType(0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance);
|
||||||
|
maxwell3d.SetHLEReplacementAttributeType(0, 0x648, Maxwell3D::HLEReplacementAttributeType::DrawID);
|
||||||
|
maxwell3d.draw_manager.DrawIndexedIndirect(maxwell3d, topology, 0, estimate);
|
||||||
|
maxwell3d.engine_state = Maxwell3D::EngineHint::None;
|
||||||
|
maxwell3d.replace_table.clear();
|
||||||
|
} else {
|
||||||
|
Fallback(system, maxwell3d, parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
const u32 start_indirect = parameters[0];
|
|
||||||
const u32 end_indirect = parameters[1];
|
|
||||||
if (start_indirect >= end_indirect) {
|
|
||||||
// Nothing to do.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const u32 padding = parameters[3]; // padding is in words
|
|
||||||
|
|
||||||
// size of each indirect segment
|
|
||||||
const u32 indirect_words = 5 + padding;
|
|
||||||
const u32 stride = indirect_words * sizeof(u32);
|
|
||||||
const std::size_t draw_count = end_indirect - start_indirect;
|
|
||||||
const u32 estimate = static_cast<u32>(maxwell3d.EstimateIndexBufferSize());
|
|
||||||
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
|
|
||||||
auto& params = maxwell3d.draw_manager.indirect_state;
|
|
||||||
params.is_byte_count = false;
|
|
||||||
params.is_indexed = true;
|
|
||||||
params.include_count = true;
|
|
||||||
params.count_start_address = maxwell3d.GetMacroAddress(4);
|
|
||||||
params.indirect_start_address = maxwell3d.GetMacroAddress(5);
|
|
||||||
params.buffer_size = stride * draw_count;
|
|
||||||
params.max_draw_counts = draw_count;
|
|
||||||
params.stride = stride;
|
|
||||||
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
|
|
||||||
maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro;
|
|
||||||
maxwell3d.SetHLEReplacementAttributeType(0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseVertex);
|
|
||||||
maxwell3d.SetHLEReplacementAttributeType(0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance);
|
|
||||||
maxwell3d.SetHLEReplacementAttributeType(0, 0x648, Maxwell3D::HLEReplacementAttributeType::DrawID);
|
|
||||||
maxwell3d.draw_manager.DrawIndexedIndirect(maxwell3d, topology, 0, estimate);
|
|
||||||
maxwell3d.engine_state = Maxwell3D::EngineHint::None;
|
|
||||||
maxwell3d.replace_table.clear();
|
|
||||||
}
|
}
|
||||||
void HLE_MultiDrawIndexedIndirectCount::Fallback(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters) {
|
void HLE_MultiDrawIndexedIndirectCount::Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters) {
|
||||||
SCOPE_EXIT {
|
SCOPE_EXIT {
|
||||||
// Clean everything.
|
// Clean everything.
|
||||||
maxwell3d.regs.vertex_id_base = 0x0;
|
maxwell3d.regs.vertex_id_base = 0x0;
|
||||||
|
|
@ -250,7 +247,7 @@ void HLE_MultiDrawIndexedIndirectCount::Fallback(Engines::Maxwell3D& maxwell3d,
|
||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[2]);
|
const auto topology = Maxwell3D::Regs::PrimitiveTopology(parameters[2]);
|
||||||
const u32 padding = parameters[3];
|
const u32 padding = parameters[3];
|
||||||
const std::size_t max_draws = parameters[4];
|
const std::size_t max_draws = parameters[4];
|
||||||
const u32 indirect_words = 5 + padding;
|
const u32 indirect_words = 5 + padding;
|
||||||
|
|
@ -265,41 +262,41 @@ void HLE_MultiDrawIndexedIndirectCount::Fallback(Engines::Maxwell3D& maxwell3d,
|
||||||
maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro;
|
maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro;
|
||||||
maxwell3d.SetHLEReplacementAttributeType(0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseVertex);
|
maxwell3d.SetHLEReplacementAttributeType(0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseVertex);
|
||||||
maxwell3d.SetHLEReplacementAttributeType(0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance);
|
maxwell3d.SetHLEReplacementAttributeType(0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance);
|
||||||
maxwell3d.CallMethod(0x8e3, 0x648, true);
|
maxwell3d.CallMethod(system, 0x8e3, 0x648, true);
|
||||||
maxwell3d.CallMethod(0x8e4, static_cast<u32>(index), true);
|
maxwell3d.CallMethod(system, 0x8e4, u32(index), true);
|
||||||
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
|
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
|
||||||
maxwell3d.draw_manager.DrawIndex(maxwell3d, topology, parameters[base + 2], parameters[base], base_vertex, base_instance, parameters[base + 1]);
|
maxwell3d.draw_manager.DrawIndex(maxwell3d, topology, parameters[base + 2], parameters[base], base_vertex, base_instance, parameters[base + 1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void HLE_DrawIndirectByteCount::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
void HLE_DrawIndirectByteCount::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||||
const bool force = maxwell3d.Rasterizer().HasDrawTransformFeedback();
|
const bool force = maxwell3d.Rasterizer().HasDrawTransformFeedback();
|
||||||
if (!force) {
|
if (force) {
|
||||||
Fallback(maxwell3d, parameters);
|
auto topology = Maxwell3D::Regs::PrimitiveTopology(parameters[0] & 0xFFFFU);
|
||||||
return;
|
auto& params = maxwell3d.draw_manager.indirect_state;
|
||||||
|
params.is_byte_count = true;
|
||||||
|
params.is_indexed = false;
|
||||||
|
params.include_count = false;
|
||||||
|
params.count_start_address = 0;
|
||||||
|
params.indirect_start_address = maxwell3d.GetMacroAddress(2);
|
||||||
|
params.buffer_size = 4;
|
||||||
|
params.max_draw_counts = 1;
|
||||||
|
params.stride = parameters[1];
|
||||||
|
maxwell3d.regs.draw.begin = parameters[0];
|
||||||
|
maxwell3d.regs.draw_auto_stride = parameters[1];
|
||||||
|
maxwell3d.regs.draw_auto_byte_count = parameters[2];
|
||||||
|
maxwell3d.draw_manager.DrawArrayIndirect(maxwell3d, topology);
|
||||||
|
} else {
|
||||||
|
Fallback(system, maxwell3d, parameters);
|
||||||
}
|
}
|
||||||
auto topology = Maxwell3D::Regs::PrimitiveTopology(parameters[0] & 0xFFFFU);
|
|
||||||
auto& params = maxwell3d.draw_manager.indirect_state;
|
|
||||||
params.is_byte_count = true;
|
|
||||||
params.is_indexed = false;
|
|
||||||
params.include_count = false;
|
|
||||||
params.count_start_address = 0;
|
|
||||||
params.indirect_start_address = maxwell3d.GetMacroAddress(2);
|
|
||||||
params.buffer_size = 4;
|
|
||||||
params.max_draw_counts = 1;
|
|
||||||
params.stride = parameters[1];
|
|
||||||
maxwell3d.regs.draw.begin = parameters[0];
|
|
||||||
maxwell3d.regs.draw_auto_stride = parameters[1];
|
|
||||||
maxwell3d.regs.draw_auto_byte_count = parameters[2];
|
|
||||||
maxwell3d.draw_manager.DrawArrayIndirect(maxwell3d, topology);
|
|
||||||
}
|
}
|
||||||
void HLE_DrawIndirectByteCount::Fallback(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters) {
|
void HLE_DrawIndirectByteCount::Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters) {
|
||||||
maxwell3d.RefreshParameters();
|
maxwell3d.RefreshParameters();
|
||||||
maxwell3d.regs.draw.begin = parameters[0];
|
maxwell3d.regs.draw.begin = parameters[0];
|
||||||
maxwell3d.regs.draw_auto_stride = parameters[1];
|
maxwell3d.regs.draw_auto_stride = parameters[1];
|
||||||
maxwell3d.regs.draw_auto_byte_count = parameters[2];
|
maxwell3d.regs.draw_auto_byte_count = parameters[2];
|
||||||
maxwell3d.draw_manager.DrawArray(maxwell3d, maxwell3d.regs.draw.topology, 0, maxwell3d.regs.draw_auto_byte_count / maxwell3d.regs.draw_auto_stride, 0, 1);
|
maxwell3d.draw_manager.DrawArray(maxwell3d, maxwell3d.regs.draw.topology, 0, maxwell3d.regs.draw_auto_byte_count / maxwell3d.regs.draw_auto_stride, 0, 1);
|
||||||
}
|
}
|
||||||
void HLE_C713C83D8F63CCF3::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
void HLE_C713C83D8F63CCF3::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||||
maxwell3d.RefreshParameters();
|
maxwell3d.RefreshParameters();
|
||||||
const u32 offset = (parameters[0] & 0x3FFFFFFF) << 2;
|
const u32 offset = (parameters[0] & 0x3FFFFFFF) << 2;
|
||||||
const u32 address = maxwell3d.regs.shadow_scratch[24];
|
const u32 address = maxwell3d.regs.shadow_scratch[24];
|
||||||
|
|
@ -309,7 +306,7 @@ void HLE_C713C83D8F63CCF3::Execute(Engines::Maxwell3D& maxwell3d, std::span<cons
|
||||||
const_buffer.address_low = address << 8;
|
const_buffer.address_low = address << 8;
|
||||||
const_buffer.offset = offset;
|
const_buffer.offset = offset;
|
||||||
}
|
}
|
||||||
void HLE_D7333D26E0A93EDE::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
void HLE_D7333D26E0A93EDE::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||||
maxwell3d.RefreshParameters();
|
maxwell3d.RefreshParameters();
|
||||||
const size_t index = parameters[0];
|
const size_t index = parameters[0];
|
||||||
const u32 address = maxwell3d.regs.shadow_scratch[42 + index];
|
const u32 address = maxwell3d.regs.shadow_scratch[42 + index];
|
||||||
|
|
@ -319,7 +316,7 @@ void HLE_D7333D26E0A93EDE::Execute(Engines::Maxwell3D& maxwell3d, std::span<cons
|
||||||
const_buffer.address_high = (address >> 24) & 0xFF;
|
const_buffer.address_high = (address >> 24) & 0xFF;
|
||||||
const_buffer.address_low = address << 8;
|
const_buffer.address_low = address << 8;
|
||||||
}
|
}
|
||||||
void HLE_BindShader::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
void HLE_BindShader::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||||
maxwell3d.RefreshParameters();
|
maxwell3d.RefreshParameters();
|
||||||
auto& regs = maxwell3d.regs;
|
auto& regs = maxwell3d.regs;
|
||||||
const u32 index = parameters[0];
|
const u32 index = parameters[0];
|
||||||
|
|
@ -343,7 +340,7 @@ void HLE_BindShader::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32>
|
||||||
bind_group.raw_config = 0x11;
|
bind_group.raw_config = 0x11;
|
||||||
maxwell3d.ProcessCBBind(bind_group_id);
|
maxwell3d.ProcessCBBind(bind_group_id);
|
||||||
}
|
}
|
||||||
void HLE_SetRasterBoundingBox::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
void HLE_SetRasterBoundingBox::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||||
maxwell3d.RefreshParameters();
|
maxwell3d.RefreshParameters();
|
||||||
const u32 raster_mode = parameters[0];
|
const u32 raster_mode = parameters[0];
|
||||||
auto& regs = maxwell3d.regs;
|
auto& regs = maxwell3d.regs;
|
||||||
|
|
@ -352,7 +349,7 @@ void HLE_SetRasterBoundingBox::Execute(Engines::Maxwell3D& maxwell3d, std::span<
|
||||||
regs.raster_bounding_box.raw = raster_mode & 0xFFFFF00F;
|
regs.raster_bounding_box.raw = raster_mode & 0xFFFFF00F;
|
||||||
regs.raster_bounding_box.pad.Assign(scratch_data & raster_enabled);
|
regs.raster_bounding_box.pad.Assign(scratch_data & raster_enabled);
|
||||||
}
|
}
|
||||||
void HLE_ClearConstBuffer::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
void HLE_ClearConstBuffer::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||||
static constexpr std::array<u32, 0x7000> zeroes{}; //must be bigger than either 7000 or 5F00
|
static constexpr std::array<u32, 0x7000> zeroes{}; //must be bigger than either 7000 or 5F00
|
||||||
maxwell3d.RefreshParameters();
|
maxwell3d.RefreshParameters();
|
||||||
auto& regs = maxwell3d.regs;
|
auto& regs = maxwell3d.regs;
|
||||||
|
|
@ -362,7 +359,7 @@ void HLE_ClearConstBuffer::Execute(Engines::Maxwell3D& maxwell3d, std::span<cons
|
||||||
regs.const_buffer.offset = 0;
|
regs.const_buffer.offset = 0;
|
||||||
maxwell3d.ProcessCBMultiData(zeroes.data(), parameters[2] * 4);
|
maxwell3d.ProcessCBMultiData(zeroes.data(), parameters[2] * 4);
|
||||||
}
|
}
|
||||||
void HLE_ClearMemory::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
void HLE_ClearMemory::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||||
maxwell3d.RefreshParameters();
|
maxwell3d.RefreshParameters();
|
||||||
const u32 needed_memory = parameters[2] / sizeof(u32);
|
const u32 needed_memory = parameters[2] / sizeof(u32);
|
||||||
if (needed_memory > zero_memory.size()) {
|
if (needed_memory > zero_memory.size()) {
|
||||||
|
|
@ -373,10 +370,10 @@ void HLE_ClearMemory::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32
|
||||||
regs.upload.line_count = 1;
|
regs.upload.line_count = 1;
|
||||||
regs.upload.dest.address_high = parameters[0];
|
regs.upload.dest.address_high = parameters[0];
|
||||||
regs.upload.dest.address_low = parameters[1];
|
regs.upload.dest.address_low = parameters[1];
|
||||||
maxwell3d.CallMethod(size_t(MAXWELL3D_REG_INDEX(launch_dma)), 0x1011, true);
|
maxwell3d.CallMethod(system, size_t(MAXWELL3D_REG_INDEX(launch_dma)), 0x1011, true);
|
||||||
maxwell3d.CallMultiMethod(size_t(MAXWELL3D_REG_INDEX(inline_data)), zero_memory.data(), needed_memory, needed_memory);
|
maxwell3d.CallMultiMethod(system, size_t(MAXWELL3D_REG_INDEX(inline_data)), zero_memory.data(), needed_memory, needed_memory);
|
||||||
}
|
}
|
||||||
void HLE_TransformFeedbackSetup::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
void HLE_TransformFeedbackSetup::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||||
maxwell3d.RefreshParameters();
|
maxwell3d.RefreshParameters();
|
||||||
auto& regs = maxwell3d.regs;
|
auto& regs = maxwell3d.regs;
|
||||||
regs.transform_feedback_enabled = 1;
|
regs.transform_feedback_enabled = 1;
|
||||||
|
|
@ -388,8 +385,8 @@ void HLE_TransformFeedbackSetup::Execute(Engines::Maxwell3D& maxwell3d, std::spa
|
||||||
regs.upload.line_count = 1;
|
regs.upload.line_count = 1;
|
||||||
regs.upload.dest.address_high = parameters[0];
|
regs.upload.dest.address_high = parameters[0];
|
||||||
regs.upload.dest.address_low = parameters[1];
|
regs.upload.dest.address_low = parameters[1];
|
||||||
maxwell3d.CallMethod(size_t(MAXWELL3D_REG_INDEX(launch_dma)), 0x1011, true);
|
maxwell3d.CallMethod(system, size_t(MAXWELL3D_REG_INDEX(launch_dma)), 0x1011, true);
|
||||||
maxwell3d.CallMethod(size_t(MAXWELL3D_REG_INDEX(inline_data)), regs.transform_feedback.controls[0].stride, true);
|
maxwell3d.CallMethod(system, size_t(MAXWELL3D_REG_INDEX(inline_data)), regs.transform_feedback.controls[0].stride, true);
|
||||||
maxwell3d.Rasterizer().RegisterTransformFeedback(regs.upload.dest.Address());
|
maxwell3d.Rasterizer().RegisterTransformFeedback(regs.upload.dest.Address());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -429,7 +426,7 @@ void HLE_TransformFeedbackSetup::Execute(Engines::Maxwell3D& maxwell3d, std::spa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroInterpreterImpl::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> params, u32 method) {
|
void MacroInterpreterImpl::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> params, u32 method) {
|
||||||
Reset();
|
Reset();
|
||||||
|
|
||||||
registers[1] = params[0];
|
registers[1] = params[0];
|
||||||
|
|
@ -439,7 +436,7 @@ void MacroInterpreterImpl::Execute(Engines::Maxwell3D& maxwell3d, std::span<cons
|
||||||
// Execute the code until we hit an exit condition.
|
// Execute the code until we hit an exit condition.
|
||||||
bool keep_executing = true;
|
bool keep_executing = true;
|
||||||
while (keep_executing) {
|
while (keep_executing) {
|
||||||
keep_executing = Step(maxwell3d, false);
|
keep_executing = Step(system, maxwell3d, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assert the the macro used all the input parameters
|
// Assert the the macro used all the input parameters
|
||||||
|
|
@ -462,7 +459,7 @@ void MacroInterpreterImpl::Reset() {
|
||||||
/// @brief Executes a single macro instruction located at the current program counter. Returns whether
|
/// @brief Executes a single macro instruction located at the current program counter. Returns whether
|
||||||
/// the interpreter should keep running.
|
/// the interpreter should keep running.
|
||||||
/// @param is_delay_slot Whether the current step is being executed due to a delay slot in a previous instruction.
|
/// @param is_delay_slot Whether the current step is being executed due to a delay slot in a previous instruction.
|
||||||
bool MacroInterpreterImpl::Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slot) {
|
bool MacroInterpreterImpl::Step(Core::System& system, Engines::Maxwell3D& maxwell3d, bool is_delay_slot) {
|
||||||
u32 base_address = pc;
|
u32 base_address = pc;
|
||||||
|
|
||||||
Macro::Opcode opcode = GetOpcode();
|
Macro::Opcode opcode = GetOpcode();
|
||||||
|
|
@ -478,11 +475,11 @@ bool MacroInterpreterImpl::Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slo
|
||||||
switch (opcode.operation) {
|
switch (opcode.operation) {
|
||||||
case Macro::Operation::ALU: {
|
case Macro::Operation::ALU: {
|
||||||
u32 result = GetALUResult(opcode.alu_operation, GetRegister(opcode.src_a), GetRegister(opcode.src_b));
|
u32 result = GetALUResult(opcode.alu_operation, GetRegister(opcode.src_a), GetRegister(opcode.src_b));
|
||||||
ProcessResult(maxwell3d, opcode.result_operation, opcode.dst, result);
|
ProcessResult(system, maxwell3d, opcode.result_operation, opcode.dst, result);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Macro::Operation::AddImmediate: {
|
case Macro::Operation::AddImmediate: {
|
||||||
ProcessResult(maxwell3d, opcode.result_operation, opcode.dst, GetRegister(opcode.src_a) + opcode.immediate);
|
ProcessResult(system, maxwell3d, opcode.result_operation, opcode.dst, GetRegister(opcode.src_a) + opcode.immediate);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Macro::Operation::ExtractInsert: {
|
case Macro::Operation::ExtractInsert: {
|
||||||
|
|
@ -492,7 +489,7 @@ bool MacroInterpreterImpl::Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slo
|
||||||
src = (src >> opcode.bf_src_bit) & opcode.GetBitfieldMask();
|
src = (src >> opcode.bf_src_bit) & opcode.GetBitfieldMask();
|
||||||
dst &= ~(opcode.GetBitfieldMask() << opcode.bf_dst_bit);
|
dst &= ~(opcode.GetBitfieldMask() << opcode.bf_dst_bit);
|
||||||
dst |= src << opcode.bf_dst_bit;
|
dst |= src << opcode.bf_dst_bit;
|
||||||
ProcessResult(maxwell3d, opcode.result_operation, opcode.dst, dst);
|
ProcessResult(system, maxwell3d, opcode.result_operation, opcode.dst, dst);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Macro::Operation::ExtractShiftLeftImmediate: {
|
case Macro::Operation::ExtractShiftLeftImmediate: {
|
||||||
|
|
@ -501,7 +498,7 @@ bool MacroInterpreterImpl::Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slo
|
||||||
|
|
||||||
u32 result = ((src >> dst) & opcode.GetBitfieldMask()) << opcode.bf_dst_bit;
|
u32 result = ((src >> dst) & opcode.GetBitfieldMask()) << opcode.bf_dst_bit;
|
||||||
|
|
||||||
ProcessResult(maxwell3d, opcode.result_operation, opcode.dst, result);
|
ProcessResult(system, maxwell3d, opcode.result_operation, opcode.dst, result);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Macro::Operation::ExtractShiftLeftRegister: {
|
case Macro::Operation::ExtractShiftLeftRegister: {
|
||||||
|
|
@ -510,12 +507,12 @@ bool MacroInterpreterImpl::Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slo
|
||||||
|
|
||||||
u32 result = ((src >> opcode.bf_src_bit) & opcode.GetBitfieldMask()) << dst;
|
u32 result = ((src >> opcode.bf_src_bit) & opcode.GetBitfieldMask()) << dst;
|
||||||
|
|
||||||
ProcessResult(maxwell3d, opcode.result_operation, opcode.dst, result);
|
ProcessResult(system, maxwell3d, opcode.result_operation, opcode.dst, result);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Macro::Operation::Read: {
|
case Macro::Operation::Read: {
|
||||||
u32 result = Read(maxwell3d, GetRegister(opcode.src_a) + opcode.immediate);
|
u32 result = Read(maxwell3d, GetRegister(opcode.src_a) + opcode.immediate);
|
||||||
ProcessResult(maxwell3d, opcode.result_operation, opcode.dst, result);
|
ProcessResult(system, maxwell3d, opcode.result_operation, opcode.dst, result);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Macro::Operation::Branch: {
|
case Macro::Operation::Branch: {
|
||||||
|
|
@ -531,7 +528,7 @@ bool MacroInterpreterImpl::Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slo
|
||||||
|
|
||||||
delayed_pc = base_address + opcode.GetBranchTarget();
|
delayed_pc = base_address + opcode.GetBranchTarget();
|
||||||
// Execute one more instruction due to the delay slot.
|
// Execute one more instruction due to the delay slot.
|
||||||
return Step(maxwell3d, true);
|
return Step(system, maxwell3d, true);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -544,7 +541,7 @@ bool MacroInterpreterImpl::Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slo
|
||||||
// cause an exit if it's executed inside a delay slot.
|
// cause an exit if it's executed inside a delay slot.
|
||||||
if (opcode.is_exit && !is_delay_slot) {
|
if (opcode.is_exit && !is_delay_slot) {
|
||||||
// Exit has a delay slot, execute the next instruction
|
// Exit has a delay slot, execute the next instruction
|
||||||
Step(maxwell3d, true);
|
Step(system, maxwell3d, true);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -591,7 +588,7 @@ u32 MacroInterpreterImpl::GetALUResult(Macro::ALUOperation operation, u32 src_a,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs the result operation on the input result and stores it in the specified register (if necessary).
|
/// Performs the result operation on the input result and stores it in the specified register (if necessary).
|
||||||
void MacroInterpreterImpl::ProcessResult(Engines::Maxwell3D& maxwell3d, Macro::ResultOperation operation, u32 reg, u32 result) {
|
void MacroInterpreterImpl::ProcessResult(Core::System& system, Engines::Maxwell3D& maxwell3d, Macro::ResultOperation operation, u32 reg, u32 result) {
|
||||||
switch (operation) {
|
switch (operation) {
|
||||||
case Macro::ResultOperation::IgnoreAndFetch:
|
case Macro::ResultOperation::IgnoreAndFetch:
|
||||||
// Fetch parameter and ignore result.
|
// Fetch parameter and ignore result.
|
||||||
|
|
@ -609,12 +606,12 @@ void MacroInterpreterImpl::ProcessResult(Engines::Maxwell3D& maxwell3d, Macro::R
|
||||||
case Macro::ResultOperation::FetchAndSend:
|
case Macro::ResultOperation::FetchAndSend:
|
||||||
// Fetch parameter and send result.
|
// Fetch parameter and send result.
|
||||||
SetRegister(reg, FetchParameter());
|
SetRegister(reg, FetchParameter());
|
||||||
Send(maxwell3d, result);
|
Send(system, maxwell3d, result);
|
||||||
break;
|
break;
|
||||||
case Macro::ResultOperation::MoveAndSend:
|
case Macro::ResultOperation::MoveAndSend:
|
||||||
// Move and send result.
|
// Move and send result.
|
||||||
SetRegister(reg, result);
|
SetRegister(reg, result);
|
||||||
Send(maxwell3d, result);
|
Send(system, maxwell3d, result);
|
||||||
break;
|
break;
|
||||||
case Macro::ResultOperation::FetchAndSetMethod:
|
case Macro::ResultOperation::FetchAndSetMethod:
|
||||||
// Fetch parameter and use result as Method Address.
|
// Fetch parameter and use result as Method Address.
|
||||||
|
|
@ -625,13 +622,13 @@ void MacroInterpreterImpl::ProcessResult(Engines::Maxwell3D& maxwell3d, Macro::R
|
||||||
// Move result and use as Method Address, then fetch and send parameter.
|
// Move result and use as Method Address, then fetch and send parameter.
|
||||||
SetRegister(reg, result);
|
SetRegister(reg, result);
|
||||||
SetMethodAddress(result);
|
SetMethodAddress(result);
|
||||||
Send(maxwell3d, FetchParameter());
|
Send(system, maxwell3d, FetchParameter());
|
||||||
break;
|
break;
|
||||||
case Macro::ResultOperation::MoveAndSetMethodSend:
|
case Macro::ResultOperation::MoveAndSetMethodSend:
|
||||||
// Move result and use as Method Address, then send bits 12:17 of result.
|
// Move result and use as Method Address, then send bits 12:17 of result.
|
||||||
SetRegister(reg, result);
|
SetRegister(reg, result);
|
||||||
SetMethodAddress(result);
|
SetMethodAddress(result);
|
||||||
Send(maxwell3d, (result >> 12) & 0b111111);
|
Send(system, maxwell3d, (result >> 12) & 0b111111);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Unimplemented result operation {}", operation);
|
UNIMPLEMENTED_MSG("Unimplemented result operation {}", operation);
|
||||||
|
|
@ -672,8 +669,8 @@ void MacroInterpreterImpl::SetRegister(u32 register_id, u32 value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calls a GPU Engine method with the input parameter.
|
/// Calls a GPU Engine method with the input parameter.
|
||||||
void MacroInterpreterImpl::Send(Engines::Maxwell3D& maxwell3d, u32 value) {
|
void MacroInterpreterImpl::Send(Core::System& system, Engines::Maxwell3D& maxwell3d, u32 value) {
|
||||||
maxwell3d.CallMethod(method_address.address, value, true);
|
maxwell3d.CallMethod(system, method_address.address, value, true);
|
||||||
// Increment the method address by the method increment.
|
// Increment the method address by the method increment.
|
||||||
method_address.address.Assign(method_address.address.Value() + method_address.increment.Value());
|
method_address.address.Assign(method_address.address.Value() + method_address.increment.Value());
|
||||||
}
|
}
|
||||||
|
|
@ -724,34 +721,35 @@ static const auto default_cg_mode = nullptr; //Allow RWE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct MacroJITx64Impl final : public Xbyak::CodeGenerator, public DynamicCachedMacro {
|
struct MacroJITx64Impl final : public Xbyak::CodeGenerator, public DynamicCachedMacro {
|
||||||
explicit MacroJITx64Impl(std::span<const u32> code_)
|
explicit MacroJITx64Impl(Core::System& system, std::span<const u32> code_)
|
||||||
: Xbyak::CodeGenerator(MAX_CODE_SIZE, default_cg_mode)
|
: Xbyak::CodeGenerator(MAX_CODE_SIZE, default_cg_mode)
|
||||||
, code{code_}
|
, code{code_}
|
||||||
{
|
{
|
||||||
Compile();
|
Compile(system);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, u32 method) override;
|
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, u32 method) override;
|
||||||
|
|
||||||
void Compile_ALU(Macro::Opcode opcode);
|
void Compile_ALU(Core::System& system, Macro::Opcode opcode);
|
||||||
void Compile_AddImmediate(Macro::Opcode opcode);
|
void Compile_AddImmediate(Core::System& system, Macro::Opcode opcode);
|
||||||
void Compile_ExtractInsert(Macro::Opcode opcode);
|
void Compile_ExtractInsert(Core::System& system, Macro::Opcode opcode);
|
||||||
void Compile_ExtractShiftLeftImmediate(Macro::Opcode opcode);
|
void Compile_ExtractShiftLeftImmediate(Core::System& system, Macro::Opcode opcode);
|
||||||
void Compile_ExtractShiftLeftRegister(Macro::Opcode opcode);
|
void Compile_ExtractShiftLeftRegister(Core::System& system, Macro::Opcode opcode);
|
||||||
void Compile_Read(Macro::Opcode opcode);
|
void Compile_Read(Core::System& system, Macro::Opcode opcode);
|
||||||
void Compile_Branch(Macro::Opcode opcode);
|
void Compile_Branch(Macro::Opcode opcode);
|
||||||
|
|
||||||
void Optimizer_ScanFlags();
|
void Optimizer_ScanFlags();
|
||||||
void Compile();
|
void Compile(Core::System& system);
|
||||||
bool Compile_NextInstruction();
|
bool Compile_NextInstruction(Core::System& system);
|
||||||
Xbyak::Reg32 Compile_FetchParameter();
|
Xbyak::Reg32 Compile_FetchParameter();
|
||||||
Xbyak::Reg32 Compile_GetRegister(u32 index, Xbyak::Reg32 dst);
|
Xbyak::Reg32 Compile_GetRegister(u32 index, Xbyak::Reg32 dst);
|
||||||
void Compile_ProcessResult(Macro::ResultOperation operation, u32 reg);
|
void Compile_ProcessResult(Core::System& system, Macro::ResultOperation operation, u32 reg);
|
||||||
void Compile_Send(Xbyak::Reg32 value);
|
void Compile_Send(Core::System& system, Xbyak::Reg32 value);
|
||||||
Macro::Opcode GetOpCode() const;
|
Macro::Opcode GetOpCode() const;
|
||||||
|
|
||||||
struct JITState {
|
struct JITState {
|
||||||
Engines::Maxwell3D* maxwell3d{};
|
Engines::Maxwell3D* maxwell3d = nullptr;
|
||||||
|
Core::System* system = nullptr;
|
||||||
std::array<u32, Macro::NUM_MACRO_REGISTERS> registers{};
|
std::array<u32, Macro::NUM_MACRO_REGISTERS> registers{};
|
||||||
u32 carry_flag{};
|
u32 carry_flag{};
|
||||||
};
|
};
|
||||||
|
|
@ -777,15 +775,16 @@ struct MacroJITx64Impl final : public Xbyak::CodeGenerator, public DynamicCached
|
||||||
std::span<const u32> code;
|
std::span<const u32> code;
|
||||||
};
|
};
|
||||||
|
|
||||||
void MacroJITx64Impl::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, u32 method) {
|
void MacroJITx64Impl::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, u32 method) {
|
||||||
ASSERT_OR_EXECUTE(program != nullptr, { return; });
|
ASSERT_OR_EXECUTE(program != nullptr, { return; });
|
||||||
JITState state{};
|
JITState state{};
|
||||||
state.maxwell3d = &maxwell3d;
|
state.maxwell3d = &maxwell3d;
|
||||||
|
state.system = &system;
|
||||||
state.registers = {};
|
state.registers = {};
|
||||||
program(&state, parameters.data(), parameters.data() + parameters.size());
|
program(&state, parameters.data(), parameters.data() + parameters.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroJITx64Impl::Compile_ALU(Macro::Opcode opcode) {
|
void MacroJITx64Impl::Compile_ALU(Core::System& system, Macro::Opcode opcode) {
|
||||||
const bool is_a_zero = opcode.src_a == 0;
|
const bool is_a_zero = opcode.src_a == 0;
|
||||||
const bool is_b_zero = opcode.src_b == 0;
|
const bool is_b_zero = opcode.src_b == 0;
|
||||||
const bool valid_operation = !is_a_zero && !is_b_zero;
|
const bool valid_operation = !is_a_zero && !is_b_zero;
|
||||||
|
|
@ -902,10 +901,10 @@ void MacroJITx64Impl::Compile_ALU(Macro::Opcode opcode) {
|
||||||
UNIMPLEMENTED_MSG("Unimplemented ALU operation {}", opcode.alu_operation.Value());
|
UNIMPLEMENTED_MSG("Unimplemented ALU operation {}", opcode.alu_operation.Value());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Compile_ProcessResult(opcode.result_operation, opcode.dst);
|
Compile_ProcessResult(system, opcode.result_operation, opcode.dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroJITx64Impl::Compile_AddImmediate(Macro::Opcode opcode) {
|
void MacroJITx64Impl::Compile_AddImmediate(Core::System& system, Macro::Opcode opcode) {
|
||||||
if (optimizer.skip_dummy_addimmediate) {
|
if (optimizer.skip_dummy_addimmediate) {
|
||||||
// Games tend to use this as an exit instruction placeholder. It's to encode an instruction
|
// Games tend to use this as an exit instruction placeholder. It's to encode an instruction
|
||||||
// without doing anything. In our case we can just not emit anything.
|
// without doing anything. In our case we can just not emit anything.
|
||||||
|
|
@ -940,10 +939,10 @@ void MacroJITx64Impl::Compile_AddImmediate(Macro::Opcode opcode) {
|
||||||
sub(result, opcode.immediate * -1);
|
sub(result, opcode.immediate * -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Compile_ProcessResult(opcode.result_operation, opcode.dst);
|
Compile_ProcessResult(system, opcode.result_operation, opcode.dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroJITx64Impl::Compile_ExtractInsert(Macro::Opcode opcode) {
|
void MacroJITx64Impl::Compile_ExtractInsert(Core::System& system, Macro::Opcode opcode) {
|
||||||
auto dst = Compile_GetRegister(opcode.src_a, RESULT);
|
auto dst = Compile_GetRegister(opcode.src_a, RESULT);
|
||||||
auto src = Compile_GetRegister(opcode.src_b, eax);
|
auto src = Compile_GetRegister(opcode.src_b, eax);
|
||||||
|
|
||||||
|
|
@ -954,10 +953,10 @@ void MacroJITx64Impl::Compile_ExtractInsert(Macro::Opcode opcode) {
|
||||||
shl(src, opcode.bf_dst_bit);
|
shl(src, opcode.bf_dst_bit);
|
||||||
or_(dst, src);
|
or_(dst, src);
|
||||||
|
|
||||||
Compile_ProcessResult(opcode.result_operation, opcode.dst);
|
Compile_ProcessResult(system, opcode.result_operation, opcode.dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroJITx64Impl::Compile_ExtractShiftLeftImmediate(Macro::Opcode opcode) {
|
void MacroJITx64Impl::Compile_ExtractShiftLeftImmediate(Core::System& system, Macro::Opcode opcode) {
|
||||||
const auto dst = Compile_GetRegister(opcode.src_a, ecx);
|
const auto dst = Compile_GetRegister(opcode.src_a, ecx);
|
||||||
const auto src = Compile_GetRegister(opcode.src_b, RESULT);
|
const auto src = Compile_GetRegister(opcode.src_b, RESULT);
|
||||||
|
|
||||||
|
|
@ -965,10 +964,10 @@ void MacroJITx64Impl::Compile_ExtractShiftLeftImmediate(Macro::Opcode opcode) {
|
||||||
and_(src, opcode.GetBitfieldMask());
|
and_(src, opcode.GetBitfieldMask());
|
||||||
shl(src, opcode.bf_dst_bit);
|
shl(src, opcode.bf_dst_bit);
|
||||||
|
|
||||||
Compile_ProcessResult(opcode.result_operation, opcode.dst);
|
Compile_ProcessResult(system, opcode.result_operation, opcode.dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroJITx64Impl::Compile_ExtractShiftLeftRegister(Macro::Opcode opcode) {
|
void MacroJITx64Impl::Compile_ExtractShiftLeftRegister(Core::System& system, Macro::Opcode opcode) {
|
||||||
const auto dst = Compile_GetRegister(opcode.src_a, ecx);
|
const auto dst = Compile_GetRegister(opcode.src_a, ecx);
|
||||||
const auto src = Compile_GetRegister(opcode.src_b, RESULT);
|
const auto src = Compile_GetRegister(opcode.src_b, RESULT);
|
||||||
|
|
||||||
|
|
@ -976,10 +975,10 @@ void MacroJITx64Impl::Compile_ExtractShiftLeftRegister(Macro::Opcode opcode) {
|
||||||
and_(src, opcode.GetBitfieldMask());
|
and_(src, opcode.GetBitfieldMask());
|
||||||
shl(src, dst.cvt8());
|
shl(src, dst.cvt8());
|
||||||
|
|
||||||
Compile_ProcessResult(opcode.result_operation, opcode.dst);
|
Compile_ProcessResult(system, opcode.result_operation, opcode.dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroJITx64Impl::Compile_Read(Macro::Opcode opcode) {
|
void MacroJITx64Impl::Compile_Read(Core::System& system, Macro::Opcode opcode) {
|
||||||
if (optimizer.zero_reg_skip && opcode.src_a == 0) {
|
if (optimizer.zero_reg_skip && opcode.src_a == 0) {
|
||||||
if (opcode.immediate == 0) {
|
if (opcode.immediate == 0) {
|
||||||
xor_(RESULT, RESULT);
|
xor_(RESULT, RESULT);
|
||||||
|
|
@ -1005,23 +1004,21 @@ void MacroJITx64Impl::Compile_Read(Macro::Opcode opcode) {
|
||||||
int3();
|
int3();
|
||||||
L(pass_range_check);
|
L(pass_range_check);
|
||||||
}
|
}
|
||||||
mov(rax, qword[STATE]);
|
mov(rax, qword[STATE + offsetof(JITState, maxwell3d)]);
|
||||||
mov(RESULT,
|
mov(RESULT, dword[rax + offsetof(Engines::Maxwell3D, regs) + offsetof(Engines::Maxwell3D::Regs, reg_array) + RESULT.cvt64() * sizeof(u32)]);
|
||||||
dword[rax + offsetof(Engines::Maxwell3D, regs) +
|
Compile_ProcessResult(system, opcode.result_operation, opcode.dst);
|
||||||
offsetof(Engines::Maxwell3D::Regs, reg_array) + RESULT.cvt64() * sizeof(u32)]);
|
|
||||||
|
|
||||||
Compile_ProcessResult(opcode.result_operation, opcode.dst);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void MacroJIT_SendThunk(Engines::Maxwell3D* maxwell3d, Macro::MethodAddress method_address, u32 value) {
|
static void MacroJIT_SendThunk(Core::System* system, Engines::Maxwell3D* maxwell3d, Macro::MethodAddress method_address, u32 value) {
|
||||||
maxwell3d->CallMethod(method_address.address, value, true);
|
maxwell3d->CallMethod(*system, method_address.address, value, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroJITx64Impl::Compile_Send(Xbyak::Reg32 value) {
|
void MacroJITx64Impl::Compile_Send(Core::System& system, Xbyak::Reg32 value) {
|
||||||
Common::X64::ABI_PushRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0);
|
Common::X64::ABI_PushRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0);
|
||||||
mov(Common::X64::ABI_PARAM1, qword[STATE]);
|
mov(Common::X64::ABI_PARAM1, qword[STATE + offsetof(JITState, system)]);
|
||||||
mov(Common::X64::ABI_PARAM2.cvt32(), METHOD_ADDRESS);
|
mov(Common::X64::ABI_PARAM2, qword[STATE + offsetof(JITState, maxwell3d)]);
|
||||||
mov(Common::X64::ABI_PARAM3.cvt32(), value);
|
mov(Common::X64::ABI_PARAM3.cvt32(), METHOD_ADDRESS);
|
||||||
|
mov(Common::X64::ABI_PARAM4.cvt32(), value);
|
||||||
Common::X64::CallFarFunction(*this, &MacroJIT_SendThunk);
|
Common::X64::CallFarFunction(*this, &MacroJIT_SendThunk);
|
||||||
Common::X64::ABI_PopRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0);
|
Common::X64::ABI_PopRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0);
|
||||||
|
|
||||||
|
|
@ -1045,9 +1042,8 @@ void MacroJITx64Impl::Compile_Send(Xbyak::Reg32 value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroJITx64Impl::Compile_Branch(Macro::Opcode opcode) {
|
void MacroJITx64Impl::Compile_Branch(Macro::Opcode opcode) {
|
||||||
ASSERT_MSG(!is_delay_slot, "Executing a branch in a delay slot is not valid");
|
ASSERT(!is_delay_slot && "Executing a branch in a delay slot is not valid");
|
||||||
const s32 jump_address =
|
const s32 jump_address = s32(pc) + s32(opcode.GetBranchTarget() / sizeof(s32));
|
||||||
static_cast<s32>(pc) + static_cast<s32>(opcode.GetBranchTarget() / sizeof(s32));
|
|
||||||
|
|
||||||
Xbyak::Label end;
|
Xbyak::Label end;
|
||||||
auto value = Compile_GetRegister(opcode.src_a, eax);
|
auto value = Compile_GetRegister(opcode.src_a, eax);
|
||||||
|
|
@ -1116,7 +1112,7 @@ void MacroJITx64Impl::Optimizer_ScanFlags() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroJITx64Impl::Compile() {
|
void MacroJITx64Impl::Compile(Core::System& system) {
|
||||||
labels.fill(Xbyak::Label());
|
labels.fill(Xbyak::Label());
|
||||||
|
|
||||||
Common::X64::ABI_PushRegistersAndAdjustStack(*this, Common::X64::ABI_ALL_CALLEE_SAVED, 8);
|
Common::X64::ABI_PushRegistersAndAdjustStack(*this, Common::X64::ABI_ALL_CALLEE_SAVED, 8);
|
||||||
|
|
@ -1156,7 +1152,7 @@ void MacroJITx64Impl::Compile() {
|
||||||
next_opcode = {};
|
next_opcode = {};
|
||||||
}
|
}
|
||||||
pc = i;
|
pc = i;
|
||||||
Compile_NextInstruction();
|
Compile_NextInstruction(system);
|
||||||
}
|
}
|
||||||
|
|
||||||
L(end_of_code);
|
L(end_of_code);
|
||||||
|
|
@ -1167,7 +1163,7 @@ void MacroJITx64Impl::Compile() {
|
||||||
program = getCode<ProgramType>();
|
program = getCode<ProgramType>();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacroJITx64Impl::Compile_NextInstruction() {
|
bool MacroJITx64Impl::Compile_NextInstruction(Core::System& system) {
|
||||||
const auto opcode = GetOpCode();
|
const auto opcode = GetOpCode();
|
||||||
if (labels[pc].getAddress()) {
|
if (labels[pc].getAddress()) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -1177,22 +1173,22 @@ bool MacroJITx64Impl::Compile_NextInstruction() {
|
||||||
|
|
||||||
switch (opcode.operation) {
|
switch (opcode.operation) {
|
||||||
case Macro::Operation::ALU:
|
case Macro::Operation::ALU:
|
||||||
Compile_ALU(opcode);
|
Compile_ALU(system, opcode);
|
||||||
break;
|
break;
|
||||||
case Macro::Operation::AddImmediate:
|
case Macro::Operation::AddImmediate:
|
||||||
Compile_AddImmediate(opcode);
|
Compile_AddImmediate(system, opcode);
|
||||||
break;
|
break;
|
||||||
case Macro::Operation::ExtractInsert:
|
case Macro::Operation::ExtractInsert:
|
||||||
Compile_ExtractInsert(opcode);
|
Compile_ExtractInsert(system, opcode);
|
||||||
break;
|
break;
|
||||||
case Macro::Operation::ExtractShiftLeftImmediate:
|
case Macro::Operation::ExtractShiftLeftImmediate:
|
||||||
Compile_ExtractShiftLeftImmediate(opcode);
|
Compile_ExtractShiftLeftImmediate(system, opcode);
|
||||||
break;
|
break;
|
||||||
case Macro::Operation::ExtractShiftLeftRegister:
|
case Macro::Operation::ExtractShiftLeftRegister:
|
||||||
Compile_ExtractShiftLeftRegister(opcode);
|
Compile_ExtractShiftLeftRegister(system, opcode);
|
||||||
break;
|
break;
|
||||||
case Macro::Operation::Read:
|
case Macro::Operation::Read:
|
||||||
Compile_Read(opcode);
|
Compile_Read(system, opcode);
|
||||||
break;
|
break;
|
||||||
case Macro::Operation::Branch:
|
case Macro::Operation::Branch:
|
||||||
Compile_Branch(opcode);
|
Compile_Branch(opcode);
|
||||||
|
|
@ -1264,7 +1260,7 @@ Xbyak::Reg32 MacroJITx64Impl::Compile_GetRegister(u32 index, Xbyak::Reg32 dst) {
|
||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroJITx64Impl::Compile_ProcessResult(Macro::ResultOperation operation, u32 reg) {
|
void MacroJITx64Impl::Compile_ProcessResult(Core::System& system, Macro::ResultOperation operation, u32 reg) {
|
||||||
const auto SetRegister = [this](u32 reg_index, const Xbyak::Reg32& result) {
|
const auto SetRegister = [this](u32 reg_index, const Xbyak::Reg32& result) {
|
||||||
// Register 0 is supposed to always return 0. NOP is implemented as a store to the zero
|
// Register 0 is supposed to always return 0. NOP is implemented as a store to the zero
|
||||||
// register.
|
// register.
|
||||||
|
|
@ -1289,12 +1285,12 @@ void MacroJITx64Impl::Compile_ProcessResult(Macro::ResultOperation operation, u3
|
||||||
case Macro::ResultOperation::FetchAndSend:
|
case Macro::ResultOperation::FetchAndSend:
|
||||||
// Fetch parameter and send result.
|
// Fetch parameter and send result.
|
||||||
SetRegister(reg, Compile_FetchParameter());
|
SetRegister(reg, Compile_FetchParameter());
|
||||||
Compile_Send(RESULT);
|
Compile_Send(system, RESULT);
|
||||||
break;
|
break;
|
||||||
case Macro::ResultOperation::MoveAndSend:
|
case Macro::ResultOperation::MoveAndSend:
|
||||||
// Move and send result.
|
// Move and send result.
|
||||||
SetRegister(reg, RESULT);
|
SetRegister(reg, RESULT);
|
||||||
Compile_Send(RESULT);
|
Compile_Send(system, RESULT);
|
||||||
break;
|
break;
|
||||||
case Macro::ResultOperation::FetchAndSetMethod:
|
case Macro::ResultOperation::FetchAndSetMethod:
|
||||||
// Fetch parameter and use result as Method Address.
|
// Fetch parameter and use result as Method Address.
|
||||||
|
|
@ -1305,7 +1301,7 @@ void MacroJITx64Impl::Compile_ProcessResult(Macro::ResultOperation operation, u3
|
||||||
// Move result and use as Method Address, then fetch and send parameter.
|
// Move result and use as Method Address, then fetch and send parameter.
|
||||||
SetRegister(reg, RESULT);
|
SetRegister(reg, RESULT);
|
||||||
SetMethodAddress(RESULT);
|
SetMethodAddress(RESULT);
|
||||||
Compile_Send(Compile_FetchParameter());
|
Compile_Send(system, Compile_FetchParameter());
|
||||||
break;
|
break;
|
||||||
case Macro::ResultOperation::MoveAndSetMethodSend:
|
case Macro::ResultOperation::MoveAndSetMethodSend:
|
||||||
// Move result and use as Method Address, then send bits 12:17 of result.
|
// Move result and use as Method Address, then send bits 12:17 of result.
|
||||||
|
|
@ -1313,7 +1309,7 @@ void MacroJITx64Impl::Compile_ProcessResult(Macro::ResultOperation operation, u3
|
||||||
SetMethodAddress(RESULT);
|
SetMethodAddress(RESULT);
|
||||||
shr(RESULT, 12);
|
shr(RESULT, 12);
|
||||||
and_(RESULT, 0b111111);
|
and_(RESULT, 0b111111);
|
||||||
Compile_Send(RESULT);
|
Compile_Send(system, RESULT);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Unimplemented macro operation {}", operation);
|
UNIMPLEMENTED_MSG("Unimplemented macro operation {}", operation);
|
||||||
|
|
@ -1354,36 +1350,36 @@ static void Dump(u64 hash, std::span<const u32> code, bool decompiled = false) {
|
||||||
macro_file.write(reinterpret_cast<const char*>(code.data()), code.size_bytes());
|
macro_file.write(reinterpret_cast<const char*>(code.data()), code.size_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroEngine::Execute(Engines::Maxwell3D& maxwell3d, u32 method, std::span<const u32> parameters) {
|
void MacroEngine::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, u32 method, std::span<const u32> parameters) {
|
||||||
auto const execute_variant = [&maxwell3d, ¶meters, method](AnyCachedMacro& acm) {
|
auto const execute_variant = [&system, &maxwell3d, ¶meters, method](AnyCachedMacro& acm) {
|
||||||
if (auto a = std::get_if<HLE_DrawArraysIndirect>(&acm))
|
if (auto a = std::get_if<HLE_DrawArraysIndirect>(&acm))
|
||||||
a->Execute(maxwell3d, parameters, method);
|
a->Execute(system, maxwell3d, parameters, method);
|
||||||
if (auto a = std::get_if<HLE_DrawIndexedIndirect>(&acm))
|
if (auto a = std::get_if<HLE_DrawIndexedIndirect>(&acm))
|
||||||
a->Execute(maxwell3d, parameters, method);
|
a->Execute(system, maxwell3d, parameters, method);
|
||||||
if (auto a = std::get_if<HLE_MultiDrawIndexedIndirectCount>(&acm))
|
if (auto a = std::get_if<HLE_MultiDrawIndexedIndirectCount>(&acm))
|
||||||
a->Execute(maxwell3d, parameters, method);
|
a->Execute(system, maxwell3d, parameters, method);
|
||||||
if (auto a = std::get_if<HLE_MultiLayerClear>(&acm))
|
if (auto a = std::get_if<HLE_MultiLayerClear>(&acm))
|
||||||
a->Execute(maxwell3d, parameters, method);
|
a->Execute(system, maxwell3d, parameters, method);
|
||||||
if (auto a = std::get_if<HLE_C713C83D8F63CCF3>(&acm))
|
if (auto a = std::get_if<HLE_C713C83D8F63CCF3>(&acm))
|
||||||
a->Execute(maxwell3d, parameters, method);
|
a->Execute(system, maxwell3d, parameters, method);
|
||||||
if (auto a = std::get_if<HLE_D7333D26E0A93EDE>(&acm))
|
if (auto a = std::get_if<HLE_D7333D26E0A93EDE>(&acm))
|
||||||
a->Execute(maxwell3d, parameters, method);
|
a->Execute(system, maxwell3d, parameters, method);
|
||||||
if (auto a = std::get_if<HLE_BindShader>(&acm))
|
if (auto a = std::get_if<HLE_BindShader>(&acm))
|
||||||
a->Execute(maxwell3d, parameters, method);
|
a->Execute(system, maxwell3d, parameters, method);
|
||||||
if (auto a = std::get_if<HLE_SetRasterBoundingBox>(&acm))
|
if (auto a = std::get_if<HLE_SetRasterBoundingBox>(&acm))
|
||||||
a->Execute(maxwell3d, parameters, method);
|
a->Execute(system, maxwell3d, parameters, method);
|
||||||
if (auto a = std::get_if<HLE_ClearConstBuffer>(&acm))
|
if (auto a = std::get_if<HLE_ClearConstBuffer>(&acm))
|
||||||
a->Execute(maxwell3d, parameters, method);
|
a->Execute(system, maxwell3d, parameters, method);
|
||||||
if (auto a = std::get_if<HLE_ClearMemory>(&acm))
|
if (auto a = std::get_if<HLE_ClearMemory>(&acm))
|
||||||
a->Execute(maxwell3d, parameters, method);
|
a->Execute(system, maxwell3d, parameters, method);
|
||||||
if (auto a = std::get_if<HLE_TransformFeedbackSetup>(&acm))
|
if (auto a = std::get_if<HLE_TransformFeedbackSetup>(&acm))
|
||||||
a->Execute(maxwell3d, parameters, method);
|
a->Execute(system, maxwell3d, parameters, method);
|
||||||
if (auto a = std::get_if<HLE_DrawIndirectByteCount>(&acm))
|
if (auto a = std::get_if<HLE_DrawIndirectByteCount>(&acm))
|
||||||
a->Execute(maxwell3d, parameters, method);
|
a->Execute(system, maxwell3d, parameters, method);
|
||||||
if (auto a = std::get_if<MacroInterpreterImpl>(&acm))
|
if (auto a = std::get_if<MacroInterpreterImpl>(&acm))
|
||||||
a->Execute(maxwell3d, parameters, method);
|
a->Execute(system, maxwell3d, parameters, method);
|
||||||
if (auto a = std::get_if<std::unique_ptr<DynamicCachedMacro>>(&acm))
|
if (auto a = std::get_if<std::unique_ptr<DynamicCachedMacro>>(&acm))
|
||||||
a->get()->Execute(maxwell3d, parameters, method);
|
a->get()->Execute(system, maxwell3d, parameters, method);
|
||||||
};
|
};
|
||||||
if (auto const it = macro_cache.find(method); it != macro_cache.end()) {
|
if (auto const it = macro_cache.find(method); it != macro_cache.end()) {
|
||||||
auto& ci = it->second;
|
auto& ci = it->second;
|
||||||
|
|
@ -1414,9 +1410,9 @@ void MacroEngine::Execute(Engines::Maxwell3D& maxwell3d, u32 method, std::span<c
|
||||||
code.resize(macro_cached.size() - rebased_method);
|
code.resize(macro_cached.size() - rebased_method);
|
||||||
std::memcpy(code.data(), macro_cached.data() + rebased_method, code.size() * sizeof(u32));
|
std::memcpy(code.data(), macro_cached.data() + rebased_method, code.size() * sizeof(u32));
|
||||||
ci.hash = Common::HashValue(code);
|
ci.hash = Common::HashValue(code);
|
||||||
ci.program = Compile(maxwell3d, code);
|
ci.program = Compile(system, maxwell3d, code);
|
||||||
} else {
|
} else {
|
||||||
ci.program = Compile(maxwell3d, macro_code->second);
|
ci.program = Compile(system, maxwell3d, macro_code->second);
|
||||||
ci.hash = Common::HashValue(macro_code->second);
|
ci.hash = Common::HashValue(macro_code->second);
|
||||||
}
|
}
|
||||||
if (CanBeHLEProgram(ci.hash) && !Settings::values.disable_macro_hle) {
|
if (CanBeHLEProgram(ci.hash) && !Settings::values.disable_macro_hle) {
|
||||||
|
|
@ -1431,10 +1427,10 @@ void MacroEngine::Execute(Engines::Maxwell3D& maxwell3d, u32 method, std::span<c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AnyCachedMacro MacroEngine::Compile(Engines::Maxwell3D& maxwell3d, std::span<const u32> code) {
|
AnyCachedMacro MacroEngine::Compile(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> code) {
|
||||||
#ifdef ARCHITECTURE_x86_64
|
#ifdef ARCHITECTURE_x86_64
|
||||||
if (!is_interpreted)
|
if (!is_interpreted)
|
||||||
return std::make_unique<MacroJITx64Impl>(code);
|
return std::make_unique<MacroJITx64Impl>(system, code);
|
||||||
#endif
|
#endif
|
||||||
return MacroInterpreterImpl(code);
|
return MacroInterpreterImpl(code);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,10 @@
|
||||||
#include "common/bit_field.h"
|
#include "common/bit_field.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
class System;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Tegra {
|
namespace Tegra {
|
||||||
|
|
||||||
namespace Engines {
|
namespace Engines {
|
||||||
|
|
@ -106,61 +110,61 @@ struct HLEMacro {
|
||||||
/// also assigning the base vertex/instance.
|
/// also assigning the base vertex/instance.
|
||||||
struct HLE_DrawArraysIndirect final {
|
struct HLE_DrawArraysIndirect final {
|
||||||
HLE_DrawArraysIndirect(bool extended_) noexcept : extended{extended_} {}
|
HLE_DrawArraysIndirect(bool extended_) noexcept : extended{extended_} {}
|
||||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||||
void Fallback(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters);
|
void Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters);
|
||||||
bool extended;
|
bool extended;
|
||||||
};
|
};
|
||||||
/// @note: these macros have two versions, a normal and extended version, with the extended version
|
/// @note: these macros have two versions, a normal and extended version, with the extended version
|
||||||
/// also assigning the base vertex/instance.
|
/// also assigning the base vertex/instance.
|
||||||
struct HLE_DrawIndexedIndirect final {
|
struct HLE_DrawIndexedIndirect final {
|
||||||
explicit HLE_DrawIndexedIndirect(bool extended_) noexcept : extended{extended_} {}
|
explicit HLE_DrawIndexedIndirect(bool extended_) noexcept : extended{extended_} {}
|
||||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||||
void Fallback(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters);
|
void Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters);
|
||||||
bool extended;
|
bool extended;
|
||||||
};
|
};
|
||||||
struct HLE_MultiLayerClear final {
|
struct HLE_MultiLayerClear final {
|
||||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||||
};
|
};
|
||||||
struct HLE_MultiDrawIndexedIndirectCount final {
|
struct HLE_MultiDrawIndexedIndirectCount final {
|
||||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||||
void Fallback(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters);
|
void Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters);
|
||||||
};
|
};
|
||||||
struct HLE_DrawIndirectByteCount final {
|
struct HLE_DrawIndirectByteCount final {
|
||||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||||
void Fallback(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters);
|
void Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters);
|
||||||
};
|
};
|
||||||
struct HLE_C713C83D8F63CCF3 final {
|
struct HLE_C713C83D8F63CCF3 final {
|
||||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||||
};
|
};
|
||||||
struct HLE_D7333D26E0A93EDE final {
|
struct HLE_D7333D26E0A93EDE final {
|
||||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||||
};
|
};
|
||||||
struct HLE_BindShader final {
|
struct HLE_BindShader final {
|
||||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||||
};
|
};
|
||||||
struct HLE_SetRasterBoundingBox final {
|
struct HLE_SetRasterBoundingBox final {
|
||||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||||
};
|
};
|
||||||
struct HLE_ClearConstBuffer final {
|
struct HLE_ClearConstBuffer final {
|
||||||
HLE_ClearConstBuffer(size_t base_size_) noexcept : base_size{base_size_} {}
|
HLE_ClearConstBuffer(size_t base_size_) noexcept : base_size{base_size_} {}
|
||||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||||
size_t base_size;
|
size_t base_size;
|
||||||
};
|
};
|
||||||
struct HLE_ClearMemory final {
|
struct HLE_ClearMemory final {
|
||||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||||
std::vector<u32> zero_memory;
|
std::vector<u32> zero_memory;
|
||||||
};
|
};
|
||||||
struct HLE_TransformFeedbackSetup final {
|
struct HLE_TransformFeedbackSetup final {
|
||||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||||
};
|
};
|
||||||
struct MacroInterpreterImpl final {
|
struct MacroInterpreterImpl final {
|
||||||
MacroInterpreterImpl() {}
|
MacroInterpreterImpl() {}
|
||||||
MacroInterpreterImpl(std::span<const u32> code_) : code{code_} {}
|
MacroInterpreterImpl(std::span<const u32> code_) : code{code_} {}
|
||||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> params, u32 method);
|
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> params, u32 method);
|
||||||
void Reset();
|
void Reset();
|
||||||
bool Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slot);
|
bool Step(Core::System& system, Engines::Maxwell3D& maxwell3d, bool is_delay_slot);
|
||||||
u32 GetALUResult(Macro::ALUOperation operation, u32 src_a, u32 src_b);
|
u32 GetALUResult(Macro::ALUOperation operation, u32 src_a, u32 src_b);
|
||||||
void ProcessResult(Engines::Maxwell3D& maxwell3d, Macro::ResultOperation operation, u32 reg, u32 result);
|
void ProcessResult(Core::System& system, Engines::Maxwell3D& maxwell3d, Macro::ResultOperation operation, u32 reg, u32 result);
|
||||||
bool EvaluateBranchCondition(Macro::BranchCondition cond, u32 value) const;
|
bool EvaluateBranchCondition(Macro::BranchCondition cond, u32 value) const;
|
||||||
Macro::Opcode GetOpcode() const;
|
Macro::Opcode GetOpcode() const;
|
||||||
u32 GetRegister(u32 register_id) const;
|
u32 GetRegister(u32 register_id) const;
|
||||||
|
|
@ -169,7 +173,7 @@ struct MacroInterpreterImpl final {
|
||||||
[[nodiscard]] inline void SetMethodAddress(u32 address) noexcept {
|
[[nodiscard]] inline void SetMethodAddress(u32 address) noexcept {
|
||||||
method_address.raw = address;
|
method_address.raw = address;
|
||||||
}
|
}
|
||||||
void Send(Engines::Maxwell3D& maxwell3d, u32 value);
|
void Send(Core::System& system, Engines::Maxwell3D& maxwell3d, u32 value);
|
||||||
u32 Read(Engines::Maxwell3D& maxwell3d, u32 method) const;
|
u32 Read(Engines::Maxwell3D& maxwell3d, u32 method) const;
|
||||||
u32 FetchParameter();
|
u32 FetchParameter();
|
||||||
/// General purpose macro registers.
|
/// General purpose macro registers.
|
||||||
|
|
@ -192,7 +196,7 @@ struct DynamicCachedMacro {
|
||||||
/// Executes the macro code with the specified input parameters.
|
/// Executes the macro code with the specified input parameters.
|
||||||
/// @param parameters The parameters of the macro
|
/// @param parameters The parameters of the macro
|
||||||
/// @param method The method to execute
|
/// @param method The method to execute
|
||||||
virtual void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, u32 method) = 0;
|
virtual void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, u32 method) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
using AnyCachedMacro = std::variant<
|
using AnyCachedMacro = std::variant<
|
||||||
|
|
@ -227,8 +231,8 @@ struct MacroEngine {
|
||||||
uploaded_macro_code.erase(method);
|
uploaded_macro_code.erase(method);
|
||||||
}
|
}
|
||||||
// Compiles the macro if its not in the cache, and executes the compiled macro
|
// Compiles the macro if its not in the cache, and executes the compiled macro
|
||||||
void Execute(Engines::Maxwell3D& maxwell3d, u32 method, std::span<const u32> parameters);
|
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, u32 method, std::span<const u32> parameters);
|
||||||
AnyCachedMacro Compile(Engines::Maxwell3D& maxwell3d, std::span<const u32> code);
|
AnyCachedMacro Compile(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> code);
|
||||||
struct CacheInfo {
|
struct CacheInfo {
|
||||||
AnyCachedMacro program;
|
AnyCachedMacro program;
|
||||||
u64 hash{};
|
u64 hash{};
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|
@ -204,7 +207,7 @@ void SetupDirtyMisc(Tables& tables) {
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) {
|
void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) {
|
||||||
auto& tables{channel_state.maxwell_3d->dirty.tables};
|
auto& tables{channel_state.payload->maxwell_3d.dirty.tables};
|
||||||
SetupDirtyFlags(tables);
|
SetupDirtyFlags(tables);
|
||||||
SetupDirtyColorMasks(tables);
|
SetupDirtyColorMasks(tables);
|
||||||
SetupDirtyViewports(tables);
|
SetupDirtyViewports(tables);
|
||||||
|
|
@ -231,7 +234,7 @@ void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) {
|
void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) {
|
||||||
flags = &channel_state.maxwell_3d->dirty.flags;
|
flags = &channel_state.payload->maxwell_3d.dirty.flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StateTracker::InvalidateState() {
|
void StateTracker::InvalidateState() {
|
||||||
|
|
|
||||||
|
|
@ -126,16 +126,14 @@ public:
|
||||||
current_query = nullptr;
|
current_query = nullptr;
|
||||||
amend_value = 0;
|
amend_value = 0;
|
||||||
accumulation_value = 0;
|
accumulation_value = 0;
|
||||||
queries_prefix_scan_pass = std::make_unique<QueriesPrefixScanPass>(
|
queries_prefix_scan_pass.emplace(device, scheduler, descriptor_pool, compute_pass_descriptor_queue);
|
||||||
device, scheduler, descriptor_pool, compute_pass_descriptor_queue);
|
|
||||||
|
|
||||||
const VkBufferCreateInfo buffer_ci = {
|
const VkBufferCreateInfo buffer_ci = {
|
||||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
.size = 8,
|
.size = 8,
|
||||||
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
|
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
|
||||||
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
|
|
||||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||||
.queueFamilyIndexCount = 0,
|
.queueFamilyIndexCount = 0,
|
||||||
.pQueueFamilyIndices = nullptr,
|
.pQueueFamilyIndices = nullptr,
|
||||||
|
|
@ -592,8 +590,7 @@ private:
|
||||||
VideoCommon::HostQueryBase* current_query;
|
VideoCommon::HostQueryBase* current_query;
|
||||||
bool has_started{};
|
bool has_started{};
|
||||||
std::mutex flush_guard;
|
std::mutex flush_guard;
|
||||||
|
std::optional<QueriesPrefixScanPass> queries_prefix_scan_pass;
|
||||||
std::unique_ptr<QueriesPrefixScanPass> queries_prefix_scan_pass;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Transform feedback queries
|
// Transform feedback queries
|
||||||
|
|
@ -1266,41 +1263,23 @@ private:
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
struct QueryCacheRuntimeImpl {
|
struct QueryCacheRuntimeImpl {
|
||||||
QueryCacheRuntimeImpl(QueryCacheRuntime& runtime, VideoCore::RasterizerInterface* rasterizer_,
|
QueryCacheRuntimeImpl(QueryCacheRuntime& runtime, VideoCore::RasterizerInterface* rasterizer_, Tegra::MaxwellDeviceMemoryManager& device_memory_, Vulkan::BufferCache& buffer_cache_, const Device& device_, const MemoryAllocator& memory_allocator_, Scheduler& scheduler_, StagingBufferPool& staging_pool_, ComputePassDescriptorQueue& compute_pass_descriptor_queue, DescriptorPool& descriptor_pool, TextureCache& texture_cache_)
|
||||||
Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
: rasterizer{rasterizer_}, device_memory{device_memory_}, buffer_cache{buffer_cache_}
|
||||||
Vulkan::BufferCache& buffer_cache_, const Device& device_,
|
, device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_}
|
||||||
const MemoryAllocator& memory_allocator_, Scheduler& scheduler_,
|
, staging_pool{staging_pool_}, guest_streamer(0, runtime)
|
||||||
StagingBufferPool& staging_pool_,
|
, sample_streamer(size_t(QueryType::ZPassPixelCount64), runtime, rasterizer, texture_cache_, device, scheduler, memory_allocator, compute_pass_descriptor_queue, descriptor_pool)
|
||||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue,
|
, tfb_streamer(size_t(QueryType::StreamingByteCount), runtime, device, scheduler, memory_allocator, staging_pool)
|
||||||
DescriptorPool& descriptor_pool, TextureCache& texture_cache_)
|
, primitives_succeeded_streamer(size_t(QueryType::StreamingPrimitivesSucceeded), runtime, tfb_streamer, device_memory_)
|
||||||
: rasterizer{rasterizer_}, device_memory{device_memory_}, buffer_cache{buffer_cache_},
|
, primitives_needed_minus_succeeded_streamer(size_t(QueryType::StreamingPrimitivesNeededMinusSucceeded), runtime, 0u)
|
||||||
device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_},
|
, hcr_setup{}, hcr_is_set{}, is_hcr_running{}, maxwell3d{} {
|
||||||
staging_pool{staging_pool_}, guest_streamer(0, runtime),
|
|
||||||
sample_streamer(static_cast<size_t>(QueryType::ZPassPixelCount64), runtime, rasterizer,
|
|
||||||
texture_cache_, device, scheduler, memory_allocator,
|
|
||||||
compute_pass_descriptor_queue, descriptor_pool),
|
|
||||||
tfb_streamer(static_cast<size_t>(QueryType::StreamingByteCount), runtime, device,
|
|
||||||
scheduler, memory_allocator, staging_pool),
|
|
||||||
primitives_succeeded_streamer(
|
|
||||||
static_cast<size_t>(QueryType::StreamingPrimitivesSucceeded), runtime, tfb_streamer,
|
|
||||||
device_memory_),
|
|
||||||
primitives_needed_minus_succeeded_streamer(
|
|
||||||
static_cast<size_t>(QueryType::StreamingPrimitivesNeededMinusSucceeded), runtime, 0u),
|
|
||||||
hcr_setup{}, hcr_is_set{}, is_hcr_running{}, maxwell3d{} {
|
|
||||||
|
|
||||||
hcr_setup.sType = VK_STRUCTURE_TYPE_CONDITIONAL_RENDERING_BEGIN_INFO_EXT;
|
hcr_setup.sType = VK_STRUCTURE_TYPE_CONDITIONAL_RENDERING_BEGIN_INFO_EXT;
|
||||||
hcr_setup.pNext = nullptr;
|
hcr_setup.pNext = nullptr;
|
||||||
hcr_setup.flags = 0;
|
hcr_setup.flags = 0;
|
||||||
|
|
||||||
const bool has_conditional_rendering = device.IsExtConditionalRendering();
|
VkBufferUsageFlags hcr_buffer_usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
||||||
if (has_conditional_rendering) {
|
if (device.IsExtConditionalRendering()) {
|
||||||
conditional_resolve_pass = std::make_unique<ConditionalRenderingResolvePass>(
|
conditional_resolve_pass.emplace(device, scheduler, descriptor_pool, compute_pass_descriptor_queue);
|
||||||
device, scheduler, descriptor_pool, compute_pass_descriptor_queue);
|
|
||||||
}
|
|
||||||
|
|
||||||
VkBufferUsageFlags hcr_buffer_usage =
|
|
||||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
|
||||||
if (has_conditional_rendering) {
|
|
||||||
hcr_buffer_usage |= VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT;
|
hcr_buffer_usage |= VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1339,7 +1318,7 @@ struct QueryCacheRuntimeImpl {
|
||||||
std::vector<std::vector<VkBufferCopy>> copies_setup;
|
std::vector<std::vector<VkBufferCopy>> copies_setup;
|
||||||
|
|
||||||
// Host conditional rendering data
|
// Host conditional rendering data
|
||||||
std::unique_ptr<ConditionalRenderingResolvePass> conditional_resolve_pass;
|
std::optional<ConditionalRenderingResolvePass> conditional_resolve_pass;
|
||||||
vk::Buffer hcr_resolve_buffer;
|
vk::Buffer hcr_resolve_buffer;
|
||||||
VkConditionalRenderingBeginInfoEXT hcr_setup;
|
VkConditionalRenderingBeginInfoEXT hcr_setup;
|
||||||
VkBuffer hcr_buffer;
|
VkBuffer hcr_buffer;
|
||||||
|
|
@ -1351,13 +1330,7 @@ struct QueryCacheRuntimeImpl {
|
||||||
Maxwell3D* maxwell3d;
|
Maxwell3D* maxwell3d;
|
||||||
};
|
};
|
||||||
|
|
||||||
QueryCacheRuntime::QueryCacheRuntime(VideoCore::RasterizerInterface* rasterizer,
|
QueryCacheRuntime::QueryCacheRuntime(VideoCore::RasterizerInterface* rasterizer, Tegra::MaxwellDeviceMemoryManager& device_memory_, Vulkan::BufferCache& buffer_cache_, const Device& device_, const MemoryAllocator& memory_allocator_, Scheduler& scheduler_, StagingBufferPool& staging_pool_, ComputePassDescriptorQueue& compute_pass_descriptor_queue, DescriptorPool& descriptor_pool, TextureCache& texture_cache_) {
|
||||||
Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
|
||||||
Vulkan::BufferCache& buffer_cache_, const Device& device_,
|
|
||||||
const MemoryAllocator& memory_allocator_,
|
|
||||||
Scheduler& scheduler_, StagingBufferPool& staging_pool_,
|
|
||||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue,
|
|
||||||
DescriptorPool& descriptor_pool, TextureCache& texture_cache_) {
|
|
||||||
impl = std::make_unique<QueryCacheRuntimeImpl>(
|
impl = std::make_unique<QueryCacheRuntimeImpl>(
|
||||||
*this, rasterizer, device_memory_, buffer_cache_, device_, memory_allocator_, scheduler_,
|
*this, rasterizer, device_memory_, buffer_cache_, device_, memory_allocator_, scheduler_,
|
||||||
staging_pool_, compute_pass_descriptor_queue, descriptor_pool, texture_cache_);
|
staging_pool_, compute_pass_descriptor_queue, descriptor_pool, texture_cache_);
|
||||||
|
|
@ -1686,8 +1659,7 @@ void QueryCacheRuntime::SyncValues(std::span<SyncValuesType> values, VkBuffer ba
|
||||||
}
|
}
|
||||||
|
|
||||||
impl->scheduler.RequestOutsideRenderPassOperationContext();
|
impl->scheduler.RequestOutsideRenderPassOperationContext();
|
||||||
impl->scheduler.Record([src_buffer, dst_buffers = std::move(impl->buffers_to_upload_to),
|
impl->scheduler.Record([src_buffer, dst_buffers = std::move(impl->buffers_to_upload_to), vk_copies = std::move(impl->copies_setup)](vk::CommandBuffer cmdbuf) {
|
||||||
vk_copies = std::move(impl->copies_setup)](vk::CommandBuffer cmdbuf) {
|
|
||||||
size_t size = dst_buffers.size();
|
size_t size = dst_buffers.size();
|
||||||
for (size_t i = 0; i < size; i++) {
|
for (size_t i = 0; i < size; i++) {
|
||||||
cmdbuf.CopyBuffer(src_buffer, dst_buffers[i].first, vk_copies[i]);
|
cmdbuf.CopyBuffer(src_buffer, dst_buffers[i].first, vk_copies[i]);
|
||||||
|
|
|
||||||
|
|
@ -235,7 +235,7 @@ void SetupRasterModes(Maxwell3D::DirtyState::Tables &tables) {
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) {
|
void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) {
|
||||||
auto& tables{channel_state.maxwell_3d->dirty.tables};
|
auto& tables{channel_state.payload->maxwell_3d.dirty.tables};
|
||||||
SetupDirtyFlags(tables);
|
SetupDirtyFlags(tables);
|
||||||
SetupDirtyViewports(tables);
|
SetupDirtyViewports(tables);
|
||||||
SetupDirtyScissors(tables);
|
SetupDirtyScissors(tables);
|
||||||
|
|
@ -258,7 +258,7 @@ void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) {
|
void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) {
|
||||||
flags = &channel_state.maxwell_3d->dirty.flags;
|
flags = &channel_state.payload->maxwell_3d.dirty.flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StateTracker::InvalidateState() {
|
void StateTracker::InvalidateState() {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <variant>
|
||||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
@ -12,20 +15,15 @@ namespace Vulkan {
|
||||||
class Device;
|
class Device;
|
||||||
class Scheduler;
|
class Scheduler;
|
||||||
|
|
||||||
struct DescriptorUpdateEntry {
|
union DescriptorUpdateEntry {
|
||||||
struct Empty {};
|
|
||||||
|
|
||||||
DescriptorUpdateEntry() = default;
|
DescriptorUpdateEntry() = default;
|
||||||
DescriptorUpdateEntry(VkDescriptorImageInfo image_) : image{image_} {}
|
DescriptorUpdateEntry(VkDescriptorImageInfo image_) : image{image_} {}
|
||||||
DescriptorUpdateEntry(VkDescriptorBufferInfo buffer_) : buffer{buffer_} {}
|
DescriptorUpdateEntry(VkDescriptorBufferInfo buffer_) : buffer{buffer_} {}
|
||||||
DescriptorUpdateEntry(VkBufferView texel_buffer_) : texel_buffer{texel_buffer_} {}
|
DescriptorUpdateEntry(VkBufferView texel_buffer_) : texel_buffer{texel_buffer_} {}
|
||||||
|
std::monostate empty{};
|
||||||
union {
|
VkDescriptorImageInfo image;
|
||||||
Empty empty{};
|
VkDescriptorBufferInfo buffer;
|
||||||
VkDescriptorImageInfo image;
|
VkBufferView texel_buffer;
|
||||||
VkDescriptorBufferInfo buffer;
|
|
||||||
VkBufferView texel_buffer;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class UpdateDescriptorQueue final {
|
class UpdateDescriptorQueue final {
|
||||||
|
|
|
||||||
|
|
@ -45,23 +45,23 @@ std::unique_ptr<VideoCore::RendererBase> CreateRenderer(Core::System& system, Co
|
||||||
|
|
||||||
namespace VideoCore {
|
namespace VideoCore {
|
||||||
|
|
||||||
std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) {
|
/// @brief Creates an emulated GPU instance using the given system context.
|
||||||
|
void CreateGPU(std::optional<Tegra::GPU>& gpu, Core::Frontend::EmuWindow& emu_window, Core::System& system) {
|
||||||
Settings::UpdateRescalingInfo();
|
Settings::UpdateRescalingInfo();
|
||||||
|
|
||||||
const auto nvdec_value = Settings::values.nvdec_emulation.GetValue();
|
const auto nvdec_value = Settings::values.nvdec_emulation.GetValue();
|
||||||
const bool use_nvdec = nvdec_value != Settings::NvdecEmulation::Off;
|
const bool use_nvdec = nvdec_value != Settings::NvdecEmulation::Off;
|
||||||
const bool use_async = Settings::values.use_asynchronous_gpu_emulation.GetValue();
|
const bool use_async = Settings::values.use_asynchronous_gpu_emulation.GetValue();
|
||||||
auto gpu = std::make_unique<Tegra::GPU>(system, use_async, use_nvdec);
|
gpu.emplace(system, use_async, use_nvdec);
|
||||||
auto context = emu_window.CreateSharedContext();
|
auto context = emu_window.CreateSharedContext();
|
||||||
auto scope = context->Acquire();
|
auto scope = context->Acquire();
|
||||||
try {
|
try {
|
||||||
auto renderer = CreateRenderer(system, emu_window, *gpu, std::move(context));
|
auto renderer = CreateRenderer(system, emu_window, *gpu, std::move(context));
|
||||||
gpu->BindRenderer(std::move(renderer));
|
gpu->BindRenderer(std::move(renderer));
|
||||||
return gpu;
|
|
||||||
} catch (const std::runtime_error& exception) {
|
} catch (const std::runtime_error& exception) {
|
||||||
scope.Cancel();
|
scope.Cancel();
|
||||||
LOG_ERROR(HW_GPU, "Failed to initialize GPU: {}", exception.what());
|
LOG_ERROR(HW_GPU, "Failed to initialize GPU: {}", exception.what());
|
||||||
return nullptr;
|
gpu.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,13 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
class System;
|
class System;
|
||||||
|
|
@ -20,8 +24,6 @@ class GPU;
|
||||||
namespace VideoCore {
|
namespace VideoCore {
|
||||||
|
|
||||||
class RendererBase;
|
class RendererBase;
|
||||||
|
void CreateGPU(std::optional<Tegra::GPU>& gpu, Core::Frontend::EmuWindow& emu_window, Core::System& system);
|
||||||
/// Creates an emulated GPU instance using the given system context.
|
|
||||||
std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system);
|
|
||||||
|
|
||||||
} // namespace VideoCore
|
} // namespace VideoCore
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue