Skip to content

Commit 4f94dd4

Browse files
author
Tim Foley
authoredMar 9, 2019
Improve support for interfaces as shader parameters (shader-slang#886)
* Improve support for interfaces as shader parameters This change adds two main things over the existing support: 1. It is now possible to plug in concrete types that actually contain (uniform/ordinary) fields for the existential type parameters introduced by interface-type shader parameters. The `interface-shader-param2.slang` test shows that this works. 2. There is a limited amount of support for doing correct layout computation and generating output code that matches that layout, so that interface and ordinary-type fields can be interleaved to a limited extent. The `interface-shader-param3.slang` test confirms this behavior. There are several moving pieces in the change. * When it comes to terminology, we try to draw a more clear distinction between existial type parameters/arguments and existential/object value parametes/arguments. A simple way to look at it is that an `IFoo[3]` shader parameter introduces a single existential type parameter (so that a concrete type argument like `SomeThing` can be plugged in for the `IFoo`) but introduces three existential object/value parameters (to represent the concrete values for the array elements). * At the IR level, we support a few new operations. A `BindExistentialsType` can take a type that is not itself an interface/existential type but which depends on interfaces/existentials (e.g., `ConstantBuffer<IFoo>`) and plug in the concrete types to be used for its existential type slots. * Then a `wrapExistentials` instruction can take a type with all the existentials plugged in (possibly by `BindExistentialsType`) and wrap it into a value of the existential-using type (e.g., turn `ConstantBuffer<SomeThing>` into a `ConstantBuffer<IFoo>`). * The IR passes for doing generic/existential specialization have been updated to be able to desugar uses of these new operations just enough so that a `ConstantBuffer<IFoo>` can be used. * When we specialize an IR parameter of an interface type like `IFoo` based on a concrete type `SomeThing`, we turn the parameter into an `ExistentialBox<SomeThing>` to reflect the fact that we are conceptually referring to `SomeThing` indirectly (it shouldn't be factored into the layout of its surrounding type). * Parameter binding was updated so that it passes along the bound existential type arguments in a `Program` or `EntryPoint` to type layout, so that we can take them into account. The type layout code needs to do a little work to pass the appropriate range of arguments along to sub-fields when computing layout for aggregate types. * Type layout was updated to have a notion of "pending" items, which represent the concrete types of data that are logically being referenced by existential value slots. The basic idea is that these values aren't included in the layout of a type by default, but then they get "flushed" to come after all the non-existential-related data in a constant buffer, parameter block, etc. * The logic for computing a parameter group (`ConstantBuffer` or `ParameterBlock`) layout was updated to always "flush" the pending items on the element type of the group, so that the resource usage of specialized existential slots would be taken into account. * The type legalization pass has been adapted so that we can derive two different passes from it. One does resource-type legalization (which is all that the original pass did). The new pass uses the same basic machinery to legalize `ExistentialBox<T>` types by moving them out of their containing type(s), and then turning them into ordinary variables/parameters of type `T`. Big things missing from this change include: - Nothing is making sure that "pending" items at the global or entry-point level will get proper registers/bindings allocated to them. For the uniform case, all that matters in the current compiler is that we declare them in the right order in the output HLSL/GLSL, but for resources to be supported we will need to compute this layout information and start associating it with the existential/interface-type fields. - Nothing is being done to support `BindExistentials<S, ...>` where `S` is a `struct` type that might have existential-type fields (or nested fields...). Eventually we need to desugar a type like this into a fresh `struct` type that has the same field keys as `S`, but with fields replaced by suitable `BindExistentials` as needed. (The hard part of this would seem to be computing which slots go to which fields). As a practial matter, this missing feature means that interface-type members of `cbuffer` declarations won't work. The current tests carefully avoid both of these problems. They don't declare any buffer/texture fields in the concrete types, and they don't make use of `cbuffer` declarations or `ConstantBuffer`s over structure types with interface-type fields. * fixup: add override to methods * fixup: typos
1 parent 281c67b commit 4f94dd4

25 files changed

+1945
-377
lines changed
 

‎examples/model-viewer/main.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ RefPtr<EntryPoint> loadEntryPoint(
195195
// Along with the entry points themselves, the `Program` object will
196196
// cache information gleaned from Slang's reflection interface. Notably:
197197
//
198-
// * The number of `ParamterBlock`s that the program uses
198+
// * The number of `ParameterBlock`s that the program uses
199199
// * Information about generic (type) parameters
200200
//
201201
struct Program : RefObject

‎slang.h

+43-9
Original file line numberDiff line numberDiff line change
@@ -1303,14 +1303,14 @@ extern "C"
13031303
that has an interface or array-of-interface type introduces an existential
13041304
slot. The number of slots consumed by a shader parameter, and the starting
13051305
slot of each parameter can be queried via the reflection API using
1306-
`SLANG_PARAMETER_CATEGORY_EXISTENTIAL_SLOT`.
1306+
`SLANG_PARAMETER_CATEGORY_EXISTENTIAL_TYPE_PARAM`.
13071307
13081308
In order to generate specialized code, a concrete type needs to be specified
13091309
for each existential slot. This function specifies the name of the type
13101310
(or in general a type *expression*) to use for a specific slot at the
13111311
global scope.
13121312
*/
1313-
SLANG_API SlangResult spSetTypeNameForGlobalExistentialSlot(
1313+
SLANG_API SlangResult spSetTypeNameForGlobalExistentialTypeParam(
13141314
SlangCompileRequest* request,
13151315
int slotIndex,
13161316
char const* typeName);
@@ -1321,14 +1321,14 @@ extern "C"
13211321
that has an interface or array-of-interface type introduces an existential
13221322
slot. The number of slots consumed by a shader parameter, and the starting
13231323
slot of each parameter can be queried via the reflection API using
1324-
`SLANG_PARAMETER_CATEGORY_EXISTENTIAL_SLOT`.
1324+
`SLANG_PARAMETER_CATEGORY_EXISTENTIAL_TYPE_PARAM`.
13251325
13261326
In order to generate specialized code, a concrete type needs to be specified
13271327
for each existential slot. This function specifies the name of the type
13281328
(or in general a type *expression*) to use for a specific slot at the
13291329
entry-point scope.
13301330
*/
1331-
SLANG_API SlangResult spSetTypeNameForEntryPointExistentialSlot(
1331+
SLANG_API SlangResult spSetTypeNameForEntryPointExistentialTypeParam(
13321332
SlangCompileRequest* request,
13331333
int entryPointIndex,
13341334
int slotIndex,
@@ -1554,11 +1554,44 @@ extern "C"
15541554
SLANG_PARAMETER_CATEGORY_CALLABLE_PAYLOAD,
15551555
SLANG_PARAMETER_CATEGORY_SHADER_RECORD,
15561556

1557-
// A parameter of interface or array-of-interface type introduces
1558-
// one existential slot, into which a concrete type must be plugged
1559-
// to enable specialized code generation.
1557+
// An existential type parameter represents a "hole" that
1558+
// needs to be filled with a concrete type to enable
1559+
// generation of specialized code.
15601560
//
1561-
SLANG_PARAMETER_CATEGORY_EXISTENTIAL_SLOT,
1561+
// Consider this example:
1562+
//
1563+
// struct MyParams
1564+
// {
1565+
// IMaterial material;
1566+
// ILight lights[3];
1567+
// };
1568+
//
1569+
// This `MyParams` type introduces two existential type parameters:
1570+
// one for `material` and one for `lights`. Even though `lights`
1571+
// is an array, it only introduces one type parameter, because
1572+
// we need to hae a *single* concrete type for all the array
1573+
// elements to be able to generate specialized code.
1574+
//
1575+
SLANG_PARAMETER_CATEGORY_EXISTENTIAL_TYPE_PARAM,
1576+
1577+
// An existential object parameter represents a value
1578+
// that needs to be passed in to provide data for some
1579+
// interface-type shader paameter.
1580+
//
1581+
// Consider this example:
1582+
//
1583+
// struct MyParams
1584+
// {
1585+
// IMaterial material;
1586+
// ILight lights[3];
1587+
// };
1588+
//
1589+
// This `MyParams` type introduces four existential object parameters:
1590+
// one for `material` and three for `lights` (one for each array
1591+
// element). This is consistent with the number of interface-type
1592+
// "objects" that are being passed through to the shader.
1593+
//
1594+
SLANG_PARAMETER_CATEGORY_EXISTENTIAL_OBJECT_PARAM,
15621595

15631596
//
15641597
SLANG_PARAMETER_CATEGORY_COUNT,
@@ -1939,7 +1972,8 @@ namespace slang
19391972

19401973
ShaderRecord = SLANG_PARAMETER_CATEGORY_SHADER_RECORD,
19411974

1942-
ExistentialSlot = SLANG_PARAMETER_CATEGORY_EXISTENTIAL_SLOT,
1975+
ExistentialTypeParam = SLANG_PARAMETER_CATEGORY_EXISTENTIAL_TYPE_PARAM,
1976+
ExistentialObjectParam = SLANG_PARAMETER_CATEGORY_EXISTENTIAL_OBJECT_PARAM,
19431977

19441978
// DEPRECATED:
19451979
VertexInput = SLANG_PARAMETER_CATEGORY_VERTEX_INPUT,

‎source/slang/check.cpp

+39-22
Original file line numberDiff line numberDiff line change
@@ -9363,12 +9363,15 @@ namespace Slang
93639363
}
93649364

93659365
/// Recursively walk `paramDeclRef` and add any required existential slots to `ioSlots`.
9366-
static void _collectExistentialSlotsRec(
9367-
ExistentialSlots& ioSlots,
9368-
DeclRef<VarDeclBase> paramDeclRef)
9366+
static void _collectExistentialTypeParamsRec(
9367+
ExistentialTypeSlots& ioSlots,
9368+
DeclRef<VarDeclBase> paramDeclRef);
9369+
9370+
/// Recursively walk `type` and discover any required existential type parameters.
9371+
static void _collectExistentialTypeParamsRec(
9372+
ExistentialTypeSlots& ioSlots,
9373+
Type* type)
93699374
{
9370-
auto type = GetType(paramDeclRef);
9371-
93729375
// Whether or not something is an array does not affect
93739376
// the number of existential slots it introduces.
93749377
//
@@ -9377,14 +9380,20 @@ namespace Slang
93779380
type = arrayType->baseType;
93789381
}
93799382

9383+
if( auto parameterGroupType = as<ParameterGroupType>(type) )
9384+
{
9385+
_collectExistentialTypeParamsRec(ioSlots, parameterGroupType->getElementType());
9386+
return;
9387+
}
9388+
93809389
if( auto declRefType = as<DeclRefType>(type) )
93819390
{
93829391
auto typeDeclRef = declRefType->declRef;
93839392
if( auto interfaceDeclRef = typeDeclRef.as<InterfaceDecl>() )
93849393
{
93859394
// Each leaf parameter of interface type adds one slot.
93869395
//
9387-
ioSlots.types.Add(type);
9396+
ioSlots.paramTypes.Add(type);
93889397
}
93899398
else if( auto structDeclRef = typeDeclRef.as<StructDecl>() )
93909399
{
@@ -9396,7 +9405,7 @@ namespace Slang
93969405
if(fieldDeclRef.getDecl()->HasModifier<HLSLStaticModifier>())
93979406
continue;
93989407

9399-
_collectExistentialSlotsRec(ioSlots, fieldDeclRef);
9408+
_collectExistentialTypeParamsRec(ioSlots, fieldDeclRef);
94009409
}
94019410
}
94029411
}
@@ -9406,15 +9415,23 @@ namespace Slang
94069415
// element types.
94079416
}
94089417

9418+
static void _collectExistentialTypeParamsRec(
9419+
ExistentialTypeSlots& ioSlots,
9420+
DeclRef<VarDeclBase> paramDeclRef)
9421+
{
9422+
_collectExistentialTypeParamsRec(ioSlots, GetType(paramDeclRef));
9423+
}
9424+
9425+
94099426
/// Add information about a shader parameter to `ioParams` and `ioSlots`
94109427
static void _collectExistentialSlotsForShaderParam(
94119428
ShaderParamInfo& ioParamInfo,
9412-
ExistentialSlots& ioSlots,
9429+
ExistentialTypeSlots& ioSlots,
94139430
DeclRef<VarDeclBase> paramDeclRef)
94149431
{
9415-
UInt startSlot = ioSlots.types.Count();
9416-
_collectExistentialSlotsRec(ioSlots, paramDeclRef);
9417-
UInt endSlot = ioSlots.types.Count();
9432+
UInt startSlot = ioSlots.paramTypes.Count();
9433+
_collectExistentialTypeParamsRec(ioSlots, paramDeclRef);
9434+
UInt endSlot = ioSlots.paramTypes.Count();
94189435
UInt slotCount = endSlot - startSlot;
94199436

94209437
ioParamInfo.firstExistentialTypeSlot = startSlot;
@@ -10343,13 +10360,13 @@ static bool doesParameterMatch(
1034310360
return program;
1034410361
}
1034510362

10346-
static void _specializeExistentialSlots(
10363+
static void _specializeExistentialTypeParams(
1034710364
Linkage* linkage,
10348-
ExistentialSlots& ioSlots,
10365+
ExistentialTypeSlots& ioSlots,
1034910366
List<RefPtr<Expr>> const& args,
1035010367
DiagnosticSink* sink)
1035110368
{
10352-
UInt slotCount = ioSlots.types.Count();
10369+
UInt slotCount = ioSlots.paramTypes.Count();
1035310370
UInt argCount = args.Count();
1035410371

1035510372
if( slotCount != argCount )
@@ -10362,7 +10379,7 @@ static bool doesParameterMatch(
1036210379

1036310380
for( UInt ii = 0; ii < slotCount; ++ii )
1036410381
{
10365-
auto slotType = ioSlots.types[ii];
10382+
auto slotType = ioSlots.paramTypes[ii];
1036610383
auto argExpr = args[ii];
1036710384

1036810385
auto argType = checkProperType(linkage, TypeExp(argExpr), sink);
@@ -10385,18 +10402,18 @@ static bool doesParameterMatch(
1038510402
return;
1038610403
}
1038710404

10388-
ExistentialSlots::Arg arg;
10405+
ExistentialTypeSlots::Arg arg;
1038910406
arg.type = argType;
1039010407
arg.witness = witness;
1039110408
ioSlots.args.Add(arg);
1039210409
}
1039310410
}
1039410411

10395-
void EntryPoint::_specializeExistentialSlots(
10412+
void EntryPoint::_specializeExistentialTypeParams(
1039610413
List<RefPtr<Expr>> const& args,
1039710414
DiagnosticSink* sink)
1039810415
{
10399-
Slang::_specializeExistentialSlots(getLinkage(), m_existentialSlots, args, sink);
10416+
Slang::_specializeExistentialTypeParams(getLinkage(), m_existentialSlots, args, sink);
1040010417
}
1040110418

1040210419
/// Create a specialization an existing entry point based on generic arguments.
@@ -10489,7 +10506,7 @@ static bool doesParameterMatch(
1048910506
unspecializedEntryPoint->getProfile());
1049010507

1049110508
// Next we need to validate the existential arguments.
10492-
specializedEntryPoint->_specializeExistentialSlots(existentialArgs, sink);
10509+
specializedEntryPoint->_specializeExistentialTypeParams(existentialArgs, sink);
1049310510

1049410511
return specializedEntryPoint;
1049510512
}
@@ -10548,11 +10565,11 @@ static bool doesParameterMatch(
1054810565
}
1054910566
}
1055010567

10551-
void Program::_specializeExistentialSlots(
10568+
void Program::_specializeExistentialTypeParams(
1055210569
List<RefPtr<Expr>> const& args,
1055310570
DiagnosticSink* sink)
1055410571
{
10555-
Slang::_specializeExistentialSlots(getLinkage(), m_globalExistentialSlots, args, sink);
10572+
Slang::_specializeExistentialTypeParams(getLinkage(), m_globalExistentialSlots, args, sink);
1055610573
}
1055710574

1055810575

@@ -10733,7 +10750,7 @@ static bool doesParameterMatch(
1073310750
// unspecialized on first, which is maybe not always desirable.
1073410751
//
1073510752
specializedProgram->_collectShaderParams(sink);
10736-
specializedProgram->_specializeExistentialSlots(globalExistentialArgs, sink);
10753+
specializedProgram->_specializeExistentialTypeParams(globalExistentialArgs, sink);
1073710754

1073810755
return specializedProgram;
1073910756
}

‎source/slang/compiler.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -746,7 +746,7 @@ namespace Slang
746746
flags |= D3DCOMPILE_ENABLE_STRICTNESS;
747747
flags |= D3DCOMPILE_ENABLE_UNBOUNDED_DESCRIPTOR_TABLES;
748748

749-
const String sourcePath = "slang-geneated";// calcTranslationUnitSourcePath(entryPoint->getTranslationUnit());
749+
const String sourcePath = calcSourcePathForEntryPoint(endToEndReq, entryPointIndex);
750750

751751
ComPtr<ID3DBlob> codeBlob;
752752
ComPtr<ID3DBlob> diagnosticsBlob;

‎source/slang/compiler.h

+54-19
Original file line numberDiff line numberDiff line change
@@ -117,23 +117,28 @@ namespace Slang
117117
ComPtr<ISlangBlob> blob;
118118
};
119119

120-
/// Collects information about placeholder "slots" for interface/existential types.
121-
struct ExistentialSlots
120+
/// Collects information about existential type parameters and their arguments.
121+
struct ExistentialTypeSlots
122122
{
123-
/// The existential/interface type associated with each slot.
124-
List<RefPtr<Type>> types;
123+
/// For each type parameter, holds the interface/existential type that constrains it.
124+
List<RefPtr<Type>> paramTypes;
125125

126-
/// Source code for concrete type to plug in for each slot.
127-
// List<String> argStrings;
128-
129-
/// A concrete type argument plus a witness table for its conformance to the desired interface
126+
/// An argument for an existential type parameter.
127+
///
128+
/// Comprises a concrete type and a witness for its conformance to the desired
129+
/// interface/existential type for the corresponding parameter.
130+
///
130131
struct Arg
131132
{
132133
RefPtr<Type> type;
133134
RefPtr<Val> witness;
134135
};
135136

136-
/// Concrete type arguments to plug into each slot
137+
/// Any arguments provided for the existential type parameters.
138+
///
139+
/// It is possible for `args` to be empty even if `paramTypes` is non-empty;
140+
/// that situation represents an unspecialized program or entry point.
141+
///
137142
List<Arg> args;
138143
};
139144

@@ -299,13 +304,28 @@ namespace Slang
299304
Name* name,
300305
Profile profile);
301306

302-
UInt getExistentialSlotCount() { return m_existentialSlots.types.Count(); }
303-
Type* getExistentialSlotType(UInt index) { return m_existentialSlots.types[index]; }
304-
ExistentialSlots::Arg getExistentialSlotArg(UInt index) { return m_existentialSlots.args[index]; }
307+
/// Get the number of existential type parameters for the entry point.
308+
UInt getExistentialTypeParamCount() { return m_existentialSlots.paramTypes.Count(); }
309+
310+
/// Get the existential type parameter at `index`.
311+
Type* getExistentialTypeParam(UInt index) { return m_existentialSlots.paramTypes[index]; }
312+
313+
/// Get the number of arguments supplied for existential type parameters.
314+
///
315+
/// Note that the number of arguments may not match the number of parameters.
316+
/// In particular, an unspecialized entry point may have many parameters, but zero arguments.
317+
UInt getExistentialTypeArgCount() { return m_existentialSlots.args.Count(); }
305318

319+
/// Get the existential type argument (type and witness table) at `index`.
320+
ExistentialTypeSlots::Arg getExistentialTypeArg(UInt index) { return m_existentialSlots.args[index]; }
321+
322+
/// Get an array of all existential type arguments.
323+
ExistentialTypeSlots::Arg const* getExistentialTypeArgs() { return m_existentialSlots.args.Buffer(); }
324+
325+
/// Get an array of all entry-point shader parameters.
306326
List<ShaderParamInfo> const& getShaderParams() { return m_shaderParams; }
307327

308-
void _specializeExistentialSlots(
328+
void _specializeExistentialTypeParams(
309329
List<RefPtr<Expr>> const& args,
310330
DiagnosticSink* sink);
311331

@@ -326,7 +346,7 @@ namespace Slang
326346
DeclRef<FuncDecl> m_funcDeclRef;
327347

328348
/// The existential/interface slots associated with the entry point parameter scope.
329-
ExistentialSlots m_existentialSlots;
349+
ExistentialTypeSlots m_existentialSlots;
330350

331351
/// Information about entry-point parameters
332352
List<ShaderParamInfo> m_shaderParams;
@@ -939,14 +959,29 @@ namespace Slang
939959
///
940960
RefPtr<IRModule> getOrCreateIRModule(DiagnosticSink* sink);
941961

942-
UInt getExistentialSlotCount() { return m_globalExistentialSlots.types.Count(); }
943-
Type* getExistentialSlotType(UInt index) { return m_globalExistentialSlots.types[index]; }
944-
ExistentialSlots::Arg getExistentialSlotArg(UInt index) { return m_globalExistentialSlots.args[index]; }
962+
/// Get the number of existential type parameters for the program.
963+
UInt getExistentialTypeParamCount() { return m_globalExistentialSlots.paramTypes.Count(); }
964+
965+
/// Get the existential type parameter at `index`.
966+
Type* getExistentialTypeParam(UInt index) { return m_globalExistentialSlots.paramTypes[index]; }
967+
968+
/// Get the number of arguments supplied for existential type parameters.
969+
///
970+
/// Note that the number of arguments may not match the number of parameters.
971+
/// In particular, an unspecialized program may have many parameters, but zero arguments.
972+
UInt getExistentialTypeArgCount() { return m_globalExistentialSlots.args.Count(); }
973+
974+
/// Get the existential type argument (type and witness table) at `index`.
975+
ExistentialTypeSlots::Arg getExistentialTypeArg(UInt index) { return m_globalExistentialSlots.args[index]; }
976+
977+
/// Get an array of all existential type arguments.
978+
ExistentialTypeSlots::Arg const* getExistentialTypeArgs() { return m_globalExistentialSlots.args.Buffer(); }
945979

980+
/// Get an array of all global shader parameters.
946981
List<GlobalShaderParamInfo> const& getShaderParams() { return m_shaderParams; }
947982

948983
void _collectShaderParams(DiagnosticSink* sink);
949-
void _specializeExistentialSlots(
984+
void _specializeExistentialTypeParams(
950985
List<RefPtr<Expr>> const& args,
951986
DiagnosticSink* sink);
952987

@@ -972,7 +1007,7 @@ namespace Slang
9721007
RefPtr<Substitutions> m_globalGenericSubst;
9731008

9741009
// The existential/interface slots associated with the global scope.
975-
ExistentialSlots m_globalExistentialSlots;
1010+
ExistentialTypeSlots m_globalExistentialSlots;
9761011

9771012
/// Information about global shader parameters
9781013
List<GlobalShaderParamInfo> m_shaderParams;

0 commit comments

Comments
 (0)