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