summaryrefslogtreecommitdiffstats
path: root/tools/gfx/d3d12/d3d12-shader-object-layout.h
blob: c6219d1a220cedb490ff587bfd610fe4812d57fc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
// d3d12-shader-object-layout.h
#pragma once

#include "d3d12-base.h"

namespace gfx
{
namespace d3d12
{

using namespace Slang;

/// A representation of the offset at which to bind a shader parameter or sub-object
struct BindingOffset
{
    // Note: When we actually bind a shader object to the pipeline we do not care about
    // HLSL-specific notions like `t` registers and `space`s. Those concepts are all
    // mediated by the root signature.
    //
    // Instead, we need to consider the offsets at which the object will be bound
    // into the actual D3D12 API state, which consists of the index of the current
    // root parameter to bind from, as well as indices into the current descriptor
    // tables (for resource views and samplers).

    uint32_t rootParam = 0;
    uint32_t resource = 0;
    uint32_t sampler = 0;

    void operator+=(BindingOffset const& offset)
    {
        rootParam += offset.rootParam;
        resource += offset.resource;
        sampler += offset.sampler;
    }
};

// Provides information on how binding ranges are stored in descriptor tables for
// a shader object.
// We allocate one CPU descriptor table for each descriptor heap type for the shader
// object. In `ShaderObjectLayoutImpl`, we store the offset into the descriptor tables
// for each binding, so we know where to write the descriptor when the user sets
// a resource or sampler binding.
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 shape of the resource
        SlangResourceShape resourceShape;

        /// The number of distinct bindings in this range.
        uint32_t count;

        /// A "flat" index for this range in whatever array provides backing storage for it
        uint32_t baseIndex;

        /// An index into the sub-object array if this binding range is treated
        /// as a sub-object.
        uint32_t subObjectIndex;

        /// The stride of a structured buffer.
        uint32_t bufferElementStride;

        bool isRootParameter;

        /// Is this binding range represent a specialization point, such as an existential value, or
        /// a `ParameterBlock<IFoo>`.
        bool isSpecializable;
    };

    /// Offset information for a sub-object range
    struct SubObjectRangeOffset : BindingOffset
    {
        SubObjectRangeOffset() {}

        SubObjectRangeOffset(slang::VariableLayoutReflection* varLayout);

        /// The offset for "pending" ordinary data related to this range
        uint32_t pendingOrdinaryData = 0;
    };

    /// Stride information for a sub-object range
    struct SubObjectRangeStride : BindingOffset
    {
        SubObjectRangeStride() {}

        SubObjectRangeStride(slang::TypeLayoutReflection* typeLayout);

        /// 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;

        /// 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
    {
        IResourceView::Type type;
    };

    static bool isBindingRangeRootParameter(
        SlangSession* globalSession,
        const char* rootParameterAttributeName,
        slang::TypeLayoutReflection* typeLayout,
        Index bindingRangeIndex);

    struct Builder
    {
    public:
        Builder(RendererBase* renderer, slang::ISession* session)
            : m_renderer(renderer), m_session(session)
        {
        }

        RendererBase* m_renderer;
        slang::ISession* m_session;
        slang::TypeLayoutReflection* m_elementTypeLayout;
        List<BindingRangeInfo> m_bindingRanges;
        List<SubObjectRangeInfo> m_subObjectRanges;
        List<RootParameterInfo> m_rootParamsInfo;

        /// The number of sub-objects (not just sub-object *ranges*) stored in instances of this
        /// layout
        uint32_t m_subObjectCount = 0;

        /// Counters for the number of root parameters, resources, and samplers in this object
        /// itself
        BindingOffset m_ownCounts;

        /// Counters for the number of root parameters, resources, and sampler in this object
        /// and transitive sub-objects
        BindingOffset m_totalCounts;

        /// The number of root parameter consumed by (transitive) sub-objects
        uint32_t m_childRootParameterCount = 0;

        /// The total size in bytes of the ordinary data for this object and transitive
        /// sub-object.
        uint32_t m_totalOrdinaryDataSize = 0;

        /// The container type of this shader object. When `m_containerType` is
        /// `StructuredBuffer` or `UnsizedArray`, this shader object represents a collection
        /// instead of a single object.
        ShaderObjectContainerType m_containerType = ShaderObjectContainerType::None;

        Result setElementTypeLayout(slang::TypeLayoutReflection* typeLayout);

        Result build(ShaderObjectLayoutImpl** outLayout);
    };

    static Result createForElementType(
        RendererBase* renderer,
        slang::ISession* session,
        slang::TypeLayoutReflection* elementType,
        ShaderObjectLayoutImpl** outLayout);

    List<BindingRangeInfo> const& getBindingRanges() { return m_bindingRanges; }

    Index getBindingRangeCount() { return m_bindingRanges.getCount(); }

    BindingRangeInfo const& getBindingRange(Index index) { return m_bindingRanges[index]; }

    uint32_t getResourceSlotCount() { return m_ownCounts.resource; }
    uint32_t getSamplerSlotCount() { return m_ownCounts.sampler; }
    Index getSubObjectSlotCount() { return m_subObjectCount; }
    Index getSubObjectCount() { return m_subObjectCount; }

    uint32_t getTotalResourceDescriptorCount() { return m_totalCounts.resource; }
    uint32_t getTotalSamplerDescriptorCount() { return m_totalCounts.sampler; }

    uint32_t getOrdinaryDataBufferCount() { return m_totalOrdinaryDataSize ? 1 : 0; }
    bool hasOrdinaryDataBuffer() { return m_totalOrdinaryDataSize != 0; }

    uint32_t getTotalResourceDescriptorCountWithoutOrdinaryDataBuffer()
    {
        return m_totalCounts.resource - getOrdinaryDataBufferCount();
    }

    uint32_t getOwnUserRootParameterCount() { return (uint32_t)m_rootParamsInfo.getCount(); }
    uint32_t getTotalRootTableParameterCount() { return m_totalCounts.rootParam; }
    uint32_t getChildRootParameterCount() { return m_childRootParameterCount; }

    uint32_t getTotalOrdinaryDataSize() const { return m_totalOrdinaryDataSize; }

    SubObjectRangeInfo const& getSubObjectRange(Index index) { return m_subObjectRanges[index]; }
    List<SubObjectRangeInfo> const& getSubObjectRanges() { return m_subObjectRanges; }

    RendererBase* getRenderer() { return m_renderer; }

    slang::TypeReflection* getType() { return m_elementTypeLayout->getType(); }

    const RootParameterInfo& getRootParameterInfo(Index index) { return m_rootParamsInfo[index]; }

protected:
    Result init(Builder* builder);

    List<BindingRangeInfo> m_bindingRanges;
    List<SubObjectRangeInfo> m_subObjectRanges;
    List<RootParameterInfo> m_rootParamsInfo;

    BindingOffset m_ownCounts;
    BindingOffset m_totalCounts;

    uint32_t m_subObjectCount = 0;
    uint32_t m_childRootParameterCount = 0;

    uint32_t m_totalOrdinaryDataSize = 0;
};

class RootShaderObjectLayoutImpl : public ShaderObjectLayoutImpl
{
    typedef ShaderObjectLayoutImpl Super;

public:
    struct EntryPointInfo
    {
        RefPtr<ShaderObjectLayoutImpl> layout;
        BindingOffset offset;
    };

    struct Builder : Super::Builder
    {
        Builder(
            RendererBase* renderer,
            slang::IComponentType* program,
            slang::ProgramLayout* programLayout)
            : Super::Builder(renderer, program->getSession())
            , m_program(program)
            , m_programLayout(programLayout)
        {
        }

        Result build(RootShaderObjectLayoutImpl** outLayout);

        void addGlobalParams(slang::VariableLayoutReflection* globalsLayout);

        void addEntryPoint(SlangStage stage, ShaderObjectLayoutImpl* entryPointLayout);

        slang::IComponentType* m_program;
        slang::ProgramLayout* m_programLayout;
        List<EntryPointInfo> m_entryPoints;
    };

    EntryPointInfo& getEntryPoint(Index index) { return m_entryPoints[index]; }

    List<EntryPointInfo>& getEntryPoints() { return m_entryPoints; }

    struct DescriptorSetLayout
    {
        List<D3D12_DESCRIPTOR_RANGE1> m_resourceRanges;
        List<D3D12_DESCRIPTOR_RANGE1> m_samplerRanges;
        uint32_t m_resourceCount = 0;
        uint32_t m_samplerCount = 0;
    };

    struct RootSignatureDescBuilder
    {
        DeviceImpl* m_device;

        RootSignatureDescBuilder(DeviceImpl* device)
            : m_device(device)
        {
        }

        // We will use one descriptor set for the global scope and one additional
        // descriptor set for each `ParameterBlock` binding range in the shader object
        // hierarchy, regardless of the shader's `space` indices.
        List<DescriptorSetLayout> m_descriptorSets;
        List<D3D12_ROOT_PARAMETER1> m_rootParameters;
        List<D3D12_ROOT_PARAMETER1> m_rootDescTableParameters;

        D3D12_ROOT_SIGNATURE_DESC1 m_rootSignatureDesc = {};

        static Result translateDescriptorRangeType(
            slang::BindingType c,
            D3D12_DESCRIPTOR_RANGE_TYPE* outType);

        /// Stores offset information to apply to the reflected register/space for a descriptor
        /// range.
        ///
        struct BindingRegisterOffset
        {
            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[kRangeTypeCount] = {0, 0, 0, 0};

            uint32_t& operator[](D3D12_DESCRIPTOR_RANGE_TYPE type)
            {
                return offsetForRangeType[int(type)];
            }

            uint32_t operator[](D3D12_DESCRIPTOR_RANGE_TYPE type) const
            {
                return offsetForRangeType[int(type)];
            }

            BindingRegisterOffset() {}

            BindingRegisterOffset(slang::VariableLayoutReflection* varLayout)
            {
                if (varLayout)
                {
                    spaceOffset = (UINT)varLayout->getOffset(
                        SLANG_PARAMETER_CATEGORY_SUB_ELEMENT_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;
            }
        };
        /// Add a new descriptor set to the layout being computed.
        ///
        /// Note that a "descriptor set" in the layout may amount to
        /// zero, one, or two different descriptor *tables* in the
        /// final D3D12 root signature. Each descriptor set may
        /// contain zero or more view ranges (CBV/SRV/UAV) and zero
        /// or more sampler ranges. It maps to a view descriptor table
        /// if the number of view ranges is non-zero and to a sampler
        /// descriptor table if the number of sampler ranges is non-zero.
        ///
        uint32_t addDescriptorSet();

        Result addDescriptorRange(
            Index physicalDescriptorSetIndex,
            D3D12_DESCRIPTOR_RANGE_TYPE rangeType,
            UINT registerIndex,
            UINT spaceIndex,
            UINT count,
            bool isRootParameter);
        /// Add one descriptor range as specified in Slang reflection information to the layout.
        ///
        /// The layout information is taken from `typeLayout` for the descriptor
        /// range with the given `descriptorRangeIndex` within the logical
        /// descriptor set (reflected by Slang) with the given `logicalDescriptorSetIndex`.
        ///
        /// The `physicalDescriptorSetIndex` is the index in the `m_descriptorSets` array of
        /// the descriptor set that the range should be added to.
        ///
        /// The `offset` encodes information about space and/or register offsets that
        /// should be applied to descrptor ranges.
        ///
        /// This operation can fail if the given descriptor range encodes a range that
        /// doesn't map to anything directly supported by D3D12. Higher-level routines
        /// will often want to ignore such failures.
        ///
        Result addDescriptorRange(
            slang::TypeLayoutReflection* typeLayout,
            Index physicalDescriptorSetIndex,
            BindingRegisterOffset const& containerOffset,
            BindingRegisterOffset const& elementOffset,
            Index logicalDescriptorSetIndex,
            Index descriptorRangeIndex,
            bool isRootParameter);

        /// Add one binding range to the computed layout.
        ///
        /// The layout information is taken from `typeLayout` for the binding
        /// range with the given `bindingRangeIndex`.
        ///
        /// The `physicalDescriptorSetIndex` is the index in the `m_descriptorSets` array of
        /// the descriptor set that the range should be added to.
        ///
        /// The `offset` encodes information about space and/or register offsets that
        /// should be applied to descrptor ranges.
        ///
        /// Note that a single binding range may encompass zero or more descriptor ranges.
        ///
        void addBindingRange(
            slang::TypeLayoutReflection* typeLayout,
            Index physicalDescriptorSetIndex,
            BindingRegisterOffset const& containerOffset,
            BindingRegisterOffset const& elementOffset,
            Index bindingRangeIndex);

        void addAsValue(
            slang::VariableLayoutReflection* varLayout,
            Index physicalDescriptorSetIndex);

        /// Add binding ranges and parameter blocks to the root signature.
        ///
        /// The layout information is taken from `typeLayout` 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.
        ///
        /// The `offset` encodes information about space and/or register offsets that
        /// should be applied to descrptor ranges.
        ///
        void addAsConstantBuffer(
            slang::TypeLayoutReflection* typeLayout,
            Index physicalDescriptorSetIndex,
            BindingRegisterOffsetPair containerOffset,
            BindingRegisterOffsetPair elementOffset);

        void addAsValue(
            slang::TypeLayoutReflection* typeLayout,
            Index physicalDescriptorSetIndex,
            BindingRegisterOffsetPair containerOffset,
            BindingRegisterOffsetPair elementOffset);

        D3D12_ROOT_SIGNATURE_DESC1& build();
    };

    static Result createRootSignatureFromSlang(
        DeviceImpl* device,
        RootShaderObjectLayoutImpl* rootLayout,
        slang::IComponentType* program,
        ID3D12RootSignature** outRootSignature,
        ID3DBlob** outError);

    static Result create(
        DeviceImpl* device,
        slang::IComponentType* program,
        slang::ProgramLayout* programLayout,
        RootShaderObjectLayoutImpl** outLayout,
        ID3DBlob** outError);

    slang::IComponentType* getSlangProgram() const { return m_program; }
    slang::ProgramLayout* getSlangProgramLayout() const { return m_programLayout; }

protected:
    Result init(Builder* builder);

    ComPtr<slang::IComponentType> m_program;
    slang::ProgramLayout* m_programLayout = nullptr;

    List<EntryPointInfo> m_entryPoints;

public:
    ComPtr<ID3D12RootSignature> m_rootSignature;
};

} // namespace d3d12
} // namespace gfx