summaryrefslogtreecommitdiffstats
path: root/source/slang/parameter-binding.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/parameter-binding.cpp')
-rw-r--r--source/slang/parameter-binding.cpp644
1 files changed, 61 insertions, 583 deletions
diff --git a/source/slang/parameter-binding.cpp b/source/slang/parameter-binding.cpp
index dda3a8621..a472a9280 100644
--- a/source/slang/parameter-binding.cpp
+++ b/source/slang/parameter-binding.cpp
@@ -395,9 +395,6 @@ struct ParameterBindingContext
// the resource usage of shader parameters.
TypeLayoutContext layoutContext;
- // A dictionary to accelerate looking up parameters by name
- Dictionary<Name*, ParameterInfo*> mapNameToParameterInfo;
-
// What stage (if any) are we compiling for?
Stage stage;
@@ -573,424 +570,6 @@ LayoutSemanticInfo ExtractLayoutSemanticInfo(
return info;
}
-static Name* getReflectionName(VarDeclBase* varDecl)
-{
- if (auto reflectionNameModifier = varDecl->FindModifier<ParameterGroupReflectionName>())
- return reflectionNameModifier->nameAndLoc.name;
-
- return varDecl->getName();
-}
-
-// Information tracked when doing a structural
-// match of types.
-struct StructuralTypeMatchStack
-{
- DeclRef<VarDeclBase> leftDecl;
- DeclRef<VarDeclBase> rightDecl;
- StructuralTypeMatchStack* parent;
-};
-
-static void diagnoseParameterTypeMismatch(
- ParameterBindingContext* context,
- StructuralTypeMatchStack* inStack)
-{
- SLANG_ASSERT(inStack);
-
- // The bottom-most entry in the stack should represent
- // the shader parameters that kicked things off
- auto stack = inStack;
- while(stack->parent)
- stack = stack->parent;
-
- getSink(context)->diagnose(stack->leftDecl, Diagnostics::shaderParameterDeclarationsDontMatch, getReflectionName(stack->leftDecl));
- getSink(context)->diagnose(stack->rightDecl, Diagnostics::seeOtherDeclarationOf, getReflectionName(stack->rightDecl));
-}
-
-// Two types that were expected to match did not.
-// Inform the user with a suitable message.
-static void diagnoseTypeMismatch(
- ParameterBindingContext* context,
- StructuralTypeMatchStack* inStack)
-{
- auto stack = inStack;
- SLANG_ASSERT(stack);
- diagnoseParameterTypeMismatch(context, stack);
-
- auto leftType = GetType(stack->leftDecl);
- auto rightType = GetType(stack->rightDecl);
-
- if( stack->parent )
- {
- getSink(context)->diagnose(stack->leftDecl, Diagnostics::fieldTypeMisMatch, getReflectionName(stack->leftDecl), leftType, rightType);
- getSink(context)->diagnose(stack->rightDecl, Diagnostics::seeOtherDeclarationOf, getReflectionName(stack->rightDecl));
-
- stack = stack->parent;
- if( stack )
- {
- while( stack->parent )
- {
- getSink(context)->diagnose(stack->leftDecl, Diagnostics::usedInDeclarationOf, getReflectionName(stack->leftDecl));
- stack = stack->parent;
- }
- }
- }
- else
- {
- getSink(context)->diagnose(stack->leftDecl, Diagnostics::shaderParameterTypeMismatch, leftType, rightType);
- }
-}
-
-// Two types that were expected to match did not.
-// Inform the user with a suitable message.
-static void diagnoseTypeFieldsMismatch(
- ParameterBindingContext* context,
- DeclRef<Decl> const& left,
- DeclRef<Decl> const& right,
- StructuralTypeMatchStack* stack)
-{
- diagnoseParameterTypeMismatch(context, stack);
-
- getSink(context)->diagnose(left, Diagnostics::fieldDeclarationsDontMatch, left.GetName());
- getSink(context)->diagnose(right, Diagnostics::seeOtherDeclarationOf, right.GetName());
-
- if( stack )
- {
- while( stack->parent )
- {
- getSink(context)->diagnose(stack->leftDecl, Diagnostics::usedInDeclarationOf, getReflectionName(stack->leftDecl));
- stack = stack->parent;
- }
- }
-}
-
-static void collectFields(
- DeclRef<AggTypeDecl> declRef,
- List<DeclRef<VarDecl>>& outFields)
-{
- for( auto fieldDeclRef : getMembersOfType<VarDecl>(declRef) )
- {
- if(fieldDeclRef.getDecl()->HasModifier<HLSLStaticModifier>())
- continue;
-
- outFields.Add(fieldDeclRef);
- }
-}
-
-static bool validateTypesMatch(
- ParameterBindingContext* context,
- Type* left,
- Type* right,
- StructuralTypeMatchStack* stack);
-
-static bool validateIntValuesMatch(
- ParameterBindingContext* context,
- IntVal* left,
- IntVal* right,
- StructuralTypeMatchStack* stack)
-{
- if(left->EqualsVal(right))
- return true;
-
- // TODO: are there other cases we need to handle here?
-
- diagnoseTypeMismatch(context, stack);
- return false;
-}
-
-
-static bool validateValuesMatch(
- ParameterBindingContext* context,
- Val* left,
- Val* right,
- StructuralTypeMatchStack* stack)
-{
- if( auto leftType = dynamicCast<Type>(left) )
- {
- if( auto rightType = dynamicCast<Type>(right) )
- {
- return validateTypesMatch(context, leftType, rightType, stack);
- }
- }
-
- if( auto leftInt = dynamicCast<IntVal>(left) )
- {
- if( auto rightInt = dynamicCast<IntVal>(right) )
- {
- return validateIntValuesMatch(context, leftInt, rightInt, stack);
- }
- }
-
- if( auto leftWitness = dynamicCast<SubtypeWitness>(left) )
- {
- if( auto rightWitness = dynamicCast<SubtypeWitness>(right) )
- {
- return true;
- }
- }
-
- diagnoseTypeMismatch(context, stack);
- return false;
-}
-
-static bool validateGenericSubstitutionsMatch(
- ParameterBindingContext* context,
- GenericSubstitution* left,
- GenericSubstitution* right,
- StructuralTypeMatchStack* stack)
-{
- if( !left )
- {
- if( !right )
- {
- return true;
- }
-
- diagnoseTypeMismatch(context, stack);
- return false;
- }
-
-
-
- UInt argCount = left->args.Count();
- if( argCount != right->args.Count() )
- {
- diagnoseTypeMismatch(context, stack);
- return false;
- }
-
- for( UInt aa = 0; aa < argCount; ++aa )
- {
- auto leftArg = left->args[aa];
- auto rightArg = right->args[aa];
-
- if(!validateValuesMatch(context, leftArg, rightArg, stack))
- return false;
- }
-
- return true;
-}
-
-static bool validateThisTypeSubstitutionsMatch(
- ParameterBindingContext* /*context*/,
- ThisTypeSubstitution* /*left*/,
- ThisTypeSubstitution* /*right*/,
- StructuralTypeMatchStack* /*stack*/)
-{
- // TODO: actual checking.
- return true;
-}
-
-static bool validateSpecializationsMatch(
- ParameterBindingContext* context,
- SubstitutionSet left,
- SubstitutionSet right,
- StructuralTypeMatchStack* stack)
-{
- auto ll = left.substitutions;
- auto rr = right.substitutions;
- for(;;)
- {
- // Skip any global generic substitutions.
- if(auto leftGlobalGeneric = as<GlobalGenericParamSubstitution>(ll))
- {
- ll = leftGlobalGeneric->outer;
- continue;
- }
- if(auto rightGlobalGeneric = as<GlobalGenericParamSubstitution>(rr))
- {
- rr = rightGlobalGeneric->outer;
- continue;
- }
-
- // If either ran out, then we expect both to have run out.
- if(!ll || !rr)
- return !ll && !rr;
-
- auto leftSubst = ll;
- auto rightSubst = rr;
-
- ll = ll->outer;
- rr = rr->outer;
-
- if(auto leftGeneric = as<GenericSubstitution>(leftSubst))
- {
- if(auto rightGeneric = as<GenericSubstitution>(rightSubst))
- {
- if(validateGenericSubstitutionsMatch(context, leftGeneric, rightGeneric, stack))
- {
- continue;
- }
- }
- }
- else if(auto leftThisType = as<ThisTypeSubstitution>(leftSubst))
- {
- if(auto rightThisType = as<ThisTypeSubstitution>(rightSubst))
- {
- if(validateThisTypeSubstitutionsMatch(context, leftThisType, rightThisType, stack))
- {
- continue;
- }
- }
- }
-
- return false;
- }
-
- return true;
-}
-
-// Determine if two types "match" for the purposes of `cbuffer` layout rules.
-//
-static bool validateTypesMatch(
- ParameterBindingContext* context,
- Type* left,
- Type* right,
- StructuralTypeMatchStack* stack)
-{
- if(left->Equals(right))
- return true;
-
- // It is possible that the types don't match exactly, but
- // they *do* match structurally.
-
- // Note: the following code will lead to infinite recursion if there
- // are ever recursive types. We'd need a more refined system to
- // cache the matches we've already found.
-
- if( auto leftDeclRefType = as<DeclRefType>(left) )
- {
- if( auto rightDeclRefType = as<DeclRefType>(right) )
- {
- // Are they references to matching decl refs?
- auto leftDeclRef = leftDeclRefType->declRef;
- auto rightDeclRef = rightDeclRefType->declRef;
-
- // Do the reference the same declaration? Or declarations
- // with the same name?
- //
- // TODO: we should only consider the same-name case if the
- // declarations come from translation units being compiled
- // (and not an imported module).
- if( leftDeclRef.getDecl() == rightDeclRef.getDecl()
- || leftDeclRef.GetName() == rightDeclRef.GetName() )
- {
- // Check that any generic arguments match
- if( !validateSpecializationsMatch(
- context,
- leftDeclRef.substitutions,
- rightDeclRef.substitutions,
- stack) )
- {
- return false;
- }
-
- // Check that any declared fields match too.
- if( auto leftStructDeclRef = leftDeclRef.as<AggTypeDecl>() )
- {
- if( auto rightStructDeclRef = rightDeclRef.as<AggTypeDecl>() )
- {
- List<DeclRef<VarDecl>> leftFields;
- List<DeclRef<VarDecl>> rightFields;
-
- collectFields(leftStructDeclRef, leftFields);
- collectFields(rightStructDeclRef, rightFields);
-
- UInt leftFieldCount = leftFields.Count();
- UInt rightFieldCount = rightFields.Count();
-
- if( leftFieldCount != rightFieldCount )
- {
- diagnoseTypeFieldsMismatch(context, leftDeclRef, rightDeclRef, stack);
- return false;
- }
-
- for( UInt ii = 0; ii < leftFieldCount; ++ii )
- {
- auto leftField = leftFields[ii];
- auto rightField = rightFields[ii];
-
- if( leftField.GetName() != rightField.GetName() )
- {
- diagnoseTypeFieldsMismatch(context, leftDeclRef, rightDeclRef, stack);
- return false;
- }
-
- auto leftFieldType = GetType(leftField);
- auto rightFieldType = GetType(rightField);
-
- StructuralTypeMatchStack subStack;
- subStack.parent = stack;
- subStack.leftDecl = leftField;
- subStack.rightDecl = rightField;
-
- if(!validateTypesMatch(context, leftFieldType,rightFieldType, &subStack))
- return false;
- }
- }
- }
-
- // Everything seemed to match recursively.
- return true;
- }
- }
- }
-
- // If we are looking at `T[N]` and `U[M]` we want to check that
- // `T` is structurally equivalent to `U` and `N` is the same as `M`.
- else if( auto leftArrayType = as<ArrayExpressionType>(left) )
- {
- if( auto rightArrayType = as<ArrayExpressionType>(right) )
- {
- if(!validateTypesMatch(context, leftArrayType->baseType, rightArrayType->baseType, stack) )
- return false;
-
- if(!validateValuesMatch(context, leftArrayType->ArrayLength, rightArrayType->ArrayLength, stack))
- return false;
-
- return true;
- }
- }
-
- diagnoseTypeMismatch(context, stack);
- return false;
-}
-
-// This function is supposed to determine if two global shader
-// parameter declarations represent the same logical parameter
-// (so that they should get the exact same binding(s) allocated).
-//
-static bool doesParameterMatch(
- ParameterBindingContext* context,
- RefPtr<VarLayout> varLayout,
- ParameterInfo* parameterInfo)
-{
- // Any "varying" parameter should automatically be excluded
- //
- // Note that we use the `typeLayout` field rather than
- // looking at resource information on the variable directly,
- // because this may be called when binding hasn't been performed.
- for (auto rr : varLayout->typeLayout->resourceInfos)
- {
- switch (rr.kind)
- {
- case LayoutResourceKind::VertexInput:
- case LayoutResourceKind::FragmentOutput:
- return false;
-
- default:
- break;
- }
- }
-
- StructuralTypeMatchStack stack;
- stack.parent = nullptr;
- stack.leftDecl = varLayout->varDecl;
- stack.rightDecl = parameterInfo->varLayouts[0]->varDecl;
-
- validateTypesMatch(context, varLayout->typeLayout->type, parameterInfo->varLayouts[0]->typeLayout->type, &stack);
-
- return true;
-}
//
@@ -1020,90 +599,6 @@ static bool findLayoutArg(
return findLayoutArg<T>(declRef.getDecl(), outVal);
}
-//
-
-static bool isGLSLBuiltinName(VarDeclBase* varDecl)
-{
- return getText(getReflectionName(varDecl)).StartsWith("gl_");
-}
-
-RefPtr<Type> tryGetEffectiveTypeForGLSLVaryingInput(
- ParameterBindingContext* context,
- VarDeclBase* varDecl)
-{
- if (isGLSLBuiltinName(varDecl))
- return nullptr;
-
- auto type = varDecl->getType();
- if( varDecl->HasModifier<InModifier>() || as<GLSLInputParameterGroupType>(type))
- {
- // Special case to handle "arrayed" shader inputs, as used
- // for Geometry and Hull input
- switch( context->stage )
- {
- case Stage::Geometry:
- case Stage::Hull:
- case Stage::Domain:
- // Tessellation `patch` variables should stay as written
- if( !varDecl->HasModifier<GLSLPatchModifier>() )
- {
- // Unwrap array type, if present
- if( auto arrayType = as<ArrayExpressionType>(type) )
- {
- type = arrayType->baseType.Ptr();
- }
- }
- break;
-
- default:
- break;
- }
-
- return type;
- }
-
- return nullptr;
-}
-
-RefPtr<Type> tryGetEffectiveTypeForGLSLVaryingOutput(
- ParameterBindingContext* context,
- VarDeclBase* varDecl)
-{
- if (isGLSLBuiltinName(varDecl))
- return nullptr;
-
- auto type = varDecl->getType();
- if( varDecl->HasModifier<OutModifier>() || as<GLSLOutputParameterGroupType>(type))
- {
- // Special case to handle "arrayed" shader outputs, as used
- // for Hull Shader output
- //
- // Note(tfoley): there is unfortunate code duplication
- // with the `in` case above.
- switch( context->stage )
- {
- case Stage::Hull:
- // Tessellation `patch` variables should stay as written
- if( !varDecl->HasModifier<GLSLPatchModifier>() )
- {
- // Unwrap array type, if present
- if( auto arrayType = as<ArrayExpressionType>(type) )
- {
- type = arrayType->baseType.Ptr();
- }
- }
- break;
-
- default:
- break;
- }
-
- return type;
- }
-
- return nullptr;
-}
-
/// Determine how to lay out a global variable that might be a shader parameter.
///
/// Returns `nullptr` if the declaration does not represent a shader parameter.
@@ -1183,19 +678,21 @@ static void collectGlobalGenericParameter(
// Collect a single declaration into our set of parameters
static void collectGlobalScopeParameter(
- ParameterBindingContext* context,
- RefPtr<VarDeclBase> varDecl,
- SubstitutionSet globalGenericSubst)
+ ParameterBindingContext* context,
+ GlobalShaderParamInfo const& shaderParamInfo,
+ SubstitutionSet globalGenericSubst)
{
+ auto varDeclRef = shaderParamInfo.paramDeclRef;
+
// We apply any substitutions for global generic parameters here.
- auto type = varDecl->getType()->Substitute(globalGenericSubst).as<Type>();
+ auto type = GetType(varDeclRef)->Substitute(globalGenericSubst).as<Type>();
// We use a single operation to both check whether the
// variable represents a shader parameter, and to compute
// the layout for that parameter's type.
auto typeLayout = getTypeLayoutForGlobalShaderParameter(
context,
- varDecl,
+ varDeclRef.getDecl(),
type);
// If we did not find appropriate layout rules, then it
@@ -1207,45 +704,43 @@ static void collectGlobalScopeParameter(
// Now create a variable layout that we can use
RefPtr<VarLayout> varLayout = new VarLayout();
varLayout->typeLayout = typeLayout;
- varLayout->varDecl = DeclRef<Decl>(varDecl.Ptr(), nullptr).as<VarDeclBase>();
+ varLayout->varDecl = varDeclRef;
- // This declaration may represent the same logical parameter
- // as a declaration that came from a different translation unit.
- // If that is the case, we want to re-use the same `VarLayout`
- // across both parameters.
+ // The logic in `check.cpp` that created the `GlobalShaderParamInfo`
+ // will have identified any cases where there might be multiple
+ // global variables that logically represent the same shader parameter.
//
- // TODO: This logic currently detects *any* global-scope parameters
- // with matching names, but it should eventually be narrowly
- // scoped so that it only applies to parameters from unnamed modules.
+ // We will track the same basic information during layout using
+ // the `ParameterInfo` type.
//
- // First we look for an existing entry matching the name
- // of this parameter:
- auto parameterName = getReflectionName(varDecl);
- ParameterInfo* parameterInfo = nullptr;
- if( context->mapNameToParameterInfo.TryGetValue(parameterName, parameterInfo) )
- {
- // If the parameters have the same name, but don't "match" according to some reasonable rules,
- // then we need to bail out.
- if( !doesParameterMatch(context, varLayout, parameterInfo) )
- {
- parameterInfo = nullptr;
- }
- }
+ // TODO: `ParameterInfo` should probably become `LayoutParamInfo`.
+ //
+ ParameterInfo* parameterInfo = new ParameterInfo();
+ context->shared->parameters.Add(parameterInfo);
- // If we didn't find a matching parameter, then we need to create one here
- if( !parameterInfo )
- {
- parameterInfo = new ParameterInfo();
- context->shared->parameters.Add(parameterInfo);
- context->mapNameToParameterInfo.AddIfNotExists(parameterName, parameterInfo);
- }
- else
+ // Add the first variable declaration to the list of declarations for the parameter
+ parameterInfo->varLayouts.Add(varLayout);
+
+ // Add any additional variables to the list of declarations
+ for( auto additionalVarDeclRef : shaderParamInfo.additionalParamDeclRefs )
{
- varLayout->flags |= VarLayoutFlag::IsRedeclaration;
- }
+ // TODO: We should either eliminate the design choice where different
+ // declarations of the "same" shade parameter get merged across
+ // translation units (it is effectively just a compatiblity feature),
+ // or we should clean things up earlier in the chain so that we can
+ // re-use a single `VarLayout` across all of the different declarations.
+ //
+ // TODO: It would also make sense in these cases to ensure that
+ // such global shader parameters get the same mangled name across
+ // all translation units, so that they can automatically be collapsed
+ // during linking.
- // Add this variable declaration to the list of declarations for the parameter
- parameterInfo->varLayouts.Add(varLayout);
+ RefPtr<VarLayout> additionalVarLayout = new VarLayout();
+ additionalVarLayout->typeLayout = typeLayout;
+ additionalVarLayout->varDecl = additionalVarDeclRef;
+
+ parameterInfo->varLayouts.Add(additionalVarLayout);
+ }
}
static RefPtr<UsedRangeSet> findUsedRangeSetForSpace(
@@ -1783,41 +1278,6 @@ static void completeBindingsForParameter(
applyBindingInfoToParameter(varLayout, bindingInfos);
}
-
-
-static void collectGlobalScopeParameters(
- ParameterBindingContext* context,
- ModuleDecl* program,
- SubstitutionSet globalGenericSubst)
-{
- // First enumerate parameters at global scope
- // We collect two things here:
- // 1. A shader parameter, which is always a variable
- // 2. A global entry-point generic parameter type (`type_param`),
- // which is a GlobalGenericParamDecl
- // We collect global generic type parameters in the first pass,
- // So we can fill in the correct index into ordinary type layouts
- // for generic types in the second pass.
- for (auto decl : program->Members)
- {
- if (auto genParamDecl = as<GlobalGenericParamDecl>(decl))
- collectGlobalGenericParameter(context, genParamDecl);
- }
- for (auto decl : program->Members)
- {
- if (auto varDecl = as<VarDeclBase>(decl))
- collectGlobalScopeParameter(context, varDecl, globalGenericSubst);
- }
-
- // Next, we need to enumerate the parameters of
- // each entry point (which requires knowing what the
- // entry points *are*)
-
- // TODO(tfoley): Entry point functions should be identified
- // by looking for a generated modifier that is attached
- // to global-scope function declarations.
-}
-
struct SimpleSemanticInfo
{
String name;
@@ -2274,7 +1734,7 @@ static RefPtr<TypeLayout> processEntryPointVaryingParameter(
static RefPtr<TypeLayout> computeEntryPointParameterTypeLayout(
ParameterBindingContext* context,
SubstitutionSet typeSubst,
- DeclRef<ParamDecl> paramDeclRef,
+ DeclRef<VarDeclBase> paramDeclRef,
RefPtr<VarLayout> paramVarLayout,
EntryPointParameterState& state)
{
@@ -2537,8 +1997,10 @@ static void collectEntryPointParameters(
scopeBuilder.beginLayout(context);
auto paramsStructLayout = scopeBuilder.m_structLayout;
- for( auto paramDeclRef : getMembersOfType<ParamDecl>(entryPointFuncDeclRef) )
+ for( auto& shaderParamInfo : entryPoint->getShaderParams() )
{
+ auto paramDeclRef = shaderParamInfo.paramDeclRef;
+
// Any error messages we emit during the process should
// refer to the location of this parameter.
//
@@ -2656,17 +2118,33 @@ static void collectParameters(
// logical namespace/"linkage" so that two parameters
// with the same name should represent the same
// parameter, and get the same binding(s)
+
ParameterBindingContext contextData = *inContext;
auto context = &contextData;
+ context->stage = Stage::Unknown;
auto globalGenericSubst = program->getGlobalGenericSubstitution();
+ // We will start by looking for any global generic type parameters.
+
for(RefPtr<Module> module : program->getModuleDependencies())
{
- context->stage = Stage::Unknown;
+ for( auto genParamDecl : module->getModuleDecl()->getMembersOfType<GlobalGenericParamDecl>() )
+ {
+ collectGlobalGenericParameter(context, genParamDecl);
+ }
+ }
+
+ // Once we have enumerated global generic type parameters, we can
+ // begin enumerating shader parameters, starting at the global scope.
+ //
+ // Because we have already enumerated the global generic type parameters,
+ // we will be able to look up the index of a global generic type parameter
+ // when we see it referenced in the type of one of the shader parameters.
- // First look at global-scope parameters
- collectGlobalScopeParameters(context, module->getModuleDecl(), globalGenericSubst);
+ for(auto& globalParamInfo : program->getShaderParams() )
+ {
+ collectGlobalScopeParameter(context, globalParamInfo, globalGenericSubst);
}
// Next consider parameters for entry points