Skip to content

[SPIR-V] Add SPV_EXT_descriptor_heap + SPV_KHR_untyped_pointers codegen#8517

Open
jzakharovnv wants to merge 1 commit into
microsoft:mainfrom
jzakharovnv:pr1-descriptor-heap-core
Open

[SPIR-V] Add SPV_EXT_descriptor_heap + SPV_KHR_untyped_pointers codegen#8517
jzakharovnv wants to merge 1 commit into
microsoft:mainfrom
jzakharovnv:pr1-descriptor-heap-core

Conversation

@jzakharovnv

@jzakharovnv jzakharovnv commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

Building off of #8281, this PR adds a native lowering via SPV_EXT_descriptor_heap and SPV_KHR_untyped_pointers and is part 1/4 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.

@dnovillo

@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

✅ With the latest revision this PR passed the C/C++ code formatter.

Building off of microsoft#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.
@jzakharovnv jzakharovnv force-pushed the pr1-descriptor-heap-core branch from f640327 to cbcae38 Compare June 4, 2026 22:42
@jzakharovnv

Copy link
Copy Markdown
Collaborator Author

@microsoft-github-policy-service agree company="NVIDIA"

@dnovillo dnovillo left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this! I just started looking at it and have a couple of questions. I'll add more as I read the PRs.

SpirvEmitter::getDescriptorHeapRuntimeArrayType(const SpirvType *elemType,
bool onSamplerHeap) {
constexpr uint32_t kDefaultResourceHeapStride = 64;
constexpr uint32_t kDefaultSamplerHeapStride = 32;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I would float these defaults to SpirvEmitter.h and document where the seemingly magic values 32 and 64 come from.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely agree that these need some better documentation. I placed them at this scope because they seem a little niche, and I want to try to avoid polluting very wide scopes, but I can move them up.

constexpr uint32_t kDefaultSamplerHeapStride = 32;
const uint32_t stride =
onSamplerHeap ? kDefaultSamplerHeapStride : kDefaultResourceHeapStride;
return spvContext.getRuntimeArrayType(elemType, stride);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't seem that there are tests for OpDecorate ArrayStride N in this PR. I haven't checked the others. Could you add one (unless I missed it?)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, there is a healthy amount of testing in the last 2 PRs of this series, but I neglected to have for this one. I can fix that.

@github-project-automation github-project-automation Bot moved this from New to In progress in HLSL Roadmap Jun 8, 2026
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, {});

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function can return failure (which is getting dropped here), and there is no test verifying its error handling.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching that, I'll add a test.

tryToAssignToDescriptorHeapBuffer(expr))
return aliasResult.getValue();

auto *rhs = loadIfGLValue(expr->getRHS());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LLVM's coding standards which DXC adopts (although not historically well enforced), have an "almost never auto" policy:

https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/CodingStandards.rst#use-auto-type-deduction-to-make-code-more-readable

The heavy use of auto in this code makes it significantly harder to review this PR in a browser without an IDE telling me the types of things.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noted, thanks for tip!

Comment on lines +5110 to +5113
if (result && !result->isRValue()) {
result =
spvBuilder.createLoad(structType, result, buffer->getExprLoc(), range);
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (result && !result->isRValue()) {
result =
spvBuilder.createLoad(structType, result, buffer->getExprLoc(), range);
}
if (result && !result->isRValue())
result =
spvBuilder.createLoad(structType, result, buffer->getExprLoc(), range);

nit: https://llvm.org/docs/CodingStandards.html#don-t-use-braces-on-simple-single-statement-bodies-of-if-else-loop-statements

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will fix in next commit

}

// ConstantBuffer -> Uniform (UBO); all others -> StorageBuffer (SSBO)
// TODO: Remove this manual override once LowerTypeVisitor returns the

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be fixed before we merge this change? Seems like you're working around a clear bug here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To get the storage class here rather than in LowerTypeVisitor is bit of a bandage but provides functionally correct output. I had left this TODO in with the aim of making this PR as small/unintrusive as possible and with room to improve with future commits. If you're okay with filing an issue, let me know, otherwise I can go about cleaning up this fix.


float4 main(uint idx : A) : SV_Target {
Texture2D<float4> tex = ResourceDescriptorHeap[NonUniformResourceIndex(idx)];
SamplerState samp = SamplerDescriptorHeap[NonUniformResourceIndex(idx + 1)];

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like something we should have the compiler issue a diagnostic on. Silently dropping something the user explicitly wrote seems unfortunate.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, this is a bit esoteric but correct according to @Tobski. How should we handle a diagnostic here, just a simple warning?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh... Actually I think the correct solution for HLSL is that in the absence of NonUniformResourceIndex an access should be marked as Uniform, and NonUniformResourceIndex suppresses that.

It should have an effect, so no diagnostic should be required.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

3 participants