buffer cache: grow stream reserves toward incoming requests

This commit is contained in:
nmzik
2026-09-09 14:32:07 +04:00
parent ba18551d6e
commit 57844a07e2
2 changed files with 76 additions and 27 deletions
+4 -2
View File
@@ -340,10 +340,12 @@ BufferCache::OverlapResult BufferCache::ResolveOverlaps(uint64_t vaddr, uint64_t
end = std::max(end, buffer_end); end = std::max(end, buffer_end);
if (!has_stream_leap && (stream_score += buffer.StreamScore()) > StreamLeapThreshold) { if (!has_stream_leap && (stream_score += buffer.StreamScore()) > StreamLeapThreshold) {
has_stream_leap = true; has_stream_leap = true;
if (expands_right) { // Fix the shadPS4 bug that reserves space opposite to the incoming stream's growth.
// The old buffer extending left of the request predicts growth to the right, and vice versa.
if (expands_left) {
end += std::min(StreamLeapSize, PageTable::kAddressSpaceSize - end); end += std::min(StreamLeapSize, PageTable::kAddressSpaceSize - end);
} }
if (expands_left) { if (expands_right) {
const auto minimum = CACHING_PAGESIZE * 2; const auto minimum = CACHING_PAGESIZE * 2;
if (begin > minimum) { if (begin > minimum) {
begin -= std::min(StreamLeapSize, begin - minimum); begin -= std::min(StreamLeapSize, begin - minimum);
+72 -25
View File
@@ -3605,33 +3605,80 @@ public:
resolved_left_offset != resolved_right_offset, resolved_left_offset != resolved_right_offset,
"saved descriptor IDs did not resolve through the merged owner"); "saved descriptor IDs did not resolve through the merged owner");
constexpr uint64_t stream_begin = base + 0x1800000;
constexpr uint64_t stream_page = BufferCache::CACHING_PAGESIZE; constexpr uint64_t stream_page = BufferCache::CACHING_PAGESIZE;
constexpr uint64_t stream_leap = stream_page * 128; constexpr uint64_t stream_leap = stream_page * 128;
const auto caught = for (const bool forward : {true, false}) {
cache.FindBuffer(stream_begin - stream_leap / 2, stream_page); const char *stream_name =
const auto anchor = cache.FindBuffer(stream_begin, stream_page * 2); forward ? "ForwardBufferStream" : "BackwardBufferStream";
cache.GetBuffer(anchor).IncreaseStreamScore(16); const uint64_t stream_begin = base + (forward ? 0x1800000 : 0x2000000);
const auto scored = const uint64_t caught_address = forward
cache.FindBuffer(stream_begin + stream_page * 2 - 4, 8); ? stream_begin + stream_leap / 2
Require(name, "stream growth threshold", : stream_begin - stream_leap / 2;
cache.GetBuffer(scored).CpuAddress() == stream_begin && const uint64_t untouched_address = forward
cache.GetBuffer(scored).Size() == stream_page * 3 && ? stream_begin - stream_leap / 2
cache.GetBuffer(scored).StreamScore() == 17, : stream_begin + stream_leap / 2;
"buffer range grew before the stream threshold"); const auto caught = cache.FindBuffer(caught_address, stream_page);
const auto grown = const auto untouched = cache.FindBuffer(untouched_address, stream_page);
cache.FindBuffer(stream_begin + stream_page * 3 - 4, 8); auto grown = cache.FindBuffer(stream_begin, stream_page * 2);
const auto &grown_buffer = cache.GetBuffer(grown); constexpr uint32_t stream_value = 0x6a3b92d1u;
Require(name, "stream range leap", for (const auto address : {stream_begin, caught_address}) {
grown_buffer.CpuAddress() == stream_begin - stream_leap && MarkGpuWrite(address, sizeof(stream_value));
grown_buffer.Size() == stream_leap + stream_page * 4 && cache.FillBuffer(address, sizeof(stream_value), stream_value, false);
grown_buffer.StreamScore() == 0 && }
cache.GetBuffer(caught).is_deleted &&
cache.GetBuffer(scored).is_deleted && uint64_t requested_begin = stream_begin;
BufferCacheTestAccess::PageOwner( uint64_t requested_end = stream_begin + stream_page * 2;
cache, stream_begin - stream_leap / 2) == grown, for (uint32_t step = 1; step <= 18; ++step) {
"buffer stream leap changed its direction, size, reset, or " const auto address = (forward ? requested_end : requested_begin) - 4;
"overlap capture"); grown = cache.FindBuffer(address, 8);
if (forward) {
requested_end += stream_page;
} else {
requested_begin -= stream_page;
}
if (step < 18) {
const auto &owner = cache.GetBuffer(grown);
Require(stream_name, "growth before threshold",
owner.CpuAddress() == requested_begin &&
owner.Size() == requested_end - requested_begin &&
owner.StreamScore() == static_cast<int>(step),
"sequential requests grew beyond their consumed pages too "
"early");
}
}
const auto &grown_buffer = cache.GetBuffer(grown);
Require(stream_name, "growth reserve",
grown_buffer.CpuAddress() ==
(forward ? requested_begin
: requested_begin - stream_leap) &&
grown_buffer.Size() ==
requested_end - requested_begin + stream_leap &&
grown_buffer.StreamScore() == 0 &&
cache.GetBuffer(caught).is_deleted &&
!cache.GetBuffer(untouched).is_deleted &&
BufferCacheTestAccess::PageOwner(cache, caught_address) ==
grown &&
BufferCacheTestAccess::PageOwner(
cache, untouched_address) == untouched,
"streaming did not reserve the next pages or retain correct "
"neighboring owners");
for (uint32_t step = 0; step < 128; ++step) {
const auto address = forward
? requested_end + step * stream_page - 4
: requested_begin - step * stream_page - 4;
Require(stream_name, "reuse reserved pages",
cache.FindBuffer(address, 8) == grown,
"continued sequential requests replaced the buffer within "
"its growth reserve");
}
for (const auto address : {stream_begin, caught_address}) {
Require(
stream_name, "preserve GPU contents",
ReadNativeValue(grown_buffer, grown_buffer.Offset(address)) ==
stream_value,
"stream growth lost GPU-written contents from a merged owner");
}
}
resources.UnmapMemory(index_begin, index_span); resources.UnmapMemory(index_begin, index_span);
resources.UnmapMemory(base + recycled_offset, index_page); resources.UnmapMemory(base + recycled_offset, index_page);