fix(agc): bound the draw cache by bytes

This commit is contained in:
Yunus Shaikh committed 2026-10-23 19:17:00 -07:00
1 parent 5e6ae6e3d0
commit fa02afeb47
9 files changed
+136 -5

No files matched your search

@@ -98,7 +98,7 @@ struct DrawEntryCounters {
std::uint64_t registerKeyLookups = 0, registerKeyHits = 0, decodeSkipped = 0, decodePartial = 0, facadeMismatches = 0, verifyDecodes = 0, verifyDecodeMismatches = 0;
double keyUs = 0;
std::uint64_t absentNewRegisters = 0, absentUserWords = 0, absentEvicted = 0, absentNeverInserted = 0, evictions = 0;
std::uint64_t absentNewRegisters = 0, absentUserWords = 0, absentEvicted = 0, absentNeverInserted = 0, evictions = 0, byteEvictions = 0;
std::array<std::uint64_t, 6> registerKeyMissChanges{};
std::uint64_t differingSameRuns = 0, differingWords = 0, differingRunsChanged = 0;
std::uint64_t dataHits = 0, dataStagesReused = 0, dataStagesCompiled = 0, dataVerified = 0;
@@ -96,7 +96,9 @@ public:
std::uint64_t variantBytes = 0;
};
DispatchCacheFootprint DispatchCacheSize();
DispatchCacheFootprint DrawCacheSize();
static std::uint64_t DispatchCacheBytes();
static std::uint64_t DrawCacheBytes();
static ShaderMemory::PendingWrite QueryPendingSpan(std::uint64_t address, std::size_t bytes, std::span<std::byte> known);
std::shared_ptr<const ShaderRegistry> RegisteredShaders();
@@ -408,6 +410,7 @@ private:
std::uint64_t drawCacheHits = 0, drawCacheEvictions = 0, drawCacheVariants = 0, drawCacheVariantBytes = 0;
std::optional<DrawKeySightings> drawKeySightings;
std::optional<DrawKeySightings> drawShapeSightings;
std::uint64_t drawCacheByteEvictions = 0;
DrawEntryCounters drawEntryCounters;
@@ -81,6 +81,7 @@ public:
};
static SupplyCounts Supplies();
[[nodiscard]] std::vector<std::shared_ptr<const ShaderRecompiler::SrtGuestWords>> SuppliedBlocks() const;
[[nodiscard]] std::size_t Bytes() const;
struct VouchCounts {
std::uint64_t calls, blocks, refused, words;
};
@@ -65,7 +65,8 @@ Driver::DispatchCacheFootprint Driver::DispatchCacheSize() {
}
std::uint64_t Driver::variantBytes(const DispatchVariant& variant) {
return sizeof(DispatchVariant) + variant.words.size() * sizeof(std::uint32_t) + variant.shared.size() * sizeof(SharedWords) + variant.runs.size() * sizeof(std::pair<std::uint64_t, std::uint64_t>) + variant.captured.size() * sizeof(ShaderRecompiler::MemoryRegion) + (variant.vertexInfo != nullptr ? sizeof(ShaderRecompiler::ShaderVertexStageInfo) : 0);
const auto held = [](const auto& values) { return static_cast<std::uint64_t>(values.capacity() * sizeof(values[0])); };
return sizeof(DispatchVariant) + held(variant.runs) + held(variant.words) + held(variant.shared) + held(variant.dataPositions) + held(variant.dataSlots) + held(variant.runBases) + held(variant.shaderDataPatches) + held(variant.captured) + held(variant.spans) + (variant.vertexInfo != nullptr ? sizeof(ShaderRecompiler::ShaderVertexStageInfo) : 0) + (variant.memory != nullptr ? variant.memory->Bytes() : 0);
}
void Driver::accountVariant(const DispatchVariant& variant, bool added) {
@@ -65,6 +65,15 @@ std::size_t Driver::drawCacheEntries() {
return entries;
}
std::uint64_t Driver::DrawCacheBytes() {
static const std::uint64_t bytes = [] {
const char* text = std::getenv("APS5_DRAW_CACHE_BYTES");
const auto parsed = text != nullptr ? std::strtoull(text, nullptr, 10) : 0ull;
return parsed != 0 ? static_cast<std::uint64_t>(parsed) : std::uint64_t{256} << 20u;
}();
return bytes;
}
void Driver::accountDrawVariant(const DispatchVariant& variant, bool added) {
if (added) {
++drawCacheVariants;
@@ -180,7 +189,8 @@ void Driver::insertDrawEntry(std::uint64_t key, std::vector<std::shared_ptr<Disp
drawOrder.splice(drawOrder.begin(), drawOrder, replacement->order);
found->second = std::move(replacement);
}
while (drawCache.size() > drawCacheEntries()) {
while (drawCache.size() > drawCacheEntries() || (drawCache.size() > 1 && drawCacheVariantBytes > DrawCacheBytes())) {
const bool overBytes = drawCache.size() <= drawCacheEntries();
const auto last = drawCache.find(drawOrder.back());
for (const auto& variants : last->second->stages) {
for (const auto& variant : variants) accountDrawVariant(*variant, false);
@@ -191,6 +201,10 @@ void Driver::insertDrawEntry(std::uint64_t key, std::vector<std::shared_ptr<Disp
drawCache.erase(last);
++drawCacheEvictions;
++counters.evictions;
if (overBytes) {
++drawCacheByteEvictions;
++counters.byteEvictions;
}
}
}
@@ -31,7 +31,7 @@ void Driver::reportDrawCache(DrawEntryCounters& counters, bool profile) {
char line[1536];
std::uint64_t misses = 0;
for (const auto value : counters.misses) misses += value;
std::snprintf(line, sizeof(line), "[draw-cache] 10 s: %llu lookups, %llu hits (%.1f%%), %llu stage misses, %llu without an entry, %llu data-only hits (stages: %llu reused, %llu compiled; %llu verified); no entry: new registers %llu, user words/pointers only %llu, evicted %llu, seen but never inserted %llu; stage misses: front differing %llu, fragment differing %llu, other differing %llu, layout %llu, gate %llu, stage count %llu; differing captures: same runs %llu (%.1f words changed each), other runs %llu; %zu entries, %llu evicted\n", count(counters.lookups), count(counters.hits), counters.lookups != 0 ? 100.0 * static_cast<double>(counters.hits) / static_cast<double>(counters.lookups) : 0.0, count(misses), count(counters.absent), count(counters.dataHits), count(counters.dataStagesReused), count(counters.dataStagesCompiled), count(counters.dataVerified), count(counters.absentNewRegisters), count(counters.absentUserWords), count(counters.absentEvicted), count(counters.absentNeverInserted), miss(DrawMiss::FrontDiffering), miss(DrawMiss::FragmentDiffering), miss(DrawMiss::OtherDiffering), miss(DrawMiss::Layout), miss(DrawMiss::Gate), miss(DrawMiss::Stages), count(counters.differingSameRuns), counters.differingSameRuns != 0 ? static_cast<double>(counters.differingWords) / static_cast<double>(counters.differingSameRuns) : 0.0, count(counters.differingRunsChanged), drawCache.size(), count(counters.evictions));
std::snprintf(line, sizeof(line), "[draw-cache] 10 s: %llu lookups, %llu hits (%.1f%%), %llu stage misses, %llu without an entry, %llu data-only hits (stages: %llu reused, %llu compiled; %llu verified); no entry: new registers %llu, user words/pointers only %llu, evicted %llu, seen but never inserted %llu; stage misses: front differing %llu, fragment differing %llu, other differing %llu, layout %llu, gate %llu, stage count %llu; differing captures: same runs %llu (%.1f words changed each), other runs %llu; %zu entries, %llu evicted (%llu over the byte budget)\n", count(counters.lookups), count(counters.hits), counters.lookups != 0 ? 100.0 * static_cast<double>(counters.hits) / static_cast<double>(counters.lookups) : 0.0, count(misses), count(counters.absent), count(counters.dataHits), count(counters.dataStagesReused), count(counters.dataStagesCompiled), count(counters.dataVerified), count(counters.absentNewRegisters), count(counters.absentUserWords), count(counters.absentEvicted), count(counters.absentNeverInserted), miss(DrawMiss::FrontDiffering), miss(DrawMiss::FragmentDiffering), miss(DrawMiss::OtherDiffering), miss(DrawMiss::Layout), miss(DrawMiss::Gate), miss(DrawMiss::Stages), count(counters.differingSameRuns), counters.differingSameRuns != 0 ? static_cast<double>(counters.differingWords) / static_cast<double>(counters.differingSameRuns) : 0.0, count(counters.differingRunsChanged), drawCache.size(), count(counters.evictions), count(counters.byteEvictions));
text += line;
std::snprintf(line, sizeof(line), "[draw-cache] register-key miss component changes (10 s): stable registers %llu, user data %llu, auxiliary registers %llu, device %llu, first shader set %llu, unchanged components %llu\n", count(counters.registerKeyMissChanges[0]), count(counters.registerKeyMissChanges[1]), count(counters.registerKeyMissChanges[2]), count(counters.registerKeyMissChanges[3]), count(counters.registerKeyMissChanges[4]), count(counters.registerKeyMissChanges[5]));
text += line;
@@ -46,6 +46,11 @@ DrawEntryCounters Driver::DrawCacheCounters() {
return drawEntryCounters;
}
Driver::DispatchCacheFootprint Driver::DrawCacheSize() {
std::lock_guard cacheLock(drawCacheMutex);
return {drawCache.size(), drawCacheVariantBytes};
}
std::string Driver::profileDrawCacheLines(const DrawEntryCounters& counters) {
std::string text;
const auto append = [&](const char* format, auto... values) {
@@ -62,7 +67,7 @@ std::string Driver::profileDrawCacheLines(const DrawEntryCounters& counters) {
std::snprintf(item, sizeof(item), "%s%llu", rank == 0 ? "" : " / ", count(counters.variantHitsByRank[rank]));
ranks += item;
}
append("[draw-cache] %llu lookups (10 s): %llu no entry, %llu validated in %.1f us each (key, lookup and every stage): %llu hits (every stage equal; stage hits by variant rank 1..k %s), misses by reason: front stage differing %llu, fragment differing %llu, other stage differing %llu, layout (push offset) %llu, gate %llu, stage count %llu; %llu stage validations (%llu equal, %.2f variants compared each); %llu inserts (%llu shape updates, %llu variants inserted, %llu evicted beyond k, %llu already present, %llu unstable), %zu entries (%llu variants, ~%.1f MiB), %llu evictions in total, %llu LRU moves; verify: %llu hits captured again, %llu stages differed\n", count(counters.lookups), count(counters.absent), count(validated), validated != 0 ? counters.validateUs / static_cast<double>(validated) : 0.0, count(counters.hits), ranks.c_str(), miss(DrawMiss::FrontDiffering), miss(DrawMiss::FragmentDiffering), miss(DrawMiss::OtherDiffering), miss(DrawMiss::Layout), miss(DrawMiss::Gate), miss(DrawMiss::Stages), count(counters.stageValidations), count(counters.stageEqual), counters.stageValidations != 0 ? static_cast<double>(counters.variantsCompared) / static_cast<double>(counters.stageValidations) : 0.0, count(counters.inserts), count(counters.shapeUpdates), count(counters.variantsInserted), count(counters.variantsEvicted), count(counters.present), count(counters.unstable), drawCache.size(), count(drawCacheVariants), static_cast<double>(drawCacheVariantBytes) / (1024.0 * 1024.0), count(drawCacheEvictions), count(counters.touches), count(counters.verifyHits), count(counters.verifyMismatches));
append("[draw-cache] %llu lookups (10 s): %llu no entry, %llu validated in %.1f us each (key, lookup and every stage): %llu hits (every stage equal; stage hits by variant rank 1..k %s), misses by reason: front stage differing %llu, fragment differing %llu, other stage differing %llu, layout (push offset) %llu, gate %llu, stage count %llu; %llu stage validations (%llu equal, %.2f variants compared each); %llu inserts (%llu shape updates, %llu variants inserted, %llu evicted beyond k, %llu already present, %llu unstable), %zu entries (%llu variants, ~%.1f MiB of a %.1f MiB budget), %llu evictions in total (%llu over the byte budget), %llu LRU moves; verify: %llu hits captured again, %llu stages differed\n", count(counters.lookups), count(counters.absent), count(validated), validated != 0 ? counters.validateUs / static_cast<double>(validated) : 0.0, count(counters.hits), ranks.c_str(), miss(DrawMiss::FrontDiffering), miss(DrawMiss::FragmentDiffering), miss(DrawMiss::OtherDiffering), miss(DrawMiss::Layout), miss(DrawMiss::Gate), miss(DrawMiss::Stages), count(counters.stageValidations), count(counters.stageEqual), counters.stageValidations != 0 ? static_cast<double>(counters.variantsCompared) / static_cast<double>(counters.stageValidations) : 0.0, count(counters.inserts), count(counters.shapeUpdates), count(counters.variantsInserted), count(counters.variantsEvicted), count(counters.present), count(counters.unstable), drawCache.size(), count(drawCacheVariants), static_cast<double>(drawCacheVariantBytes) / (1024.0 * 1024.0), static_cast<double>(DrawCacheBytes()) / (1024.0 * 1024.0), count(drawCacheEvictions), count(drawCacheByteEvictions), count(counters.touches), count(counters.verifyHits), count(counters.verifyMismatches));
append("[draw-cache] register key (10 s): %llu lookups, %llu hits, key build %.1f us each; decode skipped %llu, partial (state/pixel/programs from the entry) %llu; facade log != table %llu; verify: %llu decodes compared, %llu differed\n", count(counters.registerKeyLookups), count(counters.registerKeyHits), counters.registerKeyLookups != 0 ? counters.keyUs / static_cast<double>(counters.registerKeyLookups) : 0.0, count(counters.decodeSkipped), count(counters.decodePartial), count(counters.facadeMismatches), count(counters.verifyDecodes), count(counters.verifyDecodeMismatches));
append("[draw-cache] stage data hits (10 s): %llu stages (%llu words refreshed), verified %llu; stage inserts with data positions %llu (%.1f positions each)\n", count(counters.stageDataHits), count(counters.stageDataWords), count(counters.stageDataVerified), count(counters.stageDataInserts), counters.stageDataInserts != 0 ? static_cast<double>(counters.stageDataPositions) / static_cast<double>(counters.stageDataInserts) : 0.0);
return text;
@@ -179,6 +179,10 @@ ShaderMemory::~ShaderMemory() {
}
}
std::size_t ShaderMemory::Bytes() const {
return sizeof(ShaderMemory) + initial.capacity() * sizeof(initial[0]) + pages.capacity() * sizeof(pages[0]) + pages.size() * sizeof(Page) + supplied.capacity() * sizeof(Supplied) + spilled.capacity() * sizeof(spilled[0]);
}
void ShaderMemory::WordMask::SetRun(std::size_t first, std::size_t count) {
while (count != 0) {
const auto shift = first % 64u;
@@ -23,6 +23,7 @@
#include <algorithm>
#include <array>
#include <atomic>
#include <bit>
#include <chrono>
#include <cstddef>
#include <cstdint>
@@ -35,6 +36,7 @@
#include <string>
#include <thread>
#include <tuple>
#include <utility>
#include <vector>
namespace {
@@ -66,6 +68,14 @@ constexpr std::array<std::size_t, 3> WideSlots{SrtSlots - 1u, SrtSlots - 2u, Srt
constexpr std::array<std::uint32_t, 17> WideTableCode{0xf4080100u, 0xfa000000u, 0xf4080200u, 0xfa000010u, 0xf4080300u, 0xfa000020u, 0xf4080700u, 0xfa000030u, 0x7e200500u, 0x9310b010u, 0xf42c0502u, 0x20000000u, 0xf09c0f08u, 0x00450000u, 0xe0700000u, 0x80070000u, 0xbf810000u};
constexpr std::array<std::uint32_t, 17> TableCode{0xf4080100u, 0xfa000000u, 0xf4080200u, 0xfa000010u, 0xf4080300u, 0xfa000020u, 0xf4080700u, 0xfa000030u, 0x7e200500u, 0x8f108510u, 0xf42c0502u, 0x20000000u, 0xf09c0f08u, 0x00450000u, 0xe0700000u, 0x80070000u, 0xbf810000u};
constexpr std::size_t VertexOffset = 256;
constexpr std::size_t PixelOffset = 512;
constexpr std::size_t DrawSrtBytes = 64;
constexpr std::size_t DrawKeys = SrtSlots * SrtBytes / DrawSrtBytes;
constexpr std::size_t ColorBytes = 1024;
constexpr std::array<std::uint32_t, 13> TriangleVertexCode{0x7e000d05u, 0x080200f4u, 0x10020300u, 0x100202f4u, 0x060202f3u, 0x060400f3u, 0x10040500u, 0x060404f3u, 0x7e060280u, 0x7e0802f2u, 0xf80008cfu, 0x04030201u, 0xbf810000u};
constexpr std::array<std::uint32_t, 20> TablePixelCode{0xf4080100u, 0xfa000000u, 0xf4080200u, 0xfa000010u, 0xd7650002u, 0x000100c1u, 0x7e200502u, 0x8f108510u, 0xbf8cc07fu, 0xf42c0502u, 0x20000000u, 0x7e000280u, 0x7e020280u, 0xbf8cc07fu, 0xf09c0f08u, 0x00450000u, 0xbf8c3f70u, 0xf800180fu, 0x03020100u, 0xbf810000u};
class GuestBlock {
public:
GuestBlock() {
@@ -166,6 +176,95 @@ void SrtMovingPerDispatch(const GuestBlock& block) {
Require(most <= Driver::DispatchCacheBytes(), "image table cache bounds: dispatches reading a 2 MiB table through " + std::to_string(SrtSlots) + " SRTs kept " + std::to_string(most >> 20u) + " MiB of captured words in the dispatch cache, over its budget of " + std::to_string(Driver::DispatchCacheBytes() >> 20u) + " MiB");
}
void Draw(const GuestBlock& block, std::size_t key) {
const auto color = block.At(OutputOffset);
const auto vertex = block.At(VertexOffset);
const auto pixel = block.At(PixelOffset);
const auto srt = block.At(SrtOffset + key * DrawSrtBytes);
const std::vector<std::pair<std::uint32_t, std::uint32_t>> context{
{0x2d5, 0x2000}, {0x1b6, 0}, {0x207, 0}, {0x200, 0}, {0x203, 0x800}, {0x2dc, 0xaa00}, {0x2f8, 0}, {0x292, 2}, {0x293, 0},
{0x80, 0}, {0x8d, 0}, {0x83, 0xffff}, {0x8c, 0xa}, {0x2f9, 0x2d}, {0x313, 0x6000}, {0x30e, 0xffffffff}, {0x30f, 0xffffffff},
{0x206, 0x43f}, {0x204, 0x80000}, {0x205, 0x240}, {0x8e, 0xf}, {0x8f, 0xf}, {0x202, 0xcc0010},
{0x1c4, 0}, {0x1c5, 9}, {0x1c3, 4}, {0x31c, 0x28028}, {0x31b, 0}, {0x31d, 0}, {0x3b0, (63u << 14u) | 3u}, {0x3b8, 0x9000000}, {0x1e0, 0},
{0xc, 0}, {0xd, 0x40040}, {0x81, 0x80000000}, {0x82, 0x40040}, {0x90, 0x80000000}, {0x91, 0x40040}, {0x94, 0x80000000}, {0x95, 0x40040},
{0x318, static_cast<std::uint32_t>(color >> 8u)}, {0x390, static_cast<std::uint32_t>(color >> 40u)},
{0x10f, std::bit_cast<std::uint32_t>(32.0f)}, {0x110, std::bit_cast<std::uint32_t>(32.0f)}, {0x111, std::bit_cast<std::uint32_t>(-2.0f)}, {0x112, std::bit_cast<std::uint32_t>(2.0f)},
{0x113, std::bit_cast<std::uint32_t>(1.0f)}, {0x114, 0}, {0xb4, 0}, {0xb5, std::bit_cast<std::uint32_t>(1.0f)}, {0x2fe, 0}, {0x302, 0}, {0x306, 0}, {0x30a, 0},
};
const std::vector<std::pair<std::uint32_t, std::uint32_t>> shader{
{0xc8, static_cast<std::uint32_t>(vertex >> 8u)}, {0xc9, static_cast<std::uint32_t>(vertex >> 40u)}, {0x8b, 0}, {0x8c, 0},
{0x008, static_cast<std::uint32_t>(pixel >> 8u)}, {0x009, static_cast<std::uint32_t>(pixel >> 40u)}, {0x00b, 2u << 1u},
{0x00c, static_cast<std::uint32_t>(srt)}, {0x00d, static_cast<std::uint32_t>(srt >> 32u)},
};
std::vector<std::uint32_t> words;
const auto append = [&](const std::vector<std::uint32_t>& packet) { words.insert(words.end(), packet.begin(), packet.end()); };
append(Pm4(0x79, {0x242, 4}));
for (const auto& [offset, value] : context) append(Pm4(0x69, {offset, value}));
for (const auto& [offset, value] : shader) append(Pm4(0x76, {offset, value}));
append(Pm4(0x2d, {3, 2}));
Packet dcb{words.data(), static_cast<std::uint32_t>(words.size()), 0, {}};
Require(sceAgcDriverSubmitDcb(&dcb) == 0, "image table draw cache bounds: the draw submission failed");
AgcDriverWaitIdle_nid_postfix();
}
unsigned BrightestColor(const GuestBlock& block) {
AgcDriver::GuestMemory::FlushGpuWrites(block.At(OutputOffset), ColorBytes);
const auto* bytes = block.Bytes(OutputOffset);
return *std::max_element(bytes, bytes + ColorBytes);
}
void SrtMovingPerDraw(const GuestBlock& block) {
using AgcDriver::DriverDetail::Driver;
std::memcpy(block.Bytes(VertexOffset), TriangleVertexCode.data(), sizeof(TriangleVertexCode));
std::memcpy(block.Bytes(PixelOffset), TablePixelCode.data(), sizeof(TablePixelCode));
const auto fill = [&](std::uint32_t surface) {
for (std::uint32_t record = 0; record < Records; ++record) SetRecord(block, record, surface * Aliases);
};
fill(3u);
for (std::size_t key = 0; key < DrawKeys; ++key) {
auto* srt = reinterpret_cast<std::uint32_t*>(block.Bytes(SrtOffset + key * DrawSrtBytes));
std::fill(srt, srt + DrawSrtBytes / 4u, 0u);
const auto heap = V(block.At(HeapOffset + key * Stride), Stride, Records - static_cast<std::uint32_t>(key));
std::copy(heap.begin(), heap.end(), srt);
}
auto& driver = Driver::Get();
const auto budget = Driver::DrawCacheBytes();
const auto before = driver.DrawCacheCounters();
std::uint64_t most = 0;
std::size_t entries = 0;
for (std::size_t key = 0; key < DrawKeys; ++key) {
for (std::size_t sight = 0; sight < 2; ++sight) {
Draw(block, key);
const auto size = driver.DrawCacheSize();
Require(size.variantBytes <= budget, "image table draw cache bounds: after draw " + std::to_string(2 * key + sight) + " the draw cache kept " + std::to_string(size.entries) + " entries holding " + std::to_string(size.variantBytes >> 10u) + " KiB, over its budget of " + std::to_string(budget >> 10u) + " KiB");
most = std::max(most, size.variantBytes);
entries = std::max(entries, size.entries);
}
}
const auto filled = driver.DrawCacheCounters();
const auto inserts = filled.inserts - before.inserts;
const auto evictions = filled.evictions - before.evictions;
const auto byteEvictions = filled.byteEvictions - before.byteEvictions;
const auto counts = " (" + std::to_string(inserts) + " inserts, " + std::to_string(evictions) + " evictions, " + std::to_string(byteEvictions) + " over the byte budget, at most " + std::to_string(entries) + " entries holding " + std::to_string(most >> 10u) + " KiB)";
Require(BrightestColor(block) == 3u, "image table draw cache bounds: the draws through the table wrote " + std::to_string(BrightestColor(block)) + ", expected 3");
Require(entries >= 2 && inserts >= DrawKeys / 2, "image table draw cache bounds: the draws were not kept in the draw cache" + counts);
Require(inserts * (most / entries) > 2 * budget, "image table draw cache bounds: the draws did not insert more than twice the budget" + counts);
Require(byteEvictions >= 1 && byteEvictions == evictions, "image table draw cache bounds: the draw cache was not bounded by its byte budget" + counts);
fill(9u);
std::memset(block.Bytes(OutputOffset), 0, ColorBytes);
Draw(block, 0);
const auto rebuilt = driver.DrawCacheCounters();
Require(rebuilt.absent - filled.absent == 1 && rebuilt.inserts - filled.inserts == 1, "image table draw cache bounds: the first key, evicted, was not looked up without an entry and inserted again (" + std::to_string(rebuilt.absent - filled.absent) + " absent, " + std::to_string(rebuilt.inserts - filled.inserts) + " inserts)");
Require(BrightestColor(block) == 9u, "image table draw cache bounds: the evicted key drawn again wrote " + std::to_string(BrightestColor(block)) + ", expected 9");
std::memset(block.Bytes(OutputOffset), 0, ColorBytes);
Draw(block, 0);
const auto hit = driver.DrawCacheCounters();
Require(hit.hits - rebuilt.hits == 1, "image table draw cache bounds: the evicted key, inserted again, was not hit by its next draw");
Require(BrightestColor(block) == 9u, "image table draw cache bounds: the hit on the rebuilt key wrote " + std::to_string(BrightestColor(block)) + ", expected 9");
std::puts(("image table draw cache bounds passed" + counts).c_str());
}
std::array<std::uint32_t, 8> TextureWords(const GuestBlock& block, std::uint32_t element) {
const auto address = block.At(TexturesOffset + static_cast<std::size_t>(element / Aliases) * TextureBytes);
return {static_cast<std::uint32_t>(address >> 8u), static_cast<std::uint32_t>((address >> 40u) & 0xffu) | (Format8888UNorm << 20u) | (3u << 30u), 7u << 14u, 0xfacu | (Type2D << 28u), 0u, 0u, element % Aliases, 0u};
@@ -337,9 +436,11 @@ void SetBudgets() {
#ifdef _WIN32
_putenv_s("APS5_RESOURCE_CACHE_TABLE_ELEMENTS", "8192");
_putenv_s("APS5_DISPATCH_CACHE_BYTES", "16777216");
_putenv_s("APS5_DRAW_CACHE_BYTES", "16777216");
#else
setenv("APS5_RESOURCE_CACHE_TABLE_ELEMENTS", "8192", 1);
setenv("APS5_DISPATCH_CACHE_BYTES", "16777216", 1);
setenv("APS5_DRAW_CACHE_BYTES", "16777216", 1);
#endif
}
@@ -398,6 +499,7 @@ int main(int argc, char** argv) {
}
TablesChangingPerDispatch(block);
SrtMovingPerDispatch(block);
SrtMovingPerDraw(block);
AgcDriverShutdown_nid_postfix();
std::puts("image table cache bounds tests passed");
return 0;
+1
View File
@@ -116,6 +116,7 @@ The code reads environment names through `getenv`, `IsEnvironmentSet`, `kibSetti
| `APS5_DISPMOVE_MAX_KIB` | integer | `core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp` | 1024 | upper bound in KiB on the read-only buffers a dispatch-cache hit moves into its template, above which the dispatch is prepared again |
| `APS5_DRAIN_ALL` | presence | `core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketMemory.cpp` | | drains every packet, disabling the batched recording path |
| `APS5_DRAIN_COMPLETION_LABELS` | presence | `core/libs/prx/libSceAgcDriver/Execution/src/Driver/Synchronization/DeferredLabels.cpp` | | records completion labels immediately instead of deferring them with the batch, and drains a label recorded while a copied write-back is pending instead of storing it behind the write-backs |
| `APS5_DRAW_CACHE_BYTES` | integer | `core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawCache.cpp` | 268435456 | size of the draw cache in bytes, evicting least-recently-used entries past it (0 keeps the default of 256 MiB) |
| `APS5_DRAW_CACHE_ENTRIES` | integer | `core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawCache.cpp` | 4096 | number of entries in the draw cache; 0 keeps the default 4096 |
| `APS5_DRAW_DRAIN` | presence | `core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketMemory.cpp` | | treats draw packets as drains, waiting for them instead of recording ahead |
| `APS5_DRAW_INPUTS_IN_PLACE` | presence | `core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp` | | binds draw vertex and index ranges in place from host imports also with NVIDIA's Windows driver, which copies them by default |