yum-mirror/slang

Making it easier to work with shaders

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

AlexisPollonniFixed various queryInterface implementations (#6863)785669c87

master
52.7 KiB1534 linesraw
1#include "renderer-shared.h"
2
3#include "../../source/core/slang-file-system.h"
4#include "../../source/core/slang-stable-hash.h"
5#include "core/slang-io.h"
6#include "core/slang-token-reader.h"
7#include "mutable-shader-object.h"
8#include "slang.h"
9
10using namespace Slang;
11
12namespace gfx
13{
14
15const Slang::Guid GfxGUID::IID_ISlangUnknown = SLANG_UUID_ISlangUnknown;
16const Slang::Guid GfxGUID::IID_IShaderProgram = SLANG_UUID_IShaderProgram;
17const Slang::Guid GfxGUID::IID_IInputLayout = SLANG_UUID_IInputLayout;
18const Slang::Guid GfxGUID::IID_IPipelineState = SLANG_UUID_IPipelineState;
19const Slang::Guid GfxGUID::IID_ITransientResourceHeap = SLANG_UUID_ITransientResourceHeap;
20const Slang::Guid GfxGUID::IID_IResourceView = SLANG_UUID_IResourceView;
21const Slang::Guid GfxGUID::IID_IFramebuffer = SLANG_UUID_IFrameBuffer;
22const Slang::Guid GfxGUID::IID_IFramebufferLayout = SLANG_UUID_IFramebufferLayout;
23
24const Slang::Guid GfxGUID::IID_ISwapchain = SLANG_UUID_ISwapchain;
25const Slang::Guid GfxGUID::IID_ISamplerState = SLANG_UUID_ISamplerState;
26const Slang::Guid GfxGUID::IID_IResource = SLANG_UUID_IResource;
27const Slang::Guid GfxGUID::IID_IBufferResource = SLANG_UUID_IBufferResource;
28const Slang::Guid GfxGUID::IID_ITextureResource = SLANG_UUID_ITextureResource;
29const Slang::Guid GfxGUID::IID_IDevice = SLANG_UUID_IDevice;
30const Slang::Guid GfxGUID::IID_IShaderCache = SLANG_UUID_IShaderCache;
31const Slang::Guid GfxGUID::IID_IShaderObject = SLANG_UUID_IShaderObject;
32
33const Slang::Guid GfxGUID::IID_IRenderPassLayout = SLANG_UUID_IRenderPassLayout;
34const Slang::Guid GfxGUID::IID_IRayTracingCommandEncoder = IRayTracingCommandEncoder::getTypeGuid();
35const Slang::Guid GfxGUID::IID_IResourceCommandEncoder = IResourceCommandEncoder::getTypeGuid();
36const Slang::Guid GfxGUID::IID_IComputeCommandEncoder = IComputeCommandEncoder::getTypeGuid();
37const Slang::Guid GfxGUID::IID_IRenderCommandEncoder = IRenderCommandEncoder::getTypeGuid();
38
39const Slang::Guid GfxGUID::IID_ICommandBuffer = SLANG_UUID_ICommandBuffer;
40const Slang::Guid GfxGUID::IID_ICommandBufferD3D12 = SLANG_UUID_ICommandBufferD3D12;
41
42const Slang::Guid GfxGUID::IID_ICommandQueue = SLANG_UUID_ICommandQueue;
43const Slang::Guid GfxGUID::IID_IQueryPool = SLANG_UUID_IQueryPool;
44const Slang::Guid GfxGUID::IID_IAccelerationStructure = SLANG_UUID_IAccelerationStructure;
45const Slang::Guid GfxGUID::IID_IFence = SLANG_UUID_IFence;
46const Slang::Guid GfxGUID::IID_IShaderTable = SLANG_UUID_IShaderTable;
47const Slang::Guid GfxGUID::IID_IPipelineCreationAPIDispatcher =
48    SLANG_UUID_IPipelineCreationAPIDispatcher;
49const Slang::Guid GfxGUID::IID_IVulkanPipelineCreationAPIDispatcher =
50    SLANG_UUID_IVulkanPipelineCreationAPIDispatcher;
51const Slang::Guid GfxGUID::IID_ITransientResourceHeapD3D12 = SLANG_UUID_ITransientResourceHeapD3D12;
52
53
54StageType translateStage(SlangStage slangStage)
55{
56    switch (slangStage)
57    {
58    default:
59        SLANG_ASSERT(!"unhandled case");
60        return gfx::StageType::Unknown;
61
62#define CASE(FROM, TO)       \
63    case SLANG_STAGE_##FROM: \
64        return gfx::StageType::TO
65
66        CASE(VERTEX, Vertex);
67        CASE(HULL, Hull);
68        CASE(DOMAIN, Domain);
69        CASE(GEOMETRY, Geometry);
70        CASE(FRAGMENT, Fragment);
71
72        CASE(COMPUTE, Compute);
73
74        CASE(RAY_GENERATION, RayGeneration);
75        CASE(INTERSECTION, Intersection);
76        CASE(ANY_HIT, AnyHit);
77        CASE(CLOSEST_HIT, ClosestHit);
78        CASE(MISS, Miss);
79        CASE(CALLABLE, Callable);
80
81#undef CASE
82    }
83}
84
85IFence* FenceBase::getInterface(const Slang::Guid& guid)
86{
87    if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IFence)
88        return static_cast<IFence*>(this);
89    return nullptr;
90}
91
92IResource* BufferResource::getInterface(const Slang::Guid& guid)
93{
94    if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IResource ||
95        guid == GfxGUID::IID_IBufferResource)
96        return static_cast<IBufferResource*>(this);
97    return nullptr;
98}
99
100SLANG_NO_THROW IResource::Type SLANG_MCALL BufferResource::getType()
101{
102    return m_type;
103}
104SLANG_NO_THROW IBufferResource::Desc* SLANG_MCALL BufferResource::getDesc()
105{
106    return &m_desc;
107}
108
109Result BufferResource::getNativeResourceHandle(InteropHandle* outHandle)
110{
111    outHandle->handleValue = 0;
112    outHandle->api = InteropHandleAPI::Unknown;
113    return SLANG_FAIL;
114}
115
116Result BufferResource::getSharedHandle(InteropHandle* outHandle)
117{
118    outHandle->api = InteropHandleAPI::Unknown;
119    outHandle->handleValue = 0;
120    return SLANG_FAIL;
121}
122
123IResource* TextureResource::getInterface(const Slang::Guid& guid)
124{
125    if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IResource ||
126        guid == GfxGUID::IID_ITextureResource)
127        return static_cast<ITextureResource*>(this);
128    return nullptr;
129}
130
131SLANG_NO_THROW IResource::Type SLANG_MCALL TextureResource::getType()
132{
133    return m_type;
134}
135SLANG_NO_THROW ITextureResource::Desc* SLANG_MCALL TextureResource::getDesc()
136{
137    return &m_desc;
138}
139
140Result TextureResource::getNativeResourceHandle(InteropHandle* outHandle)
141{
142    outHandle->handleValue = 0;
143    outHandle->api = InteropHandleAPI::Unknown;
144    return SLANG_FAIL;
145}
146
147Result TextureResource::getSharedHandle(InteropHandle* outHandle)
148{
149    outHandle->api = InteropHandleAPI::Unknown;
150    outHandle->handleValue = 0;
151    return SLANG_OK;
152}
153
154StageType mapStage(SlangStage stage)
155{
156    switch (stage)
157    {
158    default:
159        return StageType::Unknown;
160
161    case SLANG_STAGE_AMPLIFICATION:
162        return gfx::StageType::Amplification;
163    case SLANG_STAGE_ANY_HIT:
164        return gfx::StageType::AnyHit;
165    case SLANG_STAGE_CALLABLE:
166        return gfx::StageType::Callable;
167    case SLANG_STAGE_CLOSEST_HIT:
168        return gfx::StageType::ClosestHit;
169    case SLANG_STAGE_COMPUTE:
170        return gfx::StageType::Compute;
171    case SLANG_STAGE_DOMAIN:
172        return gfx::StageType::Domain;
173    case SLANG_STAGE_FRAGMENT:
174        return gfx::StageType::Fragment;
175    case SLANG_STAGE_GEOMETRY:
176        return gfx::StageType::Geometry;
177    case SLANG_STAGE_HULL:
178        return gfx::StageType::Hull;
179    case SLANG_STAGE_INTERSECTION:
180        return gfx::StageType::Intersection;
181    case SLANG_STAGE_MESH:
182        return gfx::StageType::Mesh;
183    case SLANG_STAGE_MISS:
184        return gfx::StageType::Miss;
185    case SLANG_STAGE_RAY_GENERATION:
186        return gfx::StageType::RayGeneration;
187    case SLANG_STAGE_VERTEX:
188        return gfx::StageType::Vertex;
189    }
190}
191
192IResourceView* ResourceViewBase::getInterface(const Guid& guid)
193{
194    if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IResourceView)
195        return static_cast<IResourceView*>(this);
196    return nullptr;
197}
198
199Result ResourceViewBase::getNativeHandle(InteropHandle* outHandle)
200{
201    outHandle->api = InteropHandleAPI::Unknown;
202    outHandle->handleValue = 0;
203    return SLANG_E_NOT_IMPLEMENTED;
204}
205
206ISamplerState* SamplerStateBase::getInterface(const Slang::Guid& guid)
207{
208    if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_ISamplerState)
209        return static_cast<ISamplerState*>(this);
210    return nullptr;
211}
212
213Result SamplerStateBase::getNativeHandle(InteropHandle* outHandle)
214{
215    outHandle->api = InteropHandleAPI::Unknown;
216    outHandle->handleValue = 0;
217    return SLANG_E_NOT_IMPLEMENTED;
218}
219
220IAccelerationStructure* AccelerationStructureBase::getInterface(const Slang::Guid& guid)
221{
222    if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IResourceView ||
223        guid == GfxGUID::IID_IAccelerationStructure)
224        return static_cast<IAccelerationStructure*>(this);
225    return nullptr;
226}
227
228bool _doesValueFitInExistentialPayload(
229    slang::TypeLayoutReflection* concreteTypeLayout,
230    slang::TypeLayoutReflection* existentialTypeLayout)
231{
232    // Our task here is to figure out if a value of `concreteTypeLayout`
233    // can fit into an existential value using `existentialTypelayout`.
234
235    // We can start by asking how many bytes the concrete type of the object consumes.
236    //
237    auto concreteValueSize = concreteTypeLayout->getSize();
238
239    // We can also compute how many bytes the existential-type value provides,
240    // but we need to remember that the *payload* part of that value comes after
241    // the header with RTTI and witness-table IDs, so the payload is 16 bytes
242    // smaller than the entire value.
243    //
244    auto existentialValueSize = existentialTypeLayout->getSize();
245    auto existentialPayloadSize = existentialValueSize - 16;
246
247    // If the concrete type consumes more ordinary bytes than we have in the payload,
248    // it cannot possibly fit.
249    //
250    if (concreteValueSize > existentialPayloadSize)
251        return false;
252
253    // It is possible that the ordinary bytes of `concreteTypeLayout` can fit
254    // in the payload, but that type might also use storage other than ordinary
255    // bytes. In that case, the value would *not* fit, because all the non-ordinary
256    // data can't fit in the payload at all.
257    //
258    auto categoryCount = concreteTypeLayout->getCategoryCount();
259    for (unsigned int i = 0; i < categoryCount; ++i)
260    {
261        auto category = concreteTypeLayout->getCategoryByIndex(i);
262        switch (category)
263        {
264        // We want to ignore any ordinary/uniform data usage, since that
265        // was already checked above.
266        //
267        case slang::ParameterCategory::Uniform:
268            break;
269
270        // Any other kind of data consumed means the value cannot possibly fit.
271        default:
272            return false;
273
274            // TODO: Are there any cases of resource usage that need to be ignored here?
275            // E.g., if the sub-object contains its own existential-type fields (which
276            // get reflected as consuming "existential value" storage) should that be
277            // ignored?
278        }
279    }
280
281    // If we didn't reject the concrete type above for either its ordinary
282    // data or some use of non-ordinary data, then it seems like it must fit.
283    //
284    return true;
285}
286
287IShaderProgram* ShaderProgramBase::getInterface(const Guid& guid)
288{
289    if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IShaderProgram)
290        return static_cast<IShaderProgram*>(this);
291    return nullptr;
292}
293
294IInputLayout* InputLayoutBase::getInterface(const Guid& guid)
295{
296    if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IInputLayout)
297        return static_cast<IInputLayout*>(this);
298    return nullptr;
299}
300
301IFramebufferLayout* FramebufferLayoutBase::getInterface(const Guid& guid)
302{
303    if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IFramebufferLayout)
304        return static_cast<IFramebufferLayout*>(this);
305    return nullptr;
306}
307
308IFramebuffer* FramebufferBase::getInterface(const Guid& guid)
309{
310    if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IFramebuffer)
311        return static_cast<IFramebuffer*>(this);
312    return nullptr;
313}
314
315IQueryPool* QueryPoolBase::getInterface(const Guid& guid)
316{
317    if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IQueryPool)
318        return static_cast<IQueryPool*>(this);
319    return nullptr;
320}
321
322IPipelineState* PipelineStateBase::getInterface(const Guid& guid)
323{
324    if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IPipelineState)
325        return static_cast<IPipelineState*>(this);
326    return nullptr;
327}
328
329Result PipelineStateBase::getNativeHandle(InteropHandle* outHandle)
330{
331    outHandle->api = InteropHandleAPI::Unknown;
332    outHandle->handleValue = 0;
333    return SLANG_E_NOT_IMPLEMENTED;
334}
335
336void PipelineStateBase::initializeBase(const PipelineStateDesc& inDesc)
337{
338    desc = inDesc;
339
340    auto program = desc.getProgram();
341    m_program = program;
342    isSpecializable = false;
343    if (program->slangGlobalScope && program->slangGlobalScope->getSpecializationParamCount() != 0)
344        isSpecializable = true;
345    for (auto& entryPoint : program->slangEntryPoints)
346    {
347        if (entryPoint->getSpecializationParamCount() != 0)
348        {
349            isSpecializable = true;
350            break;
351        }
352    }
353    // Hold a strong reference to inputLayout and framebufferLayout objects to prevent it from
354    // destruction.
355    if (inDesc.type == PipelineType::Graphics)
356    {
357        inputLayout = static_cast<InputLayoutBase*>(inDesc.graphics.inputLayout);
358        framebufferLayout = static_cast<FramebufferLayoutBase*>(inDesc.graphics.framebufferLayout);
359    }
360}
361
362Result RendererBase::getEntryPointCodeFromShaderCache(
363    slang::IComponentType* program,
364    SlangInt entryPointIndex,
365    SlangInt targetIndex,
366    slang::IBlob** outCode,
367    slang::IBlob** outDiagnostics)
368{
369    // Immediately call getEntryPointCode if no shader cache has been initialized
370    if (!persistentShaderCache)
371    {
372        return program->getEntryPointCode(entryPointIndex, targetIndex, outCode, outDiagnostics);
373    }
374
375    // Hash all relevant state for generating the entry point shader code to use as a key
376    // for the shader cache.
377    ComPtr<ISlangBlob> hashBlob;
378    program->getEntryPointHash(entryPointIndex, targetIndex, hashBlob.writeRef());
379    PersistentCache::Key cacheKey(hashBlob);
380
381    // Query the shader cache.
382    ComPtr<ISlangBlob> codeBlob;
383    if (persistentShaderCache->readEntry(cacheKey, codeBlob.writeRef()) != SLANG_OK)
384    {
385        // No cached entry found. Generate the code and add it to the cache.
386        SLANG_RETURN_ON_FAIL(program->getEntryPointCode(
387            entryPointIndex,
388            targetIndex,
389            codeBlob.writeRef(),
390            outDiagnostics));
391        persistentShaderCache->writeEntry(cacheKey, codeBlob);
392    }
393
394    *outCode = codeBlob.detach();
395    return SLANG_OK;
396}
397
398SlangResult RendererBase::queryInterface(SlangUUID const& uuid, void** outObject)
399{
400    // Only return the shader cache interface if it is enabled.
401    if (uuid == GfxGUID::IID_IShaderCache && persistentShaderCache)
402    {
403        *outObject = static_cast<IShaderCache*>(this);
404        addRef();
405        return SLANG_OK;
406    }
407
408    if (IDevice* device_ptr = getInterface(uuid))
409    {
410        *outObject = device_ptr;
411        addRef();
412        return SLANG_OK;
413    }
414    return SLANG_E_NO_INTERFACE;
415}
416
417IDevice* gfx::RendererBase::getInterface(const Guid& guid)
418{
419    return (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IDevice)
420               ? static_cast<IDevice*>(this)
421               : nullptr;
422}
423
424SLANG_NO_THROW Result SLANG_MCALL RendererBase::initialize(const Desc& desc)
425{
426    // We only want to initialize the shader cache if a shader cache path was provided.
427    if (desc.shaderCache.shaderCachePath)
428    {
429        PersistentCache::Desc cacheDesc;
430        cacheDesc.directory = desc.shaderCache.shaderCachePath;
431        cacheDesc.maxEntryCount = desc.shaderCache.maxEntryCount;
432        persistentShaderCache = new PersistentCache(cacheDesc);
433    }
434
435    if (desc.apiCommandDispatcher)
436    {
437        if (desc.deviceType == DeviceType::Vulkan)
438        {
439            desc.apiCommandDispatcher->queryInterface(
440                GfxGUID::IID_IVulkanPipelineCreationAPIDispatcher,
441                (void**)m_pipelineCreationAPIDispatcher.writeRef());
442        }
443        else
444        {
445            desc.apiCommandDispatcher->queryInterface(
446                GfxGUID::IID_IPipelineCreationAPIDispatcher,
447                (void**)m_pipelineCreationAPIDispatcher.writeRef());
448        }
449    }
450    return SLANG_OK;
451}
452
453SLANG_NO_THROW Result SLANG_MCALL RendererBase::getNativeDeviceHandles(InteropHandles* outHandles)
454{
455    return SLANG_OK;
456}
457
458SLANG_NO_THROW Result SLANG_MCALL
459RendererBase::getFeatures(const char** outFeatures, Size bufferSize, GfxCount* outFeatureCount)
460{
461    if (bufferSize >= (UInt)m_features.getCount())
462    {
463        for (Index i = 0; i < m_features.getCount(); i++)
464        {
465            outFeatures[i] = m_features[i].getUnownedSlice().begin();
466        }
467    }
468    if (outFeatureCount)
469        *outFeatureCount = (GfxCount)m_features.getCount();
470    return SLANG_OK;
471}
472
473SLANG_NO_THROW bool SLANG_MCALL RendererBase::hasFeature(const char* featureName)
474{
475    return m_features.findFirstIndex([&](Slang::String x) { return x == featureName; }) != -1;
476}
477
478Result RendererBase::getFormatSupportedResourceStates(Format format, ResourceStateSet* outStates)
479{
480    SLANG_UNUSED(format);
481    outStates->add(ResourceState::AccelerationStructure);
482    outStates->add(ResourceState::AccelerationStructureBuildInput);
483    outStates->add(ResourceState::ConstantBuffer);
484    outStates->add(ResourceState::CopyDestination);
485    outStates->add(ResourceState::CopySource);
486    outStates->add(ResourceState::DepthRead);
487    outStates->add(ResourceState::DepthWrite);
488    outStates->add(ResourceState::IndexBuffer);
489    outStates->add(ResourceState::IndirectArgument);
490    outStates->add(ResourceState::PreInitialized);
491    outStates->add(ResourceState::Present);
492    outStates->add(ResourceState::RenderTarget);
493    outStates->add(ResourceState::ResolveDestination);
494    outStates->add(ResourceState::ResolveSource);
495    outStates->add(ResourceState::ShaderResource);
496    outStates->add(ResourceState::PixelShaderResource);
497    outStates->add(ResourceState::NonPixelShaderResource);
498    outStates->add(ResourceState::StreamOutput);
499    outStates->add(ResourceState::Undefined);
500    outStates->add(ResourceState::UnorderedAccess);
501    outStates->add(ResourceState::VertexBuffer);
502    return SLANG_OK;
503}
504
505SLANG_NO_THROW Result SLANG_MCALL RendererBase::getSlangSession(slang::ISession** outSlangSession)
506{
507    *outSlangSession = slangContext.session.get();
508    slangContext.session->addRef();
509    return SLANG_OK;
510}
511
512SLANG_NO_THROW Result SLANG_MCALL RendererBase::createTextureFromNativeHandle(
513    InteropHandle handle,
514    const ITextureResource::Desc& srcDesc,
515    ITextureResource** outResource)
516{
517    SLANG_UNUSED(handle);
518    SLANG_UNUSED(srcDesc);
519    SLANG_UNUSED(outResource);
520    return SLANG_E_NOT_AVAILABLE;
521}
522
523SLANG_NO_THROW Result SLANG_MCALL RendererBase::createTextureFromSharedHandle(
524    InteropHandle handle,
525    const ITextureResource::Desc& srcDesc,
526    const Size size,
527    ITextureResource** outResource)
528{
529    SLANG_UNUSED(handle);
530    SLANG_UNUSED(srcDesc);
531    SLANG_UNUSED(size);
532    SLANG_UNUSED(outResource);
533    return SLANG_E_NOT_AVAILABLE;
534}
535
536SLANG_NO_THROW Result SLANG_MCALL RendererBase::createBufferFromNativeHandle(
537    InteropHandle handle,
538    const IBufferResource::Desc& srcDesc,
539    IBufferResource** outResource)
540{
541    SLANG_UNUSED(handle);
542    SLANG_UNUSED(srcDesc);
543    SLANG_UNUSED(outResource);
544    return SLANG_E_NOT_AVAILABLE;
545}
546
547SLANG_NO_THROW Result SLANG_MCALL RendererBase::createBufferFromSharedHandle(
548    InteropHandle handle,
549    const IBufferResource::Desc& srcDesc,
550    IBufferResource** outResource)
551{
552    SLANG_UNUSED(handle);
553    SLANG_UNUSED(srcDesc);
554    SLANG_UNUSED(outResource);
555    return SLANG_E_NOT_AVAILABLE;
556}
557
558SLANG_NO_THROW Result SLANG_MCALL RendererBase::createShaderObject(
559    slang::TypeReflection* type,
560    ShaderObjectContainerType container,
561    IShaderObject** outObject)
562{
563    return createShaderObject2(slangContext.session, type, container, outObject);
564}
565
566SLANG_NO_THROW Result SLANG_MCALL RendererBase::createShaderObject2(
567    slang::ISession* slangSession,
568    slang::TypeReflection* type,
569    ShaderObjectContainerType container,
570    IShaderObject** outObject)
571{
572    RefPtr<ShaderObjectLayoutBase> shaderObjectLayout;
573    SLANG_RETURN_ON_FAIL(
574        getShaderObjectLayout(slangSession, type, container, shaderObjectLayout.writeRef()));
575    return createShaderObject(shaderObjectLayout, outObject);
576}
577
578SLANG_NO_THROW Result SLANG_MCALL RendererBase::createMutableShaderObject(
579    slang::TypeReflection* type,
580    ShaderObjectContainerType containerType,
581    IShaderObject** outObject)
582{
583    return createMutableShaderObject2(slangContext.session, type, containerType, outObject);
584}
585
586SLANG_NO_THROW Result SLANG_MCALL RendererBase::createMutableShaderObject2(
587    slang::ISession* slangSession,
588    slang::TypeReflection* type,
589    ShaderObjectContainerType containerType,
590    IShaderObject** outObject)
591{
592    RefPtr<ShaderObjectLayoutBase> shaderObjectLayout;
593    SLANG_RETURN_ON_FAIL(
594        getShaderObjectLayout(slangSession, type, containerType, shaderObjectLayout.writeRef()));
595    return createMutableShaderObject(shaderObjectLayout, outObject);
596}
597
598Result RendererBase::createProgram2(
599    const IShaderProgram::CreateDesc2& desc,
600    IShaderProgram** outProgram,
601    ISlangBlob** outDiagnostic)
602{
603    auto slangSession = slangContext.session.get();
604    slang::IModule* module = nullptr;
605    ComPtr<slang::IBlob> diagnosticsBlob;
606    switch (desc.sourceType)
607    {
608    case ShaderModuleSourceType::SlangSourceFile:
609        {
610            auto fileName = (char*)desc.sourceData;
611            module = slangSession->loadModule(fileName, diagnosticsBlob.writeRef());
612            if (!module)
613                return SLANG_FAIL;
614            break;
615        }
616    case ShaderModuleSourceType::SlangSource:
617        {
618            auto hash = getStableHashCode32((char*)desc.sourceData, desc.sourceDataSize);
619            auto hashStr = String(hash);
620            auto srcBlob = UnownedRawBlob::create(desc.sourceData, desc.sourceDataSize);
621            module = slangSession->loadModuleFromSource(
622                hashStr.getBuffer(),
623                hashStr.getBuffer(),
624                srcBlob,
625                diagnosticsBlob.writeRef());
626            if (!module)
627                return SLANG_FAIL;
628            break;
629        }
630    default:
631        SLANG_RELEASE_ASSERT(false);
632    }
633
634    Slang::List<ComPtr<slang::IComponentType>> componentTypes;
635    componentTypes.add(ComPtr<slang::IComponentType>(module));
636
637    if (desc.entryPointCount == 0)
638    {
639        for (SlangInt32 i = 0; i < module->getDefinedEntryPointCount(); i++)
640        {
641            ComPtr<slang::IEntryPoint> entryPoint;
642            SLANG_RETURN_ON_FAIL(module->getDefinedEntryPoint(i, entryPoint.writeRef()));
643            componentTypes.add(ComPtr<slang::IComponentType>(entryPoint.get()));
644        }
645    }
646    else
647    {
648        for (GfxCount i = 0; i < desc.entryPointCount; i++)
649        {
650            ComPtr<slang::IEntryPoint> entryPoint;
651            SLANG_RETURN_ON_FAIL(
652                module->findEntryPointByName(desc.entryPointNames[i], entryPoint.writeRef()));
653            componentTypes.add(ComPtr<slang::IComponentType>(entryPoint.get()));
654        }
655    }
656
657    Slang::List<slang::IComponentType*> rawComponentTypes;
658    for (auto& compType : componentTypes)
659        rawComponentTypes.add(compType.get());
660
661    ComPtr<slang::IComponentType> linkedProgram;
662    SlangResult result = slangSession->createCompositeComponentType(
663        rawComponentTypes.getBuffer(),
664        rawComponentTypes.getCount(),
665        linkedProgram.writeRef(),
666        diagnosticsBlob.writeRef());
667    SLANG_RETURN_ON_FAIL(result);
668
669    gfx::IShaderProgram::Desc programDesc = {};
670    programDesc.slangGlobalScope = linkedProgram;
671    SLANG_RETURN_ON_FAIL(createProgram(programDesc, outProgram, outDiagnostic));
672
673    return SLANG_OK;
674}
675
676SLANG_NO_THROW Result SLANG_MCALL RendererBase::createShaderObjectFromTypeLayout(
677    slang::TypeLayoutReflection* typeLayout,
678    IShaderObject** outObject)
679{
680    RefPtr<ShaderObjectLayoutBase> shaderObjectLayout;
681    SLANG_RETURN_ON_FAIL(
682        getShaderObjectLayout(slangContext.session, typeLayout, shaderObjectLayout.writeRef()));
683    return createShaderObject(shaderObjectLayout, outObject);
684}
685
686SLANG_NO_THROW Result SLANG_MCALL RendererBase::createMutableShaderObjectFromTypeLayout(
687    slang::TypeLayoutReflection* typeLayout,
688    IShaderObject** outObject)
689{
690    RefPtr<ShaderObjectLayoutBase> shaderObjectLayout;
691    SLANG_RETURN_ON_FAIL(
692        getShaderObjectLayout(slangContext.session, typeLayout, shaderObjectLayout.writeRef()));
693    return createMutableShaderObject(shaderObjectLayout, outObject);
694}
695
696Result RendererBase::getAccelerationStructurePrebuildInfo(
697    const IAccelerationStructure::BuildInputs& buildInputs,
698    IAccelerationStructure::PrebuildInfo* outPrebuildInfo)
699{
700    SLANG_UNUSED(buildInputs);
701    SLANG_UNUSED(outPrebuildInfo);
702    return SLANG_E_NOT_AVAILABLE;
703}
704
705Result RendererBase::createAccelerationStructure(
706    const IAccelerationStructure::CreateDesc& desc,
707    IAccelerationStructure** outView)
708{
709    SLANG_UNUSED(desc);
710    SLANG_UNUSED(outView);
711    return SLANG_E_NOT_AVAILABLE;
712}
713
714Result RendererBase::createShaderTable(const IShaderTable::Desc& desc, IShaderTable** outTable)
715{
716    SLANG_UNUSED(desc);
717    SLANG_UNUSED(outTable);
718    return SLANG_E_NOT_AVAILABLE;
719}
720
721Result RendererBase::createRayTracingPipelineState(
722    const RayTracingPipelineStateDesc& desc,
723    IPipelineState** outState)
724{
725    SLANG_UNUSED(desc);
726    SLANG_UNUSED(outState);
727    return SLANG_E_NOT_AVAILABLE;
728}
729
730Result RendererBase::createMutableRootShaderObject(
731    IShaderProgram* program,
732    IShaderObject** outObject)
733{
734    SLANG_UNUSED(program);
735    SLANG_UNUSED(outObject);
736    return SLANG_E_NOT_AVAILABLE;
737}
738
739Result RendererBase::createFence(const IFence::Desc& desc, IFence** outFence)
740{
741    SLANG_UNUSED(desc);
742    *outFence = nullptr;
743    return SLANG_E_NOT_AVAILABLE;
744}
745
746Result RendererBase::waitForFences(
747    GfxCount fenceCount,
748    IFence** fences,
749    uint64_t* fenceValues,
750    bool waitForAll,
751    uint64_t timeout)
752{
753    SLANG_UNUSED(fenceCount);
754    SLANG_UNUSED(fences);
755    SLANG_UNUSED(fenceValues);
756    SLANG_UNUSED(waitForAll);
757    SLANG_UNUSED(timeout);
758    return SLANG_E_NOT_AVAILABLE;
759}
760
761Result RendererBase::getTextureAllocationInfo(
762    const ITextureResource::Desc& desc,
763    Size* outSize,
764    Size* outAlignment)
765{
766    SLANG_UNUSED(desc);
767    *outSize = 0;
768    *outAlignment = 0;
769    return SLANG_E_NOT_AVAILABLE;
770}
771
772Result RendererBase::getTextureRowAlignment(Size* outAlignment)
773{
774    *outAlignment = 0;
775    return SLANG_E_NOT_AVAILABLE;
776}
777
778Result RendererBase::getCooperativeVectorProperties(
779    CooperativeVectorProperties* properties,
780    uint32_t* propertyCount)
781{
782    *propertyCount = 0;
783    return SLANG_E_NOT_AVAILABLE;
784}
785
786Result RendererBase::getShaderObjectLayout(
787    slang::ISession* session,
788    slang::TypeReflection* type,
789    ShaderObjectContainerType container,
790    ShaderObjectLayoutBase** outLayout)
791{
792    switch (container)
793    {
794    case ShaderObjectContainerType::StructuredBuffer:
795        type = session->getContainerType(type, slang::ContainerType::StructuredBuffer);
796        break;
797    case ShaderObjectContainerType::Array:
798        type = session->getContainerType(type, slang::ContainerType::UnsizedArray);
799        break;
800    default:
801        break;
802    }
803
804    auto typeLayout = session->getTypeLayout(type);
805    SLANG_RETURN_ON_FAIL(getShaderObjectLayout(session, typeLayout, outLayout));
806    (*outLayout)->m_slangSession = session;
807    return SLANG_OK;
808}
809
810Result RendererBase::getShaderObjectLayout(
811    slang::ISession* session,
812    slang::TypeLayoutReflection* typeLayout,
813    ShaderObjectLayoutBase** outLayout)
814{
815    RefPtr<ShaderObjectLayoutBase> shaderObjectLayout;
816    if (!m_shaderObjectLayoutCache.tryGetValue(typeLayout, shaderObjectLayout))
817    {
818        SLANG_RETURN_ON_FAIL(
819            createShaderObjectLayout(session, typeLayout, shaderObjectLayout.writeRef()));
820        m_shaderObjectLayoutCache.add(typeLayout, shaderObjectLayout);
821    }
822    *outLayout = shaderObjectLayout.detach();
823    return SLANG_OK;
824}
825
826Result RendererBase::clearShaderCache()
827{
828    SLANG_ASSERT(persistentShaderCache);
829    return persistentShaderCache->clear();
830}
831
832Result RendererBase::getShaderCacheStats(ShaderCacheStats* outStats)
833{
834    SLANG_ASSERT(persistentShaderCache);
835    if (!outStats)
836    {
837        return SLANG_E_INVALID_ARG;
838    }
839
840    const auto& stats = persistentShaderCache->getStats();
841    outStats->entryCount = (GfxCount)stats.entryCount;
842    outStats->hitCount = (GfxCount)stats.hitCount;
843    outStats->missCount = (GfxCount)stats.missCount;
844    return SLANG_OK;
845}
846
847Result RendererBase::resetShaderCacheStats()
848{
849    SLANG_ASSERT(persistentShaderCache);
850    persistentShaderCache->resetStats();
851    return SLANG_OK;
852}
853
854ShaderComponentID ShaderCache::getComponentId(slang::TypeReflection* type)
855{
856    ComponentKey key;
857    key.typeName = UnownedStringSlice(type->getName());
858    switch (type->getKind())
859    {
860    case slang::TypeReflection::Kind::Specialized:
861        {
862            auto baseType = type->getElementType();
863
864            StringBuilder builder;
865            builder.append(UnownedTerminatedStringSlice(baseType->getName()));
866
867            auto rawType = (SlangReflectionType*)type;
868
869            builder.appendChar('<');
870            SlangInt argCount = spReflectionType_getSpecializedTypeArgCount(rawType);
871            for (SlangInt a = 0; a < argCount; ++a)
872            {
873                if (a != 0)
874                    builder.appendChar(',');
875                if (auto rawArgType = spReflectionType_getSpecializedTypeArgType(rawType, a))
876                {
877                    auto argType = (slang::TypeReflection*)rawArgType;
878                    builder.append(argType->getName());
879                }
880            }
881            builder.appendChar('>');
882            key.typeName = builder.getUnownedSlice();
883            key.updateHash();
884            return getComponentId(key);
885        }
886        // TODO: collect specialization arguments and append them to `key`.
887        SLANG_UNIMPLEMENTED_X("specialized type");
888    default:
889        break;
890    }
891    key.updateHash();
892    return getComponentId(key);
893}
894
895ShaderComponentID ShaderCache::getComponentId(UnownedStringSlice name)
896{
897    ComponentKey key;
898    key.typeName = name;
899    key.updateHash();
900    return getComponentId(key);
901}
902
903ShaderComponentID ShaderCache::getComponentId(ComponentKey key)
904{
905    ShaderComponentID componentId = 0;
906    if (componentIds.tryGetValue(key, componentId))
907        return componentId;
908    OwningComponentKey owningTypeKey;
909    owningTypeKey.hash = key.hash;
910    owningTypeKey.typeName = key.typeName;
911    owningTypeKey.specializationArgs.addRange(key.specializationArgs);
912    ShaderComponentID resultId = static_cast<ShaderComponentID>(componentIds.getCount());
913    componentIds[owningTypeKey] = resultId;
914    return resultId;
915}
916
917void ShaderCache::addSpecializedPipeline(
918    PipelineKey key,
919    Slang::RefPtr<PipelineStateBase> specializedPipeline)
920{
921    specializedPipelines[key] = specializedPipeline;
922}
923
924void ShaderObjectLayoutBase::initBase(
925    RendererBase* renderer,
926    slang::ISession* session,
927    slang::TypeLayoutReflection* elementTypeLayout)
928{
929    m_renderer = renderer;
930    m_slangSession = session;
931    m_elementTypeLayout = elementTypeLayout;
932    m_componentID = m_renderer->shaderCache.getComponentId(m_elementTypeLayout->getType());
933}
934
935// Get the final type this shader object represents. If the shader object's type has existential
936// fields, this function will return a specialized type using the bound sub-objects' type as
937// specialization argument.
938Result ShaderObjectBase::getSpecializedShaderObjectType(ExtendedShaderObjectType* outType)
939{
940    return _getSpecializedShaderObjectType(outType);
941}
942
943Result ShaderObjectBase::_getSpecializedShaderObjectType(ExtendedShaderObjectType* outType)
944{
945    if (shaderObjectType.slangType)
946        *outType = shaderObjectType;
947    ExtendedShaderObjectTypeList specializationArgs;
948    SLANG_RETURN_ON_FAIL(collectSpecializationArgs(specializationArgs));
949    if (specializationArgs.getCount() == 0)
950    {
951        shaderObjectType.componentID = getLayoutBase()->getComponentID();
952        shaderObjectType.slangType = getLayoutBase()->getElementTypeLayout()->getType();
953    }
954    else
955    {
956        shaderObjectType.slangType = getRenderer()->slangContext.session->specializeType(
957            _getElementTypeLayout()->getType(),
958            specializationArgs.components.getArrayView().getBuffer(),
959            specializationArgs.getCount());
960        shaderObjectType.componentID =
961            getRenderer()->shaderCache.getComponentId(shaderObjectType.slangType);
962    }
963    *outType = shaderObjectType;
964    return SLANG_OK;
965}
966
967Result ShaderObjectBase::setExistentialHeader(
968    slang::TypeReflection* existentialType,
969    slang::TypeReflection* concreteType,
970    ShaderOffset offset)
971{
972    // The first field of the tuple (offset zero) is the run-time type information
973    // (RTTI) ID for the concrete type being stored into the field.
974    //
975    // TODO: We need to be able to gather the RTTI type ID from `object` and then
976    // use `setData(offset, &TypeID, sizeof(TypeID))`.
977
978    // The second field of the tuple (offset 8) is the ID of the "witness" for the
979    // conformance of the concrete type to the interface used by this field.
980    //
981    auto witnessTableOffset = offset;
982    witnessTableOffset.uniformOffset += 8;
983    //
984    // Conformances of a type to an interface are computed and then stored by the
985    // Slang runtime, so we can look up the ID for this particular conformance (which
986    // will create it on demand).
987    //
988    // Note: If the type doesn't actually conform to the required interface for
989    // this sub-object range, then this is the point where we will detect that
990    // fact and error out.
991    //
992    uint32_t conformanceID = 0xFFFFFFFF;
993    SLANG_RETURN_ON_FAIL(getLayoutBase()->m_slangSession->getTypeConformanceWitnessSequentialID(
994        concreteType,
995        existentialType,
996        &conformanceID));
997    //
998    // Once we have the conformance ID, then we can write it into the object
999    // at the required offset.
1000    //
1001    SLANG_RETURN_ON_FAIL(setData(witnessTableOffset, &conformanceID, sizeof(conformanceID)));
1002
1003    return SLANG_OK;
1004}
1005
1006ResourceViewBase* SimpleShaderObjectData::getResourceView(
1007    RendererBase* device,
1008    slang::TypeLayoutReflection* elementLayout,
1009    slang::BindingType bindingType)
1010{
1011    if (!m_structuredBuffer)
1012    {
1013        // Create structured buffer resource if it has not been created.
1014        IBufferResource::Desc desc = {};
1015        desc.allowedStates =
1016            ResourceStateSet(ResourceState::ShaderResource, ResourceState::UnorderedAccess);
1017        desc.defaultState = ResourceState::ShaderResource;
1018        desc.elementSize = (int)elementLayout->getSize();
1019        desc.format = Format::Unknown;
1020        desc.type = IResource::Type::Buffer;
1021        desc.sizeInBytes = (Size)m_ordinaryData.getCount();
1022        ComPtr<IBufferResource> bufferResource;
1023        SLANG_RETURN_NULL_ON_FAIL(device->createBufferResource(
1024            desc,
1025            m_ordinaryData.getBuffer(),
1026            bufferResource.writeRef()));
1027        m_structuredBuffer = static_cast<BufferResource*>(bufferResource.get());
1028
1029        // Create read-only (shader-resource) and mutable (unordered access) views.
1030        ComPtr<IResourceView> resourceView;
1031        IResourceView::Desc viewDesc = {};
1032        viewDesc.format = Format::Unknown;
1033        viewDesc.type = IResourceView::Type::ShaderResource;
1034        SLANG_RETURN_NULL_ON_FAIL(device->createBufferView(
1035            bufferResource.get(),
1036            nullptr,
1037            viewDesc,
1038            resourceView.writeRef()));
1039        m_structuredBufferView = static_cast<ResourceViewBase*>(resourceView.get());
1040        viewDesc.type = IResourceView::Type::UnorderedAccess;
1041        SLANG_RETURN_NULL_ON_FAIL(device->createBufferView(
1042            bufferResource.get(),
1043            nullptr,
1044            viewDesc,
1045            resourceView.writeRef()));
1046        m_rwStructuredBufferView = static_cast<ResourceViewBase*>(resourceView.get());
1047    }
1048
1049    switch (bindingType)
1050    {
1051    case slang::BindingType::RawBuffer:
1052        return m_structuredBufferView.Ptr();
1053    case slang::BindingType::MutableRawBuffer:
1054        return m_rwStructuredBufferView.Ptr();
1055    default:
1056        SLANG_ASSERT(false && "Invalid binding type.");
1057        return nullptr;
1058    }
1059}
1060
1061void ShaderProgramBase::init(const IShaderProgram::Desc& inDesc)
1062{
1063    desc = inDesc;
1064
1065    slangGlobalScope = desc.slangGlobalScope;
1066    for (GfxIndex i = 0; i < desc.entryPointCount; i++)
1067    {
1068        slangEntryPoints.add(ComPtr<slang::IComponentType>(desc.slangEntryPoints[i]));
1069    }
1070
1071    auto session = desc.slangGlobalScope ? desc.slangGlobalScope->getSession() : nullptr;
1072    if (desc.linkingStyle == IShaderProgram::LinkingStyle::SingleProgram)
1073    {
1074        List<slang::IComponentType*> components;
1075        if (desc.slangGlobalScope)
1076        {
1077            components.add(desc.slangGlobalScope);
1078        }
1079        for (GfxIndex i = 0; i < desc.entryPointCount; i++)
1080        {
1081            if (!session)
1082            {
1083                session = desc.slangEntryPoints[i]->getSession();
1084            }
1085            components.add(desc.slangEntryPoints[i]);
1086        }
1087        session->createCompositeComponentType(
1088            components.getBuffer(),
1089            components.getCount(),
1090            linkedProgram.writeRef());
1091    }
1092    else
1093    {
1094        for (GfxIndex i = 0; i < desc.entryPointCount; i++)
1095        {
1096            if (desc.slangGlobalScope)
1097            {
1098                slang::IComponentType* entryPointComponents[2] = {
1099                    desc.slangGlobalScope,
1100                    desc.slangEntryPoints[i]};
1101                ComPtr<slang::IComponentType> linkedEntryPoint;
1102                session->createCompositeComponentType(
1103                    entryPointComponents,
1104                    2,
1105                    linkedEntryPoint.writeRef());
1106                linkedEntryPoints.add(linkedEntryPoint);
1107            }
1108            else
1109            {
1110                linkedEntryPoints.add(ComPtr<slang::IComponentType>(desc.slangEntryPoints[i]));
1111            }
1112        }
1113        linkedProgram = desc.slangGlobalScope;
1114    }
1115}
1116
1117Result ShaderProgramBase::compileShaders(RendererBase* device)
1118{
1119    auto compileTarget = device->slangContext.compileTarget;
1120    // For a fully specialized program, read and store its kernel code in `shaderProgram`.
1121    auto compileShader = [&](slang::EntryPointReflection* entryPointInfo,
1122                             slang::IComponentType* entryPointComponent,
1123                             SlangInt entryPointIndex)
1124    {
1125        auto stage = entryPointInfo->getStage();
1126        List<ComPtr<ISlangBlob>> kernelCodes;
1127        {
1128            ComPtr<ISlangBlob> downstreamIR;
1129            ComPtr<ISlangBlob> diagnostics;
1130            auto compileResult = device->getEntryPointCodeFromShaderCache(
1131                entryPointComponent,
1132                entryPointIndex,
1133                0,
1134                downstreamIR.writeRef(),
1135                diagnostics.writeRef());
1136            if (diagnostics)
1137            {
1138                DebugMessageType msgType = DebugMessageType::Warning;
1139                if (compileResult != SLANG_OK)
1140                    msgType = DebugMessageType::Error;
1141                getDebugCallback()->handleMessage(
1142                    msgType,
1143                    DebugMessageSource::Slang,
1144                    (char*)diagnostics->getBufferPointer());
1145            }
1146            SLANG_RETURN_ON_FAIL(compileResult);
1147
1148            kernelCodes.add(downstreamIR);
1149        }
1150
1151        // If target precompilation with deferred downstream linking is enabled,
1152        // kernelCode may only represent the glue code holding together the
1153        // bits of precompiled target IR. It's the application's job to pull it
1154        // together. Collect those dependency target IRs too.
1155        ComPtr<slang::IModulePrecompileService_Experimental> componentPrecompileService;
1156        if (this->desc.downstreamLinkMode == DownstreamLinkMode::Deferred &&
1157            entryPointComponent->queryInterface(
1158                slang::IModulePrecompileService_Experimental::getTypeGuid(),
1159                (void**)componentPrecompileService.writeRef()) == SLANG_OK)
1160        {
1161            SlangInt dependencyCount = componentPrecompileService->getModuleDependencyCount();
1162            if (dependencyCount > 0)
1163            {
1164                for (int dependencyIndex = 0; dependencyIndex < dependencyCount; dependencyIndex++)
1165                {
1166                    ComPtr<slang::IModule> dependencyModule;
1167                    {
1168                        ComPtr<slang::IBlob> diagnosticsBlob;
1169                        auto result = componentPrecompileService->getModuleDependency(
1170                            dependencyIndex,
1171                            dependencyModule.writeRef(),
1172                            diagnosticsBlob.writeRef());
1173                        if (diagnosticsBlob)
1174                        {
1175                            DebugMessageType msgType = DebugMessageType::Warning;
1176                            if (result != SLANG_OK)
1177                                msgType = DebugMessageType::Error;
1178                            getDebugCallback()->handleMessage(
1179                                msgType,
1180                                DebugMessageSource::Slang,
1181                                (char*)diagnosticsBlob->getBufferPointer());
1182                        }
1183                        SLANG_RETURN_ON_FAIL(result);
1184                    }
1185
1186                    ComPtr<slang::IBlob> downstreamIR;
1187                    {
1188                        ComPtr<slang::IBlob> diagnosticsBlob;
1189                        SlangResult result = SLANG_OK;
1190                        ComPtr<slang::IModulePrecompileService_Experimental> precompileService;
1191                        result = dependencyModule->queryInterface(
1192                            slang::IModulePrecompileService_Experimental::getTypeGuid(),
1193                            (void**)precompileService.writeRef());
1194                        if (result == SLANG_OK)
1195                        {
1196                            ComPtr<slang::IBlob> diagnosticsBlob;
1197                            auto result = precompileService->getPrecompiledTargetCode(
1198                                compileTarget,
1199                                downstreamIR.writeRef(),
1200                                diagnosticsBlob.writeRef());
1201                            if (result == SLANG_OK)
1202                            {
1203                                kernelCodes.add(downstreamIR);
1204                            }
1205                            if (diagnosticsBlob)
1206                            {
1207                                DebugMessageType msgType = DebugMessageType::Warning;
1208                                if (result != SLANG_OK)
1209                                    msgType = DebugMessageType::Error;
1210                                getDebugCallback()->handleMessage(
1211                                    msgType,
1212                                    DebugMessageSource::Slang,
1213                                    (char*)diagnosticsBlob->getBufferPointer());
1214                            }
1215                        }
1216                        SLANG_RETURN_ON_FAIL(result);
1217                    }
1218                }
1219            }
1220        }
1221
1222        SLANG_RETURN_ON_FAIL(createShaderModule(entryPointInfo, kernelCodes));
1223        return SLANG_OK;
1224    };
1225
1226    if (linkedEntryPoints.getCount() == 0)
1227    {
1228        // If the user does not explicitly specify entry point components, find them from
1229        // `linkedEntryPoints`.
1230        auto programReflection = linkedProgram->getLayout();
1231        for (SlangUInt i = 0; i < programReflection->getEntryPointCount(); i++)
1232        {
1233            SLANG_RETURN_ON_FAIL(compileShader(
1234                programReflection->getEntryPointByIndex(i),
1235                linkedProgram,
1236                (SlangInt)i));
1237        }
1238    }
1239    else
1240    {
1241        // If the user specifies entry point components via the separated entry point array,
1242        // compile code from there.
1243        for (auto& entryPoint : linkedEntryPoints)
1244        {
1245            SLANG_RETURN_ON_FAIL(
1246                compileShader(entryPoint->getLayout()->getEntryPointByIndex(0), entryPoint, 0));
1247        }
1248    }
1249    return SLANG_OK;
1250}
1251
1252Result ShaderProgramBase::createShaderModule(
1253    slang::EntryPointReflection* entryPointInfo,
1254    List<ComPtr<ISlangBlob>>& kernelCodes)
1255{
1256    SLANG_UNUSED(entryPointInfo);
1257    SLANG_UNUSED(kernelCodes);
1258    return SLANG_OK;
1259}
1260
1261bool ShaderProgramBase::isMeshShaderProgram() const
1262{
1263    // Similar to above, interrogate either explicity specified entry point
1264    // componenets or the ones in the linked program entry point array
1265    if (linkedEntryPoints.getCount())
1266    {
1267        for (const auto& e : linkedEntryPoints)
1268            if (e->getLayout()->getEntryPointByIndex(0)->getStage() == SLANG_STAGE_MESH)
1269                return true;
1270    }
1271    else
1272    {
1273        const auto programReflection = linkedProgram->getLayout();
1274        for (SlangUInt i = 0; i < programReflection->getEntryPointCount(); ++i)
1275            if (programReflection->getEntryPointByIndex(i)->getStage() == SLANG_STAGE_MESH)
1276                return true;
1277    }
1278    return false;
1279}
1280
1281Result RendererBase::maybeSpecializePipeline(
1282    PipelineStateBase* currentPipeline,
1283    ShaderObjectBase* rootObject,
1284    RefPtr<PipelineStateBase>& outNewPipeline)
1285{
1286    outNewPipeline = static_cast<PipelineStateBase*>(currentPipeline);
1287
1288    auto pipelineType = currentPipeline->desc.type;
1289    if (currentPipeline->unspecializedPipelineState)
1290        currentPipeline = currentPipeline->unspecializedPipelineState;
1291    // If the currently bound pipeline is specializable, we need to specialize it based on bound
1292    // shader objects.
1293    if (currentPipeline->isSpecializable)
1294    {
1295        specializationArgs.clear();
1296        SLANG_RETURN_ON_FAIL(rootObject->collectSpecializationArgs(specializationArgs));
1297
1298        // Construct a shader cache key that represents the specialized shader kernels.
1299        PipelineKey pipelineKey;
1300        pipelineKey.pipeline = currentPipeline;
1301        pipelineKey.specializationArgs.addRange(specializationArgs.componentIDs);
1302        pipelineKey.updateHash();
1303
1304        RefPtr<PipelineStateBase> specializedPipelineState =
1305            shaderCache.getSpecializedPipelineState(pipelineKey);
1306        // Try to find specialized pipeline from shader cache.
1307        if (!specializedPipelineState)
1308        {
1309            auto unspecializedProgram = static_cast<ShaderProgramBase*>(
1310                pipelineType == PipelineType::Compute ? currentPipeline->desc.compute.program
1311                                                      : currentPipeline->desc.graphics.program);
1312            auto unspecializedProgramLayout = unspecializedProgram->linkedProgram->getLayout();
1313
1314            ComPtr<slang::IComponentType> specializedComponentType;
1315            ComPtr<slang::IBlob> diagnosticBlob;
1316            auto compileRs = unspecializedProgram->linkedProgram->specialize(
1317                specializationArgs.components.getArrayView().getBuffer(),
1318                specializationArgs.getCount(),
1319                specializedComponentType.writeRef(),
1320                diagnosticBlob.writeRef());
1321            if (diagnosticBlob)
1322            {
1323                getDebugCallback()->handleMessage(
1324                    compileRs == SLANG_OK ? DebugMessageType::Warning : DebugMessageType::Error,
1325                    DebugMessageSource::Slang,
1326                    (char*)diagnosticBlob->getBufferPointer());
1327            }
1328            SLANG_RETURN_ON_FAIL(compileRs);
1329
1330            // Now create the specialized shader program using compiled binaries.
1331            ComPtr<IShaderProgram> specializedProgram;
1332            IShaderProgram::Desc specializedProgramDesc = unspecializedProgram->desc;
1333            specializedProgramDesc.slangGlobalScope = specializedComponentType;
1334
1335            if (specializedProgramDesc.linkingStyle == IShaderProgram::LinkingStyle::SingleProgram)
1336            {
1337                // When linking style is GraphicsCompute, the specialized global scope already
1338                // contains entry-points, so we do not need to supply them again when creating the
1339                // specialized pipeline.
1340                specializedProgramDesc.entryPointCount = 0;
1341            }
1342            SLANG_RETURN_ON_FAIL(
1343                createProgram(specializedProgramDesc, specializedProgram.writeRef()));
1344
1345            // Create specialized pipeline state.
1346            ComPtr<IPipelineState> specializedPipelineComPtr;
1347            switch (pipelineType)
1348            {
1349            case PipelineType::Compute:
1350                {
1351                    auto pipelineDesc = currentPipeline->desc.compute;
1352                    pipelineDesc.program = specializedProgram;
1353                    SLANG_RETURN_ON_FAIL(createComputePipelineState(
1354                        pipelineDesc,
1355                        specializedPipelineComPtr.writeRef()));
1356                    break;
1357                }
1358            case PipelineType::Graphics:
1359                {
1360                    auto pipelineDesc = currentPipeline->desc.graphics;
1361                    pipelineDesc.program =
1362                        static_cast<ShaderProgramBase*>(specializedProgram.get());
1363                    SLANG_RETURN_ON_FAIL(createGraphicsPipelineState(
1364                        pipelineDesc,
1365                        specializedPipelineComPtr.writeRef()));
1366                    break;
1367                }
1368            case PipelineType::RayTracing:
1369                {
1370                    auto pipelineDesc = currentPipeline->desc.rayTracing;
1371                    pipelineDesc.program =
1372                        static_cast<ShaderProgramBase*>(specializedProgram.get());
1373                    SLANG_RETURN_ON_FAIL(createRayTracingPipelineState(
1374                        pipelineDesc.get(),
1375                        specializedPipelineComPtr.writeRef()));
1376                    break;
1377                }
1378            default:
1379                break;
1380            }
1381            specializedPipelineState =
1382                static_cast<PipelineStateBase*>(specializedPipelineComPtr.get());
1383            specializedPipelineState->unspecializedPipelineState = currentPipeline;
1384            shaderCache.addSpecializedPipeline(pipelineKey, specializedPipelineState);
1385        }
1386        auto specializedPipelineStateBase =
1387            static_cast<PipelineStateBase*>(specializedPipelineState.Ptr());
1388        outNewPipeline = specializedPipelineStateBase;
1389    }
1390    return SLANG_OK;
1391}
1392
1393IDebugCallback*& _getDebugCallback()
1394{
1395    static IDebugCallback* callback = nullptr;
1396    return callback;
1397}
1398
1399class NullDebugCallback : public IDebugCallback
1400{
1401public:
1402    virtual SLANG_NO_THROW void SLANG_MCALL
1403    handleMessage(DebugMessageType type, DebugMessageSource source, const char* message) override
1404    {
1405        SLANG_UNUSED(type);
1406        SLANG_UNUSED(source);
1407        SLANG_UNUSED(message);
1408    }
1409};
1410IDebugCallback* _getNullDebugCallback()
1411{
1412    static NullDebugCallback result = {};
1413    return &result;
1414}
1415
1416Result ShaderObjectBase::copyFrom(IShaderObject* object, ITransientResourceHeap* transientHeap)
1417{
1418    if (auto srcObj = dynamic_cast<MutableRootShaderObject*>(object))
1419    {
1420        setData(
1421            gfx::ShaderOffset(),
1422            srcObj->m_data.begin(),
1423            (size_t)srcObj->m_data.getCount()); // TODO: Change size_t to Count?
1424        for (auto& kv : srcObj->m_objects)
1425        {
1426            ComPtr<IShaderObject> subObject;
1427            SLANG_RETURN_ON_FAIL(kv.value->getCurrentVersion(transientHeap, subObject.writeRef()));
1428            setObject(kv.key, subObject);
1429        }
1430        for (auto& kv : srcObj->m_resources)
1431        {
1432            setResource(kv.key, kv.value.Ptr());
1433        }
1434        for (auto& kv : srcObj->m_samplers)
1435        {
1436            setSampler(kv.key, kv.value.Ptr());
1437        }
1438        for (auto& kv : srcObj->m_specializationArgs)
1439        {
1440            setSpecializationArgs(kv.key, kv.value.begin(), (uint32_t)kv.value.getCount());
1441        }
1442        return SLANG_OK;
1443    }
1444    return SLANG_FAIL;
1445}
1446
1447Result ShaderTableBase::init(const IShaderTable::Desc& desc)
1448{
1449    m_rayGenShaderCount = desc.rayGenShaderCount;
1450    m_missShaderCount = desc.missShaderCount;
1451    m_hitGroupCount = desc.hitGroupCount;
1452    m_callableShaderCount = desc.callableShaderCount;
1453    m_shaderGroupNames.reserve(
1454        desc.hitGroupCount + desc.missShaderCount + desc.rayGenShaderCount +
1455        desc.callableShaderCount);
1456    m_recordOverwrites.reserve(
1457        desc.hitGroupCount + desc.missShaderCount + desc.rayGenShaderCount +
1458        desc.callableShaderCount);
1459    for (GfxIndex i = 0; i < desc.rayGenShaderCount; i++)
1460    {
1461        m_shaderGroupNames.add(desc.rayGenShaderEntryPointNames[i]);
1462        if (desc.rayGenShaderRecordOverwrites)
1463        {
1464            m_recordOverwrites.add(desc.rayGenShaderRecordOverwrites[i]);
1465        }
1466        else
1467        {
1468            m_recordOverwrites.add(ShaderRecordOverwrite{});
1469        }
1470    }
1471    for (GfxIndex i = 0; i < desc.missShaderCount; i++)
1472    {
1473        m_shaderGroupNames.add(desc.missShaderEntryPointNames[i]);
1474        if (desc.missShaderRecordOverwrites)
1475        {
1476            m_recordOverwrites.add(desc.missShaderRecordOverwrites[i]);
1477        }
1478        else
1479        {
1480            m_recordOverwrites.add(ShaderRecordOverwrite{});
1481        }
1482    }
1483    for (GfxIndex i = 0; i < desc.hitGroupCount; i++)
1484    {
1485        m_shaderGroupNames.add(desc.hitGroupNames[i]);
1486        if (desc.hitGroupRecordOverwrites)
1487        {
1488            m_recordOverwrites.add(desc.hitGroupRecordOverwrites[i]);
1489        }
1490        else
1491        {
1492            m_recordOverwrites.add(ShaderRecordOverwrite{});
1493        }
1494    }
1495    for (GfxIndex i = 0; i < desc.callableShaderCount; i++)
1496    {
1497        m_shaderGroupNames.add(desc.callableShaderEntryPointNames[i]);
1498        if (desc.callableShaderRecordOverwrites)
1499        {
1500            m_recordOverwrites.add(desc.callableShaderRecordOverwrites[i]);
1501        }
1502        else
1503        {
1504            m_recordOverwrites.add(ShaderRecordOverwrite{});
1505        }
1506    }
1507    return SLANG_OK;
1508}
1509
1510bool isDepthFormat(Format format)
1511{
1512    switch (format)
1513    {
1514    case Format::D16_UNORM:
1515    case Format::D32_FLOAT:
1516    case Format::D32_FLOAT_S8_UINT:
1517        return true;
1518    default:
1519        return false;
1520    }
1521}
1522
1523bool isStencilFormat(Format format)
1524{
1525    switch (format)
1526    {
1527    case Format::D32_FLOAT_S8_UINT:
1528        return true;
1529    default:
1530        return false;
1531    }
1532}
1533
1534} // namespace gfx