yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

Ellie Hermaszewskaformatf65d756bf

master
18.0 KiB501 linesraw
1// d3d12-shader-object-layout.h
2#pragma once
3
4#include "d3d12-base.h"
5
6namespace gfx
7{
8namespace d3d12
9{
10
11using namespace Slang;
12
13/// A representation of the offset at which to bind a shader parameter or sub-object
14struct BindingOffset
15{
16    // Note: When we actually bind a shader object to the pipeline we do not care about
17    // HLSL-specific notions like `t` registers and `space`s. Those concepts are all
18    // mediated by the root signature.
19    //
20    // Instead, we need to consider the offsets at which the object will be bound
21    // into the actual D3D12 API state, which consists of the index of the current
22    // root parameter to bind from, as well as indices into the current descriptor
23    // tables (for resource views and samplers).
24
25    uint32_t rootParam = 0;
26    uint32_t resource = 0;
27    uint32_t sampler = 0;
28
29    void operator+=(BindingOffset const& offset)
30    {
31        rootParam += offset.rootParam;
32        resource += offset.resource;
33        sampler += offset.sampler;
34    }
35};
36
37// Provides information on how binding ranges are stored in descriptor tables for
38// a shader object.
39// We allocate one CPU descriptor table for each descriptor heap type for the shader
40// object. In `ShaderObjectLayoutImpl`, we store the offset into the descriptor tables
41// for each binding, so we know where to write the descriptor when the user sets
42// a resource or sampler binding.
43class ShaderObjectLayoutImpl : public ShaderObjectLayoutBase
44{
45public:
46    /// Information about a single logical binding range
47    struct BindingRangeInfo
48    {
49        // Some of the information we store on binding ranges is redundant with
50        // the information that Slang's reflection information stores, but having
51        // it here can make the code more compact and obvious.
52
53        /// The type of binding in this range.
54        slang::BindingType bindingType;
55
56        /// The shape of the resource
57        SlangResourceShape resourceShape;
58
59        /// The number of distinct bindings in this range.
60        uint32_t count;
61
62        /// A "flat" index for this range in whatever array provides backing storage for it
63        uint32_t baseIndex;
64
65        /// An index into the sub-object array if this binding range is treated
66        /// as a sub-object.
67        uint32_t subObjectIndex;
68
69        /// The stride of a structured buffer.
70        uint32_t bufferElementStride;
71
72        bool isRootParameter;
73
74        /// Is this binding range represent a specialization point, such as an existential value, or
75        /// a `ParameterBlock<IFoo>`.
76        bool isSpecializable;
77    };
78
79    /// Offset information for a sub-object range
80    struct SubObjectRangeOffset : BindingOffset
81    {
82        SubObjectRangeOffset() {}
83
84        SubObjectRangeOffset(slang::VariableLayoutReflection* varLayout);
85
86        /// The offset for "pending" ordinary data related to this range
87        uint32_t pendingOrdinaryData = 0;
88    };
89
90    /// Stride information for a sub-object range
91    struct SubObjectRangeStride : BindingOffset
92    {
93        SubObjectRangeStride() {}
94
95        SubObjectRangeStride(slang::TypeLayoutReflection* typeLayout);
96
97        /// The strid for "pending" ordinary data related to this range
98        uint32_t pendingOrdinaryData = 0;
99    };
100
101    /// Information about a sub-objecrt range
102    struct SubObjectRangeInfo
103    {
104        /// The index of the binding range corresponding to this sub-object range
105        Index bindingRangeIndex = 0;
106
107        /// Layout information for the type of sub-object expected to be bound, if known
108        RefPtr<ShaderObjectLayoutImpl> layout;
109
110        /// The offset to use when binding the first object in this range
111        SubObjectRangeOffset offset;
112
113        /// Stride between consecutive objects in this range
114        SubObjectRangeStride stride;
115    };
116
117    struct RootParameterInfo
118    {
119        IResourceView::Type type;
120    };
121
122    static bool isBindingRangeRootParameter(
123        SlangSession* globalSession,
124        const char* rootParameterAttributeName,
125        slang::TypeLayoutReflection* typeLayout,
126        Index bindingRangeIndex);
127
128    struct Builder
129    {
130    public:
131        Builder(RendererBase* renderer, slang::ISession* session)
132            : m_renderer(renderer), m_session(session)
133        {
134        }
135
136        RendererBase* m_renderer;
137        slang::ISession* m_session;
138        slang::TypeLayoutReflection* m_elementTypeLayout;
139        List<BindingRangeInfo> m_bindingRanges;
140        List<SubObjectRangeInfo> m_subObjectRanges;
141        List<RootParameterInfo> m_rootParamsInfo;
142
143        /// The number of sub-objects (not just sub-object *ranges*) stored in instances of this
144        /// layout
145        uint32_t m_subObjectCount = 0;
146
147        /// Counters for the number of root parameters, resources, and samplers in this object
148        /// itself
149        BindingOffset m_ownCounts;
150
151        /// Counters for the number of root parameters, resources, and sampler in this object
152        /// and transitive sub-objects
153        BindingOffset m_totalCounts;
154
155        /// The number of root parameter consumed by (transitive) sub-objects
156        uint32_t m_childRootParameterCount = 0;
157
158        /// The total size in bytes of the ordinary data for this object and transitive
159        /// sub-object.
160        uint32_t m_totalOrdinaryDataSize = 0;
161
162        /// The container type of this shader object. When `m_containerType` is
163        /// `StructuredBuffer` or `UnsizedArray`, this shader object represents a collection
164        /// instead of a single object.
165        ShaderObjectContainerType m_containerType = ShaderObjectContainerType::None;
166
167        Result setElementTypeLayout(slang::TypeLayoutReflection* typeLayout);
168
169        Result build(ShaderObjectLayoutImpl** outLayout);
170    };
171
172    static Result createForElementType(
173        RendererBase* renderer,
174        slang::ISession* session,
175        slang::TypeLayoutReflection* elementType,
176        ShaderObjectLayoutImpl** outLayout);
177
178    List<BindingRangeInfo> const& getBindingRanges() { return m_bindingRanges; }
179
180    Index getBindingRangeCount() { return m_bindingRanges.getCount(); }
181
182    BindingRangeInfo const& getBindingRange(Index index) { return m_bindingRanges[index]; }
183
184    uint32_t getResourceSlotCount() { return m_ownCounts.resource; }
185    uint32_t getSamplerSlotCount() { return m_ownCounts.sampler; }
186    Index getSubObjectSlotCount() { return m_subObjectCount; }
187    Index getSubObjectCount() { return m_subObjectCount; }
188
189    uint32_t getTotalResourceDescriptorCount() { return m_totalCounts.resource; }
190    uint32_t getTotalSamplerDescriptorCount() { return m_totalCounts.sampler; }
191
192    uint32_t getOrdinaryDataBufferCount() { return m_totalOrdinaryDataSize ? 1 : 0; }
193    bool hasOrdinaryDataBuffer() { return m_totalOrdinaryDataSize != 0; }
194
195    uint32_t getTotalResourceDescriptorCountWithoutOrdinaryDataBuffer()
196    {
197        return m_totalCounts.resource - getOrdinaryDataBufferCount();
198    }
199
200    uint32_t getOwnUserRootParameterCount() { return (uint32_t)m_rootParamsInfo.getCount(); }
201    uint32_t getTotalRootTableParameterCount() { return m_totalCounts.rootParam; }
202    uint32_t getChildRootParameterCount() { return m_childRootParameterCount; }
203
204    uint32_t getTotalOrdinaryDataSize() const { return m_totalOrdinaryDataSize; }
205
206    SubObjectRangeInfo const& getSubObjectRange(Index index) { return m_subObjectRanges[index]; }
207    List<SubObjectRangeInfo> const& getSubObjectRanges() { return m_subObjectRanges; }
208
209    RendererBase* getRenderer() { return m_renderer; }
210
211    slang::TypeReflection* getType() { return m_elementTypeLayout->getType(); }
212
213    const RootParameterInfo& getRootParameterInfo(Index index) { return m_rootParamsInfo[index]; }
214
215protected:
216    Result init(Builder* builder);
217
218    List<BindingRangeInfo> m_bindingRanges;
219    List<SubObjectRangeInfo> m_subObjectRanges;
220    List<RootParameterInfo> m_rootParamsInfo;
221
222    BindingOffset m_ownCounts;
223    BindingOffset m_totalCounts;
224
225    uint32_t m_subObjectCount = 0;
226    uint32_t m_childRootParameterCount = 0;
227
228    uint32_t m_totalOrdinaryDataSize = 0;
229};
230
231class RootShaderObjectLayoutImpl : public ShaderObjectLayoutImpl
232{
233    typedef ShaderObjectLayoutImpl Super;
234
235public:
236    struct EntryPointInfo
237    {
238        RefPtr<ShaderObjectLayoutImpl> layout;
239        BindingOffset offset;
240    };
241
242    struct Builder : Super::Builder
243    {
244        Builder(
245            RendererBase* renderer,
246            slang::IComponentType* program,
247            slang::ProgramLayout* programLayout)
248            : Super::Builder(renderer, program->getSession())
249            , m_program(program)
250            , m_programLayout(programLayout)
251        {
252        }
253
254        Result build(RootShaderObjectLayoutImpl** outLayout);
255
256        void addGlobalParams(slang::VariableLayoutReflection* globalsLayout);
257
258        void addEntryPoint(SlangStage stage, ShaderObjectLayoutImpl* entryPointLayout);
259
260        slang::IComponentType* m_program;
261        slang::ProgramLayout* m_programLayout;
262        List<EntryPointInfo> m_entryPoints;
263    };
264
265    EntryPointInfo& getEntryPoint(Index index) { return m_entryPoints[index]; }
266
267    List<EntryPointInfo>& getEntryPoints() { return m_entryPoints; }
268
269    struct DescriptorSetLayout
270    {
271        List<D3D12_DESCRIPTOR_RANGE1> m_resourceRanges;
272        List<D3D12_DESCRIPTOR_RANGE1> m_samplerRanges;
273        uint32_t m_resourceCount = 0;
274        uint32_t m_samplerCount = 0;
275    };
276
277    struct RootSignatureDescBuilder
278    {
279        DeviceImpl* m_device;
280
281        RootSignatureDescBuilder(DeviceImpl* device)
282            : m_device(device)
283        {
284        }
285
286        // We will use one descriptor set for the global scope and one additional
287        // descriptor set for each `ParameterBlock` binding range in the shader object
288        // hierarchy, regardless of the shader's `space` indices.
289        List<DescriptorSetLayout> m_descriptorSets;
290        List<D3D12_ROOT_PARAMETER1> m_rootParameters;
291        List<D3D12_ROOT_PARAMETER1> m_rootDescTableParameters;
292
293        D3D12_ROOT_SIGNATURE_DESC1 m_rootSignatureDesc = {};
294
295        static Result translateDescriptorRangeType(
296            slang::BindingType c,
297            D3D12_DESCRIPTOR_RANGE_TYPE* outType);
298
299        /// Stores offset information to apply to the reflected register/space for a descriptor
300        /// range.
301        ///
302        struct BindingRegisterOffset
303        {
304            uint32_t spaceOffset = 0; // The `space` index as specified in shader.
305
306            enum
307            {
308                kRangeTypeCount = 4
309            };
310
311            /// An offset to apply for each D3D12 register class, as given
312            /// by a `D3D12_DESCRIPTOR_RANGE_TYPE`.
313            ///
314            /// Note that the `D3D12_DESCRIPTOR_RANGE_TYPE` enumeration has
315            /// values between 0 and 3, inclusive.
316            ///
317            uint32_t offsetForRangeType[kRangeTypeCount] = {0, 0, 0, 0};
318
319            uint32_t& operator[](D3D12_DESCRIPTOR_RANGE_TYPE type)
320            {
321                return offsetForRangeType[int(type)];
322            }
323
324            uint32_t operator[](D3D12_DESCRIPTOR_RANGE_TYPE type) const
325            {
326                return offsetForRangeType[int(type)];
327            }
328
329            BindingRegisterOffset() {}
330
331            BindingRegisterOffset(slang::VariableLayoutReflection* varLayout)
332            {
333                if (varLayout)
334                {
335                    spaceOffset = (UINT)varLayout->getOffset(
336                        SLANG_PARAMETER_CATEGORY_SUB_ELEMENT_REGISTER_SPACE);
337                    offsetForRangeType[D3D12_DESCRIPTOR_RANGE_TYPE_CBV] =
338                        (UINT)varLayout->getOffset(SLANG_PARAMETER_CATEGORY_CONSTANT_BUFFER);
339                    offsetForRangeType[D3D12_DESCRIPTOR_RANGE_TYPE_SRV] =
340                        (UINT)varLayout->getOffset(SLANG_PARAMETER_CATEGORY_SHADER_RESOURCE);
341                    offsetForRangeType[D3D12_DESCRIPTOR_RANGE_TYPE_UAV] =
342                        (UINT)varLayout->getOffset(SLANG_PARAMETER_CATEGORY_UNORDERED_ACCESS);
343                    offsetForRangeType[D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER] =
344                        (UINT)varLayout->getOffset(SLANG_PARAMETER_CATEGORY_SAMPLER_STATE);
345                }
346            }
347
348            void operator+=(BindingRegisterOffset const& other)
349            {
350                spaceOffset += other.spaceOffset;
351                for (int i = 0; i < kRangeTypeCount; ++i)
352                {
353                    offsetForRangeType[i] += other.offsetForRangeType[i];
354                }
355            }
356        };
357
358        struct BindingRegisterOffsetPair
359        {
360            BindingRegisterOffset primary;
361            BindingRegisterOffset pending;
362
363            BindingRegisterOffsetPair() {}
364
365            BindingRegisterOffsetPair(slang::VariableLayoutReflection* varLayout)
366                : primary(varLayout), pending(varLayout->getPendingDataLayout())
367            {
368            }
369
370            void operator+=(BindingRegisterOffsetPair const& other)
371            {
372                primary += other.primary;
373                pending += other.pending;
374            }
375        };
376        /// Add a new descriptor set to the layout being computed.
377        ///
378        /// Note that a "descriptor set" in the layout may amount to
379        /// zero, one, or two different descriptor *tables* in the
380        /// final D3D12 root signature. Each descriptor set may
381        /// contain zero or more view ranges (CBV/SRV/UAV) and zero
382        /// or more sampler ranges. It maps to a view descriptor table
383        /// if the number of view ranges is non-zero and to a sampler
384        /// descriptor table if the number of sampler ranges is non-zero.
385        ///
386        uint32_t addDescriptorSet();
387
388        Result addDescriptorRange(
389            Index physicalDescriptorSetIndex,
390            D3D12_DESCRIPTOR_RANGE_TYPE rangeType,
391            UINT registerIndex,
392            UINT spaceIndex,
393            UINT count,
394            bool isRootParameter);
395        /// Add one descriptor range as specified in Slang reflection information to the layout.
396        ///
397        /// The layout information is taken from `typeLayout` for the descriptor
398        /// range with the given `descriptorRangeIndex` within the logical
399        /// descriptor set (reflected by Slang) with the given `logicalDescriptorSetIndex`.
400        ///
401        /// The `physicalDescriptorSetIndex` is the index in the `m_descriptorSets` array of
402        /// the descriptor set that the range should be added to.
403        ///
404        /// The `offset` encodes information about space and/or register offsets that
405        /// should be applied to descrptor ranges.
406        ///
407        /// This operation can fail if the given descriptor range encodes a range that
408        /// doesn't map to anything directly supported by D3D12. Higher-level routines
409        /// will often want to ignore such failures.
410        ///
411        Result addDescriptorRange(
412            slang::TypeLayoutReflection* typeLayout,
413            Index physicalDescriptorSetIndex,
414            BindingRegisterOffset const& containerOffset,
415            BindingRegisterOffset const& elementOffset,
416            Index logicalDescriptorSetIndex,
417            Index descriptorRangeIndex,
418            bool isRootParameter);
419
420        /// Add one binding range to the computed layout.
421        ///
422        /// The layout information is taken from `typeLayout` for the binding
423        /// range with the given `bindingRangeIndex`.
424        ///
425        /// The `physicalDescriptorSetIndex` is the index in the `m_descriptorSets` array of
426        /// the descriptor set that the range should be added to.
427        ///
428        /// The `offset` encodes information about space and/or register offsets that
429        /// should be applied to descrptor ranges.
430        ///
431        /// Note that a single binding range may encompass zero or more descriptor ranges.
432        ///
433        void addBindingRange(
434            slang::TypeLayoutReflection* typeLayout,
435            Index physicalDescriptorSetIndex,
436            BindingRegisterOffset const& containerOffset,
437            BindingRegisterOffset const& elementOffset,
438            Index bindingRangeIndex);
439
440        void addAsValue(
441            slang::VariableLayoutReflection* varLayout,
442            Index physicalDescriptorSetIndex);
443
444        /// Add binding ranges and parameter blocks to the root signature.
445        ///
446        /// The layout information is taken from `typeLayout` which should
447        /// be a layout for either a program or an entry point.
448        ///
449        /// The `physicalDescriptorSetIndex` is the index in the `m_descriptorSets` array of
450        /// the descriptor set that binding ranges not belonging to nested
451        /// parameter blocks should be added to.
452        ///
453        /// The `offset` encodes information about space and/or register offsets that
454        /// should be applied to descrptor ranges.
455        ///
456        void addAsConstantBuffer(
457            slang::TypeLayoutReflection* typeLayout,
458            Index physicalDescriptorSetIndex,
459            BindingRegisterOffsetPair containerOffset,
460            BindingRegisterOffsetPair elementOffset);
461
462        void addAsValue(
463            slang::TypeLayoutReflection* typeLayout,
464            Index physicalDescriptorSetIndex,
465            BindingRegisterOffsetPair containerOffset,
466            BindingRegisterOffsetPair elementOffset);
467
468        D3D12_ROOT_SIGNATURE_DESC1& build();
469    };
470
471    static Result createRootSignatureFromSlang(
472        DeviceImpl* device,
473        RootShaderObjectLayoutImpl* rootLayout,
474        slang::IComponentType* program,
475        ID3D12RootSignature** outRootSignature,
476        ID3DBlob** outError);
477
478    static Result create(
479        DeviceImpl* device,
480        slang::IComponentType* program,
481        slang::ProgramLayout* programLayout,
482        RootShaderObjectLayoutImpl** outLayout,
483        ID3DBlob** outError);
484
485    slang::IComponentType* getSlangProgram() const { return m_program; }
486    slang::ProgramLayout* getSlangProgramLayout() const { return m_programLayout; }
487
488protected:
489    Result init(Builder* builder);
490
491    ComPtr<slang::IComponentType> m_program;
492    slang::ProgramLayout* m_programLayout = nullptr;
493
494    List<EntryPointInfo> m_entryPoints;
495
496public:
497    ComPtr<ID3D12RootSignature> m_rootSignature;
498};
499
500} // namespace d3d12
501} // namespace gfx