summaryrefslogtreecommitdiffstats
path: root/source/slang/ir.cpp
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2018-02-16 12:00:20 -0800
committerGitHub <noreply@github.com>2018-02-16 12:00:20 -0800
commit8c872597540b2287bc18d5723c7ae3b8832246b8 (patch)
treea40698aad0732e6078bf81229df6b1a54b1fd388 /source/slang/ir.cpp
parent1b93da040ef00836438437e998c1c9584e3fd4ac (diff)
Implement IR-level translation of system values for GLSL (#413)
The approach here isn't ideal. We already have a pass that transforms HLSL varying input/output types into GLSL global variables. This change makes it so that when those inputs/outputs have system-value semantics, we generate a global variable declaration with the appropriate `gl_*` name (leaving the type the same for now). Later, when emitting code, we just skip emitting declarations for declarations with mangled names that start with `gl_*`. A more complete implementation will be needed later on, which handles cases where the translation requires types to be changed (so that conversion code needs to be inserted).
Diffstat (limited to 'source/slang/ir.cpp')
-rw-r--r--source/slang/ir.cpp335
1 files changed, 307 insertions, 28 deletions
diff --git a/source/slang/ir.cpp b/source/slang/ir.cpp
index 3d04e9807..441db09a6 100644
--- a/source/slang/ir.cpp
+++ b/source/slang/ir.cpp
@@ -2871,7 +2871,242 @@ namespace Slang
GlobalVaryingDeclarator* next;
};
+ struct GLSLSystemValueInfo
+ {
+ char const* name;
+ };
+
+ void requireGLSLVersionImpl(
+ ExtensionUsageTracker* tracker,
+ ProfileVersion version);
+
+ void requireGLSLExtension(
+ ExtensionUsageTracker* tracker,
+ String const& name);
+
+ struct GLSLLegalizationContext
+ {
+ ExtensionUsageTracker* extensionUsageTracker;
+ DiagnosticSink* sink;
+ Stage stage;
+
+ void requireGLSLExtension(String const& name)
+ {
+ Slang::requireGLSLExtension(extensionUsageTracker, name);
+ }
+
+ void requireGLSLVersion(ProfileVersion version)
+ {
+ Slang::requireGLSLVersionImpl(extensionUsageTracker, version);
+ }
+
+ Stage getStage()
+ {
+ return stage;
+ }
+
+ DiagnosticSink* getSink()
+ {
+ return sink;
+ }
+ };
+
+ GLSLSystemValueInfo* getGLSLSystemValueInfo(
+ GLSLLegalizationContext* context,
+ VarLayout* varLayout,
+ LayoutResourceKind kind,
+ GLSLSystemValueInfo* inStorage)
+ {
+ char const* name = nullptr;
+
+ auto semanticNameSpelling = varLayout->systemValueSemantic;
+ if(semanticNameSpelling.Length() == 0)
+ return nullptr;
+
+ auto semanticName = semanticNameSpelling.ToLower();
+
+ if(semanticName == "sv_position")
+ {
+ // TODO: need to pick between `gl_Position` and
+ // `gl_FragCoord` based on whether this is an input
+ // or an output.
+ if( kind == LayoutResourceKind::VaryingOutput )
+ {
+ name = "gl_Position";
+ }
+ else
+ {
+ name = "gl_FragCoord";
+ }
+ }
+ else if(semanticName == "sv_target")
+ {
+ // Note: we do *not* need to generate some kind of `gl_`
+ // builtin for fragment-shader outputs: they are just
+ // ordinary `out` variables, with ordinary `location`s,
+ // as far as GLSL is concerned.
+ return nullptr;
+ }
+ else if(semanticName == "sv_clipdistance")
+ {
+ // TODO: type conversion is required here.
+ name = "gl_ClipDistance";
+ }
+ else if(semanticName == "sv_culldistance")
+ {
+ context->requireGLSLExtension("ARB_cull_distance");
+
+ // TODO: type conversion is required here.
+ name = "gl_CullDistance";
+ }
+ else if(semanticName == "sv_coverage")
+ {
+ // TODO: deal with `gl_SampleMaskIn` when used as an input.
+
+ // TODO: type conversion is required here.
+ name = "gl_SampleMask";
+ }
+ else if(semanticName == "sv_depth")
+ {
+ name = "gl_FragDepth";
+ }
+ else if(semanticName == "sv_depthgreaterequal")
+ {
+ // TODO: layout(depth_greater) out float gl_FragDepth;
+ name = "gl_FragDepth";
+ }
+ else if(semanticName == "sv_depthlessequal")
+ {
+ // TODO: layout(depth_greater) out float gl_FragDepth;
+ name = "gl_FragDepth";
+ }
+ else if(semanticName == "sv_dispatchthreadid")
+ {
+ name = "gl_GlobalInvocationID";
+ }
+ else if(semanticName == "sv_domainlocation")
+ {
+ name = "gl_TessCoord";
+ }
+ else if(semanticName == "sv_groupid")
+ {
+ name = "gl_WorkGroupID";
+ }
+ else if(semanticName == "sv_groupindex")
+ {
+ name = "gl_LocalInvocationIndex";
+ }
+ else if(semanticName == "sv_groupthreadid")
+ {
+ name = "gl_LocalInvocationID";
+ }
+ else if(semanticName == "sv_gsinstanceid")
+ {
+ name = "gl_InvocationID";
+ }
+ else if(semanticName == "sv_instanceid")
+ {
+ name = "gl_InstanceIndex";
+ }
+ else if(semanticName == "sv_isfrontface")
+ {
+ name = "gl_FrontFacing";
+ }
+ else if(semanticName == "sv_outputcontrolpointid")
+ {
+ name = "gl_InvocationID";
+ }
+ else if(semanticName == "sv_primitiveid")
+ {
+ name = "gl_PrimitiveID";
+ }
+ else if (semanticName == "sv_rendertargetarrayindex")
+ {
+ switch (context->getStage())
+ {
+ case Stage::Geometry:
+ context->requireGLSLVersion(ProfileVersion::GLSL_150);
+ break;
+
+ case Stage::Fragment:
+ context->requireGLSLVersion(ProfileVersion::GLSL_430);
+ break;
+
+ default:
+ context->requireGLSLVersion(ProfileVersion::GLSL_450);
+ context->requireGLSLExtension("GL_ARB_shader_viewport_layer_array");
+ break;
+ }
+
+ name = "gl_Layer";
+ }
+ else if (semanticName == "sv_sampleindex")
+ {
+ name = "gl_SampleID";
+ }
+ else if (semanticName == "sv_stencilref")
+ {
+ context->requireGLSLExtension("ARB_shader_stencil_export");
+ name = "gl_FragStencilRef";
+ }
+ else if (semanticName == "sv_tessfactor")
+ {
+ name = "gl_TessLevelOuter";
+ }
+ else if (semanticName == "sv_vertexid")
+ {
+ name = "gl_VertexIndex";
+ }
+ else if (semanticName == "sv_viewportarrayindex")
+ {
+ name = "gl_ViewportIndex";
+ }
+ else if (semanticName == "nv_x_right")
+ {
+ context->requireGLSLVersion(ProfileVersion::GLSL_450);
+ context->requireGLSLExtension("GL_NVX_multiview_per_view_attributes");
+
+ // The actual output in GLSL is:
+ //
+ // vec4 gl_PositionPerViewNV[];
+ //
+ // and is meant to support an arbitrary number of views,
+ // while the HLSL case just defines a second position
+ // output.
+ //
+ // For now we will hack this by:
+ // 1. Mapping an `NV_X_Right` output to `gl_PositionPerViewNV[1]`
+ // (that is, just one element of the output array)
+ // 2. Adding logic to copy the traditional `gl_Position` output
+ // over to `gl_PositionPerViewNV[0]`
+ //
+
+ name = "gl_PositionPerViewNV[1]";
+
+// shared->requiresCopyGLPositionToPositionPerView = true;
+ }
+ else if (semanticName == "nv_viewport_mask")
+ {
+ context->requireGLSLVersion(ProfileVersion::GLSL_450);
+ context->requireGLSLExtension("GL_NVX_multiview_per_view_attributes");
+
+ name = "gl_ViewportMaskPerViewNV";
+// globalVarExpr = createGLSLBuiltinRef("gl_ViewportMaskPerViewNV",
+// getUnsizedArrayType(getIntType()));
+ }
+
+ if( name )
+ {
+ inStorage->name = name;
+ return inStorage;
+ }
+
+ context->getSink()->diagnose(varLayout->varDecl.getDecl()->loc, Diagnostics::unknownSystemValueSemantic, semanticNameSpelling);
+ return nullptr;
+ }
+
ScalarizedVal createSimpleGLSLGlobalVarying(
+ GLSLLegalizationContext* context,
IRBuilder* builder,
Type* inType,
VarLayout* inVarLayout,
@@ -2880,13 +3115,18 @@ namespace Slang
UInt bindingIndex,
GlobalVaryingDeclarator* declarator)
{
- // TODO: detect when the layout represents a system input/output
- if( inVarLayout->systemValueSemantic.Length() != 0 )
- {
- // This variable represents a system input/output,
- // and we should probably handle that differently, right?
- }
+ // Check if we have a system value on our hands.
+ GLSLSystemValueInfo systemValueInfoStorage;
+ auto systemValueInfo = getGLSLSystemValueInfo(
+ context,
+ inVarLayout,
+ kind,
+ &systemValueInfoStorage);
+ // Construct the actual type and type-layout for the global variable
+ //
+ // TODO: in the case of a system value, we may need to override the type
+ //
RefPtr<Type> type = inType;
RefPtr<TypeLayout> typeLayout = inTypeLayout;
for( auto dd = declarator; dd; dd = dd->next )
@@ -2919,13 +3159,6 @@ namespace Slang
typeLayout = arrayTypeLayout;
}
- // Simple case: just create a global variable of the matching type,
- // and then use the value of the global as a replacement for the
- // value of the original parameter.
- //
- auto globalVariable = addGlobalVariable(builder->getModule(), type);
- moveValueBefore(globalVariable, builder->getFunc());
-
// We need to construct a fresh layout for the variable, even
// if the original had its own layout, because it might be
// an `inout` parameter, and we only want to deal with the case
@@ -2941,12 +3174,25 @@ namespace Slang
varLayout->stage = inVarLayout->stage;
varLayout->AddResourceInfo(kind)->index = bindingIndex;
+ // Simple case: just create a global variable of the matching type,
+ // and then use the value of the global as a replacement for the
+ // value of the original parameter.
+ //
+ auto globalVariable = addGlobalVariable(builder->getModule(), type);
+ moveValueBefore(globalVariable, builder->getFunc());
+
+ if( systemValueInfo )
+ {
+ globalVariable->mangledName = systemValueInfo->name;
+ }
+
builder->addLayoutDecoration(globalVariable, varLayout);
return ScalarizedVal::address(globalVariable);
}
ScalarizedVal createGLSLGlobalVaryingsImpl(
+ GLSLLegalizationContext* context,
IRBuilder* builder,
Type* type,
VarLayout* varLayout,
@@ -2957,16 +3203,22 @@ namespace Slang
{
if( type->As<BasicExpressionType>() )
{
- return createSimpleGLSLGlobalVarying(builder, type, varLayout, typeLayout, kind, bindingIndex, declarator);
+ return createSimpleGLSLGlobalVarying(
+ context,
+ builder, type, varLayout, typeLayout, kind, bindingIndex, declarator);
}
else if( type->As<VectorExpressionType>() )
{
- return createSimpleGLSLGlobalVarying(builder, type, varLayout, typeLayout, kind, bindingIndex, declarator);
+ return createSimpleGLSLGlobalVarying(
+ context,
+ builder, type, varLayout, typeLayout, kind, bindingIndex, declarator);
}
else if( type->As<MatrixExpressionType>() )
{
// TODO: a matrix-type varying should probably be handled like an array of rows
- return createSimpleGLSLGlobalVarying(builder, type, varLayout, typeLayout, kind, bindingIndex, declarator);
+ return createSimpleGLSLGlobalVarying(
+ context,
+ builder, type, varLayout, typeLayout, kind, bindingIndex, declarator);
}
else if( auto arrayType = type->As<ArrayExpressionType>() )
{
@@ -2984,6 +3236,7 @@ namespace Slang
arrayDeclarator.next = declarator;
return createGLSLGlobalVaryingsImpl(
+ context,
builder,
elementType,
varLayout,
@@ -3020,6 +3273,7 @@ namespace Slang
fieldBindingIndex += fieldResInfo->index;
auto fieldVal = createGLSLGlobalVaryingsImpl(
+ context,
builder,
ff->typeLayout->type,
ff,
@@ -3041,19 +3295,24 @@ namespace Slang
}
// Default case is to fall back on the simple behavior
- return createSimpleGLSLGlobalVarying(builder, type, varLayout, typeLayout, kind, bindingIndex, declarator);
+ return createSimpleGLSLGlobalVarying(
+ context,
+ builder, type, varLayout, typeLayout, kind, bindingIndex, declarator);
}
ScalarizedVal createGLSLGlobalVaryings(
- IRBuilder* builder,
- Type* type,
- VarLayout* layout,
- LayoutResourceKind kind)
+ GLSLLegalizationContext* context,
+ IRBuilder* builder,
+ Type* type,
+ VarLayout* layout,
+ LayoutResourceKind kind)
{
UInt bindingIndex = 0;
if(auto rr = layout->FindResourceInfo(kind))
bindingIndex = rr->index;
- return createGLSLGlobalVaryingsImpl(builder, type, layout, layout->typeLayout, kind, bindingIndex, nullptr);
+ return createGLSLGlobalVaryingsImpl(
+ context,
+ builder, type, layout, layout->typeLayout, kind, bindingIndex, nullptr);
}
ScalarizedVal extractField(
@@ -3189,8 +3448,15 @@ namespace Slang
void legalizeEntryPointForGLSL(
Session* session,
IRFunc* func,
- EntryPointLayout* entryPointLayout)
+ EntryPointLayout* entryPointLayout,
+ DiagnosticSink* sink,
+ ExtensionUsageTracker* extensionUsageTracker)
{
+ GLSLLegalizationContext context;
+ context.stage = entryPointLayout->profile.GetStage();
+ context.sink = sink;
+ context.extensionUsageTracker = extensionUsageTracker;
+
auto module = func->parentModule;
// We require that the entry-point function has no uses,
@@ -3250,6 +3516,7 @@ namespace Slang
// code to write to that variable.
auto resultGlobal = createGLSLGlobalVaryings(
+ &context,
&builder,
resultType,
entryPointLayout->resultLayout,
@@ -3345,7 +3612,9 @@ namespace Slang
// In the `in out` case we need to declare two
// sets of global variables: one for the `in`
// side and one for the `out` side.
- auto globalInputVal = createGLSLGlobalVaryings(&builder, valueType, paramLayout, LayoutResourceKind::VertexInput);
+ auto globalInputVal = createGLSLGlobalVaryings(
+ &context,
+ &builder, valueType, paramLayout, LayoutResourceKind::VertexInput);
assign(&builder, localVal, globalInputVal);
}
@@ -3358,7 +3627,9 @@ namespace Slang
// We also need one or more global variabels to write the output to
// when the function is done. We create them here.
- auto globalOutputVal = createGLSLGlobalVaryings(&builder, valueType, paramLayout, LayoutResourceKind::FragmentOutput);
+ auto globalOutputVal = createGLSLGlobalVaryings(
+ &context,
+ &builder, valueType, paramLayout, LayoutResourceKind::FragmentOutput);
// 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
@@ -3392,7 +3663,9 @@ namespace Slang
// and attach the required layout information to it along
// the way.
- auto globalValue = createGLSLGlobalVaryings(&builder, paramType, paramLayout, LayoutResourceKind::VertexInput);
+ auto globalValue = createGLSLGlobalVaryings(
+ &context,
+ &builder, paramType, paramLayout, LayoutResourceKind::VertexInput);
// Next we need to replace uses of the parameter with
// references to the variable(s). We are going to do that
@@ -4636,7 +4909,8 @@ namespace Slang
void specializeIRForEntryPoint(
IRSpecializationState* state,
- EntryPointRequest* entryPointRequest)
+ EntryPointRequest* entryPointRequest,
+ ExtensionUsageTracker* extensionUsageTracker)
{
auto target = state->target;
@@ -4675,7 +4949,12 @@ namespace Slang
{
case CodeGenTarget::GLSL:
{
- legalizeEntryPointForGLSL(session, irEntryPoint, entryPointLayout);
+ legalizeEntryPointForGLSL(
+ session,
+ irEntryPoint,
+ entryPointLayout,
+ &compileRequest->mSink,
+ extensionUsageTracker);
}
break;