From c4d5804f6021270499cde9a03d48a3ddbca7f4d7 Mon Sep 17 00:00:00 2001 From: "Admiral H. Curtiss" Date: Fri, 2 Sep 2022 22:20:14 +0200 Subject: [PATCH] HW: Move DVDThread variables to Core::System. --- Source/Core/Core/HW/DVD/DVDThread.cpp | 188 +++++++++++++++----------- Source/Core/Core/HW/DVD/DVDThread.h | 17 +++ Source/Core/Core/System.cpp | 8 ++ Source/Core/Core/System.h | 7 + 4 files changed, 144 insertions(+), 76 deletions(-) diff --git a/Source/Core/Core/HW/DVD/DVDThread.cpp b/Source/Core/Core/HW/DVD/DVDThread.cpp index 5590f58b6e..6c0ff43452 100644 --- a/Source/Core/Core/HW/DVD/DVDThread.cpp +++ b/Source/Core/Core/HW/DVD/DVDThread.cpp @@ -29,6 +29,7 @@ #include "Core/HW/Memmap.h" #include "Core/HW/SystemTimers.h" #include "Core/IOS/ES/Formats.h" +#include "Core/System.h" #include "DiscIO/Enums.h" #include "DiscIO/Volume.h" @@ -61,8 +62,8 @@ struct ReadRequest using ReadResult = std::pair>; -static void StartDVDThread(); -static void StopDVDThread(); +static void StartDVDThread(DVDThreadState::Data& state); +static void StopDVDThread(DVDThreadState::Data& state); static void DVDThread(); static void WaitUntilIdle(); @@ -72,82 +73,97 @@ static void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offs DVDInterface::ReplyType reply_type, s64 ticks_until_completion); static void FinishRead(u64 id, s64 cycles_late); -static CoreTiming::EventType* s_finish_read; -static u64 s_next_id = 0; +struct DVDThreadState::Data +{ + CoreTiming::EventType* finish_read; -static std::thread s_dvd_thread; -static Common::Event s_request_queue_expanded; // Is set by CPU thread -static Common::Event s_result_queue_expanded; // Is set by DVD thread -static Common::Flag s_dvd_thread_exiting(false); // Is set by CPU thread + u64 next_id = 0; -static Common::SPSCQueue s_request_queue; -static Common::SPSCQueue s_result_queue; -static std::map s_result_map; + std::thread dvd_thread; + Common::Event request_queue_expanded; // Is set by CPU thread + Common::Event result_queue_expanded; // Is set by DVD thread + Common::Flag dvd_thread_exiting = Common::Flag(false); // Is set by CPU thread -static std::unique_ptr s_disc; + Common::SPSCQueue request_queue; + Common::SPSCQueue result_queue; + std::map result_map; + + std::unique_ptr disc; +}; + +DVDThreadState::DVDThreadState() : m_data(std::make_unique()) +{ +} + +DVDThreadState::~DVDThreadState() = default; void Start() { - s_finish_read = CoreTiming::RegisterEvent("FinishReadDVDThread", FinishRead); + auto& state = Core::System::GetInstance().GetDVDThreadState().GetData(); - s_request_queue_expanded.Reset(); - s_result_queue_expanded.Reset(); - s_request_queue.Clear(); - s_result_queue.Clear(); + state.finish_read = CoreTiming::RegisterEvent("FinishReadDVDThread", FinishRead); + + state.request_queue_expanded.Reset(); + state.result_queue_expanded.Reset(); + state.request_queue.Clear(); + state.result_queue.Clear(); // This is reset on every launch for determinism, but it doesn't matter // much, because this will never get exposed to the emulated game. - s_next_id = 0; + state.next_id = 0; - StartDVDThread(); + StartDVDThread(state); } -static void StartDVDThread() +static void StartDVDThread(DVDThreadState::Data& state) { - ASSERT(!s_dvd_thread.joinable()); - s_dvd_thread_exiting.Clear(); - s_dvd_thread = std::thread(DVDThread); + ASSERT(!state.dvd_thread.joinable()); + state.dvd_thread_exiting.Clear(); + state.dvd_thread = std::thread(DVDThread); } void Stop() { - StopDVDThread(); - s_disc.reset(); + auto& state = Core::System::GetInstance().GetDVDThreadState().GetData(); + StopDVDThread(state); + state.disc.reset(); } -static void StopDVDThread() +static void StopDVDThread(DVDThreadState::Data& state) { - ASSERT(s_dvd_thread.joinable()); + ASSERT(state.dvd_thread.joinable()); - // By setting s_DVD_thread_exiting, we ask the DVD thread to cleanly exit. - // In case the request queue is empty, we need to set s_request_queue_expanded - // so that the DVD thread will wake up and check s_DVD_thread_exiting. - s_dvd_thread_exiting.Set(); - s_request_queue_expanded.Set(); + // By setting dvd_thread_exiting, we ask the DVD thread to cleanly exit. + // In case the request queue is empty, we need to set request_queue_expanded + // so that the DVD thread will wake up and check dvd_thread_exiting. + state.dvd_thread_exiting.Set(); + state.request_queue_expanded.Set(); - s_dvd_thread.join(); + state.dvd_thread.join(); } void DoState(PointerWrap& p) { + auto& state = Core::System::GetInstance().GetDVDThreadState().GetData(); + // By waiting for the DVD thread to be done working, we ensure - // that s_request_queue will be empty and that the DVD thread + // that request_queue will be empty and that the DVD thread // won't be touching anything while this function runs. WaitUntilIdle(); - // Move all results from s_result_queue to s_result_map because + // Move all results from result_queue to result_map because // PointerWrap::Do supports std::map but not Common::SPSCQueue. // This won't affect the behavior of FinishRead. ReadResult result; - while (s_result_queue.Pop(result)) - s_result_map.emplace(result.first.id, std::move(result)); + while (state.result_queue.Pop(result)) + state.result_map.emplace(result.first.id, std::move(result)); // Both queues are now empty, so we don't need to savestate them. - p.Do(s_result_map); - p.Do(s_next_id); + p.Do(state.result_map); + p.Do(state.next_id); - // s_disc isn't savestated (because it points to files on the + // state.disc isn't savestated (because it points to files on the // local system). Instead, we check that the status of the disc // is the same as when the savestate was made. This won't catch // cases of having the wrong disc inserted, though. @@ -159,7 +175,7 @@ void DoState(PointerWrap& p) if (had_disc) PanicAlertFmtT("An inserted disc was expected but not found."); else - s_disc.reset(); + state.disc.reset(); } // TODO: Savestates can be smaller if the buffers of results aren't saved, @@ -175,70 +191,82 @@ void DoState(PointerWrap& p) void SetDisc(std::unique_ptr disc) { + auto& state = Core::System::GetInstance().GetDVDThreadState().GetData(); + WaitUntilIdle(); - s_disc = std::move(disc); + state.disc = std::move(disc); } bool HasDisc() { - return s_disc != nullptr; + auto& state = Core::System::GetInstance().GetDVDThreadState().GetData(); + return state.disc != nullptr; } bool HasWiiHashes() { // HasWiiHashes is thread-safe, so calling WaitUntilIdle isn't necessary. - return s_disc->HasWiiHashes(); + auto& state = Core::System::GetInstance().GetDVDThreadState().GetData(); + return state.disc->HasWiiHashes(); } DiscIO::Platform GetDiscType() { // GetVolumeType is thread-safe, so calling WaitUntilIdle isn't necessary. - return s_disc->GetVolumeType(); + auto& state = Core::System::GetInstance().GetDVDThreadState().GetData(); + return state.disc->GetVolumeType(); } u64 PartitionOffsetToRawOffset(u64 offset, const DiscIO::Partition& partition) { // PartitionOffsetToRawOffset is thread-safe, so calling WaitUntilIdle isn't necessary. - return s_disc->PartitionOffsetToRawOffset(offset, partition); + auto& state = Core::System::GetInstance().GetDVDThreadState().GetData(); + return state.disc->PartitionOffsetToRawOffset(offset, partition); } IOS::ES::TMDReader GetTMD(const DiscIO::Partition& partition) { + auto& state = Core::System::GetInstance().GetDVDThreadState().GetData(); WaitUntilIdle(); - return s_disc->GetTMD(partition); + return state.disc->GetTMD(partition); } IOS::ES::TicketReader GetTicket(const DiscIO::Partition& partition) { + auto& state = Core::System::GetInstance().GetDVDThreadState().GetData(); WaitUntilIdle(); - return s_disc->GetTicket(partition); + return state.disc->GetTicket(partition); } bool IsInsertedDiscRunning() { - if (!s_disc) + auto& state = Core::System::GetInstance().GetDVDThreadState().GetData(); + + if (!state.disc) return false; WaitUntilIdle(); - return SConfig::GetInstance().GetGameID() == s_disc->GetGameID(); + return SConfig::GetInstance().GetGameID() == state.disc->GetGameID(); } bool UpdateRunningGameMetadata(const DiscIO::Partition& partition, std::optional title_id) { - if (!s_disc) + auto& state = Core::System::GetInstance().GetDVDThreadState().GetData(); + + if (!state.disc) return false; WaitUntilIdle(); if (title_id) { - const std::optional volume_title_id = s_disc->GetTitleID(partition); + const std::optional volume_title_id = state.disc->GetTitleID(partition); if (!volume_title_id || *volume_title_id != *title_id) return false; } - SConfig::GetInstance().SetRunningGameMetadata(*s_disc, partition); + SConfig::GetInstance().SetRunningGameMetadata(*state.disc, partition); return true; } @@ -246,11 +274,13 @@ void WaitUntilIdle() { ASSERT(Core::IsCPUThread()); - while (!s_request_queue.Empty()) - s_result_queue_expanded.Wait(); + auto& state = Core::System::GetInstance().GetDVDThreadState().GetData(); - StopDVDThread(); - StartDVDThread(); + while (!state.request_queue.Empty()) + state.result_queue_expanded.Wait(); + + StopDVDThread(state); + StartDVDThread(state); } void StartRead(u64 dvd_offset, u32 length, const DiscIO::Partition& partition, @@ -273,6 +303,8 @@ static void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offs { ASSERT(Core::IsCPUThread()); + auto& state = Core::System::GetInstance().GetDVDThreadState().GetData(); + ReadRequest request; request.copy_to_ram = copy_to_ram; @@ -282,21 +314,23 @@ static void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offs request.partition = partition; request.reply_type = reply_type; - u64 id = s_next_id++; + u64 id = state.next_id++; request.id = id; request.time_started_ticks = CoreTiming::GetTicks(); request.realtime_started_us = Common::Timer::NowUs(); - s_request_queue.Push(std::move(request)); - s_request_queue_expanded.Set(); + state.request_queue.Push(std::move(request)); + state.request_queue_expanded.Set(); - CoreTiming::ScheduleEvent(ticks_until_completion, s_finish_read, id); + CoreTiming::ScheduleEvent(ticks_until_completion, state.finish_read, id); } static void FinishRead(u64 id, s64 cycles_late) { - // We can't simply pop s_result_queue and always get the ReadResult + auto& state = Core::System::GetInstance().GetDVDThreadState().GetData(); + + // We can't simply pop result_queue and always get the ReadResult // we want, because the DVD thread may add ReadResults to the queue // in a different order than we want to get them. What we do instead // is to pop the queue until we find the ReadResult we want (the one @@ -307,23 +341,23 @@ static void FinishRead(u64 id, s64 cycles_late) // When this function is called again later, it will check the map for // the wanted ReadResult before it starts searching through the queue. ReadResult result; - auto it = s_result_map.find(id); - if (it != s_result_map.end()) + auto it = state.result_map.find(id); + if (it != state.result_map.end()) { result = std::move(it->second); - s_result_map.erase(it); + state.result_map.erase(it); } else { while (true) { - while (!s_result_queue.Pop(result)) - s_result_queue_expanded.Wait(); + while (!state.result_queue.Pop(result)) + state.result_queue_expanded.Wait(); if (result.first.id == id) break; else - s_result_map.emplace(result.first.id, std::move(result)); + state.result_map.emplace(result.first.id, std::move(result)); } } // We have now obtained the right ReadResult. @@ -363,30 +397,32 @@ static void FinishRead(u64 id, s64 cycles_late) static void DVDThread() { + auto& state = Core::System::GetInstance().GetDVDThreadState().GetData(); + Common::SetCurrentThreadName("DVD thread"); while (true) { - s_request_queue_expanded.Wait(); + state.request_queue_expanded.Wait(); - if (s_dvd_thread_exiting.IsSet()) + if (state.dvd_thread_exiting.IsSet()) return; ReadRequest request; - while (s_request_queue.Pop(request)) + while (state.request_queue.Pop(request)) { - FileMonitor::Log(*s_disc, request.partition, request.dvd_offset); + FileMonitor::Log(*state.disc, request.partition, request.dvd_offset); std::vector buffer(request.length); - if (!s_disc->Read(request.dvd_offset, request.length, buffer.data(), request.partition)) + if (!state.disc->Read(request.dvd_offset, request.length, buffer.data(), request.partition)) buffer.resize(0); request.realtime_done_us = Common::Timer::NowUs(); - s_result_queue.Push(ReadResult(std::move(request), std::move(buffer))); - s_result_queue_expanded.Set(); + state.result_queue.Push(ReadResult(std::move(request), std::move(buffer))); + state.result_queue_expanded.Set(); - if (s_dvd_thread_exiting.IsSet()) + if (state.dvd_thread_exiting.IsSet()) return; } } diff --git a/Source/Core/Core/HW/DVD/DVDThread.h b/Source/Core/Core/HW/DVD/DVDThread.h index fe427e2e57..66ffa246ee 100644 --- a/Source/Core/Core/HW/DVD/DVDThread.h +++ b/Source/Core/Core/HW/DVD/DVDThread.h @@ -34,6 +34,23 @@ class TicketReader; namespace DVDThread { +class DVDThreadState +{ +public: + DVDThreadState(); + DVDThreadState(const DVDThreadState&) = delete; + DVDThreadState(DVDThreadState&&) = delete; + DVDThreadState& operator=(const DVDThreadState&) = delete; + DVDThreadState& operator=(DVDThreadState&&) = delete; + ~DVDThreadState(); + + struct Data; + Data& GetData() { return *m_data; } + +private: + std::unique_ptr m_data; +}; + void Start(); void Stop(); void DoState(PointerWrap& p); diff --git a/Source/Core/Core/System.cpp b/Source/Core/Core/System.cpp index 4320bded39..aa257af6f3 100644 --- a/Source/Core/Core/System.cpp +++ b/Source/Core/Core/System.cpp @@ -7,6 +7,7 @@ #include "AudioCommon/SoundStream.h" #include "Core/Config/MainSettings.h" +#include "Core/HW/DVD/DVDThread.h" namespace Core { @@ -15,6 +16,8 @@ struct System::Impl std::unique_ptr m_sound_stream; bool m_sound_stream_running = false; bool m_audio_dump_started = false; + + DVDThread::DVDThreadState m_dvd_thread_state; }; System::System() : m_impl{std::make_unique()} @@ -58,4 +61,9 @@ void System::SetAudioDumpStarted(bool started) { m_impl->m_audio_dump_started = started; } + +DVDThread::DVDThreadState& System::GetDVDThreadState() const +{ + return m_impl->m_dvd_thread_state; +} } // namespace Core diff --git a/Source/Core/Core/System.h b/Source/Core/Core/System.h index 85b7087095..659caae914 100644 --- a/Source/Core/Core/System.h +++ b/Source/Core/Core/System.h @@ -7,6 +7,11 @@ class SoundStream; +namespace DVDThread +{ +class DVDThreadState; +} + namespace Core { // Central class that encapsulates the running system. @@ -40,6 +45,8 @@ public: bool IsAudioDumpStarted() const; void SetAudioDumpStarted(bool started); + DVDThread::DVDThreadState& GetDVDThreadState() const; + private: System();