diff options
| author | Yong He <yonghe@outlook.com> | 2024-09-26 09:44:08 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-09-26 09:44:08 -0700 |
| commit | 5a0224a0773f6d7f5eae8515424af5fa8faa9c14 (patch) | |
| tree | ac14b54ad253d4ac5413a3c86254929f332b9e1a | |
| parent | 7398e1e09312ed4e19195e060de9a2c9a073fcc1 (diff) | |
Move texture format inference to frontend and add reflection api for it. (#5155)
| -rw-r--r-- | include/slang-image-format-defs.h | 49 | ||||
| -rw-r--r-- | include/slang.h | 15 | ||||
| -rw-r--r-- | source/slang/slang-ast-modifier.h | 1 | ||||
| -rw-r--r-- | source/slang/slang-ast-support-types.h | 3 | ||||
| -rw-r--r-- | source/slang/slang-ast-type.cpp | 5 | ||||
| -rw-r--r-- | source/slang/slang-ast-type.h | 1 | ||||
| -rw-r--r-- | source/slang/slang-check-decl.cpp | 153 | ||||
| -rw-r--r-- | source/slang/slang-check.h | 2 | ||||
| -rw-r--r-- | source/slang/slang-emit-glsl.cpp | 124 | ||||
| -rw-r--r-- | source/slang/slang-emit.cpp | 10 | ||||
| -rw-r--r-- | source/slang/slang-image-format-defs.h | 49 | ||||
| -rw-r--r-- | source/slang/slang-ir-insts.h | 3 | ||||
| -rw-r--r-- | source/slang/slang-ir-resolve-texture-format.cpp | 142 | ||||
| -rw-r--r-- | source/slang/slang-ir-resolve-texture-format.h | 6 | ||||
| -rw-r--r-- | source/slang/slang-ir-spirv-legalize.cpp | 187 | ||||
| -rw-r--r-- | source/slang/slang-reflection-api.cpp | 18 | ||||
| -rw-r--r-- | source/slang/slang-syntax.cpp | 4 | ||||
| -rw-r--r-- | tools/slang-unit-test/unit-test-image-format-reflection.cpp | 58 |
18 files changed, 524 insertions, 306 deletions
diff --git a/include/slang-image-format-defs.h b/include/slang-image-format-defs.h new file mode 100644 index 000000000..410c73339 --- /dev/null +++ b/include/slang-image-format-defs.h @@ -0,0 +1,49 @@ +// slang-image-format-defs.h +#ifndef SLANG_FORMAT +#error Must define SLANG_FORMAT macro before including image-format-defs.h +#endif + +SLANG_FORMAT(unknown, (NONE, 0, 0)) +SLANG_FORMAT(rgba32f, (FLOAT32, 4, sizeof(float) * 4)) +SLANG_FORMAT(rgba16f, (FLOAT16, 4, sizeof(uint16_t) * 4)) +SLANG_FORMAT(rg32f, (FLOAT32, 2, sizeof(float) * 2)) +SLANG_FORMAT(rg16f, (FLOAT16, 2, sizeof(uint16_t) * 2)) +SLANG_FORMAT(r11f_g11f_b10f, (NONE, 3, sizeof(uint32_t))) +SLANG_FORMAT(r32f, (FLOAT32, 1, sizeof(float))) +SLANG_FORMAT(r16f, (FLOAT16, 1, sizeof(uint16_t))) +SLANG_FORMAT(rgba16, (UINT16, 4, sizeof(uint16_t) * 4)) +SLANG_FORMAT(rgb10_a2, (NONE, 4, sizeof(uint32_t))) +SLANG_FORMAT(rgba8, (UINT8, 4, sizeof(uint32_t))) +SLANG_FORMAT(rg16, (UINT16, 2, sizeof(uint16_t) * 2 )) +SLANG_FORMAT(rg8, (UINT8, 2, sizeof(char) * 2)) +SLANG_FORMAT(r16, (UINT16, 1, sizeof(uint16_t))) +SLANG_FORMAT(r8, (UINT8, 1, sizeof(uint8_t))) +SLANG_FORMAT(rgba16_snorm, (UINT16, 4, sizeof(uint16_t) * 4)) +SLANG_FORMAT(rgba8_snorm, (UINT8, 4, sizeof(uint8_t) * 4)) +SLANG_FORMAT(rg16_snorm, (UINT16, 2, sizeof(uint16_t) * 2)) +SLANG_FORMAT(rg8_snorm, (UINT8, 2, sizeof(uint8_t) * 2)) +SLANG_FORMAT(r16_snorm, (UINT16, 1, sizeof(uint16_t))) +SLANG_FORMAT(r8_snorm, (UINT8, 1, sizeof(uint8_t))) +SLANG_FORMAT(rgba32i, (INT32, 4, sizeof(int32_t) * 4)) +SLANG_FORMAT(rgba16i, (INT16, 4, sizeof(int16_t) * 4)) +SLANG_FORMAT(rgba8i, (INT8, 4, sizeof(int8_t) * 4)) +SLANG_FORMAT(rg32i, (INT32, 2, sizeof(int32_t) * 2)) +SLANG_FORMAT(rg16i, (INT16, 2, sizeof(int16_t) * 2)) +SLANG_FORMAT(rg8i, (INT8, 2, sizeof(int8_t) * 2)) +SLANG_FORMAT(r32i, (INT32, 1, sizeof(int32_t))) +SLANG_FORMAT(r16i, (INT16, 1, sizeof(int16_t))) +SLANG_FORMAT(r8i, (INT8, 1, sizeof(int8_t))) +SLANG_FORMAT(rgba32ui, (UINT32, 4, sizeof(uint32_t) * 4)) +SLANG_FORMAT(rgba16ui, (UINT16, 4, sizeof(uint16_t) * 4)) +SLANG_FORMAT(rgb10_a2ui, (NONE, 4, sizeof(uint32_t))) +SLANG_FORMAT(rgba8ui, (UINT8, 4, sizeof(uint8_t) * 4)) +SLANG_FORMAT(rg32ui, (UINT32, 2, sizeof(uint32_t) * 2)) +SLANG_FORMAT(rg16ui, (UINT16, 2, sizeof(uint16_t) * 2)) +SLANG_FORMAT(rg8ui, (UINT8, 2, sizeof(uint8_t) * 2)) +SLANG_FORMAT(r32ui, (UINT32, 1, sizeof(uint32_t))) +SLANG_FORMAT(r16ui, (UINT16, 1, sizeof(uint16_t))) +SLANG_FORMAT(r8ui, (UINT8, 1, sizeof(uint8_t))) +SLANG_FORMAT(r64ui, (UINT64, 1, sizeof(uint64_t))) +SLANG_FORMAT(r64i, (INT64, 1, sizeof(int64_t))) + +#undef SLANG_FORMAT diff --git a/include/slang.h b/include/slang.h index 1c05b4c08..8e42632ff 100644 --- a/include/slang.h +++ b/include/slang.h @@ -2433,6 +2433,14 @@ extern "C" SLANG_MODIFIER_INOUT }; + typedef SlangUInt32 SlangImageFormatIntegral; + enum SlangImageFormat : SlangImageFormatIntegral + { +#define SLANG_FORMAT(NAME, DESC) SLANG_IMAGE_FORMAT_##NAME, +#include "slang-image-format-defs.h" +#undef SLANG_FORMAT + }; + // User Attribute SLANG_API char const* spReflectionUserAttribute_GetName(SlangReflectionUserAttribute* attrib); SLANG_API unsigned int spReflectionUserAttribute_GetArgumentCount(SlangReflectionUserAttribute* attrib); @@ -2526,6 +2534,7 @@ extern "C" SLANG_API SlangInt spReflectionTypeLayout_getBindingRangeBindingCount(SlangReflectionTypeLayout* typeLayout, SlangInt index); SLANG_API SlangReflectionTypeLayout* spReflectionTypeLayout_getBindingRangeLeafTypeLayout(SlangReflectionTypeLayout* typeLayout, SlangInt index); SLANG_API SlangReflectionVariable* spReflectionTypeLayout_getBindingRangeLeafVariable(SlangReflectionTypeLayout* typeLayout, SlangInt index); + SLANG_API SlangImageFormat spReflectionTypeLayout_getBindingRangeImageFormat(SlangReflectionTypeLayout* typeLayout, SlangInt index); SLANG_API SlangInt spReflectionTypeLayout_getFieldBindingRangeOffset(SlangReflectionTypeLayout* typeLayout, SlangInt fieldIndex); SLANG_API SlangInt spReflectionTypeLayout_getExplicitCounterBindingRangeOffset(SlangReflectionTypeLayout* inTypeLayout); @@ -3299,6 +3308,12 @@ namespace slang (SlangReflectionTypeLayout*)this, index); } + SlangImageFormat getBindingRangeImageFormat(SlangInt index) + { + return spReflectionTypeLayout_getBindingRangeImageFormat( + (SlangReflectionTypeLayout*)this, index); + } + SlangInt getBindingRangeDescriptorSetIndex(SlangInt index) { return spReflectionTypeLayout_getBindingRangeDescriptorSetIndex( diff --git a/source/slang/slang-ast-modifier.h b/source/slang/slang-ast-modifier.h index 69f86a43d..5cc060806 100644 --- a/source/slang/slang-ast-modifier.h +++ b/source/slang/slang-ast-modifier.h @@ -455,7 +455,6 @@ class ColumnMajorLayoutModifier : public MatrixLayoutModifier SLANG_AST_CLASS(ColumnMajorLayoutModifier) }; - // The HLSL flavor of those modifiers class HLSLRowMajorLayoutModifier : public RowMajorLayoutModifier { diff --git a/source/slang/slang-ast-support-types.h b/source/slang/slang-ast-support-types.h index 56101bb91..e35f970f5 100644 --- a/source/slang/slang-ast-support-types.h +++ b/source/slang/slang-ast-support-types.h @@ -187,8 +187,9 @@ namespace Slang enum class ImageFormat { -#define FORMAT(NAME, OTHER) NAME, +#define SLANG_FORMAT(NAME, OTHER) NAME, #include "slang-image-format-defs.h" +#undef SLANG_FORMAT }; struct ImageFormatInfo diff --git a/source/slang/slang-ast-type.cpp b/source/slang/slang-ast-type.cpp index 616c2a67a..9f848ddc4 100644 --- a/source/slang/slang-ast-type.cpp +++ b/source/slang/slang-ast-type.cpp @@ -1216,6 +1216,11 @@ Val* TextureTypeBase::getSampleCount() return as<Type>(_getGenericTypeArg(this, 4)); } +Val* TextureTypeBase::getFormat() +{ + return as<Type>(_getGenericTypeArg(this, 8)); +} + Type* removeParamDirType(Type* type) { for (auto paramDirType = as<ParamDirectionType>(type); paramDirType;) diff --git a/source/slang/slang-ast-type.h b/source/slang/slang-ast-type.h index 1239283d7..1731cdb53 100644 --- a/source/slang/slang-ast-type.h +++ b/source/slang/slang-ast-type.h @@ -179,6 +179,7 @@ class TextureTypeBase : public ResourceType SLANG_ABSTRACT_AST_CLASS(TextureTypeBase) Val* getSampleCount(); + Val* getFormat(); }; class TextureType : public TextureTypeBase diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp index 2307c1669..4d742d9f4 100644 --- a/source/slang/slang-check-decl.cpp +++ b/source/slang/slang-check-decl.cpp @@ -93,7 +93,7 @@ namespace Slang void checkDerivativeMemberAttributeParent(VarDeclBase* varDecl, DerivativeMemberAttribute* attr); void checkExtensionExternVarAttribute(VarDeclBase* varDecl, ExtensionExternVarModifier* m); void checkMeshOutputDecl(VarDeclBase* varDecl); - void maybeApplyMatrixLayoutModifier(VarDeclBase* varDecl); + void maybeApplyLayoutModifier(VarDeclBase* varDecl); void checkVarDeclCommon(VarDeclBase* varDecl); void visitVarDecl(VarDecl* varDecl) @@ -1518,7 +1518,122 @@ namespace Slang } } } - void SemanticsDeclHeaderVisitor::maybeApplyMatrixLayoutModifier(VarDeclBase* varDecl) + + ImageFormat inferImageFormatFromTextureType(VarDeclBase* varDecl, TextureTypeBase* textureType, bool &outIsInferred) + { + outIsInferred = false; + ImageFormat format = ImageFormat::unknown; + if (auto formatVal = as<ConstantIntVal>(textureType->getFormat())) + { + format = (ImageFormat)formatVal->getValue(); + } + if (format != ImageFormat::unknown) + return format; + + if (auto formatAttrib = varDecl->findModifier<FormatAttribute>()) + { + format = formatAttrib->format; + } + else + { + // If format is not specified explicitly through format attribute, we will derive a default + // value from the element format. + outIsInferred = true; + auto elementType = textureType->getElementType(); + Int vectorWidth = 1; + if (auto elementVecType = as<VectorExpressionType>(elementType)) + { + if (auto intLitVal = as<ConstantIntVal>(elementVecType->getElementCount())) + { + vectorWidth = (Int)intLitVal->getValue(); + } + else + { + vectorWidth = 1; + } + elementType = elementVecType->getElementType(); + } + if (auto basicType = as<BasicExpressionType>(elementType)) + { + switch (basicType->getBaseType()) + { + case BaseType::UInt: + switch (vectorWidth) + { + case 1: format = ImageFormat::r32ui; break; + case 2: format = ImageFormat::rg32ui; break; + case 4: format = ImageFormat::rgba32ui; break; + } + break; + case BaseType::Int: + switch (vectorWidth) + { + case 1: format = ImageFormat::r32i; break; + case 2: format = ImageFormat::rg32i; break; + case 4: format = ImageFormat::rgba32i; break; + } + break; + case BaseType::UInt16: + switch (vectorWidth) + { + case 1: format = ImageFormat::r16ui; break; + case 2: format = ImageFormat::rg16ui; break; + case 4: format = ImageFormat::rgba16ui; break; + } + break; + case BaseType::Int16: + switch (vectorWidth) + { + case 1: format = ImageFormat::r16i; break; + case 2: format = ImageFormat::rg16i; break; + case 4: format = ImageFormat::rgba16i; break; + } + break; + case BaseType::UInt8: + switch (vectorWidth) + { + case 1: format = ImageFormat::r8ui; break; + case 2: format = ImageFormat::rg8ui; break; + case 4: format = ImageFormat::rgba8ui; break; + } + break; + case BaseType::Int8: + switch (vectorWidth) + { + case 1: format = ImageFormat::r8i; break; + case 2: format = ImageFormat::rg8i; break; + case 4: format = ImageFormat::rgba8i; break; + } + break; + case BaseType::Int64: + switch (vectorWidth) + { + case 1: format = ImageFormat::r64i; break; + default: break; + } + break; + case BaseType::UInt64: + switch (vectorWidth) + { + case 1: format = ImageFormat::r64ui; break; + default: break; + } + break; + case BaseType::Half: + switch (vectorWidth) + { + case 1: format = ImageFormat::r16f; break; + case 2: format = ImageFormat::rg16f; break; + case 4: format = ImageFormat::rgba16f; break; + } + break; + } + } + } + return format; + } + + void SemanticsDeclHeaderVisitor::maybeApplyLayoutModifier(VarDeclBase* varDecl) { if (auto matrixType = as<MatrixExpressionType>(varDecl->type.type)) { @@ -1533,6 +1648,23 @@ namespace Slang varDecl->type.type = newMatrixType; } } + else if (auto textureType = as<TextureTypeBase>(unwrapArrayType(varDecl->type.type))) + { + if (getLinkage()->m_optionSet.getBoolOption(CompilerOptionName::DefaultImageFormatUnknown)) + return; + + // For texture types, we will ensure there is a [format] attribute declared on the decl, + // if not, we will infer the format from the texture type if it is not specified. + // + bool isInferred = false; + auto format = inferImageFormatFromTextureType(varDecl, textureType, isInferred); + if (format != ImageFormat::unknown && isInferred) + { + auto formatAttrib = m_astBuilder->create<FormatAttribute>(); + formatAttrib->format = format; + addModifier(varDecl, formatAttrib); + } + } } void SemanticsDeclHeaderVisitor::checkVarDeclCommon(VarDeclBase* varDecl) @@ -1615,8 +1747,8 @@ namespace Slang validateArraySizeForVariable(varDecl); } - // If there is a matrix layout modifier, we will modify the matrix type now. - maybeApplyMatrixLayoutModifier(varDecl); + // If there is a matrix layout modifier or texture format modifier, we will modify the type now. + maybeApplyLayoutModifier(varDecl); if (varDecl->initExpr) { @@ -2841,6 +2973,17 @@ namespace Slang _registerBuiltinDeclsRec(session, decl); } + Type* unwrapArrayType(Type* type) + { + for (;;) + { + if (auto arrayType = as<ArrayExpressionType>(type)) + type = arrayType->getElementType(); + else + return type; + } + } + void discoverExtensionDecls(List<ExtensionDecl*>& decls, Decl* parent) { if (auto extDecl = as<ExtensionDecl>(parent)) @@ -8190,7 +8333,7 @@ namespace Slang } } - maybeApplyMatrixLayoutModifier(paramDecl); + maybeApplyLayoutModifier(paramDecl); // Only texture types are allowed to have memory qualifiers on parameters if(!paramDecl->type || paramDecl->type->astNodeType != ASTNodeType::TextureType) diff --git a/source/slang/slang-check.h b/source/slang/slang-check.h index b5e64f47f..d22d62b9b 100644 --- a/source/slang/slang-check.h +++ b/source/slang/slang-check.h @@ -23,6 +23,8 @@ namespace Slang void registerBuiltinDecls(Session* session, Decl* decl); + Type* unwrapArrayType(Type* type); + OrderedDictionary<GenericTypeParamDeclBase*, List<Type*>> getCanonicalGenericConstraints( ASTBuilder* builder, DeclRef<ContainerDecl> genericDecl); } diff --git a/source/slang/slang-emit-glsl.cpp b/source/slang/slang-emit-glsl.cpp index 116bf67cd..c0a0bf3db 100644 --- a/source/slang/slang-emit-glsl.cpp +++ b/source/slang/slang-emit-glsl.cpp @@ -488,6 +488,8 @@ void GLSLSourceEmitter::_emitGLSLParameterGroup(IRGlobalParam* varDecl, IRUnifor void GLSLSourceEmitter::_emitGLSLImageFormatModifier(IRInst* var, IRTextureType* resourceType) { + SLANG_UNUSED(resourceType); + // If the user specified a format manually, using `[format(...)]`, // then we will respect that format and emit a matching `layout` modifier. // @@ -585,72 +587,72 @@ void GLSLSourceEmitter::_emitGLSLImageFormatModifier(IRInst* var, IRTextureType* m_writer->emit("layout("); switch (vectorWidth) { - default: m_writer->emit("rgba"); break; + default: m_writer->emit("rgba"); break; - case 3: - { - // TODO: GLSL doesn't support 3-component formats so for now we are going to - // default to rgba - // - // The SPIR-V spec (https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.pdf) - // section 3.11 on Image Formats it does not list rgbf32. - // - // It seems SPIR-V can support having an image with an unknown-at-compile-time - // format, so long as the underlying API supports it. Ideally this would mean that we can - // just drop all these qualifiers when emitting GLSL for Vulkan targets. - // - // This raises the question of what to do more long term. For Vulkan hopefully we can just - // drop the layout. For OpenGL targets it would seem reasonable to have well-defined rules - // for inferring the format (and just document that 3-component formats map to 4-component formats, - // but that shouldn't matter because the API wouldn't let the user allocate those 3-component formats anyway), - // and add an attribute for specifying the format manually if you really want to override our - // inference (e.g., to specify r11fg11fb10f). - - m_writer->emit("rgba"); - //Emit("rgb"); - break; - } + case 3: + { + // TODO: GLSL doesn't support 3-component formats so for now we are going to + // default to rgba + // + // The SPIR-V spec (https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.pdf) + // section 3.11 on Image Formats it does not list rgbf32. + // + // It seems SPIR-V can support having an image with an unknown-at-compile-time + // format, so long as the underlying API supports it. Ideally this would mean that we can + // just drop all these qualifiers when emitting GLSL for Vulkan targets. + // + // This raises the question of what to do more long term. For Vulkan hopefully we can just + // drop the layout. For OpenGL targets it would seem reasonable to have well-defined rules + // for inferring the format (and just document that 3-component formats map to 4-component formats, + // but that shouldn't matter because the API wouldn't let the user allocate those 3-component formats anyway), + // and add an attribute for specifying the format manually if you really want to override our + // inference (e.g., to specify r11fg11fb10f). + + m_writer->emit("rgba"); + //Emit("rgb"); + break; + } - case 2: m_writer->emit("rg"); break; - case 1: m_writer->emit("r"); break; + case 2: m_writer->emit("rg"); break; + case 1: m_writer->emit("r"); break; } switch (elementBasicType->getBaseType()) { - default: - case BaseType::Float: m_writer->emit("32f"); break; - case BaseType::Half: m_writer->emit("16f"); break; - case BaseType::UInt: m_writer->emit("32ui"); break; - case BaseType::Int: m_writer->emit("32i"); break; - case BaseType::Int8: m_writer->emit("8i"); break; - case BaseType::Int16: m_writer->emit("16i"); break; - case BaseType::Int64: m_writer->emit("64i"); break; - case BaseType::IntPtr: m_writer->emit("64i"); break; - case BaseType::UInt8: m_writer->emit("8ui"); break; - case BaseType::UInt16: m_writer->emit("16ui"); break; - case BaseType::UInt64: m_writer->emit("64ui"); break; - case BaseType::UIntPtr: m_writer->emit("64ui"); break; - - // TODO: Here are formats that are available in GLSL, - // but that are not handled by the above cases. - // - // r11f_g11f_b10f - // - // rgba16 - // rgb10_a2 - // rgba8 - // rg16 - // rg8 - // r16 - // r8 - // - // rgba16_snorm - // rgba8_snorm - // rg16_snorm - // rg8_snorm - // r16_snorm - // r8_snorm - // - // rgb10_a2ui + default: + case BaseType::Float: m_writer->emit("32f"); break; + case BaseType::Half: m_writer->emit("16f"); break; + case BaseType::UInt: m_writer->emit("32ui"); break; + case BaseType::Int: m_writer->emit("32i"); break; + case BaseType::Int8: m_writer->emit("8i"); break; + case BaseType::Int16: m_writer->emit("16i"); break; + case BaseType::Int64: m_writer->emit("64i"); break; + case BaseType::IntPtr: m_writer->emit("64i"); break; + case BaseType::UInt8: m_writer->emit("8ui"); break; + case BaseType::UInt16: m_writer->emit("16ui"); break; + case BaseType::UInt64: m_writer->emit("64ui"); break; + case BaseType::UIntPtr: m_writer->emit("64ui"); break; + + // TODO: Here are formats that are available in GLSL, + // but that are not handled by the above cases. + // + // r11f_g11f_b10f + // + // rgba16 + // rgb10_a2 + // rgba8 + // rg16 + // rg8 + // r16 + // r8 + // + // rgba16_snorm + // rgba8_snorm + // rg16_snorm + // rg8_snorm + // r16_snorm + // r8_snorm + // + // rgb10_a2ui } m_writer->emit(")\n"); } diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp index a29142ba1..97ee2a595 100644 --- a/source/slang/slang-emit.cpp +++ b/source/slang/slang-emit.cpp @@ -63,6 +63,7 @@ #include "slang-ir-redundancy-removal.h" #include "slang-ir-restructure.h" #include "slang-ir-restructure-scoping.h" +#include "slang-ir-resolve-texture-format.h" #include "slang-ir-sccp.h" #include "slang-ir-specialize.h" #include "slang-ir-specialize-arrays.h" @@ -1251,6 +1252,15 @@ Result linkAndOptimizeIR( break; } + switch (target) + { + case CodeGenTarget::GLSL: + case CodeGenTarget::SPIRV: + case CodeGenTarget::WGSL: + resolveTextureFormat(irModule); + break; + } + // For GLSL only, we will need to perform "legalization" of // the entry point and any entry-point parameters. // diff --git a/source/slang/slang-image-format-defs.h b/source/slang/slang-image-format-defs.h deleted file mode 100644 index 97c41270b..000000000 --- a/source/slang/slang-image-format-defs.h +++ /dev/null @@ -1,49 +0,0 @@ -// slang-image-format-defs.h -#ifndef FORMAT -#error Must define FORMAT macro before including image-format-defs.h -#endif - -FORMAT(unknown, (NONE, 0, 0)) -FORMAT(rgba32f, (FLOAT32, 4, sizeof(float) * 4)) -FORMAT(rgba16f, (FLOAT16, 4, sizeof(uint16_t) * 4)) -FORMAT(rg32f, (FLOAT32, 2, sizeof(float) * 2)) -FORMAT(rg16f, (FLOAT16, 2, sizeof(uint16_t) * 2)) -FORMAT(r11f_g11f_b10f, (NONE, 3, sizeof(uint32_t))) -FORMAT(r32f, (FLOAT32, 1, sizeof(float))) -FORMAT(r16f, (FLOAT16, 1, sizeof(uint16_t))) -FORMAT(rgba16, (UINT16, 4, sizeof(uint16_t) * 4)) -FORMAT(rgb10_a2, (NONE, 4, sizeof(uint32_t))) -FORMAT(rgba8, (UINT8, 4, sizeof(uint32_t))) -FORMAT(rg16, (UINT16, 2, sizeof(uint16_t) * 2 )) -FORMAT(rg8, (UINT8, 2, sizeof(char) * 2)) -FORMAT(r16, (UINT16, 1, sizeof(uint16_t))) -FORMAT(r8, (UINT8, 1, sizeof(uint8_t))) -FORMAT(rgba16_snorm, (UINT16, 4, sizeof(uint16_t) * 4)) -FORMAT(rgba8_snorm, (UINT8, 4, sizeof(uint8_t) * 4)) -FORMAT(rg16_snorm, (UINT16, 2, sizeof(uint16_t) * 2)) -FORMAT(rg8_snorm, (UINT8, 2, sizeof(uint8_t) * 2)) -FORMAT(r16_snorm, (UINT16, 1, sizeof(uint16_t))) -FORMAT(r8_snorm, (UINT8, 1, sizeof(uint8_t))) -FORMAT(rgba32i, (INT32, 4, sizeof(int32_t) * 4)) -FORMAT(rgba16i, (INT16, 4, sizeof(int16_t) * 4)) -FORMAT(rgba8i, (INT8, 4, sizeof(int8_t) * 4)) -FORMAT(rg32i, (INT32, 2, sizeof(int32_t) * 2)) -FORMAT(rg16i, (INT16, 2, sizeof(int16_t) * 2)) -FORMAT(rg8i, (INT8, 2, sizeof(int8_t) * 2)) -FORMAT(r32i, (INT32, 1, sizeof(int32_t))) -FORMAT(r16i, (INT16, 1, sizeof(int16_t))) -FORMAT(r8i, (INT8, 1, sizeof(int8_t))) -FORMAT(rgba32ui, (UINT32, 4, sizeof(uint32_t) * 4)) -FORMAT(rgba16ui, (UINT16, 4, sizeof(uint16_t) * 4)) -FORMAT(rgb10_a2ui, (NONE, 4, sizeof(uint32_t))) -FORMAT(rgba8ui, (UINT8, 4, sizeof(uint8_t) * 4)) -FORMAT(rg32ui, (UINT32, 2, sizeof(uint32_t) * 2)) -FORMAT(rg16ui, (UINT16, 2, sizeof(uint16_t) * 2)) -FORMAT(rg8ui, (UINT8, 2, sizeof(uint8_t) * 2)) -FORMAT(r32ui, (UINT32, 1, sizeof(uint32_t))) -FORMAT(r16ui, (UINT16, 1, sizeof(uint16_t))) -FORMAT(r8ui, (UINT8, 1, sizeof(uint8_t))) -FORMAT(r64ui, (UINT64, 1, sizeof(uint64_t))) -FORMAT(r64i, (INT64, 1, sizeof(int64_t))) - -#undef FORMAT diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h index 24a779aed..81b916791 100644 --- a/source/slang/slang-ir-insts.h +++ b/source/slang/slang-ir-insts.h @@ -2374,6 +2374,9 @@ struct IRStore : IRInst IRInst* getPtr() { return ptr.get(); } IRInst* getVal() { return val.get(); } + + IRUse* getPtrUse() { return &ptr; } + IRUse* getValUse() { return &val; } }; struct IRAtomicStore : IRInst diff --git a/source/slang/slang-ir-resolve-texture-format.cpp b/source/slang/slang-ir-resolve-texture-format.cpp new file mode 100644 index 000000000..7cd81fc2d --- /dev/null +++ b/source/slang/slang-ir-resolve-texture-format.cpp @@ -0,0 +1,142 @@ +#include "slang-ir-resolve-texture-format.h" +#include "slang-ir-insts.h" +#include "slang-ir-clone.h" + +namespace Slang +{ + static IRType* replaceImageElementType(IRInst* originalType, IRInst* newElementType) + { + switch (originalType->getOp()) + { + case kIROp_ArrayType: + case kIROp_UnsizedArrayType: + case kIROp_PtrType: + case kIROp_OutType: + case kIROp_RefType: + case kIROp_ConstRefType: + case kIROp_InOutType: + { + auto newInnerType = replaceImageElementType(originalType->getOperand(0), newElementType); + if (newInnerType != originalType->getOperand(0)) + { + IRBuilder builder(originalType); + builder.setInsertBefore(originalType); + IRCloneEnv cloneEnv; + cloneEnv.mapOldValToNew.add(originalType->getOperand(0), newInnerType); + return (IRType*)cloneInst(&cloneEnv, &builder, originalType); + } + return (IRType*)originalType; + } + + default: + if (as<IRResourceTypeBase>(originalType)) + return (IRType*)newElementType; + return (IRType*)originalType; + } + } + + static void resolveTextureFormatForParameter(IRInst* textureInst, IRTextureTypeBase* textureType) + { + ImageFormat format = (ImageFormat)(textureType->getFormat()); + auto decor = textureInst->findDecoration<IRFormatDecoration>(); + if (!decor) + return; + if (decor->getFormat() == (ImageFormat)textureType->getFormat()) + return; + + format = decor->getFormat(); + if (format != ImageFormat::unknown) + { + IRBuilder builder(textureInst->getModule()); + builder.setInsertBefore(textureInst); + auto formatArg = builder.getIntValue(builder.getUIntType(), IRIntegerValue(format)); + + auto newType = builder.getTextureType( + textureType->getElementType(), + textureType->getShapeInst(), + textureType->getIsArrayInst(), + textureType->getIsMultisampleInst(), + textureType->getSampleCountInst(), + textureType->getAccessInst(), + textureType->getIsShadowInst(), + textureType->getIsCombinedInst(), + formatArg); + + List<IRUse*> typeReplacementWorkList; + HashSet<IRUse*> typeReplacementWorkListSet; + + auto newInstType = (IRType*)replaceImageElementType(textureInst->getFullType(), newType); + textureInst->setFullType(newInstType); + + for (auto use = textureInst->firstUse; use; use = use->nextUse) + { + if (typeReplacementWorkListSet.add(use)) + typeReplacementWorkList.add(use); + } + + // Update the types on dependent insts. + for (Index i = 0; i < typeReplacementWorkList.getCount(); i++) + { + auto use = typeReplacementWorkList[i]; + auto user = use->getUser(); + switch (user->getOp()) + { + case kIROp_GetElementPtr: + case kIROp_GetElement: + case kIROp_Load: + case kIROp_Var: + { + auto newUserType = (IRType*)replaceImageElementType(user->getFullType(), newType); + if (newUserType != user->getFullType()) + { + user->setFullType(newUserType); + for (auto u = user->firstUse; u; u = u->nextUse) + { + if (typeReplacementWorkListSet.add(u)) + typeReplacementWorkList.add(u); + } + } + break; + } + case kIROp_Store: + { + auto store = as<IRStore>(user); + if (use == store->getValUse()) + { + auto ptr = store->getPtr(); + auto newPtrType = (IRType*)replaceImageElementType(ptr->getFullType(), newType); + if (newPtrType != ptr->getFullType()) + { + ptr->setFullType(newPtrType); + for (auto u = ptr->firstUse; u; u = u->nextUse) + { + if (typeReplacementWorkListSet.add(u)) + typeReplacementWorkList.add(u); + } + } + } + break; + } + } + } + } + } + + void resolveTextureFormat(IRModule* module) + { + for (auto globalInst : module->getGlobalInsts()) + { + if (as<IRTextureTypeBase>(globalInst->getDataType())) + { + resolveTextureFormatForParameter(globalInst, (IRTextureTypeBase*)globalInst->getDataType()); + } + else if (auto arrayType = as<IRArrayTypeBase>(globalInst->getDataType())) + { + if (as<IRTextureTypeBase>(arrayType->getElementType())) + { + resolveTextureFormatForParameter(globalInst, (IRTextureTypeBase*)arrayType->getElementType()); + } + } + } + } +} diff --git a/source/slang/slang-ir-resolve-texture-format.h b/source/slang/slang-ir-resolve-texture-format.h new file mode 100644 index 000000000..bae8931d8 --- /dev/null +++ b/source/slang/slang-ir-resolve-texture-format.h @@ -0,0 +1,6 @@ +#include "slang-ir.h" + +namespace Slang +{ + void resolveTextureFormat(IRModule* module); +} diff --git a/source/slang/slang-ir-spirv-legalize.cpp b/source/slang/slang-ir-spirv-legalize.cpp index c3d0874e7..7f815df95 100644 --- a/source/slang/slang-ir-spirv-legalize.cpp +++ b/source/slang/slang-ir-spirv-legalize.cpp @@ -344,195 +344,8 @@ struct SPIRVLegalizationContext : public SourceEmitterBase return false; } - static IRType* replaceImageElementType(IRInst* originalType, IRInst* newElementType) - { - switch(originalType->getOp()) - { - case kIROp_ArrayType: - case kIROp_UnsizedArrayType: - case kIROp_PtrType: - case kIROp_OutType: - case kIROp_RefType: - case kIROp_ConstRefType: - case kIROp_InOutType: - { - auto newInnerType = replaceImageElementType(originalType->getOperand(0), newElementType); - if (newInnerType != originalType->getOperand(0)) - { - IRBuilder builder(originalType); - builder.setInsertBefore(originalType); - IRCloneEnv cloneEnv; - cloneEnv.mapOldValToNew.add(originalType->getOperand(0), newInnerType); - return (IRType*)cloneInst(&cloneEnv, &builder, originalType); - } - return (IRType*)originalType; - } - - default: - if (as<IRResourceTypeBase>(originalType)) - return (IRType*)newElementType; - return (IRType*)originalType; - } - } - - static void inferTextureFormat(IRInst* textureInst, IRTextureTypeBase* textureType) - { - ImageFormat format = (ImageFormat)(textureType->getFormat()); - if (auto decor = textureInst->findDecoration<IRFormatDecoration>()) - { - format = decor->getFormat(); - } - - // If the texture has no format decoration, try to infer it from the type. - if (format == ImageFormat::unknown) - { - auto elementType = textureType->getElementType(); - Int vectorWidth = 1; - if (auto elementVecType = as<IRVectorType>(elementType)) - { - if (auto intLitVal = as<IRIntLit>(elementVecType->getElementCount())) - { - vectorWidth = (Int)intLitVal->getValue(); - } - else - { - vectorWidth = 0; - } - elementType = elementVecType->getElementType(); - } - switch (elementType->getOp()) - { - case kIROp_UIntType: - switch (vectorWidth) - { - case 1: format = ImageFormat::r32ui; break; - case 2: format = ImageFormat::rg32ui; break; - case 4: format = ImageFormat::rgba32ui; break; - } - break; - case kIROp_IntType: - switch (vectorWidth) - { - case 1: format = ImageFormat::r32i; break; - case 2: format = ImageFormat::rg32i; break; - case 4: format = ImageFormat::rgba32i; break; - } - break; - case kIROp_UInt16Type: - switch (vectorWidth) - { - case 1: format = ImageFormat::r16ui; break; - case 2: format = ImageFormat::rg16ui; break; - case 4: format = ImageFormat::rgba16ui; break; - } - break; - case kIROp_Int16Type: - switch (vectorWidth) - { - case 1: format = ImageFormat::r16i; break; - case 2: format = ImageFormat::rg16i; break; - case 4: format = ImageFormat::rgba16i; break; - } - break; - case kIROp_UInt8Type: - switch (vectorWidth) - { - case 1: format = ImageFormat::r8ui; break; - case 2: format = ImageFormat::rg8ui; break; - case 4: format = ImageFormat::rgba8ui; break; - } - break; - case kIROp_Int8Type: - switch (vectorWidth) - { - case 1: format = ImageFormat::r8i; break; - case 2: format = ImageFormat::rg8i; break; - case 4: format = ImageFormat::rgba8i; break; - } - break; - case kIROp_Int64Type: - switch (vectorWidth) - { - case 1: format = ImageFormat::r64i; break; - default: break; - } - break; - case kIROp_UInt64Type: - switch (vectorWidth) - { - case 1: format = ImageFormat::r64ui; break; - default: break; - } - break; - } - } - if (format != ImageFormat::unknown) - { - IRBuilder builder(textureInst->getModule()); - builder.setInsertBefore(textureInst); - auto formatArg = builder.getIntValue(builder.getUIntType(), IRIntegerValue(format)); - - auto newType = builder.getTextureType( - textureType->getElementType(), - textureType->getShapeInst(), - textureType->getIsArrayInst(), - textureType->getIsMultisampleInst(), - textureType->getSampleCountInst(), - textureType->getAccessInst(), - textureType->getIsShadowInst(), - textureType->getIsCombinedInst(), - formatArg); - - if (textureInst->getFullType() == textureType) - { - // Simple texture typed global param. - textureInst->setFullType(newType); - } - else - { - // Array typed global param. We need to replace the type and the types of all getElement insts. - auto newInstType = (IRType*)replaceImageElementType(textureInst->getFullType(), newType); - textureInst->setFullType(newInstType); - List<IRUse*> typeReplacementWorkList; - HashSet<IRUse*> typeReplacementWorkListSet; - for (auto use = textureInst->firstUse; use; use = use->nextUse) - { - if (typeReplacementWorkListSet.add(use)) - typeReplacementWorkList.add(use); - } - for (Index i = 0; i < typeReplacementWorkList.getCount(); i++) - { - auto use = typeReplacementWorkList[i]; - auto user = use->getUser(); - switch (user->getOp()) - { - case kIROp_GetElementPtr: - case kIROp_GetElement: - case kIROp_Load: - { - auto newUserType = (IRType*)replaceImageElementType(user->getFullType(), newType); - user->setFullType(newUserType); - for (auto u = user->firstUse; u; u = u->nextUse) - { - if (typeReplacementWorkListSet.add(u)) - typeReplacementWorkList.add(u); - } - break; - }; - } - } - } - } - } - void processGlobalParam(IRGlobalParam* inst) { - // If the param is a texture, infer its format. - if (auto textureType = as<IRTextureTypeBase>(unwrapArray(inst->getDataType()))) - { - inferTextureFormat(inst, textureType); - } - // If the global param is not a pointer type, make it so and insert explicit load insts. auto ptrType = as<IRPtrTypeBase>(inst->getDataType()); if (!ptrType) diff --git a/source/slang/slang-reflection-api.cpp b/source/slang/slang-reflection-api.cpp index b6fc05986..fdcac94d7 100644 --- a/source/slang/slang-reflection-api.cpp +++ b/source/slang/slang-reflection-api.cpp @@ -2421,6 +2421,24 @@ SLANG_API SlangReflectionVariable* spReflectionTypeLayout_getBindingRangeLeafVar return convert(DeclRef<Decl>(bindingRange.leafVariable)); } +SLANG_API SlangImageFormat spReflectionTypeLayout_getBindingRangeImageFormat(SlangReflectionTypeLayout* typeLayout, SlangInt index) +{ + auto typeLayout_ = convert(typeLayout); + if (!typeLayout_) return SLANG_IMAGE_FORMAT_unknown; + + auto extTypeLayout = Slang::getExtendedTypeLayout(typeLayout_); + if (index < 0) return SLANG_IMAGE_FORMAT_unknown; + if (index >= extTypeLayout->m_bindingRanges.getCount()) return SLANG_IMAGE_FORMAT_unknown; + auto& bindingRange = extTypeLayout->m_bindingRanges[index]; + + auto leafVar = bindingRange.leafVariable; + if (auto formatAttrib = leafVar->findModifier<FormatAttribute>()) + { + return (SlangImageFormat)formatAttrib->format; + } + return SLANG_IMAGE_FORMAT_unknown; +} + SLANG_API SlangInt spReflectionTypeLayout_getBindingRangeDescriptorSetIndex(SlangReflectionTypeLayout* inTypeLayout, SlangInt index) { diff --git a/source/slang/slang-syntax.cpp b/source/slang/slang-syntax.cpp index ec7169e04..8422a304b 100644 --- a/source/slang/slang-syntax.cpp +++ b/source/slang/slang-syntax.cpp @@ -890,10 +890,10 @@ Decl* getParentFunc(Decl* decl) static const ImageFormatInfo kImageFormatInfos[] = { #define SLANG_IMAGE_FORMAT_INFO(TYPE, COUNT, SIZE) SLANG_SCALAR_TYPE_##TYPE, uint8_t(COUNT), uint8_t(SIZE) -#define FORMAT(NAME, OTHER) \ +#define SLANG_FORMAT(NAME, OTHER) \ { SLANG_IMAGE_FORMAT_INFO OTHER, UnownedStringSlice::fromLiteral(#NAME) }, #include "slang-image-format-defs.h" -#undef FORMAT +#undef SLANG_FORMAT #undef SLANG_IMAGE_FORMAT_INFO }; diff --git a/tools/slang-unit-test/unit-test-image-format-reflection.cpp b/tools/slang-unit-test/unit-test-image-format-reflection.cpp new file mode 100644 index 000000000..2ada598b4 --- /dev/null +++ b/tools/slang-unit-test/unit-test-image-format-reflection.cpp @@ -0,0 +1,58 @@ +// unit-test-translation-unit-import.cpp + +#include "slang.h" + +#include <stdio.h> +#include <stdlib.h> + +#include "tools/unit-test/slang-unit-test.h" +#include "slang-com-ptr.h" +#include "../../source/core/slang-io.h" +#include "../../source/core/slang-process.h" + +using namespace Slang; + +// Test that the getBindingRangeImageFormat API works. + +SLANG_UNIT_TEST(imageFormatReflection) +{ + // Source for a module that contains an undecorated entrypoint. + const char* userSourceBody = R"( + Texture2D<uint4> g_tex : register(t0); + float4 fragMain(float4 pos:SV_Position) : SV_Position + { + return pos; + } + )"; + + auto moduleName = "moduleG" + String(Process::getId()); + String userSource = "import " + moduleName + ";\n" + userSourceBody; + ComPtr<slang::IGlobalSession> globalSession; + SLANG_CHECK(slang_createGlobalSession(SLANG_API_VERSION, globalSession.writeRef()) == SLANG_OK); + slang::TargetDesc targetDesc = {}; + targetDesc.format = SLANG_HLSL; + targetDesc.profile = globalSession->findProfile("sm_5_0"); + slang::SessionDesc sessionDesc = {}; + sessionDesc.targetCount = 1; + sessionDesc.targets = &targetDesc; + ComPtr<slang::ISession> session; + SLANG_CHECK(globalSession->createSession(sessionDesc, session.writeRef()) == SLANG_OK); + + ComPtr<slang::IBlob> diagnosticBlob; + auto module = session->loadModuleFromSourceString("m", "m.slang", userSourceBody, diagnosticBlob.writeRef()); + SLANG_CHECK(module != nullptr); + + ComPtr<slang::IEntryPoint> entryPoint; + module->findAndCheckEntryPoint("fragMain", SLANG_STAGE_FRAGMENT, entryPoint.writeRef(), diagnosticBlob.writeRef()); + SLANG_CHECK(entryPoint != nullptr); + + ComPtr<slang::IComponentType> compositeProgram; + slang::IComponentType* components[] = { module, entryPoint.get() }; + session->createCompositeComponentType(components, 2, compositeProgram.writeRef(), diagnosticBlob.writeRef()); + SLANG_CHECK(compositeProgram != nullptr); + + auto layout = compositeProgram->getLayout(0); + auto format = layout->getGlobalParamsTypeLayout()->getBindingRangeImageFormat(0); + SLANG_CHECK(format == SLANG_IMAGE_FORMAT_rgba32ui); +} + |
