fix(shader): keep runtime image mode switches inside continue targets
An image with several runtime modes is emitted as a switch whose default and disabled modes end in OpUnreachable. In a loop latch that exit breaks the continue construct, and spirv-val rejected the shader. Inside a continue target those cases now join the merge with an undefined result. Fixes #1921 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f145139676
commit
33603bf8a9
|
|
@ -419,6 +419,14 @@ void verifyBindlessTable() {
|
|||
require(wholeCapture->snapshot.images[wholeRoot].dwords == heap[0] && wholeCapture->snapshot.images[wholeDirect].dwords == heap[1] && wholeCapture->snapshot.images[wholeDirect + 1u].dwords == heap[2] && wholeCapture->snapshot.images[wholeDirect + 2u].dwords == heap[3], "bindless: mode T slots are wrong");
|
||||
whole.context.memory = wholeMemory.Regions();
|
||||
require(!Recompile(whole, *wholeCapture)->spirv.empty(), "bindless: mode T did not compile");
|
||||
std::vector<std::uint32_t> latchCode(wholeCode.begin(), wholeCode.begin() + 12);
|
||||
latchCode.insert(latchCode.end(), {0xbea80380u, 0xf09c0f08u, 0x00450000u, 0x80288128u, 0xbf0a8428u, 0xbf85fffbu, 0xe0700000u, 0x80070000u, 0xbf810000u});
|
||||
auto latch = makeRequest(latchCode);
|
||||
AgcDriver::ShaderMemory latchMemory({});
|
||||
const auto latchCapture = latchMemory.Capture(latch);
|
||||
const auto latchRegions = latchMemory.Regions();
|
||||
latch.context.memory = latchRegions;
|
||||
require(!Recompile(latch, *latchCapture)->spirv.empty(), "bindless: a sample in a loop latch did not compile");
|
||||
|
||||
// A table wider than the slots without a material pattern is rejected.
|
||||
fillSrt(100u);
|
||||
|
|
@ -1402,6 +1410,8 @@ void verifyUnnormalizedSamplers() {
|
|||
for (const std::uint32_t mimg : {0xf0c00f08u, 0xf11c0108u, 0xf09c0f18u}) {
|
||||
static_cast<void>(recompile(program(mimg), imageData(normalized, mimg == 0xf09c0f18u ? TypeCube : Type2D, Format8888UNorm, mimg == 0xf09c0f18u ? 5u : 0u)));
|
||||
}
|
||||
const std::vector<std::uint32_t> latchSample{0xbe900380u, 0x7e020280u, 0x7e040280u, 0x7e060280u, 0x7e080280u, 0x7e0a0280u, 0x7e0c0280u, 0xf09c0f28u, 0x00400801u, 0x80108110u, 0xbf0a8410u, 0xbf85fffbu, 0xe0700000u, 0x80030800u, 0xbf810000u};
|
||||
static_cast<void>(recompile(latchSample, imageData(normalized, Type2DArray, Format8888UNorm, 5u)));
|
||||
|
||||
const std::array<std::uint32_t, 5> pixelCode{0xf0800f08u, 0x00400801u, 0xf800180fu, 0x0b0a0908u, 0xbf810000u};
|
||||
const auto pixelData = userData(unnormalized);
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ struct SpirvEmitterState {
|
|||
std::uint32_t bdaStopValue = 0;
|
||||
// False for programs with workgroup barriers: faulting BDA accesses then continue (see BdaInvocationsMayStop).
|
||||
bool bdaStopsInvocations = true;
|
||||
bool continueTarget = false;
|
||||
// Execution scope of the barriers that keep one guest wave's LDS accesses in program order across
|
||||
// host invocations (see WaveLdsScope); 0 when none are emitted.
|
||||
std::uint32_t waveLdsScope = 0;
|
||||
|
|
|
|||
|
|
@ -811,11 +811,13 @@ void EmitControlFlow(SpirvValueEmitContext& context, StructuredFunctionState& fu
|
|||
context.Fail("structured control flow block has no terminator metadata");
|
||||
}
|
||||
const bool stops = state.bdaStopsInvocations;
|
||||
state.bdaStopsInvocations = stops && !IsContinueTarget(program, info->id);
|
||||
state.continueTarget = IsContinueTarget(program, info->id);
|
||||
state.bdaStopsInvocations = stops && !state.continueTarget;
|
||||
EmitStructuredBlock(context, functionState, block);
|
||||
functionState.blockExitLabels.emplace(block, state.currentLabel);
|
||||
EmitStructuredTerminator(context, program, *info);
|
||||
state.bdaStopsInvocations = stops;
|
||||
state.continueTarget = false;
|
||||
}
|
||||
PatchStructuredPhis(context, functionState);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1568,17 +1568,23 @@ void EmitImage(SpirvValueEmitContext& ctx, const IrValue& inst) {
|
|||
const bool returnsValue = inst.Opcode() != IrOpcode::ImageWrite;
|
||||
const auto resultId = returnsValue ? ctx.Result(inst) : 0u;
|
||||
ctx.definitions.erase(&inst);
|
||||
const bool exits = !state.continueTarget;
|
||||
const auto merge = state.module.AllocateId();
|
||||
const auto invalid = state.module.AllocateId();
|
||||
const auto invalid = exits ? state.module.AllocateId() : merge;
|
||||
const auto undefined = returnsValue && !exits ? state.module.AllocateId() : 0u;
|
||||
std::vector<std::uint32_t> labels(modes.size());
|
||||
std::vector<std::uint32_t> words{spv::OpSwitch, selector, invalid};
|
||||
for (std::uint32_t index = 0u; index < modes.size(); ++index) {
|
||||
labels[index] = state.module.AllocateId();
|
||||
words.insert(words.end(), {index, labels[index]});
|
||||
}
|
||||
std::vector<std::uint32_t> incoming;
|
||||
if (undefined != 0u) {
|
||||
state.module.AddFunction(spv::OpUndef, TypeId(state, inst.Type()), undefined);
|
||||
incoming.insert(incoming.end(), {undefined, state.currentLabel});
|
||||
}
|
||||
state.module.AddFunction(spv::OpSelectionMerge, merge, spv::SelectionControlMaskNone);
|
||||
state.module.AddFunction(words);
|
||||
std::vector<std::uint32_t> incoming;
|
||||
for (std::uint32_t index = 0u; index < modes.size(); ++index) {
|
||||
EmitLabel(state, labels[index]);
|
||||
std::uint32_t modeMerge = 0u;
|
||||
|
|
@ -1586,18 +1592,26 @@ void EmitImage(SpirvValueEmitContext& ctx, const IrValue& inst) {
|
|||
const auto enabled = state.module.SpecializationConstant(TypeU32(state), PipelineSpecialization::ImageModeBase + memory.resource * PipelineSpecialization::ImageModeStride + index, 1u);
|
||||
const auto active = Binary(state, spv::OpINotEqual, TypeBool(state), enabled, ConstantU32(state, 0u));
|
||||
const auto activeLabel = state.module.AllocateId();
|
||||
const auto inactiveLabel = state.module.AllocateId();
|
||||
modeMerge = state.module.AllocateId();
|
||||
const auto inactiveLabel = exits ? state.module.AllocateId() : modeMerge;
|
||||
state.module.AddFunction(spv::OpSelectionMerge, modeMerge, spv::SelectionControlMaskNone);
|
||||
state.module.AddFunction(spv::OpBranchConditional, active, activeLabel, inactiveLabel);
|
||||
EmitLabel(state, inactiveLabel);
|
||||
state.module.AddFunction(spv::OpUnreachable);
|
||||
if (exits) {
|
||||
EmitLabel(state, inactiveLabel);
|
||||
state.module.AddFunction(spv::OpUnreachable);
|
||||
}
|
||||
EmitLabel(state, activeLabel);
|
||||
}
|
||||
emitMode(modes[index]);
|
||||
if (modeMerge != 0u) {
|
||||
const auto activeEnd = state.currentLabel;
|
||||
state.module.AddFunction(spv::OpBranch, modeMerge);
|
||||
EmitLabel(state, modeMerge);
|
||||
if (undefined != 0u) {
|
||||
const auto joined = state.module.AllocateId();
|
||||
state.module.AddFunction(spv::OpPhi, TypeId(state, inst.Type()), joined, ctx.Def(&inst), activeEnd, undefined, labels[index]);
|
||||
ctx.definitions.insert_or_assign(&inst, joined);
|
||||
}
|
||||
}
|
||||
if (returnsValue) incoming.insert(incoming.end(), {ctx.Def(&inst), state.currentLabel});
|
||||
ctx.definitions.erase(&inst);
|
||||
|
|
@ -1605,8 +1619,10 @@ void EmitImage(SpirvValueEmitContext& ctx, const IrValue& inst) {
|
|||
}
|
||||
state.runtimeImage = nullptr;
|
||||
state.runtimeImageMetadata = 0u;
|
||||
EmitLabel(state, invalid);
|
||||
state.module.AddFunction(spv::OpUnreachable);
|
||||
if (exits) {
|
||||
EmitLabel(state, invalid);
|
||||
state.module.AddFunction(spv::OpUnreachable);
|
||||
}
|
||||
EmitLabel(state, merge);
|
||||
if (returnsValue) {
|
||||
std::vector<std::uint32_t> phi{spv::OpPhi, TypeId(state, inst.Type()), resultId};
|
||||
|
|
|
|||
Loading…
Reference in New Issue