fix(shader): read the ancillary render target index from Layer when the draw exports it

This commit is contained in:
TideOPL 2026-10-13 12:23:17 +01:00
parent 5d7f1ffc1c
commit 7015c0443e
16 changed files with 80 additions and 25 deletions

View File

@ -116,6 +116,7 @@ std::shared_ptr<DrawDecode> Driver::decodeDraw(const QueueState& queue, const Su
product->pixel.dualSourceBlend = product->state.dualSourceBlend;
product->pixel.alphaToCoverage = product->state.alphaToCoverage;
if (product->pixel.ancillary) product->pixel.rasterPrimitive = Graphics::RasterPrimitiveOf(product->state);
if (product->pixel.ancillary) product->pixel.layerExported = product->state.layerExports != 0;
return product;
}

View File

@ -1842,12 +1842,14 @@ void verifyPixelRequestSerialization() {
minimal.context.waveSize = 64;
minimal.context.pixel = ShaderPixelStageInfo{};
minimal.context.pixel->rasterPrimitive = RasterPrimitive::Rectangle;
minimal.context.pixel->layerExported = true;
const auto encoded = serializer.Serialize(minimal);
require(requestPrefix(encoded, 8u) == "NVNQQRYAAAA=", "new requests did not use serialization version 22");
require(serializer.Deserialize(encoded).request.context.pixel->rasterPrimitive == RasterPrimitive::Rectangle, "the pixel primitive class did not round-trip");
require(serializer.Deserialize(encoded).request.context.pixel->layerExported, "the pixel layer export did not round-trip");
constexpr std::size_t mappingOffset = 8u + 37u + 18u + 163u;
for (std::size_t bytes = 0; bytes < 21u; ++bytes) {
expectFailure([&] { static_cast<void>(serializer.Deserialize(requestPrefix(encoded, mappingOffset + bytes))); }, "truncated data", "a truncated pixel mapping, packing, dual-source, alpha-to-coverage, stencil reference export flag or primitive class was accepted");
for (std::size_t bytes = 0; bytes < 22u; ++bytes) {
expectFailure([&] { static_cast<void>(serializer.Deserialize(requestPrefix(encoded, mappingOffset + bytes))); }, "truncated data", "a truncated pixel mapping, packing, dual-source, alpha-to-coverage, stencil reference export flag, primitive class or layer export flag was accepted");
}
for (const auto unsupported : {"NVNQQQAAAAA=", "NVNQQRcAAAA="}) {
expectFailure([&] { static_cast<void>(serializer.Deserialize(unsupported)); }, "serialization version", "an unsupported request version was accepted");
@ -1960,11 +1962,20 @@ void verifyVertexInfoVersion19() {
pixel.context.pixel = ShaderPixelStageInfo{};
pixel.context.pixel->rasterPrimitive = RasterPrimitive::Rectangle;
const auto encodedPixel = serializer.Serialize(pixel);
auto exportedPixel = pixel;
exportedPixel.context.pixel->layerExported = true;
const auto exportedBytes = decodeBase64(serializer.Serialize(exportedPixel));
auto legacyPixel = decodeBase64(encodedPixel);
legacyPixel[4] = 19u;
const auto replayPixel = serializer.Deserialize(encodeBase64(legacyPixel));
require(replayPixel.request.context.pixel.has_value() && replayPixel.request.context.pixel->rasterPrimitive == RasterPrimitive::Rectangle, "a main version 19 request lost its pixel primitive class");
require(serializer.Serialize(replayPixel.request) == encodedPixel, "a main version 19 request misread the fields after its pixel primitive class");
std::size_t flagOffset = 0;
while (flagOffset < legacyPixel.size() && legacyPixel[flagOffset] == exportedBytes[flagOffset]) ++flagOffset;
require(flagOffset < legacyPixel.size(), "the pixel layer export flag is not serialized");
legacyPixel.erase(legacyPixel.begin() + static_cast<std::ptrdiff_t>(flagOffset));
for (const auto version : {19u, 20u, 21u}) {
legacyPixel[4] = static_cast<std::uint8_t>(version);
const auto replayPixel = serializer.Deserialize(encodeBase64(legacyPixel));
require(replayPixel.request.context.pixel.has_value() && replayPixel.request.context.pixel->rasterPrimitive == RasterPrimitive::Rectangle && !replayPixel.request.context.pixel->layerExported, "a main version 19 to 21 request lost its pixel primitive class or read a layer export flag");
require(serializer.Serialize(replayPixel.request) == encodedPixel, "a main version 19 to 21 request misread the fields after its pixel primitive class");
}
}
void verifyLegacyPixelRequests() {

View File

@ -66,6 +66,13 @@ alignas(256) constexpr std::array<std::uint32_t, 4> PixelCode{
0x7e0e02f2, 0xf800180f, 0x07070707, 0xbf810000,
};
alignas(256) constexpr std::array<std::uint32_t, 6> LayerFieldPixelCode{
0x2c020090, 0x7e020d01, 0x100202f0, 0xf800180f, 0x01010101, 0xbf810000,
};
constexpr std::array<std::uint8_t, Layers> WhiteBands{255, 255, 255};
constexpr std::array<std::uint8_t, Layers> LayerFieldBands{0, 128, 255};
struct Vertex {
std::array<float, 4> position;
std::array<float, 3> color;
@ -142,7 +149,7 @@ bool DeclaresLayer(const std::vector<std::uint32_t>& words) {
return false;
}
void Run(AgcDriver::VulkanDevice& device, const AgcDriver::Graphics::ColorTarget& color, bool mesh) {
void Run(AgcDriver::VulkanDevice& device, const AgcDriver::Graphics::ColorTarget& color, bool mesh, std::span<const std::uint32_t> pixelCode, bool layerField) {
const auto target = device.Target();
const auto vertexBuffer = BufferDescriptor(Vertices.data(), sizeof(Vertex), static_cast<std::uint32_t>(Vertices.size()));
const AgcDriver::Pm4::DrawParameters draw{0, static_cast<std::uint32_t>(Vertices.size()), 0, 1, 0, false};
@ -167,10 +174,15 @@ void Run(AgcDriver::VulkanDevice& device, const AgcDriver::Graphics::ColorTarget
ShaderRecompiler::ShaderPixelStageInfo pixel{};
pixel.targetOutputMode[0] = 9;
pixel.targetExportMapping.fill(0xe4u);
if (layerField) {
pixel.inputAddr = ShaderRecompiler::PixelInputBit(ShaderRecompiler::PixelInput::Ancillary);
pixel.ancillary = true;
pixel.layerExported = true;
}
const std::vector<std::uint32_t> pixelUserData(8, 0u);
const std::array<ShaderRecompiler::MemoryRegion, 1> pixelMemory{{{reinterpret_cast<std::uintptr_t>(PixelCode.data()), std::as_bytes(std::span(PixelCode))}}};
const std::array<ShaderRecompiler::MemoryRegion, 1> pixelMemory{{{reinterpret_cast<std::uintptr_t>(pixelCode.data()), std::as_bytes(pixelCode)}}};
ShaderRecompiler::RecompileRequest fragment{
{ShaderStage::Fragment, reinterpret_cast<std::uintptr_t>(PixelCode.data()), PixelCode, 0, {}},
{ShaderStage::Fragment, reinterpret_cast<std::uintptr_t>(pixelCode.data()), pixelCode, 0, {}},
{64, 0, pixelUserData, std::nullopt, pixel, std::nullopt, pixelMemory},
target,
PixelPushLayout(geometryPush, target)
@ -226,7 +238,7 @@ std::uint64_t BlockOffset(const AgcDriver::Graphics::TextureSwizzleEquation& equ
return offset;
}
void Check(const std::byte* volume, const AgcDriver::Graphics::ColorTarget& color, const std::string& what) {
void Check(const std::byte* volume, const AgcDriver::Graphics::ColorTarget& color, const std::string& what, const std::array<std::uint8_t, Layers>& bands) {
const auto geometry = AgcDriver::Graphics::DescribeSurface(AgcDriver::Graphics::SurfaceForTarget(color));
Require(!geometry.thick, "a 64 KiB render-target volume is not stored as thin slices");
const auto block = AgcDriver::Graphics::ThinBlockLayout(AgcDriver::Graphics::TextureTileMode::kR64KBX, 4);
@ -238,11 +250,11 @@ void Check(const std::byte* volume, const AgcDriver::Graphics::ColorTarget& colo
for (std::uint32_t y = 0; y < Height; ++y) {
for (std::uint32_t x = 0; x < Width; ++x) {
const bool drawn = slice >= FirstSlice && x / BandWidth == slice - FirstSlice;
const auto expected = drawn ? std::uint8_t{255} : Kept;
const auto expected = drawn ? bands[slice - FirstSlice] : Kept;
const auto* texel = base + BlockOffset(*equation, x, y, slice);
for (std::uint32_t channel = 0; channel < 4; ++channel) {
const auto actual = std::to_integer<std::uint8_t>(texel[channel]);
Require(actual == expected, what + ": slice " + std::to_string(slice) + " texel (" + std::to_string(x) + ", " + std::to_string(y) + ") channel " + std::to_string(channel) + " is " + std::to_string(actual) + ", expected " + std::to_string(expected) + (drawn ? " (the band of layer " + std::to_string(slice - FirstSlice) + ")" : slice < FirstSlice ? " (outside the color view)" : " (another layer's band)"));
Require(actual == expected || (drawn && expected == 128 && actual == 127), what + ": slice " + std::to_string(slice) + " texel (" + std::to_string(x) + ", " + std::to_string(y) + ") channel " + std::to_string(channel) + " is " + std::to_string(actual) + ", expected " + std::to_string(expected) + (drawn ? " (the band of layer " + std::to_string(slice - FirstSlice) + ")" : slice < FirstSlice ? " (outside the color view)" : " (another layer's band)"));
}
}
}
@ -265,10 +277,14 @@ int main(int argc, char** argv) {
BuildBands();
const GuestVolume volume((VolumeTarget(nullptr).bytes + 65535u) & ~std::size_t{65535u});
const auto color = VolumeTarget(volume.Data());
Run(*device, color, false);
Check(volume.Data(), color, "vertex draw");
Run(*device, color, false, PixelCode, false);
Check(volume.Data(), color, "vertex draw", WhiteBands);
const GuestVolume fieldVolume((VolumeTarget(nullptr).bytes + 65535u) & ~std::size_t{65535u});
const auto fieldColor = VolumeTarget(fieldVolume.Data());
Run(*device, fieldColor, false, LayerFieldPixelCode, true);
Check(fieldVolume.Data(), fieldColor, "a pixel shader reading the render target index of its packed ancillary word", LayerFieldBands);
if (vertexOnly) std::puts("layered volume vertex draw tests passed");
else if (target.mesh.has_value()) Run(*device, color, true);
else if (target.mesh.has_value()) Run(*device, color, true, PixelCode, false);
else std::puts("mesh case skipped, the device has no VK_EXT_mesh_shader");
std::puts("layered volume draw tests passed");
return 0;

View File

@ -20,13 +20,16 @@ struct Row {
std::uint32_t layer;
bool frontFacing;
RasterPrimitive primitive = RasterPrimitive::Triangle;
bool layerExported = false;
};
constexpr std::array<Row, 6> Rows {{{0u, 0u, false}, {0u, 0u, true}, {15u, 0x1FFFu, false}, {15u, 0x1FFFu, true}, {0u, 0x1FFFu, true}, {15u, 0u, false}}};
constexpr std::array<Row, 10> Rows {{{0u, 0u, false}, {0u, 0u, true}, {15u, 0x1FFFu, false}, {15u, 0x1FFFu, true}, {0u, 0x1FFFu, true}, {15u, 0u, false},
{0u, 0x1FFFu, false, RasterPrimitive::Triangle, true}, {15u, 0x1FFFu, true, RasterPrimitive::Triangle, true}, {15u, 3u, false, RasterPrimitive::Triangle, true}, {0u, 0u, true, RasterPrimitive::Triangle, true}}};
static std::uint32_t Evaluate(const IrValue* value, const Row& row);
static std::uint32_t BitField(std::uint32_t word, std::uint32_t offset, std::uint32_t count, bool isSigned);
static void RequireField(const IrValue& user, StageInputKind kind, std::uint32_t fieldOffset, std::uint32_t count) {
if (kind == StageInputKind::Layer) {
for (const Row& row : Rows) Require(Evaluate(&user, row) == 0u);
for (const Row& row : Rows) Require(Evaluate(&user, row) == (row.layerExported ? BitField(row.layer, fieldOffset, count, user.Opcode() == IrOpcode::BitFieldSExtract) : 0u));
return;
}
const IrValue* field = user.Argument(0)->Resolve();
@ -157,11 +160,13 @@ static std::uint32_t Evaluate(const IrValue* value, const Row& row) {
case StageInputKind::Layer: return row.layer;
case StageInputKind::FrontFacing: return row.frontFacing ? 1u : 0u;
case StageInputKind::RasterPrimitive: return static_cast<std::uint32_t>(row.primitive);
case StageInputKind::LayerExported: return row.layerExported ? 1u : 0u;
default: break;
}
break;
case IrOpcode::ShiftLeftLogical32: return Evaluate(value->Argument(0), row) << Evaluate(value->Argument(1), row);
case IrOpcode::BitwiseOr32: return Evaluate(value->Argument(0), row) | Evaluate(value->Argument(1), row);
case IrOpcode::IMul32: return Evaluate(value->Argument(0), row) * Evaluate(value->Argument(1), row);
case IrOpcode::BitFieldUExtract:
case IrOpcode::BitFieldSExtract:
return BitField(Evaluate(value->Argument(0), row), Evaluate(value->Argument(1), row), Evaluate(value->Argument(2), row), value->Opcode() == IrOpcode::BitFieldSExtract);
@ -172,7 +177,7 @@ static std::uint32_t Evaluate(const IrValue* value, const Row& row) {
throw std::runtime_error("packed pixel ancillary evaluation reached an unsupported value");
}
static std::uint32_t PackedWord(const Row& row) {
return UnmodelledBits | (row.sample << 8u);
return UnmodelledBits | (row.sample << 8u) | (row.layerExported ? row.layer << 16u : 0u);
}
static void Values(IrOpcode opcode, std::uint32_t offset, std::uint32_t count, bool throughSelect) {
IrProgram program;
@ -471,11 +476,12 @@ static void ExportedWholeWord() {
static_cast<void>(builder.Emit(IrOpcode::Return, IrType::Void, {}));
LowerMasked(program, true);
Require(!AncillaryRemains(program) && program.Info().rasterPrimitive);
Require(!BuiltinRemains(program, StageInputKind::Layer));
Require(BuiltinRemains(program, StageInputKind::LayerExported));
const IrValue* word = carried.Argument(0)->Resolve();
constexpr std::array<Row, 5> words {{{0u, 0u, false, RasterPrimitive::Triangle}, {0u, 0u, false, RasterPrimitive::Rectangle}, {0u, 0u, false, RasterPrimitive::Line}, {15u, 0x1FFFu, false, RasterPrimitive::Point}, {3u, 0x7FFu, false, RasterPrimitive::Triangle}}};
constexpr std::array<Row, 7> words {{{0u, 0u, false, RasterPrimitive::Triangle}, {0u, 0u, false, RasterPrimitive::Rectangle}, {0u, 0u, false, RasterPrimitive::Line}, {15u, 0x1FFFu, false, RasterPrimitive::Point}, {3u, 0x7FFu, false, RasterPrimitive::Triangle},
{15u, 0x7FFu, false, RasterPrimitive::Point, true}, {3u, 5u, false, RasterPrimitive::Triangle, true}}};
for (const Row& row : words) {
Require(Evaluate(word, row) == (static_cast<std::uint32_t>(row.primitive) | (row.sample << 8u)));
Require(Evaluate(word, row) == (static_cast<std::uint32_t>(row.primitive) | (row.sample << 8u) | (row.layerExported ? row.layer << 16u : 0u)));
}
}
static void WholeWordReplacesRefusal(HelperRead read) {

View File

@ -335,6 +335,7 @@ void writePixelInfo(Writer& writer, const ShaderPixelStageInfo& info) {
writer.WriteBool(info.alphaToCoverage);
writer.WriteBool(info.stencilRefExportEnable);
writer.WriteU8(static_cast<std::uint8_t>(info.rasterPrimitive));
writer.WriteBool(info.layerExported);
}
ShaderPixelStageInfo readPixelInfo(Reader& reader, std::uint32_t version) {
@ -385,6 +386,7 @@ ShaderPixelStageInfo readPixelInfo(Reader& reader, std::uint32_t version) {
if (version >= 16u) info.alphaToCoverage = reader.ReadBool();
if (version >= 17u) info.stencilRefExportEnable = reader.ReadBool();
if (version >= 19u) info.rasterPrimitive = readRasterPrimitive(reader);
if (version >= 22u) info.layerExported = reader.ReadBool();
if (version < 5u) {
const auto input = [](PixelInput value, bool present) { return present ? PixelInputBit(value) : 0u; };
info.inputAddr = input(PixelInput::PerspectiveSample, info.hasPerspectiveCenterVgpr && inputAddrOrCenterVgpr == 2u) | input(PixelInput::PerspectiveCenter, info.hasPerspectiveCenterVgpr) |

View File

@ -36,6 +36,7 @@ struct ShaderInfo {
bool usesFaultBuffer = false;
bool dispatchThreadLimit = false;
bool rasterPrimitive = false;
bool layerExported = false;
bool operator==(const ShaderInfo& other) const = default;
};

View File

@ -45,6 +45,7 @@ enum class StageInputKind {
Parameter,
HelperInvocation,
RasterPrimitive,
LayerExported,
};
enum class StageOutputKind {

View File

@ -384,7 +384,7 @@ void lowerPackedAncillary(IrProgram& program, IrBuilder& builder, IrValue& ancil
}
std::array<IrValue*, 2> fields {};
const auto field = [&](std::size_t index) -> IrValue& {
if (fields[index] == nullptr && index == 1u) fields[index] = &builder.Constant(0u);
if (fields[index] == nullptr && index == 1u) fields[index] = &insertBefore(program, ancillary, IrOpcode::IMul32, insertBuiltin(program, builder, ancillary, StageInputKind::Layer), insertBuiltin(program, builder, ancillary, StageInputKind::LayerExported));
if (fields[index] == nullptr) {
fields[index] = &insertBuiltin(program, builder, ancillary, StageInputKind::SampleId);
}
@ -859,7 +859,10 @@ bool ConstantFolder::ComposePackedAncillary(IrProgram& program) const {
for (IrValue* word : words) {
IrValue& primitive = insertBuiltin(program, builder, *word, StageInputKind::RasterPrimitive);
IrValue& sample = insertBefore(program, *word, IrOpcode::ShiftLeftLogical32, insertBuiltin(program, builder, *word, StageInputKind::SampleId), builder.Constant(8u));
word->ReplaceAllUsesWith(&insertBefore(program, *word, IrOpcode::BitwiseOr32, primitive, sample));
IrValue& exportedLayer = insertBefore(program, *word, IrOpcode::IMul32, insertBuiltin(program, builder, *word, StageInputKind::Layer), insertBuiltin(program, builder, *word, StageInputKind::LayerExported));
IrValue& layer = insertBefore(program, *word, IrOpcode::ShiftLeftLogical32, exportedLayer, builder.Constant(16u));
IrValue& fields = insertBefore(program, *word, IrOpcode::BitwiseOr32, sample, layer);
word->ReplaceAllUsesWith(&insertBefore(program, *word, IrOpcode::BitwiseOr32, primitive, fields));
}
return !words.empty();
}

View File

@ -144,6 +144,7 @@ void ValidateValueReferences(const IrProgram& program, ShaderStageInputInfo inpu
case StageInputKind::Layer:
case StageInputKind::SampleId:
case StageInputKind::RasterPrimitive:
case StageInputKind::LayerExported:
if (program.Resources().stage != IrShaderStage::Pixel || component != 0u) {
return Fail("typed pixel scalar input is invalid");
}
@ -390,6 +391,9 @@ void CollectBuiltinInputs(const IrProgram& program, ShaderInfo& info) {
case StageInputKind::RasterPrimitive:
info.rasterPrimitive = true;
break;
case StageInputKind::LayerExported:
info.layerExported = true;
break;
case StageInputKind::PackedAncillary:
case StageInputKind::Parameter:
break;

View File

@ -27,6 +27,7 @@ inline constexpr std::uint32_t ExportBase = 8192u;
inline constexpr std::uint32_t ExportPackingBase = 8224u;
inline constexpr std::uint32_t DualSourceBlend = 8232u;
inline constexpr std::uint32_t RasterPrimitive = 8233u;
inline constexpr std::uint32_t LayerExported = 8234u;
inline constexpr std::uint32_t HeapCountBase = 8256u;
inline constexpr std::uint32_t MipCountBase = 8320u;
inline constexpr std::uint32_t DescriptorIndexBase = 16384u;

View File

@ -777,6 +777,7 @@ RecompileResult materializeResult(const CompiledVariant& variant, const Recompil
for (const auto packing : request.context.pixel->targetExportPacking) moduleKey.push_back(static_cast<std::uint32_t>(packing));
moduleKey.push_back(request.context.pixel->dualSourceBlend ? 1u : 0u);
if (variant.info.info.rasterPrimitive) moduleKey.push_back(static_cast<std::uint32_t>(request.context.pixel->rasterPrimitive));
if (variant.info.info.layerExported) moduleKey.push_back(request.context.pixel->layerExported ? 1u : 0u);
}
result.vertexAttributes.reserve(result.vertexInputs.size());
std::array<std::uint32_t, ShaderVertexStageInfo::MaxResources> vertexClasses{};
@ -824,6 +825,7 @@ RecompileResult materializeResult(const CompiledVariant& variant, const Recompil
for (std::uint32_t target = 0; target < request.context.pixel->targetExportPacking.size(); ++target) constants.push_back({PipelineSpecialization::ExportPackingBase + target, moduleKey[index++]});
constants.push_back({PipelineSpecialization::DualSourceBlend, moduleKey[index++]});
if (variant.info.info.rasterPrimitive) constants.push_back({PipelineSpecialization::RasterPrimitive, moduleKey[index++]});
if (variant.info.info.layerExported) constants.push_back({PipelineSpecialization::LayerExported, moduleKey[index++]});
}
if (!artifact.vertexInputPatches.empty()) {
for (const auto& input : result.vertexInputs) {
@ -1025,6 +1027,7 @@ std::uint64_t snapshotHash(const RecompileRequest& request, const ResourceSnapsh
for (const auto packing : request.context.pixel->targetExportPacking) mix(static_cast<std::uint64_t>(packing));
mix(request.context.pixel->dualSourceBlend);
mix(static_cast<std::uint64_t>(request.context.pixel->rasterPrimitive));
mix(request.context.pixel->layerExported);
}
if (request.context.vertex) {
const auto& vertex = *request.context.vertex;

View File

@ -150,6 +150,7 @@ struct ShaderPixelStageInfo {
bool alphaToCoverage;
bool stencilRefExportEnable;
RasterPrimitive rasterPrimitive = RasterPrimitive::Triangle;
bool layerExported = false;
};
struct ShaderVertexBufferResource {

View File

@ -557,6 +557,7 @@ void encodeInfo(Writer& writer, const CompiledShaderInfo& compiled) {
writer.Value(info.usesFaultBuffer);
writer.Value(info.dispatchThreadLimit);
writer.Value(info.rasterPrimitive);
writer.Value(info.layerExported);
encodeLayout(writer, compiled.bindings);
}
@ -667,6 +668,7 @@ void decodeInfo(Reader& reader, CompiledShaderInfo& compiled) {
reader.Value(info.usesFaultBuffer);
reader.Value(info.dispatchThreadLimit);
reader.Value(info.rasterPrimitive);
reader.Value(info.layerExported);
decodeLayout(reader, compiled.bindings);
}

View File

@ -12,7 +12,7 @@
namespace ShaderRecompiler::ShaderDiskCache {
inline constexpr std::uint32_t FormatVersion = 28;
inline constexpr std::uint32_t FormatVersion = 29;
enum class LoadStatus {
Loaded,

View File

@ -48,6 +48,9 @@ std::uint32_t EmitBuiltinU32(SpirvEmitterState& state, StageInputKind kind, std:
if (kind == StageInputKind::RasterPrimitive) {
return state.module.SpecializationConstant(TypeU32(state), PipelineSpecialization::RasterPrimitive, static_cast<std::uint32_t>(RasterPrimitive::Triangle));
}
if (kind == StageInputKind::LayerExported) {
return state.module.SpecializationConstant(TypeU32(state), PipelineSpecialization::LayerExported, 0u);
}
if (state.laneCount == 2 && (kind == StageInputKind::LocalInvocationId || kind == StageInputKind::GlobalInvocationId)) {
const auto* workgroup = ShaderWorkgroupInputFor(state);
if (workgroup == nullptr) {

View File

@ -6,7 +6,7 @@
### Unknown function info
- [Packed pixel ancillary word](../../../core/shader/recompiler/Optimization/src/ConstantFolder.cpp) (shader recompiler) - the sample index at bits 8-11 and the render target index at bits 16-28 follow #295. The sample index is the SampleId builtin, an equality inferred, not measured. The render target index is 0: vertex layer exports are ignored, so no pre-rasterization stage writes Layer and the first layer is used. Reading the unwritten Layer builtin instead makes AMD's Windows driver drop the fragment's writes (#4800). Mesa reads both fields from those bits, with the layer 14 bits wide only from GFX12 on ([ac_nir_lower_intrinsics_to_args.c](https://gitlab.freedesktop.org/mesa/mesa/-/blob/bffd91947efc1932da99aca69fc540d6d4201b0c/src/amd/common/nir/ac_nir_lower_intrinsics_to_args.c#L342-L368)). A value passed through a select, move or phi whose every read is an extract inside one of those fields is rebuilt from the two fields alone. Any other read the masked-select pass cannot drop gets the whole word: the primitive class at bits 0-1 (0 point, 1 line, 2 triangle, 3 rectangle), which the draw sets as a pipeline specialization constant ([RasterPrimitiveOf](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp)) in the shaders that read it, plus the sample field, with every other bit zero. That layout comes from the RDNA 3 and 3.5 ISA guides (prim type 1:0, VRS rates 3:2 and 5:4, eye id 7, sample 11:8, render target index 28:16). The GCN register guides give the same prim type and sample fields and put the render target index at 26:16, so the two sources agree for layers below 2048. AMD's LLPC reads the primitive class from bits 1:0 with the same encoding ([LowerGlCompatibility.cpp](https://github.com/GPUOpen-Drivers/llpc/blob/40cb8d95ad8d6f7f1652e3fd47d39667594cce08/llpc/lowering/LowerGlCompatibility.cpp#L830-L836)) and without a GFX-level check ([getPrimType](https://github.com/GPUOpen-Drivers/llpc/blob/40cb8d95ad8d6f7f1652e3fd47d39667594cce08/lgc/lowering/LowerInOut.cpp#L5307-L5315)). Nobody has checked it on gfx1013. In the whole word only bits 0-1 and 8-11 have a source. Bits 2-7, 12-15 and 29-31 are assumed zero, not measured: the VRS rates (2-5) and the eye id (7) because the driver supports neither variable-rate shading nor multiview, so a title that enables either would read other bits on the console, and bits 6, 12-15 and 29-31 because no source gives them a meaning. The sample field reads SampleId, which is always 0 here because the driver rejects MSAA and per-sample shading (`PA_SC_AA_CONFIG`, `PS_ITER_SAMPLE`). The whole word has the same 0 at bits 16-28. A change that makes draws export a layer must restore the Layer read in both places
- [Packed pixel ancillary word](../../../core/shader/recompiler/Optimization/src/ConstantFolder.cpp) (shader recompiler) - the sample index at bits 8-11 and the render target index at bits 16-28 follow #295. The sample index is the SampleId builtin, an equality inferred, not measured. The render target index is the Layer builtin when the draw exports it (a layered draw, `PA_CL_VS_OUT_CNTL` bit 18; the pipeline specialization constant `LayerExported`) and 0 otherwise, the first layer Vulkan renders when no pre-rasterization stage writes Layer. Reading the unwritten Layer builtin instead makes AMD's Windows driver drop the fragment's writes (#4800); with the constant at 0 the load is folded away. Mesa reads both fields from those bits, with the layer 14 bits wide only from GFX12 on ([ac_nir_lower_intrinsics_to_args.c](https://gitlab.freedesktop.org/mesa/mesa/-/blob/bffd91947efc1932da99aca69fc540d6d4201b0c/src/amd/common/nir/ac_nir_lower_intrinsics_to_args.c#L342-L368)). A value passed through a select, move or phi whose every read is an extract inside one of those fields is rebuilt from the two fields alone. Any other read the masked-select pass cannot drop gets the whole word: the primitive class at bits 0-1 (0 point, 1 line, 2 triangle, 3 rectangle), which the draw sets as a pipeline specialization constant ([RasterPrimitiveOf](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp)) in the shaders that read it, plus the sample field, with every other bit zero. That layout comes from the RDNA 3 and 3.5 ISA guides (prim type 1:0, VRS rates 3:2 and 5:4, eye id 7, sample 11:8, render target index 28:16). The GCN register guides give the same prim type and sample fields and put the render target index at 26:16, so the two sources agree for layers below 2048. AMD's LLPC reads the primitive class from bits 1:0 with the same encoding ([LowerGlCompatibility.cpp](https://github.com/GPUOpen-Drivers/llpc/blob/40cb8d95ad8d6f7f1652e3fd47d39667594cce08/llpc/lowering/LowerGlCompatibility.cpp#L830-L836)) and without a GFX-level check ([getPrimType](https://github.com/GPUOpen-Drivers/llpc/blob/40cb8d95ad8d6f7f1652e3fd47d39667594cce08/lgc/lowering/LowerInOut.cpp#L5307-L5315)). Nobody has checked it on gfx1013. In the whole word only bits 0-1 and 8-11 have a source. Bits 2-7, 12-15 and 29-31 are assumed zero, not measured: the VRS rates (2-5) and the eye id (7) because the driver supports neither variable-rate shading nor multiview, so a title that enables either would read other bits on the console, and bits 6, 12-15 and 29-31 because no source gives them a meaning. The sample field reads SampleId, which is always 0 here because the driver rejects MSAA and per-sample shading (`PA_SC_AA_CONFIG`, `PS_ITER_SAMPLE`). The whole word has the same 0 at bits 16-28. A change that makes draws export a layer must restore the Layer read in both places
- [Color comparison descriptor forms](../../../core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp): emulated color comparison supports UNORM, SNORM and float formats for 2D, 2D-array and cube views. 8-bit sRGB and UNORM (`Format8Srgb`, `Format8_8Srgb`, `Format8_8_8_8Srgb`, `Format8UNorm`, `Format8_8UNorm`, `Format8_8_8_8UNorm`) return 0 for every compare function, measured with `image_sample_c_lz` on gfx1035 over references -0.5..1.5 and texel codes 0..255. Anisotropic filters are refused, except on those 8-bit formats, where every texel comparison is 0 (not measured with an anisotropic S#). Bilinear cube comparisons of float, snorm and the other unorm formats without `DISABLE_CUBE_WRAP` are refused; on that hardware a 4x4 R32 float cube blends across faces (46 of 90 edge rows differ from a per-face clamp; face 0, ref 0.1, u=3.9 v=2.5 is 0.6016 and u=1.5 v=0.1 is 0.3984), and with `DISABLE_CUBE_WRAP` the per-face clamp matches. An 8-bit sRGB or UNORM bilinear cube without that bit returns 0, since every texel comparison is already 0 for every compare function and the selected face cannot change the result. Cube-edge sampling behavior and console coverage on PS5's gfx1013 remain unverified.
- [s_setkill](../../../core/shader/recompiler/RdnaDecoder/src/RdnaScalarOpDecoder.cpp) (core/shader/recompiler) - decoded as `s_endpgm` for any immediate: measured on RDNA2 hardware (gfx1035), a store before it is visible and nothing after it runs ([#771](https://github.com/boykopovar/AnyPS5/pull/771)); whether the KILL status bit it sets is observable differently from `s_endpgm` is unverified
- [s_sethalt](../../../core/shader/recompiler/Translation/src/ScalarInstructions.cpp) (core/shader/recompiler) - with SIMM16[0] clear it is a no-op: the RDNA 2 ISA guide describes that value as resuming a halted wave, and a wave that executes it is not halted; measured on a gfx1035 (RDNA 2) wave: `s_sethalt 0` followed by a VALU op completes and gives the normal result. Only SIMM16[0] is read; with it set the instruction throws