From cbcae3883c0ab9c9633ec6b03c489698f186e045 Mon Sep 17 00:00:00 2001 From: Jonathan Zakharov Date: Thu, 4 Jun 2026 13:19:50 -0700 Subject: [PATCH 1/2] [SPIR-V] Add SPV_EXT_descriptor_heap + SPV_KHR_untyped_pointers codegen Building off of #8281, this commit adds a native lowering via SPV_EXT_descriptor_heap and SPV_KHR_untyped_pointers. ResourceDescriptorHeap and SamplerDescriptorHeap are lowered to untyped variables decorated with ResourceHeapEXT and SamplerHeapEXT. Each heap access emits OpUntypedAccessChainKHR into a runtime array of the appropriate descriptor type. Buffer-like resources (StructuredBuffer, ByteAddressBuffer, ConstantBuffer, TextureBuffer) use OpTypeBufferEXT and OpBufferPointerEXT; image and sampler resources use OpLoad. Interlocked operations on RWTexture use OpUntypedImageTexelPointerEXT. Requires -fspv-target-env=vulkan1.3. Assisted-by: Claude. --- docs/SPIR-V.rst | 87 +++- .../clang/include/clang/SPIRV/SpirvBuilder.h | 2 +- .../clang/include/clang/SPIRV/SpirvContext.h | 4 + .../include/clang/SPIRV/SpirvInstruction.h | 12 + tools/clang/lib/SPIRV/CapabilityVisitor.cpp | 6 +- tools/clang/lib/SPIRV/DeclResultIdMapper.cpp | 6 + tools/clang/lib/SPIRV/DeclResultIdMapper.h | 4 +- tools/clang/lib/SPIRV/EmitVisitor.cpp | 1 + tools/clang/lib/SPIRV/LowerTypeVisitor.cpp | 6 + tools/clang/lib/SPIRV/SpirvBuilder.cpp | 3 +- tools/clang/lib/SPIRV/SpirvContext.cpp | 11 + tools/clang/lib/SPIRV/SpirvEmitter.cpp | 458 ++++++++++++++++-- tools/clang/lib/SPIRV/SpirvEmitter.h | 131 +++++ tools/clang/lib/SPIRV/SpirvInstruction.cpp | 8 +- .../resource-heap-ext-texture.hlsl | 59 --- ...scriptorheap.ext.append-consume.error.hlsl | 19 + .../sm6_6.descriptorheap.ext.buffer.hlsl | 57 +++ ...orheap.ext.constant-buffer-assignment.hlsl | 78 +++ ...iptorheap.ext.constant-texture-buffer.hlsl | 51 ++ ....descriptorheap.ext.counter-ops.error.hlsl | 24 + ..._6.descriptorheap.ext.discarded.error.hlsl | 14 + ..._6.descriptorheap.ext.function-params.hlsl | 97 ++++ .../sm6_6.descriptorheap.ext.gather.hlsl | 42 ++ .../sm6_6.descriptorheap.ext.groupshared.hlsl | 35 ++ .../sm6_6.descriptorheap.ext.load-offset.hlsl | 27 ++ .../sm6_6.descriptorheap.ext.mixed-bound.hlsl | 54 +++ .../sm6_6.descriptorheap.ext.nonuniform.hlsl | 30 ++ ...escriptorheap.ext.rwbyteaddressbuffer.hlsl | 21 + ....descriptorheap.ext.rwtexture-atomics.hlsl | 53 ++ ...6_6.descriptorheap.ext.rwtexture-dims.hlsl | 53 ++ ...6.descriptorheap.ext.sample-grad-bias.hlsl | 40 ++ ...descriptorheap.ext.sampler-comparison.hlsl | 30 ++ ...m6_6.descriptorheap.ext.static-global.hlsl | 64 +++ ...ptorheap.ext.structured-buffer-atomic.hlsl | 28 ++ ...sm6_6.descriptorheap.ext.texture-dims.hlsl | 54 +++ .../sm6_6.descriptorheap.ext.texture-ms.hlsl | 33 ++ ...orheap.ext.texture-sampler-assignment.hlsl | 77 +++ .../sm6_6.descriptorheap.ext.texture.hlsl | 34 ++ .../sm6_6.descriptorheap.ext.texturecube.hlsl | 46 ++ ...m6_6.descriptorheap.ext.typed-formats.hlsl | 59 +++ .../unittests/SPIRV/SpirvContextTest.cpp | 3 + 41 files changed, 1818 insertions(+), 103 deletions(-) delete mode 100644 tools/clang/test/CodeGenSPIRV/resource-heap-ext-texture.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.append-consume.error.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.buffer.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.constant-buffer-assignment.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.constant-texture-buffer.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.counter-ops.error.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.discarded.error.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.function-params.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.gather.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.groupshared.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.load-offset.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.mixed-bound.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.nonuniform.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.rwbyteaddressbuffer.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.rwtexture-atomics.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.rwtexture-dims.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.sample-grad-bias.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.sampler-comparison.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.static-global.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.structured-buffer-atomic.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.texture-dims.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.texture-ms.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.texture-sampler-assignment.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.texture.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.texturecube.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.typed-formats.hlsl diff --git a/docs/SPIR-V.rst b/docs/SPIR-V.rst index 423262e15d..70f1f934c3 100644 --- a/docs/SPIR-V.rst +++ b/docs/SPIR-V.rst @@ -336,6 +336,8 @@ Supported extensions * SPV_KHR_float_controls * SPV_NV_shader_subgroup_partitioned * SPV_KHR_quad_control +* SPV_KHR_untyped_pointers +* SPV_EXT_descriptor_heap Vulkan specific attributes -------------------------- @@ -1993,10 +1995,14 @@ responsibility to provide proper numbers and avoid binding overlaps. ResourceDescriptorHeaps & SamplerDescriptorHeaps ------------------------------------------------ -The SPIR-V backend supported SM6.6 resource heaps, using 2 extensions: +By default, the SPIR-V backend supports SM6.6 resource heaps by emulating the +heaps with descriptor-indexing runtime arrays, using 2 extensions: + - `SPV_EXT_descriptor_indexing` - `VK_EXT_mutable_descriptor_type` +This is also the behavior selected by ``-fspv-use-emulated-heap``. + Each type loaded from a heap is considered to be an unbounded RuntimeArray bound to the descriptor set 0. @@ -2074,6 +2080,85 @@ Bindings & sets associated with each heap can be explicitly set using: - `-fvk-bind-counter-heap `: Specify Vulkan binding number and set number for the counter heap. +Native descriptor heap extension lowering +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When ``-fspv-use-descriptor-heap`` is specified, DXC lowers +``ResourceDescriptorHeap`` and ``SamplerDescriptorHeap`` through +``SPV_EXT_descriptor_heap`` instead of the default emulated heap path. This +also requires ``SPV_KHR_untyped_pointers`` and ``-fspv-target-env=vulkan1.3`` +(targeting a lower environment is an error), and a SPIRV-Headers / SPIRV-Tools +build that defines these extensions. The emitted module declares the heap +objects as untyped variables in ``UniformConstant`` storage class: + +.. code:: spirv + + %uptr_uc = OpTypeUntypedPointerKHR UniformConstant + %resource_heap = OpUntypedVariableKHR %uptr_uc UniformConstant + %sampler_heap = OpUntypedVariableKHR %uptr_uc UniformConstant + OpDecorate %resource_heap BuiltIn ResourceHeapEXT + OpDecorate %sampler_heap BuiltIn SamplerHeapEXT + +The concrete descriptor type is selected at each heap access. For image, +sampler, and texel buffer resources, DXC forms a runtime array of that +descriptor type and decorates the array with a byte ``ArrayStride``, then uses +``OpUntypedAccessChainKHR`` followed by ``OpLoad``: + +.. code:: spirv + + %image_type = OpTypeImage %float 2D 2 0 0 1 Unknown + %image_array = OpTypeRuntimeArray %image_type + OpDecorate %image_array ArrayStride 64 + %descriptor = OpUntypedAccessChainKHR %uptr_uc %image_array %resource_heap %index + %image = OpLoad %image_type %descriptor + +For buffer-like resources, DXC uses ``OpTypeBufferEXT`` as the descriptor type +and ``OpBufferPointerEXT`` to recover the pointer to the buffer data. The +descriptor storage class matches the recovered buffer pointer storage class; for +example, ``ConstantBuffer`` uses ``Uniform`` and ``TextureBuffer`` uses +``StorageBuffer``: + +.. code:: spirv + + %buffer_type = OpTypeBufferEXT Uniform + %buffer_array = OpTypeRuntimeArray %buffer_type + OpDecorate %buffer_array ArrayStride 64 + %descriptor = OpUntypedAccessChainKHR %uptr_uc %buffer_array %resource_heap %index + %buffer_ptr = OpBufferPointerEXT %_ptr_Uniform_type_BufferData %descriptor + +For ``RWTexture`` resources loaded from ``ResourceDescriptorHeap``, interlocked +operations that need a texel pointer use ``OpUntypedImageTexelPointerEXT``. +The image descriptor pointer produced by ``OpUntypedAccessChainKHR`` is passed +directly to the texel-pointer instruction instead of first storing the image +handle into a function-scope image variable: + +.. code:: spirv + + %image_type = OpTypeImage %uint 2D 2 0 0 2 R32ui + %image_array = OpTypeRuntimeArray %image_type + %descriptor = OpUntypedAccessChainKHR %uptr_uc %image_array %resource_heap %index + %uptr_image = OpTypeUntypedPointerKHR Image + %texel_ptr = OpUntypedImageTexelPointerEXT %uptr_image %image_type %descriptor %coord %sample + %old = OpAtomicIAdd %uint %texel_ptr %scope %semantics %value + +This path supports texture, RWTexture, sampler, Buffer/RWBuffer, +StructuredBuffer/RWStructuredBuffer without associated counter operations, +ByteAddressBuffer/RWByteAddressBuffer, ConstantBuffer, and TextureBuffer heap +loads, including direct field and array-element accesses for +``ConstantBuffer`` and ``TextureBuffer``. ``NonUniformResourceIndex`` is +accepted but the ``NonUniform`` decoration is not emitted on +``OpUntypedAccessChainKHR`` or the loaded value; ``SPV_EXT_descriptor_heap`` +deprecates the ``NonUniform`` decoration for heap accesses. + +Append/consume structured buffers and UAV counter heap lowering are not +supported by the native descriptor heap path yet. Those forms should continue +to use the default emulated heap lowering, or DXC will emit a diagnostic for +unsupported append/consume structured-buffer heap loads. Heap-loaded +``RWStructuredBuffer`` resources are supported for ordinary data access, but +associated counter operations such as ``IncrementCounter`` and +``DecrementCounter`` emit a diagnostic because the native descriptor heap path +does not recover an associated counter descriptor. + HLSL Expressions ================ diff --git a/tools/clang/include/clang/SPIRV/SpirvBuilder.h b/tools/clang/include/clang/SPIRV/SpirvBuilder.h index dba103207c..ae8add20a9 100644 --- a/tools/clang/include/clang/SPIRV/SpirvBuilder.h +++ b/tools/clang/include/clang/SPIRV/SpirvBuilder.h @@ -277,7 +277,7 @@ class SpirvBuilder { /// \brief Creates an OpUntypedImageTexelPointerEXT SPIR-V instruction with /// the given parameters. SpirvUntypedImageTexelPointerEXT *createUntypedImageTexelPointerEXT( - QualType resultType, SpirvInstruction *image, + QualType resultType, const SpirvType *imageType, SpirvInstruction *image, SpirvInstruction *coordinate, SpirvInstruction *sample, SourceLocation); /// \brief Creates an OpConverPtrToU SPIR-V instruction with the given diff --git a/tools/clang/include/clang/SPIRV/SpirvContext.h b/tools/clang/include/clang/SPIRV/SpirvContext.h index 97a62d4af8..8a49f961ca 100644 --- a/tools/clang/include/clang/SPIRV/SpirvContext.h +++ b/tools/clang/include/clang/SPIRV/SpirvContext.h @@ -298,6 +298,7 @@ class SpirvContext { spv::StorageClass); const UntypedPointerKHRType *getUntypedPointerKHRType(spv::StorageClass sc); + const BufferEXTType *getBufferEXTType(spv::StorageClass sc); FunctionType *getFunctionType(const SpirvType *ret, llvm::ArrayRef param); @@ -536,6 +537,9 @@ class SpirvContext { llvm::DenseMap untypedPointerKHRTypes; + llvm::DenseMap + bufferEXTTypes; llvm::MapVector forwardPointerTypes; llvm::MapVector forwardReferences; llvm::DenseSet functionTypes; diff --git a/tools/clang/include/clang/SPIRV/SpirvInstruction.h b/tools/clang/include/clang/SPIRV/SpirvInstruction.h index 39cff251c6..d83d8f7294 100644 --- a/tools/clang/include/clang/SPIRV/SpirvInstruction.h +++ b/tools/clang/include/clang/SPIRV/SpirvInstruction.h @@ -2065,6 +2065,7 @@ class SpirvImageTexelPointer : public SpirvInstruction { class SpirvUntypedImageTexelPointerEXT : public SpirvInstruction { public: SpirvUntypedImageTexelPointerEXT(QualType resultType, SourceLocation loc, + const SpirvType *imageType, SpirvInstruction *image, SpirvInstruction *coordinate, SpirvInstruction *sample); @@ -2078,11 +2079,22 @@ class SpirvUntypedImageTexelPointerEXT : public SpirvInstruction { bool invokeVisitor(Visitor *v) override; + const SpirvType *getImageType() const { return imageType; } SpirvInstruction *getImage() const { return image; } SpirvInstruction *getCoordinate() const { return coordinate; } SpirvInstruction *getSample() const { return sample; } + void replaceOperand( + llvm::function_ref remapOp, + bool inEntryFunctionWrapper) override { + // imageType is a compile-time SpirvType, not an SSA operand. + image = remapOp(image); + coordinate = remapOp(coordinate); + sample = remapOp(sample); + } + private: + const SpirvType *imageType; SpirvInstruction *image; SpirvInstruction *coordinate; SpirvInstruction *sample; diff --git a/tools/clang/lib/SPIRV/CapabilityVisitor.cpp b/tools/clang/lib/SPIRV/CapabilityVisitor.cpp index 9f012d24e8..c48f898a28 100644 --- a/tools/clang/lib/SPIRV/CapabilityVisitor.cpp +++ b/tools/clang/lib/SPIRV/CapabilityVisitor.cpp @@ -955,8 +955,10 @@ bool CapabilityVisitor::visit(SpirvModule *, Visitor::Phase phase) { {spv::Capability::QuadControlKHR}); if (spvOptions.useDescriptorHeap) { - addExtension(Extension::EXT_descriptor_heap, "DescriptorHeap", {}); - addExtension(Extension::KHR_untyped_pointers, "DescriptorHeap", {}); + const llvm::StringRef feature = "DescriptorHeap"; + featureManager.requestTargetEnv(SPV_ENV_VULKAN_1_3, feature, {}); + addExtension(Extension::EXT_descriptor_heap, feature, {}); + addExtension(Extension::KHR_untyped_pointers, feature, {}); addCapability(spv::Capability::DescriptorHeapEXT); } diff --git a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp index 1a1078bf0b..62e5ff1e3e 100644 --- a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp +++ b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp @@ -1136,6 +1136,12 @@ DeclResultIdMapper::createFnVar(const VarDecl *var, return varInstr; } +void DeclResultIdMapper::registerFnVarAlias(const VarDecl *var, + SpirvInstruction *varInstr) { + if (varInstr) + registerVariableForDecl(var, createDeclSpirvInfo(varInstr)); +} + SpirvDebugGlobalVariable *DeclResultIdMapper::createDebugGlobalVariable( SpirvVariable *var, const QualType &type, const SourceLocation &loc, const StringRef &name) { diff --git a/tools/clang/lib/SPIRV/DeclResultIdMapper.h b/tools/clang/lib/SPIRV/DeclResultIdMapper.h index 6e8177edf2..d6ae75e8fa 100644 --- a/tools/clang/lib/SPIRV/DeclResultIdMapper.h +++ b/tools/clang/lib/SPIRV/DeclResultIdMapper.h @@ -10,7 +10,6 @@ #ifndef LLVM_CLANG_LIB_SPIRV_DECLRESULTIDMAPPER_H #define LLVM_CLANG_LIB_SPIRV_DECLRESULTIDMAPPER_H -#include #include #include "dxc/Support/SPIRVOptions.h" @@ -286,6 +285,9 @@ class DeclResultIdMapper { SpirvVariable *createFnVar(const VarDecl *var, llvm::Optional init); + /// \brief Registers a function-scope alias to an existing instruction. + void registerFnVarAlias(const VarDecl *var, SpirvInstruction *varInstr); + /// \brief Creates a file-scope variable and returns its instruction. SpirvVariable *createFileVar(const VarDecl *var, llvm::Optional init); diff --git a/tools/clang/lib/SPIRV/EmitVisitor.cpp b/tools/clang/lib/SPIRV/EmitVisitor.cpp index fcdcf5bcd0..75d081ee62 100644 --- a/tools/clang/lib/SPIRV/EmitVisitor.cpp +++ b/tools/clang/lib/SPIRV/EmitVisitor.cpp @@ -820,6 +820,7 @@ bool EmitVisitor::visit(SpirvUntypedImageTexelPointerEXT *inst) { initInstruction(inst); curInst.push_back(inst->getResultTypeId()); curInst.push_back(getOrAssignResultId(inst)); + curInst.push_back(typeHandler.emitType(inst->getImageType())); curInst.push_back(getOrAssignResultId(inst->getImage())); curInst.push_back( getOrAssignResultId(inst->getCoordinate())); diff --git a/tools/clang/lib/SPIRV/LowerTypeVisitor.cpp b/tools/clang/lib/SPIRV/LowerTypeVisitor.cpp index 4c06cd4113..2d9abfb1c9 100644 --- a/tools/clang/lib/SPIRV/LowerTypeVisitor.cpp +++ b/tools/clang/lib/SPIRV/LowerTypeVisitor.cpp @@ -224,6 +224,12 @@ bool LowerTypeVisitor::visitInstruction(SpirvInstruction *instr) { instr->setResultType(pointerType); break; } + case spv::Op::OpUntypedImageTexelPointerEXT: { + instr->setResultType( + spvContext.getUntypedPointerKHRType(spv::StorageClass::Image)); + instr->setStorageClass(spv::StorageClass::Image); + break; + } // Sparse image operations return a sparse residency struct. case spv::Op::OpImageSparseSampleImplicitLod: case spv::Op::OpImageSparseSampleExplicitLod: diff --git a/tools/clang/lib/SPIRV/SpirvBuilder.cpp b/tools/clang/lib/SPIRV/SpirvBuilder.cpp index f61d61ed71..9a48ed5f85 100644 --- a/tools/clang/lib/SPIRV/SpirvBuilder.cpp +++ b/tools/clang/lib/SPIRV/SpirvBuilder.cpp @@ -533,13 +533,14 @@ SpirvImageTexelPointer *SpirvBuilder::createImageTexelPointer( SpirvUntypedImageTexelPointerEXT * SpirvBuilder::createUntypedImageTexelPointerEXT(QualType resultType, + const SpirvType *imageType, SpirvInstruction *image, SpirvInstruction *coordinate, SpirvInstruction *sample, SourceLocation loc) { assert(insertPoint && "null insert point"); auto *instruction = new (context) SpirvUntypedImageTexelPointerEXT( - resultType, loc, image, coordinate, sample); + resultType, loc, imageType, image, coordinate, sample); insertPoint->addInstruction(instruction); return instruction; } diff --git a/tools/clang/lib/SPIRV/SpirvContext.cpp b/tools/clang/lib/SPIRV/SpirvContext.cpp index c28da78f9d..a3397d74b4 100644 --- a/tools/clang/lib/SPIRV/SpirvContext.cpp +++ b/tools/clang/lib/SPIRV/SpirvContext.cpp @@ -65,6 +65,9 @@ SpirvContext::~SpirvContext() { for (auto *npaType : nodePayloadArrayTypes) npaType->~NodePayloadArrayType(); + for (auto &pair : bufferEXTTypes) + pair.second->~BufferEXTType(); + for (auto *fnType : functionTypes) fnType->~FunctionType(); @@ -402,6 +405,14 @@ const StructType *SpirvContext::getByteAddressBufferType(bool isWritable) { !isWritable, StructInterfaceType::StorageBuffer); } +const BufferEXTType *SpirvContext::getBufferEXTType(spv::StorageClass sc) { + auto found = bufferEXTTypes.find(sc); + if (found != bufferEXTTypes.end()) + return found->second; + + return bufferEXTTypes[sc] = new (this) BufferEXTType(sc); +} + const StructType *SpirvContext::getACSBufferCounterType() { // Create int32. const auto *int32Type = getSIntType(32); diff --git a/tools/clang/lib/SPIRV/SpirvEmitter.cpp b/tools/clang/lib/SPIRV/SpirvEmitter.cpp index 288b926f24..d967520fe2 100644 --- a/tools/clang/lib/SPIRV/SpirvEmitter.cpp +++ b/tools/clang/lib/SPIRV/SpirvEmitter.cpp @@ -1227,6 +1227,12 @@ SpirvInstruction *SpirvEmitter::doExpr(const Expr *expr, auto *decl = declRefExpr->getDecl(); if (isImplicitVarDeclInVkNamespace(declRefExpr->getDecl())) { result = doExpr(cast(decl)->getInit()); + } else if (const auto *varDecl = dyn_cast(decl)) { + if (auto *alias = + emitDescriptorHeapBufferPointer(varDecl, expr->getLocStart())) + result = alias; + else + result = declIdMapper.getDeclEvalInfo(decl, expr->getLocStart(), range); } else { result = declIdMapper.getDeclEvalInfo(decl, expr->getLocStart(), range); } @@ -2020,6 +2026,22 @@ void SpirvEmitter::doEnumDecl(const EnumDecl *decl) { declIdMapper.createEnumConstant(*it); } +bool SpirvEmitter::tryToCreateDescriptorHeapAlias(const VarDecl *decl, + const Expr *init) { + if (!spirvOptions.useDescriptorHeap || !init || + !isDescriptorHeap(init->IgnoreParenCasts())) + return false; + + if (isConstantTextureBuffer(decl->getType()) || + isAKindOfStructuredOrByteBuffer(decl->getType())) { + (void)doExpr(init->IgnoreParenCasts()); + tryToAssignDescriptorHeapBufferAlias(decl, init); + return true; + } + + return false; +} + void SpirvEmitter::doVarDecl(const VarDecl *decl) { if (!validateVKAttributes(decl)) return; @@ -2209,8 +2231,11 @@ void SpirvEmitter::doVarDecl(const VarDecl *decl) { declIdMapper.tryToCreateConstantVar(decl)) return; var = declIdMapper.createFileVar(decl, llvm::None); - } else + } else { + if (tryToCreateDescriptorHeapAlias(decl, decl->getInit())) + return; var = declIdMapper.createFnVar(decl, llvm::None); + } // Emit OpStore to initialize the variable // TODO: revert back to use OpVariable initializer @@ -2233,6 +2258,7 @@ void SpirvEmitter::doVarDecl(const VarDecl *decl) { spvBuilder.createStore(var, constInit, loc, range); } else { storeValue(var, loadIfGLValue(init), decl->getType(), loc, range); + tryToAssignDescriptorHeapImageAlias(decl, init); } // Update counter variable associated with local variables @@ -3123,6 +3149,33 @@ SpirvEmitter::doArraySubscriptExpr(const ArraySubscriptExpr *expr, return loadVal; } +llvm::Optional +SpirvEmitter::tryToAssignToDescriptorHeapBuffer( + const BinaryOperator *assignExpr) { + if (!spirvOptions.useDescriptorHeap) + return llvm::None; + + const QualType lhsType = assignExpr->getLHS()->getType(); + if (!isConstantTextureBuffer(lhsType) && + !isAKindOfStructuredOrByteBuffer(lhsType)) + return llvm::None; + + const Expr *rhsValue = assignExpr->getRHS()->IgnoreParenCasts(); + if (!isDescriptorHeap(rhsValue)) + return llvm::None; + + (void)doExpr(rhsValue); + if (!tryToAssignDescriptorHeapBufferAlias(assignExpr->getLHS(), + assignExpr->getRHS())) + return llvm::None; + + const auto *decl = + dyn_cast_or_null(getReferencedDef(assignExpr->getLHS())); + if (!decl) + return static_cast(nullptr); + return emitDescriptorHeapBufferPointer(decl, assignExpr->getExprLoc()); +} + SpirvInstruction *SpirvEmitter::doBinaryOperator(const BinaryOperator *expr) { const auto opcode = expr->getOpcode(); @@ -3132,7 +3185,14 @@ SpirvInstruction *SpirvEmitter::doBinaryOperator(const BinaryOperator *expr) { // Update counter variable associated with lhs of assignments tryToAssignCounterVar(expr->getLHS(), expr->getRHS()); - return processAssignment(expr->getLHS(), loadIfGLValue(expr->getRHS()), + if (llvm::Optional aliasResult = + tryToAssignToDescriptorHeapBuffer(expr)) + return aliasResult.getValue(); + + auto *rhs = loadIfGLValue(expr->getRHS()); + tryToAssignDescriptorHeapImageAlias(expr->getLHS(), expr->getRHS()); + + return processAssignment(expr->getLHS(), rhs, /*isCompoundAssignment=*/false, nullptr, expr->getSourceRange()); } @@ -5036,9 +5096,237 @@ SpirvEmitter::processStructuredBufferLoad(const CXXMemberCallExpr *expr) { auto *zero = spvBuilder.getConstantInt(astContext.IntTy, llvm::APInt(32, 0)); auto *index = doExpr(expr->getArg(0)); - return derefOrCreatePointerToValue(buffer->getType(), info, structType, - {zero, index}, buffer->getExprLoc(), - range); + auto *result = + derefOrCreatePointerToValue(buffer->getType(), info, structType, + {zero, index}, buffer->getExprLoc(), range); + + // derefOrCreatePointerToValue returns an lvalue (AccessChain) when the base + // is an lvalue. This covers descriptor-heap buffers reached either directly + // (ResourceDescriptorHeap[i].Load()) or through a local alias var. + // StructuredBuffer::Load semantically returns a value, and the AST emits no + // LValueToRValue cast for the call expression, so emit the load explicitly. + // (Verified required: scoping this to alias vars only regresses the direct + // heap-access tests; non-heap callers are unaffected in the existing suite.) + if (result && !result->isRValue()) { + result = + spvBuilder.createLoad(structType, result, buffer->getExprLoc(), range); + } + + return result; +} + +void SpirvEmitter::markDescriptorHeapCounterUnsupported( + const DeclaratorDecl *decl) { + if (decl) + descriptorHeapUnsupportedCounters.insert(decl); +} + +bool SpirvEmitter::isDescriptorHeapCounterUnsupported(const Expr *expr) const { + if (const auto *decl = getReferencedDef(expr)) + return descriptorHeapUnsupportedCounters.count(decl) != 0; + return false; +} + +SpirvInstruction *SpirvEmitter::emitDescriptorHeapAccessChain( + const SpirvType *arrayType, SpirvInstruction *heap, SpirvVariable *indexVar, + SourceLocation loc) { + const auto *untypedUniformConstantType = + spvContext.getUntypedPointerKHRType(spv::StorageClass::UniformConstant); + auto *index = spvBuilder.createLoad(astContext.UnsignedIntTy, indexVar, loc); + return spvBuilder.createUntypedAccessChainKHR(untypedUniformConstantType, + arrayType, heap, index, loc); +} + +void SpirvEmitter::storeDescriptorHeapIndex(SpirvVariable *indexVar, + SpirvInstruction *index, + QualType indexType, + const Expr *srcExpr) { + if (!astContext.hasSameType(indexType, astContext.UnsignedIntTy)) + index = castToType(index, indexType, astContext.UnsignedIntTy, + srcExpr->getExprLoc(), srcExpr->getSourceRange()); + spvBuilder.createStore(indexVar, index, srcExpr->getExprLoc(), + srcExpr->getSourceRange()); +} + +SpirvVariable * +SpirvEmitter::createDescriptorHeapIndexVar(const VarDecl *dstVar) { + const std::string name = dstVar->getName().str() + ".descriptor.index"; + return spvBuilder.addFnVar(astContext.UnsignedIntTy, dstVar->getLocation(), + name); +} + +bool SpirvEmitter::tryToAssignDescriptorHeapImageAlias( + const DeclaratorDecl *dstDecl, const Expr *srcExpr) { + if (!spirvOptions.useDescriptorHeap || !dstDecl || !srcExpr) + return false; + + const auto *dstVar = dyn_cast(dstDecl); + if (!dstVar || + (!isRWTexture(dstVar->getType()) && !isRWBuffer(dstVar->getType()))) + return false; + + const auto *src = srcExpr->IgnoreParenCasts(); + auto found = descriptorHeapImageAccesses.find(src); + if (found == descriptorHeapImageAccesses.end()) + return false; + + auto &alias = descriptorHeapImageAliasVars[dstVar]; + if (!alias.indexVar) + alias.indexVar = createDescriptorHeapIndexVar(dstVar); + alias.imageType = found->second.imageType; + alias.arrayType = found->second.arrayType; + alias.heap = found->second.heap; + storeDescriptorHeapIndex(alias.indexVar, found->second.index, + found->second.indexType, srcExpr); + return true; +} + +bool SpirvEmitter::tryToAssignDescriptorHeapImageAlias(const Expr *dstExpr, + const Expr *srcExpr) { + return tryToAssignDescriptorHeapImageAlias(getReferencedDef(dstExpr), + srcExpr); +} + +bool SpirvEmitter::tryToAssignDescriptorHeapBufferAlias( + const DeclaratorDecl *dstDecl, const Expr *srcExpr) { + if (!spirvOptions.useDescriptorHeap || !dstDecl || !srcExpr) + return false; + + const auto *dstVar = dyn_cast(dstDecl); + if (!dstVar || !(isConstantTextureBuffer(dstVar->getType()) || + isAKindOfStructuredOrByteBuffer(dstVar->getType()))) + return false; + + const auto *src = srcExpr->IgnoreParenCasts(); + auto found = descriptorHeapBufferAccesses.find(src); + if (found == descriptorHeapBufferAccesses.end()) + return false; + + if (isRWStructuredBuffer(dstVar->getType())) + markDescriptorHeapCounterUnsupported(dstVar); + + auto &alias = descriptorHeapBufferAliasVars[dstVar]; + if (!alias.indexVar) + alias.indexVar = createDescriptorHeapIndexVar(dstVar); + alias.bufferPointerType = found->second.bufferPointerType; + alias.arrayType = found->second.arrayType; + alias.heap = found->second.heap; + alias.layoutRule = found->second.layoutRule; + storeDescriptorHeapIndex(alias.indexVar, found->second.index, + found->second.indexType, srcExpr); + return true; +} + +bool SpirvEmitter::tryToAssignDescriptorHeapBufferAlias(const Expr *dstExpr, + const Expr *srcExpr) { + return tryToAssignDescriptorHeapBufferAlias(getReferencedDef(dstExpr), + srcExpr); +} + +SpirvInstruction * +SpirvEmitter::emitDescriptorHeapBufferPointer(const VarDecl *decl, + SourceLocation loc) { + auto found = descriptorHeapBufferAliasVars.find(decl); + if (found == descriptorHeapBufferAliasVars.end()) + return nullptr; + + auto *descriptorPtr = emitDescriptorHeapAccessChain( + found->second.arrayType, found->second.heap, found->second.indexVar, loc); + auto *bufferDataPtr = spvBuilder.createUnaryOp( + spv::Op::OpBufferPointerEXT, found->second.bufferPointerType, + descriptorPtr, loc); + bufferDataPtr->setStorageClass( + found->second.bufferPointerType->getStorageClass()); + bufferDataPtr->setLayoutRule(found->second.layoutRule); + bufferDataPtr->setRValue(false); + return bufferDataPtr; +} + +SpirvInstruction *SpirvEmitter::emitDescriptorHeapImageTexelPointer( + const VarDecl *decl, SpirvInstruction *coordinate, SpirvInstruction *sample, + QualType resultType, SourceLocation loc) { + auto found = descriptorHeapImageAliasVars.find(decl); + if (found == descriptorHeapImageAliasVars.end()) + return nullptr; + + auto *descriptorPtr = emitDescriptorHeapAccessChain( + found->second.arrayType, found->second.heap, found->second.indexVar, loc); + auto *ptr = spvBuilder.createUntypedImageTexelPointerEXT( + resultType, found->second.imageType, descriptorPtr, coordinate, sample, + loc); + ptr->setStorageClass(spv::StorageClass::Image); + return ptr; +} + +// Descriptor-heap buffers: ConstantBuffer is a UBO (Uniform); every other +// buffer resource (Structured/RW/ByteAddress, TextureBuffer) is an SSBO +// (StorageBuffer). Here because the opaque OpTypeBufferEXT descriptor +// carries no pointee interface type, so RemoveBufferBlockVisitor +// cannot infer/correct its storage class post-lowering. +static spv::StorageClass +getDescriptorHeapBufferStorageClass(QualType resourceType) { + return isConstantBuffer(resourceType) ? spv::StorageClass::Uniform + : spv::StorageClass::StorageBuffer; +} + +SpirvInstruction *SpirvEmitter::emitDescriptorHeapBufferAccess( + QualType resourceType, SpirvInstruction *heapVar, SpirvInstruction *index, + const Expr *expr, const Expr *baseExpr, const Expr *indexExpr) { + const auto *untypedUniformConstantType = + spvContext.getUntypedPointerKHRType(spv::StorageClass::UniformConstant); + LowerTypeVisitor lowerTypeVisitor(astContext, spvContext, spirvOptions, + spvBuilder); + const SpirvType *bufferDataType = lowerTypeVisitor.lowerType( + resourceType, SpirvLayoutRule::Void, llvm::None, baseExpr->getExprLoc()); + + const SpirvPointerType *bufferDataPointerType = nullptr; + SpirvLayoutRule layoutRule = spirvOptions.sBufferLayoutRule; + if (isConstantTextureBuffer(resourceType)) { + layoutRule = isConstantBuffer(resourceType) + ? spirvOptions.cBufferLayoutRule + : spirvOptions.tBufferLayoutRule; + bufferDataPointerType = spvContext.getPointerType( + bufferDataType, getDescriptorHeapBufferStorageClass(resourceType)); + } else { + bufferDataPointerType = dyn_cast(bufferDataType); + } + + if (!bufferDataPointerType) { + emitError("descriptor heap buffer type lowering failed", + expr->getExprLoc()); + return nullptr; + } + + // ConstantBuffer -> Uniform (UBO); all others -> StorageBuffer (SSBO) + // TODO: Remove this manual override once LowerTypeVisitor returns the + // correct StorageClass for descriptor-heap alias pointer types + // (currently it returns Uniform for all of them). + const spv::StorageClass bufferExtSC = isConstantBuffer(resourceType) + ? spv::StorageClass::Uniform + : spv::StorageClass::StorageBuffer; + const auto *bufferDescriptorType = spvContext.getBufferEXTType(bufferExtSC); + // Buffer descriptors are always on the resource heap. + const auto *arrayType = getDescriptorHeapRuntimeArrayType( + bufferDescriptorType, /*onSamplerHeap=*/false); + auto *untypedAccessChainPtr = spvBuilder.createUntypedAccessChainKHR( + untypedUniformConstantType, arrayType, heapVar, index, + baseExpr->getExprLoc()); + auto *bufferDataPtr = spvBuilder.createUnaryOp( + spv::Op::OpBufferPointerEXT, bufferDataPointerType, untypedAccessChainPtr, + baseExpr->getExprLoc()); + bufferDataPtr->setStorageClass(bufferDataPointerType->getStorageClass()); + bufferDataPtr->setLayoutRule(layoutRule); + bufferDataPtr->setRValue(false); + if (isRasterizerOrderedView(resourceType)) { + bufferDataPtr->setRasterizerOrdered(true); + spvBuilder.addExecutionMode(entryFunction, + declIdMapper.getInterlockExecutionMode(), {}, + baseExpr->getExprLoc()); + } + descriptorHeapBufferAccesses[expr] = { + bufferDataPointerType, arrayType, heapVar, index, + indexExpr->getType(), layoutRule}; + return bufferDataPtr; } SpirvInstruction * @@ -5062,6 +5350,22 @@ SpirvEmitter::incDecRWACSBufferCounter(const CXXMemberCallExpr *expr, (void)doExpr(object); } + if (isDescriptorHeapCounterUnsupported(object)) { + emitError("counter operations on heap-loaded RWStructuredBuffer are not " + "supported with SPV_EXT_descriptor_heap", + expr->getCallee()->getExprLoc()); + return nullptr; + } + + if (spirvOptions.useDescriptorHeap && + (isAppendStructuredBuffer(object->getType()) || + isConsumeStructuredBuffer(object->getType()))) { + emitError("append/consume structured buffers are not supported with " + "SPV_EXT_descriptor_heap", + expr->getCallee()->getExprLoc()); + return nullptr; + } + auto *counter = getFinalACSBufferCounterInstruction(object); if (!counter) { emitFatalError("Cannot access associated counter variable for an array of " @@ -5108,6 +5412,11 @@ bool SpirvEmitter::tryToAssignCounterVar(const DeclaratorDecl *dstDecl, declIdMapper.getOrCreateCounterIdAliasPair(dstDecl)) { auto *srcCounter = getFinalACSBufferCounterInstruction(srcExpr); if (!srcCounter) { + if (spirvOptions.useDescriptorHeap && + isDescriptorHeap(srcExpr->IgnoreParenCasts())) { + markDescriptorHeapCounterUnsupported(dstDecl); + return true; + } emitFatalError("cannot find the associated counter variable", srcExpr->getExprLoc()); return false; @@ -5147,6 +5456,11 @@ bool SpirvEmitter::tryToAssignCounterVar(const Expr *dstExpr, auto *srcCounter = getFinalACSBufferCounterInstruction(srcExpr); if ((dstCounter == nullptr) != (srcCounter == nullptr)) { + if (spirvOptions.useDescriptorHeap && dstCounter && + isDescriptorHeap(srcExpr->IgnoreParenCasts())) { + markDescriptorHeapCounterUnsupported(getReferencedDef(dstExpr)); + return true; + } emitFatalError("cannot handle associated counter variable assignment", srcExpr->getExprLoc()); return false; @@ -5278,6 +5592,8 @@ SpirvEmitter::processACSBufferAppendConsume(const CXXMemberCallExpr *expr) { expr, isAppend, // We have already translated the object in the above. Avoid duplication. /*loadObject=*/false); + if (!index) + return nullptr; auto bufferElemTy = hlsl::GetHLSLResourceResultType(object->getType()); @@ -5700,16 +6016,16 @@ SpirvEmitter::processIntrinsicMemberCall(const CXXMemberCallExpr *expr, retVal = processTextureLevelOfDetail(expr, /* unclamped */ true); break; case IntrinsicOp::MOP_IncrementCounter: - retVal = spvBuilder.createUnaryOp( - spv::Op::OpBitcast, astContext.UnsignedIntTy, - incDecRWACSBufferCounter(expr, /*isInc*/ true), - expr->getCallee()->getExprLoc(), expr->getCallee()->getSourceRange()); + if (auto *counter = incDecRWACSBufferCounter(expr, /*isInc*/ true)) + retVal = spvBuilder.createUnaryOp( + spv::Op::OpBitcast, astContext.UnsignedIntTy, counter, + expr->getCallee()->getExprLoc(), expr->getCallee()->getSourceRange()); break; case IntrinsicOp::MOP_DecrementCounter: - retVal = spvBuilder.createUnaryOp( - spv::Op::OpBitcast, astContext.UnsignedIntTy, - incDecRWACSBufferCounter(expr, /*isInc*/ false), - expr->getCallee()->getExprLoc(), expr->getCallee()->getSourceRange()); + if (auto *counter = incDecRWACSBufferCounter(expr, /*isInc*/ false)) + retVal = spvBuilder.createUnaryOp( + spv::Op::OpBitcast, astContext.UnsignedIntTy, counter, + expr->getCallee()->getExprLoc(), expr->getCallee()->getSourceRange()); break; case IntrinsicOp::MOP_Append: if (hlsl::IsHLSLStreamOutputType( @@ -6652,36 +6968,75 @@ SpirvEmitter::doCXXOperatorCallExpr(const CXXOperatorCallExpr *expr, const Expr *indexExpr = nullptr; getDescriptorHeapOperands(expr, &baseExpr, &indexExpr); - const Expr *parentExpr = cast(parentMap->getParent(expr)); + // The heap index expression must be immediately converted to a concrete + // resource type (an implicit cast inserted by the front-end). If the + // parent is missing or is not a cast (e.g. the result is discarded as + // a statement, or used in a context with no target resource type) we + // cannot determine the resource type. + const auto *parentExpr = + dyn_cast_or_null(parentMap->getParent(expr)); + if (!parentExpr) { + emitError("ResourceDescriptorHeap/SamplerDescriptorHeap indexing must " + "be used as a resource", + expr->getExprLoc()); + return nullptr; + } QualType resourceType = parentExpr->getType(); + // The heap object must be a direct reference to the builtin heap + // variable. Anything else (e.g. a non-variable expression) has no backing + // VarDecl. const auto *declRefExpr = dyn_cast(baseExpr->IgnoreCasts()); - auto *decl = cast(declRefExpr->getDecl()); + const auto *decl = + declRefExpr ? dyn_cast(declRefExpr->getDecl()) : nullptr; + if (!decl) { + emitError("unsupported ResourceDescriptorHeap/SamplerDescriptorHeap " + "expression", + baseExpr->getExprLoc()); + return nullptr; + } auto *var = declIdMapper.createResourceHeap(decl, resourceType); auto *index = doExpr(indexExpr); if (spirvOptions.useDescriptorHeap) { - emitWarning("SPV_EXT_descriptor_heap support is incomplete.", - baseExpr->getExprLoc()); needsLegalization = true; - if (isAKindOfStructuredOrByteBuffer(resourceType)) { - emitError("UAV support not implemented with non-emulated heaps.", + if (isAppendStructuredBuffer(resourceType) || + isConsumeStructuredBuffer(resourceType)) { + emitError("append/consume structured buffers are not supported with " + "SPV_EXT_descriptor_heap", expr->getExprLoc()); return nullptr; } - const auto *untypedType = spvContext.getUntypedPointerKHRType( - spv::StorageClass::UniformConstant); + if (isAKindOfStructuredOrByteBuffer(resourceType) || + isConstantTextureBuffer(resourceType)) { + return emitDescriptorHeapBufferAccess(resourceType, var, index, expr, + baseExpr, indexExpr); + } + + const auto *untypedUniformConstantType = + spvContext.getUntypedPointerKHRType( + spv::StorageClass::UniformConstant); LowerTypeVisitor lowerTypeVisitor(astContext, spvContext, spirvOptions, spvBuilder); const SpirvType *handleType = lowerTypeVisitor.lowerType(resourceType, SpirvLayoutRule::Void, llvm::None, baseExpr->getExprLoc()); - const auto *arrayType = - spvContext.getRuntimeArrayType(handleType, llvm::None); + // Images/samplers may come from either heap; pick the right stride. + const auto *arrayType = getDescriptorHeapRuntimeArrayType( + handleType, isSamplerDescriptorHeap(decl)); auto *untypedAccessChainPtr = spvBuilder.createUntypedAccessChainKHR( - untypedType, arrayType, var, index, baseExpr->getExprLoc()); + untypedUniformConstantType, arrayType, var, index, + baseExpr->getExprLoc()); + if (isRasterizerOrderedView(resourceType)) { + spvBuilder.addExecutionMode(entryFunction, + declIdMapper.getInterlockExecutionMode(), + {}, baseExpr->getExprLoc()); + } + descriptorHeapImageAccesses[expr] = { + untypedAccessChainPtr, handleType, arrayType, var, index, + indexExpr->getType()}; return spvBuilder.createLoad(resourceType, untypedAccessChainPtr, baseExpr->getExprLoc(), range); } @@ -8876,6 +9231,16 @@ void SpirvEmitter::createSpecConstant(const VarDecl *varDecl) { declIdMapper.registerSpecConstant(varDecl, specConstant); } +const SpirvType * +SpirvEmitter::getDescriptorHeapRuntimeArrayType(const SpirvType *elemType, + bool onSamplerHeap) { + constexpr uint32_t kDefaultResourceHeapStride = 64; + constexpr uint32_t kDefaultSamplerHeapStride = 32; + const uint32_t stride = + onSamplerHeap ? kDefaultSamplerHeapStride : kDefaultResourceHeapStride; + return spvContext.getRuntimeArrayType(elemType, stride); +} + SpirvInstruction * SpirvEmitter::processMatrixBinaryOp(const Expr *lhs, const Expr *rhs, const BinaryOperatorKind opcode, @@ -10575,18 +10940,41 @@ SpirvEmitter::processIntrinsicInterlockedMethod(const CallExpr *expr, return nullptr; } } - auto *baseInstr = doExpr(base); - if (baseInstr->isRValue()) { - // OpImageTexelPointer's Image argument must have a type of - // OpTypePointer with Type OpTypeImage. Need to create a temporary - // variable if the baseId is an rvalue. - baseInstr = - createTemporaryVar(base->getType(), getAstTypeName(base->getType()), - baseInstr, base->getExprLoc()); - } auto *coordInstr = doExpr(index); - ptr = spvBuilder.createImageTexelPointer(baseType, baseInstr, coordInstr, - zero, srcLoc); + + if (spirvOptions.useDescriptorHeap) { + const Expr *heapBase = base->IgnoreParenCasts(); + auto access = descriptorHeapImageAccesses.find(heapBase); + if (access == descriptorHeapImageAccesses.end() && + isDescriptorHeap(heapBase)) { + (void)doExpr(heapBase); + access = descriptorHeapImageAccesses.find(heapBase); + } + if (access != descriptorHeapImageAccesses.end()) { + ptr = spvBuilder.createUntypedImageTexelPointerEXT( + baseType, access->second.imageType, access->second.accessChain, + coordInstr, zero, srcLoc); + ptr->setStorageClass(spv::StorageClass::Image); + } else if (const auto *decl = + dyn_cast_or_null(getReferencedDef(base))) { + ptr = emitDescriptorHeapImageTexelPointer(decl, coordInstr, zero, + baseType, srcLoc); + } + } + + if (!ptr) { + auto *baseInstr = doExpr(base); + if (baseInstr->isRValue()) { + // OpImageTexelPointer's Image argument must have a type of + // OpTypePointer with Type OpTypeImage. Need to create a temporary + // variable if the baseId is an rvalue. + baseInstr = createTemporaryVar(base->getType(), + getAstTypeName(base->getType()), + baseInstr, base->getExprLoc()); + } + ptr = spvBuilder.createImageTexelPointer(baseType, baseInstr, + coordInstr, zero, srcLoc); + } } } if (!ptr) { diff --git a/tools/clang/lib/SPIRV/SpirvEmitter.h b/tools/clang/lib/SPIRV/SpirvEmitter.h index 10cc31023c..76c174eab4 100644 --- a/tools/clang/lib/SPIRV/SpirvEmitter.h +++ b/tools/clang/lib/SPIRV/SpirvEmitter.h @@ -33,6 +33,7 @@ #include "clang/SPIRV/FeatureManager.h" #include "clang/SPIRV/SpirvBuilder.h" #include "clang/SPIRV/SpirvContext.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/STLExtras.h" #include "ConstEvaluator.h" @@ -398,6 +399,23 @@ class SpirvEmitter : public ASTConsumer { /// Translates the given varDecl into a spec constant. void createSpecConstant(const VarDecl *varDecl); + /// Returns the OpTypeRuntimeArray for a descriptor-heap array of elemType + /// decorated with the default ArrayStride (64 bytes for the resource heap, + /// 32 bytes for the sampler heap). + const SpirvType *getDescriptorHeapRuntimeArrayType(const SpirvType *elemType, + bool onSamplerHeap); + + /// Emits the native (SPV_EXT_descriptor_heap) access for a buffer-like + /// resource (StructuredBuffer/ByteAddressBuffer/ConstantBuffer/TextureBuffer + /// and their RW variants) loaded from heapVar at index: + /// OpUntypedAccessChainKHR -> OpBufferPointerEXT. Records the access in + /// descriptorHeapBufferAccesses[expr] and returns the buffer-data pointer, or + /// nullptr (after emitting an error) on type-lowering failure. Caller must + /// have already checked the resource is buffer-like. + SpirvInstruction *emitDescriptorHeapBufferAccess( + QualType resourceType, SpirvInstruction *heapVar, SpirvInstruction *index, + const Expr *expr, const Expr *baseExpr, const Expr *indexExpr); + /// Generates the necessary instructions for conducting the given binary /// operation on lhs and rhs. /// @@ -1202,6 +1220,74 @@ class SpirvEmitter : public ASTConsumer { const Expr *srcExpr); bool tryToAssignCounterVar(const Expr *dstExpr, const Expr *srcExpr); + /// \brief Marks an alias resource as heap-loaded with no associated counter. + void markDescriptorHeapCounterUnsupported(const DeclaratorDecl *decl); + + /// \brief Returns true if counter operations on the resource expression are + /// known to be unsupported because the resource came from + /// ResourceDescriptorHeap. + bool isDescriptorHeapCounterUnsupported(const Expr *expr) const; + + /// \brief Records the descriptor heap index assigned to a local image + /// resource alias, if the source expression came directly from a descriptor + /// heap. This mirrors the normal resource handle store while preserving + /// enough information to recreate OpUntypedImageTexelPointerEXT after + /// reassignment. + bool tryToAssignDescriptorHeapImageAlias(const DeclaratorDecl *dstDecl, + const Expr *srcExpr); + bool tryToAssignDescriptorHeapImageAlias(const Expr *dstExpr, + const Expr *srcExpr); + bool tryToAssignDescriptorHeapBufferAlias(const DeclaratorDecl *dstDecl, + const Expr *srcExpr); + bool tryToAssignDescriptorHeapBufferAlias(const Expr *dstExpr, + const Expr *srcExpr); + + /// \brief Creates the ".descriptor.index" function variable used to + /// remember the descriptor heap index of a local resource alias dstVar. + SpirvVariable *createDescriptorHeapIndexVar(const VarDecl *dstVar); + + /// \brief If decl is a function-local variable initialized directly from a + /// descriptor heap subscript (e.g. ResourceDescriptorHeap[i]), creates the + /// appropriate alias and returns true. Returns false if decl is not such a + /// descriptor-heap alias and should be emitted as a normal variable. + bool tryToCreateDescriptorHeapAlias(const VarDecl *decl, const Expr *init); + + /// \brief Handles a buffer = ResourceDescriptorHeap[i] assignment. Returns + /// None if assignExpr is not such an assignment (caller should fall back to a + /// normal assignment). Otherwise the alias was created and the wrapped value + /// is the result of the assignment expression (possibly nullptr). + llvm::Optional + tryToAssignToDescriptorHeapBuffer(const BinaryOperator *assignExpr); + + /// \brief Emits the instructions that re-derive the buffer-data pointer for a + /// descriptor-heap buffer alias decl (OpLoad of the saved index, then + /// OpUntypedAccessChainKHR + OpBufferPointerEXT). Returns nullptr if decl + /// is not a recorded heap buffer alias. Not a pure lookup -- it emits. + SpirvInstruction *emitDescriptorHeapBufferPointer(const VarDecl *decl, + SourceLocation loc); + + /// \brief Emits an OpUntypedImageTexelPointerEXT for a descriptor-heap image + /// alias decl (OpLoad of the saved index, then OpUntypedAccessChainKHR + /// feeding the texel pointer). Returns nullptr if decl is not a recorded heap + /// image alias. Symmetric with emitDescriptorHeapBufferPointer. + SpirvInstruction *emitDescriptorHeapImageTexelPointer( + const VarDecl *decl, SpirvInstruction *coordinate, + SpirvInstruction *sample, QualType resultType, SourceLocation loc); + + /// \brief Emits OpLoad of indexVar then OpUntypedAccessChainKHR into the + /// heap, yielding the per-descriptor pointer shared by the buffer/image alias + /// re-derivation paths above. + SpirvInstruction *emitDescriptorHeapAccessChain(const SpirvType *arrayType, + SpirvInstruction *heap, + SpirvVariable *indexVar, + SourceLocation loc); + + /// \brief Stores index (cast to uint when needed) into the alias indexVar, + /// shared by the image/buffer alias-assignment paths. + void storeDescriptorHeapIndex(SpirvVariable *indexVar, + SpirvInstruction *index, QualType indexType, + const Expr *srcExpr); + /// Returns an instruction that points to the alias counter variable with the /// entity represented by expr. /// @@ -1555,6 +1641,51 @@ class SpirvEmitter : public ASTConsumer { /// The SPIR-V function parameter for the current this object. SpirvInstruction *curThis; + /// Native descriptor heap image descriptors used to directly form image + /// atomics. The emitter is single-use per translation unit, so these + /// AST-pointer maps live for the emitter lifetime. + struct DescriptorHeapImageAccess { + SpirvInstruction *accessChain; + const SpirvType *imageType; + const SpirvType *arrayType; + SpirvInstruction *heap; + SpirvInstruction *index; + QualType indexType; + }; + struct DescriptorHeapImageAlias { + SpirvVariable *indexVar; + const SpirvType *imageType; + const SpirvType *arrayType; + SpirvInstruction *heap; + }; + struct DescriptorHeapBufferAccess { + const SpirvPointerType *bufferPointerType; + const SpirvType *arrayType; + SpirvInstruction *heap; + SpirvInstruction *index; + QualType indexType; + SpirvLayoutRule layoutRule; + }; + struct DescriptorHeapBufferAlias { + SpirvVariable *indexVar; + const SpirvPointerType *bufferPointerType; + const SpirvType *arrayType; + SpirvInstruction *heap; + SpirvLayoutRule layoutRule; + }; + llvm::DenseMap + descriptorHeapImageAccesses; + llvm::DenseMap + descriptorHeapImageAliasVars; + llvm::DenseMap + descriptorHeapBufferAccesses; + llvm::DenseMap + descriptorHeapBufferAliasVars; + + /// RWStructuredBuffer aliases loaded from ResourceDescriptorHeap have no + /// associated UAV counter descriptor in the native descriptor heap path. + llvm::DenseSet descriptorHeapUnsupportedCounters; + /// The source location of a push constant block we have previously seen. /// Invalid means no push constant blocks defined thus far. SourceLocation seenPushConstantAt; diff --git a/tools/clang/lib/SPIRV/SpirvInstruction.cpp b/tools/clang/lib/SPIRV/SpirvInstruction.cpp index ea985e5da4..153a2f9c66 100644 --- a/tools/clang/lib/SPIRV/SpirvInstruction.cpp +++ b/tools/clang/lib/SPIRV/SpirvInstruction.cpp @@ -968,11 +968,13 @@ SpirvImageTexelPointer::SpirvImageTexelPointer(QualType resultType, image(imageInst), coordinate(coordinateInst), sample(sampleInst) {} SpirvUntypedImageTexelPointerEXT::SpirvUntypedImageTexelPointerEXT( - QualType resultType, SourceLocation loc, SpirvInstruction *imageInst, - SpirvInstruction *coordinateInst, SpirvInstruction *sampleInst) + QualType resultType, SourceLocation loc, const SpirvType *spvImageType, + SpirvInstruction *imageInst, SpirvInstruction *coordinateInst, + SpirvInstruction *sampleInst) : SpirvInstruction(IK_UntypedImageTexelPointerEXT, spv::Op::OpUntypedImageTexelPointerEXT, resultType, loc), - image(imageInst), coordinate(coordinateInst), sample(sampleInst) {} + imageType(spvImageType), image(imageInst), coordinate(coordinateInst), + sample(sampleInst) {} SpirvLoad::SpirvLoad(QualType resultType, SourceLocation loc, SpirvInstruction *pointerInst, SourceRange range, diff --git a/tools/clang/test/CodeGenSPIRV/resource-heap-ext-texture.hlsl b/tools/clang/test/CodeGenSPIRV/resource-heap-ext-texture.hlsl deleted file mode 100644 index 1641d1f038..0000000000 --- a/tools/clang/test/CodeGenSPIRV/resource-heap-ext-texture.hlsl +++ /dev/null @@ -1,59 +0,0 @@ -// RUN: %dxc -T cs_6_6 -E main -fspv-use-descriptor-heap -spirv %s | FileCheck %s - -// CHECK: OpCapability DescriptorHeapEXT -// CHECK: OpExtension "SPV_EXT_descriptor_heap" - -// CHECK-DAG: OpDecorate %[[ResourceHeap:[a-zA-Z0-9_]+]] BuiltIn ResourceHeapEXT -// CHECK-DAG: OpDecorate %[[SamplerHeap:[a-zA-Z0-9_]+]] BuiltIn SamplerHeapEXT - -// CHECK-DAG: %[[UntypedPtrType:[a-zA-Z0-9_]+]] = OpTypeUntypedPointerKHR UniformConstant -// CHECK-DAG: %[[Tex2DType:[a-zA-Z0-9_]+]] = OpTypeImage %float 2D 2 0 0 1 Unknown -// CHECK-DAG: %[[RWTex2DType:[a-zA-Z0-9_]+]] = OpTypeImage %float 2D 2 0 0 2 Rgba32f -// CHECK-DAG: %[[BufferType:[a-zA-Z0-9_]+]] = OpTypeImage %float Buffer 2 0 0 1 Rgba32f -// CHECK-DAG: %[[RWBufferType:[a-zA-Z0-9_]+]] = OpTypeImage %float Buffer 2 0 0 2 Rgba32f -// CHECK-DAG: %[[SamplerType:[a-zA-Z0-9_]+]] = OpTypeSampler - -// CHECK-DAG: %[[RA_Tex2DType:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[Tex2DType]] -// CHECK-DAG: %[[RA_RWTex2DType:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[RWTex2DType]] -// CHECK-DAG: %[[RA_BufferType:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[BufferType]] -// CHECK-DAG: %[[RA_RWBufferType:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[RWBufferType]] -// CHECK-DAG: %[[RA_SamplerType:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[SamplerType]] - -// CHECK: %[[ResourceHeap]] = OpUntypedVariableKHR %[[UntypedPtrType]] UniformConstant -// CHECK: %[[SamplerHeap]] = OpUntypedVariableKHR %[[UntypedPtrType]] UniformConstant - -[numthreads(1, 1, 1)] -void main(uint3 tid : SV_DispatchThreadID) { - Texture2D myTex = ResourceDescriptorHeap[0]; - // CHECK: %[[TexIndex:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtrType]] %[[RA_Tex2DType]] %[[ResourceHeap]] %uint_0 - // CHECK: %[[TexHandle:[a-zA-Z0-9_]+]] = OpLoad %[[Tex2DType]] %[[TexIndex]] - - RWTexture2D myRWTex = ResourceDescriptorHeap[1]; - // CHECK: %[[RWTexIndex:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtrType]] %[[RA_RWTex2DType]] %[[ResourceHeap]] %uint_1 - // CHECK: %[[RWTexHandle:[a-zA-Z0-9_]+]] = OpLoad %[[RWTex2DType]] %[[RWTexIndex]] - - Buffer myBuf = ResourceDescriptorHeap[2]; - // CHECK: %[[BufIndex:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtrType]] %[[RA_BufferType]] %[[ResourceHeap]] %uint_2 - // CHECK: %[[BufHandle:[a-zA-Z0-9_]+]] = OpLoad %[[BufferType]] %[[BufIndex]] - - RWBuffer myRWBuf = ResourceDescriptorHeap[3]; - // CHECK: %[[RWBufIndex:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtrType]] %[[RA_RWBufferType]] %[[ResourceHeap]] %uint_3 - // CHECK: %[[RWBufHandle:[a-zA-Z0-9_]+]] = OpLoad %[[RWBufferType]] %[[RWBufIndex]] - - SamplerState mySamp = SamplerDescriptorHeap[0]; - // CHECK: %[[SampIndex:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtrType]] %[[RA_SamplerType]] %[[SamplerHeap]] %uint_0 - // CHECK: %[[SampHandle:[a-zA-Z0-9_]+]] = OpLoad %[[SamplerType]] %[[SampIndex]] - - // CHECK: %[[SampledImage:[a-zA-Z0-9_]+]] = OpSampledImage %{{.*}} %[[TexHandle]] %[[SampHandle]] - // CHECK: %[[TexResult:[a-zA-Z0-9_]+]] = OpImageSampleExplicitLod %v4float %[[SampledImage]] - float4 texVal = myTex.SampleLevel(mySamp, float2(0, 0), 0); - - // CHECK: %[[BufResult:[a-zA-Z0-9_]+]] = OpImageFetch %v4float %[[BufHandle]] - float4 bufVal = myBuf.Load(tid.x); - - // CHECK: OpImageWrite %[[RWTexHandle]] - myRWTex[tid.xy] = texVal; - - // CHECK: OpImageWrite %[[RWBufHandle]] - myRWBuf[tid.x] = bufVal; -} diff --git a/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.append-consume.error.hlsl b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.append-consume.error.hlsl new file mode 100644 index 0000000000..89bf18acaf --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.append-consume.error.hlsl @@ -0,0 +1,19 @@ +// RUN: not %dxc -T cs_6_6 -E main -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv -DTEST_APPEND %s 2>&1 | FileCheck --check-prefix=APPEND %s +// RUN: not %dxc -T cs_6_6 -E main -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv %s 2>&1 | FileCheck --check-prefix=CONSUME %s + +// Verifies: Append/Consume structured buffers via ResourceDescriptorHeap +// emit a hard error under SPV_EXT_descriptor_heap. + +// APPEND: append/consume structured buffers are not supported with SPV_EXT_descriptor_heap +// CONSUME: append/consume structured buffers are not supported with SPV_EXT_descriptor_heap + +[numthreads(1, 1, 1)] +void main() { +#ifdef TEST_APPEND + AppendStructuredBuffer output = ResourceDescriptorHeap[0]; + output.Append(1); +#else + ConsumeStructuredBuffer input = ResourceDescriptorHeap[0]; + uint val = input.Consume(); +#endif +} diff --git a/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.buffer.hlsl b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.buffer.hlsl new file mode 100644 index 0000000000..eb0b6b25ca --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.buffer.hlsl @@ -0,0 +1,57 @@ +// RUN: %dxc -T cs_6_6 -E main -Od -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv %s | FileCheck %s + +// Verifies: StructuredBuffer / RWStructuredBuffer / ByteAddressBuffer +// share one StorageBuffer runtime array, yet each materializes its own +// typed OpBufferPointerEXT, ConstantBuffer uses a separate Uniform array, +// and reassignment re-indexes the shared array. +// +// StructuredBuffer -> shared OpTypeBufferEXT StorageBuffer array -> own typed OpBufferPointerEXT (%type_StructuredBuffer_*) +// RWStructuredBuffer -> shared OpTypeBufferEXT StorageBuffer array -> own typed OpBufferPointerEXT (%type_RWStructuredBuffer_*) +// ByteAddressBuffer -> shared OpTypeBufferEXT StorageBuffer array -> own typed OpBufferPointerEXT (%type_ByteAddressBuffer) +// ConstantBuffer -> separate OpTypeBufferEXT Uniform array -> own typed OpBufferPointerEXT (%type_ConstantBuffer_*) +// reassignment -> re-indexes shared StorageBuffer array (uint_4) -> same %type_StructuredBuffer_* pointer + +// CHECK-DAG: %[[UntypedPtrType:[a-zA-Z0-9_]+]] = OpTypeUntypedPointerKHR UniformConstant +// CHECK-DAG: %[[SBBufDesc:[a-zA-Z0-9_]+]] = OpTypeBufferEXT StorageBuffer +// CHECK-DAG: %[[SBBufArray:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[SBBufDesc]] +// CHECK-DAG: %[[UBufDesc:[a-zA-Z0-9_]+]] = OpTypeBufferEXT Uniform +// CHECK-DAG: %[[UBufArray:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[UBufDesc]] +// Anchor each pointer type to its struct-type prefix to prevent FileCheck from +// binding all three StorageBuffer captures to the scalar %_ptr_StorageBuffer_uint. +// CHECK-DAG: %[[SBInputPtr:[a-zA-Z0-9_]+]] = OpTypePointer StorageBuffer %type_StructuredBuffer_{{.*}} +// CHECK-DAG: %[[SBOutputPtr:[a-zA-Z0-9_]+]] = OpTypePointer StorageBuffer %type_RWStructuredBuffer_{{.*}} +// CHECK-DAG: %[[SBBytesPtr:[a-zA-Z0-9_]+]] = OpTypePointer StorageBuffer %type_ByteAddressBuffer{{$}} +// CHECK-DAG: %[[UConstPtr:[a-zA-Z0-9_]+]] = OpTypePointer Uniform %type_ConstantBuffer_{{.*}} + +// CHECK: %[[ResourceHeap:[a-zA-Z0-9_]+]] = OpUntypedVariableKHR %[[UntypedPtrType]] UniformConstant + +struct Constants { + uint value; +}; + +RWByteAddressBuffer outputBytes : register(u0); + +[numthreads(1, 1, 1)] +void main(uint3 tid : SV_DispatchThreadID) { + StructuredBuffer input = ResourceDescriptorHeap[0]; + RWStructuredBuffer output = ResourceDescriptorHeap[1]; + ByteAddressBuffer inputBytes = ResourceDescriptorHeap[2]; + ConstantBuffer constants = ResourceDescriptorHeap[3]; + + // CHECK: %[[InputDesc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtrType]] %[[SBBufArray]] %[[ResourceHeap]] %uint_0 + // CHECK: OpBufferPointerEXT %[[SBInputPtr]] %[[InputDesc]] + // CHECK: %[[OutputDesc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtrType]] %[[SBBufArray]] %[[ResourceHeap]] %uint_1 + // CHECK: OpBufferPointerEXT %[[SBOutputPtr]] %[[OutputDesc]] + // CHECK: %[[InputBytesDesc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtrType]] %[[SBBufArray]] %[[ResourceHeap]] %uint_2 + // CHECK: OpBufferPointerEXT %[[SBBytesPtr]] %[[InputBytesDesc]] + // CHECK: %[[ConstantsDesc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtrType]] %[[UBufArray]] %[[ResourceHeap]] %uint_3 + // CHECK: OpBufferPointerEXT %[[UConstPtr]] %[[ConstantsDesc]] + output[tid.x] = input.Load(tid.x) + inputBytes.Load(tid.x * 4) + constants.value; + outputBytes.Store(tid.x * 4, output[tid.x]); + + // Reassignment: verify new descriptor (index 4) is used after reassign. + input = ResourceDescriptorHeap[4]; + // CHECK: %[[ReassignedDesc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtrType]] %[[SBBufArray]] %[[ResourceHeap]] %uint_4 + // CHECK: OpBufferPointerEXT %[[SBInputPtr]] %[[ReassignedDesc]] + outputBytes.Store(tid.x * 4 + 4, input.Load(tid.x)); +} diff --git a/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.constant-buffer-assignment.hlsl b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.constant-buffer-assignment.hlsl new file mode 100644 index 0000000000..3db71dcc13 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.constant-buffer-assignment.hlsl @@ -0,0 +1,78 @@ +// RUN: %dxc -T cs_6_6 -E main -Od -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv %s | FileCheck %s + +// Verifies: ConstantBuffer reassignment from the descriptor heap re-indexes +// the Uniform array on every assignment and each member load uses the latest +// descriptor, in three forms that buffer.hlsl does not cover: +// +// dynamic runtime index -> %[[Idx]] (OpLoad %uint) + OpAccessChain %int_0 +// OpIAdd-computed index -> %[[IdxPlus2]] (OpIAdd) + OpAccessChain %int_1 +// reassignment inside a branch -> %[[IdxMinus2]] (OpUGreaterThan/OpBranchConditional/OpISub) + OpAccessChain %int_2 + +// CHECK-DAG: %[[UntypedPtr:[a-zA-Z0-9_]+]] = OpTypeUntypedPointerKHR UniformConstant +// CHECK-DAG: %[[BufferDesc:[a-zA-Z0-9_]+]] = OpTypeBufferEXT Uniform +// CHECK-DAG: %[[BufferArray:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[BufferDesc]] +// CHECK-DAG: %[[CBPtr:[a-zA-Z0-9_]+]] = OpTypePointer Uniform %type_ConstantBuffer_Constants + +// CHECK: %[[ResourceHeap:[a-zA-Z0-9_]+]] = OpUntypedVariableKHR %[[UntypedPtr]] UniformConstant + +struct Constants +{ + uint a; + uint b; + uint c; +}; + +RWByteAddressBuffer outputBytes : register(u0); + +[numthreads(1, 1, 1)] +void main(uint3 tid : SV_DispatchThreadID) +{ + uint idx = tid.x; + uint cond = tid.y; + + ConstantBuffer constants = ResourceDescriptorHeap[idx]; + + // CHECK: %[[Idx:[a-zA-Z0-9_]+]] = OpLoad %uint + // CHECK: %[[Cond:[a-zA-Z0-9_]+]] = OpLoad %uint + // CHECK: %[[InitDesc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[BufferArray]] %[[ResourceHeap]] %[[Idx]] + // CHECK: OpBufferPointerEXT %[[CBPtr]] %[[InitDesc]] + // CHECK: %[[ADesc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[BufferArray]] %[[ResourceHeap]] %[[Idx]] + // CHECK: %[[APtr:[a-zA-Z0-9_]+]] = OpBufferPointerEXT %[[CBPtr]] %[[ADesc]] + // CHECK: %[[AAccess:[a-zA-Z0-9_]+]] = OpAccessChain %{{[a-zA-Z0-9_]+}} %[[APtr]] %int_0 + // CHECK: %[[A:[a-zA-Z0-9_]+]] = OpLoad %uint %[[AAccess]] + uint value = constants.a; + + constants = ResourceDescriptorHeap[idx + 2]; + + // CHECK: %[[IdxPlus2:[a-zA-Z0-9_]+]] = OpIAdd %uint %[[Idx]] %uint_2 + // CHECK: %[[AssignDesc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[BufferArray]] %[[ResourceHeap]] %[[IdxPlus2]] + // CHECK: OpBufferPointerEXT %[[CBPtr]] %[[AssignDesc]] + // CHECK: %[[BDesc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[BufferArray]] %[[ResourceHeap]] %[[IdxPlus2]] + // CHECK: %[[BPtr:[a-zA-Z0-9_]+]] = OpBufferPointerEXT %[[CBPtr]] %[[BDesc]] + // CHECK: %[[BUseDesc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[BufferArray]] %[[ResourceHeap]] %[[IdxPlus2]] + // CHECK: %[[BUsePtr:[a-zA-Z0-9_]+]] = OpBufferPointerEXT %[[CBPtr]] %[[BUseDesc]] + // CHECK: %[[BAccess:[a-zA-Z0-9_]+]] = OpAccessChain %{{[a-zA-Z0-9_]+}} %[[BUsePtr]] %int_1 + // CHECK: %[[B:[a-zA-Z0-9_]+]] = OpLoad %uint %[[BAccess]] + // CHECK: %[[AB:[a-zA-Z0-9_]+]] = OpIAdd %uint %[[A]] %[[B]] + value += constants.b; + + if (cond > 4) { + constants = ResourceDescriptorHeap[idx - 2]; + + // CHECK: %[[CondCmp:[a-zA-Z0-9_]+]] = OpUGreaterThan %bool %[[Cond]] %uint_4 + // CHECK: OpBranchConditional %[[CondCmp]] + // CHECK: %[[IdxMinus2:[a-zA-Z0-9_]+]] = OpISub %uint %[[Idx]] %uint_2 + // CHECK: %[[BranchDesc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[BufferArray]] %[[ResourceHeap]] %[[IdxMinus2]] + // CHECK: OpBufferPointerEXT %[[CBPtr]] %[[BranchDesc]] + // CHECK: %[[CDesc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[BufferArray]] %[[ResourceHeap]] %[[IdxMinus2]] + // CHECK: %[[CPtr:[a-zA-Z0-9_]+]] = OpBufferPointerEXT %[[CBPtr]] %[[CDesc]] + // CHECK: %[[CUseDesc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[BufferArray]] %[[ResourceHeap]] %[[IdxMinus2]] + // CHECK: %[[CUsePtr:[a-zA-Z0-9_]+]] = OpBufferPointerEXT %[[CBPtr]] %[[CUseDesc]] + // CHECK: %[[CAccess:[a-zA-Z0-9_]+]] = OpAccessChain %{{[a-zA-Z0-9_]+}} %[[CUsePtr]] %int_2 + // CHECK: %[[C:[a-zA-Z0-9_]+]] = OpLoad %uint %[[CAccess]] + // CHECK: OpIAdd %uint %[[AB]] %[[C]] + value += constants.c; + } + + outputBytes.Store(0, value); +} diff --git a/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.constant-texture-buffer.hlsl b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.constant-texture-buffer.hlsl new file mode 100644 index 0000000000..1a41f3ea44 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.constant-texture-buffer.hlsl @@ -0,0 +1,51 @@ +// RUN: %dxc -T cs_6_6 -E main -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv %s | FileCheck %s + +// Verifies: ConstantBuffer and TextureBuffer from the heap map to different element types, +// both carrying Block and supporting member access. +// +// ConstantBuffer -> OpTypeBufferEXT Uniform -> Block +// TextureBuffer -> OpTypeBufferEXT StorageBuffer -> Block + NonWritable members + +// CHECK-DAG: OpDecorate %type_ConstantBuffer_Data Block +// CHECK-DAG: OpDecorate %type_TextureBuffer_Data Block +// CHECK-DAG: OpMemberDecorate %type_TextureBuffer_Data 0 NonWritable +// CHECK-DAG: OpMemberDecorate %type_TextureBuffer_Data 1 NonWritable + +// CHECK-DAG: %[[UntypedUniformConstant:[a-zA-Z0-9_]+]] = OpTypeUntypedPointerKHR UniformConstant +// CHECK-DAG: %[[CBDesc:[a-zA-Z0-9_]+]] = OpTypeBufferEXT Uniform +// CHECK-DAG: %[[CBDescArray:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[CBDesc]] +// CHECK-DAG: %[[CBPtr:[a-zA-Z0-9_]+]] = OpTypePointer Uniform %type_ConstantBuffer_Data +// CHECK-DAG: %[[TBDesc:[a-zA-Z0-9_]+]] = OpTypeBufferEXT StorageBuffer +// CHECK-DAG: %[[TBDescArray:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[TBDesc]] +// CHECK-DAG: %[[TBPtr:[a-zA-Z0-9_]+]] = OpTypePointer StorageBuffer %type_TextureBuffer_Data + +// CHECK: %[[ResourceHeap:[a-zA-Z0-9_]+]] = OpUntypedVariableKHR %[[UntypedUniformConstant]] UniformConstant + +struct Data { + uint a; + uint b[2]; +}; + +RWByteAddressBuffer outputBytes : register(u0); + +[numthreads(1, 1, 1)] +void main(uint3 tid : SV_DispatchThreadID) { + ConstantBuffer cb = ResourceDescriptorHeap[0]; + TextureBuffer tb = ResourceDescriptorHeap[1]; + + uint index = tid.x & 1; + + // CHECK: %[[CBDescPtr:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedUniformConstant]] %[[CBDescArray]] %[[ResourceHeap]] %uint_0 + // CHECK: OpBufferPointerEXT %[[CBPtr]] %[[CBDescPtr]] + // CHECK: %[[TBDescPtr:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedUniformConstant]] %[[TBDescArray]] %[[ResourceHeap]] %uint_1 + // CHECK: OpBufferPointerEXT %[[TBPtr]] %[[TBDescPtr]] + // CHECK: %[[CBDataA:[a-zA-Z0-9_]+]] = OpBufferPointerEXT %[[CBPtr]] %[[CBDescPtr]] + // CHECK: OpAccessChain %{{[a-zA-Z0-9_]+}} %[[CBDataA]] %int_0 + // CHECK: %[[CBDataB:[a-zA-Z0-9_]+]] = OpBufferPointerEXT %[[CBPtr]] %[[CBDescPtr]] + // CHECK: OpAccessChain %{{[a-zA-Z0-9_]+}} %[[CBDataB]] %int_1 %{{[a-zA-Z0-9_]+}} + // CHECK: %[[TBDataA:[a-zA-Z0-9_]+]] = OpBufferPointerEXT %[[TBPtr]] %[[TBDescPtr]] + // CHECK: OpAccessChain %{{[a-zA-Z0-9_]+}} %[[TBDataA]] %int_0 + // CHECK: %[[TBDataB:[a-zA-Z0-9_]+]] = OpBufferPointerEXT %[[TBPtr]] %[[TBDescPtr]] + // CHECK: OpAccessChain %{{[a-zA-Z0-9_]+}} %[[TBDataB]] %int_1 %int_1 + outputBytes.Store(0, cb.a + cb.b[index] + tb.a + tb.b[1]); +} diff --git a/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.counter-ops.error.hlsl b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.counter-ops.error.hlsl new file mode 100644 index 0000000000..c0b406428c --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.counter-ops.error.hlsl @@ -0,0 +1,24 @@ +// RUN: not %dxc -T cs_6_6 -E main -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv -DTEST_INCREMENT %s 2>&1 | FileCheck --check-prefix=INC %s +// RUN: not %dxc -T cs_6_6 -E main -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv %s 2>&1 | FileCheck --check-prefix=DEC %s + +// Verifies: IncrementCounter/DecrementCounter on a +// heap-loaded RWStructuredBuffer is rejected with a hard error under +// SPV_EXT_descriptor_heap. + +// INC: counter operations on heap-loaded RWStructuredBuffer are not supported with SPV_EXT_descriptor_heap +// DEC: counter operations on heap-loaded RWStructuredBuffer are not supported with SPV_EXT_descriptor_heap + +RWByteAddressBuffer outputBytes : register(u0); + +[numthreads(1, 1, 1)] +void main() { + RWStructuredBuffer buffer = ResourceDescriptorHeap[0]; + +#ifdef TEST_INCREMENT + uint value = buffer.IncrementCounter(); +#else + uint value = buffer.DecrementCounter(); +#endif + + outputBytes.Store(0, value); +} diff --git a/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.discarded.error.hlsl b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.discarded.error.hlsl new file mode 100644 index 0000000000..3e26d746e4 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.discarded.error.hlsl @@ -0,0 +1,14 @@ +// RUN: not %dxc -T cs_6_6 -E main -Od -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv %s 2>&1 | FileCheck %s + +// Regression test for an unguarded cast in doCXXOperatorCallExpr: +// cast(parentMap->getParent(expr)) +// A descriptor-heap index whose result is discarded (used as a bare expression +// statement) has no parent cast to a concrete resource type, so the resource +// type cannot be determined. This must emit a diagnostic, not an ICE. + +// CHECK: error: {{.*}}ResourceDescriptorHeap/SamplerDescriptorHeap indexing must be used as a resource + +[numthreads(1, 1, 1)] +void main(uint3 tid : SV_DispatchThreadID) { + ResourceDescriptorHeap[tid.x]; +} diff --git a/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.function-params.hlsl b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.function-params.hlsl new file mode 100644 index 0000000000..3a72fa3482 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.function-params.hlsl @@ -0,0 +1,97 @@ +// RUN: %dxc -T cs_6_6 -E main -Od -fcgl -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv %s | FileCheck %s + +// Verifies: a heap-loaded resource passed by value to a user function lowers to +// pass-by-Function-pointer. +// +// heap handle -> OpStore into a Function-class var -> per-call OpLoad/OpStore/OpFunctionCall +// parameter slot -> OpFunctionParameter %[[*PtrType]] -> Function storage class + +// CHECK-DAG: OpName %[[ReadTex:[a-zA-Z0-9_]+]] "ReadTex" +// CHECK-DAG: OpName %[[ReadBuf:[a-zA-Z0-9_]+]] "ReadBuf" +// CHECK-DAG: OpName %[[WriteBuf:[a-zA-Z0-9_]+]] "WriteBuf" + +// CHECK-DAG: %[[UntypedPtrType:[a-zA-Z0-9_]+]] = OpTypeUntypedPointerKHR UniformConstant +// CHECK-DAG: %[[TexType:[a-zA-Z0-9_]+]] = OpTypeImage %float 2D 2 0 0 1 Unknown +// CHECK-DAG: %[[BufType:[a-zA-Z0-9_]+]] = OpTypeImage %float Buffer 2 0 0 1 Rgba32f +// CHECK-DAG: %[[RWBufType:[a-zA-Z0-9_]+]] = OpTypeImage %float Buffer 2 0 0 2 Rgba32f +// CHECK-DAG: %[[SamplerType:[a-zA-Z0-9_]+]] = OpTypeSampler +// CHECK-DAG: %[[TexPtrType:[a-zA-Z0-9_]+]] = OpTypePointer Function %[[TexType]] +// CHECK-DAG: %[[BufPtrType:[a-zA-Z0-9_]+]] = OpTypePointer Function %[[BufType]] +// CHECK-DAG: %[[RWBufPtrType:[a-zA-Z0-9_]+]] = OpTypePointer Function %[[RWBufType]] +// CHECK-DAG: %[[SamplerPtrType:[a-zA-Z0-9_]+]] = OpTypePointer Function %[[SamplerType]] +// CHECK-DAG: %[[RA_TexType:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[TexType]] +// CHECK-DAG: %[[RA_BufType:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[BufType]] +// CHECK-DAG: %[[RA_RWBufType:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[RWBufType]] +// CHECK-DAG: %[[RA_SamplerType:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[SamplerType]] + +float4 ReadTex(Texture2D tex, SamplerState samp); +float4 ReadBuf(Buffer buf, uint index); +void WriteBuf(RWBuffer buf, uint index, float4 value); + +RWByteAddressBuffer outputBytes : register(u0); + +[numthreads(1, 1, 1)] +void main(uint3 tid : SV_DispatchThreadID) { + Texture2D tex = ResourceDescriptorHeap[0]; + Buffer buf = ResourceDescriptorHeap[1]; + RWBuffer outBuf = ResourceDescriptorHeap[2]; + SamplerState samp = SamplerDescriptorHeap[0]; + + // CHECK: %[[ResourceHeap:[a-zA-Z0-9_]+]] = OpUntypedVariableKHR %[[UntypedPtrType]] UniformConstant + // CHECK: %[[SamplerHeap:[a-zA-Z0-9_]+]] = OpUntypedVariableKHR %[[UntypedPtrType]] UniformConstant + + // CHECK: %[[TexDesc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtrType]] %[[RA_TexType]] %[[ResourceHeap]] %uint_0 + // CHECK: %[[TexHandle:[a-zA-Z0-9_]+]] = OpLoad %[[TexType]] %[[TexDesc]] + // CHECK: OpStore %[[TexVar:[a-zA-Z0-9_]+]] %[[TexHandle]] + // CHECK: %[[BufDesc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtrType]] %[[RA_BufType]] %[[ResourceHeap]] %uint_1 + // CHECK: %[[BufHandle:[a-zA-Z0-9_]+]] = OpLoad %[[BufType]] %[[BufDesc]] + // CHECK: OpStore %[[BufVar:[a-zA-Z0-9_]+]] %[[BufHandle]] + // CHECK: %[[RWBufDesc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtrType]] %[[RA_RWBufType]] %[[ResourceHeap]] %uint_2 + // CHECK: %[[RWBufHandle:[a-zA-Z0-9_]+]] = OpLoad %[[RWBufType]] %[[RWBufDesc]] + // CHECK: OpStore %[[RWBufVar:[a-zA-Z0-9_]+]] %[[RWBufHandle]] + // CHECK: %[[SamplerDesc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtrType]] %[[RA_SamplerType]] %[[SamplerHeap]] %uint_0 + // CHECK: %[[SamplerHandle:[a-zA-Z0-9_]+]] = OpLoad %[[SamplerType]] %[[SamplerDesc]] + // CHECK: OpStore %[[SamplerVar:[a-zA-Z0-9_]+]] %[[SamplerHandle]] + + // CHECK: %[[TexArg:[a-zA-Z0-9_]+]] = OpLoad %[[TexType]] %[[TexVar]] + // CHECK: OpStore %[[TexParam:[a-zA-Z0-9_]+]] %[[TexArg]] + // CHECK: %[[SamplerArg:[a-zA-Z0-9_]+]] = OpLoad %[[SamplerType]] %[[SamplerVar]] + // CHECK: OpStore %[[SamplerParam:[a-zA-Z0-9_]+]] %[[SamplerArg]] + // CHECK: %[[TexValue:[a-zA-Z0-9_]+]] = OpFunctionCall %v4float %[[ReadTex]] %[[TexParam]] %[[SamplerParam]] + float4 value = ReadTex(tex, samp); + + // CHECK: %[[BufArg:[a-zA-Z0-9_]+]] = OpLoad %[[BufType]] %[[BufVar]] + // CHECK: OpStore %[[BufParam:[a-zA-Z0-9_]+]] %[[BufArg]] + // CHECK: %[[BufValue:[a-zA-Z0-9_]+]] = OpFunctionCall %v4float %[[ReadBuf]] %[[BufParam]] + value += ReadBuf(buf, tid.x); + + // CHECK: %[[RWBufArg:[a-zA-Z0-9_]+]] = OpLoad %[[RWBufType]] %[[RWBufVar]] + // CHECK: OpStore %[[RWBufParam:[a-zA-Z0-9_]+]] %[[RWBufArg]] + // CHECK: OpFunctionCall %void %[[WriteBuf]] %[[RWBufParam]] + WriteBuf(outBuf, tid.x, value); + + outputBytes.Store(tid.x * 4, asuint(value.x)); +} + +// Callees emit after the entry point; each receives its resource by Function pointer. +float4 ReadTex(Texture2D tex, SamplerState samp) { + // CHECK: %[[ReadTex]] = OpFunction %v4float + // CHECK: OpFunctionParameter %[[TexPtrType]] + // CHECK: OpFunctionParameter %[[SamplerPtrType]] + // CHECK: OpSampledImage + return tex.SampleLevel(samp, float2(0.0, 0.0), 0.0); +} + +float4 ReadBuf(Buffer buf, uint index) { + // CHECK: %[[ReadBuf]] = OpFunction %v4float + // CHECK: OpFunctionParameter %[[BufPtrType]] + // CHECK: OpImageFetch + return buf.Load(index); +} + +void WriteBuf(RWBuffer buf, uint index, float4 value) { + // CHECK: %[[WriteBuf]] = OpFunction %void + // CHECK: OpFunctionParameter %[[RWBufPtrType]] + // CHECK: OpImageWrite + buf[index] = value; +} diff --git a/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.gather.hlsl b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.gather.hlsl new file mode 100644 index 0000000000..9b1a720201 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.gather.hlsl @@ -0,0 +1,42 @@ +// RUN: %dxc -T ps_6_6 -E main -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv %s | FileCheck %s + +// Verifies: GatherRed/Green/Blue/Alpha each emit a fresh OpSampledImage that +// reuses the once-loaded heap-sourced texture and sampler handles, selecting +// channel %int_0/1/2/3 via OpImageGather %v4float. + +// CHECK-DAG: %[[UntypedPtr:[a-zA-Z0-9_]+]] = OpTypeUntypedPointerKHR UniformConstant +// CHECK-DAG: %[[Tex2DType:[a-zA-Z0-9_]+]] = OpTypeImage %float 2D 2 0 0 1 Unknown +// CHECK-DAG: %[[SamplerType:[a-zA-Z0-9_]+]] = OpTypeSampler +// CHECK-DAG: %[[SampledImgType:[a-zA-Z0-9_]+]] = OpTypeSampledImage %[[Tex2DType]] +// CHECK-DAG: %[[RA_Tex2D:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[Tex2DType]]{{$}} +// CHECK-DAG: %[[RA_Sampler:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[SamplerType]]{{$}} + +// CHECK-DAG: %[[ResourceHeap:[a-zA-Z0-9_]+]] = OpUntypedVariableKHR %[[UntypedPtr]] UniformConstant +// CHECK-DAG: %[[SamplerHeap:[a-zA-Z0-9_]+]] = OpUntypedVariableKHR %[[UntypedPtr]] UniformConstant + +float4 main(float2 uv : TEXCOORD0) : SV_Target { + Texture2D tex = ResourceDescriptorHeap[0]; + SamplerState samp = SamplerDescriptorHeap[0]; + + // Tex and sampler are each loaded once from the heap; each Gather* creates a + // fresh OpSampledImage but reuses the same loaded image and sampler handles. + // CHECK: %[[TexChain:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[RA_Tex2D]] %[[ResourceHeap]] %uint_0 + // CHECK: %[[TexH:[a-zA-Z0-9_]+]] = OpLoad %[[Tex2DType]] %[[TexChain]] + // CHECK: %[[SampChain:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[RA_Sampler]] %[[SamplerHeap]] %uint_0 + // CHECK: %[[SampH:[a-zA-Z0-9_]+]] = OpLoad %[[SamplerType]] %[[SampChain]] + + // CHECK: %[[SI0:[a-zA-Z0-9_]+]] = OpSampledImage %[[SampledImgType]] %[[TexH]] %[[SampH]] + // CHECK: OpImageGather %v4float %[[SI0]] {{.*}} %int_0 + float4 r = tex.GatherRed(samp, uv); + // CHECK: %[[SI1:[a-zA-Z0-9_]+]] = OpSampledImage %[[SampledImgType]] %[[TexH]] %[[SampH]] + // CHECK: OpImageGather %v4float %[[SI1]] {{.*}} %int_1 + float4 g = tex.GatherGreen(samp, uv); + // CHECK: %[[SI2:[a-zA-Z0-9_]+]] = OpSampledImage %[[SampledImgType]] %[[TexH]] %[[SampH]] + // CHECK: OpImageGather %v4float %[[SI2]] {{.*}} %int_2 + float4 b = tex.GatherBlue(samp, uv); + // CHECK: %[[SI3:[a-zA-Z0-9_]+]] = OpSampledImage %[[SampledImgType]] %[[TexH]] %[[SampH]] + // CHECK: OpImageGather %v4float %[[SI3]] {{.*}} %int_3 + float4 a = tex.GatherAlpha(samp, uv); + + return r + g + b + a; +} diff --git a/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.groupshared.hlsl b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.groupshared.hlsl new file mode 100644 index 0000000000..f68e7b6b59 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.groupshared.hlsl @@ -0,0 +1,35 @@ +// RUN: %dxc -T cs_6_6 -E main -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv %s | FileCheck %s + +// Verifies: heap StructuredBuffer/RWStructuredBuffer coexist with groupshared +// (Workgroup storage class) and GroupMemoryBarrierWithGroupSync. +// 1) groupshared backed by OpTypePointer Workgroup, distinct from heap descriptor pointers. +// 2) GroupMemoryBarrierWithGroupSync lowers to OpControlBarrier alongside heap accesses. +// 3) heap SB/RWSB accessed via OpUntypedAccessChainKHR + OpBufferPointerEXT in same entry point. + +// CHECK-DAG: %[[UntypedPtr:[a-zA-Z0-9_]+]] = OpTypeUntypedPointerKHR UniformConstant +// CHECK-DAG: %[[SBBufDesc:[a-zA-Z0-9_]+]] = OpTypeBufferEXT StorageBuffer +// CHECK-DAG: %[[SBBufArray:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[SBBufDesc]] + +// groupshared lives in Workgroup storage class. +// CHECK-DAG: %[[WorkgroupPtr:[a-zA-Z0-9_]+]] = OpTypePointer Workgroup +// CHECK: %[[ResourceHeap:[a-zA-Z0-9_]+]] = OpUntypedVariableKHR %[[UntypedPtr]] UniformConstant + +groupshared float4 shared_data[64]; + +[numthreads(64, 1, 1)] +void main(uint3 tid : SV_DispatchThreadID, uint gi : SV_GroupIndex) { + StructuredBuffer input = ResourceDescriptorHeap[0]; + RWStructuredBuffer output = ResourceDescriptorHeap[1]; + + // CHECK-DAG: OpUntypedAccessChainKHR %[[UntypedPtr]] %[[SBBufArray]] %[[ResourceHeap]] %uint_0 + // CHECK-DAG: OpUntypedAccessChainKHR %[[UntypedPtr]] %[[SBBufArray]] %[[ResourceHeap]] %uint_1 + shared_data[gi] = input[tid.x]; + + // CHECK: OpControlBarrier + GroupMemoryBarrierWithGroupSync(); + + uint neighbor = (gi + 1) % 64; + + // CHECK: OpBufferPointerEXT + output[tid.x] = shared_data[gi] + shared_data[neighbor]; +} diff --git a/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.load-offset.hlsl b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.load-offset.hlsl new file mode 100644 index 0000000000..a2390ee75e --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.load-offset.hlsl @@ -0,0 +1,27 @@ +// RUN: %dxc -T ps_6_6 -E main -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv %s | FileCheck %s + +// Verifies: Texture.Load with a constant offset emits the ConstOffset +// image operand on OpImageFetch, contrasted against the no-offset fetch baseline. + +// CHECK-DAG: %[[UntypedPtr:[a-zA-Z0-9_]+]] = OpTypeUntypedPointerKHR UniformConstant +// CHECK-DAG: %[[Tex2DType:[a-zA-Z0-9_]+]] = OpTypeImage %float 2D 2 0 0 1 Unknown +// CHECK-DAG: %[[RA_Tex2D:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[Tex2DType]]{{$}} + +float4 main(float4 pos : SV_Position) : SV_Target { + Texture2D tex = ResourceDescriptorHeap[0]; + + // CHECK: OpUntypedAccessChainKHR %[[UntypedPtr]] %[[RA_Tex2D]] %[[ResourceHeap:[a-zA-Z0-9_]+]] %uint_0 + // CHECK: %[[Handle:[a-zA-Z0-9_]+]] = OpLoad %[[Tex2DType]] + + int3 coord = int3(pos.xy, 0); + + // Load without offset. + // CHECK: OpImageFetch %v4float %[[Handle]] + float4 a = tex.Load(coord); + + // Load with constant offset — the ConstOffset image operand must appear. + // CHECK: OpImageFetch %v4float %[[Handle]] {{.*}}ConstOffset + float4 b = tex.Load(coord, int2(1, -1)); + + return a + b; +} diff --git a/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.mixed-bound.hlsl b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.mixed-bound.hlsl new file mode 100644 index 0000000000..25089d9e43 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.mixed-bound.hlsl @@ -0,0 +1,54 @@ +// RUN: %dxc -T ps_6_6 -E main -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv %s | FileCheck %s + +// Verifies: explicitly-bound resources coexist with descriptor-heap resources, and a bound image combines with a heap sampler. +// register(t0) Texture2D -> OpVariable %[[PtrTex]] UniformConstant + DescriptorSet/Binding -> explicitly-bound resource +// ResourceHeap/SamplerHeap -> OpUntypedVariableKHR UniformConstant (BuiltIn ResourceHeapEXT/SamplerHeapEXT) -> heap resources +// bound image + heap sampler -> OpSampledImage %[[BoundVal]] %[[SampH]] -> bound-image/heap-sampler combination + +// CHECK: OpCapability DescriptorHeapEXT +// CHECK-NOT: OpCapability UntypedPointersKHR +// CHECK: OpExtension "SPV_EXT_descriptor_heap" +// CHECK: OpExtension "SPV_KHR_untyped_pointers" + +// CHECK-DAG: OpDecorate %[[ResourceHeap:[a-zA-Z0-9_]+]] BuiltIn ResourceHeapEXT +// CHECK-DAG: OpDecorate %[[SamplerHeap:[a-zA-Z0-9_]+]] BuiltIn SamplerHeapEXT + +// CHECK-DAG: OpDecorate %[[BoundTex:[a-zA-Z0-9_]+]] DescriptorSet +// CHECK-DAG: OpDecorate %[[BoundTex]] Binding + +// CHECK-DAG: %[[UntypedPtr:[a-zA-Z0-9_]+]] = OpTypeUntypedPointerKHR UniformConstant +// CHECK-DAG: %[[Tex2DType:[a-zA-Z0-9_]+]] = OpTypeImage %float 2D 2 0 0 1 Unknown +// CHECK-DAG: %[[SamplerType:[a-zA-Z0-9_]+]] = OpTypeSampler +// CHECK-DAG: %[[RA_Tex2D:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[Tex2DType]]{{$}} +// CHECK-DAG: %[[RA_Sampler:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[SamplerType]]{{$}} +// CHECK-DAG: %[[PtrTex:[a-zA-Z0-9_]+]] = OpTypePointer UniformConstant %[[Tex2DType]] + +// CHECK: %[[BoundTex]] = OpVariable %[[PtrTex]] UniformConstant +// CHECK-DAG: %[[ResourceHeap]] = OpUntypedVariableKHR %[[UntypedPtr]] UniformConstant +// CHECK-DAG: %[[SamplerHeap]] = OpUntypedVariableKHR %[[UntypedPtr]] UniformConstant + +[[vk::binding(0, 0)]] +Texture2D boundTex : register(t0); + +float4 main(float2 uv : TEXCOORD0) : SV_Target { + Texture2D heapTex = ResourceDescriptorHeap[1]; + SamplerState samp = SamplerDescriptorHeap[0]; + + // CHECK: %[[HeapDesc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[RA_Tex2D]] %[[ResourceHeap]] %uint_1 + // CHECK: %[[HeapVal:[a-zA-Z0-9_]+]] = OpLoad %[[Tex2DType]] %[[HeapDesc]] + + // One heap sampler drives both samples, proving heap-sampler reuse across bound and heap images. + // CHECK: %[[SampChain:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[RA_Sampler]] %[[SamplerHeap]] %uint_0 + // CHECK: %[[SampH:[a-zA-Z0-9_]+]] = OpLoad %[[SamplerType]] %[[SampChain]] + + // CHECK: %[[BoundVal:[a-zA-Z0-9_]+]] = OpLoad %[[Tex2DType]] %[[BoundTex]] + + // OWNED: bound image (OpVariable) combined with heap sampler. + // CHECK: OpSampledImage %{{.*}} %[[BoundVal]] %[[SampH]] + float4 a = boundTex.Sample(samp, uv); + + // CHECK: OpSampledImage %{{.*}} %[[HeapVal]] %[[SampH]] + float4 b = heapTex.Sample(samp, uv + 0.5); + + return a + b; +} diff --git a/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.nonuniform.hlsl b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.nonuniform.hlsl new file mode 100644 index 0000000000..591fac84ff --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.nonuniform.hlsl @@ -0,0 +1,30 @@ +// RUN: %dxc -T ps_6_6 -E main -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv %s | FileCheck %s + +// Verifies: NonUniformResourceIndex on descriptor-heap accesses does NOT +// propagate NonUniform to the access-chain result or the loaded value. +// SPV_EXT_descriptor_heap deprecates the NonUniform decoration; drivers handle +// divergent indices natively and the decoration must be omitted. + +// CHECK-DAG: %[[UntypedPtrType:[a-zA-Z0-9_]+]] = OpTypeUntypedPointerKHR UniformConstant +// CHECK-DAG: %[[Tex2DType:[a-zA-Z0-9_]+]] = OpTypeImage %float 2D 2 0 0 1 Unknown +// CHECK-DAG: %[[SamplerType:[a-zA-Z0-9_]+]] = OpTypeSampler +// CHECK-DAG: %[[RA_Tex2DType:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[Tex2DType]] +// CHECK-DAG: %[[RA_SamplerType:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[SamplerType]] + +// CHECK-NOT: OpDecorate %{{.*}} NonUniform + +// CHECK: %[[ResourceHeap:[a-zA-Z0-9_]+]] = OpUntypedVariableKHR %[[UntypedPtrType]] UniformConstant +// CHECK: %[[SamplerHeap:[a-zA-Z0-9_]+]] = OpUntypedVariableKHR %[[UntypedPtrType]] UniformConstant + +float4 main(uint idx : A) : SV_Target { + Texture2D tex = ResourceDescriptorHeap[NonUniformResourceIndex(idx)]; + SamplerState samp = SamplerDescriptorHeap[NonUniformResourceIndex(idx + 1)]; + + // CHECK: %[[TexChain:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtrType]] %[[RA_Tex2DType]] %[[ResourceHeap]] %{{.*}} + // CHECK: %[[TexLoaded:[a-zA-Z0-9_]+]] = OpLoad %[[Tex2DType]] %[[TexChain]] + // CHECK: %[[SampChain:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtrType]] %[[RA_SamplerType]] %[[SamplerHeap]] %{{.*}} + // CHECK: %[[SampLoaded:[a-zA-Z0-9_]+]] = OpLoad %[[SamplerType]] %[[SampChain]] + // CHECK: %[[Combined:[a-zA-Z0-9_]+]] = OpSampledImage %{{.*}} %[[TexLoaded]] %[[SampLoaded]] + // CHECK: OpImageSampleExplicitLod %v4float %[[Combined]] + return tex.SampleLevel(samp, float2(0.0, 0.0), 0.0); +} diff --git a/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.rwbyteaddressbuffer.hlsl b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.rwbyteaddressbuffer.hlsl new file mode 100644 index 0000000000..3e84ff294d --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.rwbyteaddressbuffer.hlsl @@ -0,0 +1,21 @@ +// RUN: %dxc -T cs_6_6 -E main -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv %s | FileCheck %s + +// Verifies: an RWByteAddressBuffer sourced from the descriptor heap +// produces the typed StorageBuffer pointer %type_RWByteAddressBuffer +// and a read-WRITE OpBufferPointerEXT through it. + +// CHECK-DAG: %[[UntypedPtr:[a-zA-Z0-9_]+]] = OpTypeUntypedPointerKHR UniformConstant +// CHECK-DAG: %[[SBBufDesc:[a-zA-Z0-9_]+]] = OpTypeBufferEXT StorageBuffer +// CHECK-DAG: %[[SBBufArray:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[SBBufDesc]] +// CHECK-DAG: %[[RWBABPtr:[a-zA-Z0-9_]+]] = OpTypePointer StorageBuffer %type_RWByteAddressBuffer +// CHECK: %[[ResourceHeap:[a-zA-Z0-9_]+]] = OpUntypedVariableKHR %[[UntypedPtr]] UniformConstant + +[numthreads(64, 1, 1)] +void main(uint3 tid : SV_DispatchThreadID) { + RWByteAddressBuffer buf = ResourceDescriptorHeap[0]; + + // CHECK: %[[Desc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[SBBufArray]] %[[ResourceHeap]] %uint_0 + // CHECK: OpBufferPointerEXT %[[RWBABPtr]] %[[Desc]] + uint val = buf.Load(tid.x * 4); + buf.Store(tid.x * 4 + 256, val + 1); +} diff --git a/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.rwtexture-atomics.hlsl b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.rwtexture-atomics.hlsl new file mode 100644 index 0000000000..6937f828ef --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.rwtexture-atomics.hlsl @@ -0,0 +1,53 @@ +// RUN: %dxc -T cs_6_6 -E main -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv %s | FileCheck %s + +// Verifies: InterlockedAdd on a heap-sourced RWTexture2D lowers +// to OpUntypedImageTexelPointerEXT (not OpImageTexelPointer) +// feeding OpAtomicIAdd, and reassignment targets the new descriptor. +// +// RWTexture2D heap access -> OpUntypedAccessChainKHR -> descriptor pointer in resource heap +// atomic texel pointer -> OpUntypedImageTexelPointerEXT -> EXT untyped image texel pointer +// InterlockedAdd -> OpAtomicIAdd -> image atomic on untyped texel pointer +// reassignment -> OpUntypedAccessChainKHR %uint_3 -> atomic uses new descriptor index + +// CHECK-DAG: %[[UntypedUniformConstant:[a-zA-Z0-9_]+]] = OpTypeUntypedPointerKHR UniformConstant +// CHECK-DAG: %[[RWTexType:[a-zA-Z0-9_]+]] = OpTypeImage %uint 2D 2 0 0 2 R32ui +// CHECK-DAG: %[[RWTexArray:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[RWTexType]] +// CHECK-DAG: %[[UntypedImage:[a-zA-Z0-9_]+]] = OpTypeUntypedPointerKHR Image + +// CHECK: %[[ResourceHeap:[a-zA-Z0-9_]+]] = OpUntypedVariableKHR %[[UntypedUniformConstant]] UniformConstant + +RWByteAddressBuffer outputBytes : register(u0); + +[numthreads(1, 1, 1)] +void main(uint3 tid : SV_DispatchThreadID) { + RWTexture2D tex = ResourceDescriptorHeap[0]; + + uint original; + // CHECK: %[[Desc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedUniformConstant]] %[[RWTexArray]] %[[ResourceHeap]] %uint_0 + // CHECK-NOT: OpImageTexelPointer + // CHECK: %[[TexelPtr:[a-zA-Z0-9_]+]] = OpUntypedImageTexelPointerEXT %[[UntypedImage]] %[[RWTexType]] %[[Desc]] + // CHECK: OpAtomicIAdd %uint %[[TexelPtr]] + InterlockedAdd(tex[tid.xy], 1, original); + + uint directOriginal; + // CHECK: %[[DirectDesc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedUniformConstant]] %[[RWTexArray]] %[[ResourceHeap]] %uint_1 + // CHECK-NOT: OpImageTexelPointer + // CHECK: %[[DirectTexelPtr:[a-zA-Z0-9_]+]] = OpUntypedImageTexelPointerEXT %[[UntypedImage]] %[[RWTexType]] %[[DirectDesc]] + // CHECK: OpAtomicIAdd %uint %[[DirectTexelPtr]] + InterlockedAdd(((RWTexture2D)ResourceDescriptorHeap[1])[tid.xy], 2, + directOriginal); + + // Reassignment: atomic must use the NEW descriptor (index 3, not 2). + RWTexture2D reassigned = ResourceDescriptorHeap[2]; + reassigned = ResourceDescriptorHeap[3]; + uint reassignedOriginal; + // CHECK: %[[ReassignDesc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedUniformConstant]] %[[RWTexArray]] %[[ResourceHeap]] %uint_3 + // CHECK-NOT: OpImageTexelPointer + // CHECK: %[[ReassignTexelPtr:[a-zA-Z0-9_]+]] = OpUntypedImageTexelPointerEXT %[[UntypedImage]] %[[RWTexType]] %[[ReassignDesc]] + // CHECK: OpAtomicIAdd %uint %[[ReassignTexelPtr]] + InterlockedAdd(reassigned[tid.xy], 3, reassignedOriginal); + + outputBytes.Store(0, original); + outputBytes.Store(4, directOriginal); + outputBytes.Store(8, reassignedOriginal); +} diff --git a/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.rwtexture-dims.hlsl b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.rwtexture-dims.hlsl new file mode 100644 index 0000000000..87d2591318 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.rwtexture-dims.hlsl @@ -0,0 +1,53 @@ +// RUN: %dxc -T cs_6_6 -E main -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv %s | FileCheck %s + +// Verifies: storage RW texture dimensionality lowers to distinct +// OpTypeImage dim/flags, each driving OpImageRead/OpImageWrite. +// +// RWTexture1D -> OpTypeImage %float 1D 2 0 0 2 Rgba32f -> storage +// RWTexture1DArray -> OpTypeImage %float 1D 2 1 0 2 Rgba32f -> storage +// RWTexture2DArray -> OpTypeImage %float 2D 2 1 0 2 Rgba32f -> storage +// RWTexture3D -> OpTypeImage %float 3D 2 0 0 2 Rgba32f -> storage + +// CHECK-DAG: %[[UntypedPtr:[a-zA-Z0-9_]+]] = OpTypeUntypedPointerKHR UniformConstant +// CHECK-DAG: %[[RW1DType:[a-zA-Z0-9_]+]] = OpTypeImage %float 1D 2 0 0 2 Rgba32f +// CHECK-DAG: %[[RW1DArrType:[a-zA-Z0-9_]+]] = OpTypeImage %float 1D 2 1 0 2 Rgba32f +// CHECK-DAG: %[[RW2DArrType:[a-zA-Z0-9_]+]] = OpTypeImage %float 2D 2 1 0 2 Rgba32f +// CHECK-DAG: %[[RW3DType:[a-zA-Z0-9_]+]] = OpTypeImage %float 3D 2 0 0 2 Rgba32f + +// CHECK-DAG: %[[RA_RW1D:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[RW1DType]]{{$}} +// CHECK-DAG: %[[RA_RW1DArr:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[RW1DArrType]]{{$}} +// CHECK-DAG: %[[RA_RW2DArr:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[RW2DArrType]]{{$}} +// CHECK-DAG: %[[RA_RW3D:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[RW3DType]]{{$}} + +// CHECK: %[[ResourceHeap:[a-zA-Z0-9_]+]] = OpUntypedVariableKHR %[[UntypedPtr]] UniformConstant + +[numthreads(1, 1, 1)] +void main(uint3 tid : SV_DispatchThreadID) { + RWTexture1D rwTex1d = ResourceDescriptorHeap[0]; + // CHECK: %[[RW1D_Desc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[RA_RW1D]] %[[ResourceHeap]] %uint_0 + // CHECK: %[[RW1D:[a-zA-Z0-9_]+]] = OpLoad %[[RW1DType]] %[[RW1D_Desc]] + + RWTexture1DArray rwTex1dArr = ResourceDescriptorHeap[1]; + // CHECK: %[[RW1DA_Desc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[RA_RW1DArr]] %[[ResourceHeap]] %uint_1 + // CHECK: %[[RW1DA:[a-zA-Z0-9_]+]] = OpLoad %[[RW1DArrType]] %[[RW1DA_Desc]] + + RWTexture2DArray rwTex2dArr = ResourceDescriptorHeap[2]; + // CHECK: %[[RW2DA_Desc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[RA_RW2DArr]] %[[ResourceHeap]] %uint_2 + // CHECK: %[[RW2DA:[a-zA-Z0-9_]+]] = OpLoad %[[RW2DArrType]] %[[RW2DA_Desc]] + + RWTexture3D rwTex3d = ResourceDescriptorHeap[3]; + // CHECK: %[[RW3D_Desc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[RA_RW3D]] %[[ResourceHeap]] %uint_3 + // CHECK: %[[RW3D:[a-zA-Z0-9_]+]] = OpLoad %[[RW3DType]] %[[RW3D_Desc]] + + // CHECK: OpImageRead %v4float %[[RW1D]] + float4 v = rwTex1d[tid.x]; + + // CHECK: OpImageWrite %[[RW1DA]] + rwTex1dArr[uint2(tid.x, 0)] = v; + + // CHECK: OpImageWrite %[[RW2DA]] + rwTex2dArr[uint3(tid.xy, 0)] = v; + + // CHECK: OpImageWrite %[[RW3D]] + rwTex3d[tid.xyz] = v; +} diff --git a/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.sample-grad-bias.hlsl b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.sample-grad-bias.hlsl new file mode 100644 index 0000000000..756dd53e2d --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.sample-grad-bias.hlsl @@ -0,0 +1,40 @@ +// RUN: %dxc -T ps_6_6 -E main -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv %s | FileCheck %s + +// Verifies: SampleGrad lowers to OpImageSampleExplicitLod with the Grad image +// operand and SampleBias lowers to OpImageSampleImplicitLod with the Bias +// image operand, where the texture and sampler handles are loaded once from +// the descriptor heaps and reused across both samples (each sample a fresh +// OpSampledImage). + +// Capture anchors only (heap-source decorations owned by mixed-bound). +// CHECK-DAG: OpDecorate %[[ResourceHeap:[a-zA-Z0-9_]+]] BuiltIn ResourceHeapEXT +// CHECK-DAG: OpDecorate %[[SamplerHeap:[a-zA-Z0-9_]+]] BuiltIn SamplerHeapEXT + +// CHECK-DAG: %[[UntypedPtr:[a-zA-Z0-9_]+]] = OpTypeUntypedPointerKHR UniformConstant +// CHECK-DAG: %[[Tex2DType:[a-zA-Z0-9_]+]] = OpTypeImage %float 2D 2 0 0 1 Unknown +// CHECK-DAG: %[[SamplerType:[a-zA-Z0-9_]+]] = OpTypeSampler +// CHECK-DAG: %[[SampledImgType:[a-zA-Z0-9_]+]] = OpTypeSampledImage %[[Tex2DType]] +// CHECK-DAG: %[[RA_Tex2D:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[Tex2DType]]{{$}} +// CHECK-DAG: %[[RA_Sampler:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[SamplerType]]{{$}} + +float4 main(float2 uv : TEXCOORD0) : SV_Target { + Texture2D tex = ResourceDescriptorHeap[0]; + SamplerState samp = SamplerDescriptorHeap[0]; + + // Tex and sampler each loaded once; each sample op creates its own OpSampledImage + // but both reuse the same loaded handles. + // CHECK: %[[TexChain:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[RA_Tex2D]] %[[ResourceHeap]] %uint_0 + // CHECK: %[[TexH:[a-zA-Z0-9_]+]] = OpLoad %[[Tex2DType]] %[[TexChain]] + // CHECK: %[[SampChain:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[RA_Sampler]] %[[SamplerHeap]] %uint_0 + // CHECK: %[[SampH:[a-zA-Z0-9_]+]] = OpLoad %[[SamplerType]] %[[SampChain]] + + // CHECK: %[[SI0:[a-zA-Z0-9_]+]] = OpSampledImage %[[SampledImgType]] %[[TexH]] %[[SampH]] + // CHECK: OpImageSampleExplicitLod %v4float %[[SI0]] {{.*}} Grad + float4 a = tex.SampleGrad(samp, uv, ddx(uv), ddy(uv)); + + // CHECK: %[[SI1:[a-zA-Z0-9_]+]] = OpSampledImage %[[SampledImgType]] %[[TexH]] %[[SampH]] + // CHECK: OpImageSampleImplicitLod %v4float %[[SI1]] {{.*}} Bias + float4 b = tex.SampleBias(samp, uv, -1.0); + + return a + b; +} diff --git a/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.sampler-comparison.hlsl b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.sampler-comparison.hlsl new file mode 100644 index 0000000000..f5a118715e --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.sampler-comparison.hlsl @@ -0,0 +1,30 @@ +// RUN: %dxc -T ps_6_6 -E main -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv %s | FileCheck %s + +// Verifies: a heap-sourced SamplerComparisonState combined with a heap-sourced +// Texture2D drives OpImageSampleDrefExplicitLod (SampleCmpLevelZero), +// with the comparison sampler loaded from the sampler heap and joined +// via OpSampledImage. + +// CHECK-DAG: %[[UntypedPtr:[a-zA-Z0-9_]+]] = OpTypeUntypedPointerKHR UniformConstant +// CHECK-DAG: %[[TexType:[a-zA-Z0-9_]+]] = OpTypeImage %float 2D 2 0 0 1 Unknown +// CHECK-DAG: %[[SamplerType:[a-zA-Z0-9_]+]] = OpTypeSampler +// CHECK-DAG: %[[RA_Tex:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[TexType]]{{$}} +// CHECK-DAG: %[[RA_Sampler:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[SamplerType]]{{$}} + +// CHECK: %[[ResourceHeap:[a-zA-Z0-9_]+]] = OpUntypedVariableKHR %[[UntypedPtr]] UniformConstant +// CHECK: %[[SamplerHeap:[a-zA-Z0-9_]+]] = OpUntypedVariableKHR %[[UntypedPtr]] UniformConstant + +float4 main(float2 uv : TEXCOORD0) : SV_Target { + Texture2D depthTex = ResourceDescriptorHeap[0]; + // CHECK: %[[TexDesc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[RA_Tex]] %[[ResourceHeap]] %uint_0 + // CHECK: %[[TexHandle:[a-zA-Z0-9_]+]] = OpLoad %[[TexType]] %[[TexDesc]] + + SamplerComparisonState shadowSamp = SamplerDescriptorHeap[0]; + // CHECK: %[[SampDesc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[RA_Sampler]] %[[SamplerHeap]] %uint_0 + // CHECK: %[[SampHandle:[a-zA-Z0-9_]+]] = OpLoad %[[SamplerType]] %[[SampDesc]] + + // CHECK: %[[Combined:[a-zA-Z0-9_]+]] = OpSampledImage %{{[a-zA-Z0-9_]+}} %[[TexHandle]] %[[SampHandle]] + // CHECK: %[[Shadow:[a-zA-Z0-9_]+]] = OpImageSampleDrefExplicitLod %float %[[Combined]] + float shadow = depthTex.SampleCmpLevelZero(shadowSamp, uv, 0.5); + return float4(shadow, shadow, shadow, 1.0); +} diff --git a/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.static-global.hlsl b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.static-global.hlsl new file mode 100644 index 0000000000..e12cf41076 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.static-global.hlsl @@ -0,0 +1,64 @@ +// RUN: %dxc -T cs_6_6 -E main -Od -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv %s | FileCheck %s + +// Verifies: UE bindless idiom — static const resources at global scope +// initialized from dynamic runtime uint indices ($Globals), across +// Texture2D / StructuredBuffer / RWTexture2D, plus a literal-index +// SamplerState. + +// CHECK-DAG: %[[UntypedPtr:[a-zA-Z0-9_]+]] = OpTypeUntypedPointerKHR UniformConstant +// CHECK-DAG: %[[Tex2DType:[a-zA-Z0-9_]+]] = OpTypeImage %float 2D 2 0 0 1 Unknown +// CHECK-DAG: %[[SamplerType:[a-zA-Z0-9_]+]] = OpTypeSampler +// CHECK-DAG: %[[SBBufDesc:[a-zA-Z0-9_]+]] = OpTypeBufferEXT StorageBuffer +// CHECK-DAG: %[[RWTex2DType:[a-zA-Z0-9_]+]] = OpTypeImage %float 2D 2 0 0 2 Rgba32f + +// CHECK-DAG: %[[RA_Tex2D:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[Tex2DType]]{{$}} +// CHECK-DAG: %[[RA_Sampler:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[SamplerType]]{{$}} +// CHECK-DAG: %[[RA_SBBuf:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[SBBufDesc]]{{$}} +// CHECK-DAG: %[[RA_RWTex2D:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[RWTex2DType]]{{$}} + +// CHECK-DAG: %[[ResourceHeap:[a-zA-Z0-9_]+]] = OpUntypedVariableKHR %[[UntypedPtr]] UniformConstant +// CHECK-DAG: %[[SamplerHeap:[a-zA-Z0-9_]+]] = OpUntypedVariableKHR %[[UntypedPtr]] UniformConstant + +// Runtime uint indices (placed in $Globals cbuffer by DXC). +uint BindlessSRV_ColorTex; +uint BindlessSRV_DataBuf; +uint BindlessUAV_OutTex; + +// UE bindless pattern: static const from runtime heap index. +static const Texture2D ColorTex = ResourceDescriptorHeap[BindlessSRV_ColorTex]; +static const StructuredBuffer DataBuf = ResourceDescriptorHeap[BindlessSRV_DataBuf]; +static const RWTexture2D OutTex = ResourceDescriptorHeap[BindlessUAV_OutTex]; + +// Literal-index sampler (common UE pattern for global bilinear/point samplers). +static const SamplerState BilinearSamp = SamplerDescriptorHeap[2]; + +[numthreads(8, 8, 1)] +void main(uint3 tid : SV_DispatchThreadID) { + // Heap indices are dynamic (loaded from $Globals), not literal constants. + // CHECK: %[[TexIdx:[a-zA-Z0-9_]+]] = OpLoad %uint + // CHECK: %[[TexDesc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[RA_Tex2D]] %[[ResourceHeap]] %[[TexIdx]] + // CHECK: OpLoad %[[Tex2DType]] %[[TexDesc]] + + // CHECK: %[[BufIdx:[a-zA-Z0-9_]+]] = OpLoad %uint + // CHECK: OpUntypedAccessChainKHR %[[UntypedPtr]] %[[RA_SBBuf]] %[[ResourceHeap]] %[[BufIdx]] + // CHECK: OpBufferPointerEXT + + // CHECK: %[[OutIdx:[a-zA-Z0-9_]+]] = OpLoad %uint + // CHECK: %[[OutDesc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[RA_RWTex2D]] %[[ResourceHeap]] %[[OutIdx]] + // CHECK: OpLoad %[[RWTex2DType]] %[[OutDesc]] + + // Literal sampler index. + // CHECK: OpUntypedAccessChainKHR %[[UntypedPtr]] %[[RA_Sampler]] %[[SamplerHeap]] %uint_2 + // CHECK: OpLoad %[[SamplerType]] + + float2 uv = float2(tid.xy) / 512.0; + + // CHECK: OpSampledImage + // CHECK: OpImageSampleExplicitLod + float4 color = ColorTex.SampleLevel(BilinearSamp, uv, 0); + + float4 data = DataBuf[tid.x]; + + // CHECK: OpImageWrite %{{[a-zA-Z0-9_]+}} + OutTex[tid.xy] = color + data; +} diff --git a/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.structured-buffer-atomic.hlsl b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.structured-buffer-atomic.hlsl new file mode 100644 index 0000000000..bbf811e6ca --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.structured-buffer-atomic.hlsl @@ -0,0 +1,28 @@ +// RUN: %dxc -T cs_6_6 -E main -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv %s | FileCheck %s + +// Verifies: BUFFER atomics on a heap-sourced RWStructuredBuffer +// lower through a heap access-chain + OpBufferPointerEXT into +// OpAtomicIAdd/OpAtomicCompareExchange on %uint. +// +// RWStructuredBuffer -> heap access-chain + OpBufferPointerEXT -> OpAtomicIAdd %uint +// RWStructuredBuffer -> heap access-chain + OpBufferPointerEXT -> OpAtomicCompareExchange %uint + +// CHECK-DAG: %[[UntypedPtr:[a-zA-Z0-9_]+]] = OpTypeUntypedPointerKHR UniformConstant +// CHECK-DAG: %[[SBBufDesc:[a-zA-Z0-9_]+]] = OpTypeBufferEXT StorageBuffer +// CHECK-DAG: %[[SBBufArray:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[SBBufDesc]] +// CHECK: %[[ResourceHeap:[a-zA-Z0-9_]+]] = OpUntypedVariableKHR %[[UntypedPtr]] UniformConstant + +[numthreads(64, 1, 1)] +void main(uint3 tid : SV_DispatchThreadID) { + RWStructuredBuffer counter = ResourceDescriptorHeap[0]; + + // CHECK: %[[Desc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[SBBufArray]] %[[ResourceHeap]] %uint_0 + // CHECK: OpBufferPointerEXT + uint original; + // CHECK: OpAtomicIAdd %uint + InterlockedAdd(counter[0], 1, original); + + // CHECK: OpAtomicCompareExchange %uint + uint cmp; + InterlockedCompareExchange(counter[1], original, original + 1, cmp); +} diff --git a/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.texture-dims.hlsl b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.texture-dims.hlsl new file mode 100644 index 0000000000..120830aa4c --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.texture-dims.hlsl @@ -0,0 +1,54 @@ +// RUN: %dxc -T cs_6_6 -E main -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv %s | FileCheck %s + +// Verifies: sampled (non-MS, non-cube) texture dimensionalities lower +// to the correct OpTypeImage dim flags and feed OpImageFetch. +// +// Texture1D -> OpTypeImage %float 1D 2 0 0 1 Unknown -> sampled +// Texture1DArray -> OpTypeImage %float 1D 2 1 0 1 Unknown -> sampled +// Texture2DArray -> OpTypeImage %float 2D 2 1 0 1 Unknown -> sampled +// Texture3D -> OpTypeImage %float 3D 2 0 0 1 Unknown -> sampled + +// CHECK-DAG: %[[UntypedPtr:[a-zA-Z0-9_]+]] = OpTypeUntypedPointerKHR UniformConstant +// CHECK-DAG: %[[Tex1DType:[a-zA-Z0-9_]+]] = OpTypeImage %float 1D 2 0 0 1 Unknown +// CHECK-DAG: %[[Tex1DArrType:[a-zA-Z0-9_]+]] = OpTypeImage %float 1D 2 1 0 1 Unknown +// CHECK-DAG: %[[Tex2DArrType:[a-zA-Z0-9_]+]] = OpTypeImage %float 2D 2 1 0 1 Unknown +// CHECK-DAG: %[[Tex3DType:[a-zA-Z0-9_]+]] = OpTypeImage %float 3D 2 0 0 1 Unknown + +// CHECK-DAG: %[[RA_Tex1D:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[Tex1DType]]{{$}} +// CHECK-DAG: %[[RA_Tex1DArr:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[Tex1DArrType]]{{$}} +// CHECK-DAG: %[[RA_Tex2DArr:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[Tex2DArrType]]{{$}} +// CHECK-DAG: %[[RA_Tex3D:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[Tex3DType]]{{$}} + +// CHECK: %[[ResourceHeap:[a-zA-Z0-9_]+]] = OpUntypedVariableKHR %[[UntypedPtr]] UniformConstant + +RWByteAddressBuffer output : register(u0); + +[numthreads(1, 1, 1)] +void main(uint3 tid : SV_DispatchThreadID) { + Texture1D tex1d = ResourceDescriptorHeap[0]; + // CHECK: %[[T1D_Desc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[RA_Tex1D]] %[[ResourceHeap]] %uint_0 + // CHECK: %[[T1D:[a-zA-Z0-9_]+]] = OpLoad %[[Tex1DType]] %[[T1D_Desc]] + + Texture1DArray tex1dArr = ResourceDescriptorHeap[1]; + // CHECK: %[[T1DA_Desc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[RA_Tex1DArr]] %[[ResourceHeap]] %uint_1 + // CHECK: %[[T1DA:[a-zA-Z0-9_]+]] = OpLoad %[[Tex1DArrType]] %[[T1DA_Desc]] + + Texture2DArray tex2dArr = ResourceDescriptorHeap[2]; + // CHECK: %[[T2DA_Desc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[RA_Tex2DArr]] %[[ResourceHeap]] %uint_2 + // CHECK: %[[T2DA:[a-zA-Z0-9_]+]] = OpLoad %[[Tex2DArrType]] %[[T2DA_Desc]] + + Texture3D tex3d = ResourceDescriptorHeap[3]; + // CHECK: %[[T3D_Desc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[RA_Tex3D]] %[[ResourceHeap]] %uint_3 + // CHECK: %[[T3D:[a-zA-Z0-9_]+]] = OpLoad %[[Tex3DType]] %[[T3D_Desc]] + + // CHECK: OpImageFetch %v4float %[[T1D]] + float4 v = tex1d.Load(int2(tid.x, 0)); + // CHECK: OpImageFetch %v4float %[[T1DA]] + v += tex1dArr.Load(int3(tid.x, 0, 0)); + // CHECK: OpImageFetch %v4float %[[T2DA]] + v += tex2dArr.Load(int4(tid.xy, 0, 0)); + // CHECK: OpImageFetch %v4float %[[T3D]] + v += tex3d.Load(int4(tid.xyz, 0)); + + output.Store(0, asuint(v.x)); +} diff --git a/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.texture-ms.hlsl b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.texture-ms.hlsl new file mode 100644 index 0000000000..edb2d78821 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.texture-ms.hlsl @@ -0,0 +1,33 @@ +// RUN: %dxc -T cs_6_6 -E main -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv %s | FileCheck %s + +// Verifies: multisampled image types (Texture2DMS `%float 2D 2 0 1 1` and Texture2DMSArray `2D 2 1 1 1`) +// sourced from the descriptor heap are loaded and drive OpImageFetch %v4float. + +// CHECK-DAG: %[[UntypedPtr:[a-zA-Z0-9_]+]] = OpTypeUntypedPointerKHR UniformConstant +// CHECK-DAG: %[[MSType:[a-zA-Z0-9_]+]] = OpTypeImage %float 2D 2 0 1 1 Unknown +// CHECK-DAG: %[[MSArrType:[a-zA-Z0-9_]+]] = OpTypeImage %float 2D 2 1 1 1 Unknown + +// CHECK-DAG: %[[RA_MS:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[MSType]]{{$}} +// CHECK-DAG: %[[RA_MSArr:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[MSArrType]]{{$}} + +// CHECK: %[[ResourceHeap:[a-zA-Z0-9_]+]] = OpUntypedVariableKHR %[[UntypedPtr]] UniformConstant + +RWByteAddressBuffer output : register(u0); + +[numthreads(1, 1, 1)] +void main(uint3 tid : SV_DispatchThreadID) { + Texture2DMS texMS = ResourceDescriptorHeap[0]; + // CHECK: %[[MS_Desc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[RA_MS]] %[[ResourceHeap]] %uint_0 + // CHECK: %[[MS:[a-zA-Z0-9_]+]] = OpLoad %[[MSType]] %[[MS_Desc]] + + Texture2DMSArray texMSArr = ResourceDescriptorHeap[1]; + // CHECK: %[[MSArr_Desc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[RA_MSArr]] %[[ResourceHeap]] %uint_1 + // CHECK: %[[MSArr:[a-zA-Z0-9_]+]] = OpLoad %[[MSArrType]] %[[MSArr_Desc]] + + // CHECK: OpImageFetch %v4float %[[MS]] + float4 v = texMS.Load(int2(tid.xy), 0); + // CHECK: OpImageFetch %v4float %[[MSArr]] + v += texMSArr.Load(int3(tid.xy, 0), 0); + + output.Store(0, asuint(v.x)); +} diff --git a/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.texture-sampler-assignment.hlsl b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.texture-sampler-assignment.hlsl new file mode 100644 index 0000000000..6b4297741e --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.texture-sampler-assignment.hlsl @@ -0,0 +1,77 @@ +// RUN: %dxc -T ps_6_6 -E PSMain -Od -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv %s | FileCheck %s + +// Verifies: reassigning both a Texture2D and a SamplerState across a +// dynamic index, a +2 index, and inside a conditional branch reloads +// the latest texture+sampler from BOTH the resource and sampler +// heaps per reassignment, producing a fresh +// OpSampledImage/OpImageSampleImplicitLod each time. + +// CHECK-DAG: %[[UntypedPtr:[a-zA-Z0-9_]+]] = OpTypeUntypedPointerKHR UniformConstant +// CHECK-DAG: %[[Tex2D:[a-zA-Z0-9_]+]] = OpTypeImage %float 2D 2 0 0 1 Unknown +// CHECK-DAG: %[[Sampler:[a-zA-Z0-9_]+]] = OpTypeSampler +// CHECK-DAG: %[[TexArray:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[Tex2D]] +// CHECK-DAG: %[[SamplerArray:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[Sampler]] +// CHECK-DAG: %[[SampledImage:[a-zA-Z0-9_]+]] = OpTypeSampledImage %[[Tex2D]] + +// CHECK: %[[ResourceHeap:[a-zA-Z0-9_]+]] = OpUntypedVariableKHR %[[UntypedPtr]] UniformConstant +// CHECK: %[[SamplerHeap:[a-zA-Z0-9_]+]] = OpUntypedVariableKHR %[[UntypedPtr]] UniformConstant + +struct PSInput +{ + float4 position : SV_Position; + float4 color : COLOR0; +}; + +float4 PSMain(PSInput input) : SV_Target0 +{ + uint texIdx = uint(input.color.x); + uint sampIdx = uint(input.color.y); + uint cond = uint(input.color.z); + + Texture2D myTexture = ResourceDescriptorHeap[texIdx]; + SamplerState samp = SamplerDescriptorHeap[sampIdx]; + + // CHECK: %[[TexIdx:[a-zA-Z0-9_]+]] = OpConvertFToU %uint + // CHECK: %[[SampIdx:[a-zA-Z0-9_]+]] = OpConvertFToU %uint + // CHECK: %[[Cond:[a-zA-Z0-9_]+]] = OpConvertFToU %uint + // CHECK: %[[TexDesc0:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[TexArray]] %[[ResourceHeap]] %[[TexIdx]] + // CHECK: %[[Tex0:[a-zA-Z0-9_]+]] = OpLoad %[[Tex2D]] %[[TexDesc0]] + // CHECK: %[[SampDesc0:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[SamplerArray]] %[[SamplerHeap]] %[[SampIdx]] + // CHECK: %[[Samp0:[a-zA-Z0-9_]+]] = OpLoad %[[Sampler]] %[[SampDesc0]] + // CHECK: %[[Sampled0:[a-zA-Z0-9_]+]] = OpSampledImage %[[SampledImage]] %[[Tex0]] %[[Samp0]] + // CHECK: %[[Color0:[a-zA-Z0-9_]+]] = OpImageSampleImplicitLod %v4float %[[Sampled0]] + float4 color = myTexture.Sample(samp, float2(1,1)); + + myTexture = ResourceDescriptorHeap[texIdx + 2]; + samp = SamplerDescriptorHeap[sampIdx]; + + // CHECK: %[[TexIdxPlus2:[a-zA-Z0-9_]+]] = OpIAdd %uint %[[TexIdx]] %uint_2 + // CHECK: %[[TexDesc1:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[TexArray]] %[[ResourceHeap]] %[[TexIdxPlus2]] + // CHECK: %[[Tex1:[a-zA-Z0-9_]+]] = OpLoad %[[Tex2D]] %[[TexDesc1]] + // CHECK: %[[SampDesc1:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[SamplerArray]] %[[SamplerHeap]] %[[SampIdx]] + // CHECK: %[[Samp1:[a-zA-Z0-9_]+]] = OpLoad %[[Sampler]] %[[SampDesc1]] + // CHECK: %[[Sampled1:[a-zA-Z0-9_]+]] = OpSampledImage %[[SampledImage]] %[[Tex1]] %[[Samp1]] + // CHECK: %[[Color1:[a-zA-Z0-9_]+]] = OpImageSampleImplicitLod %v4float %[[Sampled1]] + // CHECK: %[[ColorSum:[a-zA-Z0-9_]+]] = OpFAdd %v4float %[[Color0]] %[[Color1]] + color += myTexture.Sample(samp, float2(2,2)); + + if (cond > 4) { + myTexture = ResourceDescriptorHeap[texIdx - 2]; + samp = SamplerDescriptorHeap[sampIdx + 2]; + + // CHECK: %[[CondCmp:[a-zA-Z0-9_]+]] = OpUGreaterThan %bool %[[Cond]] %uint_4 + // CHECK: OpBranchConditional %[[CondCmp]] + // CHECK: %[[TexIdxMinus2:[a-zA-Z0-9_]+]] = OpISub %uint %[[TexIdx]] %uint_2 + // CHECK: %[[TexDesc2:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[TexArray]] %[[ResourceHeap]] %[[TexIdxMinus2]] + // CHECK: %[[Tex2:[a-zA-Z0-9_]+]] = OpLoad %[[Tex2D]] %[[TexDesc2]] + // CHECK: %[[SampIdxPlus2:[a-zA-Z0-9_]+]] = OpIAdd %uint %[[SampIdx]] %uint_2 + // CHECK: %[[SampDesc2:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[SamplerArray]] %[[SamplerHeap]] %[[SampIdxPlus2]] + // CHECK: %[[Samp2:[a-zA-Z0-9_]+]] = OpLoad %[[Sampler]] %[[SampDesc2]] + // CHECK: %[[Sampled2:[a-zA-Z0-9_]+]] = OpSampledImage %[[SampledImage]] %[[Tex2]] %[[Samp2]] + // CHECK: %[[Color2:[a-zA-Z0-9_]+]] = OpImageSampleImplicitLod %v4float %[[Sampled2]] + // CHECK: OpFAdd %v4float %[[ColorSum]] %[[Color2]] + color += myTexture.Sample(samp, float2(2,2)); + } + + return color; +} diff --git a/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.texture.hlsl b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.texture.hlsl new file mode 100644 index 0000000000..6d769383f6 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.texture.hlsl @@ -0,0 +1,34 @@ +// RUN: %dxc -T cs_6_6 -E main -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv %s | FileCheck %s + +// Verifies: Buffer and RWBuffer from the heap lower to +// Buffer-dimension OpTypeImage (sampled vs storage), +// load a typed image handle, and drive OpImageFetch / OpImageWrite. +// +// Buffer -> OpTypeImage %float Buffer ... Sampled(1), handle OpLoad, OpImageFetch (Load) +// RWBuffer -> OpTypeImage %float Buffer ... Storage(2), handle OpLoad, OpImageWrite (store) + +// CHECK-DAG: %[[UntypedPtrType:[a-zA-Z0-9_]+]] = OpTypeUntypedPointerKHR UniformConstant +// CHECK-DAG: %[[BufferType:[a-zA-Z0-9_]+]] = OpTypeImage %float Buffer 2 0 0 1 Rgba32f +// CHECK-DAG: %[[RWBufferType:[a-zA-Z0-9_]+]] = OpTypeImage %float Buffer 2 0 0 2 Rgba32f + +// CHECK-DAG: %[[RA_BufferType:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[BufferType]]{{$}} +// CHECK-DAG: %[[RA_RWBufferType:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[RWBufferType]]{{$}} + +// CHECK: %[[ResourceHeap:[a-zA-Z0-9_]+]] = OpUntypedVariableKHR %[[UntypedPtrType]] UniformConstant + +[numthreads(1, 1, 1)] +void main(uint3 tid : SV_DispatchThreadID) { + Buffer myBuf = ResourceDescriptorHeap[0]; + // CHECK: %[[BufIndex:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtrType]] %[[RA_BufferType]] %[[ResourceHeap]] %uint_0 + // CHECK: %[[BufHandle:[a-zA-Z0-9_]+]] = OpLoad %[[BufferType]] %[[BufIndex]] + + RWBuffer myRWBuf = ResourceDescriptorHeap[1]; + // CHECK: %[[RWBufIndex:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtrType]] %[[RA_RWBufferType]] %[[ResourceHeap]] %uint_1 + // CHECK: %[[RWBufHandle:[a-zA-Z0-9_]+]] = OpLoad %[[RWBufferType]] %[[RWBufIndex]] + + // CHECK: %[[BufResult:[a-zA-Z0-9_]+]] = OpImageFetch %v4float %[[BufHandle]] + float4 bufVal = myBuf.Load(tid.x); + + // CHECK: OpImageWrite %[[RWBufHandle]] + myRWBuf[tid.x] = bufVal; +} diff --git a/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.texturecube.hlsl b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.texturecube.hlsl new file mode 100644 index 0000000000..237fed4aa9 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.texturecube.hlsl @@ -0,0 +1,46 @@ +// RUN: %dxc -T cs_6_6 -E main -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv %s | FileCheck %s + +// Verifies: TextureCube and TextureCubeArray cube image types, +// each loaded from the resource heap and combined with a heap +// sampler to drive OpImageSampleExplicitLod. + +// CHECK-DAG: %[[UntypedPtr:[a-zA-Z0-9_]+]] = OpTypeUntypedPointerKHR UniformConstant +// CHECK-DAG: %[[CubeType:[a-zA-Z0-9_]+]] = OpTypeImage %float Cube 2 0 0 1 Unknown +// CHECK-DAG: %[[CubeArrType:[a-zA-Z0-9_]+]] = OpTypeImage %float Cube 2 1 0 1 Unknown +// CHECK-DAG: %[[SamplerType:[a-zA-Z0-9_]+]] = OpTypeSampler + +// CHECK-DAG: %[[RA_Cube:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[CubeType]]{{$}} +// CHECK-DAG: %[[RA_CubeArr:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[CubeArrType]]{{$}} +// CHECK-DAG: %[[RA_Sampler:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[SamplerType]]{{$}} + +// CHECK: %[[ResourceHeap:[a-zA-Z0-9_]+]] = OpUntypedVariableKHR %[[UntypedPtr]] UniformConstant +// CHECK: %[[SamplerHeap:[a-zA-Z0-9_]+]] = OpUntypedVariableKHR %[[UntypedPtr]] UniformConstant + +RWByteAddressBuffer output : register(u0); + +[numthreads(1, 1, 1)] +void main(uint3 tid : SV_DispatchThreadID) { + TextureCube cube = ResourceDescriptorHeap[0]; + // CHECK: %[[CubeDesc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[RA_Cube]] %[[ResourceHeap]] %uint_0 + // CHECK: %[[CubeHandle:[a-zA-Z0-9_]+]] = OpLoad %[[CubeType]] %[[CubeDesc]] + + TextureCubeArray cubeArr = ResourceDescriptorHeap[1]; + // CHECK: %[[CubeArrDesc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[RA_CubeArr]] %[[ResourceHeap]] %uint_1 + // CHECK: %[[CubeArrHandle:[a-zA-Z0-9_]+]] = OpLoad %[[CubeArrType]] %[[CubeArrDesc]] + + SamplerState samp = SamplerDescriptorHeap[0]; + // CHECK: %[[SampDesc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[RA_Sampler]] %[[SamplerHeap]] %uint_0 + // CHECK: %[[SampHandle:[a-zA-Z0-9_]+]] = OpLoad %[[SamplerType]] %[[SampDesc]] + + float3 dir = normalize(float3(tid)); + + // CHECK: %[[CubeSI:[a-zA-Z0-9_]+]] = OpSampledImage %{{.*}} %[[CubeHandle]] %[[SampHandle]] + // CHECK: OpImageSampleExplicitLod %v4float %[[CubeSI]] + float4 v = cube.SampleLevel(samp, dir, 0); + + // CHECK: %[[CubeArrSI:[a-zA-Z0-9_]+]] = OpSampledImage %{{.*}} %[[CubeArrHandle]] %[[SampHandle]] + // CHECK: OpImageSampleExplicitLod %v4float %[[CubeArrSI]] + v += cubeArr.SampleLevel(samp, float4(dir, 0), 0); + + output.Store(0, asuint(v.x)); +} diff --git a/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.typed-formats.hlsl b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.typed-formats.hlsl new file mode 100644 index 0000000000..4fb723c186 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.typed-formats.hlsl @@ -0,0 +1,59 @@ +// RUN: %dxc -T cs_6_6 -E main -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv %s | FileCheck %s + +// Verifies: each HLSL resource element type lowers to the +// correct OpTypeImage format and sampled-vs-storage mode +// when accessed through the descriptor heap. +// +// Texture2D -> OpTypeImage %uint 2D ... Unknown -> sampled +// RWTexture2D -> OpTypeImage %float 2D ... Rg32f -> storage +// RWTexture2D -> OpTypeImage %uint 2D ... Rg32ui -> storage +// RWTexture2D -> OpTypeImage %int 2D ... R32i -> storage + +// CHECK-DAG: %[[UntypedPtr:[a-zA-Z0-9_]+]] = OpTypeUntypedPointerKHR UniformConstant + +// CHECK-DAG: %[[TexUintType:[a-zA-Z0-9_]+]] = OpTypeImage %uint 2D 2 0 0 1 Unknown +// CHECK-DAG: %[[RA_TexUint:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[TexUintType]]{{$}} + +// CHECK-DAG: %[[RWTexF2Type:[a-zA-Z0-9_]+]] = OpTypeImage %float 2D 2 0 0 2 Rg32f +// CHECK-DAG: %[[RA_RWTexF2:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[RWTexF2Type]]{{$}} + +// CHECK-DAG: %[[RWTexU2Type:[a-zA-Z0-9_]+]] = OpTypeImage %uint 2D 2 0 0 2 Rg32ui +// CHECK-DAG: %[[RA_RWTexU2:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[RWTexU2Type]]{{$}} + +// CHECK-DAG: %[[RWTexIType:[a-zA-Z0-9_]+]] = OpTypeImage %int 2D 2 0 0 2 R32i +// CHECK-DAG: %[[RA_RWTexI:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[RWTexIType]]{{$}} + +// CHECK: %[[ResourceHeap:[a-zA-Z0-9_]+]] = OpUntypedVariableKHR %[[UntypedPtr]] UniformConstant + +RWByteAddressBuffer output : register(u0); + +[numthreads(1, 1, 1)] +void main(uint3 tid : SV_DispatchThreadID) { + Texture2D texUint = ResourceDescriptorHeap[0]; + // CHECK: %[[TexUintChain:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[RA_TexUint]] %[[ResourceHeap]] %uint_0 + // CHECK: %[[TexUintH:[a-zA-Z0-9_]+]] = OpLoad %[[TexUintType]] %[[TexUintChain]] + + RWTexture2D rwTexF2 = ResourceDescriptorHeap[1]; + // CHECK: %[[RWTexF2Chain:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[RA_RWTexF2]] %[[ResourceHeap]] %uint_1 + // CHECK: %[[RWTexF2H:[a-zA-Z0-9_]+]] = OpLoad %[[RWTexF2Type]] %[[RWTexF2Chain]] + + RWTexture2D rwTexU2 = ResourceDescriptorHeap[2]; + // CHECK: %[[RWTexU2Chain:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[RA_RWTexU2]] %[[ResourceHeap]] %uint_2 + // CHECK: %[[RWTexU2H:[a-zA-Z0-9_]+]] = OpLoad %[[RWTexU2Type]] %[[RWTexU2Chain]] + + RWTexture2D rwTexI = ResourceDescriptorHeap[3]; + // CHECK: %[[RWTexIChain:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedPtr]] %[[RA_RWTexI]] %[[ResourceHeap]] %uint_3 + // CHECK: %[[RWTexIH:[a-zA-Z0-9_]+]] = OpLoad %[[RWTexIType]] %[[RWTexIChain]] + + // CHECK: OpImageFetch %v4uint %[[TexUintH]] + uint val = texUint.Load(int3(tid.xy, 0)).x; + + // CHECK: OpImageWrite %[[RWTexF2H]] + rwTexF2[tid.xy] = float2(val, val); + // CHECK: OpImageWrite %[[RWTexU2H]] + rwTexU2[tid.xy] = uint2(val, val); + // CHECK: OpImageWrite %[[RWTexIH]] + rwTexI[tid.xy] = int(val); + + output.Store(0, val); +} diff --git a/tools/clang/unittests/SPIRV/SpirvContextTest.cpp b/tools/clang/unittests/SPIRV/SpirvContextTest.cpp index a0f7a4b4c3..08d33e4892 100644 --- a/tools/clang/unittests/SPIRV/SpirvContextTest.cpp +++ b/tools/clang/unittests/SPIRV/SpirvContextTest.cpp @@ -324,6 +324,9 @@ TEST_F(SpirvContextTest, RuntimeArrayTypeUnique3) { EXPECT_NE(spvContext.getRuntimeArrayType(int32, 4), spvContext.getRuntimeArrayType(int32, llvm::None)); + + EXPECT_NE(spvContext.getRuntimeArrayType(int32, llvm::None), + spvContext.getRuntimeArrayType(int32, 32)); } TEST_F(SpirvContextTest, PointerTypeUnique1) { From b12b9ea5679e4b9e9d89421a6cf3e622a0e3bdf5 Mon Sep 17 00:00:00 2001 From: Jonathan Zakharov Date: Thu, 4 Jun 2026 13:19:50 -0700 Subject: [PATCH 2/2] [SPIR-V] Add descriptor heap RaytracingAccelerationStructure support Extends the SPV_EXT_descriptor_heap native heap lowering to cover RaytracingAccelerationStructure resources loaded from ResourceDescriptorHeap. Acceleration structure descriptors are accessed via OpUntypedAccessChainKHR into a runtime array of OpTypeAccelerationStructureKHR, consistent with the image and sampler paths added in the previous commit. --- .../clang/include/clang/SPIRV/AstTypeProbe.h | 4 ++ tools/clang/lib/SPIRV/AstTypeProbe.cpp | 7 ++++ tools/clang/lib/SPIRV/SpirvEmitter.cpp | 10 +++++ ...riptorheap.ext.acceleration-structure.hlsl | 42 +++++++++++++++++++ 4 files changed, 63 insertions(+) create mode 100644 tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.acceleration-structure.hlsl diff --git a/tools/clang/include/clang/SPIRV/AstTypeProbe.h b/tools/clang/include/clang/SPIRV/AstTypeProbe.h index 214df82d45..207d02a3b2 100644 --- a/tools/clang/include/clang/SPIRV/AstTypeProbe.h +++ b/tools/clang/include/clang/SPIRV/AstTypeProbe.h @@ -250,6 +250,10 @@ bool isBuffer(QualType type); /// \brief Returns true if the given type is the HLSL RWBuffer type. bool isRWBuffer(QualType type); +/// \brief Returns true if the given type is the HLSL +/// RaytracingAccelerationStructure type. +bool isRaytracingAccelerationStructure(QualType type); + /// \brief Returns true if the given type is an HLSL Texture type. bool isTexture(QualType); diff --git a/tools/clang/lib/SPIRV/AstTypeProbe.cpp b/tools/clang/lib/SPIRV/AstTypeProbe.cpp index c933a82c16..7a9f5e859c 100644 --- a/tools/clang/lib/SPIRV/AstTypeProbe.cpp +++ b/tools/clang/lib/SPIRV/AstTypeProbe.cpp @@ -917,6 +917,13 @@ bool isBuffer(QualType type) { return false; } +bool isRaytracingAccelerationStructure(QualType type) { + if (const auto *rt = type->getAs()) { + return rt->getDecl()->getName() == "RaytracingAccelerationStructure"; + } + return false; +} + bool isRWTexture(QualType type) { if (const auto *rt = type->getAs()) { const auto name = rt->getDecl()->getName(); diff --git a/tools/clang/lib/SPIRV/SpirvEmitter.cpp b/tools/clang/lib/SPIRV/SpirvEmitter.cpp index d967520fe2..28a906695b 100644 --- a/tools/clang/lib/SPIRV/SpirvEmitter.cpp +++ b/tools/clang/lib/SPIRV/SpirvEmitter.cpp @@ -2039,6 +2039,16 @@ bool SpirvEmitter::tryToCreateDescriptorHeapAlias(const VarDecl *decl, return true; } + if (isRaytracingAccelerationStructure(decl->getType())) { + if (auto *initVal = loadIfGLValue(init)) + declIdMapper.registerFnVarAlias(decl, initVal); + else + emitError("cannot create descriptor heap acceleration structure alias " + "from initializer", + init->getExprLoc()); + return true; + } + return false; } diff --git a/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.acceleration-structure.hlsl b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.acceleration-structure.hlsl new file mode 100644 index 0000000000..7c5c9fdcb9 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.acceleration-structure.hlsl @@ -0,0 +1,42 @@ +// RUN: %dxc -T lib_6_6 -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -fvk-resource-heap-stride 64 -fvk-sampler-heap-stride 32 -fspv-extension=SPV_KHR_ray_tracing -fspv-extension=SPV_EXT_descriptor_heap -fspv-extension=SPV_KHR_untyped_pointers -spirv %s | FileCheck %s + +// Verifies: ResourceDescriptorHeap of a RaytracingAccelerationStructure +// lowers to an acceleration-structure runtime array, loads the accel +// handle from the heap, and emits OpTraceRayKHR under the ray-tracing +// capability/extension. + +// CHECK: OpCapability RayTracingKHR +// CHECK: OpExtension "SPV_KHR_ray_tracing" + +// CHECK-DAG: %[[UntypedUniformConstant:[a-zA-Z0-9_]+]] = OpTypeUntypedPointerKHR UniformConstant +// CHECK-DAG: %[[Accel:[a-zA-Z0-9_]+]] = OpTypeAccelerationStructureKHR +// CHECK-DAG: %[[ASArray:[a-zA-Z0-9_]+]] = OpTypeRuntimeArray %[[Accel]] +// CHECK: %[[ResourceHeap:[a-zA-Z0-9_]+]] = OpUntypedVariableKHR %[[UntypedUniformConstant]] UniformConstant + +struct Payload { + float4 color; +}; + +struct Attribute { + float2 bary; +}; + +[shader("closesthit")] +void main(inout Payload payload, in Attribute attr) { + RaytracingAccelerationStructure scene = ResourceDescriptorHeap[3]; + + RayDesc ray; + ray.Origin = float3(0.0f, 0.0f, 0.0f); + ray.Direction = float3(0.0f, 0.0f, -1.0f); + ray.TMin = 0.0f; + ray.TMax = 1000.0f; + + Payload childPayload = { float4(attr.bary, 0.0f, 1.0f) }; + + // CHECK: %[[Desc:[a-zA-Z0-9_]+]] = OpUntypedAccessChainKHR %[[UntypedUniformConstant]] %[[ASArray]] %[[ResourceHeap]] %uint_3 + // CHECK: %[[Scene:[a-zA-Z0-9_]+]] = OpLoad %[[Accel]] %[[Desc]] + // CHECK: OpTraceRayKHR %[[Scene]] + TraceRay(scene, 0x0, 0xff, 0, 1, 0, ray, childPayload); + + payload.color = childPayload.color; +}