CDROMAsyncReader: Support reading ahead more sectors
This commit is contained in:
parent
552b0098ef
commit
a32ef4a963
|
@ -99,8 +99,8 @@ void CDROM::Initialize()
|
|||
[](void* param, TickCount ticks, TickCount ticks_late) { static_cast<CDROM*>(param)->ExecuteDrive(ticks_late); },
|
||||
this, false);
|
||||
|
||||
if (g_settings.cdrom_read_thread)
|
||||
m_reader.StartThread();
|
||||
if (g_settings.cdrom_readahead_sectors > 0)
|
||||
m_reader.StartThread(g_settings.cdrom_readahead_sectors);
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
@ -232,7 +232,8 @@ void CDROM::SoftReset(TickCount ticks_late)
|
|||
{
|
||||
m_drive_state = DriveState::SeekingImplicit;
|
||||
m_drive_event->SetIntervalAndSchedule(total_ticks);
|
||||
m_reader.QueueReadSector(0);
|
||||
m_requested_lba = 0;
|
||||
m_reader.QueueReadSector(m_requested_lba);
|
||||
m_seek_start_lba = m_current_lba;
|
||||
m_seek_end_lba = 0;
|
||||
}
|
||||
|
@ -306,14 +307,12 @@ bool CDROM::DoState(StateWrapper& sw)
|
|||
}
|
||||
|
||||
sw.Do(&m_audio_fifo);
|
||||
|
||||
u32 requested_sector = (sw.IsWriting() ? (m_reader.WaitForReadToComplete(), m_reader.GetLastReadSector()) : 0);
|
||||
sw.Do(&requested_sector);
|
||||
sw.Do(&m_requested_lba);
|
||||
|
||||
if (sw.IsReading())
|
||||
{
|
||||
if (m_reader.HasMedia())
|
||||
m_reader.QueueReadSector(requested_sector);
|
||||
m_reader.QueueReadSector(m_requested_lba);
|
||||
UpdateCommandEvent();
|
||||
m_drive_event->SetState(!IsDriveIdle());
|
||||
}
|
||||
|
@ -397,15 +396,18 @@ std::unique_ptr<CDImage> CDROM::RemoveMedia(bool force /* = false */)
|
|||
return image;
|
||||
}
|
||||
|
||||
void CDROM::SetUseReadThread(bool enabled)
|
||||
void CDROM::SetReadaheadSectors(u32 readahead_sectors)
|
||||
{
|
||||
if (enabled == m_reader.IsUsingThread())
|
||||
const bool want_thread = (readahead_sectors > 0);
|
||||
if (want_thread == m_reader.IsUsingThread() && m_reader.GetReadaheadCount() == readahead_sectors)
|
||||
return;
|
||||
|
||||
if (enabled)
|
||||
m_reader.StartThread();
|
||||
if (want_thread)
|
||||
m_reader.StartThread(readahead_sectors);
|
||||
else
|
||||
m_reader.StopThread();
|
||||
|
||||
m_reader.QueueReadSector(m_requested_lba);
|
||||
}
|
||||
|
||||
void CDROM::CPUClockChanged()
|
||||
|
@ -1417,8 +1419,6 @@ void CDROM::ExecuteCommand(TickCount ticks_late)
|
|||
Log_DebugPrintf("CDROM GetTN command");
|
||||
if (CanReadMedia())
|
||||
{
|
||||
m_reader.WaitForReadToComplete();
|
||||
|
||||
Log_DevPrintf("GetTN -> %u %u", m_reader.GetMedia()->GetFirstTrackNumber(),
|
||||
m_reader.GetMedia()->GetLastTrackNumber());
|
||||
|
||||
|
@ -1757,7 +1757,8 @@ void CDROM::BeginReading(TickCount ticks_late /* = 0 */, bool after_seek /* = fa
|
|||
m_current_read_sector_buffer = 0;
|
||||
m_current_write_sector_buffer = 0;
|
||||
|
||||
m_reader.QueueReadSector(m_current_lba);
|
||||
m_requested_lba = m_current_lba;
|
||||
m_reader.QueueReadSector(m_requested_lba);
|
||||
}
|
||||
|
||||
void CDROM::BeginPlaying(u8 track, TickCount ticks_late /* = 0 */, bool after_seek /* = false */)
|
||||
|
@ -1799,7 +1800,8 @@ void CDROM::BeginPlaying(u8 track, TickCount ticks_late /* = 0 */, bool after_se
|
|||
m_current_read_sector_buffer = 0;
|
||||
m_current_write_sector_buffer = 0;
|
||||
|
||||
m_reader.QueueReadSector(m_current_lba);
|
||||
m_requested_lba = m_current_lba;
|
||||
m_reader.QueueReadSector(m_requested_lba);
|
||||
}
|
||||
|
||||
void CDROM::BeginSeeking(bool logical, bool read_after_seek, bool play_after_seek)
|
||||
|
@ -1828,7 +1830,8 @@ void CDROM::BeginSeeking(bool logical, bool read_after_seek, bool play_after_see
|
|||
|
||||
m_seek_start_lba = m_current_lba;
|
||||
m_seek_end_lba = seek_lba;
|
||||
m_reader.QueueReadSector(seek_lba);
|
||||
m_requested_lba = seek_lba;
|
||||
m_reader.QueueReadSector(m_requested_lba);
|
||||
}
|
||||
|
||||
void CDROM::UpdatePositionWhileSeeking()
|
||||
|
@ -2022,9 +2025,10 @@ bool CDROM::CompleteSeek()
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_current_lba = m_reader.GetLastReadSector();
|
||||
}
|
||||
|
||||
m_current_lba = m_reader.GetLastReadSector();
|
||||
m_physical_lba = m_current_lba;
|
||||
m_physical_lba_update_tick = TimingEvents::GetGlobalTickCounter();
|
||||
m_physical_lba_update_carry = 0;
|
||||
|
@ -2275,7 +2279,8 @@ void CDROM::DoSectorRead()
|
|||
is_data_sector ? "data" : "audio", is_data_sector ? "reading" : "playing");
|
||||
}
|
||||
|
||||
m_reader.QueueReadSector(next_sector);
|
||||
m_requested_lba = next_sector;
|
||||
m_reader.QueueReadSector(m_requested_lba);
|
||||
}
|
||||
|
||||
void CDROM::ProcessDataSectorHeader(const u8* raw_sector)
|
||||
|
@ -2698,12 +2703,13 @@ void CDROM::DrawDebugWindow()
|
|||
|
||||
if (media->HasSubImages())
|
||||
{
|
||||
ImGui::Text("Filename: %s [Subimage %u of %u]", media->GetFileName().c_str(), media->GetCurrentSubImage() + 1u,
|
||||
media->GetSubImageCount());
|
||||
ImGui::Text("Filename: %s [Subimage %u of %u] [%u buffered sectors]", media->GetFileName().c_str(),
|
||||
media->GetCurrentSubImage() + 1u, media->GetSubImageCount(), m_reader.GetBufferedSectorCount());
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::Text("Filename: %s", media->GetFileName().c_str());
|
||||
ImGui::Text("Filename: %s [%u buffered sectors]", media->GetFileName().c_str(),
|
||||
m_reader.GetBufferedSectorCount());
|
||||
}
|
||||
|
||||
ImGui::Text("Disc Position: MSF[%02u:%02u:%02u] LBA[%u]", disc_position.minute, disc_position.second,
|
||||
|
|
|
@ -45,7 +45,7 @@ public:
|
|||
// Render statistics debug window.
|
||||
void DrawDebugWindow();
|
||||
|
||||
void SetUseReadThread(bool enabled);
|
||||
void SetReadaheadSectors(u32 readahead_sectors);
|
||||
|
||||
/// Reads a frame from the audio FIFO, used by the SPU.
|
||||
ALWAYS_INLINE std::tuple<s16, s16> GetAudioFrame()
|
||||
|
@ -347,6 +347,7 @@ private:
|
|||
u8 m_pending_async_interrupt = 0;
|
||||
|
||||
CDImage::Position m_setloc_position = {};
|
||||
CDImage::LBA m_requested_lba{};
|
||||
CDImage::LBA m_current_lba{}; // this is the hold position
|
||||
CDImage::LBA m_seek_start_lba{};
|
||||
CDImage::LBA m_seek_end_lba{};
|
||||
|
|
|
@ -11,13 +11,18 @@ CDROMAsyncReader::~CDROMAsyncReader()
|
|||
StopThread();
|
||||
}
|
||||
|
||||
void CDROMAsyncReader::StartThread()
|
||||
void CDROMAsyncReader::StartThread(u32 readahead_count)
|
||||
{
|
||||
if (IsUsingThread())
|
||||
return;
|
||||
StopThread();
|
||||
|
||||
m_buffers.clear();
|
||||
m_buffers.resize(readahead_count);
|
||||
EmptyBuffers();
|
||||
|
||||
m_shutdown_flag.store(false);
|
||||
m_read_thread = std::thread(&CDROMAsyncReader::WorkerThreadEntryPoint, this);
|
||||
Log_InfoPrintf("Read thread started with readahead of %u sectors", readahead_count);
|
||||
}
|
||||
|
||||
void CDROMAsyncReader::StopThread()
|
||||
|
@ -27,25 +32,28 @@ void CDROMAsyncReader::StopThread()
|
|||
|
||||
{
|
||||
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();
|
||||
EmptyBuffers();
|
||||
m_buffers.clear();
|
||||
}
|
||||
|
||||
void CDROMAsyncReader::SetMedia(std::unique_ptr<CDImage> media)
|
||||
{
|
||||
WaitForReadToComplete();
|
||||
if (IsUsingThread())
|
||||
CancelReadahead();
|
||||
|
||||
m_media = std::move(media);
|
||||
}
|
||||
|
||||
std::unique_ptr<CDImage> CDROMAsyncReader::RemoveMedia()
|
||||
{
|
||||
WaitForReadToComplete();
|
||||
if (IsUsingThread())
|
||||
CancelReadahead();
|
||||
|
||||
return std::move(m_media);
|
||||
}
|
||||
|
||||
|
@ -53,26 +61,39 @@ 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();
|
||||
ReadSectorNonThreaded(lba);
|
||||
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())
|
||||
const u32 buffer_count = m_buffer_count.load();
|
||||
if (buffer_count > 0)
|
||||
{
|
||||
Log_DebugPrintf("Skipping re-reading same sector %u", lba);
|
||||
return;
|
||||
// don't re-read the same sector if it was the last one we read
|
||||
// the CDC code does this when seeking->reading
|
||||
const u32 buffer_front = m_buffer_front.load();
|
||||
if (m_buffers[buffer_front].lba == lba)
|
||||
{
|
||||
Log_DebugPrintf("Skipping re-reading same sector %u", lba);
|
||||
return;
|
||||
}
|
||||
|
||||
// did we readahead to the correct sector?
|
||||
const u32 next_buffer = (buffer_front + 1) % static_cast<u32>(m_buffers.size());
|
||||
if (m_buffer_count > 1 && m_buffers[next_buffer].lba == lba)
|
||||
{
|
||||
// great, don't need a seek, but still kick the thread to start reading ahead again
|
||||
Log_DebugPrintf("Readahead buffer hit for sector %u", lba);
|
||||
m_buffer_front.store(next_buffer);
|
||||
m_buffer_count.fetch_sub(1);
|
||||
m_can_readahead.store(true);
|
||||
m_do_read_cv.notify_one();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_sector_read_pending.store(true);
|
||||
// we need to toss away our readahead and start fresh
|
||||
Log_DebugPrintf("Readahead buffer miss, queueing seek to %u", lba);
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
m_next_position_set.store(true);
|
||||
m_next_position = lba;
|
||||
m_do_read_cv.notify_one();
|
||||
|
@ -80,7 +101,8 @@ void CDROMAsyncReader::QueueReadSector(CDImage::LBA lba)
|
|||
|
||||
bool CDROMAsyncReader::ReadSectorUncached(CDImage::LBA lba, CDImage::SubChannelQ* subq, SectorBuffer* data)
|
||||
{
|
||||
WaitForReadToComplete();
|
||||
if (IsUsingThread())
|
||||
CancelReadahead();
|
||||
|
||||
if (m_media->GetPositionOnDisc() != lba && !m_media->Seek(lba))
|
||||
{
|
||||
|
@ -97,88 +119,203 @@ bool CDROMAsyncReader::ReadSectorUncached(CDImage::LBA lba, CDImage::SubChannelQ
|
|||
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())
|
||||
// Safe without locking with memory_order_seq_cst.
|
||||
if (!m_next_position_set.load() && m_buffer_count.load() > 0)
|
||||
{
|
||||
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);
|
||||
Log_TracePrintf("Returning sector %u", m_buffers[m_buffer_front.load()].lba);
|
||||
return m_buffers[m_buffer_front.load()].result;
|
||||
}
|
||||
|
||||
return m_sector_read_result.load();
|
||||
Common::Timer wait_timer;
|
||||
Log_DebugPrintf("Sector read pending, waiting");
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
m_notify_read_complete_cv.wait(
|
||||
lock, [this]() { return (m_buffer_count.load() > 0 || m_seek_error.load()) && !m_next_position_set.load(); });
|
||||
if (m_seek_error.load())
|
||||
{
|
||||
m_seek_error.store(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
const u32 front = m_buffer_front.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_buffers[front].lba);
|
||||
|
||||
Log_TracePrintf("Returning sector %u after waiting", m_buffers[front].lba);
|
||||
return m_buffers[front].result;
|
||||
}
|
||||
|
||||
void CDROMAsyncReader::DoSectorRead()
|
||||
void CDROMAsyncReader::WaitForIdle()
|
||||
{
|
||||
if (!IsUsingThread())
|
||||
return;
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
m_notify_read_complete_cv.wait(lock, [this]() { return (!m_is_reading.load() && !m_next_position_set.load()); });
|
||||
}
|
||||
|
||||
void CDROMAsyncReader::EmptyBuffers()
|
||||
{
|
||||
m_buffer_front.store(0);
|
||||
m_buffer_back.store(0);
|
||||
m_buffer_count.store(0);
|
||||
}
|
||||
|
||||
bool CDROMAsyncReader::ReadSectorIntoBuffer(std::unique_lock<std::mutex>& lock)
|
||||
{
|
||||
Common::Timer timer;
|
||||
|
||||
if (m_next_position_set.load())
|
||||
const u32 slot = m_buffer_back.load();
|
||||
m_buffer_back.store((slot + 1) % static_cast<u32>(m_buffers.size()));
|
||||
|
||||
BufferSlot& buffer = m_buffers[slot];
|
||||
buffer.lba = m_media->GetPositionOnDisc();
|
||||
m_is_reading.store(true);
|
||||
lock.unlock();
|
||||
|
||||
Log_TracePrintf("Reading LBA %u...", buffer.lba);
|
||||
|
||||
buffer.result = m_media->ReadRawSector(buffer.data.data(), &buffer.subq);
|
||||
if (buffer.result)
|
||||
{
|
||||
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;
|
||||
}
|
||||
const double read_time = timer.GetTimeMilliseconds();
|
||||
if (read_time > 1.0f)
|
||||
Log_DevPrintf("Read LBA %u took %.2f msec", buffer.lba, read_time);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log_ErrorPrintf("Read of LBA %u failed", buffer.lba);
|
||||
}
|
||||
|
||||
const CDImage::LBA pos = m_media->GetPositionOnDisc();
|
||||
if (!m_media->ReadRawSector(m_sector_buffer.data(), &m_subq))
|
||||
lock.lock();
|
||||
m_is_reading.store(false);
|
||||
m_buffer_count.fetch_add(1);
|
||||
m_notify_read_complete_cv.notify_all();
|
||||
return true;
|
||||
}
|
||||
|
||||
void CDROMAsyncReader::ReadSectorNonThreaded(CDImage::LBA lba)
|
||||
{
|
||||
Common::Timer timer;
|
||||
|
||||
m_buffers.resize(1);
|
||||
m_seek_error.store(false);
|
||||
EmptyBuffers();
|
||||
|
||||
if (m_media->GetPositionOnDisc() != lba && !m_media->Seek(lba))
|
||||
{
|
||||
m_sector_read_result.store(false);
|
||||
Log_WarningPrintf("Read of LBA %u failed", pos);
|
||||
Log_WarningPrintf("Seek to LBA %u failed", lba);
|
||||
m_seek_error.store(true);
|
||||
return;
|
||||
}
|
||||
|
||||
m_last_read_sector = pos;
|
||||
m_sector_read_result.store(true);
|
||||
BufferSlot& buffer = m_buffers.front();
|
||||
buffer.lba = m_media->GetPositionOnDisc();
|
||||
|
||||
const double read_time = timer.GetTimeMilliseconds();
|
||||
if (read_time > 1.0f)
|
||||
Log_DevPrintf("Read LBA %u took %.2f msec", pos, read_time);
|
||||
Log_TracePrintf("Reading LBA %u...", buffer.lba);
|
||||
|
||||
buffer.result = m_media->ReadRawSector(buffer.data.data(), &buffer.subq);
|
||||
if (buffer.result)
|
||||
{
|
||||
const double read_time = timer.GetTimeMilliseconds();
|
||||
if (read_time > 1.0f)
|
||||
Log_DevPrintf("Read LBA %u took %.2f msec", buffer.lba, read_time);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log_ErrorPrintf("Read of LBA %u failed", buffer.lba);
|
||||
}
|
||||
|
||||
m_buffer_count.fetch_add(1);
|
||||
}
|
||||
|
||||
void CDROMAsyncReader::CancelReadahead()
|
||||
{
|
||||
Log_DevPrintf("Cancelling readahead");
|
||||
|
||||
std::unique_lock lock(m_mutex);
|
||||
|
||||
// wait until the read thread is idle
|
||||
m_notify_read_complete_cv.wait(lock, [this]() { return !m_is_reading.load(); });
|
||||
|
||||
// prevent it from doing any more when it re-acquires the lock
|
||||
m_can_readahead.store(false);
|
||||
EmptyBuffers();
|
||||
}
|
||||
|
||||
void CDROMAsyncReader::WorkerThreadEntryPoint()
|
||||
{
|
||||
std::unique_lock lock(m_mutex);
|
||||
|
||||
while (!m_shutdown_flag.load())
|
||||
for (;;)
|
||||
{
|
||||
m_do_read_cv.wait(lock, [this]() { return (m_shutdown_flag.load() || m_sector_read_pending.load()); });
|
||||
if (m_sector_read_pending.load())
|
||||
m_do_read_cv.wait(
|
||||
lock, [this]() { return (m_shutdown_flag.load() || m_next_position_set.load() || m_can_readahead.load()); });
|
||||
if (m_shutdown_flag.load())
|
||||
break;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
lock.unlock();
|
||||
DoSectorRead();
|
||||
lock.lock();
|
||||
m_sector_read_pending.store(false);
|
||||
m_notify_read_complete_cv.notify_one();
|
||||
if (m_next_position_set.load())
|
||||
{
|
||||
// discard buffers, we're seeking to a new location
|
||||
const CDImage::LBA seek_location = m_next_position.load();
|
||||
EmptyBuffers();
|
||||
m_next_position_set.store(false);
|
||||
m_seek_error.store(false);
|
||||
m_is_reading.store(true);
|
||||
lock.unlock();
|
||||
|
||||
// seek without lock held in case it takes time
|
||||
Log_DebugPrintf("Seeking to LBA %u...", seek_location);
|
||||
const bool seek_result = (m_media->GetPositionOnDisc() == seek_location || m_media->Seek(seek_location));
|
||||
|
||||
lock.lock();
|
||||
m_is_reading.store(false);
|
||||
|
||||
// did another request come in? abort if so
|
||||
if (m_next_position_set.load())
|
||||
continue;
|
||||
|
||||
// did we fail the seek?
|
||||
if (!seek_result)
|
||||
{
|
||||
// add the error result, and don't try to read ahead
|
||||
Log_WarningPrintf("Seek to LBA %u failed", seek_location);
|
||||
m_seek_error.store(true);
|
||||
m_notify_read_complete_cv.notify_all();
|
||||
break;
|
||||
}
|
||||
|
||||
// go go read ahead!
|
||||
m_can_readahead.store(true);
|
||||
}
|
||||
|
||||
if (!m_can_readahead.load())
|
||||
break;
|
||||
|
||||
// readahead time! read as many sectors as we have space for
|
||||
Log_DebugPrintf("Reading ahead %u sectors...", static_cast<u32>(m_buffers.size()) - m_buffer_count.load());
|
||||
while (m_buffer_count.load() < static_cast<u32>(m_buffers.size()))
|
||||
{
|
||||
if (m_next_position_set.load())
|
||||
{
|
||||
// a seek request came in while we're reading, so bail out
|
||||
break;
|
||||
}
|
||||
|
||||
// stop reading if we hit the end or get an error
|
||||
if (!ReadSectorIntoBuffer(lock))
|
||||
break;
|
||||
}
|
||||
|
||||
// readahead buffer is full or errored at this point
|
||||
m_can_readahead.store(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,33 +11,49 @@ class CDROMAsyncReader
|
|||
public:
|
||||
using SectorBuffer = std::array<u8, CDImage::RAW_SECTOR_SIZE>;
|
||||
|
||||
struct BufferSlot
|
||||
{
|
||||
CDImage::LBA lba;
|
||||
SectorBuffer data;
|
||||
CDImage::SubChannelQ subq;
|
||||
bool result;
|
||||
};
|
||||
|
||||
CDROMAsyncReader();
|
||||
~CDROMAsyncReader();
|
||||
|
||||
const CDImage::LBA GetLastReadSector() const { return m_last_read_sector; }
|
||||
const SectorBuffer& GetSectorBuffer() const { return m_sector_buffer; }
|
||||
const CDImage::SubChannelQ& GetSectorSubQ() const { return m_subq; }
|
||||
const CDImage::LBA GetLastReadSector() const { return m_buffers[m_buffer_front.load()].lba; }
|
||||
const SectorBuffer& GetSectorBuffer() const { return m_buffers[m_buffer_front.load()].data; }
|
||||
const CDImage::SubChannelQ& GetSectorSubQ() const { return m_buffers[m_buffer_front.load()].subq; }
|
||||
const u32 GetBufferedSectorCount() const { return m_buffer_count.load(); }
|
||||
const bool HasBufferedSectors() const { return (m_buffer_count.load() > 0); }
|
||||
const u32 GetReadaheadCount() const { return static_cast<u32>(m_buffers.size()); }
|
||||
|
||||
const bool HasMedia() const { return static_cast<bool>(m_media); }
|
||||
const CDImage* GetMedia() const { return m_media.get(); }
|
||||
const std::string& GetMediaFileName() const { return m_media->GetFileName(); }
|
||||
|
||||
bool IsUsingThread() const { return m_read_thread.joinable(); }
|
||||
void StartThread();
|
||||
void StartThread(u32 readahead_count = 8);
|
||||
void StopThread();
|
||||
|
||||
void SetMedia(std::unique_ptr<CDImage> media);
|
||||
std::unique_ptr<CDImage> RemoveMedia();
|
||||
|
||||
void QueueReadSector(CDImage::LBA lba);
|
||||
void QueueReadNextSector();
|
||||
|
||||
bool WaitForReadToComplete();
|
||||
void WaitForIdle();
|
||||
|
||||
/// Bypasses the sector cache and reads directly from the image.
|
||||
bool ReadSectorUncached(CDImage::LBA lba, CDImage::SubChannelQ* subq, SectorBuffer* data);
|
||||
|
||||
private:
|
||||
void DoSectorRead();
|
||||
void EmptyBuffers();
|
||||
bool ReadSectorIntoBuffer(std::unique_lock<std::mutex>& lock);
|
||||
void ReadSectorNonThreaded(CDImage::LBA lba);
|
||||
void CancelReadahead();
|
||||
|
||||
void WorkerThreadEntryPoint();
|
||||
|
||||
std::unique_ptr<CDImage> m_media;
|
||||
|
@ -47,13 +63,16 @@ private:
|
|||
std::condition_variable m_do_read_cv;
|
||||
std::condition_variable m_notify_read_complete_cv;
|
||||
|
||||
CDImage::LBA m_next_position{};
|
||||
std::atomic<CDImage::LBA> m_next_position{};
|
||||
std::atomic_bool m_next_position_set{false};
|
||||
std::atomic_bool m_sector_read_pending{false};
|
||||
std::atomic_bool m_shutdown_flag{true};
|
||||
|
||||
CDImage::LBA m_last_read_sector{};
|
||||
CDImage::SubChannelQ m_subq{};
|
||||
SectorBuffer m_sector_buffer{};
|
||||
std::atomic_bool m_sector_read_result{false};
|
||||
std::atomic_bool m_is_reading{ false };
|
||||
std::atomic_bool m_can_readahead{ false };
|
||||
std::atomic_bool m_seek_error{ false };
|
||||
|
||||
std::vector<BufferSlot> m_buffers;
|
||||
std::atomic<u32> m_buffer_front{ 0 };
|
||||
std::atomic<u32> m_buffer_back{ 0 };
|
||||
std::atomic<u32> m_buffer_count{ 0 };
|
||||
};
|
||||
|
|
|
@ -565,7 +565,7 @@ void HostInterface::SetDefaultSettings(SettingsInterface& si)
|
|||
si.SetStringValue("Display", "PostProcessChain", "");
|
||||
si.SetFloatValue("Display", "MaxFPS", Settings::DEFAULT_DISPLAY_MAX_FPS);
|
||||
|
||||
si.SetBoolValue("CDROM", "ReadThread", true);
|
||||
si.SetIntValue("CDROM", "ReadaheadSectors", Settings::DEFAULT_CDROM_READAHEAD_SECTORS);
|
||||
si.SetBoolValue("CDROM", "RegionCheck", false);
|
||||
si.SetBoolValue("CDROM", "LoadImageToRAM", false);
|
||||
si.SetBoolValue("CDROM", "MuteCDAudio", false);
|
||||
|
@ -853,8 +853,8 @@ void HostInterface::CheckForSettingsChanges(const Settings& old_settings)
|
|||
PGXP::Initialize();
|
||||
}
|
||||
|
||||
if (g_settings.cdrom_read_thread != old_settings.cdrom_read_thread)
|
||||
g_cdrom.SetUseReadThread(g_settings.cdrom_read_thread);
|
||||
if (g_settings.cdrom_readahead_sectors != old_settings.cdrom_readahead_sectors)
|
||||
g_cdrom.SetReadaheadSectors(g_settings.cdrom_readahead_sectors);
|
||||
|
||||
if (g_settings.memory_card_types != old_settings.memory_card_types ||
|
||||
g_settings.memory_card_paths != old_settings.memory_card_paths ||
|
||||
|
|
|
@ -242,7 +242,7 @@ void Settings::Load(SettingsInterface& si)
|
|||
display_post_process_chain = si.GetStringValue("Display", "PostProcessChain", "");
|
||||
display_max_fps = si.GetFloatValue("Display", "MaxFPS", DEFAULT_DISPLAY_MAX_FPS);
|
||||
|
||||
cdrom_read_thread = si.GetBoolValue("CDROM", "ReadThread", true);
|
||||
cdrom_readahead_sectors = static_cast<u8>(si.GetIntValue("CDROM", "ReadaheadSectors", DEFAULT_CDROM_READAHEAD_SECTORS));
|
||||
cdrom_region_check = si.GetBoolValue("CDROM", "RegionCheck", false);
|
||||
cdrom_load_image_to_ram = si.GetBoolValue("CDROM", "LoadImageToRAM", false);
|
||||
cdrom_mute_cd_audio = si.GetBoolValue("CDROM", "MuteCDAudio", false);
|
||||
|
@ -419,7 +419,7 @@ void Settings::Save(SettingsInterface& si) const
|
|||
si.SetStringValue("Display", "PostProcessChain", display_post_process_chain.c_str());
|
||||
si.SetFloatValue("Display", "MaxFPS", display_max_fps);
|
||||
|
||||
si.SetBoolValue("CDROM", "ReadThread", cdrom_read_thread);
|
||||
si.SetIntValue("CDROM", "ReadaheadSectors", cdrom_readahead_sectors);
|
||||
si.SetBoolValue("CDROM", "RegionCheck", cdrom_region_check);
|
||||
si.SetBoolValue("CDROM", "LoadImageToRAM", cdrom_load_image_to_ram);
|
||||
si.SetBoolValue("CDROM", "MuteCDAudio", cdrom_mute_cd_audio);
|
||||
|
|
|
@ -152,7 +152,7 @@ struct Settings
|
|||
float gpu_pgxp_tolerance = -1.0f;
|
||||
float gpu_pgxp_depth_clear_threshold = 300.0f / 4096.0f;
|
||||
|
||||
bool cdrom_read_thread = true;
|
||||
u8 cdrom_readahead_sectors = DEFAULT_CDROM_READAHEAD_SECTORS;
|
||||
bool cdrom_region_check = false;
|
||||
bool cdrom_load_image_to_ram = false;
|
||||
bool cdrom_mute_cd_audio = false;
|
||||
|
@ -379,6 +379,9 @@ struct Settings
|
|||
|
||||
static constexpr DisplayCropMode DEFAULT_DISPLAY_CROP_MODE = DisplayCropMode::Overscan;
|
||||
static constexpr DisplayAspectRatio DEFAULT_DISPLAY_ASPECT_RATIO = DisplayAspectRatio::Auto;
|
||||
|
||||
static constexpr u8 DEFAULT_CDROM_READAHEAD_SECTORS = 8;
|
||||
|
||||
static constexpr ControllerType DEFAULT_CONTROLLER_1_TYPE = ControllerType::DigitalController;
|
||||
static constexpr ControllerType DEFAULT_CONTROLLER_2_TYPE = ControllerType::None;
|
||||
static constexpr MemoryCardType DEFAULT_MEMORY_CARD_1_TYPE = MemoryCardType::PerGameTitle;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "consolesettingswidget.h"
|
||||
#include "common/cd_image.h"
|
||||
#include "core/system.h"
|
||||
#include "qtutils.h"
|
||||
#include "settingsdialog.h"
|
||||
|
@ -28,6 +29,17 @@ ConsoleSettingsWidget::ConsoleSettingsWidget(QtHostInterface* host_interface, QW
|
|||
qApp->translate("MultitapMode", Settings::GetMultitapModeDisplayName(static_cast<MultitapMode>(i))));
|
||||
}
|
||||
|
||||
static constexpr float TIME_PER_SECTOR_DOUBLE_SPEED = 1000.0f / 150.0f;
|
||||
m_ui.cdromReadaheadSectors->addItem(tr("Disabled (Synchronous)"));
|
||||
for (u32 i = 1; i <= 32; i++)
|
||||
{
|
||||
m_ui.cdromReadaheadSectors->addItem(tr("%1 sectors (%2 KB / %3 ms)")
|
||||
.arg(i)
|
||||
|
||||
.arg(static_cast<float>(i) * TIME_PER_SECTOR_DOUBLE_SPEED, 0, 'f', 0)
|
||||
.arg(static_cast<float>(i * CDImage::DATA_SECTOR_SIZE) / 1024.0f));
|
||||
}
|
||||
|
||||
SettingWidgetBinder::BindWidgetToEnumSetting(m_host_interface, m_ui.region, "Console", "Region",
|
||||
&Settings::ParseConsoleRegionName, &Settings::GetConsoleRegionName,
|
||||
Settings::DEFAULT_CONSOLE_REGION);
|
||||
|
@ -37,7 +49,8 @@ ConsoleSettingsWidget::ConsoleSettingsWidget(QtHostInterface* host_interface, QW
|
|||
Settings::DEFAULT_CPU_EXECUTION_MODE);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.enableCPUClockSpeedControl, "CPU",
|
||||
"OverclockEnable", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.cdromReadThread, "CDROM", "ReadThread", true);
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(m_host_interface, m_ui.cdromReadaheadSectors, "CDROM", "ReadaheadSectors",
|
||||
Settings::DEFAULT_CDROM_READAHEAD_SECTORS);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.cdromRegionCheck, "CDROM", "RegionCheck", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.cdromLoadImageToRAM, "CDROM", "LoadImageToRAM",
|
||||
false);
|
||||
|
@ -74,9 +87,10 @@ ConsoleSettingsWidget::ConsoleSettingsWidget(QtHostInterface* host_interface, QW
|
|||
m_ui.cdromSeekSpeedup, tr("CD-ROM Seek Speedup"), tr("None (Normal Speed)"),
|
||||
tr("Reduces the simulated time for the CD-ROM sled to move to different areas of the disc. Can improve loading "
|
||||
"times, but crash games which do not expect the CD-ROM to operate faster."));
|
||||
dialog->registerWidgetHelp(
|
||||
m_ui.cdromReadThread, tr("Use Read Thread (Asynchronous)"), tr("Checked"),
|
||||
tr("Reduces hitches in emulation by reading/decompressing CD data asynchronously on a worker thread."));
|
||||
dialog->registerWidgetHelp(m_ui.cdromReadaheadSectors, tr("Asynchronous Readahead"), tr("8 Sectors"),
|
||||
tr("Reduces hitches in emulation by reading/decompressing CD data asynchronously on a "
|
||||
"worker thread. Higher sector numbers can reduce spikes when streaming FMVs or audio "
|
||||
"on slower storage or when using compression formats such as CHD."));
|
||||
dialog->registerWidgetHelp(m_ui.cdromRegionCheck, tr("Enable Region Check"), tr("Checked"),
|
||||
tr("Simulates the region check present in original, unmodified consoles."));
|
||||
dialog->registerWidgetHelp(
|
||||
|
|
|
@ -137,14 +137,17 @@
|
|||
<string>CD-ROM Emulation</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_4">
|
||||
<item row="0" column="0">
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="cdromReadaheadSectors"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Read Speedup:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="cdromReadSpeedup">
|
||||
<item>
|
||||
<property name="text">
|
||||
|
@ -198,46 +201,14 @@
|
|||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="cdromReadThread">
|
||||
<property name="text">
|
||||
<string>Use Read Thread (Asynchronous)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="cdromRegionCheck">
|
||||
<property name="text">
|
||||
<string>Enable Region Check</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="cdromLoadImageToRAM">
|
||||
<property name="text">
|
||||
<string>Preload Image To RAM</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="cdromLoadImagePatches">
|
||||
<property name="text">
|
||||
<string>Apply Image Patches</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Seek Speedup:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="cdromSeekSpeedup">
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
|
@ -299,6 +270,38 @@
|
|||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="cdromRegionCheck">
|
||||
<property name="text">
|
||||
<string>Enable Region Check</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="cdromLoadImageToRAM">
|
||||
<property name="text">
|
||||
<string>Preload Image To RAM</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="cdromLoadImagePatches">
|
||||
<property name="text">
|
||||
<string>Apply Image Patches</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Async Readahead:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -1588,10 +1588,16 @@ void DrawSettingsWindow()
|
|||
});
|
||||
}
|
||||
|
||||
settings_changed |= ToggleButton(
|
||||
"Enable Read Thread",
|
||||
"Reduces hitches in emulation by reading/decompressing CD data asynchronously on a worker thread.",
|
||||
&s_settings_copy.cdrom_read_thread);
|
||||
s32 readahead_sectors = s_settings_copy.cdrom_readahead_sectors;
|
||||
if (RangeButton(
|
||||
"Readahead Sectors",
|
||||
"Reduces hitches in emulation by reading/decompressing CD data asynchronously on a worker thread.",
|
||||
&readahead_sectors, 0, 32, 1))
|
||||
{
|
||||
s_settings_copy.cdrom_readahead_sectors = static_cast<u8>(readahead_sectors);
|
||||
settings_changed = true;
|
||||
}
|
||||
|
||||
settings_changed |=
|
||||
ToggleButton("Enable Region Check", "Simulates the region check present in original, unmodified consoles.",
|
||||
&s_settings_copy.cdrom_region_check);
|
||||
|
|
Loading…
Reference in New Issue