From d4f87112ecb70fe5a8a93e40fd2ccadf0101087e Mon Sep 17 00:00:00 2001 From: TellowKrinkle Date: Sat, 27 Mar 2021 06:20:58 -0500 Subject: [PATCH] CDVD: Add minimum buffer size to ThreadedFileReader If buffers are smaller than the threshold PCSX2 uses to decide whether to time a read as a seek, ThreadedFileReader fails to provide data in time for the very small linear read time cutoff --- pcsx2/CDVD/ThreadedFileReader.cpp | 132 +++++++++++++++--------------- pcsx2/CDVD/ThreadedFileReader.h | 7 +- 2 files changed, 69 insertions(+), 70 deletions(-) diff --git a/pcsx2/CDVD/ThreadedFileReader.cpp b/pcsx2/CDVD/ThreadedFileReader.cpp index e7c12f2449..07f0a91c0e 100644 --- a/pcsx2/CDVD/ThreadedFileReader.cpp +++ b/pcsx2/CDVD/ThreadedFileReader.cpp @@ -16,6 +16,10 @@ #include "PrecompiledHeader.h" #include "ThreadedFileReader.h" +// Make sure buffer size is bigger than the cutoff where PCSX2 emulates a seek +// If buffers are smaller than that, we can't keep up with linear reads +static constexpr u32 MINIMUM_SIZE = 128 * 1024; + ThreadedFileReader::ThreadedFileReader() { m_readThread = std::thread([](ThreadedFileReader* r){ r->Loop(); }, this); @@ -86,13 +90,33 @@ void ThreadedFileReader::Loop() if (ok) { // Readahead - Chunk blk = ChunkForOffset(requestOffset + requestSize); - if (blk.chunkID >= 0) + Chunk chunk = ChunkForOffset(requestOffset + requestSize); + if (chunk.chunkID >= 0) { - (void)GetBlockPtr(blk, true); - blk = ChunkForOffset(blk.offset + blk.length); - if (blk.chunkID >= 0) - (void)GetBlockPtr(blk, true); + int buffersFilled = 0; + Buffer* buf = GetBlockPtr(chunk); + // Cancel readahead if a new request comes in + while (buf && !m_requestPtr.load(std::memory_order_acquire)) + { + u32 bufsize = buf->size.load(std::memory_order_relaxed); + chunk = ChunkForOffset(buf->offset + bufsize); + if (chunk.chunkID < 0) + break; + if (buf->offset + bufsize != chunk.offset || chunk.length + bufsize > buf->cap) + { + buffersFilled++; + if (buffersFilled >= 2) + break; + buf = GetBlockPtr(chunk); + } + else + { + int amt = ReadChunk(static_cast(buf->ptr) + bufsize, chunk.chunkID); + if (amt <= 0) + break; + buf->size.store(bufsize + amt, std::memory_order_release); + } + } } } @@ -108,11 +132,13 @@ void ThreadedFileReader::Loop() } } -ThreadedFileReader::Buffer* ThreadedFileReader::GetBlockPtr(const Chunk& block, bool isReadahead) +ThreadedFileReader::Buffer* ThreadedFileReader::GetBlockPtr(const Chunk& block) { for (int i = 0; i < static_cast(ArraySize(m_buffer)); i++) { - if (m_buffer[i].valid.load(std::memory_order_acquire) && m_buffer[i].offset == block.offset) + u32 size = m_buffer[i].size.load(std::memory_order_relaxed); + u64 offset = m_buffer[i].offset; + if (size && offset <= block.offset && offset + size >= block.offset + block.length) { m_nextBuffer = (i + 1) % ArraySize(m_buffer); return m_buffer + i; @@ -127,17 +153,19 @@ ThreadedFileReader::Buffer* ThreadedFileReader::GetBlockPtr(const Chunk& block, std::unique_lock lock(m_mtx, std::defer_lock); if (std::this_thread::get_id() == m_readThread.get_id()) lock.lock(); - if (buf.size < block.length) - buf.ptr = realloc(buf.ptr, block.length); - buf.valid.store(false, std::memory_order_relaxed); - + u32 size = std::max(block.length, MINIMUM_SIZE); + if (buf.cap < size) + { + buf.ptr = realloc(buf.ptr, size); + buf.cap = size; + } + buf.size.store(0, std::memory_order_relaxed); } int size = ReadChunk(buf.ptr, block.chunkID); if (size > 0) { buf.offset = block.offset; - buf.size = size; - buf.valid.store(true, std::memory_order_release); + buf.size.store(size, std::memory_order_release); m_nextBuffer = (m_nextBuffer + 1) % ArraySize(m_buffer); return &buf; } @@ -146,63 +174,34 @@ ThreadedFileReader::Buffer* ThreadedFileReader::GetBlockPtr(const Chunk& block, bool ThreadedFileReader::Decompress(void* target, u64 begin, u32 size) { - Chunk blk = ChunkForOffset(begin); char* write = static_cast(target); u32 remaining = size; - if (blk.offset != begin) + u64 off = begin; + while (remaining) { - u32 off = begin - blk.offset; - u32 len = std::min(blk.length - off, size); - // Partial block - if (Buffer* buf = GetBlockPtr(blk)) + Chunk chunk = ChunkForOffset(off); + if (m_internalBlockSize || chunk.offset != off || chunk.length > remaining) { - if (buf->size < blk.length) + Buffer* buf = GetBlockPtr(chunk); + if (!buf) return false; - write += CopyBlocks(write, static_cast(buf->ptr) + off, len); + u32 bufoff = off - buf->offset; + u32 bufsize = buf->size.load(std::memory_order_relaxed); + if (bufsize <= bufoff) + return false; + u32 len = std::min(bufsize - bufoff, remaining); + write += CopyBlocks(write, static_cast(buf->ptr) + bufoff, len); remaining -= len; - blk = ChunkForOffset(blk.offset + blk.length); + off += len; } else { - return false; - } - } - while (blk.length <= remaining) - { - if (m_requestCancelled.load(std::memory_order_relaxed)) - { - return false; - } - if (m_internalBlockSize) - { - if (Buffer* buf = GetBlockPtr(blk)) - { - if (buf->size < blk.length) - return false; - write += CopyBlocks(write, buf->ptr, blk.length); - } - } - else - { - int amt = ReadChunk(write, blk.chunkID); - if (amt < static_cast(blk.length)) + int amt = ReadChunk(write, chunk.chunkID); + if (amt < static_cast(chunk.length)) return false; - write += blk.length; - } - remaining -= blk.length; - blk = ChunkForOffset(blk.offset + blk.length); - } - if (remaining) - { - if (Buffer* buf = GetBlockPtr(blk)) - { - if (buf->size < remaining) - return false; - write += CopyBlocks(write, buf->ptr, remaining); - } - else - { - return false; + write += chunk.length; + remaining -= chunk.length; + off += chunk.length; } } m_amtRead += write - static_cast(target); @@ -218,19 +217,20 @@ bool ThreadedFileReader::TryCachedRead(void*& buffer, u64& offset, u32& size, co for (int i = 0; i < static_cast(ArraySize(m_buffer) * 2); i++) { Buffer& buf = m_buffer[i % ArraySize(m_buffer)]; - if (!buf.valid.load(std::memory_order_acquire)) + u32 bufsize = buf.size.load(std::memory_order_acquire); + if (!bufsize) continue; - if (buf.offset <= offset && buf.offset + buf.size > offset) + if (buf.offset <= offset && buf.offset + bufsize > offset) { u32 off = offset - buf.offset; - u32 cpysize = std::min(size, buf.size - off); + u32 cpysize = std::min(size, bufsize - off); size_t read = CopyBlocks(buffer, static_cast(buf.ptr) + off, cpysize); m_amtRead += read; size -= cpysize; offset += cpysize; buffer = static_cast(buffer) + read; if (size == 0) - end = buf.offset + buf.size; + end = buf.offset + bufsize; } // Do buffers contain the current and next block? if (end > 0 && buf.offset == end) @@ -345,7 +345,7 @@ void ThreadedFileReader::Close(void) { CancelAndWaitUntilStopped(); for (auto& buf : m_buffer) - buf.valid.store(false, std::memory_order_relaxed); + buf.size.store(0, std::memory_order_relaxed); Close2(); } diff --git a/pcsx2/CDVD/ThreadedFileReader.h b/pcsx2/CDVD/ThreadedFileReader.h index eb520a834a..4216f363a4 100644 --- a/pcsx2/CDVD/ThreadedFileReader.h +++ b/pcsx2/CDVD/ThreadedFileReader.h @@ -71,8 +71,8 @@ private: { void* ptr = nullptr; u64 offset = 0; - u32 size = 0; - std::atomic valid{false}; + std::atomic size{0}; + u32 cap = 0; }; /// 2 buffers for readahead (current block, next block) Buffer m_buffer[2]; @@ -98,8 +98,7 @@ private: void Loop(); /// Load the given block into one of the `m_buffer` buffers if necessary and return a pointer to its contents if successful - /// Writes to `m_status` if `!isReadahead` - Buffer* GetBlockPtr(const Chunk& block, bool isReadahead = false); + Buffer* GetBlockPtr(const Chunk& block); /// Decompress from offset to size into bool Decompress(void* ptr, u64 offset, u32 size); /// Cancel any inflight read and wait until the thread is no longer doing anything