From 18cfed2080d9f31495afd6a71f6534e12f53a621 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Tue, 5 Nov 2024 11:00:55 -0800 Subject: [PATCH 01/14] Initial commit --- proposals/NNNN-constant-buffers.md | 102 +++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 proposals/NNNN-constant-buffers.md diff --git a/proposals/NNNN-constant-buffers.md b/proposals/NNNN-constant-buffers.md new file mode 100644 index 0000000..5970e5e --- /dev/null +++ b/proposals/NNNN-constant-buffers.md @@ -0,0 +1,102 @@ + +# Constant buffers + +* Proposal: [NNNN](NNNN-constant-buffers.md) +* Author(s): [Helena Kotas](https://github.com/hekota) +* Status: **Design In Progress** + +## Introduction + +Shader inputs usually include a number of constants which are stored in one or more buffer resources in memory with specific packing rules. These resources can be organized into two types of buffers: constant buffers and texture buffers. + +From the compiler point of view constant buffers and texture buffers are very similar. The major difference is that constant buffers load from a constant buffer view (CBV) and bind to register `b` while texture buffers load from a typed buffer (SRV) and bind to the `t` register. + +Declaring a constant buffer or a texture buffer looks very much like a structure declaration in C, with the addition of the register and packoffset keywords for manually assigning registers or packing data. + +``` +[cbuffer|tbuffer] ConstBufferName [register(b#)|register(t#)] { + VariableDeclaration [ : packoffset() ]; + ... +} +``` + +Constant buffer variables can be accessed anywhere from a shader using the variable name without referencing the constant buffer name. + +Another way of declaring buffers with constants is via `ConstantBuffer` or `TextureBuffer` resource classes. This document currently focuses on the first style of declaration and primarily on `cbuffer`. + + +## Motivation + +We need to support constant buffers in Clang as they are a fundamental part of the HLSL language. + +## Proposed solution + +### Parsing cbuffer declaration + +In Clang frontend the `cbuffer` declarations will be parsed into a new AST Node called `HLSLConstantBufferDecl`. This class will be based on from `NameDecl` and `DeclContext`. + +Variable declarations inside the `cbuffer` context will be children of this new AST node. If a variable declaration specifies a `packoffset`, this information will be parsed into an attribute `HLSLPackOffsetAttr` and applied to the variable declaration. See [packoffset attribute](0003-packoffset.md). + +In order to make the variables declared in constant buffer exposed into global scope we can take advantage of `DeclContext::isTransparentContext` and make sure it is true for `HLSLConstantBufferDecl`. + +Because the syntax similarities the`tbuffer` declaration will also be using `HLSLConstantBufferDecl` AST node. The method `isCBuffer()` can be used to determine which kind of constant buffer the declaration represents. + +*Note: This is already implemented in Clang as `HLSLBufferDecl`. Since constant buffers are not the only buffers in HLSL we should rename it to `HLSLConstantBufferDecl`.* + +*Q: Does resource handle with typed attributes come into play here at all?* + +### Lowering cbuffer to LLVM + +Constant buffers will be lowered to LLVM target type `target(dx.CBuffer, ..)`. The LLVM target types can include a list of types and a list of integer constants. Any information needed for lowering to DXIL or SPIRV needs to be encoded using these parameters. + +To encode the shape of the `cbuffer` we can use the type parameter of the LLVM target type to be a struct with all of the `cbuffer` variable declarations. + +To encode the `packoffset` information we can use the list of integer constant on the target type. If there is no `packoffset` specified, the list would be empty. If the `cbuffer` variables have a `packoffset`, then the target type would contain a list of constant integers where `n`-th constant would either be a non-negative number specifying the packoffset of the `n`-th variable. + +**Note: `packoffset` offset must either be specified of all `cbuffer` variable declarations or on none.* + +For example: + +```c++ +cbuffer MyConstants { + float2 a : packoffset(c0.x); + int2 b : packoffset(c1.z); +} +``` + +Would be lowered to LLVM target type: + +``` +target("dx.CBuffer", %struct.MyConstants = type { <2 x float>, <2 x i32> }, 0, 6) +``` + +### Lowering cbuffer variable access + +Access to `cbuffer` variables would be lowered to LLVM in the same way and other resource types handle read-only subscript operator. The constant value access would be translated into a memory access in a specific "resource address space". This would be a simple "resource pointer arithmetic". + +Later, during lowering to DXIL, an LLVM pass would translate these specific "resource address space" memory accesses into `cbufferLoadLegacy` DXIL ops. This pass would take into account specific constant buffer layout rules and `packoffset` data, which are specific to DirectX. + +### Handle initialization + +Constant buffers will be initialized the same way as other resources using the `createHandleFromBinding` intrinsics. Module initialization will need to be updated to initialize all the constant buffers declared in a shader in addition to initialization of resource declared in global variables. + +## Detailed design + +*TBD* + +## Alternatives considered (Optional) + +Should we handle the constant buffer layout and `packoffset` info earlier? + +## Links + +[Shader Constants](https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-constants)
+[HLSL Constant Buffer Layout Visualizer](https://maraneshi.github.io/HLSL-ConstantBufferLayoutVisualizer)
+[packoffset attribute](0003-packoffset.md) + +## Acknowledgments (Optional) + +Take a moment to acknowledge the contributions of people other than the author +and sponsor. + + From f5a3fc80bcfa3b1bd028760ebe27f3f8485acc38 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Wed, 6 Nov 2024 12:22:20 -0800 Subject: [PATCH 02/14] use offsets always --- proposals/NNNN-constant-buffers.md | 35 ++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/proposals/NNNN-constant-buffers.md b/proposals/NNNN-constant-buffers.md index 5970e5e..57ce5b1 100644 --- a/proposals/NNNN-constant-buffers.md +++ b/proposals/NNNN-constant-buffers.md @@ -47,11 +47,9 @@ Because the syntax similarities the`tbuffer` declaration will also be using `HLS ### Lowering cbuffer to LLVM -Constant buffers will be lowered to LLVM target type `target(dx.CBuffer, ..)`. The LLVM target types can include a list of types and a list of integer constants. Any information needed for lowering to DXIL or SPIRV needs to be encoded using these parameters. +Constant buffers will be lowered to global variables with LLVM target type `target("dx.CBuffer", ..)`. In addition to the type name (`"dx.CBuffer"`) LLVM target types can also include a list of types and a list of integer constants. Any information needed for lowering to DXIL or SPIRV needs to be encoded using these parameters. -To encode the shape of the `cbuffer` we can use the type parameter of the LLVM target type to be a struct with all of the `cbuffer` variable declarations. - -To encode the `packoffset` information we can use the list of integer constant on the target type. If there is no `packoffset` specified, the list would be empty. If the `cbuffer` variables have a `packoffset`, then the target type would contain a list of constant integers where `n`-th constant would either be a non-negative number specifying the packoffset of the `n`-th variable. +To encode the shape of the `cbuffer` we can set the type parameter of the LLVM target type to be a struct with all of the `cbuffer` variable declarations. The list of integer constant can be used to encode the `cbuffer` memory layout where the number of constants in the list would be equal to the number of `cbuffer` variable declarations and `n`-th constant would contain the offset `n`-th variable in bytes. **Note: `packoffset` offset must either be specified of all `cbuffer` variable declarations or on none.* @@ -59,15 +57,31 @@ For example: ```c++ cbuffer MyConstants { - float2 a : packoffset(c0.x); - int2 b : packoffset(c1.z); + float2 a; + float b[2]; + int c; } ``` Would be lowered to LLVM target type: ``` -target("dx.CBuffer", %struct.MyConstants = type { <2 x float>, <2 x i32> }, 0, 6) +target("dx.CBuffer", %struct.MyConstants = type { <2 x float>, [2 x float], int }, 0, 16, 36) +``` + +In this example with `packoffset`: + +```c++ +cbuffer MyConstants { + float2 a : packoffset(c0.y); + int2 b : packoffset(c1.z); +} +``` + +The `cbuffer` type would be lowered to: + +``` +target("dx.CBuffer", %struct.MyConstants = type { <2 x float>, <2 x i32> }, 4, 24) ``` ### Lowering cbuffer variable access @@ -91,12 +105,9 @@ Should we handle the constant buffer layout and `packoffset` info earlier? ## Links [Shader Constants](https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-constants)
+[Packing Rules for Constant Variables](https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-packing-rules)
[HLSL Constant Buffer Layout Visualizer](https://maraneshi.github.io/HLSL-ConstantBufferLayoutVisualizer)
-[packoffset attribute](0003-packoffset.md) +[`packoffset` Attribute](0003-packoffset.md) ## Acknowledgments (Optional) -Take a moment to acknowledge the contributions of people other than the author -and sponsor. - - From 18114914b41a335dc641ebd2292213a7c0091ab2 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Wed, 6 Nov 2024 15:24:53 -0800 Subject: [PATCH 03/14] do not encode offsets in target type --- proposals/NNNN-constant-buffers.md | 31 ++++++++---------------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/proposals/NNNN-constant-buffers.md b/proposals/NNNN-constant-buffers.md index 57ce5b1..dd4f6da 100644 --- a/proposals/NNNN-constant-buffers.md +++ b/proposals/NNNN-constant-buffers.md @@ -47,11 +47,7 @@ Because the syntax similarities the`tbuffer` declaration will also be using `HLS ### Lowering cbuffer to LLVM -Constant buffers will be lowered to global variables with LLVM target type `target("dx.CBuffer", ..)`. In addition to the type name (`"dx.CBuffer"`) LLVM target types can also include a list of types and a list of integer constants. Any information needed for lowering to DXIL or SPIRV needs to be encoded using these parameters. - -To encode the shape of the `cbuffer` we can set the type parameter of the LLVM target type to be a struct with all of the `cbuffer` variable declarations. The list of integer constant can be used to encode the `cbuffer` memory layout where the number of constants in the list would be equal to the number of `cbuffer` variable declarations and `n`-th constant would contain the offset `n`-th variable in bytes. - -**Note: `packoffset` offset must either be specified of all `cbuffer` variable declarations or on none.* +Constant buffers will be lowered to global variables with LLVM target type `target("dx.CBuffer", ..)`. In addition to the type name (`"dx.CBuffer"`) LLVM target types can also include a list of types and a list of integer constants. Any information needed for lowering to DXIL or SPIRV needs to be encoded using these parameters. To encode the shape of the `cbuffer` we can set the type parameter of the LLVM target type to be a struct with all of the `cbuffer` variable declarations. For example: @@ -66,29 +62,18 @@ cbuffer MyConstants { Would be lowered to LLVM target type: ``` -target("dx.CBuffer", %struct.MyConstants = type { <2 x float>, [2 x float], int }, 0, 16, 36) -``` - -In this example with `packoffset`: - -```c++ -cbuffer MyConstants { - float2 a : packoffset(c0.y); - int2 b : packoffset(c1.z); -} +@MyConstants.cb = global target("dx.CBuffer", %struct.MyConstants = type { <2 x float>, [2 x float], int }) ``` -The `cbuffer` type would be lowered to: +### Lowering cbuffer variable access -``` -target("dx.CBuffer", %struct.MyConstants = type { <2 x float>, <2 x i32> }, 4, 24) -``` +The layout of constant buffers will be calculated during codegen in `CGHLSLRuntime`, which will also take into account `packoffset` attributes. -### Lowering cbuffer variable access +Access to `cbuffer` variables will be lowered to LLVM IR the same way as other resource types lower read-only access via subscript operator, except it will use the calculated layout offset. The constant value access would be translated into a memory access in a specific "resource address space" using the `cbuffer` global variable and offset. -Access to `cbuffer` variables would be lowered to LLVM in the same way and other resource types handle read-only subscript operator. The constant value access would be translated into a memory access in a specific "resource address space". This would be a simple "resource pointer arithmetic". +### DXIL Lowering -Later, during lowering to DXIL, an LLVM pass would translate these specific "resource address space" memory accesses into `cbufferLoadLegacy` DXIL ops. This pass would take into account specific constant buffer layout rules and `packoffset` data, which are specific to DirectX. +Later, during lowering to DXIL, an LLVM pass would translate these specific "resource address space" memory accesses into `cbufferLoadLegacy` DXIL ops. This pass would take into account specific constant buffer layout rules (loading data one row at a time and extracing specific elements). ### Handle initialization @@ -100,7 +85,7 @@ Constant buffers will be initialized the same way as other resources using the ` ## Alternatives considered (Optional) -Should we handle the constant buffer layout and `packoffset` info earlier? +Should we handle the constant buffer layout and `packoffset` later? Should we encode it int into the CBuffer LLVM target type? ## Links From 41329425d88bacd0362f562668297cbd2d67e854 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Thu, 7 Nov 2024 11:01:03 -0800 Subject: [PATCH 04/14] add ConstantBuffers --- proposals/NNNN-constant-buffers.md | 54 ++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/proposals/NNNN-constant-buffers.md b/proposals/NNNN-constant-buffers.md index dd4f6da..e1a47cd 100644 --- a/proposals/NNNN-constant-buffers.md +++ b/proposals/NNNN-constant-buffers.md @@ -11,19 +11,27 @@ Shader inputs usually include a number of constants which are stored in one or m From the compiler point of view constant buffers and texture buffers are very similar. The major difference is that constant buffers load from a constant buffer view (CBV) and bind to register `b` while texture buffers load from a typed buffer (SRV) and bind to the `t` register. -Declaring a constant buffer or a texture buffer looks very much like a structure declaration in C, with the addition of the register and packoffset keywords for manually assigning registers or packing data. +Declaring a constant buffer or a texture buffer looks very much like a structure declaration in C, with the addition of the register and packoffset keywords for manually assigning registers or packing data. For example: -``` -[cbuffer|tbuffer] ConstBufferName [register(b#)|register(t#)] { - VariableDeclaration [ : packoffset() ]; - ... +```c++ +cbuffer MyConstant register(b1) { + float4 F; } ``` -Constant buffer variables can be accessed anywhere from a shader using the variable name without referencing the constant buffer name. +Constant buffer variables can be accessed anywhere from a shader using the variable name `F` without referencing the constant buffer name . -Another way of declaring buffers with constants is via `ConstantBuffer` or `TextureBuffer` resource classes. This document currently focuses on the first style of declaration and primarily on `cbuffer`. +Another way of declaring buffers with constants is via `ConstantBuffer` or `TextureBuffer` resource classes: +```c++ +struct MyConstants { + float4 F; +}; + +ConstantBuffer CB; +``` + +In this case the buffer variables are reference as if they were members of the `ConstantBuffer` class: `CB.F`. ## Motivation @@ -31,11 +39,11 @@ We need to support constant buffers in Clang as they are a fundamental part of t ## Proposed solution -### Parsing cbuffer declaration +### Parsing cbuffer/tbuffer declaration -In Clang frontend the `cbuffer` declarations will be parsed into a new AST Node called `HLSLConstantBufferDecl`. This class will be based on from `NameDecl` and `DeclContext`. +In Clang frontend the `cbuffer` and `tbuffer` declarations will be parsed into a new AST Node called `HLSLConstantBufferDecl`. This class will be based on from `NameDecl` and `DeclContext`. -Variable declarations inside the `cbuffer` context will be children of this new AST node. If a variable declaration specifies a `packoffset`, this information will be parsed into an attribute `HLSLPackOffsetAttr` and applied to the variable declaration. See [packoffset attribute](0003-packoffset.md). +Variable declarations inside the `cbuffer` or `tbuffer` context will be children of this new AST node. If a variable declaration specifies a `packoffset`, this information will be parsed into an attribute `HLSLPackOffsetAttr` and applied to the variable declaration. See [packoffset attribute](0003-packoffset.md). In order to make the variables declared in constant buffer exposed into global scope we can take advantage of `DeclContext::isTransparentContext` and make sure it is true for `HLSLConstantBufferDecl`. @@ -45,9 +53,15 @@ Because the syntax similarities the`tbuffer` declaration will also be using `HLS *Q: Does resource handle with typed attributes come into play here at all?* -### Lowering cbuffer to LLVM +### Parsing ConstantBuffer/TextureBuffer declaration + +`ConstantBuffer`/`TextureBuffer` will be added to the `HLSLExternalSemaSource` the same way other resource buffers are added. At the same time Clang needs to recognize these classes represents constant buffers. + +One way to do that is to create `HLSLConstantBufferDecl` instance in addition to the `ConstantBuffer`, basically treating `ConstantBuffer CB;` as `cbuffer CB { MyConstants CB; }`. The constant buffer declaration would need to keep track that is it based on `ConstantBuffer` declaration. -Constant buffers will be lowered to global variables with LLVM target type `target("dx.CBuffer", ..)`. In addition to the type name (`"dx.CBuffer"`) LLVM target types can also include a list of types and a list of integer constants. Any information needed for lowering to DXIL or SPIRV needs to be encoded using these parameters. To encode the shape of the `cbuffer` we can set the type parameter of the LLVM target type to be a struct with all of the `cbuffer` variable declarations. +### Lowering cbuffer to LLVM IR + +Constant buffers will be lowered to global variables with LLVM target type `target("dx.CBuffer", ..)`. In addition to the type name (`"dx.CBuffer"`) LLVM target types can also include a list of types and a list of integer constants. Any information needed for lowering to DXIL or SPIRV needs to be encoded using these parameters. To encode the shape of the constant buffers we can set the type parameter of the LLVM target type to be a struct with all of the buffer variable declarations. For example: @@ -65,7 +79,15 @@ Would be lowered to LLVM target type: @MyConstants.cb = global target("dx.CBuffer", %struct.MyConstants = type { <2 x float>, [2 x float], int }) ``` -### Lowering cbuffer variable access +### Lowering ConstantBuffer to LLVM IR + +The result of codegen for `cbuffer` and `ConstantBuffer` should be identical, or at least very close. + +### Lowering `tbuffer` and `TextureBuffer` to LLVM IR + +These should be lowered to `target("dx.TypedBuffer", ..)`.Detailed design TBD. + +### Lowering constant buffer variable access The layout of constant buffers will be calculated during codegen in `CGHLSLRuntime`, which will also take into account `packoffset` attributes. @@ -77,7 +99,11 @@ Later, during lowering to DXIL, an LLVM pass would translate these specific "res ### Handle initialization -Constant buffers will be initialized the same way as other resources using the `createHandleFromBinding` intrinsics. Module initialization will need to be updated to initialize all the constant buffers declared in a shader in addition to initialization of resource declared in global variables. +Constant buffers will be initialized the same way as other resources using the `createHandleFromBinding` intrinsics. Module initialization will need to be updated to initialize all the constant buffers declared in a shader in addition to initialization of resources declared in global variables. + +### Constant buffers metadata + +TBD ## Detailed design From 2d1b1f55ecbc5435b343e24ec9058164e6cadf02 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Tue, 26 Nov 2024 14:51:16 -0800 Subject: [PATCH 05/14] Update doc after design meeting discussion; still many open questions --- proposals/NNNN-constant-buffers.md | 86 ++++++++++++++++-------------- 1 file changed, 47 insertions(+), 39 deletions(-) diff --git a/proposals/NNNN-constant-buffers.md b/proposals/NNNN-constant-buffers.md index e1a47cd..44ed2f3 100644 --- a/proposals/NNNN-constant-buffers.md +++ b/proposals/NNNN-constant-buffers.md @@ -7,21 +7,19 @@ ## Introduction -Shader inputs usually include a number of constants which are stored in one or more buffer resources in memory with specific packing rules. These resources can be organized into two types of buffers: constant buffers and texture buffers. +Shader inputs usually include a number of constants which are stored in one or more buffer resources in memory with specific packing rules. These resources can be organized into two types of buffers: constant buffers and texture buffers. This document describes design decisions related to constant buffers. -From the compiler point of view constant buffers and texture buffers are very similar. The major difference is that constant buffers load from a constant buffer view (CBV) and bind to register `b` while texture buffers load from a typed buffer (SRV) and bind to the `t` register. - -Declaring a constant buffer or a texture buffer looks very much like a structure declaration in C, with the addition of the register and packoffset keywords for manually assigning registers or packing data. For example: +Constant buffer loads from a constant buffer view (CBV) and binds to register `b`. It can be declared using the `cbuffer` keyword and it looks very much like a structure declaration in C, with the addition of the register and packoffset keywords for manually assigning registers or packing data. For example: ```c++ -cbuffer MyConstant register(b1) { +cbuffer MyConstant : register(b1) { float4 F; } ``` -Constant buffer variables can be accessed anywhere from a shader using the variable name `F` without referencing the constant buffer name . +Constant buffer variables declared within the `cbuffer` scope can be accessed anywhere from a shader by directly using the variable name (`F`) without referencing the name of the constant buffer. -Another way of declaring buffers with constants is via `ConstantBuffer` or `TextureBuffer` resource classes: +Another way of declaring constant buffers is with via `ConstantBuffer` class: ```c++ struct MyConstants { @@ -31,39 +29,41 @@ struct MyConstants { ConstantBuffer CB; ``` -In this case the buffer variables are reference as if they were members of the `ConstantBuffer` class: `CB.F`. +In this case the buffer variables are referenced as if they were members of the `ConstantBuffer` class: `CB.F`. ## Motivation We need to support constant buffers in Clang as they are a fundamental part of the HLSL language. -## Proposed solution - -### Parsing cbuffer/tbuffer declaration +## Proposed Solution -In Clang frontend the `cbuffer` and `tbuffer` declarations will be parsed into a new AST Node called `HLSLConstantBufferDecl`. This class will be based on from `NameDecl` and `DeclContext`. +### Parsing `cbuffer` Declaration -Variable declarations inside the `cbuffer` or `tbuffer` context will be children of this new AST node. If a variable declaration specifies a `packoffset`, this information will be parsed into an attribute `HLSLPackOffsetAttr` and applied to the variable declaration. See [packoffset attribute](0003-packoffset.md). +In Clang frontend the `cbuffer` declaration will be parsed into a new AST Node called `HLSLConstantBufferDecl`. This class will be based on from `NameDecl` and `DeclContext`. -In order to make the variables declared in constant buffer exposed into global scope we can take advantage of `DeclContext::isTransparentContext` and make sure it is true for `HLSLConstantBufferDecl`. +Variable declarations inside the `cbuffer` context will be children of this new AST node. If a variable declaration specifies a `packoffset`, this information will be parsed into an attribute `HLSLPackOffsetAttr` and applied to the variable declaration. See [packoffset attribute](0003-packoffset.md). -Because the syntax similarities the`tbuffer` declaration will also be using `HLSLConstantBufferDecl` AST node. The method `isCBuffer()` can be used to determine which kind of constant buffer the declaration represents. +In order to make the variables declared in constant buffer exposed into global scope we can take advantage of `DeclContext::isTransparentContext` method and overload it to return true for `HLSLConstantBufferDecl`. This is the same way variables declared in `export` declaration context are exposed at the global scope. *Note: This is already implemented in Clang as `HLSLBufferDecl`. Since constant buffers are not the only buffers in HLSL we should rename it to `HLSLConstantBufferDecl`.* -*Q: Does resource handle with typed attributes come into play here at all?* +### Parsing `ConstantBuffer` declaration + +`ConstantBuffer` definition will be added to the `HLSLExternalSemaSource` the same way as other resource classes. It will have a resource handle with `CBuffer` resource class and the contained type would be the template type argument. It will be handled as other resources classes, for example it can be passed into a function. -### Parsing ConstantBuffer/TextureBuffer declaration +At the same time Clang needs to recognize this class represents a constant buffer and the contained type fields are accessed using the `.` operator on `ConstantBuffer` instance. In other words treating `ConstantBuffer CB;` as if it was declared as `cbuffer __CB { MyConstants CB; }`. The exact way how to do this is TBD. -`ConstantBuffer`/`TextureBuffer` will be added to the `HLSLExternalSemaSource` the same way other resource buffers are added. At the same time Clang needs to recognize these classes represents constant buffers. +### Lowering Constant Buffers to LLVM IR -One way to do that is to create `HLSLConstantBufferDecl` instance in addition to the `ConstantBuffer`, basically treating `ConstantBuffer CB;` as `cbuffer CB { MyConstants CB; }`. The constant buffer declaration would need to keep track that is it based on `ConstantBuffer` declaration. +During CodeGen constant buffers will be lowered to global variables with LLVM target type `target("dx.CBuffer", ..)` which will include type information about constants, the buffer size and its memory layout. -### Lowering cbuffer to LLVM IR +Note: LLVM target types can optionally include a list of one or more types and a list of one or more integer constants. We can use these lists to encode any information needed for lowering from LLVM IR to DXIL and SPIRV. -Constant buffers will be lowered to global variables with LLVM target type `target("dx.CBuffer", ..)`. In addition to the type name (`"dx.CBuffer"`) LLVM target types can also include a list of types and a list of integer constants. Any information needed for lowering to DXIL or SPIRV needs to be encoded using these parameters. To encode the shape of the constant buffers we can set the type parameter of the LLVM target type to be a struct with all of the buffer variable declarations. +To encode the shape of the constant buffer the LLVM target type will include a structure type that represents the constant buffer variable declarations. -For example: +The size of the constant buffer will be included as the first item in the list of integer constants. The rest of the list will be used to encode the constant buffer layout. The layout will always be included whether the constant buffer uses any `packoffset` attributes or not. The exact way how the layout will be encoded is TBD and will be covered in a separate design document. + +For simplicity, let's assume the layout will be encoded as a list of offsets of all cbuffer declarations. In that case this example: ```c++ cbuffer MyConstants { @@ -76,49 +76,57 @@ cbuffer MyConstants { Would be lowered to LLVM target type: ``` -@MyConstants.cb = global target("dx.CBuffer", %struct.MyConstants = type { <2 x float>, [2 x float], int }) +@MyConstants.cb = global target("dx.CBuffer", { <2 x float>, [2 x float], i32}, 40, 0, 16, 32, 36) ``` -### Lowering ConstantBuffer to LLVM IR +This layout encoding can obviously get very long and unwieldy for more complicated cbuffers, and especially since target type parameters are all included in the name mangling for function overloads. We need to investigate how to make it smaller, or at least more manageable. -The result of codegen for `cbuffer` and `ConstantBuffer` should be identical, or at least very close. +One possibility is compressing the list of offsets into a smaller number of integers - taking advantage of the fact that outside of `packoffset` use the difference between two adjancent offsets is never more than 16. The compression could also include repetition construct that would help with encoding of array offset. But, as with any compressions, there's always the chance of degenerate cases that will end up with the compressed shape being the same size or larger than the original. -### Lowering `tbuffer` and `TextureBuffer` to LLVM IR +> Note: The most tricky part of the layout encoding are probably arrays of structures because the structure-specific layout gets repeated many times and might not be easy to compress. One idea how to solve this could be translating structures embedded in `cbuffer` into separate target types with their own encoded layout and including them in the `cbuffer` target type. It is not clear though if this is possible (probably yes) or if it would actually make things easier or not. -These should be lowered to `target("dx.TypedBuffer", ..)`.Detailed design TBD. +Another way could be introducing a typedef concept into the LLVM IR textual representation so that the full LLVM target type with long layout representation could occur just once. -### Lowering constant buffer variable access +### Lowering ConstantBuffer to LLVM IR -The layout of constant buffers will be calculated during codegen in `CGHLSLRuntime`, which will also take into account `packoffset` attributes. +The result of codegen for `cbuffer` and `ConstantBuffer` code should be identical. -Access to `cbuffer` variables will be lowered to LLVM IR the same way as other resource types lower read-only access via subscript operator, except it will use the calculated layout offset. The constant value access would be translated into a memory access in a specific "resource address space" using the `cbuffer` global variable and offset. +### Lowering Constant Buffer Variable Access + +Accesses to `cbuffer` variables will be lowered to LLVM IR as memory accesses in specific "resource address space" using the standard C++ structure layout rules. ### DXIL Lowering -Later, during lowering to DXIL, an LLVM pass would translate these specific "resource address space" memory accesses into `cbufferLoadLegacy` DXIL ops. This pass would take into account specific constant buffer layout rules (loading data one row at a time and extracing specific elements). +LLVM pass `DXILResourceAccess` will translate these specific "resource address space" memory accesses into cbuffer DXIL ops adjusting the offsets using the cbuffer layout information encoded in `target("dx.CBuffer", ..)`. That means translating standand C++ structure layout offsets to cbuffer layout offsets and replacing the memory accesses with `llvm.dx.cbufferBufferLoad`, `llvm.dx.cbufferBufferStore`, and `extractelement ` instructions. The load and store instructions will be later lowered to `cbufferLoadLegacy` and `cbufferStoreLegacy` DXIL ops. ### Handle initialization -Constant buffers will be initialized the same way as other resources using the `createHandleFromBinding` intrinsics. Module initialization will need to be updated to initialize all the constant buffers declared in a shader in addition to initialization of resources declared in global variables. - -### Constant buffers metadata - -TBD +Constant buffers will be initialized the same way as other resources using the `createHandleFromBinding` intrinsics. Module initialization code need to be updated to include all constant buffers declared in a shader. ## Detailed design *TBD* -## Alternatives considered (Optional) +## Alternatives considered + +- Generate access to `cbuffer` varibles as memory accesses with the offset based on cbuffer layout and treat cbuffers as one big type-less memory blob in LLVM IR. + - There is a concern that losing the type information could lead to unnecessary copying of values. + +- 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. -Should we handle the constant buffer layout and `packoffset` later? Should we encode it int into the CBuffer LLVM target type? +## Open issues +- How to encode the cbuffer layout into LLVM target type +- How to implement `ConstantBuffer` member access +- Handling of `$Globals` constant buffer +- Nested `cbuffer` declarations ## Links [Shader Constants](https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-constants)
[Packing Rules for Constant Variables](https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-packing-rules)
[HLSL Constant Buffer Layout Visualizer](https://maraneshi.github.io/HLSL-ConstantBufferLayoutVisualizer)
-[`packoffset` Attribute](0003-packoffset.md) +[`packoffset` Attribute](0003-packoffset.md)
## Acknowledgments (Optional) From 61a0ec6d6cd14a28cf6ce78cbc9cb8f0be3e0d49 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Tue, 26 Nov 2024 15:54:55 -0800 Subject: [PATCH 06/14] wrap to 80 --- proposals/NNNN-constant-buffers.md | 144 ++++++++++++++++++++++------- 1 file changed, 110 insertions(+), 34 deletions(-) diff --git a/proposals/NNNN-constant-buffers.md b/proposals/NNNN-constant-buffers.md index 44ed2f3..e0711d3 100644 --- a/proposals/NNNN-constant-buffers.md +++ b/proposals/NNNN-constant-buffers.md @@ -7,9 +7,15 @@ ## Introduction -Shader inputs usually include a number of constants which are stored in one or more buffer resources in memory with specific packing rules. These resources can be organized into two types of buffers: constant buffers and texture buffers. This document describes design decisions related to constant buffers. +Shader inputs usually include a number of constants which are stored in one or +more buffer resources in memory with specific packing rules. These resources can +be organized into two types of buffers: constant buffers and texture buffers. +This document describes design decisions related to constant buffers. -Constant buffer loads from a constant buffer view (CBV) and binds to register `b`. It can be declared using the `cbuffer` keyword and it looks very much like a structure declaration in C, with the addition of the register and packoffset keywords for manually assigning registers or packing data. For example: +Constant buffer loads from a constant buffer view (CBV) and binds to register +`b`. It can be declared using the `cbuffer` keyword and it looks very much like +a structure declaration in C, with the addition of the register and packoffset +keywords for manually assigning registers or packing data. For example: ```c++ cbuffer MyConstant : register(b1) { @@ -17,7 +23,9 @@ cbuffer MyConstant : register(b1) { } ``` -Constant buffer variables declared within the `cbuffer` scope can be accessed anywhere from a shader by directly using the variable name (`F`) without referencing the name of the constant buffer. +Constant buffer variables declared within the `cbuffer` scope can be accessed +anywhere from a shader by directly using the variable name (`F`) without +referencing the name of the constant buffer. Another way of declaring constant buffers is with via `ConstantBuffer` class: @@ -29,41 +37,72 @@ struct MyConstants { ConstantBuffer CB; ``` -In this case the buffer variables are referenced as if they were members of the `ConstantBuffer` class: `CB.F`. +In this case the buffer variables are referenced as if they were members of the +`ConstantBuffer` class: `CB.F`. ## Motivation -We need to support constant buffers in Clang as they are a fundamental part of the HLSL language. +We need to support constant buffers in Clang as they are a fundamental part of +the HLSL language. ## Proposed Solution ### Parsing `cbuffer` Declaration -In Clang frontend the `cbuffer` declaration will be parsed into a new AST Node called `HLSLConstantBufferDecl`. This class will be based on from `NameDecl` and `DeclContext`. +In Clang frontend the `cbuffer` declaration will be parsed into a new AST Node +called `HLSLConstantBufferDecl`. This class will be based on from `NameDecl` and +`DeclContext`. -Variable declarations inside the `cbuffer` context will be children of this new AST node. If a variable declaration specifies a `packoffset`, this information will be parsed into an attribute `HLSLPackOffsetAttr` and applied to the variable declaration. See [packoffset attribute](0003-packoffset.md). +Variable declarations inside the `cbuffer` context will be children of this new +AST node. If a variable declaration specifies a `packoffset`, this information +will be parsed into an attribute `HLSLPackOffsetAttr` and applied to the +variable declaration. See [packoffset attribute](0003-packoffset.md). -In order to make the variables declared in constant buffer exposed into global scope we can take advantage of `DeclContext::isTransparentContext` method and overload it to return true for `HLSLConstantBufferDecl`. This is the same way variables declared in `export` declaration context are exposed at the global scope. +In order to make the variables declared in constant buffer exposed into global +scope we can take advantage of `DeclContext::isTransparentContext` method and +overload it to return true for `HLSLConstantBufferDecl`. This is the same way +variables declared in `export` declaration context are exposed at the global +scope. -*Note: This is already implemented in Clang as `HLSLBufferDecl`. Since constant buffers are not the only buffers in HLSL we should rename it to `HLSLConstantBufferDecl`.* +*Note: This is already implemented in Clang as `HLSLBufferDecl`. Since constant +buffers are not the only buffers in HLSL we should rename it to +`HLSLConstantBufferDecl`.* ### Parsing `ConstantBuffer` declaration -`ConstantBuffer` definition will be added to the `HLSLExternalSemaSource` the same way as other resource classes. It will have a resource handle with `CBuffer` resource class and the contained type would be the template type argument. It will be handled as other resources classes, for example it can be passed into a function. +`ConstantBuffer` definition will be added to the `HLSLExternalSemaSource` the +same way as other resource classes. It will have a resource handle with +`CBuffer` resource class and the contained type would be the template type +argument. It will be handled as other resources classes, for example it can be +passed into a function. -At the same time Clang needs to recognize this class represents a constant buffer and the contained type fields are accessed using the `.` operator on `ConstantBuffer` instance. In other words treating `ConstantBuffer CB;` as if it was declared as `cbuffer __CB { MyConstants CB; }`. The exact way how to do this is TBD. +At the same time Clang needs to recognize this class represents a constant +buffer and the contained type fields are accessed using the `.` operator on +`ConstantBuffer` instance. In other words treating `ConstantBuffer +CB;` as if it was declared as `cbuffer __CB { MyConstants CB; }`. The exact way +how to do this is TBD. ### Lowering Constant Buffers to LLVM IR -During CodeGen constant buffers will be lowered to global variables with LLVM target type `target("dx.CBuffer", ..)` which will include type information about constants, the buffer size and its memory layout. +During CodeGen constant buffers will be lowered to global variables with LLVM +target type `target("dx.CBuffer", ..)` which will include type information about +constants, the buffer size and its memory layout. -Note: LLVM target types can optionally include a list of one or more types and a list of one or more integer constants. We can use these lists to encode any information needed for lowering from LLVM IR to DXIL and SPIRV. +Note: LLVM target types can optionally include a list of one or more types and a +list of one or more integer constants. We can use these lists to encode any +information needed for lowering from LLVM IR to DXIL and SPIRV. -To encode the shape of the constant buffer the LLVM target type will include a structure type that represents the constant buffer variable declarations. +To encode the shape of the constant buffer the LLVM target type will include a +structure type that represents the constant buffer variable declarations. -The size of the constant buffer will be included as the first item in the list of integer constants. The rest of the list will be used to encode the constant buffer layout. The layout will always be included whether the constant buffer uses any `packoffset` attributes or not. The exact way how the layout will be encoded is TBD and will be covered in a separate design document. +The size of the constant buffer will be included as the first item in the list +of integer constants. The rest of the list will be used to encode the constant +buffer layout. The layout will always be included whether the constant buffer +uses any `packoffset` attributes or not. The exact way how the layout will be +encoded is TBD and will be covered in a separate design document. -For simplicity, let's assume the layout will be encoded as a list of offsets of all cbuffer declarations. In that case this example: +For simplicity, let's assume the layout will be encoded as a list of offsets of +all cbuffer declarations. In that case this example: ```c++ cbuffer MyConstants { @@ -79,29 +118,57 @@ Would be lowered to LLVM target type: @MyConstants.cb = global target("dx.CBuffer", { <2 x float>, [2 x float], i32}, 40, 0, 16, 32, 36) ``` -This layout encoding can obviously get very long and unwieldy for more complicated cbuffers, and especially since target type parameters are all included in the name mangling for function overloads. We need to investigate how to make it smaller, or at least more manageable. - -One possibility is compressing the list of offsets into a smaller number of integers - taking advantage of the fact that outside of `packoffset` use the difference between two adjancent offsets is never more than 16. The compression could also include repetition construct that would help with encoding of array offset. But, as with any compressions, there's always the chance of degenerate cases that will end up with the compressed shape being the same size or larger than the original. - -> Note: The most tricky part of the layout encoding are probably arrays of structures because the structure-specific layout gets repeated many times and might not be easy to compress. One idea how to solve this could be translating structures embedded in `cbuffer` into separate target types with their own encoded layout and including them in the `cbuffer` target type. It is not clear though if this is possible (probably yes) or if it would actually make things easier or not. - -Another way could be introducing a typedef concept into the LLVM IR textual representation so that the full LLVM target type with long layout representation could occur just once. +This layout encoding can obviously get very long and unwieldy for more +complicated cbuffers, and especially since target type parameters are all +included in the name mangling for function overloads. We need to investigate how +to make it smaller, or at least more manageable. + +One possibility is compressing the list of offsets into a smaller number of +integers - taking advantage of the fact that outside of `packoffset` use the +difference between two adjancent offsets is never more than 16. The compression +could also include repetition construct that would help with encoding of array +offset. But, as with any compressions, there's always the chance of degenerate +cases that will end up with the compressed shape being the same size or larger +than the original. + +> Note: The most tricky part of the layout encoding are probably arrays of +> structures because the structure-specific layout gets repeated many times and +> might not be easy to compress. One idea how to solve this could be translating +> structures embedded in `cbuffer` into separate target types with their own +> encoded layout and including them in the `cbuffer` target type. It is not +> clear though if this is possible (probably yes) or if it would actually make +> things easier or not. + +Another way could be introducing a typedef concept into the LLVM IR textual +representation so that the full LLVM target type with long layout representation +could occur just once. ### Lowering ConstantBuffer to LLVM IR -The result of codegen for `cbuffer` and `ConstantBuffer` code should be identical. +The result of codegen for `cbuffer` and `ConstantBuffer` code should be +identical. ### Lowering Constant Buffer Variable Access -Accesses to `cbuffer` variables will be lowered to LLVM IR as memory accesses in specific "resource address space" using the standard C++ structure layout rules. +Accesses to `cbuffer` variables will be lowered to LLVM IR as memory accesses in +specific "resource address space" using the standard C++ structure layout rules. ### DXIL Lowering -LLVM pass `DXILResourceAccess` will translate these specific "resource address space" memory accesses into cbuffer DXIL ops adjusting the offsets using the cbuffer layout information encoded in `target("dx.CBuffer", ..)`. That means translating standand C++ structure layout offsets to cbuffer layout offsets and replacing the memory accesses with `llvm.dx.cbufferBufferLoad`, `llvm.dx.cbufferBufferStore`, and `extractelement ` instructions. The load and store instructions will be later lowered to `cbufferLoadLegacy` and `cbufferStoreLegacy` DXIL ops. +LLVM pass `DXILResourceAccess` will translate these specific "resource address +space" memory accesses into cbuffer DXIL ops adjusting the offsets using the +cbuffer layout information encoded in `target("dx.CBuffer", ..)`. That means +translating standand C++ structure layout offsets to cbuffer layout offsets and +replacing the memory accesses with `llvm.dx.cbufferBufferLoad`, +`llvm.dx.cbufferBufferStore`, and `extractelement ` instructions. The load and +store instructions will be later lowered to `cbufferLoadLegacy` and +`cbufferStoreLegacy` DXIL ops. ### Handle initialization -Constant buffers will be initialized the same way as other resources using the `createHandleFromBinding` intrinsics. Module initialization code need to be updated to include all constant buffers declared in a shader. +Constant buffers will be initialized the same way as other resources using the +`createHandleFromBinding` intrinsics. Module initialization code need to be +updated to include all constant buffers declared in a shader. ## Detailed design @@ -109,11 +176,17 @@ Constant buffers will be initialized the same way as other resources using the ` ## Alternatives considered -- Generate access to `cbuffer` varibles as memory accesses with the offset based on cbuffer layout and treat cbuffers as one big type-less memory blob in LLVM IR. - - There is a concern that losing the type information could lead to unnecessary copying of values. +- Generate access to `cbuffer` varibles as memory accesses with the offset based + on cbuffer layout and treat cbuffers as one big type-less memory blob in LLVM + IR. + - There is a concern that losing the type information could lead to + unnecessary copying of values. -- 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. +- 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. ## Open issues - How to encode the cbuffer layout into LLVM target type @@ -123,9 +196,12 @@ Constant buffers will be initialized the same way as other resources using the ` ## Links -[Shader Constants](https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-constants)
-[Packing Rules for Constant Variables](https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-packing-rules)
-[HLSL Constant Buffer Layout Visualizer](https://maraneshi.github.io/HLSL-ConstantBufferLayoutVisualizer)
+[Shader +Constants](https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-constants)
+[Packing Rules for Constant +Variables](https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-packing-rules)
+[HLSL Constant Buffer Layout +Visualizer](https://maraneshi.github.io/HLSL-ConstantBufferLayoutVisualizer)
[`packoffset` Attribute](0003-packoffset.md)
## Acknowledgments (Optional) From 20fef57d5f6080b36284b23e7cb85816bf0695cd Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Mon, 2 Dec 2024 11:11:42 -0800 Subject: [PATCH 07/14] cr feedback - fix wording --- proposals/NNNN-constant-buffers.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/proposals/NNNN-constant-buffers.md b/proposals/NNNN-constant-buffers.md index e0711d3..b7ae73b 100644 --- a/proposals/NNNN-constant-buffers.md +++ b/proposals/NNNN-constant-buffers.md @@ -37,8 +37,8 @@ struct MyConstants { ConstantBuffer CB; ``` -In this case the buffer variables are referenced as if they were members of the -`ConstantBuffer` class: `CB.F`. +In this case the buffer variables are referenced as if `CB` was of type +`MyConstants` and the fields are members of that object. ## Motivation @@ -50,7 +50,7 @@ the HLSL language. ### Parsing `cbuffer` Declaration In Clang frontend the `cbuffer` declaration will be parsed into a new AST Node -called `HLSLConstantBufferDecl`. This class will be based on from `NameDecl` and +called `HLSLConstantBufferDecl`. This class will be based on `NameDecl` and `DeclContext`. Variable declarations inside the `cbuffer` context will be children of this new @@ -76,11 +76,11 @@ same way as other resource classes. It will have a resource handle with argument. It will be handled as other resources classes, for example it can be passed into a function. -At the same time Clang needs to recognize this class represents a constant -buffer and the contained type fields are accessed using the `.` operator on -`ConstantBuffer` instance. In other words treating `ConstantBuffer -CB;` as if it was declared as `cbuffer __CB { MyConstants CB; }`. The exact way -how to do this is TBD. +Clang needs to recognize that this class represents a constant buffer and the +contained type fields are accessed using the `.` operator on `ConstantBuffer` +instance. In other words treating `ConstantBuffer CB;` as if it was +declared as `cbuffer __CB { MyConstants CB; }`. The exact way how to do this is +TBD. ### Lowering Constant Buffers to LLVM IR From 466cc11ceaa6bd0c0bbe32e56cb513c8228d094e Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Wed, 22 Jan 2025 12:35:24 -0800 Subject: [PATCH 08/14] Update - use hlsl_constant address space and a pass to lower constant access to cbuffer load intrinsics --- proposals/NNNN-constant-buffers.md | 290 ++++++++++++++++++----------- 1 file changed, 185 insertions(+), 105 deletions(-) diff --git a/proposals/NNNN-constant-buffers.md b/proposals/NNNN-constant-buffers.md index b7ae73b..5e3399d 100644 --- a/proposals/NNNN-constant-buffers.md +++ b/proposals/NNNN-constant-buffers.md @@ -12,10 +12,17 @@ more buffer resources in memory with specific packing rules. These resources can be organized into two types of buffers: constant buffers and texture buffers. This document describes design decisions related to constant buffers. -Constant buffer loads from a constant buffer view (CBV) and binds to register -`b`. It can be declared using the `cbuffer` keyword and it looks very much like -a structure declaration in C, with the addition of the register and packoffset -keywords for manually assigning registers or packing data. For example: +Constant buffers load from a constant buffer views (CBVs) and binds to registers +`b`. + +There are three ways to declare a constant buffer in HLSL. + +### `cbuffer` Declaration Block + +A constant buffer can be declared using the `cbuffer` keyword. This looks very +much like a structure declaration in C with the addition of `register` and +`packoffset` keywords for manually assigning binding register or packing info. +For example: ```c++ cbuffer MyConstant : register(b1) { @@ -23,11 +30,31 @@ cbuffer MyConstant : register(b1) { } ``` -Constant buffer variables declared within the `cbuffer` scope can be accessed -anywhere from a shader by directly using the variable name (`F`) without -referencing the name of the constant buffer. +Variables declared within the `cbuffer` scope can be accessed anywhere in a +shader by directly using the variable name (`F`) without referencing the name of +the constant buffer. Note that the name of the constant buffer is not a +recognized identifier and does not actually have to be unique. -Another way of declaring constant buffers is with via `ConstantBuffer` class: +### Default Constant Buffer `$Globals` + +Any variable declaration in global scope that is not static and is not a +resource is implicitly added to a default constant buffer named `$Global`. That +means a global scope declaration like this: + +```c++ +float4 F; +``` +is equivalent to + +```c++ +cbuffer $Globals { + float4 F; +} +``` + +### `ConstantBuffer` Resource Class + +Third way of declaring constant buffers is by using the `ConstantBuffer` class: ```c++ struct MyConstants { @@ -38,7 +65,8 @@ ConstantBuffer CB; ``` In this case the buffer variables are referenced as if `CB` was of type -`MyConstants` and the fields are members of that object. +`MyConstants`. In other words, the float value in `MyConstants` struct is +referenced as `CB.F`. ## Motivation @@ -47,128 +75,180 @@ the HLSL language. ## Proposed Solution -### Parsing `cbuffer` Declaration +### `hlsl_constant` address space + +Constant buffer views (CBV) will be treated as a storage class with a new +address space `hlsl_constant` with value `2` for DXIL. Constant buffer elements +will be generated as global variables in `hlsl_constant` address space. Later on +in the backend there will be a pass that will collects all `addrspace(2)` +globals and loads from this address space and replace them with constant buffer +load intrinsics off a CBV handle. + +### Parsing of `cbuffer` Declaration -In Clang frontend the `cbuffer` declaration will be parsed into a new AST Node -called `HLSLConstantBufferDecl`. This class will be based on `NameDecl` and -`DeclContext`. +In Clang frontend the `cbuffer` declaration will be parsed into a new AST node + `HLSLBufferDecl`. This class will be based on `NameDecl` and `DeclContext`. Variable declarations inside the `cbuffer` context will be children of this new -AST node. If a variable declaration specifies a `packoffset`, this information -will be parsed into an attribute `HLSLPackOffsetAttr` and applied to the -variable declaration. See [packoffset attribute](0003-packoffset.md). +AST node and will have `hlsl_constant` address space. -In order to make the variables declared in constant buffer exposed into global +If a variable declaration specifies a `packoffset`, this information will be +parsed into an `HLSLPackOffsetAttr` attribute and applied to the variable +declaration. See [packoffset attribute](0003-packoffset.md). + +In order to make the variables declared in `cbuffer` scope exposed into global scope we can take advantage of `DeclContext::isTransparentContext` method and -overload it to return true for `HLSLConstantBufferDecl`. This is the same way -variables declared in `export` declaration context are exposed at the global -scope. +overload it to return true for `HLSLBufferDecl`. This is the same way variables +declared in `export` declaration context are exposed at the global scope. + +### Layout Structure + +The `cbuffer` block can contain any declaration that can occur at a global +scope, but not all of the declarations correspond to data in the CBV and +contribute to the buffer layout. As part of the semantic analysis the +declarations in `cbuffer` scope will be processed into a layout struct that will +represent the actual content of the constant buffer. + +The layout struct will contain all declaration from the `cbuffer` block except: +- static variable declarations +- resource classes +- empty structs +- zero-sized arrays +- any non-variable declarations (functions, classes, ...) + +If the constant buffer includes a struct variable, this struct it will also need +to be inspected and transformed into a new layout struct if it contains any of +the undesired declarations above. -*Note: This is already implemented in Clang as `HLSLBufferDecl`. Since constant -buffers are not the only buffers in HLSL we should rename it to -`HLSLConstantBufferDecl`.* +For example for this `cbuffer` declaration: -### Parsing `ConstantBuffer` declaration +``` +struct Something { + int a; + float f[0]; // zero-sized array +}; -`ConstantBuffer` definition will be added to the `HLSLExternalSemaSource` the -same way as other resource classes. It will have a resource handle with -`CBuffer` resource class and the contained type would be the template type -argument. It will be handled as other resources classes, for example it can be -passed into a function. +cbuffer CB { + float x; + RWBuffer buf; // resource class + Something s; // embedded struct + static float y; // static variable +} +``` -Clang needs to recognize that this class represents a constant buffer and the -contained type fields are accessed using the `.` operator on `ConstantBuffer` -instance. In other words treating `ConstantBuffer CB;` as if it was -declared as `cbuffer __CB { MyConstants CB; }`. The exact way how to do this is -TBD. +The buffer layout struct will look like this: +``` + struct __layout_Something { + int a; + }; + + struct __layout_CB { + float x; + __layout_Something s; + }; +``` +All of the layout structs will be declared as `CXXRecordDecl`s in the +`HLSLBufferDecl` context. The layout struct for the constant buffer is going to +be the last `CXXRecordDecl` child of `HLSLBufferDecl`. -### Lowering Constant Buffers to LLVM IR +### Default Constant Buffer -During CodeGen constant buffers will be lowered to global variables with LLVM -target type `target("dx.CBuffer", ..)` which will include type information about -constants, the buffer size and its memory layout. + If there is any variable declaration at global scope that is not static or a + resource the semantic analysis will create an implicit instance of + `HLSLBufferDecl` named `$Globals` to represent the default constant buffer. + This implicit `HLSLBufferDecl` instance will be used to store references to all + variable declarations that belong to the default constant buffer. It will also + be used as the context for declaring the layout structures for the constant + buffer or any embedded structs. -Note: LLVM target types can optionally include a list of one or more types and a -list of one or more integer constants. We can use these lists to encode any -information needed for lowering from LLVM IR to DXIL and SPIRV. +### `ConstantBuffer` Declaration -To encode the shape of the constant buffer the LLVM target type will include a -structure type that represents the constant buffer variable declarations. +`ConstantBuffer` is effectively an alias for type `T` in `hlsl_constant` +address space. If the `hlsl_constant` address space would be spellable it could +be defined as: -The size of the constant buffer will be included as the first item in the list -of integer constants. The rest of the list will be used to encode the constant -buffer layout. The layout will always be included whether the constant buffer -uses any `packoffset` attributes or not. The exact way how the layout will be -encoded is TBD and will be covered in a separate design document. +``` +template using ConstantBuffer = hlsl_constant T; +``` -For simplicity, let's assume the layout will be encoded as a list of offsets of -all cbuffer declarations. In that case this example: +Definition of `ConstantBuffer` equivalent to the statement above will be added +to the `HLSLExternalSemaSource`. -```c++ +Treating `ConstantBuffer` as an alias of `T` takes care the member access issue +- the `.` access refers directly to members of `T`, and global variables +declared using the `ConstantBuffer` syntax will be have the `hlsl_constant` +address space. + +If `ConstantBuffer` allowed `T` to include only types allowed in CBV this is all +that would be needed to make `ConstantBuffer` work. Unfortunately DXC allows `T` +to have resources and or empty types, which means a layout struct might need to +created for `T`. For this reason we are most likely going to need to handle this +in a similar way as the default constant buffer by creating an implicit +`HLSLBufferDecl` that will reference the `ConstantBuffer` global variable and +hold the layout struct definitions. + +### Lowering Constant Buffer Resources to LLVM IR + +For each constant buffer the Clang codegen will create a global variable in +default address space. The type of the global will be `target("dx.CBuffer", +...)`. This global variable will be used for the resource handle initialization +and will be eventually removed. The target type will include 1 parameters - the +buffer layout structure. + +For example this `cbuffer`: +``` cbuffer MyConstants { float2 a; - float b[2]; int c; } ``` - -Would be lowered to LLVM target type: - +would be translated a global variable with the following target type: ``` -@MyConstants.cb = global target("dx.CBuffer", { <2 x float>, [2 x float], i32}, 40, 0, 16, 32, 36) +%class.__layout_MyConstants = type { <2 x float>, i32 } +@MyConstants.cb = global target("dx.CBuffer", %class.__layout_MyConstants) ``` -This layout encoding can obviously get very long and unwieldy for more -complicated cbuffers, and especially since target type parameters are all -included in the name mangling for function overloads. We need to investigate how -to make it smaller, or at least more manageable. +### Lowering Accesses to Individual Constants to LLVM IR -One possibility is compressing the list of offsets into a smaller number of -integers - taking advantage of the fact that outside of `packoffset` use the -difference between two adjancent offsets is never more than 16. The compression -could also include repetition construct that would help with encoding of array -offset. But, as with any compressions, there's always the chance of degenerate -cases that will end up with the compressed shape being the same size or larger -than the original. +For explicit `HLSLBufferDecl`s declarations (`cbuffer` syntax) Clang codegen +will create global variables for all of its variable declarations. Since the +declaration are already using the `hlsl_constant` address space the global +variables will be declared in this address space as well. -> Note: The most tricky part of the layout encoding are probably arrays of -> structures because the structure-specific layout gets repeated many times and -> might not be easy to compress. One idea how to solve this could be translating -> structures embedded in `cbuffer` into separate target types with their own -> encoded layout and including them in the `cbuffer` target type. It is not -> clear though if this is possible (probably yes) or if it would actually make -> things easier or not. +For implicit `HLSLBufferDecl`s declarations (`$Globals` and possibly +`ConstantBuffer` syntax) the declarations already exist at the global scope +in the `hlsl_constant` address space, no changes should be needed here. -Another way could be introducing a typedef concept into the LLVM IR textual -representation so that the full LLVM target type with long layout representation -could occur just once. +Accesses to these global constants will be translated to `load` instruction from +a pointer in `addrspace(2)`. These will be later on collected in an pass +`DXILConstantAccess` and replaced with load operations using a constant buffer +resource handle. -### Lowering ConstantBuffer to LLVM IR +In order for the `DXILConstantAccess` pass to generate generate the correct CBV +load instructions 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. The exact +way the metadata will look like is TBD. -The result of codegen for `cbuffer` and `ConstantBuffer` code should be -identical. - -### Lowering Constant Buffer Variable Access +### DXIL Lowering -Accesses to `cbuffer` variables will be lowered to LLVM IR as memory accesses in -specific "resource address space" using the standard C++ structure layout rules. +A new pass `DXILConstantAccess` will use the constant buffer information from +the metadata, the buffer global variables and its handle types +`target("dx.CBuffer", ...)`, and it will translate all `load` instructions in +`addrspace(2)` to the constant buffer DXIL ops. -### DXIL Lowering +It is an open question whether this pass should be translating the constant +accesses to `llvm.dx.cbufferBufferLoad` instructions which would later need to +be lowered to `cbufferLoadLegacy` ops, or whether it should generate the +`cbufferLoadLegacy` ops directly. -LLVM pass `DXILResourceAccess` will translate these specific "resource address -space" memory accesses into cbuffer DXIL ops adjusting the offsets using the -cbuffer layout information encoded in `target("dx.CBuffer", ..)`. That means -translating standand C++ structure layout offsets to cbuffer layout offsets and -replacing the memory accesses with `llvm.dx.cbufferBufferLoad`, -`llvm.dx.cbufferBufferStore`, and `extractelement ` instructions. The load and -store instructions will be later lowered to `cbufferLoadLegacy` and -`cbufferStoreLegacy` DXIL ops. +Similar transformation pass is going to be needed for SPIR-V target and if +possible we should share code related to this. ### Handle initialization -Constant buffers will be initialized the same way as other resources using the -`createHandleFromBinding` intrinsics. Module initialization code need to be -updated to include all constant buffers declared in a shader. +Clang codegen will constant buffer handle initialization the same way as it does +with other resource classes like raw buffers or structured buffers. ## Detailed design @@ -182,17 +262,12 @@ updated to include all constant buffers declared in a shader. - There is a concern that losing the type information could lead to unnecessary copying of values. -- 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. +- Generate `llvm.dx.cbufferBufferLoad` instruction in Clang codegen whenever a + global constant variable is accesses. + - This would require intercepting all emits of `load` instructions in the + codegen and might turn out quite messy. ## Open issues -- How to encode the cbuffer layout into LLVM target type -- How to implement `ConstantBuffer` member access -- Handling of `$Globals` constant buffer -- Nested `cbuffer` declarations ## Links @@ -204,5 +279,10 @@ Variables](https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graph Visualizer](https://maraneshi.github.io/HLSL-ConstantBufferLayoutVisualizer)
[`packoffset` Attribute](0003-packoffset.md)
+## Open issues +-- Nested `cbuffer` declarations +-- Format of the constant buffer metadata emitted by codegen +-- Should the `DXILConstantAccess` pass generate `cbufferLoadLegacy` ops directly? + ## Acknowledgments (Optional) From 73211aa1ef2d3746732cb4c3dc5030761bec5ee4 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Thu, 23 Jan 2025 12:56:04 -0800 Subject: [PATCH 09/14] layout struct will be defined in the same decl context as original struct --- proposals/NNNN-constant-buffers.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/proposals/NNNN-constant-buffers.md b/proposals/NNNN-constant-buffers.md index 5e3399d..247316c 100644 --- a/proposals/NNNN-constant-buffers.md +++ b/proposals/NNNN-constant-buffers.md @@ -147,9 +147,13 @@ The buffer layout struct will look like this: __layout_Something s; }; ``` -All of the layout structs will be declared as `CXXRecordDecl`s in the -`HLSLBufferDecl` context. The layout struct for the constant buffer is going to -be the last `CXXRecordDecl` child of `HLSLBufferDecl`. + +The layout struct for the constant buffer will be defined in the +`HLSLBufferDecl` declaration context and is going to be the last `CXXRecordDecl` +child of `HLSLBufferDecl`. + +Layout structs for user defined structs will be added to the same declaration +context as the original struct (to the same namespace). ### Default Constant Buffer @@ -158,8 +162,7 @@ be the last `CXXRecordDecl` child of `HLSLBufferDecl`. `HLSLBufferDecl` named `$Globals` to represent the default constant buffer. This implicit `HLSLBufferDecl` instance will be used to store references to all variable declarations that belong to the default constant buffer. It will also - be used as the context for declaring the layout structures for the constant - buffer or any embedded structs. + be used as the declaration context for the buffer layout structure. ### `ConstantBuffer` Declaration @@ -185,7 +188,7 @@ to have resources and or empty types, which means a layout struct might need to created for `T`. For this reason we are most likely going to need to handle this in a similar way as the default constant buffer by creating an implicit `HLSLBufferDecl` that will reference the `ConstantBuffer` global variable and -hold the layout struct definitions. +hold the buffer layout struct definition. ### Lowering Constant Buffer Resources to LLVM IR From 8d01477c9af3f1c652f23c3f51b966c62caab209 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Mon, 27 Jan 2025 15:50:31 -0800 Subject: [PATCH 10/14] add metadata format and examples --- proposals/NNNN-constant-buffers.md | 141 ++++++++++++++++++++++------- 1 file changed, 110 insertions(+), 31 deletions(-) diff --git a/proposals/NNNN-constant-buffers.md b/proposals/NNNN-constant-buffers.md index 247316c..a16af61 100644 --- a/proposals/NNNN-constant-buffers.md +++ b/proposals/NNNN-constant-buffers.md @@ -207,8 +207,8 @@ cbuffer MyConstants { ``` would be translated a global variable with the following target type: ``` -%class.__layout_MyConstants = type { <2 x float>, i32 } -@MyConstants.cb = global target("dx.CBuffer", %class.__layout_MyConstants) +%struct.__layout_MyConstants = type { <2 x float>, i32 } +@MyConstants.cb = global target("dx.CBuffer", %struct.__layout_MyConstants) ``` ### Lowering Accesses to Individual Constants to LLVM IR @@ -222,41 +222,124 @@ For implicit `HLSLBufferDecl`s declarations (`$Globals` and possibly `ConstantBuffer` syntax) the declarations already exist at the global scope in the `hlsl_constant` address space, no changes should be needed here. -Accesses to these global constants will be translated to `load` instruction from -a pointer in `addrspace(2)`. These will be later on collected in an pass -`DXILConstantAccess` and replaced with load operations using a constant buffer -resource handle. +Note that these globals are temporary. They will be eventually transformed into +appropriate intrinsics calls and will not exist in the final DXIL or SPIR-V +code. -In order for the `DXILConstantAccess` pass to generate generate the correct CBV -load instructions 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. The exact -way the metadata will look like is TBD. -### DXIL Lowering +For example for this HLSL code: -A new pass `DXILConstantAccess` will use the constant buffer information from -the metadata, the buffer global variables and its handle types -`target("dx.CBuffer", ...)`, and it will translate all `load` instructions in -`addrspace(2)` to the constant buffer DXIL ops. +```c++ +struct S { + float f; +}; + +cbuffer Constants { + int i; + S s; +}; + +cbuffer OtherConstants { + float4 v; + int array[10]; +}; +``` + +Clang codegen will create these struct definition and global variabless: + +``` +$struct.S = type { float } +%struct.__layout_Constants = type { i32, %struct.S } +%struct.__layout_MyConstants = type { <4 x float>, [10 x i32] } -It is an open question whether this pass should be translating the constant -accesses to `llvm.dx.cbufferBufferLoad` instructions which would later need to -be lowered to `cbufferLoadLegacy` ops, or whether it should generate the -`cbufferLoadLegacy` ops directly. +@Constants.cb = external constant target("dx.CBuffer", %struct.__layout_Constants) +@i = external addrspace(2) global float +@s = external addrspace(2) global %struct.S -Similar transformation pass is going to be needed for SPIR-V target and if -possible we should share code related to this. +@OtherConstants.cb = external constant target("dx.CBuffer", %struct.__layout_OtherConstants) +@v = external addrspace(2) global <3 x float> +@array = external addrspace(2) global [10 x i32] +``` + +Clang codegen will translate accesses to these global constants to `load` +instruction from a pointer in `addrspace(2)`. These `load` instructions will be +later replaced in an LLVM pass with constant buffer load intrinsic calls on a +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. + +### Format of constant buffer metadata + +#### Mapping of constant global variables to constant buffer + +Clang codegen needs to emit metadata that will link `hlsl_constant` globals to +individual constant buffers. Metadata node for a single buffer will be a list of +global variables where the first item will be the global variable represending +the constant buffer. It will be followed by 1 or more `hlsl_constant` global +variables that belong to this constant buffer, in the same order as they were +declared in the source code. + +A named metadata node `hlsl.cbs` will then store a list all metadata nodes for +individual buffers. + +For the HLSL code above the metadata will look like: + +``` +!hlsl.cbs = !{!1, !2} +!1 = !{ptr @Constants.cb, ptr addrspace(2) @i, ptr addrspace(2) @s} +!2 = !{ptr @OtherConstants.cb, ptr addrspace(2) @v, ptr addrspace(2) @array} +``` + +#### Layout information + +Clang also needs to include layout information for each constant buffer, and +for any user defined structs used in a constant buffer. Metadata node storing +this information will include the name of the struct, its size, and then a list +of offset for each field. If the field is an array, we also need to include the +stride information, which is the size of a single array element including +padding. The size of the array does not need to be in the list since it is +already stored on the struct type. + +A named metadata node `hlsl.layouts` will then capture a list of all layout +metadata. + +For the HLSL code above the layout metadata will look like this: + +``` +!hlsl.layouts = !{!3, !4, !5} +!3 = !{!"struct.S", i32 4, i32 0} ; size 4, element offsets 0 +!4 = !{!"struct.__layout_Constants", i32 20, i32 0, i32 16} ; size 20, element offsets 0, 16 +!5 = !{!"struct.__layout_OtherConstants", i32 164, i32 0, i32 16, i32 16} + ; size 164, element offsets 0, 16, array stride 16 +``` + +Since the module contains the struct definitions, we know which struct field is +an array or not, and therefore we know whether to expect a single offset or an +offset & stride pair when processing the list of offsets. + +### Lowering to buffer load intrinsics + +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 +maps the constants global variables to individual constant buffers and specifies +the constant buffer layout. The pass will also transform related `getelementptr` +instructions to use the constant buffer layout offsets. + +After the `HLSLConstantAccess` pass completes the constant globals and the +constant buffer and layout metadata are no longer needed and should be removed. + +### Lowering to DXIL + +Separate DXIL pass will translate the `llvm.dx.resource.load.cbuffer` intrinsics +to `cbufferLoadLegacy` DXIL ops. ### Handle initialization Clang codegen will constant buffer handle initialization the same way as it does with other resource classes like raw buffers or structured buffers. -## Detailed design - -*TBD* - ## Alternatives considered - Generate access to `cbuffer` varibles as memory accesses with the offset based @@ -271,6 +354,7 @@ with other resource classes like raw buffers or structured buffers. codegen and might turn out quite messy. ## Open issues +-- Nested `cbuffer` declarations ## Links @@ -282,10 +366,5 @@ Variables](https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graph Visualizer](https://maraneshi.github.io/HLSL-ConstantBufferLayoutVisualizer)
[`packoffset` Attribute](0003-packoffset.md)
-## Open issues --- Nested `cbuffer` declarations --- Format of the constant buffer metadata emitted by codegen --- Should the `DXILConstantAccess` pass generate `cbufferLoadLegacy` ops directly? - ## Acknowledgments (Optional) From 99334da0155b4a5228eeb9d8b71bd81a390a3483 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Tue, 28 Jan 2025 16:38:37 -0800 Subject: [PATCH 11/14] Update - remove stride, ConstantBuffer design is TBD, add note about reflection --- proposals/NNNN-constant-buffers.md | 54 ++++++++++++++++++------------ 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/proposals/NNNN-constant-buffers.md b/proposals/NNNN-constant-buffers.md index a16af61..dabf841 100644 --- a/proposals/NNNN-constant-buffers.md +++ b/proposals/NNNN-constant-buffers.md @@ -110,7 +110,7 @@ declarations in `cbuffer` scope will be processed into a layout struct that will represent the actual content of the constant buffer. The layout struct will contain all declaration from the `cbuffer` block except: -- static variable declarations +- static and groupshared variable declarations - resource classes - empty structs - zero-sized arrays @@ -174,21 +174,23 @@ be defined as: template using ConstantBuffer = hlsl_constant T; ``` -Definition of `ConstantBuffer` equivalent to the statement above will be added -to the `HLSLExternalSemaSource`. - -Treating `ConstantBuffer` as an alias of `T` takes care the member access issue -- the `.` access refers directly to members of `T`, and global variables -declared using the `ConstantBuffer` syntax will be have the `hlsl_constant` +Treating `ConstantBuffer` as an alias of `T` would take care the member access +issue; the `.` access refers directly to members of `T`, and global variables +declared using the `ConstantBuffer` syntax would be have the `hlsl_constant` address space. -If `ConstantBuffer` allowed `T` to include only types allowed in CBV this is all -that would be needed to make `ConstantBuffer` work. Unfortunately DXC allows `T` -to have resources and or empty types, which means a layout struct might need to -created for `T`. For this reason we are most likely going to need to handle this -in a similar way as the default constant buffer by creating an implicit -`HLSLBufferDecl` that will reference the `ConstantBuffer` global variable and -hold the buffer layout struct definition. +On the other hand, we need to make sure `ConstantBuffer` can also be handled as +other resources, i.e. as a record class that contains a resource handle. That +would be useful when creating arrays of `ConstantBuffer` or when +`ConstantBuffer` is used a as function argument. It is not clear how +that would work with the alias declaration. + +*At this point the design of the `ConstantBuffer` class is still work in progress.* + + +Note that resources and other non-constant buffer constructs should not be +allowed inside the template type `T` used in `ConstantBuffer`. The compiler +should report an error. ### Lowering Constant Buffer Resources to LLVM IR @@ -296,10 +298,7 @@ For the HLSL code above the metadata will look like: Clang also needs to include layout information for each constant buffer, and for any user defined structs used in a constant buffer. Metadata node storing this information will include the name of the struct, its size, and then a list -of offset for each field. If the field is an array, we also need to include the -stride information, which is the size of a single array element including -padding. The size of the array does not need to be in the list since it is -already stored on the struct type. +of offset for each field. A named metadata node `hlsl.layouts` will then capture a list of all layout metadata. @@ -310,13 +309,16 @@ For the HLSL code above the layout metadata will look like this: !hlsl.layouts = !{!3, !4, !5} !3 = !{!"struct.S", i32 4, i32 0} ; size 4, element offsets 0 !4 = !{!"struct.__layout_Constants", i32 20, i32 0, i32 16} ; size 20, element offsets 0, 16 -!5 = !{!"struct.__layout_OtherConstants", i32 164, i32 0, i32 16, i32 16} - ; size 164, element offsets 0, 16, array stride 16 +!5 = !{!"struct.__layout_OtherConstants", i32 164, i32 0, i32 16} + ; size 164, element offsets 0, 16 ``` Since the module contains the struct definitions, we know which struct field is -an array or not, and therefore we know whether to expect a single offset or an -offset & stride pair when processing the list of offsets. +an array or not and how many subelements is contains. The only additional layout +information that is not included here is the stride of the array elements. This +can can be calculated as the size of the array element aligned to 16 (size of +constant buffer row) because each array element always starts at the begining of +a row. ### Lowering to buffer load intrinsics @@ -340,6 +342,14 @@ to `cbufferLoadLegacy` DXIL ops. Clang codegen will constant buffer handle initialization the same way as it does with other resource classes like raw buffers or structured buffers. +### Reflectiom consideration + +The temporary metadata generated by Clang codegen is not sufficient to generate +shader reflection data. When we are going to design how to produce it, we will +most likely create a additional metadata structures for reflection it which will +not be stripped from the module, and that will contain only the necessary +reflection data. + ## Alternatives considered - Generate access to `cbuffer` varibles as memory accesses with the offset based From 930b514104bc68e84be4aa909b9f3f40cdab8490 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Tue, 28 Jan 2025 16:41:33 -0800 Subject: [PATCH 12/14] assign proposal number and rename file --- .../{NNNN-constant-buffers.md => 0015-constant-buffers.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename proposals/{NNNN-constant-buffers.md => 0015-constant-buffers.md} (99%) diff --git a/proposals/NNNN-constant-buffers.md b/proposals/0015-constant-buffers.md similarity index 99% rename from proposals/NNNN-constant-buffers.md rename to proposals/0015-constant-buffers.md index dabf841..b4a87eb 100644 --- a/proposals/NNNN-constant-buffers.md +++ b/proposals/0015-constant-buffers.md @@ -1,7 +1,7 @@ # Constant buffers -* Proposal: [NNNN](NNNN-constant-buffers.md) +* Proposal: [15](0015-constant-buffers.md) * Author(s): [Helena Kotas](https://github.com/hekota) * Status: **Design In Progress** From 3f96ccea14e11c0d482ad442983bd19ba1cdd019 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Tue, 18 Feb 2025 16:59:15 -0800 Subject: [PATCH 13/14] code review feedback - minor edits, add a note about duplicate cbuffer names --- proposals/0015-constant-buffers.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/proposals/0015-constant-buffers.md b/proposals/0015-constant-buffers.md index b4a87eb..264a038 100644 --- a/proposals/0015-constant-buffers.md +++ b/proposals/0015-constant-buffers.md @@ -110,13 +110,13 @@ declarations in `cbuffer` scope will be processed into a layout struct that will represent the actual content of the constant buffer. The layout struct will contain all declaration from the `cbuffer` block except: -- static and groupshared variable declarations +- static or groupshared variable declarations - resource classes - empty structs - zero-sized arrays - any non-variable declarations (functions, classes, ...) -If the constant buffer includes a struct variable, this struct it will also need +If the constant buffer includes a struct variable, it will also need to be inspected and transformed into a new layout struct if it contains any of the undesired declarations above. @@ -252,7 +252,7 @@ Clang codegen will create these struct definition and global variabless: ``` $struct.S = type { float } %struct.__layout_Constants = type { i32, %struct.S } -%struct.__layout_MyConstants = type { <4 x float>, [10 x i32] } +%struct.__layout_OtherConstants = type { <4 x float>, [10 x i32] } @Constants.cb = external constant target("dx.CBuffer", %struct.__layout_Constants) @i = external addrspace(2) global float @@ -271,6 +271,12 @@ 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. +Note that the name of `cbuffer` declaration does not have to be unique. A shader +can have multiple `cbuffer` declarations using the same name. The name of the +layout struct created for the buffer should be derived from the `cbuffer` name. +The compiler must ensure that it is unique by adding a number to the name or +other similar mechanism. + ### Format of constant buffer metadata #### Mapping of constant global variables to constant buffer From 6d248d7fc6e7c14f591724964ef6020fd4854a8c Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Fri, 21 Feb 2025 12:21:47 -0800 Subject: [PATCH 14/14] Update the proposal to use HLSL explicit layout types and remove layout metadata --- proposals/0015-constant-buffers.md | 92 +++++++++++------------------- 1 file changed, 32 insertions(+), 60 deletions(-) diff --git a/proposals/0015-constant-buffers.md b/proposals/0015-constant-buffers.md index 264a038..9724040 100644 --- a/proposals/0015-constant-buffers.md +++ b/proposals/0015-constant-buffers.md @@ -116,9 +116,9 @@ The layout struct will contain all declaration from the `cbuffer` block except: - zero-sized arrays - any non-variable declarations (functions, classes, ...) -If the constant buffer includes a struct variable, it will also need -to be inspected and transformed into a new layout struct if it contains any of -the undesired declarations above. +If the constant buffer includes a struct variable, it will also need to be +inspected and transformed into a new layout struct if it contains any of the +undesired declarations above. For example for this `cbuffer` declaration: @@ -149,11 +149,9 @@ The buffer layout struct will look like this: ``` The layout struct for the constant buffer will be defined in the -`HLSLBufferDecl` declaration context and is going to be the last `CXXRecordDecl` -child of `HLSLBufferDecl`. - -Layout structs for user defined structs will be added to the same declaration -context as the original struct (to the same namespace). +`HLSLBufferDecl` declaration contex. If a layout struct needs to be created for +a user defined struct it will be added to the same declaration context as the +original struct (the same namespace). ### Default Constant Buffer @@ -182,10 +180,11 @@ address space. On the other hand, we need to make sure `ConstantBuffer` can also be handled as other resources, i.e. as a record class that contains a resource handle. That would be useful when creating arrays of `ConstantBuffer` or when -`ConstantBuffer` is used a as function argument. It is not clear how -that would work with the alias declaration. +`ConstantBuffer` is used a as function argument. It is not clear how that +would work with the alias declaration. -*At this point the design of the `ConstantBuffer` class is still work in progress.* +*At this point the design of the `ConstantBuffer` class is still work in +progress.* Note that resources and other non-constant buffer constructs should not be @@ -197,8 +196,9 @@ should report an error. For each constant buffer the Clang codegen will create a global variable in default address space. The type of the global will be `target("dx.CBuffer", ...)`. This global variable will be used for the resource handle initialization -and will be eventually removed. The target type will include 1 parameters - the -buffer layout structure. +and will be eventually removed. The target type `target("dx.CBuffer", ...)` will +include 1 parameter - an explicit HLSL layout type representing the buffer +layout structure. For example this `cbuffer`: ``` @@ -209,10 +209,13 @@ cbuffer MyConstants { ``` would be translated a global variable with the following target type: ``` -%struct.__layout_MyConstants = type { <2 x float>, i32 } -@MyConstants.cb = global target("dx.CBuffer", %struct.__layout_MyConstants) +%__cblayout_MyConstants = type <{ <2 x float>, i32 }> +@MyConstants.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_MyConstants, 12, 0, 8)) ``` +The explicit HLSL layout types are described +[here](0016-NNNN-explicit-layout-struct.md). + ### Lowering Accesses to Individual Constants to LLVM IR For explicit `HLSLBufferDecl`s declarations (`cbuffer` syntax) Clang codegen @@ -228,7 +231,6 @@ Note that these globals are temporary. They will be eventually transformed into appropriate intrinsics calls and will not exist in the final DXIL or SPIR-V code. - For example for this HLSL code: ```c++ @@ -250,17 +252,17 @@ cbuffer OtherConstants { Clang codegen will create these struct definition and global variabless: ``` -$struct.S = type { float } -%struct.__layout_Constants = type { i32, %struct.S } -%struct.__layout_OtherConstants = type { <4 x float>, [10 x i32] } +%__cblayout_Constants = type <{ i32, target("dx.Layout", %S, 4, 0) }> +%S = type <{ float }> +%__cblayout_OtherConstants = type <{ <4 x float>, [10 x i32] }> -@Constants.cb = external constant target("dx.CBuffer", %struct.__layout_Constants) -@i = external addrspace(2) global float -@s = external addrspace(2) global %struct.S +@Constants.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_Constants, 20, 0, 16)) +@i = external addrspace(2) global i32, align 4 +@s = external addrspace(2) global target("dx.Layout", %S, 4, 0), align 4 -@OtherConstants.cb = external constant target("dx.CBuffer", %struct.__layout_OtherConstants) -@v = external addrspace(2) global <3 x float> -@array = external addrspace(2) global [10 x i32] +@OtherConstants.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_OtherConstants, 164, 0, 16)) +@v = external addrspace(2) global <4 x float>, align 16 +@array = external addrspace(2) global [10 x i32], align 4 ``` Clang codegen will translate accesses to these global constants to `load` @@ -268,8 +270,7 @@ instruction from a pointer in `addrspace(2)`. These `load` instructions will be later replaced in an LLVM pass with constant buffer load intrinsic calls on a 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. +which constant buffer. Codegen will generate this information as metadata. Note that the name of `cbuffer` declaration does not have to be unique. A shader can have multiple `cbuffer` declarations using the same name. The name of the @@ -279,8 +280,6 @@ other similar mechanism. ### Format of constant buffer metadata -#### Mapping of constant global variables to constant buffer - Clang codegen needs to emit metadata that will link `hlsl_constant` globals to individual constant buffers. Metadata node for a single buffer will be a list of global variables where the first item will be the global variable represending @@ -299,44 +298,17 @@ For the HLSL code above the metadata will look like: !2 = !{ptr @OtherConstants.cb, ptr addrspace(2) @v, ptr addrspace(2) @array} ``` -#### Layout information - -Clang also needs to include layout information for each constant buffer, and -for any user defined structs used in a constant buffer. Metadata node storing -this information will include the name of the struct, its size, and then a list -of offset for each field. - -A named metadata node `hlsl.layouts` will then capture a list of all layout -metadata. - -For the HLSL code above the layout metadata will look like this: - -``` -!hlsl.layouts = !{!3, !4, !5} -!3 = !{!"struct.S", i32 4, i32 0} ; size 4, element offsets 0 -!4 = !{!"struct.__layout_Constants", i32 20, i32 0, i32 16} ; size 20, element offsets 0, 16 -!5 = !{!"struct.__layout_OtherConstants", i32 164, i32 0, i32 16} - ; size 164, element offsets 0, 16 -``` - -Since the module contains the struct definitions, we know which struct field is -an array or not and how many subelements is contains. The only additional layout -information that is not included here is the stride of the array elements. This -can can be calculated as the size of the array element aligned to 16 (size of -constant buffer row) because each array element always starts at the begining of -a row. - ### Lowering to buffer load intrinsics 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 -maps the constants global variables to individual constant buffers and specifies -the constant buffer layout. The pass will also transform related `getelementptr` -instructions to use the constant buffer layout offsets. +maps the constants global variables to individual constant buffers. The pass +will also transform related `getelementptr` instructions to use the constant +buffer layout offsets. After the `HLSLConstantAccess` pass completes the constant globals and the -constant buffer and layout metadata are no longer needed and should be removed. +constant buffer metadata are no longer needed and should be removed. ### Lowering to DXIL