Merge pull request #11029 from AdmiralCurtiss/global-state-dvd

HW: Move DVD variables to Core::System.
This commit is contained in:
Mai 2022-09-07 23:40:16 -04:00 committed by GitHub
commit 74851fe94a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 536 additions and 383 deletions

File diff suppressed because it is too large Load Diff

View File

@ -23,6 +23,23 @@ class Mapping;
namespace DVDInterface namespace DVDInterface
{ {
class DVDInterfaceState
{
public:
DVDInterfaceState();
DVDInterfaceState(const DVDInterfaceState&) = delete;
DVDInterfaceState(DVDInterfaceState&&) = delete;
DVDInterfaceState& operator=(const DVDInterfaceState&) = delete;
DVDInterfaceState& operator=(DVDInterfaceState&&) = delete;
~DVDInterfaceState();
struct Data;
Data& GetData() { return *m_data; }
private:
std::unique_ptr<Data> m_data;
};
enum class DICommand : u8 enum class DICommand : u8
{ {
Inquiry = 0x12, Inquiry = 0x12,

View File

@ -29,6 +29,7 @@
#include "Core/HW/Memmap.h" #include "Core/HW/Memmap.h"
#include "Core/HW/SystemTimers.h" #include "Core/HW/SystemTimers.h"
#include "Core/IOS/ES/Formats.h" #include "Core/IOS/ES/Formats.h"
#include "Core/System.h"
#include "DiscIO/Enums.h" #include "DiscIO/Enums.h"
#include "DiscIO/Volume.h" #include "DiscIO/Volume.h"
@ -61,8 +62,8 @@ struct ReadRequest
using ReadResult = std::pair<ReadRequest, std::vector<u8>>; using ReadResult = std::pair<ReadRequest, std::vector<u8>>;
static void StartDVDThread(); static void StartDVDThread(DVDThreadState::Data& state);
static void StopDVDThread(); static void StopDVDThread(DVDThreadState::Data& state);
static void DVDThread(); static void DVDThread();
static void WaitUntilIdle(); static void WaitUntilIdle();
@ -72,82 +73,97 @@ static void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offs
DVDInterface::ReplyType reply_type, s64 ticks_until_completion); DVDInterface::ReplyType reply_type, s64 ticks_until_completion);
static void FinishRead(u64 id, s64 cycles_late); static void FinishRead(u64 id, s64 cycles_late);
static CoreTiming::EventType* s_finish_read;
static u64 s_next_id = 0; struct DVDThreadState::Data
{
CoreTiming::EventType* finish_read;
static std::thread s_dvd_thread; u64 next_id = 0;
static Common::Event s_request_queue_expanded; // Is set by CPU thread
static Common::Event s_result_queue_expanded; // Is set by DVD thread
static Common::Flag s_dvd_thread_exiting(false); // Is set by CPU thread
static Common::SPSCQueue<ReadRequest, false> s_request_queue; std::thread dvd_thread;
static Common::SPSCQueue<ReadResult, false> s_result_queue; Common::Event request_queue_expanded; // Is set by CPU thread
static std::map<u64, ReadResult> s_result_map; Common::Event result_queue_expanded; // Is set by DVD thread
Common::Flag dvd_thread_exiting = Common::Flag(false); // Is set by CPU thread
static std::unique_ptr<DiscIO::Volume> s_disc; Common::SPSCQueue<ReadRequest, false> request_queue;
Common::SPSCQueue<ReadResult, false> result_queue;
std::map<u64, ReadResult> result_map;
std::unique_ptr<DiscIO::Volume> disc;
};
DVDThreadState::DVDThreadState() : m_data(std::make_unique<Data>())
{
}
DVDThreadState::~DVDThreadState() = default;
void Start() void Start()
{ {
s_finish_read = CoreTiming::RegisterEvent("FinishReadDVDThread", FinishRead); auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
s_request_queue_expanded.Reset(); state.finish_read = CoreTiming::RegisterEvent("FinishReadDVDThread", FinishRead);
s_result_queue_expanded.Reset();
s_request_queue.Clear(); state.request_queue_expanded.Reset();
s_result_queue.Clear(); state.result_queue_expanded.Reset();
state.request_queue.Clear();
state.result_queue.Clear();
// This is reset on every launch for determinism, but it doesn't matter // This is reset on every launch for determinism, but it doesn't matter
// much, because this will never get exposed to the emulated game. // much, because this will never get exposed to the emulated game.
s_next_id = 0; state.next_id = 0;
StartDVDThread(); StartDVDThread(state);
} }
static void StartDVDThread() static void StartDVDThread(DVDThreadState::Data& state)
{ {
ASSERT(!s_dvd_thread.joinable()); ASSERT(!state.dvd_thread.joinable());
s_dvd_thread_exiting.Clear(); state.dvd_thread_exiting.Clear();
s_dvd_thread = std::thread(DVDThread); state.dvd_thread = std::thread(DVDThread);
} }
void Stop() void Stop()
{ {
StopDVDThread(); auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
s_disc.reset(); StopDVDThread(state);
state.disc.reset();
} }
static void StopDVDThread() static void StopDVDThread(DVDThreadState::Data& state)
{ {
ASSERT(s_dvd_thread.joinable()); ASSERT(state.dvd_thread.joinable());
// By setting s_DVD_thread_exiting, we ask the DVD thread to cleanly exit. // By setting 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 // In case the request queue is empty, we need to set request_queue_expanded
// so that the DVD thread will wake up and check s_DVD_thread_exiting. // so that the DVD thread will wake up and check dvd_thread_exiting.
s_dvd_thread_exiting.Set(); state.dvd_thread_exiting.Set();
s_request_queue_expanded.Set(); state.request_queue_expanded.Set();
s_dvd_thread.join(); state.dvd_thread.join();
} }
void DoState(PointerWrap& p) void DoState(PointerWrap& p)
{ {
auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
// By waiting for the DVD thread to be done working, we ensure // By waiting for the DVD thread to be done working, we ensure
// that s_request_queue will be empty and that the DVD thread // that request_queue will be empty and that the DVD thread
// won't be touching anything while this function runs. // won't be touching anything while this function runs.
WaitUntilIdle(); WaitUntilIdle();
// Move all results from s_result_queue to s_result_map because // Move all results from result_queue to result_map because
// PointerWrap::Do supports std::map but not Common::SPSCQueue. // PointerWrap::Do supports std::map but not Common::SPSCQueue.
// This won't affect the behavior of FinishRead. // This won't affect the behavior of FinishRead.
ReadResult result; ReadResult result;
while (s_result_queue.Pop(result)) while (state.result_queue.Pop(result))
s_result_map.emplace(result.first.id, std::move(result)); state.result_map.emplace(result.first.id, std::move(result));
// Both queues are now empty, so we don't need to savestate them. // Both queues are now empty, so we don't need to savestate them.
p.Do(s_result_map); p.Do(state.result_map);
p.Do(s_next_id); p.Do(state.next_id);
// s_disc isn't savestated (because it points to files on the // state.disc isn't savestated (because it points to files on the
// local system). Instead, we check that the status of the disc // local system). Instead, we check that the status of the disc
// is the same as when the savestate was made. This won't catch // is the same as when the savestate was made. This won't catch
// cases of having the wrong disc inserted, though. // cases of having the wrong disc inserted, though.
@ -159,7 +175,7 @@ void DoState(PointerWrap& p)
if (had_disc) if (had_disc)
PanicAlertFmtT("An inserted disc was expected but not found."); PanicAlertFmtT("An inserted disc was expected but not found.");
else else
s_disc.reset(); state.disc.reset();
} }
// TODO: Savestates can be smaller if the buffers of results aren't saved, // TODO: Savestates can be smaller if the buffers of results aren't saved,
@ -175,70 +191,82 @@ void DoState(PointerWrap& p)
void SetDisc(std::unique_ptr<DiscIO::Volume> disc) void SetDisc(std::unique_ptr<DiscIO::Volume> disc)
{ {
auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
WaitUntilIdle(); WaitUntilIdle();
s_disc = std::move(disc); state.disc = std::move(disc);
} }
bool HasDisc() bool HasDisc()
{ {
return s_disc != nullptr; auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
return state.disc != nullptr;
} }
bool HasWiiHashes() bool HasWiiHashes()
{ {
// HasWiiHashes is thread-safe, so calling WaitUntilIdle isn't necessary. // HasWiiHashes is thread-safe, so calling WaitUntilIdle isn't necessary.
return s_disc->HasWiiHashes(); auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
return state.disc->HasWiiHashes();
} }
DiscIO::Platform GetDiscType() DiscIO::Platform GetDiscType()
{ {
// GetVolumeType is thread-safe, so calling WaitUntilIdle isn't necessary. // GetVolumeType is thread-safe, so calling WaitUntilIdle isn't necessary.
return s_disc->GetVolumeType(); auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
return state.disc->GetVolumeType();
} }
u64 PartitionOffsetToRawOffset(u64 offset, const DiscIO::Partition& partition) u64 PartitionOffsetToRawOffset(u64 offset, const DiscIO::Partition& partition)
{ {
// PartitionOffsetToRawOffset is thread-safe, so calling WaitUntilIdle isn't necessary. // PartitionOffsetToRawOffset is thread-safe, so calling WaitUntilIdle isn't necessary.
return s_disc->PartitionOffsetToRawOffset(offset, partition); auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
return state.disc->PartitionOffsetToRawOffset(offset, partition);
} }
IOS::ES::TMDReader GetTMD(const DiscIO::Partition& partition) IOS::ES::TMDReader GetTMD(const DiscIO::Partition& partition)
{ {
auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
WaitUntilIdle(); WaitUntilIdle();
return s_disc->GetTMD(partition); return state.disc->GetTMD(partition);
} }
IOS::ES::TicketReader GetTicket(const DiscIO::Partition& partition) IOS::ES::TicketReader GetTicket(const DiscIO::Partition& partition)
{ {
auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
WaitUntilIdle(); WaitUntilIdle();
return s_disc->GetTicket(partition); return state.disc->GetTicket(partition);
} }
bool IsInsertedDiscRunning() bool IsInsertedDiscRunning()
{ {
if (!s_disc) auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
if (!state.disc)
return false; return false;
WaitUntilIdle(); WaitUntilIdle();
return SConfig::GetInstance().GetGameID() == s_disc->GetGameID(); return SConfig::GetInstance().GetGameID() == state.disc->GetGameID();
} }
bool UpdateRunningGameMetadata(const DiscIO::Partition& partition, std::optional<u64> title_id) bool UpdateRunningGameMetadata(const DiscIO::Partition& partition, std::optional<u64> title_id)
{ {
if (!s_disc) auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
if (!state.disc)
return false; return false;
WaitUntilIdle(); WaitUntilIdle();
if (title_id) if (title_id)
{ {
const std::optional<u64> volume_title_id = s_disc->GetTitleID(partition); const std::optional<u64> volume_title_id = state.disc->GetTitleID(partition);
if (!volume_title_id || *volume_title_id != *title_id) if (!volume_title_id || *volume_title_id != *title_id)
return false; return false;
} }
SConfig::GetInstance().SetRunningGameMetadata(*s_disc, partition); SConfig::GetInstance().SetRunningGameMetadata(*state.disc, partition);
return true; return true;
} }
@ -246,11 +274,13 @@ void WaitUntilIdle()
{ {
ASSERT(Core::IsCPUThread()); ASSERT(Core::IsCPUThread());
while (!s_request_queue.Empty()) auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
s_result_queue_expanded.Wait();
StopDVDThread(); while (!state.request_queue.Empty())
StartDVDThread(); state.result_queue_expanded.Wait();
StopDVDThread(state);
StartDVDThread(state);
} }
void StartRead(u64 dvd_offset, u32 length, const DiscIO::Partition& partition, void StartRead(u64 dvd_offset, u32 length, const DiscIO::Partition& partition,
@ -273,6 +303,8 @@ static void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offs
{ {
ASSERT(Core::IsCPUThread()); ASSERT(Core::IsCPUThread());
auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
ReadRequest request; ReadRequest request;
request.copy_to_ram = copy_to_ram; request.copy_to_ram = copy_to_ram;
@ -282,21 +314,23 @@ static void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offs
request.partition = partition; request.partition = partition;
request.reply_type = reply_type; request.reply_type = reply_type;
u64 id = s_next_id++; u64 id = state.next_id++;
request.id = id; request.id = id;
request.time_started_ticks = CoreTiming::GetTicks(); request.time_started_ticks = CoreTiming::GetTicks();
request.realtime_started_us = Common::Timer::NowUs(); request.realtime_started_us = Common::Timer::NowUs();
s_request_queue.Push(std::move(request)); state.request_queue.Push(std::move(request));
s_request_queue_expanded.Set(); state.request_queue_expanded.Set();
CoreTiming::ScheduleEvent(ticks_until_completion, s_finish_read, id); CoreTiming::ScheduleEvent(ticks_until_completion, state.finish_read, id);
} }
static void FinishRead(u64 id, s64 cycles_late) static void FinishRead(u64 id, s64 cycles_late)
{ {
// We can't simply pop s_result_queue and always get the ReadResult auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
// We can't simply pop result_queue and always get the ReadResult
// we want, because the DVD thread may add ReadResults to the queue // 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 // 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 // is to pop the queue until we find the ReadResult we want (the one
@ -307,23 +341,23 @@ static void FinishRead(u64 id, s64 cycles_late)
// When this function is called again later, it will check the map for // When this function is called again later, it will check the map for
// the wanted ReadResult before it starts searching through the queue. // the wanted ReadResult before it starts searching through the queue.
ReadResult result; ReadResult result;
auto it = s_result_map.find(id); auto it = state.result_map.find(id);
if (it != s_result_map.end()) if (it != state.result_map.end())
{ {
result = std::move(it->second); result = std::move(it->second);
s_result_map.erase(it); state.result_map.erase(it);
} }
else else
{ {
while (true) while (true)
{ {
while (!s_result_queue.Pop(result)) while (!state.result_queue.Pop(result))
s_result_queue_expanded.Wait(); state.result_queue_expanded.Wait();
if (result.first.id == id) if (result.first.id == id)
break; break;
else else
s_result_map.emplace(result.first.id, std::move(result)); state.result_map.emplace(result.first.id, std::move(result));
} }
} }
// We have now obtained the right ReadResult. // We have now obtained the right ReadResult.
@ -363,30 +397,32 @@ static void FinishRead(u64 id, s64 cycles_late)
static void DVDThread() static void DVDThread()
{ {
auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
Common::SetCurrentThreadName("DVD thread"); Common::SetCurrentThreadName("DVD thread");
while (true) while (true)
{ {
s_request_queue_expanded.Wait(); state.request_queue_expanded.Wait();
if (s_dvd_thread_exiting.IsSet()) if (state.dvd_thread_exiting.IsSet())
return; return;
ReadRequest request; ReadRequest request;
while (s_request_queue.Pop(request)) while (state.request_queue.Pop(request))
{ {
FileMonitor::Log(*s_disc, request.partition, request.dvd_offset); FileMonitor::Log(*state.disc, request.partition, request.dvd_offset);
std::vector<u8> buffer(request.length); std::vector<u8> buffer(request.length);
if (!s_disc->Read(request.dvd_offset, request.length, buffer.data(), request.partition)) if (!state.disc->Read(request.dvd_offset, request.length, buffer.data(), request.partition))
buffer.resize(0); buffer.resize(0);
request.realtime_done_us = Common::Timer::NowUs(); request.realtime_done_us = Common::Timer::NowUs();
s_result_queue.Push(ReadResult(std::move(request), std::move(buffer))); state.result_queue.Push(ReadResult(std::move(request), std::move(buffer)));
s_result_queue_expanded.Set(); state.result_queue_expanded.Set();
if (s_dvd_thread_exiting.IsSet()) if (state.dvd_thread_exiting.IsSet())
return; return;
} }
} }

View File

@ -34,6 +34,23 @@ class TicketReader;
namespace DVDThread namespace DVDThread
{ {
class DVDThreadState
{
public:
DVDThreadState();
DVDThreadState(const DVDThreadState&) = delete;
DVDThreadState(DVDThreadState&&) = delete;
DVDThreadState& operator=(const DVDThreadState&) = delete;
DVDThreadState& operator=(DVDThreadState&&) = delete;
~DVDThreadState();
struct Data;
Data& GetData() { return *m_data; }
private:
std::unique_ptr<Data> m_data;
};
void Start(); void Start();
void Stop(); void Stop();
void DoState(PointerWrap& p); void DoState(PointerWrap& p);

View File

@ -7,6 +7,8 @@
#include "AudioCommon/SoundStream.h" #include "AudioCommon/SoundStream.h"
#include "Core/Config/MainSettings.h" #include "Core/Config/MainSettings.h"
#include "Core/HW/DVD/DVDInterface.h"
#include "Core/HW/DVD/DVDThread.h"
namespace Core namespace Core
{ {
@ -15,6 +17,9 @@ struct System::Impl
std::unique_ptr<SoundStream> m_sound_stream; std::unique_ptr<SoundStream> m_sound_stream;
bool m_sound_stream_running = false; bool m_sound_stream_running = false;
bool m_audio_dump_started = false; bool m_audio_dump_started = false;
DVDInterface::DVDInterfaceState m_dvd_interface_state;
DVDThread::DVDThreadState m_dvd_thread_state;
}; };
System::System() : m_impl{std::make_unique<Impl>()} System::System() : m_impl{std::make_unique<Impl>()}
@ -58,4 +63,14 @@ void System::SetAudioDumpStarted(bool started)
{ {
m_impl->m_audio_dump_started = started; m_impl->m_audio_dump_started = started;
} }
DVDInterface::DVDInterfaceState& System::GetDVDInterfaceState() const
{
return m_impl->m_dvd_interface_state;
}
DVDThread::DVDThreadState& System::GetDVDThreadState() const
{
return m_impl->m_dvd_thread_state;
}
} // namespace Core } // namespace Core

View File

@ -7,6 +7,15 @@
class SoundStream; class SoundStream;
namespace DVDInterface
{
class DVDInterfaceState;
}
namespace DVDThread
{
class DVDThreadState;
}
namespace Core namespace Core
{ {
// Central class that encapsulates the running system. // Central class that encapsulates the running system.
@ -40,6 +49,9 @@ public:
bool IsAudioDumpStarted() const; bool IsAudioDumpStarted() const;
void SetAudioDumpStarted(bool started); void SetAudioDumpStarted(bool started);
DVDInterface::DVDInterfaceState& GetDVDInterfaceState() const;
DVDThread::DVDThreadState& GetDVDThreadState() const;
private: private:
System(); System();