yum-mirror/slang

Making it easier to work with shaders

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

Theresa FoleyFix a bug in examples/reflection-api (#7652)e52885347

master
42.1 KiB1502 linesraw
1// main.cpp
2
3// Reflection API Example Program
4// ==============================
5//
6// This file provides the application code for the `reflection-api` example.
7// This example uses the Slang reflection API to travserse the structure
8// of the parameters of a Slang program and their types.
9//
10// This program is a companion Slang reflection API documentation:
11// https://shader-slang.org/slang/user-guide/compiling.html
12//
13// Boilerplate
14// -----------
15//
16// The following lines are boilerplate common to set up this example
17// to use the infrastructure for example programs in the Slang
18// repository.
19//
20
21#include "slang-com-ptr.h"
22#include "slang.h"
23typedef SlangResult Result;
24
25#include "core/slang-basic.h"
26#include "examples/example-base/example-base.h"
27using Slang::ComPtr;
28using Slang::String;
29using Slang::List;
30
31static const ExampleResources resourceBase("reflection-api");
32
33// Configuration
34// -------------
35//
36// For simplicity, this example uses a hard-coded list of shader programs
37// to compile, each represented as the name of a `.slang` file, along with
38// a hard-coded list of targets to compile and reflect the programs for.
39//
40
41static const char* kSourceFileNames[] = {
42    "raster-simple.slang",
43    "compute-simple.slang",
44};
45
46static const struct
47{
48    SlangCompileTarget format;
49    const char* profile;
50} kTargets[] = {
51    {SLANG_DXIL, "sm_6_0"},
52    {SLANG_SPIRV, "sm_6_0"},
53};
54static const int kTargetCount = SLANG_COUNT_OF(kTargets);
55
56// The `ReflectingPrinting` Type
57// -------------------------
58//
59// We wrap most of the code for this example in a `struct`
60// type, in order to provide a bit more freedom in order
61// of declaration.
62//
63// When possible, we will follow the order of declarations
64// in the accompanying document, to help readers who want
65// to following along in the code while reading.
66//
67struct ReflectingPrinting
68{
69    // Scoping things in a type allows us to declare functions
70    // out of order more easily, but we still have to forward-declare
71    // types when they will be used before they are declared.
72    //
73    struct AccessPath;
74
75    // Output Formatting
76    // -----------------
77    //
78    // This example program outputs reflection information in a format
79    // that is (or at least is intended to be) compatible with YAML.
80    //
81    // We do not want the code to be overly complicated with issues
82    // around formatting, so the details of the actual printing logic
83    // are largely left until later. However, there are a pair of
84    // macros that help to keep things tidy that we need to introduce
85    // here, before they are used.
86    //
87#define WITH_ARRAY() for (int _i = (beginArray(), 1); _i; _i = (endArray(), 0))
88
89#define SCOPED_OBJECT() ScopedObject scopedObject##__COUNTER__(this)
90
91    // Compiling a Program
92    // -------------------
93    //
94    Result compileAndReflectProgram(slang::ISession* session, const char* sourceFileName)
95    {
96        SCOPED_OBJECT();
97        printComment("program");
98
99        key("file name");
100        printQuotedString(sourceFileName);
101        String sourceFilePath = resourceBase.resolveResource(sourceFileName);
102
103        ComPtr<slang::IBlob> diagnostics;
104        Result result = SLANG_OK;
105
106        // ### Loading a Module
107        //
108
109        ComPtr<slang::IModule> module;
110        module = session->loadModule(sourceFilePath.getBuffer(), diagnostics.writeRef());
111        diagnoseIfNeeded(diagnostics);
112        if (!module)
113            return SLANG_FAIL;
114
115        List<ComPtr<slang::IComponentType>> componentsToLink;
116
117        // ### Variable decls
118        //
119        key("global constants");
120        WITH_ARRAY()
121        for (auto decl : module->getModuleReflection()->getChildren())
122        {
123            if (auto varDecl = decl->asVariable(); varDecl &&
124                                                   varDecl->findModifier(slang::Modifier::Const) &&
125                                                   varDecl->findModifier(slang::Modifier::Static))
126            {
127                element();
128                printVariable(varDecl);
129            }
130        }
131
132        // ### Finding Entry Points
133        //
134
135        key("defined entry points");
136        int definedEntryPointCount = module->getDefinedEntryPointCount();
137        WITH_ARRAY()
138        for (int i = 0; i < definedEntryPointCount; i++)
139        {
140            ComPtr<slang::IEntryPoint> entryPoint;
141            SLANG_RETURN_ON_FAIL(module->getDefinedEntryPoint(i, entryPoint.writeRef()));
142
143            element();
144            SCOPED_OBJECT();
145            key("name");
146            printQuotedString(entryPoint->getFunctionReflection()->getName());
147
148            componentsToLink.add(ComPtr<slang::IComponentType>(entryPoint.get()));
149        }
150
151        // ### Composing and Linking
152        //
153
154        ComPtr<slang::IComponentType> composed;
155        result = session->createCompositeComponentType(
156            (slang::IComponentType**)componentsToLink.getBuffer(),
157            componentsToLink.getCount(),
158            composed.writeRef(),
159            diagnostics.writeRef());
160        diagnoseIfNeeded(diagnostics);
161        SLANG_RETURN_ON_FAIL(result);
162
163        ComPtr<slang::IComponentType> program;
164        result = composed->link(program.writeRef(), diagnostics.writeRef());
165        diagnoseIfNeeded(diagnostics);
166        SLANG_RETURN_ON_FAIL(result);
167
168        key("layouts");
169        WITH_ARRAY()
170        for (int targetIndex = 0; targetIndex < kTargetCount; ++targetIndex)
171        {
172            element();
173
174            // ### Getting the Program Layout
175            //
176            slang::ProgramLayout* programLayout =
177                program->getLayout(targetIndex, diagnostics.writeRef());
178            diagnoseIfNeeded(diagnostics);
179            if (!programLayout)
180            {
181                result = SLANG_FAIL;
182                continue;
183            }
184
185            SLANG_RETURN_ON_FAIL(
186                collectEntryPointMetadata(program, targetIndex, definedEntryPointCount));
187
188            _programLayout = programLayout;
189            auto targetFormat = kTargets[targetIndex].format;
190            printProgramLayout(programLayout, targetFormat);
191        }
192
193        return result;
194    }
195    slang::ProgramLayout* _programLayout = nullptr;
196
197    Result compileAndReflectPrograms(slang::ISession* session)
198    {
199        Result result = SLANG_OK;
200
201        WITH_ARRAY()
202        for (auto fileName : kSourceFileNames)
203        {
204            element();
205            auto programResult = compileAndReflectProgram(session, fileName);
206            if (SLANG_FAILED(programResult))
207            {
208                result = programResult;
209            }
210        }
211
212        return result;
213    }
214
215    // Types and Variables
216    // -------------------
217    //
218    // ### Variables
219    //
220    void printVariable(slang::VariableReflection* variable)
221    {
222        SCOPED_OBJECT();
223
224        const char* name = variable->getName();
225        slang::TypeReflection* type = variable->getType();
226
227        key("name");
228        printQuotedString(name);
229        key("type");
230        printType(type);
231
232        int64_t value;
233        if (SLANG_SUCCEEDED(variable->getDefaultValueInt(&value)))
234        {
235            key("value");
236            printf("%" PRId64, value);
237        }
238    }
239
240    // ### Types
241    //
242    void printType(slang::TypeReflection* type)
243    {
244        SCOPED_OBJECT();
245
246        const char* name = type->getName();
247        slang::TypeReflection::Kind kind = type->getKind();
248
249        key("name");
250        printQuotedString(name);
251        key("kind");
252        printTypeKind(kind);
253
254        // There is information that we would like to
255        // print for both types and type layouts, so
256        // we will factor the common logic into a
257        // subroutine so that we can share the code.
258        //
259        printCommonTypeInfo(type);
260
261        switch (type->getKind())
262        {
263        default:
264            break;
265
266        // #### Structure Types
267        //
268        case slang::TypeReflection::Kind::Struct:
269            {
270                key("fields");
271                int fieldCount = type->getFieldCount();
272
273                WITH_ARRAY();
274                for (int f = 0; f < fieldCount; f++)
275                {
276                    element();
277                    auto field = type->getFieldByIndex(f);
278
279                    printVariable(field);
280                }
281            }
282            break;
283
284        // #### Array Types
285        // #### Vector Types
286        // #### Matrix Types
287        //
288        case slang::TypeReflection::Kind::Array:
289        case slang::TypeReflection::Kind::Vector:
290        case slang::TypeReflection::Kind::Matrix:
291            {
292                key("element type");
293                printType(type->getElementType());
294            }
295            break;
296
297        // #### Resource Types
298        //
299        case slang::TypeReflection::Kind::Resource:
300            {
301                key("result type");
302                printType(type->getResourceResultType());
303            }
304            break;
305
306        // #### Single-Element Container Types
307        //
308        case slang::TypeReflection::Kind::ConstantBuffer:
309        case slang::TypeReflection::Kind::ParameterBlock:
310        case slang::TypeReflection::Kind::TextureBuffer:
311        case slang::TypeReflection::Kind::ShaderStorageBuffer:
312            {
313                key("element type");
314                printType(type->getElementType());
315            }
316            break;
317        }
318    }
319
320    // #### Array Types
321    //
322    void printPossiblyUnbounded(size_t value)
323    {
324        if (value == ~size_t(0))
325        {
326            printf("unbounded");
327        }
328        else
329        {
330            printf("%u", unsigned(value));
331        }
332    }
333
334    void printCommonTypeInfo(slang::TypeReflection* type)
335    {
336        switch (type->getKind())
337        {
338        // #### Scalar Types
339        //
340        case slang::TypeReflection::Kind::Scalar:
341            {
342                key("scalar type");
343                printScalarType(type->getScalarType());
344            }
345            break;
346
347        // #### Array Types
348        //
349        case slang::TypeReflection::Kind::Array:
350            {
351                key("element count");
352                printPossiblyUnbounded(type->getElementCount());
353            }
354            break;
355
356        // #### Vector Types
357        //
358        case slang::TypeReflection::Kind::Vector:
359            {
360                key("element count");
361                print(type->getElementCount());
362            }
363            break;
364
365        // #### Matrix Types
366        //
367        case slang::TypeReflection::Kind::Matrix:
368            {
369                key("row count");
370                print(type->getRowCount());
371
372                key("column count");
373                print(type->getColumnCount());
374            }
375            break;
376
377        // #### Resource Types
378        //
379        case slang::TypeReflection::Kind::Resource:
380            {
381                key("shape");
382                printResourceShape(type->getResourceShape());
383
384                key("access");
385                printResourceAccess(type->getResourceAccess());
386            }
387            break;
388
389        default:
390            break;
391        }
392    }
393
394    // Layout for Types and Variables
395    // ------------------------------
396    //
397    // ### Variable Layouts
398    //
399    void printVariableLayout(slang::VariableLayoutReflection* variableLayout, AccessPath accessPath)
400    {
401        SCOPED_OBJECT();
402
403        key("name");
404        printQuotedString(variableLayout->getName());
405
406        printOffsets(variableLayout, accessPath);
407
408        printVaryingParameterInfo(variableLayout);
409
410        ExtendedAccessPath variablePath(accessPath, variableLayout);
411
412        key("type layout");
413        printTypeLayout(variableLayout->getTypeLayout(), variablePath);
414    }
415
416    // #### Offsets
417
418    void printRelativeOffsets(slang::VariableLayoutReflection* variableLayout)
419    {
420        key("relative");
421        int usedLayoutUnitCount = variableLayout->getCategoryCount();
422        WITH_ARRAY();
423        for (int i = 0; i < usedLayoutUnitCount; ++i)
424        {
425            element();
426
427            auto layoutUnit = variableLayout->getCategoryByIndex(i);
428            printOffset(variableLayout, layoutUnit);
429        }
430    }
431
432    void printOffset(
433        slang::VariableLayoutReflection* variableLayout,
434        slang::ParameterCategory layoutUnit)
435    {
436        printOffset(
437            layoutUnit,
438            variableLayout->getOffset(layoutUnit),
439            variableLayout->getBindingSpace(layoutUnit));
440    }
441
442    void printOffset(slang::ParameterCategory layoutUnit, size_t offset, size_t spaceOffset)
443    {
444        SCOPED_OBJECT();
445
446        key("value");
447        print(offset);
448        key("unit");
449        printLayoutUnit(layoutUnit);
450
451        // #### Spaces / Sets
452
453        switch (layoutUnit)
454        {
455        default:
456            break;
457
458        case slang::ParameterCategory::ConstantBuffer:
459        case slang::ParameterCategory::ShaderResource:
460        case slang::ParameterCategory::UnorderedAccess:
461        case slang::ParameterCategory::SamplerState:
462        case slang::ParameterCategory::DescriptorTableSlot:
463            key("space");
464            print(spaceOffset);
465            break;
466        }
467    }
468
469    // ### Type Layouts
470    //
471    void printTypeLayout(slang::TypeLayoutReflection* typeLayout, AccessPath accessPath)
472    {
473        SCOPED_OBJECT();
474
475        key("name");
476        printQuotedString(typeLayout->getName());
477        key("kind");
478        printTypeKind(typeLayout->getKind());
479        printCommonTypeInfo(typeLayout->getType());
480
481        printSizes(typeLayout);
482
483        printKindSpecificInfo(typeLayout, accessPath);
484    }
485
486    // #### Size
487    //
488    void printSizes(slang::TypeLayoutReflection* typeLayout)
489    {
490        key("size");
491
492        int usedLayoutUnitCount = typeLayout->getCategoryCount();
493        WITH_ARRAY()
494        for (int i = 0; i < usedLayoutUnitCount; ++i)
495        {
496            element();
497
498            auto layoutUnit = typeLayout->getCategoryByIndex(i);
499            printSize(typeLayout, layoutUnit);
500        }
501
502        // #### Alignment and Stride
503        if (typeLayout->getSize() != 0)
504        {
505            key("alignment in bytes");
506            print(typeLayout->getAlignment());
507
508            key("stride in bytes");
509            print(typeLayout->getStride());
510        }
511    }
512
513    void printSize(slang::TypeLayoutReflection* typeLayout, slang::ParameterCategory layoutUnit)
514    {
515        printSize(layoutUnit, typeLayout->getSize(layoutUnit));
516    }
517
518    void printSize(slang::ParameterCategory layoutUnit, size_t size)
519    {
520        SCOPED_OBJECT();
521
522        key("value");
523        printPossiblyUnbounded(size);
524        key("unit");
525        printLayoutUnit(layoutUnit);
526    }
527
528    // #### Kind-Specific Information
529    //
530    void printKindSpecificInfo(slang::TypeLayoutReflection* typeLayout, AccessPath accessPath)
531    {
532        switch (typeLayout->getKind())
533        {
534        // #### Structure Type Layouts
535        //
536        case slang::TypeReflection::Kind::Struct:
537            {
538                key("fields");
539
540                int fieldCount = typeLayout->getFieldCount();
541                WITH_ARRAY()
542                for (int f = 0; f < fieldCount; f++)
543                {
544                    element();
545
546                    auto field = typeLayout->getFieldByIndex(f);
547                    printVariableLayout(field, accessPath);
548                }
549            }
550            break;
551
552        // #### Array Type Layouts
553        //
554        case slang::TypeReflection::Kind::Array:
555            {
556                key("element type layout");
557                printTypeLayout(typeLayout->getElementTypeLayout(), AccessPath());
558            }
559            break;
560
561        // #### Matrix Type Layouts
562        //
563        case slang::TypeReflection::Kind::Matrix:
564            {
565                key("matrix layout mode");
566                printMatrixLayoutMode(typeLayout->getMatrixLayoutMode());
567
568                key("element type layout");
569                printTypeLayout(typeLayout->getElementTypeLayout(), AccessPath());
570            }
571            break;
572
573        case slang::TypeReflection::Kind::Vector:
574            {
575                key("element type layout");
576                printTypeLayout(typeLayout->getElementTypeLayout(), AccessPath());
577            }
578            break;
579
580        // #### Single-Element Containers
581        //
582        case slang::TypeReflection::Kind::ConstantBuffer:
583        case slang::TypeReflection::Kind::ParameterBlock:
584        case slang::TypeReflection::Kind::TextureBuffer:
585        case slang::TypeReflection::Kind::ShaderStorageBuffer:
586            {
587                auto containerVarLayout = typeLayout->getContainerVarLayout();
588                auto elementVarLayout = typeLayout->getElementVarLayout();
589
590                AccessPath innerOffsets = accessPath;
591                innerOffsets.deepestConstantBufer = innerOffsets.leaf;
592                if (containerVarLayout->getTypeLayout()->getSize(
593                        slang::ParameterCategory::SubElementRegisterSpace) != 0)
594                {
595                    innerOffsets.deepestParameterBlock = innerOffsets.leaf;
596                }
597
598                key("container");
599                {
600                    SCOPED_OBJECT();
601                    printOffsets(containerVarLayout, innerOffsets);
602                }
603
604                key("content");
605                {
606                    SCOPED_OBJECT();
607
608                    printOffsets(elementVarLayout, innerOffsets);
609
610                    ExtendedAccessPath elementOffsets(innerOffsets, elementVarLayout);
611
612                    key("type layout");
613                    printTypeLayout(elementVarLayout->getTypeLayout(), elementOffsets);
614                }
615            }
616            break;
617
618        case slang::TypeReflection::Kind::Resource:
619            {
620                if ((typeLayout->getResourceShape() & SLANG_RESOURCE_BASE_SHAPE_MASK) ==
621                    SLANG_STRUCTURED_BUFFER)
622                {
623                    key("element type layout");
624                    printTypeLayout(typeLayout->getElementTypeLayout(), accessPath);
625                }
626                else
627                {
628                    key("result type");
629                    printType(typeLayout->getResourceResultType());
630                }
631            }
632            break;
633
634        default:
635            break;
636        }
637    }
638
639    // Programs and Scopes
640    // -------------------
641    //
642    void printProgramLayout(slang::ProgramLayout* programLayout, SlangCompileTarget targetFormat)
643    {
644        SCOPED_OBJECT();
645
646        key("target");
647        printTargetFormat(targetFormat);
648
649        AccessPath rootOffsets;
650        rootOffsets.valid = true;
651
652        key("global scope");
653        {
654            SCOPED_OBJECT();
655            printScope(programLayout->getGlobalParamsVarLayout(), rootOffsets);
656        }
657
658        key("entry points");
659        int entryPointCount = programLayout->getEntryPointCount();
660        WITH_ARRAY()
661        for (int i = 0; i < entryPointCount; ++i)
662        {
663            element();
664            printEntryPointLayout(programLayout->getEntryPointByIndex(i), rootOffsets);
665        }
666    }
667
668    // ### Global Scope
669    //
670    void printScope(slang::VariableLayoutReflection* scopeVarLayout, AccessPath accessPath)
671    {
672        ExtendedAccessPath scopeOffsets(accessPath, scopeVarLayout);
673
674        auto scopeTypeLayout = scopeVarLayout->getTypeLayout();
675        switch (scopeTypeLayout->getKind())
676        {
677        // #### Parameters are Grouped Into a Structure
678        //
679        case slang::TypeReflection::Kind::Struct:
680            {
681                key("parameters");
682
683                int paramCount = scopeTypeLayout->getFieldCount();
684                for (int i = 0; i < paramCount; i++)
685                {
686                    element();
687
688                    auto param = scopeTypeLayout->getFieldByIndex(i);
689
690                    printVariableLayout(param, scopeOffsets);
691                }
692            }
693            break;
694
695        // #### Wrapped in a Constant Buffer If Needed
696        //
697        case slang::TypeReflection::Kind::ConstantBuffer:
698            key("automatically-introduced constant buffer");
699            {
700                SCOPED_OBJECT();
701                printOffsets(scopeTypeLayout->getContainerVarLayout(), scopeOffsets);
702            }
703
704            printScope(scopeTypeLayout->getElementVarLayout(), scopeOffsets);
705            break;
706
707        // #### Wrapped in a Parameter Block If Needed
708        //
709        case slang::TypeReflection::Kind::ParameterBlock:
710            key("automatically-introduced parameter block");
711            {
712                SCOPED_OBJECT();
713                printOffsets(scopeTypeLayout->getContainerVarLayout(), scopeOffsets);
714            }
715
716            printScope(scopeTypeLayout->getElementVarLayout(), scopeOffsets);
717            break;
718
719        default:
720            // Note that this default case is never expected to
721            // arise with the current Slang compiler and reflection
722            // API, but we include it here as a kind of failsafe.
723            //
724            key("variable layout");
725            printVariableLayout(scopeVarLayout, accessPath);
726            break;
727        }
728    }
729
730    // ### Entry Points
731    //
732    void printEntryPointLayout(slang::EntryPointReflection* entryPointLayout, AccessPath accessPath)
733    {
734        SCOPED_OBJECT();
735
736        key("stage");
737        printStage(entryPointLayout->getStage());
738
739        printStageSpecificInfo(entryPointLayout);
740
741        printScope(entryPointLayout->getVarLayout(), accessPath);
742
743        auto resultVariableLayout = entryPointLayout->getResultVarLayout();
744        if (resultVariableLayout->getTypeLayout()->getKind() != slang::TypeReflection::Kind::None)
745        {
746            key("result");
747            printVariableLayout(resultVariableLayout, accessPath);
748        }
749    }
750
751    // #### Stage-Specific Information
752    //
753    void printStageSpecificInfo(slang::EntryPointReflection* entryPointLayout)
754    {
755        switch (entryPointLayout->getStage())
756        {
757        default:
758            break;
759
760        case SLANG_STAGE_COMPUTE:
761            {
762                static const int kAxisCount = 3;
763                SlangUInt sizes[kAxisCount];
764                entryPointLayout->getComputeThreadGroupSize(kAxisCount, sizes);
765
766                key("thread group size");
767                SCOPED_OBJECT();
768                key("x");
769                print(sizes[0]);
770                key("y");
771                print(sizes[1]);
772                key("z");
773                print(sizes[2]);
774            }
775            break;
776
777        case SLANG_STAGE_FRAGMENT:
778            key("uses any sample-rate inputs");
779            printBool(entryPointLayout->usesAnySampleRateInput());
780            break;
781        }
782    }
783
784    // #### Varying Parameters
785    //
786    void printVaryingParameterInfo(slang::VariableLayoutReflection* variableLayout)
787    {
788        if (auto semanticName = variableLayout->getSemanticName())
789        {
790            key("semantic");
791            SCOPED_OBJECT();
792            key("name");
793            printQuotedString(semanticName);
794            key("index");
795            print(variableLayout->getSemanticIndex());
796        }
797    }
798
799    // Calculating Cumulative Offsets
800    // ------------------------------
801    //
802    struct CumulativeOffset
803    {
804        size_t value = 0;
805        size_t space = 0;
806    };
807
808    // ### Access Paths
809
810    struct AccessPathNode
811    {
812        slang::VariableLayoutReflection* variableLayout = nullptr;
813        AccessPathNode* outer = nullptr;
814    };
815
816    struct AccessPath
817    {
818        AccessPath() {}
819
820        bool valid = false;
821        AccessPathNode* deepestConstantBufer = nullptr;
822        AccessPathNode* deepestParameterBlock = nullptr;
823        AccessPathNode* leaf = nullptr;
824    };
825
826    void printCumulativeOffsets(
827        slang::VariableLayoutReflection* variableLayout,
828        AccessPath accessPath)
829    {
830        key("cumulative");
831
832        int usedLayoutUnitCount = variableLayout->getCategoryCount();
833        WITH_ARRAY();
834        for (int i = 0; i < usedLayoutUnitCount; ++i)
835        {
836            element();
837
838            auto layoutUnit = variableLayout->getCategoryByIndex(i);
839            printCumulativeOffset(variableLayout, layoutUnit, accessPath);
840        }
841    }
842
843    CumulativeOffset calculateCumulativeOffset(
844        slang::VariableLayoutReflection* variableLayout,
845        slang::ParameterCategory layoutUnit,
846        AccessPath accessPath)
847    {
848        CumulativeOffset result = calculateCumulativeOffset(layoutUnit, accessPath);
849        result.value += variableLayout->getOffset(layoutUnit);
850        result.space += variableLayout->getBindingSpace(layoutUnit);
851        return result;
852    }
853
854    void printCumulativeOffset(
855        slang::VariableLayoutReflection* variableLayout,
856        slang::ParameterCategory layoutUnit,
857        AccessPath accessPath)
858    {
859        CumulativeOffset cumulativeOffset =
860            calculateCumulativeOffset(variableLayout, layoutUnit, accessPath);
861
862        printOffset(layoutUnit, cumulativeOffset.value, cumulativeOffset.space);
863    }
864
865    // ### Tracking Access Paths
866
867    struct ExtendedAccessPath : AccessPath
868    {
869        ExtendedAccessPath(AccessPath const& base, slang::VariableLayoutReflection* variableLayout)
870            : AccessPath(base)
871        {
872            if (!valid)
873                return;
874
875            element.variableLayout = variableLayout;
876            element.outer = leaf;
877
878            leaf = &element;
879        }
880
881        AccessPathNode element;
882    };
883
884    // ### Accumulating Offsets Along An Access Path
885
886    CumulativeOffset calculateCumulativeOffset(
887        slang::ParameterCategory layoutUnit,
888        AccessPath accessPath)
889    {
890        CumulativeOffset result;
891        switch (layoutUnit)
892        {
893        // #### Layout Units That Don't Require Special Handling
894        //
895        default:
896            for (auto node = accessPath.leaf; node != nullptr; node = node->outer)
897            {
898                result.value += node->variableLayout->getOffset(layoutUnit);
899            }
900            break;
901
902        // #### Bytes
903        //
904        case slang::ParameterCategory::Uniform:
905            for (auto node = accessPath.leaf; node != accessPath.deepestConstantBufer;
906                 node = node->outer)
907            {
908                result.value += node->variableLayout->getOffset(layoutUnit);
909            }
910            break;
911
912        // #### Layout Units That Care About Spaces
913        //
914        case slang::ParameterCategory::ConstantBuffer:
915        case slang::ParameterCategory::ShaderResource:
916        case slang::ParameterCategory::UnorderedAccess:
917        case slang::ParameterCategory::SamplerState:
918        case slang::ParameterCategory::DescriptorTableSlot:
919            for (auto node = accessPath.leaf; node != accessPath.deepestParameterBlock;
920                 node = node->outer)
921            {
922                result.value += node->variableLayout->getOffset(layoutUnit);
923                result.space += node->variableLayout->getBindingSpace(layoutUnit);
924            }
925            for (auto node = accessPath.deepestParameterBlock; node != nullptr; node = node->outer)
926            {
927                result.space += node->variableLayout->getOffset(
928                    slang::ParameterCategory::SubElementRegisterSpace);
929            }
930            break;
931        }
932        return result;
933    }
934
935    // Determining Whether Parameters Are Used
936    // ---------------------------------------
937
938    Result collectEntryPointMetadata(
939        slang::IComponentType* program,
940        int targetIndex,
941        int entryPointCount)
942    {
943        _metadataForEntryPoints.setCount(entryPointCount);
944        for (int entryPointIndex = 0; entryPointIndex < entryPointCount; entryPointIndex++)
945        {
946            ComPtr<slang::IMetadata> entryPointMetadata;
947            ComPtr<slang::IBlob> diagnostics;
948            SLANG_RETURN_ON_FAIL(program->getEntryPointMetadata(
949                entryPointIndex,
950                targetIndex,
951                entryPointMetadata.writeRef(),
952                diagnostics.writeRef()));
953            diagnoseIfNeeded(diagnostics);
954
955            _metadataForEntryPoints[entryPointIndex] = entryPointMetadata;
956        }
957        return SLANG_OK;
958    }
959    Slang::List<ComPtr<slang::IMetadata>> _metadataForEntryPoints;
960
961    typedef unsigned int StageMask;
962
963    StageMask calculateParameterStageMask(
964        slang::ParameterCategory layoutUnit,
965        CumulativeOffset offset)
966    {
967        unsigned mask = 0;
968        auto entryPointCount = _metadataForEntryPoints.getCount();
969        for (int i = 0; i < entryPointCount; ++i)
970        {
971            bool isUsed = false;
972            _metadataForEntryPoints[i]->isParameterLocationUsed(
973                SlangParameterCategory(layoutUnit),
974                offset.space,
975                offset.value,
976                isUsed);
977            if (isUsed)
978            {
979                auto entryPointStage = _programLayout->getEntryPointByIndex(i)->getStage();
980
981                mask |= 1 << unsigned(entryPointStage);
982            }
983        }
984        return mask;
985    }
986
987    StageMask calculateStageMask(
988        slang::VariableLayoutReflection* variableLayout,
989        AccessPath accessPath)
990    {
991        StageMask mask = 0;
992
993        int usedLayoutUnitCount = variableLayout->getCategoryCount();
994        for (int i = 0; i < usedLayoutUnitCount; ++i)
995        {
996            auto layoutUnit = variableLayout->getCategoryByIndex(i);
997            auto offset = calculateCumulativeOffset(variableLayout, layoutUnit, accessPath);
998
999            mask |= calculateParameterStageMask(layoutUnit, offset);
1000        }
1001
1002        return mask;
1003    }
1004
1005    void printStageUsage(slang::VariableLayoutReflection* variableLayout, AccessPath accessPath)
1006    {
1007        StageMask stageMask = calculateStageMask(variableLayout, accessPath);
1008
1009        key("used by stages");
1010        WITH_ARRAY()
1011        for (int i = 0; i < SLANG_STAGE_COUNT; i++)
1012        {
1013            if (stageMask & (1 << i))
1014            {
1015                element();
1016                printStage(SlangStage(i));
1017            }
1018        }
1019    }
1020
1021    void printOffsets(slang::VariableLayoutReflection* variableLayout, AccessPath accessPath)
1022    {
1023        key("offset");
1024        {
1025            SCOPED_OBJECT();
1026            printRelativeOffsets(variableLayout);
1027
1028            if (accessPath.valid)
1029            {
1030                printCumulativeOffsets(variableLayout, accessPath);
1031            }
1032        }
1033
1034
1035        if (accessPath.valid)
1036        {
1037            printStageUsage(variableLayout, accessPath);
1038        }
1039    }
1040
1041    // Formatting
1042    // ----------
1043    //
1044    // Here we'll cover the logic for how we implement
1045    // the various formatting operations used in the
1046    // code above.
1047    //
1048    // ### Indentation
1049    //
1050    // We track a global indentation level, and whenever
1051    // we begin a new line, we'll emit a corresponding
1052    // amount of space (two spaces per indent, consistent
1053    // with typical YAML formatting).
1054
1055    int indentation = 0;
1056
1057    void printIndentation()
1058    {
1059        for (int i = 1; i < indentation; ++i)
1060        {
1061            printf("  ");
1062        }
1063    }
1064
1065    // ### Objects and Arrays
1066    //
1067    // Both objects and arrays can be marked up purely
1068    // with indentation in YAML. If we eventually
1069    // change the output format to something like JSON,
1070    // these operations would need to do more actual
1071    // work.
1072
1073    void beginObject() { indentation++; }
1074
1075    void endObject() { indentation--; }
1076
1077    void beginArray() { indentation++; }
1078
1079    void endArray() { indentation--; }
1080
1081    // #### Scope-Based Objects
1082    //
1083    // In order to make it easier to keep the `beginObject()`
1084    // and `endObject()` calls properly paired, we introduce
1085    // a helper type that uses an RAII idiom to automatically
1086    // pair up the calls.
1087    //
1088    struct ScopedObject
1089    {
1090        ScopedObject(ReflectingPrinting* outer)
1091            : outer(outer)
1092        {
1093            outer->beginObject();
1094        }
1095
1096        ~ScopedObject() { outer->endObject(); }
1097
1098        ReflectingPrinting* outer = nullptr;
1099    };
1100
1101    // ### Starting New Lines
1102    //
1103    // Typically, when we are about to emit a key
1104    // in an object, or an element in an array,
1105    // we need to start a new line (and print
1106    // the appropriate indentation).
1107    //
1108    void newLine()
1109    {
1110        printf("\n");
1111        printIndentation();
1112    }
1113
1114
1115    // The main exception is that if we've just
1116    // emitted the `- ` for an array element then
1117    // we don't need to start a new line if
1118    // the next thing we emit is an object key.
1119    //
1120    // We *also* don't need to start a new line
1121    // at the very beginning of the output, so
1122    // we handle that by setting the intial state
1123    // *as if* we have just started an array element.
1124
1125    bool afterArrayElement = true;
1126
1127    // ### Array Elements
1128    //
1129    void element()
1130    {
1131        newLine();
1132        printf("- ");
1133        afterArrayElement = true;
1134    }
1135
1136    // ### Object Keys
1137    //
1138    void key(char const* key)
1139    {
1140        if (!afterArrayElement)
1141        {
1142            newLine();
1143        }
1144        afterArrayElement = false;
1145
1146        printf("%s: ", key);
1147    }
1148
1149    // ### Printing Simple Values
1150    //
1151    // Simple scalar values like strings,
1152    // `bool`s, and numbers don't need
1153    // much special handling.
1154
1155    void printQuotedString(char const* text)
1156    {
1157        if (text)
1158        {
1159            printf("\"%s\"", text);
1160        }
1161        else
1162        {
1163            printf("null");
1164        }
1165    }
1166
1167    void printBool(bool value) { printf(value ? "true" : "false"); }
1168
1169    void print(size_t value) { printf("%u", unsigned(value)); }
1170
1171    // YAML supports comments, but JSON doesn't.
1172    // This function could be stubbed out if
1173    // we switch up the output format.
1174    //
1175    void printComment(char const* text) { printf("# %s", text); }
1176
1177
1178    // Printing Enumerants
1179    // -------------------
1180    //
1181    // Here we'll gather all the logic for printing the various
1182    // `enum` types that we've worked with in the logic above.
1183
1184    void printTypeKind(slang::TypeReflection::Kind kind)
1185    {
1186        switch (kind)
1187        {
1188#define CASE(TAG)                          \
1189    case slang::TypeReflection::Kind::TAG: \
1190        printf("%s", #TAG);                \
1191        break
1192
1193            CASE(None);
1194            CASE(Struct);
1195            CASE(Array);
1196            CASE(Matrix);
1197            CASE(Vector);
1198            CASE(Scalar);
1199            CASE(ConstantBuffer);
1200            CASE(Resource);
1201            CASE(SamplerState);
1202            CASE(TextureBuffer);
1203            CASE(ShaderStorageBuffer);
1204            CASE(ParameterBlock);
1205            CASE(GenericTypeParameter);
1206            CASE(Interface);
1207            CASE(OutputStream);
1208            CASE(Specialized);
1209            CASE(Feedback);
1210            CASE(Pointer);
1211            CASE(DynamicResource);
1212#undef CASE
1213
1214        default:
1215            printf("%d # unexpected enumerant", int(kind));
1216            break;
1217        }
1218    }
1219
1220    void printResourceShape(SlangResourceShape shape)
1221    {
1222        SCOPED_OBJECT();
1223
1224        key("base");
1225        auto baseShape = shape & SLANG_RESOURCE_BASE_SHAPE_MASK;
1226        switch (baseShape)
1227        {
1228#define CASE(TAG)           \
1229    case SLANG_##TAG:       \
1230        printf("%s", #TAG); \
1231        break
1232
1233            CASE(TEXTURE_1D);
1234            CASE(TEXTURE_2D);
1235            CASE(TEXTURE_3D);
1236            CASE(TEXTURE_CUBE);
1237            CASE(TEXTURE_BUFFER);
1238            CASE(STRUCTURED_BUFFER);
1239            CASE(BYTE_ADDRESS_BUFFER);
1240            CASE(RESOURCE_UNKNOWN);
1241            CASE(ACCELERATION_STRUCTURE);
1242            CASE(TEXTURE_SUBPASS);
1243#undef CASE
1244
1245        default:
1246            printf("%d # unexpected enumerant", int(baseShape));
1247            break;
1248        }
1249
1250#define CASE(TAG)                               \
1251    do                                          \
1252    {                                           \
1253        if (shape & SLANG_TEXTURE_##TAG##_FLAG) \
1254        {                                       \
1255            key(#TAG);                          \
1256            printf("true");                     \
1257        }                                       \
1258    } while (0)
1259
1260        CASE(FEEDBACK);
1261        CASE(SHADOW);
1262        CASE(ARRAY);
1263        CASE(MULTISAMPLE);
1264#undef CASE
1265    }
1266
1267    void printResourceAccess(SlangResourceAccess access)
1268    {
1269        switch (access)
1270        {
1271#define CASE(TAG)                     \
1272    case SLANG_RESOURCE_ACCESS_##TAG: \
1273        printf("%s", #TAG);           \
1274        break
1275
1276            CASE(NONE);
1277            CASE(READ);
1278            CASE(READ_WRITE);
1279            CASE(RASTER_ORDERED);
1280            CASE(APPEND);
1281            CASE(CONSUME);
1282            CASE(WRITE);
1283            CASE(FEEDBACK);
1284#undef CASE
1285
1286        default:
1287            printf("%d # unexpected enumerant", int(access));
1288            break;
1289        }
1290    }
1291
1292    void printLayoutUnit(slang::ParameterCategory layoutUnit)
1293    {
1294        switch (layoutUnit)
1295        {
1296#define CASE(TAG, DESCRIPTION)                \
1297    case slang::ParameterCategory::TAG:       \
1298        printf("%s # %s", #TAG, DESCRIPTION); \
1299        break
1300
1301            CASE(ConstantBuffer, "constant buffer slots");
1302            CASE(ShaderResource, "texture slots");
1303            CASE(UnorderedAccess, "uav slots");
1304            CASE(VaryingInput, "varying input slots");
1305            CASE(VaryingOutput, "varying output slots");
1306            CASE(SamplerState, "sampler slots");
1307            CASE(Uniform, "bytes");
1308            CASE(DescriptorTableSlot, "bindings");
1309            CASE(SpecializationConstant, "specialization constant ids");
1310            CASE(PushConstantBuffer, "push-constant buffers");
1311            CASE(RegisterSpace, "register space offset for a variable");
1312            CASE(GenericResource, "generic resources");
1313            CASE(RayPayload, "ray payloads");
1314            CASE(HitAttributes, "hit attributes");
1315            CASE(CallablePayload, "callable payloads");
1316            CASE(ShaderRecord, "shader records");
1317            CASE(ExistentialTypeParam, "existential type parameters");
1318            CASE(ExistentialObjectParam, "existential object parameters");
1319            CASE(SubElementRegisterSpace, "register spaces / descriptor sets");
1320            CASE(InputAttachmentIndex, "subpass input attachments");
1321            CASE(MetalArgumentBufferElement, "Metal argument buffer elements");
1322            CASE(MetalAttribute, "Metal attributes");
1323            CASE(MetalPayload, "Metal payloads");
1324#undef CASE
1325
1326        default:
1327            printf("%d # unknown enumerant", int(layoutUnit));
1328            break;
1329        }
1330    }
1331
1332    void printStage(SlangStage stage)
1333    {
1334        switch (stage)
1335        {
1336#define CASE(NAME)           \
1337    case SLANG_STAGE_##NAME: \
1338        printf(#NAME);       \
1339        break
1340
1341            CASE(NONE);
1342            CASE(VERTEX);
1343            CASE(HULL);
1344            CASE(DOMAIN);
1345            CASE(GEOMETRY);
1346            CASE(FRAGMENT);
1347            CASE(COMPUTE);
1348            CASE(RAY_GENERATION);
1349            CASE(INTERSECTION);
1350            CASE(ANY_HIT);
1351            CASE(CLOSEST_HIT);
1352            CASE(MISS);
1353            CASE(CALLABLE);
1354            CASE(MESH);
1355            CASE(AMPLIFICATION);
1356#undef CASE
1357
1358        default:
1359            printf("%d # unexpected enumerant", int(stage));
1360            break;
1361        };
1362    }
1363    void printTargetFormat(SlangCompileTarget targetFormat)
1364    {
1365        switch (targetFormat)
1366        {
1367#define CASE(TAG)           \
1368    case SLANG_##TAG:       \
1369        printf("%s", #TAG); \
1370        break
1371
1372            CASE(TARGET_UNKNOWN);
1373            CASE(TARGET_NONE);
1374            CASE(GLSL);
1375            CASE(GLSL_VULKAN_DEPRECATED);
1376            CASE(GLSL_VULKAN_ONE_DESC_DEPRECATED);
1377            CASE(HLSL);
1378            CASE(SPIRV);
1379            CASE(SPIRV_ASM);
1380            CASE(DXBC);
1381            CASE(DXBC_ASM);
1382            CASE(DXIL);
1383            CASE(DXIL_ASM);
1384            CASE(C_SOURCE);
1385            CASE(CPP_SOURCE);
1386            CASE(HOST_EXECUTABLE);
1387            CASE(SHADER_SHARED_LIBRARY);
1388            CASE(SHADER_HOST_CALLABLE);
1389            CASE(CUDA_SOURCE);
1390            CASE(PTX);
1391            CASE(CUDA_OBJECT_CODE);
1392            CASE(OBJECT_CODE);
1393            CASE(HOST_CPP_SOURCE);
1394            CASE(HOST_HOST_CALLABLE);
1395            CASE(CPP_PYTORCH_BINDING);
1396            CASE(METAL);
1397            CASE(METAL_LIB);
1398            CASE(METAL_LIB_ASM);
1399            CASE(HOST_SHARED_LIBRARY);
1400            CASE(WGSL);
1401            CASE(WGSL_SPIRV_ASM);
1402            CASE(WGSL_SPIRV);
1403#undef CASE
1404
1405        default:
1406            printf("%d # unhandled enumerant", int(targetFormat));
1407        }
1408    }
1409
1410    void printScalarType(slang::TypeReflection::ScalarType scalarType)
1411    {
1412        switch (scalarType)
1413        {
1414#define CASE(TAG)                    \
1415    case slang::TypeReflection::TAG: \
1416        printf("%s", #TAG);          \
1417        break
1418
1419            CASE(None);
1420            CASE(Void);
1421            CASE(Bool);
1422            CASE(Int32);
1423            CASE(UInt32);
1424            CASE(Int64);
1425            CASE(UInt64);
1426            CASE(Float16);
1427            CASE(Float32);
1428            CASE(Float64);
1429            CASE(Int8);
1430            CASE(UInt8);
1431            CASE(Int16);
1432            CASE(UInt16);
1433#undef CASE
1434
1435        default:
1436            printf("%d # unhandled enumerant", int(scalarType));
1437        }
1438    }
1439
1440    void printMatrixLayoutMode(SlangMatrixLayoutMode mode)
1441    {
1442        switch (mode)
1443        {
1444#define CASE(TAG)                   \
1445    case SLANG_MATRIX_LAYOUT_##TAG: \
1446        printf("%s", #TAG);         \
1447        break
1448
1449            CASE(MODE_UNKNOWN);
1450            CASE(ROW_MAJOR);
1451            CASE(COLUMN_MAJOR);
1452#undef CASE
1453
1454        default:
1455            printf("%d # unhandled enumerant", int(mode));
1456        }
1457    }
1458};
1459
1460struct ExampleProgram : public TestBase
1461{
1462    Result execute(int argc, char* argv[])
1463    {
1464        parseOption(argc, argv);
1465
1466        ComPtr<slang::IGlobalSession> globalSession;
1467        SLANG_RETURN_ON_FAIL(slang::createGlobalSession(globalSession.writeRef()));
1468
1469        Slang::List<slang::TargetDesc> targetDescs;
1470        for (auto target : kTargets)
1471        {
1472            auto profile = globalSession->findProfile(target.profile);
1473
1474            slang::TargetDesc targetDesc;
1475            targetDesc.format = target.format;
1476            targetDesc.profile = profile;
1477            targetDescs.add(targetDesc);
1478        }
1479
1480        slang::SessionDesc sessionDesc;
1481        sessionDesc.targetCount = targetDescs.getCount();
1482        sessionDesc.targets = targetDescs.getBuffer();
1483
1484        ComPtr<slang::ISession> session;
1485        SLANG_RETURN_ON_FAIL(globalSession->createSession(sessionDesc, session.writeRef()));
1486
1487        ReflectingPrinting printingContext;
1488        printingContext.compileAndReflectPrograms(session);
1489
1490        return SLANG_OK;
1491    }
1492};
1493
1494int exampleMain(int argc, char** argv)
1495{
1496    ExampleProgram app;
1497    if (SLANG_FAILED(app.execute(argc, argv)))
1498    {
1499        return -1;
1500    }
1501    return 0;
1502}