yum-mirror/slang

Making it easier to work with shaders

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

Jay KwakCheck the available VK extensions before using CoopVec APIs in GFX (#6849)591affaf7

master
60.6 KiB1608 linesraw
1#pragma once
2
3#include "core/slang-basic.h"
4#include "core/slang-com-object.h"
5#include "core/slang-persistent-cache.h"
6#include "resource-desc-utils.h"
7#include "slang-context.h"
8#include "slang-gfx.h"
9
10namespace gfx
11{
12
13struct GfxGUID
14{
15    static const Slang::Guid IID_ISlangUnknown;
16    static const Slang::Guid IID_IShaderProgram;
17    static const Slang::Guid IID_ITransientResourceHeap;
18    static const Slang::Guid IID_IPipelineState;
19    static const Slang::Guid IID_IResourceView;
20    static const Slang::Guid IID_IFramebuffer;
21    static const Slang::Guid IID_IFramebufferLayout;
22    static const Slang::Guid IID_ISwapchain;
23    static const Slang::Guid IID_ISamplerState;
24    static const Slang::Guid IID_IResource;
25    static const Slang::Guid IID_IBufferResource;
26    static const Slang::Guid IID_ITextureResource;
27    static const Slang::Guid IID_IInputLayout;
28    static const Slang::Guid IID_IDevice;
29    static const Slang::Guid IID_IShaderCache;
30    static const Slang::Guid IID_IShaderObjectLayout;
31    static const Slang::Guid IID_IShaderObject;
32    static const Slang::Guid IID_IRenderPassLayout;
33    static const Slang::Guid IID_ICommandEncoder;
34    static const Slang::Guid IID_IRenderCommandEncoder;
35    static const Slang::Guid IID_IComputeCommandEncoder;
36    static const Slang::Guid IID_IResourceCommandEncoder;
37    static const Slang::Guid IID_IRayTracingCommandEncoder;
38    static const Slang::Guid IID_ICommandBuffer;
39    static const Slang::Guid IID_ICommandBufferD3D12;
40    static const Slang::Guid IID_ICommandQueue;
41    static const Slang::Guid IID_IQueryPool;
42    static const Slang::Guid IID_IAccelerationStructure;
43    static const Slang::Guid IID_IFence;
44    static const Slang::Guid IID_IShaderTable;
45    static const Slang::Guid IID_IPipelineCreationAPIDispatcher;
46    static const Slang::Guid IID_IVulkanPipelineCreationAPIDispatcher;
47    static const Slang::Guid IID_ITransientResourceHeapD3D12;
48};
49
50bool isGfxDebugLayerEnabled();
51
52// We use a `BreakableReference` to avoid the cyclic reference situation in gfx implementation.
53// It is a common scenario where objects created from an `IDevice` implementation needs to hold
54// a strong reference to the device object that creates them. For example, a `Buffer` or a
55// `CommandQueue` needs to store a `m_device` member that points to the `IDevice`. At the same
56// time, the device implementation may also hold a reference to some of the objects it created
57// to represent the current device/binding state. Both parties would like to maintain a strong
58// reference to each other to achieve robustness against arbitrary ordering of destruction that
59// can be triggered by the user. However this creates cyclic reference situations that break
60// the `RefPtr` recyling mechanism. To solve this problem, we instead make each object reference
61// the device via a `BreakableReference<TDeviceImpl>` pointer. A breakable reference can be
62// turned into a weak reference via its `breakStrongReference()` call.
63// If we know there is a cyclic reference between an API object and the device/pool that creates it,
64// we can break the cycle when there is no longer any public references that come from `ComPtr`s to
65// the API object, by turning the reference to the device object from the API object to a weak
66// reference.
67// The following example illustrate how this mechanism works:
68// Suppose we have
69// ```
70// class DeviceImpl : IDevice { RefPtr<ShaderObject> m_currentObject; };
71// class ShaderObjectImpl : IShaderObject { BreakableReference<DeviceImpl> m_device; };
72// ```
73// And the user creates a device and a shader object, then somehow having the device reference
74// the shader object (this may not happen in actual implemetations, we just use it to illustrate
75// the situation):
76// ```
77// ComPtr<IDevice> device = createDevice();
78// ComPtr<ISomeResource> res = device->createResourceX(...);
79// device->m_currentResource = res;
80// ```
81// This setup is robust to any destruction ordering. If user releases reference to `device` first,
82// then the device object will not be freed yet, since there is still a strong reference to the
83// device implementation via `res->m_device`. Next when the user releases reference to `res`, the
84// public reference count to `res` via `ComPtr`s will go to 0, therefore triggering the call to
85// `res->m_device.breakStrongReference()`, releasing the remaining reference to device. This will
86// cause `device` to start destruction, which will release its strong reference to `res` during
87// execution of its destructor. Finally, this will triger the actual destruction of `res`. On the
88// other hand, if the user releases reference to `res` first, then the strong reference to `device`
89// will be broken immediately, but the actual destruction of `res` will not start. Next when the
90// user releases `device`, there will no longer be any other references to `device`, so the
91// destruction of `device` will start, causing the release of the internal reference to `res`,
92// leading to its destruction. Note that the above logic only works if it is known that there is a
93// cyclic reference. If there are no such cyclic reference, then it will be incorrect to break the
94// strong reference to `IDevice` upon public reference counter dropping to 0. This is because the
95// actual destructor of `res` take place after breaking the cycle, but if the resource's strong
96// reference to the device is already the last reference, turning that reference to weak reference
97// will immediately trigger destruction of `device`, after which we can no longer destruct `res` if
98// the destructor needs `device`. Therefore we need to be careful when using `BreakableReference`,
99// and make sure we only call `breakStrongReference` only when it is known that there is a cyclic
100// reference. Luckily for all scenarios so far this is statically known.
101template<typename T>
102class BreakableReference
103{
104private:
105    Slang::RefPtr<T> m_strongPtr;
106    T* m_weakPtr = nullptr;
107
108public:
109    BreakableReference() = default;
110
111    BreakableReference(T* p) { *this = p; }
112
113    BreakableReference(Slang::RefPtr<T> const& p) { *this = p; }
114
115    void setWeakReference(T* p)
116    {
117        m_weakPtr = p;
118        m_strongPtr = nullptr;
119    }
120
121    T& operator*() const { return *get(); }
122
123    T* operator->() const { return get(); }
124
125    T* get() const { return m_weakPtr; }
126
127    operator T*() const { return get(); }
128
129    void operator=(Slang::RefPtr<T> const& p)
130    {
131        m_strongPtr = p;
132        m_weakPtr = p.Ptr();
133    }
134
135    void operator=(T* p)
136    {
137        m_strongPtr = p;
138        m_weakPtr = p;
139    }
140
141    void breakStrongReference() { m_strongPtr = nullptr; }
142
143    void establishStrongReference() { m_strongPtr = m_weakPtr; }
144};
145
146// Helpers for returning an object implementation as COM pointer.
147template<typename TInterface, typename TImpl>
148void returnComPtr(TInterface** outInterface, TImpl* rawPtr)
149{
150    static_assert(
151        !std::is_base_of<Slang::RefObject, TInterface>::value,
152        "TInterface must be an interface type.");
153    rawPtr->addRef();
154    *outInterface = rawPtr;
155}
156
157template<typename TInterface, typename TImpl>
158void returnComPtr(TInterface** outInterface, const Slang::RefPtr<TImpl>& refPtr)
159{
160    static_assert(
161        !std::is_base_of<Slang::RefObject, TInterface>::value,
162        "TInterface must be an interface type.");
163    refPtr->addRef();
164    *outInterface = refPtr.Ptr();
165}
166
167template<typename TInterface, typename TImpl>
168void returnComPtr(TInterface** outInterface, Slang::ComPtr<TImpl>& comPtr)
169{
170    static_assert(
171        !std::is_base_of<Slang::RefObject, TInterface>::value,
172        "TInterface must be an interface type.");
173    *outInterface = comPtr.detach();
174}
175
176// Helpers for returning an object implementation as RefPtr.
177template<typename TDest, typename TImpl>
178void returnRefPtr(TDest** outPtr, Slang::RefPtr<TImpl>& refPtr)
179{
180    static_assert(
181        std::is_base_of<Slang::RefObject, TDest>::value,
182        "TDest must be a non-interface type.");
183    static_assert(
184        std::is_base_of<Slang::RefObject, TImpl>::value,
185        "TImpl must be a non-interface type.");
186    *outPtr = refPtr.Ptr();
187    refPtr->addReference();
188}
189
190template<typename TDest, typename TImpl>
191void returnRefPtrMove(TDest** outPtr, Slang::RefPtr<TImpl>& refPtr)
192{
193    static_assert(
194        std::is_base_of<Slang::RefObject, TDest>::value,
195        "TDest must be a non-interface type.");
196    static_assert(
197        std::is_base_of<Slang::RefObject, TImpl>::value,
198        "TImpl must be a non-interface type.");
199    *outPtr = refPtr.detach();
200}
201
202
203gfx::StageType translateStage(SlangStage slangStage);
204
205class FenceBase : public IFence, public Slang::ComObject
206{
207public:
208    SLANG_COM_OBJECT_IUNKNOWN_ALL
209    IFence* getInterface(const Slang::Guid& guid);
210
211protected:
212    InteropHandle sharedHandle = {};
213};
214
215class Resource : public Slang::ComObject
216{
217public:
218    /// Get the type
219    SLANG_FORCE_INLINE IResource::Type getType() const { return m_type; }
220    /// True if it's a texture derived type
221    SLANG_FORCE_INLINE bool isTexture() const
222    {
223        return int(m_type) >= int(IResource::Type::Texture1D);
224    }
225    /// True if it's a buffer derived type
226    SLANG_FORCE_INLINE bool isBuffer() const { return m_type == IResource::Type::Buffer; }
227
228protected:
229    Resource(IResource::Type type)
230        : m_type(type)
231    {
232    }
233
234    IResource::Type m_type;
235    InteropHandle sharedHandle = {};
236    Slang::String m_debugName;
237};
238
239class BufferResource : public IBufferResource, public Resource
240{
241public:
242    SLANG_COM_OBJECT_IUNKNOWN_ALL
243    IResource* getInterface(const Slang::Guid& guid);
244
245public:
246    typedef Resource Parent;
247
248    /// Ctor
249    BufferResource(const Desc& desc)
250        : Parent(Type::Buffer), m_desc(desc)
251    {
252    }
253
254    virtual SLANG_NO_THROW IResource::Type SLANG_MCALL getType() SLANG_OVERRIDE;
255    virtual SLANG_NO_THROW IBufferResource::Desc* SLANG_MCALL getDesc() SLANG_OVERRIDE;
256    virtual SLANG_NO_THROW Result SLANG_MCALL getNativeResourceHandle(InteropHandle* outHandle)
257        SLANG_OVERRIDE;
258    virtual SLANG_NO_THROW Result SLANG_MCALL getSharedHandle(InteropHandle* outHandle)
259        SLANG_OVERRIDE;
260
261    virtual SLANG_NO_THROW Result SLANG_MCALL setDebugName(const char* name) override
262    {
263        m_debugName = name;
264        return SLANG_OK;
265    }
266    virtual SLANG_NO_THROW const char* SLANG_MCALL getDebugName() override
267    {
268        return m_debugName.getBuffer();
269    }
270
271protected:
272    Desc m_desc;
273};
274
275class TextureResource : public ITextureResource, public Resource
276{
277public:
278    SLANG_COM_OBJECT_IUNKNOWN_ALL
279    IResource* getInterface(const Slang::Guid& guid);
280
281public:
282    typedef Resource Parent;
283
284    /// Ctor
285    TextureResource(const Desc& desc)
286        : Parent(desc.type), m_desc(desc)
287    {
288    }
289
290    virtual SLANG_NO_THROW IResource::Type SLANG_MCALL getType() SLANG_OVERRIDE;
291    virtual SLANG_NO_THROW ITextureResource::Desc* SLANG_MCALL getDesc() SLANG_OVERRIDE;
292    virtual SLANG_NO_THROW Result SLANG_MCALL getNativeResourceHandle(InteropHandle* outHandle)
293        SLANG_OVERRIDE;
294    virtual SLANG_NO_THROW Result SLANG_MCALL getSharedHandle(InteropHandle* outHandle)
295        SLANG_OVERRIDE;
296
297    virtual SLANG_NO_THROW Result SLANG_MCALL setDebugName(const char* name) override
298    {
299        m_debugName = name;
300        return SLANG_OK;
301    }
302    virtual SLANG_NO_THROW const char* SLANG_MCALL getDebugName() override
303    {
304        return m_debugName.getBuffer();
305    }
306
307protected:
308    Desc m_desc;
309};
310
311class ResourceViewInternalBase : public Slang::ComObject
312{
313};
314
315class ResourceViewBase : public IResourceView, public ResourceViewInternalBase
316{
317public:
318    Desc m_desc = {};
319    SLANG_COM_OBJECT_IUNKNOWN_ALL
320    IResourceView* getInterface(const Slang::Guid& guid);
321    virtual SLANG_NO_THROW Desc* SLANG_MCALL getViewDesc() override { return &m_desc; }
322    virtual SLANG_NO_THROW Result SLANG_MCALL getNativeHandle(InteropHandle* outHandle) override;
323};
324
325class SamplerStateBase : public ISamplerState, public Slang::ComObject
326{
327public:
328    SLANG_COM_OBJECT_IUNKNOWN_ALL
329    ISamplerState* getInterface(const Slang::Guid& guid);
330    virtual SLANG_NO_THROW Result SLANG_MCALL getNativeHandle(InteropHandle* outHandle) override;
331};
332
333class AccelerationStructureBase : public IAccelerationStructure, public ResourceViewInternalBase
334{
335public:
336    IResourceView::Desc m_desc = {};
337
338    SLANG_COM_OBJECT_IUNKNOWN_ALL
339    IAccelerationStructure* getInterface(const Slang::Guid& guid);
340    virtual SLANG_NO_THROW Desc* SLANG_MCALL getViewDesc() override { return &m_desc; }
341};
342
343class RendererBase;
344
345typedef uint32_t ShaderComponentID;
346const ShaderComponentID kInvalidComponentID = 0xFFFFFFFF;
347
348struct ExtendedShaderObjectType
349{
350    slang::TypeReflection* slangType;
351    ShaderComponentID componentID;
352};
353
354struct ExtendedShaderObjectTypeList
355{
356    Slang::ShortList<ShaderComponentID, 16> componentIDs;
357    Slang::ShortList<slang::SpecializationArg, 16> components;
358    void add(const ExtendedShaderObjectType& component)
359    {
360        componentIDs.add(component.componentID);
361        components.add(
362            slang::SpecializationArg{slang::SpecializationArg::Kind::Type, {component.slangType}});
363    }
364    void addRange(const ExtendedShaderObjectTypeList& list)
365    {
366        for (Slang::Index i = 0; i < list.getCount(); i++)
367        {
368            add(list[i]);
369        }
370    }
371    ExtendedShaderObjectType operator[](Slang::Index index) const
372    {
373        ExtendedShaderObjectType result;
374        result.componentID = componentIDs[index];
375        result.slangType = components[index].type;
376        return result;
377    }
378    void clear()
379    {
380        componentIDs.clear();
381        components.clear();
382    }
383    Slang::Index getCount() const { return componentIDs.getCount(); }
384};
385
386struct ExtendedShaderObjectTypeListObject : public ExtendedShaderObjectTypeList,
387                                            public Slang::RefObject
388{
389};
390
391class ShaderObjectLayoutBase : public Slang::RefObject
392{
393protected:
394    // We always use a weak reference to the `IDevice` object here.
395    // `ShaderObject` implementations will make sure to hold a strong reference to `IDevice`
396    // while a `ShaderObjectLayout` may still be used.
397    RendererBase* m_renderer;
398    slang::TypeLayoutReflection* m_elementTypeLayout = nullptr;
399    ShaderComponentID m_componentID = 0;
400
401    /// The container type of this shader object. When `m_containerType` is `StructuredBuffer` or
402    /// `UnsizedArray`, this shader object represents a collection instead of a single object.
403    ShaderObjectContainerType m_containerType = ShaderObjectContainerType::None;
404
405public:
406    ComPtr<slang::ISession> m_slangSession;
407
408    ShaderObjectContainerType getContainerType() { return m_containerType; }
409
410    static slang::TypeLayoutReflection* _unwrapParameterGroups(
411        slang::TypeLayoutReflection* typeLayout,
412        ShaderObjectContainerType& outContainerType)
413    {
414        outContainerType = ShaderObjectContainerType::None;
415        for (;;)
416        {
417            if (!typeLayout->getType())
418            {
419                if (auto elementTypeLayout = typeLayout->getElementTypeLayout())
420                    typeLayout = elementTypeLayout;
421            }
422            switch (typeLayout->getKind())
423            {
424            case slang::TypeReflection::Kind::Array:
425                SLANG_ASSERT(outContainerType == ShaderObjectContainerType::None);
426                outContainerType = ShaderObjectContainerType::Array;
427                typeLayout = typeLayout->getElementTypeLayout();
428                return typeLayout;
429            case slang::TypeReflection::Kind::Resource:
430                {
431                    if (typeLayout->getResourceShape() != SLANG_STRUCTURED_BUFFER)
432                        break;
433                    SLANG_ASSERT(outContainerType == ShaderObjectContainerType::None);
434                    outContainerType = ShaderObjectContainerType::StructuredBuffer;
435                    typeLayout = typeLayout->getElementTypeLayout();
436                }
437                return typeLayout;
438            case slang::TypeReflection::Kind::ConstantBuffer:
439            case slang::TypeReflection::Kind::ParameterBlock:
440                typeLayout = typeLayout->getElementTypeLayout();
441                continue;
442            default:
443                return typeLayout;
444            }
445        }
446    }
447
448
449public:
450    RendererBase* getDevice() { return m_renderer; }
451
452    slang::TypeLayoutReflection* getElementTypeLayout() { return m_elementTypeLayout; }
453
454    ShaderComponentID getComponentID() { return m_componentID; }
455
456    void initBase(
457        RendererBase* renderer,
458        slang::ISession* session,
459        slang::TypeLayoutReflection* elementTypeLayout);
460};
461
462class SimpleShaderObjectData
463{
464public:
465    // Any "ordinary" / uniform data for this object
466    Slang::List<char> m_ordinaryData;
467    // The structured buffer resource used when the object represents a structured buffer.
468    Slang::RefPtr<BufferResource> m_structuredBuffer;
469    // The structured buffer resource view used when the object represents a structured buffer.
470    Slang::RefPtr<ResourceViewBase> m_structuredBufferView;
471    Slang::RefPtr<ResourceViewBase> m_rwStructuredBufferView;
472
473    Slang::Index getCount() { return m_ordinaryData.getCount(); }
474    void setCount(Slang::Index count) { m_ordinaryData.setCount(count); }
475    char* getBuffer() { return m_ordinaryData.getBuffer(); }
476
477    /// Returns a StructuredBuffer resource view for GPU access into the buffer content.
478    /// Creates a StructuredBuffer resource if it has not been created.
479    ResourceViewBase* getResourceView(
480        RendererBase* device,
481        slang::TypeLayoutReflection* elementLayout,
482        slang::BindingType bindingType);
483};
484
485bool _doesValueFitInExistentialPayload(
486    slang::TypeLayoutReflection* concreteTypeLayout,
487    slang::TypeLayoutReflection* existentialFieldLayout);
488
489class ShaderObjectBase : public IShaderObject, public Slang::ComObject
490{
491public:
492    SLANG_COM_OBJECT_IUNKNOWN_ALL
493    IShaderObject* getInterface(const Slang::Guid& guid)
494    {
495        if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IShaderObject)
496            return static_cast<IShaderObject*>(this);
497        return nullptr;
498    }
499
500protected:
501    // A strong reference to `IDevice` to make sure the weak device reference in
502    // `ShaderObjectLayout`s are valid whenever they might be used.
503    BreakableReference<RendererBase> m_device;
504
505    // The shader object layout used to create this shader object.
506    Slang::RefPtr<ShaderObjectLayoutBase> m_layout = nullptr;
507
508    // The specialized shader object type.
509    ExtendedShaderObjectType shaderObjectType = {nullptr, kInvalidComponentID};
510
511    Result _getSpecializedShaderObjectType(ExtendedShaderObjectType* outType);
512    slang::TypeLayoutReflection* _getElementTypeLayout()
513    {
514        return m_layout->getElementTypeLayout();
515    }
516
517public:
518    void breakStrongReferenceToDevice() { m_device.breakStrongReference(); }
519
520public:
521    ShaderComponentID getComponentID() { return shaderObjectType.componentID; }
522
523    // Get the final type this shader object represents. If the shader object's type has existential
524    // fields, this function will return a specialized type using the bound sub-objects' type as
525    // specialization argument.
526    virtual Result getSpecializedShaderObjectType(ExtendedShaderObjectType* outType);
527
528    virtual Result collectSpecializationArgs(ExtendedShaderObjectTypeList& args) = 0;
529
530    RendererBase* getRenderer() { return m_layout->getDevice(); }
531
532    ShaderObjectLayoutBase* getLayoutBase() { return m_layout; }
533
534    /// Sets the RTTI ID and RTTI witness table fields of an existential value.
535    Result setExistentialHeader(
536        slang::TypeReflection* existentialType,
537        slang::TypeReflection* concreteType,
538        ShaderOffset offset);
539
540public:
541    SLANG_NO_THROW GfxCount SLANG_MCALL getEntryPointCount() SLANG_OVERRIDE { return 0; }
542
543    SLANG_NO_THROW Result SLANG_MCALL getEntryPoint(GfxIndex index, IShaderObject** outEntryPoint)
544        SLANG_OVERRIDE
545    {
546        *outEntryPoint = nullptr;
547        return SLANG_OK;
548    }
549
550    SLANG_NO_THROW slang::TypeLayoutReflection* SLANG_MCALL getElementTypeLayout() SLANG_OVERRIDE
551    {
552        return m_layout->getElementTypeLayout();
553    }
554
555    virtual SLANG_NO_THROW ShaderObjectContainerType SLANG_MCALL getContainerType() SLANG_OVERRIDE
556    {
557        return m_layout->getContainerType();
558    }
559
560    virtual SLANG_NO_THROW Result SLANG_MCALL
561    getCurrentVersion(ITransientResourceHeap* transientHeap, IShaderObject** outObject) override
562    {
563        returnComPtr(outObject, this);
564        return SLANG_OK;
565    }
566
567    virtual SLANG_NO_THROW Result SLANG_MCALL
568    copyFrom(IShaderObject* object, ITransientResourceHeap* transientHeap);
569
570    virtual SLANG_NO_THROW const void* SLANG_MCALL getRawData() override { return nullptr; }
571
572    virtual SLANG_NO_THROW Result SLANG_MCALL
573    setConstantBufferOverride(IBufferResource* outBuffer) override
574    {
575        return SLANG_E_NOT_AVAILABLE;
576    }
577};
578
579template<typename TShaderObjectImpl, typename TShaderObjectLayoutImpl, typename TShaderObjectData>
580class ShaderObjectBaseImpl : public ShaderObjectBase
581{
582protected:
583    TShaderObjectData m_data;
584    Slang::List<Slang::RefPtr<TShaderObjectImpl>> m_objects;
585    Slang::List<Slang::RefPtr<ExtendedShaderObjectTypeListObject>> m_userProvidedSpecializationArgs;
586
587    // Specialization args for a StructuredBuffer object.
588    ExtendedShaderObjectTypeList m_structuredBufferSpecializationArgs;
589
590public:
591    TShaderObjectLayoutImpl* getLayout()
592    {
593        return static_cast<TShaderObjectLayoutImpl*>(m_layout.Ptr());
594    }
595
596    void* getBuffer() { return m_data.getBuffer(); }
597    size_t getBufferSize() { return (size_t)m_data.getCount(); } // TODO: Change size_t to Count?
598
599    virtual SLANG_NO_THROW Result SLANG_MCALL
600    getObject(ShaderOffset const& offset, IShaderObject** outObject) SLANG_OVERRIDE
601    {
602        SLANG_ASSERT(outObject);
603        if (offset.bindingRangeIndex < 0)
604            return SLANG_E_INVALID_ARG;
605        auto layout = getLayout();
606        if (offset.bindingRangeIndex >= layout->getBindingRangeCount())
607            return SLANG_E_INVALID_ARG;
608        auto bindingRange = layout->getBindingRange(offset.bindingRangeIndex);
609
610        returnComPtr(outObject, m_objects[bindingRange.subObjectIndex + offset.bindingArrayIndex]);
611        return SLANG_OK;
612    }
613
614    void setSpecializationArgsForContainerElement(ExtendedShaderObjectTypeList& specializationArgs);
615
616    Slang::Index getSubObjectIndex(ShaderOffset offset)
617    {
618        auto layout = getLayout();
619        auto bindingRange = layout->getBindingRange(offset.bindingRangeIndex);
620        return bindingRange.subObjectIndex + offset.bindingArrayIndex;
621    }
622
623    virtual SLANG_NO_THROW Result SLANG_MCALL
624    setObject(ShaderOffset const& offset, IShaderObject* object) SLANG_OVERRIDE
625    {
626        auto layout = getLayout();
627        auto subObject = static_cast<TShaderObjectImpl*>(object);
628        // There are three different cases in `setObject`.
629        // 1. `this` object represents a StructuredBuffer, and `object` is an
630        //    element to be written into the StructuredBuffer.
631        // 2. `object` represents a StructuredBuffer and we are setting it into
632        //    a StructuredBuffer typed field in `this` object.
633        // 3. We are setting `object` as an ordinary sub-object, e.g. an existential
634        //    field, a constant buffer or a parameter block.
635        // We handle each case separately below.
636
637        if (layout->getContainerType() != ShaderObjectContainerType::None)
638        {
639            // Case 1:
640            // We are setting an element into a `StructuredBuffer` object.
641            // We need to hold a reference to the element object, as well as
642            // writing uniform data to the plain buffer.
643            if (offset.bindingArrayIndex >= m_objects.getCount())
644            {
645                m_objects.setCount(offset.bindingArrayIndex + 1);
646                auto stride = layout->getElementTypeLayout()->getStride();
647                m_data.setCount(m_objects.getCount() * stride);
648            }
649            m_objects[offset.bindingArrayIndex] = subObject;
650
651            ExtendedShaderObjectTypeList specializationArgs;
652
653            auto payloadOffset = offset;
654
655            // If the element type of the StructuredBuffer field is an existential type,
656            // we need to make sure to fill in the existential value header (RTTI ID and
657            // witness table IDs).
658            if (layout->getElementTypeLayout()->getKind() == slang::TypeReflection::Kind::Interface)
659            {
660                auto existentialType = layout->getElementTypeLayout()->getType();
661                ExtendedShaderObjectType concreteType;
662                SLANG_RETURN_ON_FAIL(subObject->getSpecializedShaderObjectType(&concreteType));
663                SLANG_RETURN_ON_FAIL(
664                    setExistentialHeader(existentialType, concreteType.slangType, offset));
665                payloadOffset.uniformOffset += 16;
666
667                // If this object is a `StructuredBuffer<ISomeInterface>`, then the
668                // specialization argument should be the specialized type of the sub object
669                // itself.
670                specializationArgs.add(concreteType);
671            }
672            else
673            {
674                // If this object is a `StructuredBuffer<SomeConcreteType>`, then the
675                // specialization
676                // argument should come recursively from the sub object.
677                subObject->collectSpecializationArgs(specializationArgs);
678            }
679            SLANG_RETURN_ON_FAIL(setData(
680                payloadOffset,
681                subObject->m_data.getBuffer(),
682                (size_t)subObject->m_data.getCount())); // TODO: Change size_t to Count?
683
684            setSpecializationArgsForContainerElement(specializationArgs);
685            return SLANG_OK;
686        }
687
688        // Case 2 & 3, setting object as an StructuredBuffer, ConstantBuffer, ParameterBlock or
689        // existential value.
690
691        if (offset.bindingRangeIndex < 0)
692            return SLANG_E_INVALID_ARG;
693        if (offset.bindingRangeIndex >= layout->getBindingRangeCount())
694            return SLANG_E_INVALID_ARG;
695
696        auto bindingRangeIndex = offset.bindingRangeIndex;
697        auto bindingRange = layout->getBindingRange(bindingRangeIndex);
698
699        m_objects[bindingRange.subObjectIndex + offset.bindingArrayIndex] = subObject;
700
701        switch (bindingRange.bindingType)
702        {
703        case slang::BindingType::ExistentialValue:
704            {
705                // If the range being assigned into represents an interface/existential-type
706                // leaf field, then we need to consider how the `object` being assigned here
707                // affects specialization. We may also need to assign some data from the
708                // sub-object into the ordinary data buffer for the parent object.
709                //
710                // A leaf field of interface type is laid out inside of the parent object
711                // as a tuple of `(RTTI, WitnessTable, Payload)`. The layout of these fields
712                // is a contract between the compiler and any runtime system, so we will
713                // need to rely on details of the binary layout.
714
715                // We start by querying the layout/type of the concrete value that the
716                // application is trying to store into the field, and also the layout/type of
717                // the leaf existential-type field itself.
718                //
719                auto concreteTypeLayout = subObject->getElementTypeLayout();
720                auto concreteType = concreteTypeLayout->getType();
721                //
722                auto existentialTypeLayout =
723                    layout->getElementTypeLayout()->getBindingRangeLeafTypeLayout(
724                        bindingRangeIndex);
725                auto existentialType = existentialTypeLayout->getType();
726
727                // Fills in the first and second field of the tuple that specify RTTI type ID
728                // and witness table ID.
729                SLANG_RETURN_ON_FAIL(setExistentialHeader(existentialType, concreteType, offset));
730
731                // The third field of the tuple (offset 16) is the "payload" that is supposed to
732                // hold the data for a value of the given concrete type.
733                //
734                auto payloadOffset = offset;
735                payloadOffset.uniformOffset += 16;
736
737                // There are two cases we need to consider here for how the payload might be
738                // used:
739                //
740                // * If the concrete type of the value being bound is one that can "fit" into
741                // the
742                //   available payload space,  then it should be stored in the payload.
743                //
744                // * If the concrete type of the value cannot fit in the payload space, then it
745                //   will need to be stored somewhere else.
746                //
747                if (_doesValueFitInExistentialPayload(concreteTypeLayout, existentialTypeLayout))
748                {
749                    // If the value can fit in the payload area, then we will go ahead and copy
750                    // its bytes into that area.
751                    //
752                    setData(
753                        payloadOffset,
754                        subObject->m_data.getBuffer(),
755                        subObject->m_data.getCount());
756                }
757                else
758                {
759                    // If the value does *not *fit in the payload area, then there is nothing
760                    // we can do at this point (beyond saving a reference to the sub-object,
761                    // which was handled above).
762                    //
763                    // Once all the sub-objects have been set into the parent object, we can
764                    // compute a specialized layout for it, and that specialized layout can tell
765                    // us where the data for these sub-objects has been laid out.
766                    return SLANG_E_NOT_IMPLEMENTED;
767                }
768            }
769            break;
770        case slang::BindingType::MutableRawBuffer:
771        case slang::BindingType::RawBuffer:
772            {
773                // If we are setting into a `StructuredBuffer` field, make sure we create and set
774                // the StructuredBuffer resource as well.
775                auto resourceView = subObject->m_data.getResourceView(
776                    getRenderer(),
777                    subObject->getElementTypeLayout(),
778                    bindingRange.bindingType);
779                if (resourceView)
780                    setResource(offset, resourceView);
781            }
782            break;
783        }
784        return SLANG_OK;
785    }
786
787    Result getExtendedShaderTypeListFromSpecializationArgs(
788        ExtendedShaderObjectTypeList& list,
789        const slang::SpecializationArg* args,
790        uint32_t count);
791
792    virtual SLANG_NO_THROW Result SLANG_MCALL setSpecializationArgs(
793        ShaderOffset const& offset,
794        const slang::SpecializationArg* args,
795        GfxCount count) override
796    {
797        auto layout = getLayout();
798
799        // If the shader object is a container, delegate the processing to
800        // `setSpecializationArgsForContainerElements`.
801        if (layout->getContainerType() != ShaderObjectContainerType::None)
802        {
803            ExtendedShaderObjectTypeList argList;
804            SLANG_RETURN_ON_FAIL(
805                getExtendedShaderTypeListFromSpecializationArgs(argList, args, count));
806            setSpecializationArgsForContainerElement(argList);
807            return SLANG_OK;
808        }
809
810        if (offset.bindingRangeIndex < 0)
811            return SLANG_E_INVALID_ARG;
812        if (offset.bindingRangeIndex >= layout->getBindingRangeCount())
813            return SLANG_E_INVALID_ARG;
814
815        auto bindingRangeIndex = offset.bindingRangeIndex;
816        auto bindingRange = layout->getBindingRange(bindingRangeIndex);
817        Slang::Index objectIndex = bindingRange.subObjectIndex + offset.bindingArrayIndex;
818        if (objectIndex >= m_userProvidedSpecializationArgs.getCount())
819            m_userProvidedSpecializationArgs.setCount(objectIndex + 1);
820        if (!m_userProvidedSpecializationArgs[objectIndex])
821        {
822            m_userProvidedSpecializationArgs[objectIndex] =
823                new ExtendedShaderObjectTypeListObject();
824        }
825        else
826        {
827            m_userProvidedSpecializationArgs[objectIndex]->clear();
828        }
829        SLANG_RETURN_ON_FAIL(getExtendedShaderTypeListFromSpecializationArgs(
830            *m_userProvidedSpecializationArgs[objectIndex],
831            args,
832            count));
833        return SLANG_OK;
834    }
835
836    // Appends all types that are used to specialize the element type of this shader object in
837    // `args` list.
838    virtual Result collectSpecializationArgs(ExtendedShaderObjectTypeList& args) override;
839};
840
841class ShaderProgramBase : public IShaderProgram, public Slang::ComObject
842{
843public:
844    SLANG_COM_OBJECT_IUNKNOWN_ALL
845    IShaderProgram* getInterface(const Slang::Guid& guid);
846
847    Desc desc;
848
849    Slang::ComPtr<slang::IComponentType> slangGlobalScope;
850    Slang::List<ComPtr<slang::IComponentType>> slangEntryPoints;
851
852    // Linked program when linkingStyle is GraphicsCompute, or the original global scope
853    // when linking style is RayTracing.
854    Slang::ComPtr<slang::IComponentType> linkedProgram;
855
856    // Linked program for each entry point when linkingStyle is RayTracing.
857    Slang::List<Slang::ComPtr<slang::IComponentType>> linkedEntryPoints;
858
859    void init(const IShaderProgram::Desc& desc);
860
861    bool isSpecializable()
862    {
863        if (slangGlobalScope->getSpecializationParamCount() != 0)
864        {
865            return true;
866        }
867        for (auto& entryPoint : slangEntryPoints)
868        {
869            if (entryPoint->getSpecializationParamCount() != 0)
870            {
871                return true;
872            }
873        }
874        return false;
875    }
876
877    Slang::Result compileShaders(RendererBase* device);
878    virtual Slang::Result createShaderModule(
879        slang::EntryPointReflection* entryPointInfo,
880        Slang::List<Slang::ComPtr<ISlangBlob>>& kernelCodes);
881
882    virtual SLANG_NO_THROW slang::TypeReflection* SLANG_MCALL
883    findTypeByName(const char* name) override
884    {
885        return linkedProgram->getLayout()->findTypeByName(name);
886    }
887
888    bool isMeshShaderProgram() const;
889};
890
891class InputLayoutBase : public IInputLayout, public Slang::ComObject
892{
893public:
894    SLANG_COM_OBJECT_IUNKNOWN_ALL
895    IInputLayout* getInterface(const Slang::Guid& guid);
896};
897
898class FramebufferLayoutBase : public IFramebufferLayout, public Slang::ComObject
899{
900public:
901    SLANG_COM_OBJECT_IUNKNOWN_ALL
902    IFramebufferLayout* getInterface(const Slang::Guid& guid);
903};
904
905class FramebufferBase : public IFramebuffer, public Slang::ComObject
906{
907public:
908    SLANG_COM_OBJECT_IUNKNOWN_ALL
909    IFramebuffer* getInterface(const Slang::Guid& guid);
910};
911
912class QueryPoolBase : public IQueryPool, public Slang::ComObject
913{
914public:
915    SLANG_COM_OBJECT_IUNKNOWN_ALL
916    IQueryPool* getInterface(const Slang::Guid& guid);
917    virtual SLANG_NO_THROW Result SLANG_MCALL reset() override { return SLANG_OK; }
918
919    IQueryPool::Desc m_desc;
920};
921
922enum class PipelineType
923{
924    Unknown,
925    Graphics,
926    Compute,
927    RayTracing,
928    CountOf,
929};
930
931struct OwnedHitGroupDesc
932{
933    Slang::String hitGroupName;
934    Slang::String closestHitEntryPoint;
935    Slang::String anyHitEntryPoint;
936    Slang::String intersectionEntryPoint;
937
938    void set(const HitGroupDesc& desc)
939    {
940        hitGroupName = desc.hitGroupName;
941        closestHitEntryPoint = desc.closestHitEntryPoint;
942        anyHitEntryPoint = desc.anyHitEntryPoint;
943        intersectionEntryPoint = desc.intersectionEntryPoint;
944    }
945
946    HitGroupDesc get()
947    {
948        HitGroupDesc desc;
949        desc.hitGroupName = hitGroupName.getBuffer();
950        desc.closestHitEntryPoint = closestHitEntryPoint.getBuffer();
951        desc.anyHitEntryPoint = anyHitEntryPoint.getBuffer();
952        desc.intersectionEntryPoint = intersectionEntryPoint.getBuffer();
953        return desc;
954    }
955};
956
957struct OwnedRayTracingPipelineStateDesc
958{
959    Slang::RefPtr<ShaderProgramBase> program;
960    Slang::List<OwnedHitGroupDesc> hitGroups;
961    Slang::List<HitGroupDesc> hitGroupDescs;
962    int maxRecursion = 0;
963    Size maxRayPayloadSize = 0;
964    Size maxAttributeSizeInBytes = 8;
965    RayTracingPipelineFlags::Enum flags = RayTracingPipelineFlags::None;
966
967    RayTracingPipelineStateDesc get()
968    {
969        RayTracingPipelineStateDesc desc;
970        desc.program = program.Ptr();
971        desc.hitGroupCount = (int32_t)hitGroupDescs.getCount();
972        desc.hitGroups = hitGroupDescs.getBuffer();
973        desc.maxRecursion = maxRecursion;
974        desc.maxRayPayloadSize = maxRayPayloadSize;
975        desc.maxAttributeSizeInBytes = maxAttributeSizeInBytes;
976        desc.flags = flags;
977        return desc;
978    }
979
980    void set(const RayTracingPipelineStateDesc& inDesc)
981    {
982        program = static_cast<ShaderProgramBase*>(inDesc.program);
983        for (int32_t i = 0; i < inDesc.hitGroupCount; i++)
984        {
985            OwnedHitGroupDesc ownedHitGroupDesc;
986            ownedHitGroupDesc.set(inDesc.hitGroups[i]);
987            hitGroups.add(ownedHitGroupDesc);
988            hitGroupDescs.add(ownedHitGroupDesc.get());
989        }
990        maxRecursion = inDesc.maxRecursion;
991        maxRayPayloadSize = inDesc.maxRayPayloadSize;
992        maxAttributeSizeInBytes = inDesc.maxAttributeSizeInBytes;
993        flags = inDesc.flags;
994    }
995};
996
997class PipelineStateBase : public IPipelineState, public Slang::ComObject
998{
999public:
1000    SLANG_COM_OBJECT_IUNKNOWN_ALL
1001    IPipelineState* getInterface(const Slang::Guid& guid);
1002
1003    struct PipelineStateDesc
1004    {
1005        PipelineType type;
1006        GraphicsPipelineStateDesc graphics;
1007        ComputePipelineStateDesc compute;
1008        OwnedRayTracingPipelineStateDesc rayTracing;
1009        ShaderProgramBase* getProgram()
1010        {
1011            switch (type)
1012            {
1013            case PipelineType::Compute:
1014                return static_cast<ShaderProgramBase*>(compute.program);
1015            case PipelineType::Graphics:
1016                return static_cast<ShaderProgramBase*>(graphics.program);
1017            case PipelineType::RayTracing:
1018                return static_cast<ShaderProgramBase*>(rayTracing.program);
1019            }
1020            return nullptr;
1021        }
1022    } desc;
1023
1024    // We need to hold inputLayout and framebufferLayout objects alive, since we may use it to
1025    // create specialized pipeline states later.
1026    Slang::RefPtr<InputLayoutBase> inputLayout;
1027    Slang::RefPtr<FramebufferLayoutBase> framebufferLayout;
1028
1029    // The pipeline state from which this pipeline state is specialized.
1030    // If null, this pipeline is either an unspecialized pipeline.
1031    Slang::RefPtr<PipelineStateBase> unspecializedPipelineState = nullptr;
1032
1033    // Indicates whether this is a specializable pipeline. A specializable
1034    // pipeline cannot be used directly and must be specialized first.
1035    bool isSpecializable = false;
1036    Slang::RefPtr<ShaderProgramBase> m_program;
1037    template<typename TProgram>
1038    TProgram* getProgram()
1039    {
1040        return static_cast<TProgram*>(m_program.Ptr());
1041    }
1042
1043    virtual SLANG_NO_THROW Result SLANG_MCALL getNativeHandle(InteropHandle* outHandle) override;
1044    virtual Result ensureAPIPipelineStateCreated() { return SLANG_OK; };
1045
1046protected:
1047    void initializeBase(const PipelineStateDesc& inDesc);
1048};
1049
1050struct ComponentKey
1051{
1052    Slang::UnownedStringSlice typeName;
1053    Slang::ShortList<ShaderComponentID> specializationArgs;
1054    Slang::HashCode hash;
1055    Slang::HashCode getHashCode() const { return hash; }
1056    void updateHash()
1057    {
1058        hash = typeName.getHashCode();
1059        for (auto& arg : specializationArgs)
1060            hash = Slang::combineHash(hash, arg);
1061    }
1062};
1063
1064struct PipelineKey
1065{
1066    PipelineStateBase* pipeline;
1067    Slang::ShortList<ShaderComponentID> specializationArgs;
1068    Slang::HashCode hash;
1069    Slang::HashCode getHashCode() const { return hash; }
1070    void updateHash()
1071    {
1072        hash = Slang::getHashCode(pipeline);
1073        for (auto& arg : specializationArgs)
1074            hash = Slang::combineHash(hash, arg);
1075    }
1076    bool operator==(const PipelineKey& other) const
1077    {
1078        if (pipeline != other.pipeline)
1079            return false;
1080        if (specializationArgs.getCount() != other.specializationArgs.getCount())
1081            return false;
1082        for (Slang::Index i = 0; i < other.specializationArgs.getCount(); i++)
1083        {
1084            if (specializationArgs[i] != other.specializationArgs[i])
1085                return false;
1086        }
1087        return true;
1088    }
1089};
1090
1091struct OwningComponentKey
1092{
1093    Slang::String typeName;
1094    Slang::ShortList<ShaderComponentID> specializationArgs;
1095    Slang::HashCode hash;
1096    Slang::HashCode getHashCode() const { return hash; }
1097    template<typename KeyType>
1098    bool operator==(const KeyType& other) const
1099    {
1100        if (typeName != other.typeName)
1101            return false;
1102        if (specializationArgs.getCount() != other.specializationArgs.getCount())
1103            return false;
1104        for (Slang::Index i = 0; i < other.specializationArgs.getCount(); i++)
1105        {
1106            if (specializationArgs[i] != other.specializationArgs[i])
1107                return false;
1108        }
1109        return true;
1110    }
1111};
1112
1113// A cache from specialization keys to a specialized `ShaderKernel`.
1114class ShaderCache : public Slang::RefObject
1115{
1116public:
1117    ShaderComponentID getComponentId(slang::TypeReflection* type);
1118    ShaderComponentID getComponentId(Slang::UnownedStringSlice name);
1119    ShaderComponentID getComponentId(ComponentKey key);
1120
1121    Slang::RefPtr<PipelineStateBase> getSpecializedPipelineState(PipelineKey programKey)
1122    {
1123        Slang::RefPtr<PipelineStateBase> result;
1124        if (specializedPipelines.tryGetValue(programKey, result))
1125            return result;
1126        return nullptr;
1127    }
1128    void addSpecializedPipeline(
1129        PipelineKey key,
1130        Slang::RefPtr<PipelineStateBase> specializedPipeline);
1131    void free()
1132    {
1133        specializedPipelines = decltype(specializedPipelines)();
1134        componentIds = decltype(componentIds)();
1135    }
1136
1137protected:
1138    Slang::OrderedDictionary<OwningComponentKey, ShaderComponentID> componentIds;
1139    Slang::OrderedDictionary<PipelineKey, Slang::RefPtr<PipelineStateBase>> specializedPipelines;
1140};
1141
1142class TransientResourceHeapBase : public ITransientResourceHeap, public Slang::ComObject
1143{
1144public:
1145    uint64_t m_version = 0;
1146    uint64_t getVersion() { return m_version; }
1147    uint64_t& getVersionCounter()
1148    {
1149        static uint64_t version = 1;
1150        return version;
1151    }
1152    TransientResourceHeapBase() { m_version = getVersionCounter()++; }
1153    virtual ~TransientResourceHeapBase() {}
1154
1155public:
1156    SLANG_COM_OBJECT_IUNKNOWN_ALL
1157    ITransientResourceHeap* getInterface(const Slang::Guid& guid)
1158    {
1159        if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_ITransientResourceHeap)
1160            return static_cast<ITransientResourceHeap*>(this);
1161        return nullptr;
1162    }
1163
1164    virtual SLANG_NO_THROW Result SLANG_MCALL finish() override { return SLANG_OK; }
1165};
1166
1167static const int kRayGenRecordSize = 64; // D3D12_RAYTRACING_SHADER_TABLE_BYTE_ALIGNMENT;
1168
1169class ShaderTableBase : public IShaderTable, public Slang::ComObject
1170{
1171public:
1172    Slang::List<Slang::String> m_shaderGroupNames;
1173    Slang::List<ShaderRecordOverwrite> m_recordOverwrites;
1174
1175    uint32_t m_rayGenShaderCount;
1176    uint32_t m_missShaderCount;
1177    uint32_t m_hitGroupCount;
1178    uint32_t m_callableShaderCount;
1179
1180    Slang::Dictionary<PipelineStateBase*, Slang::RefPtr<BufferResource>> m_deviceBuffers;
1181
1182    SLANG_COM_OBJECT_IUNKNOWN_ALL
1183    IShaderTable* getInterface(const Slang::Guid& guid)
1184    {
1185        if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IShaderTable)
1186            return static_cast<IShaderTable*>(this);
1187        return nullptr;
1188    }
1189
1190    virtual Slang::RefPtr<BufferResource> createDeviceBuffer(
1191        PipelineStateBase* pipeline,
1192        TransientResourceHeapBase* transientHeap,
1193        IResourceCommandEncoder* encoder) = 0;
1194
1195    BufferResource* getOrCreateBuffer(
1196        PipelineStateBase* pipeline,
1197        TransientResourceHeapBase* transientHeap,
1198        IResourceCommandEncoder* encoder)
1199    {
1200        if (auto ptr = m_deviceBuffers.tryGetValue(pipeline))
1201        {
1202            return ptr->Ptr();
1203        }
1204        auto result = createDeviceBuffer(pipeline, transientHeap, encoder);
1205        m_deviceBuffers[pipeline] = result;
1206        return result;
1207    }
1208
1209    Result init(const IShaderTable::Desc& desc);
1210};
1211
1212// Renderer implementation shared by all platforms.
1213// Responsible for shader compilation, specialization and caching.
1214class RendererBase : public IDevice, public IShaderCache, public Slang::ComObject
1215{
1216    friend class ShaderObjectBase;
1217
1218public:
1219    SLANG_COM_OBJECT_IUNKNOWN_ADD_REF
1220    SLANG_COM_OBJECT_IUNKNOWN_RELEASE
1221
1222    virtual SLANG_NO_THROW Result SLANG_MCALL getNativeDeviceHandles(InteropHandles* outHandles)
1223        SLANG_OVERRIDE;
1224    virtual SLANG_NO_THROW Result SLANG_MCALL getFeatures(
1225        const char** outFeatures,
1226        Size bufferSize,
1227        GfxCount* outFeatureCount) SLANG_OVERRIDE;
1228    virtual SLANG_NO_THROW bool SLANG_MCALL hasFeature(const char* featureName) SLANG_OVERRIDE;
1229    virtual SLANG_NO_THROW Result SLANG_MCALL
1230    getFormatSupportedResourceStates(Format format, ResourceStateSet* outStates) override;
1231    virtual SLANG_NO_THROW Result SLANG_MCALL getSlangSession(slang::ISession** outSlangSession)
1232        SLANG_OVERRIDE;
1233    virtual SLANG_NO_THROW SlangResult SLANG_MCALL
1234    queryInterface(SlangUUID const& uuid, void** outObject) SLANG_OVERRIDE;
1235    IDevice* getInterface(const Slang::Guid& guid);
1236
1237    virtual SLANG_NO_THROW Result SLANG_MCALL createTextureFromNativeHandle(
1238        InteropHandle handle,
1239        const ITextureResource::Desc& srcDesc,
1240        ITextureResource** outResource) SLANG_OVERRIDE;
1241
1242    virtual SLANG_NO_THROW Result SLANG_MCALL createTextureFromSharedHandle(
1243        InteropHandle handle,
1244        const ITextureResource::Desc& srcDesc,
1245        const Size size,
1246        ITextureResource** outResource) SLANG_OVERRIDE;
1247
1248    virtual SLANG_NO_THROW Result SLANG_MCALL createBufferFromNativeHandle(
1249        InteropHandle handle,
1250        const IBufferResource::Desc& srcDesc,
1251        IBufferResource** outResource) SLANG_OVERRIDE;
1252
1253    virtual SLANG_NO_THROW Result SLANG_MCALL createBufferFromSharedHandle(
1254        InteropHandle handle,
1255        const IBufferResource::Desc& srcDesc,
1256        IBufferResource** outResource) SLANG_OVERRIDE;
1257
1258    virtual SLANG_NO_THROW Result SLANG_MCALL createProgram2(
1259        const IShaderProgram::CreateDesc2& desc,
1260        IShaderProgram** outProgram,
1261        ISlangBlob** outDiagnostic) override;
1262
1263    virtual SLANG_NO_THROW Result SLANG_MCALL createShaderObject(
1264        slang::TypeReflection* type,
1265        ShaderObjectContainerType containerType,
1266        IShaderObject** outObject) SLANG_OVERRIDE;
1267
1268    virtual SLANG_NO_THROW Result SLANG_MCALL createShaderObject2(
1269        slang::ISession* session,
1270        slang::TypeReflection* type,
1271        ShaderObjectContainerType containerType,
1272        IShaderObject** outObject) SLANG_OVERRIDE;
1273
1274    virtual SLANG_NO_THROW Result SLANG_MCALL createMutableShaderObject(
1275        slang::TypeReflection* type,
1276        ShaderObjectContainerType containerType,
1277        IShaderObject** outObject) SLANG_OVERRIDE;
1278
1279    virtual SLANG_NO_THROW Result SLANG_MCALL createMutableShaderObject2(
1280        slang::ISession* session,
1281        slang::TypeReflection* type,
1282        ShaderObjectContainerType containerType,
1283        IShaderObject** outObject) SLANG_OVERRIDE;
1284
1285    virtual SLANG_NO_THROW Result SLANG_MCALL createShaderObjectFromTypeLayout(
1286        slang::TypeLayoutReflection* typeLayout,
1287        IShaderObject** outObject) override;
1288
1289    virtual SLANG_NO_THROW Result SLANG_MCALL createMutableShaderObjectFromTypeLayout(
1290        slang::TypeLayoutReflection* typeLayout,
1291        IShaderObject** outObject) override;
1292
1293    // Provides a default implementation that returns SLANG_E_NOT_AVAILABLE for platforms
1294    // without ray tracing support.
1295    virtual SLANG_NO_THROW Result SLANG_MCALL getAccelerationStructurePrebuildInfo(
1296        const IAccelerationStructure::BuildInputs& buildInputs,
1297        IAccelerationStructure::PrebuildInfo* outPrebuildInfo) override;
1298
1299    // Provides a default implementation that returns SLANG_E_NOT_AVAILABLE for platforms
1300    // without ray tracing support.
1301    virtual SLANG_NO_THROW Result SLANG_MCALL createAccelerationStructure(
1302        const IAccelerationStructure::CreateDesc& desc,
1303        IAccelerationStructure** outView) override;
1304
1305    // Provides a default implementation that returns SLANG_E_NOT_AVAILABLE for platforms
1306    // without ray tracing support.
1307    virtual SLANG_NO_THROW Result SLANG_MCALL
1308    createShaderTable(const IShaderTable::Desc& desc, IShaderTable** outTable) override;
1309
1310    // Provides a default implementation that returns SLANG_E_NOT_AVAILABLE for platforms
1311    // without ray tracing support.
1312    virtual SLANG_NO_THROW Result SLANG_MCALL createRayTracingPipelineState(
1313        const RayTracingPipelineStateDesc& desc,
1314        IPipelineState** outState) override;
1315
1316    // Provides a default implementation that returns SLANG_E_NOT_AVAILABLE.
1317    virtual SLANG_NO_THROW Result SLANG_MCALL
1318    createMutableRootShaderObject(IShaderProgram* program, IShaderObject** outObject) override;
1319
1320    // Provides a default implementation that returns SLANG_E_NOT_AVAILABLE.
1321    virtual SLANG_NO_THROW Result SLANG_MCALL
1322    createFence(const IFence::Desc& desc, IFence** outFence) override;
1323
1324    // Provides a default implementation that returns SLANG_E_NOT_AVAILABLE.
1325    virtual SLANG_NO_THROW Result SLANG_MCALL waitForFences(
1326        GfxCount fenceCount,
1327        IFence** fences,
1328        uint64_t* fenceValues,
1329        bool waitForAll,
1330        uint64_t timeout) override;
1331
1332    // Provides a default implementation that returns SLANG_E_NOT_AVAILABLE.
1333    virtual SLANG_NO_THROW Result SLANG_MCALL getTextureAllocationInfo(
1334        const ITextureResource::Desc& desc,
1335        Size* outSize,
1336        Size* outAlignment) override;
1337
1338    // Provides a default implementation that returns SLANG_E_NOT_AVAILABLE.
1339    virtual SLANG_NO_THROW Result SLANG_MCALL getTextureRowAlignment(size_t* outAlignment) override;
1340
1341    // Provides a default implementation that returns SLANG_E_NOT_AVAILABLE.
1342    virtual SLANG_NO_THROW Result SLANG_MCALL getCooperativeVectorProperties(
1343        CooperativeVectorProperties* properties,
1344        uint32_t* propertyCount) override;
1345
1346    Result getEntryPointCodeFromShaderCache(
1347        slang::IComponentType* program,
1348        SlangInt entryPointIndex,
1349        SlangInt targetIndex,
1350        slang::IBlob** outCode,
1351        slang::IBlob** outDiagnostics = nullptr);
1352
1353    Result getShaderObjectLayout(
1354        slang::ISession* session,
1355        slang::TypeReflection* type,
1356        ShaderObjectContainerType container,
1357        ShaderObjectLayoutBase** outLayout);
1358
1359    Result getShaderObjectLayout(
1360        slang::ISession* session,
1361        slang::TypeLayoutReflection* typeLayout,
1362        ShaderObjectLayoutBase** outLayout);
1363
1364public:
1365    ExtendedShaderObjectTypeList specializationArgs;
1366    // Given current pipeline and root shader object binding, generate and bind a specialized
1367    // pipeline if necessary. The newly specialized pipeline is held alive by the pipeline cache so
1368    // users of `outNewPipeline` do not need to maintain its lifespan.
1369    Result maybeSpecializePipeline(
1370        PipelineStateBase* currentPipeline,
1371        ShaderObjectBase* rootObject,
1372        Slang::RefPtr<PipelineStateBase>& outNewPipeline);
1373
1374
1375    virtual Result createShaderObjectLayout(
1376        slang::ISession* session,
1377        slang::TypeLayoutReflection* typeLayout,
1378        ShaderObjectLayoutBase** outLayout) = 0;
1379
1380    virtual Result createShaderObject(
1381        ShaderObjectLayoutBase* layout,
1382        IShaderObject** outObject) = 0;
1383
1384    virtual Result createMutableShaderObject(
1385        ShaderObjectLayoutBase* layout,
1386        IShaderObject** outObject) = 0;
1387
1388public:
1389    // IShaderCache interface
1390    virtual SLANG_NO_THROW Result SLANG_MCALL clearShaderCache() SLANG_OVERRIDE;
1391    virtual SLANG_NO_THROW Result SLANG_MCALL getShaderCacheStats(ShaderCacheStats* outStats)
1392        SLANG_OVERRIDE;
1393    virtual SLANG_NO_THROW Result SLANG_MCALL resetShaderCacheStats() SLANG_OVERRIDE;
1394
1395protected:
1396    virtual SLANG_NO_THROW SlangResult SLANG_MCALL initialize(const Desc& desc);
1397
1398protected:
1399    Slang::List<Slang::String> m_features;
1400    std::vector<CooperativeVectorProperties> m_cooperativeVectorProperties;
1401
1402public:
1403    SlangContext slangContext;
1404    ShaderCache shaderCache;
1405
1406    Slang::RefPtr<Slang::PersistentCache> persistentShaderCache;
1407
1408    Slang::Dictionary<slang::TypeLayoutReflection*, Slang::RefPtr<ShaderObjectLayoutBase>>
1409        m_shaderObjectLayoutCache;
1410    Slang::ComPtr<IPipelineCreationAPIDispatcher> m_pipelineCreationAPIDispatcher;
1411};
1412
1413bool isDepthFormat(Format format);
1414
1415IDebugCallback*& _getDebugCallback();
1416IDebugCallback* _getNullDebugCallback();
1417inline IDebugCallback* getDebugCallback()
1418{
1419    auto rs = _getDebugCallback();
1420    if (rs)
1421    {
1422        return rs;
1423    }
1424    else
1425    {
1426        return _getNullDebugCallback();
1427    }
1428}
1429
1430
1431// Implementations that have to come after RendererBase
1432
1433//--------------------------------------------------------------------------------
1434template<typename TShaderObjectImpl, typename TShaderObjectLayoutImpl, typename TShaderObjectData>
1435void ShaderObjectBaseImpl<TShaderObjectImpl, TShaderObjectLayoutImpl, TShaderObjectData>::
1436    setSpecializationArgsForContainerElement(ExtendedShaderObjectTypeList& specializationArgs)
1437{
1438    // Compute specialization args for the structured buffer object.
1439    // If we haven't filled anything to `m_structuredBufferSpecializationArgs` yet,
1440    // use `specializationArgs` directly.
1441    if (m_structuredBufferSpecializationArgs.getCount() == 0)
1442    {
1443        m_structuredBufferSpecializationArgs = Slang::_Move(specializationArgs);
1444    }
1445    else
1446    {
1447        // If `m_structuredBufferSpecializationArgs` already contains some arguments, we
1448        // need to check if they are the same as `specializationArgs`, and replace
1449        // anything that is different with `__Dynamic` because we cannot specialize the
1450        // buffer type if the element types are not the same.
1451        SLANG_ASSERT(
1452            m_structuredBufferSpecializationArgs.getCount() == specializationArgs.getCount());
1453        auto device = getRenderer();
1454        for (Slang::Index i = 0; i < m_structuredBufferSpecializationArgs.getCount(); i++)
1455        {
1456            if (m_structuredBufferSpecializationArgs[i].componentID !=
1457                specializationArgs[i].componentID)
1458            {
1459                auto dynamicType = device->slangContext.session->getDynamicType();
1460                m_structuredBufferSpecializationArgs.componentIDs[i] =
1461                    device->shaderCache.getComponentId(dynamicType);
1462                m_structuredBufferSpecializationArgs.components[i] =
1463                    slang::SpecializationArg::fromType(dynamicType);
1464            }
1465        }
1466    }
1467}
1468
1469//--------------------------------------------------------------------------------
1470template<typename TShaderObjectImpl, typename TShaderObjectLayoutImpl, typename TShaderObjectData>
1471Result ShaderObjectBaseImpl<TShaderObjectImpl, TShaderObjectLayoutImpl, TShaderObjectData>::
1472    getExtendedShaderTypeListFromSpecializationArgs(
1473        ExtendedShaderObjectTypeList& list,
1474        const slang::SpecializationArg* args,
1475        uint32_t count)
1476{
1477    auto device = getRenderer();
1478    for (uint32_t i = 0; i < count; i++)
1479    {
1480        gfx::ExtendedShaderObjectType extendedType;
1481        switch (args[i].kind)
1482        {
1483        case slang::SpecializationArg::Kind::Type:
1484            extendedType.slangType = args[i].type;
1485            extendedType.componentID = device->shaderCache.getComponentId(args[i].type);
1486            break;
1487        default:
1488            SLANG_ASSERT(false && "Unexpected specialization argument kind.");
1489            return SLANG_FAIL;
1490        }
1491        list.add(extendedType);
1492    }
1493    return SLANG_OK;
1494}
1495
1496//--------------------------------------------------------------------------------
1497template<typename TShaderObjectImpl, typename TShaderObjectLayoutImpl, typename TShaderObjectData>
1498Result ShaderObjectBaseImpl<TShaderObjectImpl, TShaderObjectLayoutImpl, TShaderObjectData>::
1499    collectSpecializationArgs(ExtendedShaderObjectTypeList& args)
1500{
1501    if (m_layout->getContainerType() != ShaderObjectContainerType::None)
1502    {
1503        args.addRange(m_structuredBufferSpecializationArgs);
1504        return SLANG_OK;
1505    }
1506
1507    auto device = getRenderer();
1508    auto& subObjectRanges = getLayout()->getSubObjectRanges();
1509    // The following logic is built on the assumption that all fields that involve
1510    // existential types (and therefore require specialization) will results in a sub-object
1511    // range in the type layout. This allows us to simply scan the sub-object ranges to find
1512    // out all specialization arguments.
1513    Slang::Index subObjectRangeCount = subObjectRanges.getCount();
1514
1515    for (Slang::Index subObjectRangeIndex = 0; subObjectRangeIndex < subObjectRangeCount;
1516         subObjectRangeIndex++)
1517    {
1518        auto const& subObjectRange = subObjectRanges[subObjectRangeIndex];
1519        auto const& bindingRange = getLayout()->getBindingRange(subObjectRange.bindingRangeIndex);
1520
1521        Slang::Index oldArgsCount = args.getCount();
1522
1523        Slang::Index count = bindingRange.count;
1524
1525        for (Slang::Index subObjectIndexInRange = 0; subObjectIndexInRange < count;
1526             subObjectIndexInRange++)
1527        {
1528            ExtendedShaderObjectTypeList typeArgs;
1529            Slang::Index objectIndex = bindingRange.subObjectIndex + subObjectIndexInRange;
1530            auto subObject = m_objects[objectIndex];
1531
1532            if (!subObject)
1533                continue;
1534
1535            if (objectIndex < m_userProvidedSpecializationArgs.getCount() &&
1536                m_userProvidedSpecializationArgs[objectIndex])
1537            {
1538                args.addRange(*m_userProvidedSpecializationArgs[objectIndex]);
1539                continue;
1540            }
1541
1542            switch (bindingRange.bindingType)
1543            {
1544            case slang::BindingType::ExistentialValue:
1545                {
1546                    // A binding type of `ExistentialValue` means the sub-object represents a
1547                    // interface-typed field. In this case the specialization argument for this
1548                    // field is the actual specialized type of the bound shader object. If the
1549                    // shader object's type is an ordinary type without existential fields, then
1550                    // the type argument will simply be the ordinary type. But if the sub
1551                    // object's type is itself a specialized type, we need to make sure to use
1552                    // that type as the specialization argument.
1553
1554                    ExtendedShaderObjectType specializedSubObjType;
1555                    SLANG_RETURN_ON_FAIL(
1556                        subObject->getSpecializedShaderObjectType(&specializedSubObjType));
1557                    typeArgs.add(specializedSubObjType);
1558                    break;
1559                }
1560            case slang::BindingType::ParameterBlock:
1561            case slang::BindingType::ConstantBuffer:
1562            case slang::BindingType::RawBuffer:
1563            case slang::BindingType::MutableRawBuffer:
1564                // If the field's type is `ParameterBlock<IFoo>`, we want to pull in the type
1565                // argument from the sub object for specialization.
1566                if (bindingRange.isSpecializable)
1567                {
1568                    ExtendedShaderObjectType specializedSubObjType;
1569                    SLANG_RETURN_ON_FAIL(
1570                        subObject->getSpecializedShaderObjectType(&specializedSubObjType));
1571                    typeArgs.add(specializedSubObjType);
1572                }
1573
1574                // If field's type is `ParameterBlock<SomeStruct>` or
1575                // `ConstantBuffer<SomeStruct>`, where `SomeStruct` is a struct type (not
1576                // directly an interface type), we need to recursively collect the
1577                // specialization arguments from the bound sub object.
1578                SLANG_RETURN_ON_FAIL(subObject->collectSpecializationArgs(typeArgs));
1579                break;
1580            }
1581
1582            auto addedTypeArgCountForCurrentRange = args.getCount() - oldArgsCount;
1583            if (addedTypeArgCountForCurrentRange == 0)
1584            {
1585                args.addRange(typeArgs);
1586            }
1587            else
1588            {
1589                // If type arguments for each elements in the array is different, use
1590                // `__Dynamic` type for the differing argument to disable specialization.
1591                SLANG_ASSERT(addedTypeArgCountForCurrentRange == typeArgs.getCount());
1592                for (Slang::Index i = 0; i < addedTypeArgCountForCurrentRange; i++)
1593                {
1594                    if (args[i + oldArgsCount].componentID != typeArgs[i].componentID)
1595                    {
1596                        auto dynamicType = device->slangContext.session->getDynamicType();
1597                        args.componentIDs[i + oldArgsCount] =
1598                            device->shaderCache.getComponentId(dynamicType);
1599                        args.components[i + oldArgsCount] =
1600                            slang::SpecializationArg::fromType(dynamicType);
1601                    }
1602                }
1603            }
1604        }
1605    }
1606    return SLANG_OK;
1607}
1608} // namespace gfx