summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--source/slang/type-layout.cpp226
-rw-r--r--source/slang/type-layout.h10
-rw-r--r--tests/bindings/array-of-struct-of-resource.hlsl42
3 files changed, 276 insertions, 2 deletions
diff --git a/source/slang/type-layout.cpp b/source/slang/type-layout.cpp
index 9689fde0c..02dc67aac 100644
--- a/source/slang/type-layout.cpp
+++ b/source/slang/type-layout.cpp
@@ -1348,6 +1348,222 @@ int findGenericParam(List<RefPtr<GenericParamLayout>> & genericParameters, Globa
return (int)genericParameters.FindFirst([=](RefPtr<GenericParamLayout> & x) {return x->decl.Ptr() == decl; });
}
+// When constructing a new var layout from an existing one,
+// copy fields to the new var from the old.
+void copyVarLayoutFields(
+ VarLayout* dstVarLayout,
+ VarLayout* srcVarLayout)
+{
+ dstVarLayout->varDecl = srcVarLayout->varDecl;
+ dstVarLayout->typeLayout = srcVarLayout->typeLayout;
+ dstVarLayout->flags = srcVarLayout->flags;
+ dstVarLayout->systemValueSemantic = srcVarLayout->systemValueSemantic;
+ dstVarLayout->systemValueSemanticIndex = srcVarLayout->systemValueSemanticIndex;
+ dstVarLayout->semanticName = srcVarLayout->semanticName;
+ dstVarLayout->semanticIndex = srcVarLayout->semanticIndex;
+ dstVarLayout->stage = srcVarLayout->stage;
+ dstVarLayout->resourceInfos = srcVarLayout->resourceInfos;
+}
+
+// When constructing a new type layout from an existing one,
+// copy fields to the new type from the old.
+void copyTypeLayoutFields(
+ TypeLayout* dstTypeLayout,
+ TypeLayout* srcTypeLayout)
+{
+ dstTypeLayout->type = srcTypeLayout->type;
+ dstTypeLayout->rules = srcTypeLayout->rules;
+ dstTypeLayout->uniformAlignment = srcTypeLayout->uniformAlignment;
+ dstTypeLayout->resourceInfos = srcTypeLayout->resourceInfos;
+}
+
+// Does this layout resource kind require adjustment when used in
+// an array-of-structs fashion?
+bool doesResourceRequireAdjustmentForArrayOfStructs(LayoutResourceKind kind)
+{
+ switch( kind )
+ {
+ case LayoutResourceKind::ConstantBuffer:
+ case LayoutResourceKind::ShaderResource:
+ case LayoutResourceKind::UnorderedAccess:
+ case LayoutResourceKind::SamplerState:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+// Given the type layout for an element of an array, apply any adjustments required
+// based on the element count of the array.
+//
+// The particular case where this matters is when we have an array of an aggregate
+// type that contains resources, since each resource field might need to be at
+// a different offset than we would otherwise expect.
+//
+// For example, given:
+//
+// struct Foo { Texture2D a; Texture2D b; }
+//
+// if we just write:
+//
+// Foo foo;
+//
+// it gets split into:
+//
+// Texture2D foo_a;
+// Texture2D foo_b;
+//
+// we expect `foo_a` to get `register(t0)` and
+// `foo_b` to get `register(t1)`. However, if we instead have an array:
+//
+// Foo foo[10];
+//
+// then we expect it to be split into:
+//
+// Texture2D foo_a[8];
+// Texture2D foo_b[8];
+//
+// and then we expect `foo_b` to get `register(t8)`, rather
+// than `register(t1)`.
+static RefPtr<TypeLayout>maybeAdjustLayoutForArrayElementType(
+ RefPtr<TypeLayout> originalTypeLayout,
+ UInt elementCount)
+{
+ // We will start by looking for cases that we can reject out
+ // of hand.
+
+ // If the original element type layout doesn't use any
+ // resource registers, then we are fine.
+ bool anyResource = false;
+ for( auto resInfo : originalTypeLayout->resourceInfos )
+ {
+ if( doesResourceRequireAdjustmentForArrayOfStructs(resInfo.kind) )
+ {
+ anyResource = true;
+ break;
+ }
+ }
+ if(!anyResource)
+ return originalTypeLayout;
+
+ // Let's look at the type layout we have, and see if there is anything
+ // that we need to do with it.
+ //
+ if( auto originalArrayTypeLayout = originalTypeLayout.As<ArrayTypeLayout>() )
+ {
+ // The element type is itself an array, so we'll need to adjust
+ // *its* element type accordingly.
+ //
+ // We adjust the already-adjusted element type of the inner
+ // array type, so that we pick up adjustments already made:
+ auto originalInnerElementTypeLayout = originalArrayTypeLayout->elementTypeLayout;
+ auto adjustedInnerElementTypeLayout = maybeAdjustLayoutForArrayElementType(
+ originalInnerElementTypeLayout,
+ elementCount);
+
+ // If nothing needed to be changed on the inner element type,
+ // then we are done.
+ if(adjustedInnerElementTypeLayout == originalInnerElementTypeLayout)
+ return originalTypeLayout;
+
+ // Otherwise, we need to construct a new array type layout
+ RefPtr<ArrayTypeLayout> adjustedArrayTypeLayout = new ArrayTypeLayout();
+ adjustedArrayTypeLayout->originalElementTypeLayout = originalInnerElementTypeLayout;
+ adjustedArrayTypeLayout->elementTypeLayout = adjustedInnerElementTypeLayout;
+ adjustedArrayTypeLayout->uniformStride = originalArrayTypeLayout->uniformStride;
+
+ copyTypeLayoutFields(adjustedArrayTypeLayout, originalArrayTypeLayout);
+
+ return adjustedArrayTypeLayout;
+ }
+ else if(auto originalParameterGroupTypeLayout = originalTypeLayout.As<ParameterGroupTypeLayout>() )
+ {
+ auto originalInnerElementTypeLayout = originalParameterGroupTypeLayout->elementVarLayout->typeLayout;
+ auto adjustedInnerElementTypeLayout = maybeAdjustLayoutForArrayElementType(
+ originalInnerElementTypeLayout,
+ elementCount);
+
+ // If nothing needed to be changed on the inner element type,
+ // then we are done.
+ if(adjustedInnerElementTypeLayout == originalInnerElementTypeLayout)
+ return originalTypeLayout;
+
+ // TODO: actually adjust the element type, and create all the required bits and
+ // pieces of layout.
+
+ SLANG_UNIMPLEMENTED_X("array of parameter group");
+ UNREACHABLE_RETURN(originalTypeLayout);
+ }
+ else if(auto originalStructTypeLayout = originalTypeLayout.As<StructTypeLayout>() )
+ {
+ UInt fieldCount = originalStructTypeLayout->fields.Count();
+
+ // Empty struct? Bail out.
+ if(fieldCount == 0)
+ return originalTypeLayout;
+
+ // TODO: we could try to special-case a `struct` type with a single
+ // field that needs no adjustment, just to avoid some extra allocation.
+
+ RefPtr<StructTypeLayout> adjustedStructTypeLayout = new StructTypeLayout();
+ copyTypeLayoutFields(adjustedStructTypeLayout, originalStructTypeLayout);
+
+ Dictionary<RefPtr<VarLayout>, RefPtr<VarLayout>> mapOriginalFieldToAdjusted;
+ for( auto originalField : originalStructTypeLayout->fields )
+ {
+ // Compute the adjusted type for the field
+ auto originalFieldTypeLayout = originalField->typeLayout;
+ auto adjustedFieldTypeLayout = maybeAdjustLayoutForArrayElementType(
+ originalFieldTypeLayout,
+ elementCount);
+
+ // Create an adjusted field variable, that is mostly
+ // a clone of the original field (just with our
+ // adjusted type in place).
+ RefPtr<VarLayout> adjustedField = new VarLayout();
+ copyVarLayoutFields(adjustedField, originalField);
+ adjustedField->typeLayout = adjustedFieldTypeLayout;
+
+ // Finally we get down to the real meat of the change,
+ // which is that the field offsets for any resource-type
+ // fields need to be "adjusted" which amounts to just
+ // multiplying them by the element count of the array.
+
+ for( auto& resInfo : adjustedField->resourceInfos )
+ {
+ if( doesResourceRequireAdjustmentForArrayOfStructs(resInfo.kind) )
+ {
+ resInfo.index *= elementCount;
+ }
+ }
+
+ adjustedStructTypeLayout->fields.Add(adjustedField);
+
+ mapOriginalFieldToAdjusted.Add(originalField, adjustedField);
+ }
+
+ for( auto p : originalStructTypeLayout->mapVarToLayout )
+ {
+ Decl* key = p.Key;
+ RefPtr<VarLayout> originalVal = p.Value;
+ RefPtr<VarLayout> adjustedVal;
+ if( mapOriginalFieldToAdjusted.TryGetValue(originalVal, adjustedVal) )
+ {
+ adjustedStructTypeLayout->mapVarToLayout.Add(key, adjustedVal);
+ }
+ }
+
+ return adjustedStructTypeLayout;
+ }
+ else
+ {
+ // If the leaf type layout isn't some kind of aggregate,
+ // then we can just bail out here.
+ return originalTypeLayout;
+ }
+}
+
SimpleLayoutInfo GetLayoutImpl(
TypeLayoutContext const& context,
Type* type,
@@ -1585,8 +1801,16 @@ SimpleLayoutInfo GetLayoutImpl(
RefPtr<ArrayTypeLayout> typeLayout = new ArrayTypeLayout();
*outTypeLayout = typeLayout;
+ // If we construct an array over an aggregate type that contains
+ // resource fields, we may need to adjust the layout we create
+ // for the element type to
+ RefPtr<TypeLayout> adjustedElementTypeLayout = maybeAdjustLayoutForArrayElementType(
+ elementTypeLayout,
+ elementCount);
+
typeLayout->type = type;
- typeLayout->elementTypeLayout = elementTypeLayout;
+ typeLayout->originalElementTypeLayout = elementTypeLayout;
+ typeLayout->elementTypeLayout = adjustedElementTypeLayout;
typeLayout->rules = rules;
typeLayout->uniformAlignment = arrayUniformInfo.alignment;
diff --git a/source/slang/type-layout.h b/source/slang/type-layout.h
index 904dacd91..ed4c3eda5 100644
--- a/source/slang/type-layout.h
+++ b/source/slang/type-layout.h
@@ -340,7 +340,12 @@ public:
class ArrayTypeLayout : public TypeLayout
{
public:
- // The layout used for the element type
+ // The original type layout for the array elements,
+ // which doesn't include any adjustments based on
+ // resource type splitting.
+ RefPtr<TypeLayout> originalElementTypeLayout;
+
+ // The *adjusted* layout used for the element type
RefPtr<TypeLayout> elementTypeLayout;
// the stride between elements when used in
@@ -380,6 +385,9 @@ public:
// one will appear in `fields`, while all of
// them will be reflected in `mapVarToLayout`.
//
+ // TODO: This should map from a declaration to the *index*
+ // in the array above, rather than to the actual pointer,
+ // so that we
Dictionary<Decl*, RefPtr<VarLayout>> mapVarToLayout;
};
diff --git a/tests/bindings/array-of-struct-of-resource.hlsl b/tests/bindings/array-of-struct-of-resource.hlsl
new file mode 100644
index 000000000..ecf3672c9
--- /dev/null
+++ b/tests/bindings/array-of-struct-of-resource.hlsl
@@ -0,0 +1,42 @@
+//TEST:COMPARE_HLSL:-no-mangle -target dxbc-assembly -profile ps_5_1 -entry main
+
+// Let's first confirm that Spire can reproduce what the
+// HLSL compiler would already do in the simple case (when
+// all shader parameters are actually used).
+
+float4 use(Texture2D t, SamplerState s) { return t.Sample(s, 0.0); }
+
+#ifdef __SLANG__
+
+struct Test
+{
+ Texture2D a;
+ Texture2D b;
+};
+
+Test test[2];
+SamplerState s;
+
+float4 main() : SV_Target
+{
+ return use(test[0].a,s)
+ + use(test[0].b,s)
+ + use(test[1].a,s)
+ + use(test[1].b,s);
+}
+
+#else
+
+Texture2D test_a[2];
+Texture2D test_b[2];
+SamplerState s;
+
+float4 main() : SV_Target
+{
+ return use(test_a[0],s)
+ + use(test_b[0],s)
+ + use(test_a[1],s)
+ + use(test_b[1],s);
+}
+
+#endif