Support the SPV_EXT_demote_to_helper_invocation extension.

This extension provides a new operation which causes a fragment to be
discarded without terminating the fragment shader invocation. The
invocation for the discarded fragment becomes a helper invocation, so
that derivatives will remain defined. The old `HelperInvocation` builtin
becomes undefined when this occurs, so a second new instruction queries
the current helper invocation status.

This is only fully supported for GLSL. HLSL doesn't support the
`IsHelperInvocation` operation and MSL doesn't support the
`DemoteToHelperInvocation` op.

Fixes #1052.
This commit is contained in:
Chip Davis 2019-07-13 15:57:33 -05:00
parent fb71a337a2
commit 50dce10c5d
13 changed files with 106 additions and 0 deletions

View File

@ -0,0 +1,9 @@
void frag_main()
{
discard;
}
void main()
{
frag_main();
}

View File

@ -0,0 +1,9 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
fragment void main0()
{
}

View File

@ -0,0 +1,9 @@
#version 450
#extension GL_EXT_demote_to_helper_invocation : require
void main()
{
demote;
bool helper = helperInvocationEXT();
}

View File

@ -0,0 +1,9 @@
void frag_main()
{
discard;
}
void main()
{
frag_main();
}

View File

@ -0,0 +1,10 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
fragment void main0()
{
bool helper = simd_is_helper_thread();
}

View File

@ -0,0 +1,9 @@
#version 450
#extension GL_EXT_demote_to_helper_invocation : require
void main()
{
demote;
bool helper = helperInvocationEXT();
}

View File

@ -0,0 +1,7 @@
#version 450
#extension GL_EXT_demote_to_helper_invocation : require
void main()
{
demote;
}

View File

@ -0,0 +1,8 @@
#version 450
#extension GL_EXT_demote_to_helper_invocation : require
void main()
{
//demote; // FIXME: Not implemented for MSL
bool helper = helperInvocationEXT();
}

View File

@ -0,0 +1,8 @@
#version 450
#extension GL_EXT_demote_to_helper_invocation : require
void main()
{
demote;
bool helper = helperInvocationEXT();
}

View File

@ -9778,6 +9778,20 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
case OpNoLine: case OpNoLine:
break; break;
case OpDemoteToHelperInvocationEXT:
if (!options.vulkan_semantics)
SPIRV_CROSS_THROW("GL_EXT_demote_to_helper_invocation is only supported in Vulkan GLSL.");
require_extension_internal("GL_EXT_demote_to_helper_invocation");
statement(backend.demote_literal, ";");
break;
case OpIsHelperInvocationEXT:
if (!options.vulkan_semantics)
SPIRV_CROSS_THROW("GL_EXT_demote_to_helper_invocation is only supported in Vulkan GLSL.");
require_extension_internal("GL_EXT_demote_to_helper_invocation");
emit_op(ops[0], ops[1], "helperInvocationEXT()", true);
break;
default: default:
statement("// unimplemented op ", instruction.op); statement("// unimplemented op ", instruction.op);
break; break;

View File

@ -369,6 +369,7 @@ protected:
struct BackendVariations struct BackendVariations
{ {
std::string discard_literal = "discard"; std::string discard_literal = "discard";
std::string demote_literal = "demote";
std::string null_pointer_literal = ""; std::string null_pointer_literal = "";
bool float_literal_suffix = false; bool float_literal_suffix = false;
bool double_literal_suffix = true; bool double_literal_suffix = true;

View File

@ -4663,6 +4663,9 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction)
break; break;
} }
case OpIsHelperInvocationEXT:
SPIRV_CROSS_THROW("helperInvocationEXT() is not supported in HLSL.");
default: default:
CompilerGLSL::emit_instruction(instruction); CompilerGLSL::emit_instruction(instruction);
break; break;
@ -4819,6 +4822,7 @@ string CompilerHLSL::compile()
backend.uint16_t_literal_suffix = "u"; backend.uint16_t_literal_suffix = "u";
backend.basic_int_type = "int"; backend.basic_int_type = "int";
backend.basic_uint_type = "uint"; backend.basic_uint_type = "uint";
backend.demote_literal = "discard";
backend.boolean_mix_function = ""; backend.boolean_mix_function = "";
backend.swizzle_is_function = false; backend.swizzle_is_function = false;
backend.shared_is_implied = true; backend.shared_is_implied = true;

View File

@ -757,6 +757,7 @@ string CompilerMSL::compile()
backend.basic_int16_type = "short"; backend.basic_int16_type = "short";
backend.basic_uint16_type = "ushort"; backend.basic_uint16_type = "ushort";
backend.discard_literal = "discard_fragment()"; backend.discard_literal = "discard_fragment()";
backend.demote_literal = "unsupported-demote";
backend.boolean_mix_function = "select"; backend.boolean_mix_function = "select";
backend.swizzle_is_function = false; backend.swizzle_is_function = false;
backend.shared_is_implied = false; backend.shared_is_implied = false;
@ -4467,6 +4468,14 @@ void CompilerMSL::emit_instruction(const Instruction &instruction)
break; break;
} }
case OpIsHelperInvocationEXT:
if (msl_options.is_ios())
SPIRV_CROSS_THROW("simd_is_helper_thread() is only supported on macOS.");
else if (msl_options.is_macos() && !msl_options.supports_msl_version(2, 1))
SPIRV_CROSS_THROW("simd_is_helper_thread() requires version 2.1 on macOS.");
emit_op(ops[0], ops[1], "simd_is_helper_thread()", true);
break;
default: default:
CompilerGLSL::emit_instruction(instruction); CompilerGLSL::emit_instruction(instruction);
break; break;