summaryrefslogtreecommitdiffstats
path: root/examples/reflection-api/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'examples/reflection-api/main.cpp')
-rw-r--r--examples/reflection-api/main.cpp1481
1 files changed, 1481 insertions, 0 deletions
diff --git a/examples/reflection-api/main.cpp b/examples/reflection-api/main.cpp
new file mode 100644
index 000000000..9d773fffd
--- /dev/null
+++ b/examples/reflection-api/main.cpp
@@ -0,0 +1,1481 @@
+// main.cpp
+
+// Reflection API Example Program
+// ==============================
+//
+// This file provides the application code for the `reflection-api` example.
+// This example uses the Slang reflection API to travserse the structure
+// of the parameters of a Slang program and their types.
+//
+// This program is a companion to a document on the Slang reflection API.
+// Once that document is published, this paragraph will be updated to
+// properly link to that document.
+//
+// Boilerplate
+// -----------
+//
+// The following lines are boilerplate common to set up this example
+// to use the infrastructure for example programs in the Slang
+// repository.
+//
+
+#include "slang-com-ptr.h"
+#include "slang.h"
+typedef SlangResult Result;
+
+#include "core/slang-basic.h"
+#include "examples/example-base/example-base.h"
+using Slang::ComPtr;
+using Slang::String;
+using Slang::List;
+
+static const ExampleResources resourceBase("reflection-api");
+
+// Configuration
+// -------------
+//
+// For simplicity, this example uses a hard-coded list of shader programs
+// to compile, each represented as the name of a `.slang` file, along with
+// a hard-coded list of targets to compile and reflect the programs for.
+//
+
+static const char* kSourceFileNames[] = {
+ "raster-simple.slang",
+ "compute-simple.slang",
+};
+
+static const struct
+{
+ SlangCompileTarget format;
+ const char* profile;
+} kTargets[] = {
+ {SLANG_DXIL, "sm_6_0"},
+ {SLANG_SPIRV, "sm_6_0"},
+};
+static const int kTargetCount = SLANG_COUNT_OF(kTargets);
+
+// The `ReflectingPrinting` Type
+// -------------------------
+//
+// We wrap most of the code for this example in a `struct`
+// type, in order to provide a bit more freedom in order
+// of declaration.
+//
+// When possible, we will follow the order of declarations
+// in the accompanying document, to help readers who want
+// to following along in the code while reading.
+//
+struct ReflectingPrinting
+{
+ // Scoping things in a type allows us to declare functions
+ // out of order more easily, but we still have to forward-declare
+ // types when they will be used before they are declared.
+ //
+ struct AccessPath;
+
+ // Output Formatting
+ // -----------------
+ //
+ // This example program outputs reflection information in a format
+ // that is (or at least is intended to be) compatible with YAML.
+ //
+ // We do not want the code to be overly complicated with issues
+ // around formatting, so the details of the actual printing logic
+ // are largely left until later. However, there are a pair of
+ // macros that help to keep things tidy that we need to introduce
+ // here, before they are used.
+ //
+#define WITH_ARRAY() for (int _i = (beginArray(), 1); _i; _i = (endArray(), 0))
+
+#define SCOPED_OBJECT() ScopedObject scopedObject##__COUNTER__(this)
+
+ // Compiling a Program
+ // -------------------
+ //
+ Result compileAndReflectProgram(slang::ISession* session, const char* sourceFileName)
+ {
+ SCOPED_OBJECT();
+ printComment("program");
+
+ key("file name");
+ printQuotedString(sourceFileName);
+ String sourceFilePath = resourceBase.resolveResource(sourceFileName);
+
+ ComPtr<slang::IBlob> diagnostics;
+ Result result = SLANG_OK;
+
+ // ### Loading a Module
+ //
+
+ ComPtr<slang::IModule> module;
+ module = session->loadModule(sourceFilePath.getBuffer(), diagnostics.writeRef());
+ diagnoseIfNeeded(diagnostics);
+ if (!module)
+ return SLANG_FAIL;
+
+ List<ComPtr<slang::IComponentType>> componentsToLink;
+
+ // ### Finding Entry Points
+ //
+
+ key("defined entry points");
+ int definedEntryPointCount = module->getDefinedEntryPointCount();
+ WITH_ARRAY()
+ for (int i = 0; i < definedEntryPointCount; i++)
+ {
+ ComPtr<slang::IEntryPoint> entryPoint;
+ SLANG_RETURN_ON_FAIL(module->getDefinedEntryPoint(i, entryPoint.writeRef()));
+
+ element();
+ SCOPED_OBJECT();
+ key("name");
+ printQuotedString(entryPoint->getFunctionReflection()->getName());
+
+ componentsToLink.add(ComPtr<slang::IComponentType>(entryPoint.get()));
+ }
+
+ // ### Composing and Linking
+ //
+
+ ComPtr<slang::IComponentType> composed;
+ result = session->createCompositeComponentType(
+ (slang::IComponentType**)componentsToLink.getBuffer(),
+ componentsToLink.getCount(),
+ composed.writeRef(),
+ diagnostics.writeRef());
+ diagnoseIfNeeded(diagnostics);
+ SLANG_RETURN_ON_FAIL(result);
+
+ ComPtr<slang::IComponentType> program;
+ result = composed->link(program.writeRef(), diagnostics.writeRef());
+ diagnoseIfNeeded(diagnostics);
+ SLANG_RETURN_ON_FAIL(result);
+
+ key("layouts");
+ WITH_ARRAY()
+ for (int targetIndex = 0; targetIndex < kTargetCount; ++targetIndex)
+ {
+ element();
+
+ // ### Getting the Program Layout
+ //
+ slang::ProgramLayout* programLayout =
+ program->getLayout(targetIndex, diagnostics.writeRef());
+ diagnoseIfNeeded(diagnostics);
+ if (!programLayout)
+ {
+ result = SLANG_FAIL;
+ continue;
+ }
+
+ SLANG_RETURN_ON_FAIL(
+ collectEntryPointMetadata(program, targetIndex, definedEntryPointCount));
+
+ _programLayout = programLayout;
+ auto targetFormat = kTargets[targetIndex].format;
+ printProgramLayout(programLayout, targetFormat);
+ }
+
+ return result;
+ }
+ slang::ProgramLayout* _programLayout = nullptr;
+
+ Result compileAndReflectPrograms(slang::ISession* session)
+ {
+ Result result = SLANG_OK;
+
+ WITH_ARRAY()
+ for (auto fileName : kSourceFileNames)
+ {
+ element();
+ auto programResult = compileAndReflectProgram(session, fileName);
+ if (SLANG_FAILED(programResult))
+ {
+ result = programResult;
+ }
+ }
+
+ return result;
+ }
+
+ // Types and Variables
+ // -------------------
+ //
+ // ### Variables
+ //
+ void printVariable(slang::VariableReflection* variable)
+ {
+ SCOPED_OBJECT();
+
+ const char* name = variable->getName();
+ slang::TypeReflection* type = variable->getType();
+
+ key("name");
+ printQuotedString(name);
+ key("type");
+ printType(type);
+ }
+
+ // ### Types
+ //
+ void printType(slang::TypeReflection* type)
+ {
+ SCOPED_OBJECT();
+
+ const char* name = type->getName();
+ slang::TypeReflection::Kind kind = type->getKind();
+
+ key("name");
+ printQuotedString(name);
+ key("kind");
+ printTypeKind(kind);
+
+ // There is information that we would like to
+ // print for both types and type layouts, so
+ // we will factor the common logic into a
+ // subroutine so that we can share the code.
+ //
+ printCommonTypeInfo(type);
+
+ switch (type->getKind())
+ {
+ default:
+ break;
+
+ // #### Structure Types
+ //
+ case slang::TypeReflection::Kind::Struct:
+ {
+ key("fields");
+ int fieldCount = type->getFieldCount();
+
+ WITH_ARRAY();
+ for (int f = 0; f < fieldCount; f++)
+ {
+ element();
+ auto field = type->getFieldByIndex(f);
+
+ printVariable(field);
+ }
+ }
+ break;
+
+ // #### Array Types
+ // #### Vector Types
+ // #### Matrix Types
+ //
+ case slang::TypeReflection::Kind::Array:
+ case slang::TypeReflection::Kind::Vector:
+ case slang::TypeReflection::Kind::Matrix:
+ {
+ key("element type");
+ printType(type->getElementType());
+ }
+ break;
+
+ // #### Resource Types
+ //
+ case slang::TypeReflection::Kind::Resource:
+ {
+ key("result type");
+ printType(type->getResourceResultType());
+ }
+ break;
+
+ // #### Single-Element Container Types
+ //
+ case slang::TypeReflection::Kind::ConstantBuffer:
+ case slang::TypeReflection::Kind::ParameterBlock:
+ case slang::TypeReflection::Kind::TextureBuffer:
+ case slang::TypeReflection::Kind::ShaderStorageBuffer:
+ {
+ key("element type");
+ printType(type->getElementType());
+ }
+ break;
+ }
+ }
+
+ // #### Array Types
+ //
+ void printPossiblyUnbounded(size_t value)
+ {
+ if (value == ~size_t(0))
+ {
+ printf("unbounded");
+ }
+ else
+ {
+ printf("%u", unsigned(value));
+ }
+ }
+
+ void printCommonTypeInfo(slang::TypeReflection* type)
+ {
+ switch (type->getKind())
+ {
+ // #### Scalar Types
+ //
+ case slang::TypeReflection::Kind::Scalar:
+ {
+ key("scalar type");
+ printScalarType(type->getScalarType());
+ }
+ break;
+
+ // #### Array Types
+ //
+ case slang::TypeReflection::Kind::Array:
+ {
+ key("element count");
+ printPossiblyUnbounded(type->getElementCount());
+ }
+ break;
+
+ // #### Vector Types
+ //
+ case slang::TypeReflection::Kind::Vector:
+ {
+ key("element count");
+ print(type->getElementCount());
+ }
+ break;
+
+ // #### Matrix Types
+ //
+ case slang::TypeReflection::Kind::Matrix:
+ {
+ key("row count");
+ print(type->getRowCount());
+
+ key("column count");
+ print(type->getColumnCount());
+ }
+ break;
+
+ // #### Resource Types
+ //
+ case slang::TypeReflection::Kind::Resource:
+ {
+ key("shape");
+ printResourceShape(type->getResourceShape());
+
+ key("access");
+ printResourceAccess(type->getResourceAccess());
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // Layout for Types and Variables
+ // ------------------------------
+ //
+ // ### Variable Layouts
+ //
+ void printVariableLayout(slang::VariableLayoutReflection* variableLayout, AccessPath accessPath)
+ {
+ SCOPED_OBJECT();
+
+ key("name");
+ printQuotedString(variableLayout->getName());
+
+ printOffsets(variableLayout, accessPath);
+
+ printVaryingParameterInfo(variableLayout);
+
+ ExtendedAccessPath variablePath(accessPath, variableLayout);
+
+ key("type layout");
+ printTypeLayout(variableLayout->getTypeLayout(), variablePath);
+ }
+
+ // #### Offsets
+
+ void printRelativeOffsets(slang::VariableLayoutReflection* variableLayout)
+ {
+ key("relative");
+ int usedLayoutUnitCount = variableLayout->getCategoryCount();
+ WITH_ARRAY();
+ for (int i = 0; i < usedLayoutUnitCount; ++i)
+ {
+ element();
+
+ auto layoutUnit = variableLayout->getCategoryByIndex(i);
+ printOffset(variableLayout, layoutUnit);
+ }
+ }
+
+ void printOffset(
+ slang::VariableLayoutReflection* variableLayout,
+ slang::ParameterCategory layoutUnit)
+ {
+ printOffset(
+ layoutUnit,
+ variableLayout->getOffset(layoutUnit),
+ variableLayout->getBindingSpace(layoutUnit));
+ }
+
+ void printOffset(slang::ParameterCategory layoutUnit, size_t offset, size_t spaceOffset)
+ {
+ SCOPED_OBJECT();
+
+ key("value");
+ print(offset);
+ key("unit");
+ printLayoutUnit(layoutUnit);
+
+ // #### Spaces / Sets
+
+ switch (layoutUnit)
+ {
+ default:
+ break;
+
+ case slang::ParameterCategory::ConstantBuffer:
+ case slang::ParameterCategory::ShaderResource:
+ case slang::ParameterCategory::UnorderedAccess:
+ case slang::ParameterCategory::SamplerState:
+ case slang::ParameterCategory::DescriptorTableSlot:
+ key("space");
+ print(spaceOffset);
+ break;
+ }
+ }
+
+ // ### Type Layouts
+ //
+ void printTypeLayout(slang::TypeLayoutReflection* typeLayout, AccessPath accessPath)
+ {
+ SCOPED_OBJECT();
+
+ key("name");
+ printQuotedString(typeLayout->getName());
+ key("kind");
+ printTypeKind(typeLayout->getKind());
+ printCommonTypeInfo(typeLayout->getType());
+
+ printSizes(typeLayout);
+
+ printKindSpecificInfo(typeLayout, accessPath);
+ }
+
+ // #### Size
+ //
+ void printSizes(slang::TypeLayoutReflection* typeLayout)
+ {
+ key("size");
+
+ int usedLayoutUnitCount = typeLayout->getCategoryCount();
+ WITH_ARRAY()
+ for (int i = 0; i < usedLayoutUnitCount; ++i)
+ {
+ element();
+
+ auto layoutUnit = typeLayout->getCategoryByIndex(i);
+ printSize(typeLayout, layoutUnit);
+ }
+
+ // #### Alignment and Stride
+ if (typeLayout->getSize() != 0)
+ {
+ key("alignment in bytes");
+ print(typeLayout->getAlignment());
+
+ key("stride in bytes");
+ print(typeLayout->getStride());
+ }
+ }
+
+ void printSize(slang::TypeLayoutReflection* typeLayout, slang::ParameterCategory layoutUnit)
+ {
+ printSize(layoutUnit, typeLayout->getSize(layoutUnit));
+ }
+
+ void printSize(slang::ParameterCategory layoutUnit, size_t size)
+ {
+ SCOPED_OBJECT();
+
+ key("value");
+ printPossiblyUnbounded(size);
+ key("unit");
+ printLayoutUnit(layoutUnit);
+ }
+
+ // #### Kind-Specific Information
+ //
+ void printKindSpecificInfo(slang::TypeLayoutReflection* typeLayout, AccessPath accessPath)
+ {
+ switch (typeLayout->getKind())
+ {
+ // #### Structure Type Layouts
+ //
+ case slang::TypeReflection::Kind::Struct:
+ {
+ key("fields");
+
+ int fieldCount = typeLayout->getFieldCount();
+ WITH_ARRAY()
+ for (int f = 0; f < fieldCount; f++)
+ {
+ element();
+
+ auto field = typeLayout->getFieldByIndex(f);
+ printVariableLayout(field, accessPath);
+ }
+ }
+ break;
+
+ // #### Array Type Layouts
+ //
+ case slang::TypeReflection::Kind::Array:
+ {
+ key("element type layout");
+ printTypeLayout(typeLayout->getElementTypeLayout(), AccessPath());
+ }
+ break;
+
+ // #### Matrix Type Layouts
+ //
+ case slang::TypeReflection::Kind::Matrix:
+ {
+ key("matrix layout mode");
+ printMatrixLayoutMode(typeLayout->getMatrixLayoutMode());
+
+ key("element type layout");
+ printTypeLayout(typeLayout->getElementTypeLayout(), AccessPath());
+ }
+ break;
+
+ case slang::TypeReflection::Kind::Vector:
+ {
+ key("element type layout");
+ printTypeLayout(typeLayout->getElementTypeLayout(), AccessPath());
+ }
+ break;
+
+ // #### Single-Element Containers
+ //
+ case slang::TypeReflection::Kind::ConstantBuffer:
+ case slang::TypeReflection::Kind::ParameterBlock:
+ case slang::TypeReflection::Kind::TextureBuffer:
+ case slang::TypeReflection::Kind::ShaderStorageBuffer:
+ {
+ auto containerVarLayout = typeLayout->getContainerVarLayout();
+ auto elementVarLayout = typeLayout->getElementVarLayout();
+
+ key("container");
+ {
+ SCOPED_OBJECT();
+ printOffsets(containerVarLayout, accessPath);
+ }
+
+ AccessPath innerOffsets = accessPath;
+ innerOffsets.deepestConstantBufer = innerOffsets.leaf;
+ if (containerVarLayout->getTypeLayout()->getSize(
+ slang::ParameterCategory::SubElementRegisterSpace) != 0)
+ {
+ innerOffsets.deepestParameterBlock = innerOffsets.leaf;
+ }
+
+ key("content");
+ {
+ SCOPED_OBJECT();
+
+ printOffsets(elementVarLayout, innerOffsets);
+
+ ExtendedAccessPath elementOffsets(innerOffsets, elementVarLayout);
+
+ key("type layout");
+ printTypeLayout(elementVarLayout->getTypeLayout(), elementOffsets);
+ }
+ }
+ break;
+
+ case slang::TypeReflection::Kind::Resource:
+ {
+ if ((typeLayout->getResourceShape() & SLANG_RESOURCE_BASE_SHAPE_MASK) ==
+ SLANG_STRUCTURED_BUFFER)
+ {
+ key("element type layout");
+ printTypeLayout(typeLayout->getElementTypeLayout(), accessPath);
+ }
+ else
+ {
+ key("result type");
+ printType(typeLayout->getResourceResultType());
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // Programs and Scopes
+ // -------------------
+ //
+ void printProgramLayout(slang::ProgramLayout* programLayout, SlangCompileTarget targetFormat)
+ {
+ SCOPED_OBJECT();
+
+ key("target");
+ printTargetFormat(targetFormat);
+
+ AccessPath rootOffsets;
+ rootOffsets.valid = true;
+
+ key("global scope");
+ {
+ SCOPED_OBJECT();
+ printScope(programLayout->getGlobalParamsVarLayout(), rootOffsets);
+ }
+
+ key("entry points");
+ int entryPointCount = programLayout->getEntryPointCount();
+ WITH_ARRAY()
+ for (int i = 0; i < entryPointCount; ++i)
+ {
+ element();
+ printEntryPointLayout(programLayout->getEntryPointByIndex(i), rootOffsets);
+ }
+ }
+
+ // ### Global Scope
+ //
+ void printScope(slang::VariableLayoutReflection* scopeVarLayout, AccessPath accessPath)
+ {
+ ExtendedAccessPath scopeOffsets(accessPath, scopeVarLayout);
+
+ auto scopeTypeLayout = scopeVarLayout->getTypeLayout();
+ switch (scopeTypeLayout->getKind())
+ {
+ // #### Parameters are Grouped Into a Structure
+ //
+ case slang::TypeReflection::Kind::Struct:
+ {
+ key("parameters");
+
+ int paramCount = scopeTypeLayout->getFieldCount();
+ for (int i = 0; i < paramCount; i++)
+ {
+ element();
+
+ auto param = scopeTypeLayout->getFieldByIndex(i);
+
+ printVariableLayout(param, scopeOffsets);
+ }
+ }
+ break;
+
+ // #### Wrapped in a Constant Buffer If Needed
+ //
+ case slang::TypeReflection::Kind::ConstantBuffer:
+ key("automatically-introduced constant buffer");
+ {
+ SCOPED_OBJECT();
+ printOffsets(scopeTypeLayout->getContainerVarLayout(), scopeOffsets);
+ }
+
+ printScope(scopeTypeLayout->getElementVarLayout(), scopeOffsets);
+ break;
+
+ // #### Wrapped in a Parameter Block If Needed
+ //
+ case slang::TypeReflection::Kind::ParameterBlock:
+ key("automatically-introduced parameter block");
+ {
+ SCOPED_OBJECT();
+ printOffsets(scopeTypeLayout->getContainerVarLayout(), scopeOffsets);
+ }
+
+ printScope(scopeTypeLayout->getElementVarLayout(), scopeOffsets);
+ break;
+
+ default:
+ // Note that this default case is never expected to
+ // arise with the current Slang compiler and reflection
+ // API, but we include it here as a kind of failsafe.
+ //
+ key("variable layout");
+ printVariableLayout(scopeVarLayout, accessPath);
+ break;
+ }
+ }
+
+ // ### Entry Points
+ //
+ void printEntryPointLayout(slang::EntryPointReflection* entryPointLayout, AccessPath accessPath)
+ {
+ SCOPED_OBJECT();
+
+ key("stage");
+ printStage(entryPointLayout->getStage());
+
+ printStageSpecificInfo(entryPointLayout);
+
+ printScope(entryPointLayout->getVarLayout(), accessPath);
+
+ auto resultVariableLayout = entryPointLayout->getResultVarLayout();
+ if (resultVariableLayout->getTypeLayout()->getKind() != slang::TypeReflection::Kind::None)
+ {
+ key("result");
+ printVariableLayout(resultVariableLayout, accessPath);
+ }
+ }
+
+ // #### Stage-Specific Information
+ //
+ void printStageSpecificInfo(slang::EntryPointReflection* entryPointLayout)
+ {
+ switch (entryPointLayout->getStage())
+ {
+ default:
+ break;
+
+ case SLANG_STAGE_COMPUTE:
+ {
+ static const int kAxisCount = 3;
+ SlangUInt sizes[kAxisCount];
+ entryPointLayout->getComputeThreadGroupSize(kAxisCount, sizes);
+
+ key("thread group size");
+ SCOPED_OBJECT();
+ key("x");
+ print(sizes[0]);
+ key("y");
+ print(sizes[1]);
+ key("z");
+ print(sizes[2]);
+ }
+ break;
+
+ case SLANG_STAGE_FRAGMENT:
+ key("uses any sample-rate inputs");
+ printBool(entryPointLayout->usesAnySampleRateInput());
+ break;
+ }
+ }
+
+ // #### Varying Parameters
+ //
+ void printVaryingParameterInfo(slang::VariableLayoutReflection* variableLayout)
+ {
+ if (auto semanticName = variableLayout->getSemanticName())
+ {
+ key("semantic");
+ SCOPED_OBJECT();
+ key("name");
+ printQuotedString(semanticName);
+ key("index");
+ print(variableLayout->getSemanticIndex());
+ }
+ }
+
+ // Calculating Cumulative Offsets
+ // ------------------------------
+ //
+ struct CumulativeOffset
+ {
+ size_t value = 0;
+ size_t space = 0;
+ };
+
+ // ### Access Paths
+
+ struct AccessPathNode
+ {
+ slang::VariableLayoutReflection* variableLayout = nullptr;
+ AccessPathNode* outer = nullptr;
+ };
+
+ struct AccessPath
+ {
+ AccessPath() {}
+
+ bool valid = false;
+ AccessPathNode* deepestConstantBufer = nullptr;
+ AccessPathNode* deepestParameterBlock = nullptr;
+ AccessPathNode* leaf = nullptr;
+ };
+
+ void printCumulativeOffsets(
+ slang::VariableLayoutReflection* variableLayout,
+ AccessPath accessPath)
+ {
+ key("cumulative");
+
+ int usedLayoutUnitCount = variableLayout->getCategoryCount();
+ WITH_ARRAY();
+ for (int i = 0; i < usedLayoutUnitCount; ++i)
+ {
+ element();
+
+ auto layoutUnit = variableLayout->getCategoryByIndex(i);
+ printCumulativeOffset(variableLayout, layoutUnit, accessPath);
+ }
+ }
+
+ CumulativeOffset calculateCumulativeOffset(
+ slang::VariableLayoutReflection* variableLayout,
+ slang::ParameterCategory layoutUnit,
+ AccessPath accessPath)
+ {
+ CumulativeOffset result = calculateCumulativeOffset(layoutUnit, accessPath);
+ result.value += variableLayout->getOffset(layoutUnit);
+ result.space += variableLayout->getBindingSpace(layoutUnit);
+ return result;
+ }
+
+ void printCumulativeOffset(
+ slang::VariableLayoutReflection* variableLayout,
+ slang::ParameterCategory layoutUnit,
+ AccessPath accessPath)
+ {
+ CumulativeOffset cumulativeOffset =
+ calculateCumulativeOffset(variableLayout, layoutUnit, accessPath);
+
+ printOffset(layoutUnit, cumulativeOffset.value, cumulativeOffset.space);
+ }
+
+ // ### Tracking Access Paths
+
+ struct ExtendedAccessPath : AccessPath
+ {
+ ExtendedAccessPath(AccessPath const& base, slang::VariableLayoutReflection* variableLayout)
+ : AccessPath(base)
+ {
+ if (!valid)
+ return;
+
+ element.variableLayout = variableLayout;
+ element.outer = leaf;
+
+ leaf = &element;
+ }
+
+ AccessPathNode element;
+ };
+
+ // ### Accumulating Offsets Along An Access Path
+
+ CumulativeOffset calculateCumulativeOffset(
+ slang::ParameterCategory layoutUnit,
+ AccessPath accessPath)
+ {
+ CumulativeOffset result;
+ switch (layoutUnit)
+ {
+ // #### Layout Units That Don't Require Special Handling
+ //
+ default:
+ for (auto node = accessPath.leaf; node != nullptr; node = node->outer)
+ {
+ result.value += node->variableLayout->getOffset(layoutUnit);
+ }
+ break;
+
+ // #### Bytes
+ //
+ case slang::ParameterCategory::Uniform:
+ for (auto node = accessPath.leaf; node != accessPath.deepestConstantBufer;
+ node = node->outer)
+ {
+ result.value += node->variableLayout->getOffset(layoutUnit);
+ }
+ break;
+
+ // #### Layout Units That Care About Spaces
+ //
+ case slang::ParameterCategory::ConstantBuffer:
+ case slang::ParameterCategory::ShaderResource:
+ case slang::ParameterCategory::UnorderedAccess:
+ case slang::ParameterCategory::SamplerState:
+ case slang::ParameterCategory::DescriptorTableSlot:
+ for (auto node = accessPath.leaf; node != accessPath.deepestParameterBlock;
+ node = node->outer)
+ {
+ result.value += node->variableLayout->getOffset(layoutUnit);
+ result.space += node->variableLayout->getBindingSpace(layoutUnit);
+ }
+ for (auto node = accessPath.deepestParameterBlock; node != nullptr; node = node->outer)
+ {
+ result.space += node->variableLayout->getOffset(
+ slang::ParameterCategory::SubElementRegisterSpace);
+ }
+ break;
+ }
+ return result;
+ }
+
+ // Determining Whether Parameters Are Used
+ // ---------------------------------------
+
+ Result collectEntryPointMetadata(
+ slang::IComponentType* program,
+ int targetIndex,
+ int entryPointCount)
+ {
+ _metadataForEntryPoints.setCount(entryPointCount);
+ for (int entryPointIndex = 0; entryPointIndex < entryPointCount; entryPointIndex++)
+ {
+ ComPtr<slang::IMetadata> entryPointMetadata;
+ ComPtr<slang::IBlob> diagnostics;
+ SLANG_RETURN_ON_FAIL(program->getEntryPointMetadata(
+ entryPointIndex,
+ targetIndex,
+ entryPointMetadata.writeRef(),
+ diagnostics.writeRef()));
+ diagnoseIfNeeded(diagnostics);
+
+ _metadataForEntryPoints[entryPointIndex] = entryPointMetadata;
+ }
+ return SLANG_OK;
+ }
+ Slang::List<ComPtr<slang::IMetadata>> _metadataForEntryPoints;
+
+ typedef unsigned int StageMask;
+
+ StageMask calculateParameterStageMask(
+ slang::ParameterCategory layoutUnit,
+ CumulativeOffset offset)
+ {
+ unsigned mask = 0;
+ auto entryPointCount = _metadataForEntryPoints.getCount();
+ for (int i = 0; i < entryPointCount; ++i)
+ {
+ bool isUsed = false;
+ _metadataForEntryPoints[i]->isParameterLocationUsed(
+ SlangParameterCategory(layoutUnit),
+ offset.space,
+ offset.value,
+ isUsed);
+ if (isUsed)
+ {
+ auto entryPointStage = _programLayout->getEntryPointByIndex(i)->getStage();
+
+ mask |= 1 << unsigned(entryPointStage);
+ }
+ }
+ return mask;
+ }
+
+ StageMask calculateStageMask(
+ slang::VariableLayoutReflection* variableLayout,
+ AccessPath accessPath)
+ {
+ StageMask mask = 0;
+
+ int usedLayoutUnitCount = variableLayout->getCategoryCount();
+ for (int i = 0; i < usedLayoutUnitCount; ++i)
+ {
+ auto layoutUnit = variableLayout->getCategoryByIndex(i);
+ auto offset = calculateCumulativeOffset(variableLayout, layoutUnit, accessPath);
+
+ mask |= calculateParameterStageMask(layoutUnit, offset);
+ }
+
+ return mask;
+ }
+
+ void printStageUsage(slang::VariableLayoutReflection* variableLayout, AccessPath accessPath)
+ {
+ StageMask stageMask = calculateStageMask(variableLayout, accessPath);
+
+ key("used by stages");
+ WITH_ARRAY()
+ for (int i = 0; i < SLANG_STAGE_COUNT; i++)
+ {
+ if (stageMask & (1 << i))
+ {
+ element();
+ printStage(SlangStage(i));
+ }
+ }
+ }
+
+ void printOffsets(slang::VariableLayoutReflection* variableLayout, AccessPath accessPath)
+ {
+ key("offset");
+ {
+ SCOPED_OBJECT();
+ printRelativeOffsets(variableLayout);
+
+ if (accessPath.valid)
+ {
+ printCumulativeOffsets(variableLayout, accessPath);
+ }
+ }
+
+
+ if (accessPath.valid)
+ {
+ printStageUsage(variableLayout, accessPath);
+ }
+ }
+
+ // Formatting
+ // ----------
+ //
+ // Here we'll cover the logic for how we implement
+ // the various formatting operations used in the
+ // code above.
+ //
+ // ### Indentation
+ //
+ // We track a global indentation level, and whenever
+ // we begin a new line, we'll emit a corresponding
+ // amount of space (two spaces per indent, consistent
+ // with typical YAML formatting).
+
+ int indentation = 0;
+
+ void printIndentation()
+ {
+ for (int i = 1; i < indentation; ++i)
+ {
+ printf(" ");
+ }
+ }
+
+ // ### Objects and Arrays
+ //
+ // Both objects and arrays can be marked up purely
+ // with indentation in YAML. If we eventually
+ // change the output format to something like JSON,
+ // these operations would need to do more actual
+ // work.
+
+ void beginObject() { indentation++; }
+
+ void endObject() { indentation--; }
+
+ void beginArray() { indentation++; }
+
+ void endArray() { indentation--; }
+
+ // #### Scope-Based Objects
+ //
+ // In order to make it easier to keep the `beginObject()`
+ // and `endObject()` calls properly paired, we introduce
+ // a helper type that uses an RAII idiom to automatically
+ // pair up the calls.
+ //
+ struct ScopedObject
+ {
+ ScopedObject(ReflectingPrinting* outer)
+ : outer(outer)
+ {
+ outer->beginObject();
+ }
+
+ ~ScopedObject() { outer->endObject(); }
+
+ ReflectingPrinting* outer = nullptr;
+ };
+
+ // ### Starting New Lines
+ //
+ // Typically, when we are about to emit a key
+ // in an object, or an element in an array,
+ // we need to start a new line (and print
+ // the appropriate indentation).
+ //
+ void newLine()
+ {
+ printf("\n");
+ printIndentation();
+ }
+
+
+ // The main exception is that if we've just
+ // emitted the `- ` for an array element then
+ // we don't need to start a new line if
+ // the next thing we emit is an object key.
+ //
+ // We *also* don't need to start a new line
+ // at the very beginning of the output, so
+ // we handle that by setting the intial state
+ // *as if* we have just started an array element.
+
+ bool afterArrayElement = true;
+
+ // ### Array Elements
+ //
+ void element()
+ {
+ newLine();
+ printf("- ");
+ afterArrayElement = true;
+ }
+
+ // ### Object Keys
+ //
+ void key(char const* key)
+ {
+ if (!afterArrayElement)
+ {
+ newLine();
+ }
+ afterArrayElement = false;
+
+ printf("%s: ", key);
+ }
+
+ // ### Printing Simple Values
+ //
+ // Simple scalar values like strings,
+ // `bool`s, and numbers don't need
+ // much special handling.
+
+ void printQuotedString(char const* text)
+ {
+ if (text)
+ {
+ printf("\"%s\"", text);
+ }
+ else
+ {
+ printf("null");
+ }
+ }
+
+ void printBool(bool value) { printf(value ? "true" : "false"); }
+
+ void print(size_t value) { printf("%u", unsigned(value)); }
+
+ // YAML supports comments, but JSON doesn't.
+ // This function could be stubbed out if
+ // we switch up the output format.
+ //
+ void printComment(char const* text) { printf("# %s", text); }
+
+
+ // Printing Enumerants
+ // -------------------
+ //
+ // Here we'll gather all the logic for printing the various
+ // `enum` types that we've worked with in the logic above.
+
+ void printTypeKind(slang::TypeReflection::Kind kind)
+ {
+ switch (kind)
+ {
+#define CASE(TAG) \
+ case slang::TypeReflection::Kind::TAG: \
+ printf("%s", #TAG); \
+ break
+
+ CASE(None);
+ CASE(Struct);
+ CASE(Array);
+ CASE(Matrix);
+ CASE(Vector);
+ CASE(Scalar);
+ CASE(ConstantBuffer);
+ CASE(Resource);
+ CASE(SamplerState);
+ CASE(TextureBuffer);
+ CASE(ShaderStorageBuffer);
+ CASE(ParameterBlock);
+ CASE(GenericTypeParameter);
+ CASE(Interface);
+ CASE(OutputStream);
+ CASE(Specialized);
+ CASE(Feedback);
+ CASE(Pointer);
+ CASE(DynamicResource);
+#undef CASE
+
+ default:
+ printf("%d # unexpected enumerant", int(kind));
+ break;
+ }
+ }
+
+ void printResourceShape(SlangResourceShape shape)
+ {
+ SCOPED_OBJECT();
+
+ key("base");
+ auto baseShape = shape & SLANG_RESOURCE_BASE_SHAPE_MASK;
+ switch (baseShape)
+ {
+#define CASE(TAG) \
+ case SLANG_##TAG: \
+ printf("%s", #TAG); \
+ break
+
+ CASE(TEXTURE_1D);
+ CASE(TEXTURE_2D);
+ CASE(TEXTURE_3D);
+ CASE(TEXTURE_CUBE);
+ CASE(TEXTURE_BUFFER);
+ CASE(STRUCTURED_BUFFER);
+ CASE(BYTE_ADDRESS_BUFFER);
+ CASE(RESOURCE_UNKNOWN);
+ CASE(ACCELERATION_STRUCTURE);
+ CASE(TEXTURE_SUBPASS);
+#undef CASE
+
+ default:
+ printf("%d # unexpected enumerant", int(baseShape));
+ break;
+ }
+
+#define CASE(TAG) \
+ do \
+ { \
+ if (shape & SLANG_TEXTURE_##TAG##_FLAG) \
+ { \
+ key(#TAG); \
+ printf("true"); \
+ } \
+ } while (0)
+
+ CASE(FEEDBACK);
+ CASE(SHADOW);
+ CASE(ARRAY);
+ CASE(MULTISAMPLE);
+#undef CASE
+ }
+
+ void printResourceAccess(SlangResourceAccess access)
+ {
+ switch (access)
+ {
+#define CASE(TAG) \
+ case SLANG_RESOURCE_ACCESS_##TAG: \
+ printf("%s", #TAG); \
+ break
+
+ CASE(NONE);
+ CASE(READ);
+ CASE(READ_WRITE);
+ CASE(RASTER_ORDERED);
+ CASE(APPEND);
+ CASE(CONSUME);
+ CASE(WRITE);
+ CASE(FEEDBACK);
+#undef CASE
+
+ default:
+ printf("%d # unexpected enumerant", int(access));
+ break;
+ }
+ }
+
+ void printLayoutUnit(slang::ParameterCategory layoutUnit)
+ {
+ switch (layoutUnit)
+ {
+#define CASE(TAG, DESCRIPTION) \
+ case slang::ParameterCategory::TAG: \
+ printf("%s # %s", #TAG, DESCRIPTION); \
+ break
+
+ CASE(ConstantBuffer, "constant buffer slots");
+ CASE(ShaderResource, "texture slots");
+ CASE(UnorderedAccess, "uav slots");
+ CASE(VaryingInput, "varying input slots");
+ CASE(VaryingOutput, "varying output slots");
+ CASE(SamplerState, "sampler slots");
+ CASE(Uniform, "bytes");
+ CASE(DescriptorTableSlot, "bindings");
+ CASE(SpecializationConstant, "specialization constant ids");
+ CASE(PushConstantBuffer, "push-constant buffers");
+ CASE(RegisterSpace, "register space offset for a variable");
+ CASE(GenericResource, "generic resources");
+ CASE(RayPayload, "ray payloads");
+ CASE(HitAttributes, "hit attributes");
+ CASE(CallablePayload, "callable payloads");
+ CASE(ShaderRecord, "shader records");
+ CASE(ExistentialTypeParam, "existential type parameters");
+ CASE(ExistentialObjectParam, "existential object parameters");
+ CASE(SubElementRegisterSpace, "register spaces / descriptor sets");
+ CASE(InputAttachmentIndex, "subpass input attachments");
+ CASE(MetalArgumentBufferElement, "Metal argument buffer elements");
+ CASE(MetalAttribute, "Metal attributes");
+ CASE(MetalPayload, "Metal payloads");
+#undef CASE
+
+ default:
+ printf("%d # unknown enumerant", int(layoutUnit));
+ break;
+ }
+ }
+
+ void printStage(SlangStage stage)
+ {
+ switch (stage)
+ {
+#define CASE(NAME) \
+ case SLANG_STAGE_##NAME: \
+ printf(#NAME); \
+ break
+
+ CASE(NONE);
+ CASE(VERTEX);
+ CASE(HULL);
+ CASE(DOMAIN);
+ CASE(GEOMETRY);
+ CASE(FRAGMENT);
+ CASE(COMPUTE);
+ CASE(RAY_GENERATION);
+ CASE(INTERSECTION);
+ CASE(ANY_HIT);
+ CASE(CLOSEST_HIT);
+ CASE(MISS);
+ CASE(CALLABLE);
+ CASE(MESH);
+ CASE(AMPLIFICATION);
+#undef CASE
+
+ default:
+ printf("%d # unexpected enumerant", int(stage));
+ break;
+ };
+ }
+ void printTargetFormat(SlangCompileTarget targetFormat)
+ {
+ switch (targetFormat)
+ {
+#define CASE(TAG) \
+ case SLANG_##TAG: \
+ printf("%s", #TAG); \
+ break
+
+ CASE(TARGET_UNKNOWN);
+ CASE(TARGET_NONE);
+ CASE(GLSL);
+ CASE(GLSL_VULKAN_DEPRECATED);
+ CASE(GLSL_VULKAN_ONE_DESC_DEPRECATED);
+ CASE(HLSL);
+ CASE(SPIRV);
+ CASE(SPIRV_ASM);
+ CASE(DXBC);
+ CASE(DXBC_ASM);
+ CASE(DXIL);
+ CASE(DXIL_ASM);
+ CASE(C_SOURCE);
+ CASE(CPP_SOURCE);
+ CASE(HOST_EXECUTABLE);
+ CASE(SHADER_SHARED_LIBRARY);
+ CASE(SHADER_HOST_CALLABLE);
+ CASE(CUDA_SOURCE);
+ CASE(PTX);
+ CASE(CUDA_OBJECT_CODE);
+ CASE(OBJECT_CODE);
+ CASE(HOST_CPP_SOURCE);
+ CASE(HOST_HOST_CALLABLE);
+ CASE(CPP_PYTORCH_BINDING);
+ CASE(METAL);
+ CASE(METAL_LIB);
+ CASE(METAL_LIB_ASM);
+ CASE(HOST_SHARED_LIBRARY);
+ CASE(WGSL);
+ CASE(WGSL_SPIRV_ASM);
+ CASE(WGSL_SPIRV);
+#undef CASE
+
+ default:
+ printf("%d # unhandled enumerant", int(targetFormat));
+ }
+ }
+
+ void printScalarType(slang::TypeReflection::ScalarType scalarType)
+ {
+ switch (scalarType)
+ {
+#define CASE(TAG) \
+ case slang::TypeReflection::TAG: \
+ printf("%s", #TAG); \
+ break
+
+ CASE(None);
+ CASE(Void);
+ CASE(Bool);
+ CASE(Int32);
+ CASE(UInt32);
+ CASE(Int64);
+ CASE(UInt64);
+ CASE(Float16);
+ CASE(Float32);
+ CASE(Float64);
+ CASE(Int8);
+ CASE(UInt8);
+ CASE(Int16);
+ CASE(UInt16);
+#undef CASE
+
+ default:
+ printf("%d # unhandled enumerant", int(scalarType));
+ }
+ }
+
+ void printMatrixLayoutMode(SlangMatrixLayoutMode mode)
+ {
+ switch (mode)
+ {
+#define CASE(TAG) \
+ case SLANG_MATRIX_LAYOUT_##TAG: \
+ printf("%s", #TAG); \
+ break
+
+ CASE(MODE_UNKNOWN);
+ CASE(ROW_MAJOR);
+ CASE(COLUMN_MAJOR);
+#undef CASE
+
+ default:
+ printf("%d # unhandled enumerant", int(mode));
+ }
+ }
+};
+
+struct ExampleProgram : public TestBase
+{
+ Result execute(int argc, char* argv[])
+ {
+ parseOption(argc, argv);
+
+ ComPtr<slang::IGlobalSession> globalSession;
+ SLANG_RETURN_ON_FAIL(slang::createGlobalSession(globalSession.writeRef()));
+
+ Slang::List<slang::TargetDesc> targetDescs;
+ for (auto target : kTargets)
+ {
+ auto profile = globalSession->findProfile(target.profile);
+
+ slang::TargetDesc targetDesc;
+ targetDesc.format = target.format;
+ targetDesc.profile = profile;
+ targetDescs.add(targetDesc);
+ }
+
+ slang::SessionDesc sessionDesc;
+ sessionDesc.targetCount = targetDescs.getCount();
+ sessionDesc.targets = targetDescs.getBuffer();
+
+ ComPtr<slang::ISession> session;
+ SLANG_RETURN_ON_FAIL(globalSession->createSession(sessionDesc, session.writeRef()));
+
+ ReflectingPrinting printingContext;
+ printingContext.compileAndReflectPrograms(session);
+
+ return SLANG_OK;
+ }
+};
+
+int main(int argc, char* argv[])
+{
+ ExampleProgram app;
+ if (SLANG_FAILED(app.execute(argc, argv)))
+ {
+ return -1;
+ }
+ return 0;
+}