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