Merge pull request #2939 from CnRJay/fix/agc-dcc-comp-to-single
fix(agc): expand comp-to-single DCC keys in a DCC decompress pass
This commit is contained in:
commit
b64c75fc12
|
|
@ -19,7 +19,7 @@ struct Context;
|
|||
// uncompressed here, so the keys that matter are the fast-clear codes a title writes into the metadata
|
||||
// (the surface then reads as a constant whatever its texels hold) and "uncompressed", which the driver
|
||||
// stores after it writes a surface so later reads see the texels.
|
||||
enum class DccKeys { Uncompressed, Clear0000, Clear0001, Clear1110, Clear1111, ClearRegister, Mixed, Unreadable };
|
||||
enum class DccKeys { Uncompressed, Clear0000, Clear0001, Clear1110, Clear1111, ClearRegister, ClearSingle, Mixed, Unreadable };
|
||||
|
||||
const char* DccKeysName(DccKeys keys);
|
||||
std::size_t DccKeyBytes(std::uint64_t surfaceBytes);
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ DccKeys ByteKeys(std::uint8_t key) {
|
|||
case 0x80: return DccKeys::Clear1110;
|
||||
case 0xc0: return DccKeys::Clear1111;
|
||||
case 0x20: return DccKeys::ClearRegister;
|
||||
case 0x10: return DccKeys::ClearSingle;
|
||||
case 0xff: return DccKeys::Uncompressed;
|
||||
default: return DccKeys::Mixed;
|
||||
}
|
||||
|
|
@ -384,6 +385,7 @@ const char* DccKeysName(DccKeys keys) {
|
|||
case DccKeys::Clear1110: return "1110";
|
||||
case DccKeys::Clear1111: return "1111";
|
||||
case DccKeys::ClearRegister: return "register";
|
||||
case DccKeys::ClearSingle: return "single";
|
||||
case DccKeys::Mixed: return "mixed";
|
||||
case DccKeys::Unreadable: return "unreadable";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,6 +132,25 @@ void storeClearTexels(const Context& context, const ColorTarget& color, const st
|
|||
MarkDccUncompressed(context, color.dccAddress, color.bytes, colorKeyCount(color, color.bytes));
|
||||
}
|
||||
|
||||
void storeSingleTexels(const Context& context, const ColorTarget& color) {
|
||||
Require(color.tileMode != ColorTileMode::Linear, "comp-to-single DCC keys of a linear color target");
|
||||
const ColorTargetLayout surface(color.extent.width, color.extent.height, color.tileMode, color.elementBytes, color.pipeBankXor);
|
||||
Require(surface.Bytes() == color.bytes, "comp-to-single DCC keys over a color target whose layout differs from its surface");
|
||||
StorageTexture::FlushPending(color.address, color.bytes, nullptr, "comp-to-single expansion");
|
||||
std::vector<std::byte> texels(color.bytes);
|
||||
GuestMemory::Read(color.address, texels, surface.Alignment());
|
||||
constexpr std::size_t BlockBytes = 256;
|
||||
for (std::uint32_t y = 0; y < color.extent.height; ++y) {
|
||||
for (std::uint32_t x = 0; x < color.extent.width; ++x) {
|
||||
const auto offset = surface.Offset(x, y);
|
||||
const auto first = offset / BlockBytes * BlockBytes;
|
||||
if (offset != first) std::memcpy(texels.data() + offset, texels.data() + first, color.elementBytes);
|
||||
}
|
||||
}
|
||||
GuestMemory::Write(color.address, texels, surface.Alignment());
|
||||
MarkDccUncompressed(context, color.dccAddress, color.bytes, colorKeyCount(color, color.bytes));
|
||||
}
|
||||
|
||||
void materializeRegisterClear(const Context& context, const ColorTarget& color, StorageTexture& resident) {
|
||||
if (color.dccAddress == 0 || resident.Descriptor().dccAddress != color.dccAddress) return;
|
||||
if (ProvedCurrentDccKeys(color.dccAddress, color.bytes, resident.TargetKeyProof()) != DccKeys::ClearRegister) return;
|
||||
|
|
@ -2459,6 +2478,12 @@ void RunColorMetadataPass(const Context& context, const ColorMetadataPass& pass)
|
|||
if (color.dccAddress == 0) continue;
|
||||
auto keys = CurrentDccKeys(color.dccAddress, color.bytes);
|
||||
if (keys == DccKeys::Uncompressed) continue;
|
||||
if (keys == DccKeys::ClearSingle) {
|
||||
Require(pass.mode == ColorMetadataPass::Mode::DccDecompress, "CB fast-clear eliminate over comp-to-single DCC keys (whether it expands them is not modeled)");
|
||||
storeSingleTexels(context, color);
|
||||
if (const auto resident = metadataPassResident(context, color)) resident->Refresh();
|
||||
continue;
|
||||
}
|
||||
Require(IsDccClear(keys), std::string("CB metadata pass over DCC keys that are ") + DccKeysName(keys) + " (per-block metadata is not modeled)");
|
||||
const auto texel = clearTexel(color, keys);
|
||||
const auto resident = metadataPassResident(context, color);
|
||||
|
|
|
|||
|
|
@ -1282,6 +1282,28 @@ void metadataPassTests() {
|
|||
for (std::size_t offset = 0; offset < tiledBytes; offset += 4) filled = filled && std::memcmp(tiledTexels + offset, &texel, 4) == 0;
|
||||
Require(filled && stored == expected && std::all_of(tiledKeys, tiledKeys + expected, [](std::uint8_t value) { return value == 0xff; }), std::string("a ") + code + " fast clear eliminate of a 128x128 SW_64KB_R_X target with " + alignment + " DCC stored uncompressed keys over " + std::to_string(stored) + " of its " + std::to_string(keyCount) + " DCC key bytes, expected " + std::to_string(expected));
|
||||
}
|
||||
std::vector<std::uint32_t> original(tiledBytes / 4);
|
||||
for (std::size_t index = 0; index < original.size(); ++index) original[index] = 0x01000000u + static_cast<std::uint32_t>(index);
|
||||
const auto resetSingle = [&] {
|
||||
std::memcpy(tiledTexels, original.data(), tiledBytes);
|
||||
std::memset(tiledKeys, 0x10, keyCount);
|
||||
};
|
||||
resetSingle();
|
||||
expectFailure([&] { AgcDriver::Graphics::RunColorMetadataPass(context, *tiledPass); }, "fast-clear eliminate over comp-to-single");
|
||||
Require(std::memcmp(tiledTexels, original.data(), tiledBytes) == 0 && std::all_of(tiledKeys, tiledKeys + keyCount, [](std::uint8_t value) { return value == 0x10; }), "a refused fast-clear eliminate over comp-to-single keys changed the " + alignment + " target");
|
||||
resetSingle();
|
||||
AgcDriver::Graphics::RunColorMetadataPass(context, {AgcDriver::Graphics::ColorMetadataPass::Mode::DccDecompress, tiledPass->targets});
|
||||
const AgcDriver::Graphics::ColorTargetLayout layout(tiledSide, tiledSide, AgcDriver::Graphics::ColorTileMode::RenderTarget, 4);
|
||||
bool expanded = true;
|
||||
for (std::uint32_t y = 0; y < tiledSide && expanded; ++y) {
|
||||
for (std::uint32_t x = 0; x < tiledSide && expanded; ++x) {
|
||||
std::uint32_t texel = 0;
|
||||
std::memcpy(&texel, tiledTexels + layout.Offset(x, y), 4);
|
||||
expanded = texel == original[layout.Offset(x & ~7u, y & ~7u) / 4];
|
||||
}
|
||||
}
|
||||
const auto stored = static_cast<std::size_t>(std::count(tiledKeys, tiledKeys + keyCount, std::uint8_t{0xff}));
|
||||
Require(expanded && stored == expected, "a DCC decompress of comp-to-single keys over a 128x128 SW_64KB_R_X target with " + alignment + " DCC did not give every 8x8 block its first texel (" + std::to_string(stored) + " of " + std::to_string(keyCount) + " keys uncompressed, expected " + std::to_string(expected) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -437,6 +437,7 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
- [Storage image write-back](../../core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp) (libSceAgcDriver) stores the GPU results over a CPU-stamped range whose guest bytes equal the copy taken when the image's layers got their generation (images up to 1 MiB whose guest bytes were settled at that point, with no unit shadow, pending image results or recorded GPU store over them; other images keep the previous rule: the CPU's bytes win). A CPU store that rewrites the baseline value after the GPU changed it is not told apart from no store, and the GPU result wins there. The 1 MiB limit is a choice, not a console value.
|
||||
- [DCC key stores](../../core/libs/prx/libSceAgcDriver/Graphics/src/DccMetadata.cpp) cover the console's DCC extent only for pipe-aligned DCC of single-level, single-layer 4-byte SW_64KB_R_X surfaces; other surfaces, and every texture and color target key read, cover one byte per 256 bytes.
|
||||
- [DCC key fills over other images](../../core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp) (libSceAgcDriver) - a uniform fill at a live image's `dccAddress`, at least one key per 256 bytes long, is a key fill of that image even when other live images overlap it, but only while it is no longer than the image's key extent (`DccKeyCount`, the pipe-aligned count); a longer one is classified by its overlaps as before. The bound is inferred, not measured on a title: a longer fill also writes the bytes past the keys, which the overlapping image may own. Whether the title's bloom-target key fills stay within the extent was not checked (`APS5_TRACE_DCC_KEYS` prints the length of the fills classified as keys, not of the refused ones).
|
||||
- [Comp-to-single DCC keys](../../core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp) (libSceAgcDriver) - key 0x10 (`GFX9_DCC_CLEAR_SINGLE` in Mesa `ac_descriptors.h`) is expanded only by a CB DCC decompress pass over uniform keys of a non-linear target: every texel takes the element at the start of its 256-byte block, the top-left texel of the block where radv's `build_clear_dcc_comp_to_single_shader` stores the clear color. A fast-clear eliminate over such keys and mixed keys throw; textures and color targets that read them use their stored texels. "Uniform" is judged over the key bytes the driver reads, one per 256 surface bytes (see DCC key stores above), so a pipe-aligned surface whose keys differ past that range is expanded as if all were comp-to-single, as for the other clear codes. Not verified on hardware
|
||||
- [8_SRGB color targets](../../core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp) (libSceAgcDriver) on a device that cannot attach and blend R8_SRGB render into an RGBA8_SRGB copy of the target, so the sRGB encode, decode and blend rounding are the device's; the console's are not measured.
|
||||
- [8_SRGB color targets](../../core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp) (libSceAgcDriver) on such a device throw unless they are single-mip 2D surfaces with a resident image; linear targets have none.
|
||||
- [COPY_DATA from the GPU clock](../../core/libs/prx/libSceAgcDriver/Execution/src/Pm4.cpp) (libSceAgcDriver, `SRC_SEL` 9) writes the host steady clock in 10 ns ticks when the packet runs, the same counter as the `RELEASE_MEM` timestamp, not the console's GPU clock. The `COPY_DATA` cache policy fields are accepted and have no effect.
|
||||
|
|
|
|||
Loading…
Reference in New Issue