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 @@ -Artboard 1 \ No newline at end of file +newAsset 7 \ 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");