diff --git a/README.md b/README.md
index d31836839..9d5888f2c 100755
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
yuzu emulator early access
=============
-This is the source code for early-access 3240.
+This is the source code for early-access 3241.
## Legal Notice
diff --git a/dist/yuzu.ico b/dist/yuzu.ico
index df3be8464..7c998a5c5 100755
Binary files a/dist/yuzu.ico and b/dist/yuzu.ico differ
diff --git a/dist/yuzu.svg b/dist/yuzu.svg
index 93171d1bf..98ded2d8f 100755
--- a/dist/yuzu.svg
+++ b/dist/yuzu.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/src/core/hle/service/time/clock_types.h b/src/core/hle/service/time/clock_types.h
index 617d13f73..788bbff2b 100755
--- a/src/core/hle/service/time/clock_types.h
+++ b/src/core/hle/service/time/clock_types.h
@@ -49,6 +49,7 @@ struct SteadyClockContext {
static_assert(sizeof(SteadyClockContext) == 0x18, "SteadyClockContext is incorrect size");
static_assert(std::is_trivially_copyable_v,
"SteadyClockContext must be trivially copyable");
+using StandardSteadyClockTimePointType = SteadyClockContext;
struct SystemClockContext {
s64 offset;
diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp
index 04ffbd23d..73993b0c2 100755
--- a/src/core/hle/service/time/time_sharedmemory.cpp
+++ b/src/core/hle/service/time/time_sharedmemory.cpp
@@ -26,23 +26,24 @@ void SharedMemory::SetupStandardSteadyClock(const Common::UUID& clock_source_id,
const Clock::SteadyClockContext context{
static_cast(current_time_point.nanoseconds - ticks_time_span.nanoseconds),
clock_source_id};
- shared_memory_format.standard_steady_clock_timepoint.StoreData(
- system.Kernel().GetTimeSharedMem().GetPointer(), context);
+ StoreToLockFreeAtomicType(&GetFormat()->standard_steady_clock_timepoint, context);
}
void SharedMemory::UpdateLocalSystemClockContext(const Clock::SystemClockContext& context) {
- shared_memory_format.standard_local_system_clock_context.StoreData(
- system.Kernel().GetTimeSharedMem().GetPointer(), context);
+ StoreToLockFreeAtomicType(&GetFormat()->standard_local_system_clock_context, context);
}
void SharedMemory::UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context) {
- shared_memory_format.standard_network_system_clock_context.StoreData(
- system.Kernel().GetTimeSharedMem().GetPointer(), context);
+ StoreToLockFreeAtomicType(&GetFormat()->standard_network_system_clock_context, context);
}
void SharedMemory::SetAutomaticCorrectionEnabled(bool is_enabled) {
- shared_memory_format.standard_user_system_clock_automatic_correction.StoreData(
- system.Kernel().GetTimeSharedMem().GetPointer(), is_enabled);
+ StoreToLockFreeAtomicType(
+ &GetFormat()->is_standard_user_system_clock_automatic_correction_enabled, is_enabled);
+}
+
+SharedMemory::Format* SharedMemory::GetFormat() {
+ return reinterpret_cast(system.Kernel().GetTimeSharedMem().GetPointer());
}
} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_sharedmemory.h b/src/core/hle/service/time/time_sharedmemory.h
index 0613f3f56..b707a1581 100755
--- a/src/core/hle/service/time/time_sharedmemory.h
+++ b/src/core/hle/service/time/time_sharedmemory.h
@@ -10,45 +10,68 @@
namespace Service::Time {
+// Note: this type is not safe for concurrent writes.
+template
+struct LockFreeAtomicType {
+ u32 counter_;
+ std::array value_;
+};
+
+template
+static inline void StoreToLockFreeAtomicType(LockFreeAtomicType* 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
+static inline T LoadFromLockFreeAtomicType(const LockFreeAtomicType* 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 {
public:
explicit SharedMemory(Core::System& system_);
~SharedMemory();
- // TODO(ogniK): We have to properly simulate memory barriers, how are we going to do this?
- template
- struct MemoryBarrier {
- static_assert(std::is_trivially_copyable_v, "T must be trivially copyable");
- u32_le read_attempt{};
- std::array 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
struct Format {
- MemoryBarrier standard_steady_clock_timepoint;
- MemoryBarrier standard_local_system_clock_context;
- MemoryBarrier standard_network_system_clock_context;
- MemoryBarrier standard_user_system_clock_automatic_correction;
- u32_le format_version;
+ LockFreeAtomicType standard_steady_clock_timepoint;
+ LockFreeAtomicType standard_local_system_clock_context;
+ LockFreeAtomicType standard_network_system_clock_context;
+ LockFreeAtomicType is_standard_user_system_clock_automatic_correction_enabled;
+ 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");
void SetupStandardSteadyClock(const Common::UUID& clock_source_id,
@@ -56,10 +79,10 @@ public:
void UpdateLocalSystemClockContext(const Clock::SystemClockContext& context);
void UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context);
void SetAutomaticCorrectionEnabled(bool is_enabled);
+ Format* GetFormat();
private:
Core::System& system;
- Format shared_memory_format{};
};
} // namespace Service::Time
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index ef74d621e..ab2c1ae27 100755
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -78,7 +78,6 @@ void EmuThread::run() {
gpu.Start();
m_system.GetCpuManager().OnGpuReady();
- m_system.RegisterExitCallback([this] { m_stop_source.request_stop(); });
if (m_system.DebuggerEnabled()) {
m_system.InitializeDebugger();
diff --git a/src/yuzu/discord_impl.cpp b/src/yuzu/discord_impl.cpp
index 020b8ae2c..567a89345 100755
--- a/src/yuzu/discord_impl.cpp
+++ b/src/yuzu/discord_impl.cpp
@@ -38,7 +38,7 @@ void DiscordImpl::Update() {
system.GetAppLoader().ReadTitle(title);
}
DiscordRichPresence presence{};
- presence.largeImageKey = "yuzu_logo";
+ presence.largeImageKey = "yuzu_logo_ea";
presence.largeImageText = "yuzu is an emulator for the Nintendo Switch";
if (system.IsPoweredOn()) {
presence.state = title.c_str();
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 0c9752fcd..823e431e4 100755
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -1710,6 +1710,11 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
system->RegisterExecuteProgramCallback(
[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::MouseActivity, this, &GMainWindow::OnMouseActivity);
// BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views
@@ -4177,6 +4182,10 @@ bool GMainWindow::ConfirmForceLockedExit() {
}
void GMainWindow::RequestGameExit() {
+ if (!system->IsPoweredOn()) {
+ return;
+ }
+
auto& sm{system->ServiceManager()};
auto applet_oe = sm.GetService("appletOE");
auto applet_ae = sm.GetService("appletAE");