diff options
| author | Tim Foley <tfoleyNV@users.noreply.github.com> | 2021-04-29 15:27:24 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-04-29 15:27:24 -0700 |
| commit | 37a341775e410c02df5072244becdc416fd15c86 (patch) | |
| tree | 4f8110596c22140fd529c56915e60283493eaa63 /tools/gfx/d3d12/render-d3d12.cpp | |
| parent | aba8ec619de2bcf908901fa33677bb1d35296df8 (diff) | |
Update gfx back-ends to handle static specialization (#1826)
* Update gfx back-ends to handle static specialization
The main goal here is to make the D3D11, D3D12 and Vulkan back-ends support static specialization of interface types in the case where the data for the type won't "fit" in the pre-allocated space for existential values. This includes all cases where the concrete type being specialized to has resources/samplers/etc., as well as any cases where its ordinary/uniform data exceeds the space available.
(Note that the CPU and CUDA targets don't need this work since they can (in theory) support arbitrary-size data in the fixed-size existential payload by using pointer indirection. Actually supporting indirection in those cases should be a distinct change)
The Slang compiler already performs layout for programs that have this kind of data that doesn't "fit," and it lays them out using an idea of "pending" type layouts. Basically, a type that contains some amount of specialized interface-type fields will produce both a "primary" type layout that just covers the data for the unspecialized case, as well as "pending" type layout that describes the layout for all the extra data needed by specialization.
When laying out a `ConstantBuffer<X>` or `ParameterBlocK<X>` ("CB" or "PB"), the front-end will try to place as much of that "pending" data into the layout of the buffer/block itself as is possible. That means that both CBs and PBs will be able to allocate trailing bytes for any ordinary data in the "pending" layout. PBs will be able to allocate any trailing resources/samplers into their layout, but for CBs they will spill out to be part of the pending layout for the buffer itself.
In order for the back-ends to properly handle pending data, they need to *either* assume the exact layout rules used by the front-end and try to reproduce them (e.g., by iterating over binding ranges and sub-objects in the exact same order that front-end layout would enumerate them), *or* they need to respect the reflection information produced by the front-end. This change takes the latter approach, trying to make only minimal assumptions about the layout rules being used. This choice is motivated by wanting to decouple the `gfx` implementation from the compiler front-end, especially insofar as this work has made me question whether the current layout rules are the best ones possible.
A common theme across all the implementations is to have a fixed-size type that can represent "binding offsets" for the chosen back-end. The offset type has fields that depend on the API-specific way bindings are indexed; e.g., for D3D11 it has offsets for CBV, SRV, UAV, and sampler bindings. This fixed-size offset type can be filled in based on Slang reflecton information, and then used to compute derived offsets with just a few add operations.
The simple offset type for each API is then extended to produce an offset type that includes both the offsets for "primary" data and also the offsets for "pending" data. Most logic that traffics in offsets doesn't have to know about this more complicated representation.
Making consistent use of these offsets required that I pretty much rewrite the logic that actually applies shader objects to the API state. Doing so might be lowering the efficiency of the system in the near term, but the increase in clarity was important for getting the work done, and it seems like it will also be important if/when we start trying to perform special-case optimizations around root and entry-point parameter setting.
While there are many API-specific differences, we can identify a repeated pattern where many steps, whether applying parameters to the pipeline stage or constructing signatures / layouts, can be broken down into three main operations on `ShaderObject`s or their layouts:
* `*AsValue()` is the core operation, and is the one used for the `ExistentialValue` case most of the time. It ignores the ordinary data in the object, and instead processes all nested binding ranges (for resources/smaplers) and sub-objects.
* `*AsConstantBuffer()` handles the `ConstntBuffer<X>` case, by dealing with the implicit buffer for ordinary data (if it is needed) and then delegates to the `*AsValue()` case.
* `*AsParameterBlock()` handles the `ParameterBlock<X>` case, by allocating/preparing/etc. any descriptor tables/sets that would be required for the current object/layout and then delegating to `*AsConstantBuffer()` to do the rest
The idea is that by having the parameter block case delegate to the constant buffer case, which delegates to the value/existential case, we can streamline a lot of the logic so that it doesn't seem quite as full of special cases.
Note: When preparing this pull request I spent a reasonable amount of time trying to clean up the D3D11 and Vulkan implementations, so they are probably the easiest to read and understand when it comes to the new code. Doing the cleanup work also helped to work out some weird corner case bugs/issues. In contrast, the D3D12 path hasn't had as much attention given to cleanliness and comments, so it really needs some attention down the line to get things into a state that is easier to understand.
* fixup: remove debugging code spotted in review
Diffstat (limited to 'tools/gfx/d3d12/render-d3d12.cpp')
| -rw-r--r-- | tools/gfx/d3d12/render-d3d12.cpp | 1070 |
1 files changed, 809 insertions, 261 deletions
diff --git a/tools/gfx/d3d12/render-d3d12.cpp b/tools/gfx/d3d12/render-d3d12.cpp index 8cb9ce197..67223088a 100644 --- a/tools/gfx/d3d12/render-d3d12.cpp +++ b/tools/gfx/d3d12/render-d3d12.cpp @@ -606,12 +606,18 @@ public: { DescriptorHeapReference heap; uint32_t table; - }; - struct BindingOffset - { - int32_t resource; - int32_t sampler; + /// Get the GPU handle at the specified index + SLANG_FORCE_INLINE D3D12_GPU_DESCRIPTOR_HANDLE getGpuHandle(uint32_t index) const + { + return heap.getGpuHandle(table + index); + } + + /// Get the CPU handle at the specified index + SLANG_FORCE_INLINE D3D12_CPU_DESCRIPTOR_HANDLE getCpuHandle(uint32_t index) const + { + return heap.getCpuHandle(table + index); + } }; struct RootBindingState @@ -619,21 +625,35 @@ public: TransientResourceHeapImpl* transientHeap; D3D12Device* device; ArrayView<DescriptorTable> descriptorTables; - BindingOffset offset; - uint32_t rootParamIndex; // The root parameter index of this object. - uint32_t futureRootParamOffset; // The starting offset of additional sub-object descriptor tables. }; - struct DescriptorSetInfo + struct BindingCounts { - uint32_t resourceDescriptorCount = 0; - uint32_t samplerDescriptorCount = 0; + uint32_t rootParam = 0; + uint32_t resource = 0; + uint32_t sampler = 0; + + BindingCounts() + {} }; - struct BindingLocation + struct HeapBindingIndex { - int32_t index; - BindingOffset offsetInDescriptorTable; + uint32_t descriptorTableIndex = 0; + uint32_t descriptorIndex = 0; + }; + + struct RootBindingIndex + { + uint32_t rootParamIndex = 0; + HeapBindingIndex resource; + HeapBindingIndex sampler; + }; + + struct DescriptorSetInfo + { + uint32_t resourceDescriptorCount = 0; + uint32_t samplerDescriptorCount = 0; }; // Provides information on how binding ranges are stored in descriptor tables for @@ -645,31 +665,79 @@ public: class ShaderObjectLayoutImpl : public ShaderObjectLayoutBase { public: + + /// Information about a single logical binding range struct BindingRangeInfo { + // Some of the information we store on binding ranges is redundant with + // the information that Slang's reflection information stores, but having + // it here can make the code more compact and obvious. + + /// The type of binding in this range. slang::BindingType bindingType; + + /// The number of distinct bindings in this range. uint32_t count; - uint32_t spaceIndex; - uint32_t flatResourceOffset; // Offset in flattend array of resource binding slots. - BindingLocation binding; - // Returns true if this binding range consumes a specialization argument slot. - bool isSpecializationArg() const + /// A "flat" index for this range in whatever array provides backing storage for it + uint32_t flatIndex; + }; + + /// Offset information for a sub-object range + struct SubObjectRangeOffset : BindingCounts + { + SubObjectRangeOffset() + {} + + SubObjectRangeOffset(slang::VariableLayoutReflection* varLayout) { - return bindingType == slang::BindingType::ExistentialValue; + if(auto pendingLayout = varLayout->getPendingDataLayout()) + { + pendingOrdinaryData = (uint32_t) pendingLayout->getOffset(SLANG_PARAMETER_CATEGORY_UNIFORM); + } } + + /// The offset for "pending" ordinary data related to this range + uint32_t pendingOrdinaryData = 0; }; + + /// Stride information for a sub-object range + struct SubObjectRangeStride + { + SubObjectRangeStride() + {} + + SubObjectRangeStride(slang::TypeLayoutReflection* typeLayout) + { + if(auto pendingLayout = typeLayout->getPendingDataTypeLayout()) + { + pendingOrdinaryData = (uint32_t) pendingLayout->getSize(SLANG_PARAMETER_CATEGORY_UNIFORM); + } + } + + /// The strid for "pending" ordinary data related to this range + uint32_t pendingOrdinaryData = 0; + }; + + /// Information about a sub-objecrt range struct SubObjectRangeInfo { + /// The index of the binding range corresponding to this sub-object range + Index bindingRangeIndex = 0; + + /// Layout information for the type of sub-object expected to be bound, if known RefPtr<ShaderObjectLayoutImpl> layout; - Index bindingRangeIndex; - slang::BindingType bindingType; - // The offset for the constant buffer descriptor if this - // sub-object is referenced as a `ConstantBuffer<T>`. - // For a `ParameterBlock` binding range, this is always 0 since - // parameter blocks start in a fresh descriptor table. - BindingOffset descriptorOffset; + /// The offset to use when binding the first object in this range + SubObjectRangeOffset offset; + + /// Stride between consecutive objects in this range + SubObjectRangeStride stride; + }; + + struct RootParameterInfo + { + D3D12_ROOT_PARAMETER rootParameter; }; struct Builder @@ -683,9 +751,29 @@ public: slang::TypeLayoutReflection* m_elementTypeLayout; List<BindingRangeInfo> m_bindingRanges; List<SubObjectRangeInfo> m_subObjectRanges; - DescriptorSetInfo m_descriptorSetInfo; - uint32_t m_subObjectCount = 0; - uint32_t m_flatResourceCount = 0; +// DescriptorSetInfo m_descriptorSetInfo; + +// uint32_t m_resourceSlotCount = 0; +// uint32_t m_samplerSlotCount = 0; + uint32_t m_subObjectSlotCount = 0; + + BindingCounts m_ownCounts; +// BindingCounts m_childCounts; + BindingCounts m_totalCounts; + + uint32_t m_childRootParameterCount = 0; + + uint32_t m_ordinaryDataBufferCount = 0; + + uint32_t m_totalOrdinaryDataSize = 0; + + +// uint32_t m_totalResourceDescriptorCount = 0; +// uint32_t m_totalSamplerDescriptorCount = 0; + +// uint32_t m_ownRootParameterCount = 0; +// uint32_t m_childRootParameterCount = 0; +// uint32_t m_totalRootParameterCount = 0; void addBindingRangesOfType(slang::TypeLayoutReflection* typeLayout) { @@ -695,7 +783,9 @@ public: // ordinary uniform data fields. if (typeLayout->getSize(slang::ParameterCategory::Uniform) != 0) { - m_descriptorSetInfo.resourceDescriptorCount = 1; + m_ordinaryDataBufferCount++; + m_ownCounts.resource++; +// m_descriptorSetInfo.resourceDescriptorCount = 1; } for (SlangInt r = 0; r < bindingRangeCount; ++r) @@ -707,42 +797,25 @@ public: BindingRangeInfo bindingRangeInfo = {}; bindingRangeInfo.bindingType = slangBindingType; bindingRangeInfo.count = count; - bindingRangeInfo.flatResourceOffset = m_flatResourceCount; - bindingRangeInfo.spaceIndex = - (uint32_t)typeLayout->getBindingRangeDescriptorSetIndex(r); + +// bindingRangeInfo.flatIndex = m_flatResourceCount; switch (slangBindingType) { case slang::BindingType::ConstantBuffer: case slang::BindingType::ParameterBlock: case slang::BindingType::ExistentialValue: - bindingRangeInfo.binding.index = m_subObjectCount; - m_subObjectCount += count; + bindingRangeInfo.flatIndex = m_subObjectSlotCount; + m_subObjectSlotCount += count; break; case slang::BindingType::Sampler: - bindingRangeInfo.binding.offsetInDescriptorTable.sampler = - m_descriptorSetInfo.samplerDescriptorCount; - m_descriptorSetInfo.samplerDescriptorCount += count; + bindingRangeInfo.flatIndex = m_ownCounts.sampler; + m_ownCounts.sampler += count; break; case slang::BindingType::CombinedTextureSampler: - bindingRangeInfo.binding.offsetInDescriptorTable.sampler = - m_descriptorSetInfo.samplerDescriptorCount; - bindingRangeInfo.binding.offsetInDescriptorTable.resource = - m_descriptorSetInfo.resourceDescriptorCount; - m_descriptorSetInfo.samplerDescriptorCount += count; - m_descriptorSetInfo.resourceDescriptorCount += count; - m_flatResourceCount += count; - break; - - case slang::BindingType::MutableRawBuffer: - case slang::BindingType::MutableTexture: - case slang::BindingType::MutableTypedBuffer: - bindingRangeInfo.binding.offsetInDescriptorTable.resource = - m_descriptorSetInfo.resourceDescriptorCount; - m_descriptorSetInfo.resourceDescriptorCount += count; - m_flatResourceCount += count; + // TODO: support this case... break; case slang::BindingType::VaryingInput: @@ -750,10 +823,8 @@ public: break; default: - bindingRangeInfo.binding.offsetInDescriptorTable.resource = - m_descriptorSetInfo.resourceDescriptorCount; - m_descriptorSetInfo.resourceDescriptorCount += count; - m_flatResourceCount += count; + bindingRangeInfo.flatIndex = m_ownCounts.resource; + m_ownCounts.resource += count; break; } m_bindingRanges.add(bindingRangeInfo); @@ -766,16 +837,21 @@ public: m_elementTypeLayout = typeLayout; + m_totalOrdinaryDataSize = (uint32_t) typeLayout->getSize(); + // Compute the binding ranges that are used to store // the logical contents of the object in memory. addBindingRangesOfType(typeLayout); + m_totalCounts = m_ownCounts; + SlangInt subObjectRangeCount = typeLayout->getSubObjectRangeCount(); for (SlangInt r = 0; r < subObjectRangeCount; ++r) { SlangInt bindingRangeIndex = typeLayout->getSubObjectRangeBindingRangeIndex(r); auto slangBindingType = typeLayout->getBindingRangeType(bindingRangeIndex); + auto count = (uint32_t) typeLayout->getBindingRangeBindingCount(bindingRangeIndex); slang::TypeLayoutReflection* slangLeafTypeLayout = typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex); @@ -787,7 +863,17 @@ public: // know the appropraite type/layout of sub-object to allocate. // RefPtr<ShaderObjectLayoutImpl> subObjectLayout; - if (slangBindingType != slang::BindingType::ExistentialValue) + if (slangBindingType == slang::BindingType::ExistentialValue) + { + if(auto pendingTypeLayout = slangLeafTypeLayout->getPendingDataTypeLayout()) + { + createForElementType( + m_renderer, + pendingTypeLayout, + subObjectLayout.writeRef()); + } + } + else { createForElementType( m_renderer, @@ -798,14 +884,83 @@ public: SubObjectRangeInfo subObjectRange; subObjectRange.bindingRangeIndex = bindingRangeIndex; subObjectRange.layout = subObjectLayout; - subObjectRange.bindingType = slangBindingType; - subObjectRange.descriptorOffset.resource = - m_descriptorSetInfo.resourceDescriptorCount; - subObjectRange.descriptorOffset.sampler = - m_descriptorSetInfo.samplerDescriptorCount; +// subObjectRange.bindingType = slangBindingType; + + subObjectRange.offset = SubObjectRangeOffset(typeLayout->getSubObjectRangeOffset(r)); + subObjectRange.stride = SubObjectRangeStride(slangLeafTypeLayout); + + subObjectRange.offset.rootParam = m_childRootParameterCount; + subObjectRange.offset.resource = m_totalCounts.resource; + subObjectRange.offset.sampler = m_totalCounts.sampler; + + BindingCounts objectCounts; + switch(slangBindingType) + { + default: + break; + + case slang::BindingType::ConstantBuffer: + { + SLANG_ASSERT(subObjectLayout); + + // The resource and sampler descriptors of a nested + // constant buffer will "leak" into those of the + // parent type, and we need to account for them + // whenever we allocate storage. + // + + objectCounts.resource = subObjectLayout->getTotalResourceDescriptorCount(); + objectCounts.sampler = subObjectLayout->getTotalSamplerDescriptorCount(); + objectCounts.rootParam = subObjectRange.layout->getChildRootParameterCount(); + } + break; + + case slang::BindingType::ExistentialValue: + if(subObjectLayout) + { + objectCounts.resource = subObjectLayout->getTotalResourceDescriptorCountWithoutOrdinaryDataBuffer(); + objectCounts.sampler = subObjectLayout->getTotalSamplerDescriptorCount(); + objectCounts.rootParam = subObjectRange.layout->getChildRootParameterCount(); + + // An interface-type range that includes ordinary data can + // increase the size of the ordinary data buffer we need to + // allocate for the parent object. + // + uint32_t ordinaryDataEnd = subObjectRange.offset.pendingOrdinaryData + + (uint32_t) count * subObjectRange.stride.pendingOrdinaryData; + + if(ordinaryDataEnd > m_totalOrdinaryDataSize) + { + m_totalOrdinaryDataSize = ordinaryDataEnd; + } + } + break; + + case slang::BindingType::ParameterBlock: + { + SLANG_ASSERT(subObjectLayout); + + objectCounts.rootParam = subObjectRange.layout->getTotalRootParameterCount(); + } + break; + } + + auto rangeResourceCount = count * objectCounts.resource; + auto rangeSamplerCount = count * objectCounts.sampler; + auto rangeRootParamCount = count * objectCounts.rootParam; + + m_totalCounts.resource += rangeResourceCount; + m_totalCounts.sampler += rangeSamplerCount; + m_childRootParameterCount += rangeRootParamCount; + m_subObjectRanges.add(subObjectRange); } + if(m_totalCounts.resource) m_ownCounts.rootParam++; + if(m_totalCounts.sampler) m_ownCounts.rootParam++; + + m_totalCounts.rootParam = m_ownCounts.rootParam + m_childRootParameterCount; + return SLANG_OK; } @@ -835,13 +990,26 @@ public: BindingRangeInfo const& getBindingRange(Index index) { return m_bindingRanges[index]; } - DescriptorSetInfo getDescriptorSetInfo() { return m_descriptorSetInfo; } +// DescriptorSetInfo getDescriptorSetInfo() { return m_descriptorSetInfo; } slang::TypeLayoutReflection* getElementTypeLayout() { return m_elementTypeLayout; } - uint32_t getResourceCount() { return m_resourceSlotCount; } + uint32_t getResourceSlotCount() { return m_ownCounts.resource; } + uint32_t getSamplerSlotCount() { return m_ownCounts.sampler; } + Index getSubObjectSlotCount() { return m_subObjectSlotCount; } + + uint32_t getTotalResourceDescriptorCount() { return m_totalCounts.resource; } + uint32_t getTotalSamplerDescriptorCount() { return m_totalCounts.sampler; } + + uint32_t getOrdinaryDataBufferCount() { return m_ordinaryDataBufferCount; } + bool hasOrdinaryDataBuffer() { return m_ordinaryDataBufferCount != 0; } - Index getSubObjectCount() { return m_subObjectCount; } + uint32_t getTotalResourceDescriptorCountWithoutOrdinaryDataBuffer() { return m_totalCounts.resource - m_ordinaryDataBufferCount; } + + uint32_t getTotalRootParameterCount() { return m_totalCounts.rootParam; } + uint32_t getChildRootParameterCount() { return m_childRootParameterCount; } + + uint32_t getTotalOrdinaryDataSize() const { return m_totalOrdinaryDataSize; } SubObjectRangeInfo const& getSubObjectRange(Index index) { @@ -860,19 +1028,54 @@ public: initBase(renderer, builder->m_elementTypeLayout); - m_descriptorSetInfo = builder->m_descriptorSetInfo; +// m_descriptorSetInfo = builder->m_descriptorSetInfo; m_bindingRanges = _Move(builder->m_bindingRanges); - m_subObjectCount = builder->m_subObjectCount; m_subObjectRanges = builder->m_subObjectRanges; - m_resourceSlotCount = builder->m_flatResourceCount; + + m_ownCounts = builder->m_ownCounts; + m_totalCounts = builder->m_totalCounts; + m_subObjectSlotCount = builder->m_subObjectSlotCount; + m_childRootParameterCount = builder->m_childRootParameterCount; + m_ordinaryDataBufferCount = builder->m_ordinaryDataBufferCount; + m_totalOrdinaryDataSize = builder->m_totalOrdinaryDataSize; + +#if 0 + m_resourceSlotCount = builder->m_resourceSlotCount; + m_samplerSlotCount = builder->m_samplerSlotCount; + m_subObjectSlotCount = builder->m_subObjectSlotCount; + + m_totalResourceDescriptorCount = builder->m_totalResourceDescriptorCount; + m_totalSamplerDescriptorCount = builder->m_totalSamplerDescriptorCount; + + m_childRootParameterCount = builder->m_childRootParameterCount; + m_totalRootParameterCount = builder->m_totalRootParameterCount; +#endif + return SLANG_OK; } List<BindingRangeInfo> m_bindingRanges; - DescriptorSetInfo m_descriptorSetInfo; - Index m_subObjectCount = 0; List<SubObjectRangeInfo> m_subObjectRanges; - uint32_t m_resourceSlotCount; + + +// DescriptorSetInfo m_descriptorSetInfo; +// Index m_subObjectCount = 0; + + BindingCounts m_ownCounts; + BindingCounts m_totalCounts; + +// uint32_t m_resourceSlotCount; +// uint32_t m_samplerSlotCount; + uint32_t m_subObjectSlotCount; + +// uint32_t m_totalResourceDescriptorCount; +// uint32_t m_totalSamplerDescriptorCount; + + uint32_t m_childRootParameterCount = 0; +// uint32_t m_totalRootParameterCount = 0; + + uint32_t m_ordinaryDataBufferCount = 0; + uint32_t m_totalOrdinaryDataSize = 0; }; class RootShaderObjectLayoutImpl : public ShaderObjectLayoutImpl @@ -883,6 +1086,7 @@ public: struct EntryPointInfo { RefPtr<ShaderObjectLayoutImpl> layout; + BindingCounts offset; }; struct Builder : Super::Builder @@ -914,6 +1118,17 @@ public: { EntryPointInfo info; info.layout = entryPointLayout; + + info.offset.resource = m_totalCounts.resource; + info.offset.sampler = m_totalCounts.sampler; + info.offset.rootParam = m_childRootParameterCount; + + m_totalCounts.resource += entryPointLayout->getTotalResourceDescriptorCount(); + m_totalCounts.sampler += entryPointLayout->getTotalSamplerDescriptorCount(); + + // TODO(tfoley): Check this to make sure it is reasonable... + m_childRootParameterCount += entryPointLayout->getChildRootParameterCount(); + m_entryPoints.add(info); } @@ -976,13 +1191,15 @@ public: { uint32_t spaceOffset = 0; // The `space` index as specified in shader. + enum { kRangeTypeCount = 4 }; + /// An offset to apply for each D3D12 register class, as given /// by a `D3D12_DESCRIPTOR_RANGE_TYPE`. /// /// Note that the `D3D12_DESCRIPTOR_RANGE_TYPE` enumeration has /// values between 0 and 3, inclusive. /// - uint32_t offsetForRangeType[4] = {0, 0, 0, 0}; + uint32_t offsetForRangeType[kRangeTypeCount] = {0, 0, 0, 0}; uint32_t& operator[](D3D12_DESCRIPTOR_RANGE_TYPE type) { @@ -993,8 +1210,60 @@ public: { return offsetForRangeType[int(type)]; } + + BindingRegisterOffset() + {} + + BindingRegisterOffset(slang::VariableLayoutReflection* varLayout) + { + if(varLayout) + { + spaceOffset = (UINT) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_REGISTER_SPACE); + offsetForRangeType[D3D12_DESCRIPTOR_RANGE_TYPE_CBV] = (UINT) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_CONSTANT_BUFFER); + offsetForRangeType[D3D12_DESCRIPTOR_RANGE_TYPE_SRV] = (UINT) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_SHADER_RESOURCE); + offsetForRangeType[D3D12_DESCRIPTOR_RANGE_TYPE_UAV] = (UINT) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_UNORDERED_ACCESS); + offsetForRangeType[D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER] = (UINT) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_SAMPLER_STATE); + } + } + + void operator+=(BindingRegisterOffset const& other) + { + spaceOffset += other.spaceOffset; + for(int i = 0; i < kRangeTypeCount; ++i) + { + offsetForRangeType[i] += other.offsetForRangeType[i]; + } + } + }; + struct BindingRegisterOffsetPair + { + BindingRegisterOffset primary; + BindingRegisterOffset pending; + + BindingRegisterOffsetPair() + {} + + BindingRegisterOffsetPair(slang::VariableLayoutReflection* varLayout) + : primary(varLayout) + , pending(varLayout->getPendingDataLayout()) + {} + + void operator+=(BindingRegisterOffsetPair const& other) + { + primary += other.primary; + pending += other.pending; + } + }; + + Index reserveRootParameters(Index count) + { + Index result = m_rootParameters.getCount(); + m_rootParameters.setCount(result + count); + return result; + } + /// Add a new descriptor set to the layout being computed. /// /// Note that a "descriptor set" in the layout may amount to @@ -1114,33 +1383,15 @@ public: } } - /// Add binding ranges and parameter blocks to the root signature. - /// - /// The layout information is taken from `varLayout` which should - /// be a layout for either a program or an entry point. - /// - /// The `physicalDescriptorSetIndex` is the index in the `m_descriptorSets` array of - /// the descriptor set that binding ranges not belonging to nested - /// parameter blocks should be added to. - /// - /// This routine will use absolute offset information computed from `varLayout` - /// to apply appropriate space/register offsets to the bindings and parameter - /// blocks inside the layout. - /// - void addBindingRangesAndParameterBlocks( + void addAsValue( slang::VariableLayoutReflection* varLayout, Index physicalDescriptorSetIndex) { - BindingRegisterOffset offset; - offset.spaceOffset = (UINT) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_REGISTER_SPACE); - offset[D3D12_DESCRIPTOR_RANGE_TYPE_CBV] = (UINT) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_CONSTANT_BUFFER); - offset[D3D12_DESCRIPTOR_RANGE_TYPE_SRV] = (UINT) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_SHADER_RESOURCE); - offset[D3D12_DESCRIPTOR_RANGE_TYPE_UAV] = (UINT) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_UNORDERED_ACCESS); - offset[D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER] = (UINT) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_SAMPLER_STATE); - - addBindingRangesAndParameterBlocks(varLayout->getTypeLayout(), physicalDescriptorSetIndex, offset); + BindingRegisterOffsetPair offset(varLayout); + addAsValue(varLayout->getTypeLayout(), physicalDescriptorSetIndex, offset); } + /// Add binding ranges and parameter blocks to the root signature. /// /// The layout information is taken from `typeLayout` which should @@ -1153,10 +1404,31 @@ public: /// The `offset` encodes information about space and/or register offsets that /// should be applied to descrptor ranges. /// - void addBindingRangesAndParameterBlocks( + void addAsConstantBuffer( slang::TypeLayoutReflection* typeLayout, Index physicalDescriptorSetIndex, - BindingRegisterOffset const& offset) + BindingRegisterOffsetPair const& containerOffset, + BindingRegisterOffsetPair const& elementOffset) + { + if(typeLayout->getSize(SLANG_PARAMETER_CATEGORY_UNIFORM) != 0) + { + auto descriptorRangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV; + auto& offsetForRangeType = containerOffset.primary.offsetForRangeType[descriptorRangeType]; + addDescriptorRange( + physicalDescriptorSetIndex, + descriptorRangeType, + offsetForRangeType, + containerOffset.primary.spaceOffset, + 1); + } + + addAsValue(typeLayout, physicalDescriptorSetIndex, elementOffset); + } + + void addAsValue( + slang::TypeLayoutReflection* typeLayout, + Index physicalDescriptorSetIndex, + BindingRegisterOffsetPair const& offset) { // Our first task is to add the binding ranges for stuff that is // directly contained in `typeLayout` rather than via sub-objects. @@ -1186,68 +1458,100 @@ public: // For binding ranges that don't represent sub-objects, we will add // all of the descriptor ranges they encompass to the root signature. // - addBindingRange(typeLayout, physicalDescriptorSetIndex, offset, bindingRangeIndex); + addBindingRange(typeLayout, physicalDescriptorSetIndex, offset.primary, bindingRangeIndex); } - // Next we need to add any sub binding ranges in `ConstantBuffer` bindings. - // - Index subObjectCount = typeLayout->getSubObjectRangeCount(); - for (Index subObjectRangeIndex = 0; subObjectRangeIndex < subObjectCount; subObjectRangeIndex++) + // Next we need to recursively include everything bound via sub-objects + Index subObjectRangeCount = typeLayout->getSubObjectRangeCount(); + for (Index subObjectRangeIndex = 0; subObjectRangeIndex < subObjectRangeCount; subObjectRangeIndex++) { auto bindingRangeIndex = typeLayout->getSubObjectRangeBindingRangeIndex(subObjectRangeIndex); auto bindingType = typeLayout->getBindingRangeType(bindingRangeIndex); - switch (bindingType) + + auto subObjectTypeLayout = typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex); + + BindingRegisterOffsetPair subObjectRangeOffset = offset; + subObjectRangeOffset += BindingRegisterOffsetPair(typeLayout->getSubObjectRangeOffset(subObjectRangeIndex)); + + switch(bindingType) { case slang::BindingType::ConstantBuffer: { - // Constant buffer ranges (for `ConstantBuffer<ConcreteType>`) will "leak" their - // binding ranges into the surrounding type, so we can add them here like any other - // binding range. - // - // Note: It would be valid to allow `slang::BindingType::ConstantBuffer` to be handled - // in the earlier loop, but that would mean that descriptor ranges coming directly - // from the fields of `typeLayout` could be broken up with ranges coming from constant-buffer - // sub-objects. By moving the handling of constant buffers to this later loop, we - // guarantee that the descritpors used by non-sub-object binding ranges are all - // contiguous. - // - // This call will add all descriptor ranges reported in `typeLayout` that is associated - // with `bindingRangeIndex`. - // - addBindingRange( - typeLayout, - physicalDescriptorSetIndex, - offset, - bindingRangeIndex); + auto containerVarLayout = subObjectTypeLayout->getContainerVarLayout(); + SLANG_ASSERT(containerVarLayout); - // We also need to recurse into the element type of the constant buffer to add - // any binding ranges defined in the element type. - auto subObjectType = - typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex); - BindingRegisterOffset subOffset; - subOffset.spaceOffset = - offset.spaceOffset + - (uint32_t)typeLayout->getSubObjectRangeSpaceOffset( - subObjectRangeIndex); - addParameterBlocks( - _unwrapParameterGroups(subObjectType), - physicalDescriptorSetIndex, - subOffset); + auto elementVarLayout = subObjectTypeLayout->getElementVarLayout(); + SLANG_ASSERT(elementVarLayout); + + auto elementTypeLayout = elementVarLayout->getTypeLayout(); + SLANG_ASSERT(elementTypeLayout); + + BindingRegisterOffsetPair containerOffset = subObjectRangeOffset; + containerOffset += BindingRegisterOffsetPair(containerVarLayout); + + BindingRegisterOffsetPair elementOffset = subObjectRangeOffset; + elementOffset += BindingRegisterOffsetPair(elementVarLayout); + + addAsConstantBuffer(elementTypeLayout, physicalDescriptorSetIndex, containerOffset, elementOffset); } break; - default: + + case slang::BindingType::ParameterBlock: + { + auto containerVarLayout = subObjectTypeLayout->getContainerVarLayout(); + SLANG_ASSERT(containerVarLayout); + + auto elementVarLayout = subObjectTypeLayout->getElementVarLayout(); + SLANG_ASSERT(elementVarLayout); + + auto elementTypeLayout = elementVarLayout->getTypeLayout(); + SLANG_ASSERT(elementTypeLayout); + + BindingRegisterOffsetPair subDescriptorSetOffset; + subDescriptorSetOffset.primary.spaceOffset = subObjectRangeOffset.primary.spaceOffset; + subDescriptorSetOffset.pending.spaceOffset = subObjectRangeOffset.pending.spaceOffset; + + auto subPhysicalDescriptorSetIndex = addDescriptorSet(); + + BindingRegisterOffsetPair containerOffset = subDescriptorSetOffset; + containerOffset += BindingRegisterOffsetPair(containerVarLayout); + + BindingRegisterOffsetPair elementOffset = subDescriptorSetOffset; + elementOffset += BindingRegisterOffsetPair(elementVarLayout); + + addAsConstantBuffer(elementTypeLayout, subPhysicalDescriptorSetIndex, containerOffset, elementOffset); + } + break; + + case slang::BindingType::ExistentialValue: + { + // Any nested binding ranges in the sub-object will "leak" into the + // binding ranges for the surrounding context. + // + auto specializedTypeLayout = subObjectTypeLayout->getPendingDataTypeLayout(); + if(specializedTypeLayout) + { + BindingRegisterOffsetPair pendingOffset; + pendingOffset.primary = subObjectRangeOffset.pending; + + addAsValue(specializedTypeLayout, physicalDescriptorSetIndex, pendingOffset); + } + } break; } } - addParameterBlocks(typeLayout, physicalDescriptorSetIndex, offset); +// BindingRegisterOffsetPair pendingOffset; +// pendingOffset.primary = offset.pending; +// addPendingResourceBindingRanges(typeLayout, physicalDescriptorSetIndex, pendingOffset); } +#if 0 /// Add child parameter blocks defined in `typeLayout` to the root signature. void addParameterBlocks( - slang::TypeLayoutReflection* typeLayout, - Index physicalDescriptorSetIndex, - BindingRegisterOffset const& offset) + slang::TypeLayoutReflection* typeLayout, + Index physicalDescriptorSetIndex, + BindingRegisterOffsetPair const& offset) { Index subObjectCount = typeLayout->getSubObjectRangeCount(); for (Index subObjectRangeIndex = 0; subObjectRangeIndex < subObjectCount; @@ -1261,6 +1565,24 @@ public: auto bindingType = typeLayout->getBindingRangeType(bindingRangeIndex); switch (bindingType) { + case slang::BindingType::ConstantBuffer: + { + // We also need to recurse into the element type of the constant buffer to add + // any binding ranges defined in the element type. + auto subObjectType = + typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex); + auto spaceOffset = (uint32_t)typeLayout->getSubObjectRangeSpaceOffset(subObjectRangeIndex); + + BindingRegisterOffsetPair subOffset; + subOffset.primary.spaceOffset = offset.primary.spaceOffset + spaceOffset; + subOffset.pending.spaceOffset = offset.pending.spaceOffset + spaceOffset; + addParameterBlocks( + _unwrapParameterGroups(subObjectType), + physicalDescriptorSetIndex, + subOffset); + } + break; + case slang::BindingType::ParameterBlock: { // A parameter block (`ParameterBlock<ConcreteType>`) will always map to @@ -1293,8 +1615,9 @@ public: // this point, because `register` offsets from outside of the block // don't affect layout within the block. // - BindingRegisterOffset blockOffset; - blockOffset.spaceOffset = offset.spaceOffset + (uint32_t)spaceOffset; + BindingRegisterOffsetPair blockOffset; + blockOffset.primary.spaceOffset = offset.primary.spaceOffset + (uint32_t)spaceOffset; + blockOffset.pending.spaceOffset = offset.pending.spaceOffset + (uint32_t)spaceOffset; // Note: there is an important subtlety going on here. We are passing in // the type `blockTypeLayout` which corresponds to @@ -1311,15 +1634,15 @@ public: blockPhysicalDescriptorSetIndex, D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 0, - blockOffset.spaceOffset, + blockOffset.primary.spaceOffset, 1); - blockOffset.offsetForRangeType[D3D12_DESCRIPTOR_RANGE_TYPE_CBV] = 1; + blockOffset.primary.offsetForRangeType[D3D12_DESCRIPTOR_RANGE_TYPE_CBV] = 1; } // Once we have all the details worked out, we can write the binding // ranges for the block's type into the newly-allocated descriptor set. // - addBindingRangesAndParameterBlocks( + addAsConstantBuffer( elementLayout, blockPhysicalDescriptorSetIndex, blockOffset); } break; @@ -1332,7 +1655,109 @@ public: break; } } + +// addPendingParameterBlocks(typeLayout, physicalDescriptorSetIndex, offset); } +#endif + +#if 0 + void addPendingResourceBindingRanges( + slang::TypeLayoutReflection* typeLayout, + Index physicalDescriptorSetIndex, + BindingRegisterOffsetPair const& offset) + { + Index subObjectRangeCount = typeLayout->getSubObjectRangeCount(); + for (Index subObjectRangeIndex = 0; subObjectRangeIndex < subObjectRangeCount; subObjectRangeIndex++) + { + auto bindingRangeIndex = typeLayout->getSubObjectRangeBindingRangeIndex(subObjectRangeIndex); + auto bindingType = typeLayout->getBindingRangeType(bindingRangeIndex); + switch (bindingType) + { + case slang::BindingType::ExistentialValue: + { + // Any nested binding ranges in the sub-object will "leak" into the + // binding ranges for the surrounding context. + // + auto subObjectTypeLayout = typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex); + auto specializedTypeLayout = subObjectTypeLayout->getPendingDataTypeLayout(); + if(specializedTypeLayout) + { + // TODO: We need to compute the offsets that should be applied to + // any resources bound via the sub-object. + BindingRegisterOffsetPair subOffset = offset; + subOffset.primary += BindingRegisterOffset(typeLayout->getSubObjectRangePendingDataOffset(subObjectRangeIndex)); + + addResourceBindingRanges(specializedTypeLayout, physicalDescriptorSetIndex, subOffset); + } + } + break; + + case slang::BindingType::ConstantBuffer: + { + auto subObjectTypeLayout = typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex); + auto elementTypeLayout = subObjectTypeLayout->getElementTypeLayout(); + SLANG_ASSERT(elementTypeLayout); + + BindingRegisterOffsetPair subOffset = offset; + subOffset.primary += BindingRegisterOffset(typeLayout->getSubObjectRangePendingDataOffset(subObjectRangeIndex)); + + addPendingResourceBindingRanges(elementTypeLayout, physicalDescriptorSetIndex, subOffset); + } + break; + + default: + break; + } + } + } +#endif + +#if 0 + void addPendingParameterBlocks( + slang::TypeLayoutReflection* typeLayout, + Index physicalDescriptorSetIndex, + BindingRegisterOffset const& offset) + { + Index subObjectRangeCount = typeLayout->getSubObjectRangeCount(); + for (Index subObjectRangeIndex = 0; subObjectRangeIndex < subObjectRangeCount; subObjectRangeIndex++) + { + auto bindingRangeIndex = typeLayout->getSubObjectRangeBindingRangeIndex(subObjectRangeIndex); + auto bindingType = typeLayout->getBindingRangeType(bindingRangeIndex); + switch (bindingType) + { + case slang::BindingType::ExistentialValue: + { + // Any nested binding ranges in the sub-object will "leak" into the + // binding ranges for the surrounding context. + // + auto subObjectTypeLayout = typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex); + auto pendingTypeLayout = subObjectTypeLayout->getPendingDataTypeLayout(); + if(pendingTypeLayout) + { + // TODO: We need to compute the offsets that should be applied to + // any resources bound via the sub-object. + BindingRegisterOffset subOffset = offset; + + addParameterBlocks(pendingTypeLayout, physicalDescriptorSetIndex, subOffset); + } + } + break; + + case slang::BindingType::ConstantBuffer: + case slang::BindingType::ParameterBlock: + { + auto subObjectTypeLayout = typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex); + BindingRegisterOffset subOffset = offset; + addPendingParameterBlocks(subObjectTypeLayout, physicalDescriptorSetIndex, subOffset); + } + break; + + default: + break; + } + } + } +#endif D3D12_ROOT_SIGNATURE_DESC& build( List<D3D12Device::DescriptorSetInfo>& outRootDescriptorSetInfos) @@ -1385,6 +1810,7 @@ public: static Result createRootSignatureFromSlang( D3D12Device* device, + RootShaderObjectLayoutImpl* rootLayout, slang::IComponentType* program, ID3D12RootSignature** outRootSignature, List<DescriptorSetInfo>& outRootDescriptorSetInfos) @@ -1411,7 +1837,7 @@ public: // parameters. // auto rootDescriptorSetIndex = builder.addDescriptorSet(); - builder.addBindingRangesAndParameterBlocks(layout->getGlobalParamsVarLayout(), rootDescriptorSetIndex); + builder.addAsValue(layout->getGlobalParamsVarLayout(), rootDescriptorSetIndex); for (SlangUInt i = 0; i < layout->getEntryPointCount(); i++) { @@ -1428,7 +1854,7 @@ public: // being included in the global root signature as is being done here. // auto entryPoint = layout->getEntryPointByIndex(i); - builder.addBindingRangesAndParameterBlocks(entryPoint->getVarLayout(), rootDescriptorSetIndex); + builder.addAsValue(entryPoint->getVarLayout(), rootDescriptorSetIndex); } auto& rootSignatureDesc = builder.build(outRootDescriptorSetInfos); @@ -1476,7 +1902,8 @@ public: builder.addEntryPoint(slangEntryPoint->getStage(), entryPointLayout); } - SLANG_RETURN_ON_FAIL(builder.build(outLayout)); + RefPtr<RootShaderObjectLayoutImpl> layout; + SLANG_RETURN_ON_FAIL(builder.build(layout.writeRef())); if (program->getSpecializationParamCount() == 0) { @@ -1488,10 +1915,14 @@ public: // it in `m_gpuDescriptorSetInfos`. SLANG_RETURN_ON_FAIL(createRootSignatureFromSlang( device, + layout, program, - (*outLayout)->m_rootSignature.writeRef(), - (*outLayout)->m_gpuDescriptorSetInfos)); + layout->m_rootSignature.writeRef(), + layout->m_gpuDescriptorSetInfos)); } + + *outLayout = layout.detach(); + return SLANG_OK; } @@ -1548,16 +1979,17 @@ public: ~ShaderObjectImpl() { - auto layoutImpl = static_cast<ShaderObjectLayoutImpl*>(m_layout.Ptr()); - if (m_descriptorSet.m_resourceCount) - { - m_resourceHeap.freeIfSupported( - m_descriptorSet.m_resourceTable, m_descriptorSet.m_resourceCount); - } - if (m_descriptorSet.m_samplerCount) + auto layoutImpl = getLayout(); + if(layoutImpl) { - m_samplerHeap.freeIfSupported( - m_descriptorSet.m_samplerTable, m_descriptorSet.m_samplerCount); + if(auto resourceCount = layoutImpl->getResourceSlotCount()) + { + m_resourceHeap.freeIfSupported(m_descriptorSet.m_resourceTable, resourceCount); + } + if(auto samplerCount = layoutImpl->getSamplerSlotCount()) + { + m_samplerHeap.freeIfSupported(m_descriptorSet.m_samplerTable, samplerCount); + } } } @@ -1625,7 +2057,7 @@ public: auto bindingRangeIndex = offset.bindingRangeIndex; auto& bindingRange = layout->getBindingRange(bindingRangeIndex); - m_objects[bindingRange.binding.index + offset.bindingArrayIndex] = subObject; + m_objects[bindingRange.flatIndex + offset.bindingArrayIndex] = subObject; // If the range being assigned into represents an interface/existential-type leaf field, // then we need to consider how the `object` being assigned here affects specialization. @@ -1734,7 +2166,7 @@ public: return SLANG_E_INVALID_ARG; auto& bindingRange = layout->getBindingRange(offset.bindingRangeIndex); - returnComPtr(outObject, m_objects[bindingRange.binding.index + offset.bindingArrayIndex]); + returnComPtr(outObject, m_objects[bindingRange.flatIndex + offset.bindingArrayIndex]); return SLANG_OK; } @@ -1750,18 +2182,16 @@ public: auto resourceViewImpl = static_cast<ResourceViewImpl*>(resourceView); auto& bindingRange = layout->getBindingRange(offset.bindingRangeIndex); - auto descriptorSlotIndex = bindingRange.binding.offsetInDescriptorTable.resource + - (int32_t)offset.bindingArrayIndex; + auto descriptorSlotIndex = bindingRange.flatIndex + (int32_t)offset.bindingArrayIndex; // Hold a reference to the resource to prevent its destruction. - m_boundResources[bindingRange.flatResourceOffset + offset.bindingArrayIndex] = + m_boundResources[bindingRange.flatIndex + offset.bindingArrayIndex] = resourceViewImpl->m_resource; ID3D12Device* d3dDevice = static_cast<D3D12Device*>(getDevice())->m_device; d3dDevice->CopyDescriptorsSimple( 1, m_resourceHeap.getCpuHandle( m_descriptorSet.m_resourceTable + - bindingRange.binding.offsetInDescriptorTable.resource + - (int32_t)offset.bindingArrayIndex), + bindingRange.flatIndex + (int32_t)offset.bindingArrayIndex), resourceViewImpl->m_descriptor.cpuHandle, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); return SLANG_OK; @@ -1782,7 +2212,7 @@ public: 1, m_samplerHeap.getCpuHandle( m_descriptorSet.m_samplerTable + - bindingRange.binding.offsetInDescriptorTable.sampler + + bindingRange.flatIndex + (int32_t)offset.bindingArrayIndex), samplerImpl->m_descriptor.cpuHandle, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); @@ -1794,6 +2224,7 @@ public: IResourceView* textureView, ISamplerState* sampler) SLANG_OVERRIDE { +#if 0 if (offset.bindingRangeIndex < 0) return SLANG_E_INVALID_ARG; auto layout = getLayout(); @@ -1819,6 +2250,7 @@ public: (int32_t)offset.bindingArrayIndex), samplerImpl->m_descriptor.cpuHandle, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); +#endif return SLANG_OK; } @@ -1844,7 +2276,7 @@ public: SLANG_ASSERT(count == 1); Index subObjectIndexInRange = 0; - auto subObject = m_objects[bindingRange.binding.index + subObjectIndexInRange]; + auto subObject = m_objects[bindingRange.flatIndex + subObjectIndexInRange]; switch (bindingRange.bindingType) { @@ -1913,29 +2345,39 @@ public: memset(m_ordinaryData.getBuffer(), 0, uniformSize); } - // Allocate descriptor tables for this shader object. + // Each shader object will own CPU descriptor heap memory + // for any resource or sampler descriptors it might store + // as part of its value. + // + // This allocate includes a reservation for any constant + // buffer descriptor pertaining to the ordinary data, + // but does *not* include any descriptors that are managed + // as part of sub-objects. + // m_resourceHeap = viewHeap; m_samplerHeap = samplerHeap; - auto descSetInfo = layout->getDescriptorSetInfo(); - m_descriptorSet.m_resourceCount = descSetInfo.resourceDescriptorCount; - if (descSetInfo.resourceDescriptorCount) + + if(auto resourceCount = layout->getResourceSlotCount()) { - m_descriptorSet.m_resourceTable = - viewHeap.allocate(descSetInfo.resourceDescriptorCount); + m_descriptorSet.m_resourceTable = viewHeap.allocate(resourceCount); + + // We must also ensure that the memory for any resources + // referenced by descriptors in this object does not get + // freed while the object is still live. + // + m_boundResources.setCount(resourceCount); } - m_descriptorSet.m_samplerCount = descSetInfo.samplerDescriptorCount; - if (descSetInfo.samplerDescriptorCount) + + if(auto samplerCount = layout->getSamplerSlotCount()) { - m_descriptorSet.m_samplerTable = - samplerHeap.allocate(descSetInfo.samplerDescriptorCount); + m_descriptorSet.m_samplerTable = samplerHeap.allocate(samplerCount); } - m_boundResources.setCount(layout->getResourceCount()); // If the layout specifies that we have any sub-objects, then // we need to size the array to account for them. // - Index subObjectCount = layout->getSubObjectCount(); + Index subObjectCount = layout->getSubObjectSlotCount(); m_objects.setCount(subObjectCount); for (auto subObjectRangeInfo : layout->getSubObjectRanges()) @@ -1962,7 +2404,7 @@ public: RefPtr<ShaderObjectImpl> subObject; SLANG_RETURN_ON_FAIL( ShaderObjectImpl::create(device, subObjectLayout, subObject.writeRef())); - m_objects[bindingRangeInfo.binding.index + i] = subObject; + m_objects[bindingRangeInfo.flatIndex + i] = subObject; } } @@ -2039,10 +2481,8 @@ public: // layout logic does for complex cases with multiple layers of nested arrays and // structures. // - size_t subObjectRangePendingDataOffset = - _getSubObjectRangePendingDataOffset(specializedLayout, subObjectRangeIndex); - size_t subObjectRangePendingDataStride = - _getSubObjectRangePendingDataStride(specializedLayout, subObjectRangeIndex); + size_t subObjectRangePendingDataOffset = subObjectRangeInfo.offset.pendingOrdinaryData; + size_t subObjectRangePendingDataStride = subObjectRangeInfo.stride.pendingOrdinaryData; // If the range doesn't actually need/use the "pending" allocation at all, then // we need to detect that case and skip such ranges. @@ -2056,7 +2496,7 @@ public: for (uint32_t i = 0; i < count; ++i) { - auto subObject = m_objects[bindingRangeInfo.binding.index + i]; + auto subObject = m_objects[bindingRangeInfo.flatIndex + i]; RefPtr<ShaderObjectLayoutImpl> subObjectLayout; SLANG_RETURN_ON_FAIL( @@ -2077,24 +2517,10 @@ public: return SLANG_OK; } - // As discussed in `_writeOrdinaryData()`, these methods are just stubs waiting for - // the "flat" Slang refelction information to provide access to the relevant data. - // - size_t _getSubObjectRangePendingDataOffset( - ShaderObjectLayoutImpl* specializedLayout, - Index subObjectRangeIndex) - { - return 0; - } - size_t _getSubObjectRangePendingDataStride( - ShaderObjectLayoutImpl* specializedLayout, - Index subObjectRangeIndex) - { - return 0; - } - /// Ensure that the `m_ordinaryDataBuffer` has been created, if it is needed - Result _ensureOrdinaryDataBufferCreatedIfNeeded(PipelineCommandEncoder* encoder) + Result _ensureOrdinaryDataBufferCreatedIfNeeded( + PipelineCommandEncoder* encoder, + ShaderObjectLayoutImpl* specializedLayout) { // If we have already created a buffer to hold ordinary data, then we should // simply re-use that buffer rather than re-create it. @@ -2117,15 +2543,7 @@ public: // store the concrete values that logically belong in those interface-type // fields but wouldn't fit in the fixed-size allocation we gave them. // - // TODO: We need to actually implement that logic by using reflection - // data computed for the specialized type of this shader object. - // For now we just make the simple assumption described above despite - // knowing that it is false. - // - RefPtr<ShaderObjectLayoutImpl> specializedLayout; - SLANG_RETURN_ON_FAIL(getSpecializedLayout(specializedLayout.writeRef())); - - m_constantBufferSize = specializedLayout->getElementTypeLayout()->getSize(); + m_constantBufferSize = specializedLayout->getTotalOrdinaryDataSize(); if (m_constantBufferSize == 0) { m_upToDateConstantBufferHeapVersion = @@ -2180,78 +2598,197 @@ public: } public: - Result bindObject(PipelineCommandEncoder* encoder, RootBindingState* bindingState) + RootBindingIndex prepareToBindAsParameterBlock(uint32_t inRootParameterIndex) { ShaderObjectLayoutImpl* layout = getLayout(); - SLANG_RETURN_ON_FAIL(_ensureOrdinaryDataBufferCreatedIfNeeded(encoder)); - uint32_t descTableIndex = bindingState->rootParamIndex; + + auto rootParameterIndex = inRootParameterIndex; + + RootBindingIndex rootBindingIndex; + + if(layout->getTotalResourceDescriptorCount()) + { + rootBindingIndex.resource.descriptorTableIndex = rootParameterIndex++; + } + if(layout->getTotalSamplerDescriptorCount()) + { + rootBindingIndex.sampler.descriptorTableIndex = rootParameterIndex++; + } + rootBindingIndex.rootParamIndex = rootParameterIndex; + + return rootBindingIndex; + } + + Result bindAsParameterBlock(PipelineCommandEncoder* encoder, RootBindingState& bindingState, uint32_t rootParameterIndex, ShaderObjectLayoutImpl* layout) + { + auto rootBindingIndex = prepareToBindAsParameterBlock(rootParameterIndex); + SLANG_RETURN_ON_FAIL(bindAsConstantBuffer(encoder, bindingState, rootBindingIndex, layout)); + return SLANG_OK; + } + + Result bindAsConstantBuffer( + PipelineCommandEncoder* encoder, + RootBindingState& bindingState, + RootBindingIndex const& rootBindingIndex, + ShaderObjectLayoutImpl* specializedLayout) + { + SLANG_RETURN_ON_FAIL(_ensureOrdinaryDataBufferCreatedIfNeeded(encoder, specializedLayout)); + SLANG_RETURN_ON_FAIL(_bindImpl(encoder, bindingState, rootBindingIndex, specializedLayout, 0)); + return SLANG_OK; + } + + Result bindAsValue( + PipelineCommandEncoder* encoder, + RootBindingState& bindingState, + RootBindingIndex const& rootBindingIndex, + ShaderObjectLayoutImpl* layout) + { + SLANG_RETURN_ON_FAIL(_bindImpl(encoder, bindingState, rootBindingIndex, layout, layout->getOrdinaryDataBufferCount())); + return SLANG_OK; + } + + Result _bindImpl( + PipelineCommandEncoder* encoder, + RootBindingState& bindingState, + RootBindingIndex const& rootBindingIndex, + ShaderObjectLayoutImpl* layout, + uint32_t skipResourceCount) + { auto& descSet = m_descriptorSet; - if (descSet.m_resourceCount) + if(auto resourceCount = (layout->getResourceSlotCount() - skipResourceCount)) { - auto gpuDescriptorTable = bindingState->descriptorTables[descTableIndex]; - auto& gpuHeap = gpuDescriptorTable.heap; + auto dstIndex = rootBindingIndex.resource; + auto& dstTable = bindingState.descriptorTables[dstIndex.descriptorTableIndex]; auto& cpuHeap = m_resourceHeap; auto cpuDescriptorTable = descSet.m_resourceTable; - bindingState->device->m_device->CopyDescriptorsSimple( - UINT(descSet.m_resourceCount), - gpuHeap.getCpuHandle( - gpuDescriptorTable.table + bindingState->offset.resource), - cpuHeap.getCpuHandle(cpuDescriptorTable), + bindingState.device->m_device->CopyDescriptorsSimple( + UINT(resourceCount), + dstTable.getCpuHandle(dstIndex.descriptorIndex), + cpuHeap.getCpuHandle(cpuDescriptorTable + skipResourceCount), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); - bindingState->offset.resource += descSet.m_resourceCount; - descTableIndex++; } - if (descSet.m_samplerCount) + if (auto samplerCount = layout->getSamplerSlotCount()) { - auto gpuDescriptorTable = bindingState->descriptorTables[descTableIndex]; - auto& gpuHeap = gpuDescriptorTable.heap; + auto dstIndex = rootBindingIndex.sampler; + auto& dstTable = bindingState.descriptorTables[dstIndex.descriptorTableIndex]; auto& cpuHeap = m_samplerHeap; auto cpuDescriptorTable = (int)descSet.m_samplerTable; - bindingState->device->m_device->CopyDescriptorsSimple( - UINT(descSet.m_samplerCount), - gpuHeap.getCpuHandle( - gpuDescriptorTable.table + bindingState->offset.sampler), + bindingState.device->m_device->CopyDescriptorsSimple( + UINT(samplerCount), + dstTable.getCpuHandle(dstIndex.descriptorIndex), cpuHeap.getCpuHandle(cpuDescriptorTable), D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); - bindingState->offset.sampler += descSet.m_samplerCount; - descTableIndex++; } - bindingState->futureRootParamOffset = - Math::Max(descTableIndex, bindingState->futureRootParamOffset); + auto& subObjectRanges = layout->getSubObjectRanges(); - for (Index i = 0; i < subObjectRanges.getCount(); i++) + auto subObjectRangeCount = subObjectRanges.getCount(); + for (Index i = 0; i < subObjectRangeCount; i++) { - auto bindingRange = - layout->getBindingRange(layout->getSubObjectRange(i).bindingRangeIndex); - switch (layout->getSubObjectRange(i).bindingType) + auto subObjectRange = layout->getSubObjectRange(i); + auto bindingRange = layout->getBindingRange(subObjectRange.bindingRangeIndex); + auto subObjectLayout = subObjectRange.layout.Ptr(); + switch(bindingRange.bindingType) { + case slang::BindingType::ConstantBuffer: + { + auto baseIndex = bindingRange.flatIndex; + for (uint32_t j = 0; j < bindingRange.count; j++) + { + auto& object = m_objects[baseIndex + j]; + + RootBindingIndex subIndex = rootBindingIndex; + subIndex.rootParamIndex += subObjectRange.offset.rootParam; + subIndex.resource.descriptorIndex += subObjectRange.offset.resource; + subIndex.sampler.descriptorTableIndex += subObjectRange.offset.sampler; + + object->bindAsConstantBuffer( + encoder, + bindingState, + subIndex, + subObjectLayout); + } + } + break; + case slang::BindingType::ParameterBlock: { - auto baseIndex = bindingRange.binding.index; + auto baseIndex = bindingRange.flatIndex; for (uint32_t j = 0; j < bindingRange.count; j++) { - auto newBindingState = *bindingState; - newBindingState.offset.resource = 0; - newBindingState.offset.sampler = 0; - newBindingState.rootParamIndex = bindingState->futureRootParamOffset; - newBindingState.futureRootParamOffset = newBindingState.rootParamIndex; - m_objects[baseIndex + j]->bindObject(encoder, &newBindingState); - bindingState->futureRootParamOffset = - newBindingState.futureRootParamOffset; + auto& object = m_objects[baseIndex + j]; + + auto subRootParamIndex = rootBindingIndex.rootParamIndex + subObjectRange.offset.rootParam; + + object->bindAsParameterBlock( + encoder, + bindingState, + subRootParamIndex, + subObjectLayout); } } break; - case slang::BindingType::ConstantBuffer: + + case slang::BindingType::ExistentialValue: + if(subObjectLayout) { - auto baseIndex = bindingRange.binding.index; + auto baseIndex = bindingRange.flatIndex; for (uint32_t j = 0; j < bindingRange.count; j++) { - m_objects[baseIndex + j]->bindObject(encoder, bindingState); + auto& object = m_objects[baseIndex + j]; + + RootBindingIndex subIndex = rootBindingIndex; + subIndex.rootParamIndex += subObjectRange.offset.rootParam; + subIndex.resource.descriptorIndex += subObjectRange.offset.resource; + subIndex.sampler.descriptorTableIndex += subObjectRange.offset.sampler; + + object->bindAsValue( + encoder, + bindingState, + subIndex, + subObjectLayout); } } break; + } + } + +// SLANG_RETURN_ON_FAIL(bindChildRootParameters(encoder, bindingState, rootParameterIndex)); + + return SLANG_OK; + } + +#if 0 + Result bindChildRootParameters(PipelineCommandEncoder* encoder, RootBindingState& bindingState, SlangInt rootParameterIndex) + { + ShaderObjectLayoutImpl* layout = getLayout(); + auto& subObjectRanges = layout->getSubObjectRanges(); + auto subObjectRangeCount = subObjectRanges.getCount(); + for (Index i = 0; i < subObjectRangeCount; i++) + { + auto subObjectRange = layout->getSubObjectRange(i); + auto bindingRange = layout->getBindingRange(subObjectRange.bindingRangeIndex); + switch(bindingRange.bindingType) + { + case slang::BindingType::ParameterBlock: + { + auto baseIndex = bindingRange.flatIndex; + auto subRootParameterIndex = rootParameterIndex + subObjectRange.childRootParameterOffset; + for (uint32_t j = 0; j < bindingRange.count; j++) + { + auto& object = m_objects[baseIndex + j]; + + object->bindAsParameterBlock(encoder, bindingState, subRootParameterIndex); + subRootParameterIndex += subObjectRange.layout->getTotalRootParameterCount(); + } + } + break; + case slang::BindingType::ConstantBuffer: + { + // TODO: + } + break; case slang::BindingType::ExistentialValue: // If the existential object contains only ordinary data fields, // the data is already written into m_ordinaryDataBuffer during `setObject`, @@ -2263,8 +2800,10 @@ public: break; } } + return SLANG_OK; } +#endif /// Any "ordinary" / uniform data for this object List<char> m_ordinaryData; @@ -2279,8 +2818,8 @@ public: { int32_t m_resourceTable = 0; int32_t m_samplerTable = 0; - uint32_t m_resourceCount = 0; - uint32_t m_samplerCount = 0; +// uint32_t m_resourceCount = 0; +// uint32_t m_samplerCount = 0; }; DescriptorSet m_descriptorSet; @@ -2374,15 +2913,24 @@ public: } public: - Result bindObject(PipelineCommandEncoder* encoder, RootBindingState* bindingState) + Result bindRootObject(PipelineCommandEncoder* encoder, RootBindingState& bindingState, RootShaderObjectLayoutImpl* layout) { - SLANG_RETURN_ON_FAIL(Super::bindObject(encoder, bindingState)); + auto rootBindingIndex = prepareToBindAsParameterBlock(0); + + SLANG_RETURN_ON_FAIL(Super::bindAsConstantBuffer(encoder, bindingState, rootBindingIndex, layout)); auto entryPointCount = m_entryPoints.getCount(); for (Index i = 0; i < entryPointCount; ++i) { auto entryPoint = m_entryPoints[i]; - SLANG_RETURN_ON_FAIL(entryPoint->bindObject(encoder, bindingState)); + auto& entryPointInfo = layout->getEntryPoint(i); + + auto entryPointBindingIndex = rootBindingIndex; + entryPointBindingIndex.rootParamIndex += entryPointInfo.offset.rootParam; + entryPointBindingIndex.resource.descriptorIndex += entryPointInfo.offset.resource; + entryPointBindingIndex.sampler.descriptorIndex += entryPointInfo.offset.sampler; + + SLANG_RETURN_ON_FAIL(entryPoint->bindAsConstantBuffer(encoder, bindingState, entryPointBindingIndex, entryPointInfo.layout)); } return SLANG_OK; @@ -3392,7 +3940,7 @@ Result D3D12Device::PipelineCommandEncoder::_bindRenderState(Submitter* submitte static_cast<RootShaderObjectLayoutImpl*>(specializedRootLayout.Ptr()); ShortList<DescriptorTable> descriptorTables; - auto descSetInfo = rootLayoutImpl->getDescriptorSetInfo(); +// auto descSetInfo = rootLayoutImpl->getDescriptorSetInfo(); auto heap = m_commandBuffer->m_transientHeap; for (auto& descSet : rootLayoutImpl->m_gpuDescriptorSetInfos) { @@ -3416,7 +3964,7 @@ Result D3D12Device::PipelineCommandEncoder::_bindRenderState(Submitter* submitte bindState.transientHeap = m_transientHeap; auto descTablesView = descriptorTables.getArrayView(); bindState.descriptorTables = descTablesView.arrayView; - SLANG_RETURN_ON_FAIL(rootObjectImpl->bindObject(this, &bindState)); + SLANG_RETURN_ON_FAIL(rootObjectImpl->bindRootObject(this, bindState, rootLayoutImpl)); for (Index i = 0; i < descriptorTables.getCount(); i++) { |
