summaryrefslogtreecommitdiffstats
path: root/tools/slang-reflection-test
diff options
context:
space:
mode:
authortareksander <57038324+tareksander@users.noreply.github.com>2024-11-11 20:32:00 +0100
committerGitHub <noreply@github.com>2024-11-11 11:32:00 -0800
commit7e51180ed80ce1c5718b65c6625dc12fe64c8bfa (patch)
tree6a6451b388d52ba01e8b4190dc498b435c5ea978 /tools/slang-reflection-test
parent98dab05e80a90ddf64c9d31420a3b49ec49e31d1 (diff)
Reflection compiler option (#5507)
* Moved the pretty writer code from slang-reflection-test into core * Moved reflection test code into the slang codebase and added the compiler option -reflection-json to store the reflection data in a separate file. * Documented -reflection-json command line option * moved PrettyWriter from core to compiler-core * Fixed variable shadowing warning * Use File::writeAllText instead of OSFilesystem and write to stdout if - is used as the path * format code * Fixed linker error * Fix COM Ptr life time issues. * Move enum to the end. * Fix formatting. --------- Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com> Co-authored-by: Yong He <yonghe@outlook.com>
Diffstat (limited to 'tools/slang-reflection-test')
-rw-r--r--tools/slang-reflection-test/slang-reflection-test-main.cpp1378
1 files changed, 4 insertions, 1374 deletions
diff --git a/tools/slang-reflection-test/slang-reflection-test-main.cpp b/tools/slang-reflection-test/slang-reflection-test-main.cpp
index 3bfa809fd..aa6f8012a 100644
--- a/tools/slang-reflection-test/slang-reflection-test-main.cpp
+++ b/tools/slang-reflection-test/slang-reflection-test-main.cpp
@@ -1,5 +1,6 @@
// slang-reflection-test-main.cpp
+#include "../../source/compiler-core/slang-pretty-writer.h"
#include "../../source/core/slang-char-util.h"
#include "../../source/core/slang-string-escape-util.h"
#include "../../source/core/slang-string-util.h"
@@ -14,1385 +15,14 @@
using namespace Slang;
-template<typename T>
-struct Range
-{
-public:
- Range(T begin, T end)
- : m_begin(begin), m_end(end)
- {
- }
-
- struct Iterator
- {
- public:
- explicit Iterator(T value)
- : m_value(value)
- {
- }
-
- T operator*() const { return m_value; }
- void operator++() { m_value++; }
-
- bool operator!=(Iterator const& other) { return m_value != other.m_value; }
-
- private:
- T m_value;
- };
-
- Iterator begin() const { return Iterator(m_begin); }
- Iterator end() const { return Iterator(m_end); }
-
-private:
- T m_begin;
- T m_end;
-};
-
-template<typename T>
-Range<T> makeRange(T begin, T end)
-{
- return Range<T>(begin, end);
-}
-
-template<typename T>
-Range<T> makeRange(T end)
-{
- return Range<T>(T(0), end);
-}
-
-
-struct PrettyWriter
-{
- typedef PrettyWriter ThisType;
-
- friend struct CommaTrackerRAII;
-
- struct CommaState
- {
- bool needComma = false;
- };
-
- void writeRaw(const UnownedStringSlice& slice) { m_builder.append(slice); }
- void writeRaw(char const* begin, char const* end);
- void writeRaw(PrettyWriter& writer, char const* begin) { writeRaw(UnownedStringSlice(begin)); }
-
- void writeRawChar(int c) { m_builder.appendChar(char(c)); }
-
- void writeHexChar(int c) { writeRawChar(CharUtil::getHexChar(Index(c))); }
-
- /// Adjusts indentation if at start of a line
- void adjust();
-
- /// Increase indentation
- void indent() { m_indent++; }
- /// Decreate indentation
- void dedent();
-
- /// Write taking into account any CR that might be in a slice
- void write(const UnownedStringSlice& slice);
- void write(char const* text) { write(UnownedStringSlice(text)); }
- void write(char const* text, size_t length) { write(UnownedStringSlice(text, length)); }
-
- /// Write the slice as an escaped string
- void writeEscapedString(const UnownedStringSlice& slice);
-
- /// Call before items in a comma-separated JSON list to emit the comma if/when needed
- void maybeComma();
-
- /// Get the builder the result is being constructed in
- StringBuilder& getBuilder() { return m_builder; }
-
- ThisType& operator<<(const UnownedStringSlice& slice)
- {
- write(slice);
- return *this;
- }
- ThisType& operator<<(const char* text)
- {
- write(text);
- return *this;
- }
- ThisType& operator<<(uint64_t val)
- {
- adjust();
- m_builder << val;
- return *this;
- }
- ThisType& operator<<(int64_t val)
- {
- adjust();
- m_builder << val;
- return *this;
- }
- ThisType& operator<<(int32_t val)
- {
- adjust();
- m_builder << val;
- return *this;
- }
- ThisType& operator<<(uint32_t val)
- {
- adjust();
- m_builder << val;
- return *this;
- }
- ThisType& operator<<(float val)
- {
- adjust();
- // We want to use a specific format, so we use the StringUtil to specify format, and not
- // just use <<
- StringUtil::appendFormat(m_builder, "%f", val);
- return *this;
- }
-
- bool m_startOfLine = true;
- int m_indent = 0;
- CommaState* m_commaState = nullptr;
- StringBuilder m_builder;
-};
-
-void PrettyWriter::writeRaw(char const* begin, char const* end)
-{
- SLANG_ASSERT(end >= begin);
- writeRaw(UnownedStringSlice(begin, end));
-}
-
-void PrettyWriter::adjust()
-{
- // Only indent if at start of a line
- if (m_startOfLine)
- {
- // Output current indentation
- m_builder.appendRepeatedChar(' ', m_indent * 4);
- m_startOfLine = false;
- }
-}
-
-void PrettyWriter::dedent()
-{
- SLANG_ASSERT(m_indent > 0);
- m_indent--;
-}
-
-void PrettyWriter::write(const UnownedStringSlice& slice)
-{
- const auto end = slice.end();
- auto start = slice.begin();
-
- while (start < end)
- {
- const char* cur = start;
-
- // Search for \n if there is one
- while (cur < end && *cur != '\n')
- cur++;
-
- // If there were some chars, adjust and write
- if (cur > start)
- {
- adjust();
- writeRaw(UnownedStringSlice(start, cur));
- }
-
- if (cur < end && *cur == '\n')
- {
- writeRawChar('\n');
- // Skip the CR
- cur++;
- // Mark we are at the start of a line
- m_startOfLine = true;
- }
-
- start = cur;
- }
-}
-
-void PrettyWriter::writeEscapedString(const UnownedStringSlice& slice)
-{
- adjust();
- auto handler = StringEscapeUtil::getHandler(StringEscapeUtil::Style::Cpp);
- StringEscapeUtil::appendQuoted(handler, slice, m_builder);
-}
-
-void PrettyWriter::maybeComma()
-{
- if (auto state = m_commaState)
- {
- if (!state->needComma)
- {
- state->needComma = true;
- return;
- }
- }
-
- write(toSlice(",\n"));
-}
-
-/// Type for tracking whether a comma is needed in a comma-separated JSON list
-struct CommaTrackerRAII
-{
- CommaTrackerRAII(PrettyWriter& writer)
- : m_writer(&writer), m_previousState(writer.m_commaState)
- {
- writer.m_commaState = &m_state;
- }
-
- ~CommaTrackerRAII() { m_writer->m_commaState = m_previousState; }
-
-private:
- PrettyWriter::CommaState m_state;
- PrettyWriter* m_writer;
- PrettyWriter::CommaState* m_previousState;
-};
-
-
-static void emitReflectionVarInfoJSON(PrettyWriter& writer, slang::VariableReflection* var);
-static void emitReflectionTypeLayoutJSON(PrettyWriter& writer, slang::TypeLayoutReflection* type);
-static void emitReflectionTypeJSON(PrettyWriter& writer, slang::TypeReflection* type);
-
-static void emitReflectionVarBindingInfoJSON(
- PrettyWriter& writer,
- SlangParameterCategory category,
- SlangUInt index,
- SlangUInt count,
- SlangUInt space = 0)
-{
- if (category == SLANG_PARAMETER_CATEGORY_UNIFORM)
- {
- writer << "\"kind\": \"uniform\"";
- writer << ", ";
- writer << "\"offset\": " << index;
- writer << ", ";
- writer << "\"size\": " << count;
- }
- else
- {
- writer << "\"kind\": \"";
- switch (category)
- {
-#define CASE(NAME, KIND) \
- case SLANG_PARAMETER_CATEGORY_##NAME: \
- writer.write(toSlice(#KIND)); \
- break
- CASE(CONSTANT_BUFFER, constantBuffer);
- CASE(SHADER_RESOURCE, shaderResource);
- CASE(UNORDERED_ACCESS, unorderedAccess);
- CASE(VARYING_INPUT, varyingInput);
- CASE(VARYING_OUTPUT, varyingOutput);
- CASE(SAMPLER_STATE, samplerState);
- CASE(UNIFORM, uniform);
- CASE(PUSH_CONSTANT_BUFFER, pushConstantBuffer);
- CASE(DESCRIPTOR_TABLE_SLOT, descriptorTableSlot);
- CASE(SPECIALIZATION_CONSTANT, specializationConstant);
- CASE(MIXED, mixed);
- CASE(REGISTER_SPACE, registerSpace);
- CASE(SUB_ELEMENT_REGISTER_SPACE, subElementRegisterSpace);
- CASE(GENERIC, generic);
- CASE(METAL_ARGUMENT_BUFFER_ELEMENT, metalArgumentBufferElement);
-#undef CASE
-
- default:
- writer << "unknown";
- assert(!"unhandled case");
- break;
- }
- writer << "\"";
- if (space && category != SLANG_PARAMETER_CATEGORY_REGISTER_SPACE)
- {
- writer << ", ";
- writer << "\"space\": " << space;
- }
- writer << ", ";
- writer << "\"index\": ";
- writer << index;
- if (count != 1)
- {
- writer << ", ";
- writer << "\"count\": ";
- if (count == SLANG_UNBOUNDED_SIZE)
- {
- writer << "\"unbounded\"";
- }
- else
- {
- writer << count;
- }
- }
- }
-}
-
-static void emitReflectionVarBindingInfoJSON(
- PrettyWriter& writer,
- slang::VariableLayoutReflection* var,
- SlangCompileRequest* request = nullptr,
- int entryPointIndex = -1)
-{
- auto stage = var->getStage();
- if (stage != SLANG_STAGE_NONE)
- {
- writer.maybeComma();
- char const* stageName = "UNKNOWN";
- switch (stage)
- {
- case SLANG_STAGE_VERTEX:
- stageName = "vertex";
- break;
- case SLANG_STAGE_HULL:
- stageName = "hull";
- break;
- case SLANG_STAGE_DOMAIN:
- stageName = "domain";
- break;
- case SLANG_STAGE_GEOMETRY:
- stageName = "geometry";
- break;
- case SLANG_STAGE_FRAGMENT:
- stageName = "fragment";
- break;
- case SLANG_STAGE_COMPUTE:
- stageName = "compute";
- break;
-
- default:
- break;
- }
-
- writer << "\"stage\": \"" << stageName << "\"";
- }
-
- auto typeLayout = var->getTypeLayout();
- auto categoryCount = var->getCategoryCount();
-
- if (categoryCount)
- {
- writer.maybeComma();
- if (categoryCount != 1)
- {
- writer << "\"bindings\": [\n";
- }
- else
- {
- writer << "\"binding\": ";
- }
- writer.indent();
-
- for (uint32_t cc = 0; cc < categoryCount; ++cc)
- {
- auto category = SlangParameterCategory(var->getCategoryByIndex(cc));
- auto index = var->getOffset(category);
- auto space = var->getBindingSpace(category);
- auto count = typeLayout->getSize(category);
-
- // Query the paramater usage for the specified entry point.
- // Note: both `request` and `entryPointIndex` may be invalid here, but that should just
- // make the function return a failure.
- bool used = false;
- bool usedAvailable = spIsParameterLocationUsed(
- request,
- entryPointIndex,
- 0,
- category,
- space,
- index,
- used) == SLANG_OK;
-
- if (cc != 0)
- writer << ",\n";
-
- writer << "{";
-
- emitReflectionVarBindingInfoJSON(writer, category, index, count, space);
-
- if (usedAvailable)
- {
- writer << ", \"used\": ";
- writer << used;
- }
-
- writer << "}";
- }
-
- writer.dedent();
- if (categoryCount != 1)
- {
- writer << "\n]";
- }
- }
-
- if (auto semanticName = var->getSemanticName())
- {
- writer.maybeComma();
- writer << "\"semanticName\": ";
- writer.writeEscapedString(UnownedStringSlice(semanticName));
-
- if (auto semanticIndex = var->getSemanticIndex())
- {
- writer.maybeComma();
- writer << "\"semanticIndex\": " << int(semanticIndex);
- }
- }
-}
-
-static void emitReflectionNameInfoJSON(PrettyWriter& writer, char const* name)
-{
- // TODO: deal with escaping special characters if/when needed
- writer << "\"name\": ";
- writer.writeEscapedString(UnownedStringSlice(name));
-}
-
-static void emitUserAttributes(PrettyWriter& writer, slang::VariableReflection* var);
-
-static void emitReflectionModifierInfoJSON(PrettyWriter& writer, slang::VariableReflection* var)
-{
- if (var->findModifier(slang::Modifier::Shared))
- {
- writer.maybeComma();
- writer << "\"shared\": true";
- }
-
- emitUserAttributes(writer, var);
-}
-
-static void emitUserAttributeJSON(PrettyWriter& writer, slang::UserAttribute* userAttribute)
-{
- writer << "{\n";
- writer.indent();
- writer << "\"name\": \"";
- writer.write(userAttribute->getName());
- writer << "\",\n";
- writer << "\"arguments\": [\n";
- writer.indent();
- for (unsigned int i = 0; i < userAttribute->getArgumentCount(); i++)
- {
- int intVal;
- float floatVal;
- size_t bufSize = 0;
- if (i > 0)
- writer << ",\n";
- if (SLANG_SUCCEEDED(userAttribute->getArgumentValueInt(i, &intVal)))
- {
- writer << intVal;
- }
- else if (SLANG_SUCCEEDED(userAttribute->getArgumentValueFloat(i, &floatVal)))
- {
- writer << floatVal;
- }
- else if (auto str = userAttribute->getArgumentValueString(i, &bufSize))
- {
- writer.write(str, bufSize);
- }
- else
- writer << "\"invalid value\"";
- }
- writer.dedent();
- writer << "\n]\n";
- writer.dedent();
- writer << "}\n";
-}
-
-static void emitUserAttributes(PrettyWriter& writer, slang::TypeReflection* type)
-{
- auto attribCount = type->getUserAttributeCount();
- if (attribCount)
- {
- writer << ",\n\"userAttribs\": [";
- for (unsigned int i = 0; i < attribCount; i++)
- {
- if (i > 0)
- writer << ",\n";
- auto attrib = type->getUserAttributeByIndex(i);
- emitUserAttributeJSON(writer, attrib);
- }
- writer << "]";
- }
-}
-static void emitUserAttributes(PrettyWriter& writer, slang::VariableReflection* var)
-{
- auto attribCount = var->getUserAttributeCount();
- if (attribCount)
- {
- writer << ",\n\"userAttribs\": [";
- for (unsigned int i = 0; i < attribCount; i++)
- {
- if (i > 0)
- writer << ",\n";
- auto attrib = var->getUserAttributeByIndex(i);
- emitUserAttributeJSON(writer, attrib);
- }
- writer << "]";
- }
-}
-
-static void emitReflectionVarLayoutJSON(PrettyWriter& writer, slang::VariableLayoutReflection* var)
-{
- writer << "{\n";
- writer.indent();
-
- CommaTrackerRAII commaTracker(writer);
-
- if (auto name = var->getName())
- {
- writer.maybeComma();
- emitReflectionNameInfoJSON(writer, name);
- }
-
- writer.maybeComma();
- writer << "\"type\": ";
- emitReflectionTypeLayoutJSON(writer, var->getTypeLayout());
-
- emitReflectionModifierInfoJSON(writer, var->getVariable());
-
- emitReflectionVarBindingInfoJSON(writer, var);
-
- emitUserAttributes(writer, var->getVariable());
- writer.dedent();
- writer << "\n}";
-}
-
-static void emitReflectionScalarTypeInfoJSON(PrettyWriter& writer, SlangScalarType scalarType)
-{
- writer << "\"scalarType\": \"";
- switch (scalarType)
- {
- default:
- writer << "unknown";
- assert(!"unhandled case");
- break;
-#define CASE(TAG, ID) \
- case static_cast<SlangScalarType>(slang::TypeReflection::ScalarType::TAG): \
- writer.write(toSlice(#ID)); \
- break
- CASE(Void, void);
- CASE(Bool, bool);
-
- CASE(Int8, int8);
- CASE(UInt8, uint8);
- CASE(Int16, int16);
- CASE(UInt16, uint16);
- CASE(Int32, int32);
- CASE(UInt32, uint32);
- CASE(Int64, int64);
- CASE(UInt64, uint64);
-
- CASE(Float16, float16);
- CASE(Float32, float32);
- CASE(Float64, float64);
-#undef CASE
- }
- writer << "\"";
-}
-
-static void emitReflectionResourceTypeBaseInfoJSON(
- PrettyWriter& writer,
- slang::TypeReflection* type)
-{
- auto shape = type->getResourceShape();
- auto access = type->getResourceAccess();
- writer.maybeComma();
- writer << "\"kind\": \"resource\"";
- writer.maybeComma();
- writer << "\"baseShape\": \"";
- switch (shape & SLANG_RESOURCE_BASE_SHAPE_MASK)
- {
- default:
- writer << "unknown";
- assert(!"unhandled case");
- break;
-
-#define CASE(SHAPE, NAME) \
- case SLANG_##SHAPE: \
- writer.write(toSlice(#NAME)); \
- break
- CASE(TEXTURE_1D, texture1D);
- CASE(TEXTURE_2D, texture2D);
- CASE(TEXTURE_3D, texture3D);
- CASE(TEXTURE_CUBE, textureCube);
- CASE(TEXTURE_BUFFER, textureBuffer);
- CASE(STRUCTURED_BUFFER, structuredBuffer);
- CASE(BYTE_ADDRESS_BUFFER, byteAddressBuffer);
-#undef CASE
- }
- writer << "\"";
- if (shape & SLANG_TEXTURE_ARRAY_FLAG)
- {
- writer.maybeComma();
- writer << "\"array\": true";
- }
- if (shape & SLANG_TEXTURE_MULTISAMPLE_FLAG)
- {
- writer.maybeComma();
- writer << "\"multisample\": true";
- }
- if (shape & SLANG_TEXTURE_FEEDBACK_FLAG)
- {
- writer.maybeComma();
- writer << "\"feedback\": true";
- }
-
- if (access != SLANG_RESOURCE_ACCESS_READ)
- {
- writer.maybeComma();
- writer << "\"access\": \"";
- switch (access)
- {
- default:
- writer << "unknown";
- assert(!"unhandled case");
- break;
-
- case SLANG_RESOURCE_ACCESS_READ:
- break;
- case SLANG_RESOURCE_ACCESS_WRITE:
- writer << "write";
- break;
- case SLANG_RESOURCE_ACCESS_READ_WRITE:
- writer << "readWrite";
- break;
- case SLANG_RESOURCE_ACCESS_RASTER_ORDERED:
- writer << "rasterOrdered";
- break;
- case SLANG_RESOURCE_ACCESS_APPEND:
- writer << "append";
- break;
- case SLANG_RESOURCE_ACCESS_CONSUME:
- writer << "consume";
- break;
- case SLANG_RESOURCE_ACCESS_FEEDBACK:
- writer << "feedback";
- break;
- }
- writer << "\"";
- }
-}
-
-
-static void emitReflectionTypeInfoJSON(PrettyWriter& writer, slang::TypeReflection* type)
-{
- auto kind = type->getKind();
- switch (kind)
- {
- case slang::TypeReflection::Kind::SamplerState:
- writer.maybeComma();
- writer << "\"kind\": \"samplerState\"";
- break;
-
- case slang::TypeReflection::Kind::Resource:
- {
- emitReflectionResourceTypeBaseInfoJSON(writer, type);
-
- // TODO: We should really print the result type for all resource
- // types, but current test output depends on the old behavior, so
- // we only add result type output for structured buffers at first.
- //
- auto shape = type->getResourceShape();
- switch (shape & SLANG_RESOURCE_BASE_SHAPE_MASK)
- {
- default:
- break;
-
- case SLANG_STRUCTURED_BUFFER:
- if (auto resultType = type->getResourceResultType())
- {
- writer.maybeComma();
- writer << "\"resultType\": ";
- emitReflectionTypeJSON(writer, resultType);
- }
- break;
- }
- }
- break;
-
- case slang::TypeReflection::Kind::ConstantBuffer:
- writer.maybeComma();
- writer << "\"kind\": \"constantBuffer\"";
- writer.maybeComma();
- writer << "\"elementType\": ";
- emitReflectionTypeJSON(writer, type->getElementType());
- break;
-
- case slang::TypeReflection::Kind::ParameterBlock:
- writer.maybeComma();
- writer << "\"kind\": \"parameterBlock\"";
- writer.maybeComma();
- writer << "\"elementType\": ";
- emitReflectionTypeJSON(writer, type->getElementType());
- break;
-
- case slang::TypeReflection::Kind::TextureBuffer:
- writer.maybeComma();
- writer << "\"kind\": \"textureBuffer\"";
- writer.maybeComma();
- writer << "\"elementType\": ";
- emitReflectionTypeJSON(writer, type->getElementType());
- break;
-
- case slang::TypeReflection::Kind::ShaderStorageBuffer:
- writer.maybeComma();
- writer << "\"kind\": \"shaderStorageBuffer\"";
- writer.maybeComma();
- writer << "\"elementType\": ";
- emitReflectionTypeJSON(writer, type->getElementType());
- break;
-
- case slang::TypeReflection::Kind::Scalar:
- writer.maybeComma();
- writer << "\"kind\": \"scalar\"";
- writer.maybeComma();
- emitReflectionScalarTypeInfoJSON(writer, SlangScalarType(type->getScalarType()));
- break;
-
- case slang::TypeReflection::Kind::Vector:
- writer.maybeComma();
- writer << "\"kind\": \"vector\"";
- writer.maybeComma();
- writer << "\"elementCount\": ";
- writer << int(type->getElementCount());
- writer.maybeComma();
- writer << "\"elementType\": ";
- emitReflectionTypeJSON(writer, type->getElementType());
- break;
-
- case slang::TypeReflection::Kind::Matrix:
- writer.maybeComma();
- writer << "\"kind\": \"matrix\"";
- writer.maybeComma();
- writer << "\"rowCount\": ";
- writer << type->getRowCount();
- writer.maybeComma();
- writer << "\"columnCount\": ";
- writer << type->getColumnCount();
- writer.maybeComma();
- writer << "\"elementType\": ";
- emitReflectionTypeJSON(writer, type->getElementType());
- break;
-
- case slang::TypeReflection::Kind::Array:
- {
- auto arrayType = type;
- writer.maybeComma();
- writer << "\"kind\": \"array\"";
- writer.maybeComma();
- writer << "\"elementCount\": ";
- writer << int(arrayType->getElementCount());
- writer.maybeComma();
- writer << "\"elementType\": ";
- emitReflectionTypeJSON(writer, arrayType->getElementType());
- }
- break;
- case slang::TypeReflection::Kind::Pointer:
- {
- auto pointerType = type;
- writer.maybeComma();
- writer << "\"kind\": \"pointer\"";
- writer.maybeComma();
- writer << "\"targetType\": ";
- emitReflectionTypeJSON(writer, pointerType->getElementType());
- }
- break;
-
- case slang::TypeReflection::Kind::Struct:
- {
- writer.maybeComma();
- writer << "\"kind\": \"struct\"";
- writer.maybeComma();
- writer << "\"fields\": [\n";
- writer.indent();
-
- auto structType = type;
- auto fieldCount = structType->getFieldCount();
- for (uint32_t ff = 0; ff < fieldCount; ++ff)
- {
- if (ff != 0)
- writer << ",\n";
- emitReflectionVarInfoJSON(writer, structType->getFieldByIndex(ff));
- }
- writer.dedent();
- writer << "\n]";
- }
- break;
-
- case slang::TypeReflection::Kind::GenericTypeParameter:
- writer.maybeComma();
- writer << "\"kind\": \"GenericTypeParameter\"";
- writer.maybeComma();
- emitReflectionNameInfoJSON(writer, type->getName());
- break;
- case slang::TypeReflection::Kind::Interface:
- writer.maybeComma();
- writer << "\"kind\": \"Interface\"";
- writer.maybeComma();
- emitReflectionNameInfoJSON(writer, type->getName());
- break;
- case slang::TypeReflection::Kind::Feedback:
- writer.maybeComma();
- writer << "\"kind\": \"Feedback\"";
- writer.maybeComma();
- emitReflectionNameInfoJSON(writer, type->getName());
- break;
- case slang::TypeReflection::Kind::DynamicResource:
- writer.maybeComma();
- writer << "\"kind\": \"DynamicResource\"";
- break;
- default:
- assert(!"unhandled case");
- break;
- }
- emitUserAttributes(writer, type);
-}
-
-static void emitReflectionParameterGroupTypeLayoutInfoJSON(
- PrettyWriter& writer,
- slang::TypeLayoutReflection* typeLayout,
- const char* kind)
-{
- writer << "\"kind\": \"";
- writer.write(kind);
- writer << "\"";
-
- writer << ",\n\"elementType\": ";
- emitReflectionTypeLayoutJSON(writer, typeLayout->getElementTypeLayout());
-
- // Note: There is a subtle detail below when it comes to the
- // container/element variable layouts that get nested inside
- // a parameter group type layout.
- //
- // A top-level parameter group type layout like `ConstantBuffer<Foo>`
- // needs to store both information about the `ConstantBuffer` part of
- // things (e.g., it might consume 1 `binding`), as well as the `Foo`
- // part (e.g., it might consume 4 bytes plus 1 `binding`), and there
- // is offset information for each.
- //
- // The "element" part is easy: it is a variable layout for a variable
- // of type `Foo`. The actual variable will be null, but everything else
- // will be filled in as a client would expect.
- //
- // The "container" part is thornier: what should the type and type
- // layout of the "container" variable be? The obvious answer (which
- // the Slang reflection implementation uses today) is that the type
- // is the type of the parameter group itself (e.g., `ConstantBuffer<Foo>`),
- // and the layout is a dummy `TypeLayout` that just reflects the
- // resource usage of the "container" part of things.
- //
- // That means that at runtime the "container var layout" will have
- // a parameter group type (e.g., `TYPE_KIND_CONSTANT_BUFFER`)
- // but its type layotu will be a base `TypeLayout` and not a
- // `ParameterGroupLayout` (since that would introduce infinite regress).
- //
- // We thus have to guard here against the recursive path where
- // we are emitting reflection info for the "container" part of things.
- //
- // TODO: We should probably
-
- {
- CommaTrackerRAII commaTracker(writer);
-
- writer << ",\n\"containerVarLayout\": {\n";
- writer.indent();
- emitReflectionVarBindingInfoJSON(writer, typeLayout->getContainerVarLayout());
- writer.dedent();
- writer << "\n}";
- }
-
- writer << ",\n\"elementVarLayout\": ";
- emitReflectionVarLayoutJSON(writer, typeLayout->getElementVarLayout());
-}
-
-static void emitReflectionTypeLayoutInfoJSON(
- PrettyWriter& writer,
- slang::TypeLayoutReflection* typeLayout)
-{
- switch (typeLayout->getKind())
- {
- default:
- emitReflectionTypeInfoJSON(writer, typeLayout->getType());
- break;
-
- case slang::TypeReflection::Kind::Pointer:
- {
- auto valueTypeLayout = typeLayout->getElementTypeLayout();
- SLANG_ASSERT(valueTypeLayout);
-
- writer.maybeComma();
- writer << "\"kind\": \"pointer\"";
-
- writer.maybeComma();
- writer << "\"valueType\": ";
-
- auto typeName = valueTypeLayout->getType()->getName();
-
- if (typeName && typeName[0])
- {
- // TODO(JS):
- // We can't emit the type layout, because the type could contain
- // a pointer and we end up in a recursive loop. For now we output the typename.
- writer.writeEscapedString(UnownedStringSlice(typeName));
- }
- else
- {
- // TODO(JS): We will need to generate name that we will associate with this type
- // as it doesn't seem to have one
- writer.writeEscapedString(toSlice("unknown name!"));
- SLANG_ASSERT(!"Doesn't have an associated name");
- }
-
- /*
- emitReflectionTypeLayoutJSON(
- writer,
- valueTypeLayout); */
- }
- break;
- case slang::TypeReflection::Kind::Array:
- {
- auto arrayTypeLayout = typeLayout;
- auto elementTypeLayout = arrayTypeLayout->getElementTypeLayout();
- writer.maybeComma();
- writer << "\"kind\": \"array\"";
-
- writer.maybeComma();
- writer << "\"elementCount\": ";
- writer << int(arrayTypeLayout->getElementCount());
-
- writer.maybeComma();
- writer << "\"elementType\": ";
- emitReflectionTypeLayoutJSON(writer, elementTypeLayout);
-
- if (arrayTypeLayout->getSize(SLANG_PARAMETER_CATEGORY_UNIFORM) != 0)
- {
- writer.maybeComma();
- writer << "\"uniformStride\": ";
- writer << int(arrayTypeLayout->getElementStride(SLANG_PARAMETER_CATEGORY_UNIFORM));
- }
- }
- break;
-
- case slang::TypeReflection::Kind::Struct:
- {
- auto structTypeLayout = typeLayout;
-
- writer.maybeComma();
- writer << "\"kind\": \"struct\"";
- if (auto name = structTypeLayout->getName())
- {
- writer.maybeComma();
- emitReflectionNameInfoJSON(writer, structTypeLayout->getName());
- }
- writer.maybeComma();
- writer << "\"fields\": [\n";
- writer.indent();
-
- auto fieldCount = structTypeLayout->getFieldCount();
- for (uint32_t ff = 0; ff < fieldCount; ++ff)
- {
- if (ff != 0)
- writer << ",\n";
- emitReflectionVarLayoutJSON(writer, structTypeLayout->getFieldByIndex(ff));
- }
- writer.dedent();
- writer << "\n]";
- emitUserAttributes(writer, structTypeLayout->getType());
- }
- break;
-
- case slang::TypeReflection::Kind::ConstantBuffer:
- emitReflectionParameterGroupTypeLayoutInfoJSON(writer, typeLayout, "constantBuffer");
- break;
-
- case slang::TypeReflection::Kind::ParameterBlock:
- emitReflectionParameterGroupTypeLayoutInfoJSON(writer, typeLayout, "parameterBlock");
- break;
-
- case slang::TypeReflection::Kind::TextureBuffer:
- emitReflectionParameterGroupTypeLayoutInfoJSON(writer, typeLayout, "textureBuffer");
- break;
-
- case slang::TypeReflection::Kind::ShaderStorageBuffer:
- writer.maybeComma();
- writer << "\"kind\": \"shaderStorageBuffer\"";
-
- writer.maybeComma();
- writer << "\"elementType\": ";
- emitReflectionTypeLayoutJSON(writer, typeLayout->getElementTypeLayout());
- break;
- case slang::TypeReflection::Kind::GenericTypeParameter:
- writer.maybeComma();
- writer << "\"kind\": \"GenericTypeParameter\"";
-
- writer.maybeComma();
- emitReflectionNameInfoJSON(writer, typeLayout->getName());
- break;
- case slang::TypeReflection::Kind::Interface:
- writer.maybeComma();
- writer << "\"kind\": \"Interface\"";
-
- writer.maybeComma();
- emitReflectionNameInfoJSON(writer, typeLayout->getName());
- break;
-
- case slang::TypeReflection::Kind::Resource:
- {
- // Some resource types (notably structured buffers)
- // encode layout information for their result/element
- // type, but others don't. We need to check for
- // the relevant cases here.
- //
- auto type = typeLayout->getType();
- auto shape = type->getResourceShape();
-
- const auto baseType = shape & SLANG_RESOURCE_BASE_SHAPE_MASK;
-
- if (baseType == SLANG_STRUCTURED_BUFFER)
- {
- emitReflectionResourceTypeBaseInfoJSON(writer, type);
-
- if (auto resultTypeLayout = typeLayout->getElementTypeLayout())
- {
- writer.maybeComma();
- writer << "\"resultType\": ";
- emitReflectionTypeLayoutJSON(writer, resultTypeLayout);
- }
- }
- else if (shape & SLANG_TEXTURE_FEEDBACK_FLAG)
- {
- emitReflectionResourceTypeBaseInfoJSON(writer, type);
-
- if (auto resultType = typeLayout->getResourceResultType())
- {
- writer.maybeComma();
- writer << "\"resultType\": ";
- emitReflectionTypeJSON(writer, resultType);
- }
- }
- else
- {
- emitReflectionTypeInfoJSON(writer, type);
- }
- }
- break;
- }
-}
-
-static void emitReflectionTypeLayoutJSON(
- PrettyWriter& writer,
- slang::TypeLayoutReflection* typeLayout)
-{
- CommaTrackerRAII commaTracker(writer);
- writer << "{\n";
- writer.indent();
- emitReflectionTypeLayoutInfoJSON(writer, typeLayout);
- writer.dedent();
- writer << "\n}";
-}
-
-static void emitReflectionTypeJSON(PrettyWriter& writer, slang::TypeReflection* type)
-{
- CommaTrackerRAII commaTracker(writer);
- writer << "{\n";
- writer.indent();
- emitReflectionTypeInfoJSON(writer, type);
- writer.dedent();
- writer << "\n}";
-}
-
-static void emitReflectionVarInfoJSON(PrettyWriter& writer, slang::VariableReflection* var)
-{
- emitReflectionNameInfoJSON(writer, var->getName());
-
- emitReflectionModifierInfoJSON(writer, var);
-
- writer << ",\n";
- writer << "\"type\": ";
- emitReflectionTypeJSON(writer, var->getType());
-}
-
-static void emitReflectionParamJSON(PrettyWriter& writer, slang::VariableLayoutReflection* param)
-{
- // TODO: This function is likely redundant with `emitReflectionVarLayoutJSON`
- // and we should try to collapse them into one.
-
- writer << "{\n";
- writer.indent();
-
- CommaTrackerRAII commaTracker(writer);
-
- if (auto name = param->getName())
- {
- writer.maybeComma();
- emitReflectionNameInfoJSON(writer, name);
- }
-
- emitReflectionModifierInfoJSON(writer, param->getVariable());
-
- emitReflectionVarBindingInfoJSON(writer, param);
-
- writer.maybeComma();
- writer << "\"type\": ";
- emitReflectionTypeLayoutJSON(writer, param->getTypeLayout());
-
- writer.dedent();
- writer << "\n}";
-}
-
-
-static void emitEntryPointParamJSON(
- PrettyWriter& writer,
- slang::VariableLayoutReflection* param,
- SlangCompileRequest* request,
- int entryPointIndex)
-{
- writer << "{\n";
- writer.indent();
-
- if (auto name = param->getName())
- {
- emitReflectionNameInfoJSON(writer, name);
- }
-
- emitReflectionVarBindingInfoJSON(writer, param, request, entryPointIndex);
-
- writer.dedent();
- writer << "\n}";
-}
-
-
-static void emitReflectionTypeParamJSON(
- PrettyWriter& writer,
- slang::TypeParameterReflection* typeParam)
-{
- writer << "{\n";
- writer.indent();
- emitReflectionNameInfoJSON(writer, typeParam->getName());
- writer << ",\n";
- writer << "\"constraints\": \n";
- writer << "[\n";
- writer.indent();
- auto constraintCount = typeParam->getConstraintCount();
- for (auto ee : makeRange(constraintCount))
- {
- if (ee != 0)
- writer << ",\n";
- writer << "{\n";
- writer.indent();
- CommaTrackerRAII commaTracker(writer);
- emitReflectionTypeInfoJSON(writer, typeParam->getConstraintByIndex(ee));
- writer.dedent();
- writer << "\n}";
- }
- writer.dedent();
- writer << "\n]";
- writer.dedent();
- writer << "\n}";
-}
-
-static void emitReflectionEntryPointJSON(
- PrettyWriter& writer,
- SlangCompileRequest* request,
- slang::ShaderReflection* programReflection,
- int entryPointIndex)
-{
- slang::EntryPointReflection* entryPoint =
- programReflection->getEntryPointByIndex(entryPointIndex);
-
- writer << "{\n";
- writer.indent();
-
- emitReflectionNameInfoJSON(writer, entryPoint->getName());
-
- switch (entryPoint->getStage())
- {
- case SLANG_STAGE_VERTEX:
- writer << ",\n\"stage:\": \"vertex\"";
- break;
- case SLANG_STAGE_HULL:
- writer << ",\n\"stage:\": \"hull\"";
- break;
- case SLANG_STAGE_DOMAIN:
- writer << ",\n\"stage:\": \"domain\"";
- break;
- case SLANG_STAGE_GEOMETRY:
- writer << ",\n\"stage:\": \"geometry\"";
- break;
- case SLANG_STAGE_FRAGMENT:
- writer << ",\n\"stage:\": \"fragment\"";
- break;
- case SLANG_STAGE_COMPUTE:
- writer << ",\n\"stage:\": \"compute\"";
- break;
- default:
- break;
- }
-
- auto parameterCount = entryPoint->getParameterCount();
- if (parameterCount)
- {
- writer << ",\n\"parameters\": [\n";
- writer.indent();
-
- for (auto pp : makeRange(parameterCount))
- {
- if (pp != 0)
- writer << ",\n";
-
- auto parameter = entryPoint->getParameterByIndex(pp);
- emitReflectionParamJSON(writer, parameter);
- }
-
- writer.dedent();
- writer << "\n]";
- }
- if (entryPoint->usesAnySampleRateInput())
- {
- writer << ",\n\"usesAnySampleRateInput\": true";
- }
- if (auto resultVarLayout = entryPoint->getResultVarLayout())
- {
- writer << ",\n\"result:\": ";
- emitReflectionParamJSON(writer, resultVarLayout);
- }
-
- if (entryPoint->getStage() == SLANG_STAGE_COMPUTE)
- {
- SlangUInt threadGroupSize[3];
- entryPoint->getComputeThreadGroupSize(3, threadGroupSize);
-
- writer << ",\n\"threadGroupSize\": [";
- for (int ii = 0; ii < 3; ++ii)
- {
- if (ii != 0)
- writer << ", ";
- writer << threadGroupSize[ii];
- }
- writer << "]";
- }
-
- // If code generation has been performed, print out the parameter usage by this entry point.
- if ((request->getCompileFlags() & SLANG_COMPILE_FLAG_NO_CODEGEN) == 0)
- {
- writer << ",\n\"bindings\": [\n";
- writer.indent();
-
- auto parameterCount = programReflection->getParameterCount();
- for (auto pp : makeRange(parameterCount))
- {
- if (pp != 0)
- writer << ",\n";
-
- auto parameter = programReflection->getParameterByIndex(pp);
- emitEntryPointParamJSON(writer, parameter, request, entryPointIndex);
- }
-
- writer.dedent();
- writer << "\n]";
- }
-
- writer.dedent();
- writer << "\n}";
-}
-
-static void emitReflectionJSON(
- PrettyWriter& writer,
- SlangCompileRequest* request,
- slang::ShaderReflection* programReflection)
-{
- writer << "{\n";
- writer.indent();
- writer << "\"parameters\": [\n";
- writer.indent();
-
- auto parameterCount = programReflection->getParameterCount();
- for (auto pp : makeRange(parameterCount))
- {
- if (pp != 0)
- writer << ",\n";
-
- auto parameter = programReflection->getParameterByIndex(pp);
- emitReflectionParamJSON(writer, parameter);
- }
-
- writer.dedent();
- writer << "\n]";
-
- auto entryPointCount = programReflection->getEntryPointCount();
- if (entryPointCount)
- {
- writer << ",\n\"entryPoints\": [\n";
- writer.indent();
-
- for (auto ee : makeRange(entryPointCount))
- {
- if (ee != 0)
- writer << ",\n";
-
- emitReflectionEntryPointJSON(writer, request, programReflection, (int)ee);
- }
-
- writer.dedent();
- writer << "\n]";
- }
-
- auto genParamCount = programReflection->getTypeParameterCount();
- if (genParamCount)
- {
- writer << ",\n\"typeParams\":\n";
- writer << "[\n";
- writer.indent();
- for (auto ee : makeRange(genParamCount))
- {
- if (ee != 0)
- writer << ",\n";
-
- auto typeParam = programReflection->getTypeParameterByIndex(ee);
- emitReflectionTypeParamJSON(writer, typeParam);
- }
- writer.dedent();
- writer << "\n]";
- }
-
- {
- SlangUInt count = programReflection->getHashedStringCount();
- if (count)
- {
- writer << ",\n\"hashedStrings\": {\n";
- writer.indent();
-
- for (SlangUInt i = 0; i < count; ++i)
- {
- if (i)
- {
- writer << ",\n";
- }
-
- size_t charsCount;
- const char* chars = programReflection->getHashedString(i, &charsCount);
- const int hash = spComputeStringHash(chars, charsCount);
-
- writer.writeEscapedString(UnownedStringSlice(chars, charsCount));
- writer << ": ";
-
- writer << hash;
- }
-
- writer.dedent();
- writer << "\n}\n";
- }
- }
-
- writer.dedent();
- writer << "\n}\n";
-}
-
void emitReflectionJSON(SlangCompileRequest* request, SlangReflection* reflection)
{
- auto programReflection = (slang::ShaderReflection*)reflection;
-
- PrettyWriter writer;
-
- emitReflectionJSON(writer, request, programReflection);
+ ComPtr<ISlangBlob> b;
- // Get the contents of the writer
- const auto slice = writer.getBuilder().getUnownedSlice();
+ spReflection_ToJson(reflection, request, b.writeRef());
// Output the writer content to out stream
- StdWriters::getOut().write(slice.begin(), slice.getLength());
+ StdWriters::getOut().write((const char*)b->getBufferPointer(), b->getBufferSize());
}
static SlangResult maybeDumpDiagnostic(SlangResult res, SlangCompileRequest* request)