CDROMAsyncReader: Support reading ahead more sectors

This commit is contained in:
Connor McLaughlin 2021-07-12 21:08:04 +10:00
parent 552b0098ef
commit a32ef4a963
10 changed files with 341 additions and 152 deletions

View file

@ -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,

View file

@ -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{};

View file

@ -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()
bool CDROMAsyncReader::WaitForReadToComplete()
{
if (!IsUsingThread())
// Safe without locking with memory_order_seq_cst.
if (!m_next_position_set.load() && m_buffer_count.load() > 0)
{
m_sector_read_pending.store(true);
DoSectorRead();
return;
Log_TracePrintf("Returning sector %u", m_buffers[m_buffer_front.load()].lba);
return m_buffers[m_buffer_front.load()].result;
}
Common::Timer wait_timer;
Log_DebugPrintf("Sector read pending, waiting");
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_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;
}
m_sector_read_pending.store(true);
m_do_read_cv.notify_one();
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;
}
bool CDROMAsyncReader::WaitForReadToComplete()
void CDROMAsyncReader::WaitForIdle()
{
if (!IsUsingThread())
return m_sector_read_result.load();
return;
std::unique_lock<std::mutex> lock(m_mutex);
if (m_sector_read_pending.load())
{
Log_DebugPrintf("Sector read pending, waiting");
m_notify_read_complete_cv.wait(lock, [this]() { return (!m_is_reading.load() && !m_next_position_set.load()); });
}
Common::Timer wait_timer;
m_notify_read_complete_cv.wait(lock, [this]() { return !m_sector_read_pending.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;
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);
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);
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);
}
return m_sector_read_result.load();
lock.lock();
m_is_reading.store(false);
m_buffer_count.fetch_add(1);
m_notify_read_complete_cv.notify_all();
return true;
}
void CDROMAsyncReader::DoSectorRead()
void CDROMAsyncReader::ReadSectorNonThreaded(CDImage::LBA lba)
{
Common::Timer timer;
if (m_next_position_set.load())
m_buffers.resize(1);
m_seek_error.store(false);
EmptyBuffers();
if (m_media->GetPositionOnDisc() != lba && !m_media->Seek(lba))
{
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;
}
Log_WarningPrintf("Seek to LBA %u failed", lba);
m_seek_error.store(true);
return;
}
const CDImage::LBA pos = m_media->GetPositionOnDisc();
if (!m_media->ReadRawSector(m_sector_buffer.data(), &m_subq))
BufferSlot& buffer = m_buffers.front();
buffer.lba = m_media->GetPositionOnDisc();
Log_TracePrintf("Reading LBA %u...", buffer.lba);
buffer.result = m_media->ReadRawSector(buffer.data.data(), &buffer.subq);
if (buffer.result)
{
m_sector_read_result.store(false);
Log_WarningPrintf("Read of LBA %u failed", pos);
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);
}
m_last_read_sector = pos;
m_sector_read_result.store(true);
m_buffer_count.fetch_add(1);
}
void CDROMAsyncReader::CancelReadahead()
{
Log_DevPrintf("Cancelling readahead");
const double read_time = timer.GetTimeMilliseconds();
if (read_time > 1.0f)
Log_DevPrintf("Read LBA %u took %.2f msec", pos, read_time);
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;
}
}
}

View file

@ -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 };
};

View file

@ -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 ||

View file

@ -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);

View file

@ -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;

View file

@ -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(

View file

@ -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>

View file

@ -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);