early-access version 3241
This commit is contained in:
parent
3b9e1ccd4b
commit
7c2fb8c0da
9 changed files with 77 additions and 44 deletions
|
@ -1,7 +1,7 @@
|
||||||
yuzu emulator early access
|
yuzu emulator early access
|
||||||
=============
|
=============
|
||||||
|
|
||||||
This is the source code for early-access 3240.
|
This is the source code for early-access 3241.
|
||||||
|
|
||||||
## Legal Notice
|
## Legal Notice
|
||||||
|
|
||||||
|
|
BIN
dist/yuzu.ico
vendored
BIN
dist/yuzu.ico
vendored
Binary file not shown.
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 25 KiB |
2
dist/yuzu.svg
vendored
2
dist/yuzu.svg
vendored
|
@ -1 +1 @@
|
||||||
<svg id="svg815" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 614.4 682.67"><defs><style>.cls-1{fill:none;}.cls-2{clip-path:url(#clip-path);}.cls-3{fill:#ff3c28;}.cls-4{fill:#0ab9e6;}</style><clipPath id="clip-path"><rect class="cls-1" x="-43" y="-46.67" width="699.6" height="777.33"/></clipPath></defs><title>Artboard 1</title><g id="g823"><g id="right"><g class="cls-2"><g id="g827"><g id="g833"><path id="path835" class="cls-3" d="M340.81,138V682.08c150.26,0,272.06-121.81,272.06-272.06S491.07,138,340.81,138M394,197.55a219.06,219.06,0,0,1,0,424.94V197.55"/></g></g></g></g><g id="left"><g class="cls-2"><g id="g839"><g id="g845"><path id="path847" class="cls-4" d="M272.79,1.92C122.53,1.92.73,123.73.73,274s121.8,272.07,272.06,272.07ZM219.65,61.51v425A219,219,0,0,1,118,119.18,217.51,217.51,0,0,1,219.65,61.51"/></g></g></g></g></g></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 612.15 680.17"><defs><style>.cls-1{fill:#c6c6c6;}.cls-2{fill:#ffdc00;}</style></defs><title>newAsset 7</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><g id="g823"><g id="right"><g id="g827"><g id="g833"><path id="path835" class="cls-1" d="M340.08,136V680.17c150.26,0,272.07-121.81,272.07-272.07S490.34,136,340.08,136m53.14,59.6a219.06,219.06,0,0,1,0,424.94V195.63"/></g></g></g><g id="left"><g id="g839"><g id="g845"><path id="path847" class="cls-2" d="M272.07,0C121.81,0,0,121.81,0,272.07S121.81,544.13,272.07,544.13ZM218.93,59.6V484.54A219,219,0,0,1,117.26,117.26,217.44,217.44,0,0,1,218.93,59.6"/></g></g></g></g></g></g></svg>
|
Before Width: | Height: | Size: 889 B After Width: | Height: | Size: 717 B |
|
@ -49,6 +49,7 @@ struct SteadyClockContext {
|
||||||
static_assert(sizeof(SteadyClockContext) == 0x18, "SteadyClockContext is incorrect size");
|
static_assert(sizeof(SteadyClockContext) == 0x18, "SteadyClockContext is incorrect size");
|
||||||
static_assert(std::is_trivially_copyable_v<SteadyClockContext>,
|
static_assert(std::is_trivially_copyable_v<SteadyClockContext>,
|
||||||
"SteadyClockContext must be trivially copyable");
|
"SteadyClockContext must be trivially copyable");
|
||||||
|
using StandardSteadyClockTimePointType = SteadyClockContext;
|
||||||
|
|
||||||
struct SystemClockContext {
|
struct SystemClockContext {
|
||||||
s64 offset;
|
s64 offset;
|
||||||
|
|
|
@ -26,23 +26,24 @@ void SharedMemory::SetupStandardSteadyClock(const Common::UUID& clock_source_id,
|
||||||
const Clock::SteadyClockContext context{
|
const Clock::SteadyClockContext context{
|
||||||
static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds),
|
static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds),
|
||||||
clock_source_id};
|
clock_source_id};
|
||||||
shared_memory_format.standard_steady_clock_timepoint.StoreData(
|
StoreToLockFreeAtomicType(&GetFormat()->standard_steady_clock_timepoint, context);
|
||||||
system.Kernel().GetTimeSharedMem().GetPointer(), context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SharedMemory::UpdateLocalSystemClockContext(const Clock::SystemClockContext& context) {
|
void SharedMemory::UpdateLocalSystemClockContext(const Clock::SystemClockContext& context) {
|
||||||
shared_memory_format.standard_local_system_clock_context.StoreData(
|
StoreToLockFreeAtomicType(&GetFormat()->standard_local_system_clock_context, context);
|
||||||
system.Kernel().GetTimeSharedMem().GetPointer(), context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SharedMemory::UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context) {
|
void SharedMemory::UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context) {
|
||||||
shared_memory_format.standard_network_system_clock_context.StoreData(
|
StoreToLockFreeAtomicType(&GetFormat()->standard_network_system_clock_context, context);
|
||||||
system.Kernel().GetTimeSharedMem().GetPointer(), context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SharedMemory::SetAutomaticCorrectionEnabled(bool is_enabled) {
|
void SharedMemory::SetAutomaticCorrectionEnabled(bool is_enabled) {
|
||||||
shared_memory_format.standard_user_system_clock_automatic_correction.StoreData(
|
StoreToLockFreeAtomicType(
|
||||||
system.Kernel().GetTimeSharedMem().GetPointer(), is_enabled);
|
&GetFormat()->is_standard_user_system_clock_automatic_correction_enabled, is_enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedMemory::Format* SharedMemory::GetFormat() {
|
||||||
|
return reinterpret_cast<SharedMemory::Format*>(system.Kernel().GetTimeSharedMem().GetPointer());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Service::Time
|
} // namespace Service::Time
|
||||||
|
|
|
@ -10,45 +10,68 @@
|
||||||
|
|
||||||
namespace Service::Time {
|
namespace Service::Time {
|
||||||
|
|
||||||
|
// Note: this type is not safe for concurrent writes.
|
||||||
|
template <typename T>
|
||||||
|
struct LockFreeAtomicType {
|
||||||
|
u32 counter_;
|
||||||
|
std::array<T, 2> value_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static inline void StoreToLockFreeAtomicType(LockFreeAtomicType<T>* p, const T& value) {
|
||||||
|
// Get the current counter.
|
||||||
|
auto counter = p->counter_;
|
||||||
|
|
||||||
|
// Increment the counter.
|
||||||
|
++counter;
|
||||||
|
|
||||||
|
// Store the updated value.
|
||||||
|
p->value_[counter % 2] = value;
|
||||||
|
|
||||||
|
// Fence memory.
|
||||||
|
std::atomic_thread_fence(std::memory_order_release);
|
||||||
|
|
||||||
|
// Set the updated counter.
|
||||||
|
p->counter_ = counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static inline T LoadFromLockFreeAtomicType(const LockFreeAtomicType<T>* p) {
|
||||||
|
while (true) {
|
||||||
|
// Get the counter.
|
||||||
|
auto counter = p->counter_;
|
||||||
|
|
||||||
|
// Get the value.
|
||||||
|
auto value = p->value_[counter % 2];
|
||||||
|
|
||||||
|
// Fence memory.
|
||||||
|
std::atomic_thread_fence(std::memory_order_acquire);
|
||||||
|
|
||||||
|
// Check that the counter matches.
|
||||||
|
if (counter == p->counter_) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class SharedMemory final {
|
class SharedMemory final {
|
||||||
public:
|
public:
|
||||||
explicit SharedMemory(Core::System& system_);
|
explicit SharedMemory(Core::System& system_);
|
||||||
~SharedMemory();
|
~SharedMemory();
|
||||||
|
|
||||||
// TODO(ogniK): We have to properly simulate memory barriers, how are we going to do this?
|
|
||||||
template <typename T, std::size_t Offset>
|
|
||||||
struct MemoryBarrier {
|
|
||||||
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
|
|
||||||
u32_le read_attempt{};
|
|
||||||
std::array<T, 2> data{};
|
|
||||||
|
|
||||||
// These are not actually memory barriers at the moment as we don't have multicore and all
|
|
||||||
// HLE is mutexed. This will need to properly be implemented when we start updating the time
|
|
||||||
// points on threads. As of right now, we'll be updated both values synchronously and just
|
|
||||||
// incrementing the read_attempt to indicate that we waited.
|
|
||||||
void StoreData(u8* shared_memory, T data_to_store) {
|
|
||||||
std::memcpy(this, shared_memory + Offset, sizeof(*this));
|
|
||||||
read_attempt++;
|
|
||||||
data[read_attempt & 1] = data_to_store;
|
|
||||||
std::memcpy(shared_memory + Offset, this, sizeof(*this));
|
|
||||||
}
|
|
||||||
|
|
||||||
// For reading we're just going to read the last stored value. If there was no value stored
|
|
||||||
// it will just end up reading an empty value as intended.
|
|
||||||
T ReadData(u8* shared_memory) {
|
|
||||||
std::memcpy(this, shared_memory + Offset, sizeof(*this));
|
|
||||||
return data[(read_attempt - 1) & 1];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Shared memory format
|
// Shared memory format
|
||||||
struct Format {
|
struct Format {
|
||||||
MemoryBarrier<Clock::SteadyClockContext, 0x0> standard_steady_clock_timepoint;
|
LockFreeAtomicType<Clock::StandardSteadyClockTimePointType> standard_steady_clock_timepoint;
|
||||||
MemoryBarrier<Clock::SystemClockContext, 0x38> standard_local_system_clock_context;
|
LockFreeAtomicType<Clock::SystemClockContext> standard_local_system_clock_context;
|
||||||
MemoryBarrier<Clock::SystemClockContext, 0x80> standard_network_system_clock_context;
|
LockFreeAtomicType<Clock::SystemClockContext> standard_network_system_clock_context;
|
||||||
MemoryBarrier<bool, 0xc8> standard_user_system_clock_automatic_correction;
|
LockFreeAtomicType<bool> is_standard_user_system_clock_automatic_correction_enabled;
|
||||||
u32_le format_version;
|
u32 format_version;
|
||||||
};
|
};
|
||||||
|
static_assert(offsetof(Format, standard_steady_clock_timepoint) == 0x0);
|
||||||
|
static_assert(offsetof(Format, standard_local_system_clock_context) == 0x38);
|
||||||
|
static_assert(offsetof(Format, standard_network_system_clock_context) == 0x80);
|
||||||
|
static_assert(offsetof(Format, is_standard_user_system_clock_automatic_correction_enabled) ==
|
||||||
|
0xc8);
|
||||||
static_assert(sizeof(Format) == 0xd8, "Format is an invalid size");
|
static_assert(sizeof(Format) == 0xd8, "Format is an invalid size");
|
||||||
|
|
||||||
void SetupStandardSteadyClock(const Common::UUID& clock_source_id,
|
void SetupStandardSteadyClock(const Common::UUID& clock_source_id,
|
||||||
|
@ -56,10 +79,10 @@ public:
|
||||||
void UpdateLocalSystemClockContext(const Clock::SystemClockContext& context);
|
void UpdateLocalSystemClockContext(const Clock::SystemClockContext& context);
|
||||||
void UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context);
|
void UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context);
|
||||||
void SetAutomaticCorrectionEnabled(bool is_enabled);
|
void SetAutomaticCorrectionEnabled(bool is_enabled);
|
||||||
|
Format* GetFormat();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
Format shared_memory_format{};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::Time
|
} // namespace Service::Time
|
||||||
|
|
|
@ -78,7 +78,6 @@ void EmuThread::run() {
|
||||||
gpu.Start();
|
gpu.Start();
|
||||||
|
|
||||||
m_system.GetCpuManager().OnGpuReady();
|
m_system.GetCpuManager().OnGpuReady();
|
||||||
m_system.RegisterExitCallback([this] { m_stop_source.request_stop(); });
|
|
||||||
|
|
||||||
if (m_system.DebuggerEnabled()) {
|
if (m_system.DebuggerEnabled()) {
|
||||||
m_system.InitializeDebugger();
|
m_system.InitializeDebugger();
|
||||||
|
|
|
@ -38,7 +38,7 @@ void DiscordImpl::Update() {
|
||||||
system.GetAppLoader().ReadTitle(title);
|
system.GetAppLoader().ReadTitle(title);
|
||||||
}
|
}
|
||||||
DiscordRichPresence presence{};
|
DiscordRichPresence presence{};
|
||||||
presence.largeImageKey = "yuzu_logo";
|
presence.largeImageKey = "yuzu_logo_ea";
|
||||||
presence.largeImageText = "yuzu is an emulator for the Nintendo Switch";
|
presence.largeImageText = "yuzu is an emulator for the Nintendo Switch";
|
||||||
if (system.IsPoweredOn()) {
|
if (system.IsPoweredOn()) {
|
||||||
presence.state = title.c_str();
|
presence.state = title.c_str();
|
||||||
|
|
|
@ -1710,6 +1710,11 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
|
||||||
system->RegisterExecuteProgramCallback(
|
system->RegisterExecuteProgramCallback(
|
||||||
[this](std::size_t program_index_) { render_window->ExecuteProgram(program_index_); });
|
[this](std::size_t program_index_) { render_window->ExecuteProgram(program_index_); });
|
||||||
|
|
||||||
|
system->RegisterExitCallback([this] {
|
||||||
|
emu_thread->ForceStop();
|
||||||
|
render_window->Exit();
|
||||||
|
});
|
||||||
|
|
||||||
connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
|
connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
|
||||||
connect(render_window, &GRenderWindow::MouseActivity, this, &GMainWindow::OnMouseActivity);
|
connect(render_window, &GRenderWindow::MouseActivity, this, &GMainWindow::OnMouseActivity);
|
||||||
// BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views
|
// BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views
|
||||||
|
@ -4177,6 +4182,10 @@ bool GMainWindow::ConfirmForceLockedExit() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::RequestGameExit() {
|
void GMainWindow::RequestGameExit() {
|
||||||
|
if (!system->IsPoweredOn()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto& sm{system->ServiceManager()};
|
auto& sm{system->ServiceManager()};
|
||||||
auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
|
auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
|
||||||
auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
|
auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
|
||||||
|
|
Loading…
Reference in a new issue