Compare commits

..

8 Commits

Author SHA1 Message Date
lizzie faa771646c fix 2026-07-03 17:44:22 +00:00
lizzie de59fa1ab4 fix 2026-07-03 17:44:12 +00:00
lizzie b79295c528 windows sucks 2026-07-03 17:44:02 +00:00
lizzie c47bae4c4e license 2026-07-03 17:44:02 +00:00
lizzie ca0eebcfe8 [core] move event creation to attached Core::System, remove unused atomics/prevent false share on Core::Timing
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-07-03 17:44:02 +00:00
lizzie 4c65780f11 Revert "[common/dynamic_library] fix AUR build error (#4156)" (#4157)
This reverts commit a9c4c8aefd.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4157
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-07-02 22:07:37 +02:00
lizzie a9c4c8aefd [common/dynamic_library] fix AUR build error (#4156)
not gonna question why there was an ifdef there

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4156
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-07-02 19:49:19 +02:00
lizzie a769505a45 [vk, ogl] Remove dedicated precomputed swizzle table; use shorter inline table in block_linear swizzle shaders (#4146)
old table = 64 * 8 * sizeof(u32) = 512 * 4 = 4096 bytes
new table = 8 * sizeof(u32) = 8 * 4 = 32 bytes

the expression
```glsl
    return ((pos & 0x0180) >> 1)
         | ((pos & 0x0040) >> 2)
         | ((pos & 0x0020) << 3)
         | ((pos & 0x0010) << 1)
         | ((pos & 0x000f) << 0);
```
is equivalent for generating the table but idk if we'd want that

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Co-authored-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4146
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-07-02 19:24:07 +02:00
40 changed files with 294 additions and 665 deletions
+5 -3
View File
@@ -22,9 +22,11 @@ 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_}, thread_event{Core::Timing::CreateEvent( : system{system_}
"AudioOutSampleTick", , thread_event{system_.CreateTimingEvent("AudioOutSampleTick", [this](s64 time, std::chrono::nanoseconds) {
[this](s64 time, std::chrono::nanoseconds) { return ThreadFunc(); })} {} return ThreadFunc();
})}
{}
DeviceSession::~DeviceSession() { DeviceSession::~DeviceSession() {
Finalize(); Finalize();
+4
View File
@@ -969,4 +969,8 @@ 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
+4
View File
@@ -19,6 +19,7 @@
#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;
@@ -438,6 +439,9 @@ 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;
}; };
+14 -45
View File
@@ -23,10 +23,6 @@ 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;
@@ -36,11 +32,10 @@ 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) { friend bool operator>(const Event& left, const Event& right) noexcept {
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);
} }
}; };
@@ -60,8 +55,6 @@ 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()) {
@@ -77,8 +70,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();
@@ -144,39 +137,28 @@ void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,
event.Set(); event.Set();
} }
void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_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::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, void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, UnscheduleEventType 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 itr = event_queue.begin(); itr != event_queue.end(); itr++) { for (auto it = event_queue.begin(); it != event_queue.end(); it++) {
const Event& e = *itr; auto const& e = *it;
if (e.type.lock().get() == event_type.get()) { if (e.type.lock().get() == event_type.get()) {
to_remove.push_back(itr->handle); to_remove.push_back(it->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++;
} }
@@ -187,16 +169,15 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
} }
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 -= static_cast<s64>(ticks); downcount -= s64(ticks);
} }
void CoreTiming::Idle() { void CoreTiming::Idle() {
@@ -270,19 +251,14 @@ 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, event_queue.update(evt.handle, Event{next_time, event_fifo_id++, evt.type, next_schedule_time, evt.handle});
next_schedule_time, evt.handle});
} }
} }
global_timer = GetGlobalTimeNs().count(); global_timer = GetGlobalTimeNs().count();
} }
if (!event_queue.empty()) { return event_queue.empty() ? std::optional<s64>{} : event_queue.top().time;
return event_queue.top().time;
} else {
return std::nullopt;
}
} }
void CoreTiming::Reset() { void CoreTiming::Reset() {
@@ -293,7 +269,6 @@ 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.
@@ -310,10 +285,4 @@ 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
+11 -33
View File
@@ -29,8 +29,7 @@ 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_) explicit EventType(TimedCallback&& callback_, std::string&& name_) : callback{std::move(callback_)}, name{std::move(name_)}, sequence_number{0} {}
: 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;
@@ -90,11 +89,6 @@ 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;
@@ -134,45 +128,29 @@ 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
+5 -6
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
@@ -13,11 +13,10 @@ 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 = Core::Timing::CreateEvent("KHardwareTimer::Callback", m_event_type = m_kernel.System().CreateTimingEvent("KHardwareTimer::Callback", [this](s64, std::chrono::nanoseconds) {
[this](s64, std::chrono::nanoseconds) { this->DoTask();
this->DoTask(); return std::nullopt;
return std::nullopt; });
});
} }
void KHardwareTimer::Finalize() { void KHardwareTimer::Finalize() {
+1 -1
View File
@@ -255,7 +255,7 @@ struct KernelCore::Impl {
} }
void InitializePreemption(KernelCore& kernel) { void InitializePreemption(KernelCore& kernel) {
preemption_event = Core::Timing::CreateEvent("PreemptionCallback", [this, &kernel](s64 time, std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> { preemption_event = system.CreateTimingEvent("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,16 +25,11 @@ 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 = Core::Timing::CreateEvent( m_timer_timing_event = m_system.CreateTimingEvent("Glue:AlarmWorker::AlarmTimer", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
"Glue:AlarmWorker::AlarmTimer", m_timer_event->Signal(m_system.Kernel());
[this](s64 time, return std::nullopt;
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { });
m_timer_event->Signal(m_system.Kernel());
return std::nullopt;
});
AttachToClosestAlarmEvent(); AttachToClosestAlarmEvent();
} }
+10 -19
View File
@@ -22,28 +22,19 @@ 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( : m_system{system}, m_ctx{m_system, "Glue:TimeWorker"}, m_event{m_ctx.CreateEvent("Glue:TimeWorker:Event")},
"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( m_file_timestamp_worker{file_timestamp_worker}, m_timer_steady_clock{m_ctx.CreateEvent("Glue:TimeWorker:SteadyClockTimerEvent")},
"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 = Core::Timing::CreateEvent( m_timer_steady_clock_timing_event = m_system.CreateTimingEvent("Time::SteadyClockEvent", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
"Time::SteadyClockEvent", m_timer_steady_clock->Signal(m_system.Kernel());
[this](s64 time, return std::nullopt;
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { });
m_timer_steady_clock->Signal(m_system.Kernel()); m_timer_file_system_timing_event = m_system.CreateTimingEvent("Time::SteadyClockEvent", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
return std::nullopt; m_timer_file_system->Signal(m_system.Kernel());
}); 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() {
+6 -11
View File
@@ -51,17 +51,12 @@ Hidbus::Hidbus(Core::System& system_)
RegisterHandlers(functions); RegisterHandlers(functions);
// Register update callbacks // Register update callbacks
hidbus_update_event = Core::Timing::CreateEvent( hidbus_update_event = system_.CreateTimingEvent("Hidbus::UpdateCallback", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
"Hidbus::UpdateCallback", const auto guard = LockService();
[this](s64 time, UpdateHidbus(ns_late);
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { return std::nullopt;
const auto guard = LockService(); });
UpdateHidbus(ns_late); system_.CoreTiming().ScheduleLoopingEvent(hidbus_update_ns, hidbus_update_ns, hidbus_update_event);
return std::nullopt;
});
system_.CoreTiming().ScheduleLoopingEvent(hidbus_update_ns, hidbus_update_ns,
hidbus_update_event);
} }
Hidbus::~Hidbus() { Hidbus::~Hidbus() {
+8 -16
View File
@@ -23,25 +23,17 @@ Conductor::Conductor(Core::System& system, Container& container, DisplayList& di
}); });
if (system.IsMulticore()) { if (system.IsMulticore()) {
m_event = Core::Timing::CreateEvent( m_event = system.CreateTimingEvent("ScreenComposition", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
"ScreenComposition", m_signal.Set();
[this](s64 time, return std::chrono::nanoseconds(this->GetNextTicks());
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 = Core::Timing::CreateEvent( m_event = system.CreateTimingEvent("ScreenComposition", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
"ScreenComposition", this->ProcessVsync();
[this](s64 time, return std::chrono::nanoseconds(this->GetNextTicks());
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);
} }
} }
+4 -6
View File
@@ -231,12 +231,10 @@ CheatEngine::~CheatEngine() {
} }
void CheatEngine::Initialize() { void CheatEngine::Initialize() {
event = Core::Timing::CreateEvent( 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> {
"CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), FrameCallback(ns_late);
[this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { return std::nullopt;
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();
+5 -7
View File
@@ -52,14 +52,12 @@ void MemoryWriteWidth(Core::Memory::Memory& memory, u32 width, VAddr addr, u64 v
} // Anonymous namespace } // Anonymous namespace
Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_) Freezer::Freezer(Core::System& system_, Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_)
: core_timing{core_timing_}, memory{memory_} { : core_timing{core_timing_}, memory{memory_} {
event = Core::Timing::CreateEvent("MemoryFreezer::FrameCallback", event = system_.CreateTimingEvent("MemoryFreezer::FrameCallback", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
[this](s64 time, std::chrono::nanoseconds ns_late) FrameCallback(ns_late);
-> std::optional<std::chrono::nanoseconds> { return std::nullopt;
FrameCallback(ns_late); });
return std::nullopt;
});
core_timing.ScheduleEvent(memory_freezer_ns, event); core_timing.ScheduleEvent(memory_freezer_ns, event);
} }
+10 -5
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -11,14 +14,16 @@
#include <vector> #include <vector>
#include "common/common_types.h" #include "common/common_types.h"
namespace Core::Timing { namespace Core {
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 {
@@ -38,7 +43,7 @@ public:
u64 value; u64 value;
}; };
explicit Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_); explicit Freezer(Core::System& system_, 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.
+20 -34
View File
@@ -56,33 +56,22 @@ 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 = Core::Timing::CreateEvent("HID::UpdatePadCallback", npad_update_event = system.CreateTimingEvent("HID::UpdatePadCallback", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
[this](s64 time, std::chrono::nanoseconds ns_late) UpdateNpad(ns_late);
-> std::optional<std::chrono::nanoseconds> { return std::nullopt;
UpdateNpad(ns_late); });
return std::nullopt; default_update_event = system.CreateTimingEvent("HID::UpdateDefaultCallback", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
}); UpdateControllers(ns_late);
default_update_event = Core::Timing::CreateEvent( return std::nullopt;
"HID::UpdateDefaultCallback", });
[this](s64 time, mouse_keyboard_update_event = system.CreateTimingEvent("HID::UpdateMouseKeyboardCallback", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { UpdateMouseKeyboard(ns_late);
UpdateControllers(ns_late); return std::nullopt;
return std::nullopt; });
}); motion_update_event = system.CreateTimingEvent("HID::UpdateMotionCallback", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
mouse_keyboard_update_event = Core::Timing::CreateEvent( UpdateMotion(ns_late);
"HID::UpdateMouseKeyboardCallback", return std::nullopt;
[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() {
@@ -267,13 +256,10 @@ 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 = Core::Timing::CreateEvent( touch_update_event = system.CreateTimingEvent("HID::TouchUpdateCallback", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
"HID::TouchUpdateCallback", touch_resource->OnTouchUpdate(time);
[this](s64 time, return std::nullopt;
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);
+1 -6
View File
@@ -266,12 +266,7 @@ 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", LOG_INFO(Frontend, "Host Timer Resolution: {:.4f} ms", std::chrono::duration_cast<std::chrono::duration<f64, std::milli>>(Common::Windows::SetCurrentTimerResolutionToMaximum()).count());
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
@@ -332,9 +332,6 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) {
void SetupDenormControl(const Profile& profile, const IR::Program& program, EmitContext& ctx, void SetupDenormControl(const Profile& profile, const IR::Program& program, EmitContext& ctx,
Id main_func) { Id main_func) {
const Info& info{program.info}; const Info& info{program.info};
if (profile.has_broken_fp16_float_controls && info.uses_fp16) {
return;
}
if (info.uses_fp32_denorms_flush && info.uses_fp32_denorms_preserve) { if (info.uses_fp32_denorms_flush && info.uses_fp32_denorms_preserve) {
LOG_DEBUG(Shader_SPIRV, "Fp32 denorm flush and preserve on the same shader"); LOG_DEBUG(Shader_SPIRV, "Fp32 denorm flush and preserve on the same shader");
} else if (info.uses_fp32_denorms_flush) { } else if (info.uses_fp32_denorms_flush) {
@@ -435,7 +432,7 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
} }
if ((info.uses_subgroup_vote || info.uses_subgroup_invocation_id || if ((info.uses_subgroup_vote || info.uses_subgroup_invocation_id ||
info.uses_subgroup_shuffles) && info.uses_subgroup_shuffles) &&
profile.support_vote && profile.SupportsSubgroupStage(ctx.stage)) { profile.support_vote) {
ctx.AddCapability(spv::Capability::GroupNonUniformBallot); ctx.AddCapability(spv::Capability::GroupNonUniformBallot);
ctx.AddCapability(spv::Capability::GroupNonUniformShuffle); ctx.AddCapability(spv::Capability::GroupNonUniformShuffle);
if (!profile.warp_size_potentially_larger_than_guest) { if (!profile.warp_size_potentially_larger_than_guest) {
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -18,7 +15,7 @@ Id SharedPointer(EmitContext& ctx, Id offset, u32 index_offset = 0) {
if (index_offset > 0) { if (index_offset > 0) {
index = ctx.OpIAdd(ctx.U32[1], index, ctx.Const(index_offset)); index = ctx.OpIAdd(ctx.U32[1], index, ctx.Const(index_offset));
} }
return ctx.uses_explicit_workgroup_layout return ctx.profile.support_explicit_workgroup_layout
? ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, ctx.u32_zero_value, index) ? ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, ctx.u32_zero_value, index)
: ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index); : ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index);
} }
@@ -158,7 +155,7 @@ Id EmitSharedAtomicExchange32(EmitContext& ctx, Id offset, Id value) {
} }
Id EmitSharedAtomicExchange64(EmitContext& ctx, Id offset, Id value) { Id EmitSharedAtomicExchange64(EmitContext& ctx, Id offset, Id value) {
if (ctx.profile.support_shared_int64_atomics && ctx.uses_explicit_workgroup_layout) { if (ctx.profile.support_int64_atomics && ctx.profile.support_explicit_workgroup_layout) {
const Id shift_id{ctx.Const(3U)}; const Id shift_id{ctx.Const(3U)};
const Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)}; const Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
const Id pointer{ const Id pointer{
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -31,7 +28,7 @@ std::pair<Id, Id> ExtractArgs(EmitContext& ctx, Id offset, u32 mask, u32 count)
} // Anonymous namespace } // Anonymous namespace
Id EmitLoadSharedU8(EmitContext& ctx, Id offset) { Id EmitLoadSharedU8(EmitContext& ctx, Id offset) {
if (ctx.uses_explicit_workgroup_layout) { if (ctx.profile.support_explicit_workgroup_layout) {
const Id pointer{ const Id pointer{
ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)}; ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)};
return ctx.OpUConvert(ctx.U32[1], ctx.OpLoad(ctx.U8, pointer)); return ctx.OpUConvert(ctx.U32[1], ctx.OpLoad(ctx.U8, pointer));
@@ -42,7 +39,7 @@ Id EmitLoadSharedU8(EmitContext& ctx, Id offset) {
} }
Id EmitLoadSharedS8(EmitContext& ctx, Id offset) { Id EmitLoadSharedS8(EmitContext& ctx, Id offset) {
if (ctx.uses_explicit_workgroup_layout) { if (ctx.profile.support_explicit_workgroup_layout) {
const Id pointer{ const Id pointer{
ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)}; ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)};
return ctx.OpSConvert(ctx.U32[1], ctx.OpLoad(ctx.U8, pointer)); return ctx.OpSConvert(ctx.U32[1], ctx.OpLoad(ctx.U8, pointer));
@@ -53,7 +50,7 @@ Id EmitLoadSharedS8(EmitContext& ctx, Id offset) {
} }
Id EmitLoadSharedU16(EmitContext& ctx, Id offset) { Id EmitLoadSharedU16(EmitContext& ctx, Id offset) {
if (ctx.uses_explicit_workgroup_layout) { if (ctx.profile.support_explicit_workgroup_layout) {
const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)}; const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)};
return ctx.OpUConvert(ctx.U32[1], ctx.OpLoad(ctx.U16, pointer)); return ctx.OpUConvert(ctx.U32[1], ctx.OpLoad(ctx.U16, pointer));
} else { } else {
@@ -63,7 +60,7 @@ Id EmitLoadSharedU16(EmitContext& ctx, Id offset) {
} }
Id EmitLoadSharedS16(EmitContext& ctx, Id offset) { Id EmitLoadSharedS16(EmitContext& ctx, Id offset) {
if (ctx.uses_explicit_workgroup_layout) { if (ctx.profile.support_explicit_workgroup_layout) {
const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)}; const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)};
return ctx.OpSConvert(ctx.U32[1], ctx.OpLoad(ctx.U16, pointer)); return ctx.OpSConvert(ctx.U32[1], ctx.OpLoad(ctx.U16, pointer));
} else { } else {
@@ -73,7 +70,7 @@ Id EmitLoadSharedS16(EmitContext& ctx, Id offset) {
} }
Id EmitLoadSharedU32(EmitContext& ctx, Id offset) { Id EmitLoadSharedU32(EmitContext& ctx, Id offset) {
if (ctx.uses_explicit_workgroup_layout) { if (ctx.profile.support_explicit_workgroup_layout) {
const Id pointer{Pointer(ctx, ctx.shared_u32, ctx.shared_memory_u32, offset, 2)}; const Id pointer{Pointer(ctx, ctx.shared_u32, ctx.shared_memory_u32, offset, 2)};
return ctx.OpLoad(ctx.U32[1], pointer); return ctx.OpLoad(ctx.U32[1], pointer);
} else { } else {
@@ -82,7 +79,7 @@ Id EmitLoadSharedU32(EmitContext& ctx, Id offset) {
} }
Id EmitLoadSharedU64(EmitContext& ctx, Id offset) { Id EmitLoadSharedU64(EmitContext& ctx, Id offset) {
if (ctx.uses_explicit_workgroup_layout) { if (ctx.profile.support_explicit_workgroup_layout) {
const Id pointer{Pointer(ctx, ctx.shared_u32x2, ctx.shared_memory_u32x2, offset, 3)}; const Id pointer{Pointer(ctx, ctx.shared_u32x2, ctx.shared_memory_u32x2, offset, 3)};
return ctx.OpLoad(ctx.U32[2], pointer); return ctx.OpLoad(ctx.U32[2], pointer);
} else { } else {
@@ -97,7 +94,7 @@ Id EmitLoadSharedU64(EmitContext& ctx, Id offset) {
} }
Id EmitLoadSharedU128(EmitContext& ctx, Id offset) { Id EmitLoadSharedU128(EmitContext& ctx, Id offset) {
if (ctx.uses_explicit_workgroup_layout) { if (ctx.profile.support_explicit_workgroup_layout) {
const Id pointer{Pointer(ctx, ctx.shared_u32x4, ctx.shared_memory_u32x4, offset, 4)}; const Id pointer{Pointer(ctx, ctx.shared_u32x4, ctx.shared_memory_u32x4, offset, 4)};
return ctx.OpLoad(ctx.U32[4], pointer); return ctx.OpLoad(ctx.U32[4], pointer);
} }
@@ -113,7 +110,7 @@ Id EmitLoadSharedU128(EmitContext& ctx, Id offset) {
} }
void EmitWriteSharedU8(EmitContext& ctx, Id offset, Id value) { void EmitWriteSharedU8(EmitContext& ctx, Id offset, Id value) {
if (ctx.uses_explicit_workgroup_layout) { if (ctx.profile.support_explicit_workgroup_layout) {
const Id pointer{ const Id pointer{
ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)}; ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)};
ctx.OpStore(pointer, ctx.OpUConvert(ctx.U8, value)); ctx.OpStore(pointer, ctx.OpUConvert(ctx.U8, value));
@@ -123,7 +120,7 @@ void EmitWriteSharedU8(EmitContext& ctx, Id offset, Id value) {
} }
void EmitWriteSharedU16(EmitContext& ctx, Id offset, Id value) { void EmitWriteSharedU16(EmitContext& ctx, Id offset, Id value) {
if (ctx.uses_explicit_workgroup_layout) { if (ctx.profile.support_explicit_workgroup_layout) {
const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)}; const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)};
ctx.OpStore(pointer, ctx.OpUConvert(ctx.U16, value)); ctx.OpStore(pointer, ctx.OpUConvert(ctx.U16, value));
} else { } else {
@@ -133,7 +130,7 @@ void EmitWriteSharedU16(EmitContext& ctx, Id offset, Id value) {
void EmitWriteSharedU32(EmitContext& ctx, Id offset, Id value) { void EmitWriteSharedU32(EmitContext& ctx, Id offset, Id value) {
Id pointer{}; Id pointer{};
if (ctx.uses_explicit_workgroup_layout) { if (ctx.profile.support_explicit_workgroup_layout) {
pointer = Pointer(ctx, ctx.shared_u32, ctx.shared_memory_u32, offset, 2); pointer = Pointer(ctx, ctx.shared_u32, ctx.shared_memory_u32, offset, 2);
} else { } else {
const Id shift{ctx.Const(2U)}; const Id shift{ctx.Const(2U)};
@@ -144,7 +141,7 @@ void EmitWriteSharedU32(EmitContext& ctx, Id offset, Id value) {
} }
void EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value) { void EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value) {
if (ctx.uses_explicit_workgroup_layout) { if (ctx.profile.support_explicit_workgroup_layout) {
const Id pointer{Pointer(ctx, ctx.shared_u32x2, ctx.shared_memory_u32x2, offset, 3)}; const Id pointer{Pointer(ctx, ctx.shared_u32x2, ctx.shared_memory_u32x2, offset, 3)};
ctx.OpStore(pointer, value); ctx.OpStore(pointer, value);
return; return;
@@ -159,7 +156,7 @@ void EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value) {
} }
void EmitWriteSharedU128(EmitContext& ctx, Id offset, Id value) { void EmitWriteSharedU128(EmitContext& ctx, Id offset, Id value) {
if (ctx.uses_explicit_workgroup_layout) { if (ctx.profile.support_explicit_workgroup_layout) {
const Id pointer{Pointer(ctx, ctx.shared_u32x4, ctx.shared_memory_u32x4, offset, 4)}; const Id pointer{Pointer(ctx, ctx.shared_u32x4, ctx.shared_memory_u32x4, offset, 4)};
ctx.OpStore(pointer, value); ctx.OpStore(pointer, value);
return; return;
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -13,14 +10,7 @@ Id SubgroupScope(EmitContext& ctx) {
return ctx.Const(static_cast<u32>(spv::Scope::Subgroup)); return ctx.Const(static_cast<u32>(spv::Scope::Subgroup));
} }
bool StageSupportsSubgroups(EmitContext& ctx) {
return ctx.profile.SupportsSubgroupStage(ctx.stage);
}
Id GetThreadId(EmitContext& ctx) { Id GetThreadId(EmitContext& ctx) {
if (!StageSupportsSubgroups(ctx)) {
return ctx.u32_zero_value;
}
return ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id); return ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id);
} }
@@ -78,9 +68,6 @@ Id GetMaxThreadId(EmitContext& ctx, Id thread_id, Id clamp, Id segmentation_mask
} }
Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) { Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) {
if (!StageSupportsSubgroups(ctx)) {
return value;
}
return ctx.OpSelect( return ctx.OpSelect(
ctx.U32[1], in_range, ctx.U32[1], in_range,
ctx.OpGroupNonUniformShuffle(ctx.U32[1], SubgroupScope(ctx), value, src_thread_id), value); ctx.OpGroupNonUniformShuffle(ctx.U32[1], SubgroupScope(ctx), value, src_thread_id), value);
@@ -102,9 +89,6 @@ Id EmitLaneId(EmitContext& ctx) {
} }
Id EmitVoteAll(EmitContext& ctx, Id pred) { Id EmitVoteAll(EmitContext& ctx, Id pred) {
if (!StageSupportsSubgroups(ctx)) {
return pred;
}
if (!ctx.profile.warp_size_potentially_larger_than_guest) { if (!ctx.profile.warp_size_potentially_larger_than_guest) {
return ctx.OpGroupNonUniformAll(ctx.U1, SubgroupScope(ctx), pred); return ctx.OpGroupNonUniformAll(ctx.U1, SubgroupScope(ctx), pred);
} }
@@ -118,9 +102,6 @@ Id EmitVoteAll(EmitContext& ctx, Id pred) {
} }
Id EmitVoteAny(EmitContext& ctx, Id pred) { Id EmitVoteAny(EmitContext& ctx, Id pred) {
if (!StageSupportsSubgroups(ctx)) {
return pred;
}
if (!ctx.profile.warp_size_potentially_larger_than_guest) { if (!ctx.profile.warp_size_potentially_larger_than_guest) {
return ctx.OpGroupNonUniformAny(ctx.U1, SubgroupScope(ctx), pred); return ctx.OpGroupNonUniformAny(ctx.U1, SubgroupScope(ctx), pred);
} }
@@ -134,9 +115,6 @@ Id EmitVoteAny(EmitContext& ctx, Id pred) {
} }
Id EmitVoteEqual(EmitContext& ctx, Id pred) { Id EmitVoteEqual(EmitContext& ctx, Id pred) {
if (!StageSupportsSubgroups(ctx)) {
return ctx.true_value;
}
if (!ctx.profile.warp_size_potentially_larger_than_guest) { if (!ctx.profile.warp_size_potentially_larger_than_guest) {
return ctx.OpGroupNonUniformAllEqual(ctx.U1, SubgroupScope(ctx), pred); return ctx.OpGroupNonUniformAllEqual(ctx.U1, SubgroupScope(ctx), pred);
} }
@@ -151,9 +129,6 @@ Id EmitVoteEqual(EmitContext& ctx, Id pred) {
} }
Id EmitSubgroupBallot(EmitContext& ctx, Id pred) { Id EmitSubgroupBallot(EmitContext& ctx, Id pred) {
if (!StageSupportsSubgroups(ctx)) {
return ctx.OpSelect(ctx.U32[1], pred, ctx.Const(1u), ctx.u32_zero_value);
}
const Id ballot{ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred)}; const Id ballot{ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred)};
if (!ctx.profile.warp_size_potentially_larger_than_guest) { if (!ctx.profile.warp_size_potentially_larger_than_guest) {
return ctx.OpCompositeExtract(ctx.U32[1], ballot, 0U); return ctx.OpCompositeExtract(ctx.U32[1], ballot, 0U);
@@ -162,37 +137,22 @@ Id EmitSubgroupBallot(EmitContext& ctx, Id pred) {
} }
Id EmitSubgroupEqMask(EmitContext& ctx) { Id EmitSubgroupEqMask(EmitContext& ctx) {
if (!StageSupportsSubgroups(ctx)) {
return ctx.Const(1u);
}
return LoadMask(ctx, ctx.subgroup_mask_eq); return LoadMask(ctx, ctx.subgroup_mask_eq);
} }
Id EmitSubgroupLtMask(EmitContext& ctx) { Id EmitSubgroupLtMask(EmitContext& ctx) {
if (!StageSupportsSubgroups(ctx)) {
return ctx.u32_zero_value;
}
return LoadMask(ctx, ctx.subgroup_mask_lt); return LoadMask(ctx, ctx.subgroup_mask_lt);
} }
Id EmitSubgroupLeMask(EmitContext& ctx) { Id EmitSubgroupLeMask(EmitContext& ctx) {
if (!StageSupportsSubgroups(ctx)) {
return ctx.Const(1u);
}
return LoadMask(ctx, ctx.subgroup_mask_le); return LoadMask(ctx, ctx.subgroup_mask_le);
} }
Id EmitSubgroupGtMask(EmitContext& ctx) { Id EmitSubgroupGtMask(EmitContext& ctx) {
if (!StageSupportsSubgroups(ctx)) {
return ctx.u32_zero_value;
}
return LoadMask(ctx, ctx.subgroup_mask_gt); return LoadMask(ctx, ctx.subgroup_mask_gt);
} }
Id EmitSubgroupGeMask(EmitContext& ctx) { Id EmitSubgroupGeMask(EmitContext& ctx) {
if (!StageSupportsSubgroups(ctx)) {
return ctx.Const(1u);
}
return LoadMask(ctx, ctx.subgroup_mask_ge); return LoadMask(ctx, ctx.subgroup_mask_ge);
} }
@@ -262,7 +222,7 @@ Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id
Id EmitFSwizzleAdd(EmitContext& ctx, Id op_a, Id op_b, Id swizzle) { Id EmitFSwizzleAdd(EmitContext& ctx, Id op_a, Id op_b, Id swizzle) {
const Id three{ctx.Const(3U)}; const Id three{ctx.Const(3U)};
Id mask{GetThreadId(ctx)}; Id mask{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)};
mask = ctx.OpBitwiseAnd(ctx.U32[1], mask, three); mask = ctx.OpBitwiseAnd(ctx.U32[1], mask, three);
mask = ctx.OpShiftLeftLogical(ctx.U32[1], mask, ctx.Const(1U)); mask = ctx.OpShiftLeftLogical(ctx.U32[1], mask, ctx.Const(1U));
mask = ctx.OpShiftRightLogical(ctx.U32[1], swizzle, mask); mask = ctx.OpShiftRightLogical(ctx.U32[1], swizzle, mask);
@@ -371,7 +371,7 @@ Id CasFunction(EmitContext& ctx, Operation operation, Id value_type) {
Id CasLoop(EmitContext& ctx, Operation operation, Id array_pointer, Id element_pointer, Id CasLoop(EmitContext& ctx, Operation operation, Id array_pointer, Id element_pointer,
Id value_type, Id memory_type, spv::Scope scope) { Id value_type, Id memory_type, spv::Scope scope) {
const bool is_shared{scope == spv::Scope::Workgroup}; const bool is_shared{scope == spv::Scope::Workgroup};
const bool is_struct{!is_shared || ctx.uses_explicit_workgroup_layout}; const bool is_struct{!is_shared || ctx.profile.support_explicit_workgroup_layout};
const Id cas_func{CasFunction(ctx, operation, value_type)}; const Id cas_func{CasFunction(ctx, operation, value_type)};
const Id zero{ctx.u32_zero_value}; const Id zero{ctx.u32_zero_value};
const Id scope_id{ctx.Const(static_cast<u32>(scope))}; const Id scope_id{ctx.Const(static_cast<u32>(scope))};
@@ -620,11 +620,7 @@ void EmitContext::DefineSharedMemory(const IR::Program& program) {
return std::make_tuple(variable, element_pointer, pointer); return std::make_tuple(variable, element_pointer, pointer);
}}; }};
uses_explicit_workgroup_layout = if (profile.support_explicit_workgroup_layout) {
profile.support_explicit_workgroup_layout &&
(!program.info.uses_int8 || profile.support_workgroup_layout_8bit_access) &&
(!program.info.uses_int16 || profile.support_workgroup_layout_16bit_access);
if (uses_explicit_workgroup_layout) {
AddExtension("SPV_KHR_workgroup_memory_explicit_layout"); AddExtension("SPV_KHR_workgroup_memory_explicit_layout");
AddCapability(spv::Capability::WorkgroupMemoryExplicitLayoutKHR); AddCapability(spv::Capability::WorkgroupMemoryExplicitLayoutKHR);
if (program.info.uses_int8) { if (program.info.uses_int8) {
@@ -942,10 +938,6 @@ void EmitContext::DefineGlobalMemoryFunctions(const Info& info) {
if (!info.uses_global_memory || !profile.support_int64) { if (!info.uses_global_memory || !profile.support_int64) {
return; return;
} }
if (!profile.support_descriptor_aliasing) {
DefineGlobalMemoryFunctionsU32Fallback(info);
return;
}
using DefPtr = Id StorageDefinitions::*; using DefPtr = Id StorageDefinitions::*;
const Id zero{u32_zero_value}; const Id zero{u32_zero_value};
const auto define_body{[&](DefPtr ssbo_member, Id addr, Id element_pointer, u32 shift, const auto define_body{[&](DefPtr ssbo_member, Id addr, Id element_pointer, u32 shift,
@@ -1023,111 +1015,6 @@ void EmitContext::DefineGlobalMemoryFunctions(const Info& info) {
define(&StorageDefinitions::U32x4, storage_types.U32x4, U32[4], sizeof(u32[4])); define(&StorageDefinitions::U32x4, storage_types.U32x4, U32[4], sizeof(u32[4]));
} }
void EmitContext::DefineGlobalMemoryFunctionsU32Fallback(const Info& info) {
const Id zero{u32_zero_value};
const auto define_body{[&](Id addr, u32 num_words, auto&& callback) {
AddLabel();
const size_t num_buffers{info.storage_buffers_descriptors.size()};
for (size_t index = 0; index < num_buffers; ++index) {
if (!info.nvn_buffer_used[index]) {
continue;
}
const auto& ssbo{info.storage_buffers_descriptors[index]};
const u32 addr_word{ssbo.cbuf_offset / 4};
const u32 addr_lo_comp{addr_word % 4};
const Id cbuf{cbufs[ssbo.cbuf_index].U32x4};
const Id addr_vec_pointer{
OpAccessChain(uniform_types.U32x4, cbuf, zero, Const(addr_word / 4))};
const Id addr_vec{OpLoad(U32[4], addr_vec_pointer)};
const Id addr_lo{OpCompositeExtract(U32[1], addr_vec, addr_lo_comp)};
const Id addr_hi{OpCompositeExtract(U32[1], addr_vec, addr_lo_comp + 1U)};
const Id unaligned_addr{
OpBitcast(U64, OpCompositeConstruct(U32[2], addr_lo, addr_hi))};
const u64 ssbo_align_mask{~(profile.min_ssbo_alignment - 1U)};
const Id ssbo_addr{OpBitwiseAnd(U64, unaligned_addr, Constant(U64, ssbo_align_mask))};
const u32 size_word{addr_word + 2};
Id size_vec{addr_vec};
if (size_word / 4 != addr_word / 4) {
const Id size_vec_pointer{
OpAccessChain(uniform_types.U32x4, cbuf, zero, Const(size_word / 4))};
size_vec = OpLoad(U32[4], size_vec_pointer);
}
const Id ssbo_size{
OpUConvert(U64, OpCompositeExtract(U32[1], size_vec, size_word % 4))};
const Id ssbo_end{OpIAdd(U64, ssbo_addr, ssbo_size)};
const Id cond{OpLogicalAnd(U1, OpUGreaterThanEqual(U1, addr, ssbo_addr),
OpULessThan(U1, addr, ssbo_end))};
const Id then_label{OpLabel()};
const Id else_label{OpLabel()};
OpSelectionMerge(else_label, spv::SelectionControlMask::MaskNone);
OpBranchConditional(cond, then_label, else_label);
AddLabel(then_label);
const Id ssbo_id{ssbos[index].U32};
const Id ssbo_offset{OpUConvert(U32[1], OpISub(U64, addr, ssbo_addr))};
const Id base_word{OpShiftRightLogical(U32[1], ssbo_offset, Const(2U))};
std::array<Id, 4> word_pointers{};
for (u32 word = 0; word < num_words; ++word) {
const Id word_index{word == 0 ? base_word
: OpIAdd(U32[1], base_word, Const(word))};
word_pointers[word] =
OpAccessChain(storage_types.U32.element, ssbo_id, zero, word_index);
}
callback(word_pointers);
AddLabel(else_label);
}
}};
const auto define_load{[&](Id type, u32 num_words) {
const Id function_type{TypeFunction(type, U64)};
const Id func_id{OpFunction(type, spv::FunctionControlMask::MaskNone, function_type)};
const Id addr{OpFunctionParameter(U64)};
define_body(addr, num_words, [&](const std::array<Id, 4>& pointers) {
std::array<Id, 4> words{};
for (u32 word = 0; word < num_words; ++word) {
words[word] = OpLoad(U32[1], pointers[word]);
}
switch (num_words) {
case 1:
OpReturnValue(words[0]);
break;
case 2:
OpReturnValue(OpCompositeConstruct(type, words[0], words[1]));
break;
default:
OpReturnValue(
OpCompositeConstruct(type, words[0], words[1], words[2], words[3]));
break;
}
});
OpReturnValue(ConstantNull(type));
OpFunctionEnd();
return func_id;
}};
const auto define_write{[&](Id type, u32 num_words) {
const Id function_type{TypeFunction(void_id, U64, type)};
const Id func_id{OpFunction(void_id, spv::FunctionControlMask::MaskNone, function_type)};
const Id addr{OpFunctionParameter(U64)};
const Id data{OpFunctionParameter(type)};
define_body(addr, num_words, [&](const std::array<Id, 4>& pointers) {
for (u32 word = 0; word < num_words; ++word) {
const Id value{num_words == 1 ? data : OpCompositeExtract(U32[1], data, word)};
OpStore(pointers[word], value);
}
OpReturn();
});
OpReturn();
OpFunctionEnd();
return func_id;
}};
load_global_func_u32 = define_load(U32[1], 1);
load_global_func_u32x2 = define_load(U32[2], 2);
load_global_func_u32x4 = define_load(U32[4], 4);
write_global_func_u32 = define_write(U32[1], 1);
write_global_func_u32x2 = define_write(U32[2], 2);
write_global_func_u32x4 = define_write(U32[4], 4);
}
void EmitContext::DefineRescalingInput(const Info& info) { void EmitContext::DefineRescalingInput(const Info& info) {
if (!info.uses_rescaling_uniform) { if (!info.uses_rescaling_uniform) {
return; return;
@@ -1551,7 +1438,7 @@ void EmitContext::DefineInputs(const IR::Program& program) {
if (info.uses_is_helper_invocation) { if (info.uses_is_helper_invocation) {
is_helper_invocation = DefineInput(*this, U1, false, spv::BuiltIn::HelperInvocation); is_helper_invocation = DefineInput(*this, U1, false, spv::BuiltIn::HelperInvocation);
} }
if (info.uses_subgroup_mask && profile.SupportsSubgroupStage(stage)) { if (info.uses_subgroup_mask) {
subgroup_mask_eq = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupEqMaskKHR); subgroup_mask_eq = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupEqMaskKHR);
subgroup_mask_lt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLtMaskKHR); subgroup_mask_lt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLtMaskKHR);
subgroup_mask_le = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLeMaskKHR); subgroup_mask_le = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLeMaskKHR);
@@ -1565,10 +1452,9 @@ void EmitContext::DefineInputs(const IR::Program& program) {
Decorate(subgroup_mask_ge, spv::Decoration::Flat); Decorate(subgroup_mask_ge, spv::Decoration::Flat);
} }
} }
if ((info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles || if (info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles ||
(profile.warp_size_potentially_larger_than_guest && (profile.warp_size_potentially_larger_than_guest &&
(info.uses_subgroup_vote || info.uses_subgroup_mask))) && (info.uses_subgroup_vote || info.uses_subgroup_mask))) {
profile.SupportsSubgroupStage(stage)) {
AddCapability(spv::Capability::GroupNonUniform); AddCapability(spv::Capability::GroupNonUniform);
subgroup_local_invocation_id = subgroup_local_invocation_id =
DefineInput(*this, U32[1], false, spv::BuiltIn::SubgroupLocalInvocationId); DefineInput(*this, U32[1], false, spv::BuiltIn::SubgroupLocalInvocationId);
@@ -310,10 +310,6 @@ public:
Id local_memory{}; Id local_memory{};
/// True when this shader's shared memory uses SPV_KHR_workgroup_memory_explicit_layout.
/// False when the host lacks the extension or a width this shader accesses natively.
bool uses_explicit_workgroup_layout{};
Id shared_memory_u8{}; Id shared_memory_u8{};
Id shared_memory_u16{}; Id shared_memory_u16{};
Id shared_memory_u32{}; Id shared_memory_u32{};
@@ -391,7 +387,6 @@ private:
void DefineAttributeMemAccess(const Info& info); void DefineAttributeMemAccess(const Info& info);
void DefineWriteStorageCasLoopFunction(const Info& info); void DefineWriteStorageCasLoopFunction(const Info& info);
void DefineGlobalMemoryFunctions(const Info& info); void DefineGlobalMemoryFunctions(const Info& info);
void DefineGlobalMemoryFunctionsU32Fallback(const Info& info);
void DefineRescalingInput(const Info& info); void DefineRescalingInput(const Info& info);
void DefineRescalingInputPushConstant(); void DefineRescalingInputPushConstant();
void DefineRescalingInputUniformConstant(); void DefineRescalingInputUniformConstant();
-10
View File
@@ -10,8 +10,6 @@
namespace Shader { namespace Shader {
enum class Stage : u32;
struct Profile { struct Profile {
u32 supported_spirv{0x00010000}; u32 supported_spirv{0x00010000};
bool unified_descriptor_binding{}; bool unified_descriptor_binding{};
@@ -31,16 +29,12 @@ struct Profile {
bool support_fp32_signed_zero_nan_preserve{}; bool support_fp32_signed_zero_nan_preserve{};
bool support_fp64_signed_zero_nan_preserve{}; bool support_fp64_signed_zero_nan_preserve{};
bool support_explicit_workgroup_layout{}; bool support_explicit_workgroup_layout{};
bool support_workgroup_layout_8bit_access{};
bool support_workgroup_layout_16bit_access{};
bool support_vote{}; bool support_vote{};
u32 supported_subgroup_stages{0x7F};
bool support_viewport_index_layer_non_geometry{}; bool support_viewport_index_layer_non_geometry{};
bool support_viewport_mask{}; bool support_viewport_mask{};
bool support_typeless_image_loads{}; bool support_typeless_image_loads{};
bool support_demote_to_helper_invocation{}; bool support_demote_to_helper_invocation{};
bool support_int64_atomics{}; bool support_int64_atomics{};
bool support_shared_int64_atomics{};
bool support_derivative_control{}; bool support_derivative_control{};
bool support_geometry_shader_passthrough{}; bool support_geometry_shader_passthrough{};
bool support_native_ndc{}; bool support_native_ndc{};
@@ -99,10 +93,6 @@ struct Profile {
u64 min_ssbo_alignment{}; u64 min_ssbo_alignment{};
u32 max_user_clip_distances{}; u32 max_user_clip_distances{};
bool SupportsSubgroupStage(Stage stage) const {
return (supported_subgroup_stages & (1u << static_cast<u32>(stage))) != 0;
}
}; };
} // namespace Shader } // namespace Shader
+15 -10
View File
@@ -1,3 +1,6 @@
// 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
@@ -53,14 +56,15 @@ 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{
Core::Timing::CreateEvent("callbackA", HostCallbackTemplate<0>), system.CreateTimingEvent("callbackA", HostCallbackTemplate<0>),
Core::Timing::CreateEvent("callbackB", HostCallbackTemplate<1>), system.CreateTimingEvent("callbackB", HostCallbackTemplate<1>),
Core::Timing::CreateEvent("callbackC", HostCallbackTemplate<2>), system.CreateTimingEvent("callbackC", HostCallbackTemplate<2>),
Core::Timing::CreateEvent("callbackD", HostCallbackTemplate<3>), system.CreateTimingEvent("callbackD", HostCallbackTemplate<3>),
Core::Timing::CreateEvent("callbackE", HostCallbackTemplate<4>), system.CreateTimingEvent("callbackE", HostCallbackTemplate<4>),
}; };
expected_callback = 0; expected_callback = 0;
@@ -93,14 +97,15 @@ 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{
Core::Timing::CreateEvent("callbackA", HostCallbackTemplate<0>), system.CreateTimingEvent("callbackA", HostCallbackTemplate<0>),
Core::Timing::CreateEvent("callbackB", HostCallbackTemplate<1>), system.CreateTimingEvent("callbackB", HostCallbackTemplate<1>),
Core::Timing::CreateEvent("callbackC", HostCallbackTemplate<2>), system.CreateTimingEvent("callbackC", HostCallbackTemplate<2>),
Core::Timing::CreateEvent("callbackD", HostCallbackTemplate<3>), system.CreateTimingEvent("callbackD", HostCallbackTemplate<3>),
Core::Timing::CreateEvent("callbackE", HostCallbackTemplate<4>), system.CreateTimingEvent("callbackE", HostCallbackTemplate<4>),
}; };
core_timing.SyncPause(true); core_timing.SyncPause(true);
@@ -11,9 +11,8 @@
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants { #define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
#define END_PUSH_CONSTANTS }; #define END_PUSH_CONSTANTS };
#define UNIFORM(n) #define UNIFORM(n)
#define BINDING_SWIZZLE_BUFFER 0 #define BINDING_INPUT_BUFFER 0
#define BINDING_INPUT_BUFFER 1 #define BINDING_OUTPUT_IMAGE 1
#define BINDING_OUTPUT_IMAGE 2
#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv #else // ^^^ Vulkan ^^^ // vvv OpenGL vvv
@@ -26,8 +25,7 @@
#define BEGIN_PUSH_CONSTANTS #define BEGIN_PUSH_CONSTANTS
#define END_PUSH_CONSTANTS #define END_PUSH_CONSTANTS
#define UNIFORM(n) layout (location = n) uniform #define UNIFORM(n) layout (location = n) uniform
#define BINDING_SWIZZLE_BUFFER 0 #define BINDING_INPUT_BUFFER 0
#define BINDING_INPUT_BUFFER 1
#define BINDING_OUTPUT_IMAGE 0 #define BINDING_OUTPUT_IMAGE 0
#endif #endif
@@ -43,10 +41,6 @@ UNIFORM(6) uint block_height;
UNIFORM(7) uint block_height_mask; UNIFORM(7) uint block_height_mask;
END_PUSH_CONSTANTS END_PUSH_CONSTANTS
layout(binding = BINDING_SWIZZLE_BUFFER, std430) readonly buffer SwizzleTable {
uint swizzle_table[];
};
#if HAS_EXTENDED_TYPES #if HAS_EXTENDED_TYPES
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU8 { uint8_t u8data[]; }; layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU8 { uint8_t u8data[]; };
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU16 { uint16_t u16data[]; }; layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU16 { uint16_t u16data[]; };
@@ -71,9 +65,19 @@ const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHI
const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1, GOB_SIZE_Y - 1); const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1, GOB_SIZE_Y - 1);
uint SwizzleTable(uint pos) {
const uint t[8] = uint[](
0x12100200, 0x13110301, 0x16140604, 0x17150705,
0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d
);
const uint i = pos >> 4;
const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff;
return (h << 4) | (pos & 0xf);
}
uint SwizzleOffset(uvec2 pos) { uint SwizzleOffset(uvec2 pos) {
pos = pos & SWIZZLE_MASK; pos = pos & SWIZZLE_MASK;
return swizzle_table[pos.y * 64 + pos.x]; return SwizzleTable(pos.y * 64 + pos.x);
} }
uvec4 ReadTexel(uint offset) { uvec4 ReadTexel(uint offset) {
@@ -11,9 +11,8 @@
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants { #define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
#define END_PUSH_CONSTANTS }; #define END_PUSH_CONSTANTS };
#define UNIFORM(n) #define UNIFORM(n)
#define BINDING_SWIZZLE_BUFFER 0 #define BINDING_INPUT_BUFFER 0
#define BINDING_INPUT_BUFFER 1 #define BINDING_OUTPUT_IMAGE 1
#define BINDING_OUTPUT_IMAGE 2
#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv #else // ^^^ Vulkan ^^^ // vvv OpenGL vvv
@@ -26,8 +25,7 @@
#define BEGIN_PUSH_CONSTANTS #define BEGIN_PUSH_CONSTANTS
#define END_PUSH_CONSTANTS #define END_PUSH_CONSTANTS
#define UNIFORM(n) layout (location = n) uniform #define UNIFORM(n) layout (location = n) uniform
#define BINDING_SWIZZLE_BUFFER 0 #define BINDING_INPUT_BUFFER 0
#define BINDING_INPUT_BUFFER 1
#define BINDING_OUTPUT_IMAGE 0 #define BINDING_OUTPUT_IMAGE 0
#endif #endif
@@ -45,10 +43,6 @@ UNIFORM(8) uint block_depth;
UNIFORM(9) uint block_depth_mask; UNIFORM(9) uint block_depth_mask;
END_PUSH_CONSTANTS END_PUSH_CONSTANTS
layout(binding = BINDING_SWIZZLE_BUFFER, std430) readonly buffer SwizzleTable {
uint swizzle_table[];
};
#if HAS_EXTENDED_TYPES #if HAS_EXTENDED_TYPES
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU8 { uint8_t u8data[]; }; layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU8 { uint8_t u8data[]; };
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU16 { uint16_t u16data[]; }; layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU16 { uint16_t u16data[]; };
@@ -73,9 +67,19 @@ const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHI
const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1, GOB_SIZE_Y - 1); const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1, GOB_SIZE_Y - 1);
uint SwizzleTable(uint pos) {
const uint t[8] = uint[](
0x12100200, 0x13110301, 0x16140604, 0x17150705,
0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d
);
const uint i = pos >> 4;
const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff;
return (h << 4) | (pos & 0xf);
}
uint SwizzleOffset(uvec2 pos) { uint SwizzleOffset(uvec2 pos) {
pos = pos & SWIZZLE_MASK; pos = pos & SWIZZLE_MASK;
return swizzle_table[pos.y * 64 + pos.x]; return SwizzleTable(pos.y * 64 + pos.x);
} }
uvec4 ReadTexel(uint offset) { uvec4 ReadTexel(uint offset) {
@@ -10,9 +10,8 @@
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants { #define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
#define END_PUSH_CONSTANTS }; #define END_PUSH_CONSTANTS };
#define UNIFORM(n) #define UNIFORM(n)
#define BINDING_SWIZZLE_BUFFER 0 #define BINDING_INPUT_BUFFER 0
#define BINDING_INPUT_BUFFER 1 #define BINDING_OUTPUT_BUFFER 1
#define BINDING_OUTPUT_BUFFER 2
#else #else
#extension GL_NV_gpu_shader5 : enable #extension GL_NV_gpu_shader5 : enable
#ifdef GL_NV_gpu_shader5 #ifdef GL_NV_gpu_shader5
@@ -23,7 +22,6 @@
#define BEGIN_PUSH_CONSTANTS #define BEGIN_PUSH_CONSTANTS
#define END_PUSH_CONSTANTS #define END_PUSH_CONSTANTS
#define UNIFORM(n) layout(location = n) uniform #define UNIFORM(n) layout(location = n) uniform
#define BINDING_SWIZZLE_BUFFER 0
#define BINDING_INPUT_BUFFER 1 #define BINDING_INPUT_BUFFER 1
#define BINDING_OUTPUT_BUFFER 0 #define BINDING_OUTPUT_BUFFER 0
#endif #endif
@@ -66,13 +64,9 @@ END_PUSH_CONSTANTS
#endif #endif
// --- Buffers --- // --- Buffers ---
layout(binding = BINDING_SWIZZLE_BUFFER, std430) readonly buffer SwizzleTable {
uint swizzle_table[];
};
#if HAS_EXTENDED_TYPES #if HAS_EXTENDED_TYPES
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU8 { uint8_t u8data[]; }; layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU8 { uint8_t u8data[]; };
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU16 { uint16_t u16data[]; }; layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU16 { uint16_t u16data[]; };
#endif #endif
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU32 { uint u32data[]; }; layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU32 { uint u32data[]; };
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU64 { uvec2 u64data[]; }; layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU64 { uvec2 u64data[]; };
@@ -96,10 +90,20 @@ const uint GOB_SIZE_Z_SHIFT = 0;
const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHIFT; const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHIFT;
const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1u, GOB_SIZE_Y - 1u); const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1u, GOB_SIZE_Y - 1u);
uint SwizzleTable(uint pos) {
const uint t[8] = uint[](
0x12100200, 0x13110301, 0x16140604, 0x17150705,
0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d
);
const uint i = pos >> 4;
const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff;
return (h << 4) | (pos & 0xf);
}
// --- Helpers --- // --- Helpers ---
uint SwizzleOffset(uvec2 pos) { uint SwizzleOffset(uvec2 pos) {
pos &= SWIZZLE_MASK; pos &= SWIZZLE_MASK;
return swizzle_table[pos.y * 64u + pos.x]; return SwizzleTable(pos.y * 64u + pos.x);
} }
uvec4 ReadTexel(uint offset) { uvec4 ReadTexel(uint offset) {
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -56,10 +59,8 @@ UtilShaders::UtilShaders(ProgramManager& program_manager_)
copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)), copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)),
convert_s8d24_program(MakeProgram(OPENGL_CONVERT_S8D24_COMP)), convert_s8d24_program(MakeProgram(OPENGL_CONVERT_S8D24_COMP)),
convert_ms_to_nonms_program(MakeProgram(CONVERT_MSAA_TO_NON_MSAA_COMP)), convert_ms_to_nonms_program(MakeProgram(CONVERT_MSAA_TO_NON_MSAA_COMP)),
convert_nonms_to_ms_program(MakeProgram(CONVERT_NON_MSAA_TO_MSAA_COMP)) { convert_nonms_to_ms_program(MakeProgram(CONVERT_NON_MSAA_TO_MSAA_COMP))
const auto swizzle_table = Tegra::Texture::MakeSwizzleTable(); {
swizzle_table_buffer.Create();
glNamedBufferStorage(swizzle_table_buffer.handle, sizeof(swizzle_table), &swizzle_table, 0);
} }
UtilShaders::~UtilShaders() = default; UtilShaders::~UtilShaders() = default;
@@ -116,13 +117,11 @@ void UtilShaders::ASTCDecode(Image& image, const StagingBufferMap& map,
void UtilShaders::BlockLinearUpload2D(Image& image, const StagingBufferMap& map, void UtilShaders::BlockLinearUpload2D(Image& image, const StagingBufferMap& map,
std::span<const SwizzleParameters> swizzles) { std::span<const SwizzleParameters> swizzles) {
static constexpr Extent3D WORKGROUP_SIZE{32, 32, 1}; static constexpr Extent3D WORKGROUP_SIZE{32, 32, 1};
static constexpr GLuint BINDING_SWIZZLE_BUFFER = 0; static constexpr GLuint BINDING_INPUT_BUFFER = 0;
static constexpr GLuint BINDING_INPUT_BUFFER = 1;
static constexpr GLuint BINDING_OUTPUT_IMAGE = 0; static constexpr GLuint BINDING_OUTPUT_IMAGE = 0;
program_manager.BindComputeProgram(block_linear_unswizzle_2d_program.handle); program_manager.BindComputeProgram(block_linear_unswizzle_2d_program.handle);
glFlushMappedNamedBufferRange(map.buffer, map.offset, image.guest_size_bytes); glFlushMappedNamedBufferRange(map.buffer, map.offset, image.guest_size_bytes);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, BINDING_SWIZZLE_BUFFER, swizzle_table_buffer.handle);
const GLenum store_format = StoreFormat(BytesPerBlock(image.info.format)); const GLenum store_format = StoreFormat(BytesPerBlock(image.info.format));
for (const SwizzleParameters& swizzle : swizzles) { for (const SwizzleParameters& swizzle : swizzles) {
@@ -153,14 +152,11 @@ void UtilShaders::BlockLinearUpload2D(Image& image, const StagingBufferMap& map,
void UtilShaders::BlockLinearUpload3D(Image& image, const StagingBufferMap& map, void UtilShaders::BlockLinearUpload3D(Image& image, const StagingBufferMap& map,
std::span<const SwizzleParameters> swizzles) { std::span<const SwizzleParameters> swizzles) {
static constexpr Extent3D WORKGROUP_SIZE{16, 8, 8}; static constexpr Extent3D WORKGROUP_SIZE{16, 8, 8};
static constexpr GLuint BINDING_INPUT_BUFFER = 0;
static constexpr GLuint BINDING_SWIZZLE_BUFFER = 0;
static constexpr GLuint BINDING_INPUT_BUFFER = 1;
static constexpr GLuint BINDING_OUTPUT_IMAGE = 0; static constexpr GLuint BINDING_OUTPUT_IMAGE = 0;
glFlushMappedNamedBufferRange(map.buffer, map.offset, image.guest_size_bytes); glFlushMappedNamedBufferRange(map.buffer, map.offset, image.guest_size_bytes);
program_manager.BindComputeProgram(block_linear_unswizzle_3d_program.handle); program_manager.BindComputeProgram(block_linear_unswizzle_3d_program.handle);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, BINDING_SWIZZLE_BUFFER, swizzle_table_buffer.handle);
const GLenum store_format = StoreFormat(BytesPerBlock(image.info.format)); const GLenum store_format = StoreFormat(BytesPerBlock(image.info.format));
for (const SwizzleParameters& swizzle : swizzles) { for (const SwizzleParameters& swizzle : swizzles) {
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -45,9 +48,6 @@ public:
private: private:
ProgramManager& program_manager; ProgramManager& program_manager;
OGLBuffer swizzle_table_buffer;
OGLProgram astc_decoder_program; OGLProgram astc_decoder_program;
OGLProgram block_linear_unswizzle_2d_program; OGLProgram block_linear_unswizzle_2d_program;
OGLProgram block_linear_unswizzle_3d_program; OGLProgram block_linear_unswizzle_3d_program;
@@ -653,71 +653,8 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
scheduler.Finish(); scheduler.Finish();
} }
constexpr u32 BL3D_BINDING_SWIZZLE_TABLE = 0; constexpr u32 BL3D_BINDING_INPUT_BUFFER = 0;
constexpr u32 BL3D_BINDING_INPUT_BUFFER = 1; constexpr u32 BL3D_BINDING_OUTPUT_BUFFER = 1;
constexpr u32 BL3D_BINDING_OUTPUT_BUFFER = 2;
constexpr std::array<VkDescriptorSetLayoutBinding, 3> BL3D_DESCRIPTOR_SET_BINDINGS{{
{
.binding = BL3D_BINDING_SWIZZLE_TABLE,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, // swizzle_table[]
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.pImmutableSamplers = nullptr,
},
{
.binding = BL3D_BINDING_INPUT_BUFFER,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, // block-linear input
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.pImmutableSamplers = nullptr,
},
{
.binding = BL3D_BINDING_OUTPUT_BUFFER,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.pImmutableSamplers = nullptr,
},
}};
constexpr DescriptorBankInfo BL3D_BANK_INFO{
.uniform_buffers = 0,
.storage_buffers = 3,
.texture_buffers = 0,
.image_buffers = 0,
.textures = 0,
.images = 0,
.score = 3,
};
constexpr std::array<VkDescriptorUpdateTemplateEntry, 3>
BL3D_DESCRIPTOR_UPDATE_TEMPLATE_ENTRY{{
{
.dstBinding = BL3D_BINDING_SWIZZLE_TABLE,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.offset = BL3D_BINDING_SWIZZLE_TABLE * sizeof(DescriptorUpdateEntry),
.stride = sizeof(DescriptorUpdateEntry),
},
{
.dstBinding = BL3D_BINDING_INPUT_BUFFER,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.offset = BL3D_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry),
.stride = sizeof(DescriptorUpdateEntry),
},
{
.dstBinding = BL3D_BINDING_OUTPUT_BUFFER,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.offset = BL3D_BINDING_OUTPUT_BUFFER * sizeof(DescriptorUpdateEntry),
.stride = sizeof(DescriptorUpdateEntry),
}
}};
struct alignas(16) BlockLinearUnswizzle3DPushConstants { struct alignas(16) BlockLinearUnswizzle3DPushConstants {
u32 blocks_dim[3]; // Offset 0 u32 blocks_dim[3]; // Offset 0
@@ -745,11 +682,50 @@ BlockLinearUnswizzle3DPass::BlockLinearUnswizzle3DPass(
DescriptorPool& descriptor_pool_, DescriptorPool& descriptor_pool_,
StagingBufferPool& staging_buffer_pool_, StagingBufferPool& staging_buffer_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue_) ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
: ComputePass( : ComputePass(device_, scheduler_, descriptor_pool_,
device_, scheduler_, descriptor_pool_, std::array<VkDescriptorSetLayoutBinding, 2>{{
BL3D_DESCRIPTOR_SET_BINDINGS, {
BL3D_DESCRIPTOR_UPDATE_TEMPLATE_ENTRY, .binding = BL3D_BINDING_INPUT_BUFFER,
BL3D_BANK_INFO, .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, // block-linear input
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.pImmutableSamplers = nullptr,
},
{
.binding = BL3D_BINDING_OUTPUT_BUFFER,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.pImmutableSamplers = nullptr,
},
}},
std::array<VkDescriptorUpdateTemplateEntry, 2>{{
{
.dstBinding = BL3D_BINDING_INPUT_BUFFER,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.offset = BL3D_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry),
.stride = sizeof(DescriptorUpdateEntry),
},
{
.dstBinding = BL3D_BINDING_OUTPUT_BUFFER,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.offset = BL3D_BINDING_OUTPUT_BUFFER * sizeof(DescriptorUpdateEntry),
.stride = sizeof(DescriptorUpdateEntry),
}
}},
DescriptorBankInfo{
.uniform_buffers = 0,
.storage_buffers = 2,
.texture_buffers = 0,
.image_buffers = 0,
.textures = 0,
.images = 0,
.score = 2,
},
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(BlockLinearUnswizzle3DPushConstants)>, COMPUTE_PUSH_CONSTANT_RANGE<sizeof(BlockLinearUnswizzle3DPushConstants)>,
BLOCK_LINEAR_UNSWIZZLE_3D_BCN_COMP_SPV), BLOCK_LINEAR_UNSWIZZLE_3D_BCN_COMP_SPV),
scheduler{scheduler_}, scheduler{scheduler_},
@@ -822,8 +798,6 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk(
pc.blocks_dim[2] = z_count; // Only process the count pc.blocks_dim[2] = z_count; // Only process the count
compute_pass_descriptor_queue.Acquire(scheduler, 3); compute_pass_descriptor_queue.Acquire(scheduler, 3);
compute_pass_descriptor_queue.AddBuffer(*image.runtime->swizzle_table_buffer, 0,
image.runtime->swizzle_table_size);
compute_pass_descriptor_queue.AddBuffer(swizzled.buffer, compute_pass_descriptor_queue.AddBuffer(swizzled.buffer,
sw.buffer_offset + swizzled.offset, sw.buffer_offset + swizzled.offset,
image.guest_size_bytes - sw.buffer_offset); image.guest_size_bytes - sw.buffer_offset);
@@ -378,20 +378,6 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
serialization_thread(1, "VkPipelineSerialization") { serialization_thread(1, "VkPipelineSerialization") {
const auto& float_control{device.FloatControlProperties()}; const auto& float_control{device.FloatControlProperties()};
const VkDriverId driver_id{device.GetDriverID()}; const VkDriverId driver_id{device.GetDriverID()};
const VkShaderStageFlags subgroup_stages{device.GetSubgroupSupportedStages()};
const auto subgroup_stage_bit{[subgroup_stages](VkShaderStageFlags flag, Shader::Stage stage) {
return (subgroup_stages & flag) != 0 ? (1u << static_cast<u32>(stage)) : 0u;
}};
const u32 supported_subgroup_stages{
subgroup_stage_bit(VK_SHADER_STAGE_VERTEX_BIT, Shader::Stage::VertexA) |
subgroup_stage_bit(VK_SHADER_STAGE_VERTEX_BIT, Shader::Stage::VertexB) |
subgroup_stage_bit(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
Shader::Stage::TessellationControl) |
subgroup_stage_bit(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
Shader::Stage::TessellationEval) |
subgroup_stage_bit(VK_SHADER_STAGE_GEOMETRY_BIT, Shader::Stage::Geometry) |
subgroup_stage_bit(VK_SHADER_STAGE_FRAGMENT_BIT, Shader::Stage::Fragment) |
subgroup_stage_bit(VK_SHADER_STAGE_COMPUTE_BIT, Shader::Stage::Compute)};
profile = Shader::Profile{ profile = Shader::Profile{
.supported_spirv = device.SupportedSpirvVersion(), .supported_spirv = device.SupportedSpirvVersion(),
.unified_descriptor_binding = true, .unified_descriptor_binding = true,
@@ -416,12 +402,7 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
.support_fp64_signed_zero_nan_preserve = .support_fp64_signed_zero_nan_preserve =
float_control.shaderSignedZeroInfNanPreserveFloat64 != VK_FALSE, float_control.shaderSignedZeroInfNanPreserveFloat64 != VK_FALSE,
.support_explicit_workgroup_layout = device.IsKhrWorkgroupMemoryExplicitLayoutSupported(), .support_explicit_workgroup_layout = device.IsKhrWorkgroupMemoryExplicitLayoutSupported(),
.support_workgroup_layout_8bit_access =
device.IsWorkgroupMemoryExplicitLayout8BitSupported(),
.support_workgroup_layout_16bit_access =
device.IsWorkgroupMemoryExplicitLayout16BitSupported(),
.support_vote = device.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_VOTE_BIT), .support_vote = device.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_VOTE_BIT),
.supported_subgroup_stages = supported_subgroup_stages,
.support_viewport_index_layer_non_geometry = .support_viewport_index_layer_non_geometry =
device.IsExtShaderViewportIndexLayerSupported(), device.IsExtShaderViewportIndexLayerSupported(),
.support_viewport_mask = device.IsNvViewportArray2Supported(), .support_viewport_mask = device.IsNvViewportArray2Supported(),
@@ -429,7 +410,6 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
.support_demote_to_helper_invocation = .support_demote_to_helper_invocation =
device.IsExtShaderDemoteToHelperInvocationSupported(), device.IsExtShaderDemoteToHelperInvocationSupported(),
.support_int64_atomics = device.IsExtShaderAtomicInt64Supported(), .support_int64_atomics = device.IsExtShaderAtomicInt64Supported(),
.support_shared_int64_atomics = device.IsSharedInt64AtomicsSupported(),
.support_derivative_control = true, .support_derivative_control = true,
.support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(), .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(),
.support_native_ndc = device.IsExtDepthClipControlSupported(), .support_native_ndc = device.IsExtDepthClipControlSupported(),
@@ -453,8 +433,7 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
.has_broken_spirv_position_input = driver_id == false, .has_broken_spirv_position_input = driver_id == false,
.has_broken_unsigned_image_offsets = false, .has_broken_unsigned_image_offsets = false,
.has_broken_signed_operations = false, .has_broken_signed_operations = false,
.has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY || .has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY,
driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY,
.ignore_nan_fp_comparisons = false, .ignore_nan_fp_comparisons = false,
.has_broken_spirv_subgroup_mask_vector_extract_dynamic = false, .has_broken_spirv_subgroup_mask_vector_extract_dynamic = false,
.has_broken_robust = .has_broken_robust =
@@ -925,8 +904,12 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
} }
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)}; auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
const VkDriverIdKHR driver_id = device.GetDriverID();
const bool needs_shared_mem_clamp =
driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
driver_id == VK_DRIVER_ID_ARM_PROPRIETARY;
const u32 max_shared_memory = device.GetMaxComputeSharedMemorySize(); const u32 max_shared_memory = device.GetMaxComputeSharedMemorySize();
if (program.shared_memory_size > max_shared_memory) { if (needs_shared_mem_clamp && program.shared_memory_size > max_shared_memory) {
LOG_WARNING(Render_Vulkan, LOG_WARNING(Render_Vulkan,
"Compute shader 0x{:016x} requests {}KB shared memory but device max is {}KB - clamping", "Compute shader 0x{:016x} requests {}KB shared memory but device max is {}KB - clamping",
key.unique_hash, key.unique_hash,
@@ -1255,18 +1255,6 @@ void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) {
} }
} }
const bool is_float_depth =
regs.zeta.format == Tegra::DepthFormat::Z32_FLOAT ||
regs.zeta.format == Tegra::DepthFormat::Z32_FLOAT_X24S8_UINT;
if (is_float_depth && units != 0.0f && !device.IsExtDepthBiasControlSupported()) {
static bool logged_float_bias_warning = false;
if (!logged_float_bias_warning) {
logged_float_bias_warning = true;
LOG_WARNING(Render_Vulkan,
"Depth bias on a float depth target without VK_EXT_depth_bias_control");
}
}
scheduler.Record([constant = units, clamp = regs.depth_bias_clamp, scheduler.Record([constant = units, clamp = regs.depth_bias_clamp,
factor = regs.slope_scale_depth_bias, this](vk::CommandBuffer cmdbuf) { factor = regs.slope_scale_depth_bias, this](vk::CommandBuffer cmdbuf) {
if (device.IsExtDepthBiasControlSupported()) { if (device.IsExtDepthBiasControlSupported()) {
@@ -909,40 +909,6 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched
bl3d_unswizzle_pass.emplace(device, scheduler, descriptor_pool, bl3d_unswizzle_pass.emplace(device, scheduler, descriptor_pool,
staging_buffer_pool, compute_pass_descriptor_queue); staging_buffer_pool, compute_pass_descriptor_queue);
} }
// --- Create swizzle table buffer ---
{
auto table = Tegra::Texture::MakeSwizzleTable();
swizzle_table_size = static_cast<VkDeviceSize>(table.size() * sizeof(table[0]));
auto staging = staging_buffer_pool.Request(swizzle_table_size, MemoryUsage::Upload);
std::memcpy(staging.mapped_span.data(), table.data(), static_cast<size_t>(swizzle_table_size));
VkBufferCreateInfo ci{
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = swizzle_table_size,
.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
VK_BUFFER_USAGE_TRANSFER_DST_BIT |
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
};
swizzle_table_buffer = memory_allocator.CreateBuffer(ci, MemoryUsage::DeviceLocal);
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([staging_buf = staging.buffer,
dst_buf = *swizzle_table_buffer,
size = swizzle_table_size,
src_off = staging.offset](vk::CommandBuffer cmdbuf) {
const VkBufferCopy region{
.srcOffset = src_off,
.dstOffset = 0,
.size = size,
};
cmdbuf.CopyBuffer(staging_buf, dst_buf, region);
});
}
} }
void TextureCacheRuntime::Finish() { void TextureCacheRuntime::Finish() {
@@ -2484,18 +2450,13 @@ void TextureCacheRuntime::AccelerateImageUpload(
if (!Settings::values.gpu_unswizzle_enabled.GetValue() || !bl3d_unswizzle_pass) { if (!Settings::values.gpu_unswizzle_enabled.GetValue() || !bl3d_unswizzle_pass) {
if (IsPixelFormatBCn(image.info.format) && image.info.type == ImageType::e3D) { if (IsPixelFormatBCn(image.info.format) && image.info.type == ImageType::e3D) {
ASSERT_MSG(false, "GPU unswizzle is disabled for BCn 3D texture"); ASSERT(false && "GPU unswizzle is disabled for BCn 3D texture");
} }
ASSERT(false); ASSERT(false);
return; return;
} }
if (bl3d_unswizzle_pass && if (bl3d_unswizzle_pass && IsPixelFormatBCn(image.info.format) && image.info.type == ImageType::e3D && image.info.resources.levels == 1 && image.info.resources.layers == 1) {
IsPixelFormatBCn(image.info.format) &&
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); return bl3d_unswizzle_pass->Unswizzle(image, map, swizzles, z_start, z_count);
} }
@@ -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 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -130,9 +130,6 @@ 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;
vk::Buffer swizzle_table_buffer;
VkDeviceSize swizzle_table_size = 0;
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;
+1 -4
View File
@@ -1915,16 +1915,13 @@ void TextureCache<P>::TrimInactiveSamplers(size_t budget) {
ankerl::unordered_dense::set<SamplerId> active_sampler_ids; ankerl::unordered_dense::set<SamplerId> active_sampler_ids;
for (auto const& e : channel_state->sampler_ids) for (auto const& e : channel_state->sampler_ids)
active_sampler_ids.insert(e.second); active_sampler_ids.insert(e.second);
if constexpr (requires { runtime.Finish(); }) {
runtime.Finish();
}
// Elements in the map must be necesarily valid // Elements in the map must be necesarily valid
size_t removed = 0; size_t removed = 0;
for (auto it = channel_state->samplers.begin(); it != channel_state->samplers.end();) { for (auto it = channel_state->samplers.begin(); it != channel_state->samplers.end();) {
const SamplerId sampler_id = it->second; const SamplerId sampler_id = it->second;
if (!sampler_id || sampler_id == CORRUPT_ID) { if (!sampler_id || sampler_id == CORRUPT_ID) {
it = channel_state->samplers.erase(it); it = channel_state->samplers.erase(it);
} else if (active_sampler_ids.contains(sampler_id)) { } else if (std::ranges::find(active_sampler_ids, sampler_id) != active_sampler_ids.end()) {
++it; ++it;
} else { } else {
slot_samplers.erase(sampler_id); slot_samplers.erase(sampler_id);
+3 -18
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -23,24 +26,6 @@ constexpr u32 GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_
constexpr u32 SWIZZLE_X_BITS = 0b100101111; constexpr u32 SWIZZLE_X_BITS = 0b100101111;
constexpr u32 SWIZZLE_Y_BITS = 0b011010000; constexpr u32 SWIZZLE_Y_BITS = 0b011010000;
using SwizzleTable = std::array<std::array<u32, GOB_SIZE_X>, GOB_SIZE_Y>;
/**
* This table represents the internal swizzle of a gob, in format 16 bytes x 2 sector packing.
* Calculates the offset of an (x, y) position within a swizzled texture.
* Taken from the Tegra X1 Technical Reference Manual. pages 1187-1188
*/
constexpr SwizzleTable MakeSwizzleTable() {
SwizzleTable table{};
for (u32 y = 0; y < table.size(); ++y) {
for (u32 x = 0; x < table[0].size(); ++x) {
table[y][x] = ((x % 64) / 32) * 256 + ((y % 8) / 2) * 64 + ((x % 32) / 16) * 32 +
(y % 2) * 16 + (x % 16);
}
}
return table;
}
/// Unswizzles a block linear texture into linear memory. /// Unswizzles a block linear texture into linear memory.
void UnswizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, void UnswizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel,
u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth, u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth,
+31 -5
View File
@@ -501,6 +501,14 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
LOG_WARNING(Render_Vulkan, LOG_WARNING(Render_Vulkan,
"Qualcomm drivers have slow push descriptor implementation"); "Qualcomm drivers have slow push descriptor implementation");
RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME); RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
LOG_WARNING(Render_Vulkan,
"Disabling shader float controls and 64-bit integer features on Qualcomm proprietary drivers");
RemoveExtension(extensions.shader_float_controls, VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME);
RemoveExtensionFeature(extensions.shader_atomic_int64, features.shader_atomic_int64,
VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME);
features.shader_atomic_int64.shaderBufferInt64Atomics = false;
features.shader_atomic_int64.shaderSharedInt64Atomics = false;
features.features.shaderInt64 = false;
#if defined(__ANDROID__) && defined(ARCHITECTURE_arm64) #if defined(__ANDROID__) && defined(ARCHITECTURE_arm64)
// BCn patching only safe on Android 9+ (API 28+). Older versions crash on driver load. // BCn patching only safe on Android 9+ (API 28+). Older versions crash on driver load.
@@ -553,6 +561,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
features.shader_float16_int8.shaderFloat16 = false; features.shader_float16_int8.shaderFloat16 = false;
} }
// Mali/ NVIDIA proprietary drivers: Shader stencil export not supported
// Use hardware depth/stencil blits instead when available // Use hardware depth/stencil blits instead when available
if (!extensions.shader_stencil_export) { if (!extensions.shader_stencil_export) {
LOG_INFO(Render_Vulkan, LOG_INFO(Render_Vulkan,
@@ -565,8 +574,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
if (!is_blit_depth24_stencil8_supported && !is_blit_depth32_stencil8_supported) { if (!is_blit_depth24_stencil8_supported && !is_blit_depth32_stencil8_supported) {
LOG_WARNING(Render_Vulkan, LOG_WARNING(Render_Vulkan,
"Neither shader export nor hardware blits available for " "NVIDIA: Neither shader export nor hardware blits available for "
"depth/stencil."); "depth/stencil. Performance may be degraded.");
} }
} }
} }
@@ -644,13 +653,21 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
if (is_turnip || is_qualcomm) { if (is_turnip || is_qualcomm) {
LOG_WARNING(Render_Vulkan, "Driver requires higher-than-reported binding limits"); LOG_WARNING(Render_Vulkan, "Driver requires higher-than-reported binding limits");
properties.properties.limits.maxVertexInputBindings = properties.properties.limits.maxVertexInputBindings = 32;
(std::max)(properties.properties.limits.maxVertexInputBindings, 32U);
} }
const auto dyna_state = Settings::values.dyna_state.GetValue(); const auto dyna_state = Settings::values.dyna_state.GetValue();
// Base dynamic states (VIEWPORT, SCISSOR, DEPTH_BIAS, etc.) are ALWAYS active in vk_graphics_pipeline.cpp
// This slider controls EXTENDED dynamic states with accumulative levels per Vulkan specs:
// Level 0 = Core Dynamic States only (Vulkan 1.0)
// Level 1 = Core + VK_EXT_extended_dynamic_state
// Level 2 = Core + VK_EXT_extended_dynamic_state + VK_EXT_extended_dynamic_state2
// Level 3 = Core + VK_EXT_extended_dynamic_state + VK_EXT_extended_dynamic_state2 + VK_EXT_extended_dynamic_state3
switch (dyna_state) { switch (dyna_state) {
case Settings::ExtendedDynamicState::Disabled: case Settings::ExtendedDynamicState::Disabled:
// Level 0: Disable all extended dynamic state extensions
RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state, RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state,
VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2, RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2,
@@ -661,6 +678,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
dynamic_state3_enables = false; dynamic_state3_enables = false;
break; break;
case Settings::ExtendedDynamicState::EDS1: case Settings::ExtendedDynamicState::EDS1:
// Level 1: Enable EDS1, disable EDS2 and EDS3
RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2, RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2,
VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3, RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3,
@@ -669,6 +687,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
dynamic_state3_enables = false; dynamic_state3_enables = false;
break; break;
case Settings::ExtendedDynamicState::EDS2: case Settings::ExtendedDynamicState::EDS2:
// Level 2: Enable EDS1 + EDS2, disable EDS3
RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3, RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3,
VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
dynamic_state3_blending = false; dynamic_state3_blending = false;
@@ -676,9 +695,12 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
break; break;
case Settings::ExtendedDynamicState::EDS3: case Settings::ExtendedDynamicState::EDS3:
default: default:
// Level 3: Enable all (EDS1 + EDS2 + EDS3)
break; break;
} }
// VK_EXT_vertex_input_dynamic_state is independent from EDS
// It can be enabled even without extended_dynamic_state
if (!Settings::values.vertex_input_dynamic_state.GetValue()) { if (!Settings::values.vertex_input_dynamic_state.GetValue()) {
RemoveExtensionFeature(extensions.vertex_input_dynamic_state, features.vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); RemoveExtensionFeature(extensions.vertex_input_dynamic_state, features.vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
} }
@@ -1260,7 +1282,8 @@ void Device::RemoveUnsuitableExtensions() {
VK_EXT_IMAGE_ROBUSTNESS_EXTENSION_NAME); VK_EXT_IMAGE_ROBUSTNESS_EXTENSION_NAME);
// VK_KHR_shader_atomic_int64 // VK_KHR_shader_atomic_int64
extensions.shader_atomic_int64 = features.shader_atomic_int64.shaderBufferInt64Atomics; extensions.shader_atomic_int64 = features.shader_atomic_int64.shaderBufferInt64Atomics &&
features.shader_atomic_int64.shaderSharedInt64Atomics;
RemoveExtensionFeatureIfUnsuitable(extensions.shader_atomic_int64, features.shader_atomic_int64, RemoveExtensionFeatureIfUnsuitable(extensions.shader_atomic_int64, features.shader_atomic_int64,
VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME); VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME);
@@ -1309,7 +1332,10 @@ void Device::RemoveUnsuitableExtensions() {
// VK_KHR_workgroup_memory_explicit_layout // VK_KHR_workgroup_memory_explicit_layout
extensions.workgroup_memory_explicit_layout = extensions.workgroup_memory_explicit_layout =
features.features.shaderInt16 &&
features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout && features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout &&
features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout8BitAccess &&
features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout16BitAccess &&
features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayoutScalarBlockLayout; features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayoutScalarBlockLayout;
RemoveExtensionFeatureIfUnsuitable(extensions.workgroup_memory_explicit_layout, RemoveExtensionFeatureIfUnsuitable(extensions.workgroup_memory_explicit_layout,
features.workgroup_memory_explicit_layout, features.workgroup_memory_explicit_layout,
+2 -26
View File
@@ -403,11 +403,6 @@ FN_MAX_LIMIT_LIST
return properties.subgroup_properties.supportedOperations & feature; return properties.subgroup_properties.supportedOperations & feature;
} }
/// Returns the shader stages that support subgroup operations.
VkShaderStageFlags GetSubgroupSupportedStages() const {
return properties.subgroup_properties.supportedStages;
}
/// Returns the maximum number of push descriptors. /// Returns the maximum number of push descriptors.
u32 MaxPushDescriptors() const { u32 MaxPushDescriptors() const {
return properties.push_descriptor.maxPushDescriptors; return properties.push_descriptor.maxPushDescriptors;
@@ -488,18 +483,6 @@ FN_MAX_LIMIT_LIST
return extensions.workgroup_memory_explicit_layout; return extensions.workgroup_memory_explicit_layout;
} }
/// Returns true if the device supports 8-bit accesses to workgroup explicit layout memory.
bool IsWorkgroupMemoryExplicitLayout8BitSupported() const {
return extensions.workgroup_memory_explicit_layout &&
features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout8BitAccess;
}
/// Returns true if the device supports 16-bit accesses to workgroup explicit layout memory.
bool IsWorkgroupMemoryExplicitLayout16BitSupported() const {
return extensions.workgroup_memory_explicit_layout && features.features.shaderInt16 &&
features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout16BitAccess;
}
/// Returns true if the device supports VK_KHR_image_format_list. /// Returns true if the device supports VK_KHR_image_format_list.
bool IsKhrImageFormatListSupported() const { bool IsKhrImageFormatListSupported() const {
return extensions.image_format_list || instance_version >= VK_API_VERSION_1_2; return extensions.image_format_list || instance_version >= VK_API_VERSION_1_2;
@@ -728,16 +711,9 @@ FN_MAX_LIMIT_LIST
features.provoking_vertex.transformFeedbackPreservesProvokingVertex; features.provoking_vertex.transformFeedbackPreservesProvokingVertex;
} }
/// Returns true if the device supports int64 atomics on storage buffers. /// Returns true if the device supports VK_KHR_shader_atomic_int64.
bool IsExtShaderAtomicInt64Supported() const { bool IsExtShaderAtomicInt64Supported() const {
return extensions.shader_atomic_int64 && return extensions.shader_atomic_int64;
features.shader_atomic_int64.shaderBufferInt64Atomics;
}
/// Returns true if the device supports int64 atomics on workgroup (shared) memory.
bool IsSharedInt64AtomicsSupported() const {
return extensions.shader_atomic_int64 &&
features.shader_atomic_int64.shaderSharedInt64Atomics;
} }
bool IsExtConditionalRendering() const { bool IsExtConditionalRendering() const {
-1
View File
@@ -371,7 +371,6 @@ 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>());