Skip to content

Constant buffers design document #94

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Feb 24, 2025
Merged

Constant buffers design document #94

merged 15 commits into from
Feb 24, 2025

Conversation

hekota
Copy link
Member

@hekota hekota commented Nov 5, 2024

Design document describing how HLSL constant buffers will be handled in Clang. Contains the design for parsing, codegen a lowering of cbuffer declarations, including the default constant buffer $Globals.

Design for the ConstantBuffer<T> resource class is still work in progress.


These should be lowered to `target("dx.TypedBuffer", ..)`.Detailed design TBD.

### Lowering constant buffer variable access
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm trying to understand how the layout rules for SPIR-V will work. The only real problem is cbuffers. There is a problem because cbuffers have a different layout than other buffer types, and I don't know how that will be represented in llvm-ir.

Here is the example I am thinking about: https://godbolt.org/z/f45r7vEoG.

In llvm-ir, will the load from the cbuffer be a single load of an object of type S with the cbuffer layout? Will the store to the structured buffer store an object of type S with the structured buffer layout? If the answer is yes to both, does there need to be a conversion from one to the other in between the load and store?

Copy link
Member Author

Choose a reason for hiding this comment

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

I have just updated the document. We would like to include the cbuffer layout on the LLVM target type so the backend does not need to know the specific cbuffer layout rules. Alternatively, it could be a type annotation, but we would like to investigate the including in on the target type first. It probably does not answer your question though.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think that the conversion from one to the other will look like an address space cast, or something like that. We'll need to make sure we aren't mixing raw memory operations between cbuffer memory and other resource memories, but as long as we have each type of thing in its own address space the boundaries themselves should be clear at least.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@hekota Thanks. That answers part of my question and was in line with what I was thinking. The layout rules are a language feature. This is consistent with what I see in C/C++. The basic types are encoded in the data layout, and then the layout for the structs are set by being explicit with the padding in the struct.

@bogner I'll wait to see what you do. I don't see how the address space cast will work because that works on pointer, but we need to cast after the load. We also want to make sure that whatever we do, it can be easily optimized. In SPIRV there is an OpCopyLogical instruction to do the cast, when it cannot be optimized away.

- How to encode the cbuffer layout into LLVM target type
- How to implement `ConstantBuffer` member access
- Handling of `$Globals` constant buffer
- Nested `cbuffer` declarations
Copy link
Collaborator

Choose a reason for hiding this comment

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

I put together a bit of an example that illustrates how cbuffer works in various interesting cases here:
https://godbolt.org/z/E4cdx7xj8

Same shader with FXC:
https://shader-playground.timjones.io/39c0683735c0cd79b5970b5116765940

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks Tex! I've created an issue to track this: llvm/llvm-project#118524

Comment on lines 115 to 116
- Using type annotations to store cbuffer layout information. Subtypes would have its own layout annotations.
- This is something we can fall back to if encoding the cbuffer layout on LLVM target type turns out to be too unwieldy, especially when it comes to encoding the layout of an array of structures.
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd be interested to know why the proposed solution was chosen rather than the type annotation one. What's the downside to using type annotations?

@hekota hekota changed the title Constant buffers (cbuffer) Constant buffers design document Jan 22, 2025
@hekota hekota marked this pull request as ready for review January 29, 2025 00:45
Comment on lines 269 to 272
buffer resource handle. In order for the pass to generate the correct CBV loads
it is going to need additinal information, such as which constants belong to
which constant buffer, and layout of the buffer and any embedded structs.
Codegen will generate this information as metadata.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is extra metadata requires for the offsets? Can't the layout struct be constructed so that the offsets can be obtained from it? We can add padding elements where needed.

In the SPIR-V backend, we will have to add offset decorations for each member in the constant buffer. I'd like to handle all resource types (ie StructuredBuffers) in the same way. Having a bespoke metadata to get the offsets will create special cases.

Copy link
Collaborator

@bogner bogner Feb 6, 2025

Choose a reason for hiding this comment

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

I'm not very comfortable with the idea of the layout structure having explicit padding elements - messing with the number of elements is confusing at best. We also do need to have the offsets represented somehow, because if packoffset is used we can't rely on the standard algorithm for cbuffer.

So if we have something like:

cbuffer cb0 {
  int a;
  int b : packoffset(c2.y);
}

We want to encode that element 0 is at offset 0 and element 1 is at offset 36. As you mention elsewhere, in SPIR-V we'll need this later to decorate the members:

OpMemberDecorate %type_cb0 0 Offset 0
OpMemberDecorate %type_cb0 1 Offset 36

I also don't love that we're encoding this separately in metadata and would prefer to include this in the target extension type instead, but most of the obvious schemes to do that have downsides when it comes to clarity and verbosity.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not very comfortable with the idea of the layout structure having explicit padding elements - messing with the number of elements is confusing at best.

I thought that was a standard llvm-ir solution to problem like that: https://godbolt.org/z/7q9avvdfr.

We also do need to have the offsets represented somehow, because if packoffset is used we can't rely on the standard algorithm for cbuffer.

I don't see why the standard algorithm does not work with appropriate padding: type { i32, [32 x i8], i32}. If we don't want to add the padding, then I agree we need something else.

I'm open to other solutions. The important part to me is that the layout for structured buffers, templated load/store for byte address buffers, and cbuffer be represented the same way. For Vulkan, we have different layout options, and we need to be able to represent all of them.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I suppose we could assume that any array of i8 is padding, but otherwise the problem with type { i32, [32 x i8], i32 } is that the indices get kind of funny. Here we need to know that the third element of the struct (ie, index 2) should be referred to by index 1, as if it's the second element.

Copy link
Collaborator

@s-perron s-perron Feb 6, 2025

Choose a reason for hiding this comment

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

The main disadvantage of adding extra members for padding is that it become impossible to tell if the user added them or the compiler, and they could pollute the DXIL and SPIR-V. Reflection would want to be able to give the offset of each of the original members from the HLSL, but there is no way to filter out the padding from other unused members.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Note that vulkan has the vk::offset() attribute, which can be used for resource types other than cbuffers. This is why we have issues for all resource types.

Comment on lines 332 to 333
After the `HLSLConstantAccess` pass completes the constant globals and the
constant buffer and layout metadata are no longer needed and should be removed.
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is not true. The SPIR-V needs to know the offsets to generate valid code. I've mentioned this above.

Comment on lines +325 to +327
A new pass `HLSLConstantAccess` will translate all `load` instructions in
`hlsl_constant` address space to `llvm.{dx|spv}.resource.load.cbuffer`
intrinsics. It will make use of the metadata generated by Clang codegen that
Copy link
Collaborator

Choose a reason for hiding this comment

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

What is the signature of this intrinsic? You can see how we generate code at https://godbolt.org/z/eq9E59WcM.

We need to the index of the member to generate the OpAccessChain. If the intrinsic contains the byte offset, then we will have to be able to translate the byte offset to member.

The ideal code pattern for SPIR-V is to access the CBuffers the same way we would access a StructuredBuffer. The only different would be the type for the handle. The index on the resource_getpointer would be the index of the member being accessed.

As I've mentioned in another comment, we would want the method of getting the offset for members of structuredBuffers and cbuffers to be the same.

@damyanp damyanp linked an issue Feb 10, 2025 that may be closed by this pull request
@hekota hekota added the bug Something isn't working label Feb 11, 2025
hekota added a commit to llvm/llvm-project that referenced this pull request Feb 20, 2025
Translates `cbuffer` declaration blocks to `target("dx.CBuffer")` type. Creates global variables in `hlsl_constant` address space for all `cbuffer` constant and adds metadata describing which global constant belongs to which constant buffer. For explicit constant buffer layout information an explicit layout type `target("dx.Layout")` is used. This might change in the future.

The constant globals are temporary and will be removed in upcoming pass that will translate `load` instructions in the `hlsl_constant` address space to constant buffer load intrinsics calls off a CBV handle (#124630, #112992).

See [Constant buffer design
doc](llvm/wg-hlsl#94) for more details.

Fixes #113514, #106596
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Feb 20, 2025
Translates `cbuffer` declaration blocks to `target("dx.CBuffer")` type. Creates global variables in `hlsl_constant` address space for all `cbuffer` constant and adds metadata describing which global constant belongs to which constant buffer. For explicit constant buffer layout information an explicit layout type `target("dx.Layout")` is used. This might change in the future.

The constant globals are temporary and will be removed in upcoming pass that will translate `load` instructions in the `hlsl_constant` address space to constant buffer load intrinsics calls off a CBV handle (#124630, #112992).

See [Constant buffer design
doc](llvm/wg-hlsl#94) for more details.

Fixes #113514, #106596
@hekota hekota removed the bug Something isn't working label Feb 24, 2025
@hekota hekota merged commit 8d1d8a5 into llvm:main Feb 24, 2025
@damyanp damyanp moved this from Active to Closed in HLSL Support Apr 22, 2025
joaosaffran added a commit to joaosaffran/wg-hlsl that referenced this pull request Jun 18, 2025
* [0002] RootSignatures - Versioning proposal (llvm#115)

This PR is adding the versioning information to Root Signature specs. It
includes:

- adding a new attribute to clang driver to specify root signature
versions.
- updating metadata representation to hold root signature versions.

Closes: llvm#113

* [0002] RootSignatures - Add default values to proposal (llvm#173)

- adds default value information to the Root Signature specification

Resolves llvm#172

* Proposal for mapping resource attributes to DXIL and SPIR-V (llvm#76)

* Constant buffers design document (llvm#94)

Design document describing how HLSL constant buffers will be handled in
Clang. Contains the design for parsing, codegen a lowering of `cbuffer`
declarations, including the default constant buffer `$Globals`.

Design for the `ConstantBuffer<T>` resource class is still work in
progress.

* Update proposal number on constant buffers proposal (llvm#188)

* [NNNN] Target Extension Types for Inline SPIR-V and Decorated Types (llvm#105)

This adds target extension types to represent `SpirvType` from [HLSL
0011, Inline
SPIR-V](https://github.com/microsoft/hlsl-specs/blob/main/proposals/0011-inline-spirv.md).

* [0018] SPIRV resource representation (llvm#98)

Adds a proposal for how HLSL resources will be represented in llvm-ir
when targeting SPIR-V.

* [SPIR-V] Add proposal for I/O builtin in Clang (llvm#97)

Initial proposal to implement Input/Output built ins with both semantic
& inline SPIR-V

* [SPIR-V] Add proposal for global & local variable address spaces (llvm#111)

This commit adds a proposal on how to implement local and global
variables in the SPIR-V backend given HLSL put them in the same address
space while SPIR-V requires them to be in 2 distinct ones.

---------

Signed-off-by: Nathan Gauër <brioche@google.com>
Co-authored-by: Steven Perron <stevenperron@google.com>

* [0002] RootSignatures - Add missing Descriptor Ranges `offset` (llvm#191)

- it appears the `offset` was mistakenly dropped as discarding this
information no longer allows the user to provide a manual offset from
the start of the descriptors heap and allow for register aliasing

- additionally, we do a small fix-up to update the flag values to be
their correct default as we adjust the parameter values

Resolves llvm#190

---------

Co-authored-by: Finn Plummer <finnplummer@microsoft.com>

* [0002] RootSignatures - Misc updates to spec from testing (llvm#195)

This issue addresses the discrepancies found from testing
[here](llvm/llvm-project#130826).

The pr has been broken up to commits that address the bullet points in
the original issue. Briefly:

- First 2 commits work to have a concrete EBNF description of the
grammar that denotes that all parameter args can be specified in any
order
- It is also addressed that `NUMBER` can be more restrictive with
respect to what the dxc/clang compiler will accept
- Missing documentation for `unbounded` is added
- Add additional validation of some parameters that surface as errors in
dxc
- Remove the specification of 'AllowLowTierReservedHwCbLimit'

Resolves llvm#192

---------

Co-authored-by: Finn Plummer <finnplummer@microsoft.com>

* [0021] Proposal for handling member function address spaces. (llvm#187)

As we expose new address spaces in HLSL, we will have a problem with the
address space for the `this` pointer in member functions.

* Resource Instance Analysis (llvm#207)

Fixes llvm#179

Proposes a generic architecture for organizing, resolving, and
validating Resource Instance metadata

---------

Co-authored-by: Damyan Pepper <damyanp@microsoft.com>
Co-authored-by: Helena Kotas <hekotas@microsoft.com>

* [0023] Proposal for separate counter resource handle (llvm#208)

This is a proposal to create a separte handle for the
counter resource in RWStructureBuffer and other that have associated
counters.

* [0002] RootSignatures - NFC Formatting Grammar to EBNF (llvm#198)

Update the formatting of the root signature grammar to use EBNF in a
consistent manner:

- The first commit just goes through to use `=` instead of `:` and
denote statements with ';' to comply with EBNF
- The second commit comes from a suggestion
[here](llvm#195 (comment))
to just use the `|` notation to specify the parameters in any order but
not explicitly denote that they can only occur once

Clean up pr to resolve llvm#192

---------

Co-authored-by: Finn Plummer <finnplummer@microsoft.com>

* Implicit resource binding - document current DXC behavior (llvm#193)

Initial commit of the Implicit Resource Binding proposal that includes:
- brief overview section on resource bindings
- number of examples documenting how are implicit resource bindings are
assigned in DXC

Closes llvm#176

* Implicit resource bindings design (llvm#196)

Proposed design for implicit resource bindings in Clang.

Closes llvm#177

* Resource constructors proposal (llvm#197)

Proposal for resource classes constructors.

Closes llvm#209

* Remove tools/hlslpm (llvm#228)

As the process evolves, it becomes more effort to keep tools/hlslpm up
to date.

This change removes tools/hlslpm rather than keep a stale version
around.

* Update issue_tracking.md for latest thinking (llvm#229)

Describe epics, scenarios, deliverables and tasks, and the custom fields
used in the HLSL Support project.

---------

Co-authored-by: Finn Plummer <canadienfinn@gmail.com>

* Add "Review" value for the "blocked" field (llvm#257)

Some issues are blocked on design review. This value can be used to
identify them.

* Resource binding: add another example and notes about resources in structs (llvm#214)

Add one more example showing binding of resource arrays in user-defined
structs.
Add a note to re-evaluate whether to bind resource arrays in structs
individually or as a continuous descriptor range later on when we start
working on the resources in structs design (llvm/wg-hlsl#llvm#212).

* [0026] Add proposal for symbol visibility. (llvm#272)

Section 3.6 of the
[HLSL
specification](https://microsoft.github.io/hlsl-specs/specs/hlsl.pdf)
defined the possible linkages for names. This proposal updates how these
linkages are represented in LLVM IR. The current implementation presents
challenges for the SPIR-V backend due to inconsistencies with OpenCL. In
HLSL, a
name can have external linkage and program linkage, among others. If a
name has
external linkage, it is visible outside the translation unit, but not
outside a
linked program. A name with program linkage is visible outside a
partially linked program.
We propose that names with program linkage in HLSL should have external
linkage
and default visibility in LLVM IR, while names with external linkage in
HLSL should have
external linkage and hidden visibility in LLVM IR. They both have
external
linkage because they are visible outside the translation unit. Default
visibility means the name is visible outside a shared library (program).
Hidden
visibility means the name is not visible outside the shared library
(program).

* Update proposals for implicit binding and resource constructors to match implementation (llvm#282)

* Add missing titles to proposals (llvm#283)

Adding title to proposals that were missing one.

* Unify new line endings to LF (llvm#286)

Add .gitattributes and set new line endings to LF.
Normalize existing files by running `git add --renormalize`.

* [NNNN] Implementation for `vk::constant_id` (llvm#287)

Design for the Clang implmentation of`vk::constant_id`.

---------

Signed-off-by: Nathan Gauër <brioche@google.com>
Co-authored-by: Finn Plummer <50529406+inbelic@users.noreply.github.com>
Co-authored-by: Justin Bogner <mail@justinbogner.com>
Co-authored-by: Helena Kotas <hekotas@microsoft.com>
Co-authored-by: Cassandra Beckley <cbeckley@google.com>
Co-authored-by: Steven Perron <stevenperron@google.com>
Co-authored-by: Nathan Gauër <github@keenuts.net>
Co-authored-by: Finn Plummer <finnplummer@microsoft.com>
Co-authored-by: Ashley Coleman <ascoleman@microsoft.com>
Co-authored-by: Damyan Pepper <damyanp@microsoft.com>
Co-authored-by: Finn Plummer <canadienfinn@gmail.com>
@damyanp damyanp removed this from HLSL Support Jun 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Design target("dx.CBuffer", ...) type
6 participants