Compare commits
16 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc2559ef84 | ||
|
|
cd542d5aac | ||
|
|
c425a1a857 | ||
|
|
86b55cb6df | ||
|
|
8135f4bfce | ||
|
|
c440ecfafe | ||
|
|
48d9d66dc5 | ||
|
|
444231a83d | ||
|
|
c1accfefde | ||
|
|
27e5efd265 | ||
|
|
f5ec165e8c | ||
|
|
edd668047c | ||
|
|
1ddcd0e6f0 | ||
|
|
a6a73d8892 | ||
|
|
889c646ac0 | ||
|
|
d62b0a9e29 |
@@ -37,7 +37,7 @@ Stream::Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callbac
|
||||
: sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)},
|
||||
sink_stream{sink_stream}, name{std::move(name_)} {
|
||||
|
||||
release_event = CoreTiming::RegisterEvent(
|
||||
release_event = Core::Timing::RegisterEvent(
|
||||
name, [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ Stream::State Stream::GetState() const {
|
||||
|
||||
s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
|
||||
const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
|
||||
return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
|
||||
return Core::Timing::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
|
||||
}
|
||||
|
||||
static void VolumeAdjustSamples(std::vector<s16>& samples) {
|
||||
@@ -99,7 +99,8 @@ void Stream::PlayNextBuffer() {
|
||||
|
||||
sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
|
||||
|
||||
CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
|
||||
Core::Timing::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event,
|
||||
{});
|
||||
}
|
||||
|
||||
void Stream::ReleaseActiveBuffer() {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "audio_core/buffer.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace CoreTiming {
|
||||
namespace Core::Timing {
|
||||
struct EventType;
|
||||
}
|
||||
|
||||
@@ -91,16 +91,16 @@ private:
|
||||
/// Gets the number of core cycles when the specified buffer will be released
|
||||
s64 GetBufferReleaseCycles(const Buffer& buffer) const;
|
||||
|
||||
u32 sample_rate; ///< Sample rate of the stream
|
||||
Format format; ///< Format of the stream
|
||||
ReleaseCallback release_callback; ///< Buffer release callback for the stream
|
||||
State state{State::Stopped}; ///< Playback state of the stream
|
||||
CoreTiming::EventType* release_event{}; ///< Core timing release event for the stream
|
||||
BufferPtr active_buffer; ///< Actively playing buffer in the stream
|
||||
std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream
|
||||
std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
|
||||
SinkStream& sink_stream; ///< Output sink for the stream
|
||||
std::string name; ///< Name of the stream, must be unique
|
||||
u32 sample_rate; ///< Sample rate of the stream
|
||||
Format format; ///< Format of the stream
|
||||
ReleaseCallback release_callback; ///< Buffer release callback for the stream
|
||||
State state{State::Stopped}; ///< Playback state of the stream
|
||||
Core::Timing::EventType* release_event{}; ///< Core timing release event for the stream
|
||||
BufferPtr active_buffer; ///< Actively playing buffer in the stream
|
||||
std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream
|
||||
std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
|
||||
SinkStream& sink_stream; ///< Output sink for the stream
|
||||
std::string name; ///< Name of the stream, must be unique
|
||||
};
|
||||
|
||||
using StreamPtr = std::shared_ptr<Stream>;
|
||||
|
||||
@@ -112,14 +112,14 @@ public:
|
||||
// Always execute at least one tick.
|
||||
amortized_ticks = std::max<u64>(amortized_ticks, 1);
|
||||
|
||||
CoreTiming::AddTicks(amortized_ticks);
|
||||
Timing::AddTicks(amortized_ticks);
|
||||
num_interpreted_instructions = 0;
|
||||
}
|
||||
u64 GetTicksRemaining() override {
|
||||
return std::max(CoreTiming::GetDowncount(), 0);
|
||||
return std::max(Timing::GetDowncount(), 0);
|
||||
}
|
||||
u64 GetCNTPCT() override {
|
||||
return CoreTiming::GetTicks();
|
||||
return Timing::GetTicks();
|
||||
}
|
||||
|
||||
ARM_Dynarmic& parent;
|
||||
|
||||
@@ -177,7 +177,7 @@ void ARM_Unicorn::Run() {
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
ExecuteInstructions(std::max(4000000, 0));
|
||||
} else {
|
||||
ExecuteInstructions(std::max(CoreTiming::GetDowncount(), 0));
|
||||
ExecuteInstructions(std::max(Timing::GetDowncount(), 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64));
|
||||
void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
|
||||
MICROPROFILE_SCOPE(ARM_Jit_Unicorn);
|
||||
CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions));
|
||||
CoreTiming::AddTicks(num_instructions);
|
||||
Timing::AddTicks(num_instructions);
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
if (last_bkpt_hit) {
|
||||
uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address);
|
||||
|
||||
@@ -94,7 +94,7 @@ struct System::Impl {
|
||||
ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
|
||||
LOG_DEBUG(HW_Memory, "initialized OK");
|
||||
|
||||
CoreTiming::Init();
|
||||
Timing::Init();
|
||||
kernel.Initialize();
|
||||
|
||||
const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
|
||||
@@ -205,7 +205,7 @@ struct System::Impl {
|
||||
|
||||
// Shutdown kernel and core timing
|
||||
kernel.Shutdown();
|
||||
CoreTiming::Shutdown();
|
||||
Timing::Shutdown();
|
||||
|
||||
// Close app loader
|
||||
app_loader.reset();
|
||||
@@ -232,7 +232,7 @@ struct System::Impl {
|
||||
}
|
||||
|
||||
PerfStatsResults GetAndResetPerfStats() {
|
||||
return perf_stats.GetAndResetStats(CoreTiming::GetGlobalTimeUs());
|
||||
return perf_stats.GetAndResetStats(Timing::GetGlobalTimeUs());
|
||||
}
|
||||
|
||||
Kernel::KernelCore kernel;
|
||||
|
||||
@@ -93,14 +93,14 @@ void Cpu::RunLoop(bool tight_loop) {
|
||||
|
||||
if (IsMainCore()) {
|
||||
// TODO(Subv): Only let CoreTiming idle if all 4 cores are idling.
|
||||
CoreTiming::Idle();
|
||||
CoreTiming::Advance();
|
||||
Timing::Idle();
|
||||
Timing::Advance();
|
||||
}
|
||||
|
||||
PrepareReschedule();
|
||||
} else {
|
||||
if (IsMainCore()) {
|
||||
CoreTiming::Advance();
|
||||
Timing::Advance();
|
||||
}
|
||||
|
||||
if (tight_loop) {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "core/core_timing_util.h"
|
||||
|
||||
namespace CoreTiming {
|
||||
namespace Core::Timing {
|
||||
|
||||
static s64 global_timer;
|
||||
static int slice_length;
|
||||
@@ -242,4 +242,4 @@ int GetDowncount() {
|
||||
return downcount;
|
||||
}
|
||||
|
||||
} // namespace CoreTiming
|
||||
} // namespace Core::Timing
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace CoreTiming {
|
||||
namespace Core::Timing {
|
||||
|
||||
struct EventType;
|
||||
|
||||
@@ -92,4 +92,4 @@ std::chrono::microseconds GetGlobalTimeUs();
|
||||
|
||||
int GetDowncount();
|
||||
|
||||
} // namespace CoreTiming
|
||||
} // namespace Core::Timing
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <limits>
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace CoreTiming {
|
||||
namespace Core::Timing {
|
||||
|
||||
constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits<s64>::max() / BASE_CLOCK_RATE;
|
||||
|
||||
@@ -60,4 +60,4 @@ s64 nsToCycles(u64 ns) {
|
||||
return (BASE_CLOCK_RATE * static_cast<s64>(ns)) / 1000000000;
|
||||
}
|
||||
|
||||
} // namespace CoreTiming
|
||||
} // namespace Core::Timing
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace CoreTiming {
|
||||
namespace Core::Timing {
|
||||
|
||||
// The below clock rate is based on Switch's clockspeed being widely known as 1.020GHz
|
||||
// The exact value used is of course unverified.
|
||||
@@ -61,4 +61,4 @@ inline u64 cyclesToMs(s64 cycles) {
|
||||
return cycles * 1000 / BASE_CLOCK_RATE;
|
||||
}
|
||||
|
||||
} // namespace CoreTiming
|
||||
} // namespace Core::Timing
|
||||
|
||||
@@ -37,10 +37,10 @@ void CpuCoreManager::Initialize(System& system) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (std::size_t index = 1; index < core_threads.size(); index++) {
|
||||
for (std::size_t index = 0; index < core_threads.size(); ++index) {
|
||||
core_threads[index] = std::make_unique<std::thread>(RunCpuCore, std::cref(system),
|
||||
std::ref(*cores[index]));
|
||||
thread_to_cpu[core_threads[index]->get_id()] = cores[index].get();
|
||||
std::ref(*cores[index + 1]));
|
||||
thread_to_cpu[core_threads[index]->get_id()] = cores[index + 1].get();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ struct KernelCore::Impl {
|
||||
|
||||
void InitializeThreads() {
|
||||
thread_wakeup_event_type =
|
||||
CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
|
||||
Core::Timing::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
|
||||
}
|
||||
|
||||
std::atomic<u32> next_object_id{0};
|
||||
@@ -137,7 +137,7 @@ struct KernelCore::Impl {
|
||||
|
||||
SharedPtr<ResourceLimit> system_resource_limit;
|
||||
|
||||
CoreTiming::EventType* thread_wakeup_event_type = nullptr;
|
||||
Core::Timing::EventType* thread_wakeup_event_type = nullptr;
|
||||
// TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future,
|
||||
// allowing us to simply use a pool index or similar.
|
||||
Kernel::HandleTable thread_wakeup_callback_handle_table;
|
||||
@@ -213,7 +213,7 @@ u64 KernelCore::CreateNewProcessID() {
|
||||
return impl->next_process_id++;
|
||||
}
|
||||
|
||||
CoreTiming::EventType* KernelCore::ThreadWakeupCallbackEventType() const {
|
||||
Core::Timing::EventType* KernelCore::ThreadWakeupCallbackEventType() const {
|
||||
return impl->thread_wakeup_event_type;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
template <typename T>
|
||||
class ResultVal;
|
||||
|
||||
namespace CoreTiming {
|
||||
namespace Core::Timing {
|
||||
struct EventType;
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ private:
|
||||
u64 CreateNewThreadID();
|
||||
|
||||
/// Retrieves the event type used for thread wakeup callbacks.
|
||||
CoreTiming::EventType* ThreadWakeupCallbackEventType() const;
|
||||
Core::Timing::EventType* ThreadWakeupCallbackEventType() const;
|
||||
|
||||
/// Provides a reference to the thread wakeup callback handle table.
|
||||
Kernel::HandleTable& ThreadWakeupCallbackHandleTable();
|
||||
|
||||
@@ -111,7 +111,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
|
||||
|
||||
void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
|
||||
const u64 prev_switch_ticks = last_context_switch_time;
|
||||
const u64 most_recent_switch_ticks = CoreTiming::GetTicks();
|
||||
const u64 most_recent_switch_ticks = Core::Timing::GetTicks();
|
||||
const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
|
||||
|
||||
if (thread != nullptr) {
|
||||
|
||||
@@ -927,9 +927,9 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
|
||||
const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks();
|
||||
|
||||
out_ticks = thread_ticks + (CoreTiming::GetTicks() - prev_ctx_ticks);
|
||||
out_ticks = thread_ticks + (Core::Timing::GetTicks() - prev_ctx_ticks);
|
||||
} else if (same_thread && info_sub_id == system.CurrentCoreIndex()) {
|
||||
out_ticks = CoreTiming::GetTicks() - prev_ctx_ticks;
|
||||
out_ticks = Core::Timing::GetTicks() - prev_ctx_ticks;
|
||||
}
|
||||
|
||||
*result = out_ticks;
|
||||
@@ -1546,10 +1546,10 @@ static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to
|
||||
static u64 GetSystemTick() {
|
||||
LOG_TRACE(Kernel_SVC, "called");
|
||||
|
||||
const u64 result{CoreTiming::GetTicks()};
|
||||
const u64 result{Core::Timing::GetTicks()};
|
||||
|
||||
// Advance time to defeat dumb games that busy-wait for the frame to end.
|
||||
CoreTiming::AddTicks(400);
|
||||
Core::Timing::AddTicks(400);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ Thread::~Thread() = default;
|
||||
|
||||
void Thread::Stop() {
|
||||
// Cancel any outstanding wakeup events for this thread
|
||||
CoreTiming::UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), callback_handle);
|
||||
Core::Timing::UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), callback_handle);
|
||||
kernel.ThreadWakeupCallbackHandleTable().Close(callback_handle);
|
||||
callback_handle = 0;
|
||||
|
||||
@@ -85,12 +85,13 @@ void Thread::WakeAfterDelay(s64 nanoseconds) {
|
||||
|
||||
// This function might be called from any thread so we have to be cautious and use the
|
||||
// thread-safe version of ScheduleEvent.
|
||||
CoreTiming::ScheduleEventThreadsafe(CoreTiming::nsToCycles(nanoseconds),
|
||||
kernel.ThreadWakeupCallbackEventType(), callback_handle);
|
||||
Core::Timing::ScheduleEventThreadsafe(Core::Timing::nsToCycles(nanoseconds),
|
||||
kernel.ThreadWakeupCallbackEventType(), callback_handle);
|
||||
}
|
||||
|
||||
void Thread::CancelWakeupTimer() {
|
||||
CoreTiming::UnscheduleEventThreadsafe(kernel.ThreadWakeupCallbackEventType(), callback_handle);
|
||||
Core::Timing::UnscheduleEventThreadsafe(kernel.ThreadWakeupCallbackEventType(),
|
||||
callback_handle);
|
||||
}
|
||||
|
||||
static std::optional<s32> GetNextProcessorId(u64 mask) {
|
||||
@@ -197,7 +198,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
|
||||
thread->stack_top = stack_top;
|
||||
thread->tpidr_el0 = 0;
|
||||
thread->nominal_priority = thread->current_priority = priority;
|
||||
thread->last_running_ticks = CoreTiming::GetTicks();
|
||||
thread->last_running_ticks = Core::Timing::GetTicks();
|
||||
thread->processor_id = processor_id;
|
||||
thread->ideal_core = processor_id;
|
||||
thread->affinity_mask = 1ULL << processor_id;
|
||||
@@ -257,7 +258,7 @@ void Thread::SetStatus(ThreadStatus new_status) {
|
||||
}
|
||||
|
||||
if (status == ThreadStatus::Running) {
|
||||
last_running_ticks = CoreTiming::GetTicks();
|
||||
last_running_ticks = Core::Timing::GetTicks();
|
||||
}
|
||||
|
||||
status = new_status;
|
||||
|
||||
@@ -22,7 +22,7 @@ void Controller_DebugPad::OnInit() {}
|
||||
void Controller_DebugPad::OnRelease() {}
|
||||
|
||||
void Controller_DebugPad::OnUpdate(u8* data, std::size_t size) {
|
||||
shared_memory.header.timestamp = CoreTiming::GetTicks();
|
||||
shared_memory.header.timestamp = Core::Timing::GetTicks();
|
||||
shared_memory.header.total_entry_count = 17;
|
||||
|
||||
if (!IsControllerActivated()) {
|
||||
|
||||
@@ -18,7 +18,7 @@ void Controller_Gesture::OnInit() {}
|
||||
void Controller_Gesture::OnRelease() {}
|
||||
|
||||
void Controller_Gesture::OnUpdate(u8* data, std::size_t size) {
|
||||
shared_memory.header.timestamp = CoreTiming::GetTicks();
|
||||
shared_memory.header.timestamp = Core::Timing::GetTicks();
|
||||
shared_memory.header.total_entry_count = 17;
|
||||
|
||||
if (!IsControllerActivated()) {
|
||||
|
||||
@@ -20,7 +20,7 @@ void Controller_Keyboard::OnInit() {}
|
||||
void Controller_Keyboard::OnRelease() {}
|
||||
|
||||
void Controller_Keyboard::OnUpdate(u8* data, std::size_t size) {
|
||||
shared_memory.header.timestamp = CoreTiming::GetTicks();
|
||||
shared_memory.header.timestamp = Core::Timing::GetTicks();
|
||||
shared_memory.header.total_entry_count = 17;
|
||||
|
||||
if (!IsControllerActivated()) {
|
||||
|
||||
@@ -18,7 +18,7 @@ void Controller_Mouse::OnInit() {}
|
||||
void Controller_Mouse::OnRelease() {}
|
||||
|
||||
void Controller_Mouse::OnUpdate(u8* data, std::size_t size) {
|
||||
shared_memory.header.timestamp = CoreTiming::GetTicks();
|
||||
shared_memory.header.timestamp = Core::Timing::GetTicks();
|
||||
shared_memory.header.total_entry_count = 17;
|
||||
|
||||
if (!IsControllerActivated()) {
|
||||
|
||||
@@ -308,7 +308,7 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
|
||||
const auto& last_entry =
|
||||
main_controller->npad[main_controller->common.last_entry_index];
|
||||
|
||||
main_controller->common.timestamp = CoreTiming::GetTicks();
|
||||
main_controller->common.timestamp = Core::Timing::GetTicks();
|
||||
main_controller->common.last_entry_index =
|
||||
(main_controller->common.last_entry_index + 1) % 17;
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ void Controller_Stubbed::OnUpdate(u8* data, std::size_t size) {
|
||||
}
|
||||
|
||||
CommonHeader header{};
|
||||
header.timestamp = CoreTiming::GetTicks();
|
||||
header.timestamp = Core::Timing::GetTicks();
|
||||
header.total_entry_count = 17;
|
||||
header.entry_count = 0;
|
||||
header.last_entry_index = 0;
|
||||
|
||||
@@ -21,7 +21,7 @@ void Controller_Touchscreen::OnInit() {}
|
||||
void Controller_Touchscreen::OnRelease() {}
|
||||
|
||||
void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
|
||||
shared_memory.header.timestamp = CoreTiming::GetTicks();
|
||||
shared_memory.header.timestamp = Core::Timing::GetTicks();
|
||||
shared_memory.header.total_entry_count = 17;
|
||||
|
||||
if (!IsControllerActivated()) {
|
||||
@@ -48,7 +48,7 @@ void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
|
||||
touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
|
||||
touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
|
||||
touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
|
||||
const u64 tick = CoreTiming::GetTicks();
|
||||
const u64 tick = Core::Timing::GetTicks();
|
||||
touch_entry.delta_time = tick - last_touch;
|
||||
last_touch = tick;
|
||||
touch_entry.finger = Settings::values.touchscreen.finger;
|
||||
|
||||
@@ -19,7 +19,7 @@ void Controller_XPad::OnRelease() {}
|
||||
|
||||
void Controller_XPad::OnUpdate(u8* data, std::size_t size) {
|
||||
for (auto& xpad_entry : shared_memory.shared_memory_entries) {
|
||||
xpad_entry.header.timestamp = CoreTiming::GetTicks();
|
||||
xpad_entry.header.timestamp = Core::Timing::GetTicks();
|
||||
xpad_entry.header.total_entry_count = 17;
|
||||
|
||||
if (!IsControllerActivated()) {
|
||||
|
||||
@@ -36,9 +36,9 @@ namespace Service::HID {
|
||||
|
||||
// Updating period for each HID device.
|
||||
// TODO(ogniK): Find actual polling rate of hid
|
||||
constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 66;
|
||||
constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
|
||||
constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
|
||||
constexpr u64 pad_update_ticks = Core::Timing::BASE_CLOCK_RATE / 66;
|
||||
constexpr u64 accelerometer_update_ticks = Core::Timing::BASE_CLOCK_RATE / 100;
|
||||
constexpr u64 gyroscope_update_ticks = Core::Timing::BASE_CLOCK_RATE / 100;
|
||||
constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
|
||||
|
||||
IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") {
|
||||
@@ -73,14 +73,13 @@ IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") {
|
||||
GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000);
|
||||
|
||||
// Register update callbacks
|
||||
pad_update_event =
|
||||
CoreTiming::RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, int cycles_late) {
|
||||
UpdateControllers(userdata, cycles_late);
|
||||
});
|
||||
pad_update_event = Core::Timing::RegisterEvent(
|
||||
"HID::UpdatePadCallback",
|
||||
[this](u64 userdata, int cycles_late) { UpdateControllers(userdata, cycles_late); });
|
||||
|
||||
// TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
|
||||
|
||||
CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event);
|
||||
Core::Timing::ScheduleEvent(pad_update_ticks, pad_update_event);
|
||||
|
||||
ReloadInputDevices();
|
||||
}
|
||||
@@ -94,7 +93,7 @@ void IAppletResource::DeactivateController(HidController controller) {
|
||||
}
|
||||
|
||||
IAppletResource ::~IAppletResource() {
|
||||
CoreTiming::UnscheduleEvent(pad_update_event, 0);
|
||||
Core::Timing::UnscheduleEvent(pad_update_event, 0);
|
||||
}
|
||||
|
||||
void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
|
||||
@@ -114,7 +113,7 @@ void IAppletResource::UpdateControllers(u64 userdata, int cycles_late) {
|
||||
controller->OnUpdate(shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
|
||||
}
|
||||
|
||||
CoreTiming::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event);
|
||||
Core::Timing::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event);
|
||||
}
|
||||
|
||||
class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "controllers/controller_base.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace CoreTiming {
|
||||
namespace Core::Timing {
|
||||
struct EventType;
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ private:
|
||||
|
||||
Kernel::SharedPtr<Kernel::SharedMemory> shared_mem;
|
||||
|
||||
CoreTiming::EventType* pad_update_event;
|
||||
Core::Timing::EventType* pad_update_event;
|
||||
|
||||
std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
|
||||
controllers{};
|
||||
|
||||
@@ -98,7 +98,7 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 5};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<u64>(CoreTiming::GetTicks());
|
||||
rb.PushRaw<u64>(Core::Timing::GetTicks());
|
||||
rb.PushRaw<u32>(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -184,7 +184,7 @@ u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& o
|
||||
|
||||
IoctlGetGpuTime params{};
|
||||
std::memcpy(¶ms, input.data(), input.size());
|
||||
params.gpu_time = CoreTiming::cyclesToNs(CoreTiming::GetTicks());
|
||||
params.gpu_time = Core::Timing::cyclesToNs(Core::Timing::GetTicks());
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -13,10 +13,6 @@
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
|
||||
namespace CoreTiming {
|
||||
struct EventType;
|
||||
}
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
|
||||
struct IGBPBuffer {
|
||||
|
||||
@@ -25,21 +25,21 @@
|
||||
namespace Service::NVFlinger {
|
||||
|
||||
constexpr std::size_t SCREEN_REFRESH_RATE = 60;
|
||||
constexpr u64 frame_ticks = static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE);
|
||||
constexpr u64 frame_ticks = static_cast<u64>(Core::Timing::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE);
|
||||
|
||||
NVFlinger::NVFlinger() {
|
||||
// Schedule the screen composition events
|
||||
composition_event =
|
||||
CoreTiming::RegisterEvent("ScreenComposition", [this](u64 userdata, int cycles_late) {
|
||||
Core::Timing::RegisterEvent("ScreenComposition", [this](u64 userdata, int cycles_late) {
|
||||
Compose();
|
||||
CoreTiming::ScheduleEvent(frame_ticks - cycles_late, composition_event);
|
||||
Core::Timing::ScheduleEvent(frame_ticks - cycles_late, composition_event);
|
||||
});
|
||||
|
||||
CoreTiming::ScheduleEvent(frame_ticks, composition_event);
|
||||
Core::Timing::ScheduleEvent(frame_ticks, composition_event);
|
||||
}
|
||||
|
||||
NVFlinger::~NVFlinger() {
|
||||
CoreTiming::UnscheduleEvent(composition_event, 0);
|
||||
Core::Timing::UnscheduleEvent(composition_event, 0);
|
||||
}
|
||||
|
||||
void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
namespace CoreTiming {
|
||||
namespace Core::Timing {
|
||||
struct EventType;
|
||||
}
|
||||
|
||||
@@ -115,8 +115,8 @@ private:
|
||||
/// layers.
|
||||
u32 next_buffer_queue_id = 1;
|
||||
|
||||
/// CoreTiming event that handles screen composition.
|
||||
CoreTiming::EventType* composition_event;
|
||||
/// Event that handles screen composition.
|
||||
Core::Timing::EventType* composition_event;
|
||||
};
|
||||
|
||||
} // namespace Service::NVFlinger
|
||||
|
||||
@@ -106,8 +106,8 @@ private:
|
||||
void GetCurrentTimePoint(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called");
|
||||
|
||||
SteadyClockTimePoint steady_clock_time_point{
|
||||
CoreTiming::cyclesToMs(CoreTiming::GetTicks()) / 1000};
|
||||
const SteadyClockTimePoint steady_clock_time_point{
|
||||
Core::Timing::cyclesToMs(Core::Timing::GetTicks()) / 1000};
|
||||
IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw(steady_clock_time_point);
|
||||
@@ -282,7 +282,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
const SteadyClockTimePoint steady_clock_time_point{
|
||||
CoreTiming::cyclesToMs(CoreTiming::GetTicks()) / 1000, {}};
|
||||
Core::Timing::cyclesToMs(Core::Timing::GetTicks()) / 1000, {}};
|
||||
|
||||
CalendarTime calendar_time{};
|
||||
calendar_time.year = tm->tm_year + 1900;
|
||||
|
||||
@@ -31,10 +31,10 @@ void CallbackTemplate(u64 userdata, s64 cycles_late) {
|
||||
class ScopeInit final {
|
||||
public:
|
||||
ScopeInit() {
|
||||
CoreTiming::Init();
|
||||
Core::Timing::Init();
|
||||
}
|
||||
~ScopeInit() {
|
||||
CoreTiming::Shutdown();
|
||||
Core::Timing::Shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -44,37 +44,37 @@ static void AdvanceAndCheck(u32 idx, int downcount, int expected_lateness = 0,
|
||||
expected_callback = CB_IDS[idx];
|
||||
lateness = expected_lateness;
|
||||
|
||||
CoreTiming::AddTicks(CoreTiming::GetDowncount() -
|
||||
cpu_downcount); // Pretend we executed X cycles of instructions.
|
||||
CoreTiming::Advance();
|
||||
// Pretend we executed X cycles of instructions.
|
||||
Core::Timing::AddTicks(Core::Timing::GetDowncount() - cpu_downcount);
|
||||
Core::Timing::Advance();
|
||||
|
||||
REQUIRE(decltype(callbacks_ran_flags)().set(idx) == callbacks_ran_flags);
|
||||
REQUIRE(downcount == CoreTiming::GetDowncount());
|
||||
REQUIRE(downcount == Core::Timing::GetDowncount());
|
||||
}
|
||||
|
||||
TEST_CASE("CoreTiming[BasicOrder]", "[core]") {
|
||||
ScopeInit guard;
|
||||
|
||||
CoreTiming::EventType* cb_a = CoreTiming::RegisterEvent("callbackA", CallbackTemplate<0>);
|
||||
CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", CallbackTemplate<1>);
|
||||
CoreTiming::EventType* cb_c = CoreTiming::RegisterEvent("callbackC", CallbackTemplate<2>);
|
||||
CoreTiming::EventType* cb_d = CoreTiming::RegisterEvent("callbackD", CallbackTemplate<3>);
|
||||
CoreTiming::EventType* cb_e = CoreTiming::RegisterEvent("callbackE", CallbackTemplate<4>);
|
||||
Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", CallbackTemplate<0>);
|
||||
Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", CallbackTemplate<1>);
|
||||
Core::Timing::EventType* cb_c = Core::Timing::RegisterEvent("callbackC", CallbackTemplate<2>);
|
||||
Core::Timing::EventType* cb_d = Core::Timing::RegisterEvent("callbackD", CallbackTemplate<3>);
|
||||
Core::Timing::EventType* cb_e = Core::Timing::RegisterEvent("callbackE", CallbackTemplate<4>);
|
||||
|
||||
// Enter slice 0
|
||||
CoreTiming::Advance();
|
||||
Core::Timing::Advance();
|
||||
|
||||
// D -> B -> C -> A -> E
|
||||
CoreTiming::ScheduleEvent(1000, cb_a, CB_IDS[0]);
|
||||
REQUIRE(1000 == CoreTiming::GetDowncount());
|
||||
CoreTiming::ScheduleEvent(500, cb_b, CB_IDS[1]);
|
||||
REQUIRE(500 == CoreTiming::GetDowncount());
|
||||
CoreTiming::ScheduleEvent(800, cb_c, CB_IDS[2]);
|
||||
REQUIRE(500 == CoreTiming::GetDowncount());
|
||||
CoreTiming::ScheduleEvent(100, cb_d, CB_IDS[3]);
|
||||
REQUIRE(100 == CoreTiming::GetDowncount());
|
||||
CoreTiming::ScheduleEvent(1200, cb_e, CB_IDS[4]);
|
||||
REQUIRE(100 == CoreTiming::GetDowncount());
|
||||
Core::Timing::ScheduleEvent(1000, cb_a, CB_IDS[0]);
|
||||
REQUIRE(1000 == Core::Timing::GetDowncount());
|
||||
Core::Timing::ScheduleEvent(500, cb_b, CB_IDS[1]);
|
||||
REQUIRE(500 == Core::Timing::GetDowncount());
|
||||
Core::Timing::ScheduleEvent(800, cb_c, CB_IDS[2]);
|
||||
REQUIRE(500 == Core::Timing::GetDowncount());
|
||||
Core::Timing::ScheduleEvent(100, cb_d, CB_IDS[3]);
|
||||
REQUIRE(100 == Core::Timing::GetDowncount());
|
||||
Core::Timing::ScheduleEvent(1200, cb_e, CB_IDS[4]);
|
||||
REQUIRE(100 == Core::Timing::GetDowncount());
|
||||
|
||||
AdvanceAndCheck(3, 400);
|
||||
AdvanceAndCheck(1, 300);
|
||||
@@ -86,36 +86,36 @@ TEST_CASE("CoreTiming[BasicOrder]", "[core]") {
|
||||
TEST_CASE("CoreTiming[Threadsave]", "[core]") {
|
||||
ScopeInit guard;
|
||||
|
||||
CoreTiming::EventType* cb_a = CoreTiming::RegisterEvent("callbackA", CallbackTemplate<0>);
|
||||
CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", CallbackTemplate<1>);
|
||||
CoreTiming::EventType* cb_c = CoreTiming::RegisterEvent("callbackC", CallbackTemplate<2>);
|
||||
CoreTiming::EventType* cb_d = CoreTiming::RegisterEvent("callbackD", CallbackTemplate<3>);
|
||||
CoreTiming::EventType* cb_e = CoreTiming::RegisterEvent("callbackE", CallbackTemplate<4>);
|
||||
Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", CallbackTemplate<0>);
|
||||
Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", CallbackTemplate<1>);
|
||||
Core::Timing::EventType* cb_c = Core::Timing::RegisterEvent("callbackC", CallbackTemplate<2>);
|
||||
Core::Timing::EventType* cb_d = Core::Timing::RegisterEvent("callbackD", CallbackTemplate<3>);
|
||||
Core::Timing::EventType* cb_e = Core::Timing::RegisterEvent("callbackE", CallbackTemplate<4>);
|
||||
|
||||
// Enter slice 0
|
||||
CoreTiming::Advance();
|
||||
Core::Timing::Advance();
|
||||
|
||||
// D -> B -> C -> A -> E
|
||||
CoreTiming::ScheduleEventThreadsafe(1000, cb_a, CB_IDS[0]);
|
||||
Core::Timing::ScheduleEventThreadsafe(1000, cb_a, CB_IDS[0]);
|
||||
// Manually force since ScheduleEventThreadsafe doesn't call it
|
||||
CoreTiming::ForceExceptionCheck(1000);
|
||||
REQUIRE(1000 == CoreTiming::GetDowncount());
|
||||
CoreTiming::ScheduleEventThreadsafe(500, cb_b, CB_IDS[1]);
|
||||
Core::Timing::ForceExceptionCheck(1000);
|
||||
REQUIRE(1000 == Core::Timing::GetDowncount());
|
||||
Core::Timing::ScheduleEventThreadsafe(500, cb_b, CB_IDS[1]);
|
||||
// Manually force since ScheduleEventThreadsafe doesn't call it
|
||||
CoreTiming::ForceExceptionCheck(500);
|
||||
REQUIRE(500 == CoreTiming::GetDowncount());
|
||||
CoreTiming::ScheduleEventThreadsafe(800, cb_c, CB_IDS[2]);
|
||||
Core::Timing::ForceExceptionCheck(500);
|
||||
REQUIRE(500 == Core::Timing::GetDowncount());
|
||||
Core::Timing::ScheduleEventThreadsafe(800, cb_c, CB_IDS[2]);
|
||||
// Manually force since ScheduleEventThreadsafe doesn't call it
|
||||
CoreTiming::ForceExceptionCheck(800);
|
||||
REQUIRE(500 == CoreTiming::GetDowncount());
|
||||
CoreTiming::ScheduleEventThreadsafe(100, cb_d, CB_IDS[3]);
|
||||
Core::Timing::ForceExceptionCheck(800);
|
||||
REQUIRE(500 == Core::Timing::GetDowncount());
|
||||
Core::Timing::ScheduleEventThreadsafe(100, cb_d, CB_IDS[3]);
|
||||
// Manually force since ScheduleEventThreadsafe doesn't call it
|
||||
CoreTiming::ForceExceptionCheck(100);
|
||||
REQUIRE(100 == CoreTiming::GetDowncount());
|
||||
CoreTiming::ScheduleEventThreadsafe(1200, cb_e, CB_IDS[4]);
|
||||
Core::Timing::ForceExceptionCheck(100);
|
||||
REQUIRE(100 == Core::Timing::GetDowncount());
|
||||
Core::Timing::ScheduleEventThreadsafe(1200, cb_e, CB_IDS[4]);
|
||||
// Manually force since ScheduleEventThreadsafe doesn't call it
|
||||
CoreTiming::ForceExceptionCheck(1200);
|
||||
REQUIRE(100 == CoreTiming::GetDowncount());
|
||||
Core::Timing::ForceExceptionCheck(1200);
|
||||
REQUIRE(100 == Core::Timing::GetDowncount());
|
||||
|
||||
AdvanceAndCheck(3, 400);
|
||||
AdvanceAndCheck(1, 300);
|
||||
@@ -143,42 +143,42 @@ TEST_CASE("CoreTiming[SharedSlot]", "[core]") {
|
||||
|
||||
ScopeInit guard;
|
||||
|
||||
CoreTiming::EventType* cb_a = CoreTiming::RegisterEvent("callbackA", FifoCallback<0>);
|
||||
CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", FifoCallback<1>);
|
||||
CoreTiming::EventType* cb_c = CoreTiming::RegisterEvent("callbackC", FifoCallback<2>);
|
||||
CoreTiming::EventType* cb_d = CoreTiming::RegisterEvent("callbackD", FifoCallback<3>);
|
||||
CoreTiming::EventType* cb_e = CoreTiming::RegisterEvent("callbackE", FifoCallback<4>);
|
||||
Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", FifoCallback<0>);
|
||||
Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", FifoCallback<1>);
|
||||
Core::Timing::EventType* cb_c = Core::Timing::RegisterEvent("callbackC", FifoCallback<2>);
|
||||
Core::Timing::EventType* cb_d = Core::Timing::RegisterEvent("callbackD", FifoCallback<3>);
|
||||
Core::Timing::EventType* cb_e = Core::Timing::RegisterEvent("callbackE", FifoCallback<4>);
|
||||
|
||||
CoreTiming::ScheduleEvent(1000, cb_a, CB_IDS[0]);
|
||||
CoreTiming::ScheduleEvent(1000, cb_b, CB_IDS[1]);
|
||||
CoreTiming::ScheduleEvent(1000, cb_c, CB_IDS[2]);
|
||||
CoreTiming::ScheduleEvent(1000, cb_d, CB_IDS[3]);
|
||||
CoreTiming::ScheduleEvent(1000, cb_e, CB_IDS[4]);
|
||||
Core::Timing::ScheduleEvent(1000, cb_a, CB_IDS[0]);
|
||||
Core::Timing::ScheduleEvent(1000, cb_b, CB_IDS[1]);
|
||||
Core::Timing::ScheduleEvent(1000, cb_c, CB_IDS[2]);
|
||||
Core::Timing::ScheduleEvent(1000, cb_d, CB_IDS[3]);
|
||||
Core::Timing::ScheduleEvent(1000, cb_e, CB_IDS[4]);
|
||||
|
||||
// Enter slice 0
|
||||
CoreTiming::Advance();
|
||||
REQUIRE(1000 == CoreTiming::GetDowncount());
|
||||
Core::Timing::Advance();
|
||||
REQUIRE(1000 == Core::Timing::GetDowncount());
|
||||
|
||||
callbacks_ran_flags = 0;
|
||||
counter = 0;
|
||||
lateness = 0;
|
||||
CoreTiming::AddTicks(CoreTiming::GetDowncount());
|
||||
CoreTiming::Advance();
|
||||
REQUIRE(MAX_SLICE_LENGTH == CoreTiming::GetDowncount());
|
||||
Core::Timing::AddTicks(Core::Timing::GetDowncount());
|
||||
Core::Timing::Advance();
|
||||
REQUIRE(MAX_SLICE_LENGTH == Core::Timing::GetDowncount());
|
||||
REQUIRE(0x1FULL == callbacks_ran_flags.to_ullong());
|
||||
}
|
||||
|
||||
TEST_CASE("CoreTiming[PredictableLateness]", "[core]") {
|
||||
TEST_CASE("Core::Timing[PredictableLateness]", "[core]") {
|
||||
ScopeInit guard;
|
||||
|
||||
CoreTiming::EventType* cb_a = CoreTiming::RegisterEvent("callbackA", CallbackTemplate<0>);
|
||||
CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", CallbackTemplate<1>);
|
||||
Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", CallbackTemplate<0>);
|
||||
Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", CallbackTemplate<1>);
|
||||
|
||||
// Enter slice 0
|
||||
CoreTiming::Advance();
|
||||
Core::Timing::Advance();
|
||||
|
||||
CoreTiming::ScheduleEvent(100, cb_a, CB_IDS[0]);
|
||||
CoreTiming::ScheduleEvent(200, cb_b, CB_IDS[1]);
|
||||
Core::Timing::ScheduleEvent(100, cb_a, CB_IDS[0]);
|
||||
Core::Timing::ScheduleEvent(200, cb_b, CB_IDS[1]);
|
||||
|
||||
AdvanceAndCheck(0, 90, 10, -10); // (100 - 10)
|
||||
AdvanceAndCheck(1, MAX_SLICE_LENGTH, 50, -50);
|
||||
@@ -192,9 +192,10 @@ static void RescheduleCallback(u64 userdata, s64 cycles_late) {
|
||||
REQUIRE(reschedules >= 0);
|
||||
REQUIRE(lateness == cycles_late);
|
||||
|
||||
if (reschedules > 0)
|
||||
CoreTiming::ScheduleEvent(1000, reinterpret_cast<CoreTiming::EventType*>(userdata),
|
||||
userdata);
|
||||
if (reschedules > 0) {
|
||||
Core::Timing::ScheduleEvent(1000, reinterpret_cast<Core::Timing::EventType*>(userdata),
|
||||
userdata);
|
||||
}
|
||||
}
|
||||
} // namespace ChainSchedulingTest
|
||||
|
||||
@@ -203,35 +204,35 @@ TEST_CASE("CoreTiming[ChainScheduling]", "[core]") {
|
||||
|
||||
ScopeInit guard;
|
||||
|
||||
CoreTiming::EventType* cb_a = CoreTiming::RegisterEvent("callbackA", CallbackTemplate<0>);
|
||||
CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", CallbackTemplate<1>);
|
||||
CoreTiming::EventType* cb_c = CoreTiming::RegisterEvent("callbackC", CallbackTemplate<2>);
|
||||
CoreTiming::EventType* cb_rs =
|
||||
CoreTiming::RegisterEvent("callbackReschedule", RescheduleCallback);
|
||||
Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", CallbackTemplate<0>);
|
||||
Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", CallbackTemplate<1>);
|
||||
Core::Timing::EventType* cb_c = Core::Timing::RegisterEvent("callbackC", CallbackTemplate<2>);
|
||||
Core::Timing::EventType* cb_rs =
|
||||
Core::Timing::RegisterEvent("callbackReschedule", RescheduleCallback);
|
||||
|
||||
// Enter slice 0
|
||||
CoreTiming::Advance();
|
||||
Core::Timing::Advance();
|
||||
|
||||
CoreTiming::ScheduleEvent(800, cb_a, CB_IDS[0]);
|
||||
CoreTiming::ScheduleEvent(1000, cb_b, CB_IDS[1]);
|
||||
CoreTiming::ScheduleEvent(2200, cb_c, CB_IDS[2]);
|
||||
CoreTiming::ScheduleEvent(1000, cb_rs, reinterpret_cast<u64>(cb_rs));
|
||||
REQUIRE(800 == CoreTiming::GetDowncount());
|
||||
Core::Timing::ScheduleEvent(800, cb_a, CB_IDS[0]);
|
||||
Core::Timing::ScheduleEvent(1000, cb_b, CB_IDS[1]);
|
||||
Core::Timing::ScheduleEvent(2200, cb_c, CB_IDS[2]);
|
||||
Core::Timing::ScheduleEvent(1000, cb_rs, reinterpret_cast<u64>(cb_rs));
|
||||
REQUIRE(800 == Core::Timing::GetDowncount());
|
||||
|
||||
reschedules = 3;
|
||||
AdvanceAndCheck(0, 200); // cb_a
|
||||
AdvanceAndCheck(1, 1000); // cb_b, cb_rs
|
||||
REQUIRE(2 == reschedules);
|
||||
|
||||
CoreTiming::AddTicks(CoreTiming::GetDowncount());
|
||||
CoreTiming::Advance(); // cb_rs
|
||||
Core::Timing::AddTicks(Core::Timing::GetDowncount());
|
||||
Core::Timing::Advance(); // cb_rs
|
||||
REQUIRE(1 == reschedules);
|
||||
REQUIRE(200 == CoreTiming::GetDowncount());
|
||||
REQUIRE(200 == Core::Timing::GetDowncount());
|
||||
|
||||
AdvanceAndCheck(2, 800); // cb_c
|
||||
|
||||
CoreTiming::AddTicks(CoreTiming::GetDowncount());
|
||||
CoreTiming::Advance(); // cb_rs
|
||||
Core::Timing::AddTicks(Core::Timing::GetDowncount());
|
||||
Core::Timing::Advance(); // cb_rs
|
||||
REQUIRE(0 == reschedules);
|
||||
REQUIRE(MAX_SLICE_LENGTH == CoreTiming::GetDowncount());
|
||||
REQUIRE(MAX_SLICE_LENGTH == Core::Timing::GetDowncount());
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@ add_library(video_core STATIC
|
||||
debug_utils/debug_utils.h
|
||||
engines/fermi_2d.cpp
|
||||
engines/fermi_2d.h
|
||||
engines/kepler_compute.cpp
|
||||
engines/kepler_compute.h
|
||||
engines/kepler_memory.cpp
|
||||
engines/kepler_memory.h
|
||||
engines/maxwell_3d.cpp
|
||||
engines/maxwell_3d.h
|
||||
engines/maxwell_compute.cpp
|
||||
engines/maxwell_compute.h
|
||||
engines/maxwell_dma.cpp
|
||||
engines/maxwell_dma.h
|
||||
engines/shader_bytecode.h
|
||||
|
||||
34
src/video_core/engines/kepler_compute.cpp
Normal file
34
src/video_core/engines/kepler_compute.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/engines/kepler_compute.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
|
||||
namespace Tegra::Engines {
|
||||
|
||||
KeplerCompute::KeplerCompute(MemoryManager& memory_manager) : memory_manager{memory_manager} {}
|
||||
|
||||
KeplerCompute::~KeplerCompute() = default;
|
||||
|
||||
void KeplerCompute::CallMethod(const GPU::MethodCall& method_call) {
|
||||
ASSERT_MSG(method_call.method < Regs::NUM_REGS,
|
||||
"Invalid KeplerCompute register, increase the size of the Regs structure");
|
||||
|
||||
regs.reg_array[method_call.method] = method_call.argument;
|
||||
|
||||
switch (method_call.method) {
|
||||
case KEPLER_COMPUTE_REG_INDEX(launch):
|
||||
// Abort execution since compute shaders can be used to alter game memory (e.g. CUDA
|
||||
// kernels)
|
||||
UNREACHABLE_MSG("Compute shaders are not implemented");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Tegra::Engines
|
||||
@@ -10,47 +10,48 @@
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
|
||||
namespace Tegra::Engines {
|
||||
|
||||
#define MAXWELL_COMPUTE_REG_INDEX(field_name) \
|
||||
(offsetof(Tegra::Engines::MaxwellCompute::Regs, field_name) / sizeof(u32))
|
||||
#define KEPLER_COMPUTE_REG_INDEX(field_name) \
|
||||
(offsetof(Tegra::Engines::KeplerCompute::Regs, field_name) / sizeof(u32))
|
||||
|
||||
class MaxwellCompute final {
|
||||
class KeplerCompute final {
|
||||
public:
|
||||
MaxwellCompute() = default;
|
||||
~MaxwellCompute() = default;
|
||||
explicit KeplerCompute(MemoryManager& memory_manager);
|
||||
~KeplerCompute();
|
||||
|
||||
static constexpr std::size_t NumConstBuffers = 8;
|
||||
|
||||
struct Regs {
|
||||
static constexpr std::size_t NUM_REGS = 0xCF8;
|
||||
|
||||
union {
|
||||
struct {
|
||||
INSERT_PADDING_WORDS(0x281);
|
||||
INSERT_PADDING_WORDS(0xAF);
|
||||
|
||||
union {
|
||||
u32 compute_end;
|
||||
BitField<0, 1, u32> unknown;
|
||||
} compute;
|
||||
u32 launch;
|
||||
|
||||
INSERT_PADDING_WORDS(0xA76);
|
||||
INSERT_PADDING_WORDS(0xC48);
|
||||
};
|
||||
std::array<u32, NUM_REGS> reg_array;
|
||||
};
|
||||
} regs{};
|
||||
|
||||
static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32),
|
||||
"MaxwellCompute Regs has wrong size");
|
||||
"KeplerCompute Regs has wrong size");
|
||||
|
||||
MemoryManager& memory_manager;
|
||||
|
||||
/// Write the value to the register identified by method.
|
||||
void CallMethod(const GPU::MethodCall& method_call);
|
||||
};
|
||||
|
||||
#define ASSERT_REG_POSITION(field_name, position) \
|
||||
static_assert(offsetof(MaxwellCompute::Regs, field_name) == position * 4, \
|
||||
static_assert(offsetof(KeplerCompute::Regs, field_name) == position * 4, \
|
||||
"Field " #field_name " has invalid position")
|
||||
|
||||
ASSERT_REG_POSITION(compute, 0x281);
|
||||
ASSERT_REG_POSITION(launch, 0xAF);
|
||||
|
||||
#undef ASSERT_REG_POSITION
|
||||
|
||||
@@ -317,7 +317,7 @@ void Maxwell3D::ProcessQueryGet() {
|
||||
LongQueryResult query_result{};
|
||||
query_result.value = result;
|
||||
// TODO(Subv): Generate a real GPU timestamp and write it here instead of CoreTiming
|
||||
query_result.timestamp = CoreTiming::GetTicks();
|
||||
query_result.timestamp = Core::Timing::GetTicks();
|
||||
Memory::WriteBlock(*address, &query_result, sizeof(query_result));
|
||||
}
|
||||
dirty_flags.OnMemoryWrite();
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "video_core/engines/maxwell_compute.h"
|
||||
|
||||
namespace Tegra::Engines {
|
||||
|
||||
void MaxwellCompute::CallMethod(const GPU::MethodCall& method_call) {
|
||||
ASSERT_MSG(method_call.method < Regs::NUM_REGS,
|
||||
"Invalid MaxwellCompute register, increase the size of the Regs structure");
|
||||
|
||||
regs.reg_array[method_call.method] = method_call.argument;
|
||||
|
||||
switch (method_call.method) {
|
||||
case MAXWELL_COMPUTE_REG_INDEX(compute): {
|
||||
LOG_CRITICAL(HW_GPU, "Compute shaders are not implemented");
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Tegra::Engines
|
||||
@@ -186,7 +186,7 @@ enum class SubOp : u64 {
|
||||
};
|
||||
|
||||
enum class F2iRoundingOp : u64 {
|
||||
None = 0,
|
||||
RoundEven = 0,
|
||||
Floor = 1,
|
||||
Ceil = 2,
|
||||
Trunc = 3,
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
#include "core/core_timing.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/engines/kepler_compute.h"
|
||||
#include "video_core/engines/kepler_memory.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/engines/maxwell_compute.h"
|
||||
#include "video_core/engines/maxwell_dma.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
@@ -18,6 +18,7 @@ namespace Tegra {
|
||||
u32 FramebufferConfig::BytesPerPixel(PixelFormat format) {
|
||||
switch (format) {
|
||||
case PixelFormat::ABGR8:
|
||||
case PixelFormat::BGRA8:
|
||||
return 4;
|
||||
default:
|
||||
return 4;
|
||||
@@ -31,7 +32,7 @@ GPU::GPU(VideoCore::RasterizerInterface& rasterizer) {
|
||||
dma_pusher = std::make_unique<Tegra::DmaPusher>(*this);
|
||||
maxwell_3d = std::make_unique<Engines::Maxwell3D>(rasterizer, *memory_manager);
|
||||
fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer, *memory_manager);
|
||||
maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
|
||||
kepler_compute = std::make_unique<Engines::KeplerCompute>(*memory_manager);
|
||||
maxwell_dma = std::make_unique<Engines::MaxwellDMA>(rasterizer, *memory_manager);
|
||||
kepler_memory = std::make_unique<Engines::KeplerMemory>(rasterizer, *memory_manager);
|
||||
}
|
||||
@@ -245,8 +246,8 @@ void GPU::CallEngineMethod(const MethodCall& method_call) {
|
||||
case EngineID::MAXWELL_B:
|
||||
maxwell_3d->CallMethod(method_call);
|
||||
break;
|
||||
case EngineID::MAXWELL_COMPUTE_B:
|
||||
maxwell_compute->CallMethod(method_call);
|
||||
case EngineID::KEPLER_COMPUTE_B:
|
||||
kepler_compute->CallMethod(method_call);
|
||||
break;
|
||||
case EngineID::MAXWELL_DMA_COPY_A:
|
||||
maxwell_dma->CallMethod(method_call);
|
||||
@@ -282,7 +283,7 @@ void GPU::ProcessSemaphoreTriggerMethod() {
|
||||
block.sequence = regs.semaphore_sequence;
|
||||
// TODO(Kmather73): Generate a real GPU timestamp and write it here instead of
|
||||
// CoreTiming
|
||||
block.timestamp = CoreTiming::GetTicks();
|
||||
block.timestamp = Core::Timing::GetTicks();
|
||||
Memory::WriteBlock(*address, &block, sizeof(block));
|
||||
} else {
|
||||
const auto address =
|
||||
|
||||
@@ -80,6 +80,7 @@ class DebugContext;
|
||||
struct FramebufferConfig {
|
||||
enum class PixelFormat : u32 {
|
||||
ABGR8 = 1,
|
||||
BGRA8 = 5,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -102,15 +103,15 @@ struct FramebufferConfig {
|
||||
namespace Engines {
|
||||
class Fermi2D;
|
||||
class Maxwell3D;
|
||||
class MaxwellCompute;
|
||||
class MaxwellDMA;
|
||||
class KeplerCompute;
|
||||
class KeplerMemory;
|
||||
} // namespace Engines
|
||||
|
||||
enum class EngineID {
|
||||
FERMI_TWOD_A = 0x902D, // 2D Engine
|
||||
MAXWELL_B = 0xB197, // 3D Engine
|
||||
MAXWELL_COMPUTE_B = 0xB1C0,
|
||||
KEPLER_COMPUTE_B = 0xB1C0,
|
||||
KEPLER_INLINE_TO_MEMORY_B = 0xA140,
|
||||
MAXWELL_DMA_COPY_A = 0xB0B5,
|
||||
};
|
||||
@@ -208,7 +209,7 @@ private:
|
||||
/// 2D engine
|
||||
std::unique_ptr<Engines::Fermi2D> fermi_2d;
|
||||
/// Compute engine
|
||||
std::unique_ptr<Engines::MaxwellCompute> maxwell_compute;
|
||||
std::unique_ptr<Engines::KeplerCompute> kepler_compute;
|
||||
/// DMA engine
|
||||
std::unique_ptr<Engines::MaxwellDMA> maxwell_dma;
|
||||
/// Inline memory engine
|
||||
|
||||
@@ -719,45 +719,51 @@ private:
|
||||
constexpr std::array<const char*, 4> coord_constructors = {"float", "vec2", "vec3", "vec4"};
|
||||
|
||||
const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
|
||||
const auto count = static_cast<u32>(operation.GetOperandsCount());
|
||||
ASSERT(meta);
|
||||
|
||||
const auto count = static_cast<u32>(operation.GetOperandsCount());
|
||||
const bool has_array = meta->sampler.IsArray();
|
||||
const bool has_shadow = meta->sampler.IsShadow();
|
||||
|
||||
std::string expr = func;
|
||||
expr += '(';
|
||||
expr += GetSampler(meta->sampler);
|
||||
expr += ", ";
|
||||
|
||||
expr += coord_constructors[meta->coords_count - 1];
|
||||
expr += coord_constructors.at(count + (has_array ? 1 : 0) + (has_shadow ? 1 : 0) - 1);
|
||||
expr += '(';
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
const bool is_extra = i >= meta->coords_count;
|
||||
const bool is_array = i == meta->array_index;
|
||||
expr += Visit(operation[i]);
|
||||
|
||||
std::string operand = [&]() {
|
||||
if (is_extra && is_extra_int) {
|
||||
if (const auto immediate = std::get_if<ImmediateNode>(operation[i])) {
|
||||
return std::to_string(static_cast<s32>(immediate->GetValue()));
|
||||
} else {
|
||||
return "ftoi(" + Visit(operation[i]) + ')';
|
||||
}
|
||||
} else {
|
||||
return Visit(operation[i]);
|
||||
}
|
||||
}();
|
||||
if (is_array) {
|
||||
ASSERT(!is_extra);
|
||||
operand = "float(ftoi(" + operand + "))";
|
||||
}
|
||||
|
||||
expr += operand;
|
||||
|
||||
if (i + 1 == meta->coords_count) {
|
||||
expr += ')';
|
||||
}
|
||||
if (i + 1 < count) {
|
||||
const u32 next = i + 1;
|
||||
if (next < count || has_array || has_shadow)
|
||||
expr += ", ";
|
||||
}
|
||||
if (has_array) {
|
||||
expr += "float(ftoi(" + Visit(meta->array) + "))";
|
||||
}
|
||||
if (has_shadow) {
|
||||
if (has_array)
|
||||
expr += ", ";
|
||||
expr += Visit(meta->depth_compare);
|
||||
}
|
||||
expr += ')';
|
||||
|
||||
for (const Node extra : meta->extras) {
|
||||
expr += ", ";
|
||||
if (is_extra_int) {
|
||||
if (const auto immediate = std::get_if<ImmediateNode>(extra)) {
|
||||
// Inline the string as an immediate integer in GLSL (some extra arguments are
|
||||
// required to be constant)
|
||||
expr += std::to_string(static_cast<s32>(immediate->GetValue()));
|
||||
} else {
|
||||
expr += "ftoi(" + Visit(extra) + ')';
|
||||
}
|
||||
} else {
|
||||
expr += Visit(extra);
|
||||
}
|
||||
}
|
||||
|
||||
expr += ')';
|
||||
return expr;
|
||||
}
|
||||
@@ -1134,7 +1140,7 @@ private:
|
||||
Type::HalfFloat);
|
||||
}
|
||||
|
||||
std::string F4Texture(Operation operation) {
|
||||
std::string Texture(Operation operation) {
|
||||
const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
|
||||
ASSERT(meta);
|
||||
|
||||
@@ -1145,7 +1151,7 @@ private:
|
||||
return expr + GetSwizzle(meta->element);
|
||||
}
|
||||
|
||||
std::string F4TextureLod(Operation operation) {
|
||||
std::string TextureLod(Operation operation) {
|
||||
const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
|
||||
ASSERT(meta);
|
||||
|
||||
@@ -1156,7 +1162,7 @@ private:
|
||||
return expr + GetSwizzle(meta->element);
|
||||
}
|
||||
|
||||
std::string F4TextureGather(Operation operation) {
|
||||
std::string TextureGather(Operation operation) {
|
||||
const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
|
||||
ASSERT(meta);
|
||||
|
||||
@@ -1164,7 +1170,7 @@ private:
|
||||
GetSwizzle(meta->element);
|
||||
}
|
||||
|
||||
std::string F4TextureQueryDimensions(Operation operation) {
|
||||
std::string TextureQueryDimensions(Operation operation) {
|
||||
const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
|
||||
ASSERT(meta);
|
||||
|
||||
@@ -1184,7 +1190,7 @@ private:
|
||||
return "0";
|
||||
}
|
||||
|
||||
std::string F4TextureQueryLod(Operation operation) {
|
||||
std::string TextureQueryLod(Operation operation) {
|
||||
const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
|
||||
ASSERT(meta);
|
||||
|
||||
@@ -1195,29 +1201,32 @@ private:
|
||||
return "0";
|
||||
}
|
||||
|
||||
std::string F4TexelFetch(Operation operation) {
|
||||
std::string TexelFetch(Operation operation) {
|
||||
constexpr std::array<const char*, 4> constructors = {"int", "ivec2", "ivec3", "ivec4"};
|
||||
const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
|
||||
const auto count = static_cast<u32>(operation.GetOperandsCount());
|
||||
ASSERT(meta);
|
||||
UNIMPLEMENTED_IF(meta->sampler.IsArray());
|
||||
UNIMPLEMENTED_IF(!meta->extras.empty());
|
||||
|
||||
const auto count = static_cast<u32>(operation.GetOperandsCount());
|
||||
|
||||
std::string expr = "texelFetch(";
|
||||
expr += GetSampler(meta->sampler);
|
||||
expr += ", ";
|
||||
|
||||
expr += constructors[meta->coords_count - 1];
|
||||
expr += constructors.at(count - 1);
|
||||
expr += '(';
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
expr += VisitOperand(operation, i, Type::Int);
|
||||
|
||||
if (i + 1 == meta->coords_count) {
|
||||
const u32 next = i + 1;
|
||||
if (next == count)
|
||||
expr += ')';
|
||||
}
|
||||
if (i + 1 < count) {
|
||||
if (next < count)
|
||||
expr += ", ";
|
||||
}
|
||||
}
|
||||
expr += ')';
|
||||
|
||||
return expr + GetSwizzle(meta->element);
|
||||
}
|
||||
|
||||
@@ -1454,12 +1463,12 @@ private:
|
||||
&GLSLDecompiler::Logical2HNotEqual,
|
||||
&GLSLDecompiler::Logical2HGreaterEqual,
|
||||
|
||||
&GLSLDecompiler::F4Texture,
|
||||
&GLSLDecompiler::F4TextureLod,
|
||||
&GLSLDecompiler::F4TextureGather,
|
||||
&GLSLDecompiler::F4TextureQueryDimensions,
|
||||
&GLSLDecompiler::F4TextureQueryLod,
|
||||
&GLSLDecompiler::F4TexelFetch,
|
||||
&GLSLDecompiler::Texture,
|
||||
&GLSLDecompiler::TextureLod,
|
||||
&GLSLDecompiler::TextureGather,
|
||||
&GLSLDecompiler::TextureQueryDimensions,
|
||||
&GLSLDecompiler::TextureQueryLod,
|
||||
&GLSLDecompiler::TexelFetch,
|
||||
|
||||
&GLSLDecompiler::Branch,
|
||||
&GLSLDecompiler::PushFlowStack,
|
||||
|
||||
@@ -107,7 +107,7 @@ RendererOpenGL::~RendererOpenGL() = default;
|
||||
void RendererOpenGL::SwapBuffers(
|
||||
std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) {
|
||||
|
||||
Core::System::GetInstance().GetPerfStats().EndSystemFrame();
|
||||
system.GetPerfStats().EndSystemFrame();
|
||||
|
||||
// Maintain the rasterizer's state as a priority
|
||||
OpenGLState prev_state = OpenGLState::GetCurState();
|
||||
@@ -137,8 +137,8 @@ void RendererOpenGL::SwapBuffers(
|
||||
|
||||
render_window.PollEvents();
|
||||
|
||||
Core::System::GetInstance().FrameLimiter().DoFrameLimiting(CoreTiming::GetGlobalTimeUs());
|
||||
Core::System::GetInstance().GetPerfStats().BeginSystemFrame();
|
||||
system.FrameLimiter().DoFrameLimiting(Core::Timing::GetGlobalTimeUs());
|
||||
system.GetPerfStats().BeginSystemFrame();
|
||||
|
||||
// Restore the rasterizer state
|
||||
prev_state.Apply();
|
||||
|
||||
@@ -41,7 +41,7 @@ u32 ShaderIR::DecodeArithmeticInteger(NodeBlock& bb, u32 pc) {
|
||||
|
||||
const Node value = Operation(OperationCode::IAdd, PRECISE, op_a, op_b);
|
||||
|
||||
SetInternalFlagsFromInteger(bb, value, instr.op_32.generates_cc);
|
||||
SetInternalFlagsFromInteger(bb, value, instr.generates_cc);
|
||||
SetRegister(bb, instr.gpr0, value);
|
||||
break;
|
||||
}
|
||||
@@ -284,4 +284,4 @@ void ShaderIR::WriteLop3Instruction(NodeBlock& bb, Register dest, Node op_a, Nod
|
||||
SetRegister(bb, dest, value);
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -118,8 +118,8 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) {
|
||||
|
||||
value = [&]() {
|
||||
switch (instr.conversion.f2i.rounding) {
|
||||
case Tegra::Shader::F2iRoundingOp::None:
|
||||
return value;
|
||||
case Tegra::Shader::F2iRoundingOp::RoundEven:
|
||||
return Operation(OperationCode::FRoundEven, PRECISE, value);
|
||||
case Tegra::Shader::F2iRoundingOp::Floor:
|
||||
return Operation(OperationCode::FFloor, PRECISE, value);
|
||||
case Tegra::Shader::F2iRoundingOp::Ceil:
|
||||
@@ -146,4 +146,4 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -306,7 +306,6 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
case OpCode::Id::TLD4S: {
|
||||
UNIMPLEMENTED_IF_MSG(instr.tld4s.UsesMiscMode(TextureMiscMode::AOFFI),
|
||||
"AOFFI is not implemented");
|
||||
|
||||
if (instr.tld4s.UsesMiscMode(TextureMiscMode::NODEP)) {
|
||||
LOG_WARNING(HW_GPU, "TLD4S.NODEP implementation is incomplete");
|
||||
}
|
||||
@@ -315,9 +314,8 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
const Node op_a = GetRegister(instr.gpr8);
|
||||
const Node op_b = GetRegister(instr.gpr20);
|
||||
|
||||
std::vector<Node> coords;
|
||||
|
||||
// TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction.
|
||||
std::vector<Node> coords;
|
||||
if (depth_compare) {
|
||||
// Note: TLD4S coordinate encoding works just like TEXS's
|
||||
const Node op_y = GetRegister(instr.gpr8.Value() + 1);
|
||||
@@ -328,18 +326,17 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
coords.push_back(op_a);
|
||||
coords.push_back(op_b);
|
||||
}
|
||||
const auto num_coords = static_cast<u32>(coords.size());
|
||||
coords.push_back(Immediate(static_cast<u32>(instr.tld4s.component)));
|
||||
std::vector<Node> extras;
|
||||
extras.push_back(Immediate(static_cast<u32>(instr.tld4s.component)));
|
||||
|
||||
const auto& sampler =
|
||||
GetSampler(instr.sampler, TextureType::Texture2D, false, depth_compare);
|
||||
|
||||
Node4 values;
|
||||
for (u32 element = 0; element < values.size(); ++element) {
|
||||
auto params = coords;
|
||||
MetaTexture meta{sampler, element, num_coords};
|
||||
values[element] =
|
||||
Operation(OperationCode::F4TextureGather, std::move(meta), std::move(params));
|
||||
auto coords_copy = coords;
|
||||
MetaTexture meta{sampler, {}, {}, extras, element};
|
||||
values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy));
|
||||
}
|
||||
|
||||
WriteTexsInstructionFloat(bb, instr, values);
|
||||
@@ -360,12 +357,13 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
switch (instr.txq.query_type) {
|
||||
case Tegra::Shader::TextureQueryType::Dimension: {
|
||||
for (u32 element = 0; element < 4; ++element) {
|
||||
if (instr.txq.IsComponentEnabled(element)) {
|
||||
MetaTexture meta{sampler, element};
|
||||
const Node value = Operation(OperationCode::F4TextureQueryDimensions,
|
||||
std::move(meta), GetRegister(instr.gpr8));
|
||||
SetTemporal(bb, indexer++, value);
|
||||
if (!instr.txq.IsComponentEnabled(element)) {
|
||||
continue;
|
||||
}
|
||||
MetaTexture meta{sampler, {}, {}, {}, element};
|
||||
const Node value =
|
||||
Operation(OperationCode::TextureQueryDimensions, meta, GetRegister(instr.gpr8));
|
||||
SetTemporal(bb, indexer++, value);
|
||||
}
|
||||
for (u32 i = 0; i < indexer; ++i) {
|
||||
SetRegister(bb, instr.gpr0.Value() + i, GetTemporal(i));
|
||||
@@ -412,9 +410,8 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
|
||||
for (u32 element = 0; element < 2; ++element) {
|
||||
auto params = coords;
|
||||
MetaTexture meta_texture{sampler, element, static_cast<u32>(coords.size())};
|
||||
const Node value =
|
||||
Operation(OperationCode::F4TextureQueryLod, meta_texture, std::move(params));
|
||||
MetaTexture meta{sampler, {}, {}, {}, element};
|
||||
const Node value = Operation(OperationCode::TextureQueryLod, meta, std::move(params));
|
||||
SetTemporal(bb, element, value);
|
||||
}
|
||||
for (u32 element = 0; element < 2; ++element) {
|
||||
@@ -535,15 +532,16 @@ void ShaderIR::WriteTexsInstructionHalfFloat(NodeBlock& bb, Instruction instr,
|
||||
}
|
||||
|
||||
Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type,
|
||||
TextureProcessMode process_mode, bool depth_compare, bool is_array,
|
||||
std::size_t array_offset, std::size_t bias_offset,
|
||||
std::vector<Node>&& coords) {
|
||||
UNIMPLEMENTED_IF_MSG(
|
||||
(texture_type == TextureType::Texture3D && (is_array || depth_compare)) ||
|
||||
(texture_type == TextureType::TextureCube && is_array && depth_compare),
|
||||
"This method is not supported.");
|
||||
TextureProcessMode process_mode, std::vector<Node> coords,
|
||||
Node array, Node depth_compare, u32 bias_offset) {
|
||||
const bool is_array = array;
|
||||
const bool is_shadow = depth_compare;
|
||||
|
||||
const auto& sampler = GetSampler(instr.sampler, texture_type, is_array, depth_compare);
|
||||
UNIMPLEMENTED_IF_MSG((texture_type == TextureType::Texture3D && (is_array || is_shadow)) ||
|
||||
(texture_type == TextureType::TextureCube && is_array && is_shadow),
|
||||
"This method is not supported.");
|
||||
|
||||
const auto& sampler = GetSampler(instr.sampler, texture_type, is_array, is_shadow);
|
||||
|
||||
const bool lod_needed = process_mode == TextureProcessMode::LZ ||
|
||||
process_mode == TextureProcessMode::LL ||
|
||||
@@ -552,35 +550,30 @@ Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type,
|
||||
// LOD selection (either via bias or explicit textureLod) not supported in GL for
|
||||
// sampler2DArrayShadow and samplerCubeArrayShadow.
|
||||
const bool gl_lod_supported =
|
||||
!((texture_type == Tegra::Shader::TextureType::Texture2D && is_array && depth_compare) ||
|
||||
(texture_type == Tegra::Shader::TextureType::TextureCube && is_array && depth_compare));
|
||||
!((texture_type == Tegra::Shader::TextureType::Texture2D && is_array && is_shadow) ||
|
||||
(texture_type == Tegra::Shader::TextureType::TextureCube && is_array && is_shadow));
|
||||
|
||||
const OperationCode read_method =
|
||||
lod_needed && gl_lod_supported ? OperationCode::F4TextureLod : OperationCode::F4Texture;
|
||||
lod_needed && gl_lod_supported ? OperationCode::TextureLod : OperationCode::Texture;
|
||||
|
||||
UNIMPLEMENTED_IF(process_mode != TextureProcessMode::None && !gl_lod_supported);
|
||||
|
||||
std::optional<u32> array_offset_value;
|
||||
if (is_array)
|
||||
array_offset_value = static_cast<u32>(array_offset);
|
||||
|
||||
const auto coords_count = static_cast<u32>(coords.size());
|
||||
|
||||
std::vector<Node> extras;
|
||||
if (process_mode != TextureProcessMode::None && gl_lod_supported) {
|
||||
if (process_mode == TextureProcessMode::LZ) {
|
||||
coords.push_back(Immediate(0.0f));
|
||||
extras.push_back(Immediate(0.0f));
|
||||
} else {
|
||||
// If present, lod or bias are always stored in the register indexed by the gpr20
|
||||
// field with an offset depending on the usage of the other registers
|
||||
coords.push_back(GetRegister(instr.gpr20.Value() + bias_offset));
|
||||
extras.push_back(GetRegister(instr.gpr20.Value() + bias_offset));
|
||||
}
|
||||
}
|
||||
|
||||
Node4 values;
|
||||
for (u32 element = 0; element < values.size(); ++element) {
|
||||
auto params = coords;
|
||||
MetaTexture meta{sampler, element, coords_count, array_offset_value};
|
||||
values[element] = Operation(read_method, std::move(meta), std::move(params));
|
||||
auto copy_coords = coords;
|
||||
MetaTexture meta{sampler, array, depth_compare, extras, element};
|
||||
values[element] = Operation(read_method, meta, std::move(copy_coords));
|
||||
}
|
||||
|
||||
return values;
|
||||
@@ -602,28 +595,22 @@ Node4 ShaderIR::GetTexCode(Instruction instr, TextureType texture_type,
|
||||
for (std::size_t i = 0; i < coord_count; ++i) {
|
||||
coords.push_back(GetRegister(coord_register + i));
|
||||
}
|
||||
// 1D.DC in opengl the 2nd component is ignored.
|
||||
// 1D.DC in OpenGL the 2nd component is ignored.
|
||||
if (depth_compare && !is_array && texture_type == TextureType::Texture1D) {
|
||||
coords.push_back(Immediate(0.0f));
|
||||
}
|
||||
std::size_t array_offset{};
|
||||
if (is_array) {
|
||||
array_offset = coords.size();
|
||||
coords.push_back(GetRegister(array_register));
|
||||
}
|
||||
|
||||
const Node array = is_array ? GetRegister(array_register) : nullptr;
|
||||
|
||||
Node dc{};
|
||||
if (depth_compare) {
|
||||
// Depth is always stored in the register signaled by gpr20
|
||||
// or in the next register if lod or bias are used
|
||||
// Depth is always stored in the register signaled by gpr20 or in the next register if lod
|
||||
// or bias are used
|
||||
const u64 depth_register = instr.gpr20.Value() + (lod_bias_enabled ? 1 : 0);
|
||||
coords.push_back(GetRegister(depth_register));
|
||||
}
|
||||
// Fill ignored coordinates
|
||||
while (coords.size() < total_coord_count) {
|
||||
coords.push_back(Immediate(0));
|
||||
dc = GetRegister(depth_register);
|
||||
}
|
||||
|
||||
return GetTextureCode(instr, texture_type, process_mode, depth_compare, is_array, array_offset,
|
||||
0, std::move(coords));
|
||||
return GetTextureCode(instr, texture_type, process_mode, coords, array, dc, 0);
|
||||
}
|
||||
|
||||
Node4 ShaderIR::GetTexsCode(Instruction instr, TextureType texture_type,
|
||||
@@ -641,6 +628,7 @@ Node4 ShaderIR::GetTexsCode(Instruction instr, TextureType texture_type,
|
||||
(is_array || !(lod_bias_enabled || depth_compare) || (coord_count > 2))
|
||||
? static_cast<u64>(instr.gpr20.Value())
|
||||
: coord_register + 1;
|
||||
const u32 bias_offset = coord_count > 2 ? 1 : 0;
|
||||
|
||||
std::vector<Node> coords;
|
||||
for (std::size_t i = 0; i < coord_count; ++i) {
|
||||
@@ -648,24 +636,17 @@ Node4 ShaderIR::GetTexsCode(Instruction instr, TextureType texture_type,
|
||||
coords.push_back(GetRegister(last ? last_coord_register : coord_register + i));
|
||||
}
|
||||
|
||||
std::size_t array_offset{};
|
||||
if (is_array) {
|
||||
array_offset = coords.size();
|
||||
coords.push_back(GetRegister(array_register));
|
||||
}
|
||||
const Node array = is_array ? GetRegister(array_register) : nullptr;
|
||||
|
||||
Node dc{};
|
||||
if (depth_compare) {
|
||||
// Depth is always stored in the register signaled by gpr20
|
||||
// or in the next register if lod or bias are used
|
||||
// Depth is always stored in the register signaled by gpr20 or in the next register if lod
|
||||
// or bias are used
|
||||
const u64 depth_register = instr.gpr20.Value() + (lod_bias_enabled ? 1 : 0);
|
||||
coords.push_back(GetRegister(depth_register));
|
||||
}
|
||||
// Fill ignored coordinates
|
||||
while (coords.size() < total_coord_count) {
|
||||
coords.push_back(Immediate(0));
|
||||
dc = GetRegister(depth_register);
|
||||
}
|
||||
|
||||
return GetTextureCode(instr, texture_type, process_mode, depth_compare, is_array, array_offset,
|
||||
(coord_count > 2 ? 1 : 0), std::move(coords));
|
||||
return GetTextureCode(instr, texture_type, process_mode, coords, array, dc, bias_offset);
|
||||
}
|
||||
|
||||
Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool depth_compare,
|
||||
@@ -680,24 +661,16 @@ Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool de
|
||||
const u64 coord_register = array_register + (is_array ? 1 : 0);
|
||||
|
||||
std::vector<Node> coords;
|
||||
|
||||
for (size_t i = 0; i < coord_count; ++i) {
|
||||
for (size_t i = 0; i < coord_count; ++i)
|
||||
coords.push_back(GetRegister(coord_register + i));
|
||||
}
|
||||
std::optional<u32> array_offset;
|
||||
if (is_array) {
|
||||
array_offset = static_cast<u32>(coords.size());
|
||||
coords.push_back(GetRegister(array_register));
|
||||
}
|
||||
|
||||
const auto& sampler = GetSampler(instr.sampler, texture_type, is_array, depth_compare);
|
||||
|
||||
Node4 values;
|
||||
for (u32 element = 0; element < values.size(); ++element) {
|
||||
auto params = coords;
|
||||
MetaTexture meta{sampler, element, static_cast<u32>(coords.size()), array_offset};
|
||||
values[element] =
|
||||
Operation(OperationCode::F4TextureGather, std::move(meta), std::move(params));
|
||||
auto coords_copy = coords;
|
||||
MetaTexture meta{sampler, GetRegister(array_register), {}, {}, element};
|
||||
values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy));
|
||||
}
|
||||
|
||||
return values;
|
||||
@@ -705,7 +678,6 @@ Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool de
|
||||
|
||||
Node4 ShaderIR::GetTldsCode(Instruction instr, TextureType texture_type, bool is_array) {
|
||||
const std::size_t type_coord_count = GetCoordCount(texture_type);
|
||||
const std::size_t total_coord_count = type_coord_count + (is_array ? 1 : 0);
|
||||
const bool lod_enabled = instr.tlds.GetTextureProcessMode() == TextureProcessMode::LL;
|
||||
|
||||
// If enabled arrays index is always stored in the gpr8 field
|
||||
@@ -719,33 +691,22 @@ Node4 ShaderIR::GetTldsCode(Instruction instr, TextureType texture_type, bool is
|
||||
: coord_register + 1;
|
||||
|
||||
std::vector<Node> coords;
|
||||
|
||||
for (std::size_t i = 0; i < type_coord_count; ++i) {
|
||||
const bool last = (i == (type_coord_count - 1)) && (type_coord_count > 1);
|
||||
coords.push_back(GetRegister(last ? last_coord_register : coord_register + i));
|
||||
}
|
||||
std::optional<u32> array_offset;
|
||||
if (is_array) {
|
||||
array_offset = static_cast<u32>(coords.size());
|
||||
coords.push_back(GetRegister(array_register));
|
||||
}
|
||||
const auto coords_count = static_cast<u32>(coords.size());
|
||||
|
||||
if (lod_enabled) {
|
||||
// When lod is used always is in grp20
|
||||
coords.push_back(GetRegister(instr.gpr20));
|
||||
} else {
|
||||
coords.push_back(Immediate(0));
|
||||
}
|
||||
const Node array = is_array ? GetRegister(array_register) : nullptr;
|
||||
// When lod is used always is in gpr20
|
||||
const Node lod = lod_enabled ? GetRegister(instr.gpr20) : Immediate(0);
|
||||
|
||||
const auto& sampler = GetSampler(instr.sampler, texture_type, is_array, false);
|
||||
|
||||
Node4 values;
|
||||
for (u32 element = 0; element < values.size(); ++element) {
|
||||
auto params = coords;
|
||||
MetaTexture meta{sampler, element, coords_count, array_offset};
|
||||
values[element] =
|
||||
Operation(OperationCode::F4TexelFetch, std::move(meta), std::move(params));
|
||||
auto coords_copy = coords;
|
||||
MetaTexture meta{sampler, array, {}, {lod}, element};
|
||||
values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
@@ -156,12 +156,12 @@ enum class OperationCode {
|
||||
Logical2HNotEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
|
||||
Logical2HGreaterEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
|
||||
|
||||
F4Texture, /// (MetaTexture, float[N] coords, float[M] params) -> float4
|
||||
F4TextureLod, /// (MetaTexture, float[N] coords, float[M] params) -> float4
|
||||
F4TextureGather, /// (MetaTexture, float[N] coords, float[M] params) -> float4
|
||||
F4TextureQueryDimensions, /// (MetaTexture, float a) -> float4
|
||||
F4TextureQueryLod, /// (MetaTexture, float[N] coords) -> float4
|
||||
F4TexelFetch, /// (MetaTexture, int[N], int) -> float4
|
||||
Texture, /// (MetaTexture, float[N] coords) -> float4
|
||||
TextureLod, /// (MetaTexture, float[N] coords) -> float4
|
||||
TextureGather, /// (MetaTexture, float[N] coords) -> float4
|
||||
TextureQueryDimensions, /// (MetaTexture, float a) -> float4
|
||||
TextureQueryLod, /// (MetaTexture, float[N] coords) -> float4
|
||||
TexelFetch, /// (MetaTexture, int[N], int) -> float4
|
||||
|
||||
Branch, /// (uint branch_target) -> void
|
||||
PushFlowStack, /// (uint branch_target) -> void
|
||||
@@ -288,9 +288,10 @@ struct MetaHalfArithmetic {
|
||||
|
||||
struct MetaTexture {
|
||||
const Sampler& sampler;
|
||||
Node array{};
|
||||
Node depth_compare{};
|
||||
std::vector<Node> extras;
|
||||
u32 element{};
|
||||
u32 coords_count{};
|
||||
std::optional<u32> array_index;
|
||||
};
|
||||
|
||||
constexpr MetaArithmetic PRECISE = {true};
|
||||
@@ -754,9 +755,8 @@ private:
|
||||
bool lod_bias_enabled, std::size_t max_coords, std::size_t max_inputs);
|
||||
|
||||
Node4 GetTextureCode(Tegra::Shader::Instruction instr, Tegra::Shader::TextureType texture_type,
|
||||
Tegra::Shader::TextureProcessMode process_mode, bool depth_compare,
|
||||
bool is_array, std::size_t array_offset, std::size_t bias_offset,
|
||||
std::vector<Node>&& coords);
|
||||
Tegra::Shader::TextureProcessMode process_mode, std::vector<Node> coords,
|
||||
Node array, Node depth_compare, u32 bias_offset);
|
||||
|
||||
Node GetVideoOperand(Node op, bool is_chunk, bool is_signed, Tegra::Shader::VideoType type,
|
||||
u64 byte_height);
|
||||
|
||||
@@ -426,6 +426,8 @@ PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat
|
||||
switch (format) {
|
||||
case Tegra::FramebufferConfig::PixelFormat::ABGR8:
|
||||
return PixelFormat::ABGR8U;
|
||||
case Tegra::FramebufferConfig::PixelFormat::BGRA8:
|
||||
return PixelFormat::BGRA8;
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
||||
UNREACHABLE();
|
||||
|
||||
@@ -405,6 +405,13 @@ void Config::ReadValues() {
|
||||
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)))
|
||||
.toString()
|
||||
.toStdString());
|
||||
FileUtil::GetUserPath(
|
||||
FileUtil::UserPath::KeysDir,
|
||||
qt_config
|
||||
->value("keys_directory",
|
||||
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::KeysDir)))
|
||||
.toString()
|
||||
.toStdString());
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Core");
|
||||
@@ -653,6 +660,8 @@ void Config::SaveValues() {
|
||||
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)));
|
||||
qt_config->setValue("sdmc_directory",
|
||||
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
|
||||
qt_config->setValue("keys_directory",
|
||||
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::KeysDir)));
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("System");
|
||||
|
||||
@@ -659,6 +659,8 @@ void GMainWindow::ConnectMenuEvents() {
|
||||
[this] { OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget::NAND); });
|
||||
connect(ui.action_Select_SDMC_Directory, &QAction::triggered, this,
|
||||
[this] { OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget::SDMC); });
|
||||
connect(ui.action_Select_Keys_Directory, &QAction::triggered, this,
|
||||
[this] { OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget::Keys); });
|
||||
connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close);
|
||||
connect(ui.action_Load_Amiibo, &QAction::triggered, this, &GMainWindow::OnLoadAmiibo);
|
||||
|
||||
@@ -1447,12 +1449,27 @@ void GMainWindow::OnMenuSelectGameListRoot() {
|
||||
}
|
||||
|
||||
void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target) {
|
||||
const char* directory_name;
|
||||
FileUtil::UserPath path;
|
||||
switch (target) {
|
||||
case EmulatedDirectoryTarget::SDMC:
|
||||
directory_name = "SD Card";
|
||||
path = FileUtil::UserPath::SDMCDir;
|
||||
break;
|
||||
case EmulatedDirectoryTarget::NAND:
|
||||
directory_name = "NAND";
|
||||
path = FileUtil::UserPath::NANDDir;
|
||||
break;
|
||||
default:
|
||||
directory_name = "keys";
|
||||
path = FileUtil::UserPath::KeysDir;
|
||||
}
|
||||
const auto res = QMessageBox::information(
|
||||
this, tr("Changing Emulated Directory"),
|
||||
tr("You are about to change the emulated %1 directory of the system. Please note "
|
||||
"that this does not also move the contents of the previous directory to the "
|
||||
"new one and you will have to do that yourself.")
|
||||
.arg(target == EmulatedDirectoryTarget::SDMC ? tr("SD card") : tr("NAND")),
|
||||
.arg(tr(directory_name)),
|
||||
QMessageBox::StandardButtons{QMessageBox::Ok, QMessageBox::Cancel});
|
||||
|
||||
if (res == QMessageBox::Cancel)
|
||||
@@ -1460,9 +1477,7 @@ void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target)
|
||||
|
||||
QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory"));
|
||||
if (!dir_path.isEmpty()) {
|
||||
FileUtil::GetUserPath(target == EmulatedDirectoryTarget::SDMC ? FileUtil::UserPath::SDMCDir
|
||||
: FileUtil::UserPath::NANDDir,
|
||||
dir_path.toStdString());
|
||||
FileUtil::GetUserPath(path, dir_path.toStdString());
|
||||
Service::FileSystem::CreateFactories(*vfs);
|
||||
game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ class DebugContext;
|
||||
enum class EmulatedDirectoryTarget {
|
||||
NAND,
|
||||
SDMC,
|
||||
Keys,
|
||||
};
|
||||
|
||||
enum class ReinitializeKeyBehavior {
|
||||
|
||||
@@ -67,6 +67,7 @@
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Select_NAND_Directory"/>
|
||||
<addaction name="action_Select_SDMC_Directory"/>
|
||||
<addaction name="action_Select_Keys_Directory"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Load_Amiibo"/>
|
||||
<addaction name="separator"/>
|
||||
@@ -246,6 +247,14 @@
|
||||
<string>Selects a folder to use as the root of the emulated SD card</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Select_Keys_Directory">
|
||||
<property name="text">
|
||||
<string>Select Keys Directory...</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Selects a folder to use as the root of the keys</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Fullscreen">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
|
||||
Reference in New Issue
Block a user