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
46.4 KiB1271 linesraw
1// d3d12-shader-object.cpp
2#include "d3d12-shader-object.h"
3
4#include "d3d12-buffer.h"
5#include "d3d12-command-encoder.h"
6#include "d3d12-device.h"
7#include "d3d12-helper-functions.h"
8#include "d3d12-resource-views.h"
9#include "d3d12-sampler.h"
10#include "d3d12-shader-object-layout.h"
11#include "d3d12-transient-heap.h"
12
13namespace gfx
14{
15namespace d3d12
16{
17
18using namespace Slang;
19
20GfxCount ShaderObjectImpl::getEntryPointCount()
21{
22    return 0;
23}
24
25Result ShaderObjectImpl::getEntryPoint(GfxIndex index, IShaderObject** outEntryPoint)
26{
27    *outEntryPoint = nullptr;
28    return SLANG_OK;
29}
30
31const void* ShaderObjectImpl::getRawData()
32{
33    return m_data.getBuffer();
34}
35
36Size ShaderObjectImpl::getSize()
37{
38    return (Size)m_data.getCount();
39}
40
41// TODO: Change Index to Offset/Size?
42Result ShaderObjectImpl::setData(ShaderOffset const& inOffset, void const* data, size_t inSize)
43{
44    Index offset = inOffset.uniformOffset;
45    Index size = inSize;
46
47    char* dest = m_data.getBuffer();
48    Index availableSize = m_data.getCount();
49
50    // TODO: We really should bounds-check access rather than silently ignoring sets
51    // that are too large, but we have several test cases that set more data than
52    // an object actually stores on several targets...
53    //
54    if (offset < 0)
55    {
56        size += offset;
57        offset = 0;
58    }
59    if ((offset + size) >= availableSize)
60    {
61        size = availableSize - offset;
62    }
63
64    memcpy(dest + offset, data, size);
65
66    m_isConstantBufferDirty = true;
67
68    m_version++;
69
70    return SLANG_OK;
71}
72
73Result ShaderObjectImpl::setObject(ShaderOffset const& offset, IShaderObject* object)
74{
75    SLANG_RETURN_ON_FAIL(Super::setObject(offset, object));
76    if (m_isMutable)
77    {
78        auto subObjectIndex = getSubObjectIndex(offset);
79        if (subObjectIndex >= m_subObjectVersions.getCount())
80            m_subObjectVersions.setCount(subObjectIndex + 1);
81        m_subObjectVersions[subObjectIndex] = static_cast<ShaderObjectImpl*>(object)->m_version;
82        m_version++;
83    }
84    return SLANG_OK;
85}
86
87Result ShaderObjectImpl::setSampler(ShaderOffset const& offset, ISamplerState* sampler)
88{
89    if (offset.bindingRangeIndex < 0)
90        return SLANG_E_INVALID_ARG;
91    auto layout = getLayout();
92    if (offset.bindingRangeIndex >= layout->getBindingRangeCount())
93        return SLANG_E_INVALID_ARG;
94    auto& bindingRange = layout->getBindingRange(offset.bindingRangeIndex);
95    auto samplerImpl = static_cast<SamplerStateImpl*>(sampler);
96    ID3D12Device* d3dDevice = static_cast<DeviceImpl*>(getDevice())->m_device;
97    d3dDevice->CopyDescriptorsSimple(
98        1,
99        m_descriptorSet.samplerTable.getCpuHandle(
100            bindingRange.baseIndex + (int32_t)offset.bindingArrayIndex),
101        samplerImpl->m_descriptor.cpuHandle,
102        D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
103    m_version++;
104    return SLANG_OK;
105}
106
107Result ShaderObjectImpl::setCombinedTextureSampler(
108    ShaderOffset const& offset,
109    IResourceView* textureView,
110    ISamplerState* sampler)
111{
112#if 0
113    if (offset.bindingRangeIndex < 0)
114        return SLANG_E_INVALID_ARG;
115    auto layout = getLayout();
116    if (offset.bindingRangeIndex >= layout->getBindingRangeCount())
117        return SLANG_E_INVALID_ARG;
118    auto& bindingRange = layout->getBindingRange(offset.bindingRangeIndex);
119    auto resourceViewImpl = static_cast<ResourceViewImpl*>(textureView);
120    ID3D12Device* d3dDevice = static_cast<DeviceImpl*>(getDevice())->m_device;
121    d3dDevice->CopyDescriptorsSimple(
122        1,
123        m_resourceHeap.getCpuHandle(
124            m_descriptorSet.m_resourceTable +
125            bindingRange.binding.offsetInDescriptorTable.resource +
126            (int32_t)offset.bindingArrayIndex),
127        resourceViewImpl->m_descriptor.cpuHandle,
128        D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
129    auto samplerImpl = static_cast<SamplerStateImpl*>(sampler);
130    d3dDevice->CopyDescriptorsSimple(
131        1,
132        m_samplerHeap.getCpuHandle(
133            m_descriptorSet.m_samplerTable +
134            bindingRange.binding.offsetInDescriptorTable.sampler +
135            (int32_t)offset.bindingArrayIndex),
136        samplerImpl->m_descriptor.cpuHandle,
137        D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
138#endif
139    m_version++;
140    return SLANG_OK;
141}
142
143Result ShaderObjectImpl::init(
144    DeviceImpl* device,
145    ShaderObjectLayoutImpl* layout,
146    DescriptorHeapReference viewHeap,
147    DescriptorHeapReference samplerHeap)
148{
149    m_device = device;
150
151    m_layout = layout;
152
153    m_cachedTransientHeap = nullptr;
154    m_cachedTransientHeapVersion = 0;
155    m_isConstantBufferDirty = true;
156
157    // If the layout tells us that there is any uniform data,
158    // then we will allocate a CPU memory buffer to hold that data
159    // while it is being set from the host.
160    //
161    // Once the user is done setting the parameters/fields of this
162    // shader object, we will produce a GPU-memory version of the
163    // uniform data (which includes values from this object and
164    // any existential-type sub-objects).
165    //
166    size_t uniformSize = layout->getElementTypeLayout()->getSize();
167    if (uniformSize)
168    {
169        m_data.setCount(uniformSize);
170        memset(m_data.getBuffer(), 0, uniformSize);
171    }
172    m_rootArguments.setCount(layout->getOwnUserRootParameterCount());
173    memset(
174        m_rootArguments.getBuffer(),
175        0,
176        sizeof(D3D12_GPU_VIRTUAL_ADDRESS) * m_rootArguments.getCount());
177    // Each shader object will own CPU descriptor heap memory
178    // for any resource or sampler descriptors it might store
179    // as part of its value.
180    //
181    // This allocate includes a reservation for any constant
182    // buffer descriptor pertaining to the ordinary data,
183    // but does *not* include any descriptors that are managed
184    // as part of sub-objects.
185    //
186    if (auto resourceCount = layout->getResourceSlotCount())
187    {
188        m_descriptorSet.resourceTable.allocate(viewHeap, resourceCount);
189
190        // We must also ensure that the memory for any resources
191        // referenced by descriptors in this object does not get
192        // freed while the object is still live.
193        //
194        // The doubling here is because any buffer resource could
195        // have a counter buffer associated with it, which we
196        // also need to ensure isn't destroyed prematurely.
197        m_boundResources.setCount(resourceCount);
198        m_boundCounterResources.setCount(resourceCount);
199    }
200    if (auto samplerCount = layout->getSamplerSlotCount())
201    {
202        m_descriptorSet.samplerTable.allocate(samplerHeap, samplerCount);
203    }
204
205    // If the layout specifies that we have any sub-objects, then
206    // we need to size the array to account for them.
207    //
208    Index subObjectCount = layout->getSubObjectSlotCount();
209    m_objects.setCount(subObjectCount);
210
211    for (auto subObjectRangeInfo : layout->getSubObjectRanges())
212    {
213        auto subObjectLayout = subObjectRangeInfo.layout;
214
215        // In the case where the sub-object range represents an
216        // existential-type leaf field (e.g., an `IBar`), we
217        // cannot pre-allocate the object(s) to go into that
218        // range, since we can't possibly know what to allocate
219        // at this point.
220        //
221        if (!subObjectLayout)
222            continue;
223        //
224        // Otherwise, we will allocate a sub-object to fill
225        // in each entry in this range, based on the layout
226        // information we already have.
227
228        auto& bindingRangeInfo = layout->getBindingRange(subObjectRangeInfo.bindingRangeIndex);
229        for (uint32_t i = 0; i < bindingRangeInfo.count; ++i)
230        {
231            RefPtr<ShaderObjectImpl> subObject;
232            SLANG_RETURN_ON_FAIL(
233                ShaderObjectImpl::create(device, subObjectLayout, subObject.writeRef()));
234            m_objects[bindingRangeInfo.subObjectIndex + i] = subObject;
235        }
236    }
237
238    return SLANG_OK;
239}
240
241/// Write the uniform/ordinary data of this object into the given `dest` buffer at the given
242/// `offset`
243
244Result ShaderObjectImpl::_writeOrdinaryData(
245    PipelineCommandEncoder* encoder,
246    BufferResourceImpl* buffer,
247    Offset offset,
248    Size destSize,
249    ShaderObjectLayoutImpl* specializedLayout)
250{
251    auto src = m_data.getBuffer();
252    auto srcSize = Size(m_data.getCount());
253
254    SLANG_ASSERT(srcSize <= destSize);
255
256    uploadBufferDataImpl(
257        encoder->m_device,
258        encoder->m_d3dCmdList,
259        encoder->m_transientHeap,
260        buffer,
261        offset,
262        srcSize,
263        src);
264
265    // In the case where this object has any sub-objects of
266    // existential/interface type, we need to recurse on those objects
267    // that need to write their state into an appropriate "pending" allocation.
268    //
269    // Note: Any values that could fit into the "payload" included
270    // in the existential-type field itself will have already been
271    // written as part of `setObject()`. This loop only needs to handle
272    // those sub-objects that do not "fit."
273    //
274    // An implementers looking at this code might wonder if things could be changed
275    // so that *all* writes related to sub-objects for interface-type fields could
276    // be handled in this one location, rather than having some in `setObject()` and
277    // others handled here.
278    //
279    Index subObjectRangeCounter = 0;
280    for (auto const& subObjectRangeInfo : specializedLayout->getSubObjectRanges())
281    {
282        Index subObjectRangeIndex = subObjectRangeCounter++;
283        auto const& bindingRangeInfo =
284            specializedLayout->getBindingRange(subObjectRangeInfo.bindingRangeIndex);
285
286        // We only need to handle sub-object ranges for interface/existential-type fields,
287        // because fields of constant-buffer or parameter-block type are responsible for
288        // the ordinary/uniform data of their own existential/interface-type sub-objects.
289        //
290        if (bindingRangeInfo.bindingType != slang::BindingType::ExistentialValue)
291            continue;
292
293        // Each sub-object range represents a single "leaf" field, but might be nested
294        // under zero or more outer arrays, such that the number of existential values
295        // in the same range can be one or more.
296        //
297        auto count = bindingRangeInfo.count;
298
299        // We are not concerned with the case where the existential value(s) in the range
300        // git into the payload part of the leaf field.
301        //
302        // In the case where the value didn't fit, the Slang layout strategy would have
303        // considered the requirements of the value as a "pending" allocation, and would
304        // allocate storage for the ordinary/uniform part of that pending allocation inside
305        // of the parent object's type layout.
306        //
307        // Here we assume that the Slang reflection API can provide us with a single byte
308        // offset and stride for the location of the pending data allocation in the
309        // specialized type layout, which will store the values for this sub-object range.
310        //
311        // TODO: The reflection API functions we are assuming here haven't been implemented
312        // yet, so the functions being called here are stubs.
313        //
314        // TODO: It might not be that a single sub-object range can reliably map to a single
315        // contiguous array with a single stride; we need to carefully consider what the
316        // layout logic does for complex cases with multiple layers of nested arrays and
317        // structures.
318        //
319        Offset subObjectRangePendingDataOffset = subObjectRangeInfo.offset.pendingOrdinaryData;
320        Size subObjectRangePendingDataStride = subObjectRangeInfo.stride.pendingOrdinaryData;
321
322        // If the range doesn't actually need/use the "pending" allocation at all, then
323        // we need to detect that case and skip such ranges.
324        //
325        // TODO: This should probably be handled on a per-object basis by caching a "does it
326        // fit?" bit as part of the information for bound sub-objects, given that we already
327        // compute the "does it fit?" status as part of `setObject()`.
328        //
329        if (subObjectRangePendingDataOffset == 0)
330            continue;
331
332        for (uint32_t i = 0; i < count; ++i)
333        {
334            auto subObject = m_objects[bindingRangeInfo.subObjectIndex + i];
335
336            RefPtr<ShaderObjectLayoutImpl> subObjectLayout;
337            SLANG_RETURN_ON_FAIL(subObject->getSpecializedLayout(subObjectLayout.writeRef()));
338
339            auto subObjectOffset =
340                subObjectRangePendingDataOffset + i * subObjectRangePendingDataStride;
341
342            subObject->_writeOrdinaryData(
343                encoder,
344                buffer,
345                offset + subObjectOffset,
346                destSize - subObjectOffset,
347                subObjectLayout);
348        }
349    }
350
351    return SLANG_OK;
352}
353
354bool ShaderObjectImpl::shouldAllocateConstantBuffer(TransientResourceHeapImpl* transientHeap)
355{
356    if (m_isConstantBufferDirty || m_cachedTransientHeap != transientHeap ||
357        m_cachedTransientHeapVersion != transientHeap->getVersion())
358    {
359        return true;
360    }
361    return false;
362}
363
364/// Ensure that the `m_ordinaryDataBuffer` has been created, if it is needed
365
366Result ShaderObjectImpl::_ensureOrdinaryDataBufferCreatedIfNeeded(
367    PipelineCommandEncoder* encoder,
368    ShaderObjectLayoutImpl* specializedLayout)
369{
370    // If data has been changed since last allocation/filling of constant buffer,
371    // we will need to allocate a new one.
372    //
373    if (!shouldAllocateConstantBuffer(encoder->m_transientHeap))
374    {
375        return SLANG_OK;
376    }
377    m_isConstantBufferDirty = false;
378    m_cachedTransientHeap = encoder->m_transientHeap;
379    m_cachedTransientHeapVersion = encoder->m_transientHeap->getVersion();
380
381    // Computing the size of the ordinary data buffer is *not* just as simple
382    // as using the size of the `m_ordinayData` array that we store. The reason
383    // for the added complexity is that interface-type fields may lead to the
384    // storage being specialized such that it needs extra appended data to
385    // store the concrete values that logically belong in those interface-type
386    // fields but wouldn't fit in the fixed-size allocation we gave them.
387    //
388    m_constantBufferSize = specializedLayout->getTotalOrdinaryDataSize();
389    if (m_constantBufferSize == 0)
390    {
391        return SLANG_OK;
392    }
393
394    // Once we have computed how large the buffer should be, we can allocate
395    // it from the transient resource heap.
396    //
397    auto alignedConstantBufferSize = D3DUtil::calcAligned(m_constantBufferSize, 256);
398    SLANG_RETURN_ON_FAIL(encoder->m_commandBuffer->m_transientHeap->allocateConstantBuffer(
399        alignedConstantBufferSize,
400        m_constantBufferWeakPtr,
401        m_constantBufferOffset));
402
403    // Once the buffer is allocated, we can use `_writeOrdinaryData` to fill it in.
404    //
405    // Note that `_writeOrdinaryData` is potentially recursive in the case
406    // where this object contains interface/existential-type fields, so we
407    // don't need or want to inline it into this call site.
408    //
409    SLANG_RETURN_ON_FAIL(_writeOrdinaryData(
410        encoder,
411        static_cast<BufferResourceImpl*>(m_constantBufferWeakPtr),
412        m_constantBufferOffset,
413        m_constantBufferSize,
414        specializedLayout));
415
416    {
417        // We also create and store a descriptor for our root constant buffer
418        // into the descriptor table allocation that was reserved for them.
419        //
420        // We always know that the ordinary data buffer will be the first descriptor
421        // in the table of resource views.
422        //
423        auto descriptorTable = m_descriptorSet.resourceTable;
424        D3D12_CONSTANT_BUFFER_VIEW_DESC viewDesc = {};
425        viewDesc.BufferLocation = static_cast<BufferResourceImpl*>(m_constantBufferWeakPtr)
426                                      ->m_resource.getResource()
427                                      ->GetGPUVirtualAddress() +
428                                  m_constantBufferOffset;
429        viewDesc.SizeInBytes = (UINT)alignedConstantBufferSize;
430        encoder->m_device->CreateConstantBufferView(&viewDesc, descriptorTable.getCpuHandle());
431    }
432
433    return SLANG_OK;
434}
435
436void ShaderObjectImpl::updateSubObjectsRecursive()
437{
438    if (!m_isMutable)
439        return;
440    auto& subObjectRanges = getLayout()->getSubObjectRanges();
441    for (Slang::Index subObjectRangeIndex = 0; subObjectRangeIndex < subObjectRanges.getCount();
442         subObjectRangeIndex++)
443    {
444        auto const& subObjectRange = subObjectRanges[subObjectRangeIndex];
445        auto const& bindingRange = getLayout()->getBindingRange(subObjectRange.bindingRangeIndex);
446        Slang::Index count = bindingRange.count;
447
448        for (Slang::Index subObjectIndexInRange = 0; subObjectIndexInRange < count;
449             subObjectIndexInRange++)
450        {
451            Slang::Index objectIndex = bindingRange.subObjectIndex + subObjectIndexInRange;
452            auto subObject = m_objects[objectIndex].Ptr();
453            if (!subObject)
454                continue;
455            subObject->updateSubObjectsRecursive();
456            if (m_subObjectVersions.getCount() > objectIndex &&
457                m_subObjectVersions[objectIndex] != m_objects[objectIndex]->m_version)
458            {
459                ShaderOffset offset;
460                offset.bindingRangeIndex = (GfxIndex)subObjectRange.bindingRangeIndex;
461                offset.bindingArrayIndex = (GfxIndex)subObjectIndexInRange;
462                setObject(offset, subObject);
463            }
464        }
465    }
466}
467
468static void bindPendingTables(BindingContext* context)
469{
470    for (auto& binding : *context->pendingTableBindings)
471    {
472        context->submitter->setRootDescriptorTable(binding.rootIndex, binding.handle);
473    }
474}
475
476/// Prepare to bind this object as a parameter block.
477///
478/// This involves allocating and binding any descriptor tables necessary
479/// to to store the state of the object. The function returns a descriptor
480/// set formed from any table(s) allocated. In addition, the `ioOffset`
481/// parameter will be adjusted to be correct for binding values into
482/// the resulting descriptor set.
483///
484/// Returns:
485///   SLANG_OK when successful,
486///   SLANG_E_OUT_OF_MEMORY when descriptor heap is full.
487///
488
489Result ShaderObjectImpl::prepareToBindAsParameterBlock(
490    BindingContext* context,
491    BindingOffset& ioOffset,
492    ShaderObjectLayoutImpl* specializedLayout,
493    DescriptorSet& outDescriptorSet)
494{
495    auto transientHeap = context->transientHeap;
496    auto submitter = context->submitter;
497
498    // When writing into the new descriptor set, resource and sampler
499    // descriptors will need to start at index zero in the respective
500    // tables.
501    //
502    ioOffset.resource = 0;
503    ioOffset.sampler = 0;
504
505    // The index of the next root parameter to bind will be maintained,
506    // but needs to be incremented by the number of descriptor tables
507    // we allocate (zero or one resource table and zero or one sampler
508    // table).
509    //
510    auto& rootParamIndex = ioOffset.rootParam;
511
512    if (auto descriptorCount = specializedLayout->getTotalResourceDescriptorCount())
513    {
514        // There is a non-zero number of resource descriptors needed,
515        // so we will allocate a table out of the appropriate heap,
516        // and store it into the appropriate part of `descriptorSet`.
517        //
518        auto descriptorHeap = &transientHeap->getCurrentViewHeap();
519        auto& table = outDescriptorSet.resourceTable;
520
521        // Allocate the table.
522        //
523        if (!table.allocate(descriptorHeap, descriptorCount))
524        {
525            context->outOfMemoryHeap = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
526            return SLANG_E_OUT_OF_MEMORY;
527        }
528
529        // Bind the table to the pipeline, consuming the next available
530        // root parameter.
531        //
532        auto tableRootParamIndex = rootParamIndex++;
533        context->pendingTableBindings->add(
534            PendingDescriptorTableBinding{tableRootParamIndex, table.getGpuHandle()});
535    }
536    if (auto descriptorCount = specializedLayout->getTotalSamplerDescriptorCount())
537    {
538        // There is a non-zero number of sampler descriptors needed,
539        // so we will allocate a table out of the appropriate heap,
540        // and store it into the appropriate part of `descriptorSet`.
541        //
542        auto descriptorHeap = &transientHeap->getCurrentSamplerHeap();
543        auto& table = outDescriptorSet.samplerTable;
544
545        // Allocate the table.
546        //
547        if (!table.allocate(descriptorHeap, descriptorCount))
548        {
549            context->outOfMemoryHeap = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER;
550            return SLANG_E_OUT_OF_MEMORY;
551        }
552
553        // Bind the table to the pipeline, consuming the next available
554        // root parameter.
555        //
556        auto tableRootParamIndex = rootParamIndex++;
557        context->pendingTableBindings->add(
558            PendingDescriptorTableBinding{tableRootParamIndex, table.getGpuHandle()});
559    }
560
561    return SLANG_OK;
562}
563
564bool ShaderObjectImpl::checkIfCachedDescriptorSetIsValidRecursive(BindingContext* context)
565{
566    if (shouldAllocateConstantBuffer(context->transientHeap))
567        return false;
568    if (m_isMutable && m_version != m_cachedGPUDescriptorSetVersion)
569        return false;
570    if (m_cachedGPUDescriptorSet.resourceTable.getDescriptorCount() != 0 &&
571        m_cachedGPUDescriptorSet.resourceTable.m_heap.ptr.linearHeap->getHeap() !=
572            m_cachedTransientHeap->getCurrentViewHeap().getHeap())
573        return false;
574    if (m_cachedGPUDescriptorSet.samplerTable.getDescriptorCount() != 0 &&
575        m_cachedGPUDescriptorSet.samplerTable.m_heap.ptr.linearHeap->getHeap() !=
576            m_cachedTransientHeap->getCurrentSamplerHeap().getHeap())
577        return false;
578
579    auto& subObjectRanges = getLayout()->getSubObjectRanges();
580    for (Slang::Index subObjectRangeIndex = 0; subObjectRangeIndex < subObjectRanges.getCount();
581         subObjectRangeIndex++)
582    {
583        auto const& subObjectRange = subObjectRanges[subObjectRangeIndex];
584        auto const& bindingRange = getLayout()->getBindingRange(subObjectRange.bindingRangeIndex);
585        if (bindingRange.bindingType != slang::BindingType::ParameterBlock)
586            continue;
587        Slang::Index count = bindingRange.count;
588
589        for (Slang::Index subObjectIndexInRange = 0; subObjectIndexInRange < count;
590             subObjectIndexInRange++)
591        {
592            Slang::Index objectIndex = bindingRange.subObjectIndex + subObjectIndexInRange;
593            auto subObject = m_objects[objectIndex].Ptr();
594            if (!subObject)
595                continue;
596            if (subObject->checkIfCachedDescriptorSetIsValidRecursive(context))
597                return false;
598        }
599    }
600    return true;
601}
602
603/// Bind this object as a `ParameterBlock<X>`
604
605Result ShaderObjectImpl::bindAsParameterBlock(
606    BindingContext* context,
607    BindingOffset const& offset,
608    ShaderObjectLayoutImpl* specializedLayout)
609{
610    if (checkIfCachedDescriptorSetIsValidRecursive(context))
611    {
612        // If we already have a valid gpu descriptor table in the current
613        // heap, bind it.
614        auto rootParamIndex = offset.rootParam;
615        if (m_cachedGPUDescriptorSet.resourceTable.getDescriptorCount())
616        {
617            auto tableRootParamIndex = rootParamIndex++;
618            context->submitter->setRootDescriptorTable(
619                tableRootParamIndex,
620                m_cachedGPUDescriptorSet.resourceTable.getGpuHandle());
621        }
622        if (m_cachedGPUDescriptorSet.samplerTable.getDescriptorCount())
623        {
624            auto tableRootParamIndex = rootParamIndex++;
625            context->submitter->setRootDescriptorTable(
626                tableRootParamIndex,
627                m_cachedGPUDescriptorSet.samplerTable.getGpuHandle());
628        }
629        return SLANG_OK;
630    }
631
632    // The first step to binding an object as a parameter block is to allocate a descriptor
633    // set (consisting of zero or one resource descriptor table and zero or one sampler
634    // descriptor table) to represent its values.
635    //
636    BindingOffset subOffset = offset;
637    ShortList<PendingDescriptorTableBinding> pendingTableBindings;
638    auto oldPendingTableBindings = context->pendingTableBindings;
639    context->pendingTableBindings = &pendingTableBindings;
640
641    SLANG_RETURN_ON_FAIL(prepareToBindAsParameterBlock(
642        context,
643        /* inout */ subOffset,
644        specializedLayout,
645        m_cachedGPUDescriptorSet));
646
647    // Next we bind the object into that descriptor set as if it were being used
648    // as a `ConstantBuffer<X>`.
649    //
650    SLANG_RETURN_ON_FAIL(
651        bindAsConstantBuffer(context, m_cachedGPUDescriptorSet, subOffset, specializedLayout));
652
653    bindPendingTables(context);
654    context->pendingTableBindings = oldPendingTableBindings;
655
656    m_cachedGPUDescriptorSetVersion = m_version;
657    return SLANG_OK;
658}
659
660/// Bind this object as a `ConstantBuffer<X>`
661
662Result ShaderObjectImpl::bindAsConstantBuffer(
663    BindingContext* context,
664    DescriptorSet const& descriptorSet,
665    BindingOffset const& offset,
666    ShaderObjectLayoutImpl* specializedLayout)
667{
668    // If we are to bind as a constant buffer we first need to ensure that
669    // the ordinary data buffer is created, if this object needs one.
670    //
671    SLANG_RETURN_ON_FAIL(
672        _ensureOrdinaryDataBufferCreatedIfNeeded(context->encoder, specializedLayout));
673
674    // Next, we need to bind all of the resource descriptors for this object
675    // (including any ordinary data buffer) into the provided `descriptorSet`.
676    //
677    auto resourceCount = specializedLayout->getResourceSlotCount();
678    if (resourceCount)
679    {
680        auto& dstTable = descriptorSet.resourceTable;
681        auto& srcTable = m_descriptorSet.resourceTable;
682
683        context->device->m_device->CopyDescriptorsSimple(
684            UINT(resourceCount),
685            dstTable.getCpuHandle(offset.resource),
686            srcTable.getCpuHandle(),
687            D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
688    }
689
690    // Finally, we delegate to `_bindImpl` to bind samplers and sub-objects,
691    // since the logic is shared with the `bindAsValue()` case below.
692    //
693    SLANG_RETURN_ON_FAIL(_bindImpl(context, descriptorSet, offset, specializedLayout));
694    return SLANG_OK;
695}
696
697/// Bind this object as a value (for an interface-type parameter)
698
699Result ShaderObjectImpl::bindAsValue(
700    BindingContext* context,
701    DescriptorSet const& descriptorSet,
702    BindingOffset const& offset,
703    ShaderObjectLayoutImpl* specializedLayout)
704{
705    // When binding a value for an interface-type field we do *not* want
706    // to bind a buffer for the ordinary data (if there is any) because
707    // ordinary data for interface-type fields gets allocated into the
708    // parent object's ordinary data buffer.
709    //
710    // This CPU-memory descriptor table that holds resource descriptors
711    // will have already been allocated to have space for an ordinary data
712    // buffer (if needed), so we need to take care to skip over that
713    // descriptor when copying descriptors from the CPU-memory set
714    // to the GPU-memory `descriptorSet`.
715    //
716    auto skipResourceCount = specializedLayout->getOrdinaryDataBufferCount();
717    auto resourceCount = specializedLayout->getResourceSlotCount() - skipResourceCount;
718    if (resourceCount)
719    {
720        auto& dstTable = descriptorSet.resourceTable;
721        auto& srcTable = m_descriptorSet.resourceTable;
722
723        context->device->m_device->CopyDescriptorsSimple(
724            UINT(resourceCount),
725            dstTable.getCpuHandle(offset.resource),
726            srcTable.getCpuHandle(skipResourceCount),
727            D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
728    }
729
730    // Finally, we delegate to `_bindImpl` to bind samplers and sub-objects,
731    // since the logic is shared with the `bindAsConstantBuffer()` case above.
732    //
733    // Note: Just like we had to do some subtle handling of the ordinary data buffer
734    // above, here we need to contend with the fact that the `offset.resource` fields
735    // computed for sub-object ranges were baked to take the ordinary data buffer
736    // into account, so that if `skipResourceCount` is non-zero then they are all
737    // too high by `skipResourceCount`.
738    //
739    // We will address the problem here by computing a modified offset that adjusts
740    // for the ordinary data buffer that we have not bound after all.
741    //
742    BindingOffset subOffset = offset;
743    subOffset.resource -= skipResourceCount;
744    SLANG_RETURN_ON_FAIL(_bindImpl(context, descriptorSet, subOffset, specializedLayout));
745    return SLANG_OK;
746}
747
748/// Shared logic for `bindAsConstantBuffer()` and `bindAsValue()`
749
750Result ShaderObjectImpl::_bindImpl(
751    BindingContext* context,
752    DescriptorSet const& descriptorSet,
753    BindingOffset const& offset,
754    ShaderObjectLayoutImpl* specializedLayout)
755{
756    // We start by binding all the sampler decriptors, if needed.
757    //
758    // Note: resource descriptors were handled in either `bindAsConstantBuffer()`
759    // or `bindAsValue()` before calling into `_bindImpl()`.
760    //
761    if (auto samplerCount = specializedLayout->getSamplerSlotCount())
762    {
763        auto& dstTable = descriptorSet.samplerTable;
764        auto& srcTable = m_descriptorSet.samplerTable;
765
766        context->device->m_device->CopyDescriptorsSimple(
767            UINT(samplerCount),
768            dstTable.getCpuHandle(offset.sampler),
769            srcTable.getCpuHandle(),
770            D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
771    }
772
773    // Next we iterate over the sub-object ranges and bind anything they require.
774    //
775    auto& subObjectRanges = specializedLayout->getSubObjectRanges();
776    auto subObjectRangeCount = subObjectRanges.getCount();
777    for (Index i = 0; i < subObjectRangeCount; i++)
778    {
779        auto& subObjectRange = specializedLayout->getSubObjectRange(i);
780        auto& bindingRange = specializedLayout->getBindingRange(subObjectRange.bindingRangeIndex);
781        auto subObjectIndex = bindingRange.subObjectIndex;
782        auto subObjectLayout = subObjectRange.layout.Ptr();
783
784        BindingOffset rangeOffset = offset;
785        rangeOffset += subObjectRange.offset;
786
787        BindingOffset rangeStride = subObjectRange.stride;
788
789        switch (bindingRange.bindingType)
790        {
791        case slang::BindingType::ConstantBuffer:
792            {
793                auto objOffset = rangeOffset;
794                for (uint32_t j = 0; j < bindingRange.count; j++)
795                {
796                    auto& object = m_objects[subObjectIndex + j];
797                    SLANG_RETURN_ON_FAIL(object->bindAsConstantBuffer(
798                        context,
799                        descriptorSet,
800                        objOffset,
801                        subObjectLayout));
802                    objOffset += rangeStride;
803                }
804            }
805            break;
806
807        case slang::BindingType::ParameterBlock:
808            {
809                auto objOffset = rangeOffset;
810                for (uint32_t j = 0; j < bindingRange.count; j++)
811                {
812                    auto& object = m_objects[subObjectIndex + j];
813                    SLANG_RETURN_ON_FAIL(
814                        object->bindAsParameterBlock(context, objOffset, subObjectLayout));
815                    objOffset += rangeStride;
816                }
817            }
818            break;
819
820        case slang::BindingType::ExistentialValue:
821            if (subObjectLayout)
822            {
823                auto objOffset = rangeOffset;
824                for (uint32_t j = 0; j < bindingRange.count; j++)
825                {
826                    auto& object = m_objects[subObjectIndex + j];
827                    SLANG_RETURN_ON_FAIL(
828                        object->bindAsValue(context, descriptorSet, objOffset, subObjectLayout));
829                    objOffset += rangeStride;
830                }
831            }
832            break;
833        }
834    }
835
836    return SLANG_OK;
837}
838
839Result ShaderObjectImpl::bindRootArguments(BindingContext* context, uint32_t& index)
840{
841    auto layoutImpl = getLayout();
842    for (Index i = 0; i < m_rootArguments.getCount(); i++)
843    {
844        switch (layoutImpl->getRootParameterInfo(i).type)
845        {
846        case IResourceView::Type::ShaderResource:
847        case IResourceView::Type::AccelerationStructure:
848            context->submitter->setRootSRV(index, m_rootArguments[i]);
849            break;
850        case IResourceView::Type::UnorderedAccess:
851            context->submitter->setRootUAV(index, m_rootArguments[i]);
852            break;
853        default:
854            continue;
855        }
856        index++;
857    }
858    for (auto& subObject : m_objects)
859    {
860        if (subObject)
861        {
862            SLANG_RETURN_ON_FAIL(subObject->bindRootArguments(context, index));
863        }
864    }
865    return SLANG_OK;
866}
867
868/// Get the layout of this shader object with specialization arguments considered
869///
870/// This operation should only be called after the shader object has been
871/// fully filled in and finalized.
872///
873
874Result ShaderObjectImpl::getSpecializedLayout(ShaderObjectLayoutImpl** outLayout)
875{
876    if (!m_specializedLayout)
877    {
878        SLANG_RETURN_ON_FAIL(_createSpecializedLayout(m_specializedLayout.writeRef()));
879    }
880    returnRefPtr(outLayout, m_specializedLayout);
881    return SLANG_OK;
882}
883
884/// Create the layout for this shader object with specialization arguments considered
885///
886/// This operation is virtual so that it can be customized by `RootShaderObject`.
887///
888
889Result ShaderObjectImpl::_createSpecializedLayout(ShaderObjectLayoutImpl** outLayout)
890{
891    ExtendedShaderObjectType extendedType;
892    SLANG_RETURN_ON_FAIL(getSpecializedShaderObjectType(&extendedType));
893
894    auto renderer = getRenderer();
895    RefPtr<ShaderObjectLayoutImpl> layout;
896    SLANG_RETURN_ON_FAIL(renderer->getShaderObjectLayout(
897        m_layout->m_slangSession,
898        extendedType.slangType,
899        m_layout->getContainerType(),
900        (ShaderObjectLayoutBase**)layout.writeRef()));
901
902    returnRefPtrMove(outLayout, layout);
903    return SLANG_OK;
904}
905
906Result ShaderObjectImpl::setResource(ShaderOffset const& offset, IResourceView* resourceView)
907{
908    if (offset.bindingRangeIndex < 0)
909        return SLANG_E_INVALID_ARG;
910    auto layout = getLayout();
911    if (offset.bindingRangeIndex >= layout->getBindingRangeCount())
912        return SLANG_E_INVALID_ARG;
913
914    m_version++;
915
916    ID3D12Device* d3dDevice = static_cast<DeviceImpl*>(getDevice())->m_device;
917
918    auto& bindingRange = layout->getBindingRange(offset.bindingRangeIndex);
919
920    if (bindingRange.isRootParameter && resourceView)
921    {
922        auto& rootArg = m_rootArguments[bindingRange.baseIndex];
923        switch (resourceView->getViewDesc()->type)
924        {
925        case IResourceView::Type::AccelerationStructure:
926            {
927                auto resourceViewImpl = static_cast<AccelerationStructureImpl*>(resourceView);
928                rootArg = resourceViewImpl->getDeviceAddress();
929            }
930            break;
931        case IResourceView::Type::ShaderResource:
932        case IResourceView::Type::UnorderedAccess:
933            {
934                auto resourceViewImpl = static_cast<ResourceViewImpl*>(resourceView);
935                if (resourceViewImpl->m_resource->isBuffer())
936                {
937                    rootArg = static_cast<BufferResourceImpl*>(resourceViewImpl->m_resource.Ptr())
938                                  ->getDeviceAddress();
939                }
940                else
941                {
942                    getDebugCallback()->handleMessage(
943                        DebugMessageType::Error,
944                        DebugMessageSource::Layer,
945                        "The shader parameter at the specified offset is a root parameter, and "
946                        "therefore can only be a buffer view.");
947                    return SLANG_FAIL;
948                }
949            }
950            break;
951        }
952        return SLANG_OK;
953    }
954
955    if (resourceView == nullptr)
956    {
957        if (!bindingRange.isRootParameter)
958        {
959            // Create null descriptor for the binding.
960            auto destDescriptor = m_descriptorSet.resourceTable.getCpuHandle(
961                bindingRange.baseIndex + (int32_t)offset.bindingArrayIndex);
962            return createNullDescriptor(d3dDevice, destDescriptor, bindingRange);
963        }
964        return SLANG_OK;
965    }
966
967    ResourceViewInternalImpl* internalResourceView = nullptr;
968    auto resourceViewImpl = static_cast<ResourceViewImpl*>(resourceView);
969
970    switch (resourceView->getViewDesc()->type)
971    {
972#if SLANG_GFX_HAS_DXR_SUPPORT
973    case IResourceView::Type::AccelerationStructure:
974        {
975            auto asImpl = static_cast<AccelerationStructureImpl*>(resourceView);
976            // Hold a reference to the resource to prevent its destruction.
977            m_boundResources[bindingRange.baseIndex + offset.bindingArrayIndex] = asImpl->m_buffer;
978            internalResourceView = asImpl;
979        }
980        break;
981#endif
982    default:
983        {
984            // Hold a reference to the resource to prevent its destruction.
985            const auto resourceOffset = bindingRange.baseIndex + offset.bindingArrayIndex;
986            m_boundResources[resourceOffset] = resourceViewImpl->m_resource;
987            m_boundCounterResources[resourceOffset] = resourceViewImpl->m_counterResource;
988            internalResourceView = resourceViewImpl;
989        }
990        break;
991    }
992
993    auto descriptorSlotIndex = bindingRange.baseIndex + (int32_t)offset.bindingArrayIndex;
994    D3D12Descriptor srcDescriptor = internalResourceView->m_descriptor;
995
996    // Buffer descriptors are created on demand.
997    if (!srcDescriptor.cpuHandle.ptr)
998    {
999        SLANG_RETURN_ON_FAIL(internalResourceView->getBufferDescriptorForBinding(
1000            static_cast<DeviceImpl*>(m_device.get()),
1001            resourceViewImpl,
1002            bindingRange.bufferElementStride,
1003            srcDescriptor));
1004    }
1005
1006    if (srcDescriptor.cpuHandle.ptr)
1007    {
1008        d3dDevice->CopyDescriptorsSimple(
1009            1,
1010            m_descriptorSet.resourceTable.getCpuHandle(
1011                bindingRange.baseIndex + (int32_t)offset.bindingArrayIndex),
1012            srcDescriptor.cpuHandle,
1013            D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
1014    }
1015    else
1016    {
1017        getDebugCallback()->handleMessage(
1018            DebugMessageType::Error,
1019            DebugMessageSource::Layer,
1020            "IShaderObject::setResource: the resource view cannot be set to this shader parameter. "
1021            "A possible reason is that the view is too large to be supported by D3D12.");
1022        return SLANG_FAIL;
1023    }
1024    return SLANG_OK;
1025}
1026
1027Result ShaderObjectImpl::create(
1028    DeviceImpl* device,
1029    ShaderObjectLayoutImpl* layout,
1030    ShaderObjectImpl** outShaderObject)
1031{
1032    auto object = RefPtr<ShaderObjectImpl>(new ShaderObjectImpl());
1033    SLANG_RETURN_ON_FAIL(
1034        object->init(device, layout, device->m_cpuViewHeap.Ptr(), device->m_cpuSamplerHeap.Ptr()));
1035    returnRefPtrMove(outShaderObject, object);
1036    return SLANG_OK;
1037}
1038
1039ShaderObjectImpl::~ShaderObjectImpl()
1040{
1041    m_descriptorSet.freeIfSupported();
1042}
1043
1044RootShaderObjectLayoutImpl* RootShaderObjectImpl::getLayout()
1045{
1046    return static_cast<RootShaderObjectLayoutImpl*>(m_layout.Ptr());
1047}
1048
1049GfxCount RootShaderObjectImpl::getEntryPointCount()
1050{
1051    return (GfxCount)m_entryPoints.getCount();
1052}
1053
1054SlangResult RootShaderObjectImpl::getEntryPoint(GfxIndex index, IShaderObject** outEntryPoint)
1055{
1056    returnComPtr(outEntryPoint, m_entryPoints[index]);
1057    return SLANG_OK;
1058}
1059
1060Result RootShaderObjectImpl::collectSpecializationArgs(ExtendedShaderObjectTypeList& args)
1061{
1062    SLANG_RETURN_ON_FAIL(ShaderObjectImpl::collectSpecializationArgs(args));
1063    for (auto& entryPoint : m_entryPoints)
1064    {
1065        SLANG_RETURN_ON_FAIL(entryPoint->collectSpecializationArgs(args));
1066    }
1067    return SLANG_OK;
1068}
1069
1070Result RootShaderObjectImpl::_createSpecializedLayout(ShaderObjectLayoutImpl** outLayout)
1071{
1072    ExtendedShaderObjectTypeList specializationArgs;
1073    SLANG_RETURN_ON_FAIL(collectSpecializationArgs(specializationArgs));
1074
1075    // Note: There is an important policy decision being made here that we need
1076    // to approach carefully.
1077    //
1078    // We are doing two different things that affect the layout of a program:
1079    //
1080    // 1. We are *composing* one or more pieces of code (notably the shared global/module
1081    //    stuff and the per-entry-point stuff).
1082    //
1083    // 2. We are *specializing* code that includes generic/existential parameters
1084    //    to concrete types/values.
1085    //
1086    // We need to decide the relative *order* of these two steps, because of how it impacts
1087    // layout. The layout for `specialize(compose(A,B), X, Y)` is potentially different
1088    // form that of `compose(specialize(A,X), speciealize(B,Y))`, even when both are
1089    // semantically equivalent programs.
1090    //
1091    // Right now we are using the first option: we are first generating a full composition
1092    // of all the code we plan to use (global scope plus all entry points), and then
1093    // specializing it to the concatenated specialization argumenst for all of that.
1094    //
1095    // In some cases, though, this model isn't appropriate. For example, when dealing with
1096    // ray-tracing shaders and local root signatures, we really want the parameters of each
1097    // entry point (actually, each entry-point *group*) to be allocated distinct storage,
1098    // which really means we want to compute something like:
1099    //
1100    //      SpecializedGlobals = specialize(compose(ModuleA, ModuleB, ...), X, Y, ...)
1101    //
1102    //      SpecializedEP1 = compose(SpecializedGlobals, specialize(EntryPoint1, T, U, ...))
1103    //      SpecializedEP2 = compose(SpecializedGlobals, specialize(EntryPoint2, A, B, ...))
1104    //
1105    // Note how in this case all entry points agree on the layout for the shared/common
1106    // parmaeters, but their layouts are also independent of one another.
1107    //
1108    // Furthermore, in this example, loading another entry point into the system would not
1109    // rquire re-computing the layouts (or generated kernel code) for any of the entry
1110    // points that had already been loaded (in contrast to a compose-then-specialize
1111    // approach).
1112    //
1113    ComPtr<slang::IComponentType> specializedComponentType;
1114    ComPtr<slang::IBlob> diagnosticBlob;
1115    auto result = getLayout()->getSlangProgram()->specialize(
1116        specializationArgs.components.getArrayView().getBuffer(),
1117        specializationArgs.getCount(),
1118        specializedComponentType.writeRef(),
1119        diagnosticBlob.writeRef());
1120
1121    if (diagnosticBlob && diagnosticBlob->getBufferSize())
1122    {
1123        getDebugCallback()->handleMessage(
1124            SLANG_FAILED(result) ? DebugMessageType::Error : DebugMessageType::Info,
1125            DebugMessageSource::Layer,
1126            (const char*)diagnosticBlob->getBufferPointer());
1127    }
1128
1129    if (SLANG_FAILED(result))
1130        return result;
1131
1132    ComPtr<ID3DBlob> d3dDiagnosticBlob;
1133    auto slangSpecializedLayout = specializedComponentType->getLayout();
1134    RefPtr<RootShaderObjectLayoutImpl> specializedLayout;
1135    auto rootLayoutResult = RootShaderObjectLayoutImpl::create(
1136        static_cast<DeviceImpl*>(getRenderer()),
1137        specializedComponentType,
1138        slangSpecializedLayout,
1139        specializedLayout.writeRef(),
1140        d3dDiagnosticBlob.writeRef());
1141
1142    if (SLANG_FAILED(rootLayoutResult))
1143    {
1144        return rootLayoutResult;
1145    }
1146
1147    // Note: Computing the layout for the specialized program will have also computed
1148    // the layouts for the entry points, and we really need to attach that information
1149    // to them so that they don't go and try to compute their own specializations.
1150    //
1151    // TODO: Well, if we move to the specialization model described above then maybe
1152    // we *will* want entry points to do their own specialization work...
1153    //
1154    auto entryPointCount = m_entryPoints.getCount();
1155    for (Index i = 0; i < entryPointCount; ++i)
1156    {
1157        auto entryPointInfo = specializedLayout->getEntryPoint(i);
1158        auto entryPointVars = m_entryPoints[i];
1159
1160        entryPointVars->m_specializedLayout = entryPointInfo.layout;
1161    }
1162
1163    returnRefPtrMove(outLayout, specializedLayout);
1164    return SLANG_OK;
1165}
1166
1167Result RootShaderObjectImpl::copyFrom(IShaderObject* object, ITransientResourceHeap* transientHeap)
1168{
1169    if (auto srcObj = dynamic_cast<MutableRootShaderObjectImpl*>(object))
1170    {
1171        *this = *srcObj;
1172        return SLANG_OK;
1173    }
1174    return SLANG_FAIL;
1175}
1176
1177Result RootShaderObjectImpl::bindAsRoot(
1178    BindingContext* context,
1179    RootShaderObjectLayoutImpl* specializedLayout)
1180{
1181    // Pull updates from sub-objects when this is a mutable root shader object.
1182    updateSubObjectsRecursive();
1183
1184    // A root shader object always binds as if it were a parameter block,
1185    // insofar as it needs to allocate a descriptor set to hold the bindings
1186    // for its own state and any sub-objects.
1187    //
1188    // Note: We do not direclty use `bindAsParameterBlock` here because we also
1189    // need to bind the entry points into the same descriptor set that is
1190    // being used for the root object.
1191
1192    ShortList<PendingDescriptorTableBinding> pendingTableBindings;
1193    auto oldPendingTableBindings = context->pendingTableBindings;
1194    context->pendingTableBindings = &pendingTableBindings;
1195
1196    BindingOffset rootOffset;
1197
1198    // Bind all root parameters first.
1199    Super::bindRootArguments(context, rootOffset.rootParam);
1200
1201    DescriptorSet descriptorSet;
1202    SLANG_RETURN_ON_FAIL(prepareToBindAsParameterBlock(
1203        context,
1204        /* inout */ rootOffset,
1205        specializedLayout,
1206        descriptorSet));
1207
1208    SLANG_RETURN_ON_FAIL(
1209        Super::bindAsConstantBuffer(context, descriptorSet, rootOffset, specializedLayout));
1210
1211    auto entryPointCount = m_entryPoints.getCount();
1212    for (Index i = 0; i < entryPointCount; ++i)
1213    {
1214        auto entryPoint = m_entryPoints[i];
1215        auto& entryPointInfo = specializedLayout->getEntryPoint(i);
1216
1217        auto entryPointOffset = rootOffset;
1218        entryPointOffset += entryPointInfo.offset;
1219
1220        entryPoint->updateSubObjectsRecursive();
1221
1222        SLANG_RETURN_ON_FAIL(entryPoint->bindAsConstantBuffer(
1223            context,
1224            descriptorSet,
1225            entryPointOffset,
1226            entryPointInfo.layout));
1227    }
1228
1229    bindPendingTables(context);
1230    context->pendingTableBindings = oldPendingTableBindings;
1231
1232    return SLANG_OK;
1233}
1234
1235Result RootShaderObjectImpl::resetImpl(
1236    DeviceImpl* device,
1237    RootShaderObjectLayoutImpl* layout,
1238    DescriptorHeapReference viewHeap,
1239    DescriptorHeapReference samplerHeap,
1240    bool isMutable)
1241{
1242    SLANG_RETURN_ON_FAIL(Super::init(device, layout, viewHeap, samplerHeap));
1243    m_isMutable = isMutable;
1244    m_specializedLayout = nullptr;
1245    m_entryPoints.clear();
1246    for (auto entryPointInfo : layout->getEntryPoints())
1247    {
1248        RefPtr<ShaderObjectImpl> entryPoint;
1249        SLANG_RETURN_ON_FAIL(
1250            ShaderObjectImpl::create(device, entryPointInfo.layout, entryPoint.writeRef()));
1251        entryPoint->m_isMutable = isMutable;
1252        m_entryPoints.add(entryPoint);
1253    }
1254    return SLANG_OK;
1255}
1256
1257Result RootShaderObjectImpl::reset(
1258    DeviceImpl* device,
1259    RootShaderObjectLayoutImpl* layout,
1260    TransientResourceHeapImpl* heap)
1261{
1262    return resetImpl(
1263        device,
1264        layout,
1265        &heap->m_stagingCpuViewHeap,
1266        &heap->m_stagingCpuSamplerHeap,
1267        false);
1268}
1269
1270} // namespace d3d12
1271} // namespace gfx