mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-27 00:28:14 +00:00
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:
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';
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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));
|
||||
|
||||
Reference in new issue
Block a user