CDROM: Add read buffer
This commit is contained in:
parent
a2bd424a9f
commit
3648514f1b
|
@ -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};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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};
|
||||
|
|
Loading…
Reference in New Issue