summaryrefslogtreecommitdiffstats
path: root/source/slang/slang-parameter-binding.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/slang-parameter-binding.cpp')
-rw-r--r--source/slang/slang-parameter-binding.cpp1043
1 files changed, 765 insertions, 278 deletions
diff --git a/source/slang/slang-parameter-binding.cpp b/source/slang/slang-parameter-binding.cpp
index dc99f55e2..722725af7 100644
--- a/source/slang/slang-parameter-binding.cpp
+++ b/source/slang/slang-parameter-binding.cpp
@@ -674,16 +674,22 @@ static RefPtr<TypeLayout> processEntryPointVaryingParameter(
EntryPointParameterState const& state,
RefPtr<VarLayout> varLayout);
-// Collect a single declaration into our set of parameters
-static void collectGlobalGenericParameter(
- ParameterBindingContext* context,
- RefPtr<GlobalGenericParamDecl> paramDecl)
+static RefPtr<VarLayout> _createVarLayout(
+ TypeLayout* typeLayout,
+ DeclRef<VarDeclBase> varDeclRef)
{
- RefPtr<GenericParamLayout> layout = new GenericParamLayout();
- layout->decl = paramDecl;
- layout->index = (int)context->shared->programLayout->globalGenericParams.getCount();
- context->shared->programLayout->globalGenericParams.add(layout);
- context->shared->programLayout->globalGenericParamsMap[layout->decl->getName()->text] = layout.Ptr();
+ RefPtr<VarLayout> varLayout = new VarLayout();
+ varLayout->typeLayout = typeLayout;
+ varLayout->varDecl = varDeclRef;
+
+ if(auto pendingDataTypeLayout = typeLayout->pendingDataTypeLayout)
+ {
+ RefPtr<VarLayout> pendingVarLayout = new VarLayout();
+ pendingVarLayout->typeLayout = pendingDataTypeLayout;
+ varLayout->pendingVarLayout = pendingVarLayout;
+ }
+
+ return varLayout;
}
// Collect a single declaration into our set of parameters
@@ -712,9 +718,7 @@ static void collectGlobalScopeParameter(
return;
// Now create a variable layout that we can use
- RefPtr<VarLayout> varLayout = new VarLayout();
- varLayout->typeLayout = typeLayout;
- varLayout->varDecl = varDeclRef;
+ RefPtr<VarLayout> varLayout = _createVarLayout(typeLayout, varDeclRef);
// The logic in `check.cpp` that created the `GlobalShaderParamInfo`
// will have identified any cases where there might be multiple
@@ -748,6 +752,7 @@ static void collectGlobalScopeParameter(
RefPtr<VarLayout> additionalVarLayout = new VarLayout();
additionalVarLayout->typeLayout = typeLayout;
additionalVarLayout->varDecl = additionalVarDeclRef;
+ additionalVarLayout->pendingVarLayout = varLayout->pendingVarLayout;
parameterInfo->varLayouts.add(additionalVarLayout);
}
@@ -1770,15 +1775,40 @@ static RefPtr<TypeLayout> processEntryPointVaryingParameter(
return structLayout;
}
- else if (auto globalGenericParam = declRef.as<GlobalGenericParamDecl>())
+ else if (auto globalGenericParamDecl = declRef.as<GlobalGenericParamDecl>())
{
- auto genParamTypeLayout = new GenericParamTypeLayout();
- // we should have already populated ProgramLayout::genericEntryPointParams list at this point,
- // so we can find the index of this generic param decl in the list
- genParamTypeLayout->type = type;
- genParamTypeLayout->paramIndex = findGenericParam(context->shared->programLayout->globalGenericParams, globalGenericParam.getDecl());
- genParamTypeLayout->findOrAddResourceInfo(LayoutResourceKind::GenericResource)->count += 1;
- return genParamTypeLayout;
+ auto& layoutContext = context->layoutContext;
+
+ if( auto concreteType = findGlobalGenericSpecializationArg(
+ layoutContext,
+ globalGenericParamDecl) )
+ {
+ // If we know what concrete type has been used to specialize
+ // the global generic type parameter, then we should use
+ // the concrete type instead.
+ //
+ // Note: it should be illegal for the user to use a generic
+ // type parameter in a varying parameter list without giving
+ // it an explicit user-defined semantic. Otherwise, it would be possible
+ // that the concrete type that gets plugged in is a user-defined
+ // `struct` that uses some `SV_` semantics in its definition,
+ // so that any static information about what system values
+ // the entry point uses would be incorrect.
+ //
+ return processEntryPointVaryingParameter(context, concreteType, state, varLayout);
+ }
+ else
+ {
+ // If we don't know a concrete type, then we aren't generating final
+ // code, so the reflection information should show the generic
+ // type parameter.
+ //
+ // We don't make any attempt to assign varying parameter resources
+ // to the generic type, since we can't know how many "slots"
+ // of varying input/output it would consume.
+ //
+ return createTypeLayoutForGlobalGenericTypeParam(layoutContext, type, globalGenericParamDecl);
+ }
}
else if (auto associatedTypeParam = declRef.as<AssocTypeDecl>())
{
@@ -1804,15 +1834,12 @@ static RefPtr<TypeLayout> processEntryPointVaryingParameter(
/// Compute the type layout for a parameter declared directly on an entry point.
static RefPtr<TypeLayout> computeEntryPointParameterTypeLayout(
ParameterBindingContext* context,
- SubstitutionSet typeSubst,
DeclRef<VarDeclBase> paramDeclRef,
RefPtr<VarLayout> paramVarLayout,
EntryPointParameterState& state)
{
- auto paramDeclRefType = GetType(paramDeclRef);
- SLANG_ASSERT(paramDeclRefType);
-
- auto paramType = paramDeclRefType->Substitute(typeSubst).as<Type>();
+ auto paramType = GetType(paramDeclRef);
+ SLANG_ASSERT(paramType);
if( paramDeclRef.getDecl()->HasModifier<HLSLUniformModifier>() )
{
@@ -1940,6 +1967,12 @@ struct ScopeLayoutBuilder
{
m_structLayout->mapVarToLayout.Add(firstVarLayout->varDecl.getDecl(), firstVarLayout);
}
+ }
+
+ void addParameter(
+ RefPtr<VarLayout> varLayout)
+ {
+ _addParameter(varLayout, nullptr);
// Any "pending" items on a field type become "pending" items
// on the overall `struct` type layout.
@@ -1948,42 +1981,58 @@ struct ScopeLayoutBuilder
// `struct` layout logic in `type-layout.cpp`. If this gets any
// more complicated we should see if there is a way to share it.
//
- if( auto fieldPendingDataTypeLayout = firstVarLayout->typeLayout->pendingDataTypeLayout )
+ if( auto fieldPendingDataTypeLayout = varLayout->typeLayout->pendingDataTypeLayout )
{
m_pendingDataTypeLayoutBuilder.beginLayoutIfNeeded(nullptr, m_rules);
- auto fieldPendingDataVarLayout = m_pendingDataTypeLayoutBuilder.addField(firstVarLayout->varDecl, fieldPendingDataTypeLayout);
+ auto fieldPendingDataVarLayout = m_pendingDataTypeLayoutBuilder.addField(varLayout->varDecl, fieldPendingDataTypeLayout);
m_structLayout->pendingDataTypeLayout = m_pendingDataTypeLayoutBuilder.getTypeLayout();
- if( parameterInfo )
- {
- for( auto& varLayout : parameterInfo->varLayouts )
- {
- varLayout->pendingVarLayout = fieldPendingDataVarLayout;
- }
- }
- else
- {
- firstVarLayout->pendingVarLayout = fieldPendingDataVarLayout;
- }
+ varLayout->pendingVarLayout = fieldPendingDataVarLayout;
}
}
void addParameter(
- RefPtr<VarLayout> varLayout)
- {
- _addParameter(varLayout, nullptr);
- }
-
- void addParameter(
ParameterInfo* parameterInfo)
{
SLANG_RELEASE_ASSERT(parameterInfo->varLayouts.getCount() != 0);
auto firstVarLayout = parameterInfo->varLayouts.getFirst();
_addParameter(firstVarLayout, parameterInfo);
- }
+ // Global parameters will have their non-orindary/uniform
+ // pending data handled by the main parameter binding
+ // logic, but we still need to construct a layout
+ // that includes any pending data.
+ //
+ if(auto fieldPendingVarLayout = firstVarLayout->pendingVarLayout)
+ {
+ auto fieldPendingTypeLayout = fieldPendingVarLayout->typeLayout;
+
+ m_pendingDataTypeLayoutBuilder.beginLayoutIfNeeded(nullptr, m_rules);
+ m_structLayout->pendingDataTypeLayout = m_pendingDataTypeLayoutBuilder.getTypeLayout();
+
+ auto fieldUniformLayoutInfo = fieldPendingTypeLayout->FindResourceInfo(LayoutResourceKind::Uniform);
+ LayoutSize fieldUniformSize = fieldUniformLayoutInfo ? fieldUniformLayoutInfo->count : 0;
+ if( fieldUniformSize != 0 )
+ {
+ // Make sure uniform fields get laid out properly...
+
+ UniformLayoutInfo fieldInfo(
+ fieldUniformSize,
+ fieldPendingTypeLayout->uniformAlignment);
+
+ LayoutSize uniformOffset = m_rules->AddStructField(
+ m_pendingDataTypeLayoutBuilder.getStructLayoutInfo(),
+ fieldInfo);
+
+ fieldPendingVarLayout->findOrAddResourceInfo(LayoutResourceKind::Uniform)->index = uniformOffset.getFiniteValue();
+ }
+
+ m_pendingDataTypeLayoutBuilder.getTypeLayout()->fields.add(fieldPendingVarLayout);
+ }
+
+ }
// Add a "simple" parameter that cannot have any user-defined
// register or binding modifiers, so that its layout computation
@@ -2088,21 +2137,48 @@ static ParameterBindingAndKindInfo maybeAllocateConstantBufferBinding(
return info;
}
+ /// Remove resource usage from `typeLayout` that should only be stored per-entry-point.
+ ///
+ /// This is used when constructing the overall layout for an entry point, to make sure
+ /// that certain kinds of resource usage from the entry point don't "leak" into
+ /// the resource usage of the overall program.
+ ///
+static void removePerEntryPointParameterKinds(
+ TypeLayout* typeLayout)
+{
+ typeLayout->removeResourceUsage(LayoutResourceKind::VaryingInput);
+ typeLayout->removeResourceUsage(LayoutResourceKind::VaryingOutput);
+ typeLayout->removeResourceUsage(LayoutResourceKind::ShaderRecord);
+ typeLayout->removeResourceUsage(LayoutResourceKind::HitAttributes);
+ typeLayout->removeResourceUsage(LayoutResourceKind::ExistentialObjectParam);
+ typeLayout->removeResourceUsage(LayoutResourceKind::ExistentialTypeParam);
+}
+
/// Iterate over the parameters of an entry point to compute its requirements.
///
static RefPtr<EntryPointLayout> collectEntryPointParameters(
- ParameterBindingContext* context,
- EntryPoint* entryPoint,
- SubstitutionSet typeSubst)
+ ParameterBindingContext* context,
+ EntryPoint* entryPoint,
+ EntryPoint::EntryPointSpecializationInfo* specializationInfo)
{
DeclRef<FuncDecl> entryPointFuncDeclRef = entryPoint->getFuncDeclRef();
+ // If specialization was applied to the entry point, then the side-band
+ // information that was generated will have a more specialized reference
+ // to the entry point with generic parameters filled in. We should
+ // use that version if it is available.
+ //
+ if(specializationInfo)
+ entryPointFuncDeclRef = specializationInfo->specializedFuncDeclRef;
+
+ auto entryPointType = DeclRefType::Create(context->getLinkage()->getSessionImpl(), entryPointFuncDeclRef);
+
// We will take responsibility for creating and filling in
// the `EntryPointLayout` object here.
//
RefPtr<EntryPointLayout> entryPointLayout = new EntryPointLayout();
entryPointLayout->profile = entryPoint->getProfile();
- entryPointLayout->entryPoint = entryPointFuncDeclRef.getDecl();
+ entryPointLayout->entryPoint = entryPointFuncDeclRef;
// The entry point layout must be added to the output
// program layout so that it can be accessed by reflection.
@@ -2114,19 +2190,6 @@ static RefPtr<EntryPointLayout> collectEntryPointParameters(
//
context->entryPointLayout = entryPointLayout;
- // Note: this isn't really the best place for this logic to sit,
- // but it is the simplest place where we have a direct correspondence
- // between a single `EntryPoint` and its matching `EntryPointLayout`,
- // so we'll use it.
- //
- for( auto taggedUnionType : entryPoint->getTaggedUnionTypes() )
- {
- SLANG_ASSERT(taggedUnionType);
- auto substType = taggedUnionType->Substitute(typeSubst).as<Type>();
- auto typeLayout = createTypeLayout(context->layoutContext, substType);
- entryPointLayout->taggedUnionTypeLayouts.add(typeLayout);
- }
-
// We are going to iterate over the entry-point parameters,
// and while we do so we will go ahead and perform layout/binding
// assignment for two cases:
@@ -2149,22 +2212,33 @@ static RefPtr<EntryPointLayout> collectEntryPointParameters(
ScopeLayoutBuilder scopeBuilder;
scopeBuilder.beginLayout(context);
auto paramsStructLayout = scopeBuilder.m_structLayout;
+ paramsStructLayout->type = entryPointType;
for( auto& shaderParamInfo : entryPoint->getShaderParams() )
{
auto paramDeclRef = shaderParamInfo.paramDeclRef;
+ // Any generic specialization applied to the entry-point function
+ // must also be applied to its parameters.
+ paramDeclRef.substitutions = entryPointFuncDeclRef.substitutions;
+
// When computing layout for an entry-point parameter,
// we want to make sure that the layout context has access
// to the existential type arguments (if any) that were
// provided for the entry-point existential type parameters (if any).
//
- context->layoutContext= context->layoutContext
- .withExistentialTypeArgs(
- entryPoint->getExistentialTypeArgCount(),
- entryPoint->getExistentialTypeArgs())
- .withExistentialTypeSlotsOffsetBy(
- shaderParamInfo.firstExistentialTypeSlot);
+ if(specializationInfo)
+ {
+ auto& existentialSpecializationArgs = specializationInfo->existentialSpecializationArgs;
+ auto genericSpecializationParamCount = entryPoint->getGenericSpecializationParamCount();
+
+ context->layoutContext = context->layoutContext
+ .withSpecializationArgs(
+ existentialSpecializationArgs.getBuffer(),
+ existentialSpecializationArgs.getCount())
+ .withSpecializationArgsOffsetBy(
+ shaderParamInfo.firstSpecializationParamIndex - genericSpecializationParamCount);
+ }
// Any error messages we emit during the process should
// refer to the location of this parameter.
@@ -2183,7 +2257,6 @@ static RefPtr<EntryPointLayout> collectEntryPointParameters(
auto paramTypeLayout = computeEntryPointParameterTypeLayout(
context,
- typeSubst,
paramDeclRef,
paramVarLayout,
state);
@@ -2204,6 +2277,26 @@ static RefPtr<EntryPointLayout> collectEntryPointParameters(
//
scopeBuilder.addSimpleParameter(paramVarLayout);
}
+
+ // We don't want certain kinds of resource usage within an entry
+ // point to "leak" into the overall resource usage of the entry
+ // point and thus lead to offsetting of successive entry points.
+ //
+ // For example if we have a vertex and a fragment entry point
+ // in the some program, and each has one varying input, then
+ // the both the vertex and fragment varying outputs should have
+ // a location/index of zero. It would be bad if the fragment
+ // input (or whichever entry point comes second in the global
+ // ordering) started at location one, because then it wouldn't
+ // line up correctly with any vertex stage outputs.
+ //
+ // We handle this with a bit of a kludge, by removing the
+ // particular `LayoutResourceKind`s that are susceptible to
+ // this problem from the overall resource usage of the entry
+ // point.
+ //
+ removePerEntryPointParameterKinds(scopeBuilder.m_structLayout);
+
entryPointLayout->parametersLayout = scopeBuilder.endLayout();
// For an entry point with a non-`void` return type, we need to process the
@@ -2212,7 +2305,7 @@ static RefPtr<EntryPointLayout> collectEntryPointParameters(
// TODO: Ideally we should make the layout process more robust to empty/void
// types and apply this logic unconditionally.
//
- auto resultType = GetResultType(entryPointFuncDeclRef)->Substitute(typeSubst).as<Type>();
+ auto resultType = GetResultType(entryPointFuncDeclRef);
SLANG_ASSERT(resultType);
if( !resultType->Equals(resultType->getSession()->getVoidType()) )
@@ -2226,7 +2319,7 @@ static RefPtr<EntryPointLayout> collectEntryPointParameters(
auto resultTypeLayout = processEntryPointVaryingParameterDecl(
context,
entryPointFuncDeclRef.getDecl(),
- resultType->Substitute(typeSubst).as<Type>(),
+ resultType,
state,
resultLayout);
@@ -2248,136 +2341,257 @@ static RefPtr<EntryPointLayout> collectEntryPointParameters(
return entryPointLayout;
}
- /// Remove resource usage from `typeLayout` that should only be stored per-entry-point.
- ///
- /// This is used when constructing the layout for an entry point group, to make sure
- /// that certain kinds of resource usage from the entry point don't "leak" into
- /// the resource usage of the group.
- ///
-static void removePerEntryPointParameterKinds(
- TypeLayout* typeLayout)
+ /// Visitor used by `collectGlobalGenericArguments`
+struct CollectGlobalGenericArgumentsVisitor : ComponentTypeVisitor
{
- typeLayout->removeResourceUsage(LayoutResourceKind::VaryingInput);
- typeLayout->removeResourceUsage(LayoutResourceKind::VaryingOutput);
- typeLayout->removeResourceUsage(LayoutResourceKind::ShaderRecord);
- typeLayout->removeResourceUsage(LayoutResourceKind::HitAttributes);
- typeLayout->removeResourceUsage(LayoutResourceKind::ExistentialObjectParam);
- typeLayout->removeResourceUsage(LayoutResourceKind::ExistentialTypeParam);
-}
+ CollectGlobalGenericArgumentsVisitor(
+ ParameterBindingContext* context)
+ : m_context(context)
+ {}
-static void collectParameters(
- ParameterBindingContext* inContext,
- Program* program)
-{
- // All of the parameters in translation units directly
- // referenced in the compile request are part of one
- // logical namespace/"linkage" so that two parameters
- // with the same name should represent the same
- // parameter, and get the same binding(s)
+ ParameterBindingContext* m_context;
- ParameterBindingContext contextData = *inContext;
- auto context = &contextData;
- context->stage = Stage::Unknown;
+ void visitEntryPoint(EntryPoint* entryPoint, EntryPoint::EntryPointSpecializationInfo* specializationInfo) SLANG_OVERRIDE
+ {
+ SLANG_UNUSED(entryPoint);
+ SLANG_UNUSED(specializationInfo);
+ }
- auto globalGenericSubst = program->getGlobalGenericSubstitution();
+ void visitModule(Module* module, Module::ModuleSpecializationInfo* specializationInfo) SLANG_OVERRIDE
+ {
+ SLANG_UNUSED(module);
- // We will start by looking for any global generic type parameters.
+ if(!specializationInfo)
+ return;
- for(RefPtr<Module> module : program->getModuleDependencies())
- {
- for( auto genParamDecl : module->getModuleDecl()->getMembersOfType<GlobalGenericParamDecl>() )
+ for(auto& globalGenericArg : specializationInfo->genericArgs)
{
- collectGlobalGenericParameter(context, genParamDecl);
+ if(auto globalGenericTypeParamDecl = as<GlobalGenericParamDecl>(globalGenericArg.paramDecl))
+ {
+ m_context->shared->programLayout->globalGenericArgs.Add(globalGenericTypeParamDecl, globalGenericArg.argVal);
+ }
}
}
- // 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.
+ void visitComposite(CompositeComponentType* composite, CompositeComponentType::CompositeSpecializationInfo* specializationInfo) SLANG_OVERRIDE
+ {
+ visitChildren(composite, specializationInfo);
+ }
- for(auto& globalParamInfo : program->getShaderParams() )
+ void visitSpecialized(SpecializedComponentType* specialized) SLANG_OVERRIDE
{
- // When computing layout for a global shader parameter,
- // we want to make sure that the layout context has access
- // to the existential type arguments (if any) that were
- // provided for the global existential type parameters (if any).
- //
- context->layoutContext= context->layoutContext
- .withExistentialTypeArgs(
- program->getExistentialTypeArgCount(),
- program->getExistentialTypeArgs())
- .withExistentialTypeSlotsOffsetBy(
- globalParamInfo.firstExistentialTypeSlot);
+ specialized->getBaseComponentType()->acceptVisitor(this, specialized->getSpecializationInfo());
+ }
- collectGlobalScopeParameter(context, globalParamInfo, globalGenericSubst);
+ void visitLegacy(LegacyProgram* legacy, CompositeComponentType::CompositeSpecializationInfo* specializationInfo) SLANG_OVERRIDE
+ {
+ // TODO: Need to do something in this case...
+ SLANG_UNUSED(legacy);
+ SLANG_UNUSED(specializationInfo);
}
+};
+
+ /// Collect an ordered list of all the specialization arguments given for global generic specialization parameters in `program`.
+ ///
+ /// This information is used to accelerate the process of mapping a global generic type
+ /// to its definition during type layout.
+ ///
+static void collectGlobalGenericArguments(
+ ParameterBindingContext* context,
+ ComponentType* program)
+{
+ CollectGlobalGenericArgumentsVisitor visitor(context);
+ program->acceptVisitor(&visitor, nullptr);
+}
- // Next consider parameters for entry points
- for( auto entryPointGroup : program->getEntryPointGroups() )
+ /// Collect information about the (unspecialized) specialization parameters of `program` into `context`.
+ ///
+ /// This function computes the reflection/layout for for the specialization parameters, so
+ /// that they can be exposed to the API user.
+ ///
+static void collectSpecializationParams(
+ ParameterBindingContext* context,
+ ComponentType* program)
+{
+ auto specializationParamCount = program->getSpecializationParamCount();
+ for(Index ii = 0; ii < specializationParamCount; ++ii)
{
- RefPtr<EntryPointGroupLayout> entryPointGroupLayout = new EntryPointGroupLayout();
- entryPointGroupLayout->group = entryPointGroup;
+ auto specializationParam = program->getSpecializationParam(ii);
+ switch(specializationParam.flavor)
+ {
+ case SpecializationParam::Flavor::GenericType:
+ case SpecializationParam::Flavor::GenericValue:
+ {
+ RefPtr<GenericSpecializationParamLayout> paramLayout = new GenericSpecializationParamLayout();
+ paramLayout->decl = specializationParam.object.as<Decl>();
+ context->shared->programLayout->specializationParams.add(paramLayout);
+ }
+ break;
- context->shared->programLayout->entryPointGroups.add(entryPointGroupLayout);
+ case SpecializationParam::Flavor::ExistentialType:
+ case SpecializationParam::Flavor::ExistentialValue:
+ {
+ RefPtr<ExistentialSpecializationParamLayout> paramLayout = new ExistentialSpecializationParamLayout();
+ paramLayout->type = specializationParam.object.as<Type>();
+ context->shared->programLayout->specializationParams.add(paramLayout);
+ }
+ break;
+ default:
+ SLANG_UNEXPECTED("unhandled specialization parameter flavor");
+ break;
+ }
+ }
+}
+
+ /// Visitor used by `collectParameters()`
+struct CollectParametersVisitor : ComponentTypeVisitor
+{
+ CollectParametersVisitor(
+ ParameterBindingContext* context)
+ : m_context(context)
+ {}
- ScopeLayoutBuilder scopeBuilder;
- scopeBuilder.beginLayout(context);
- auto entryPointGroupParamsStructLayout = scopeBuilder.m_structLayout;
+ ParameterBindingContext* m_context;
- // First lay out any shader parameters that belong to the group
- // itself, rather than to its nested entry points.
+ void visitComposite(CompositeComponentType* composite, CompositeComponentType::CompositeSpecializationInfo* specializationInfo) SLANG_OVERRIDE
+ {
+ // The parameters of a composite component type can
+ // be determined by just visiting its children in order.
//
- // This ensures that looking up one of the parameters of the
- // group by index in its parameters truct will Just Work.
+ visitChildren(composite, specializationInfo);
+ }
+
+ void visitSpecialized(SpecializedComponentType* specialized) SLANG_OVERRIDE
+ {
+ // The parameters of a specialized component type
+ // are just those of its base component type, with
+ // appropriate specialization information passed
+ // along.
//
- for( auto groupParam : entryPointGroup->getShaderParams() )
+ visitChildren(specialized);
+
+ // While we are at it, we will also make note of any
+ // tagged-union types that were used as part of the
+ // specialization arguments, since we need to make
+ // sure that their layout information is computed
+ // and made available for IR code generation.
+ //
+ // Note: this isn't really the best place for this logic to sit,
+ // but it is the simplest place where we can collect all the tagged
+ // union types that get referenced by a program.
+ //
+ for( auto taggedUnionType : specialized->getTaggedUnionTypes() )
{
- auto paramDeclRef = groupParam.paramDeclRef;
- auto paramType = GetType(paramDeclRef);
+ SLANG_ASSERT(taggedUnionType);
+ auto substType = taggedUnionType;
+ auto typeLayout = createTypeLayout(m_context->layoutContext, substType);
+ m_context->shared->programLayout->taggedUnionTypeLayouts.add(typeLayout);
+ }
+ }
- RefPtr<VarLayout> paramVarLayout = new VarLayout();
- paramVarLayout->varDecl = paramDeclRef;
- auto paramTypeLayout = createTypeLayout(
- context->layoutContext.with(context->getRulesFamily()->getConstantBufferRules()),
- paramType);
- paramVarLayout->typeLayout = paramTypeLayout;
+ void visitEntryPoint(EntryPoint* entryPoint, EntryPoint::EntryPointSpecializationInfo* specializationInfo) SLANG_OVERRIDE
+ {
+ // An entry point is a leaf case.
+ //
+ // In our current model an entry point does not introduce
+ // any global shader parameters, but in practice it effectively
+ // acts a lot like a single global shader parameter named after
+ // the entry point and with a `struct` type that combines
+ // all the `uniform` entry point parameters.
+ //
+ // Later passes will need to make sure that the entry point
+ // gets enumerated in the right order relative to any global
+ // shader parameters.
+ //
- scopeBuilder.addSimpleParameter(paramVarLayout);
- }
+ ParameterBindingContext contextData = *m_context;
+ auto context = &contextData;
+ context->stage = entryPoint->getStage();
- for(auto entryPoint : entryPointGroup->getEntryPoints())
- {
- // Note: we do not want the entry point group to accumulate
- // locations for varying input/output parameters: those
- // should be specific to each entry point.
- //
- // We address this issue by manually removing any
- // layout information for the relevant resource kinds
- // from the group's layout before adding the parameters
- // of any entry point for layout.
- //
- removePerEntryPointParameterKinds(scopeBuilder.m_structLayout);
+ collectEntryPointParameters(context, entryPoint, specializationInfo);
+ }
- context->stage = entryPoint->getStage();
- auto entryPointLayout = collectEntryPointParameters(context, entryPoint, globalGenericSubst);
+ void visitModule(Module* module, Module::ModuleSpecializationInfo* specializationInfo) SLANG_OVERRIDE
+ {
+ // A single module represents a leaf case for layout.
+ //
+ // We will enumerate the (global) shader parameters declared
+ // in the module and add each to our canonical ordering.
+ //
+ auto paramCount = module->getShaderParamCount();
- auto entryPointParamsLayout = entryPointLayout->parametersLayout;
- auto entryPointParamsTypeLayout = entryPointParamsLayout->typeLayout;
+ ExpandedSpecializationArg* specializationArgs = specializationInfo
+ ? specializationInfo->existentialArgs.getBuffer()
+ : nullptr;
- scopeBuilder.addSimpleParameter(entryPointParamsLayout);
+ for(Index pp = 0; pp < paramCount; ++pp)
+ {
+ auto shaderParamInfo = module->getShaderParam(pp);
+ if(specializationArgs)
+ {
+ m_context->layoutContext = m_context->layoutContext.withSpecializationArgs(
+ specializationArgs,
+ shaderParamInfo.specializationParamCount);
+ specializationArgs += shaderParamInfo.specializationParamCount;
+ }
- entryPointGroupLayout->entryPoints.add(entryPointLayout);
+ collectGlobalScopeParameter(m_context, shaderParamInfo, SubstitutionSet());
}
- removePerEntryPointParameterKinds(scopeBuilder.m_structLayout);
+ }
+
+
+ void visitLegacy(LegacyProgram* legacy, CompositeComponentType::CompositeSpecializationInfo* specializationInfo) SLANG_OVERRIDE
+ {
+ // A legacy program is also a leaf case, and we
+ // can enumerate its parameters directly.
+ //
+ // Note: there is a mismatch here where we really
+ // ought to be tracking specialization arguments
+ // for a `LegacyProgram` akin to how they are
+ // tracked for a `Module`, but right now we try
+ // to do it like a `CompositeComponentType`.
+ // As a result we are just ignoring specialization
+ // information here, which will lead to incorrect
+ // results if somebody every uses specialization
+ // together with the "legacy" program case.
+ //
+ // TODO: eliminate this problem by getting rid of
+ // `LegacyProgram`, rather than spend time trying
+ // to make this corner case actually work.
+ //
+ SLANG_UNUSED(specializationInfo);
- entryPointGroupLayout->parametersLayout = scopeBuilder.endLayout();
+ auto paramCount = legacy->getShaderParamCount();
+ for(Index pp = 0; pp < paramCount; ++pp)
+ {
+ collectGlobalScopeParameter(m_context, legacy->getShaderParam(pp), SubstitutionSet());
+ }
}
- context->entryPointLayout = nullptr;
+};
+
+ /// Recursively collect the global shader parameters and entry points in `program`.
+ ///
+ /// This function is used to establish the global ordering of parameters and
+ /// entry points used for layout.
+ ///
+static void collectParameters(
+ ParameterBindingContext* inContext,
+ ComponentType* program)
+{
+ // All of the parameters in translation units directly
+ // referenced in the compile request are part of one
+ // 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;
+
+ CollectParametersVisitor visitor(context);
+ program->acceptVisitor(&visitor, nullptr);
}
/// Emit a diagnostic about a uniform parameter at global scope.
@@ -2418,6 +2632,302 @@ static int _calcTotalNumUsedRegistersForLayoutResourceKind(ParameterBindingConte
return numUsed;
}
+ /// Keep track of the running global counter for entry points and global parameters visited.
+ ///
+ /// Because of explicit `register` and `[[vk::binding(...)]]` support, parameter binding
+ /// needs to proceed in multiple passes, and each pass must both visit the things that
+ /// need layout (parameters and entry points) in the same order in each pass, and must
+ /// also be able to look up the side-band information that flows between passes.
+ ///
+ /// Currently the `ParameterBindingContext` keeps separate arrays for global shader
+ /// parameters and entry points, but in the global ordering for layout they can be
+ /// interleaved. There is also no simple tracking structure that relates a global
+ /// parameter or entry point to its index in those arrays. Instead, we just keep
+ /// running counters during our passes over the program so that we can easily
+ /// compute the linear index of each entry point and global parameter as it
+ /// is encountered.
+ ///
+struct ParameterBindingVisitorCounters
+{
+ Index entryPointCounter = 0;
+ Index globalParamCounter = 0;
+};
+
+ /// Recursive routine to "complete" all binding for parameters and entry points in `componentType`.
+ ///
+ /// This includes allocation of as-yet-unused register/binding ranges to parameters (which
+ /// will then affect the ranges of registers/bindings that are available to subsequent
+ /// parameters), and imporantly *also* includes allocate of space to any "pending"
+ /// data for interface/existential type parameters/fields.
+ ///
+static void _completeBindings(
+ ParameterBindingContext* context,
+ ComponentType* componentType,
+ ParameterBindingVisitorCounters* ioCounters);
+
+ /// A visitor used by `_completeBindings`.
+ ///
+ /// This visitor walks the structure of a `ComponentType` to ensure that
+ /// any shader parameters (and entry points) it contains that *don't*
+ /// have explicit bindings on them get allocated registers/bindings
+ /// as appropriate.
+ ///
+ /// The main complication of this visitor is how it handles the
+ /// `SpecializedComponentType` case, because a specialized component
+ /// type needs to be handled as an atomic unit that lays out the
+ /// same in all contexts.
+ ///
+struct CompleteBindingsVisitor : ComponentTypeVisitor
+{
+ CompleteBindingsVisitor(ParameterBindingContext* context, ParameterBindingVisitorCounters* counters)
+ : m_context(context)
+ , m_counters(counters)
+ {}
+
+ ParameterBindingContext* m_context;
+ ParameterBindingVisitorCounters* m_counters;
+
+ void visitEntryPoint(EntryPoint* entryPoint, EntryPoint::EntryPointSpecializationInfo* specializationInfo) SLANG_OVERRIDE
+ {
+ SLANG_UNUSED(entryPoint);
+ SLANG_UNUSED(specializationInfo);
+
+ // We compute the index of the entry point in the global ordering,
+ // so we can look up the tracking data in our context. As a result
+ // we don't actually make use of the parameters that were passed in.
+ //
+ auto globalEntryPointIndex = m_counters->entryPointCounter++;
+ auto globalEntryPointInfo = m_context->shared->programLayout->entryPoints[globalEntryPointIndex];
+
+
+ // We mostly treat an entry point like a single shader parameter that
+ // uses its `parametersLayout`.
+ //
+ completeBindingsForParameter(m_context, globalEntryPointInfo->parametersLayout);
+ }
+
+ void visitModule(Module* module, Module::ModuleSpecializationInfo* specializationInfo) SLANG_OVERRIDE
+ {
+ SLANG_UNUSED(specializationInfo);
+ // A module is a leaf case: we just want to visit each parameter.
+ visitLeafParams(module);
+ }
+
+ void visitLegacy(LegacyProgram* legacy, CompositeComponentType::CompositeSpecializationInfo* specializationInfo) SLANG_OVERRIDE
+ {
+ SLANG_UNUSED(specializationInfo);
+ // A legacy program is a leaf case: we just want to visit each parameter.
+ visitLeafParams(legacy);
+ }
+
+ void visitLeafParams(ComponentType* componentType)
+ {
+ auto paramCount = componentType->getShaderParamCount();
+ for(Index ii = 0; ii < paramCount; ++ii)
+ {
+ auto globalParamIndex = m_counters->globalParamCounter++;
+ auto globalParamInfo = m_context->shared->parameters[globalParamIndex];
+
+ completeBindingsForParameter(m_context, globalParamInfo);
+ }
+ }
+
+ void visitComposite(CompositeComponentType* composite, CompositeComponentType::CompositeSpecializationInfo* specializationInfo) SLANG_OVERRIDE
+ {
+ // We just wnat to recurse on the children of the composite in order.
+ visitChildren(composite, specializationInfo);
+ }
+
+ void visitSpecialized(SpecializedComponentType* specialized) SLANG_OVERRIDE
+ {
+ // The handling of a specialized component type here is subtle.
+ //
+ // We do *not* simply recurse on the base component type.
+ // Doing so would ensure that the parameters would get
+ // registers/bindings allocated to them, but it wouldn't
+ // allocate space for the "pending" data related to
+ // existential/interface parameters.
+ //
+ // Instead, we recursive through `_completeBindings`,
+ // which has the job of allocating space for the parameters,
+ // and then for any "pending" data required.
+ //
+ // Handling things this way ensures that a particular
+ // `SpecializedComponentType` gets laid out exactly
+ // the same wherever it gets used, rather than
+ // getting laid out differently when it is placed
+ // into different compositions.
+ //
+ auto base = specialized->getBaseComponentType();
+ _completeBindings(m_context, base, m_counters);
+ }
+};
+
+ /// A visitor used by `_completeBindings`.
+ ///
+ /// This visitor is used to follow up after the `CompleteBindingsVisitor`
+ /// any ensure that any "pending" data required by the parameters that
+ /// got laid out now gets a location.
+ ///
+ /// To make a concrete example:
+ ///
+ /// Texture2D a;
+ /// IThing b;
+ /// Texture2D c;
+ ///
+ /// If these parameters were laid out with `b` specialized to a type
+ /// that contains a single `Texture2D`, then the `CompleteBindingsVisitor`
+ /// would visit `a`, `b`, and then `c` in order. It would give `a` the
+ /// first register/binding available (say, `t0`). It would then make
+ /// a note that due to specialization, `b`, needs a `t` register as well,
+ /// but it *cannot* be allocated just yet, because doing so would change
+ /// the location of `c`, so it is marked as "pending." Then `c` would
+ /// be visited and get `t1`. As a result the registers given to `a`
+ /// and `c` are independent of how `b` gets specialized.
+ ///
+ /// Next, the `FlushPendingDataVisitor` comes through and applies to
+ /// the parameters again. For `a` there is no pending data, but for
+ /// `b` there is a pending request for a `t` register, so it gets allocated
+ /// now (getting `t2`). The `c` parameter then has no pending data, so
+ /// we are done.
+ ///
+ /// *When* the pending data gets flushed is then significant. In general,
+ /// the order in which modules get composed an specialized is signficaint.
+ /// The module above (let's call it `M`) has one specialization parameter
+ /// (for `b`), and if we want to compose it with another module `N` that
+ /// has no specialization parameters, we could compute either:
+ ///
+ /// compose(specialize(M, SomeType), N)
+ ///
+ /// or:
+ ///
+ /// specialize(compose(M,N), SomeType)
+ ///
+ /// In the first case, the "pending" data for `M` gets flushed right after `M`,
+ /// so that `specialize(M,SomeType)` can have a consistent layout
+ /// regardless of how it is used. In the second case, the pending data for
+ /// `M` only gets flushed after `N`'s parameters are allocated, thus guaranteeing
+ /// that the `compose(M,N)` part has a consistent layout regardless of what
+ /// type gets plugged in during specialization.
+ ///
+ /// There are trade-offs to be made by an application about which approach
+ /// to prefer, and the compiler supports either policy choice.
+ ///
+struct FlushPendingDataVisitor : ComponentTypeVisitor
+{
+ FlushPendingDataVisitor(ParameterBindingContext* context, ParameterBindingVisitorCounters* counters)
+ : m_context(context)
+ , m_counters(counters)
+ {}
+
+ ParameterBindingContext* m_context;
+ ParameterBindingVisitorCounters* m_counters;
+
+ void visitEntryPoint(EntryPoint* entryPoint, EntryPoint::EntryPointSpecializationInfo* specializationInfo) SLANG_OVERRIDE
+ {
+ SLANG_UNUSED(entryPoint);
+ SLANG_UNUSED(specializationInfo);
+
+ auto globalEntryPointIndex = m_counters->entryPointCounter++;
+ auto globalEntryPointInfo = m_context->shared->programLayout->entryPoints[globalEntryPointIndex];
+
+ // We need to allocate space for any "pending" data that
+ // appeared in the entry-point parameter list.
+ //
+ _allocateBindingsForPendingData(m_context, globalEntryPointInfo->parametersLayout->pendingVarLayout);
+ }
+
+ void visitModule(Module* module, Module::ModuleSpecializationInfo* specializationInfo) SLANG_OVERRIDE
+ {
+ SLANG_UNUSED(specializationInfo);
+ visitLeafParams(module);
+ }
+
+ void visitLegacy(LegacyProgram* legacy, CompositeComponentType::CompositeSpecializationInfo* specializationInfo) SLANG_OVERRIDE
+ {
+ SLANG_UNUSED(specializationInfo);
+ visitLeafParams(legacy);
+ }
+
+ void visitLeafParams(ComponentType* componentType)
+ {
+ // In the "leaf" case we just allocate space for any
+ // pending data in the parameters, in order.
+ //
+ auto paramCount = componentType->getShaderParamCount();
+ for(Index ii = 0; ii < paramCount; ++ii)
+ {
+ auto globalParamIndex = m_counters->globalParamCounter++;
+ auto globalParamInfo = m_context->shared->parameters[globalParamIndex];
+ auto firstVarLayout = globalParamInfo->varLayouts[0];
+
+ _allocateBindingsForPendingData(m_context, firstVarLayout->pendingVarLayout);
+ }
+ }
+
+ void visitComposite(CompositeComponentType* composite, CompositeComponentType::CompositeSpecializationInfo* specializationInfo) SLANG_OVERRIDE
+ {
+ visitChildren(composite, specializationInfo);
+ }
+
+ void visitSpecialized(SpecializedComponentType* specialized) SLANG_OVERRIDE
+ {
+ // Because `SpecializedComponentType` was a special case for `CompleteBindingsVisitor`,
+ // it ends up being a special case here too.
+ //
+ // The `CompleteBindings...` pass treated a `SpecializedComponentType`
+ // as an atomic unit. Any "pending" data that came from its parameters
+ // will already have been dealt with, so it would be incorrect for
+ // us to recurse into `specialized`.
+ //
+ // Instead, we just need to *skip* `specialized`, since it was
+ // completely handled already. This isn't quite as simple
+ // as just doing nothing, because our passes are using
+ // some global counters to find the absolute/linear index
+ // of each parameter and entry point as it is encountered.
+ // We will simply bump those counters by the number of
+ // parameters and entry points contained under `specialized`,
+ // which is luckily provided by the `ComponentType` API.
+ //
+ m_counters->globalParamCounter += specialized->getShaderParamCount();
+ m_counters->entryPointCounter += specialized->getEntryPointCount();
+ }
+
+};
+
+static void _completeBindings(
+ ParameterBindingContext* context,
+ ComponentType* componentType,
+ ParameterBindingVisitorCounters* ioCounters)
+{
+ ParameterBindingVisitorCounters savedCounters = *ioCounters;
+
+ CompleteBindingsVisitor completeBindingsVisitor(context, ioCounters);
+ componentType->acceptVisitor(&completeBindingsVisitor, nullptr);
+
+ FlushPendingDataVisitor flushVisitor(context, &savedCounters);
+ componentType->acceptVisitor(&flushVisitor, nullptr);
+}
+
+ /// "Complete" binding of parametesr in the given `program`.
+ ///
+ /// Completing binding involves both assigning registers/bindings
+ /// to an parameters that didn't get explicit locations, and then
+ /// also providing locations to any "pending" data that needed
+ /// space allocated (used for existential/interface type parameters).
+ ///
+static void _completeBindings(
+ ParameterBindingContext* context,
+ ComponentType* program)
+{
+ // The process of completing binding has a recursive structure,
+ // so we will immediately delegate to a subroutine that handles
+ // the recursion.
+ //
+ ParameterBindingVisitorCounters counters;
+ _completeBindings(context, program, &counters);
+}
+
RefPtr<ProgramLayout> generateParameterBindings(
TargetProgram* targetProgram,
DiagnosticSink* sink)
@@ -2450,20 +2960,67 @@ RefPtr<ProgramLayout> generateParameterBindings(
context.shared = &sharedContext;
context.layoutContext = layoutContext;
- // Walk through AST to discover all the parameters
+ // We want to start by finding out what (if anything) has
+ // been bound to the global generic parameters of the
+ // program, since we need to know these types to compute
+ // layout for parameters that use the generic type parameters.
+ //
+ collectGlobalGenericArguments(&context, program);
+
+ // Next we want to collect a full listing of all the shader
+ // parameters that need to be considered for layout, along
+ // with all of the entry points, which also need their
+ // parameters laid out and thus act pretty much like global
+ // parameters themselves.
+ //
collectParameters(&context, program);
- // Now walk through the parameters to generate initial binding information
+ // We will also collect basic information on the specialization
+ // parameters exposed by the program.
+ //
+ // Whereas `collectGlobalGenericArguments` was collecting the
+ // concrete types that have been plugged into specialization
+ // parameters, this step is about collecting the *unspecialized*
+ // parameters (if any) for the purposes of reflection.
+ //
+ collectSpecializationParams(&context, program);
+
+ // Once we have a canonical list of all the shader parameters
+ // (and entry points) in need of layout, we will walk through
+ // the parameters that might have explicit binding annotations,
+ // and "reserve" the registers/bindings/etc. that those parameters
+ // declare so that subequent automatic layout steps do not try to
+ // overlap them.
+ //
+ // Along the way we will issue diagnostics if there appear to
+ // be overlapping, conflicting, or inconsistent explicit bindings.
+ //
+ // Note that we do *not* support explicit binding annotations
+ // on entry point parameters, so we only consider global shader
+ // parameters here.
+ //
+ // (Also note that explicit bindings end up being the main
+ // source of complexity in the layout system, and we could greatly
+ // simplify this file by eliminating support for explicit
+ // binding in the future)
+ //
for( auto& parameter : sharedContext.parameters )
{
generateParameterBindings(&context, parameter);
}
- // Determine if there are any global-scope parameters that use `Uniform`
- // resources, and thus need to get packaged into a constant buffer.
+ // Once we have a canonical list of all the parameters, we can
+ // detect if there are any global-scope parameters that make use
+ // of `LayoutResourceKind::Uniform`, since such parameters would
+ // need to be packaged into a "default" constant buffer.
+ // The fxc/dxc compilers support this step, and in reflection
+ // refer to the generated constant buffer as `$Globals`.
+ //
+ // Note that this logic doesn't account for the existance of
+ // "legacy" (non-buffer-bound) uniforms in GLSL for OpenGL.
+ // If we wanted to support legaqcy uniforms we would probably
+ // want to do so through a different feature.
//
- // Note: this doesn't account for GLSL's support for "legacy" uniforms
- // at global scope, which don't get assigned a CB.
bool needDefaultConstantBuffer = false;
for( auto& parameterInfo : sharedContext.parameters )
{
@@ -2525,6 +3082,32 @@ RefPtr<ProgramLayout> generateParameterBindings(
break;
}
}
+
+ // We also need a default space for any entry-point parameters
+ // that consume appropriate resource kinds.
+ //
+ for(auto& entryPoint : sharedContext.programLayout->entryPoints)
+ {
+ auto paramsLayout = entryPoint->parametersLayout;
+ for(auto resInfo : paramsLayout->resourceInfos )
+ {
+ switch(resInfo.kind)
+ {
+ default:
+ break;
+
+ case LayoutResourceKind::RegisterSpace:
+ case LayoutResourceKind::VaryingInput:
+ case LayoutResourceKind::VaryingOutput:
+ case LayoutResourceKind::HitAttributes:
+ case LayoutResourceKind::RayPayload:
+ continue;
+ }
+
+ needDefaultSpace = true;
+ break;
+ }
+ }
}
// If we need a space for default bindings, then allocate it here.
@@ -2575,12 +3158,14 @@ RefPtr<ProgramLayout> generateParameterBindings(
&context,
needDefaultConstantBuffer);
- // Now walk through again to actually give everything
- // ranges of registers...
- for( auto& parameter : sharedContext.parameters )
- {
- completeBindingsForParameter(&context, parameter);
- }
+ // Now that all of the explicit bindings have been dealt with
+ // and we've also allocate any space/buffer that is required
+ // for global-scope parameters, we will go through the
+ // shader parameters and entry points yet again, in order
+ // to actually allocate specific bindings/registers to
+ // parameters and entry points that need them.
+ //
+ _completeBindings(&context, program);
// Next we need to create a type layout to reflect the information
// we have collected, and we will use the `ScopeLayoutBuilder`
@@ -2602,104 +3187,6 @@ RefPtr<ProgramLayout> generateParameterBindings(
cbInfo->index = globalConstantBufferBinding.index;
}
- // After we have laid out all the ordinary global parameters,
- // we need to "flush" out any pending data that was associated with
- // the global scope as part of dealing with interface-type parameters.
- //
- _allocateBindingsForPendingData(&context, globalScopeVarLayout->pendingVarLayout);
-
- // After we have allocated registers/bindings to everything
- // in the global scope we will process the parameters
- // of the entry points.
- //
- // Note: at the moment we are laying out *all* information related to global-scope
- // parameters (including pending data from interface-type parameters) before
- // anything pertaining to entry points. This is a crucial design choice to
- // get right, and we might want to revisit it based on experience.
-
- // In some cases, a user will want to ensure that all the
- // entry points they compile get non-overlapping
- // registers/bindings. E.g., if you have a vertex and fragment
- // shader being compiled together for Vulkan, you probably want distinct
- // bindings for their entry-point `uniform` parametres, so
- // that they can be used together.
- //
- // In other cases, however, a user probably doesn't want us
- // to conservatively allocate non-overlapping bindings.
- // E.g., if they have a bunch of compute shaders in a single
- // file, then they probably want each compute shader to
- // compute its parameter layout "from scratch" as if the
- // others don't exist.
- //
- // The way we handle this is by putting the entry points of a
- // `Program` into groups, and ensuring that within each group
- // we allocate parameters that don't overlap, but we don't
- // worry about overlap across groups.
- //
- for( auto entryPointGroup : sharedContext.programLayout->entryPointGroups )
- {
- // We save off the allocation state as it was before the entry-point
- // group, so that we can restore it to this state after each group.
- //
- // TODO: We probably ought to wrap all the state relevant to allocation
- // of registers/bindings into a single struct/field so that we only
- // have one thing to save/restore here even if new state gets added.
- //
- auto savedGlobalSpaceUsedRangeSets = sharedContext.globalSpaceUsedRangeSets;
- auto savedUsedSpaces = sharedContext.usedSpaces;
-
- // The group will have been allocated a layout that combines the
- // usage of all of the contained entry-points, so we just need to
- // allocate the entry-point group to be placed after all the global-scope
- // parameters.
- //
- auto entryPointGroupParamsLayout = entryPointGroup->parametersLayout;
- completeBindingsForParameter(&context, entryPointGroupParamsLayout);
-
- _allocateBindingsForPendingData(&context, entryPointGroupParamsLayout->pendingVarLayout);
-
- // TODO: Should we add the offset information from the group to
- // the layout information for each entry point (and thence to its parameters)?
- //
- // This seems important if we want to allow clients to conveniently
- // ignore groups when doing their reflection queries.
-
- // Once we've allocated bindigns for the parameters of entry points
- // in the group, we restore the state for tracking what register/bindings
- // are used to where it was before.
- //
- sharedContext.globalSpaceUsedRangeSets = savedGlobalSpaceUsedRangeSets;
- sharedContext.usedSpaces = savedUsedSpaces;
- }
-
- // HACK: we want global parameters to not have to deal with offsetting
- // by the `VarLayout` stored in `globalScopeVarLayout`, so we will scan
- // through and for any global parameter that used "pending" data, we will manually
- // offset all of its resource infos to account for where the global pending data
- // got placed.
- //
- // TODO: A more appropriate solution would be to pass the `globalScopeVarLayout`
- // down into the pass that puts layout information onto global parameters in
- // the IR, and apply the offsetting there.
- //
- for( auto& parameterInfo : sharedContext.parameters )
- {
- for( auto varLayout : parameterInfo->varLayouts )
- {
- auto pendingVarLayout = varLayout->pendingVarLayout;
- if(!pendingVarLayout) continue;
-
- for( auto& resInfo : pendingVarLayout->resourceInfos )
- {
- if( auto globalResInfo = globalScopeVarLayout->pendingVarLayout->FindResourceInfo(resInfo.kind) )
- {
- resInfo.index += globalResInfo->index;
- resInfo.space += globalResInfo->space;
- }
- }
- }
- }
-
programLayout->parametersLayout = globalScopeVarLayout;
{
@@ -2723,7 +3210,7 @@ ProgramLayout* TargetProgram::getOrCreateLayout(DiagnosticSink* sink)
}
void generateParameterBindings(
- Program* program,
+ ComponentType* program,
TargetRequest* targetReq,
DiagnosticSink* sink)
{