From e1d0ef2002d7b0f459cf689ec1f8e37c4ff2afba Mon Sep 17 00:00:00 2001
From: ArielG-NV <159081215+ArielG-NV@users.noreply.github.com>
Date: Wed, 26 Jun 2024 09:37:18 -0400
Subject: Expand upon existing `ImageSubscript` support (Metal, GLSL, SPIRV)
(#4408)
* Add additional `ImageSubscript` features:
1. Added ImageSubscript support for Metal & a test case
* Merge GLSL/SPIRV/Metal `ImageSubscript` legalization pass
2. Added multisample support to glsl/spirv/metal for when using ImageSubscript
* Added in this PR since the overhaul of the code merges together GLSL/SPIRV/Metal implementation
3. Fixed minor metal texture `Load`/`Read` bugs
* [HLSL methods of access do not support subscript accessor for texture cube array](https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/texturecubearray)
* removed swizzling of uint/int/float
* other odd bugs which were causing compile errors
note: Compute tests do not work due to what seems to be the GFX backend (causes crash without error report). The tests are disabled.
* disable LOD with texture 1d
seems that LOD for 1d textures need to be a compile time constant as per an error metal throws
* syntax error in hlsl.meta
* static_assert alone with intrinsic_asm error
provides cleaner errors
Note: `static_assert` seems to be unstable and not be fully respected (still require `intrinsic_asm` to avoid a stdlib compile error)
* change comment to `// lod is not supported for 1D texture
* add `static_assert` in related code gen paths
* address review
* address review
* add asserts as per review comment, NOTE: unclear if these should be release 'asserts' as well
---
build/visual-studio/slang/slang.vcxproj | 2 +
build/visual-studio/slang/slang.vcxproj.filters | 6 +
source/slang/hlsl.meta.slang | 31 ++--
source/slang/slang-diagnostic-defs.h | 2 +
source/slang/slang-emit-glsl.cpp | 22 ++-
source/slang/slang-emit-metal.cpp | 41 +++++
source/slang/slang-emit-spirv.cpp | 18 +-
source/slang/slang-emit.cpp | 19 +-
source/slang/slang-ir-glsl-legalize.cpp | 142 --------------
source/slang/slang-ir-glsl-legalize.h | 2 -
source/slang/slang-ir-insts.h | 28 ++-
source/slang/slang-ir-legalize-image-subscript.cpp | 203 +++++++++++++++++++++
source/slang/slang-ir-legalize-image-subscript.h | 11 ++
source/slang/slang-ir-util.cpp | 13 ++
source/slang/slang-ir-util.h | 3 +
source/slang/slang-ir.cpp | 11 +-
tests/compute/texture-subscript-multisample.slang | 41 +++++
tests/compute/texture-subscript.slang | 58 ++++++
18 files changed, 472 insertions(+), 181 deletions(-)
create mode 100644 source/slang/slang-ir-legalize-image-subscript.cpp
create mode 100644 source/slang/slang-ir-legalize-image-subscript.h
create mode 100644 tests/compute/texture-subscript-multisample.slang
create mode 100644 tests/compute/texture-subscript.slang
diff --git a/build/visual-studio/slang/slang.vcxproj b/build/visual-studio/slang/slang.vcxproj
index c0f61ffb5..cbafa9975 100644
--- a/build/visual-studio/slang/slang.vcxproj
+++ b/build/visual-studio/slang/slang.vcxproj
@@ -422,6 +422,7 @@ IF EXIST ..\..\..\external\slang-glslang\bin\windows-aarch64\release\slang-glsla
+
@@ -665,6 +666,7 @@ IF EXIST ..\..\..\external\slang-glslang\bin\windows-aarch64\release\slang-glsla
+
diff --git a/build/visual-studio/slang/slang.vcxproj.filters b/build/visual-studio/slang/slang.vcxproj.filters
index 601de8b63..ab272f7e1 100644
--- a/build/visual-studio/slang/slang.vcxproj.filters
+++ b/build/visual-studio/slang/slang.vcxproj.filters
@@ -354,6 +354,9 @@
Header Files
+
+ Header Files
+
Header Files
@@ -1079,6 +1082,9 @@
Source Files
+
+ Source Files
+
Source Files
diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang
index 597e4dc06..94a5208b0 100644
--- a/source/slang/hlsl.meta.slang
+++ b/source/slang/hlsl.meta.slang
@@ -2416,11 +2416,12 @@ extension __TextureImpl(($1).xyz), uint(($1).w))$z";
break;
case $(SLANG_TEXTURE_CUBE):
+ static_assert(isArray == 0, "Unsupported 'Load' of 'texture cube array' for 'metal' target");
if (isShadow == 1)
{
if (isArray == 1)
// T read(uint2 coord, uint face, uint array, uint lod = 0) const
- __intrinsic_asm "$0.read(vec(($1).xy), uint(($1).z), uint(($1).w))";
+ __intrinsic_asm "";
else
// T read(uint2 coord, uint face, uint lod = 0) const
__intrinsic_asm "$c$0.read(vec(($1).xy), uint(($1).z), uint(($1).w))$z";
@@ -2429,14 +2430,14 @@ extension __TextureImpl(($1).xy), uint(($1).z), uint(($1).w))";
+ __intrinsic_asm "";
else
// Tv read(uint2 coord, uint face, uint lod = 0) const
__intrinsic_asm "$c$0.read(vec(($1).xy), uint(($1).z), uint(($1).w))$z";
}
break;
}
- // TODO: This needs to be handled by the capability system
+ static_assert(false, "Unsupported 'Load' of 'texture' for 'metal' target");
__intrinsic_asm "";
case glsl:
__intrinsic_asm "$ctexelFetch($0, ($1).$w1b, ($1).$w1e)$z";
@@ -2819,6 +2820,7 @@ extension __TextureImpl(($1).xyz))$z";
break;
case $(SLANG_TEXTURE_CUBE):
+ static_assert(isArray == 0, "Unsupported 'Load' of 'texture cube array' for 'metal' target");
if (isShadow == 1)
{
if (isArray == 1)
@@ -2839,7 +2841,7 @@ extension __TextureImpl";
}
}
@@ -2960,10 +2962,10 @@ extension __TextureImpl(($1).xyz))";
break;
case $(SLANG_TEXTURE_CUBE):
+ static_assert(isArray == 0, "Unsupported 'Store' of 'texture cube array' for 'metal' target");
if (isShadow == 1)
{
- if (isArray == 1)
- // void write(Tv color, uint2 coord, uint face, uint array, uint lod = 0) const
- __intrinsic_asm "$0.write($2, vec(($1).xy), uint(($1).z)%6, uint(($1).z)/6)";
- else
- // void write(Tv color, uint2 coord, uint face, uint lod = 0) const
- __intrinsic_asm "$0.write($2, vec(($1).xy), uint(($1).z))";
+ // void write(Tv color, uint2 coord, uint face, uint lod = 0) const
+ __intrinsic_asm "$0.write($2, vec(($1).xy), uint(($1).z), uint(($1).w))";
}
else
{
- if (isArray == 1)
- // void write(Tv color, uint2 coord, uint face, uint array, uint lod = 0) const
- __intrinsic_asm "$0.write($2, vec(($1).xy), uint(($1).z)%6, uint(($1).z)/6)";
- else
- // void write(Tv color, uint2 coord, uint face, uint lod = 0) const
- __intrinsic_asm "$0.write($2, vec(($1).xy), uint(($1).z))";
+ // void write(Tv color, uint2 coord, uint face, uint lod = 0) const
+ __intrinsic_asm "$0.write($2, vec(($1).xy), uint(($1).z), uint(($1).w))";
}
break;
}
diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h
index e27e40c59..5b9c3e65a 100644
--- a/source/slang/slang-diagnostic-defs.h
+++ b/source/slang/slang-diagnostic-defs.h
@@ -774,6 +774,8 @@ DIAGNOSTIC(41400, Error, staticAssertionFailure, "static assertion failed, $0")
DIAGNOSTIC(41401, Error, staticAssertionFailureWithoutMessage, "static assertion failed.")
DIAGNOSTIC(41402, Error, staticAssertionConditionNotConstant, "condition for static assertion cannot be evaluated at the compile-time.")
+DIAGNOSTIC(41402, Error, multiSampledTextureDoesNotAllowWrites, "cannot write to a multisampled texture with target '$0'.")
+
//
// 5xxxx - Target code generation.
//
diff --git a/source/slang/slang-emit-glsl.cpp b/source/slang/slang-emit-glsl.cpp
index 2a7f22905..b6c3c0d67 100644
--- a/source/slang/slang-emit-glsl.cpp
+++ b/source/slang/slang-emit-glsl.cpp
@@ -2021,21 +2021,33 @@ bool GLSLSourceEmitter::tryEmitInstExprImpl(IRInst* inst, const EmitOpInfo& inOu
}
case kIROp_ImageLoad:
{
+ auto imageOp = as(inst);
m_writer->emit("imageLoad(");
- emitOperand(inst->getOperand(0), getInfo(EmitOp::General));
+ emitOperand(imageOp->getImage(), getInfo(EmitOp::General));
m_writer->emit(",");
- emitOperand(inst->getOperand(1), getInfo(EmitOp::General));
+ emitOperand(imageOp->getCoord(), getInfo(EmitOp::General));
+ if (imageOp->hasAuxCoord1())
+ {
+ m_writer->emit(",");
+ emitOperand(imageOp->getAuxCoord1(), getInfo(EmitOp::General));
+ }
m_writer->emit(")");
return true;
}
case kIROp_ImageStore:
{
+ auto imageOp = as(inst);
m_writer->emit("imageStore(");
- emitOperand(inst->getOperand(0), getInfo(EmitOp::General));
+ emitOperand(imageOp->getImage(), getInfo(EmitOp::General));
m_writer->emit(",");
- emitOperand(inst->getOperand(1), getInfo(EmitOp::General));
+ emitOperand(imageOp->getCoord(), getInfo(EmitOp::General));
+ if (imageOp->hasAuxCoord1())
+ {
+ m_writer->emit(",");
+ emitOperand(imageOp->getAuxCoord1(), getInfo(EmitOp::General));
+ }
m_writer->emit(",");
- emitOperand(inst->getOperand(2), getInfo(EmitOp::General));
+ emitOperand(imageOp->getValue(), getInfo(EmitOp::General));
m_writer->emit(")");
return true;
}
diff --git a/source/slang/slang-emit-metal.cpp b/source/slang/slang-emit-metal.cpp
index d38c3de9b..e7df29e0c 100644
--- a/source/slang/slang-emit-metal.cpp
+++ b/source/slang/slang-emit-metal.cpp
@@ -451,6 +451,47 @@ bool MetalSourceEmitter::tryEmitInstExprImpl(IRInst* inst, const EmitOpInfo& inO
emitOperand(inst->getOperand(2), getInfo(EmitOp::General));
return true;
}
+ case kIROp_ImageLoad:
+ {
+ auto imageOp = as(inst);
+ emitOperand(imageOp->getImage(), getInfo(EmitOp::General));
+ m_writer->emit(".read(");
+ emitOperand(imageOp->getCoord(), getInfo(EmitOp::General));
+ if(imageOp->hasAuxCoord1())
+ {
+ m_writer->emit(",");
+ emitOperand(imageOp->getAuxCoord1(), getInfo(EmitOp::General));
+ }
+ if(imageOp->hasAuxCoord2())
+ {
+ m_writer->emit(",");
+ emitOperand(imageOp->getAuxCoord2(), getInfo(EmitOp::General));
+ }
+ m_writer->emit(")");
+ return true;
+ }
+ case kIROp_ImageStore:
+ {
+
+ auto imageOp = as(inst);
+ emitOperand(imageOp->getImage(), getInfo(EmitOp::General));
+ m_writer->emit(".write(");
+ emitOperand(imageOp->getValue(), getInfo(EmitOp::General));
+ m_writer->emit(",");
+ emitOperand(imageOp->getCoord(), getInfo(EmitOp::General));
+ if(imageOp->hasAuxCoord1())
+ {
+ m_writer->emit(",");
+ emitOperand(imageOp->getAuxCoord1(), getInfo(EmitOp::General));
+ }
+ if(imageOp->hasAuxCoord2())
+ {
+ m_writer->emit(",");
+ emitOperand(imageOp->getAuxCoord2(), getInfo(EmitOp::General));
+ }
+ m_writer->emit(")");
+ return true;
+ }
default: break;
}
// Not handled
diff --git a/source/slang/slang-emit-spirv.cpp b/source/slang/slang-emit-spirv.cpp
index 3ee0d9241..51a71d65c 100644
--- a/source/slang/slang-emit-spirv.cpp
+++ b/source/slang/slang-emit-spirv.cpp
@@ -2989,12 +2989,26 @@ struct SPIRVEmitContext
SpvInst* emitImageLoad(SpvInstParent* parent, IRImageLoad* load)
{
- return emitInst(parent, load, SpvOpImageRead, load->getDataType(), kResultID, load->getImage(), load->getCoord());
+ if (load->hasAuxCoord1())
+ {
+ return emitInst(parent, load, SpvOpImageRead, load->getDataType(), kResultID, load->getImage(), load->getCoord(), SpvImageOperandsSampleMask, load->getAuxCoord1());
+ }
+ else
+ {
+ return emitInst(parent, load, SpvOpImageRead, load->getDataType(), kResultID, load->getImage(), load->getCoord());
+ }
}
SpvInst* emitImageStore(SpvInstParent* parent, IRImageStore* store)
{
- return emitInst(parent, store, SpvOpImageWrite, store->getImage(), store->getCoord(), store->getValue());
+ if (store->hasAuxCoord1())
+ {
+ return emitInst(parent, store, SpvOpImageWrite, store->getImage(), store->getCoord(), store->getValue(), SpvImageOperandsSampleMask, store->getAuxCoord1());
+ }
+ else
+ {
+ return emitInst(parent, store, SpvOpImageWrite, store->getImage(), store->getCoord(), store->getValue());
+ }
}
SpvInst* emitImageSubscript(SpvInstParent* parent, IRImageSubscript* subscript)
diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp
index 0f53f74cd..bb6bab0ab 100644
--- a/source/slang/slang-emit.cpp
+++ b/source/slang/slang-emit.cpp
@@ -50,6 +50,7 @@
#include "slang-ir-lower-l-value-cast.h"
#include "slang-ir-lower-reinterpret.h"
#include "slang-ir-loop-unroll.h"
+#include "slang-ir-legalize-image-subscript.h"
#include "slang-ir-legalize-vector-types.h"
#include "slang-ir-metadata.h"
#include "slang-ir-optix-entry-point-uniforms.h"
@@ -1153,14 +1154,28 @@ Result linkAndOptimizeIR(
if(isD3DTarget(targetRequest))
legalizeNonStructParameterToStructForHLSL(irModule);
- // Legalize `ImageSubscript` and constant buffer loads for GLSL.
+ // Legalize `ImageSubscript` loads.
+ switch (target)
+ {
+ case CodeGenTarget::Metal:
+ case CodeGenTarget::GLSL:
+ case CodeGenTarget::SPIRV:
+ case CodeGenTarget::SPIRVAssembly:
+ {
+ legalizeImageSubscript(targetRequest, irModule, sink);
+ }
+ break;
+ default:
+ break;
+ }
+
+ // Legalize constant buffer loads.
switch (target)
{
case CodeGenTarget::GLSL:
case CodeGenTarget::SPIRV:
case CodeGenTarget::SPIRVAssembly:
{
- legalizeImageSubscriptForGLSL(irModule);
legalizeConstantBufferLoadForGLSL(irModule);
legalizeDispatchMeshPayloadForGLSL(irModule);
}
diff --git a/source/slang/slang-ir-glsl-legalize.cpp b/source/slang/slang-ir-glsl-legalize.cpp
index be222c87f..0e23460ce 100644
--- a/source/slang/slang-ir-glsl-legalize.cpp
+++ b/source/slang/slang-ir-glsl-legalize.cpp
@@ -15,148 +15,6 @@
namespace Slang
{
-int getIRVectorElementSize(IRType* type)
-{
- if (type->getOp() != kIROp_VectorType)
- return 1;
- return (int)(as(as(type)->getElementCount())->value.intVal);
-}
-IRType* getIRVectorBaseType(IRType* type)
-{
- if (type->getOp() != kIROp_VectorType)
- return type;
- return as(type)->getElementType();
-}
-
-void legalizeImageSubscriptStoreForGLSL(IRBuilder& builder, IRInst* storeInst)
-{
- builder.setInsertBefore(storeInst);
- auto getElementPtr = as(storeInst->getOperand(0));
- IRImageSubscript* imageSubscript = nullptr;
- if (getElementPtr)
- imageSubscript = as(getElementPtr->getBase());
- else
- imageSubscript = as(storeInst->getOperand(0));
- assert(imageSubscript);
- auto imageElementType = cast(imageSubscript->getDataType())->getValueType();
- auto coordType = imageSubscript->getCoord()->getDataType();
- auto coordVectorSize = getIRVectorElementSize(coordType);
- if (coordVectorSize != 1)
- {
- coordType = builder.getVectorType(
- builder.getIntType(), builder.getIntValue(builder.getIntType(), coordVectorSize));
- }
- else
- {
- coordType = builder.getIntType();
- }
- auto legalizedCoord = imageSubscript->getCoord();
- if (coordType != imageSubscript->getCoord()->getDataType())
- {
- legalizedCoord = builder.emitCast(coordType, legalizedCoord);
- }
- switch (storeInst->getOp())
- {
- case kIROp_Store:
- {
- IRInst* newValue = nullptr;
- if (getElementPtr)
- {
- auto vectorBaseType = getIRVectorBaseType(imageElementType);
- IRType* vector4Type = builder.getVectorType(vectorBaseType, 4);
- auto originalValue = builder.emitImageLoad(vector4Type, imageSubscript->getImage(), legalizedCoord);
- auto index = getElementPtr->getIndex();
- newValue = builder.emitSwizzleSet(vector4Type, originalValue, storeInst->getOperand(1), 1, &index);
- }
- else
- {
- newValue = storeInst->getOperand(1);
- if (getIRVectorElementSize(imageElementType) != 4)
- {
- auto vectorBaseType = getIRVectorBaseType(imageElementType);
- newValue = builder.emitVectorReshape(
- builder.getVectorType(
- vectorBaseType, builder.getIntValue(builder.getIntType(), 4)),
- newValue);
- }
- }
- auto imageStore = builder.emitImageStore(
- builder.getVoidType(),
- imageSubscript->getImage(),
- legalizedCoord,
- newValue);
- storeInst->replaceUsesWith(imageStore);
- storeInst->removeAndDeallocate();
- if (!imageSubscript->hasUses())
- {
- imageSubscript->removeAndDeallocate();
- }
- }
- break;
- case kIROp_SwizzledStore:
- {
- auto swizzledStore = cast(storeInst);
- // Here we assume the imageElementType is already lowered into float4/uint4 types from any
- // user-defined type.
- assert(imageElementType->getOp() == kIROp_VectorType);
- auto vectorBaseType = getIRVectorBaseType(imageElementType);
- IRType* vector4Type = builder.getVectorType(vectorBaseType, 4);
- auto originalValue = builder.emitImageLoad(vector4Type, imageSubscript->getImage(), legalizedCoord);
- Array indices;
- for (UInt i = 0; i < swizzledStore->getElementCount(); i++)
- {
- indices.add(swizzledStore->getElementIndex(i));
- }
- auto newValue = builder.emitSwizzleSet(
- vector4Type,
- originalValue,
- swizzledStore->getSource(),
- swizzledStore->getElementCount(),
- indices.getBuffer());
- auto imageStore = builder.emitImageStore(
- builder.getVoidType(), imageSubscript->getImage(), legalizedCoord, newValue);
- storeInst->replaceUsesWith(imageStore);
- storeInst->removeAndDeallocate();
- if (!imageSubscript->hasUses())
- {
- imageSubscript->removeAndDeallocate();
- }
- }
- break;
- default:
- break;
- }
-}
-
-void legalizeImageSubscriptForGLSL(IRModule* module)
-{
- IRBuilder builder(module);
- for (auto globalInst : module->getModuleInst()->getChildren())
- {
- auto func = as(globalInst);
- if (!func)
- continue;
- for (auto block : func->getBlocks())
- {
- auto inst = block->getFirstInst();
- IRInst* next;
- for ( ; inst; inst = next)
- {
- next = inst->getNextInst();
- switch (inst->getOp())
- {
- case kIROp_Store:
- case kIROp_SwizzledStore:
- if (getRootAddr(inst->getOperand(0))->getOp() == kIROp_ImageSubscript)
- {
- legalizeImageSubscriptStoreForGLSL(builder, inst);
- }
- }
- }
- }
- }
-}
-
//
// Legalization of entry points for GLSL:
//
diff --git a/source/slang/slang-ir-glsl-legalize.h b/source/slang/slang-ir-glsl-legalize.h
index 6fd0b642e..8ec164a96 100644
--- a/source/slang/slang-ir-glsl-legalize.h
+++ b/source/slang/slang-ir-glsl-legalize.h
@@ -21,8 +21,6 @@ void legalizeEntryPointsForGLSL(
CodeGenContext* context,
GLSLExtensionTracker* glslExtensionTracker);
-void legalizeImageSubscriptForGLSL(IRModule* module);
-
void legalizeConstantBufferLoadForGLSL(IRModule* module);
void legalizeDispatchMeshPayloadForGLSL(IRModule* module);
diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h
index 4781ea2c3..a63cc5c22 100644
--- a/source/slang/slang-ir-insts.h
+++ b/source/slang/slang-ir-insts.h
@@ -2440,6 +2440,8 @@ struct IRImageSubscript : IRInst
IR_LEAF_ISA(ImageSubscript);
IRInst* getImage() { return getOperand(0); }
IRInst* getCoord() { return getOperand(1); }
+ bool hasSampleCoord() { return getOperandCount() > 2 && getOperand(2) != nullptr; }
+ IRInst* getSampleCoord() { return getOperand(2); }
};
struct IRImageLoad : IRInst
@@ -2447,6 +2449,16 @@ struct IRImageLoad : IRInst
IR_LEAF_ISA(ImageLoad);
IRInst* getImage() { return getOperand(0); }
IRInst* getCoord() { return getOperand(1); }
+
+ /// If GLSL/SPIR-V, Sample coord
+ /// If Metal, Array or Sample coord
+ bool hasAuxCoord1() { return getOperandCount() > 2 && getOperand(2) != nullptr; }
+ IRInst* getAuxCoord1() { return getOperand(2); }
+
+ /// If Metal, Sample coord
+ bool hasAuxCoord2() { return getOperandCount() > 3 && getOperand(3) != nullptr; }
+ IRInst* getAuxCoord2() { return getOperand(3); }
+
};
struct IRImageStore : IRInst
@@ -2455,6 +2467,15 @@ struct IRImageStore : IRInst
IRInst* getImage() { return getOperand(0); }
IRInst* getCoord() { return getOperand(1); }
IRInst* getValue() { return getOperand(2); }
+
+ /// If GLSL/SPIR-V, Sample coord
+ /// If Metal, Array or Sample coord
+ bool hasAuxCoord1() { return getOperandCount() > 3 && getOperand(3) != nullptr; }
+ IRInst* getAuxCoord1() { return getOperand(3); }
+
+ /// If Metal, Sample coord
+ bool hasAuxCoord2() { return getOperandCount() > 4 && getOperand(4) != nullptr; }
+ IRInst* getAuxCoord2() { return getOperand(4); }
};
// Terminators
@@ -4048,14 +4069,11 @@ public:
IRInst* emitImageLoad(
IRType* type,
- IRInst* image,
- IRInst* coord);
+ ShortList params);
IRInst* emitImageStore(
IRType* type,
- IRInst* image,
- IRInst* coord,
- IRInst* value);
+ ShortList params);
IRInst* emitIsType(IRInst* value, IRInst* witness, IRInst* typeOperand, IRInst* targetWitness);
diff --git a/source/slang/slang-ir-legalize-image-subscript.cpp b/source/slang/slang-ir-legalize-image-subscript.cpp
new file mode 100644
index 000000000..b5b240675
--- /dev/null
+++ b/source/slang/slang-ir-legalize-image-subscript.cpp
@@ -0,0 +1,203 @@
+#include "slang-ir-legalize-image-subscript.h"
+
+#include "slang-ir.h"
+#include "slang-ir-insts.h"
+#include "slang-ir-util.h"
+#include "slang-ir-clone.h"
+#include "slang-ir-specialize-address-space.h"
+#include "slang-parameter-binding.h"
+#include "slang-ir-legalize-varying-params.h"
+
+namespace Slang
+{
+ void legalizeStore(TargetRequest* target, IRBuilder& builder, IRInst* storeInst, DiagnosticSink* sink)
+ {
+ SLANG_ASSERT(storeInst);
+
+ builder.setInsertBefore(storeInst);
+ IRImageSubscript* imageSubscript = nullptr;
+ auto getElementPtr = as(storeInst->getOperand(0));
+ if(getElementPtr)
+ {
+ imageSubscript = as(getElementPtr->getBase());
+ }
+ else
+ {
+ imageSubscript = as(storeInst->getOperand(0));
+ }
+ SLANG_ASSERT(imageSubscript);
+ SLANG_ASSERT(imageSubscript->getImage());
+ IRTextureType* textureType = as(imageSubscript->getImage()->getFullType());
+ SLANG_ASSERT(textureType);
+ auto imageElementType = cast(imageSubscript->getDataType())->getValueType();
+ auto vectorBaseType = getIRVectorBaseType(imageElementType);
+ IRType* vector4Type = builder.getVectorType(vectorBaseType, 4);
+ IRType* coordType = imageSubscript->getCoord()->getDataType();
+ int coordVectorSize = getIRVectorElementSize(coordType);
+
+ bool seperateArrayCoord = (isMetalTarget(target) && textureType->isArray()); // seperate array param
+ bool seperateSampleCoord = (textureType->isMultisample()); // seperate sample param
+
+ if(seperateSampleCoord && isMetalTarget(target))
+ sink->diagnose(imageSubscript->getImage(), Diagnostics::multiSampledTextureDoesNotAllowWrites, target->getTarget());
+
+ IRType* indexingType = builder.getIntType();
+ if(isMetalTarget(target))
+ indexingType = builder.getUIntType();
+
+ if (coordVectorSize != 1)
+ {
+ coordType = builder.getVectorType(
+ indexingType, builder.getIntValue(builder.getIntType(), coordVectorSize));
+ }
+ else
+ {
+ coordType = indexingType;
+ }
+
+ auto legalizedCoord = imageSubscript->getCoord();
+ if (coordType != imageSubscript->getCoord()->getDataType())
+ {
+ legalizedCoord = builder.emitCast(coordType, legalizedCoord);
+ }
+
+ const Index kCoordParamIndex = 1;
+ const Index kValueParamIndex = 2;
+
+ ShortList loadParams;
+ loadParams.reserveOverflowBuffer(4);
+ loadParams.add(imageSubscript->getImage()); // image
+ loadParams.add(legalizedCoord); // coord
+
+ ShortList storeParams;
+ storeParams.reserveOverflowBuffer(5);
+ storeParams.add(imageSubscript->getImage()); // image
+ storeParams.add(legalizedCoord); // coord
+ storeParams.add(nullptr); // value
+
+ if (seperateArrayCoord)
+ {
+
+ UInt paramIndexToFetch = coordVectorSize - 1;
+
+ auto seperatedParam = builder.emitSwizzle(indexingType, legalizedCoord, 1, ¶mIndexToFetch);
+ loadParams.add(seperatedParam);
+ storeParams.add(seperatedParam);
+
+ coordVectorSize -= 1;
+ ShortList paramToFetch;
+ paramToFetch.reserveOverflowBuffer(coordVectorSize);
+ for (int i = 0; i < coordVectorSize; i++)
+ {
+ paramToFetch.add(i);
+ }
+ auto newCoord = builder.emitSwizzle(builder.getVectorType(indexingType, builder.getIntValue(builder.getIntType(), coordVectorSize)), legalizedCoord, coordVectorSize, paramToFetch.getArrayView().getBuffer());
+ storeParams[kCoordParamIndex] = newCoord;
+ loadParams[kCoordParamIndex] = newCoord;
+ }
+ if (seperateSampleCoord)
+ {
+ loadParams.add(imageSubscript->getSampleCoord());
+ storeParams.add(imageSubscript->getSampleCoord());
+ }
+
+ IRInst* legalizedStore = storeInst->getOperand(1);
+ switch (storeInst->getOp())
+ {
+ case kIROp_Store:
+ {
+ IRInst* newValue = nullptr;
+ if (getElementPtr)
+ {
+ auto originalValue = builder.emitImageLoad(vector4Type, loadParams);
+ auto index = getElementPtr->getIndex();
+ newValue = builder.emitSwizzleSet(vector4Type, originalValue, legalizedStore, 1, &index);
+ }
+ else
+ {
+ newValue = legalizedStore;
+ if (getIRVectorElementSize(imageElementType) != 4)
+ {
+ newValue = builder.emitVectorReshape(
+ builder.getVectorType(
+ vectorBaseType, builder.getIntValue(builder.getIntType(), 4)),
+ newValue);
+ }
+ }
+
+ storeParams[kValueParamIndex] = newValue;
+ auto imageStore = builder.emitImageStore(
+ builder.getVoidType(),
+ storeParams);
+ storeInst->replaceUsesWith(imageStore);
+ storeInst->removeAndDeallocate();
+ if (!imageSubscript->hasUses())
+ {
+ imageSubscript->removeAndDeallocate();
+ }
+ }
+ break;
+ case kIROp_SwizzledStore:
+ {
+ auto swizzledStore = cast(storeInst);
+ // Here we assume the imageElementType is already lowered into float4/uint4 types from any
+ // user-defined type.
+ assert(imageElementType->getOp() == kIROp_VectorType);
+ auto originalValue = builder.emitImageLoad(vector4Type, loadParams);
+ Array indices;
+ for (UInt i = 0; i < swizzledStore->getElementCount(); i++)
+ {
+ indices.add(swizzledStore->getElementIndex(i));
+ }
+ auto newValue = builder.emitSwizzleSet(
+ vector4Type,
+ originalValue,
+ swizzledStore->getSource(),
+ swizzledStore->getElementCount(),
+ indices.getBuffer());
+ storeParams[kValueParamIndex] = newValue;
+ auto imageStore = builder.emitImageStore(
+ builder.getVoidType(),
+ storeParams);
+ storeInst->replaceUsesWith(imageStore);
+ storeInst->removeAndDeallocate();
+ if (!imageSubscript->hasUses())
+ {
+ imageSubscript->removeAndDeallocate();
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ void legalizeImageSubscript(TargetRequest* target, IRModule* module, DiagnosticSink* sink)
+ {
+ IRBuilder builder(module);
+ for (auto globalInst : module->getModuleInst()->getChildren())
+ {
+ auto func = as(globalInst);
+ if (!func)
+ continue;
+ for (auto block : func->getBlocks())
+ {
+ auto inst = block->getFirstInst();
+ IRInst* next;
+ for ( ; inst; inst = next)
+ {
+ next = inst->getNextInst();
+ switch (inst->getOp())
+ {
+ case kIROp_Store:
+ case kIROp_SwizzledStore:
+ if (getRootAddr(inst->getOperand(0))->getOp() == kIROp_ImageSubscript)
+ {
+ legalizeStore(target, builder, inst, sink);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
diff --git a/source/slang/slang-ir-legalize-image-subscript.h b/source/slang/slang-ir-legalize-image-subscript.h
new file mode 100644
index 000000000..71d20b32d
--- /dev/null
+++ b/source/slang/slang-ir-legalize-image-subscript.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "slang-ir.h"
+#include "slang-compiler.h"
+
+namespace Slang
+{
+ class DiagnosticSink;
+
+ void legalizeImageSubscript(TargetRequest* target, IRModule* module, DiagnosticSink* sink);
+}
diff --git a/source/slang/slang-ir-util.cpp b/source/slang/slang-ir-util.cpp
index 40d169c48..10c7bfea6 100644
--- a/source/slang/slang-ir-util.cpp
+++ b/source/slang/slang-ir-util.cpp
@@ -1783,4 +1783,17 @@ void verifyComputeDerivativeGroupModifiers(
}
}
+int getIRVectorElementSize(IRType* type)
+{
+ if (type->getOp() != kIROp_VectorType)
+ return 1;
+ return (int)(as(as(type)->getElementCount())->value.intVal);
+}
+IRType* getIRVectorBaseType(IRType* type)
+{
+ if (type->getOp() != kIROp_VectorType)
+ return type;
+ return as(type)->getElementType();
+}
+
}
diff --git a/source/slang/slang-ir-util.h b/source/slang/slang-ir-util.h
index d78ceaaf8..855046c04 100644
--- a/source/slang/slang-ir-util.h
+++ b/source/slang/slang-ir-util.h
@@ -345,6 +345,9 @@ inline bool isSPIRV(CodeGenTarget codeGenTarget)
|| codeGenTarget == CodeGenTarget::SPIRVAssembly;
}
+int getIRVectorElementSize(IRType* type);
+IRType* getIRVectorBaseType(IRType* type);
+
}
#endif
diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp
index c0541f4c4..a4ea100dc 100644
--- a/source/slang/slang-ir.cpp
+++ b/source/slang/slang-ir.cpp
@@ -4841,17 +4841,18 @@ namespace Slang
return inst;
}
- IRInst* IRBuilder::emitImageLoad(IRType* type, IRInst* image, IRInst* coord)
+ /// @param params An ordered list of imageLoad parameters { image, coord, [optional] seperateArrayCoord, [optional] seperateSampleCoord }
+ IRInst* IRBuilder::emitImageLoad(IRType* type, ShortList params)
{
- auto inst = createInst(this, kIROp_ImageLoad, type, image, coord);
+ auto inst = createInst(this, kIROp_ImageLoad, type, params.getCount(), params.getArrayView().getBuffer());
addInst(inst);
return inst;
}
- IRInst* IRBuilder::emitImageStore(IRType* type, IRInst* image, IRInst* coord, IRInst* value)
+ /// @param params An ordered list of imageStore parameters { image, coord, value, [optional] seperateArrayCoord, [optional] seperateSampleCoord }
+ IRInst* IRBuilder::emitImageStore(IRType* type, ShortList params)
{
- IRInst* args[] = {image, coord, value};
- auto inst = createInst(this, kIROp_ImageStore, type, 3, args);
+ auto inst = createInst(this, kIROp_ImageStore, type, params.getCount(), params.getArrayView().getBuffer());
addInst(inst);
return inst;
}
diff --git a/tests/compute/texture-subscript-multisample.slang b/tests/compute/texture-subscript-multisample.slang
new file mode 100644
index 000000000..4640a2630
--- /dev/null
+++ b/tests/compute/texture-subscript-multisample.slang
@@ -0,0 +1,41 @@
+//TEST:SIMPLE(filecheck=METAL_ERROR): -target metal -entry computeMain -stage compute
+//TEST:SIMPLE(filecheck=METALLIB): -target metallib -entry computeMain -stage compute
+// Metal currently lacks multisampled texture write support.
+// Due to this, Metal compute test is disabled
+//DISABLE_TEST(compute):COMPARE_COMPUTE(filecheck-buffer=BUF): -slang -output-using-type -shaderobj -mtl
+//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=BUF): -slang -output-using-type -shaderobj -vk
+//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=BUF): -slang -output-using-type -shaderobj -vk -glsl
+
+//METAL_ERROR: error 41402
+//METALLIB: error 41402
+
+//TEST_INPUT: RWTexture2D(format=R8G8B8A8_SINT, size=4, content = zero, sampleCount=two, mipMaps = 1):name outputTexture2DMS
+RWTexture2DMS outputTexture2DMS;
+
+//TEST_INPUT: RWTexture2D(format=R8G8B8A8_SINT, size=4, content = zero, arrayLength=2, sampleCount=two, mipMaps = 1):name outputTexture2DArrayMS
+RWTexture2DArrayMS outputTexture2DArrayMS;
+
+//TEST_INPUT:ubuffer(data=[0], stride=4):out,name=outputBuffer
+RWStructuredBuffer outputBuffer;
+
+
+[numthreads(1,1,1)]
+void computeMain()
+{
+
+ outputTexture2DMS[0, 0].xz = int2(1,2);
+ outputTexture2DMS[int2(0, 0), 1].xz = int2(3,4);
+
+ outputTexture2DArrayMS[0, 0].xz = int2(1,2);
+ outputTexture2DArrayMS[int3(0, 0, 1), 1].xz = int2(3,4);
+
+ outputBuffer[0] = uint(true
+ && all(outputTexture2DMS[0, 0] == int4(1, 0, 2, 0)) == true
+ && all(outputTexture2DMS[int2(0, 0), 1] == int4(3, 0, 4, 0)) == true
+
+ && all(outputTexture2DArrayMS[0, 0] == int4(1, 0, 2, 0)) == true
+ && all(outputTexture2DArrayMS[int3(0, 0, 1), 1] == int4(3, 0, 4, 0)) == true
+ );
+}
+
+//BUF: 1
\ No newline at end of file
diff --git a/tests/compute/texture-subscript.slang b/tests/compute/texture-subscript.slang
new file mode 100644
index 000000000..9251f49f1
--- /dev/null
+++ b/tests/compute/texture-subscript.slang
@@ -0,0 +1,58 @@
+//TEST:SIMPLE(filecheck=METAL): -target metal -entry computeMain -stage compute
+//TEST:SIMPLE(filecheck=METALLIB): -target metallib -entry computeMain -stage compute
+// Metal lacks RWTexture GFX backend support.
+// Due to this, Metal compute test is disabled
+//DISABLE_TEST(compute):COMPARE_COMPUTE(filecheck-buffer=BUF): -slang -output-using-type -shaderobj -mtl
+//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=BUF): -slang -output-using-type -shaderobj -vk
+//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=BUF): -slang -output-using-type -shaderobj -vk -glsl
+
+//METAL-NOT: error 41402
+//METALLIB: @computeMain
+
+//TEST_INPUT: RWTexture1D(format=R8G8B8A8_SINT, size=8, content = zero, mipMaps = 1):name outputTexture1D
+RWTexture1D outputTexture1D;
+
+//TEST_INPUT: RWTexture2D(format=R8G8B8A8_SINT, size=8, content = zero, mipMaps = 1):name outputTexture2D
+RWTexture2D outputTexture2D;
+
+//TEST_INPUT: RWTexture3D(format=R8G8B8A8_SINT, size=8, content = zero, mipMaps = 1):name outputTexture3D
+RWTexture3D outputTexture3D;
+
+//TEST_INPUT: RWTexture2D(format=R8G8B8A8_SINT, size=4, content = zero, arrayLength=2, mipMaps = 1):name outputTexture2DArray
+RWTexture2DArray outputTexture2DArray;
+
+//TEST_INPUT:ubuffer(data=[0], stride=4):out,name=outputBuffer
+RWStructuredBuffer outputBuffer;
+
+
+[numthreads(1,1,1)]
+void computeMain()
+{
+ outputTexture1D[0].xz = int2(1,2).xx;
+ outputTexture1D[1].x = int2(3,4).y;
+
+ outputTexture2D[0].xz = int2(1,2).xx;
+ outputTexture2D[int2(0, 1)].x = int2(3,4).y;
+
+ outputTexture3D[0].xz = int2(1,2).xx;
+ outputTexture3D[int3(0, 0, 1)].x = int2(3,4).y;
+
+ outputTexture2DArray[0].xz = int2(1,2);
+ outputTexture2DArray[int3(0, 0, 1)].xz = int2(3,4);
+
+ outputBuffer[0] = uint(true
+ && all(outputTexture1D[0] == int4(1, 0, 1, 0)) == true
+ && all(outputTexture1D[1] == int4(4, 0, 0, 0)) == true
+
+ && all(outputTexture2D[0] == int4(1, 0, 1, 0)) == true
+ && all(outputTexture2D[int2(0, 1)] == int4(4, 0, 0, 0)) == true
+
+ && all(outputTexture3D[0] == int4(1, 0, 1, 0)) == true
+ && all(outputTexture3D[int3(0, 0, 1)] == int4(4, 0, 0, 0)) == true
+
+ && all(outputTexture2DArray[0] == int4(1, 0, 2, 0)) == true
+ && all(outputTexture2DArray[int3(0, 0, 1)] == int4(3, 0, 4, 0)) == true
+ );
+}
+
+//BUF: 1
\ No newline at end of file
--
cgit v1.2.3