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