From dca8e9dc1ef0691462d464167145fe126cdcb1d2 Mon Sep 17 00:00:00 2001 From: "Admiral H. Curtiss" Date: Fri, 10 Mar 2023 17:51:26 +0100 Subject: [PATCH] HW/DVDThread: Refactor to class. --- Source/Core/Core/HW/DVD/DVDInterface.cpp | 31 +-- Source/Core/Core/HW/DVD/DVDThread.cpp | 264 ++++++++--------------- Source/Core/Core/HW/DVD/DVDThread.h | 144 +++++++++---- Source/Core/Core/IOS/DI/DI.cpp | 5 +- Source/Core/Core/System.cpp | 8 +- Source/Core/Core/System.h | 4 +- 6 files changed, 228 insertions(+), 228 deletions(-) diff --git a/Source/Core/Core/HW/DVD/DVDInterface.cpp b/Source/Core/Core/HW/DVD/DVDInterface.cpp index 7c5ee10ff6..d7e4c08e98 100644 --- a/Source/Core/Core/HW/DVD/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVD/DVDInterface.cpp @@ -117,7 +117,7 @@ void DVDInterfaceManager::DoState(PointerWrap& p) p.Do(m_disc_path_to_insert); - DVDThread::DoState(p); + m_system.GetDVDThread().DoState(p); m_adpcm_decoder.DoState(p); } @@ -228,8 +228,8 @@ void DVDInterfaceManager::DTKStreamingCallback(DIInterruptType interrupt_type, ticks_to_dtk -= cycles_late; if (read_length > 0) { - DVDThread::StartRead(read_offset, read_length, DiscIO::PARTITION_NONE, ReplyType::DTK, - ticks_to_dtk); + m_system.GetDVDThread().StartRead(read_offset, read_length, DiscIO::PARTITION_NONE, + ReplyType::DTK, ticks_to_dtk); } else { @@ -243,7 +243,7 @@ void DVDInterfaceManager::Init() { ASSERT(!IsDiscInside()); - DVDThread::Start(); + m_system.GetDVDThread().Start(); m_DISR.Hex = 0; m_DICVR.Hex = 1; // Disc Channel relies on cover being open when no disc is inserted @@ -317,7 +317,7 @@ void DVDInterfaceManager::ResetDrive(bool spinup) void DVDInterfaceManager::Shutdown() { - DVDThread::Stop(); + m_system.GetDVDThread().Stop(); } static u64 GetDiscEndOffset(const DiscIO::VolumeDisc& disc) @@ -390,7 +390,7 @@ void DVDInterfaceManager::SetDisc( if (had_disc != has_disc) ExpansionInterface::g_rtc_flags[ExpansionInterface::RTCFlag::DiscChanged] = true; - DVDThread::SetDisc(std::move(disc)); + m_system.GetDVDThread().SetDisc(std::move(disc)); SetLidOpen(); ResetDrive(false); @@ -398,7 +398,7 @@ void DVDInterfaceManager::SetDisc( bool DVDInterfaceManager::IsDiscInside() const { - return DVDThread::HasDisc(); + return m_system.GetDVDThread().HasDisc(); } void DVDInterfaceManager::AutoChangeDiscCallback(Core::System& system, u64 userdata, s64 cyclesLate) @@ -494,10 +494,12 @@ void DVDInterfaceManager::SetLidOpen() bool DVDInterfaceManager::UpdateRunningGameMetadata(std::optional title_id) { - if (!DVDThread::HasDisc()) + auto& dvd_thread = m_system.GetDVDThread(); + + if (!dvd_thread.HasDisc()) return false; - return DVDThread::UpdateRunningGameMetadata(IOS::HLE::DIDevice::GetCurrentPartition(), title_id); + return dvd_thread.UpdateRunningGameMetadata(IOS::HLE::DIDevice::GetCurrentPartition(), title_id); } void DVDInterfaceManager::RegisterMMIO(MMIO::Mapping* mmio, u32 base, bool is_wii) @@ -1069,7 +1071,7 @@ void DVDInterfaceManager::ExecuteCommand(ReplyType reply_type) const bool force_eject = eject && !kill; if (Config::Get(Config::MAIN_AUTO_DISC_CHANGE) && !Movie::IsPlayingInput() && - DVDThread::IsInsertedDiscRunning() && !m_auto_disc_change_paths.empty()) + m_system.GetDVDThread().IsInsertedDiscRunning() && !m_auto_disc_change_paths.empty()) { m_system.GetCoreTiming().ScheduleEvent( force_eject ? 0 : SystemTimers::GetTicksPerSecond() / 2, m_auto_change_disc); @@ -1306,7 +1308,8 @@ void DVDInterfaceManager::ScheduleReads(u64 offset, u32 length, const DiscIO::Pa auto& core_timing = m_system.GetCoreTiming(); const u64 current_time = core_timing.GetTicks(); const u32 ticks_per_second = SystemTimers::GetTicksPerSecond(); - const bool wii_disc = DVDThread::GetDiscType() == DiscIO::Platform::WiiDisc; + auto& dvd_thread = m_system.GetDVDThread(); + const bool wii_disc = dvd_thread.GetDiscType() == DiscIO::Platform::WiiDisc; // Whether we have performed a seek. bool seek = false; @@ -1323,7 +1326,7 @@ void DVDInterfaceManager::ScheduleReads(u64 offset, u32 length, const DiscIO::Pa // The variable dvd_offset tracks the actual offset on the DVD // that the disc drive starts reading at, which differs in two ways: // It's rounded to a whole ECC block and never uses Wii partition addressing. - u64 dvd_offset = DVDThread::PartitionOffsetToRawOffset(offset, partition); + u64 dvd_offset = dvd_thread.PartitionOffsetToRawOffset(offset, partition); dvd_offset = Common::AlignDown(dvd_offset, DVD_ECC_BLOCK_SIZE); const u64 first_block = dvd_offset; @@ -1388,7 +1391,7 @@ void DVDInterfaceManager::ScheduleReads(u64 offset, u32 length, const DiscIO::Pa u32 buffered_blocks = 0; u32 unbuffered_blocks = 0; - const u32 bytes_per_chunk = partition != DiscIO::PARTITION_NONE && DVDThread::HasWiiHashes() ? + const u32 bytes_per_chunk = partition != DiscIO::PARTITION_NONE && dvd_thread.HasWiiHashes() ? DiscIO::VolumeWii::BLOCK_DATA_SIZE : DVD_ECC_BLOCK_SIZE; @@ -1449,7 +1452,7 @@ void DVDInterfaceManager::ScheduleReads(u64 offset, u32 length, const DiscIO::Pa // Schedule this read to complete at the appropriate time const ReplyType chunk_reply_type = chunk_length == length ? reply_type : ReplyType::NoReply; - DVDThread::StartReadToEmulatedRAM(output_address, offset, chunk_length, partition, + dvd_thread.StartReadToEmulatedRAM(output_address, offset, chunk_length, partition, chunk_reply_type, ticks_until_completion); // Advance the read window diff --git a/Source/Core/Core/HW/DVD/DVDThread.cpp b/Source/Core/Core/HW/DVD/DVDThread.cpp index 3041af262c..1e64aa9fd4 100644 --- a/Source/Core/Core/HW/DVD/DVDThread.cpp +++ b/Source/Core/Core/HW/DVD/DVDThread.cpp @@ -36,120 +36,56 @@ namespace DVDThread { -struct ReadRequest -{ - bool copy_to_ram = false; - u32 output_address = 0; - u64 dvd_offset = 0; - u32 length = 0; - DiscIO::Partition partition{}; - - // This determines which code DVDInterface will run to reply - // to the emulated software. We can't use callbacks, - // because function pointers can't be stored in savestates. - DVDInterface::ReplyType reply_type = DVDInterface::ReplyType::NoReply; - - // IDs are used to uniquely identify a request. They must not be - // identical to IDs of any other requests that currently exist, but - // it's fine to re-use IDs of requests that have existed in the past. - u64 id = 0; - - // Only used for logging - u64 time_started_ticks = 0; - u64 realtime_started_us = 0; - u64 realtime_done_us = 0; -}; - -using ReadResult = std::pair>; - -static void StartDVDThread(DVDThreadState::Data& state); -static void StopDVDThread(DVDThreadState::Data& state); - -static void DVDThread(); -static void WaitUntilIdle(); - -static void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offset, u32 length, - const DiscIO::Partition& partition, - DVDInterface::ReplyType reply_type, s64 ticks_until_completion); - -static void FinishRead(Core::System& system, u64 id, s64 cycles_late); - -struct DVDThreadState::Data -{ - CoreTiming::EventType* finish_read = nullptr; - - u64 next_id = 0; - - 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 - - Common::SPSCQueue request_queue; - Common::SPSCQueue result_queue; - std::map result_map; - - std::unique_ptr disc; - - FileMonitor::FileLogger file_logger; -}; - -DVDThreadState::DVDThreadState() : m_data(std::make_unique()) +DVDThreadManager::DVDThreadManager(Core::System& system) : m_system(system) { } -DVDThreadState::~DVDThreadState() = default; +DVDThreadManager::~DVDThreadManager() = default; -void Start() +void DVDThreadManager::Start() { - auto& system = Core::System::GetInstance(); - auto& state = system.GetDVDThreadState().GetData(); + m_finish_read = m_system.GetCoreTiming().RegisterEvent("FinishReadDVDThread", GlobalFinishRead); - state.finish_read = system.GetCoreTiming().RegisterEvent("FinishReadDVDThread", FinishRead); - - state.request_queue_expanded.Reset(); - state.result_queue_expanded.Reset(); - state.request_queue.Clear(); - state.result_queue.Clear(); + m_request_queue_expanded.Reset(); + m_result_queue_expanded.Reset(); + m_request_queue.Clear(); + m_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. - state.next_id = 0; + m_next_id = 0; - StartDVDThread(state); + StartDVDThread(); } -static void StartDVDThread(DVDThreadState::Data& state) +void DVDThreadManager::StartDVDThread() { - ASSERT(!state.dvd_thread.joinable()); - state.dvd_thread_exiting.Clear(); - state.dvd_thread = std::thread(DVDThread); + ASSERT(!m_dvd_thread.joinable()); + m_dvd_thread_exiting.Clear(); + m_dvd_thread = std::thread(&DVDThreadManager::DVDThreadMain, this); } -void Stop() +void DVDThreadManager::Stop() { - auto& state = Core::System::GetInstance().GetDVDThreadState().GetData(); - StopDVDThread(state); - state.disc.reset(); + StopDVDThread(); + m_disc.reset(); } -static void StopDVDThread(DVDThreadState::Data& state) +void DVDThreadManager::StopDVDThread() { - ASSERT(state.dvd_thread.joinable()); + ASSERT(m_dvd_thread.joinable()); // 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(); + m_dvd_thread_exiting.Set(); + m_request_queue_expanded.Set(); - state.dvd_thread.join(); + m_dvd_thread.join(); } -void DoState(PointerWrap& p) +void DVDThreadManager::DoState(PointerWrap& p) { - auto& state = Core::System::GetInstance().GetDVDThreadState().GetData(); - // By waiting for the DVD thread to be done working, we ensure // that request_queue will be empty and that the DVD thread // won't be touching anything while this function runs. @@ -159,14 +95,14 @@ void DoState(PointerWrap& p) // PointerWrap::Do supports std::map but not Common::SPSCQueue. // This won't affect the behavior of FinishRead. ReadResult result; - while (state.result_queue.Pop(result)) - state.result_map.emplace(result.first.id, std::move(result)); + while (m_result_queue.Pop(result)) + m_result_map.emplace(result.first.id, std::move(result)); // Both queues are now empty, so we don't need to savestate them. - p.Do(state.result_map); - p.Do(state.next_id); + p.Do(m_result_map); + p.Do(m_next_id); - // state.disc isn't savestated (because it points to files on the + // m_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. @@ -178,7 +114,7 @@ void DoState(PointerWrap& p) if (had_disc) PanicAlertFmtT("An inserted disc was expected but not found."); else - state.disc.reset(); + m_disc.reset(); } // TODO: Savestates can be smaller if the buffers of results aren't saved, @@ -192,123 +128,110 @@ void DoState(PointerWrap& p) // was made. Handling that properly may be more effort than it's worth. } -void SetDisc(std::unique_ptr disc) +void DVDThreadManager::SetDisc(std::unique_ptr disc) { - auto& state = Core::System::GetInstance().GetDVDThreadState().GetData(); - WaitUntilIdle(); - state.disc = std::move(disc); + m_disc = std::move(disc); } -bool HasDisc() +bool DVDThreadManager::HasDisc() const { - auto& state = Core::System::GetInstance().GetDVDThreadState().GetData(); - return state.disc != nullptr; + return m_disc != nullptr; } -bool HasWiiHashes() +bool DVDThreadManager::HasWiiHashes() const { // HasWiiHashes is thread-safe, so calling WaitUntilIdle isn't necessary. - auto& state = Core::System::GetInstance().GetDVDThreadState().GetData(); - return state.disc->HasWiiHashes(); + return m_disc->HasWiiHashes(); } -DiscIO::Platform GetDiscType() +DiscIO::Platform DVDThreadManager::GetDiscType() const { // GetVolumeType is thread-safe, so calling WaitUntilIdle isn't necessary. - auto& state = Core::System::GetInstance().GetDVDThreadState().GetData(); - return state.disc->GetVolumeType(); + return m_disc->GetVolumeType(); } -u64 PartitionOffsetToRawOffset(u64 offset, const DiscIO::Partition& partition) +u64 DVDThreadManager::PartitionOffsetToRawOffset(u64 offset, const DiscIO::Partition& partition) { // PartitionOffsetToRawOffset is thread-safe, so calling WaitUntilIdle isn't necessary. - auto& state = Core::System::GetInstance().GetDVDThreadState().GetData(); - return state.disc->PartitionOffsetToRawOffset(offset, partition); + return m_disc->PartitionOffsetToRawOffset(offset, partition); } -IOS::ES::TMDReader GetTMD(const DiscIO::Partition& partition) +IOS::ES::TMDReader DVDThreadManager::GetTMD(const DiscIO::Partition& partition) { - auto& state = Core::System::GetInstance().GetDVDThreadState().GetData(); WaitUntilIdle(); - return state.disc->GetTMD(partition); + return m_disc->GetTMD(partition); } -IOS::ES::TicketReader GetTicket(const DiscIO::Partition& partition) +IOS::ES::TicketReader DVDThreadManager::GetTicket(const DiscIO::Partition& partition) { - auto& state = Core::System::GetInstance().GetDVDThreadState().GetData(); WaitUntilIdle(); - return state.disc->GetTicket(partition); + return m_disc->GetTicket(partition); } -bool IsInsertedDiscRunning() +bool DVDThreadManager::IsInsertedDiscRunning() { - auto& state = Core::System::GetInstance().GetDVDThreadState().GetData(); - - if (!state.disc) + if (!m_disc) return false; WaitUntilIdle(); - return SConfig::GetInstance().GetGameID() == state.disc->GetGameID(); + return SConfig::GetInstance().GetGameID() == m_disc->GetGameID(); } -bool UpdateRunningGameMetadata(const DiscIO::Partition& partition, std::optional title_id) +bool DVDThreadManager::UpdateRunningGameMetadata(const DiscIO::Partition& partition, + std::optional title_id) { - auto& state = Core::System::GetInstance().GetDVDThreadState().GetData(); - - if (!state.disc) + if (!m_disc) return false; WaitUntilIdle(); if (title_id) { - const std::optional volume_title_id = state.disc->GetTitleID(partition); + const std::optional volume_title_id = m_disc->GetTitleID(partition); if (!volume_title_id || *volume_title_id != *title_id) return false; } - SConfig::GetInstance().SetRunningGameMetadata(*state.disc, partition); + SConfig::GetInstance().SetRunningGameMetadata(*m_disc, partition); return true; } -void WaitUntilIdle() +void DVDThreadManager::WaitUntilIdle() { ASSERT(Core::IsCPUThread()); - auto& state = Core::System::GetInstance().GetDVDThreadState().GetData(); + while (!m_request_queue.Empty()) + m_result_queue_expanded.Wait(); - while (!state.request_queue.Empty()) - state.result_queue_expanded.Wait(); - - StopDVDThread(state); - StartDVDThread(state); + StopDVDThread(); + StartDVDThread(); } -void StartRead(u64 dvd_offset, u32 length, const DiscIO::Partition& partition, - DVDInterface::ReplyType reply_type, s64 ticks_until_completion) +void DVDThreadManager::StartRead(u64 dvd_offset, u32 length, const DiscIO::Partition& partition, + DVDInterface::ReplyType reply_type, s64 ticks_until_completion) { StartReadInternal(false, 0, dvd_offset, length, partition, reply_type, ticks_until_completion); } -void StartReadToEmulatedRAM(u32 output_address, u64 dvd_offset, u32 length, - const DiscIO::Partition& partition, DVDInterface::ReplyType reply_type, - s64 ticks_until_completion) +void DVDThreadManager::StartReadToEmulatedRAM(u32 output_address, u64 dvd_offset, u32 length, + const DiscIO::Partition& partition, + DVDInterface::ReplyType reply_type, + s64 ticks_until_completion) { StartReadInternal(true, output_address, dvd_offset, length, partition, reply_type, ticks_until_completion); } -static void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offset, u32 length, - const DiscIO::Partition& partition, - DVDInterface::ReplyType reply_type, s64 ticks_until_completion) +void DVDThreadManager::StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offset, + u32 length, const DiscIO::Partition& partition, + DVDInterface::ReplyType reply_type, + s64 ticks_until_completion) { ASSERT(Core::IsCPUThread()); - auto& system = Core::System::GetInstance(); - auto& core_timing = system.GetCoreTiming(); - auto& state = system.GetDVDThreadState().GetData(); + auto& core_timing = m_system.GetCoreTiming(); ReadRequest request; @@ -319,22 +242,25 @@ static void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offs request.partition = partition; request.reply_type = reply_type; - u64 id = state.next_id++; + u64 id = m_next_id++; request.id = id; request.time_started_ticks = core_timing.GetTicks(); request.realtime_started_us = Common::Timer::NowUs(); - state.request_queue.Push(std::move(request)); - state.request_queue_expanded.Set(); + m_request_queue.Push(std::move(request)); + m_request_queue_expanded.Set(); - core_timing.ScheduleEvent(ticks_until_completion, state.finish_read, id); + core_timing.ScheduleEvent(ticks_until_completion, m_finish_read, id); } -static void FinishRead(Core::System& system, u64 id, s64 cycles_late) +void DVDThreadManager::GlobalFinishRead(Core::System& system, u64 id, s64 cycles_late) { - auto& state = system.GetDVDThreadState().GetData(); + system.GetDVDThread().FinishRead(id, cycles_late); +} +void DVDThreadManager::FinishRead(u64 id, s64 cycles_late) +{ // 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 @@ -346,23 +272,23 @@ static void FinishRead(Core::System& system, 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 = state.result_map.find(id); - if (it != state.result_map.end()) + auto it = m_result_map.find(id); + if (it != m_result_map.end()) { result = std::move(it->second); - state.result_map.erase(it); + m_result_map.erase(it); } else { while (true) { - while (!state.result_queue.Pop(result)) - state.result_queue_expanded.Wait(); + while (!m_result_queue.Pop(result)) + m_result_queue_expanded.Wait(); if (result.first.id == id) break; else - state.result_map.emplace(result.first.id, std::move(result)); + m_result_map.emplace(result.first.id, std::move(result)); } } // We have now obtained the right ReadResult. @@ -376,10 +302,10 @@ static void FinishRead(Core::System& system, u64 id, s64 cycles_late) "Emulated time including delay: {} us.", request.realtime_done_us - request.realtime_started_us, Common::Timer::NowUs() - request.realtime_started_us, - (system.GetCoreTiming().GetTicks() - request.time_started_ticks) / + (m_system.GetCoreTiming().GetTicks() - request.time_started_ticks) / (SystemTimers::GetTicksPerSecond() / 1000000)); - auto& dvd_interface = system.GetDVDInterface(); + auto& dvd_interface = m_system.GetDVDInterface(); DVDInterface::DIInterruptType interrupt; if (buffer.size() != request.length) { @@ -393,7 +319,7 @@ static void FinishRead(Core::System& system, u64 id, s64 cycles_late) { if (request.copy_to_ram) { - auto& memory = system.GetMemory(); + auto& memory = m_system.GetMemory(); memory.CopyToEmu(request.output_address, buffer.data(), request.length); } @@ -404,34 +330,32 @@ static void FinishRead(Core::System& system, u64 id, s64 cycles_late) dvd_interface.FinishExecutingCommand(request.reply_type, interrupt, cycles_late, buffer); } -static void DVDThread() +void DVDThreadManager::DVDThreadMain() { - auto& state = Core::System::GetInstance().GetDVDThreadState().GetData(); - Common::SetCurrentThreadName("DVD thread"); while (true) { - state.request_queue_expanded.Wait(); + m_request_queue_expanded.Wait(); - if (state.dvd_thread_exiting.IsSet()) + if (m_dvd_thread_exiting.IsSet()) return; ReadRequest request; - while (state.request_queue.Pop(request)) + while (m_request_queue.Pop(request)) { - state.file_logger.Log(*state.disc, request.partition, request.dvd_offset); + m_file_logger.Log(*m_disc, request.partition, request.dvd_offset); std::vector buffer(request.length); - if (!state.disc->Read(request.dvd_offset, request.length, buffer.data(), request.partition)) + if (!m_disc->Read(request.dvd_offset, request.length, buffer.data(), request.partition)) buffer.resize(0); request.realtime_done_us = Common::Timer::NowUs(); - state.result_queue.Push(ReadResult(std::move(request), std::move(buffer))); - state.result_queue_expanded.Set(); + m_result_queue.Push(ReadResult(std::move(request), std::move(buffer))); + m_result_queue_expanded.Set(); - if (state.dvd_thread_exiting.IsSet()) + if (m_dvd_thread_exiting.IsSet()) return; } } diff --git a/Source/Core/Core/HW/DVD/DVDThread.h b/Source/Core/Core/HW/DVD/DVDThread.h index 66ffa246ee..fee48ea1ad 100644 --- a/Source/Core/Core/HW/DVD/DVDThread.h +++ b/Source/Core/Core/HW/DVD/DVDThread.h @@ -3,13 +3,32 @@ #pragma once +#include #include #include +#include +#include #include #include "Common/CommonTypes.h" +#include "Common/Event.h" +#include "Common/Flag.h" +#include "Common/SPSCQueue.h" + +#include "Core/HW/DVD/DVDInterface.h" +#include "Core/HW/DVD/FileMonitor.h" + +#include "DiscIO/Volume.h" class PointerWrap; +namespace Core +{ +class System; +} +namespace CoreTiming +{ +struct EventType; +} namespace DiscIO { struct Partition; @@ -34,46 +53,99 @@ class TicketReader; namespace DVDThread { -class DVDThreadState +class DVDThreadManager { public: - DVDThreadState(); - DVDThreadState(const DVDThreadState&) = delete; - DVDThreadState(DVDThreadState&&) = delete; - DVDThreadState& operator=(const DVDThreadState&) = delete; - DVDThreadState& operator=(DVDThreadState&&) = delete; - ~DVDThreadState(); + explicit DVDThreadManager(Core::System& system); + DVDThreadManager(const DVDThreadManager&) = delete; + DVDThreadManager(DVDThreadManager&&) = delete; + DVDThreadManager& operator=(const DVDThreadManager&) = delete; + DVDThreadManager& operator=(DVDThreadManager&&) = delete; + ~DVDThreadManager(); - struct Data; - Data& GetData() { return *m_data; } + void Start(); + void Stop(); + void DoState(PointerWrap& p); + + void SetDisc(std::unique_ptr disc); + bool HasDisc() const; + + bool HasWiiHashes() const; + DiscIO::Platform GetDiscType() const; + u64 PartitionOffsetToRawOffset(u64 offset, const DiscIO::Partition& partition); + IOS::ES::TMDReader GetTMD(const DiscIO::Partition& partition); + IOS::ES::TicketReader GetTicket(const DiscIO::Partition& partition); + bool IsInsertedDiscRunning(); + // This function returns true and calls SConfig::SetRunningGameMetadata(Volume&, Partition&) + // if both of the following conditions are true: + // - A disc is inserted + // - The title_id argument doesn't contain a value, or its value matches the disc's title ID + bool UpdateRunningGameMetadata(const DiscIO::Partition& partition, + std::optional title_id = {}); + + void StartRead(u64 dvd_offset, u32 length, const DiscIO::Partition& partition, + DVDInterface::ReplyType reply_type, s64 ticks_until_completion); + void StartReadToEmulatedRAM(u32 output_address, u64 dvd_offset, u32 length, + const DiscIO::Partition& partition, + DVDInterface::ReplyType reply_type, s64 ticks_until_completion); private: - std::unique_ptr m_data; + void StartDVDThread(); + void StopDVDThread(); + void WaitUntilIdle(); + + void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offset, u32 length, + const DiscIO::Partition& partition, DVDInterface::ReplyType reply_type, + s64 ticks_until_completion); + + static void GlobalFinishRead(Core::System& system, u64 id, s64 cycles_late); + void FinishRead(u64 id, s64 cycles_late); + + void DVDThreadMain(); + + struct ReadRequest + { + bool copy_to_ram = false; + u32 output_address = 0; + u64 dvd_offset = 0; + u32 length = 0; + DiscIO::Partition partition{}; + + // This determines which code DVDInterface will run to reply + // to the emulated software. We can't use callbacks, + // because function pointers can't be stored in savestates. + DVDInterface::ReplyType reply_type = DVDInterface::ReplyType::NoReply; + + // IDs are used to uniquely identify a request. They must not be + // identical to IDs of any other requests that currently exist, but + // it's fine to re-use IDs of requests that have existed in the past. + u64 id = 0; + + // Only used for logging + u64 time_started_ticks = 0; + u64 realtime_started_us = 0; + u64 realtime_done_us = 0; + }; + + using ReadResult = std::pair>; + + CoreTiming::EventType* m_finish_read = nullptr; + + u64 m_next_id = 0; + + std::thread m_dvd_thread; + Common::Event m_request_queue_expanded; // Is set by CPU thread + Common::Event m_result_queue_expanded; // Is set by DVD thread + Common::Flag m_dvd_thread_exiting = Common::Flag(false); // Is set by CPU thread + + Common::SPSCQueue m_request_queue; + Common::SPSCQueue m_result_queue; + std::map m_result_map; + + std::unique_ptr m_disc; + + FileMonitor::FileLogger m_file_logger; + + Core::System& m_system; }; - -void Start(); -void Stop(); -void DoState(PointerWrap& p); - -void SetDisc(std::unique_ptr disc); -bool HasDisc(); - -bool HasWiiHashes(); -DiscIO::Platform GetDiscType(); -u64 PartitionOffsetToRawOffset(u64 offset, const DiscIO::Partition& partition); -IOS::ES::TMDReader GetTMD(const DiscIO::Partition& partition); -IOS::ES::TicketReader GetTicket(const DiscIO::Partition& partition); -bool IsInsertedDiscRunning(); -// This function returns true and calls SConfig::SetRunningGameMetadata(Volume&, Partition&) -// if both of the following conditions are true: -// - A disc is inserted -// - The title_id argument doesn't contain a value, or its value matches the disc's title ID -bool UpdateRunningGameMetadata(const DiscIO::Partition& partition, - std::optional title_id = {}); - -void StartRead(u64 dvd_offset, u32 length, const DiscIO::Partition& partition, - DVDInterface::ReplyType reply_type, s64 ticks_until_completion); -void StartReadToEmulatedRAM(u32 output_address, u64 dvd_offset, u32 length, - const DiscIO::Partition& partition, DVDInterface::ReplyType reply_type, - s64 ticks_until_completion); } // namespace DVDThread diff --git a/Source/Core/Core/IOS/DI/DI.cpp b/Source/Core/Core/IOS/DI/DI.cpp index b393977c0b..abf26512a7 100644 --- a/Source/Core/Core/IOS/DI/DI.cpp +++ b/Source/Core/Core/IOS/DI/DI.cpp @@ -741,11 +741,12 @@ std::optional DIDevice::IOCtlV(const IOCtlVRequest& request) INFO_LOG_FMT(IOS_DI, "DVDLowOpenPartition: partition_offset {:#011x}", partition_offset); // Read TMD to the buffer - const ES::TMDReader tmd = DVDThread::GetTMD(m_current_partition); + auto& dvd_thread = system.GetDVDThread(); + const ES::TMDReader tmd = dvd_thread.GetTMD(m_current_partition); const std::vector& raw_tmd = tmd.GetBytes(); memory.CopyToEmu(request.io_vectors[0].address, raw_tmd.data(), raw_tmd.size()); - ReturnCode es_result = m_ios.GetES()->DIVerify(tmd, DVDThread::GetTicket(m_current_partition)); + ReturnCode es_result = m_ios.GetES()->DIVerify(tmd, dvd_thread.GetTicket(m_current_partition)); memory.Write_U32(es_result, request.io_vectors[1].address); return_value = DIResult::Success; diff --git a/Source/Core/Core/System.cpp b/Source/Core/Core/System.cpp index 4fa9da9f0c..361d837875 100644 --- a/Source/Core/Core/System.cpp +++ b/Source/Core/Core/System.cpp @@ -37,7 +37,7 @@ struct System::Impl { explicit Impl(System& system) : m_audio_interface(system), m_core_timing(system), m_dsp(system), m_dvd_interface(system), - m_gp_fifo(system), m_ppc_state(PowerPC::ppcState) + m_dvd_thread(system), m_gp_fifo(system), m_ppc_state(PowerPC::ppcState) { } @@ -51,7 +51,7 @@ struct System::Impl CPU::CPUManager m_cpu; DSP::DSPManager m_dsp; DVDInterface::DVDInterfaceManager m_dvd_interface; - DVDThread::DVDThreadState m_dvd_thread_state; + DVDThread::DVDThreadManager m_dvd_thread; ExpansionInterface::ExpansionInterfaceState m_expansion_interface_state; Fifo::FifoManager m_fifo; GeometryShaderManager m_geometry_shader_manager; @@ -143,9 +143,9 @@ DVDInterface::DVDInterfaceManager& System::GetDVDInterface() const return m_impl->m_dvd_interface; } -DVDThread::DVDThreadState& System::GetDVDThreadState() const +DVDThread::DVDThreadManager& System::GetDVDThread() const { - return m_impl->m_dvd_thread_state; + return m_impl->m_dvd_thread; } ExpansionInterface::ExpansionInterfaceState& System::GetExpansionInterfaceState() const diff --git a/Source/Core/Core/System.h b/Source/Core/Core/System.h index 4e2a86e12a..2ec79e3051 100644 --- a/Source/Core/Core/System.h +++ b/Source/Core/Core/System.h @@ -37,7 +37,7 @@ class DVDInterfaceManager; } namespace DVDThread { -class DVDThreadState; +class DVDThreadManager; } namespace ExpansionInterface { @@ -128,7 +128,7 @@ public: CommandProcessor::CommandProcessorManager& GetCommandProcessor() const; DSP::DSPManager& GetDSP() const; DVDInterface::DVDInterfaceManager& GetDVDInterface() const; - DVDThread::DVDThreadState& GetDVDThreadState() const; + DVDThread::DVDThreadManager& GetDVDThread() const; ExpansionInterface::ExpansionInterfaceState& GetExpansionInterfaceState() const; Fifo::FifoManager& GetFifo() const; GeometryShaderManager& GetGeometryShaderManager() const;