fix(agc): accept only 1 for APS5_STORE_AT_FLIP and test the switch instead of FlushAllPending

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Sasha Papo 2026-10-09 08:33:21 -04:00 committed by Adrià Franch
parent ef53b0614f
commit 3221ae8acd
6 changed files with 17 additions and 4 deletions

View File

@ -2079,7 +2079,7 @@ FrameDumps& Dumps() {
}
bool VulkanDevice::PresentDisplayBuffer(const DisplayBuffer& buffer) {
static const bool storeAtFlip = std::getenv("APS5_STORE_AT_FLIP") != nullptr;
static const bool storeAtFlip = Graphics::StorageTexture::StoreAtFlipRequested(std::getenv("APS5_STORE_AT_FLIP"));
if (storeAtFlip) Graphics::StorageTexture::FlushAllPending("flip");
if (buffer.tilingMode == 1) {
const auto pixels = ReadDisplayBuffer(buffer);

View File

@ -152,6 +152,7 @@ public:
// overlapping pending image, as before).
static bool FlushPending(std::uint64_t address, std::size_t bytes, const StorageTexture* except = nullptr, const char* reason = "memory access", PublishScope scope = PublishScope::Whole, bool* published = nullptr);
static void FlushAllPending(const char* reason);
static bool StoreAtFlipRequested(const char* value);
// See PendingSerial: a change of a surface's source outside the registry (a unit shadow
// retile) moves it too.
static void BumpPendingSerial();

View File

@ -2520,6 +2520,12 @@ void StorageTexture::FlushAllPending(const char* reason) {
static_cast<void>(FlushPending(0, std::numeric_limits<std::size_t>::max(), nullptr, reason));
}
bool StorageTexture::StoreAtFlipRequested(const char* value) {
if (value == nullptr) return false;
if (std::strcmp(value, "1") == 0) return true;
throw std::runtime_error(std::string("APS5_STORE_AT_FLIP=") + value + ": expected 1");
}
std::shared_ptr<StorageTexture> StorageTexture::FindPending(std::uint64_t address, std::uint64_t bytes) {
auto& pending = Pending();
std::lock_guard lock(pending.mutex);

View File

@ -3461,6 +3461,13 @@ void highestDrawIndexTests() {
expectFailure([&] { HighestDrawIndex(bytesOf(narrow), 1, false); }, "unsupported index size");
}
void storeAtFlipTests() {
using AgcDriver::Graphics::StorageTexture;
Require(!StorageTexture::StoreAtFlipRequested(nullptr), "an unset APS5_STORE_AT_FLIP stored at each flip");
Require(StorageTexture::StoreAtFlipRequested("1"), "APS5_STORE_AT_FLIP=1 did not store at each flip");
for (const char* value : {"0", "", "true", "11"}) expectFailure([&] { StorageTexture::StoreAtFlipRequested(value); }, "expected 1");
}
void vertexCopyTests() {
using AgcDriver::Graphics::PlanVertexCopies;
using AgcDriver::Graphics::VertexFetch;
@ -3586,6 +3593,7 @@ int main() {
validationTests();
vertexCopyTests();
highestDrawIndexTests();
storeAtFlipTests();
pixelParameterSlotTests();
rectListTests();
floatControlsModeTests();

View File

@ -3002,8 +3002,6 @@ void metadataPassTests(const Device& device, Recorder& recorder) {
std::memset(keys, 0x20, extent);
RunColorMetadataPass(context, pass);
Require(StorageTexture::FindPending(address, surfaceBytes) != nullptr && texels[0] == 0x55, "the register fast clear eliminate did not stay in the resident image");
StorageTexture::FlushAllPending("test");
Require(StorageTexture::FindPending(address, surfaceBytes) == nullptr, "storing every pending image left the eliminated target pending");
Require(keysUncompressed(), "the register fast clear eliminate left the keys compressed");
Require(CurrentDccKeys(color.dccAddress, surfaceBytes, extent) == DccKeys::Uncompressed, "the register fast clear eliminate did not store uncompressed keys over the target's console DCC extent");
Require(memoryHolds({0x10, 0x20, 0x40, 0x80}), "the register fast clear eliminate did not store CB_COLOR_CLEAR_WORD");

View File

@ -110,7 +110,7 @@ The game runs on the first Vulkan 1.1 device with graphics and compute queues an
### Storing GPU results at each flip
GPU results in storage images and render targets stay on the GPU until something reads their memory. A title that frees such an image after waiting for its GPU work and reuses the memory can have its new data overwritten when the results are stored later ([TechnicalDebt](../dev/TechnicalDebt.md)). `APS5_STORE_AT_FLIP=1` stores every pending result when a display buffer is flipped, at the cost of one write-back per pending image and frame.
GPU results in storage images and render targets stay on the GPU until something reads their memory. A title that frees such an image after waiting for its GPU work and reuses the memory can have its new data overwritten when the results are stored later ([TechnicalDebt](../dev/TechnicalDebt.md)). `APS5_STORE_AT_FLIP=1` stores every pending result when a display buffer is flipped, at the cost of one write-back per pending image and frame. Any other value stops the title at its first flip.
## Exit codes