summaryrefslogtreecommitdiffstats
path: root/source/slang/slang-parameter-binding.cpp
diff options
context:
space:
mode:
authorYong He <yonghe@outlook.com>2025-10-06 17:21:37 -0700
committerGitHub <noreply@github.com>2025-10-07 00:21:37 +0000
commit6af3381f47e3c22e1657c0e0064fa466e8bde0f6 (patch)
treebe187939b58bd6196fb0b2e5824d5d60f312b6b3 /source/slang/slang-parameter-binding.cpp
parentf4449d9376f0a5c32274fd812d135f3595159bae (diff)
Use symbol alias instead of wrapper synthesis to implement link-time types. (#8603)
This change achieves link-time type resolution with a different mechanism. For `extern struct Foo : IFoo = FooImpl;`, instead of synthesizing a wrapper type `Foo` that has a `FooImpl inner` field and dispatches all interface method calls to `inner.method()`, this PR completely removes this synthesis step, and instead just lower such `extern`/`export` types as `IRSymbolAlias` instructions that is just a reference to the type being wrapped. Then we extend the linker logic to clone the referenced symbol instead of the SymbolAlias insts itself during linking. By doing so, we greatly simply the logic need to support link-time types, and achieves higher robustness by not having to deal with many AST synthesis scenarios. Closes #8554. --------- Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com>
Diffstat (limited to 'source/slang/slang-parameter-binding.cpp')
-rw-r--r--source/slang/slang-parameter-binding.cpp675
1 files changed, 352 insertions, 323 deletions
diff --git a/source/slang/slang-parameter-binding.cpp b/source/slang/slang-parameter-binding.cpp
index 605e50d41..3c4c23a42 100644
--- a/source/slang/slang-parameter-binding.cpp
+++ b/source/slang/slang-parameter-binding.cpp
@@ -2205,386 +2205,413 @@ static RefPtr<TypeLayout> processEntryPointVaryingParameter(
varLayout->flags |= VarLayoutFlag::HasSemantic;
}
- // Scalar and vector types are treated as outputs directly
- if (auto basicType = as<BasicExpressionType>(type))
+ // We use a lambda here to process the parameter based on `type`.
+ // We need to be able to recurse on the lambda if need to translate/resolve
+ // `type` to something else, in that case we simply call the lambda recursively.
+ auto processParamOfType = [&](auto&& processParamOfTypeFunc, Type* type) -> RefPtr<TypeLayout>
{
- return processSimpleEntryPointParameter(context, basicType, state, varLayout);
- }
- else if (auto vectorType = as<VectorExpressionType>(type))
- {
- return processSimpleEntryPointParameter(context, vectorType, state, varLayout);
- }
- // A matrix is processed as if it was an array of rows
- else if (auto matrixType = as<MatrixExpressionType>(type))
- {
- auto foldedRowCountVal =
- context->getTargetProgram()->getProgram()->tryFoldIntVal(matrixType->getRowCount());
- IntegerLiteralValue rowCount = 0;
- if (!foldedRowCountVal)
+ // Scalar and vector types are treated as outputs directly
+ if (auto basicType = as<BasicExpressionType>(type))
{
- rowCount = getIntVal(foldedRowCountVal);
+ return processSimpleEntryPointParameter(context, basicType, state, varLayout);
}
- return processSimpleEntryPointParameter(
- context,
- matrixType,
- state,
- varLayout,
- (int)rowCount);
- }
- else if (auto arrayType = as<ArrayExpressionType>(type))
- {
- // Note: Bad Things will happen if we have an array input
- // without a semantic already being enforced.
- UInt elementCount = 0;
-
- if (!arrayType->isUnsized())
+ else if (auto vectorType = as<VectorExpressionType>(type))
{
- auto intVal = context->getTargetProgram()->getProgram()->tryFoldIntVal(
- arrayType->getElementCount());
- if (intVal)
- elementCount = (UInt)getIntVal(intVal);
+ return processSimpleEntryPointParameter(context, vectorType, state, varLayout);
}
-
- // We use the first element to derive the layout for the element type
- auto elementTypeLayout = processEntryPointVaryingParameter(
- context,
- arrayType->getElementType(),
- state,
- varLayout);
-
- // We still walk over subsequent elements to make sure they consume resources
- // as needed
- for (UInt ii = 1; ii < elementCount; ++ii)
+ // A matrix is processed as if it was an array of rows
+ else if (auto matrixType = as<MatrixExpressionType>(type))
{
- processEntryPointVaryingParameter(context, arrayType->getElementType(), state, nullptr);
+ auto foldedRowCountVal =
+ context->getTargetProgram()->getProgram()->tryFoldIntVal(matrixType->getRowCount());
+ IntegerLiteralValue rowCount = 0;
+ if (!foldedRowCountVal)
+ {
+ rowCount = getIntVal(foldedRowCountVal);
+ }
+ return processSimpleEntryPointParameter(
+ context,
+ matrixType,
+ state,
+ varLayout,
+ (int)rowCount);
}
-
- RefPtr<ArrayTypeLayout> arrayTypeLayout = new ArrayTypeLayout();
- arrayTypeLayout->elementTypeLayout = elementTypeLayout;
- arrayTypeLayout->type = arrayType;
-
- for (auto rr : elementTypeLayout->resourceInfos)
+ else if (auto arrayType = as<ArrayExpressionType>(type))
{
- arrayTypeLayout->findOrAddResourceInfo(rr.kind)->count = rr.count * elementCount;
- }
-
- return arrayTypeLayout;
- }
- else if (auto meshOutputType = as<MeshOutputType>(type))
- {
- // TODO: Ellie, revisit
- // Note: Bad Things will happen if we have an array input
- // without a semantic already being enforced.
+ // Note: Bad Things will happen if we have an array input
+ // without a semantic already being enforced.
+ UInt elementCount = 0;
- // We use the first element to derive the layout for the element type
- auto elementTypeLayout = processEntryPointVaryingParameter(
- context,
- meshOutputType->getElementType(),
- state,
- varLayout);
+ if (!arrayType->isUnsized())
+ {
+ auto intVal = context->getTargetProgram()->getProgram()->tryFoldIntVal(
+ arrayType->getElementCount());
+ if (intVal)
+ elementCount = (UInt)getIntVal(intVal);
+ }
- RefPtr<ArrayTypeLayout> arrayTypeLayout = new ArrayTypeLayout();
- arrayTypeLayout->elementTypeLayout = elementTypeLayout;
- arrayTypeLayout->type = arrayType;
+ // We use the first element to derive the layout for the element type
+ auto elementTypeLayout = processEntryPointVaryingParameter(
+ context,
+ arrayType->getElementType(),
+ state,
+ varLayout);
- // TODO: Ellie, this is probably not the right place to handle this
- // On GLSL the indices type is built in and as such doesn't consume
- // resources.
- if (!isKhronosTarget(context->getTargetRequest()) || !as<IndicesType>(type))
- {
- for (auto rr : elementTypeLayout->resourceInfos)
+ // We still walk over subsequent elements to make sure they consume resources
+ // as needed
+ for (UInt ii = 1; ii < elementCount; ++ii)
{
- // TODO: Ellie, explain why only one slot is consumed here
- arrayTypeLayout->findOrAddResourceInfo(rr.kind)->count = rr.count;
+ processEntryPointVaryingParameter(
+ context,
+ arrayType->getElementType(),
+ state,
+ nullptr);
}
- }
- return arrayTypeLayout;
- }
- else if (auto patchType = as<HLSLPatchType>(type))
- {
- // Similar to the MeshOutput case, a `InputPatch` or `OutputPatch` type is just like an
- // array.
- //
- auto elementTypeLayout = processEntryPointVaryingParameter(
- context,
- patchType->getElementType(),
- state,
- varLayout);
+ RefPtr<ArrayTypeLayout> arrayTypeLayout = new ArrayTypeLayout();
+ arrayTypeLayout->elementTypeLayout = elementTypeLayout;
+ arrayTypeLayout->type = arrayType;
- RefPtr<ArrayTypeLayout> arrayTypeLayout = new ArrayTypeLayout();
- arrayTypeLayout->elementTypeLayout = elementTypeLayout;
- arrayTypeLayout->type = arrayType;
+ for (auto rr : elementTypeLayout->resourceInfos)
+ {
+ arrayTypeLayout->findOrAddResourceInfo(rr.kind)->count = rr.count * elementCount;
+ }
- for (auto rr : elementTypeLayout->resourceInfos)
- {
- arrayTypeLayout->findOrAddResourceInfo(rr.kind)->count = rr.count;
+ return arrayTypeLayout;
}
-
- return arrayTypeLayout;
- }
- // Ignore a bunch of types that don't make sense here...
- else if (const auto subpassType = as<SubpassInputType>(type))
- {
- return nullptr;
- }
- else if (const auto textureType = as<TextureType>(type))
- {
- return nullptr;
- }
- else if (const auto samplerStateType = as<SamplerStateType>(type))
- {
- return nullptr;
- }
- else if (const auto constantBufferType = as<ConstantBufferType>(type))
- {
- return nullptr;
- }
- else if (auto ptrType = as<PtrType>(type))
- {
- SLANG_ASSERT(ptrType->astNodeType == ASTNodeType::PtrType);
-
- auto typeLayout = processSimpleEntryPointParameter(context, ptrType, state, varLayout);
- RefPtr<PointerTypeLayout> ptrTypeLayout = typeLayout.as<PointerTypeLayout>();
-
- // Work out the layout for the value/target type
- auto valueTypeLayout =
- processEntryPointVaryingParameter(context, ptrType->getValueType(), state, varLayout);
- ptrTypeLayout->valueTypeLayout = valueTypeLayout;
- return ptrTypeLayout;
- }
- else if (auto optionalType = as<OptionalType>(type))
- {
- Array<Type*, 2> types =
- makeArray(optionalType->getValueType(), context->getASTBuilder()->getBoolType());
- auto tupleType = context->getASTBuilder()->getTupleType(types.getView());
- return processEntryPointVaryingParameter(context, tupleType, state, varLayout);
- }
- else if (auto tupleType = as<TupleType>(type))
- {
- RefPtr<StructTypeLayout> structLayout = new StructTypeLayout();
- structLayout->type = type;
- for (Index i = 0; i < tupleType->getMemberCount(); i++)
+ else if (auto meshOutputType = as<MeshOutputType>(type))
{
- auto fieldType = tupleType->getMember(i);
- RefPtr<VarLayout> fieldVarLayout = new VarLayout();
-
- // We don't really have a "field" decl, so just use the tuple-typed decl
- // itself as the varDecl of the elements.
- auto fieldDecl = (VarDeclBase*)varLayout->varDecl.getDecl();
- fieldVarLayout->varDecl = fieldDecl;
+ // TODO: Ellie, revisit
+ // Note: Bad Things will happen if we have an array input
+ // without a semantic already being enforced.
- structLayout->fields.add(fieldVarLayout);
-
- auto fieldTypeLayout = processEntryPointVaryingParameterDecl(
+ // We use the first element to derive the layout for the element type
+ auto elementTypeLayout = processEntryPointVaryingParameter(
context,
- fieldDecl,
- fieldType,
+ meshOutputType->getElementType(),
state,
- fieldVarLayout);
+ varLayout);
+
+ RefPtr<ArrayTypeLayout> arrayTypeLayout = new ArrayTypeLayout();
+ arrayTypeLayout->elementTypeLayout = elementTypeLayout;
+ arrayTypeLayout->type = arrayType;
- if (!fieldTypeLayout)
+ // TODO: Ellie, this is probably not the right place to handle this
+ // On GLSL the indices type is built in and as such doesn't consume
+ // resources.
+ if (!isKhronosTarget(context->getTargetRequest()) || !as<IndicesType>(type))
{
- getSink(context)->diagnose(
- varLayout->varDecl,
- Diagnostics::notValidVaryingParameter,
- fieldType);
- continue;
+ for (auto rr : elementTypeLayout->resourceInfos)
+ {
+ // TODO: Ellie, explain why only one slot is consumed here
+ arrayTypeLayout->findOrAddResourceInfo(rr.kind)->count = rr.count;
+ }
}
- fieldVarLayout->typeLayout = fieldTypeLayout;
- // Assign offsets in var layout for each resource kind of the type.
- for (auto fieldTypeResInfo : fieldTypeLayout->resourceInfos)
+ return arrayTypeLayout;
+ }
+ else if (auto patchType = as<HLSLPatchType>(type))
+ {
+ // Similar to the MeshOutput case, a `InputPatch` or `OutputPatch` type is just like an
+ // array.
+ //
+ auto elementTypeLayout = processEntryPointVaryingParameter(
+ context,
+ patchType->getElementType(),
+ state,
+ varLayout);
+
+ RefPtr<ArrayTypeLayout> arrayTypeLayout = new ArrayTypeLayout();
+ arrayTypeLayout->elementTypeLayout = elementTypeLayout;
+ arrayTypeLayout->type = arrayType;
+
+ for (auto rr : elementTypeLayout->resourceInfos)
{
- auto kind = fieldTypeResInfo.kind;
- auto structTypeResInfo = structLayout->findOrAddResourceInfo(kind);
- auto fieldResInfo = fieldVarLayout->findOrAddResourceInfo(kind);
- fieldResInfo->index = structTypeResInfo->count.getFiniteValue();
- structTypeResInfo->count += fieldTypeResInfo.count;
+ arrayTypeLayout->findOrAddResourceInfo(rr.kind)->count = rr.count;
}
- }
- return structLayout;
- }
- // Catch declaration-reference types late in the sequence, since
- // otherwise they will include all of the above cases...
- else if (auto declRefType = as<DeclRefType>(type))
- {
- // If we are trying to get the layout of some extern type, do our best
- // to look it up in other loaded modules and generate the type layout
- // based on that.
- declRefType = context->layoutContext.lookupExternDeclRefType(declRefType);
- auto declRef = declRefType->getDeclRef();
+ return arrayTypeLayout;
+ }
+ // Ignore a bunch of types that don't make sense here...
+ else if (const auto subpassType = as<SubpassInputType>(type))
+ {
+ return nullptr;
+ }
+ else if (const auto textureType = as<TextureType>(type))
+ {
+ return nullptr;
+ }
+ else if (const auto samplerStateType = as<SamplerStateType>(type))
+ {
+ return nullptr;
+ }
+ else if (const auto constantBufferType = as<ConstantBufferType>(type))
+ {
+ return nullptr;
+ }
+ else if (auto ptrType = as<PtrType>(type))
+ {
+ SLANG_ASSERT(ptrType->astNodeType == ASTNodeType::PtrType);
+ auto typeLayout = processSimpleEntryPointParameter(context, ptrType, state, varLayout);
+ RefPtr<PointerTypeLayout> ptrTypeLayout = typeLayout.as<PointerTypeLayout>();
- if (auto structDeclRef = declRef.as<StructDecl>())
+ // Work out the layout for the value/target type
+ auto valueTypeLayout = processEntryPointVaryingParameter(
+ context,
+ ptrType->getValueType(),
+ state,
+ varLayout);
+ ptrTypeLayout->valueTypeLayout = valueTypeLayout;
+ return ptrTypeLayout;
+ }
+ else if (auto optionalType = as<OptionalType>(type))
+ {
+ Array<Type*, 2> types =
+ makeArray(optionalType->getValueType(), context->getASTBuilder()->getBoolType());
+ auto tupleType = context->getASTBuilder()->getTupleType(types.getView());
+ return processEntryPointVaryingParameter(context, tupleType, state, varLayout);
+ }
+ else if (auto tupleType = as<TupleType>(type))
{
RefPtr<StructTypeLayout> structLayout = new StructTypeLayout();
- structLayout->type = declRefType;
-
- // We will recursively walk the fields of a `struct` type
- // to compute layouts for those fields.
- //
- // Along the way, we may find fields with explicit layout
- // annotations, along with fields that have no explicit
- // layout. We will consider it an error to have a mix of
- // the two.
- //
- // TODO: We could support a mix of implicit and explicit
- // layout by performing layout on fields in two passes,
- // much like is done for the global scope. This would
- // complicate layout significantly for little practical
- // benefit, so it is very much a "nice to have" rather
- // than a "must have" feature.
- //
- Decl* firstExplicit = nullptr;
- Decl* firstImplicit = nullptr;
- for (auto field :
- getFields(context->getASTBuilder(), structDeclRef, MemberFilterStyle::Instance))
+ structLayout->type = type;
+ for (Index i = 0; i < tupleType->getMemberCount(); i++)
{
+ auto fieldType = tupleType->getMember(i);
RefPtr<VarLayout> fieldVarLayout = new VarLayout();
- fieldVarLayout->varDecl = field;
+
+ // We don't really have a "field" decl, so just use the tuple-typed decl
+ // itself as the varDecl of the elements.
+ auto fieldDecl = (VarDeclBase*)varLayout->varDecl.getDecl();
+ fieldVarLayout->varDecl = fieldDecl;
structLayout->fields.add(fieldVarLayout);
- structLayout->mapVarToLayout.add(field.getDecl(), fieldVarLayout);
auto fieldTypeLayout = processEntryPointVaryingParameterDecl(
context,
- field.getDecl(),
- getType(context->getASTBuilder(), field),
+ fieldDecl,
+ fieldType,
state,
fieldVarLayout);
if (!fieldTypeLayout)
{
- getSink(context)->diagnose(field, Diagnostics::notValidVaryingParameter, field);
+ getSink(context)->diagnose(
+ varLayout->varDecl,
+ Diagnostics::notValidVaryingParameter,
+ fieldType);
continue;
}
fieldVarLayout->typeLayout = fieldTypeLayout;
- // The field needs to have offset information stored
- // in `fieldVarLayout` for every kind of resource
- // consumed by `fieldTypeLayout`.
- //
+ // Assign offsets in var layout for each resource kind of the type.
for (auto fieldTypeResInfo : fieldTypeLayout->resourceInfos)
{
- // If the field is a Conditional<T, false> type, then it could have 0 size.
- // We should skip this field if it has no use of layout units.
- if (fieldTypeResInfo.count == 0)
- continue;
-
auto kind = fieldTypeResInfo.kind;
-
auto structTypeResInfo = structLayout->findOrAddResourceInfo(kind);
+ auto fieldResInfo = fieldVarLayout->findOrAddResourceInfo(kind);
+ fieldResInfo->index = structTypeResInfo->count.getFiniteValue();
+ structTypeResInfo->count += fieldTypeResInfo.count;
+ }
+ }
+ return structLayout;
+ }
+ // Catch declaration-reference types late in the sequence, since
+ // otherwise they will include all of the above cases...
+ else if (auto declRefType = as<DeclRefType>(type))
+ {
+ // If we are trying to get the layout of some extern type, do our best
+ // to look it up in other loaded modules and generate the type layout
+ // based on that.
+ auto lookedUpType = context->layoutContext.lookupExternDeclRefType(declRefType);
+
+ // If the link-time type resolved to something concrete, process the param as if it is
+ // of the concrete type by recursively calling this lambda.
+ if (type != lookedUpType)
+ return processParamOfTypeFunc(_Move(processParamOfTypeFunc), lookedUpType);
+
+ auto declRef = declRefType->getDeclRef();
+
+ if (auto structDeclRef = declRef.as<StructDecl>())
+ {
+ RefPtr<StructTypeLayout> structLayout = new StructTypeLayout();
+ structLayout->type = declRefType;
+
+ // We will recursively walk the fields of a `struct` type
+ // to compute layouts for those fields.
+ //
+ // Along the way, we may find fields with explicit layout
+ // annotations, along with fields that have no explicit
+ // layout. We will consider it an error to have a mix of
+ // the two.
+ //
+ // TODO: We could support a mix of implicit and explicit
+ // layout by performing layout on fields in two passes,
+ // much like is done for the global scope. This would
+ // complicate layout significantly for little practical
+ // benefit, so it is very much a "nice to have" rather
+ // than a "must have" feature.
+ //
+ Decl* firstExplicit = nullptr;
+ Decl* firstImplicit = nullptr;
+ for (auto field : getFields(
+ context->getASTBuilder(),
+ structDeclRef,
+ MemberFilterStyle::Instance))
+ {
+ RefPtr<VarLayout> fieldVarLayout = new VarLayout();
+ fieldVarLayout->varDecl = field;
+
+ structLayout->fields.add(fieldVarLayout);
+ structLayout->mapVarToLayout.add(field.getDecl(), fieldVarLayout);
+
+ auto fieldTypeLayout = processEntryPointVaryingParameterDecl(
+ context,
+ field.getDecl(),
+ getType(context->getASTBuilder(), field),
+ state,
+ fieldVarLayout);
- auto fieldResInfo = fieldVarLayout->FindResourceInfo(kind);
- if (!fieldResInfo)
+ if (!fieldTypeLayout)
{
- if (!firstImplicit)
- firstImplicit = field.getDecl();
-
- // In the implicit-layout case, we assign the field
- // the next available offset after the fields that
- // have preceded it.
- //
- fieldResInfo = fieldVarLayout->findOrAddResourceInfo(kind);
- fieldResInfo->index = structTypeResInfo->count.getFiniteValue();
- structTypeResInfo->count += fieldTypeResInfo.count;
+ getSink(context)->diagnose(
+ field,
+ Diagnostics::notValidVaryingParameter,
+ field);
+ continue;
}
- else
+ fieldVarLayout->typeLayout = fieldTypeLayout;
+
+ // The field needs to have offset information stored
+ // in `fieldVarLayout` for every kind of resource
+ // consumed by `fieldTypeLayout`.
+ //
+ for (auto fieldTypeResInfo : fieldTypeLayout->resourceInfos)
{
- if (!firstExplicit)
- firstExplicit = field.getDecl();
-
- // In the explicit case, the field already has offset
- // information, and we just need to update the computed
- // size of the `struct` type to account for the field.
- //
- auto fieldEndOffset = fieldResInfo->index + fieldTypeResInfo.count;
- structTypeResInfo->count =
- maximum(structTypeResInfo->count, fieldEndOffset);
+ // If the field is a Conditional<T, false> type, then it could have 0 size.
+ // We should skip this field if it has no use of layout units.
+ if (fieldTypeResInfo.count == 0)
+ continue;
+
+ auto kind = fieldTypeResInfo.kind;
+
+ auto structTypeResInfo = structLayout->findOrAddResourceInfo(kind);
+
+ auto fieldResInfo = fieldVarLayout->FindResourceInfo(kind);
+ if (!fieldResInfo)
+ {
+ if (!firstImplicit)
+ firstImplicit = field.getDecl();
+
+ // In the implicit-layout case, we assign the field
+ // the next available offset after the fields that
+ // have preceded it.
+ //
+ fieldResInfo = fieldVarLayout->findOrAddResourceInfo(kind);
+ fieldResInfo->index = structTypeResInfo->count.getFiniteValue();
+ structTypeResInfo->count += fieldTypeResInfo.count;
+ }
+ else
+ {
+ if (!firstExplicit)
+ firstExplicit = field.getDecl();
+
+ // In the explicit case, the field already has offset
+ // information, and we just need to update the computed
+ // size of the `struct` type to account for the field.
+ //
+ auto fieldEndOffset = fieldResInfo->index + fieldTypeResInfo.count;
+ structTypeResInfo->count =
+ maximum(structTypeResInfo->count, fieldEndOffset);
+ }
}
}
+ if (firstImplicit && firstExplicit)
+ {
+ getSink(context)->diagnose(
+ firstImplicit,
+ Diagnostics::mixingImplicitAndExplicitBindingForVaryingParams,
+ firstImplicit->getName(),
+ firstExplicit->getName());
+ }
+
+ return structLayout;
}
- if (firstImplicit && firstExplicit)
+ else if (auto globalGenericParamDecl = declRef.as<GlobalGenericParamDecl>())
{
- getSink(context)->diagnose(
- firstImplicit,
- Diagnostics::mixingImplicitAndExplicitBindingForVaryingParams,
- firstImplicit->getName(),
- firstExplicit->getName());
- }
-
- return structLayout;
- }
- else if (auto globalGenericParamDecl = declRef.as<GlobalGenericParamDecl>())
- {
- auto& layoutContext = context->layoutContext;
+ auto& layoutContext = context->layoutContext;
- if (auto concreteType = findGlobalGenericSpecializationArg(
- layoutContext,
- globalGenericParamDecl.getDecl()))
+ if (auto concreteType = findGlobalGenericSpecializationArg(
+ layoutContext,
+ globalGenericParamDecl.getDecl()))
+ {
+ // 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.getDecl());
+ }
+ }
+ else if (auto enumDeclRef = declRef.as<EnumDecl>())
{
- // If we know what concrete type has been used to specialize
- // the global generic type parameter, then we should use
- // the concrete type instead.
+ // We handle an enumeration type as its tag type for varying parameters.
+ // This allows enums to be used in vertex output/input similar to their
+ // underlying integer types.
//
- // 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);
+ auto tagType = enumDeclRef.getDecl()->tagType;
+ SLANG_ASSERT(tagType);
+ return processEntryPointVaryingParameter(context, tagType, state, varLayout);
+ }
+ else if (auto associatedTypeParam = declRef.as<AssocTypeDecl>())
+ {
+ RefPtr<TypeLayout> assocTypeLayout = new TypeLayout();
+ assocTypeLayout->type = type;
+ return assocTypeLayout;
}
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.getDecl());
+ SLANG_UNEXPECTED("unhandled type kind");
}
}
- else if (auto enumDeclRef = declRef.as<EnumDecl>())
- {
- // We handle an enumeration type as its tag type for varying parameters.
- // This allows enums to be used in vertex output/input similar to their
- // underlying integer types.
- //
- auto tagType = enumDeclRef.getDecl()->tagType;
- SLANG_ASSERT(tagType);
- return processEntryPointVaryingParameter(context, tagType, state, varLayout);
- }
- else if (auto associatedTypeParam = declRef.as<AssocTypeDecl>())
- {
- RefPtr<TypeLayout> assocTypeLayout = new TypeLayout();
- assocTypeLayout->type = type;
- return assocTypeLayout;
- }
- else
+
+ // If we ran into an error in checking the user's code, then skip this parameter
+ else if (const auto errorType = as<ErrorType>(type))
{
- SLANG_UNEXPECTED("unhandled type kind");
+ return nullptr;
}
- }
- // If we ran into an error in checking the user's code, then skip this parameter
- else if (const auto errorType = as<ErrorType>(type))
- {
- return nullptr;
- }
-
- SLANG_UNEXPECTED("unhandled type kind");
- UNREACHABLE_RETURN(nullptr);
+ SLANG_UNEXPECTED("unhandled type kind");
+ UNREACHABLE_RETURN(nullptr);
+ };
+ return processParamOfType(_Move(processParamOfType), type);
}
/// Compute the type layout for a parameter declared directly on an entry point.
@@ -2606,8 +2633,8 @@ static RefPtr<TypeLayout> computeEntryPointParameterTypeLayout(
LayoutRulesImpl* layoutRules = nullptr;
if (isKhronosTarget(context->getTargetRequest()))
{
- // For Vulkan, entry point uniform parameters are laid out using push constant buffer
- // rules (defaults to std430).
+ // For Vulkan, entry point uniform parameters are laid out using push constant
+ // buffer rules (defaults to std430).
layoutRules = context->getRulesFamily()->getShaderStorageBufferRules(
context->getTargetProgram()->getOptionSet());
}
@@ -3559,15 +3586,16 @@ static void collectParameters(ParameterBindingContext* inContext, ComponentType*
/// Emit a diagnostic about a uniform/ordinary parameter at global scope.
void diagnoseGlobalUniform(SharedParameterBindingContext* sharedContext, VarDeclBase* varDecl)
{
- // Don't emit the implicit global shader parameter warning if the variable is explicitly marked
- // as uniform
+ // Don't emit the implicit global shader parameter warning if the variable is explicitly
+ // marked as uniform
if (!varDecl->hasModifier<HLSLUniformModifier>())
{
getSink(sharedContext)
->diagnose(varDecl, Diagnostics::globalUniformNotExpected, varDecl->getName());
}
- // Always check and warn about binding attributes being ignored, regardless of uniform modifier
+ // Always check and warn about binding attributes being ignored, regardless of uniform
+ // modifier
if (varDecl->findModifier<GLSLBindingAttribute>())
{
sharedContext->m_sink->diagnose(
@@ -3635,7 +3663,8 @@ struct ParameterBindingVisitorCounters
Index globalParamCounter = 0;
};
-/// Recursive routine to "complete" all binding for parameters and entry points in `componentType`.
+/// 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
@@ -3973,8 +4002,8 @@ static bool _calcNeedsDefaultSpace(SharedParameterBindingContext& sharedContext)
continue;
case LayoutResourceKind::Uniform:
{
- // If it's uniform, but we have globals binding defined, we don't need a default
- // space for it as it will go in the global binding specified
+ // If it's uniform, but we have globals binding defined, we don't need a
+ // default space for it as it will go in the global binding specified
if (auto hlslToVulkanOptions =
sharedContext.getTargetProgram()->getHLSLToVulkanLayoutOptions())
{
@@ -4070,8 +4099,8 @@ static void _maybeApplyHLSLToVulkanShifts(
return;
}
- // If the user specified -fvk-b-shift for the default space but not -fvk-bind-global, we want to
- // apply the shift to the global constant buffer.
+ // If the user specified -fvk-b-shift for the default space but not -fvk-bind-global, we
+ // want to apply the shift to the global constant buffer.
if (!vulkanOptions->hasGlobalsBinding())
{
auto globalCBufferShift = vulkanOptions->getShift(
@@ -4117,10 +4146,10 @@ static void _maybeApplyHLSLToVulkanShifts(
// In essence we need to look for HLSL kinds which have inferance.
// We assume all map to Descriptor, and look for descriptor overlaps
- // We know there can't be a clash of HLSL layout kinds previously, otherwise that
- // would have already produced an a warning. We also know the only change is either
- // *all* of a set is shifted or none. That means post a shift there still can't be
- // clash between HLSL types.
+ // We know there can't be a clash of HLSL layout kinds previously, otherwise
+ // that would have already produced an a warning. We also know the only change
+ // is either *all* of a set is shifted or none. That means post a shift there
+ // still can't be clash between HLSL types.
// So clashes can only be between HLSL types and other bindings (regardless)