diff options
| author | Tim Foley <tfoleyNV@users.noreply.github.com> | 2018-02-16 12:00:20 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-02-16 12:00:20 -0800 |
| commit | 8c872597540b2287bc18d5723c7ae3b8832246b8 (patch) | |
| tree | a40698aad0732e6078bf81229df6b1a54b1fd388 /source | |
| parent | 1b93da040ef00836438437e998c1c9584e3fd4ac (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')
| -rw-r--r-- | source/slang/emit.cpp | 235 | ||||
| -rw-r--r-- | source/slang/ir-insts.h | 5 | ||||
| -rw-r--r-- | source/slang/ir.cpp | 335 |
3 files changed, 333 insertions, 242 deletions
diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp index 0cd896ced..e5fb6a8be 100644 --- a/source/slang/emit.cpp +++ b/source/slang/emit.cpp @@ -33,6 +33,8 @@ struct ExtensionUsageTracker // Record the GLSL extnsions we have already emitted a `#extension` for HashSet<String> glslExtensionsRequired; StringBuilder glslExtensionRequireLines; + + ProfileVersion profileVersion = ProfileVersion::GLSL_110; }; void requireGLSLExtension( @@ -51,6 +53,16 @@ void requireGLSLExtension( tracker->glslExtensionsRequired.Add(name); } +void requireGLSLVersionImpl( + ExtensionUsageTracker* tracker, + ProfileVersion version) +{ + // Check if this profile is newer + if ((UInt)version > (UInt)tracker->profileVersion) + { + tracker->profileVersion = version; + } +} // Shared state for an entire emit session @@ -4503,182 +4515,6 @@ emitDeclImpl(decl, nullptr); return name; } - String getGLSLSystemValueName( - IRValue* varDecl, - VarLayout* varLayout) - { - auto semanticNameSpelling = varLayout->systemValueSemantic; - 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. - return "gl_Position"; - } - 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 ""; - } - else if(semanticName == "sv_clipdistance") - { - // TODO: type conversion is required here. - return "gl_ClipDistance"; - } - else if(semanticName == "sv_culldistance") - { - requireGLSLExtension("ARB_cull_distance"); - - // TODO: type conversion is required here. - return "gl_CullDistance"; - } - else if(semanticName == "sv_coverage") - { - // TODO: deal with `gl_SampleMaskIn` when used as an input. - - // TODO: type conversion is required here. - return "gl_SampleMask"; - } - else if(semanticName == "sv_depth") - { - return "gl_FragDepth"; - } - else if(semanticName == "sv_depthgreaterequal") - { - // TODO: layout(depth_greater) out float gl_FragDepth; - return "gl_FragDepth"; - } - else if(semanticName == "sv_depthlessequal") - { - // TODO: layout(depth_greater) out float gl_FragDepth; - return "gl_FragDepth"; - } - else if(semanticName == "sv_dispatchthreadid") - { - return "gl_GlobalInvocationID"; - } - else if(semanticName == "sv_domainlocation") - { - return "gl_TessCoord"; - } - else if(semanticName == "sv_groupid") - { - return "gl_WorkGroupID"; - } - else if(semanticName == "sv_groupindex") - { - return "gl_LocalInvocationIndex"; - } - else if(semanticName == "sv_groupthreadid") - { - return "gl_LocalInvocationID"; - } - else if(semanticName == "sv_gsinstanceid") - { - return "gl_InvocationID"; - } - else if(semanticName == "sv_instanceid") - { - return "gl_InstanceIndex"; - } - else if(semanticName == "sv_isfrontface") - { - return "gl_FrontFacing"; - } - else if(semanticName == "sv_outputcontrolpointid") - { - return "gl_InvocationID"; - } - else if(semanticName == "sv_primitiveid") - { - return "gl_PrimitiveID"; - } - else if (semanticName == "sv_rendertargetarrayindex") - { - switch (context->shared->entryPoint->profile.GetStage()) - { - case Stage::Geometry: - requireGLSLVersion(ProfileVersion::GLSL_150); - break; - - case Stage::Fragment: - requireGLSLVersion(ProfileVersion::GLSL_430); - break; - - default: - requireGLSLVersion(ProfileVersion::GLSL_450); - requireGLSLExtension("GL_ARB_shader_viewport_layer_array"); - break; - } - - return "gl_Layer"; - } - else if (semanticName == "sv_sampleindex") - { - return "gl_SampleID"; - } - else if (semanticName == "sv_stencilref") - { - requireGLSLExtension("ARB_shader_stencil_export"); - return "gl_FragStencilRef"; - } - else if (semanticName == "sv_tessfactor") - { - return "gl_TessLevelOuter"; - } - else if (semanticName == "sv_vertexid") - { - return "gl_VertexIndex"; - } - else if (semanticName == "sv_viewportarrayindex") - { - return "gl_ViewportIndex"; - } - else if (semanticName == "nv_x_right") - { - requireGLSLVersion(ProfileVersion::GLSL_450); - 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]` - // - - return "gl_PositionPerViewNV[1]"; - -// shared->requiresCopyGLPositionToPositionPerView = true; - } - else if (semanticName == "nv_viewport_mask") - { - requireGLSLVersion(ProfileVersion::GLSL_450); - requireGLSLExtension("GL_NVX_multiview_per_view_attributes"); - - return "gl_ViewportMaskPerViewNV"; -// globalVarExpr = createGLSLBuiltinRef("gl_ViewportMaskPerViewNV", -// getUnsizedArrayType(getIntType())); - } - else - { - getSink()->diagnose(varDecl->sourceLoc, Diagnostics::unknownSystemValueSemantic, semanticNameSpelling); - return semanticName; - } - } - String getIRName( IRValue* inst) { @@ -4695,23 +4531,6 @@ emitDeclImpl(decl, nullptr); break; } - if(getTarget(context) == CodeGenTarget::GLSL) - { - if(auto layoutMod = inst->findDecoration<IRLayoutDecoration>()) - { - auto layout = layoutMod->layout; - if(auto varLayout = layout.As<VarLayout>()) - { - if(varLayout->systemValueSemantic.Length() != 0) - { - auto translated = getGLSLSystemValueName(inst, varLayout); - if(translated.Length()) - return translated; - } - } - } - } - if(auto decoration = inst->findDecoration<IRHighLevelDeclDecoration>()) { auto decl = decoration->decl; @@ -7743,26 +7562,15 @@ emitDeclImpl(decl, nullptr); // We want to skip the declaration of any system-value variables // when outputting GLSL (well, except in the case where they // actually *require* redeclaration...). - - if(auto layoutMod = varDecl->findDecoration<IRLayoutDecoration>()) + // + // TODO: can we detect this more robustly? + if(varDecl->mangledName.StartsWith("gl_")) { - auto layout = layoutMod->layout; - if(auto varLayout = layout.As<VarLayout>()) - { - if(varLayout->systemValueSemantic.Length() != 0) - { - auto translated = getGLSLSystemValueName(varDecl, varLayout); - if( translated.Length() ) - { - // The variable seems to translate to an OpenGL - // system value, so we will assume that it doesn't - // need to be declared. - // - // TODO: handle case where we *should* declare the variable. - return; - } - } - } + // The variable represents an OpenGL system value, + // so we will assume that it doesn't need to be declared. + // + // TODO: handle case where we *should* declare the variable. + return; } } @@ -8220,7 +8028,8 @@ String emitEntryPoint( specializeIRForEntryPoint( irSpecializationState, - entryPoint); + entryPoint, + &sharedContext.extensionUsageTracker); // If the user specified the flag that they want us to dump // IR, then do it here, for the target-specific, but diff --git a/source/slang/ir-insts.h b/source/slang/ir-insts.h index f53004ae4..e2c1c2e03 100644 --- a/source/slang/ir-insts.h +++ b/source/slang/ir-insts.h @@ -682,6 +682,8 @@ IRGlobalValue* getSpecializedGlobalValueForDeclRef( IRSpecializationState* state, DeclRef<Decl> const& declRef); +struct ExtensionUsageTracker; + // Clone the IR values reachable from the given entry point // into the IR module assocaited with the specialization state. // When multiple definitions of a symbol are found, the one @@ -689,7 +691,8 @@ IRGlobalValue* getSpecializedGlobalValueForDeclRef( // used. void specializeIRForEntryPoint( IRSpecializationState* state, - EntryPointRequest* entryPointRequest); + EntryPointRequest* entryPointRequest, + ExtensionUsageTracker* extensionUsageTracker); // Find suitable uses of the `specialize` instruction that // can be replaced with references to specialized functions. 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; |
