From c5410bac82edf81379235f42750e59b4627a726a Mon Sep 17 00:00:00 2001 From: tareksander <57038324+tareksander@users.noreply.github.com> Date: Wed, 6 Nov 2024 12:57:53 +0100 Subject: [PATCH 01/11] Moved the pretty writer code from slang-reflection-test into core --- source/core/slang-pretty-writer.cpp | 84 ++++++++ source/core/slang-pretty-writer.h | 122 ++++++++++++ .../slang-reflection-test-main.cpp | 185 +----------------- 3 files changed, 207 insertions(+), 184 deletions(-) create mode 100644 source/core/slang-pretty-writer.cpp create mode 100644 source/core/slang-pretty-writer.h diff --git a/source/core/slang-pretty-writer.cpp b/source/core/slang-pretty-writer.cpp new file mode 100644 index 0000000000..3e842683f4 --- /dev/null +++ b/source/core/slang-pretty-writer.cpp @@ -0,0 +1,84 @@ +#include "slang-pretty-writer.h" +#include "slang-string-escape-util.h" + +namespace Slang +{ + +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")); +} + +} diff --git a/source/core/slang-pretty-writer.h b/source/core/slang-pretty-writer.h new file mode 100644 index 0000000000..6ee2a1b547 --- /dev/null +++ b/source/core/slang-pretty-writer.h @@ -0,0 +1,122 @@ +#ifndef SLANG_CORE_PRETTY_WRITER_H +#define SLANG_CORE_PRETTY_WRITER_H + + +#include "slang-char-util.h" +#include "slang-string.h" +#include "slang-string-util.h" + +namespace Slang +{ + +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; +}; + +/// 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; +}; + +}; + + +#endif diff --git a/tools/slang-reflection-test/slang-reflection-test-main.cpp b/tools/slang-reflection-test/slang-reflection-test-main.cpp index 3bfa809fdb..052255a146 100644 --- a/tools/slang-reflection-test/slang-reflection-test-main.cpp +++ b/tools/slang-reflection-test/slang-reflection-test-main.cpp @@ -4,6 +4,7 @@ #include "../../source/core/slang-string-escape-util.h" #include "../../source/core/slang-string-util.h" #include "../../source/core/slang-test-tool-util.h" +#include "../../source/core/slang-pretty-writer.h" #include "slang-com-helper.h" #include "slang.h" @@ -61,190 +62,6 @@ Range makeRange(T 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); From cab8a175c2bfbf84c0ed5a441e31b079e5858908 Mon Sep 17 00:00:00 2001 From: tareksander <57038324+tareksander@users.noreply.github.com> Date: Wed, 6 Nov 2024 21:36:36 +0100 Subject: [PATCH 02/11] Moved reflection test code into the slang codebase and added the compiler option -reflection-json to store the reflection data in a separate file. --- include/slang.h | 1 + source/core/slang-pretty-writer.h | 4 +- source/slang/slang-options.cpp | 13 + source/slang/slang-reflection-json.cpp | 1195 +++++++++++++++++ source/slang/slang-reflection-json.h | 14 + source/slang/slang.cpp | 16 + .../slang-reflection-test-main.cpp | 1187 +--------------- 7 files changed, 1243 insertions(+), 1187 deletions(-) create mode 100644 source/slang/slang-reflection-json.cpp create mode 100644 source/slang/slang-reflection-json.h diff --git a/include/slang.h b/include/slang.h index 2999996e90..3f993355d4 100644 --- a/include/slang.h +++ b/include/slang.h @@ -884,6 +884,7 @@ typedef uint32_t SlangSizeT; DumpWarningDiagnostics, InputFilesRemain, EmitIr, // bool + EmitReflectionJSON, // bool ReportDownstreamTime, // bool ReportPerfBenchmark, // bool ReportCheckpointIntermediates, // bool diff --git a/source/core/slang-pretty-writer.h b/source/core/slang-pretty-writer.h index 6ee2a1b547..1af90cbb0d 100644 --- a/source/core/slang-pretty-writer.h +++ b/source/core/slang-pretty-writer.h @@ -22,7 +22,7 @@ struct PrettyWriter 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 writeRaw(char const* begin) { writeRaw(UnownedStringSlice(begin)); } void writeRawChar(int c) { m_builder.appendChar(char(c)); } @@ -116,7 +116,7 @@ struct CommaTrackerRAII PrettyWriter::CommaState* m_previousState; }; -}; +} #endif diff --git a/source/slang/slang-options.cpp b/source/slang/slang-options.cpp index 9353acd356..190bbc1654 100644 --- a/source/slang/slang-options.cpp +++ b/source/slang/slang-options.cpp @@ -524,7 +524,12 @@ void initCommandOptions(CommandOptions& options) nullptr, "Preserve all resource parameters in the output code, even if they are not used by the " "shader."}, + {OptionKind::EmitReflectionJSON, + "-reflection-json", + "reflection-json ", + "Emit reflection data in JSON format to a file."}, }; + _addOptions(makeConstArrayView(generalOpts), options); @@ -2663,6 +2668,14 @@ SlangResult OptionsParser::_parse(int argc, char const* const* argv) addOutputPath(outputPath.value.getBuffer()); break; } + case OptionKind::EmitReflectionJSON: + { + CommandLineArg outputPath; + SLANG_RETURN_ON_FAIL(m_reader.expectArg(outputPath)); + + linkage->m_optionSet.set(CompilerOptionName::EmitReflectionJSON, outputPath.value); + break; + } case OptionKind::DepFile: { CommandLineArg dependencyPath; diff --git a/source/slang/slang-reflection-json.cpp b/source/slang/slang-reflection-json.cpp new file mode 100644 index 0000000000..520648abf0 --- /dev/null +++ b/source/slang/slang-reflection-json.cpp @@ -0,0 +1,1195 @@ + +#include "slang-reflection-json.h" +#include "../core/slang-pretty-writer.h" +#include "slang.h" + +template +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 +Range makeRange(T begin, T end) +{ + return Range(begin, end); +} + +template +Range makeRange(T end) +{ + return Range(T(0), end); +} + +namespace Slang +{ + +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(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` + // 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`), + // 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, name); + } + 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, PrettyWriter& writer) +{ + auto programReflection = (slang::ShaderReflection*)reflection; + emitReflectionJSON(writer, request, programReflection); +} + +} diff --git a/source/slang/slang-reflection-json.h b/source/slang/slang-reflection-json.h new file mode 100644 index 0000000000..89360e8031 --- /dev/null +++ b/source/slang/slang-reflection-json.h @@ -0,0 +1,14 @@ +#ifndef SLANG_REFLECTION_JSON_H +#define SLANG_REFLECTION_JSON_H + +#include "slang.h" +#include "../core/slang-pretty-writer.h" + +namespace Slang +{ + +void emitReflectionJSON(SlangCompileRequest* request, SlangReflection* reflection, PrettyWriter& writer); + +} + +#endif diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index eb938f49c1..5f26dcf3b2 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -37,6 +37,7 @@ #include "slang-serialize-ir.h" #include "slang-tag-version.h" #include "slang-type-layout.h" +#include "slang-reflection-json.h" #include @@ -6856,6 +6857,21 @@ SlangResult EndToEndCompileRequest::compile() } } } + + auto reflectionPath = getOptionSet().getStringOption(CompilerOptionName::EmitReflectionJSON); + if (reflectionPath.getLength() != 0) + { + reflectionPath.appendChar('\0'); + auto bufferWriter = PrettyWriter(); + emitReflectionJSON(this, this->getReflection(), bufferWriter); + if (SLANG_FAILED(OSFileSystem::getMutableSingleton()->saveFile(reflectionPath.getBuffer(), bufferWriter.getBuilder().getBuffer(), bufferWriter.getBuilder().getLength()))) + { + getSink()->diagnose( + SourceLoc(), + Diagnostics::unableToWriteFile, + reflectionPath); + } + } return res; } diff --git a/tools/slang-reflection-test/slang-reflection-test-main.cpp b/tools/slang-reflection-test/slang-reflection-test-main.cpp index 052255a146..05e4922e35 100644 --- a/tools/slang-reflection-test/slang-reflection-test-main.cpp +++ b/tools/slang-reflection-test/slang-reflection-test-main.cpp @@ -5,6 +5,7 @@ #include "../../source/core/slang-string-util.h" #include "../../source/core/slang-test-tool-util.h" #include "../../source/core/slang-pretty-writer.h" +#include "../../source/slang/slang-reflection-json.h" #include "slang-com-helper.h" #include "slang.h" @@ -15,1195 +16,11 @@ using namespace Slang; -template -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 -Range makeRange(T begin, T end) -{ - return Range(begin, end); -} - -template -Range makeRange(T end) -{ - return Range(T(0), end); -} - - - -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(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` - // 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`), - // 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); + emitReflectionJSON(request, reflection, writer); // Get the contents of the writer const auto slice = writer.getBuilder().getUnownedSlice(); From 009b0e63de7ba3e0b78764b4814488764810cc9b Mon Sep 17 00:00:00 2001 From: tareksander <57038324+tareksander@users.noreply.github.com> Date: Wed, 6 Nov 2024 22:16:06 +0100 Subject: [PATCH 03/11] Documented -reflection-json command line option --- docs/command-line-slangc-reference.md | Bin 78338 -> 78686 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/command-line-slangc-reference.md b/docs/command-line-slangc-reference.md index a6df65ab27955a3dd7ccdea51dee690c9ee1a349..36493b2f96f7ce1c27ad37cd4bf4c63a08a0f921 100644 GIT binary patch delta 172 zcmZpA!gB8!%Z3a&ZaW4AhD?SO23rQD$p_;^xpf(`7>XJ48S;R9MPbp&1VdN|>7lMmLbPj=uIn0!o9inoBF2xv|^kW6IAoxDFznl+K3gduVA ve@Rt&kezuzo4pu<8T=XifUGp2+9HNrpxP1!y~&Ki(#=J3+l%BFCkO)om+UFP delta 22 ecmccjjHT%b%Z3cO$rJRsn$O8^KPS&PK^Opej0t@J From 733f217031d8e8fbd788c2c184f99547d66d6085 Mon Sep 17 00:00:00 2001 From: tareksander <57038324+tareksander@users.noreply.github.com> Date: Thu, 7 Nov 2024 16:52:39 +0100 Subject: [PATCH 04/11] moved PrettyWriter from core to compiler-core --- source/{core => compiler-core}/slang-pretty-writer.cpp | 2 +- source/{core => compiler-core}/slang-pretty-writer.h | 6 +++--- source/slang/slang-reflection-json.cpp | 1 - source/slang/slang-reflection-json.h | 2 +- tools/slang-reflection-test/slang-reflection-test-main.cpp | 2 +- 5 files changed, 6 insertions(+), 7 deletions(-) rename source/{core => compiler-core}/slang-pretty-writer.cpp (97%) rename source/{core => compiler-core}/slang-pretty-writer.h (96%) diff --git a/source/core/slang-pretty-writer.cpp b/source/compiler-core/slang-pretty-writer.cpp similarity index 97% rename from source/core/slang-pretty-writer.cpp rename to source/compiler-core/slang-pretty-writer.cpp index 3e842683f4..c29ca544cd 100644 --- a/source/core/slang-pretty-writer.cpp +++ b/source/compiler-core/slang-pretty-writer.cpp @@ -1,5 +1,5 @@ #include "slang-pretty-writer.h" -#include "slang-string-escape-util.h" +#include "../core/slang-string-escape-util.h" namespace Slang { diff --git a/source/core/slang-pretty-writer.h b/source/compiler-core/slang-pretty-writer.h similarity index 96% rename from source/core/slang-pretty-writer.h rename to source/compiler-core/slang-pretty-writer.h index 1af90cbb0d..5f6d33fd90 100644 --- a/source/core/slang-pretty-writer.h +++ b/source/compiler-core/slang-pretty-writer.h @@ -2,9 +2,9 @@ #define SLANG_CORE_PRETTY_WRITER_H -#include "slang-char-util.h" -#include "slang-string.h" -#include "slang-string-util.h" +#include "../core/slang-char-util.h" +#include "../core/slang-string.h" +#include "../core/slang-string-util.h" namespace Slang { diff --git a/source/slang/slang-reflection-json.cpp b/source/slang/slang-reflection-json.cpp index 520648abf0..0a280824d7 100644 --- a/source/slang/slang-reflection-json.cpp +++ b/source/slang/slang-reflection-json.cpp @@ -1,6 +1,5 @@ #include "slang-reflection-json.h" -#include "../core/slang-pretty-writer.h" #include "slang.h" template diff --git a/source/slang/slang-reflection-json.h b/source/slang/slang-reflection-json.h index 89360e8031..4cbe9ba79b 100644 --- a/source/slang/slang-reflection-json.h +++ b/source/slang/slang-reflection-json.h @@ -2,7 +2,7 @@ #define SLANG_REFLECTION_JSON_H #include "slang.h" -#include "../core/slang-pretty-writer.h" +#include "../compiler-core/slang-pretty-writer.h" namespace Slang { diff --git a/tools/slang-reflection-test/slang-reflection-test-main.cpp b/tools/slang-reflection-test/slang-reflection-test-main.cpp index 05e4922e35..994b84ee60 100644 --- a/tools/slang-reflection-test/slang-reflection-test-main.cpp +++ b/tools/slang-reflection-test/slang-reflection-test-main.cpp @@ -4,7 +4,7 @@ #include "../../source/core/slang-string-escape-util.h" #include "../../source/core/slang-string-util.h" #include "../../source/core/slang-test-tool-util.h" -#include "../../source/core/slang-pretty-writer.h" +#include "../../source/compiler-core/slang-pretty-writer.h" #include "../../source/slang/slang-reflection-json.h" #include "slang-com-helper.h" #include "slang.h" From 806a91d74392fe5495226f3bc54e41e8b8235058 Mon Sep 17 00:00:00 2001 From: tareksander <57038324+tareksander@users.noreply.github.com> Date: Thu, 7 Nov 2024 16:55:33 +0100 Subject: [PATCH 05/11] Fixed variable shadowing warning --- source/slang/slang-reflection-json.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/slang/slang-reflection-json.cpp b/source/slang/slang-reflection-json.cpp index 0a280824d7..200dde4190 100644 --- a/source/slang/slang-reflection-json.cpp +++ b/source/slang/slang-reflection-json.cpp @@ -1026,13 +1026,13 @@ static void emitReflectionEntryPointJSON( break; } - auto parameterCount = entryPoint->getParameterCount(); - if (parameterCount) + auto entryPointParameterCount = entryPoint->getParameterCount(); + if (entryPointParameterCount) { writer << ",\n\"parameters\": [\n"; writer.indent(); - for (auto pp : makeRange(parameterCount)) + for (auto pp : makeRange(entryPointParameterCount)) { if (pp != 0) writer << ",\n"; @@ -1075,8 +1075,8 @@ static void emitReflectionEntryPointJSON( writer << ",\n\"bindings\": [\n"; writer.indent(); - auto parameterCount = programReflection->getParameterCount(); - for (auto pp : makeRange(parameterCount)) + auto programParameterCount = programReflection->getParameterCount(); + for (auto pp : makeRange(programParameterCount)) { if (pp != 0) writer << ",\n"; From 87c08cac1e7e8e9d8fe5a874fcf7cf875168e1ae Mon Sep 17 00:00:00 2001 From: tareksander <57038324+tareksander@users.noreply.github.com> Date: Thu, 7 Nov 2024 16:56:52 +0100 Subject: [PATCH 06/11] Use File::writeAllText instead of OSFilesystem and write to stdout if - is used as the path --- source/slang/slang.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index 5f26dcf3b2..d10aec1bea 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -6861,10 +6861,13 @@ SlangResult EndToEndCompileRequest::compile() auto reflectionPath = getOptionSet().getStringOption(CompilerOptionName::EmitReflectionJSON); if (reflectionPath.getLength() != 0) { - reflectionPath.appendChar('\0'); auto bufferWriter = PrettyWriter(); emitReflectionJSON(this, this->getReflection(), bufferWriter); - if (SLANG_FAILED(OSFileSystem::getMutableSingleton()->saveFile(reflectionPath.getBuffer(), bufferWriter.getBuilder().getBuffer(), bufferWriter.getBuilder().getLength()))) + if (reflectionPath == "-") + { + auto builder = bufferWriter.getBuilder(); + StdWriters::getOut().write(builder.getBuffer(), builder.getLength()); + } else if (SLANG_FAILED(File::writeAllText(reflectionPath, bufferWriter.getBuilder()))) { getSink()->diagnose( SourceLoc(), From 54b3a30e041652b1767a5ff7f4fe939d4cb7543f Mon Sep 17 00:00:00 2001 From: slangbot <186143334+slangbot@users.noreply.github.com> Date: Fri, 8 Nov 2024 01:14:41 +0000 Subject: [PATCH 07/11] format code --- source/compiler-core/slang-pretty-writer.cpp | 3 ++- source/compiler-core/slang-pretty-writer.h | 4 ++-- source/slang/slang-options.cpp | 4 ++-- source/slang/slang-reflection-json.cpp | 8 ++++++-- source/slang/slang-reflection-json.h | 7 +++++-- source/slang/slang.cpp | 12 +++++------- .../slang-reflection-test-main.cpp | 2 +- 7 files changed, 23 insertions(+), 17 deletions(-) diff --git a/source/compiler-core/slang-pretty-writer.cpp b/source/compiler-core/slang-pretty-writer.cpp index c29ca544cd..682b02c1e1 100644 --- a/source/compiler-core/slang-pretty-writer.cpp +++ b/source/compiler-core/slang-pretty-writer.cpp @@ -1,4 +1,5 @@ #include "slang-pretty-writer.h" + #include "../core/slang-string-escape-util.h" namespace Slang @@ -81,4 +82,4 @@ void PrettyWriter::maybeComma() write(toSlice(",\n")); } -} +} // namespace Slang diff --git a/source/compiler-core/slang-pretty-writer.h b/source/compiler-core/slang-pretty-writer.h index 5f6d33fd90..6817047a21 100644 --- a/source/compiler-core/slang-pretty-writer.h +++ b/source/compiler-core/slang-pretty-writer.h @@ -3,8 +3,8 @@ #include "../core/slang-char-util.h" -#include "../core/slang-string.h" #include "../core/slang-string-util.h" +#include "../core/slang-string.h" namespace Slang { @@ -116,7 +116,7 @@ struct CommaTrackerRAII PrettyWriter::CommaState* m_previousState; }; -} +} // namespace Slang #endif diff --git a/source/slang/slang-options.cpp b/source/slang/slang-options.cpp index 190bbc1654..6819991688 100644 --- a/source/slang/slang-options.cpp +++ b/source/slang/slang-options.cpp @@ -529,7 +529,7 @@ void initCommandOptions(CommandOptions& options) "reflection-json ", "Emit reflection data in JSON format to a file."}, }; - + _addOptions(makeConstArrayView(generalOpts), options); @@ -2672,7 +2672,7 @@ SlangResult OptionsParser::_parse(int argc, char const* const* argv) { CommandLineArg outputPath; SLANG_RETURN_ON_FAIL(m_reader.expectArg(outputPath)); - + linkage->m_optionSet.set(CompilerOptionName::EmitReflectionJSON, outputPath.value); break; } diff --git a/source/slang/slang-reflection-json.cpp b/source/slang/slang-reflection-json.cpp index 200dde4190..2ca68d8c8c 100644 --- a/source/slang/slang-reflection-json.cpp +++ b/source/slang/slang-reflection-json.cpp @@ -1,5 +1,6 @@ #include "slang-reflection-json.h" + #include "slang.h" template @@ -1185,10 +1186,13 @@ static void emitReflectionJSON( writer << "\n}\n"; } -void emitReflectionJSON(SlangCompileRequest* request, SlangReflection* reflection, PrettyWriter& writer) +void emitReflectionJSON( + SlangCompileRequest* request, + SlangReflection* reflection, + PrettyWriter& writer) { auto programReflection = (slang::ShaderReflection*)reflection; emitReflectionJSON(writer, request, programReflection); } -} +} // namespace Slang diff --git a/source/slang/slang-reflection-json.h b/source/slang/slang-reflection-json.h index 4cbe9ba79b..bf62b87447 100644 --- a/source/slang/slang-reflection-json.h +++ b/source/slang/slang-reflection-json.h @@ -1,13 +1,16 @@ #ifndef SLANG_REFLECTION_JSON_H #define SLANG_REFLECTION_JSON_H -#include "slang.h" #include "../compiler-core/slang-pretty-writer.h" +#include "slang.h" namespace Slang { -void emitReflectionJSON(SlangCompileRequest* request, SlangReflection* reflection, PrettyWriter& writer); +void emitReflectionJSON( + SlangCompileRequest* request, + SlangReflection* reflection, + PrettyWriter& writer); } diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index d10aec1bea..25419fb33c 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -31,13 +31,13 @@ #include "slang-parameter-binding.h" #include "slang-parser.h" #include "slang-preprocessor.h" +#include "slang-reflection-json.h" #include "slang-repro.h" #include "slang-serialize-ast.h" #include "slang-serialize-container.h" #include "slang-serialize-ir.h" #include "slang-tag-version.h" #include "slang-type-layout.h" -#include "slang-reflection-json.h" #include @@ -6857,7 +6857,7 @@ SlangResult EndToEndCompileRequest::compile() } } } - + auto reflectionPath = getOptionSet().getStringOption(CompilerOptionName::EmitReflectionJSON); if (reflectionPath.getLength() != 0) { @@ -6867,12 +6867,10 @@ SlangResult EndToEndCompileRequest::compile() { auto builder = bufferWriter.getBuilder(); StdWriters::getOut().write(builder.getBuffer(), builder.getLength()); - } else if (SLANG_FAILED(File::writeAllText(reflectionPath, bufferWriter.getBuilder()))) + } + else if (SLANG_FAILED(File::writeAllText(reflectionPath, bufferWriter.getBuilder()))) { - getSink()->diagnose( - SourceLoc(), - Diagnostics::unableToWriteFile, - reflectionPath); + getSink()->diagnose(SourceLoc(), Diagnostics::unableToWriteFile, reflectionPath); } } diff --git a/tools/slang-reflection-test/slang-reflection-test-main.cpp b/tools/slang-reflection-test/slang-reflection-test-main.cpp index 994b84ee60..ed19f6343c 100644 --- a/tools/slang-reflection-test/slang-reflection-test-main.cpp +++ b/tools/slang-reflection-test/slang-reflection-test-main.cpp @@ -1,10 +1,10 @@ // 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" #include "../../source/core/slang-test-tool-util.h" -#include "../../source/compiler-core/slang-pretty-writer.h" #include "../../source/slang/slang-reflection-json.h" #include "slang-com-helper.h" #include "slang.h" From 5bb5b80df3707d2338e52c1a0b454e19f4a418f4 Mon Sep 17 00:00:00 2001 From: tareksander <57038324+tareksander@users.noreply.github.com> Date: Sat, 9 Nov 2024 12:14:49 +0100 Subject: [PATCH 08/11] Fixed linker error --- include/slang-deprecated.h | 1 + source/slang/slang-reflection-json.cpp | 10 ++++++++++ .../slang-reflection-test-main.cpp | 12 +++++------- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/include/slang-deprecated.h b/include/slang-deprecated.h index 1c1350f9f8..bbcc8deb04 100644 --- a/include/slang-deprecated.h +++ b/include/slang-deprecated.h @@ -498,6 +498,7 @@ extern "C" SLANG_API SlangReflectionGeneric* spReflectionType_GetGenericContainer( SlangReflectionType* type); + SLANG_API SlangResult spReflection_ToJson(SlangReflection* reflection, SlangCompileRequest* request, ISlangBlob** outBlob); // Type Layout Reflection SLANG_API SlangReflectionType* spReflectionTypeLayout_GetType(SlangReflectionTypeLayout* type); diff --git a/source/slang/slang-reflection-json.cpp b/source/slang/slang-reflection-json.cpp index 2ca68d8c8c..9be615b862 100644 --- a/source/slang/slang-reflection-json.cpp +++ b/source/slang/slang-reflection-json.cpp @@ -2,6 +2,7 @@ #include "slang-reflection-json.h" #include "slang.h" +#include "../core/slang-blob.h" template struct Range @@ -1196,3 +1197,12 @@ void emitReflectionJSON( } } // namespace Slang + + +extern "C" SLANG_API SlangResult spReflection_ToJson(SlangReflection* reflection, SlangCompileRequest* request, ISlangBlob** outBlob) { + using namespace Slang; + PrettyWriter writer; + emitReflectionJSON(request, reflection, writer); + *outBlob = StringBlob::moveCreate(writer.getBuilder()); + return SLANG_OK; +} diff --git a/tools/slang-reflection-test/slang-reflection-test-main.cpp b/tools/slang-reflection-test/slang-reflection-test-main.cpp index ed19f6343c..90e9308dfb 100644 --- a/tools/slang-reflection-test/slang-reflection-test-main.cpp +++ b/tools/slang-reflection-test/slang-reflection-test-main.cpp @@ -5,7 +5,8 @@ #include "../../source/core/slang-string-escape-util.h" #include "../../source/core/slang-string-util.h" #include "../../source/core/slang-test-tool-util.h" -#include "../../source/slang/slang-reflection-json.h" +#include "../../source/core/slang-blob.h" +#include "slang-deprecated.h" #include "slang-com-helper.h" #include "slang.h" @@ -18,15 +19,12 @@ using namespace Slang; void emitReflectionJSON(SlangCompileRequest* request, SlangReflection* reflection) { - PrettyWriter writer; + ISlangBlob* b; - emitReflectionJSON(request, reflection, writer); - - // Get the contents of the writer - const auto slice = writer.getBuilder().getUnownedSlice(); + spReflection_ToJson(reflection, request, &b); // 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) From 69dc0235a0106d8612e97e73580f2dbb5d9c929f Mon Sep 17 00:00:00 2001 From: Yong He Date: Mon, 11 Nov 2024 10:42:20 -0800 Subject: [PATCH 09/11] Fix COM Ptr life time issues. --- source/slang/slang-reflection-json.cpp | 20 ++++++++++++------- .../slang-reflection-test-main.cpp | 8 +++----- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/source/slang/slang-reflection-json.cpp b/source/slang/slang-reflection-json.cpp index 9be615b862..c7d11a6c36 100644 --- a/source/slang/slang-reflection-json.cpp +++ b/source/slang/slang-reflection-json.cpp @@ -1,7 +1,6 @@ #include "slang-reflection-json.h" -#include "slang.h" #include "../core/slang-blob.h" template @@ -1199,10 +1198,17 @@ void emitReflectionJSON( } // namespace Slang -extern "C" SLANG_API SlangResult spReflection_ToJson(SlangReflection* reflection, SlangCompileRequest* request, ISlangBlob** outBlob) { - using namespace Slang; - PrettyWriter writer; - emitReflectionJSON(request, reflection, writer); - *outBlob = StringBlob::moveCreate(writer.getBuilder()); - return SLANG_OK; +extern "C" +{ + SLANG_API SlangResult spReflection_ToJson( + SlangReflection* reflection, + SlangCompileRequest* request, + ISlangBlob** outBlob) + { + using namespace Slang; + PrettyWriter writer; + emitReflectionJSON(request, reflection, writer); + *outBlob = StringBlob::moveCreate(writer.getBuilder()).detach(); + return SLANG_OK; + } } diff --git a/tools/slang-reflection-test/slang-reflection-test-main.cpp b/tools/slang-reflection-test/slang-reflection-test-main.cpp index 90e9308dfb..aa6f8012a1 100644 --- a/tools/slang-reflection-test/slang-reflection-test-main.cpp +++ b/tools/slang-reflection-test/slang-reflection-test-main.cpp @@ -5,8 +5,6 @@ #include "../../source/core/slang-string-escape-util.h" #include "../../source/core/slang-string-util.h" #include "../../source/core/slang-test-tool-util.h" -#include "../../source/core/slang-blob.h" -#include "slang-deprecated.h" #include "slang-com-helper.h" #include "slang.h" @@ -19,12 +17,12 @@ using namespace Slang; void emitReflectionJSON(SlangCompileRequest* request, SlangReflection* reflection) { - ISlangBlob* b; + ComPtr b; - spReflection_ToJson(reflection, request, &b); + spReflection_ToJson(reflection, request, b.writeRef()); // Output the writer content to out stream - StdWriters::getOut().write((const char*) b->getBufferPointer(), b->getBufferSize()); + StdWriters::getOut().write((const char*)b->getBufferPointer(), b->getBufferSize()); } static SlangResult maybeDumpDiagnostic(SlangResult res, SlangCompileRequest* request) From a48487167b96053ac909ebe575fea74e737a4c15 Mon Sep 17 00:00:00 2001 From: Yong He Date: Mon, 11 Nov 2024 10:47:21 -0800 Subject: [PATCH 10/11] Move enum to the end. --- include/slang.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/slang.h b/include/slang.h index 3f993355d4..0e67243737 100644 --- a/include/slang.h +++ b/include/slang.h @@ -884,7 +884,6 @@ typedef uint32_t SlangSizeT; DumpWarningDiagnostics, InputFilesRemain, EmitIr, // bool - EmitReflectionJSON, // bool ReportDownstreamTime, // bool ReportPerfBenchmark, // bool ReportCheckpointIntermediates, // bool @@ -1004,6 +1003,8 @@ typedef uint32_t SlangSizeT; // Add this new option to the end of the list to avoid breaking ABI as much as possible. // Setting of EmitSpirvDirectly or EmitSpirvViaGLSL will turn into this option internally. EmitSpirvMethod, // enum SlangEmitSpirvMethod + + EmitReflectionJSON, // bool CountOf, }; From c0c1ccf275dc5f5c6edcaecae618d452d2f278b9 Mon Sep 17 00:00:00 2001 From: Yong He Date: Mon, 11 Nov 2024 11:15:37 -0800 Subject: [PATCH 11/11] Fix formatting. --- include/slang-deprecated.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/slang-deprecated.h b/include/slang-deprecated.h index bbcc8deb04..3ce8f61bc2 100644 --- a/include/slang-deprecated.h +++ b/include/slang-deprecated.h @@ -498,7 +498,6 @@ extern "C" SLANG_API SlangReflectionGeneric* spReflectionType_GetGenericContainer( SlangReflectionType* type); - SLANG_API SlangResult spReflection_ToJson(SlangReflection* reflection, SlangCompileRequest* request, ISlangBlob** outBlob); // Type Layout Reflection SLANG_API SlangReflectionType* spReflectionTypeLayout_GetType(SlangReflectionTypeLayout* type); @@ -854,6 +853,11 @@ extern "C" // Shader Reflection + SLANG_API SlangResult spReflection_ToJson( + SlangReflection* reflection, + SlangCompileRequest* request, + ISlangBlob** outBlob); + SLANG_API unsigned spReflection_GetParameterCount(SlangReflection* reflection); SLANG_API SlangReflectionParameter* spReflection_GetParameterByIndex( SlangReflection* reflection,