[SPIR-V] Add SPV_EXT_descriptor_heap + SPV_KHR_untyped_pointers codegen - #8517
[SPIR-V] Add SPV_EXT_descriptor_heap + SPV_KHR_untyped_pointers codegen#8517Jonathan Zakharov (jzakharovnv) wants to merge 19 commits into
Conversation
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
f640327 to
cbcae38
Compare
|
@microsoft-github-policy-service agree company="NVIDIA" |
Diego Novillo (dnovillo)
left a comment
There was a problem hiding this comment.
Thanks for this! I just started looking at it and have a couple of questions. I'll add more as I read the PRs.
|
Jonathan Zakharov (@jzakharovnv) I think the OpConstantSizeOfEXT usage in this MR isn't quite right - it looks like you're just taking the stride of the descriptor type being loaded and using that? This works for the sampler heap where there's only one type (samplers), but not for the resource heap. The stride of all resource descriptors must be the same for all descriptors, and has to be based on the biggest of the buffer/image sizes. This is illustrated in the VK extension doc here: https://docs.vulkan.org/features/latest/features/proposals/VK_EXT_descriptor_heap.html#_shader_model_6_6_samplerheap_and_resourceheap I might be misunderstanding the code, but I don't see anything for finding the largest of the two sizes? They're both POT, so it is a simple "which is max" calculation, but I don't see it being done in this PR. |
|
Tobski You are right, this is my blunder. Will fix shortly. |
|
Tobski Shared max(image,buffer) stride for descriptor heap resource arrays ought to be closer to the original intent. Please take a look when you can. Thanks! |
Looks right to me now! Thanks! |
Diego Novillo (dnovillo)
left a comment
There was a problem hiding this comment.
A question on changes that have gone in #8519 that we may to reflect here. Not sure how you want to handle it.
bd79339 to
cbcae38
Compare
|
Diego Novillo (@dnovillo) In response to your last comment on this PR, yes I think it's more correct to have the TODO commit a part of this initial branch rather than one down the line. Cherrypicked and rebased to reflect this. |
Diego Novillo (dnovillo)
left a comment
There was a problem hiding this comment.
Just one minor change and it's good to from my side.
Diego Novillo (dnovillo)
left a comment
There was a problem hiding this comment.
LGTM. Just one final nit. Thanks for doing this!
d0835c8 to
88bd33c
Compare
|
There was a problem hiding this comment.
Pull request overview
Adds native SPIR-V descriptor-heap lowering using SPV_EXT_descriptor_heap and SPV_KHR_untyped_pointers.
Changes:
- Implements native image, sampler, and buffer descriptor access.
- Adds descriptor-size-based runtime-array strides and image atomic support.
- Expands tests and documents Vulkan 1.3 requirements and limitations.
Reviewed changes
Copilot reviewed 47 out of 47 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
tools/clang/unittests/SPIRV/SpirvContextTest.cpp |
Tests runtime-array type uniqueness. |
tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.typed-formats.hlsl |
Tests typed image formats. |
tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.texturecube.hlsl |
Tests cube textures and samplers. |
tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.texture.hlsl |
Tests texel buffers. |
tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.texture-sampler-assignment.hlsl |
Tests resource reassignment. |
tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.texture-ms.hlsl |
Tests multisampled textures. |
tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.texture-dims.hlsl |
Tests sampled texture dimensions. |
tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.structured-buffer-atomic.hlsl |
Tests structured-buffer atomics. |
tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.static-global.hlsl |
Tests static global resources. |
tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.sampler-comparison.hlsl |
Tests comparison samplers. |
tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.sample-grad-bias.hlsl |
Tests sampling operands. |
tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.rwtexture-dims.hlsl |
Tests RW texture dimensions. |
tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.rwtexture-atomics.hlsl |
Tests untyped image atomics. |
tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.rwbyteaddressbuffer.hlsl |
Tests writable byte buffers. |
tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.nonuniform.hlsl |
Tests divergent heap indexing. |
tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.mixed-bound.hlsl |
Tests bound/native resource coexistence. |
tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.mixed-alias.error.hlsl |
Tests mixed-alias diagnostics. |
tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.load-offset.hlsl |
Tests texture load offsets. |
tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.groupshared.hlsl |
Tests groupshared coexistence. |
tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.gather.hlsl |
Tests gather operations. |
tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.function-params.hlsl |
Tests resource function parameters. |
tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.discarded.error.hlsl |
Tests discarded-index diagnostics. |
tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.counter-ops.error.hlsl |
Tests unsupported counter diagnostics. |
tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.constant-texture-buffer.hlsl |
Tests constant and texture buffers. |
tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.constant-buffer-assignment.hlsl |
Tests constant-buffer reassignment. |
tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.buffer.hlsl |
Tests native buffer lowering. |
tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.array-stride.hlsl |
Tests descriptor-array strides. |
tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.append-consume.error.hlsl |
Tests append/consume diagnostics. |
tools/clang/test/CodeGenSPIRV/resource-heap-ext-texture.hlsl |
Removes superseded coverage. |
tools/clang/lib/SPIRV/SpirvType.cpp |
Extends runtime-array equality. |
tools/clang/lib/SPIRV/SpirvInstruction.cpp |
Implements new SPIR-V instructions. |
tools/clang/lib/SPIRV/SpirvEmitter.h |
Declares heap lowering and alias state. |
tools/clang/lib/SPIRV/SpirvEmitter.cpp |
Implements native heap code generation. |
tools/clang/lib/SPIRV/SpirvContext.cpp |
Uniques buffer and stride-ID types. |
tools/clang/lib/SPIRV/SpirvBuilder.cpp |
Builds descriptor sizes and strides. |
tools/clang/lib/SPIRV/LowerTypeVisitor.cpp |
Lowers untyped image pointers. |
tools/clang/lib/SPIRV/EmitVisitor.h |
Declares new emission handlers. |
tools/clang/lib/SPIRV/EmitVisitor.cpp |
Serializes new instructions and decorations. |
tools/clang/lib/SPIRV/DeclResultIdMapper.h |
Declares function alias registration. |
tools/clang/lib/SPIRV/DeclResultIdMapper.cpp |
Implements alias registration. |
tools/clang/lib/SPIRV/CapabilityVisitor.cpp |
Requires Vulkan 1.3 and extensions. |
tools/clang/include/clang/SPIRV/SpirvVisitor.h |
Adds visitor hooks. |
tools/clang/include/clang/SPIRV/SpirvType.h |
Adds stride-ID runtime arrays. |
tools/clang/include/clang/SPIRV/SpirvInstruction.h |
Defines new instruction classes. |
tools/clang/include/clang/SPIRV/SpirvContext.h |
Adds type caches and APIs. |
tools/clang/include/clang/SPIRV/SpirvBuilder.h |
Exposes descriptor-stride builders. |
docs/SPIR-V.rst |
Documents native descriptor heaps. |
Fixes #8603: MergeBinaryOpSelect produced an OpSelect with a vector result type and a scalar condition, which is only legal in SPIR-V 1.4+ (KhronosGroup/SPIRV-Tools#6827). This exposes #8740 where the resource-heap-ext-texture.hlsl test fails validation - for now this is worked around by disabling the test, since #8517 looks like it is reworking it anyway. Assisted-by: copilot --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b04b21af-2d8b-4af5-b9ad-1b84073588f5
fae23d7 to
1f5a41a
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 54 out of 54 changed files in this pull request and generated 3 comments.
Suppressed comments (2)
tools/clang/lib/SPIRV/SpirvEmitter.cpp:2983
- Direct heap-buffer returns bypass the new rejection: this branch handles
return ResourceDescriptorHeap[i], then skips the alias-onlyelse ifand falls through toloadIfGLValue, which loads the whole buffer interface object and produces the same invalid SPIR-V that the alias diagnostic is meant to prevent. This form is valid HLSL and already appears insm6_6.descriptorheap.return.counter.hlsl; reject buffer-likeresourceTypehere (or implement pointer propagation) before falling through.
if (isDescriptorHeap(srcExpr)) {
tools/clang/lib/SPIRV/SpirvEmitter.cpp:3456
- This guard only runs for a
DeclRefExpr, so a direct heap expression such asconsume((StructuredBuffer<uint>)ResourceDescriptorHeap[i])bypasses the diagnostic. The later generic argument path then callsloadIfGLValueon theOpBufferPointerEXTresult and attempts to copy the whole buffer interface object into a Function variable, yielding invalid SPIR-V. Apply the heap-buffer rejection to all heap-sourced arguments before narrowing toDeclRefExpr.
if (const auto *declRefExpr = dyn_cast<DeclRefExpr>(arg)) {
|
Diego Novillo (@dnovillo) Chris B (@llvm-beanz) Gregory Roth (@pow2clk) Hi folks. It's time we bring this endless review cycle to an end. To wrap up what I've done: I've gone and made sure to address every human review comment and also done my best to address every Copilot comment. I've just filed #8823 through #8831 so no review gets lost. Any further reviews on these PRs (#8517, #8518, #8519) I will gladly file an issue for, but must draw the somewhere, otherwise this will go on forever. Thanks! |
|
Damyan Pepper (@damyanp) Chris B (@llvm-beanz) Gregory Roth (@pow2clk), I would like to propose that we submit this series: #8517, #8518 and #8519. I have gone over every open thread on all three PRs again. There are no blocking issues left. Gregory Roth (@pow2clk) has also been going over it and did not find major blockers (Gregory, please correct me if this changed since we chatted earlier). Jonathan Zakharov (@jzakharovnv) has fixed the two remaining blocking issues. The acceleration-structure stride, the CLI stride override, the There are some holes where a direct I think it is better to submit this now since it's protected by a flag, so it shouldn't destabilize anything that does not depend on DH. We have been reviewing this since early June, and at this point I think we learn more by letting developers compile real shaders against it than by continuing to iterate in review. Real usage will find the gaps that matter faster than we are finding them by inspection, and we can keep fixing them in follow-up PRs. Does this sound reasonable? Thanks. |
Chris B (llvm-beanz)
left a comment
There was a problem hiding this comment.
I think we need to work on PR etiquette here a bit. Phrases like "It's time we bring this endless review cycle to an end" come across as passive-aggressive and dismissive of the value code review provides.
From my perspective, I've been waiting on the PR author to re-apply changes baed on feedback I gave weeks ago that they dropped in more recent force-pushes.
I'm not comfortable accepting merging a PR on the basis that the review is taking too long when we have mechanical issues that should have been addressed a long time back which aren't being responded to.
This is not how code review works, and it isn't the culture we want contributing to this project.
Chris B (llvm-beanz)
left a comment
There was a problem hiding this comment.
The comment's prefixed with "nit" are nitpicks that don't need to block the change merging, but are things we should be generally looking for.
Especially with AI-assisted code we really need to look closely at code comments to assess if they add value and are correct.
One concern I have generally about some of the comments in this PR is that they describe aspects of other parts of the implementation which may not always remain accurate as other parts of the code change. This can be confusing in the future as the behavior of APIs change it isn't natural to look at all the call sites for documentation of how an API behaves.
|
|
||
| void DeclResultIdMapper::registerFnVarAlias(const VarDecl *var, | ||
| SpirvInstruction *varInstr) { | ||
| if (varInstr) |
There was a problem hiding this comment.
This seems like it should be an assert, not "silently do nothing" since it is probably a misuse of the API.
I also don't see where this function is used at all so I'm unsure if it belongs here.
There was a problem hiding this comment.
You're right, this is a weak helper function and an assert would be alot better. It seems like this function got caught in the messy mishap of trying to break up my monolithic commits into the 3 PRs we have now. That's just to say that the function is used, just not in this PR. I'll do a pass over #8517 #8518 #8519 to see if there any more such cases of misplaced code + any outdated/inlineable funcs + better assert placement. Thanks!
| // the maximum is computed with OpSpecConstantOp over two OpConstantSizeOfEXT | ||
| // placeholders. A canonical sampled 2D float image and a Uniform buffer stand | ||
| // in as representatives; VkPhysicalDeviceDescriptorHeapPropertiesEXT reports | ||
| // one size per category, so subtype and storage class do not affect the size. |
There was a problem hiding this comment.
nit: Almost half this diff is code comment, and while comments are good I'm not sure how much value this actually adds. LLMs are way overly verbose and often describe things that aren't really relevant to the code. This is one of those cases. Generally we should clean these up rather than merge them as the LLM writes them.
There was a problem hiding this comment.
Agreed, I'm still trying to understand the balance of "why" not "how" comments in DXC. I think my own bias favoring "how" comments has me more okay with what others may consider fluff, but this here is definitely needlessly verbose. I'm doing a pass now over all multi line comments to try to get some better S/N in the documentation.
| if (result && !result->isRValue()) { | ||
| result = spvBuilder.createLoad(expr->getType(), result, | ||
| buffer->getExprLoc(), range); | ||
| } |
There was a problem hiding this comment.
| if (result && !result->isRValue()) { | |
| result = spvBuilder.createLoad(expr->getType(), result, | |
| buffer->getExprLoc(), range); | |
| } | |
| if (result && !result->isRValue()) | |
| result = spvBuilder.createLoad(expr->getType(), result, | |
| buffer->getExprLoc(), range); |
There was a problem hiding this comment.
This is my bad, will do a pass to make sure the code is following standards everywhere
| // 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 |
There was a problem hiding this comment.
This comment doesn't make sense. ResourceDescriptorHeap[i].Load() is illegal in HLSL (the untyped resources don't have load methods), and StructuredBuffer::Load returns an rvalue, not an lvalue so there is no reason a cast would be present.
There was a problem hiding this comment.
Yeah, this is a really misleading comment. ResourceDescriptorHeap[i].Load() is nonsense and so is the lvalue cast. What the comment should say is that derefOrCreatePointerToValue produces an AccessChain and StructuredBuffer::Load should return the element value (not ptr) and thus, should be loaded here. I'll make this correction with the next batch of commits.
| { | ||
| auto stateIt = descriptorHeapVarState.find(dstVar); | ||
| if (stateIt != descriptorHeapVarState.end() && | ||
| stateIt->second == DescriptorHeapVarState::Mixed) | ||
| return false; | ||
| } |
There was a problem hiding this comment.
| { | |
| auto stateIt = descriptorHeapVarState.find(dstVar); | |
| if (stateIt != descriptorHeapVarState.end() && | |
| stateIt->second == DescriptorHeapVarState::Mixed) | |
| return false; | |
| } | |
| auto stateIt = descriptorHeapVarState.find(dstVar); | |
| if (stateIt != descriptorHeapVarState.end() && | |
| stateIt->second == DescriptorHeapVarState::Mixed) | |
| return false; |
There was a problem hiding this comment.
Oops, will fix to follow coding guidelines. Thanks for pointing out
| // SPV_EXT_descriptor_heap: apply a client-API-defined byte stride via an | ||
| // ArrayStrideIdEXT decoration. The sampler heap holds a single descriptor | ||
| // type, so its stride is the sampler descriptor size. The resource heap is a | ||
| // shared flat array in which any resource descriptor may sit at any slot, so | ||
| // every resource runtime array must use one common stride: max(sizeof(image), | ||
| // sizeof(buffer)). Using the accessed element size would be wrong for the | ||
| // resource heap. |
There was a problem hiding this comment.
nit: I don't know that this comment adds much value. It is certainly far more verbose than necessary.
| if (isImplicitVarDeclInVkNamespace(declRefExpr->getDecl())) { | ||
| result = doExpr(cast<VarDecl>(decl)->getInit()); | ||
| } else if (const auto *varDecl = dyn_cast<VarDecl>(decl)) { | ||
| if (auto *alias = |
There was a problem hiding this comment.
| if (auto *alias = | |
| if (SpirvInstruction *alias = |
We follow an almost never auto policy.
There was a problem hiding this comment.
Yeah, this auto is obfuscating. Will fix and make sure newer commits aren't misusing auto.
|
This series of PRs would also address #8243. Jonathan Zakharov (@jzakharovnv) please check whether this is the case once the review cycle for this series has converged. Thanks. |
Pushing shortly, will test if these PRs fix this issue first thing next week. |
There was a problem hiding this comment.
🟡 Changes recommended
Heap atomics across function calls remain invalid, acceleration-structure strides are incomplete, and rasterizer-ordered aliases lose required metadata.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (2)
tools/clang/lib/SPIRV/SpirvEmitter.cpp:5408
- Heap aliases for rasterizer-ordered buffer resources lose their ROV marker here. The initial
emitDescriptorHeapBufferAccessmarks its temporary pointer, but aliases later re-create a newOpBufferPointerEXTthrough this function; without the marker, byte-address-buffer loads/stores will not insert the required invocation-interlock region. Restore the marker from the alias declaration type on every re-derived pointer.
bufferDataPtr->setStorageClass(
found->second.bufferPointerType->getStorageClass());
bufferDataPtr->setLayoutRule(found->second.layoutRule);
bufferDataPtr->setRValue(false);
tools/clang/lib/SPIRV/SpirvBuilder.cpp:2057
- The common resource stride omits acceleration-structure descriptors, although
doCXXOperatorCallExpraccepts every remaining heap resource type andLowerTypeVisitorlowersRaytracingAccelerationStructure. If such a descriptor is used, all resource arrays are therefore indexed withmax(image, buffer)even when the acceleration-structure descriptor is larger. IncludeOpConstantSizeOfEXTfor the acceleration-structure type in the maximum when present, or reject this heap resource kind until that support lands.
// ResourceDescriptorHeap is a flat array: any descriptor may sit at any slot.
// DX12 semantics require all resource arrays share stride =
// max(sizeof(image), sizeof(buffer)).
- Files reviewed: 53/53 changed files
- Comments generated: 1
- Review effort level: Balanced
| @@ -0,0 +1,41 @@ | |||
| // XFAIL: * | |||
65aa2ac to
2f9f581
Compare
There was a problem hiding this comment.
🟡 Changes recommended
Direct counter operations, acceleration-structure strides, and function-boundary image atomics have unresolved correctness issues.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (1)
tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.image-alias-fn-param.hlsl:1
- This leaves a valid heap-sourced RWTexture atomic path producing SPIR-V that fails validation, despite the PR advertising RWTexture interlocked support. An unconditional XFAIL also provides no regression protection. Please either propagate the descriptor index across this function boundary or reject this case with a deliberate diagnostic and convert the test to a checked negative test.
- Files reviewed: 53/53 changed files
- Comments generated: 2
- Review effort level: Balanced
| // ResourceDescriptorHeap is a flat array: any descriptor may sit at any slot. | ||
| // DX12 semantics require all resource arrays share stride = | ||
| // max(sizeof(image), sizeof(buffer)). | ||
| // VkPhysicalDeviceDescriptorHeapPropertiesEXT reports one size per category | ||
| // (imageDescriptorSize / bufferDescriptorSize); textures lower to |
| (void)doExpr(object); | ||
| } | ||
|
|
||
| if (isDescriptorHeapCounterUnsupported(object)) { |
2f9f581 to
7645ee4
Compare
7645ee4 to
d8f09ec
Compare
There was a problem hiding this comment.
🟡 Changes recommended
Several unsupported cross-function paths can emit invalid SPIR-V, and acceleration-structure descriptors can receive an incorrect heap stride.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (2)
tools/clang/lib/SPIRV/SpirvEmitter.cpp:3461
- This guard only catches a bare
DeclRefExpr. A direct heap subscript (or a heap alias wrapped in an explicit cast) bypasses it and reaches the ordinary parameter path, although the comment notes that passing the resulting storage-buffer pointer requires unimplementedVariablePointersStorageBuffersupport. Detect heap-sourced buffer values viaisHeapSourcedValue(arg)regardless of AST shape, or implement the required parameter lowering.
if (const auto *declRefExpr = dyn_cast<DeclRefExpr>(arg)) {
// Buffer alias vars are not in astDecls (getDeclEvalInfo would crash);
// passing by value also requires VariablePointersStorageBuffer + callee
// parameter type changes (not yet implemented). Emit diagnostic instead.
// TODO: implement full buffer-alias function-call support
// (VariablePointersStorageBuffer + matching createFnParam type).
const auto *var = dyn_cast<VarDecl>(declRefExpr->getDecl());
if (var && descriptorHeapBufferAliasVars.count(var)) {
tools/clang/lib/SPIRV/SpirvBuilder.cpp:2060
- The generic heap lowering also accepts
RaytracingAccelerationStructure, but this cached stride only accounts for image and buffer descriptor sizes. The new array-stride test itself notes that acceleration structures require a three-way maximum; if their descriptor size is larger, every heap access is addressed with the wrong stride. Include the acceleration-structure size when that resource kind is supported, or reject native heap accesses to it until then.
// ResourceDescriptorHeap is a flat array: any descriptor may sit at any slot.
// DX12 semantics require all resource arrays share stride =
// max(sizeof(image), sizeof(buffer)).
// VkPhysicalDeviceDescriptorHeapPropertiesEXT reports one size per category
// (imageDescriptorSize / bufferDescriptorSize); textures lower to
// OpTypeImage, so image/buffer covers all relevant HLSL resource kinds. Sizes
- Files reviewed: 53/53 changed files
- Comments generated: 2
- Review effort level: Balanced
| if (isDescriptorHeap(srcExpr)) { | ||
| // Direct heap subscript: register the heap variable so the declaration | ||
| // mapper can resolve it for the return instruction. | ||
| const Expr *base = nullptr; | ||
| getDescriptorHeapOperands(srcExpr, &base, /* index= */ nullptr); | ||
| const Expr *parentExpr = cast<CastExpr>(parentMap->getParent(srcExpr)); | ||
| QualType resourceType = parentExpr->getType(); |
| } else if (const auto *decl = | ||
| dyn_cast_or_null<VarDecl>(getReferencedDef(base))) { | ||
| ptr = emitDescriptorHeapImageTexelPointer(decl, coordInstr, zero, | ||
| baseType, srcLoc); |
There was a problem hiding this comment.
🔵 Needs a closer look
Descriptor-stride and function-boundary cases can still produce incorrectly addressed or invalid SPIR-V.
Review details
Suppressed comments (4)
tools/clang/lib/SPIRV/SpirvBuilder.cpp:2078
- This computes the resource stride from image and buffer sizes only, but the generic heap path also lowers
RaytracingAccelerationStructuretoAccelerationStructureTypeNV(LowerTypeVisitor.cpp:970-972) and routes it through this same stride. IfaccelerationStructureDescriptorSizeexceeds both sizes, accesses use the wrong slot addresses. Include the acceleration-structure size when such descriptors are present, or reject acceleration structures on the native heap until that stride is supported.
SpirvInstruction *imageSize = getConstantSizeOfEXT(placeholderImage);
SpirvInstruction *bufferSize = getConstantSizeOfEXT(placeholderBuffer);
SpirvInstruction *imageIsBigger = createSpecConstantBinaryOp(
spv::Op::OpUGreaterThan, astContext.BoolTy, imageSize, bufferSize, {});
resourceHeapArrayStride =
createSpecConstantTernaryOp(spv::Op::OpSelect, astContext.UnsignedIntTy,
imageIsBigger, imageSize, bufferSize, {});
tools/clang/lib/SPIRV/SpirvEmitter.cpp:2988
- The direct-subscript branch bypasses the buffer-alias diagnostic below. For
return ResourceDescriptorHeap[i]converted toStructuredBuffer,ByteAddressBuffer,ConstantBuffer, orTextureBuffer,doExprreturns a non-rvalueOpBufferPointerEXT;loadIfGLValuethen attempts to load/return the whole resource representation, producing the same invalid SPIR-V that the alias branch rejects. Reject direct buffer-like heap returns here as well (or implement cross-function pointer propagation).
if (isDescriptorHeap(srcExpr)) {
// Direct heap subscript: register the heap variable so the declaration
// mapper can resolve it for the return instruction.
const Expr *base = nullptr;
getDescriptorHeapOperands(srcExpr, &base, /* index= */ nullptr);
const Expr *parentExpr = cast<CastExpr>(parentMap->getParent(srcExpr));
tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.image-alias-fn-param.hlsl:5
- This XFAIL documents a valid shader that still emits invalid SPIR-V when a heap-backed
RWTexturecrosses a function boundary and is used atomically. The native option therefore silently produces an invalid module for a form covered by the PR's RWTexture atomic support. Propagate the descriptor index into the callee, or emit a hard unsupported diagnostic and convert this into a passing negative test rather than landing a known invalid-codegen path.
tools/clang/test/CodeGenSPIRV/sm6_6.descriptorheap.ext.array-stride.hlsl:62 - Use the possessive “its” here.
// The sampler array just uses the sampler size as it's stride.
- Files reviewed: 53/53 changed files
- Comments generated: 0 new
- Review effort level: Balanced
Building off of #8281, this PR adds a native lowering via SPV_EXT_descriptor_heap and SPV_KHR_untyped_pointers and is part 1/3 in a series.
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 an AI agent.
Diego Novillo (@dnovillo)