mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-23 22:25:42 +00:00
185 lines
4.5 KiB
C++
185 lines
4.5 KiB
C++
#include "cdrom_async_reader.h"
|
|
#include "common/assert.h"
|
|
#include "common/log.h"
|
|
#include "common/timer.h"
|
|
Log_SetChannel(CDROMAsyncReader);
|
|
|
|
CDROMAsyncReader::CDROMAsyncReader() = default;
|
|
|
|
CDROMAsyncReader::~CDROMAsyncReader()
|
|
{
|
|
StopThread();
|
|
}
|
|
|
|
void CDROMAsyncReader::StartThread()
|
|
{
|
|
if (IsUsingThread())
|
|
return;
|
|
|
|
m_shutdown_flag.store(false);
|
|
m_read_thread = std::thread(&CDROMAsyncReader::WorkerThreadEntryPoint, this);
|
|
}
|
|
|
|
void CDROMAsyncReader::StopThread()
|
|
{
|
|
if (!IsUsingThread())
|
|
return;
|
|
|
|
{
|
|
std::unique_lock<std::mutex> lock(m_mutex);
|
|
if (m_sector_read_pending.load())
|
|
m_notify_read_complete_cv.wait(lock, [this]() { return !m_sector_read_pending.load(); });
|
|
|
|
m_shutdown_flag.store(true);
|
|
m_do_read_cv.notify_one();
|
|
}
|
|
|
|
m_read_thread.join();
|
|
}
|
|
|
|
void CDROMAsyncReader::SetMedia(std::unique_ptr<CDImage> media)
|
|
{
|
|
WaitForReadToComplete();
|
|
m_media = std::move(media);
|
|
}
|
|
|
|
std::unique_ptr<CDImage> CDROMAsyncReader::RemoveMedia()
|
|
{
|
|
WaitForReadToComplete();
|
|
return std::move(m_media);
|
|
}
|
|
|
|
void CDROMAsyncReader::QueueReadSector(CDImage::LBA lba)
|
|
{
|
|
if (!IsUsingThread())
|
|
{
|
|
m_sector_read_pending.store(true);
|
|
m_next_position_set.store(true);
|
|
m_next_position = lba;
|
|
DoSectorRead();
|
|
return;
|
|
}
|
|
|
|
std::unique_lock<std::mutex> lock(m_mutex);
|
|
if (m_sector_read_pending.load())
|
|
m_notify_read_complete_cv.wait(lock, [this]() { return !m_sector_read_pending.load(); });
|
|
|
|
// don't re-read the same sector if it was the last one we read
|
|
// the CDC code does this when seeking->reading
|
|
if (m_last_read_sector == lba && m_sector_read_result.load())
|
|
{
|
|
Log_DebugPrintf("Skipping re-reading same sector %u", lba);
|
|
return;
|
|
}
|
|
|
|
m_sector_read_pending.store(true);
|
|
m_next_position_set.store(true);
|
|
m_next_position = lba;
|
|
m_do_read_cv.notify_one();
|
|
}
|
|
|
|
bool CDROMAsyncReader::ReadSectorUncached(CDImage::LBA lba, CDImage::SubChannelQ* subq, SectorBuffer* data)
|
|
{
|
|
WaitForReadToComplete();
|
|
|
|
if (m_media->GetPositionOnDisc() != lba && !m_media->Seek(lba))
|
|
{
|
|
Log_WarningPrintf("Seek to LBA %u failed", lba);
|
|
return false;
|
|
}
|
|
|
|
if ((subq && !m_media->ReadSubChannelQ(subq)) || (data && !m_media->ReadRawSector(data->data())))
|
|
{
|
|
Log_WarningPrintf("Read of LBA %u failed", lba);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CDROMAsyncReader::QueueReadNextSector()
|
|
{
|
|
if (!IsUsingThread())
|
|
{
|
|
m_sector_read_pending.store(true);
|
|
DoSectorRead();
|
|
return;
|
|
}
|
|
|
|
std::unique_lock<std::mutex> lock(m_mutex);
|
|
if (m_sector_read_pending.load())
|
|
m_notify_read_complete_cv.wait(lock, [this]() { return !m_sector_read_pending.load(); });
|
|
|
|
m_sector_read_pending.store(true);
|
|
m_do_read_cv.notify_one();
|
|
}
|
|
|
|
bool CDROMAsyncReader::WaitForReadToComplete()
|
|
{
|
|
if (!IsUsingThread())
|
|
return m_sector_read_result.load();
|
|
|
|
std::unique_lock<std::mutex> lock(m_mutex);
|
|
if (m_sector_read_pending.load())
|
|
{
|
|
Log_DebugPrintf("Sector read pending, waiting");
|
|
|
|
Common::Timer wait_timer;
|
|
m_notify_read_complete_cv.wait(lock, [this]() { return !m_sector_read_pending.load(); });
|
|
|
|
const double wait_time = wait_timer.GetTimeMilliseconds();
|
|
if (wait_time > 1.0f)
|
|
Log_WarningPrintf("Had to wait %.2f msec for LBA %u", wait_time, m_last_read_sector);
|
|
}
|
|
|
|
return m_sector_read_result.load();
|
|
}
|
|
|
|
void CDROMAsyncReader::DoSectorRead()
|
|
{
|
|
Common::Timer timer;
|
|
|
|
if (m_next_position_set.load())
|
|
{
|
|
if (m_media->GetPositionOnDisc() != m_next_position && !m_media->Seek(m_next_position))
|
|
{
|
|
Log_WarningPrintf("Seek to LBA %u failed", m_next_position);
|
|
m_sector_read_result.store(false);
|
|
return;
|
|
}
|
|
}
|
|
|
|
CDImage::LBA pos = m_media->GetPositionOnDisc();
|
|
if (!m_media->ReadSubChannelQ(&m_subq) || !m_media->ReadRawSector(m_sector_buffer.data()))
|
|
{
|
|
m_sector_read_result.store(false);
|
|
Log_WarningPrintf("Read of LBA %u failed", pos);
|
|
return;
|
|
}
|
|
|
|
m_last_read_sector = pos;
|
|
m_sector_read_result.store(true);
|
|
|
|
const double read_time = timer.GetTimeMilliseconds();
|
|
if (read_time > 1.0f)
|
|
Log_DevPrintf("Read LBA %u took %.2f msec", pos, read_time);
|
|
}
|
|
|
|
void CDROMAsyncReader::WorkerThreadEntryPoint()
|
|
{
|
|
std::unique_lock lock(m_mutex);
|
|
|
|
while (!m_shutdown_flag.load())
|
|
{
|
|
m_do_read_cv.wait(lock, [this]() { return (m_shutdown_flag.load() || m_sector_read_pending.load()); });
|
|
if (m_sector_read_pending.load())
|
|
{
|
|
lock.unlock();
|
|
DoSectorRead();
|
|
lock.lock();
|
|
m_sector_read_pending.store(false);
|
|
m_notify_read_complete_cv.notify_one();
|
|
}
|
|
}
|
|
}
|