CDROM: Add read buffer

This commit is contained in:
Connor McLaughlin 2022-11-29 16:53:00 +10:00
parent a2bd424a9f
commit 3648514f1b
4 changed files with 93 additions and 37 deletions

View File

@ -217,8 +217,8 @@ void CDROM::SoftReset(TickCount ticks_late)
{
m_drive_state = DriveState::SeekingImplicit;
m_drive_event->SetIntervalAndSchedule(total_ticks);
m_requested_lba = 0;
m_reader.QueueReadSector(m_requested_lba);
ClearReadBuffers();
QueueSectorRead(0);
m_seek_start_lba = m_current_lba;
m_seek_end_lba = 0;
}
@ -296,8 +296,9 @@ bool CDROM::DoState(StateWrapper& sw)
if (sw.IsReading())
{
ClearReadBuffers();
if (m_reader.HasMedia())
m_reader.QueueReadSector(m_requested_lba);
QueueSectorRead(m_requested_lba);
UpdateCommandEvent();
m_drive_event->SetState(!IsDriveIdle());
@ -422,8 +423,9 @@ void CDROM::SetReadaheadSectors(u32 readahead_sectors)
else
m_reader.StopThread();
// TODO: This will probably break...
if (HasMedia())
m_reader.QueueReadSector(m_requested_lba);
QueueSectorRead(m_requested_lba);
}
void CDROM::CPUClockChanged()
@ -892,8 +894,7 @@ CDImage::LBA CDROM::GetNextSectorToBeRead()
if (!IsReadingOrPlaying())
return m_current_lba;
m_reader.WaitForReadToComplete();
return m_reader.GetLastReadSector();
return m_sector_read_buffer.lba;
}
void CDROM::BeginCommand(Command command)
@ -1807,8 +1808,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_requested_lba = m_current_lba;
m_reader.QueueReadSector(m_requested_lba);
ClearReadBuffers();
QueueSectorRead(m_current_lba);
}
void CDROM::BeginPlaying(u8 track, TickCount ticks_late /* = 0 */, bool after_seek /* = false */)
@ -1850,8 +1851,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_requested_lba = m_current_lba;
m_reader.QueueReadSector(m_requested_lba);
ClearReadBuffers();
QueueSectorRead(m_current_lba);
}
void CDROM::BeginSeeking(bool logical, bool read_after_seek, bool play_after_seek)
@ -1880,8 +1881,9 @@ 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_requested_lba = seek_lba;
m_reader.QueueReadSector(m_requested_lba);
ClearReadBuffers();
QueueSectorRead(seek_lba);
}
void CDROM::UpdatePositionWhileSeeking()
@ -1991,7 +1993,7 @@ void CDROM::UpdatePhysicalPosition(bool update_logical)
CDImage::SubChannelQ subq;
CDROMAsyncReader::SectorBuffer raw_sector;
if (!m_reader.ReadSectorUncached(new_physical_lba, &subq, update_logical ? &raw_sector : nullptr))
if (!m_reader.ReadSectorUncached(new_physical_lba, &subq, update_logical ? raw_sector.data() : nullptr))
{
Log_ErrorPrintf("Failed to read subq for sector %u for physical position", new_physical_lba);
}
@ -2267,18 +2269,18 @@ void CDROM::DoSectorRead()
if (!m_reader.WaitForReadToComplete())
Panic("Sector read failed");
m_current_lba = m_reader.GetLastReadSector();
const ReadBuffer& rb = m_sector_read_buffer;
m_current_lba = rb.lba;
m_physical_lba = m_current_lba;
m_physical_lba_update_tick = TimingEvents::GetGlobalTickCounter();
m_physical_lba_update_carry = 0;
m_secondary_status.SetReadingBits(m_drive_state == DriveState::Playing);
const CDImage::SubChannelQ& subq = m_reader.GetSectorSubQ();
const bool subq_valid = subq.IsCRCValid();
const bool subq_valid = rb.subq.IsCRCValid();
if (subq_valid)
{
m_last_subq = subq;
m_last_subq = rb.subq;
}
else
{
@ -2287,24 +2289,24 @@ void CDROM::DoSectorRead()
pos.frame);
}
if (subq.track_number_bcd == CDImage::LEAD_OUT_TRACK_NUMBER)
if (rb.subq.track_number_bcd == CDImage::LEAD_OUT_TRACK_NUMBER)
{
Log_DevPrintf("Read reached lead-out area of disc at LBA %u, stopping", m_reader.GetLastReadSector());
Log_DevPrintf("Read reached lead-out area of disc at LBA %u, stopping", rb.lba);
StopReadingWithDataEnd();
StopMotor();
return;
}
const bool is_data_sector = subq.IsData();
const bool is_data_sector = rb.subq.IsData();
if (!is_data_sector)
{
if (m_play_track_number_bcd == 0)
{
// track number was not specified, but we've found the track now
m_play_track_number_bcd = subq.track_number_bcd;
m_play_track_number_bcd = rb.subq.track_number_bcd;
Log_DebugPrintf("Setting playing track number to %u", m_play_track_number_bcd);
}
else if (m_mode.auto_pause && subq.track_number_bcd != m_play_track_number_bcd)
else if (m_mode.auto_pause && rb.subq.track_number_bcd != m_play_track_number_bcd)
{
// we don't want to update the position if the track changes, so we check it before reading the actual sector.
Log_DevPrintf("Auto pause at the start of track %02x (LBA %u)", m_last_subq.track_number_bcd, m_current_lba);
@ -2314,18 +2316,18 @@ void CDROM::DoSectorRead()
}
else
{
ProcessDataSectorHeader(m_reader.GetSectorBuffer().data());
ProcessDataSectorHeader(rb.data.data());
}
u32 next_sector = m_current_lba + 1u;
if (is_data_sector && m_drive_state == DriveState::Reading)
{
ProcessDataSector(m_reader.GetSectorBuffer().data(), subq);
ProcessDataSector(rb.data.data(), rb.subq);
}
else if (!is_data_sector &&
(m_drive_state == DriveState::Playing || (m_drive_state == DriveState::Reading && m_mode.cdda)))
{
ProcessCDDASector(m_reader.GetSectorBuffer().data(), subq);
ProcessCDDASector(rb.data.data(), rb.subq);
if (m_fast_forward_rate != 0)
next_sector = m_current_lba + SignExtend32(m_fast_forward_rate);
@ -2340,8 +2342,7 @@ void CDROM::DoSectorRead()
is_data_sector ? "data" : "audio", is_data_sector ? "reading" : "playing");
}
m_requested_lba = next_sector;
m_reader.QueueReadSector(m_requested_lba);
QueueSectorRead(next_sector);
}
void CDROM::ProcessDataSectorHeader(const u8* raw_sector)
@ -2740,6 +2741,18 @@ void CDROM::ClearSectorBuffers()
m_sector_buffers[i].size = 0;
}
void CDROM::ClearReadBuffers()
{
// noop for now
}
void CDROM::QueueSectorRead(CDImage::LBA lba)
{
m_requested_lba = lba;
m_sector_read_buffer.lba = lba;
m_reader.QueueReadSector(lba, &m_sector_read_buffer.subq, m_sector_read_buffer.data.data());
}
void CDROM::DrawDebugWindow()
{
static const ImVec4 active_color{1.0f, 1.0f, 1.0f, 1.0f};

View File

@ -330,6 +330,9 @@ private:
void LoadDataFIFO();
void ClearSectorBuffers();
void ClearReadBuffers();
void QueueSectorRead(CDImage::LBA lba);
template<bool STEREO, bool SAMPLE_RATE>
void ResampleXAADPCM(const s16* frames_in, u32 num_frames_in);
@ -393,6 +396,15 @@ private:
InlineFIFOQueue<u8, RESPONSE_FIFO_SIZE> m_async_response_fifo;
HeapFIFOQueue<u8, DATA_FIFO_SIZE> m_data_fifo;
struct ReadBuffer
{
HeapArray<u8, CDImage::RAW_SECTOR_SIZE> data;
CDImage::SubChannelQ subq;
u32 lba;
};
ReadBuffer m_sector_read_buffer;
struct SectorBuffer
{
HeapArray<u8, RAW_SECTOR_OUTPUT_SIZE> data;

View File

@ -96,11 +96,11 @@ bool CDROMAsyncReader::Precache(ProgressCallback* callback)
return (res == CDImage::PrecacheResult::Success);
}
void CDROMAsyncReader::QueueReadSector(CDImage::LBA lba)
void CDROMAsyncReader::QueueReadSector(CDImage::LBA lba, CDImage::SubChannelQ* subq, u8* data)
{
if (!IsUsingThread())
{
ReadSectorNonThreaded(lba);
ReadSectorNonThreaded(lba, subq, data);
return;
}
@ -113,6 +113,7 @@ void CDROMAsyncReader::QueueReadSector(CDImage::LBA lba)
if (m_buffers[buffer_front].lba == lba)
{
Log_DebugPrintf("Skipping re-reading same sector %u", lba);
CopyOutFrontBuffer(subq, data);
return;
}
@ -126,6 +127,7 @@ void CDROMAsyncReader::QueueReadSector(CDImage::LBA lba)
m_buffer_count.fetch_sub(1);
m_can_readahead.store(true);
m_do_read_cv.notify_one();
CopyOutFrontBuffer(subq, data);
return;
}
}
@ -133,12 +135,14 @@ void CDROMAsyncReader::QueueReadSector(CDImage::LBA lba)
// 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_out_buffer.store(data);
m_next_out_subq.store(subq);
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)
bool CDROMAsyncReader::ReadSectorUncached(CDImage::LBA lba, CDImage::SubChannelQ* subq, u8* data)
{
if (!IsUsingThread())
return InternalReadSectorUncached(lba, subq, data);
@ -160,7 +164,7 @@ bool CDROMAsyncReader::ReadSectorUncached(CDImage::LBA lba, CDImage::SubChannelQ
return result;
}
bool CDROMAsyncReader::InternalReadSectorUncached(CDImage::LBA lba, CDImage::SubChannelQ* subq, SectorBuffer* data)
bool CDROMAsyncReader::InternalReadSectorUncached(CDImage::LBA lba, CDImage::SubChannelQ* subq, u8* data)
{
if (m_media->GetPositionOnDisc() != lba && !m_media->Seek(lba))
{
@ -223,6 +227,21 @@ void CDROMAsyncReader::EmptyBuffers()
m_buffer_count.store(0);
}
void CDROMAsyncReader::CopyOutFrontBuffer(CDImage::SubChannelQ* subq, u8* data)
{
const BufferSlot& sb = m_buffers[m_buffer_front.load()];
if (sb.result)
{
if (subq)
std::memcpy(subq, &sb.subq, sizeof(CDImage::SubChannelQ));
if (data)
std::memcpy(data, sb.data.data(), CDImage::RAW_SECTOR_SIZE);
}
m_next_out_buffer.store(nullptr);
m_next_out_subq.store(nullptr);
}
bool CDROMAsyncReader::ReadSectorIntoBuffer(std::unique_lock<std::mutex>& lock)
{
Common::Timer timer;
@ -250,13 +269,19 @@ bool CDROMAsyncReader::ReadSectorIntoBuffer(std::unique_lock<std::mutex>& lock)
}
lock.lock();
if (CDImage::SubChannelQ* out_subq = m_next_out_subq.exchange(nullptr))
std::memcpy(out_subq, &buffer.subq, sizeof(CDImage::SubChannelQ));
if (u8* out_buffer = m_next_out_buffer.exchange(nullptr))
std::memcpy(out_buffer, buffer.data.data(), CDImage::RAW_SECTOR_SIZE);
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)
void CDROMAsyncReader::ReadSectorNonThreaded(CDImage::LBA lba, CDImage::SubChannelQ* subq, u8* data)
{
Common::Timer timer;
@ -276,7 +301,7 @@ void CDROMAsyncReader::ReadSectorNonThreaded(CDImage::LBA lba)
Log_TracePrintf("Reading LBA %u...", buffer.lba);
buffer.result = m_media->ReadRawSector(buffer.data.data(), &buffer.subq);
buffer.result = m_media->ReadRawSector(data, subq);
if (buffer.result)
{
const double read_time = timer.GetTimeMilliseconds();

View File

@ -8,6 +8,8 @@
class ProgressCallback;
// TODO: Use acquire/release for all atomics.
class CDROMAsyncReader
{
public:
@ -45,19 +47,20 @@ public:
/// Precaches image, either to memory, or using the underlying image precache.
bool Precache(ProgressCallback* callback);
void QueueReadSector(CDImage::LBA lba);
void QueueReadSector(CDImage::LBA lba, CDImage::SubChannelQ* subq, u8* data);
bool WaitForReadToComplete();
void WaitForIdle();
/// Bypasses the sector cache and reads directly from the image.
bool ReadSectorUncached(CDImage::LBA lba, CDImage::SubChannelQ* subq, SectorBuffer* data);
bool ReadSectorUncached(CDImage::LBA lba, CDImage::SubChannelQ* subq, u8* data);
private:
void EmptyBuffers();
void CopyOutFrontBuffer(CDImage::SubChannelQ* subq, u8* data);
bool ReadSectorIntoBuffer(std::unique_lock<std::mutex>& lock);
void ReadSectorNonThreaded(CDImage::LBA lba);
bool InternalReadSectorUncached(CDImage::LBA lba, CDImage::SubChannelQ* subq, SectorBuffer* data);
void ReadSectorNonThreaded(CDImage::LBA lba, CDImage::SubChannelQ* subq, u8* data);
bool InternalReadSectorUncached(CDImage::LBA lba, CDImage::SubChannelQ* subq, u8* data);
void CancelReadahead();
void WorkerThreadEntryPoint();
@ -69,6 +72,9 @@ private:
std::condition_variable m_do_read_cv;
std::condition_variable m_notify_read_complete_cv;
std::atomic<u8*> m_next_out_buffer{};
std::atomic<CDImage::SubChannelQ*> m_next_out_subq{};
std::atomic<CDImage::LBA> m_next_position{};
std::atomic_bool m_next_position_set{false};
std::atomic_bool m_shutdown_flag{true};