yum-mirror/slang

Making it easier to work with shaders

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

Ellie HermaszewskaMove switch statement bodies to their own lines (#5493)b118451e3

master
12.1 KiB339 linesraw
1// metal-shader-object-layout.cpp
2#include "metal-shader-object-layout.h"
3
4namespace gfx
5{
6
7using namespace Slang;
8
9namespace metal
10{
11
12ShaderObjectLayoutImpl::SubObjectRangeOffset::SubObjectRangeOffset(
13    slang::VariableLayoutReflection* varLayout)
14    : BindingOffset(varLayout)
15{
16    if (auto pendingLayout = varLayout->getPendingDataLayout())
17    {
18        pendingOrdinaryData = (uint32_t)pendingLayout->getOffset(SLANG_PARAMETER_CATEGORY_UNIFORM);
19    }
20}
21
22ShaderObjectLayoutImpl::SubObjectRangeStride::SubObjectRangeStride(
23    slang::TypeLayoutReflection* typeLayout)
24    : BindingOffset(typeLayout)
25{
26    if (auto pendingLayout = typeLayout->getPendingDataTypeLayout())
27    {
28        pendingOrdinaryData = (uint32_t)typeLayout->getStride();
29    }
30}
31
32Result ShaderObjectLayoutImpl::Builder::setElementTypeLayout(
33    slang::TypeLayoutReflection* typeLayout)
34{
35    typeLayout = _unwrapParameterGroups(typeLayout, m_containerType);
36
37    m_elementTypeLayout = typeLayout;
38
39    m_totalOrdinaryDataSize = (uint32_t)typeLayout->getSize();
40    if (m_totalOrdinaryDataSize > 0)
41    {
42        m_bufferCount++;
43    }
44
45    // Compute the binding ranges that are used to store
46    // the logical contents of the object in memory.
47
48    SlangInt bindingRangeCount = typeLayout->getBindingRangeCount();
49    for (SlangInt r = 0; r < bindingRangeCount; ++r)
50    {
51        slang::BindingType slangBindingType = typeLayout->getBindingRangeType(r);
52        SlangInt count = typeLayout->getBindingRangeBindingCount(r);
53        slang::TypeLayoutReflection* slangLeafTypeLayout =
54            typeLayout->getBindingRangeLeafTypeLayout(r);
55
56        BindingRangeInfo bindingRangeInfo;
57        bindingRangeInfo.bindingType = slangBindingType;
58        bindingRangeInfo.count = count;
59        switch (slangBindingType)
60        {
61        case slang::BindingType::ConstantBuffer:
62        case slang::BindingType::ParameterBlock:
63        case slang::BindingType::ExistentialValue:
64            bindingRangeInfo.baseIndex = m_subObjectCount;
65            bindingRangeInfo.subObjectIndex = m_subObjectCount;
66            m_subObjectCount += count;
67            break;
68        case slang::BindingType::RawBuffer:
69        case slang::BindingType::MutableRawBuffer:
70            bindingRangeInfo.baseIndex = m_bufferCount;
71            if (slangLeafTypeLayout->getType()->getElementType() != nullptr)
72            {
73                // A structured buffer occupies both a resource slot and
74                // a sub-object slot.
75                bindingRangeInfo.subObjectIndex = m_subObjectCount;
76                m_subObjectCount += count;
77            }
78            m_bufferCount += count;
79            m_bufferRanges.add(r);
80            break;
81        case slang::BindingType::Sampler:
82            bindingRangeInfo.baseIndex = m_samplerCount;
83            m_samplerCount += count;
84            m_samplerRanges.add(r);
85            break;
86        case slang::BindingType::Texture:
87        case slang::BindingType::MutableTexture:
88            bindingRangeInfo.baseIndex = m_textureCount;
89            m_textureCount += count;
90            m_textureRanges.add(r);
91            break;
92        case slang::BindingType::TypedBuffer:
93        case slang::BindingType::MutableTypedBuffer:
94            bindingRangeInfo.baseIndex = m_textureCount;
95            m_textureCount += count;
96            m_textureRanges.add(r);
97            break;
98        default:
99            break;
100        }
101
102        // We'd like to extract the information on the Metal resource
103        // index that this range should bind into.
104        //
105        // A binding range represents a logical member of the shader
106        // object type, and it may encompass zero or more *descriptor
107        // ranges* that describe how it is physically bound to pipeline
108        // state.
109        //
110        // If the current binding range is backed by at least one descriptor
111        // range then we can query the register offset of that descriptor
112        // range. We expect that in the common case there will be exactly
113        // one descriptor range, and we can extract the information easily.
114        //
115        // TODO: we might eventually need to special-case our handling
116        // of combined texture-sampler ranges since they will need to
117        // store two different offsets.
118        //
119        if (typeLayout->getBindingRangeDescriptorRangeCount(r) != 0)
120        {
121            // The Slang reflection information organizes the descriptor ranges
122            // into "descriptor sets" but Metal has no notion like that so we
123            // expect all ranges belong to a single set.
124            //
125            SlangInt descriptorSetIndex = typeLayout->getBindingRangeDescriptorSetIndex(r);
126            SLANG_ASSERT(descriptorSetIndex == 0);
127
128            SlangInt descriptorRangeIndex = typeLayout->getBindingRangeFirstDescriptorRangeIndex(r);
129            auto registerOffset = typeLayout->getDescriptorSetDescriptorRangeIndexOffset(
130                descriptorSetIndex,
131                descriptorRangeIndex);
132
133            bindingRangeInfo.registerOffset = (uint32_t)registerOffset;
134        }
135
136        m_bindingRanges.add(bindingRangeInfo);
137    }
138
139    SlangInt subObjectRangeCount = typeLayout->getSubObjectRangeCount();
140    for (SlangInt r = 0; r < subObjectRangeCount; ++r)
141    {
142        SlangInt bindingRangeIndex = typeLayout->getSubObjectRangeBindingRangeIndex(r);
143        auto& bindingRange = m_bindingRanges[bindingRangeIndex];
144
145        auto slangBindingType = typeLayout->getBindingRangeType(bindingRangeIndex);
146        slang::TypeLayoutReflection* slangLeafTypeLayout =
147            typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex);
148
149        SubObjectRangeInfo subObjectRange;
150        subObjectRange.bindingRangeIndex = bindingRangeIndex;
151
152        // We will use Slang reflection information to extract the offset and stride
153        // information for each sub-object range.
154        //
155        subObjectRange.offset = SubObjectRangeOffset(typeLayout->getSubObjectRangeOffset(r));
156        subObjectRange.stride = SubObjectRangeStride(slangLeafTypeLayout);
157
158        // A sub-object range can either represent a sub-object of a known
159        // type, like a `ConstantBuffer<Foo>` or `ParameterBlock<Foo>`
160        // *or* it can represent a sub-object of some existential type (e.g., `IBar`).
161        //
162        RefPtr<ShaderObjectLayoutImpl> subObjectLayout;
163        switch (slangBindingType)
164        {
165        default:
166            {
167                // In the case of `ConstantBuffer<X>` or `ParameterBlock<X>`
168                // we can construct a layout from the element type directly.
169                //
170                auto elementTypeLayout = slangLeafTypeLayout->getElementTypeLayout();
171                createForElementType(
172                    m_renderer,
173                    m_session,
174                    elementTypeLayout,
175                    subObjectLayout.writeRef());
176            }
177            break;
178        case slang::BindingType::ExistentialValue:
179            // In the case of an interface-type sub-object range, we can only
180            // construct a layout if we have static specialization information
181            // that tells us what type we expect to find in that range.
182            //
183            // The static specialization information is expected to take the
184            // form of a "pending" type layotu attached to the interface type
185            // of the leaf type layout.
186            //
187            if (auto pendingTypeLayout = slangLeafTypeLayout->getPendingDataTypeLayout())
188            {
189                createForElementType(
190                    m_renderer,
191                    m_session,
192                    pendingTypeLayout,
193                    subObjectLayout.writeRef());
194
195                // An interface-type range that includes ordinary data can
196                // increase the size of the ordinary data buffer we need to
197                // allocate for the parent object.
198                //
199                uint32_t ordinaryDataEnd =
200                    subObjectRange.offset.pendingOrdinaryData +
201                    (uint32_t)bindingRange.count * subObjectRange.stride.pendingOrdinaryData;
202
203                if (ordinaryDataEnd > m_totalOrdinaryDataSize)
204                {
205                    m_totalOrdinaryDataSize = ordinaryDataEnd;
206                }
207            }
208        }
209        subObjectRange.layout = subObjectLayout;
210
211        m_subObjectRanges.add(subObjectRange);
212    }
213    return SLANG_OK;
214}
215
216SlangResult ShaderObjectLayoutImpl::Builder::build(ShaderObjectLayoutImpl** outLayout)
217{
218    auto layout = RefPtr<ShaderObjectLayoutImpl>(new ShaderObjectLayoutImpl());
219    SLANG_RETURN_ON_FAIL(layout->_init(this));
220
221    returnRefPtrMove(outLayout, layout);
222    return SLANG_OK;
223}
224
225slang::TypeLayoutReflection* ShaderObjectLayoutImpl::getParameterBlockTypeLayout()
226{
227    if (!m_parameterBlockTypeLayout)
228    {
229        m_parameterBlockTypeLayout = m_slangSession->getTypeLayout(
230            m_elementTypeLayout->getType(),
231            0,
232            slang::LayoutRules::MetalArgumentBufferTier2);
233    }
234    return m_parameterBlockTypeLayout;
235}
236
237Result ShaderObjectLayoutImpl::createForElementType(
238    RendererBase* renderer,
239    slang::ISession* session,
240    slang::TypeLayoutReflection* elementType,
241    ShaderObjectLayoutImpl** outLayout)
242{
243    Builder builder(renderer, session);
244    builder.setElementTypeLayout(elementType);
245    return builder.build(outLayout);
246}
247
248Result ShaderObjectLayoutImpl::_init(Builder const* builder)
249{
250    auto renderer = builder->m_renderer;
251
252    initBase(renderer, builder->m_session, builder->m_elementTypeLayout);
253
254    m_bindingRanges = builder->m_bindingRanges;
255    m_bufferRanges = builder->m_bufferRanges;
256    m_textureRanges = builder->m_textureRanges;
257    m_samplerRanges = builder->m_samplerRanges;
258
259    m_bufferCount = builder->m_bufferCount;
260    m_textureCount = builder->m_textureCount;
261    m_samplerCount = builder->m_samplerCount;
262    m_subObjectCount = builder->m_subObjectCount;
263    m_subObjectRanges = builder->m_subObjectRanges;
264
265    m_totalOrdinaryDataSize = builder->m_totalOrdinaryDataSize;
266
267    m_containerType = builder->m_containerType;
268    return SLANG_OK;
269}
270
271Result RootShaderObjectLayoutImpl::Builder::build(RootShaderObjectLayoutImpl** outLayout)
272{
273    RefPtr<RootShaderObjectLayoutImpl> layout = new RootShaderObjectLayoutImpl();
274    SLANG_RETURN_ON_FAIL(layout->_init(this));
275
276    returnRefPtrMove(outLayout, layout);
277    return SLANG_OK;
278}
279
280void RootShaderObjectLayoutImpl::Builder::addGlobalParams(
281    slang::VariableLayoutReflection* globalsLayout)
282{
283    setElementTypeLayout(globalsLayout->getTypeLayout());
284}
285
286void RootShaderObjectLayoutImpl::Builder::addEntryPoint(
287    SlangStage stage,
288    ShaderObjectLayoutImpl* entryPointLayout,
289    slang::EntryPointLayout* slangEntryPoint)
290{
291    EntryPointInfo info;
292    info.layout = entryPointLayout;
293    info.offset = BindingOffset(slangEntryPoint->getVarLayout());
294    m_entryPoints.add(info);
295}
296
297Result RootShaderObjectLayoutImpl::create(
298    RendererBase* renderer,
299    slang::IComponentType* program,
300    slang::ProgramLayout* programLayout,
301    RootShaderObjectLayoutImpl** outLayout)
302{
303    RootShaderObjectLayoutImpl::Builder builder(renderer, program, programLayout);
304    builder.addGlobalParams(programLayout->getGlobalParamsVarLayout());
305
306    SlangInt entryPointCount = programLayout->getEntryPointCount();
307    for (SlangInt e = 0; e < entryPointCount; ++e)
308    {
309        auto slangEntryPoint = programLayout->getEntryPointByIndex(e);
310        RefPtr<ShaderObjectLayoutImpl> entryPointLayout;
311        SLANG_RETURN_ON_FAIL(ShaderObjectLayoutImpl::createForElementType(
312            renderer,
313            program->getSession(),
314            slangEntryPoint->getTypeLayout(),
315            entryPointLayout.writeRef()));
316        builder.addEntryPoint(slangEntryPoint->getStage(), entryPointLayout, slangEntryPoint);
317    }
318
319    SLANG_RETURN_ON_FAIL(builder.build(outLayout));
320
321    return SLANG_OK;
322}
323
324Result RootShaderObjectLayoutImpl::_init(Builder const* builder)
325{
326    auto renderer = builder->m_renderer;
327
328    SLANG_RETURN_ON_FAIL(Super::_init(builder));
329
330    m_program = builder->m_program;
331    m_programLayout = builder->m_programLayout;
332    m_entryPoints = builder->m_entryPoints;
333    m_slangSession = m_program->getSession();
334
335    return SLANG_OK;
336}
337
338} // namespace metal
339} // namespace gfx