diff options
| author | Tim Foley <tfoleyNV@users.noreply.github.com> | 2021-03-03 11:45:39 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-03-03 11:45:39 -0800 |
| commit | 13ff0bd345990c0fdfb7b52ebd5339cddb04889e (patch) | |
| tree | ae3773d4f3475439a55603007b3908e31c6c31fb | |
| parent | d6ae67160c33460bf952c9959077dc481a16eca2 (diff) | |
Add GLSL/SPIR-V support got GetAttributeAtVertex (#1733)
This change allows varying fragment shader inputs to be declared in a way that allows the `GetAttributeAtVertex` operation to compile to valid code for both D3D and GLSL/SPIR-V/Vulkan.
The key is that rather than just use ordinary `nointerpolation`-qualified inputs the code must declare these varying inputs with a new `pervertex` qualifier that marks them as *only* being usable with `GetAttributeAtVertex`. The `pervertex`-tagged inputs then translate to GLSL inputs using the `pervertexNV` qualifier
Note that this change does *not* include any enforcement of the requirements around how these qualifiers are used (and the compiler doesn't have enforcement for the existing operations like `EvaluateAttributeAtCentroid`). The underlying problem is that the inerpolation-mode qualifiers and explicit interpolation functions in HLSL constitute a kind of rate-qualified type system, but without any systematic rules. It seems wasteful to encode a bunch of ad hoc rules for this stuff as special cases in the compiler when the clear right answer is to implement a systematic approach to rates.
| -rw-r--r-- | source/slang/core.meta.slang | 10 | ||||
| -rw-r--r-- | source/slang/hlsl.meta.slang | 12 | ||||
| -rw-r--r-- | source/slang/slang-ast-modifier.h | 6 | ||||
| -rw-r--r-- | source/slang/slang-emit-c-like.cpp | 11 | ||||
| -rw-r--r-- | source/slang/slang-emit-c-like.h | 11 | ||||
| -rw-r--r-- | source/slang/slang-emit-glsl.cpp | 89 | ||||
| -rw-r--r-- | source/slang/slang-emit-glsl.h | 2 | ||||
| -rw-r--r-- | source/slang/slang-emit-hlsl.cpp | 1 | ||||
| -rw-r--r-- | source/slang/slang-ir-glsl-legalize.cpp | 48 | ||||
| -rw-r--r-- | source/slang/slang-ir-insts.h | 2 | ||||
| -rw-r--r-- | source/slang/slang-lower-to-ir.cpp | 4 | ||||
| -rw-r--r-- | tests/pipeline/rasterization/get-attribute-at-vertex.slang | 3 | ||||
| -rw-r--r-- | tests/pipeline/rasterization/get-attribute-at-vertex.slang.glsl | 25 |
13 files changed, 205 insertions, 19 deletions
diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang index de78229a9..39ee702c6 100644 --- a/source/slang/core.meta.slang +++ b/source/slang/core.meta.slang @@ -17,6 +17,16 @@ syntax constexpr : ConstExprModifier; // visible at the global-memory scope syntax globallycoherent : GloballyCoherentModifier; +/// Modifier to disable inteprolation and force per-vertex passing of a varying attribute. +/// +/// When a varying attribute passed to the fragment shader is marked `pervertex`, it will +/// not be interpolated during rasterization (similar to `nointerpolate` attributes). +/// Unlike a plain `nointerpolate` attribute, this modifier indicates that the attribute +/// should *only* be acccessed through the `GetAttributeAtVertex()` operation, so access its +/// distinct per-vertex values. +/// +syntax pervertex : PerVertexModifier; + // A type that can be used as an operand for builtins [sealed] [builtin] diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang index adbe6f9c8..77b371662 100644 --- a/source/slang/hlsl.meta.slang +++ b/source/slang/hlsl.meta.slang @@ -1821,6 +1821,10 @@ matrix<T, N, M> fwidth(matrix<T, N, M> x) /// __generic<T : __BuiltinType> [__readNone] +__target_intrinsic(hlsl) +__target_intrinsic(glsl, "$0[$1]") +__glsl_version(450) +__glsl_extension(GL_NV_fragment_shader_barycentric) T GetAttributeAtVertex(T attribute, uint vertexIndex); /// Get the value of a vertex attribute at a specific vertex. @@ -1839,6 +1843,10 @@ T GetAttributeAtVertex(T attribute, uint vertexIndex); /// __generic<T : __BuiltinType, let N : int> [__readNone] +__target_intrinsic(hlsl) +__target_intrinsic(glsl, "$0[$1]") +__glsl_version(450) +__glsl_extension(GL_NV_fragment_shader_barycentric) vector<T,N> GetAttributeAtVertex(vector<T,N> attribute, uint vertexIndex); /// Get the value of a vertex attribute at a specific vertex. @@ -1857,6 +1865,10 @@ vector<T,N> GetAttributeAtVertex(vector<T,N> attribute, uint vertexIndex); /// __generic<T : __BuiltinType, let N : int, let M : int> [__readNone] +__target_intrinsic(hlsl) +__target_intrinsic(glsl, "$0[$1]") +__glsl_version(450) +__glsl_extension(GL_NV_fragment_shader_barycentric) matrix<T,N,M> GetAttributeAtVertex(matrix<T,N,M> attribute, uint vertexIndex); diff --git a/source/slang/slang-ast-modifier.h b/source/slang/slang-ast-modifier.h index 10560dc84..467a9e7e7 100644 --- a/source/slang/slang-ast-modifier.h +++ b/source/slang/slang-ast-modifier.h @@ -482,6 +482,12 @@ class HLSLCentroidModifier : public InterpolationModeModifier SLANG_AST_CLASS(HLSLCentroidModifier) }; + /// Slang-defined `pervertex` modifier +class PerVertexModifier : public InterpolationModeModifier +{ + SLANG_AST_CLASS(PerVertexModifier) +}; + // HLSL `precise` modifier class PreciseModifier : public Modifier diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp index 522e5ef12..a0fa71fee 100644 --- a/source/slang/slang-emit-c-like.cpp +++ b/source/slang/slang-emit-c-like.cpp @@ -158,6 +158,17 @@ void CLikeSourceEmitter::emitDeclarator(DeclaratorInfo* declarator) } break; + case DeclaratorInfo::Flavor::LiteralSizedArray: + { + auto arrayDeclarator = (LiteralSizedArrayDeclaratorInfo*)declarator; + emitDeclarator(arrayDeclarator->next); + m_writer->emit("["); + m_writer->emit(arrayDeclarator->elementCount); + m_writer->emit("]"); + } + break; + + default: SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "unknown declarator flavor"); break; diff --git a/source/slang/slang-emit-c-like.h b/source/slang/slang-emit-c-like.h index 1a813b1e5..16a6d303f 100644 --- a/source/slang/slang-emit-c-like.h +++ b/source/slang/slang-emit-c-like.h @@ -75,6 +75,7 @@ public: Ptr, SizedArray, UnsizedArray, + LiteralSizedArray, }; Flavor flavor; @@ -130,6 +131,16 @@ public: {} }; + struct LiteralSizedArrayDeclaratorInfo : ChainedDeclaratorInfo + { + IRIntegerValue elementCount; + + LiteralSizedArrayDeclaratorInfo(DeclaratorInfo* next, IRIntegerValue elementCount) + : ChainedDeclaratorInfo(Flavor::LiteralSizedArray, next) + , elementCount(elementCount) + {} + }; + struct ComputeEmitActionsContext; // An action to be performed during code emit. diff --git a/source/slang/slang-emit-glsl.cpp b/source/slang/slang-emit-glsl.cpp index 53da7da06..7b784de8d 100644 --- a/source/slang/slang-emit-glsl.cpp +++ b/source/slang/slang-emit-glsl.cpp @@ -916,6 +916,45 @@ void GLSLSourceEmitter::emitEntryPointAttributesImpl(IRFunc* irFunc, IREntryPoin } } +void GLSLSourceEmitter::_emitGLSLPerVertexVaryingFragmentInput(IRGlobalParam* param, IRType* type) +{ + // Note: The logic here is almost identical to the default + // emit logic for global shader parameters. The main difference + // is that we emit a parameter of type `X` as an array of + // type `X[3]` to account for the per-vertex-ness of the + // parameter. + // + + // Need to emit appropriate modifiers here. + + // We expect/require all shader parameters to + // have some kind of layout information associated with them. + // + auto layout = getVarLayout(param); + SLANG_ASSERT(layout); + + emitVarModifiers(layout, param, type); + + emitRateQualifiers(param); + + auto name = getName(param); + StringSliceLoc nameAndLoc(name.getUnownedSlice()); + NameDeclaratorInfo nameDeclarator(&nameAndLoc); + + LiteralSizedArrayDeclaratorInfo arrayDeclarator(&nameDeclarator, 3); + + // Note: We are invoking `_emitType` here directly because there + // is no overload of `emitType` that works with a declarator. + // + _emitType(type, &arrayDeclarator); + + emitSemantics(param); + + emitLayoutSemantics(param); + + m_writer->emit(";\n\n"); +} + bool GLSLSourceEmitter::tryEmitGlobalParamImpl(IRGlobalParam* varDecl, IRType* varType) { // There are a number of types that are (or can be) @@ -995,6 +1034,21 @@ bool GLSLSourceEmitter::tryEmitGlobalParamImpl(IRGlobalParam* varDecl, IRType* v } } + // A varying fragment input parameter with the `pervertex` modifier + // needs to be emitted as an array. + // + if( auto interpolationModeDecor = varDecl->findDecoration<IRInterpolationModeDecoration>() ) + { + if( interpolationModeDecor->getMode() == IRInterpolationMode::PerVertex ) + { + if( m_entryPointStage == Stage::Fragment ) + { + _emitGLSLPerVertexVaryingFragmentInput(varDecl, varType); + return true; + } + } + } + // Do the default thing return false; } @@ -1789,7 +1843,7 @@ void GLSLSourceEmitter::emitRateQualifiersImpl(IRRate* rate) } } -static UnownedStringSlice _getInterpolationModifierText(IRInterpolationMode mode) +static UnownedStringSlice _getInterpolationModifierText(IRInterpolationMode mode, Stage stage, bool isInput) { switch (mode) { @@ -1798,6 +1852,17 @@ static UnownedStringSlice _getInterpolationModifierText(IRInterpolationMode mode case IRInterpolationMode::Linear: return UnownedStringSlice::fromLiteral("smooth"); case IRInterpolationMode::Sample: return UnownedStringSlice::fromLiteral("sample"); case IRInterpolationMode::Centroid: return UnownedStringSlice::fromLiteral("centroid"); + + case IRInterpolationMode::PerVertex: + if( stage == Stage::Fragment ) + { + if( isInput ) + { + return UnownedStringSlice::fromLiteral("pervertexNV"); + } + } + return UnownedStringSlice::fromLiteral("flat"); + default: return UnownedStringSlice(); } } @@ -1806,13 +1871,16 @@ void GLSLSourceEmitter::emitInterpolationModifiersImpl(IRInst* varInst, IRType* { bool anyModifiers = false; + auto stage = layout->getStage(); + auto isInput = layout->findOffsetAttr(LayoutResourceKind::VaryingInput) != nullptr; + for (auto dd : varInst->getDecorations()) { if (dd->getOp() != kIROp_InterpolationModeDecoration) continue; auto decoration = (IRInterpolationModeDecoration*)dd; - const UnownedStringSlice slice = _getInterpolationModifierText(decoration->getMode()); + const UnownedStringSlice slice = _getInterpolationModifierText(decoration->getMode(), stage, isInput); if (slice.getLength()) { @@ -1820,6 +1888,23 @@ void GLSLSourceEmitter::emitInterpolationModifiersImpl(IRInst* varInst, IRType* m_writer->emitChar(' '); anyModifiers = true; } + + switch( decoration->getMode() ) + { + default: + break; + + case IRInterpolationMode::PerVertex: + if( stage == Stage::Fragment ) + { + if( isInput ) + { + _requireGLSLVersion(ProfileVersion::GLSL_450); + _requireGLSLExtension(UnownedStringSlice::fromLiteral("GL_NV_fragment_shader_barycentric")); + } + } + break; + } } // If the user didn't explicitly qualify a varying diff --git a/source/slang/slang-emit-glsl.h b/source/slang/slang-emit-glsl.h index 56da1b064..48ed95a53 100644 --- a/source/slang/slang-emit-glsl.h +++ b/source/slang/slang-emit-glsl.h @@ -58,6 +58,8 @@ protected: void _emitGLSLByteAddressBuffer(IRGlobalParam* varDecl, IRByteAddressBufferTypeBase* byteAddressBufferType); void _emitGLSLParameterGroup(IRGlobalParam* varDecl, IRUniformParameterGroupType* type); + void _emitGLSLPerVertexVaryingFragmentInput(IRGlobalParam* param, IRType* type); + void _emitGLSLImageFormatModifier(IRInst* var, IRTextureType* resourceType); void _emitGLSLLayoutQualifiers(IRVarLayout* layout, EmitVarChain* inChain, LayoutResourceKind filter = LayoutResourceKind::None); diff --git a/source/slang/slang-emit-hlsl.cpp b/source/slang/slang-emit-hlsl.cpp index c25d94e47..3921cbbac 100644 --- a/source/slang/slang-emit-hlsl.cpp +++ b/source/slang/slang-emit-hlsl.cpp @@ -985,6 +985,7 @@ static UnownedStringSlice _getInterpolationModifierText(IRInterpolationMode mode { switch (mode) { + case IRInterpolationMode::PerVertex: case IRInterpolationMode::NoInterpolation: return UnownedStringSlice::fromLiteral("nointerpolation"); case IRInterpolationMode::NoPerspective: return UnownedStringSlice::fromLiteral("noperspective"); case IRInterpolationMode::Linear: return UnownedStringSlice::fromLiteral("linear"); diff --git a/source/slang/slang-ir-glsl-legalize.cpp b/source/slang/slang-ir-glsl-legalize.cpp index 48aafc887..ae6521076 100644 --- a/source/slang/slang-ir-glsl-legalize.cpp +++ b/source/slang/slang-ir-glsl-legalize.cpp @@ -609,7 +609,8 @@ ScalarizedVal createSimpleGLSLGlobalVarying( Stage stage, UInt bindingIndex, UInt bindingSpace, - GlobalVaryingDeclarator* declarator) + GlobalVaryingDeclarator* declarator, + IRInst* leafVar) { // Check if we have a system value on our hands. GLSLSystemValueInfo systemValueInfoStorage; @@ -685,6 +686,14 @@ ScalarizedVal createSimpleGLSLGlobalVarying( auto globalParam = addGlobalParam(builder->getModule(), paramType); moveValueBefore(globalParam, builder->getFunc()); + if( leafVar ) + { + if( auto interpolationModeDecor = leafVar->findDecoration<IRInterpolationModeDecoration>() ) + { + builder->addInterpolationModeDecoration(globalParam, interpolationModeDecor->getMode()); + } + } + ScalarizedVal val = isOutput ? ScalarizedVal::address(globalParam) : ScalarizedVal::value(globalParam); if( systemValueInfo ) @@ -729,7 +738,8 @@ ScalarizedVal createGLSLGlobalVaryingsImpl( Stage stage, UInt bindingIndex, UInt bindingSpace, - GlobalVaryingDeclarator* declarator) + GlobalVaryingDeclarator* declarator, + IRInst* leafVar) { if (as<IRVoidType>(type)) { @@ -739,20 +749,20 @@ ScalarizedVal createGLSLGlobalVaryingsImpl( { return createSimpleGLSLGlobalVarying( context, - builder, type, varLayout, typeLayout, kind, stage, bindingIndex, bindingSpace, declarator); + builder, type, varLayout, typeLayout, kind, stage, bindingIndex, bindingSpace, declarator, leafVar); } else if( as<IRVectorType>(type) ) { return createSimpleGLSLGlobalVarying( context, - builder, type, varLayout, typeLayout, kind, stage, bindingIndex, bindingSpace, declarator); + builder, type, varLayout, typeLayout, kind, stage, bindingIndex, bindingSpace, declarator, leafVar); } else if( as<IRMatrixType>(type) ) { // TODO: a matrix-type varying should probably be handled like an array of rows return createSimpleGLSLGlobalVarying( context, - builder, type, varLayout, typeLayout, kind, stage, bindingIndex, bindingSpace, declarator); + builder, type, varLayout, typeLayout, kind, stage, bindingIndex, bindingSpace, declarator, leafVar); } else if( auto arrayType = as<IRArrayType>(type) ) { @@ -779,7 +789,8 @@ ScalarizedVal createGLSLGlobalVaryingsImpl( stage, bindingIndex, bindingSpace, - &arrayDeclarator); + &arrayDeclarator, + leafVar); } else if( auto streamType = as<IRHLSLStreamOutputType>(type)) { @@ -798,7 +809,8 @@ ScalarizedVal createGLSLGlobalVaryingsImpl( stage, bindingIndex, bindingSpace, - declarator); + declarator, + leafVar); } else if(auto structType = as<IRStructType>(type)) { @@ -849,7 +861,8 @@ ScalarizedVal createGLSLGlobalVaryingsImpl( stage, fieldBindingIndex, fieldBindingSpace, - declarator); + declarator, + field->getKey()); if (fieldVal.flavor != ScalarizedVal::Flavor::none) { ScalarizedTupleValImpl::Element element; @@ -866,7 +879,7 @@ ScalarizedVal createGLSLGlobalVaryingsImpl( // Default case is to fall back on the simple behavior return createSimpleGLSLGlobalVarying( context, - builder, type, varLayout, typeLayout, kind, stage, bindingIndex, bindingSpace, declarator); + builder, type, varLayout, typeLayout, kind, stage, bindingIndex, bindingSpace, declarator, leafVar); } ScalarizedVal createGLSLGlobalVaryings( @@ -875,7 +888,8 @@ ScalarizedVal createGLSLGlobalVaryings( IRType* type, IRVarLayout* layout, LayoutResourceKind kind, - Stage stage) + Stage stage, + IRInst* leafVar) { UInt bindingIndex = 0; UInt bindingSpace = 0; @@ -886,7 +900,7 @@ ScalarizedVal createGLSLGlobalVaryings( } return createGLSLGlobalVaryingsImpl( context, - builder, type, layout, layout->getTypeLayout(), kind, stage, bindingIndex, bindingSpace, nullptr); + builder, type, layout, layout->getTypeLayout(), kind, stage, bindingIndex, bindingSpace, nullptr, leafVar); } ScalarizedVal extractField( @@ -1377,7 +1391,8 @@ void legalizeEntryPointParameterForGLSL( valueType, paramLayout, LayoutResourceKind::VaryingOutput, - stage); + stage, + pp); // TODO: a GS output stream might be passed into other // functions, so that we should really be modifying @@ -1533,7 +1548,7 @@ void legalizeEntryPointParameterForGLSL( // side and one for the `out` side. auto globalInputVal = createGLSLGlobalVaryings( context, - builder, valueType, paramLayout, LayoutResourceKind::VaryingInput, stage); + builder, valueType, paramLayout, LayoutResourceKind::VaryingInput, stage, pp); assign(builder, localVal, globalInputVal); } @@ -1548,7 +1563,7 @@ void legalizeEntryPointParameterForGLSL( // when the function is done. We create them here. auto globalOutputVal = createGLSLGlobalVaryings( context, - builder, valueType, paramLayout, LayoutResourceKind::VaryingOutput, stage); + builder, valueType, paramLayout, LayoutResourceKind::VaryingOutput, stage, pp); // Now we need to iterate over all the blocks in the function looking // for any `return*` instructions, so that we can write to the output variable @@ -1591,7 +1606,7 @@ void legalizeEntryPointParameterForGLSL( auto globalValue = createGLSLGlobalVaryings( context, - builder, paramType, paramLayout, LayoutResourceKind::VaryingInput, stage); + builder, paramType, paramLayout, LayoutResourceKind::VaryingInput, stage, pp); // Next we need to replace uses of the parameter with // references to the variable(s). We are going to do that @@ -1693,7 +1708,8 @@ void legalizeEntryPointForGLSL( resultType, entryPointLayout->getResultLayout(), LayoutResourceKind::VaryingOutput, - stage); + stage, + func); for( auto bb = func->getFirstBlock(); bb; bb = bb->getNextBlock() ) { diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h index 334815d54..904b9955e 100644 --- a/source/slang/slang-ir-insts.h +++ b/source/slang/slang-ir-insts.h @@ -116,6 +116,8 @@ enum class IRInterpolationMode Centroid, Sample, + + PerVertex, }; struct IRInterpolationModeDecoration : IRDecoration diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index ef4cb98ec..fb9fc70fd 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -1820,6 +1820,10 @@ void addVarDecorations( { builder->addInterpolationModeDecoration(inst, IRInterpolationMode::NoInterpolation); } + else if(as<PerVertexModifier>(mod)) + { + builder->addInterpolationModeDecoration(inst, IRInterpolationMode::PerVertex); + } else if(as<HLSLNoPerspectiveModifier>(mod)) { builder->addInterpolationModeDecoration(inst, IRInterpolationMode::NoPerspective); diff --git a/tests/pipeline/rasterization/get-attribute-at-vertex.slang b/tests/pipeline/rasterization/get-attribute-at-vertex.slang index 56fbcce78..87d39c806 100644 --- a/tests/pipeline/rasterization/get-attribute-at-vertex.slang +++ b/tests/pipeline/rasterization/get-attribute-at-vertex.slang @@ -3,10 +3,11 @@ // Basic test for `GetAttributeAtVertex` function //TEST:CROSS_COMPILE:-target dxil -entry main -stage fragment -profile sm_6_1 +//TEST:CROSS_COMPILE:-target spirv -entry main -stage fragment -profile glsl_450 [shader("fragment")] void main( - nointerpolation float4 color : COLOR, + pervertex float4 color : COLOR, float3 bary : SV_Barycentrics, out float4 result : SV_Target) { diff --git a/tests/pipeline/rasterization/get-attribute-at-vertex.slang.glsl b/tests/pipeline/rasterization/get-attribute-at-vertex.slang.glsl new file mode 100644 index 000000000..c07e9b61c --- /dev/null +++ b/tests/pipeline/rasterization/get-attribute-at-vertex.slang.glsl @@ -0,0 +1,25 @@ +// get-attribute-at-vertex.slang.glsl +//TEST_IGNORE_FILE: + +#version 450 + +#extension GL_NV_fragment_shader_barycentric : require + +pervertexNV layout(location = 0) +in vec4 _S1[3]; + +layout(location = 0) +out vec4 _S2; + +void main() +{ + vec4 _S3; + + _S3 = gl_BaryCoordNV.x * _S1[0] + + gl_BaryCoordNV.y * _S1[1] + + gl_BaryCoordNV.z * _S1[2]; + + _S2 = _S3; + + return; +} |
