diff --git a/core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp b/core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp index 0d4c3bd60..e06f740d6 100644 --- a/core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp @@ -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 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(recompile(program(mimg), imageData(normalized, mimg == 0xf09c0f18u ? TypeCube : Type2D, Format8888UNorm, mimg == 0xf09c0f18u ? 5u : 0u))); } + const std::vector latchSample{0xbe900380u, 0x7e020280u, 0x7e040280u, 0x7e060280u, 0x7e080280u, 0x7e0a0280u, 0x7e0c0280u, 0xf09c0f28u, 0x00400801u, 0x80108110u, 0xbf0a8410u, 0xbf85fffbu, 0xe0700000u, 0x80030800u, 0xbf810000u}; + static_cast(recompile(latchSample, imageData(normalized, Type2DArray, Format8888UNorm, 5u))); const std::array pixelCode{0xf0800f08u, 0x00400801u, 0xf800180fu, 0x0b0a0908u, 0xbf810000u}; const auto pixelData = userData(unnormalized); diff --git a/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitterState.hpp b/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitterState.hpp index a2498d029..d1fbda35d 100644 --- a/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitterState.hpp +++ b/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitterState.hpp @@ -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; diff --git a/core/shader/recompiler/SpirvBackend/src/SpirvFlowEmitter.cpp b/core/shader/recompiler/SpirvBackend/src/SpirvFlowEmitter.cpp index c08132cb3..efd063740 100644 --- a/core/shader/recompiler/SpirvBackend/src/SpirvFlowEmitter.cpp +++ b/core/shader/recompiler/SpirvBackend/src/SpirvFlowEmitter.cpp @@ -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); } diff --git a/core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp b/core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp index e9fde5d68..c24be409e 100644 --- a/core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp +++ b/core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp @@ -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 labels(modes.size()); std::vector 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 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 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 phi{spv::OpPhi, TypeId(state, inst.Type()), resultId};