summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2021-03-03 11:45:39 -0800
committerGitHub <noreply@github.com>2021-03-03 11:45:39 -0800
commit13ff0bd345990c0fdfb7b52ebd5339cddb04889e (patch)
treeae3773d4f3475439a55603007b3908e31c6c31fb
parentd6ae67160c33460bf952c9959077dc481a16eca2 (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.slang10
-rw-r--r--source/slang/hlsl.meta.slang12
-rw-r--r--source/slang/slang-ast-modifier.h6
-rw-r--r--source/slang/slang-emit-c-like.cpp11
-rw-r--r--source/slang/slang-emit-c-like.h11
-rw-r--r--source/slang/slang-emit-glsl.cpp89
-rw-r--r--source/slang/slang-emit-glsl.h2
-rw-r--r--source/slang/slang-emit-hlsl.cpp1
-rw-r--r--source/slang/slang-ir-glsl-legalize.cpp48
-rw-r--r--source/slang/slang-ir-insts.h2
-rw-r--r--source/slang/slang-lower-to-ir.cpp4
-rw-r--r--tests/pipeline/rasterization/get-attribute-at-vertex.slang3
-rw-r--r--tests/pipeline/rasterization/get-attribute-at-vertex.slang.glsl25
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;
+}