From a2f352c4225606bf0f825bf29d999776271e3825 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Thu, 11 Aug 2016 22:31:45 +0200 Subject: [PATCH 1/8] DVDThread: Use structs for requests and results This is a preparation for adding a queue to DVDThread. Currently, s_read_request and s_read_result act somewhat like queues that only can contain one object. --- Source/Core/Core/HW/DVDThread.cpp | 122 ++++++++++++++++-------------- Source/Core/Core/State.cpp | 4 +- 2 files changed, 67 insertions(+), 59 deletions(-) diff --git a/Source/Core/Core/HW/DVDThread.cpp b/Source/Core/Core/HW/DVDThread.cpp index e79de14019..491a1fea45 100644 --- a/Source/Core/Core/HW/DVDThread.cpp +++ b/Source/Core/Core/HW/DVDThread.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "Common/ChunkFile.h" @@ -27,6 +28,25 @@ namespace DVDThread { +struct ReadRequest +{ + u64 dvd_offset; + u32 output_address; + u32 length; + bool decrypt; + + // This determines which function will be used as a callback. + // We can't have a function pointer here, because they can't be in savestates. + bool reply_to_ios; + + // Only used for logging + u64 time_started_ticks; + u64 realtime_started_us; + u64 realtime_done_us; +}; + +using ReadResult = std::pair>; + static void DVDThread(); static void FinishRead(u64 userdata, s64 cycles_late); @@ -37,22 +57,8 @@ 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 u64 s_time_read_started; -static bool s_dvd_success; - -static u64 s_dvd_offset; -static u32 s_output_address; -static u32 s_length; -static bool s_decrypt; - -// This determines which function will be used as a callback. -// We can't have a function pointer here, because they can't be in savestates. -static bool s_reply_to_ios; - -// The following time variables are only used for logging -static u64 s_realtime_started_us; -static u64 s_realtime_done_us; +static ReadRequest s_read_request; +static ReadResult s_read_result; void Start() { @@ -77,23 +83,18 @@ void Stop() void DoState(PointerWrap& p) { + // By waiting for the DVD thread to be done working, we ensure + // that the value of s_read_request won't be used again. + // We thus don't need to save or load s_read_request here. WaitUntilIdle(); - // TODO: Savestates can be smaller if s_DVD_buffer is not saved - p.Do(s_dvd_buffer); - p.Do(s_time_read_started); - p.Do(s_dvd_success); + // TODO: Savestates can be smaller if s_read_result.buffer isn't saved, + // but instead gets re-read from the disc when loading the savestate. + p.Do(s_read_result); - p.Do(s_dvd_offset); - p.Do(s_output_address); - p.Do(s_length); - p.Do(s_decrypt); - p.Do(s_reply_to_ios); - - // s_realtime_started_us and s_realtime_done_us aren't savestated - // because they rely on the current system's time. - // This means that loading a savestate might cause - // incorrect times to be logged once. + // After loading a savestate, the debug log in FinishRead will report + // screwed up times for requests that were submitted before the savestate + // was made. Handling that properly may be more effort than it's worth. } void WaitUntilIdle() @@ -114,14 +115,18 @@ void StartRead(u64 dvd_offset, u32 output_address, u32 length, bool decrypt, boo s_dvd_thread_done_working.Wait(); - s_dvd_offset = dvd_offset; - s_output_address = output_address; - s_length = length; - s_decrypt = decrypt; - s_reply_to_ios = reply_to_ios; + ReadRequest request; - s_time_read_started = CoreTiming::GetTicks(); - s_realtime_started_us = Common::Timer::GetTimeUs(); + request.dvd_offset = dvd_offset; + request.output_address = output_address; + request.length = length; + request.decrypt = decrypt; + request.reply_to_ios = reply_to_ios; + + request.time_started_ticks = CoreTiming::GetTicks(); + request.realtime_started_us = Common::Timer::GetTimeUs(); + + s_read_request = std::move(request); s_dvd_thread_start_working.Set(); @@ -132,27 +137,26 @@ static void FinishRead(u64 userdata, s64 cycles_late) { WaitUntilIdle(); + const ReadResult result = std::move(s_read_result); + const ReadRequest& request = result.first; + const std::vector& buffer = result.second; + DEBUG_LOG(DVDINTERFACE, "Disc has been read. Real time: %" PRIu64 " us. " - "Real time including delay: %" PRIu64 - " us. Emulated time including delay: %" PRIu64 " us.", - s_realtime_done_us - s_realtime_started_us, - Common::Timer::GetTimeUs() - s_realtime_started_us, - (CoreTiming::GetTicks() - s_time_read_started) / - (SystemTimers::GetTicksPerSecond() / 1000 / 1000)); + "Real time including delay: %" PRIu64 " us. " + "Emulated time including delay: %" PRIu64 " us.", + request.realtime_done_us - request.realtime_started_us, + Common::Timer::GetTimeUs() - request.realtime_started_us, + (CoreTiming::GetTicks() - request.time_started_ticks) / + (SystemTimers::GetTicksPerSecond() / 1000000)); - if (s_dvd_success) - Memory::CopyToEmu(s_output_address, s_dvd_buffer.data(), s_length); + if (!buffer.empty()) + Memory::CopyToEmu(request.output_address, buffer.data(), request.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); + PanicAlertT("The disc could not be read (at 0x%" PRIx64 " - 0x%" PRIx64 ").", + request.dvd_offset, request.dvd_offset + request.length); // Notify the emulated software that the command has been executed - DVDInterface::FinishExecutingCommand(s_reply_to_ios, DVDInterface::INT_TCINT); + DVDInterface::FinishExecutingCommand(request.reply_to_ios, DVDInterface::INT_TCINT); } static void DVDThread() @@ -168,12 +172,16 @@ static void DVDThread() if (s_dvd_thread_exiting.IsSet()) return; - s_dvd_buffer.resize(s_length); + ReadRequest request = std::move(s_read_request); - s_dvd_success = - DVDInterface::GetVolume().Read(s_dvd_offset, s_length, s_dvd_buffer.data(), s_decrypt); + std::vector buffer(request.length); + const DiscIO::IVolume& volume = DVDInterface::GetVolume(); + if (!volume.Read(request.dvd_offset, request.length, buffer.data(), request.decrypt)) + buffer.resize(0); - s_realtime_done_us = Common::Timer::GetTimeUs(); + request.realtime_done_us = Common::Timer::GetTimeUs(); + + s_read_result = ReadResult(std::move(request), std::move(buffer)); } } } diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp index face57254a..1ad2b8cb26 100644 --- a/Source/Core/Core/State.cpp +++ b/Source/Core/Core/State.cpp @@ -71,14 +71,14 @@ static Common::Event g_compressAndDumpStateSyncEvent; static std::thread g_save_thread; // Don't forget to increase this after doing changes on the savestate system -static const u32 STATE_VERSION = 64; // Last changed in PR 4341 +static const u32 STATE_VERSION = 65; // Last changed in PR 4120 // Maps savestate versions to Dolphin versions. // Versions after 42 don't need to be added to this list, // beacuse they save the exact Dolphin version to savestates. static const std::map> s_old_versions = { // The 16 -> 17 change modified the size of StateHeader, - // so version older than that can't even be decompressed anymore + // so versions older than that can't even be decompressed anymore {17, {"3.5-1311", "3.5-1364"}}, {18, {"3.5-1366", "3.5-1371"}}, {19, {"3.5-1372", "3.5-1408"}}, {20, {"3.5-1409", "4.0-704"}}, {21, {"4.0-705", "4.0-889"}}, {22, {"4.0-905", "4.0-1871"}}, {23, {"4.0-1873", "4.0-1900"}}, {24, {"4.0-1902", "4.0-1919"}}, {25, {"4.0-1921", "4.0-1936"}}, From aabb17b7c949b3e66e9c9267ea1cf3773b3e4a14 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Fri, 12 Aug 2016 23:49:32 +0200 Subject: [PATCH 2/8] DVDThread: Use queues This makes it possible to submit a read without waiting for the previous read to finish. --- Source/Core/Core/HW/DVDThread.cpp | 144 ++++++++++++++++++++++-------- 1 file changed, 108 insertions(+), 36 deletions(-) diff --git a/Source/Core/Core/HW/DVDThread.cpp b/Source/Core/Core/HW/DVDThread.cpp index 491a1fea45..3a84cdc3c5 100644 --- a/Source/Core/Core/HW/DVDThread.cpp +++ b/Source/Core/Core/HW/DVDThread.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include #include #include #include @@ -11,6 +12,7 @@ #include "Common/ChunkFile.h" #include "Common/CommonTypes.h" #include "Common/Event.h" +#include "Common/FifoQueue.h" #include "Common/Flag.h" #include "Common/Logging/Log.h" #include "Common/MsgHandler.h" @@ -39,6 +41,11 @@ struct ReadRequest // We can't have a function pointer here, because they can't be in savestates. bool reply_to_ios; + // 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; + // Only used for logging u64 time_started_ticks; u64 realtime_started_us; @@ -49,20 +56,35 @@ using ReadResult = std::pair>; static void DVDThread(); -static void FinishRead(u64 userdata, s64 cycles_late); +static void FinishRead(u64 id, s64 cycles_late); static CoreTiming::EventType* 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 u64 s_next_id = 0; -static ReadRequest s_read_request; -static ReadResult s_read_result; +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::Event s_dvd_thread_done_working; // Is set by DVD thread +static Common::Flag s_dvd_thread_exiting(false); // Is set by CPU thread + +static Common::FifoQueue s_request_queue; +static Common::FifoQueue s_result_queue; +static std::map s_result_map; void Start() { s_finish_read = CoreTiming::RegisterEvent("FinishReadDVDThread", FinishRead); + + s_dvd_thread_exiting.Clear(); + s_request_queue_expanded.Reset(); + s_result_queue_expanded.Reset(); + s_request_queue.Clear(); + s_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; + _assert_(!s_dvd_thread.joinable()); s_dvd_thread = std::thread(DVDThread); } @@ -71,26 +93,37 @@ void Stop() { _assert_(s_dvd_thread.joinable()); - // The DVD thread will return if s_DVD_thread_exiting - // is set when it starts working + // 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_dvd_thread_start_working.Set(); + s_request_queue_expanded.Set(); s_dvd_thread.join(); - - s_dvd_thread_exiting.Clear(); } void DoState(PointerWrap& p) { - // By waiting for the DVD thread to be done working, we ensure - // that the value of s_read_request won't be used again. - // We thus don't need to save or load s_read_request here. + // By waiting for the DVD thread to be done working, we ensure that + // there are no pending requests. The DVD thread won't be touching + // s_result_queue, and everything we need to save will be in either + // s_result_queue or s_result_map (other than s_next_id). WaitUntilIdle(); - // TODO: Savestates can be smaller if s_read_result.buffer isn't saved, - // but instead gets re-read from the disc when loading the savestate. - p.Do(s_read_result); + // Move everything from s_result_queue to s_result_map because + // PointerWrap::Do supports std::map but not Common::FifoQueue. + // 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)); + + // Everything is now in s_result_map, so we simply savestate that. + // We also savestate s_next_id to avoid ID collisions. + p.Do(s_result_map); + p.Do(s_next_id); + + // TODO: Savestates can be smaller if the buffers of results aren't saved, + // but instead get re-read from the disc when loading the savestate. // After loading a savestate, the debug log in FinishRead will report // screwed up times for requests that were submitted before the savestate @@ -104,7 +137,9 @@ void WaitUntilIdle() // 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 + // Set the event again so that we still know that the DVD thread + // isn't working. This is needed in case this function gets called + // again before the DVD thread starts working. s_dvd_thread_done_working.Set(); } @@ -113,8 +148,6 @@ void StartRead(u64 dvd_offset, u32 output_address, u32 length, bool decrypt, boo { _assert_(Core::IsCPUThread()); - s_dvd_thread_done_working.Wait(); - ReadRequest request; request.dvd_offset = dvd_offset; @@ -123,21 +156,52 @@ void StartRead(u64 dvd_offset, u32 output_address, u32 length, bool decrypt, boo request.decrypt = decrypt; request.reply_to_ios = reply_to_ios; + u64 id = s_next_id++; + request.id = id; + request.time_started_ticks = CoreTiming::GetTicks(); request.realtime_started_us = Common::Timer::GetTimeUs(); - s_read_request = std::move(request); + s_request_queue.Push(std::move(request)); + s_request_queue_expanded.Set(); - s_dvd_thread_start_working.Set(); - - CoreTiming::ScheduleEvent(ticks_until_completion, s_finish_read); + CoreTiming::ScheduleEvent(ticks_until_completion, s_finish_read, id); } -static void FinishRead(u64 userdata, s64 cycles_late) +static void FinishRead(u64 id, s64 cycles_late) { - WaitUntilIdle(); + // We can't simply pop s_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 + // whose ID matches userdata), which means we may end up popping + // ReadResults that we don't want. We can't add those unwanted results + // back to the queue, because the queue can only have one writer. + // Instead, we add them to a map that only is used by the CPU thread. + // 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()) + { + result = std::move(it->second); + s_result_map.erase(it); + } + else + { + while (true) + { + while (!s_result_queue.Pop(result)) + s_result_queue_expanded.Wait(); + + if (result.first.id == id) + break; + else + s_result_map.emplace(result.first.id, std::move(result)); + } + } + // We have now obtained the right ReadResult. - const ReadResult result = std::move(s_read_result); const ReadRequest& request = result.first; const std::vector& buffer = result.second; @@ -167,21 +231,29 @@ static void DVDThread() { s_dvd_thread_done_working.Set(); - s_dvd_thread_start_working.Wait(); + s_request_queue_expanded.Wait(); if (s_dvd_thread_exiting.IsSet()) return; - ReadRequest request = std::move(s_read_request); + s_dvd_thread_done_working.Reset(); - std::vector buffer(request.length); - const DiscIO::IVolume& volume = DVDInterface::GetVolume(); - if (!volume.Read(request.dvd_offset, request.length, buffer.data(), request.decrypt)) - buffer.resize(0); + ReadRequest request; + while (s_request_queue.Pop(request)) + { + std::vector buffer(request.length); + const DiscIO::IVolume& volume = DVDInterface::GetVolume(); + if (!volume.Read(request.dvd_offset, request.length, buffer.data(), request.decrypt)) + buffer.resize(0); - request.realtime_done_us = Common::Timer::GetTimeUs(); + request.realtime_done_us = Common::Timer::GetTimeUs(); - s_read_result = ReadResult(std::move(request), std::move(buffer)); + s_result_queue.Push(ReadResult(std::move(request), std::move(buffer))); + s_result_queue_expanded.Set(); + + if (s_dvd_thread_exiting.IsSet()) + return; + } } } } From 28bfd52d3a4071362d10e0bb7bd5b966803df684 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sat, 13 Aug 2016 16:23:36 +0200 Subject: [PATCH 3/8] DVDInterface: Prepare for reading DTK data in DVDThread --- Source/Core/Core/HW/DVDInterface.cpp | 104 ++++++++++++++++----------- Source/Core/Core/HW/DVDInterface.h | 11 ++- Source/Core/Core/HW/DVDThread.cpp | 50 +++++++++---- Source/Core/Core/HW/DVDThread.h | 13 +++- 4 files changed, 119 insertions(+), 59 deletions(-) diff --git a/Source/Core/Core/HW/DVDInterface.cpp b/Source/Core/Core/HW/DVDInterface.cpp index 93db9dda69..ce185e1ab0 100644 --- a/Source/Core/Core/HW/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVDInterface.cpp @@ -239,7 +239,6 @@ static bool s_disc_inside = false; static bool s_stream = false; static bool s_stop_at_track_end = false; static CoreTiming::EventType* s_finish_executing_command; -static CoreTiming::EventType* s_dtk; static u64 s_last_read_offset; static u64 s_last_read_time; @@ -260,8 +259,9 @@ void GenerateDIInterrupt(DIInterruptType _DVDInterrupt); void WriteImmediate(u32 value, u32 output_address, bool reply_to_ios); bool ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, u32 output_length, - bool decrypt, bool reply_to_ios, DIInterruptType* interrupt_type, - u64* ticks_until_completion); + bool decrypt, ReplyType reply_type, DIInterruptType* interrupt_type); + +u64 PackFinishExecutingCommandUserdata(ReplyType reply_type, DIInterruptType interrupt_type); u64 SimulateDiscReadTime(u64 offset, u32 length); s64 CalculateRawDiscReadTime(u64 offset, s64 length); @@ -355,7 +355,7 @@ static u32 ProcessDTKSamples(short* tempPCM, u32 num_samples) return samples_processed; } -static void DTKStreamingCallback(u64 userdata, s64 cyclesLate) +static void DTKStreamingCallback(const std::vector& audio_data, s64 cycles_late) { // Send audio to the mixer. static const int NUM_SAMPLES = 48000 / 2000 * 7; // 3.5ms of 48kHz samples @@ -373,7 +373,8 @@ static void DTKStreamingCallback(u64 userdata, s64 cyclesLate) g_sound_stream->GetMixer()->PushStreamingSamples(tempPCM, samples_processed); int ticks_to_dtk = int(SystemTimers::GetTicksPerSecond() * u64(samples_processed) / 48000); - CoreTiming::ScheduleEvent(ticks_to_dtk - cyclesLate, s_dtk); + u64 userdata = PackFinishExecutingCommandUserdata(ReplyType::DTK, DIInterruptType::INT_TCINT); + CoreTiming::ScheduleEvent(ticks_to_dtk - cycles_late, s_finish_executing_command, userdata); } void Init() @@ -413,9 +414,9 @@ void Init() s_finish_executing_command = CoreTiming::RegisterEvent("FinishExecutingCommand", FinishExecutingCommandCallback); - s_dtk = CoreTiming::RegisterEvent("StreamingTimer", DTKStreamingCallback); - CoreTiming::ScheduleEvent(0, s_dtk); + u64 userdata = PackFinishExecutingCommandUserdata(ReplyType::DTK, DIInterruptType::INT_TCINT); + CoreTiming::ScheduleEvent(0, s_finish_executing_command, userdata); } void Shutdown() @@ -647,8 +648,7 @@ void WriteImmediate(u32 value, u32 output_address, bool reply_to_ios) // Iff false is returned, ScheduleEvent must be used to finish executing the command bool ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, u32 output_length, - bool decrypt, bool reply_to_ios, DIInterruptType* interrupt_type, - u64* ticks_until_completion) + bool decrypt, ReplyType reply_type, DIInterruptType* interrupt_type) { if (!s_disc_inside) { @@ -665,32 +665,34 @@ bool ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, u32 if (DVD_length > output_length) { - WARN_LOG( - DVDINTERFACE, - "Detected attempt to read more data from the DVD than fit inside the out buffer. Clamp."); + WARN_LOG(DVDINTERFACE, "Detected an attempt to read more data from the DVD " + "than what fits inside the out buffer. Clamping."); DVD_length = output_length; } + u64 ticks_until_completion; if (SConfig::GetInstance().bFastDiscSpeed) + { // An optional hack to speed up loading times - *ticks_until_completion = + ticks_until_completion = output_length * (SystemTimers::GetTicksPerSecond() / BUFFER_TRANSFER_RATE); + } else - *ticks_until_completion = SimulateDiscReadTime(DVD_offset, DVD_length); + { + ticks_until_completion = SimulateDiscReadTime(DVD_offset, DVD_length); + } - DVDThread::StartRead(DVD_offset, output_address, DVD_length, decrypt, reply_to_ios, - (int)*ticks_until_completion); + DVDThread::StartReadToEmulatedRAM(output_address, DVD_offset, DVD_length, decrypt, reply_type, + ticks_until_completion); return true; } -// When the command has finished executing, callback_event_type -// will be called using CoreTiming::ScheduleEvent, -// with the userdata set to the interrupt type. void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_address, u32 output_length, bool reply_to_ios) { + ReplyType reply_type = reply_to_ios ? ReplyType::IOS_HLE : ReplyType::Interrupt; DIInterruptType interrupt_type = INT_TCINT; - u64 ticks_until_completion = SystemTimers::GetTicksPerSecond() / 15000; + s64 ticks_until_completion = SystemTimers::GetTicksPerSecond() / 15000; bool command_handled_by_thread = false; // DVDLowRequestError needs access to the error code set by the previous command @@ -715,9 +717,8 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr // Only seems to be used from WII_IPC, not through direct access case DVDLowReadDiskID: INFO_LOG(DVDINTERFACE, "DVDLowReadDiskID"); - command_handled_by_thread = - ExecuteReadCommand(0, output_address, 0x20, output_length, false, reply_to_ios, - &interrupt_type, &ticks_until_completion); + command_handled_by_thread = ExecuteReadCommand(0, output_address, 0x20, output_length, false, + reply_type, &interrupt_type); break; // Only used from WII_IPC. This is the only read command that decrypts data @@ -726,7 +727,7 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr command_1); command_handled_by_thread = ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, true, - reply_to_ios, &interrupt_type, &ticks_until_completion); + reply_type, &interrupt_type); break; // Probably only used by Wii @@ -808,7 +809,7 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr { command_handled_by_thread = ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, false, - reply_to_ios, &interrupt_type, &ticks_until_completion); + reply_type, &interrupt_type); } else { @@ -856,17 +857,15 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr ", DMABuffer = %08x, SrcLength = %08x, DMALength = %08x", iDVDOffset, output_address, command_2, output_length); - command_handled_by_thread = - ExecuteReadCommand(iDVDOffset, output_address, command_2, output_length, false, - reply_to_ios, &interrupt_type, &ticks_until_completion); + command_handled_by_thread = ExecuteReadCommand( + iDVDOffset, output_address, command_2, output_length, false, reply_type, &interrupt_type); } break; case 0x40: // Read DiscID INFO_LOG(DVDINTERFACE, "Read DiscID %08x", Memory::Read_U32(output_address)); - command_handled_by_thread = - ExecuteReadCommand(0, output_address, 0x20, output_length, false, reply_to_ios, - &interrupt_type, &ticks_until_completion); + command_handled_by_thread = ExecuteReadCommand(0, output_address, 0x20, output_length, false, + reply_type, &interrupt_type); break; default: @@ -1083,33 +1082,52 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr // to simulate the speed of a real disc drive if (!command_handled_by_thread) { - u64 userdata = (static_cast(reply_to_ios) << 32) + static_cast(interrupt_type); - CoreTiming::ScheduleEvent((int)ticks_until_completion, s_finish_executing_command, userdata); + CoreTiming::ScheduleEvent(ticks_until_completion, s_finish_executing_command, + PackFinishExecutingCommandUserdata(reply_type, interrupt_type)); } } +u64 PackFinishExecutingCommandUserdata(ReplyType reply_type, DIInterruptType interrupt_type) +{ + return (static_cast(reply_type) << 32) + static_cast(interrupt_type); +} + void FinishExecutingCommandCallback(u64 userdata, s64 cycles_late) { - bool reply_to_ios = userdata >> 32 != 0; + ReplyType reply_type = static_cast(userdata >> 32); DIInterruptType interrupt_type = static_cast(userdata & 0xFFFFFFFF); - FinishExecutingCommand(reply_to_ios, interrupt_type); + FinishExecutingCommand(reply_type, interrupt_type, cycles_late); } -void FinishExecutingCommand(bool reply_to_ios, DIInterruptType interrupt_type) +void FinishExecutingCommand(ReplyType reply_type, DIInterruptType interrupt_type, s64 cycles_late, + const std::vector& data) { - if (reply_to_ios) + switch (reply_type) + { + case ReplyType::Interrupt: + { + if (s_DICR.TSTART) + { + s_DICR.TSTART = 0; + s_DILENGTH.Length = 0; + GenerateDIInterrupt(interrupt_type); + } + break; + } + + case ReplyType::IOS_HLE: { std::shared_ptr di = WII_IPC_HLE_Interface::GetDeviceByName("/dev/di"); if (di) std::static_pointer_cast(di)->FinishIOCtl(interrupt_type); - - // If di == nullptr, IOS was probably shut down, so the command shouldn't be completed + break; } - else if (s_DICR.TSTART) + + case ReplyType::DTK: { - s_DICR.TSTART = 0; - s_DILENGTH.Length = 0; - GenerateDIInterrupt(interrupt_type); + DTKStreamingCallback(data, cycles_late); + break; + } } } diff --git a/Source/Core/Core/HW/DVDInterface.h b/Source/Core/Core/HW/DVDInterface.h index e67e30ea9a..279dc15d92 100644 --- a/Source/Core/Core/HW/DVDInterface.h +++ b/Source/Core/Core/HW/DVDInterface.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include "Common/CommonTypes.h" @@ -92,6 +93,13 @@ enum DIInterruptType : int INT_CVRINT = 3, }; +enum class ReplyType : u32 +{ + Interrupt, + IOS_HLE, + DTK +}; + void Init(); void Shutdown(); void DoState(PointerWrap& p); @@ -115,6 +123,7 @@ void ChangeDiscAsCPU(const std::string& new_path); // Can only be called by th bool ChangePartition(u64 offset); void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_address, u32 output_length, bool reply_to_ios); -void FinishExecutingCommand(bool reply_to_ios, DIInterruptType interrupt_type); +void FinishExecutingCommand(ReplyType reply_type, DIInterruptType interrupt_type, s64 cycles_late, + const std::vector& data = std::vector()); } // end of namespace DVDInterface diff --git a/Source/Core/Core/HW/DVDThread.cpp b/Source/Core/Core/HW/DVDThread.cpp index 3a84cdc3c5..ad751aaac3 100644 --- a/Source/Core/Core/HW/DVDThread.cpp +++ b/Source/Core/Core/HW/DVDThread.cpp @@ -32,14 +32,16 @@ namespace DVDThread { struct ReadRequest { - u64 dvd_offset; + bool copy_to_ram; u32 output_address; + u64 dvd_offset; u32 length; bool decrypt; - // This determines which function will be used as a callback. - // We can't have a function pointer here, because they can't be in savestates. - bool reply_to_ios; + // 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; // IDs are used to uniquely identify a request. They must not be // identical to IDs of any other requests that currently exist, but @@ -56,6 +58,10 @@ using ReadResult = std::pair>; static void DVDThread(); +static void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offset, u32 length, + bool decrypt, DVDInterface::ReplyType reply_type, + s64 ticks_until_completion); + static void FinishRead(u64 id, s64 cycles_late); static CoreTiming::EventType* s_finish_read; @@ -143,18 +149,32 @@ void WaitUntilIdle() s_dvd_thread_done_working.Set(); } -void StartRead(u64 dvd_offset, u32 output_address, u32 length, bool decrypt, bool reply_to_ios, - int ticks_until_completion) +void StartRead(u64 dvd_offset, u32 length, bool decrypt, DVDInterface::ReplyType reply_type, + s64 ticks_until_completion) +{ + StartReadInternal(false, 0, dvd_offset, length, decrypt, reply_type, ticks_until_completion); +} + +void StartReadToEmulatedRAM(u32 output_address, u64 dvd_offset, u32 length, bool decrypt, + DVDInterface::ReplyType reply_type, s64 ticks_until_completion) +{ + StartReadInternal(true, output_address, dvd_offset, length, decrypt, reply_type, + ticks_until_completion); +} + +void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offset, u32 length, + bool decrypt, DVDInterface::ReplyType reply_type, s64 ticks_until_completion) { _assert_(Core::IsCPUThread()); ReadRequest request; - request.dvd_offset = dvd_offset; + request.copy_to_ram = copy_to_ram; request.output_address = output_address; + request.dvd_offset = dvd_offset; request.length = length; request.decrypt = decrypt; - request.reply_to_ios = reply_to_ios; + request.reply_type = reply_type; u64 id = s_next_id++; request.id = id; @@ -213,14 +233,20 @@ static void FinishRead(u64 id, s64 cycles_late) (CoreTiming::GetTicks() - request.time_started_ticks) / (SystemTimers::GetTicksPerSecond() / 1000000)); - if (!buffer.empty()) - Memory::CopyToEmu(request.output_address, buffer.data(), request.length); - else + if (buffer.empty()) + { PanicAlertT("The disc could not be read (at 0x%" PRIx64 " - 0x%" PRIx64 ").", request.dvd_offset, request.dvd_offset + request.length); + } + else + { + if (request.copy_to_ram) + Memory::CopyToEmu(request.output_address, buffer.data(), request.length); + } // Notify the emulated software that the command has been executed - DVDInterface::FinishExecutingCommand(request.reply_to_ios, DVDInterface::INT_TCINT); + DVDInterface::FinishExecutingCommand(request.reply_type, DVDInterface::INT_TCINT, cycles_late, + buffer); } static void DVDThread() diff --git a/Source/Core/Core/HW/DVDThread.h b/Source/Core/Core/HW/DVDThread.h index b657e29f4f..c67a236d69 100644 --- a/Source/Core/Core/HW/DVDThread.h +++ b/Source/Core/Core/HW/DVDThread.h @@ -4,9 +4,14 @@ #pragma once -#include "Common/ChunkFile.h" #include "Common/CommonTypes.h" +class PointerWrap; +namespace DVDInterface +{ +enum class ReplyType : u32; +} + namespace DVDThread { void Start(); @@ -14,6 +19,8 @@ void Stop(); void DoState(PointerWrap& p); void WaitUntilIdle(); -void StartRead(u64 dvd_offset, u32 output_address, u32 length, bool decrypt, bool reply_to_ios, - int ticks_until_completion); +void StartRead(u64 dvd_offset, u32 length, bool decrypt, DVDInterface::ReplyType reply_type, + s64 ticks_until_completion); +void StartReadToEmulatedRAM(u32 output_address, u64 dvd_offset, u32 length, bool decrypt, + DVDInterface::ReplyType reply_type, s64 ticks_until_completion); } From a450e33fb3d59772817248e6d3721bb8c014f76e Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 14 Aug 2016 16:41:21 +0200 Subject: [PATCH 4/8] DVDInterface: Read DTK data in DVDThread --- Source/Core/Core/HW/DVDInterface.cpp | 87 ++++++++++++++++++---------- 1 file changed, 57 insertions(+), 30 deletions(-) diff --git a/Source/Core/Core/HW/DVDInterface.cpp b/Source/Core/Core/HW/DVDInterface.cpp index ce185e1ab0..45bdd249ff 100644 --- a/Source/Core/Core/HW/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVDInterface.cpp @@ -233,6 +233,7 @@ static u32 s_current_start; static u32 s_current_length; static u32 s_next_start; static u32 s_next_length; +static u32 s_pending_samples; static u32 s_error_code = 0; static bool s_disc_inside = false; @@ -288,6 +289,8 @@ void DoState(PointerWrap& p) p.Do(s_current_start); p.Do(s_current_length); + p.Do(s_pending_samples); + p.Do(s_last_read_offset); p.Do(s_last_read_time); @@ -311,19 +314,34 @@ void DoState(PointerWrap& p) } } -static u32 ProcessDTKSamples(short* tempPCM, u32 num_samples) +static size_t ProcessDTKSamples(std::vector* temp_pcm, const std::vector& audio_data) { - // TODO: Read audio data using the DVD thread instead of blocking on it? - DVDThread::WaitUntilIdle(); + size_t samples_processed = 0; + size_t bytes_processed = 0; + while (samples_processed < temp_pcm->size() / 2 && bytes_processed < audio_data.size()) + { + StreamADPCM::DecodeBlock(&(*temp_pcm)[samples_processed * 2], &audio_data[bytes_processed]); + samples_processed += StreamADPCM::SAMPLES_PER_BLOCK; + bytes_processed += StreamADPCM::ONE_BLOCK_SIZE; + } + for (size_t i = 0; i < samples_processed * 2; ++i) + { + // TODO: Fix the mixer so it can accept non-byte-swapped samples. + (*temp_pcm)[i] = Common::swap16((*temp_pcm)[i]); + } + return samples_processed; +} - u32 samples_processed = 0; - do +static u32 AdvanceDTK(u32 maximum_samples, u32* samples_to_process) +{ + u32 bytes_to_process = 0; + *samples_to_process = 0; + while (*samples_to_process < maximum_samples) { if (s_audio_position >= s_current_start + s_current_length) { - DEBUG_LOG(DVDINTERFACE, "ProcessDTKSamples: " - "NextStart=%08x,NextLength=%08x,CurrentStart=%08x,CurrentLength=%08x," - "AudioPos=%08x", + DEBUG_LOG(DVDINTERFACE, "AdvanceDTK: NextStart=%08x, NextLength=%08x, " + "CurrentStart=%08x, CurrentLength=%08x, AudioPos=%08x", s_next_start, s_next_length, s_current_start, s_current_length, s_audio_position); s_audio_position = s_next_start; @@ -340,41 +358,49 @@ static u32 ProcessDTKSamples(short* tempPCM, u32 num_samples) StreamADPCM::InitFilter(); } - u8 tempADPCM[StreamADPCM::ONE_BLOCK_SIZE]; - // TODO: What if we can't read from s_audio_position? - s_inserted_volume->Read(s_audio_position, sizeof(tempADPCM), tempADPCM, false); - s_audio_position += sizeof(tempADPCM); - StreamADPCM::DecodeBlock(tempPCM + samples_processed * 2, tempADPCM); - samples_processed += StreamADPCM::SAMPLES_PER_BLOCK; - } while (samples_processed < num_samples); - for (unsigned i = 0; i < samples_processed * 2; ++i) - { - // TODO: Fix the mixer so it can accept non-byte-swapped samples. - tempPCM[i] = Common::swap16(tempPCM[i]); + s_audio_position += StreamADPCM::ONE_BLOCK_SIZE; + bytes_to_process += StreamADPCM::ONE_BLOCK_SIZE; + *samples_to_process += StreamADPCM::SAMPLES_PER_BLOCK; } - return samples_processed; + + return bytes_to_process; } static void DTKStreamingCallback(const std::vector& audio_data, s64 cycles_late) { // Send audio to the mixer. - static const int NUM_SAMPLES = 48000 / 2000 * 7; // 3.5ms of 48kHz samples - short tempPCM[NUM_SAMPLES * 2]; - unsigned samples_processed; + std::vector temp_pcm(s_pending_samples * 2, 0); + ProcessDTKSamples(&temp_pcm, audio_data); + g_sound_stream->GetMixer()->PushStreamingSamples(temp_pcm.data(), s_pending_samples); + + // Determine which audio data to read next. + static const int MAXIMUM_SAMPLES = 48000 / 2000 * 7; // 3.5ms of 48kHz samples + u64 read_offset; + u32 read_length; if (s_stream && AudioInterface::IsPlaying()) { - samples_processed = ProcessDTKSamples(tempPCM, NUM_SAMPLES); + read_offset = s_audio_position; + read_length = AdvanceDTK(MAXIMUM_SAMPLES, &s_pending_samples); } else { - memset(tempPCM, 0, sizeof(tempPCM)); - samples_processed = NUM_SAMPLES; + read_length = 0; + s_pending_samples = MAXIMUM_SAMPLES; } - g_sound_stream->GetMixer()->PushStreamingSamples(tempPCM, samples_processed); - int ticks_to_dtk = int(SystemTimers::GetTicksPerSecond() * u64(samples_processed) / 48000); - u64 userdata = PackFinishExecutingCommandUserdata(ReplyType::DTK, DIInterruptType::INT_TCINT); - CoreTiming::ScheduleEvent(ticks_to_dtk - cycles_late, s_finish_executing_command, userdata); + // Read the next chunk of audio data asynchronously. + s64 ticks_to_dtk = SystemTimers::GetTicksPerSecond() * s64(s_pending_samples) / 48000; + ticks_to_dtk -= cycles_late; + if (read_length > 0) + { + DVDThread::StartRead(read_offset, read_length, false, ReplyType::DTK, ticks_to_dtk); + } + else + { + // There's nothing to read, so using DVDThread is unnecessary. + u64 userdata = PackFinishExecutingCommandUserdata(ReplyType::DTK, DIInterruptType::INT_TCINT); + CoreTiming::ScheduleEvent(ticks_to_dtk, s_finish_executing_command, userdata); + } } void Init() @@ -398,6 +424,7 @@ void Init() s_next_length = 0; s_current_start = 0; s_current_length = 0; + s_pending_samples = 0; s_error_code = 0; s_disc_inside = false; From 98fb037dee4b4e98b601686c44ba30fdaf99f0f2 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 14 Aug 2016 17:11:39 +0200 Subject: [PATCH 5/8] DVDInterface: Handle DTK beyond 4 GiB Might as well do this while I'm already breaking savestate compatibility. --- Source/Core/Core/HW/DVDInterface.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/Source/Core/Core/HW/DVDInterface.cpp b/Source/Core/Core/HW/DVDInterface.cpp index 45bdd249ff..f47e99220e 100644 --- a/Source/Core/Core/HW/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVDInterface.cpp @@ -228,10 +228,10 @@ static UDICR s_DICR; static UDIIMMBUF s_DIIMMBUF; static UDICFG s_DICFG; -static u32 s_audio_position; -static u32 s_current_start; +static u64 s_audio_position; +static u64 s_current_start; static u32 s_current_length; -static u32 s_next_start; +static u64 s_next_start; static u32 s_next_length; static u32 s_pending_samples; @@ -977,9 +977,7 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr } else if (!s_stop_at_track_end) { - // Setting s_next_start (a u32) like this discards two bits, - // but GC games can't be 4 GiB big, so it shouldn't matter - s_next_start = command_1 << 2; + s_next_start = static_cast(command_1) << 2; s_next_length = command_2; if (!s_stream) { @@ -1012,17 +1010,17 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr case 0x01: // Returns the current offset INFO_LOG(DVDINTERFACE, "(Audio): Stream Status: Request Audio status AudioPos:%08x", s_audio_position); - WriteImmediate(s_audio_position >> 2, output_address, reply_to_ios); + WriteImmediate(static_cast(s_audio_position >> 2), output_address, reply_to_ios); break; case 0x02: // Returns the start offset INFO_LOG(DVDINTERFACE, "(Audio): Stream Status: Request Audio status CurrentStart:%08x", s_current_start); - WriteImmediate(s_current_start >> 2, output_address, reply_to_ios); + WriteImmediate(static_cast(s_current_start >> 2), output_address, reply_to_ios); break; case 0x03: // Returns the total length INFO_LOG(DVDINTERFACE, "(Audio): Stream Status: Request Audio status CurrentLength:%08x", s_current_length); - WriteImmediate(s_current_length >> 2, output_address, reply_to_ios); + WriteImmediate(static_cast(s_current_length >> 2), output_address, reply_to_ios); break; default: INFO_LOG(DVDINTERFACE, "(Audio): Subcommand: %02x Request Audio status %s", From f1879cc35605e878e04786d5de77909a4d74f6b5 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 14 Aug 2016 17:17:01 +0200 Subject: [PATCH 6/8] DVDInterface: Reorder variables Might as well do this while I'm already breaking savestate compatibility. --- Source/Core/Core/HW/DVDInterface.cpp | 33 +++++++++++++++------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/Source/Core/Core/HW/DVDInterface.cpp b/Source/Core/Core/HW/DVDInterface.cpp index f47e99220e..dabb290efa 100644 --- a/Source/Core/Core/HW/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVDInterface.cpp @@ -218,7 +218,8 @@ union UDICFG { static std::unique_ptr s_inserted_volume; // STATE_TO_SAVE -// hardware registers + +// Hardware registers static UDISR s_DISR; static UDICVR s_DICVR; static UDICMDBUF s_DICMDBUF[3]; @@ -228,6 +229,9 @@ static UDICR s_DICR; static UDIIMMBUF s_DIIMMBUF; static UDICFG s_DICFG; +// DTK +static bool s_stream = false; +static bool s_stop_at_track_end = false; static u64 s_audio_position; static u64 s_current_start; static u32 s_current_length; @@ -235,17 +239,19 @@ static u64 s_next_start; static u32 s_next_length; static u32 s_pending_samples; +// Disc drive state static u32 s_error_code = 0; static bool s_disc_inside = false; -static bool s_stream = false; -static bool s_stop_at_track_end = false; -static CoreTiming::EventType* s_finish_executing_command; +// Disc drive timing static u64 s_last_read_offset; static u64 s_last_read_time; +// Disc changing static std::string s_disc_path_to_insert; +// Events +static CoreTiming::EventType* s_finish_executing_command; static CoreTiming::EventType* s_eject_disc; static CoreTiming::EventType* s_insert_disc; @@ -278,26 +284,23 @@ void DoState(PointerWrap& p) p.Do(s_DIIMMBUF); p.DoPOD(s_DICFG); - p.Do(s_next_start); + p.Do(s_stream); + p.Do(s_stop_at_track_end); p.Do(s_audio_position); + p.Do(s_current_start); + p.Do(s_current_length); + p.Do(s_next_start); p.Do(s_next_length); + p.Do(s_pending_samples); p.Do(s_error_code); p.Do(s_disc_inside); - p.Do(s_stream); - - p.Do(s_current_start); - p.Do(s_current_length); - - p.Do(s_pending_samples); p.Do(s_last_read_offset); p.Do(s_last_read_time); p.Do(s_disc_path_to_insert); - p.Do(s_stop_at_track_end); - DVDThread::DoState(p); // s_inserted_volume isn't savestated (because it points to @@ -419,6 +422,8 @@ void Init() s_DICFG.Hex = 0; s_DICFG.CONFIG = 1; // Disable bootrom descrambler + s_stream = false; + s_stop_at_track_end = false; s_audio_position = 0; s_next_start = 0; s_next_length = 0; @@ -428,8 +433,6 @@ void Init() s_error_code = 0; s_disc_inside = false; - s_stream = false; - s_stop_at_track_end = false; s_last_read_offset = 0; s_last_read_time = 0; From e1f6ab5592157af348fb5ea7caa3added190037c Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sat, 15 Oct 2016 13:57:56 +0200 Subject: [PATCH 7/8] DVDThread: Remove s_dvd_thread_done_working and fix race condition s_dvd_thread_done_working makes the logic more complicated, and degasus pointed out a race condition that can happen if the CPU thread calls WaitForIdle right in between the DVD thread executing done_working.Set() and done_working.Reset() while there is work left to do. To avoid this, let's just get rid of s_dvd_thread_done_working. It's a relic from the old DVDThread design. Thanks to the last few commits, WaitUntilIdle only gets called rarely (disc change and savestate), so it's not a problem if WaitUntilIdle ends up being slower. --- Source/Core/Core/HW/DVDThread.cpp | 38 +++++++++++++++++++------------ 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/Source/Core/Core/HW/DVDThread.cpp b/Source/Core/Core/HW/DVDThread.cpp index ad751aaac3..beec9708fb 100644 --- a/Source/Core/Core/HW/DVDThread.cpp +++ b/Source/Core/Core/HW/DVDThread.cpp @@ -56,6 +56,9 @@ struct ReadRequest using ReadResult = std::pair>; +static void StartDVDThread(); +static void StopDVDThread(); + static void DVDThread(); static void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offset, u32 length, @@ -70,7 +73,6 @@ static u64 s_next_id = 0; 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::Event s_dvd_thread_done_working; // Is set by DVD thread static Common::Flag s_dvd_thread_exiting(false); // Is set by CPU thread static Common::FifoQueue s_request_queue; @@ -81,7 +83,6 @@ void Start() { s_finish_read = CoreTiming::RegisterEvent("FinishReadDVDThread", FinishRead); - s_dvd_thread_exiting.Clear(); s_request_queue_expanded.Reset(); s_result_queue_expanded.Reset(); s_request_queue.Clear(); @@ -91,11 +92,22 @@ void Start() // much, because this will never get exposed to the emulated game. s_next_id = 0; + StartDVDThread(); +} + +static void StartDVDThread() +{ _assert_(!s_dvd_thread.joinable()); + s_dvd_thread_exiting.Clear(); s_dvd_thread = std::thread(DVDThread); } void Stop() +{ + StopDVDThread(); +} + +static void StopDVDThread() { _assert_(s_dvd_thread.joinable()); @@ -131,6 +143,9 @@ void DoState(PointerWrap& p) // TODO: Savestates can be smaller if the buffers of results aren't saved, // but instead get re-read from the disc when loading the savestate. + // TODO: It would be possible to create a savestate faster by stopping + // the DVD thread regardless of whether there are pending requests. + // After loading a savestate, the debug log in FinishRead will report // screwed up times for requests that were submitted before the savestate // was made. Handling that properly may be more effort than it's worth. @@ -140,13 +155,11 @@ void WaitUntilIdle() { _assert_(Core::IsCPUThread()); - // Wait until DVD thread isn't working - s_dvd_thread_done_working.Wait(); + while (!s_request_queue.Empty()) + s_result_queue_expanded.Wait(); - // Set the event again so that we still know that the DVD thread - // isn't working. This is needed in case this function gets called - // again before the DVD thread starts working. - s_dvd_thread_done_working.Set(); + StopDVDThread(); + StartDVDThread(); } void StartRead(u64 dvd_offset, u32 length, bool decrypt, DVDInterface::ReplyType reply_type, @@ -162,8 +175,9 @@ void StartReadToEmulatedRAM(u32 output_address, u64 dvd_offset, u32 length, bool ticks_until_completion); } -void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offset, u32 length, - bool decrypt, DVDInterface::ReplyType reply_type, s64 ticks_until_completion) +static void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offset, u32 length, + bool decrypt, DVDInterface::ReplyType reply_type, + s64 ticks_until_completion) { _assert_(Core::IsCPUThread()); @@ -255,15 +269,11 @@ static void DVDThread() while (true) { - s_dvd_thread_done_working.Set(); - s_request_queue_expanded.Wait(); if (s_dvd_thread_exiting.IsSet()) return; - s_dvd_thread_done_working.Reset(); - ReadRequest request; while (s_request_queue.Pop(request)) { From a6bb1b642a408005bb5aa15624e1f6758e34c319 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sat, 15 Oct 2016 15:01:24 +0200 Subject: [PATCH 8/8] DVDInterface: Better memory locality for DTK byteswapping --- Source/Core/Core/HW/DVDInterface.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Source/Core/Core/HW/DVDInterface.cpp b/Source/Core/Core/HW/DVDInterface.cpp index dabb290efa..b87f3e88e2 100644 --- a/Source/Core/Core/HW/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVDInterface.cpp @@ -324,14 +324,15 @@ static size_t ProcessDTKSamples(std::vector* temp_pcm, const std::vectorsize() / 2 && bytes_processed < audio_data.size()) { StreamADPCM::DecodeBlock(&(*temp_pcm)[samples_processed * 2], &audio_data[bytes_processed]); + for (size_t i = 0; i < StreamADPCM::SAMPLES_PER_BLOCK * 2; ++i) + { + // TODO: Fix the mixer so it can accept non-byte-swapped samples. + s16* sample = &(*temp_pcm)[samples_processed * 2 + i]; + *sample = Common::swap16(*sample); + } samples_processed += StreamADPCM::SAMPLES_PER_BLOCK; bytes_processed += StreamADPCM::ONE_BLOCK_SIZE; } - for (size_t i = 0; i < samples_processed * 2; ++i) - { - // TODO: Fix the mixer so it can accept non-byte-swapped samples. - (*temp_pcm)[i] = Common::swap16((*temp_pcm)[i]); - } return samples_processed; }