fix(agc): merge kill shaders under late tests when the draw writes no depth or stencil

A packed 4_4_4_4 partial write merges inside the pixel shader after early
depth and stencil tests. Moving the tests ahead of a pixel kill only changes
the result when the draw writes depth or stencil, so the target merge now
carries whether the draw does (depth write, or stencil with a write mask and
a non-keep operation), and the late-test kill refusal applies only then.
The flag is part of the static shader key, the serialized request (now
version 40) and the pixel input info.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
This commit is contained in:
JAB-devandClaude Sonnet 5.5 committed 2026-10-20 19:09:48 +02:00
1 parent b8a21d469c
commit 27c0004991
13 files changed
+107 -16

No files matched your search

@@ -78,6 +78,7 @@ void Driver::lookupDraw(const Submission& submission, const std::shared_ptr<Vulk
mix(pixel.targetMerge.mask);
mix(pixel.targetMerge.samples);
for (const auto word : pixel.targetMerge.descriptor) mix(word);
mix(pixel.targetMerge.depthStencilWrites);
mix(pixel.alphaToCoverage);
mix(static_cast<std::uint64_t>(pixel.rasterPrimitive));
mix(pixel.ditheredAlphaToCoverage);
@@ -171,6 +171,7 @@ VkExtent2D SampleLocationGrid(const State& state);
std::array<std::uint8_t, 8> ExportMappings(const State& state);
std::array<ShaderRecompiler::ColorExportPacking, 8> ExportPackings(const State& state);
std::uint16_t PackedWriteMask(std::uint8_t componentMapping, std::uint32_t exportedMask);
bool DepthStencilWrites(const State& state);
ShaderRecompiler::TargetMerge ExportTargetMerge(const State& state);
ShaderRecompiler::RasterPrimitive RasterPrimitiveOf(const State& state);
bool PartialSampleMask(const State& state);
@@ -1529,6 +1529,12 @@ std::uint16_t PackedWriteMask(std::uint8_t componentMapping, std::uint32_t expor
return mask;
}
bool DepthStencilWrites(const State& state) {
if (!state.depth) return false;
const auto writes = [](const VkStencilOpState& face) { return (face.writeMask & 0xffu) != 0 && (face.failOp != VK_STENCIL_OP_KEEP || face.passOp != VK_STENCIL_OP_KEEP || face.depthFailOp != VK_STENCIL_OP_KEEP); };
return state.depthWrite || (state.stencilTest && (writes(state.stencilFront) || writes(state.stencilBack)));
}
ShaderRecompiler::TargetMerge ExportTargetMerge(const State& state) {
ShaderRecompiler::TargetMerge merge;
for (const auto& color : state.colors) {
@@ -1538,6 +1544,7 @@ ShaderRecompiler::TargetMerge ExportTargetMerge(const State& state) {
merge.mask = color.partialWriteMask;
merge.samples = static_cast<std::uint8_t>(color.samples);
merge.descriptor = EncodeTextureResource(SurfaceForTarget(color));
merge.depthStencilWrites = DepthStencilWrites(state);
}
return merge;
}
@@ -8813,7 +8813,38 @@ void unorm4_4_4_4TargetTests() {
const auto merge = AgcDriver::Graphics::ExportTargetMerge(partial);
const auto expected = AgcDriver::Graphics::SurfaceForTarget(partial.color);
const auto surface = AgcDriver::Graphics::DecodeTextureResource(merge.descriptor);
Require(merge.target == 0u && merge.mask == wordMask && merge.samples == 1u && surface.baseAddress == expected.baseAddress && surface.pipeBankXor == expected.pipeBankXor && surface.width == expected.width && surface.height == expected.height && surface.format == expected.format && surface.tileMode == expected.tileMode && surface.dimension == AgcDriver::Graphics::TextureDimension::k2D && surface.mipCount == 1u && surface.samples == 1u, "the merge descriptor of CB_TARGET_MASK " + std::to_string(targetMask) + " does not decode to the target's surface");
Require(merge.target == 0u && merge.mask == wordMask && merge.samples == 1u && !merge.depthStencilWrites && surface.baseAddress == expected.baseAddress && surface.pipeBankXor == expected.pipeBankXor && surface.width == expected.width && surface.height == expected.height && surface.format == expected.format && surface.tileMode == expected.tileMode && surface.dimension == AgcDriver::Graphics::TextureDimension::k2D && surface.mipCount == 1u && surface.samples == 1u, "the merge descriptor of CB_TARGET_MASK " + std::to_string(targetMask) + " does not decode to the target's surface");
}
{
auto depthQueue = queue;
depthQueue.context[0x8e] = 1;
depthQueue.context[0x000] = 0;
depthQueue.context[0x002] = 0;
depthQueue.context[0x007] = (1u << 16u) | 3u;
depthQueue.context[0x00a] = 7;
depthQueue.context[0x00b] = 0;
depthQueue.context[0x010] = 0x22900983;
depthQueue.context[0x011] = 0x20000181;
for (const auto offset : {0x012u, 0x014u}) depthQueue.context[offset] = 0x100;
for (const auto offset : {0x013u, 0x015u}) depthQueue.context[offset] = 0x200;
depthQueue.context[0x005] = 0x300;
depthQueue.context[0x01e] = 0x1;
struct DepthStencilCase {
std::uint32_t control;
std::uint32_t ops;
std::uint32_t masks;
bool writes;
};
for (const auto& [control, ops, masks, writes] : {DepthStencilCase{0x00700710u, 0x00050050u, 0x01ffff01u, false}, DepthStencilCase{0x00700712u, 0x00050050u, 0x01ffff01u, false}, DepthStencilCase{0x00700714u, 0x00050050u, 0x01ffff01u, false}, DepthStencilCase{0x00700716u, 0x00050050u, 0x01ffff01u, true}, DepthStencilCase{0x00700711u, 0x00050050u, 0x01ffff01u, true}, DepthStencilCase{0x00700711u, 0u, 0x01ffff01u, false}, DepthStencilCase{0x00700711u, 0x00050050u, 0x0100ff01u, false}, DepthStencilCase{0x00700713u, 0u, 0x01ffff01u, false}}) {
depthQueue.context[0x200] = control;
depthQueue.context[0x10b] = ops;
depthQueue.context[0x10c] = depthQueue.context[0x10d] = masks;
const auto what = "DB_DEPTH_CONTROL " + std::to_string(control) + " with stencil operations " + std::to_string(ops) + " and masks " + std::to_string(masks);
Require(AgcDriver::Graphics::DrawRejection(depthQueue, false).empty(), "the draw precheck refused a partial 4_4_4_4 write under " + what);
const auto partial = AgcDriver::Graphics::DecodeState(depthQueue);
const auto merge = AgcDriver::Graphics::ExportTargetMerge(partial);
Require(partial.depth.has_value() == ((control & 3u) != 0u) && merge.mask == 0xf000u && AgcDriver::Graphics::DepthStencilWrites(partial) == writes && merge.depthStencilWrites == writes, "a partial 4_4_4_4 write under " + what + (writes ? " did not report" : " reported") + " depth or stencil writes");
}
}
{
changed.context[0x8e] = 1;
@@ -8871,7 +8902,21 @@ void unorm4_4_4_4TargetTests() {
auto killing = merging;
killing.context.pixel->pixelKillEnable = true;
killing.context.pixel->earlyZ = false;
expectFailure([&] { static_cast<void>(ShaderRecompiler::Recompile(killing)); }, "kills pixels under late tests");
for (const bool registration : {false, true}) {
killing.target.registrationModules = registration;
const auto words = ShaderRecompiler::Recompile(killing).spirv.Copy();
std::size_t reads = 0, writes = 0;
for (std::size_t at = 5; at < words.size() && (words[at] >> 16u) != 0; at += words[at] >> 16u) {
reads += static_cast<spv::Op>(words[at] & 0xffffu) == spv::OpImageRead ? 1u : 0u;
writes += static_cast<spv::Op>(words[at] & 0xffffu) == spv::OpImageWrite ? 1u : 0u;
}
Require(reads == 1u && writes == 1u && interlockShape(words) == "capability extension ordered begin end " && declaresEarlyFragmentTests(killing), "a killing 4_4_4_4 merge module under late tests of a draw without depth or stencil writes did not merge inside a pixel interlock after early tests");
}
killing.context.pixel->targetMerge.depthStencilWrites = true;
expectFailure([&] { static_cast<void>(ShaderRecompiler::Recompile(killing)); }, "kills pixels under late tests of a draw writing depth or stencil");
auto earlyKilling = killing;
earlyKilling.context.pixel->earlyZ = true;
static_cast<void>(ShaderRecompiler::Recompile(earlyKilling));
auto exporting = merging;
exporting.context.pixel->depthExportEnable = true;
expectFailure([&] { static_cast<void>(ShaderRecompiler::Recompile(exporting)); }, "exports depth");
@@ -4831,6 +4831,10 @@ void verifyPixelRequestSerialization() {
movedMerge.context.pixel->targetMerge.descriptor[0] = 0x200u;
RecompileCacheKey::Build(movedMerge, replayKey);
require(key == replayKey, "the merged target's descriptor changed the static shader identity");
auto depthWritingMerge = request;
depthWritingMerge.context.pixel->targetMerge.depthStencilWrites = true;
RecompileCacheKey::Build(depthWritingMerge, replayKey);
require(key != replayKey, "the merge draw's depth or stencil writes did not change the static shader identity");
auto withoutMerge = request;
withoutMerge.context.pixel->targetMerge = {};
RecompileCacheKey::Build(withoutMerge, replayKey);
@@ -5114,12 +5118,12 @@ void verifyVertexInfoVersion19() {
require(allBitsOffset == flagOffset + 2u, "the front-face all-bits flag does not follow the dithered coverage flag");
require(blendOffset == allBitsOffset + 18u, "the reversed-order blend code is not serialized after the front-face all-bits flag, quad pixel mask and quad sample masks");
constexpr std::size_t iterationBytes = 35u;
constexpr std::size_t mergeBytes = 38u;
constexpr std::size_t mergeBytes = 39u;
const auto mergeOffset = static_cast<std::ptrdiff_t>(blendOffset + 4u + iterationBytes);
auto merging = pixel;
merging.context.pixel->targetMerge = {3u, 0xf00fu, 1u, {1u, 2u, 3u, 4u, 5u, 6u, 7u, 0x89abcdefu}};
merging.context.pixel->targetMerge = {3u, 0xf00fu, 1u, {1u, 2u, 3u, 4u, 5u, 6u, 7u, 0x89abcdefu}, true};
const auto mergingBytes = decodeBase64(serializer.Serialize(merging));
require(mergingBytes.size() == plainPixel.size() && std::equal(plainPixel.begin(), plainPixel.begin() + mergeOffset, mergingBytes.begin()) && mergingBytes[mergeOffset] == 3u && mergingBytes[mergeOffset + 1] == 0x0fu && mergingBytes[mergeOffset + 2] == 0xf0u && mergingBytes[mergeOffset + 5] == 1u && mergingBytes[mergeOffset + 6] == 1u && mergingBytes[mergeOffset + 34] == 0xefu && mergingBytes[mergeOffset + 37] == 0x89u && std::equal(plainPixel.begin() + mergeOffset + static_cast<std::ptrdiff_t>(mergeBytes), plainPixel.end(), mergingBytes.begin() + mergeOffset + static_cast<std::ptrdiff_t>(mergeBytes)), "the color target merge is not the 38 bytes the version 40 pixel info adds after the pixel iteration fields");
require(mergingBytes.size() == plainPixel.size() && std::equal(plainPixel.begin(), plainPixel.begin() + mergeOffset, mergingBytes.begin()) && mergingBytes[mergeOffset] == 3u && mergingBytes[mergeOffset + 1] == 0x0fu && mergingBytes[mergeOffset + 2] == 0xf0u && mergingBytes[mergeOffset + 5] == 1u && mergingBytes[mergeOffset + 6] == 1u && mergingBytes[mergeOffset + 34] == 0xefu && mergingBytes[mergeOffset + 37] == 0x89u && mergingBytes[mergeOffset + 38] == 1u && std::equal(plainPixel.begin() + mergeOffset + static_cast<std::ptrdiff_t>(mergeBytes), plainPixel.end(), mergingBytes.begin() + mergeOffset + static_cast<std::ptrdiff_t>(mergeBytes)), "the color target merge is not the 39 bytes the version 40 pixel info adds after the pixel iteration fields");
require(serializer.Deserialize(serializer.Serialize(merging)).request.context.pixel->targetMerge == merging.context.pixel->targetMerge, "a serialized request lost its color target merge");
const auto withoutMerge = [&](std::vector<std::uint8_t> bytes) {
bytes.erase(bytes.begin() + mergeOffset, bytes.begin() + mergeOffset + static_cast<std::ptrdiff_t>(mergeBytes));
@@ -47,6 +47,9 @@ constexpr std::uint32_t Unorm4_4_4_4Info = 0x8000u | (19u << 2u) | (2u << 11u);
constexpr std::uint32_t TiledAttrib3 = 0x4dc6c000u;
constexpr std::uint32_t Float32Export = 9;
constexpr std::uint32_t Float16Export = 4;
constexpr std::uint32_t PositionInputs = 0x300u;
constexpr std::uint32_t LateKill = 0x40u;
constexpr std::array<std::uint32_t, 5> CheckerboardKill{0x7e040f00u, 0x7e060f01u, 0x3a040702u, 0x36040481u, 0x7da40480u};
alignas(256) constexpr std::array<std::uint32_t, 6> VertexCode{
0xe0382000, 0x80000005, 0xbf8c3f70, 0xf80008cf, 0x03020100, 0xbf810000,
@@ -77,10 +80,13 @@ std::uint32_t Half(float value) {
return ((((bits >> 23u) & 0xffu) - 112u) << 10u) | ((bits >> 13u) & 0x3ffu);
}
std::span<const std::uint32_t> Program(const std::array<std::uint32_t, 4>& vgprs, bool compressed) {
std::span<const std::uint32_t> Program(const std::array<std::uint32_t, 4>& vgprs, bool compressed, bool killing = false) {
Require(programCount < Programs.size(), "too many test programs");
auto& code = Programs[programCount++];
std::size_t at = 0;
if (killing) {
for (const auto word : CheckerboardKill) code[at++] = word;
}
for (std::uint32_t vgpr = 0; vgpr < (compressed ? 2u : 4u); ++vgpr) {
code[at++] = 0x7e0002ffu | ((4u + vgpr) << 17u);
code[at++] = vgprs[vgpr];
@@ -91,8 +97,8 @@ std::span<const std::uint32_t> Program(const std::array<std::uint32_t, 4>& vgprs
return std::span<const std::uint32_t>(code.data(), at);
}
std::span<const std::uint32_t> Float32Program(std::array<float, 4> rgba) {
return Program({std::bit_cast<std::uint32_t>(rgba[0]), std::bit_cast<std::uint32_t>(rgba[1]), std::bit_cast<std::uint32_t>(rgba[2]), std::bit_cast<std::uint32_t>(rgba[3])}, false);
std::span<const std::uint32_t> Float32Program(std::array<float, 4> rgba, bool killing = false) {
return Program({std::bit_cast<std::uint32_t>(rgba[0]), std::bit_cast<std::uint32_t>(rgba[1]), std::bit_cast<std::uint32_t>(rgba[2]), std::bit_cast<std::uint32_t>(rgba[3])}, false, killing);
}
std::span<const std::uint32_t> Float16Program(std::array<float, 4> rgba) {
@@ -171,7 +177,7 @@ AgcDriver::Graphics::ColorTarget DecodeTarget(const Block& block) {
return color;
}
std::uint32_t Draw(AgcDriver::VulkanDevice& device, AgcDriver::Graphics::ColorTarget color, std::uint32_t exportedMask, std::uint32_t exportFormat, std::span<const std::uint32_t> pixelCode) {
std::uint32_t Draw(AgcDriver::VulkanDevice& device, AgcDriver::Graphics::ColorTarget color, std::uint32_t exportedMask, std::uint32_t exportFormat, std::span<const std::uint32_t> pixelCode, bool killing = false) {
const auto target = device.Target();
constexpr std::uint32_t waveSize = 64;
std::vector<std::uint32_t> vertexUserData(4, 0u);
@@ -210,7 +216,7 @@ std::uint32_t Draw(AgcDriver::VulkanDevice& device, AgcDriver::Graphics::ColorTa
state.blendConstants = {};
const auto codeAddress = reinterpret_cast<std::uintptr_t>(pixelCode.data());
const AgcDriver::Registers context{{0x1b6, 0x8000}, {0x1b3, 0}, {0x1b4, 0}, {0x203, 0}, {0x1c5, exportFormat}};
const AgcDriver::Registers context{{0x1b6, 0x8000}, {0x1b3, killing ? PositionInputs : 0u}, {0x1b4, killing ? PositionInputs : 0u}, {0x203, killing ? LateKill : 0u}, {0x1c5, exportFormat}};
const std::array<ShaderRecompiler::MemoryRegion, 1> pixelMemory{{{codeAddress, std::as_bytes(pixelCode)}}};
ShaderRecompiler::RecompileRequest fragment{
{ShaderStage::Fragment, codeAddress, pixelCode, 0, {}},
@@ -225,6 +231,7 @@ std::uint32_t Draw(AgcDriver::VulkanDevice& device, AgcDriver::Graphics::ColorTa
fragment.context.pixel = AgcDriver::Graphics::DecodePixelStageInfo(context, AgcDriver::Graphics::ExportMappings(state));
fragment.context.pixel->targetExportPacking = AgcDriver::Graphics::ExportPackings(state);
fragment.context.pixel->targetMerge = AgcDriver::Graphics::ExportTargetMerge(state);
Require(fragment.context.pixel->pixelKillEnable == killing && !fragment.context.pixel->earlyZ && !fragment.context.pixel->targetMerge.depthStencilWrites, "the draw's pixel kill under late tests or its depth and stencil writes did not decode");
Require((fragment.context.pixel->targetMerge.mask != 0u) == partial && fragment.context.pixel->targetMerge.mask == color.partialWriteMask, "the draw's target merge disagrees with its write mask");
const ShaderRecompiler::SrtRuntime runtime{};
const auto invocation = AgcDriver::DriverDetail::InvocationFor(snapshot, 0, fragment);
@@ -300,6 +307,21 @@ void BackToBack(AgcDriver::VulkanDevice& device, Block& block, const ColorTarget
}
}
void ExpectKilled(AgcDriver::VulkanDevice& device, const Block& block, const ColorTargetLayout& layout, const std::vector<std::uint16_t>& before, std::uint32_t word, std::uint32_t wordMask, const std::string& what) {
const auto stored = ReadBack(device, block);
for (std::uint32_t y = 0; y < Height; ++y) {
for (std::uint32_t x = 0; x < Width; ++x) {
const auto index = layout.Offset(x, y) / 2u;
const bool kept = x < ScissorWidth && ((x ^ y) & 1u) == 0u;
const std::uint32_t expected = kept ? (before[index] & ~wordMask) | (word & wordMask) : before[index];
if (stored[index] == expected) continue;
char detail[192];
std::snprintf(detail, sizeof(detail), ": %s pixel (%u, %u) holds 0x%04x, expected 0x%04x (it held 0x%04x)", kept ? "a surviving" : x < ScissorWidth ? "a killed" : "an unscissored", x, y, stored[index], expected, before[index]);
throw std::runtime_error(what + detail);
}
}
}
std::string Describe(std::uint32_t mask, std::array<float, 4> rgba) {
char text[160];
std::snprintf(text, sizeof(text), "CB_TARGET_MASK 0x%x write of (%.9g, %.9g, %.9g, %.9g)", mask, rgba[0], rgba[1], rgba[2], rgba[3]);
@@ -350,7 +372,12 @@ int main() {
const std::array<float, 4> halves{0.5f, 0.75f, 0.125f, 0.25f};
const auto wordMask = Draw(*device, color, 0x9u, Float16Export, Float16Program(halves));
ExpectSurface(*device, block, layout, before, Packed(halves), wordMask, "an FP16_ABGR " + Describe(0x9u, halves) + " into a tiled 4_4_4_4 unorm target");
std::printf("4_4_4_4 unorm partial write tests passed: %zu masked draws and 3 back-to-back sequences merged into a %ux%u tiled target, the pixels outside the scissor kept\n", cases.size() + 1u, Width, Height);
for (const auto& [mask, rgba] : {Case{0x1u, {0.6f, 0.2f, 0.8f, 0.4f}}, Case{0x6u, {1.0f, 2.0f / 15.0f, 13.0f / 15.0f, 0.0f}}}) {
before = Fill(block, 91u + mask);
const auto killedMask = Draw(*device, color, mask, Float32Export, Float32Program(rgba, true), true);
ExpectKilled(*device, block, layout, before, Packed(rgba), killedMask, "a late-test checkerboard-killing " + Describe(mask, rgba) + " into a tiled 4_4_4_4 unorm target");
}
std::printf("4_4_4_4 unorm partial write tests passed: %zu masked draws, 3 back-to-back sequences and 2 checkerboard-killing draws merged into a %ux%u tiled target, the killed pixels and those outside the scissor kept\n", cases.size() + 1u, Width, Height);
return 0;
} catch (const std::exception& error) {
std::cerr << error.what() << '\n';
+1
View File
@@ -220,6 +220,7 @@ private:
append(key, value.targetMerge.target);
append(key, value.targetMerge.mask);
append(key, value.targetMerge.samples);
append(key, value.targetMerge.depthStencilWrites);
}
static void append(std::vector<std::uint64_t>& key, const ShaderVertexResourceDestination& value) {
@@ -359,6 +359,7 @@ void writePixelInfo(Writer& writer, const ShaderPixelStageInfo& info) {
writer.WriteU32(info.targetMerge.mask);
writer.WriteU8(info.targetMerge.samples);
for (const std::uint32_t word : info.targetMerge.descriptor) writer.WriteU32(word);
writer.WriteBool(info.targetMerge.depthStencilWrites);
}
ShaderPixelStageInfo readPixelInfo(Reader& reader, std::uint32_t version) {
@@ -434,6 +435,7 @@ ShaderPixelStageInfo readPixelInfo(Reader& reader, std::uint32_t version) {
info.targetMerge.mask = static_cast<std::uint16_t>(mask);
info.targetMerge.samples = reader.ReadU8();
for (std::uint32_t& word : info.targetMerge.descriptor) word = reader.ReadU32();
info.targetMerge.depthStencilWrites = reader.ReadBool();
}
if (version < 5u) {
const auto input = [](PixelInput value, bool present) { return present ? PixelInputBit(value) : 0u; };
@@ -198,6 +198,7 @@ struct ShaderPixelInputInfo {
std::uint8_t mergeTarget = 0;
std::uint16_t mergeMask = 0;
std::uint8_t mergeSamples = 1;
bool mergeDepthStencilWrites = false;
ShaderStageRuntime stage;
bool HasPositionInput() const {
+1
View File
@@ -156,6 +156,7 @@ struct TargetMerge {
std::uint16_t mask = 0;
std::uint8_t samples = 1;
std::array<std::uint32_t, 8> descriptor{};
bool depthStencilWrites = false;
bool operator==(const TargetMerge&) const = default;
};
@@ -384,8 +384,8 @@ void DefineModule(SpirvEmitterState& state) {
state.module.AddExecutionMode(state.mainFunc, spv::ExecutionModeStencilRefReplacingEXT);
}
const bool merge = pixel.mergeMask != 0u;
if (merge && (state.depthVariable != 0 || state.stencilRefVariable != 0 || pixel.psDepthExportEnable || pixel.psSampleMaskExportEnable || pixel.psStencilRefExportEnable || pixel.psAlphaToCoverage || pixel.QuadMasked() || (pixel.psPixelKillEnable && !pixel.psEarlyZ))) {
throw std::runtime_error("a partial write of a packed color target merges inside the pixel shader after early depth and stencil tests, which a shader that kills pixels under late tests, exports depth, stencil or coverage, or runs with a quad sample mask cannot use");
if (merge && (state.depthVariable != 0 || state.stencilRefVariable != 0 || pixel.psDepthExportEnable || pixel.psSampleMaskExportEnable || pixel.psStencilRefExportEnable || pixel.psAlphaToCoverage || pixel.QuadMasked() || (pixel.psPixelKillEnable && !pixel.psEarlyZ && pixel.mergeDepthStencilWrites))) {
throw std::runtime_error("a partial write of a packed color target merges inside the pixel shader after early depth and stencil tests, which a shader that kills pixels under late tests of a draw writing depth or stencil, exports depth, stencil or coverage, or runs with a quad sample mask cannot use");
}
if ((pixel.psEarlyZ && !pixel.psAlphaToCoverage && !pixel.QuadMasked()) || merge) {
state.module.AddExecutionMode(state.mainFunc, spv::ExecutionModeEarlyFragmentTests);
@@ -159,6 +159,7 @@ ShaderStageInputInfo BuildShaderStageInputInfo(ShaderStageKind stage, const Gues
pixelStorage.mergeTarget = pixel.targetMerge.target;
pixelStorage.mergeMask = pixel.targetMerge.mask;
pixelStorage.mergeSamples = pixel.targetMerge.samples;
pixelStorage.mergeDepthStencilWrites = pixel.targetMerge.depthStencilWrites;
ShaderStageInputInfo result;
result.pixel = &pixelStorage;
return result;
@@ -54,7 +54,7 @@ void InsertMeshBarycentricFlag(std::vector<std::uint8_t>& bytes) {
void InsertPixelDefaultsAfterFrontFace(std::vector<std::uint8_t>& bytes) {
bytes.insert(bytes.begin() + 261u, {0x0fu, 0xffu, 0xffu, 0u, 0u, 0xffu, 0xffu, 0u, 0u, 0xffu, 0xffu, 0u, 0u, 0xffu, 0xffu, 0u, 0u, 0u, 0u, 0u, 0u});
bytes.insert(bytes.begin() + 282u, 73u, 0u);
bytes.insert(bytes.begin() + 282u, 74u, 0u);
bytes[322] = 1u;
}
@@ -411,7 +411,7 @@ int main() {
if (fixture.pixel) main38.insert(main38.begin() + 259u, {0u, 0u});
if (fixture.pixel) InsertPixelDefaultsAfterFrontFace(main38);
auto upgraded = main38;
if (fixture.pixel) main38.erase(main38.begin() + 317u, main38.begin() + 355u);
if (fixture.pixel) main38.erase(main38.begin() + 317u, main38.begin() + 356u);
upgraded[4] = 40u;
if (fixture.tessellation) upgraded.insert(upgraded.end() - 35u, 1u, 0u);
main38.insert(main38.end(), sizeof(std::uint64_t) + 1u + 5u + 1u, 0u);
@@ -500,7 +500,7 @@ int main() {
auto version35 = DecodeBase64(serializer.Serialize(request));
const auto stencilOperationValue = std::search(version35.begin(), version35.end(), pixel.sampleLocations.begin(), pixel.sampleLocations.begin() + 4) + pixel.sampleLocations.size();
Require(stencilOperationValue < version35.end() && *stencilOperationValue == 0u);
version35.erase(stencilOperationValue, stencilOperationValue + 1 + 38);
version35.erase(stencilOperationValue, stencilOperationValue + 1 + 39);
version35[4] = 35u;
version35.pop_back();
const auto replay35 = serializer.Deserialize(EncodeBase64(version35));