summaryrefslogtreecommitdiffstats
path: root/source/slang/type-layout.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/type-layout.cpp')
-rw-r--r--source/slang/type-layout.cpp174
1 files changed, 160 insertions, 14 deletions
diff --git a/source/slang/type-layout.cpp b/source/slang/type-layout.cpp
index ea5287999..77588036d 100644
--- a/source/slang/type-layout.cpp
+++ b/source/slang/type-layout.cpp
@@ -973,10 +973,6 @@ static SimpleLayoutInfo getParameterGroupLayoutInfo(
}
}
-RefPtr<TypeLayout> createTypeLayout(
- TypeLayoutContext const& context,
- Type* type);
-
static bool isOpenGLTarget(TargetRequest*)
{
// We aren't officially supporting OpenGL right now
@@ -1184,12 +1180,76 @@ RefPtr<TypeLayout> applyOffsetToTypeLayout(
return newTypeLayout;
}
+ /// Take a type layout that might include pending items and fold them into the layout.
+static RefPtr<TypeLayout> flushPendingItems(
+ TypeLayoutContext const& context,
+ RefPtr<TypeLayout> layout)
+{
+ SLANG_UNUSED(context);
+
+ // If there are no pending items on the layout,
+ // then there is nothing to be done.
+ //
+ if(layout->pendingItems.Count() == 0)
+ return layout;
+
+ // We need to compute a new type layout that reflects
+ // the resource usage of the provided `layout`, plus
+ // any resource usage for the pending items.
+ //
+ // TODO: To be correct we should construct a new `TypeLayout`
+ // of the same class, but that would take more work, so
+ // we'll re-use the one we already have... kind of gross...
+ //
+ for( auto pendingItem : layout->pendingItems )
+ {
+ auto itemTypeLayout = pendingItem.getTypeLayout();
+
+ // Any resources used by a pending item should be
+ // billed against the flushed layout we are computing.
+ //
+ // TODO: We need to make this handlde ordinary/uniform
+ // data carefully, so that it respects alignment and
+ // other layout rules for the target.
+ //
+ // TODO: We should only be adding in resource usage
+ // that can be "hidden" by the type of parameter block
+ // being built (e.g., only a `ParameterBlock` that allocates
+ // full `set`s/`space`s can hide the `register`s/`binding`s
+ // used by resource fields).
+ //
+ // TODO: we need to write something back to the item,
+ // which should have a `VarLayout` or something like
+ // that attached to it!
+ //
+ for( auto resInfo : itemTypeLayout->resourceInfos )
+ {
+ layout->addResourceUsage(resInfo);
+ }
+ }
+ layout->pendingItems.Clear();
+
+ return layout;
+}
+
static RefPtr<ParameterGroupTypeLayout> _createParameterGroupTypeLayout(
TypeLayoutContext const& context,
RefPtr<ParameterGroupType> parameterGroupType,
SimpleLayoutInfo parameterGroupInfo,
RefPtr<TypeLayout> rawElementTypeLayout)
{
+ // If there are any "pending" items that need to be laid out in
+ // the element type of the parameter group, then we want to flush
+ // them here.
+ //
+ // TODO: We might need to make this only flush *parts* of the pending
+ // items, based on what the parameter group can absorb, and leave
+ // other parts still pending in the type layout we return...
+ //
+ rawElementTypeLayout = flushPendingItems(
+ context.with(context.getRulesFamily()->getConstantBufferRules()),
+ rawElementTypeLayout);
+
auto parameterGroupRules = context.rules;
RefPtr<ParameterGroupTypeLayout> typeLayout = new ParameterGroupTypeLayout();
@@ -1333,7 +1393,7 @@ static RefPtr<ParameterGroupTypeLayout> _createParameterGroupTypeLayout(
{
// A parameter block type that gets its own register space will only
// include resource usage from the element type when it itself consumes
- // while register spaces.
+ // whole register spaces.
if (auto elementResInfo = rawElementTypeLayout->FindResourceInfo(LayoutResourceKind::RegisterSpace))
{
typeLayout->addResourceUsage(*elementResInfo);
@@ -1378,14 +1438,33 @@ static RefPtr<ParameterGroupTypeLayout> _createParameterGroupTypeLayout(
return typeLayout;
}
- /// Doe we need to wrap the given element type in a constant buffer layout?
+static bool usesResourceKind(RefPtr<TypeLayout> typeLayout, LayoutResourceKind kind)
+{
+ auto resInfo = typeLayout->FindResourceInfo(kind);
+ return resInfo && resInfo->count != 0;
+}
+
+static bool usesOrdinaryData(RefPtr<TypeLayout> typeLayout)
+{
+ return usesResourceKind(typeLayout, LayoutResourceKind::Uniform);
+}
+
+ /// Do we need to wrap the given element type in a constant buffer layout?
static bool needsConstantBuffer(RefPtr<TypeLayout> elementTypeLayout)
{
- auto uniformResInfo = elementTypeLayout->FindResourceInfo(LayoutResourceKind::Uniform);
- if(uniformResInfo && uniformResInfo->count != 0)
+ // We need a constant buffer if the element type has ordinary/uniform data.
+ //
+ if(usesOrdinaryData(elementTypeLayout))
return true;
- // Note: additional cases are expected here, to deal with existential types.
+ // We also need a constant buffer if there are any "pending"
+ // items that need ordinary/uniform data allocated to them.
+ //
+ for( auto pendingItem : elementTypeLayout->pendingItems )
+ {
+ if(usesOrdinaryData(pendingItem.getTypeLayout()))
+ return true;
+ }
return false;
}
@@ -2302,8 +2381,32 @@ static TypeLayoutResult _createTypeLayout(
for (auto field : GetFields(structDeclRef))
{
+ // Static fields shouldn't take part in layout.
+ if(field.getDecl()->HasModifier<HLSLStaticModifier>())
+ continue;
+
+ // The fields of a `struct` type may include existential (interface)
+ // types (including as nested sub-fields), and any types present
+ // in those fields will need to be specialized based on the
+ // input arguments being passed to `_createTypeLayout`.
+ //
+ // We won't know how many type slots each field consumes until
+ // we process it, but we can figure out the starting index for
+ // the slots its will consume by looking at the layout we've
+ // computed so far.
+ //
+ Int baseExistentialSlotIndex = 0;
+ if(auto resInfo = typeLayout->FindResourceInfo(LayoutResourceKind::ExistentialTypeParam))
+ baseExistentialSlotIndex = Int(resInfo->count.getFiniteValue());
+ //
+ // When computing the layout for the field, we will give it access
+ // to all the incoming specialized type slots that haven't already
+ // been consumed/claimed by preceding fields.
+ //
+ auto fieldLayoutContext = context.withExistentialTypeSlotsOffsetBy(baseExistentialSlotIndex);
+
TypeLayoutResult fieldResult = _createTypeLayout(
- context,
+ fieldLayoutContext,
GetType(field).Ptr(),
field.getDecl());
RefPtr<TypeLayout> fieldTypeLayout = fieldResult.layout;
@@ -2384,6 +2487,19 @@ static TypeLayoutResult _createTypeLayout(
structTypeResourceInfo->count += fieldTypeResourceInfo.count;
}
}
+
+ // Fields of a structure type may have existential/interface type,
+ // or nested existential/interface-type fields. When doing layout
+ // for a specialized program, these will show up as "pending" types
+ // that need to be laid out at the end of the surrounding block/container.
+ //
+ // Any pending types on fields of a structure become pending types
+ // on the structure itself.
+ //
+ for( auto pendingItem : fieldTypeLayout->pendingItems )
+ {
+ typeLayout->pendingItems.Add(pendingItem);
+ }
}
rules->EndStructLayout(&info);
@@ -2446,10 +2562,35 @@ static TypeLayoutResult _createTypeLayout(
// represents the indirections needed to reference the
// data to be referenced by this field.
//
- return createSimpleTypeLayout(
- SimpleLayoutInfo(LayoutResourceKind::ExistentialSlot, 1),
- type,
- rules);
+
+ RefPtr<TypeLayout> typeLayout = new TypeLayout();
+ typeLayout->type = type;
+ typeLayout->rules = rules;
+
+ typeLayout->addResourceUsage(LayoutResourceKind::ExistentialTypeParam, 1);
+ typeLayout->addResourceUsage(LayoutResourceKind::ExistentialObjectParam, 1);
+
+ // If there are any concrete types available, the first one will be
+ // the value that should be plugged into the slot we just introduced.
+ //
+ if( context.existentialTypeArgCount )
+ {
+ RefPtr<Type> concreteType = context.existentialTypeArgs[0].type;
+
+ RefPtr<TypeLayout> concreteTypeLayout = createTypeLayout(context, concreteType);
+
+ // Layout for this specialized interface type then results
+ // in a type layout that tracks both the resource usage of the
+ // interface type itself (just the type + value slots introduced
+ // above), plus a "pending" type that represents the value
+ // conceptually pointed to by the interface-type field/variable at runtime.
+ //
+ TypeLayout::PendingItem pendingItem;
+ pendingItem.typeLayout = concreteTypeLayout;
+ typeLayout->pendingItems.Add(pendingItem);
+ }
+
+ return TypeLayoutResult(typeLayout, SimpleLayoutInfo());
}
}
else if (auto errorType = as<ErrorType>(type))
@@ -2487,6 +2628,11 @@ static TypeLayoutResult _createTypeLayout(
//
for( auto caseType : taggedUnionType->caseTypes )
{
+ // Note: A tagged union type is not expected to have any existential/interface type
+ // slots; the case types that are provided must be fully specialized before the union is
+ // formed. Thus we don't need to mess around with existential type slots here the
+ // way we do for the `struct` case.
+
auto caseTypeResult = _createTypeLayout(context, caseType);
RefPtr<TypeLayout> caseTypeLayout = caseTypeResult.layout;
UniformLayoutInfo caseTypeInfo = caseTypeResult.info.getUniformLayout();