yum-mirror/slang

Making it easier to work with shaders

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

Gangzheng TongConvert gfx unit tests and examples to use slang-rhi (#7577)43d0c2100

master
24.2 KiB694 linesraw
1// main.cpp
2
3// Using Parameter Blocks With Reflection
4// ======================================
5//
6// This example program is a companion to the article
7// Using Slang Parameter Blocks, and specifically
8// the section of that article called Using Parameter
9// Blocks With Reflection.
10//
11// Where possible, the code is presented in the
12// same order as the code in the article, so that the
13// two can be read in parallel. When code relates to
14// a sub-section of the article, a comment will be used
15// to reference the relevant section.
16//
17// Boilerplate
18// ===========
19//
20// As is typical for our example programs, this one starts
21// with a certain amount of boilerplate that isn't especially
22// interesting to discuss.
23
24#include "core/slang-basic.h"
25#include "examples/example-base/example-base.h"
26#include "slang-com-ptr.h"
27#include "slang-rhi.h"
28#include "slang.h"
29
30typedef SlangResult Result;
31using Slang::ComPtr;
32using Slang::String;
33using Slang::List;
34using namespace rhi;
35
36// The example code currently only supports Vulkan, but the
37// code is factored with the intention that it could be extended
38// to support D3D12 as well.
39
40#define ENABLE_VULKAN 1
41#define ENABLE_D3D12 0
42
43#if ENABLE_VULKAN
44#include "vulkan-api.h"
45#endif
46
47static const ExampleResources resourceBase("reflection-parameter-blocks");
48static const char* kSourceFileName = "shader.slang";
49
50struct PipelineLayoutReflectionContext
51{
52    IDevice* _rhiDevice = nullptr;
53    slang::ISession* _slangSession = nullptr;
54    slang::ProgramLayout* _slangProgramLayout = nullptr;
55    slang::IBlob* _slangCompiledProgramBlob = nullptr;
56};
57
58struct PipelineLayoutReflectionContext_Vulkan : PipelineLayoutReflectionContext
59{
60    // What Goes Into a Pipeline Layout?
61    // =================================
62
63    struct PipelineLayoutBuilder
64    {
65        std::vector<VkDescriptorSetLayout> descriptorSetLayouts;
66        std::vector<VkPushConstantRange> pushConstantRanges;
67    };
68
69    // Unlike how things are presented in the document, we do not
70    // nest most of the functions under the `*Builder` types, in
71    // order to allow for more flexibility in the order of
72    // presentation. For example, instead of a
73    // `PipelineLayoutBuilder::finishBuilding()` method, we instead
74    // have a `finishBuildingPipelineLayout` function:
75
76    Result finishBuildingPipelineLayout(
77        PipelineLayoutBuilder& builder,
78        VkPipelineLayout* outPipelineLayout)
79    {
80        filterOutEmptyDescriptorSets(builder);
81
82        VkPipelineLayoutCreateInfo pipelineLayoutInfo = {
83            VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
84
85        pipelineLayoutInfo.setLayoutCount = builder.descriptorSetLayouts.size();
86        pipelineLayoutInfo.pSetLayouts = builder.descriptorSetLayouts.data();
87
88        pipelineLayoutInfo.pushConstantRangeCount = builder.pushConstantRanges.size();
89        pipelineLayoutInfo.pPushConstantRanges = builder.pushConstantRanges.data();
90
91        VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
92        vkAPI.vkCreatePipelineLayout(vkAPI.device, &pipelineLayoutInfo, nullptr, &pipelineLayout);
93
94        *outPipelineLayout = pipelineLayout;
95        return SLANG_OK;
96    }
97
98    // What Goes Into a Descriptor Set Layout?
99    // =======================================
100
101    struct DescriptorSetLayoutBuilder
102    {
103        std::vector<VkDescriptorSetLayoutBinding> descriptorRanges;
104
105        int setIndex = -1;
106    };
107
108
109    // Once we are done traversing the contents of a parameter
110    // block to collect bindings into a `DescriptorSetLayoutBuilder`,
111    // it is a simple matter to create a descriptor set layout using
112    // the Vulkan API, and to install it into the `setLayouts` array
113    // at the index that was reserved.
114    //
115    void finishBuildingDescriptorSetLayout(
116        PipelineLayoutBuilder& pipelineLayoutBuilder,
117        DescriptorSetLayoutBuilder& descriptorSetLayoutBuilder)
118    {
119        if (descriptorSetLayoutBuilder.descriptorRanges.empty())
120            return;
121
122        VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo = {
123            VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO};
124
125        descriptorSetLayoutInfo.bindingCount = descriptorSetLayoutBuilder.descriptorRanges.size();
126        descriptorSetLayoutInfo.pBindings = descriptorSetLayoutBuilder.descriptorRanges.data();
127
128        VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;
129        vkAPI.vkCreateDescriptorSetLayout(
130            vkAPI.device,
131            &descriptorSetLayoutInfo,
132            nullptr,
133            &descriptorSetLayout);
134
135        pipelineLayoutBuilder.descriptorSetLayouts[descriptorSetLayoutBuilder.setIndex] =
136            descriptorSetLayout;
137    }
138
139    // Parameter Blocks
140    // ================
141
142    void addDescriptorSetForParameterBlock(
143        PipelineLayoutBuilder& pipelineLayoutBuilder,
144        slang::TypeLayoutReflection* parameterBlockTypeLayout)
145    {
146        DescriptorSetLayoutBuilder descriptorSetLayoutBuilder;
147        startBuildingDescriptorSetLayout(pipelineLayoutBuilder, descriptorSetLayoutBuilder);
148
149        addRangesForParameterBlockElement(
150            pipelineLayoutBuilder,
151            descriptorSetLayoutBuilder,
152            parameterBlockTypeLayout->getElementTypeLayout());
153
154        finishBuildingDescriptorSetLayout(pipelineLayoutBuilder, descriptorSetLayoutBuilder);
155    }
156
157    // Automatically-Introduced Uniform Buffer
158    // ---------------------------------------
159
160    void addRangesForParameterBlockElement(
161        PipelineLayoutBuilder& pipelineLayoutBuilder,
162        DescriptorSetLayoutBuilder& descriptorSetLayoutBuilder,
163        slang::TypeLayoutReflection* elementTypeLayout)
164    {
165        if (elementTypeLayout->getSize() > 0)
166        {
167            addAutomaticallyIntroducedUniformBuffer(descriptorSetLayoutBuilder);
168        }
169
170        // Once we have accounted for the possibility of an implicitly-introduced
171        // constant buffer, we can move on and add bindings based on whatever
172        // non-ordinary data (textures, buffers, etc.) is in the element type:
173        //
174        addRanges(pipelineLayoutBuilder, descriptorSetLayoutBuilder, elementTypeLayout);
175    }
176
177    void addAutomaticallyIntroducedUniformBuffer(
178        DescriptorSetLayoutBuilder& descriptorSetLayoutBuilder)
179    {
180        auto vulkanBindingIndex = descriptorSetLayoutBuilder.descriptorRanges.size();
181
182        VkDescriptorSetLayoutBinding binding = {};
183        binding.stageFlags = VK_SHADER_STAGE_ALL;
184        binding.binding = vulkanBindingIndex;
185        binding.descriptorCount = 1;
186        binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
187
188        descriptorSetLayoutBuilder.descriptorRanges.push_back(binding);
189    }
190
191    // Ordering of Nested Parameter Blocks
192    // -----------------------------------
193
194    void startBuildingDescriptorSetLayout(
195        PipelineLayoutBuilder& pipelineLayoutBuilder,
196        DescriptorSetLayoutBuilder& descriptorSetLayoutBuilder)
197    {
198        descriptorSetLayoutBuilder.setIndex = pipelineLayoutBuilder.descriptorSetLayouts.size();
199        pipelineLayoutBuilder.descriptorSetLayouts.push_back(VK_NULL_HANDLE);
200    }
201
202    // Empty Ranges
203    // ------------
204
205    void filterOutEmptyDescriptorSets(PipelineLayoutBuilder& builder)
206    {
207        std::vector<VkDescriptorSetLayout> filteredDescriptorSetLayouts;
208        for (auto descriptorSetLayout : builder.descriptorSetLayouts)
209        {
210            if (!descriptorSetLayout)
211                continue;
212            filteredDescriptorSetLayouts.push_back(descriptorSetLayout);
213        }
214        std::swap(builder.descriptorSetLayouts, filteredDescriptorSetLayouts);
215    }
216
217    // Descritpor Ranges
218    // =================
219
220    void addDescriptorRanges(
221        DescriptorSetLayoutBuilder& descriptorSetLayoutBuilder,
222        slang::TypeLayoutReflection* typeLayout)
223    {
224        int relativeSetIndex = 0;
225        int rangeCount = typeLayout->getDescriptorSetDescriptorRangeCount(relativeSetIndex);
226
227        for (int rangeIndex = 0; rangeIndex < rangeCount; ++rangeIndex)
228        {
229            addDescriptorRange(
230                descriptorSetLayoutBuilder,
231                typeLayout,
232                relativeSetIndex,
233                rangeIndex);
234        }
235    }
236
237    void addDescriptorRange(
238        DescriptorSetLayoutBuilder& descriptorSetLayoutBuilder,
239        slang::TypeLayoutReflection* typeLayout,
240        int relativeSetIndex,
241        int rangeIndex)
242    {
243        slang::BindingType bindingType =
244            typeLayout->getDescriptorSetDescriptorRangeType(relativeSetIndex, rangeIndex);
245        auto descriptorCount = typeLayout->getDescriptorSetDescriptorRangeDescriptorCount(
246            relativeSetIndex,
247            rangeIndex);
248
249        // Some Ranges Need to Be Skipped
250        // ------------------------------
251        //
252        switch (bindingType)
253        {
254        default:
255            break;
256
257        case slang::BindingType::PushConstant:
258            return;
259        }
260
261        auto bindingIndex = descriptorSetLayoutBuilder.descriptorRanges.size();
262
263        VkDescriptorSetLayoutBinding vulkanBindingRange = {};
264        vulkanBindingRange.binding = bindingIndex;
265        vulkanBindingRange.descriptorCount = descriptorCount;
266        vulkanBindingRange.stageFlags = _currentStageFlags;
267        vulkanBindingRange.descriptorType = mapSlangBindingTypeToVulkanDescriptorType(bindingType);
268
269        descriptorSetLayoutBuilder.descriptorRanges.push_back(vulkanBindingRange);
270    }
271
272    VkDescriptorType mapSlangBindingTypeToVulkanDescriptorType(slang::BindingType bindingType)
273    {
274        switch (bindingType)
275        {
276#define CASE(FROM, TO)             \
277    case slang::BindingType::FROM: \
278        return VK_DESCRIPTOR_TYPE_##TO
279
280            CASE(Sampler, SAMPLER);
281            CASE(CombinedTextureSampler, COMBINED_IMAGE_SAMPLER);
282            CASE(Texture, SAMPLED_IMAGE);
283            CASE(MutableTexture, STORAGE_IMAGE);
284            CASE(TypedBuffer, UNIFORM_TEXEL_BUFFER);
285            CASE(MutableTypedBuffer, STORAGE_TEXEL_BUFFER);
286            CASE(ConstantBuffer, UNIFORM_BUFFER);
287            CASE(RawBuffer, STORAGE_BUFFER);
288            CASE(MutableRawBuffer, STORAGE_BUFFER);
289            CASE(InputRenderTarget, INPUT_ATTACHMENT);
290            CASE(InlineUniformData, INLINE_UNIFORM_BLOCK);
291            CASE(RayTracingAccelerationStructure, ACCELERATION_STRUCTURE_KHR);
292
293#undef CASE
294
295        default:
296            return VkDescriptorType(-1);
297        }
298    }
299
300    // Sub-Object Ranges
301    // =================
302
303    void addRanges(
304        PipelineLayoutBuilder& pipelineLayoutBuilder,
305        DescriptorSetLayoutBuilder& descriptorSetLayoutBuilder,
306        slang::TypeLayoutReflection* typeLayout)
307    {
308        addDescriptorRanges(descriptorSetLayoutBuilder, typeLayout);
309        addSubObjectRanges(pipelineLayoutBuilder, typeLayout);
310    }
311
312    void addSubObjectRanges(
313        PipelineLayoutBuilder& pipelineLayoutBuilder,
314        slang::TypeLayoutReflection* typeLayout)
315    {
316        int subObjectRangeCount = typeLayout->getSubObjectRangeCount();
317        for (int subObjectRangeIndex = 0; subObjectRangeIndex < subObjectRangeCount;
318             ++subObjectRangeIndex)
319        {
320            addSubObjectRange(pipelineLayoutBuilder, typeLayout, subObjectRangeIndex);
321        }
322    }
323
324    void addSubObjectRange(
325        PipelineLayoutBuilder& pipelineLayoutBuilder,
326        slang::TypeLayoutReflection* typeLayout,
327        int subObjectRangeIndex)
328    {
329        auto bindingRangeIndex =
330            typeLayout->getSubObjectRangeBindingRangeIndex(subObjectRangeIndex);
331        auto bindingType = typeLayout->getBindingRangeType(bindingRangeIndex);
332        switch (bindingType)
333        {
334        default:
335            return;
336
337            // Nested Parameter Blocks
338            // -----------------------
339
340        case slang::BindingType::ParameterBlock:
341            {
342                auto parameterBlockTypeLayout =
343                    typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex);
344                addDescriptorSetForParameterBlock(pipelineLayoutBuilder, parameterBlockTypeLayout);
345            }
346            break;
347
348            // Push-Constant Ranges
349            // --------------------
350
351        case slang::BindingType::PushConstant:
352            {
353                auto constantBufferTypeLayout =
354                    typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex);
355                addPushConstantRangeForConstantBuffer(
356                    pipelineLayoutBuilder,
357                    constantBufferTypeLayout);
358            }
359            break;
360        }
361    }
362
363    void addPushConstantRangeForConstantBuffer(
364        PipelineLayoutBuilder& pipelineLayoutBuilder,
365        slang::TypeLayoutReflection* pushConstantBufferTypeLayout)
366    {
367        auto elementTypeLayout = pushConstantBufferTypeLayout->getElementTypeLayout();
368        auto elementSize = elementTypeLayout->getSize();
369
370        if (elementSize == 0)
371            return;
372
373        VkPushConstantRange pushConstantRange = {};
374        pushConstantRange.stageFlags = _currentStageFlags;
375        pushConstantRange.offset = 0;
376        pushConstantRange.size = elementSize;
377
378        pipelineLayoutBuilder.pushConstantRanges.push_back(pushConstantRange);
379    }
380
381    // Creating a Pipeline Layout for a Program
382    // ========================================
383
384    Result createPipelineLayout(
385        slang::ProgramLayout* programLayout,
386        VkPipelineLayout* outPipelineLayout)
387    {
388        PipelineLayoutBuilder pipelineLayoutBuilder;
389
390        DescriptorSetLayoutBuilder defaultDescriptorSetLayoutBuilder;
391        startBuildingDescriptorSetLayout(pipelineLayoutBuilder, defaultDescriptorSetLayoutBuilder);
392
393        addGlobalScopeParameters(
394            pipelineLayoutBuilder,
395            defaultDescriptorSetLayoutBuilder,
396            programLayout);
397
398        addEntryPointParameters(
399            pipelineLayoutBuilder,
400            defaultDescriptorSetLayoutBuilder,
401            programLayout);
402
403        finishBuildingDescriptorSetLayout(pipelineLayoutBuilder, defaultDescriptorSetLayoutBuilder);
404        finishBuildingPipelineLayout(pipelineLayoutBuilder, outPipelineLayout);
405
406        return SLANG_OK;
407    }
408
409    // Global Scope
410    // ------------
411
412    void addGlobalScopeParameters(
413        PipelineLayoutBuilder& pipelineLayoutBuilder,
414        DescriptorSetLayoutBuilder& descriptorSetLayoutBuilder,
415        slang::ProgramLayout* programLayout)
416    {
417        _currentStageFlags = VK_SHADER_STAGE_ALL;
418        addRangesForParameterBlockElement(
419            pipelineLayoutBuilder,
420            descriptorSetLayoutBuilder,
421            programLayout->getGlobalParamsTypeLayout());
422    }
423
424    // Entry Points
425    // ------------
426
427    void addEntryPointParameters(
428        PipelineLayoutBuilder& pipelineLayoutBuilder,
429        DescriptorSetLayoutBuilder& descriptorSetLayoutBuilder,
430        slang::ProgramLayout* programLayout)
431    {
432        int entryPointCount = _slangProgramLayout->getEntryPointCount();
433        for (int i = 0; i < entryPointCount; ++i)
434        {
435            auto entryPointLayout = _slangProgramLayout->getEntryPointByIndex(i);
436            addEntryPointParameters(
437                pipelineLayoutBuilder,
438                descriptorSetLayoutBuilder,
439                entryPointLayout);
440        }
441    }
442
443    void addEntryPointParameters(
444        PipelineLayoutBuilder& pipelineLayoutBuilder,
445        DescriptorSetLayoutBuilder& descriptorSetLayoutBuilder,
446        slang::EntryPointLayout* entryPointLayout)
447    {
448        _currentStageFlags = getShaderStageFlags(entryPointLayout->getStage());
449        addRangesForParameterBlockElement(
450            pipelineLayoutBuilder,
451            descriptorSetLayoutBuilder,
452            entryPointLayout->getTypeLayout());
453    }
454
455    VkShaderStageFlags _currentStageFlags = VK_SHADER_STAGE_ALL;
456    VkShaderStageFlags getShaderStageFlags(SlangStage stage)
457    {
458        switch (stage)
459        {
460#define CASE(FROM, TO)       \
461    case SLANG_STAGE_##FROM: \
462        return VK_SHADER_STAGE_##TO
463
464            CASE(VERTEX, VERTEX_BIT);
465            CASE(HULL, TESSELLATION_CONTROL_BIT);
466            CASE(DOMAIN, TESSELLATION_EVALUATION_BIT);
467            CASE(GEOMETRY, GEOMETRY_BIT);
468            CASE(FRAGMENT, FRAGMENT_BIT);
469            CASE(COMPUTE, COMPUTE_BIT);
470            CASE(RAY_GENERATION, RAYGEN_BIT_KHR);
471            CASE(ANY_HIT, ANY_HIT_BIT_KHR);
472            CASE(CLOSEST_HIT, CLOSEST_HIT_BIT_KHR);
473            CASE(MISS, MISS_BIT_KHR);
474            CASE(INTERSECTION, INTERSECTION_BIT_KHR);
475            CASE(CALLABLE, CALLABLE_BIT_KHR);
476            CASE(MESH, MESH_BIT_EXT);
477            CASE(AMPLIFICATION, TASK_BIT_EXT);
478
479#undef CASE
480        default:
481            return VK_SHADER_STAGE_ALL;
482        }
483    }
484
485    // Validation
486    // ==========
487    //
488    // The published article covers how to create a pipeline layout
489    // using the reflection API, but for the purposes of an example
490    // program, we should make sure that we validate that the layout
491    // that results from that code is *actually* compatible with the
492    // shader program.
493    //
494    // The remaining operations inside this type provide the support
495    // code to create and validate a pipeline layout based on a
496    // particular compiled compute program. Mismatches between the
497    // pipeline layout and the program should be diagnosed by the
498    // Vulkan validation layer when we attempt to create a pipeline
499    // that uses the two together.
500
501    Result validatePipelineLayout(VkPipelineLayout pipelineLayout)
502    {
503        VkShaderModuleCreateInfo shaderModuleInfo = {VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO};
504        shaderModuleInfo.pCode = (uint32_t const*)_slangCompiledProgramBlob->getBufferPointer();
505        shaderModuleInfo.codeSize = _slangCompiledProgramBlob->getBufferSize();
506
507        VkShaderModule vkShaderModule;
508        vkAPI.vkCreateShaderModule(vkAPI.device, &shaderModuleInfo, nullptr, &vkShaderModule);
509
510        VkComputePipelineCreateInfo pipelineInfo = {VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO};
511        pipelineInfo.layout = pipelineLayout;
512        pipelineInfo.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
513        pipelineInfo.stage.module = vkShaderModule;
514        pipelineInfo.stage.pName = "main";
515        pipelineInfo.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT;
516
517        VkPipeline pipeline;
518        vkAPI.vkCreateComputePipelines(
519            vkAPI.device,
520            VK_NULL_HANDLE,
521            1,
522            &pipelineInfo,
523            nullptr,
524            &pipeline);
525
526        vkAPI.vkDestroyPipeline(vkAPI.device, pipeline, nullptr);
527
528        return SLANG_OK;
529    }
530
531    Result createAndValidatePipelineLayout()
532    {
533        // Here we do a little bit of complicated interaction with
534        // the `slang-rhi` library to allow us to call raw Vulkan API
535        // functions on the same device that `slang-rhi` kindly set up
536        // for us.
537        //
538        DeviceNativeHandles handle;
539        SLANG_RETURN_ON_FAIL(_rhiDevice->getNativeDeviceHandles(&handle));
540
541        vkAPI.instance = (VkInstance)handle.handles[0].value;
542        vkAPI.physicalDevice = (VkPhysicalDevice)handle.handles[1].value;
543        vkAPI.device = (VkDevice)handle.handles[2].value;
544
545        vkAPI.initGlobalProcs();
546        vkAPI.initInstanceProcs();
547        vkAPI.initDeviceProcs();
548
549        // Once the setup is dealt with, we can go ahead and
550        // create the pipeline layout, before validating that
551        // it can be used together with the compiled SPIR-V
552        // binary for the program.
553        //
554        VkPipelineLayout pipelineLayout;
555        SLANG_RETURN_ON_FAIL(createPipelineLayout(_slangProgramLayout, &pipelineLayout));
556        SLANG_RETURN_ON_FAIL(validatePipelineLayout(pipelineLayout));
557
558        vkAPI.vkDestroyPipelineLayout(vkAPI.device, pipelineLayout, nullptr);
559
560        return SLANG_OK;
561    }
562
563    VulkanAPI vkAPI;
564};
565
566// More Boilerplate
567// ================
568//
569// The logic below this point is just about setting up the necessary state
570// in the example application for the code above to be run on a simple
571// shader. Nothing here is especially relevant to the task of creating
572// a pipeline layout from Slang reflection information.
573
574struct ReflectionParameterBlocksExampleApp : public TestBase
575{
576    Result execute(int argc, char** argv)
577    {
578        parseOption(argc, argv);
579
580        // We start by initializing the `slang-rhi` system, so that
581        // it can handle most of the details of getting a
582        // Vulkan device up and running.
583
584        DeviceDesc deviceDesc = {};
585        deviceDesc.deviceType = DeviceType::Vulkan;
586
587        ComPtr<IDevice> rhiDevice = getRHI()->createDevice(deviceDesc);
588        if (!rhiDevice)
589            return SLANG_FAIL;
590
591        // The `slang-rhi` library also creates a Slang session as
592        // part of its startup, so we will use the session
593        // it already created for the compilation in
594        // this example.
595        //
596        auto slangSession = rhiDevice->getSlangSession();
597
598        // Next we go through the fairly routine steps needed to
599        // compile a Slang program from source.
600        //
601        ComPtr<slang::IBlob> diagnostics;
602        Result result = SLANG_OK;
603
604        // We load the source file as a module of Slang code.
605        //
606        String sourceFilePath = resourceBase.resolveResource(kSourceFileName);
607        ComPtr<slang::IModule> module;
608        module = slangSession->loadModule(sourceFilePath.getBuffer(), diagnostics.writeRef());
609        diagnoseIfNeeded(diagnostics);
610        if (!module)
611            return SLANG_FAIL;
612
613        // Next we will collect all of the entry points defined in the module,
614        // to form the list of components we want to link together to form
615        // a program.
616        //
617        List<ComPtr<slang::IComponentType>> componentsToLink;
618        int definedEntryPointCount = module->getDefinedEntryPointCount();
619        for (int i = 0; i < definedEntryPointCount; i++)
620        {
621            ComPtr<slang::IEntryPoint> entryPoint;
622            SLANG_RETURN_ON_FAIL(module->getDefinedEntryPoint(i, entryPoint.writeRef()));
623            componentsToLink.add(ComPtr<slang::IComponentType>(entryPoint.get()));
624        }
625
626        // Once we've collected the list of entry points we want to compose,
627        // we use the Slang compilation API to compose them.
628        //
629        ComPtr<slang::IComponentType> composed;
630        result = slangSession->createCompositeComponentType(
631            (slang::IComponentType**)componentsToLink.getBuffer(),
632            componentsToLink.getCount(),
633            composed.writeRef(),
634            diagnostics.writeRef());
635        diagnoseIfNeeded(diagnostics);
636        SLANG_RETURN_ON_FAIL(result);
637
638        // As the final compilation step, we will use the compilation API
639        // to link the composed code. Think of this as equivalent to
640        // applying the linker to a bunch of `.o` and/or `.a` files to
641        // produce a binary (executable or shared library).
642        //
643        ComPtr<slang::IComponentType> program;
644        result = composed->link(program.writeRef(), diagnostics.writeRef());
645        diagnoseIfNeeded(diagnostics);
646        SLANG_RETURN_ON_FAIL(result);
647
648        // Once the program has been compiled succcessfully, we can
649        // go ahead and grab reflection data from the program.
650        //
651        int targetIndex = 0;
652        slang::ProgramLayout* programLayout =
653            program->getLayout(targetIndex, diagnostics.writeRef());
654        diagnoseIfNeeded(diagnostics);
655        if (!programLayout)
656        {
657            return SLANG_FAIL;
658        }
659
660        // The compiled program can also have binary code (either
661        // for individual entry points, or the entire program)
662        // generated for it.
663        //
664        ComPtr<slang::IBlob> programBinary;
665        result = program->getEntryPointCode(0, 0, programBinary.writeRef(), diagnostics.writeRef());
666        diagnoseIfNeeded(diagnostics);
667        if (SLANG_FAILED(result))
668            return result;
669
670        // Finally, once all of the initialization work is dealt with,
671        // we hand control over to the actual logic of the example.
672        //
673        PipelineLayoutReflectionContext_Vulkan context;
674
675        context._rhiDevice = rhiDevice;
676        context._slangSession = slangSession;
677        context._slangProgramLayout = programLayout;
678        context._slangCompiledProgramBlob = programBinary;
679
680        SLANG_RETURN_ON_FAIL(context.createAndValidatePipelineLayout());
681
682        return SLANG_OK;
683    }
684};
685
686int main(int argc, char* argv[])
687{
688    ReflectionParameterBlocksExampleApp app;
689    if (SLANG_FAILED(app.execute(argc, argv)))
690    {
691        return -1;
692    }
693    return 0;
694}