mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-09 13:36:35 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3352f0662c | |||
| 95e42257f7 | |||
| 8961bc37f3 | |||
| e1e8ae68bf |
@@ -22,11 +22,9 @@ using namespace std::literals;
|
|||||||
constexpr auto INCREMENT_TIME{5ms};
|
constexpr auto INCREMENT_TIME{5ms};
|
||||||
|
|
||||||
DeviceSession::DeviceSession(Core::System& system_)
|
DeviceSession::DeviceSession(Core::System& system_)
|
||||||
: system{system_}
|
: system{system_}, thread_event{Core::Timing::CreateEvent(
|
||||||
, thread_event{system_.CreateTimingEvent("AudioOutSampleTick", [this](s64 time, std::chrono::nanoseconds) {
|
"AudioOutSampleTick",
|
||||||
return ThreadFunc();
|
[this](s64 time, std::chrono::nanoseconds) { return ThreadFunc(); })} {}
|
||||||
})}
|
|
||||||
{}
|
|
||||||
|
|
||||||
DeviceSession::~DeviceSession() {
|
DeviceSession::~DeviceSession() {
|
||||||
Finalize();
|
Finalize();
|
||||||
|
|||||||
@@ -969,8 +969,4 @@ void System::ApplySettings() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Core::Timing::EventType> System::CreateTimingEvent(std::string name, Core::Timing::TimedCallback&& callback) {
|
|
||||||
return std::make_shared<Core::Timing::EventType>(std::move(callback), std::move(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
#include "core/file_sys/vfs/vfs_types.h"
|
#include "core/file_sys/vfs/vfs_types.h"
|
||||||
#include "core/hle/service/os/event.h"
|
#include "core/hle/service/os/event.h"
|
||||||
#include "core/hle/service/kernel_helpers.h"
|
#include "core/hle/service/kernel_helpers.h"
|
||||||
#include "core/core_timing.h"
|
|
||||||
|
|
||||||
namespace Core::Frontend {
|
namespace Core::Frontend {
|
||||||
class EmuWindow;
|
class EmuWindow;
|
||||||
@@ -439,9 +438,6 @@ public:
|
|||||||
/// Applies any changes to settings to this core instance.
|
/// Applies any changes to settings to this core instance.
|
||||||
void ApplySettings();
|
void ApplySettings();
|
||||||
|
|
||||||
std::shared_ptr<Core::Timing::EventType> CreateTimingEvent(std::string name, Core::Timing::TimedCallback&& callback);
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct Impl;
|
struct Impl;
|
||||||
std::unique_ptr<Impl> impl;
|
std::unique_ptr<Impl> impl;
|
||||||
};
|
};
|
||||||
|
|||||||
+45
-14
@@ -23,6 +23,10 @@ namespace Core::Timing {
|
|||||||
|
|
||||||
constexpr s64 MAX_SLICE_LENGTH = 10000;
|
constexpr s64 MAX_SLICE_LENGTH = 10000;
|
||||||
|
|
||||||
|
std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) {
|
||||||
|
return std::make_shared<EventType>(std::move(callback), std::move(name));
|
||||||
|
}
|
||||||
|
|
||||||
struct CoreTiming::Event {
|
struct CoreTiming::Event {
|
||||||
s64 time;
|
s64 time;
|
||||||
u64 fifo_order;
|
u64 fifo_order;
|
||||||
@@ -32,10 +36,11 @@ struct CoreTiming::Event {
|
|||||||
|
|
||||||
// Sort by time, unless the times are the same, in which case sort by
|
// Sort by time, unless the times are the same, in which case sort by
|
||||||
// the order added to the queue
|
// the order added to the queue
|
||||||
friend bool operator>(const Event& left, const Event& right) noexcept {
|
friend bool operator>(const Event& left, const Event& right) {
|
||||||
return std::tie(left.time, left.fifo_order) > std::tie(right.time, right.fifo_order);
|
return std::tie(left.time, left.fifo_order) > std::tie(right.time, right.fifo_order);
|
||||||
}
|
}
|
||||||
friend bool operator<(const Event& left, const Event& right) noexcept {
|
|
||||||
|
friend bool operator<(const Event& left, const Event& right) {
|
||||||
return std::tie(left.time, left.fifo_order) < std::tie(right.time, right.fifo_order);
|
return std::tie(left.time, left.fifo_order) < std::tie(right.time, right.fifo_order);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -55,6 +60,8 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
|
|||||||
Common::SetCurrentThreadName("HostTiming");
|
Common::SetCurrentThreadName("HostTiming");
|
||||||
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
|
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
|
||||||
on_thread_init();
|
on_thread_init();
|
||||||
|
has_started = true;
|
||||||
|
|
||||||
// base frequency in MHz: 1ns (10^-9) = 1GHz (10^9)
|
// base frequency in MHz: 1ns (10^-9) = 1GHz (10^9)
|
||||||
while (!stop_token.stop_requested()) {
|
while (!stop_token.stop_requested()) {
|
||||||
while (!paused && !stop_token.stop_requested()) {
|
while (!paused && !stop_token.stop_requested()) {
|
||||||
@@ -70,8 +77,8 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
|
|||||||
// continue.
|
// continue.
|
||||||
wait_set = true;
|
wait_set = true;
|
||||||
event.Wait();
|
event.Wait();
|
||||||
wait_set = false;
|
|
||||||
}
|
}
|
||||||
|
wait_set = false;
|
||||||
}
|
}
|
||||||
paused_set = true;
|
paused_set = true;
|
||||||
pause_event.Wait();
|
pause_event.Wait();
|
||||||
@@ -137,28 +144,39 @@ void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,
|
|||||||
event.Set();
|
event.Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time, std::chrono::nanoseconds resched_time, const std::shared_ptr<EventType>& event_type, bool absolute_time) {
|
void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
|
||||||
|
std::chrono::nanoseconds resched_time,
|
||||||
|
const std::shared_ptr<EventType>& event_type,
|
||||||
|
bool absolute_time) {
|
||||||
{
|
{
|
||||||
std::scoped_lock scope{basic_lock};
|
std::scoped_lock scope{basic_lock};
|
||||||
const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time};
|
const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time};
|
||||||
auto h = event_queue.emplace(Event{next_time.count(), event_fifo_id++, event_type, resched_time.count()});
|
|
||||||
|
auto h{event_queue.emplace(
|
||||||
|
Event{next_time.count(), event_fifo_id++, event_type, resched_time.count()})};
|
||||||
(*h).handle = h;
|
(*h).handle = h;
|
||||||
}
|
}
|
||||||
|
|
||||||
event.Set();
|
event.Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, UnscheduleEventType type) {
|
void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
|
||||||
|
UnscheduleEventType type) {
|
||||||
{
|
{
|
||||||
std::scoped_lock lk{basic_lock};
|
std::scoped_lock lk{basic_lock};
|
||||||
|
|
||||||
std::vector<heap_t::handle_type> to_remove;
|
std::vector<heap_t::handle_type> to_remove;
|
||||||
for (auto it = event_queue.begin(); it != event_queue.end(); it++) {
|
for (auto itr = event_queue.begin(); itr != event_queue.end(); itr++) {
|
||||||
auto const& e = *it;
|
const Event& e = *itr;
|
||||||
if (e.type.lock().get() == event_type.get()) {
|
if (e.type.lock().get() == event_type.get()) {
|
||||||
to_remove.push_back(it->handle);
|
to_remove.push_back(itr->handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto& h : to_remove)
|
|
||||||
|
for (auto& h : to_remove) {
|
||||||
event_queue.erase(h);
|
event_queue.erase(h);
|
||||||
|
}
|
||||||
|
|
||||||
event_type->sequence_number++;
|
event_type->sequence_number++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,15 +187,16 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, U
|
|||||||
}
|
}
|
||||||
|
|
||||||
static u64 GetNextTickCount(u64 next_ticks) {
|
static u64 GetNextTickCount(u64 next_ticks) {
|
||||||
if (Settings::values.use_custom_cpu_ticks.GetValue())
|
if (Settings::values.use_custom_cpu_ticks.GetValue()) {
|
||||||
return Settings::values.cpu_ticks.GetValue();
|
return Settings::values.cpu_ticks.GetValue();
|
||||||
|
}
|
||||||
return next_ticks;
|
return next_ticks;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreTiming::AddTicks(u64 ticks_to_add) {
|
void CoreTiming::AddTicks(u64 ticks_to_add) {
|
||||||
const u64 ticks = GetNextTickCount(ticks_to_add);
|
const u64 ticks = GetNextTickCount(ticks_to_add);
|
||||||
cpu_ticks += ticks;
|
cpu_ticks += ticks;
|
||||||
downcount -= s64(ticks);
|
downcount -= static_cast<s64>(ticks);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreTiming::Idle() {
|
void CoreTiming::Idle() {
|
||||||
@@ -251,14 +270,19 @@ std::optional<s64> CoreTiming::Advance() {
|
|||||||
next_time = pause_end_time + next_schedule_time;
|
next_time = pause_end_time + next_schedule_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
event_queue.update(evt.handle, Event{next_time, event_fifo_id++, evt.type, next_schedule_time, evt.handle});
|
event_queue.update(evt.handle, Event{next_time, event_fifo_id++, evt.type,
|
||||||
|
next_schedule_time, evt.handle});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
global_timer = GetGlobalTimeNs().count();
|
global_timer = GetGlobalTimeNs().count();
|
||||||
}
|
}
|
||||||
|
|
||||||
return event_queue.empty() ? std::optional<s64>{} : event_queue.top().time;
|
if (!event_queue.empty()) {
|
||||||
|
return event_queue.top().time;
|
||||||
|
} else {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreTiming::Reset() {
|
void CoreTiming::Reset() {
|
||||||
@@ -269,6 +293,7 @@ void CoreTiming::Reset() {
|
|||||||
timer_thread.request_stop();
|
timer_thread.request_stop();
|
||||||
timer_thread.join();
|
timer_thread.join();
|
||||||
}
|
}
|
||||||
|
has_started = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Returns current time in nanoseconds.
|
/// @brief Returns current time in nanoseconds.
|
||||||
@@ -285,4 +310,10 @@ std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const noexcept {
|
|||||||
: std::chrono::microseconds{Common::WallClock::CPUTickToUS(cpu_ticks)};
|
: std::chrono::microseconds{Common::WallClock::CPUTickToUS(cpu_ticks)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
void CoreTiming::SetTimerResolutionNs(std::chrono::nanoseconds ns) {
|
||||||
|
timer_resolution_ns = ns.count();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace Core::Timing
|
} // namespace Core::Timing
|
||||||
|
|||||||
+33
-11
@@ -29,7 +29,8 @@ using TimedCallback = std::function<std::optional<std::chrono::nanoseconds>(
|
|||||||
|
|
||||||
/// Contains the characteristics of a particular event.
|
/// Contains the characteristics of a particular event.
|
||||||
struct EventType {
|
struct EventType {
|
||||||
explicit EventType(TimedCallback&& callback_, std::string&& name_) : callback{std::move(callback_)}, name{std::move(name_)}, sequence_number{0} {}
|
explicit EventType(TimedCallback&& callback_, std::string&& name_)
|
||||||
|
: callback{std::move(callback_)}, name{std::move(name_)}, sequence_number{0} {}
|
||||||
|
|
||||||
/// The event's callback function.
|
/// The event's callback function.
|
||||||
TimedCallback callback;
|
TimedCallback callback;
|
||||||
@@ -89,6 +90,11 @@ public:
|
|||||||
/// Checks if core timing is running.
|
/// Checks if core timing is running.
|
||||||
bool IsRunning() const;
|
bool IsRunning() const;
|
||||||
|
|
||||||
|
/// Checks if the timer thread has started.
|
||||||
|
bool HasStarted() const {
|
||||||
|
return has_started;
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks if there are any pending time events.
|
/// Checks if there are any pending time events.
|
||||||
bool HasPendingEvents() const;
|
bool HasPendingEvents() const;
|
||||||
|
|
||||||
@@ -128,29 +134,45 @@ public:
|
|||||||
/// Checks for events manually and returns time in nanoseconds for next event, threadsafe.
|
/// Checks for events manually and returns time in nanoseconds for next event, threadsafe.
|
||||||
std::optional<s64> Advance();
|
std::optional<s64> Advance();
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
void SetTimerResolutionNs(std::chrono::nanoseconds ns);
|
||||||
|
#endif
|
||||||
|
|
||||||
struct Event;
|
struct Event;
|
||||||
|
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
using heap_t = boost::heap::fibonacci_heap<CoreTiming::Event, boost::heap::compare<std::greater<>>>;
|
using heap_t = boost::heap::fibonacci_heap<CoreTiming::Event, boost::heap::compare<std::greater<>>>;
|
||||||
Common::Event event{};
|
|
||||||
Common::Event pause_event{};
|
|
||||||
|
|
||||||
alignas(64) mutable std::mutex basic_lock;
|
|
||||||
alignas(64) std::mutex advance_lock;
|
|
||||||
alignas(64) std::atomic<bool> paused{};
|
|
||||||
alignas(64) std::atomic<bool> paused_set{};
|
|
||||||
alignas(64) std::atomic<bool> wait_set{};
|
|
||||||
std::function<void()> on_thread_init{};
|
|
||||||
heap_t event_queue;
|
heap_t event_queue;
|
||||||
std::jthread timer_thread;
|
|
||||||
s64 global_timer = 0;
|
s64 global_timer = 0;
|
||||||
|
#ifdef _WIN32
|
||||||
|
s64 timer_resolution_ns;
|
||||||
|
#endif
|
||||||
u64 event_fifo_id = 0;
|
u64 event_fifo_id = 0;
|
||||||
s64 pause_end_time{};
|
s64 pause_end_time{};
|
||||||
/// Cycle timing
|
/// Cycle timing
|
||||||
u64 cpu_ticks{};
|
u64 cpu_ticks{};
|
||||||
s64 downcount{};
|
s64 downcount{};
|
||||||
|
Common::Event event{};
|
||||||
|
Common::Event pause_event{};
|
||||||
|
std::function<void()> on_thread_init{};
|
||||||
|
std::jthread timer_thread;
|
||||||
|
mutable std::mutex basic_lock;
|
||||||
|
std::mutex advance_lock;
|
||||||
|
std::atomic<bool> paused{};
|
||||||
|
std::atomic<bool> paused_set{};
|
||||||
|
std::atomic<bool> wait_set{};
|
||||||
|
std::atomic<bool> has_started{};
|
||||||
bool is_multicore{};
|
bool is_multicore{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Creates a core timing event with the given name and callback.
|
||||||
|
///
|
||||||
|
/// @param name The name of the core timing event to create.
|
||||||
|
/// @param callback The callback to execute for the event.
|
||||||
|
///
|
||||||
|
/// @returns An EventType instance representing the created event.
|
||||||
|
///
|
||||||
|
std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback);
|
||||||
|
|
||||||
} // namespace Core::Timing
|
} // namespace Core::Timing
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
@@ -13,10 +13,11 @@ namespace Kernel {
|
|||||||
|
|
||||||
void KHardwareTimer::Initialize() {
|
void KHardwareTimer::Initialize() {
|
||||||
// Create the timing callback to register with CoreTiming.
|
// Create the timing callback to register with CoreTiming.
|
||||||
m_event_type = m_kernel.System().CreateTimingEvent("KHardwareTimer::Callback", [this](s64, std::chrono::nanoseconds) {
|
m_event_type = Core::Timing::CreateEvent("KHardwareTimer::Callback",
|
||||||
this->DoTask();
|
[this](s64, std::chrono::nanoseconds) {
|
||||||
return std::nullopt;
|
this->DoTask();
|
||||||
});
|
return std::nullopt;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void KHardwareTimer::Finalize() {
|
void KHardwareTimer::Finalize() {
|
||||||
|
|||||||
@@ -255,7 +255,7 @@ struct KernelCore::Impl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void InitializePreemption(KernelCore& kernel) {
|
void InitializePreemption(KernelCore& kernel) {
|
||||||
preemption_event = system.CreateTimingEvent("PreemptionCallback", [this, &kernel](s64 time, std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
|
preemption_event = Core::Timing::CreateEvent("PreemptionCallback", [this, &kernel](s64 time, std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
|
||||||
{
|
{
|
||||||
KScopedSchedulerLock lock(kernel);
|
KScopedSchedulerLock lock(kernel);
|
||||||
global_scheduler_context->PreemptThreads(kernel);
|
global_scheduler_context->PreemptThreads(kernel);
|
||||||
|
|||||||
@@ -25,11 +25,16 @@ AlarmWorker::~AlarmWorker() {
|
|||||||
|
|
||||||
void AlarmWorker::Initialize(std::shared_ptr<Service::PSC::Time::ServiceManager> time_m) {
|
void AlarmWorker::Initialize(std::shared_ptr<Service::PSC::Time::ServiceManager> time_m) {
|
||||||
m_time_m = std::move(time_m);
|
m_time_m = std::move(time_m);
|
||||||
|
|
||||||
m_timer_event = m_ctx.CreateEvent("Glue:AlarmWorker:TimerEvent");
|
m_timer_event = m_ctx.CreateEvent("Glue:AlarmWorker:TimerEvent");
|
||||||
m_timer_timing_event = m_system.CreateTimingEvent("Glue:AlarmWorker::AlarmTimer", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
m_timer_timing_event = Core::Timing::CreateEvent(
|
||||||
m_timer_event->Signal(m_system.Kernel());
|
"Glue:AlarmWorker::AlarmTimer",
|
||||||
return std::nullopt;
|
[this](s64 time,
|
||||||
});
|
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||||
|
m_timer_event->Signal(m_system.Kernel());
|
||||||
|
return std::nullopt;
|
||||||
|
});
|
||||||
|
|
||||||
AttachToClosestAlarmEvent();
|
AttachToClosestAlarmEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,19 +22,28 @@ namespace Service::Glue::Time {
|
|||||||
|
|
||||||
TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource,
|
TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource,
|
||||||
FileTimestampWorker& file_timestamp_worker)
|
FileTimestampWorker& file_timestamp_worker)
|
||||||
: m_system{system}, m_ctx{m_system, "Glue:TimeWorker"}, m_event{m_ctx.CreateEvent("Glue:TimeWorker:Event")},
|
: m_system{system}, m_ctx{m_system, "Glue:TimeWorker"}, m_event{m_ctx.CreateEvent(
|
||||||
|
"Glue:TimeWorker:Event")},
|
||||||
m_steady_clock_resource{steady_clock_resource},
|
m_steady_clock_resource{steady_clock_resource},
|
||||||
m_file_timestamp_worker{file_timestamp_worker}, m_timer_steady_clock{m_ctx.CreateEvent("Glue:TimeWorker:SteadyClockTimerEvent")},
|
m_file_timestamp_worker{file_timestamp_worker}, m_timer_steady_clock{m_ctx.CreateEvent(
|
||||||
|
"Glue:TimeWorker:SteadyClockTimerEvent")},
|
||||||
m_timer_file_system{m_ctx.CreateEvent("Glue:TimeWorker:FileTimeTimerEvent")},
|
m_timer_file_system{m_ctx.CreateEvent("Glue:TimeWorker:FileTimeTimerEvent")},
|
||||||
m_alarm_worker{m_system, m_steady_clock_resource}, m_pm_state_change_handler{m_alarm_worker} {
|
m_alarm_worker{m_system, m_steady_clock_resource}, m_pm_state_change_handler{m_alarm_worker} {
|
||||||
m_timer_steady_clock_timing_event = m_system.CreateTimingEvent("Time::SteadyClockEvent", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
m_timer_steady_clock_timing_event = Core::Timing::CreateEvent(
|
||||||
m_timer_steady_clock->Signal(m_system.Kernel());
|
"Time::SteadyClockEvent",
|
||||||
return std::nullopt;
|
[this](s64 time,
|
||||||
});
|
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||||
m_timer_file_system_timing_event = m_system.CreateTimingEvent("Time::SteadyClockEvent", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
m_timer_steady_clock->Signal(m_system.Kernel());
|
||||||
m_timer_file_system->Signal(m_system.Kernel());
|
return std::nullopt;
|
||||||
return std::nullopt;
|
});
|
||||||
});
|
|
||||||
|
m_timer_file_system_timing_event = Core::Timing::CreateEvent(
|
||||||
|
"Time::SteadyClockEvent",
|
||||||
|
[this](s64 time,
|
||||||
|
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||||
|
m_timer_file_system->Signal(m_system.Kernel());
|
||||||
|
return std::nullopt;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
TimeWorker::~TimeWorker() {
|
TimeWorker::~TimeWorker() {
|
||||||
|
|||||||
@@ -51,12 +51,17 @@ Hidbus::Hidbus(Core::System& system_)
|
|||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
|
|
||||||
// Register update callbacks
|
// Register update callbacks
|
||||||
hidbus_update_event = system_.CreateTimingEvent("Hidbus::UpdateCallback", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
hidbus_update_event = Core::Timing::CreateEvent(
|
||||||
const auto guard = LockService();
|
"Hidbus::UpdateCallback",
|
||||||
UpdateHidbus(ns_late);
|
[this](s64 time,
|
||||||
return std::nullopt;
|
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||||
});
|
const auto guard = LockService();
|
||||||
system_.CoreTiming().ScheduleLoopingEvent(hidbus_update_ns, hidbus_update_ns, hidbus_update_event);
|
UpdateHidbus(ns_late);
|
||||||
|
return std::nullopt;
|
||||||
|
});
|
||||||
|
|
||||||
|
system_.CoreTiming().ScheduleLoopingEvent(hidbus_update_ns, hidbus_update_ns,
|
||||||
|
hidbus_update_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
Hidbus::~Hidbus() {
|
Hidbus::~Hidbus() {
|
||||||
|
|||||||
@@ -23,17 +23,25 @@ Conductor::Conductor(Core::System& system, Container& container, DisplayList& di
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (system.IsMulticore()) {
|
if (system.IsMulticore()) {
|
||||||
m_event = system.CreateTimingEvent("ScreenComposition", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
m_event = Core::Timing::CreateEvent(
|
||||||
m_signal.Set();
|
"ScreenComposition",
|
||||||
return std::chrono::nanoseconds(this->GetNextTicks());
|
[this](s64 time,
|
||||||
});
|
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||||
|
m_signal.Set();
|
||||||
|
return std::chrono::nanoseconds(this->GetNextTicks());
|
||||||
|
});
|
||||||
|
|
||||||
system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event);
|
system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event);
|
||||||
m_thread = std::jthread([this](std::stop_token token) { this->VsyncThread(token); });
|
m_thread = std::jthread([this](std::stop_token token) { this->VsyncThread(token); });
|
||||||
} else {
|
} else {
|
||||||
m_event = system.CreateTimingEvent("ScreenComposition", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
m_event = Core::Timing::CreateEvent(
|
||||||
this->ProcessVsync();
|
"ScreenComposition",
|
||||||
return std::chrono::nanoseconds(this->GetNextTicks());
|
[this](s64 time,
|
||||||
});
|
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||||
|
this->ProcessVsync();
|
||||||
|
return std::chrono::nanoseconds(this->GetNextTicks());
|
||||||
|
});
|
||||||
|
|
||||||
system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event);
|
system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -231,10 +231,12 @@ CheatEngine::~CheatEngine() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CheatEngine::Initialize() {
|
void CheatEngine::Initialize() {
|
||||||
event = system.CreateTimingEvent("CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
event = Core::Timing::CreateEvent(
|
||||||
FrameCallback(ns_late);
|
"CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id),
|
||||||
return std::nullopt;
|
[this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||||
});
|
FrameCallback(ns_late);
|
||||||
|
return std::nullopt;
|
||||||
|
});
|
||||||
core_timing.ScheduleLoopingEvent(CHEAT_ENGINE_NS, CHEAT_ENGINE_NS, event);
|
core_timing.ScheduleLoopingEvent(CHEAT_ENGINE_NS, CHEAT_ENGINE_NS, event);
|
||||||
|
|
||||||
metadata.process_id = system.ApplicationProcess()->GetProcessId();
|
metadata.process_id = system.ApplicationProcess()->GetProcessId();
|
||||||
|
|||||||
@@ -52,12 +52,14 @@ void MemoryWriteWidth(Core::Memory::Memory& memory, u32 width, VAddr addr, u64 v
|
|||||||
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
Freezer::Freezer(Core::System& system_, Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_)
|
Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_)
|
||||||
: core_timing{core_timing_}, memory{memory_} {
|
: core_timing{core_timing_}, memory{memory_} {
|
||||||
event = system_.CreateTimingEvent("MemoryFreezer::FrameCallback", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
event = Core::Timing::CreateEvent("MemoryFreezer::FrameCallback",
|
||||||
FrameCallback(ns_late);
|
[this](s64 time, std::chrono::nanoseconds ns_late)
|
||||||
return std::nullopt;
|
-> std::optional<std::chrono::nanoseconds> {
|
||||||
});
|
FrameCallback(ns_late);
|
||||||
|
return std::nullopt;
|
||||||
|
});
|
||||||
core_timing.ScheduleEvent(memory_freezer_ns, event);
|
core_timing.ScheduleEvent(memory_freezer_ns, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// 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
|
||||||
|
|
||||||
@@ -14,16 +11,14 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core::Timing {
|
||||||
class System;
|
|
||||||
namespace Timing {
|
|
||||||
class CoreTiming;
|
class CoreTiming;
|
||||||
struct EventType;
|
struct EventType;
|
||||||
} // namespace Core::Timing
|
} // namespace Core::Timing
|
||||||
namespace Memory {
|
|
||||||
|
namespace Core::Memory {
|
||||||
class Memory;
|
class Memory;
|
||||||
} //namespace Core::Memory
|
}
|
||||||
} //namespace Core
|
|
||||||
|
|
||||||
namespace Tools {
|
namespace Tools {
|
||||||
|
|
||||||
@@ -43,7 +38,7 @@ public:
|
|||||||
u64 value;
|
u64 value;
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit Freezer(Core::System& system_, Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_);
|
explicit Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_);
|
||||||
~Freezer();
|
~Freezer();
|
||||||
|
|
||||||
// Enables or disables the entire memory freezer.
|
// Enables or disables the entire memory freezer.
|
||||||
|
|||||||
@@ -56,22 +56,33 @@ ResourceManager::ResourceManager(Core::System& system_,
|
|||||||
applet_resource = std::make_shared<AppletResource>(system);
|
applet_resource = std::make_shared<AppletResource>(system);
|
||||||
|
|
||||||
// Register update callbacks
|
// Register update callbacks
|
||||||
npad_update_event = system.CreateTimingEvent("HID::UpdatePadCallback", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
npad_update_event = Core::Timing::CreateEvent("HID::UpdatePadCallback",
|
||||||
UpdateNpad(ns_late);
|
[this](s64 time, std::chrono::nanoseconds ns_late)
|
||||||
return std::nullopt;
|
-> std::optional<std::chrono::nanoseconds> {
|
||||||
});
|
UpdateNpad(ns_late);
|
||||||
default_update_event = system.CreateTimingEvent("HID::UpdateDefaultCallback", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
return std::nullopt;
|
||||||
UpdateControllers(ns_late);
|
});
|
||||||
return std::nullopt;
|
default_update_event = Core::Timing::CreateEvent(
|
||||||
});
|
"HID::UpdateDefaultCallback",
|
||||||
mouse_keyboard_update_event = system.CreateTimingEvent("HID::UpdateMouseKeyboardCallback", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
[this](s64 time,
|
||||||
UpdateMouseKeyboard(ns_late);
|
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||||
return std::nullopt;
|
UpdateControllers(ns_late);
|
||||||
});
|
return std::nullopt;
|
||||||
motion_update_event = system.CreateTimingEvent("HID::UpdateMotionCallback", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
});
|
||||||
UpdateMotion(ns_late);
|
mouse_keyboard_update_event = Core::Timing::CreateEvent(
|
||||||
return std::nullopt;
|
"HID::UpdateMouseKeyboardCallback",
|
||||||
});
|
[this](s64 time,
|
||||||
|
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||||
|
UpdateMouseKeyboard(ns_late);
|
||||||
|
return std::nullopt;
|
||||||
|
});
|
||||||
|
motion_update_event = Core::Timing::CreateEvent(
|
||||||
|
"HID::UpdateMotionCallback",
|
||||||
|
[this](s64 time,
|
||||||
|
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||||
|
UpdateMotion(ns_late);
|
||||||
|
return std::nullopt;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceManager::~ResourceManager() {
|
ResourceManager::~ResourceManager() {
|
||||||
@@ -256,10 +267,13 @@ void ResourceManager::InitializeTouchScreenSampler() {
|
|||||||
touch_screen = std::make_shared<TouchScreen>(touch_resource);
|
touch_screen = std::make_shared<TouchScreen>(touch_resource);
|
||||||
gesture = std::make_shared<Gesture>(touch_resource);
|
gesture = std::make_shared<Gesture>(touch_resource);
|
||||||
|
|
||||||
touch_update_event = system.CreateTimingEvent("HID::TouchUpdateCallback", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
touch_update_event = Core::Timing::CreateEvent(
|
||||||
touch_resource->OnTouchUpdate(time);
|
"HID::TouchUpdateCallback",
|
||||||
return std::nullopt;
|
[this](s64 time,
|
||||||
});
|
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||||
|
touch_resource->OnTouchUpdate(time);
|
||||||
|
return std::nullopt;
|
||||||
|
});
|
||||||
|
|
||||||
touch_resource->SetTouchDriver(touch_driver);
|
touch_resource->SetTouchDriver(touch_driver);
|
||||||
touch_resource->SetAppletResource(applet_resource, &shared_mutex);
|
touch_resource->SetAppletResource(applet_resource, &shared_mutex);
|
||||||
|
|||||||
@@ -266,7 +266,12 @@ void Init(QWidget* root) {
|
|||||||
Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB});
|
Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB});
|
||||||
LOG_INFO(Frontend, "Host Swap: {:.2f} GiB", Common::GetMemInfo().TotalSwapMemory / f64{1_GiB});
|
LOG_INFO(Frontend, "Host Swap: {:.2f} GiB", Common::GetMemInfo().TotalSwapMemory / f64{1_GiB});
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
LOG_INFO(Frontend, "Host Timer Resolution: {:.4f} ms", std::chrono::duration_cast<std::chrono::duration<f64, std::milli>>(Common::Windows::SetCurrentTimerResolutionToMaximum()).count());
|
LOG_INFO(Frontend, "Host Timer Resolution: {:.4f} ms",
|
||||||
|
std::chrono::duration_cast<std::chrono::duration<f64, std::milli>>(
|
||||||
|
Common::Windows::SetCurrentTimerResolutionToMaximum())
|
||||||
|
.count());
|
||||||
|
QtCommon::system->CoreTiming().SetTimerResolutionNs(
|
||||||
|
Common::Windows::GetCurrentTimerResolution());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Remove cached contents generated during the previous session
|
// Remove cached contents generated during the previous session
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2016 Dolphin Emulator Project
|
// SPDX-FileCopyrightText: 2016 Dolphin Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -56,15 +53,14 @@ u64 TestTimerSpeed(Core::Timing::CoreTiming& core_timing) {
|
|||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
TEST_CASE("CoreTiming[BasicOrder]", "[core]") {
|
TEST_CASE("CoreTiming[BasicOrder]", "[core]") {
|
||||||
Core::System system{};
|
|
||||||
ScopeInit guard;
|
ScopeInit guard;
|
||||||
auto& core_timing = guard.core_timing;
|
auto& core_timing = guard.core_timing;
|
||||||
std::vector<std::shared_ptr<Core::Timing::EventType>> events{
|
std::vector<std::shared_ptr<Core::Timing::EventType>> events{
|
||||||
system.CreateTimingEvent("callbackA", HostCallbackTemplate<0>),
|
Core::Timing::CreateEvent("callbackA", HostCallbackTemplate<0>),
|
||||||
system.CreateTimingEvent("callbackB", HostCallbackTemplate<1>),
|
Core::Timing::CreateEvent("callbackB", HostCallbackTemplate<1>),
|
||||||
system.CreateTimingEvent("callbackC", HostCallbackTemplate<2>),
|
Core::Timing::CreateEvent("callbackC", HostCallbackTemplate<2>),
|
||||||
system.CreateTimingEvent("callbackD", HostCallbackTemplate<3>),
|
Core::Timing::CreateEvent("callbackD", HostCallbackTemplate<3>),
|
||||||
system.CreateTimingEvent("callbackE", HostCallbackTemplate<4>),
|
Core::Timing::CreateEvent("callbackE", HostCallbackTemplate<4>),
|
||||||
};
|
};
|
||||||
|
|
||||||
expected_callback = 0;
|
expected_callback = 0;
|
||||||
@@ -97,15 +93,14 @@ TEST_CASE("CoreTiming[BasicOrder]", "[core]") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("CoreTiming[BasicOrderNoPausing]", "[core]") {
|
TEST_CASE("CoreTiming[BasicOrderNoPausing]", "[core]") {
|
||||||
Core::System system{};
|
|
||||||
ScopeInit guard;
|
ScopeInit guard;
|
||||||
auto& core_timing = guard.core_timing;
|
auto& core_timing = guard.core_timing;
|
||||||
std::vector<std::shared_ptr<Core::Timing::EventType>> events{
|
std::vector<std::shared_ptr<Core::Timing::EventType>> events{
|
||||||
system.CreateTimingEvent("callbackA", HostCallbackTemplate<0>),
|
Core::Timing::CreateEvent("callbackA", HostCallbackTemplate<0>),
|
||||||
system.CreateTimingEvent("callbackB", HostCallbackTemplate<1>),
|
Core::Timing::CreateEvent("callbackB", HostCallbackTemplate<1>),
|
||||||
system.CreateTimingEvent("callbackC", HostCallbackTemplate<2>),
|
Core::Timing::CreateEvent("callbackC", HostCallbackTemplate<2>),
|
||||||
system.CreateTimingEvent("callbackD", HostCallbackTemplate<3>),
|
Core::Timing::CreateEvent("callbackD", HostCallbackTemplate<3>),
|
||||||
system.CreateTimingEvent("callbackE", HostCallbackTemplate<4>),
|
Core::Timing::CreateEvent("callbackE", HostCallbackTemplate<4>),
|
||||||
};
|
};
|
||||||
|
|
||||||
core_timing.SyncPause(true);
|
core_timing.SyncPause(true);
|
||||||
|
|||||||
@@ -169,6 +169,7 @@ try
|
|||||||
}
|
}
|
||||||
|
|
||||||
RendererVulkan::~RendererVulkan() {
|
RendererVulkan::~RendererVulkan() {
|
||||||
|
scheduler.WaitWorker();
|
||||||
scheduler.RegisterOnSubmit([] {});
|
scheduler.RegisterOnSubmit([] {});
|
||||||
void(device.GetLogical().WaitIdle());
|
void(device.GetLogical().WaitIdle());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,9 @@
|
|||||||
#include "video_core/host_shaders/vulkan_quad_indexed_comp_spv.h"
|
#include "video_core/host_shaders/vulkan_quad_indexed_comp_spv.h"
|
||||||
#include "video_core/host_shaders/vulkan_uint8_comp_spv.h"
|
#include "video_core/host_shaders/vulkan_uint8_comp_spv.h"
|
||||||
#include "video_core/host_shaders/block_linear_unswizzle_3d_bcn_comp_spv.h"
|
#include "video_core/host_shaders/block_linear_unswizzle_3d_bcn_comp_spv.h"
|
||||||
|
#include "video_core/host_shaders/block_linear_unswizzle_2d_comp_spv.h"
|
||||||
|
#include "video_core/host_shaders/block_linear_unswizzle_3d_comp_spv.h"
|
||||||
|
#include "video_core/host_shaders/pitch_unswizzle_comp_spv.h"
|
||||||
#include "video_core/renderer_vulkan/vk_compute_pass.h"
|
#include "video_core/renderer_vulkan/vk_compute_pass.h"
|
||||||
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
|
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
|
||||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||||
@@ -232,6 +235,26 @@ struct QueriesPrefixScanPushConstants {
|
|||||||
struct ConditionalRenderingResolvePushConstants {
|
struct ConditionalRenderingResolvePushConstants {
|
||||||
u32 compare_to_zero;
|
u32 compare_to_zero;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct BlockLinear3DImagePushConstants {
|
||||||
|
alignas(16) std::array<u32, 3> origin;
|
||||||
|
alignas(16) std::array<s32, 3> destination;
|
||||||
|
u32 bytes_per_block_log2;
|
||||||
|
u32 slice_size;
|
||||||
|
u32 block_size;
|
||||||
|
u32 x_shift;
|
||||||
|
u32 block_height;
|
||||||
|
u32 block_height_mask;
|
||||||
|
u32 block_depth;
|
||||||
|
u32 block_depth_mask;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PitchUnswizzlePushConstants {
|
||||||
|
std::array<u32, 2> origin;
|
||||||
|
std::array<s32, 2> destination;
|
||||||
|
u32 bytes_per_block;
|
||||||
|
u32 pitch;
|
||||||
|
};
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
ComputePass::ComputePass(const Device& device_, Scheduler& scheduler, DescriptorPool& descriptor_pool,
|
ComputePass::ComputePass(const Device& device_, Scheduler& scheduler, DescriptorPool& descriptor_pool,
|
||||||
@@ -653,6 +676,309 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
|
|||||||
scheduler.Finish();
|
scheduler.Finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BlockLinearUnswizzleImage2DPass::BlockLinearUnswizzleImage2DPass(
|
||||||
|
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
||||||
|
StagingBufferPool& staging_buffer_pool_,
|
||||||
|
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
|
||||||
|
: ComputePass(device_, scheduler_, descriptor_pool_, ASTC_DESCRIPTOR_SET_BINDINGS,
|
||||||
|
ASTC_PASS_DESCRIPTOR_UPDATE_TEMPLATE_ENTRY, ASTC_BANK_INFO,
|
||||||
|
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(BlockLinearSwizzle2DParams)>,
|
||||||
|
BLOCK_LINEAR_UNSWIZZLE_2D_COMP_SPV),
|
||||||
|
scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_},
|
||||||
|
compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
|
||||||
|
|
||||||
|
BlockLinearUnswizzleImage2DPass::~BlockLinearUnswizzleImage2DPass() = default;
|
||||||
|
|
||||||
|
void BlockLinearUnswizzleImage2DPass::Unswizzle(
|
||||||
|
Image& image, const StagingBufferRef& map,
|
||||||
|
std::span<const VideoCommon::SwizzleParameters> swizzles) {
|
||||||
|
using namespace VideoCommon::Accelerated;
|
||||||
|
scheduler.RequestOutsideRenderPassOperationContext();
|
||||||
|
const VkPipeline vk_pipeline = *pipeline;
|
||||||
|
const VkImageAspectFlags aspect_mask = image.AspectMask();
|
||||||
|
const VkImage vk_image = image.Handle();
|
||||||
|
const bool is_initialized = image.ExchangeInitialization();
|
||||||
|
scheduler.Record([vk_pipeline, vk_image, aspect_mask,
|
||||||
|
is_initialized](vk::CommandBuffer cmdbuf) {
|
||||||
|
const VkImageMemoryBarrier image_barrier{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.srcAccessMask = static_cast<VkAccessFlags>(is_initialized ? VK_ACCESS_SHADER_WRITE_BIT
|
||||||
|
: VK_ACCESS_NONE),
|
||||||
|
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
|
||||||
|
.oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED,
|
||||||
|
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.image = vk_image,
|
||||||
|
.subresourceRange{
|
||||||
|
.aspectMask = aspect_mask,
|
||||||
|
.baseMipLevel = 0,
|
||||||
|
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
cmdbuf.PipelineBarrier(is_initialized ? vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER
|
||||||
|
: VkPipelineStageFlags(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT),
|
||||||
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, image_barrier);
|
||||||
|
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, vk_pipeline);
|
||||||
|
});
|
||||||
|
for (const VideoCommon::SwizzleParameters& swizzle : swizzles) {
|
||||||
|
const size_t input_offset = swizzle.buffer_offset + map.offset;
|
||||||
|
const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 32U);
|
||||||
|
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 32U);
|
||||||
|
const u32 num_dispatches_z = image.info.resources.layers;
|
||||||
|
|
||||||
|
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
||||||
|
compute_pass_descriptor_queue.AddBuffer(map.buffer, input_offset,
|
||||||
|
image.guest_size_bytes - swizzle.buffer_offset);
|
||||||
|
compute_pass_descriptor_queue.AddImage(image.StorageImageView(swizzle.level));
|
||||||
|
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
|
||||||
|
|
||||||
|
const auto params = MakeBlockLinearSwizzle2DParams(swizzle, image.info);
|
||||||
|
scheduler.Record([this, num_dispatches_x, num_dispatches_y, num_dispatches_z, params,
|
||||||
|
descriptor_data](vk::CommandBuffer cmdbuf) {
|
||||||
|
const VkDescriptorSet set = descriptor_allocator.Commit();
|
||||||
|
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
|
||||||
|
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
|
||||||
|
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, params);
|
||||||
|
cmdbuf.Dispatch(num_dispatches_x, num_dispatches_y, num_dispatches_z);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
scheduler.Record([vk_image, aspect_mask](vk::CommandBuffer cmdbuf) {
|
||||||
|
const VkImageMemoryBarrier image_barrier{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
|
||||||
|
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT |
|
||||||
|
VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||||
|
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.image = vk_image,
|
||||||
|
.subresourceRange{
|
||||||
|
.aspectMask = aspect_mask,
|
||||||
|
.baseMipLevel = 0,
|
||||||
|
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||||
|
vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, 0, image_barrier);
|
||||||
|
});
|
||||||
|
scheduler.Finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockLinearUnswizzleImage3DPass::BlockLinearUnswizzleImage3DPass(
|
||||||
|
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
||||||
|
StagingBufferPool& staging_buffer_pool_,
|
||||||
|
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
|
||||||
|
: ComputePass(device_, scheduler_, descriptor_pool_, ASTC_DESCRIPTOR_SET_BINDINGS,
|
||||||
|
ASTC_PASS_DESCRIPTOR_UPDATE_TEMPLATE_ENTRY, ASTC_BANK_INFO,
|
||||||
|
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(BlockLinear3DImagePushConstants)>,
|
||||||
|
BLOCK_LINEAR_UNSWIZZLE_3D_COMP_SPV),
|
||||||
|
scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_},
|
||||||
|
compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
|
||||||
|
|
||||||
|
BlockLinearUnswizzleImage3DPass::~BlockLinearUnswizzleImage3DPass() = default;
|
||||||
|
|
||||||
|
void BlockLinearUnswizzleImage3DPass::Unswizzle(
|
||||||
|
Image& image, const StagingBufferRef& map,
|
||||||
|
std::span<const VideoCommon::SwizzleParameters> swizzles) {
|
||||||
|
using namespace VideoCommon::Accelerated;
|
||||||
|
scheduler.RequestOutsideRenderPassOperationContext();
|
||||||
|
const VkPipeline vk_pipeline = *pipeline;
|
||||||
|
const VkImageAspectFlags aspect_mask = image.AspectMask();
|
||||||
|
const VkImage vk_image = image.Handle();
|
||||||
|
const bool is_initialized = image.ExchangeInitialization();
|
||||||
|
scheduler.Record([vk_pipeline, vk_image, aspect_mask,
|
||||||
|
is_initialized](vk::CommandBuffer cmdbuf) {
|
||||||
|
const VkImageMemoryBarrier image_barrier{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.srcAccessMask = static_cast<VkAccessFlags>(is_initialized ? VK_ACCESS_SHADER_WRITE_BIT
|
||||||
|
: VK_ACCESS_NONE),
|
||||||
|
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
|
||||||
|
.oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED,
|
||||||
|
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.image = vk_image,
|
||||||
|
.subresourceRange{
|
||||||
|
.aspectMask = aspect_mask,
|
||||||
|
.baseMipLevel = 0,
|
||||||
|
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
cmdbuf.PipelineBarrier(is_initialized ? vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER
|
||||||
|
: VkPipelineStageFlags(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT),
|
||||||
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, image_barrier);
|
||||||
|
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, vk_pipeline);
|
||||||
|
});
|
||||||
|
for (const VideoCommon::SwizzleParameters& swizzle : swizzles) {
|
||||||
|
const size_t input_offset = swizzle.buffer_offset + map.offset;
|
||||||
|
const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 16U);
|
||||||
|
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 8U);
|
||||||
|
const u32 num_dispatches_z = Common::DivCeil(swizzle.num_tiles.depth, 8U);
|
||||||
|
|
||||||
|
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
||||||
|
compute_pass_descriptor_queue.AddBuffer(map.buffer, input_offset,
|
||||||
|
image.guest_size_bytes - swizzle.buffer_offset);
|
||||||
|
compute_pass_descriptor_queue.AddImage(image.StorageImageView(swizzle.level));
|
||||||
|
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
|
||||||
|
|
||||||
|
const auto p = MakeBlockLinearSwizzle3DParams(swizzle, image.info);
|
||||||
|
const BlockLinear3DImagePushConstants params{
|
||||||
|
.origin = p.origin,
|
||||||
|
.destination = p.destination,
|
||||||
|
.bytes_per_block_log2 = p.bytes_per_block_log2,
|
||||||
|
.slice_size = p.slice_size,
|
||||||
|
.block_size = p.block_size,
|
||||||
|
.x_shift = p.x_shift,
|
||||||
|
.block_height = p.block_height,
|
||||||
|
.block_height_mask = p.block_height_mask,
|
||||||
|
.block_depth = p.block_depth,
|
||||||
|
.block_depth_mask = p.block_depth_mask,
|
||||||
|
};
|
||||||
|
scheduler.Record([this, num_dispatches_x, num_dispatches_y, num_dispatches_z, params,
|
||||||
|
descriptor_data](vk::CommandBuffer cmdbuf) {
|
||||||
|
const VkDescriptorSet set = descriptor_allocator.Commit();
|
||||||
|
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
|
||||||
|
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
|
||||||
|
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, params);
|
||||||
|
cmdbuf.Dispatch(num_dispatches_x, num_dispatches_y, num_dispatches_z);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
scheduler.Record([vk_image, aspect_mask](vk::CommandBuffer cmdbuf) {
|
||||||
|
const VkImageMemoryBarrier image_barrier{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
|
||||||
|
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT |
|
||||||
|
VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||||
|
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.image = vk_image,
|
||||||
|
.subresourceRange{
|
||||||
|
.aspectMask = aspect_mask,
|
||||||
|
.baseMipLevel = 0,
|
||||||
|
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||||
|
vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, 0, image_barrier);
|
||||||
|
});
|
||||||
|
scheduler.Finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
PitchUnswizzlePass::PitchUnswizzlePass(const Device& device_, Scheduler& scheduler_,
|
||||||
|
DescriptorPool& descriptor_pool_,
|
||||||
|
StagingBufferPool& staging_buffer_pool_,
|
||||||
|
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
|
||||||
|
: ComputePass(device_, scheduler_, descriptor_pool_, ASTC_DESCRIPTOR_SET_BINDINGS,
|
||||||
|
ASTC_PASS_DESCRIPTOR_UPDATE_TEMPLATE_ENTRY, ASTC_BANK_INFO,
|
||||||
|
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(PitchUnswizzlePushConstants)>,
|
||||||
|
PITCH_UNSWIZZLE_COMP_SPV),
|
||||||
|
scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_},
|
||||||
|
compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
|
||||||
|
|
||||||
|
PitchUnswizzlePass::~PitchUnswizzlePass() = default;
|
||||||
|
|
||||||
|
void PitchUnswizzlePass::Unswizzle(Image& image, const StagingBufferRef& map,
|
||||||
|
std::span<const VideoCommon::SwizzleParameters> swizzles) {
|
||||||
|
scheduler.RequestOutsideRenderPassOperationContext();
|
||||||
|
const VkPipeline vk_pipeline = *pipeline;
|
||||||
|
const VkImageAspectFlags aspect_mask = image.AspectMask();
|
||||||
|
const VkImage vk_image = image.Handle();
|
||||||
|
const bool is_initialized = image.ExchangeInitialization();
|
||||||
|
scheduler.Record([vk_pipeline, vk_image, aspect_mask,
|
||||||
|
is_initialized](vk::CommandBuffer cmdbuf) {
|
||||||
|
const VkImageMemoryBarrier image_barrier{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.srcAccessMask = static_cast<VkAccessFlags>(is_initialized ? VK_ACCESS_SHADER_WRITE_BIT
|
||||||
|
: VK_ACCESS_NONE),
|
||||||
|
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
|
||||||
|
.oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED,
|
||||||
|
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.image = vk_image,
|
||||||
|
.subresourceRange{
|
||||||
|
.aspectMask = aspect_mask,
|
||||||
|
.baseMipLevel = 0,
|
||||||
|
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
cmdbuf.PipelineBarrier(is_initialized ? vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER
|
||||||
|
: VkPipelineStageFlags(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT),
|
||||||
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, image_barrier);
|
||||||
|
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, vk_pipeline);
|
||||||
|
});
|
||||||
|
const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(image.info.format);
|
||||||
|
for (const VideoCommon::SwizzleParameters& swizzle : swizzles) {
|
||||||
|
const size_t input_offset = swizzle.buffer_offset + map.offset;
|
||||||
|
const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 32U);
|
||||||
|
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 32U);
|
||||||
|
|
||||||
|
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
||||||
|
compute_pass_descriptor_queue.AddBuffer(map.buffer, input_offset,
|
||||||
|
image.guest_size_bytes - swizzle.buffer_offset);
|
||||||
|
compute_pass_descriptor_queue.AddImage(image.StorageImageView(swizzle.level));
|
||||||
|
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
|
||||||
|
|
||||||
|
const PitchUnswizzlePushConstants params{
|
||||||
|
.origin = {0, 0},
|
||||||
|
.destination = {0, 0},
|
||||||
|
.bytes_per_block = bytes_per_block,
|
||||||
|
.pitch = image.info.pitch,
|
||||||
|
};
|
||||||
|
scheduler.Record([this, num_dispatches_x, num_dispatches_y, params,
|
||||||
|
descriptor_data](vk::CommandBuffer cmdbuf) {
|
||||||
|
const VkDescriptorSet set = descriptor_allocator.Commit();
|
||||||
|
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
|
||||||
|
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
|
||||||
|
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, params);
|
||||||
|
cmdbuf.Dispatch(num_dispatches_x, num_dispatches_y, 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
scheduler.Record([vk_image, aspect_mask](vk::CommandBuffer cmdbuf) {
|
||||||
|
const VkImageMemoryBarrier image_barrier{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
|
||||||
|
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT |
|
||||||
|
VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||||
|
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.image = vk_image,
|
||||||
|
.subresourceRange{
|
||||||
|
.aspectMask = aspect_mask,
|
||||||
|
.baseMipLevel = 0,
|
||||||
|
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||||
|
vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, 0, image_barrier);
|
||||||
|
});
|
||||||
|
scheduler.Finish();
|
||||||
|
}
|
||||||
|
|
||||||
constexpr u32 BL3D_BINDING_INPUT_BUFFER = 0;
|
constexpr u32 BL3D_BINDING_INPUT_BUFFER = 0;
|
||||||
constexpr u32 BL3D_BINDING_OUTPUT_BUFFER = 1;
|
constexpr u32 BL3D_BINDING_OUTPUT_BUFFER = 1;
|
||||||
|
|
||||||
@@ -833,6 +1159,23 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!is_first_chunk) {
|
||||||
|
const VkBufferMemoryBarrier reuse_barrier{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
|
||||||
|
.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
|
||||||
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.buffer = out_buffer,
|
||||||
|
.offset = 0,
|
||||||
|
.size = VK_WHOLE_SIZE,
|
||||||
|
};
|
||||||
|
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||||
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, nullptr,
|
||||||
|
reuse_barrier, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
|
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
|
||||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
|
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
|
||||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
|
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ struct SwizzleParameters;
|
|||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
|
using VideoCommon::Accelerated::BlockLinearSwizzle2DParams;
|
||||||
using VideoCommon::Accelerated::BlockLinearSwizzle3DParams;
|
using VideoCommon::Accelerated::BlockLinearSwizzle3DParams;
|
||||||
|
|
||||||
class Device;
|
class Device;
|
||||||
@@ -164,6 +165,56 @@ private:
|
|||||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class BlockLinearUnswizzleImage2DPass final : public ComputePass {
|
||||||
|
public:
|
||||||
|
explicit BlockLinearUnswizzleImage2DPass(const Device& device_, Scheduler& scheduler_,
|
||||||
|
DescriptorPool& descriptor_pool_,
|
||||||
|
StagingBufferPool& staging_buffer_pool_,
|
||||||
|
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
|
||||||
|
~BlockLinearUnswizzleImage2DPass();
|
||||||
|
|
||||||
|
void Unswizzle(Image& image, const StagingBufferRef& map,
|
||||||
|
std::span<const VideoCommon::SwizzleParameters> swizzles);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Scheduler& scheduler;
|
||||||
|
StagingBufferPool& staging_buffer_pool;
|
||||||
|
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BlockLinearUnswizzleImage3DPass final : public ComputePass {
|
||||||
|
public:
|
||||||
|
explicit BlockLinearUnswizzleImage3DPass(const Device& device_, Scheduler& scheduler_,
|
||||||
|
DescriptorPool& descriptor_pool_,
|
||||||
|
StagingBufferPool& staging_buffer_pool_,
|
||||||
|
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
|
||||||
|
~BlockLinearUnswizzleImage3DPass();
|
||||||
|
|
||||||
|
void Unswizzle(Image& image, const StagingBufferRef& map,
|
||||||
|
std::span<const VideoCommon::SwizzleParameters> swizzles);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Scheduler& scheduler;
|
||||||
|
StagingBufferPool& staging_buffer_pool;
|
||||||
|
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PitchUnswizzlePass final : public ComputePass {
|
||||||
|
public:
|
||||||
|
explicit PitchUnswizzlePass(const Device& device_, Scheduler& scheduler_,
|
||||||
|
DescriptorPool& descriptor_pool_,
|
||||||
|
StagingBufferPool& staging_buffer_pool_,
|
||||||
|
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
|
||||||
|
~PitchUnswizzlePass();
|
||||||
|
|
||||||
|
void Unswizzle(Image& image, const StagingBufferRef& map,
|
||||||
|
std::span<const VideoCommon::SwizzleParameters> swizzles);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Scheduler& scheduler;
|
||||||
|
StagingBufferPool& staging_buffer_pool;
|
||||||
|
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
||||||
|
};
|
||||||
|
|
||||||
class MSAACopyPass final : public ComputePass {
|
class MSAACopyPass final : public ComputePass {
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -195,8 +195,42 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
|||||||
return allocator.CreateImage(image_ci);
|
return allocator.CreateImage(image_ci);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] VkFormat UnswizzleStorageFormat(u32 bytes_per_block) {
|
||||||
|
switch (bytes_per_block) {
|
||||||
|
case 1:
|
||||||
|
return VK_FORMAT_R8_UINT;
|
||||||
|
case 2:
|
||||||
|
return VK_FORMAT_R16_UINT;
|
||||||
|
case 4:
|
||||||
|
return VK_FORMAT_R32_UINT;
|
||||||
|
case 8:
|
||||||
|
return VK_FORMAT_R32G32_UINT;
|
||||||
|
case 16:
|
||||||
|
return VK_FORMAT_R32G32B32A32_UINT;
|
||||||
|
default:
|
||||||
|
ASSERT_MSG(false, "Invalid bytes_per_block={} for accelerated unswizzle", bytes_per_block);
|
||||||
|
return VK_FORMAT_R32_UINT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool IsUnswizzleStorageFormatSupported(const Device& device, u32 bytes_per_block) {
|
||||||
|
switch (bytes_per_block) {
|
||||||
|
case 1:
|
||||||
|
return device.IsStorageBuffer8BitAccessSupported();
|
||||||
|
case 2:
|
||||||
|
return device.IsStorageBuffer16BitAccessSupported();
|
||||||
|
case 4:
|
||||||
|
case 8:
|
||||||
|
case 16:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] vk::ImageView MakeStorageView(const vk::Device& device, u32 level, VkImage image,
|
[[nodiscard]] vk::ImageView MakeStorageView(const vk::Device& device, u32 level, VkImage image,
|
||||||
VkFormat format) {
|
VkFormat format,
|
||||||
|
VkImageViewType view_type = VK_IMAGE_VIEW_TYPE_2D_ARRAY) {
|
||||||
static constexpr VkImageViewUsageCreateInfo storage_image_view_usage_create_info{
|
static constexpr VkImageViewUsageCreateInfo storage_image_view_usage_create_info{
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
@@ -207,7 +241,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
|||||||
.pNext = &storage_image_view_usage_create_info,
|
.pNext = &storage_image_view_usage_create_info,
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
.image = image,
|
.image = image,
|
||||||
.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY,
|
.viewType = view_type,
|
||||||
.format = format,
|
.format = format,
|
||||||
.components{
|
.components{
|
||||||
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
|
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||||
@@ -887,6 +921,12 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched
|
|||||||
if (device.IsStorageImageMultisampleSupported()) {
|
if (device.IsStorageImageMultisampleSupported()) {
|
||||||
msaa_copy_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool, compute_pass_descriptor_queue);
|
msaa_copy_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool, compute_pass_descriptor_queue);
|
||||||
}
|
}
|
||||||
|
bl_unswizzle_2d_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool,
|
||||||
|
compute_pass_descriptor_queue);
|
||||||
|
bl_unswizzle_3d_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool,
|
||||||
|
compute_pass_descriptor_queue);
|
||||||
|
pitch_unswizzle_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool,
|
||||||
|
compute_pass_descriptor_queue);
|
||||||
if (!device.IsKhrImageFormatListSupported()) {
|
if (!device.IsKhrImageFormatListSupported()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -894,6 +934,11 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched
|
|||||||
const auto image_format = static_cast<PixelFormat>(index_a);
|
const auto image_format = static_cast<PixelFormat>(index_a);
|
||||||
if (IsPixelFormatASTC(image_format) && !device.IsOptimalAstcSupported()) {
|
if (IsPixelFormatASTC(image_format) && !device.IsOptimalAstcSupported()) {
|
||||||
view_formats[index_a].push_back(VK_FORMAT_A8B8G8R8_UNORM_PACK32);
|
view_formats[index_a].push_back(VK_FORMAT_A8B8G8R8_UNORM_PACK32);
|
||||||
|
} else if (!IsPixelFormatASTC(image_format) && !IsPixelFormatBCn(image_format)) {
|
||||||
|
const u32 bpp = VideoCore::Surface::BytesPerBlock(image_format);
|
||||||
|
if (IsUnswizzleStorageFormatSupported(device, bpp)) {
|
||||||
|
view_formats[index_a].push_back(UnswizzleStorageFormat(bpp));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (size_t index_b = 0; index_b < VideoCore::Surface::MaxPixelFormat; index_b++) {
|
for (size_t index_b = 0; index_b < VideoCore::Surface::MaxPixelFormat; index_b++) {
|
||||||
const auto view_format = static_cast<PixelFormat>(index_b);
|
const auto view_format = static_cast<PixelFormat>(index_b);
|
||||||
@@ -1580,6 +1625,16 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
|
|||||||
flags |= VideoCommon::ImageFlagBits::Converted;
|
flags |= VideoCommon::ImageFlagBits::Converted;
|
||||||
flags |= VideoCommon::ImageFlagBits::CostlyLoad;
|
flags |= VideoCommon::ImageFlagBits::CostlyLoad;
|
||||||
}
|
}
|
||||||
|
if (!IsPixelFormatASTC(info.format) && !IsPixelFormatBCn(info.format) &&
|
||||||
|
VideoCore::Surface::GetFormatType(info.format) ==
|
||||||
|
VideoCore::Surface::SurfaceType::ColorTexture &&
|
||||||
|
(info.type == ImageType::e2D || info.type == ImageType::e3D ||
|
||||||
|
info.type == ImageType::Linear)) {
|
||||||
|
if (IsUnswizzleStorageFormatSupported(runtime->device,
|
||||||
|
VideoCore::Surface::BytesPerBlock(info.format))) {
|
||||||
|
flags |= VideoCommon::ImageFlagBits::AcceleratedUpload;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (runtime->device.HasDebuggingToolAttached()) {
|
if (runtime->device.HasDebuggingToolAttached()) {
|
||||||
original_image.SetObjectNameEXT(VideoCommon::Name(*this).c_str());
|
original_image.SetObjectNameEXT(VideoCommon::Name(*this).c_str());
|
||||||
}
|
}
|
||||||
@@ -1593,6 +1648,19 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
|
|||||||
storage_image_views[level] =
|
storage_image_views[level] =
|
||||||
MakeStorageView(device, level, *original_image, VK_FORMAT_A8B8G8R8_UNORM_PACK32);
|
MakeStorageView(device, level, *original_image, VK_FORMAT_A8B8G8R8_UNORM_PACK32);
|
||||||
}
|
}
|
||||||
|
} else if (True(flags & VideoCommon::ImageFlagBits::AcceleratedUpload)) {
|
||||||
|
const auto& device = runtime->device.GetLogical();
|
||||||
|
const VkFormat storage_format =
|
||||||
|
UnswizzleStorageFormat(VideoCore::Surface::BytesPerBlock(info.format));
|
||||||
|
const VkImageViewType storage_view_type = info.type == ImageType::e3D
|
||||||
|
? VK_IMAGE_VIEW_TYPE_3D
|
||||||
|
: info.type == ImageType::Linear
|
||||||
|
? VK_IMAGE_VIEW_TYPE_2D
|
||||||
|
: VK_IMAGE_VIEW_TYPE_2D_ARRAY;
|
||||||
|
for (s32 level = 0; level < info.resources.levels; ++level) {
|
||||||
|
storage_image_views[level] =
|
||||||
|
MakeStorageView(device, level, *original_image, storage_format, storage_view_type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2448,16 +2516,24 @@ void TextureCacheRuntime::AccelerateImageUpload(
|
|||||||
return astc_decoder_pass->Assemble(image, map, swizzles);
|
return astc_decoder_pass->Assemble(image, map, swizzles);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Settings::values.gpu_unswizzle_enabled.GetValue() || !bl3d_unswizzle_pass) {
|
if (IsPixelFormatBCn(image.info.format)) {
|
||||||
if (IsPixelFormatBCn(image.info.format) && image.info.type == ImageType::e3D) {
|
if (Settings::values.gpu_unswizzle_enabled.GetValue() && bl3d_unswizzle_pass &&
|
||||||
ASSERT(false && "GPU unswizzle is disabled for BCn 3D texture");
|
image.info.type == ImageType::e3D && image.info.resources.levels == 1 &&
|
||||||
|
image.info.resources.layers == 1) {
|
||||||
|
return bl3d_unswizzle_pass->Unswizzle(image, map, swizzles, z_start, z_count);
|
||||||
}
|
}
|
||||||
ASSERT(false);
|
ASSERT(false && "GPU unswizzle is disabled for BCn 3D texture");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bl3d_unswizzle_pass && IsPixelFormatBCn(image.info.format) && image.info.type == ImageType::e3D && image.info.resources.levels == 1 && image.info.resources.layers == 1) {
|
if (image.info.type == ImageType::e2D) {
|
||||||
return bl3d_unswizzle_pass->Unswizzle(image, map, swizzles, z_start, z_count);
|
return bl_unswizzle_2d_pass->Unswizzle(image, map, swizzles);
|
||||||
|
}
|
||||||
|
if (image.info.type == ImageType::e3D) {
|
||||||
|
return bl_unswizzle_3d_pass->Unswizzle(image, map, swizzles);
|
||||||
|
}
|
||||||
|
if (image.info.type == ImageType::Linear) {
|
||||||
|
return pitch_unswizzle_pass->Unswizzle(image, map, swizzles);
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(false);
|
ASSERT(false);
|
||||||
|
|||||||
@@ -130,6 +130,9 @@ public:
|
|||||||
std::optional<ASTCDecoderPass> astc_decoder_pass;
|
std::optional<ASTCDecoderPass> astc_decoder_pass;
|
||||||
|
|
||||||
std::optional<BlockLinearUnswizzle3DPass> bl3d_unswizzle_pass;
|
std::optional<BlockLinearUnswizzle3DPass> bl3d_unswizzle_pass;
|
||||||
|
std::optional<BlockLinearUnswizzleImage2DPass> bl_unswizzle_2d_pass;
|
||||||
|
std::optional<BlockLinearUnswizzleImage3DPass> bl_unswizzle_3d_pass;
|
||||||
|
std::optional<PitchUnswizzlePass> pitch_unswizzle_pass;
|
||||||
std::optional<MSAACopyPass> msaa_copy_pass;
|
std::optional<MSAACopyPass> msaa_copy_pass;
|
||||||
const Settings::ResolutionScalingInfo& resolution;
|
const Settings::ResolutionScalingInfo& resolution;
|
||||||
std::array<std::vector<VkFormat>, VideoCore::Surface::MaxPixelFormat> view_formats;
|
std::array<std::vector<VkFormat>, VideoCore::Surface::MaxPixelFormat> view_formats;
|
||||||
|
|||||||
@@ -888,6 +888,16 @@ FN_MAX_LIMIT_LIST
|
|||||||
features.bit16_storage.storageBuffer16BitAccess;
|
features.bit16_storage.storageBuffer16BitAccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the device supports reading 8-bit values from a storage buffer.
|
||||||
|
bool IsStorageBuffer8BitAccessSupported() const {
|
||||||
|
return features.bit8_storage.storageBuffer8BitAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the device supports reading 16-bit values from a storage buffer.
|
||||||
|
bool IsStorageBuffer16BitAccessSupported() const {
|
||||||
|
return features.bit16_storage.storageBuffer16BitAccess;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] static constexpr bool CheckBrokenCompute(VkDriverId driver_id,
|
[[nodiscard]] static constexpr bool CheckBrokenCompute(VkDriverId driver_id,
|
||||||
u32 driver_version) {
|
u32 driver_version) {
|
||||||
if (driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) {
|
if (driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) {
|
||||||
|
|||||||
@@ -371,6 +371,7 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
Common::Windows::SetCurrentTimerResolutionToMaximum();
|
Common::Windows::SetCurrentTimerResolutionToMaximum();
|
||||||
|
system.CoreTiming().SetTimerResolutionNs(Common::Windows::GetCurrentTimerResolution());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
|
system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
|
||||||
|
|||||||
Reference in New Issue
Block a user