From 54ff4bd0cbbcf8bed5d8dd719b1196ac7b787c55 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sat, 25 Apr 2015 12:52:47 +0200 Subject: [PATCH] Read disc data asynchronously in DVDThread This gives the CPU thread more time to do CPU things. --- Source/Core/Core/HW/DVDInterface.cpp | 10 +++- Source/Core/Core/HW/DVDThread.cpp | 87 ++++++++++++++++++++++++++-- Source/Core/Core/HW/DVDThread.h | 1 + 3 files changed, 92 insertions(+), 6 deletions(-) diff --git a/Source/Core/Core/HW/DVDInterface.cpp b/Source/Core/Core/HW/DVDInterface.cpp index a60c21f7b9..12c34b818b 100644 --- a/Source/Core/Core/HW/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVDInterface.cpp @@ -314,6 +314,9 @@ static void FinishExecuteCommand(u64 userdata, int cyclesLate) static u32 ProcessDTKSamples(short *tempPCM, u32 num_samples) { + // TODO: Read audio data using the DVD thread instead of blocking on it? + DVDThread::WaitUntilIdle(); + u32 samples_processed = 0; do { @@ -425,12 +428,14 @@ const DiscIO::IVolume& GetVolume() bool SetVolumeName(const std::string& disc_path) { + DVDThread::WaitUntilIdle(); s_inserted_volume = std::unique_ptr(DiscIO::CreateVolumeFromFilename(disc_path)); return VolumeIsValid(); } bool SetVolumeDirectory(const std::string& full_path, bool is_wii, const std::string& apploader_path, const std::string& DOL_path) { + DVDThread::WaitUntilIdle(); s_inserted_volume = std::unique_ptr(DiscIO::CreateVolumeFromDirectory(full_path, is_wii, apploader_path, DOL_path)); return VolumeIsValid(); } @@ -459,9 +464,9 @@ bool IsDiscInside() // that the userdata string exists when called void EjectDiscCallback(u64 userdata, int cyclesLate) { - // Empty the drive - SetDiscInside(false); + DVDThread::WaitUntilIdle(); s_inserted_volume.reset(); + SetDiscInside(false); } void InsertDiscCallback(u64 userdata, int cyclesLate) @@ -516,6 +521,7 @@ bool DVDRead(u64 _iDVDOffset, u32 _iRamAddress, u32 _iLength, bool decrypt) bool ChangePartition(u64 offset) { + DVDThread::WaitUntilIdle(); return s_inserted_volume->ChangePartition(offset); } diff --git a/Source/Core/Core/HW/DVDThread.cpp b/Source/Core/Core/HW/DVDThread.cpp index 1182b6c1ee..52d7f01767 100644 --- a/Source/Core/Core/HW/DVDThread.cpp +++ b/Source/Core/Core/HW/DVDThread.cpp @@ -3,23 +3,42 @@ // Refer to the license.txt file included. #include +#include +#include +#include #include "Common/ChunkFile.h" #include "Common/CommonTypes.h" +#include "Common/Event.h" +#include "Common/Flag.h" #include "Common/MsgHandler.h" +#include "Common/Thread.h" +#include "Common/Logging/Log.h" +#include "Core/Core.h" #include "Core/CoreTiming.h" #include "Core/HW/DVDInterface.h" #include "Core/HW/DVDThread.h" +#include "Core/HW/Memmap.h" #include "DiscIO/Volume.h" namespace DVDThread { +static void DVDThread(); + static void FinishRead(u64 userdata, int cyclesLate); static int s_finish_read; +static std::thread s_dvd_thread; +static Common::Event s_dvd_thread_start_working; +static Common::Event s_dvd_thread_done_working; +static Common::Flag s_dvd_thread_exiting(false); + +static std::vector s_dvd_buffer; +static bool s_dvd_success; + static u64 s_dvd_offset; static u32 s_output_address; static u32 s_length; @@ -32,15 +51,32 @@ static int s_callback_event_type; void Start() { s_finish_read = CoreTiming::RegisterEvent("FinishReadDVDThread", FinishRead); + _assert_(!s_dvd_thread.joinable()); + s_dvd_thread = std::thread(DVDThread); } void Stop() { + _assert_(s_dvd_thread.joinable()); + // The DVD thread will return if s_DVD_thread_exiting + // is set when it starts working + s_dvd_thread_exiting.Set(); + s_dvd_thread_start_working.Set(); + + s_dvd_thread.join(); + + s_dvd_thread_exiting.Clear(); } void DoState(PointerWrap &p) { + WaitUntilIdle(); + + // TODO: Savestates can be smaller if s_DVD_buffer is not saved + p.Do(s_dvd_buffer); + p.Do(s_dvd_success); + p.Do(s_dvd_offset); p.Do(s_output_address); p.Do(s_length); @@ -48,28 +84,71 @@ void DoState(PointerWrap &p) p.Do(s_callback_event_type); } +void WaitUntilIdle() +{ + _assert_(Core::IsCPUThread()); + + // Wait until DVD thread isn't working + s_dvd_thread_done_working.Wait(); + + // Set the event again so that we still know that the DVD thread isn't working + s_dvd_thread_done_working.Set(); +} + void StartRead(u64 dvd_offset, u32 output_address, u32 length, bool decrypt, int callback_event_type, int ticks_until_completion) { + _assert_(Core::IsCPUThread()); + + s_dvd_thread_done_working.Wait(); + s_dvd_offset = dvd_offset; s_output_address = output_address; s_length = length; s_decrypt = decrypt; s_callback_event_type = callback_event_type; + + s_dvd_thread_start_working.Set(); + CoreTiming::ScheduleEvent(ticks_until_completion, s_finish_read); } static void FinishRead(u64 userdata, int cyclesLate) { - // Here is the actual disc reading - if (!DVDInterface::DVDRead(s_dvd_offset, s_output_address, s_length, s_decrypt)) - { + WaitUntilIdle(); + + if (s_dvd_success) + Memory::CopyToEmu(s_output_address, s_dvd_buffer.data(), s_length); + else PanicAlertT("The disc could not be read (at 0x%" PRIx64 " - 0x%" PRIx64 ").", s_dvd_offset, s_dvd_offset + s_length); - } + + // This will make the buffer take less space in savestates. + // Reducing the size doesn't change the amount of reserved memory, + // so this doesn't lead to extra memory allocations. + s_dvd_buffer.resize(0); // Notify the emulated software that the command has been executed CoreTiming::ScheduleEvent_Immediate(s_callback_event_type, DVDInterface::INT_TCINT); } +static void DVDThread() +{ + Common::SetCurrentThreadName("DVD thread"); + + while (true) + { + s_dvd_thread_done_working.Set(); + + s_dvd_thread_start_working.Wait(); + + if (s_dvd_thread_exiting.IsSet()) + return; + + s_dvd_buffer.resize(s_length); + + s_dvd_success = DVDInterface::GetVolume().Read(s_dvd_offset, s_length, s_dvd_buffer.data(), s_decrypt); + } +} + } diff --git a/Source/Core/Core/HW/DVDThread.h b/Source/Core/Core/HW/DVDThread.h index 0aa5f296cb..c2716ae7c8 100644 --- a/Source/Core/Core/HW/DVDThread.h +++ b/Source/Core/Core/HW/DVDThread.h @@ -14,6 +14,7 @@ void Start(); void Stop(); void DoState(PointerWrap &p); +void WaitUntilIdle(); void StartRead(u64 dvd_offset, u32 output_address, u32 length, bool decrypt, int callback_event_type, int ticks_until_completion);