From c119ad46d0bbe5c6e87406ff10ac9a5d8504a80d Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sat, 28 Feb 2015 11:24:56 +0100 Subject: [PATCH 1/6] Create DVDThread (no multithreading yet) --- Source/Core/Core/CMakeLists.txt | 1 + Source/Core/Core/Core.vcxproj | 2 + Source/Core/Core/Core.vcxproj.filters | 6 ++ Source/Core/Core/HW/DVDInterface.cpp | 126 +++++++------------------- Source/Core/Core/HW/DVDThread.cpp | 75 +++++++++++++++ Source/Core/Core/HW/DVDThread.h | 20 ++++ Source/Core/Core/State.cpp | 2 +- 7 files changed, 139 insertions(+), 93 deletions(-) create mode 100644 Source/Core/Core/HW/DVDThread.cpp create mode 100644 Source/Core/Core/HW/DVDThread.h diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index bd69afeb80..99bc5ced18 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -83,6 +83,7 @@ set(SRCS ActionReplay.cpp HW/DSPLLE/DSPLLE.cpp HW/DSPLLE/DSPLLETools.cpp HW/DVDInterface.cpp + HW/DVDThread.cpp HW/EXI_Channel.cpp HW/EXI.cpp HW/EXI_Device.cpp diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index 5e54a4fab7..05af583ed2 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -115,6 +115,7 @@ + @@ -325,6 +326,7 @@ + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index 92d88bc479..a374defc07 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -315,6 +315,9 @@ HW %28Flipper/Hollywood%29\DI - Drive Interface + + HW %28Flipper/Hollywood%29\DI - Drive Interface + HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes @@ -850,6 +853,9 @@ HW %28Flipper/Hollywood%29\DI - Drive Interface + + HW %28Flipper/Hollywood%29\DI - Drive Interface + HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes diff --git a/Source/Core/Core/HW/DVDInterface.cpp b/Source/Core/Core/HW/DVDInterface.cpp index e0356ec12a..a60c21f7b9 100644 --- a/Source/Core/Core/HW/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVDInterface.cpp @@ -19,6 +19,7 @@ #include "Core/Movie.h" #include "Core/HW/AudioInterface.h" #include "Core/HW/DVDInterface.h" +#include "Core/HW/DVDThread.h" #include "Core/HW/Memmap.h" #include "Core/HW/MMIO.h" #include "Core/HW/ProcessorInterface.h" @@ -220,22 +221,6 @@ union UDICFG UDICFG(u32 _hex) {Hex = _hex;} }; -struct DVDReadCommand -{ - bool is_valid; - - u64 DVD_offset; - u32 output_address; - u32 length; - bool decrypt; - - DIInterruptType interrupt_type; - - // Used to notify emulated software after executing command. - // Pointers don't work with savestates, so CoreTiming events are used instead - int callback_event_type; -}; - static std::unique_ptr s_inserted_volume; // STATE_TO_SAVE @@ -249,8 +234,6 @@ static UDICR m_DICR; static UDIIMMBUF m_DIIMMBUF; static UDICFG m_DICFG; -static DVDReadCommand current_read_command; - static u32 AudioPos; static u32 CurrentStart; static u32 CurrentLength; @@ -263,7 +246,6 @@ static bool g_bDiscInside = false; bool g_bStream = false; static bool g_bStopAtTrackEnd = false; static int finish_execute_command = 0; -static int finish_execute_read_command = 0; static int dtk = 0; static u64 g_last_read_offset; @@ -284,8 +266,8 @@ void UpdateInterrupts(); void GenerateDIInterrupt(DIInterruptType _DVDInterrupt); void WriteImmediate(u32 value, u32 output_address, bool write_to_DIIMMBUF); -DVDReadCommand ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, u32 output_length, - bool decrypt, DIInterruptType* interrupt_type, u64* ticks_until_completion); +bool ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, u32 output_length, bool decrypt, + int callback_event_type, DIInterruptType* interrupt_type, u64* ticks_until_completion); u64 SimulateDiscReadTime(u64 offset, u32 length); s64 CalculateRawDiscReadTime(u64 offset, s64 length); @@ -301,8 +283,6 @@ void DoState(PointerWrap &p) p.Do(m_DIIMMBUF); p.DoPOD(m_DICFG); - p.Do(current_read_command); - p.Do(NextStart); p.Do(AudioPos); p.Do(NextLength); @@ -318,6 +298,8 @@ void DoState(PointerWrap &p) p.Do(g_last_read_time); p.Do(g_bStopAtTrackEnd); + + DVDThread::DoState(p); } static void FinishExecuteCommand(u64 userdata, int cyclesLate) @@ -330,30 +312,6 @@ static void FinishExecuteCommand(u64 userdata, int cyclesLate) } } -static void FinishExecuteReadCommand(u64 userdata, int cyclesLate) -{ - if (!current_read_command.is_valid) - { - PanicAlert("DVDInterface: There is no command to execute!"); - } - else - { - // Here is the actual disc reading - if (!DVDRead(current_read_command.DVD_offset, current_read_command.output_address, - current_read_command.length, current_read_command.decrypt)) - { - PanicAlertT("Can't read from DVD_Plugin - DVD-Interface: Fatal Error"); - } - } - - // The command is marked as invalid because it shouldn't be used again - current_read_command.is_valid = false; - - // The final step is to notify the emulated software that the command has been executed - CoreTiming::ScheduleEvent_Immediate(current_read_command.callback_event_type, - current_read_command.interrupt_type); -} - static u32 ProcessDTKSamples(short *tempPCM, u32 num_samples) { u32 samples_processed = 0; @@ -417,6 +375,8 @@ static void DTKStreamingCallback(u64 userdata, int cyclesLate) void Init() { + DVDThread::Start(); + m_DISR.Hex = 0; m_DICVR.Hex = 1; // Disc Channel relies on cover being open when no disc is inserted m_DICMDBUF[0].Hex = 0; @@ -429,8 +389,6 @@ void Init() m_DICFG.Hex = 0; m_DICFG.CONFIG = 1; // Disable bootrom descrambler - current_read_command.is_valid = false; - AudioPos = 0; NextStart = 0; NextLength = 0; @@ -449,7 +407,6 @@ void Init() insertDisc = CoreTiming::RegisterEvent("InsertDisc", InsertDiscCallback); finish_execute_command = CoreTiming::RegisterEvent("FinishExecuteCommand", FinishExecuteCommand); - finish_execute_read_command = CoreTiming::RegisterEvent("FinishExecuteReadCommand", FinishExecuteReadCommand); dtk = CoreTiming::RegisterEvent("StreamingTimer", DTKStreamingCallback); CoreTiming::ScheduleEvent(0, dtk); @@ -457,6 +414,7 @@ void Init() void Shutdown() { + DVDThread::Stop(); s_inserted_volume.reset(); } @@ -687,19 +645,21 @@ void WriteImmediate(u32 value, u32 output_address, bool write_to_DIIMMBUF) Memory::Write_U32(value, output_address); } -// If the returned DVDReadCommand has is_valid set to true, -// FinishExecuteReadCommand must be used to finish executing it -DVDReadCommand ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, u32 output_length, - bool decrypt, DIInterruptType* interrupt_type, u64* ticks_until_completion) +// 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, + int callback_event_type, DIInterruptType* interrupt_type, u64* ticks_until_completion) { - DVDReadCommand command; - if (!g_bDiscInside) { + // Disc read fails g_ErrorCode = ERROR_NO_DISK | ERROR_COVER_H; *interrupt_type = INT_DEINT; - command.is_valid = false; - return command; + return false; + } + else + { + // Disc read succeeds + *interrupt_type = INT_TCINT; } if (DVD_length > output_length) @@ -714,13 +674,9 @@ DVDReadCommand ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_le else *ticks_until_completion = SimulateDiscReadTime(DVD_offset, DVD_length); - *interrupt_type = INT_TCINT; - command.is_valid = true; - command.DVD_offset = DVD_offset; - command.output_address = output_address; - command.length = DVD_length; - command.decrypt = decrypt; - return command; + DVDThread::StartRead(DVD_offset, output_address, DVD_length, decrypt, + callback_event_type, (int)*ticks_until_completion); + return true; } // When the command has finished executing, callback_event_type @@ -731,8 +687,7 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr { DIInterruptType interrupt_type = INT_TCINT; u64 ticks_until_completion = SystemTimers::GetTicksPerSecond() / 15000; - DVDReadCommand read_command; - read_command.is_valid = false; + bool command_handled_by_thread = false; bool GCAM = (SConfig::GetInstance().m_SIDevice[0] == SIDEVICE_AM_BASEBOARD) && (SConfig::GetInstance().m_EXIDevice[2] == EXIDEVICE_AM_BASEBOARD); @@ -777,15 +732,15 @@ 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"); - read_command = ExecuteReadCommand(0, output_address, 0x20, output_length, - false, &interrupt_type, &ticks_until_completion); + command_handled_by_thread = ExecuteReadCommand(0, output_address, 0x20, output_length, false, + callback_event_type, &interrupt_type, &ticks_until_completion); break; // Only used from WII_IPC. This is the only read command that decrypts data case DVDLowRead: INFO_LOG(DVDINTERFACE, "DVDLowRead: DVDAddr: 0x%09" PRIx64 ", Size: 0x%x", (u64)command_2 << 2, command_1); - read_command = ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, - true, &interrupt_type, &ticks_until_completion); + command_handled_by_thread = ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, true, + callback_event_type, &interrupt_type, &ticks_until_completion); break; // Probably only used by Wii @@ -864,8 +819,8 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr (command_2 > 0x7ed40000 && command_2 < 0x7ed40008) || (((command_2 + command_1) > 0x7ed40000) && (command_2 + command_1) < 0x7ed40008))) { - read_command = ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, - false, &interrupt_type, &ticks_until_completion); + command_handled_by_thread = ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, false, + callback_event_type, &interrupt_type, &ticks_until_completion); } else { @@ -961,15 +916,15 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr } } - read_command = ExecuteReadCommand(iDVDOffset, output_address, command_2, output_length, - false, &interrupt_type, &ticks_until_completion); + command_handled_by_thread = ExecuteReadCommand(iDVDOffset, output_address, command_2, output_length, false, + callback_event_type, &interrupt_type, &ticks_until_completion); } break; case 0x40: // Read DiscID INFO_LOG(DVDINTERFACE, "Read DiscID %08x", Memory::Read_U32(output_address)); - read_command = ExecuteReadCommand(0, output_address, 0x20, output_length, - false, &interrupt_type, &ticks_until_completion); + command_handled_by_thread = ExecuteReadCommand(0, output_address, 0x20, output_length, false, + callback_event_type, &interrupt_type, &ticks_until_completion); break; default: @@ -1304,23 +1259,10 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr break; } - // The command will finish executing after a delay, + // The command will finish executing after a delay // to simulate the speed of a real disc drive - if (read_command.is_valid) - { - // We schedule a FinishExecuteReadCommand (which will call the actual callback - // once it's done) so that the data transfer isn't completed too early. - // Most games don't care about it, but if it's done wrong, Resident Evil 3 - // plays some extra noise when playing the menu selection sound effect. - read_command.callback_event_type = callback_event_type; - read_command.interrupt_type = interrupt_type; - current_read_command = read_command; - CoreTiming::ScheduleEvent((int)ticks_until_completion, finish_execute_read_command); - } - else - { + if (!command_handled_by_thread) CoreTiming::ScheduleEvent((int)ticks_until_completion, callback_event_type, interrupt_type); - } } // Simulates the timing aspects of reading data from a disc. diff --git a/Source/Core/Core/HW/DVDThread.cpp b/Source/Core/Core/HW/DVDThread.cpp new file mode 100644 index 0000000000..1182b6c1ee --- /dev/null +++ b/Source/Core/Core/HW/DVDThread.cpp @@ -0,0 +1,75 @@ +// Copyright 2015 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include + +#include "Common/ChunkFile.h" +#include "Common/CommonTypes.h" +#include "Common/MsgHandler.h" + +#include "Core/CoreTiming.h" +#include "Core/HW/DVDInterface.h" +#include "Core/HW/DVDThread.h" + +#include "DiscIO/Volume.h" + +namespace DVDThread +{ + +static void FinishRead(u64 userdata, int cyclesLate); +static int s_finish_read; + +static u64 s_dvd_offset; +static u32 s_output_address; +static u32 s_length; +static bool s_decrypt; + +// Used to notify emulated software after executing command. +// Pointers don't work with savestates, so CoreTiming events are used instead +static int s_callback_event_type; + +void Start() +{ + s_finish_read = CoreTiming::RegisterEvent("FinishReadDVDThread", FinishRead); +} + +void Stop() +{ + +} + +void DoState(PointerWrap &p) +{ + p.Do(s_dvd_offset); + p.Do(s_output_address); + p.Do(s_length); + p.Do(s_decrypt); + p.Do(s_callback_event_type); +} + +void StartRead(u64 dvd_offset, u32 output_address, u32 length, bool decrypt, + int callback_event_type, int ticks_until_completion) +{ + s_dvd_offset = dvd_offset; + s_output_address = output_address; + s_length = length; + s_decrypt = decrypt; + s_callback_event_type = callback_event_type; + 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)) + { + PanicAlertT("The disc could not be read (at 0x%" PRIx64 " - 0x%" PRIx64 ").", + s_dvd_offset, s_dvd_offset + s_length); + } + + // Notify the emulated software that the command has been executed + CoreTiming::ScheduleEvent_Immediate(s_callback_event_type, DVDInterface::INT_TCINT); +} + +} diff --git a/Source/Core/Core/HW/DVDThread.h b/Source/Core/Core/HW/DVDThread.h new file mode 100644 index 0000000000..0aa5f296cb --- /dev/null +++ b/Source/Core/Core/HW/DVDThread.h @@ -0,0 +1,20 @@ +// Copyright 2015 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "Common/ChunkFile.h" +#include "Common/CommonTypes.h" + +namespace DVDThread +{ + +void Start(); +void Stop(); +void DoState(PointerWrap &p); + +void StartRead(u64 dvd_offset, u32 output_address, u32 length, bool decrypt, + int callback_event_type, int ticks_until_completion); + +} diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp index 9a0711e6c2..c14c9dc9ef 100644 --- a/Source/Core/Core/State.cpp +++ b/Source/Core/Core/State.cpp @@ -68,7 +68,7 @@ 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 = 48; // Last changed in PR 3108 +static const u32 STATE_VERSION = 49; // Last changed in PR 2149 // Maps savestate versions to Dolphin versions. // Versions after 42 don't need to be added to this list, From 54ff4bd0cbbcf8bed5d8dd719b1196ac7b787c55 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sat, 25 Apr 2015 12:52:47 +0200 Subject: [PATCH 2/6] 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); From 3d7afdae253dce116bdb9df986a85ba7d8d88426 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Tue, 3 Mar 2015 20:00:55 +0100 Subject: [PATCH 3/6] DVDThread: Performance logging --- Source/Core/Core/HW/DVDThread.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Source/Core/Core/HW/DVDThread.cpp b/Source/Core/Core/HW/DVDThread.cpp index 52d7f01767..d1f68205c0 100644 --- a/Source/Core/Core/HW/DVDThread.cpp +++ b/Source/Core/Core/HW/DVDThread.cpp @@ -13,6 +13,7 @@ #include "Common/Flag.h" #include "Common/MsgHandler.h" #include "Common/Thread.h" +#include "Common/Timer.h" #include "Common/Logging/Log.h" #include "Core/Core.h" @@ -20,6 +21,7 @@ #include "Core/HW/DVDInterface.h" #include "Core/HW/DVDThread.h" #include "Core/HW/Memmap.h" +#include "Core/HW/SystemTimers.h" #include "DiscIO/Volume.h" @@ -37,6 +39,7 @@ 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; @@ -48,6 +51,10 @@ static bool s_decrypt; // Pointers don't work with savestates, so CoreTiming events are used instead static int s_callback_event_type; +// The following time variables are only used for logging +static u64 s_realtime_started_us; +static u64 s_realtime_done_us; + void Start() { s_finish_read = CoreTiming::RegisterEvent("FinishReadDVDThread", FinishRead); @@ -75,6 +82,7 @@ void DoState(PointerWrap &p) // 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); p.Do(s_dvd_offset); @@ -82,6 +90,11 @@ void DoState(PointerWrap &p) p.Do(s_length); p.Do(s_decrypt); p.Do(s_callback_event_type); + + // 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. } void WaitUntilIdle() @@ -108,6 +121,9 @@ void StartRead(u64 dvd_offset, u32 output_address, u32 length, bool decrypt, s_decrypt = decrypt; s_callback_event_type = callback_event_type; + s_time_read_started = CoreTiming::GetTicks(); + s_realtime_started_us = Common::Timer::GetTimeUs(); + s_dvd_thread_start_working.Set(); CoreTiming::ScheduleEvent(ticks_until_completion, s_finish_read); @@ -117,6 +133,12 @@ static void FinishRead(u64 userdata, int cyclesLate) { WaitUntilIdle(); + 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)); + if (s_dvd_success) Memory::CopyToEmu(s_output_address, s_dvd_buffer.data(), s_length); else @@ -148,6 +170,8 @@ static void DVDThread() s_dvd_buffer.resize(s_length); s_dvd_success = DVDInterface::GetVolume().Read(s_dvd_offset, s_length, s_dvd_buffer.data(), s_decrypt); + + s_realtime_done_us = Common::Timer::GetTimeUs(); } } From 555eb1162fea863e7944545938fb001d977e8530 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Fri, 6 Mar 2015 20:45:48 +0100 Subject: [PATCH 4/6] Memmap: Fix a CopyToEmu bug ValidCopyRange incorrectly returned false and stopped a CopyToEmu when pressing B+X+Start in some GameCube games (for instance Metroid Prime) after the DVD thread was implemented --- Source/Core/Core/HW/Memmap.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Core/Core/HW/Memmap.cpp b/Source/Core/Core/HW/Memmap.cpp index 9354fe94e0..467410d75b 100644 --- a/Source/Core/Core/HW/Memmap.cpp +++ b/Source/Core/Core/HW/Memmap.cpp @@ -247,8 +247,8 @@ bool AreMemoryBreakpointsActivated() static inline bool ValidCopyRange(u32 address, size_t size) { return (GetPointer(address) != nullptr && - GetPointer(address + u32(size)) != nullptr && - size < EXRAM_SIZE); // Make sure we don't have a range spanning seperate 2 banks + GetPointer(address + u32(size) - 1) != nullptr && + size < EXRAM_SIZE); // Make sure we don't have a range spanning 2 separate banks } void CopyFromEmu(void* data, u32 address, size_t size) From e3b9e89f5a6a1163cf966c424fec7f9e0f7d9174 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Wed, 22 Apr 2015 17:06:13 +0200 Subject: [PATCH 5/6] Move DVDRead from DVDInterface to Boot It was previously an important part of DVDInterface, but since its usage there was replaced with DVDThread, the only remaining uses of it are in Boot and Boot_BS2Emu. --- Source/Core/Core/Boot/Boot.cpp | 9 +++++++-- Source/Core/Core/Boot/Boot.h | 1 + Source/Core/Core/Boot/Boot_BS2Emu.cpp | 16 ++++++++-------- Source/Core/Core/HW/DVDInterface.cpp | 5 ----- Source/Core/Core/HW/DVDInterface.h | 1 - 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Source/Core/Core/Boot/Boot.cpp b/Source/Core/Core/Boot/Boot.cpp index 30fce0bc68..d4aa35b653 100644 --- a/Source/Core/Core/Boot/Boot.cpp +++ b/Source/Core/Core/Boot/Boot.cpp @@ -32,6 +32,11 @@ #include "DiscIO/NANDContentLoader.h" #include "DiscIO/VolumeCreator.h" +bool CBoot::DVDRead(u64 dvd_offset, u32 output_address, u32 length, bool decrypt) +{ + return DVDInterface::GetVolume().Read(dvd_offset, length, Memory::GetPointer(output_address), decrypt); +} + void CBoot::Load_FST(bool _bIsWii) { if (!DVDInterface::VolumeIsValid()) @@ -40,7 +45,7 @@ void CBoot::Load_FST(bool _bIsWii) const DiscIO::IVolume& volume = DVDInterface::GetVolume(); // copy first 20 bytes of disc to start of Mem 1 - DVDInterface::DVDRead(/*offset*/0, /*address*/0, /*length*/0x20, false); + DVDRead(/*offset*/0, /*address*/0, /*length*/0x20, false); // copy of game id Memory::Write_U32(Memory::Read_U32(0x0000), 0x3180); @@ -57,7 +62,7 @@ void CBoot::Load_FST(bool _bIsWii) Memory::Write_U32(arenaHigh, 0x00000034); // load FST - DVDInterface::DVDRead(fstOffset, arenaHigh, fstSize, _bIsWii); + DVDRead(fstOffset, arenaHigh, fstSize, _bIsWii); Memory::Write_U32(arenaHigh, 0x00000038); Memory::Write_U32(maxFstSize, 0x0000003c); } diff --git a/Source/Core/Core/Boot/Boot.h b/Source/Core/Core/Boot/Boot.h index fbf221b7a6..41815c00f4 100644 --- a/Source/Core/Core/Boot/Boot.h +++ b/Source/Core/Core/Boot/Boot.h @@ -41,6 +41,7 @@ public: std::string* title_id = nullptr); private: + static bool DVDRead(u64 dvd_offset, u32 output_address, u32 length, bool decrypt); static void RunFunction(u32 _iAddr); static void UpdateDebugger_MapLoaded(); diff --git a/Source/Core/Core/Boot/Boot_BS2Emu.cpp b/Source/Core/Core/Boot/Boot_BS2Emu.cpp index 6ee859c73a..c8f6f0d270 100644 --- a/Source/Core/Core/Boot/Boot_BS2Emu.cpp +++ b/Source/Core/Core/Boot/Boot_BS2Emu.cpp @@ -59,7 +59,7 @@ bool CBoot::EmulatedBS2_GC(bool skipAppLoader) // It's possible to boot DOL and ELF files without a disc inserted if (DVDInterface::VolumeIsValid()) - DVDInterface::DVDRead(/*offset*/0x00000000, /*address*/0x00000000, 0x20, false); // write disc info + DVDRead(/*offset*/0x00000000, /*address*/0x00000000, 0x20, false); // write disc info PowerPC::HostWrite_U32(0x0D15EA5E, 0x80000020); // Booted from bootrom. 0xE5207C22 = booted from jtag PowerPC::HostWrite_U32(Memory::REALRAM_SIZE, 0x80000028); // Physical Memory Size (24MB on retail) @@ -98,7 +98,7 @@ bool CBoot::EmulatedBS2_GC(bool skipAppLoader) INFO_LOG(BOOT, "GC BS2: Not running apploader!"); return false; } - DVDInterface::DVDRead(iAppLoaderOffset + 0x20, 0x01200000, iAppLoaderSize, false); + DVDRead(iAppLoaderOffset + 0x20, 0x01200000, iAppLoaderSize, false); // Setup pointers like real BS2 does if (SConfig::GetInstance().bNTSC) @@ -149,7 +149,7 @@ bool CBoot::EmulatedBS2_GC(bool skipAppLoader) u32 iDVDOffset = PowerPC::Read_U32(0x8130000c); INFO_LOG(MASTER_LOG, "DVDRead: offset: %08x memOffset: %08x length: %i", iDVDOffset, iRamAddress, iLength); - DVDInterface::DVDRead(iDVDOffset, iRamAddress, iLength, false); + DVDRead(iDVDOffset, iRamAddress, iLength, false); } while (PowerPC::ppcState.gpr[3] != 0x00); @@ -256,7 +256,7 @@ bool CBoot::SetupWiiMemory(DiscIO::IVolume::ECountry country) // When booting a WAD or the system menu, there will probably not be a disc inserted if (DVDInterface::VolumeIsValid()) - DVDInterface::DVDRead(0x00000000, 0x00000000, 0x20, false); // Game Code + DVDRead(0x00000000, 0x00000000, 0x20, false); // Game Code Memory::Write_U32(0x0D15EA5E, 0x00000020); // Another magic word Memory::Write_U32(0x00000001, 0x00000024); // Unknown @@ -333,10 +333,10 @@ bool CBoot::EmulatedBS2_Wii() if (DVDInterface::VolumeIsValid() && DVDInterface::GetVolume().GetVolumeType() == DiscIO::IVolume::WII_DISC) { // This is some kind of consistency check that is compared to the 0x00 - // values as the game boots. This location keep the 4 byte ID for as long + // values as the game boots. This location keeps the 4 byte ID for as long // as the game is running. The 6 byte ID at 0x00 is overwritten sometime // after this check during booting. - DVDInterface::DVDRead(0, 0x3180, 4, true); + DVDRead(0, 0x3180, 4, true); // Set up MSR and the BAT SPR registers. UReg_MSR& m_MSR = ((UReg_MSR&)PowerPC::ppcState.msr); @@ -376,7 +376,7 @@ bool CBoot::EmulatedBS2_Wii() ERROR_LOG(BOOT, "Invalid apploader. Probably your image is corrupted."); return false; } - DVDInterface::DVDRead(iAppLoaderOffset + 0x20, 0x01200000, iAppLoaderSize, true); + DVDRead(iAppLoaderOffset + 0x20, 0x01200000, iAppLoaderSize, true); //call iAppLoaderEntry DEBUG_LOG(BOOT, "Call iAppLoaderEntry"); @@ -414,7 +414,7 @@ bool CBoot::EmulatedBS2_Wii() u32 iDVDOffset = PowerPC::Read_U32(0x8130000c) << 2; INFO_LOG(BOOT, "DVDRead: offset: %08x memOffset: %08x length: %i", iDVDOffset, iRamAddress, iLength); - DVDInterface::DVDRead(iDVDOffset, iRamAddress, iLength, true); + DVDRead(iDVDOffset, iRamAddress, iLength, true); } while (PowerPC::ppcState.gpr[3] != 0x00); // iAppLoaderClose diff --git a/Source/Core/Core/HW/DVDInterface.cpp b/Source/Core/Core/HW/DVDInterface.cpp index 12c34b818b..1dfa08124b 100644 --- a/Source/Core/Core/HW/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVDInterface.cpp @@ -514,11 +514,6 @@ void SetLidOpen(bool _bOpen) GenerateDIInterrupt(INT_CVRINT); } -bool DVDRead(u64 _iDVDOffset, u32 _iRamAddress, u32 _iLength, bool decrypt) -{ - return s_inserted_volume->Read(_iDVDOffset, _iLength, Memory::GetPointer(_iRamAddress), decrypt); -} - bool ChangePartition(u64 offset) { DVDThread::WaitUntilIdle(); diff --git a/Source/Core/Core/HW/DVDInterface.h b/Source/Core/Core/HW/DVDInterface.h index 583f3074f5..c931e128ff 100644 --- a/Source/Core/Core/HW/DVDInterface.h +++ b/Source/Core/Core/HW/DVDInterface.h @@ -105,7 +105,6 @@ bool IsDiscInside(); void ChangeDisc(const std::string& fileName); // DVD Access Functions -bool DVDRead(u64 _iDVDOffset, u32 _iRamAddress, u32 _iLength, bool decrypt); extern bool g_bStream; bool ChangePartition(u64 offset); void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_address, u32 output_length, From 1202c2ea35a5c16c0b9f1b8fd0898e9a5b473ec4 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Wed, 22 Apr 2015 17:18:00 +0200 Subject: [PATCH 6/6] Don't use GetPointer in DVDRead --- Source/Core/Core/Boot/Boot.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Source/Core/Core/Boot/Boot.cpp b/Source/Core/Core/Boot/Boot.cpp index d4aa35b653..3e58c33db9 100644 --- a/Source/Core/Core/Boot/Boot.cpp +++ b/Source/Core/Core/Boot/Boot.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include #include "Common/CommonPaths.h" #include "Common/CommonTypes.h" @@ -34,7 +35,11 @@ bool CBoot::DVDRead(u64 dvd_offset, u32 output_address, u32 length, bool decrypt) { - return DVDInterface::GetVolume().Read(dvd_offset, length, Memory::GetPointer(output_address), decrypt); + std::vector buffer(length); + if (!DVDInterface::GetVolume().Read(dvd_offset, length, buffer.data(), decrypt)) + return false; + Memory::CopyToEmu(output_address, buffer.data(), length); + return true; } void CBoot::Load_FST(bool _bIsWii)