@@ -1149,7 +1149,7 @@ namespace Slang
1149
1149
}
1150
1150
else
1151
1151
{
1152
- SLANG_UNEXPECTED ( " unhandled resource binding type " ) ;
1152
+ return SLANG_BINDING_TYPE_UNKNOWN ;
1153
1153
}
1154
1154
}
1155
1155
@@ -1167,7 +1167,7 @@ namespace Slang
1167
1167
}
1168
1168
else
1169
1169
{
1170
- SLANG_UNEXPECTED ( " unhandled resource binding type " ) ;
1170
+ return SLANG_BINDING_TYPE_UNKNOWN ;
1171
1171
}
1172
1172
}
1173
1173
@@ -1176,6 +1176,22 @@ namespace Slang
1176
1176
Slang::TypeLayout* typeLayout,
1177
1177
LayoutResourceKind kind)
1178
1178
{
1179
+ // If the type or type layout implies a specific binding type
1180
+ // (e.g., a `Texture2D` implies a texture binding), then we
1181
+ // will always favor the binding type implied.
1182
+ //
1183
+ if ( auto bindingType = _calcResourceBindingType (typeLayout) )
1184
+ {
1185
+ if (bindingType != SLANG_BINDING_TYPE_UNKNOWN)
1186
+ return bindingType;
1187
+ }
1188
+
1189
+ // As a fallback, we may look at the kind of resources consumed
1190
+ // by a type layout, and use that to infer the type of binding
1191
+ // used. Note that, for example, a `float4` might represent
1192
+ // multiple different kinds of binding, depending on where/how
1193
+ // it is used (e.g., as a varying parameter, a root constant, etc.).
1194
+ //
1179
1195
switch ( kind )
1180
1196
{
1181
1197
default :
@@ -1194,14 +1210,10 @@ namespace Slang
1194
1210
CASE (VaryingOutput, VARYING_OUTPUT);
1195
1211
CASE (ExistentialObjectParam, EXISTENTIAL_VALUE);
1196
1212
CASE (PushConstantBuffer, PUSH_CONSTANT);
1213
+ CASE (Uniform, INLINE_UNIFORM_DATA);
1197
1214
// TODO: register space
1198
1215
1199
1216
#undef CASE
1200
-
1201
- case LayoutResourceKind::ShaderResource:
1202
- case LayoutResourceKind::UnorderedAccess:
1203
- case LayoutResourceKind::DescriptorTableSlot:
1204
- return _calcResourceBindingType (typeLayout);
1205
1217
}
1206
1218
}
1207
1219
@@ -1299,6 +1311,10 @@ namespace Slang
1299
1311
SlangBindingType bindingType = SLANG_BINDING_TYPE_CONSTANT_BUFFER;
1300
1312
Index spaceOffset = -1 ;
1301
1313
LayoutResourceKind kind = LayoutResourceKind::None;
1314
+
1315
+ // TODO: It is unclear if this should be looking at the resource
1316
+ // usage of the parameter group, or of its "container" layout.
1317
+ //
1302
1318
for (auto & resInfo : parameterGroupTypeLayout->resourceInfos )
1303
1319
{
1304
1320
kind = resInfo.kind ;
@@ -1307,10 +1323,18 @@ namespace Slang
1307
1323
default :
1308
1324
continue ;
1309
1325
1326
+ // Note: the only case where a parameter group should
1327
+ // reflect as consuming `Uniform` storage is on CPU/CUDA,
1328
+ // where that will be the only resource it contains.
1329
+ //
1330
+ // TODO: If we ever support targets that don't have
1331
+ // constant buffers at all, this logic would be questionable.
1332
+ //
1310
1333
case LayoutResourceKind::ConstantBuffer:
1311
1334
case LayoutResourceKind::PushConstantBuffer:
1312
1335
case LayoutResourceKind::RegisterSpace:
1313
1336
case LayoutResourceKind::DescriptorTableSlot:
1337
+ case LayoutResourceKind::Uniform:
1314
1338
break ;
1315
1339
}
1316
1340
@@ -1345,39 +1369,79 @@ namespace Slang
1345
1369
// It is possible that the sub-object has descriptor ranges
1346
1370
// that will need to be exposed upward, into the parent.
1347
1371
//
1372
+ // Note: it is a subtle point, but we are only going to expose
1373
+ // *descriptor ranges* upward and not *binding ranges*. The
1374
+ // distinction here comes down to:
1375
+ //
1376
+ // * Descriptor ranges are used to describe the entries that
1377
+ // must be allocated in one or more API descriptor sets to
1378
+ // physically hold a value of a given type (layout).
1379
+ //
1380
+ // * Binding ranges are used to describe the entries that must
1381
+ // be allocated in an application shader object to logically
1382
+ // hold a value of a given type (layout).
1383
+ //
1384
+ // In practice, a binding range might logically belong to a
1385
+ // sub-object, but physically belong to a parent. Consider:
1386
+ //
1387
+ // cbuffer C { Texture2D a; float b; }
1388
+ //
1389
+ // Independent of the API we compile for, we expect the global
1390
+ // scope to have a sub-object for `C`, and for that sub-object
1391
+ // to have a binding range for `a` (that is, we bind the texture
1392
+ // into the sub-object).
1393
+ //
1394
+ // When compiling for D3D12 or Vulkan, we expect that the global
1395
+ // scope must have two descriptor ranges for `C`: one for the
1396
+ // constant buffer itself, and another for the texture `a`.
1397
+ // The reason for this is that `a` needs to be bound as part
1398
+ // of a descriptor set, and `C` doesn't create/allocate its own
1399
+ // descriptor set(s).
1400
+ //
1401
+ // When compiling for CPU or CUDA, we expect that the global scope
1402
+ // will have a descriptor range for `C` but *not* one for `C.a`,
1403
+ // because the physical storage for `C.a` is provided by the
1404
+ // memory allocation for `C` itself.
1405
+
1348
1406
if ( spaceOffset != -1 )
1349
1407
{
1408
+ // The logic here assumes that when a parameter group consumes
1409
+ // resources that must "leak" into the outer scope (including
1410
+ // reosurces consumed by the group "container"), those resources
1411
+ // will amount to descriptor ranges that are part of the same
1412
+ // descriptor set.
1413
+ //
1414
+ // (If the contents of a group consume whole spaces/sets, then
1415
+ // those resources will be accounted for separately).
1416
+ //
1350
1417
Int descriptorSetIndex = _findOrAddDescriptorSet (spaceOffset);
1351
1418
auto descriptorSet = m_extendedInfo->m_descriptorSets [descriptorSetIndex];
1352
1419
auto firstDescriptorRangeIndex = descriptorSet->descriptorRanges .getCount ();
1353
1420
1354
- // TODO: We need to recursively add descriptor ranges (but not binding
1355
- // ranges!) for anything in the element type that "leaks" into
1356
- // the surrounding context.
1421
+ // First, we need to deal with any descriptor ranges that are
1422
+ // introduced by the "container" type itself.
1357
1423
//
1358
1424
switch (kind)
1359
1425
{
1426
+ // If the parameter group was allocated to consume one or
1427
+ // more whole register spaces/sets, then nothing should
1428
+ // leak through that is measured in descriptor sets.
1429
+ //
1360
1430
case LayoutResourceKind::RegisterSpace:
1361
- case LayoutResourceKind::Uniform:
1362
1431
case LayoutResourceKind::None:
1363
1432
break ;
1364
1433
1365
1434
default :
1366
1435
{
1367
- // This means we are in the constant-buffer-like case
1368
- // (even if the user wrote `ParameterBlock<T>`), and any
1369
- // resource usage inside the element type should "leak"
1370
- // out to the parent scope.
1371
- //
1372
- // It *also* means we should add a suitable descriptor
1373
- // range if one is required for the "container" type.
1436
+ // In a constant-buffer-like case, then all the (non-space/set) resource
1437
+ // usage of the "container" should be reflected as descriptor
1438
+ // ranges in the parent scope.
1374
1439
//
1375
1440
for (auto resInfo : parameterGroupTypeLayout->containerVarLayout ->typeLayout ->resourceInfos )
1376
1441
{
1377
1442
switch ( resInfo.kind )
1378
1443
{
1379
1444
case LayoutResourceKind::RegisterSpace:
1380
- case LayoutResourceKind::Uniform:
1381
1445
continue ;
1382
1446
1383
1447
default :
@@ -1400,14 +1464,53 @@ namespace Slang
1400
1464
1401
1465
descriptorSet->descriptorRanges .add (descriptorRange);
1402
1466
}
1467
+ }
1468
+
1469
+ }
1403
1470
1404
- // Now we need to recursively walk the element type and add all its
1405
- // descriptor ranges, so that they can be used for binding in
1406
- // the parent.
1471
+ // Second, we need to consider resource usage from the "element"
1472
+ // type that might leak through to the parent.
1473
+ //
1474
+ switch (kind)
1475
+ {
1476
+ // If the parameter group was allocated as a full register space/set,
1477
+ // *or* if it was allocated as ordinary uniform storage (likely
1478
+ // because it was compiled for CPU/CUDA), then there should
1479
+ // be no "leakage" of descriptor ranges from the element type
1480
+ // to the parent.
1481
+ //
1482
+ case LayoutResourceKind::RegisterSpace:
1483
+ case LayoutResourceKind::Uniform:
1484
+ case LayoutResourceKind::None:
1485
+ break ;
1486
+
1487
+ default :
1488
+ {
1489
+ // If we are in the constant-buffer-like case, on an API
1490
+ // where constant bufers "leak" resource usage to the
1491
+ // outer context, then we need to add the descriptor ranges
1492
+ // implied by the element type.
1493
+ //
1494
+ // HACK: We enumerate these nested ranges by recurisvely
1495
+ // calling `addRangesRec`, which adds all of descriptor ranges,
1496
+ // binding ranges, and sub-object ranges, and then we trim
1497
+ // the lists we don't actually care about as a post-process.
1498
+ //
1499
+ // TODO: We could try to consider a model where we first
1500
+ // query the extended layout information of the element
1501
+ // type (which might already be cached) and then enumerate
1502
+ // the descriptor ranges and copy them over.
1407
1503
//
1408
- // We have this a bit by collecting both binding ranges and
1409
- // descriptor ranges, and then throwing away the binding ranges
1410
- // from the element type.
1504
+ // TODO: It is possible that there could be cases where
1505
+ // some, but not all, of the nested descriptor ranges ought
1506
+ // to be enumerated here. In that case we might have to introduce
1507
+ // a kind of "mask" parameter that is passed down into
1508
+ // the recursive call so that only the appropriate ranges
1509
+ // get added.
1510
+
1511
+ // We need to add a link to the "path" that is used when looking
1512
+ // up binding information, to ensure that the descriptor ranges
1513
+ // that get enumerated here have correct register/binding offsets.
1411
1514
//
1412
1515
BindingRangePathLink elementPath (path, parameterGroupTypeLayout->elementVarLayout );
1413
1516
@@ -1489,7 +1592,8 @@ namespace Slang
1489
1592
auto & resInfo = typeLayout->resourceInfos [0 ];
1490
1593
LayoutResourceKind kind = resInfo.kind ;
1491
1594
1492
- if (kind == LayoutResourceKind::Uniform)
1595
+ auto bindingType = _calcBindingType (typeLayout, kind);
1596
+ if (bindingType == SLANG_BINDING_TYPE_INLINE_UNIFORM_DATA)
1493
1597
{
1494
1598
// We do not consider uniform resource usage
1495
1599
// in the ranges we compute.
@@ -1504,7 +1608,6 @@ namespace Slang
1504
1608
// This leaf field will map to a single binding range and,
1505
1609
// if it is appropriate, a single descriptor range.
1506
1610
//
1507
- auto bindingType = _calcBindingType (typeLayout, kind);
1508
1611
auto count = resInfo.count * multiplier;
1509
1612
auto indexOffset = _calcIndexOffset (path, kind);
1510
1613
auto spaceOffset = _calcSpaceOffset (path, kind);
0 commit comments