diff options
| author | ArielG-NV <159081215+ArielG-NV@users.noreply.github.com> | 2024-03-13 15:03:16 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-03-13 15:03:16 -0400 |
| commit | 9fd74379c22af14f794d48fdc22e772d47f61ca3 (patch) | |
| tree | 9d15b4139d8fdaa335197617b0bc6cab14ed42a3 /source/slang | |
| parent | 6f7c8271710b43349d34b8f7569ceb6957400548 (diff) | |
Implement glsl atomic's [non image or memory scope] with optional extension(s); resolves #3587 for GLSL & SPIR-V targets (#3755)
The following commit implements atomic operations & types associated with OpenGL 4.6, GL_EXT_vulkan_glsl_relaxed, GLSL_EXT_shader_atomic_float, GLSL_EXT_shader_atomic_float2, for GLSL & SPIR-V targets.
Fully implements all functions, and built-in type's, resolves https://github.com/shader-slang/slang/issues/3560 for GLSL & SPRI-V targets.
[Atomic extensions for GLSL can be found here](https://github.com/KhronosGroup/GLSL/tree/main)
Notes of worth:
* atomic_uint is well defined in GLSL->OpenGL, although was removed in GLSL->VK unless a compiler extension is supported (GL_EXT_vulkan_glsl_relaxed). This support entails transforming all atomic_uint operations and references into a storage buffer. SPIR-V has AtomicCounter+AtomicStorage (atomic_uint parallel) but does not implement these capabilities for SPIR-V->VK in any scenario. Due to the case we transform atomic_uint ourselves (GLSL_Syntax->Slang_IR) to accommodate transforming atomic_uint into valid syntax.
* GLSL_EXT_shader_atomic_float2 (all float16_t & some float/double operations) support is minimal and worth watching out for if enabling the tests.
Diffstat (limited to 'source/slang')
| -rw-r--r-- | source/slang/glsl.meta.slang | 603 | ||||
| -rw-r--r-- | source/slang/slang-ast-modifier.h | 8 | ||||
| -rw-r--r-- | source/slang/slang-ast-type.h | 5 | ||||
| -rw-r--r-- | source/slang/slang-check-modifier.cpp | 2 | ||||
| -rw-r--r-- | source/slang/slang-diagnostic-defs.h | 1 | ||||
| -rw-r--r-- | source/slang/slang-emit-c-like.cpp | 4 | ||||
| -rw-r--r-- | source/slang/slang-ir-inst-defs.h | 3 | ||||
| -rw-r--r-- | source/slang/slang-ir-insts.h | 12 | ||||
| -rw-r--r-- | source/slang/slang-ir-link.cpp | 138 | ||||
| -rw-r--r-- | source/slang/slang-ir-spirv-legalize.cpp | 1 | ||||
| -rw-r--r-- | source/slang/slang-ir-util.cpp | 3 | ||||
| -rw-r--r-- | source/slang/slang-ir.cpp | 12 | ||||
| -rw-r--r-- | source/slang/slang-lower-to-ir.cpp | 5 | ||||
| -rw-r--r-- | source/slang/slang-parameter-binding.cpp | 6 | ||||
| -rw-r--r-- | source/slang/slang-parser.cpp | 81 | ||||
| -rw-r--r-- | source/slang/slang-reflection-api.cpp | 4 | ||||
| -rw-r--r-- | source/slang/slang-type-layout.cpp | 8 | ||||
| -rw-r--r-- | source/slang/slang-type-layout.h | 2 |
18 files changed, 896 insertions, 2 deletions
diff --git a/source/slang/glsl.meta.slang b/source/slang/glsl.meta.slang index 824b3e3f3..d07233583 100644 --- a/source/slang/glsl.meta.slang +++ b/source/slang/glsl.meta.slang @@ -4615,4 +4615,605 @@ __spirv_version(1.3) [require(spirv)] { shader_subgroup_preamble<T>(); return QuadReadAcrossDiagonal(value); -}
\ No newline at end of file +} + +//// GLSL atomic + +// The following type internally is a Shader Storage Buffer +// as per GL_EXT_vulkan_glsl_relaxed +__magic_type(GLSLAtomicUintType) +__intrinsic_type($(kIROp_GLSLAtomicUintType)) +public struct atomic_uint +{ +}; + +// tier of float refers to atomic extension support of float1 or float2. +// if we are inside a atomic_float function we will run the check for float1 tier +// types and operations to enable the according ext needed for these operations + +__generic<T : __BuiltinType> +[ForceInline] void typeRequireChecks_atomic_using_float0_tier() +{ + __target_switch + { + case glsl: + { + if (__type_equals<T, uint64_t>() || __type_equals<T, int64_t>()) + __requireGLSLExtension("GL_EXT_shader_atomic_int64"); + } + case spirv: + return; + } +} +__generic<T : __BuiltinType> +[ForceInline] void typeRequireChecks_atomic_using_float1_tier() +{ + __target_switch + { + case glsl: + { + if (__type_equals<T, float>()) + __requireGLSLExtension("GL_EXT_shader_atomic_float"); + else if (__type_equals<T, half>() || __type_equals<T, float16_t>()) + { + __requireGLSLExtension("GL_EXT_shader_atomic_float2"); + __requireGLSLExtension("GL_EXT_shader_explicit_arithmetic_types"); + } + else if (__type_equals<T, double>()) + __requireGLSLExtension("GL_EXT_shader_atomic_float"); + else if (__type_equals<T, uint64_t>() || __type_equals<T, int64_t>()) + __requireGLSLExtension("GL_EXT_shader_atomic_int64"); + } + case spirv: + return; + } +} +__generic<T : __BuiltinType> +[ForceInline] void typeRequireChecks_atomic_using_float2_tier() +{ + __target_switch + { + case glsl: + { + if (__type_equals<T, float>()) + __requireGLSLExtension("GL_EXT_shader_atomic_float2"); + else if (__type_equals<T, half>() || __type_equals<T, float16_t>()) + { + __requireGLSLExtension("GL_EXT_shader_atomic_float2"); + __requireGLSLExtension("GL_EXT_shader_explicit_arithmetic_types"); + } + else if (__type_equals<T, double>()) + __requireGLSLExtension("GL_EXT_shader_atomic_float2"); + else if (__type_equals<T, uint64_t>() || __type_equals<T, int64_t>()) + __requireGLSLExtension("GL_EXT_shader_atomic_int64"); + } + case spirv: + return; + } +} + +__generic<T : __BuiltinType> +void typeRequireChecks_atomic_using_add() +{ + __target_switch + { + case glsl: + return; + case spirv: + { + if (__type_equals<T, float>()) + { + spirv_asm + { + OpExtension "SPV_EXT_shader_atomic_float_add"; + OpCapability AtomicFloat32AddEXT + }; + } + else if (__type_equals<T, half>() + || __type_equals<T, float16_t>()) + { + spirv_asm + { + OpExtension "SPV_EXT_shader_atomic_float_add"; + OpCapability AtomicFloat16AddEXT + }; + } + else if (__type_equals<T, double>()) + { + spirv_asm + { + OpExtension "SPV_EXT_shader_atomic_float_add"; + OpCapability AtomicFloat64AddEXT + }; + } + else if (__type_equals<T, uint64_t>() + || __type_equals<T, int64_t>()) + { + spirv_asm + { + OpCapability Int64Atomics + }; + } + } + } +} +__generic<T : __BuiltinType> +void typeRequireChecks_atomic_using_MinMax() +{ + __target_switch + { + case glsl: + return; + case spirv: + { + if (__type_equals<T, float>()) + { + spirv_asm + { + OpExtension "SPV_EXT_shader_atomic_float_add"; + OpCapability AtomicFloat32MinMaxEXT + }; + } + else if (__type_equals<T, half>() + || __type_equals<T, float16_t>()) + { + spirv_asm + { + OpExtension "SPV_EXT_shader_atomic_float_add"; + OpCapability AtomicFloat16MinMaxEXT + }; + } + else if (__type_equals<T, double>()) + { + spirv_asm + { + OpExtension "SPV_EXT_shader_atomic_float_add"; + OpCapability AtomicFloat64MinMaxEXT + }; + } + else if (__type_equals<T, uint64_t>() + || __type_equals<T, int64_t>()) + { + spirv_asm + { + OpCapability Int64Atomics + }; + } + } + } +} +__generic<T : __BuiltinType> +[ForceInline] void typeRequireChecks_atomic_using_Logical_CAS() +{ + __target_switch + { + case glsl: + return; + case spirv: + { + if (__type_equals<T, uint64_t>() + || __type_equals<T, int64_t>()) + { + spirv_asm + { + OpCapability Int64Atomics + }; + } + } + } +} + +${{{{ +static const struct { + const char* name; + const char* classType; + const char *subclassType; + const char *suffix; + const bool isFloat; +} atomics[] = + { + { + "uint", "I", "U", "", false + }, + { + "uint64_t", "I", "U", "", false + }, + { + "int", "I", "S", "", false + }, + { + "int64_t", "I", "S", "", false + }, + { + "float16_t", "F", "F", "EXT", true + }, + { + "float", "F", "F", "EXT", true + }, + { + "double", "F", "F", "EXT", true + }, + }; +for (const auto& item : atomics) +{ +}}}} + +__spirv_version(1.0)[require(spirv)] +__glsl_version(430) [require(glsl)] +[ForceInline] public $(item.name) atomicAdd(inout $(item.name) mem, $(item.name) data) +{ + typeRequireChecks_atomic_using_float1_tier<$(item.name)>(); + typeRequireChecks_atomic_using_add<$(item.name)>(); + __target_switch + { + case glsl: __intrinsic_asm "atomicAdd($0, $1)"; + case spirv: + return spirv_asm + { + OpAtomic$(item.classType)Add$(item.suffix) $$$(item.name) result &mem Device UniformMemory $data + }; + } +} + +__spirv_version(1.0)[require(spirv)] +__glsl_version(430) [require(glsl)] +[ForceInline] public $(item.name) atomicMin(inout $(item.name) mem, $(item.name) data) +{ + typeRequireChecks_atomic_using_float2_tier<$(item.name)>(); + typeRequireChecks_atomic_using_MinMax<$(item.name)>(); + __target_switch + { + case glsl: __intrinsic_asm "atomicMin($0, $1)"; + case spirv: + return spirv_asm + { + OpAtomic$(item.subclassType)Min$(item.suffix) $$$(item.name) result &mem Device UniformMemory $data + }; + } +} + +__spirv_version(1.0)[require(spirv)] +__glsl_version(430) [require(glsl)] +[ForceInline] public $(item.name) atomicMax(inout $(item.name) mem, $(item.name) data) +{ + typeRequireChecks_atomic_using_float2_tier<$(item.name)>(); + typeRequireChecks_atomic_using_MinMax<$(item.name)>(); + __target_switch + { + case glsl: __intrinsic_asm "atomicMax($0, $1)"; + case spirv: + return spirv_asm + { + OpAtomic$(item.subclassType)Max$(item.suffix) $$$(item.name) result &mem Device UniformMemory $data + }; + } +} + +__spirv_version(1.0)[require(spirv)] +__glsl_version(430) [require(glsl)] +[ForceInline] public $(item.name) atomicExchange(inout $(item.name) mem, $(item.name) data) +{ + typeRequireChecks_atomic_using_float1_tier<$(item.name)>(); + __target_switch + { + case glsl: __intrinsic_asm "atomicExchange($0, $1)"; + case spirv: + return spirv_asm + { + OpAtomicExchange $$$(item.name) result &mem Device UniformMemory $data + }; + } +} + +${{{{ +if(item.isFloat) + continue; +}}}} + +__spirv_version(1.0)[require(spirv)] +__glsl_version(430) [require(glsl)] +[ForceInline] public $(item.name) atomicAnd(inout $(item.name) mem, $(item.name) data) +{ + typeRequireChecks_atomic_using_float0_tier<$(item.name)>(); + typeRequireChecks_atomic_using_Logical_CAS<$(item.name)>(); + __target_switch + { + case glsl: + { + __intrinsic_asm "atomicAnd($0, $1)"; + } + case spirv: + return spirv_asm + { + OpAtomicAnd $$$(item.name) result &mem Device UniformMemory $data + }; + } +} + +__spirv_version(1.0)[require(spirv)] +__glsl_version(430) [require(glsl)] +[ForceInline] public $(item.name) atomicOr(inout $(item.name) mem, $(item.name) data) +{ + typeRequireChecks_atomic_using_float0_tier<$(item.name)>(); + typeRequireChecks_atomic_using_Logical_CAS<$(item.name)>(); + __target_switch + { + case glsl: __intrinsic_asm "atomicOr($0, $1)"; + case spirv: + return spirv_asm + { + OpAtomicOr $$$(item.name) result &mem Device UniformMemory $data + }; + } +} + +__spirv_version(1.0)[require(spirv)] +__glsl_version(430) [require(glsl)] +[ForceInline] public $(item.name) atomicXor(inout $(item.name) mem, $(item.name) data) +{ + typeRequireChecks_atomic_using_float0_tier<$(item.name)>(); + typeRequireChecks_atomic_using_Logical_CAS<$(item.name)>(); + __target_switch + { + case glsl: __intrinsic_asm "atomicXor($0, $1)"; + case spirv: + return spirv_asm + { + OpAtomicXor $$$(item.name) result &mem Device UniformMemory $data + }; + } +} + +__spirv_version(1.0)[require(spirv)] +__glsl_version(430) [require(glsl)] +[ForceInline] public $(item.name) atomicCompSwap(inout $(item.name) mem, $(item.name) compare, $(item.name) data) +{ + typeRequireChecks_atomic_using_float0_tier<$(item.name)>(); + typeRequireChecks_atomic_using_Logical_CAS<$(item.name)>(); + __target_switch + { + case glsl: __intrinsic_asm "atomicCompSwap($0, $1, $2)"; + case spirv: + return spirv_asm + { + result:$$$(item.name) = OpAtomicCompareExchange &mem Device None None $data $compare + }; + } +} + +${{{{ +} +}}}} + +// all atomic_uint functions are mangled at compile time, +// all types are converted into a field address of a 'uint' +// relative to the layout(offset) of the atomic_uint +__spirv_version(1.0)[require(spirv)] +__glsl_version(430) [require(glsl)] +[ForceInline] public uint atomicCounterIncrement(atomic_uint c) +{ + + __target_switch + { + case glsl: __intrinsic_asm "atomicAdd($0, 1)"; + case spirv: + { + return spirv_asm + { + OpAtomicIIncrement $$uint result $c Device UniformMemory + }; + } + } +} + +__glsl_version(430) [require(glsl)] +[ForceInline] public uint atomicCounterDecrement_GLSL_helper(atomic_uint c) +{ + __target_switch + { + case glsl: + { + __intrinsic_asm "atomicExchange($0,$0-1)"; + } + } +} + +__spirv_version(1.0)[require(spirv)] +__glsl_version(430) [require(glsl)] +[ForceInline] public uint atomicCounter(atomic_uint c) +{ + __target_switch + { + case glsl: + { + __intrinsic_asm "($0)"; + } + case spirv: + { + return spirv_asm + { + OpLoad $$uint result $c + }; + } + } +} + +__spirv_version(1.0)[require(spirv)] +__glsl_version(430) [require(glsl)] +[ForceInline] public uint atomicCounterDecrement(atomic_uint c) +{ + __target_switch + { + case glsl: + { + atomicCounterDecrement_GLSL_helper(c); + return atomicCounter(c); + } + case spirv: + { + // spirv OpAtomicIDecrement returns pre-sub-1, glsl returns the new value + // we want a discarded side effect and then return the new value + return spirv_asm + { + %discardedValue:$$uint = OpAtomicIDecrement $c Device UniformMemory; + OpLoad $$uint result $c + }; + } + } +} + +__spirv_version(1.0)[require(spirv)] +__glsl_version(430) [require(glsl)] +[ForceInline] public uint atomicCounterAdd(atomic_uint c, uint data) +{ + __target_switch + { + case glsl: __intrinsic_asm "atomicAdd($0, $1)"; + case spirv: + { + return spirv_asm + { + OpAtomicIAdd $$uint result $c Device UniformMemory $data + }; + } + } +} + +__spirv_version(1.0)[require(spirv)] +__glsl_version(430) [require(glsl)] +[ForceInline] public uint atomicCounterSubtract(atomic_uint c, uint data) +{ + __target_switch + { + case glsl: + { + __intrinsic_asm "atomicExchange($0,$0-$1)"; + } + case spirv: + { + return spirv_asm + { + OpAtomicISub $$uint result $c Device UniformMemory $data + }; + } + } +} + +__spirv_version(1.0)[require(spirv)] +__glsl_version(430) [require(glsl)] +[ForceInline] public uint atomicCounterMin(atomic_uint c, uint data) +{ + __target_switch + { + case glsl: __intrinsic_asm "atomicMin($0, $1)"; + case spirv: + { + return spirv_asm + { + OpAtomicUMin $$uint result $c Device UniformMemory $data + }; + } + } +} + +__spirv_version(1.0)[require(spirv)] +__glsl_version(430) [require(glsl)] +[ForceInline] public uint atomicCounterMax(atomic_uint c, uint data) +{ + __target_switch + { + case glsl: __intrinsic_asm "atomicMax($0, $1)"; + case spirv: + { + return spirv_asm + { + OpAtomicUMax $$uint result $c Device UniformMemory $data + }; + } + } +} + +__spirv_version(1.0)[require(spirv)] +__glsl_version(430) [require(glsl)] +[ForceInline] public uint atomicCounterAnd(atomic_uint c, uint data) +{ + __target_switch + { + case glsl: __intrinsic_asm "atomicAnd($0, $1)"; + case spirv: + { + return spirv_asm + { + OpAtomicAnd $$uint result $c Device UniformMemory $data + }; + } + } +} + +__spirv_version(1.0)[require(spirv)] +__glsl_version(430) [require(glsl)] +[ForceInline] public uint atomicCounterOr(atomic_uint c, uint data) +{ + __target_switch + { + case glsl: __intrinsic_asm "atomicOr($0, $1)"; + case spirv: + { + return spirv_asm + { + OpAtomicOr $$uint result $c Device UniformMemory $data + }; + } + } +} + +__spirv_version(1.0)[require(spirv)] +__glsl_version(430) [require(glsl)] +[ForceInline] public uint atomicCounterXor(atomic_uint c, uint data) +{ + __target_switch + { + case glsl: __intrinsic_asm "atomicXor($0, $1)"; + case spirv: + { + return spirv_asm + { + OpAtomicXor $$uint result $c Device UniformMemory $data + }; + } + } +} + +__spirv_version(1.0)[require(spirv)] +__glsl_version(430) [require(glsl)] +[ForceInline] public uint atomicCounterExchange(atomic_uint c, uint data) +{ + __target_switch + { + case glsl: __intrinsic_asm "atomicExchange($0, $1)"; + case spirv: + { + return spirv_asm + { + OpAtomicExchange $$uint result $c Device UniformMemory $data + }; + } + } +} + +__spirv_version(1.0)[require(spirv)] +__glsl_version(430) [require(glsl)] +[ForceInline] public uint atomicCounterCompSwap(atomic_uint c, uint compare, uint data) +{ + __target_switch + { + case glsl: __intrinsic_asm "atomicCompSwap($0, $1, $2)"; + case spirv: + { + return spirv_asm + { + OpAtomicCompareExchange $$uint result $c Device UniformMemory UniformMemory $data $compare + }; + } + } +} diff --git a/source/slang/slang-ast-modifier.h b/source/slang/slang-ast-modifier.h index ed8cbf514..49dc1b81f 100644 --- a/source/slang/slang-ast-modifier.h +++ b/source/slang/slang-ast-modifier.h @@ -737,6 +737,14 @@ class GLSLBindingAttribute : public Attribute int32_t set = 0; }; + +class GLSLOffsetLayoutAttribute : public Attribute +{ + SLANG_AST_CLASS(GLSLOffsetLayoutAttribute) + + int64_t offset; +}; + class GLSLSimpleIntegerLayoutAttribute : public Attribute { SLANG_AST_CLASS(GLSLSimpleIntegerLayoutAttribute) diff --git a/source/slang/slang-ast-type.h b/source/slang/slang-ast-type.h index d47e3a496..1e066fb2a 100644 --- a/source/slang/slang-ast-type.h +++ b/source/slang/slang-ast-type.h @@ -263,6 +263,11 @@ class HLSLConsumeStructuredBufferType : public HLSLStructuredBufferTypeBase SLANG_AST_CLASS(HLSLConsumeStructuredBufferType) }; +class GLSLAtomicUintType : public BuiltinType +{ + SLANG_AST_CLASS(GLSLAtomicUintType) +}; + class HLSLPatchType : public BuiltinType { SLANG_AST_CLASS(HLSLPatchType) diff --git a/source/slang/slang-check-modifier.cpp b/source/slang/slang-check-modifier.cpp index cf4bf3b02..6359096b9 100644 --- a/source/slang/slang-check-modifier.cpp +++ b/source/slang/slang-check-modifier.cpp @@ -988,6 +988,7 @@ namespace Slang case ASTNodeType::GLSLParsedLayoutModifier: case ASTNodeType::GLSLConstantIDLayoutModifier: case ASTNodeType::GLSLLocationLayoutModifier: + case ASTNodeType::GLSLOffsetLayoutAttribute: case ASTNodeType::GLSLUnparsedLayoutModifier: case ASTNodeType::GLSLLayoutModifierGroupMarker: case ASTNodeType::GLSLLayoutModifierGroupBegin: @@ -1063,6 +1064,7 @@ namespace Slang case ASTNodeType::GLSLParsedLayoutModifier: case ASTNodeType::GLSLConstantIDLayoutModifier: case ASTNodeType::GLSLLocationLayoutModifier: + case ASTNodeType::GLSLOffsetLayoutAttribute: case ASTNodeType::GLSLUnparsedLayoutModifier: case ASTNodeType::GLSLLayoutModifierGroupMarker: case ASTNodeType::GLSLLayoutModifierGroupBegin: diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h index 54761e772..8df51211a 100644 --- a/source/slang/slang-diagnostic-defs.h +++ b/source/slang/slang-diagnostic-defs.h @@ -238,6 +238,7 @@ DIAGNOSTIC(20012, Error, invalidSPIRVVersion, "Expecting SPIR-V version as eithe DIAGNOSTIC(20013, Error, invalidCUDASMVersion, "Expecting CUDA SM version as either 'major.minor', or quoted if has patch (eg for '7.0' or \"7.0\"')") DIAGNOSTIC(20014, Error, classIsReservedKeyword, "'class' is a reserved keyword in this context; use 'struct' instead.") DIAGNOSTIC(20015, Error, unknownSPIRVCapability, "unknown SPIR-V capability '$0'.") +DIAGNOSTIC(20016, Error, missingLayoutBindingModifier, "Expecting 'binding' modifier in the layout qualifier here") DIAGNOSTIC(20101, Warning, unintendedEmptyStatement, "potentially unintended empty statement at this location; use {} instead.") diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp index 451147dd0..44d74f219 100644 --- a/source/slang/slang-emit-c-like.cpp +++ b/source/slang/slang-emit-c-like.cpp @@ -2663,6 +2663,10 @@ void CLikeSourceEmitter::defaultEmitInstExpr(IRInst* inst, const EmitOpInfo& inO } break; } + case kIROp_RequireGLSLExtension: + { + break; //should already have set requirement; case covered for empty intrinsic block + } default: diagnoseUnhandledInst(inst); break; diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h index 26e0ebd69..977fca904 100644 --- a/source/slang/slang-ir-inst-defs.h +++ b/source/slang/slang-ir-inst-defs.h @@ -127,6 +127,8 @@ INST(Nop, nop, 0, 0) INST(ComPtrType, ComPtr, 1, HOISTABLE) // A NativePtr<T> type represents a native pointer to a managed resource. INST(NativePtrType, NativePtr, 1, HOISTABLE) + // An AtomicUint is a placeholder type for a storage buffer, and will be mangled during compiling. + INST(GLSLAtomicUintType, GLSLAtomicUint, 0, HOISTABLE) /* SamplerStateTypeBase */ INST(SamplerStateType, SamplerState, 0, HOISTABLE) @@ -848,6 +850,7 @@ INST(HighLevelDeclDecoration, highLevelDecl, 1, 0) INST(GlobalOutputDecoration, output, 0, 0) INST(GlobalInputDecoration, output, 0, 0) INST(GLSLLocationDecoration, glslLocation, 1, 0) + INST(GLSLOffsetDecoration, glslOffset, 1, 0) INST(PayloadDecoration, payload, 0, 0) /* Mesh Shader outputs */ diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h index 195bf577c..a85b279b8 100644 --- a/source/slang/slang-ir-insts.h +++ b/source/slang/slang-ir-insts.h @@ -370,6 +370,12 @@ struct IRGLSLLocationDecoration : IRDecoration IRIntLit* getLocation() { return cast<IRIntLit>(getOperand(0)); } }; +struct IRGLSLOffsetDecoration : IRDecoration +{ + IR_LEAF_ISA(GLSLOffsetDecoration) + IRIntLit* getOffset() { return cast<IRIntLit>(getOperand(0)); } +}; + struct IRNVAPIMagicDecoration : IRDecoration { enum { kOp = kIROp_NVAPIMagicDecoration }; @@ -3816,6 +3822,7 @@ public: // Create an initially empty `GLSLShaderStorageBufferType` type. IRGLSLShaderStorageBufferType* createGLSLShaderStorableBufferType(); + IRGLSLShaderStorageBufferType* createGLSLShaderStorableBufferType(UInt operandCount, IRInst* const* operands); // Create an empty `interface` type. IRInterfaceType* createInterfaceType(UInt operandCount, IRInst* const* operands); @@ -4431,6 +4438,11 @@ public: addDecoration(value, kIROp_DebugLocationDecoration, debugSource, getIntValue(getUIntType(), line), getIntValue(getUIntType(), col)); } + void addUnsafeForceInlineDecoration(IRInst* value) + { + addDecoration(value, kIROp_UnsafeForceInlineEarlyDecoration); + } + void addForceInlineDecoration(IRInst* value) { addDecoration(value, kIROp_ForceInlineDecoration); diff --git a/source/slang/slang-ir-link.cpp b/source/slang/slang-ir-link.cpp index 18cb850c0..a74c0c8f2 100644 --- a/source/slang/slang-ir-link.cpp +++ b/source/slang/slang-ir-link.cpp @@ -8,6 +8,7 @@ #include "slang-ir-string-hash.h" #include "slang-ir-autodiff.h" #include "slang-ir-specialize-target-switch.h" +#include "slang-ir-layout.h" #include "slang-module-library.h" #include "../core/slang-performance-profiler.h" @@ -1520,6 +1521,138 @@ static void diagnoseUnresolvedSymbols(TargetRequest* req, DiagnosticSink* sink, } } +void convertAtomicToStorageBuffer( + IRSpecContext* context, + Dictionary<int, List<IRInst*>>& bindingToInstMapUnsorted) +{ + // Atomic_uint definitions needs to become a storage buffer to follow GL_EXT_vulkan_glsl_relaxed + // and to allow translation of atomic_uint into SPIRV + + IRBuilder builder = *context->builder; + + for (auto& bindingToInstList : bindingToInstMapUnsorted) + { + int64_t maxOffset = 0; + for (auto& i : bindingToInstList.second) + { + int64_t currOffset = int64_t(i->findDecoration<IRGLSLOffsetDecoration>()->getOffset()->getValue()); + maxOffset = (maxOffset < currOffset) ? currOffset : maxOffset; + } + auto instToSwitch = *bindingToInstList.second.begin(); + builder.setInsertBefore(instToSwitch); + + auto elementType = builder.getArrayType( + builder.getUIntType(), + builder.getIntValue(builder.getUIntType(), (maxOffset / sizeof(uint32_t))+1) + ); + + StringBuilder nameStruct; + nameStruct << "atomic_uints"; + nameStruct << bindingToInstList.first; + nameStruct << "_t"; + nameStruct << "_paramGroup"; + auto structType = builder.createStructType(); + builder.addNameHintDecoration(structType, nameStruct.produceString().getUnownedSlice()); + + auto elementBufferKey = builder.createStructKey(); + builder.addNameHintDecoration(elementBufferKey, UnownedStringSlice("_data")); + auto elementBufferType = elementType; + auto _dataField = builder.createStructField(structType, elementBufferKey, elementBufferType); + + auto std430 = builder._createInst(sizeof(IRTypeLayoutRules), builder.getType(kIROp_Std430BufferLayoutType), kIROp_Std430BufferLayoutType); + IRGLSLShaderStorageBufferType* storageBuffer; + { + IRInst* ops[] = { structType, std430 }; + storageBuffer = builder.createGLSLShaderStorableBufferType(2, ops); + } + + instToSwitch->setFullType(storageBuffer); + + // All references to a atomic_uint need to be an element ref. to emulate storage buffer usage + // All function calls must be inlined since storage buffers cannot pass as parameters to atomic methods + for (auto& i : bindingToInstList.second) + { + int64_t currOffset = int64_t(i->findDecoration<IRGLSLOffsetDecoration>()->getOffset()->getValue()); + + // we need a next node to be stored since the following code + // changes IRUse* of the use->user node, meaning we will lose + // our IRUse list of the atomic_uint being swapped out + IRUse* next = nullptr; + for (auto use = i->firstUse; use; use = next) + { + next = use->nextUse; + auto user = use->user; + + switch (user->getOp()) + { + case kIROp_StructFieldLayoutAttr: + { + // Definitions do nothing if unused + break; + } + case kIROp_Call: + { + builder.setInsertBefore(user); + auto fieldAddress = builder.emitFieldAddress( + builder.getPtrType(_dataField->getFieldType()), + instToSwitch, + _dataField->getKey() + ); + auto elementAddr = builder.emitElementAddress( + builder.getPtrType(builder.getUIntType()), + fieldAddress, + builder.getIntValue(builder.getIntType(), currOffset/4)); + + user->setOperand(1, elementAddr); + auto funcTypeInst = (user->getOperand(0)); + auto funcType = funcTypeInst->getFullType(); + + auto paramReplacment = builder.getInOutType(builder.getUIntType()); + funcType->getOperand(1)->replaceUsesWith(paramReplacment); + builder.addForceInlineDecoration(funcTypeInst); + + break; + } + } + } + if (i->typeUse.usedValue->getOp() == kIROp_GLSLAtomicUintType) + { + i->removeAndDeallocate(); + } + } + } +} + +void GLSLReplaceAtomicUint(IRSpecContext* context, TargetProgram* targetProgram, IRModule* irModule) +{ + if (!targetProgram->getOptionSet().getBoolOption(CompilerOptionName::AllowGLSL)) return; + + Dictionary<int, List<IRInst*>> bindingToInstMapUnsorted; + for (auto inst : irModule->getGlobalInsts()) + { + if (inst->typeUse.usedValue) + { + switch (inst->typeUse.usedValue->getOp()) + { + case kIROp_GLSLAtomicUintType: + { + // atomic_uint are supported by GLSL->VK through converting to a different type (GL_EXT_vulkan_glsl_relaxed). + // atomic_uint are not supported by SPIR-V->VK; this means that to get SPIR-V to work we must convert the type ourselves + // to an equivlent representation (storage buffer); the added benifit is that then HLSL is possible to emit as a target as well + // since atomic_uint is not an HLSL concept, but storageBuffer->RWBuffer is and HLSL concept + auto layout = inst->findDecoration<IRLayoutDecoration>()->getLayout(); + auto layoutVal = as<IRVarOffsetAttr>(layout->getOperand(1)); + assert(layoutVal != nullptr); + bindingToInstMapUnsorted.getOrAddValue(uint32_t(layoutVal->getOffset()), List<IRInst*>()).add(inst); + break; + } + }; + } + } + + convertAtomicToStorageBuffer(context, bindingToInstMapUnsorted); +} + LinkedIR linkIR( CodeGenContext* codeGenContext) { @@ -1728,6 +1861,11 @@ LinkedIR linkIR( // definition. diagnoseUnresolvedSymbols(targetReq, codeGenContext->getSink(), state->irModule); + // type-use reformatter of GLSL types (only if compiler is set to AllowGLSL mode) + // which are not supported by SPIRV->Vulkan but is supported by GLSL->Vulkan through + // compiler magic tricks + GLSLReplaceAtomicUint(context, targetProgram, state->irModule); + // TODO: *technically* we should consider the case where // we have global variables with initializers, since // these should get run whether or not the entry point diff --git a/source/slang/slang-ir-spirv-legalize.cpp b/source/slang/slang-ir-spirv-legalize.cpp index 511c596a4..1feba361b 100644 --- a/source/slang/slang-ir-spirv-legalize.cpp +++ b/source/slang/slang-ir-spirv-legalize.cpp @@ -275,6 +275,7 @@ struct SPIRVLegalizationContext : public SourceEmitterBase switch (type->getOp()) { case kIROp_RaytracingAccelerationStructureType: + case kIROp_GLSLAtomicUintType: case kIROp_RayQueryType: return true; default: diff --git a/source/slang/slang-ir-util.cpp b/source/slang/slang-ir-util.cpp index 5b29d23a8..295fbd642 100644 --- a/source/slang/slang-ir-util.cpp +++ b/source/slang/slang-ir-util.cpp @@ -181,6 +181,7 @@ bool isValueType(IRInst* dataType) case kIROp_ArrayType: case kIROp_FuncType: case kIROp_RaytracingAccelerationStructureType: + case kIROp_GLSLAtomicUintType: return true; default: // Read-only resource handles are considered as Value type. @@ -507,6 +508,8 @@ void getTypeNameHint(StringBuilder& sb, IRInst* type) case kIROp_HLSLRasterizerOrderedByteAddressBufferType: sb << "RasterizerOrderedByteAddressBuffer"; break; + case kIROp_GLSLAtomicUintType: + sb << "AtomicCounter"; case kIROp_RaytracingAccelerationStructureType: sb << "RayTracingAccelerationStructure"; break; diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp index 696e862d6..fdc10e774 100644 --- a/source/slang/slang-ir.cpp +++ b/source/slang/slang-ir.cpp @@ -4495,6 +4495,18 @@ namespace Slang return ssboType; } + IRGLSLShaderStorageBufferType* IRBuilder::createGLSLShaderStorableBufferType(UInt operandCount, IRInst* const* operands) + { + IRGLSLShaderStorageBufferType* ssboType = createInst<IRGLSLShaderStorageBufferType>( + this, + kIROp_GLSLShaderStorageBufferType, + getTypeKind(), + operandCount, + operands); + addGlobalValue(this, ssboType); + return ssboType; + } + IRInterfaceType* IRBuilder::createInterfaceType(UInt operandCount, IRInst* const* operands) { IRInterfaceType* interfaceType = createInst<IRInterfaceType>( diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index 566e5a878..6e6ba6255 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -2183,6 +2183,11 @@ void addVarDecorations( builder->addDecoration(inst, kIROp_GLSLLocationDecoration, builder->getIntValue(builder->getIntType(), stringToInt(glslLocationMod->valToken.getContent()))); } + else if (auto glslOffsetMod = as<GLSLOffsetLayoutAttribute>(mod)) + { + builder->addDecoration(inst, kIROp_GLSLOffsetDecoration, + builder->getIntValue(builder->getIntType(), glslOffsetMod->offset)); + } else if (auto hlslSemantic = as< HLSLSimpleSemantic>(mod)) { builder->addSemanticDecoration(inst, hlslSemantic->name.getContent()); diff --git a/source/slang/slang-parameter-binding.cpp b/source/slang/slang-parameter-binding.cpp index 267f23e6c..ebaa58adb 100644 --- a/source/slang/slang-parameter-binding.cpp +++ b/source/slang/slang-parameter-binding.cpp @@ -934,6 +934,12 @@ static void addExplicitParameterBinding( if (overlappedVarLayout) { + //legal if atomicUint + if(parameterInfo->varLayout->varDecl.getDecl()->getType()->astNodeType == ASTNodeType::GLSLAtomicUintType + && overlappedVarLayout->varDecl.getDecl()->getType()->astNodeType == ASTNodeType::GLSLAtomicUintType) + { + return; + } auto paramA = parameterInfo->varLayout->varDecl.getDecl(); auto paramB = overlappedVarLayout->varDecl.getDecl(); diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp index 3315c786a..cc87a3daa 100644 --- a/source/slang/slang-parser.cpp +++ b/source/slang/slang-parser.cpp @@ -229,6 +229,29 @@ namespace Slang lastErrorLoc = loc; } } + + public: + void setBindingOffset(int binding, int64_t byteOffset) + { + bindingToByteOffset.set(binding, byteOffset); + } + int64_t getNextBindingOffset(int binding) + { + int64_t currentOffset; + if (bindingToByteOffset.addIfNotExists(binding, 0)) + currentOffset = 0; + else + currentOffset = bindingToByteOffset.getValue(binding) + sizeof(uint32_t); + + bindingToByteOffset.set( + binding, + currentOffset + sizeof(uint32_t) + ); + return currentOffset; + } + + private: + Dictionary<int, int64_t> bindingToByteOffset; }; // Forward Declarations @@ -4365,6 +4388,42 @@ namespace Slang return attrDecl; } + static void addSpecialGLSLModifiersBasedOnType( + Parser* parser, + Decl* decl, + Modifiers* modifiers) + { + auto varDeclBase = as<VarDeclBase>(decl); + if (!varDeclBase) return; + auto declRefExpr = as<DeclRefExpr>(varDeclBase->type.exp); + if (!declRefExpr) return; + auto bindingMod = modifiers->findModifier<GLSLBindingAttribute>(); + if (!bindingMod) return; + + // here is a problem; we link types into a literal in IR stage post parse + // but, order (top down) mattter when parsing atomic_uint offset + // more over, we can have patterns like: offset = 20, no offset [+4], offset = 16. + // Therefore we must parse all in order. The issue then is we will struggle to + // subsitute atomic_uint for storage buffers... + if (auto name = declRefExpr->name) + { + if (name->text.equals("atomic_uint")) + { + if (!modifiers->findModifier<GLSLOffsetLayoutAttribute>()) + { + const int64_t nextOffset = parser->getNextBindingOffset(bindingMod->binding); + GLSLOffsetLayoutAttribute* modifier = parser->astBuilder->create<GLSLOffsetLayoutAttribute>(); + modifier->keywordName = NULL; //no keyword name given + modifier->loc = bindingMod->loc; //has no location in file, set to parent binding + modifier->offset = nextOffset; + + Modifiers newModifier; + newModifier.first = modifier; + _addModifiers(decl, newModifier); + } + } + } + } // Finish up work on a declaration that was parsed static void CompleteDecl( Parser* parser, @@ -4403,6 +4462,10 @@ namespace Slang } else { + if (parser->options.allowGLSLInput) + { + addSpecialGLSLModifiersBasedOnType(parser, declToModify, &modifiers); + } _addModifiers(declToModify, modifiers); } @@ -7787,6 +7850,7 @@ namespace Slang CASE(std140, GLSLStd140Modifier) CASE(std430, GLSLStd430Modifier) CASE(scalar, GLSLScalarModifier) + CASE(offset, GLSLOffsetLayoutAttribute) CASE(location, GLSLLocationLayoutModifier) { modifier = parser->astBuilder->create<GLSLUnparsedLayoutModifier>(); @@ -7797,12 +7861,27 @@ namespace Slang modifier->keywordName = nameAndLoc.name; modifier->loc = nameAndLoc.loc; + // Special handling for GLSLLayoutModifier if (auto glslModifier = as<GLSLLayoutModifier>(modifier)) { + // not all GLSLLayoutModifier subtypes have an OpAssign after if (AdvanceIf(parser, TokenType::OpAssign)) - { glslModifier->valToken = parser->ReadToken(TokenType::IntegerLiteral); + } + //Special handling for GLSLOffsetLayoutAttribute to add to the byte offset tracker at a binding location + else if (auto glslOffset = as<GLSLOffsetLayoutAttribute>(modifier)) + { + if (auto binding = listBuilder.find<GLSLBindingAttribute>()) + { + // all GLSLOffsetLayoutAttribute have an OpAssign with value token + parser->ReadToken(TokenType::OpAssign); + glslOffset->offset = int64_t(getIntegerLiteralValue(parser->ReadToken(TokenType::IntegerLiteral))); + parser->setBindingOffset(binding->binding, glslOffset->offset); + } + else + { + parser->diagnose(modifier->loc, Diagnostics::missingLayoutBindingModifier); } } diff --git a/source/slang/slang-reflection-api.cpp b/source/slang/slang-reflection-api.cpp index d91dd5858..9b20e2933 100644 --- a/source/slang/slang-reflection-api.cpp +++ b/source/slang/slang-reflection-api.cpp @@ -1315,6 +1315,10 @@ namespace Slang return SLANG_BINDING_TYPE_MUTABLE_RAW_BUFFER; } } + else if (as<GLSLAtomicUintType>(type)) + { + return SLANG_BINDING_TYPE_MUTABLE_RAW_BUFFER; + } else if( as<GLSLShaderStorageBufferType>(type) ) { // TODO Immutable buffers diff --git a/source/slang/slang-type-layout.cpp b/source/slang/slang-type-layout.cpp index 3f49d62d2..f7efd4ee4 100644 --- a/source/slang/slang-type-layout.cpp +++ b/source/slang/slang-type-layout.cpp @@ -3858,6 +3858,14 @@ static TypeLayoutResult _createTypeLayout( type, rules); } + else if (auto atomicType = as<GLSLAtomicUintType>(type)) + { + ShaderParameterKind kind = ShaderParameterKind::AtomicUint; + return createSimpleTypeLayout( + rules->GetObjectLayout(kind, context.objectLayoutOptions), + type, + rules); + } // TODO: need a better way to handle this stuff... #define CASE(TYPE, KIND) \ diff --git a/source/slang/slang-type-layout.h b/source/slang/slang-type-layout.h index c17f2ebb4..a12c310b9 100644 --- a/source/slang/slang-type-layout.h +++ b/source/slang/slang-type-layout.h @@ -944,6 +944,8 @@ enum class ShaderParameterKind RegisterSpace, AppendConsumeStructuredBuffer, + + AtomicUint, }; struct SimpleLayoutRulesImpl |
