Shader/SPIRV: Fix double termination of blocks when branching

Which could happen when using OpKill (discard) for example
This commit is contained in:
Jérôme Leclercq 2021-12-26 22:00:44 +01:00
parent feb1774eb2
commit e21b45946f
3 changed files with 77 additions and 10 deletions

View File

@ -376,7 +376,8 @@ namespace Nz
SpirvBlock mergeBlock(m_writer); SpirvBlock mergeBlock(m_writer);
previousContentBlock.Append(SpirvOp::OpBranch, mergeBlock.GetLabelId()); //< FIXME: Shouldn't terminate twice if (!previousContentBlock.IsTerminated())
previousContentBlock.Append(SpirvOp::OpBranch, mergeBlock.GetLabelId());
m_functionBlocks.back().Append(SpirvOp::OpSelectionMerge, mergeBlock.GetLabelId(), SpirvSelectionControl::None); m_functionBlocks.back().Append(SpirvOp::OpSelectionMerge, mergeBlock.GetLabelId(), SpirvSelectionControl::None);
@ -397,7 +398,8 @@ namespace Nz
statement.statement->Visit(*this); statement.statement->Visit(*this);
previousContentBlock.Append(SpirvOp::OpBranch, mergeBlock.GetLabelId()); //< FIXME: Shouldn't terminate twice if (!previousContentBlock.IsTerminated())
previousContentBlock.Append(SpirvOp::OpBranch, mergeBlock.GetLabelId());
} }
if (node.elseStatement) if (node.elseStatement)
@ -408,7 +410,8 @@ namespace Nz
node.elseStatement->Visit(*this); node.elseStatement->Visit(*this);
elseBlock.Append(SpirvOp::OpBranch, mergeBlock.GetLabelId()); //< FIXME: Shouldn't terminate twice if (!elseBlock.IsTerminated())
elseBlock.Append(SpirvOp::OpBranch, mergeBlock.GetLabelId());
m_functionBlocks.back().Append(SpirvOp::OpBranchConditional, previousConditionId, previousContentBlock.GetLabelId(), elseBlock.GetLabelId()); m_functionBlocks.back().Append(SpirvOp::OpBranchConditional, previousConditionId, previousContentBlock.GetLabelId(), elseBlock.GetLabelId());
m_functionBlocks.emplace_back(std::move(previousContentBlock)); m_functionBlocks.emplace_back(std::move(previousContentBlock));

View File

@ -8,7 +8,9 @@
TEST_CASE("branching", "[Shader]") TEST_CASE("branching", "[Shader]")
{ {
std::string_view nzslSource = R"( WHEN("using a simple branch")
{
std::string_view nzslSource = R"(
struct inputStruct struct inputStruct
{ {
value: f32 value: f32
@ -30,9 +32,9 @@ fn main()
} }
)"; )";
Nz::ShaderAst::StatementPtr shader = Nz::ShaderLang::Parse(nzslSource); Nz::ShaderAst::StatementPtr shader = Nz::ShaderLang::Parse(nzslSource);
ExpectGLSL(*shader, R"( ExpectGLSL(*shader, R"(
void main() void main()
{ {
float value; float value;
@ -48,7 +50,7 @@ void main()
} }
)"); )");
ExpectNZSL(*shader, R"( ExpectNZSL(*shader, R"(
[entry(frag)] [entry(frag)]
fn main() fn main()
{ {
@ -65,7 +67,7 @@ fn main()
} }
)"); )");
ExpectSpirV(*shader, R"( ExpectSpirV(*shader, R"(
OpFunction OpFunction
OpLabel OpLabel
OpVariable OpVariable
@ -83,4 +85,66 @@ OpBranch
OpLabel OpLabel
OpReturn OpReturn
OpFunctionEnd)"); OpFunctionEnd)");
}
WHEN("discarding in a branch")
{
std::string_view nzslSource = R"(
struct inputStruct
{
value: f32
}
external
{
[set(0), binding(0)] data: uniform<inputStruct>
}
[entry(frag)]
fn main()
{
if (data.value > 0.0)
discard;
}
)";
Nz::ShaderAst::StatementPtr shader = Nz::ShaderLang::Parse(nzslSource);
ExpectGLSL(*shader, R"(
void main()
{
if (data.value > (0.000000))
{
discard;
}
}
)");
ExpectNZSL(*shader, R"(
[entry(frag)]
fn main()
{
if (data.value > (0.000000))
{
discard;
}
}
)");
ExpectSpirV(*shader, R"(
OpFunction
OpLabel
OpAccessChain
OpLoad
OpFOrdGreaterThanEqual
OpSelectionMerge
OpBranchConditional
OpLabel
OpKill
OpLabel
OpReturn
OpFunctionEnd)");
}
} }

View File

@ -137,8 +137,8 @@ void ExpectGLSL(Nz::ShaderAst::Statement& shader, std::string_view expectedOutpu
WHEN("Validating full GLSL code (using glslang)") WHEN("Validating full GLSL code (using glslang)")
{ {
glslang::TShader shader(EShLangVertex); glslang::TShader shader(EShLangFragment);
shader.setEnvInput(glslang::EShSourceGlsl, EShLangVertex, glslang::EShClientOpenGL, 300); shader.setEnvInput(glslang::EShSourceGlsl, EShLangFragment, glslang::EShClientOpenGL, 300);
shader.setEnvClient(glslang::EShClientOpenGL, glslang::EShTargetOpenGL_450); shader.setEnvClient(glslang::EShClientOpenGL, glslang::EShTargetOpenGL_450);
shader.setEnvTarget(glslang::EShTargetNone, static_cast<glslang::EShTargetLanguageVersion>(0)); shader.setEnvTarget(glslang::EShTargetNone, static_cast<glslang::EShTargetLanguageVersion>(0));
shader.setEntryPoint("main"); shader.setEntryPoint("main");