@@ -1172,6 +1172,35 @@ namespace Slang
1172
1172
}
1173
1173
}
1174
1174
1175
+ SlangBindingType _calcBindingType (
1176
+ LayoutResourceKind kind)
1177
+ {
1178
+ switch ( kind )
1179
+ {
1180
+ default :
1181
+ return SLANG_BINDING_TYPE_UNKNOWN;
1182
+
1183
+ // Some cases of `LayoutResourceKind` can be mapped
1184
+ // directly to a `BindingType` because there is only
1185
+ // one case of types that have that resource kind.
1186
+
1187
+ #define CASE (FROM, TO ) \
1188
+ case LayoutResourceKind::FROM: return SLANG_BINDING_TYPE_##TO
1189
+
1190
+ CASE (ConstantBuffer, CONSTANT_BUFFER);
1191
+ CASE (SamplerState, SAMPLER);
1192
+ CASE (VaryingInput, VARYING_INPUT);
1193
+ CASE (VaryingOutput, VARYING_OUTPUT);
1194
+ CASE (ExistentialObjectParam, EXISTENTIAL_VALUE);
1195
+ CASE (PushConstantBuffer, PUSH_CONSTANT);
1196
+ CASE (Uniform, INLINE_UNIFORM_DATA);
1197
+ // TODO: register space
1198
+
1199
+ #undef CASE
1200
+ }
1201
+ }
1202
+
1203
+
1175
1204
1176
1205
SlangBindingType _calcBindingType (
1177
1206
Slang::TypeLayout* typeLayout,
@@ -1193,29 +1222,7 @@ namespace Slang
1193
1222
// multiple different kinds of binding, depending on where/how
1194
1223
// it is used (e.g., as a varying parameter, a root constant, etc.).
1195
1224
//
1196
- switch ( kind )
1197
- {
1198
- default :
1199
- return SLANG_BINDING_TYPE_UNKNOWN;
1200
-
1201
- // Some cases of `LayoutResourceKind` can be mapped
1202
- // directly to a `BindingType` because there is only
1203
- // one case of types that have that resource kind.
1204
-
1205
- #define CASE (FROM, TO ) \
1206
- case LayoutResourceKind::FROM: return SLANG_BINDING_TYPE_##TO
1207
-
1208
- CASE (ConstantBuffer, CONSTANT_BUFFER);
1209
- CASE (SamplerState, SAMPLER);
1210
- CASE (VaryingInput, VARYING_INPUT);
1211
- CASE (VaryingOutput, VARYING_OUTPUT);
1212
- CASE (ExistentialObjectParam, EXISTENTIAL_VALUE);
1213
- CASE (PushConstantBuffer, PUSH_CONSTANT);
1214
- CASE (Uniform, INLINE_UNIFORM_DATA);
1215
- // TODO: register space
1216
-
1217
- #undef CASE
1218
- }
1225
+ return _calcBindingType (kind);
1219
1226
}
1220
1227
1221
1228
static DeclRefType* asInterfaceType (Type* type)
@@ -1538,147 +1545,186 @@ namespace Slang
1538
1545
}
1539
1546
else if (asInterfaceType (typeLayout->type ))
1540
1547
{
1541
- // An `interface` type should introduce a sub-object range,
1542
- // with no concrete descriptor ranges to store its value
1543
- // (since we don't know until runtime what type of
1544
- // value will be plugged in).
1548
+ // An `interface` type should introduce a binding range and a matching
1549
+ // sub-object range.
1550
+ //
1551
+ // We currently do *not* allocate any descriptor ranges to represent
1552
+ // an interface-type field, since the only direct storage required
1553
+ // is all uniform/ordinary data.
1545
1554
//
1546
-
1547
- LayoutResourceKind kind = LayoutResourceKind::ExistentialObjectParam;
1548
- auto count = multiplier;
1549
- auto spaceOffset = _calcSpaceOffset (path, kind);
1550
-
1551
- Int descriptorSetIndex = _findOrAddDescriptorSet (spaceOffset);
1552
- auto descriptorSet = m_extendedInfo->m_descriptorSets [descriptorSetIndex];
1553
-
1554
1555
TypeLayout::ExtendedInfo::BindingRangeInfo bindingRange;
1555
1556
bindingRange.leafTypeLayout = typeLayout;
1556
1557
bindingRange.bindingType = SLANG_BINDING_TYPE_EXISTENTIAL_VALUE;
1557
1558
bindingRange.count = multiplier;
1558
- bindingRange.descriptorSetIndex = descriptorSetIndex ;
1559
- bindingRange.firstDescriptorRangeIndex = descriptorSet-> descriptorRanges . getCount () ;
1560
- bindingRange.descriptorRangeCount = 1 ;
1559
+ bindingRange.descriptorSetIndex = 0 ;
1560
+ bindingRange.descriptorRangeCount = 0 ;
1561
+ bindingRange.firstDescriptorRangeIndex = 0 ;
1561
1562
1562
1563
TypeLayout::ExtendedInfo::SubObjectRangeInfo subObjectRange;
1563
1564
subObjectRange.bindingRangeIndex = m_extendedInfo->m_bindingRanges .getCount ();
1564
1565
1566
+ // TODO: if we have "pending" layout information that tells us where
1567
+ // data for the sub-object range has been allocated, then we need
1568
+ // a way to reference that data here.
1569
+
1565
1570
m_extendedInfo->m_bindingRanges .add (bindingRange);
1566
1571
m_extendedInfo->m_subObjectRanges .add (subObjectRange);
1567
1572
}
1568
- // TODO: We need to handle `interface` types here, because they are
1569
- // another case that introduces a "sub-object" for the purposes of
1570
- // application-side allocation.
1571
- //
1572
- // TODO: There are a few cases of "leaf" fields that might
1573
- // still result in multiple descriptors (or at least multiple
1574
- // `LayoutResourceKind`s) depending on the target.
1575
- //
1576
- // For eample, combined texture-sampler types should be treated
1577
- // as "leaf" fields for this code (since a portable engine would
1578
- // need to abstract over them), but would map to two descriptors
1579
- // on targets that don't actually support combining them.
1580
1573
else
1581
1574
{
1582
- Int resourceKindCount = typeLayout->resourceInfos .getCount ();
1583
- if (resourceKindCount == 0 )
1575
+ // Here we have the catch-all case that handles "leaf" fields
1576
+ // that should never introduce a sub-object range, but might
1577
+ // need to introduce a binding range and descriptor ranges.
1578
+ //
1579
+ // First, we want to determine what type of binding this
1580
+ // leaf field should map to, if any. We being by querying
1581
+ // the type itself, since there are many distinct descriptor
1582
+ // types for textures/buffers that can only be determined
1583
+ // by type, rather than by a `LayoutResourceKind`.
1584
+ //
1585
+ auto bindingType = _calcResourceBindingType (typeLayout);
1586
+
1587
+ // It is possible that the type alone isn't enough to tell
1588
+ // us a specific binding type, at which point we need to
1589
+ // start looking at the actual resources the type layout
1590
+ // consumes.
1591
+ //
1592
+ if (bindingType == SLANG_BINDING_TYPE_UNKNOWN)
1584
1593
{
1585
- // This is a field that consumes no resources, and as
1586
- // such does not need a binding or descriptor ranges
1587
- // allocated for it.
1594
+ // We will search through all the resource kinds that
1595
+ // the type layout consumes, to see if we can find
1596
+ // one that indicates a binding type we actually
1597
+ // want to reflect.
1588
1598
//
1589
- return ;
1590
- }
1591
- else
1592
- {
1593
- // `resourceKindCount` should be 1 in most cases.
1594
- // However if this field is a buffer of existential type,
1595
- // the resourceInfo array will contain additional ExistentialTypeParam
1596
- // and ExistentialObjectParam entries. These entries doesn't affect the
1597
- // logic here, so we only need to care about the first entry.
1598
- auto & resInfo = typeLayout->resourceInfos [0 ];
1599
- LayoutResourceKind kind = resInfo.kind ;
1600
-
1601
- auto bindingType = _calcBindingType (typeLayout, kind);
1602
- if (bindingType == SLANG_BINDING_TYPE_INLINE_UNIFORM_DATA)
1599
+ for ( auto resInfo : typeLayout->resourceInfos )
1603
1600
{
1604
- // We do not consider uniform resource usage
1605
- // in the ranges we compute.
1601
+ auto kind = resInfo.kind ;
1602
+ if (kind == LayoutResourceKind::Uniform)
1603
+ continue ;
1604
+
1605
+ auto kindBindingType = _calcBindingType (kind);
1606
+ if (kindBindingType == SLANG_BINDING_TYPE_UNKNOWN)
1607
+ continue ;
1608
+
1609
+ // If we find a relevant binding type based on
1610
+ // one of the resource kinds that are consumed,
1611
+ // then we immediately stop the search and use
1612
+ // the first one found (whether or not later
1613
+ // entries might also provide something relevant).
1606
1614
//
1607
- // TODO: We may need to revise that rule for types that
1608
- // represent resources, even when one or more targets
1609
- // map those resource types to ordinary/uniform data.
1610
- //
1611
- return ;
1615
+ bindingType = kindBindingType;
1616
+ break ;
1612
1617
}
1618
+ }
1613
1619
1614
- // This leaf field will map to a single binding range and,
1615
- // if it is appropriate, a single descriptor range.
1616
- //
1617
- auto count = resInfo.count * multiplier;
1618
- auto indexOffset = _calcIndexOffset (path, kind);
1619
- auto spaceOffset = _calcSpaceOffset (path, kind);
1620
+ // After we've tried to determine a binding type, if
1621
+ // we have nothing to go on then we don't want to add
1622
+ // a binding range.
1623
+ //
1624
+ if (bindingType == SLANG_BINDING_TYPE_UNKNOWN)
1625
+ return ;
1626
+
1627
+ // We now know that the leaf field will map to a single binding range,
1628
+ // and zero or more descriptor ranges.
1629
+ //
1630
+ TypeLayout::ExtendedInfo::BindingRangeInfo bindingRange;
1631
+ bindingRange.leafTypeLayout = typeLayout;
1632
+ bindingRange.bindingType = bindingType;
1633
+ bindingRange.count = multiplier;
1634
+ bindingRange.descriptorSetIndex = 0 ;
1635
+ bindingRange.firstDescriptorRangeIndex = 0 ;
1636
+ bindingRange.descriptorRangeCount = 0 ;
1620
1637
1621
- Int descriptorSetIndex = -1 ;
1622
- Int firstDescriptorIndex = 0 ;
1623
- RefPtr<TypeLayout::ExtendedInfo::DescriptorSetInfo> descriptorSet;
1638
+ // We will associate the binding range with a specific descriptor
1639
+ // set on demand *if* we discover that it shold contain any
1640
+ // descriptor ranges.
1641
+ //
1642
+ RefPtr<TypeLayout::ExtendedInfo::DescriptorSetInfo> descriptorSet;
1643
+
1644
+
1645
+ // We will add a descriptor range for each relevant resource kind
1646
+ // that the type layout consumes.
1647
+ //
1648
+ for (auto resInfo : typeLayout->resourceInfos )
1649
+ {
1650
+ auto kind = resInfo.kind ;
1624
1651
switch ( kind )
1625
1652
{
1653
+ default :
1654
+ break ;
1655
+
1656
+
1657
+ // There are many resource kinds that we do not want
1658
+ // to expose as descriptor ranges simply because they
1659
+ // do not actually allocate descriptors on our target
1660
+ // APIs.
1661
+ //
1662
+ // Notably included here are uniform/ordinary data and
1663
+ // varying input/output (including the ray-tracing cases).
1664
+ //
1665
+ // It is worth noting that we *do* allow root/push-constant
1666
+ // ranges to be reflected as "descriptor" ranges here,
1667
+ // despite the fact that they are not descriptor-bound
1668
+ // under D3D12/Vulkan.
1669
+ //
1670
+ // In practice, even with us filtering out some cases here,
1671
+ // an application/renderer layer will need to filter/translate
1672
+ // or descriptor ranges into API-specific ones, and a one-to-one
1673
+ // mapping should not be assumed.
1674
+ //
1675
+ // TODO: Make some clear decisions about what should and should
1676
+ // not appear here.
1677
+ //
1678
+ case LayoutResourceKind::Uniform:
1626
1679
case LayoutResourceKind::RegisterSpace:
1627
1680
case LayoutResourceKind::VaryingInput:
1628
1681
case LayoutResourceKind::VaryingOutput:
1629
1682
case LayoutResourceKind::HitAttributes:
1630
1683
case LayoutResourceKind::RayPayload:
1631
- // Resource kinds that represent "varying" input/output
1632
- // do not manifest as entries in API descriptor tables.
1633
- //
1634
- // TODO: Neither do root constants, if we are being
1635
- // precise. This API really needs to carefully match
1636
- // the semantics of the target platform/API in terms
1637
- // of what things are descriptor-bound and which are
1638
- // not, so that a user can easily allocate the platform-specific
1639
- // descriptor sets using this info.
1640
- //
1641
- // (That said, we are purposefully *not* breaking apart
1642
- // samplers and SRV/UAV/CBV stuff for our D3D reflection
1643
- // of descriptor sets. It seems like the policy here
1644
- // really requires careful thought)
1645
- //
1646
- // TODO: Maybe the best answer is to leave decomposition
1647
- // of stuff into descriptor sets up to the application
1648
- // layer? This is especially true if a common case would
1649
- // be an application that doesn't support arbitrary manual
1650
- // binding of parameters to register/spaces.
1651
- //
1652
- break ;
1684
+ continue ;
1685
+ }
1653
1686
1654
- default :
1655
- {
1656
- TypeLayout::ExtendedInfo::DescriptorRangeInfo descriptorRange;
1657
- descriptorRange.kind = kind;
1658
- descriptorRange.bindingType = bindingType;
1659
- descriptorRange.count = count;
1660
- descriptorRange.indexOffset = indexOffset;
1687
+ // We will prefer to use a binding type derived from the specific
1688
+ // resource kind, but will fall back to information from the
1689
+ // type layout when that is not available.
1690
+ //
1691
+ // TODO: This logic probably needs a bit more work to handle
1692
+ // the case of a combined texture-sampler field that is being
1693
+ // compiled for an API with separate textures and samplers.
1694
+ //
1695
+ auto kindBindingType = _calcBindingType (kind);
1696
+ if ( kindBindingType == SLANG_BINDING_TYPE_UNKNOWN )
1697
+ {
1698
+ kindBindingType = bindingType;
1699
+ }
1661
1700
1662
- descriptorSetIndex = _findOrAddDescriptorSet (spaceOffset);
1663
- descriptorSet = m_extendedInfo->m_descriptorSets [descriptorSetIndex];
1701
+ // We now expect to allocate a descriptor range for this
1702
+ // `resInfo` representing resouce usage.
1703
+ //
1704
+ auto count = resInfo.count * multiplier;
1705
+ auto indexOffset = _calcIndexOffset (path, kind);
1706
+ auto spaceOffset = _calcSpaceOffset (path, kind);
1664
1707
1665
- firstDescriptorIndex = descriptorSet-> descriptorRanges . getCount () ;
1666
- descriptorSet-> descriptorRanges . add (descriptorRange) ;
1667
- }
1668
- break ;
1669
- }
1708
+ TypeLayout::ExtendedInfo::DescriptorRangeInfo descriptorRange ;
1709
+ descriptorRange. kind = kind ;
1710
+ descriptorRange. bindingType = kindBindingType;
1711
+ descriptorRange. count = count ;
1712
+ descriptorRange. indexOffset = indexOffset;
1670
1713
1714
+ if (!descriptorSet)
1715
+ {
1716
+ Int descriptorSetIndex = _findOrAddDescriptorSet (spaceOffset);
1717
+ descriptorSet = m_extendedInfo->m_descriptorSets [descriptorSetIndex];
1671
1718
1672
- TypeLayout::ExtendedInfo::BindingRangeInfo bindingRange;
1673
- bindingRange.leafTypeLayout = typeLayout;
1674
- bindingRange.bindingType = _calcBindingType (typeLayout, kind);
1675
- bindingRange.count = count;
1676
- bindingRange.descriptorSetIndex = descriptorSetIndex;
1677
- bindingRange.firstDescriptorRangeIndex = firstDescriptorIndex;
1678
- bindingRange.descriptorRangeCount = 1 ;
1719
+ bindingRange.descriptorSetIndex = descriptorSetIndex;
1720
+ bindingRange.firstDescriptorRangeIndex = descriptorSet->descriptorRanges .getCount ();
1721
+ }
1679
1722
1680
- m_extendedInfo->m_bindingRanges .add (bindingRange);
1723
+ descriptorSet->descriptorRanges .add (descriptorRange);
1724
+ bindingRange.descriptorRangeCount ++;
1681
1725
}
1726
+
1727
+ m_extendedInfo->m_bindingRanges .add (bindingRange);
1682
1728
}
1683
1729
}
1684
1730
};
0 commit comments