summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYong He <yonghe@outlook.com>2024-09-26 09:44:08 -0700
committerGitHub <noreply@github.com>2024-09-26 09:44:08 -0700
commit5a0224a0773f6d7f5eae8515424af5fa8faa9c14 (patch)
treeac14b54ad253d4ac5413a3c86254929f332b9e1a
parent7398e1e09312ed4e19195e060de9a2c9a073fcc1 (diff)
Move texture format inference to frontend and add reflection api for it. (#5155)
-rw-r--r--include/slang-image-format-defs.h49
-rw-r--r--include/slang.h15
-rw-r--r--source/slang/slang-ast-modifier.h1
-rw-r--r--source/slang/slang-ast-support-types.h3
-rw-r--r--source/slang/slang-ast-type.cpp5
-rw-r--r--source/slang/slang-ast-type.h1
-rw-r--r--source/slang/slang-check-decl.cpp153
-rw-r--r--source/slang/slang-check.h2
-rw-r--r--source/slang/slang-emit-glsl.cpp124
-rw-r--r--source/slang/slang-emit.cpp10
-rw-r--r--source/slang/slang-image-format-defs.h49
-rw-r--r--source/slang/slang-ir-insts.h3
-rw-r--r--source/slang/slang-ir-resolve-texture-format.cpp142
-rw-r--r--source/slang/slang-ir-resolve-texture-format.h6
-rw-r--r--source/slang/slang-ir-spirv-legalize.cpp187
-rw-r--r--source/slang/slang-reflection-api.cpp18
-rw-r--r--source/slang/slang-syntax.cpp4
-rw-r--r--tools/slang-unit-test/unit-test-image-format-reflection.cpp58
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);
+}
+