Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 125 additions & 6 deletions proposals/0002-cxx-attributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ params:
---



* Planned Version: 202y

## Introduction
Expand Down Expand Up @@ -86,7 +86,7 @@ above can be written as:

```c++
struct {
uint i [[hlsl::SV_RenderTargetArrayIndex]];
uint i [[hlsl::system_value(RenderTargetArrayIndex)]];
}
```

Expand All @@ -110,11 +110,11 @@ Below are a few more examples of C++ attributes that we could support:

Texture2D<float4> Tex [[hlsl::register(1, 0)]]; // applies to `Tex`;

uint i [[hlsl::SV_RenderTargetArrayIndex]]; // applies to `i`.
[[hlsl::SV_RenderTargetArrayIndex]] uint j; // applies to `j`.
uint i [[hlsl::system_value(RenderTargetArrayIndex)]]; // applies to `i`.
[[hlsl::system_value(RenderTargetArrayIndex)]] uint j; // applies to `j`.
uint &[[hlsl::AddressSpace(1)]] Ref = ...; // applies to the type `uint &`.
Comment on lines +113 to 115
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
uint i [[hlsl::system_value(RenderTargetArrayIndex)]]; // applies to `i`.
[[hlsl::system_value(RenderTargetArrayIndex)]] uint j; // applies to `j`.
uint &[[hlsl::AddressSpace(1)]] Ref = ...; // applies to the type `uint &`.
uint i [[hlsl::system_value(RenderTargetArrayIndex)]]; // applies to `i`.
[[hlsl::system_value(RenderTargetArrayIndex)]] uint j; // applies to `j`.
uint &[[hlsl::address_space(1)]] Ref = ...; // applies to the type `uint &`.

The attribute names should have consistent naming pattern.


[[hlsl::SV_Target]] // applies to the function `fn`.
[[hlsl::system_value(Target)]] // applies to the function `fn`.
float3 fn( ) {
[[hlsl::fast]] // applies to the compound expression `{...}`.
{
Expand Down Expand Up @@ -233,6 +233,125 @@ following grammar formulations are valid:
\terminal{using} \textit{identifier} \opt{attribute-specifier-seq} \terminal{=} \textit{type-id} \terminal{;}\br
\end{grammar}
```

![Latex Rendering](0002-assets/ClassGrammarRender.png)

Copy link
Member

Choose a reason for hiding this comment

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

Rendering error?
image

### Attribute Specification Language

Attributes annotate source constructs with information. An attribute is said to
be _applied to_ the entity or statement identified by the source construct.

Some attributes may be required by an implementation for correct code
generation, others may be optionally ignored. An implementation must issue a
diagnostic on all ignored attributes unless otherwise specified by the
definition of the attribute behavior.

> Note: an example here would be optimization hint attributes which an
> implementation is allowed to ignore without diagnosing.

Each attribute may specify specific behavior for parsing attribute arguments.
Any attribute that does not specify specific parsing behavior shall be parsed
with the general behavior described here.

Comment on lines +251 to +254
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Each attribute may specify specific behavior for parsing attribute arguments.
Any attribute that does not specify specific parsing behavior shall be parsed
with the general behavior described here.
Each attribute may define specific behavior for how its arguments are parsed.
Attributes that do not define custom parsing behavior shall be parsed according to the general rules outlined here.

> Note: The clause above enables attributes like the clang availability
> attribute which supports named parameters (e.g.
> `[[clang::availability(shadermodel, introduced=6.3)]]`), HLSL has a use for
> similar functionality.

An empty attribute specifier has no effect. The order in which attributes
applied to the same source construct are written shall not be significant. When
Copy link
Collaborator

Choose a reason for hiding this comment

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

So this means attribute overriding/duplication/conflict will be determined by each attribute definition?

Like if I did uint field [[hlsl::system_value(something), hlsl::system_value(something_else)]]

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yea, and conflicting attributes should generally be an error.

parsing attributes any token that satisfies the requirements of an identifier
shall be treated as an identifier even if it has alternate meaning outside the
attribute (e.g. keywords). Name lookup is not performed on identifiers within
attribute-tokens. The attribute-token refers to the attribute being parsed,
which determines requirements for parsing the optional
attribute-argument-clause.
Comment on lines +264 to +267
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
attribute (e.g. keywords). Name lookup is not performed on identifiers within
attribute-tokens. The attribute-token refers to the attribute being parsed,
which determines requirements for parsing the optional
attribute-argument-clause.
attribute (e.g. keywords). Name lookup is not performed on identifiers within
_attribute-token_. The _attribute-token_ refers to the attribute being parsed,
which determines requirements for parsing the optional
_attribute-argument-clause_.

Consider making grammar tokens references in italic.


If an attribute is applied to an entity or statement for which the attribute is
not allowed to be applied, the program is ill-formed.

For attribute-tokens not specified in this specification the behavior is
implementation-defined.
Comment on lines +272 to +273
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
For attribute-tokens not specified in this specification the behavior is
implementation-defined.
The behavior of _attribute-token_ not specified in this specification is
implementation-defined.


Two consecutive square bracket tokens shall only appear when introducing an
attribute-specifier. Any other occurrence of two consecutive square brackets is
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
attribute-specifier. Any other occurrence of two consecutive square brackets is
_attribute-specifier_. Any other occurrence of two consecutive square brackets is

ill-formed.
Copy link
Member

Choose a reason for hiding this comment

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

Doesn't

"Any other occurrence of two consecutive square brackets is ill-formed."

conflict with

"An empty attribute specifier has no effect. "?


### Removal of HLSL Annotation Syntax

With the introduction of C++ attribute syntax the HLSL annotation syntax will be
removed from the language. In Clang, C++ attribute syntax can be supported in
both HLSL 202x and 202y language modes with deprecation warnings, fix-it and
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
both HLSL 202x and 202y language modes with deprecation warnings, fix-it and
both HLSL 202x and 202y language modes with deprecation warnings reported for the old HLSL annotation syntax, including fix-it and

rewriting tool support in Clang. This will allow easier migration of code from
HLSL 202x to 202y. This feature will not be supported in DXC.

The following new attributes are introduced to replace HLSL annotations.

#### hlsl::user_value(string[, int=0])
Copy link
Collaborator

Choose a reason for hiding this comment

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

In SPIR-V, we currently ignore the user semantics. We assign a location based on the order in which variables are declared, or using the vk::location attribute. There have also been requests to allow the users to specify the component for the input as well as the location.

This does does not really work for SPIR-V since we have no good way to turn the string into a location index. The only thing we do with the the user semantic is to keep it around for reflection information.

I don't know if we want to try to come up with something like hlsl::binding that would be well defined for both DX and SPIR-V. Then we could deprecate vk::location, and close issues for the component.

Maybe something like hlsl::user_value(string[, int=0, int=0]), with the statement: "The interpretation of this attribute is
defined by the target runtime's binding model." Then we can define DX and Vulkan behaviour separately.

@Keenuts any more thoughts?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I've read this more as a 'rewrite' of the attribute form with a new syntax, but also assumed we'd need to ignore this in SPIR-V since Location index is independant from the user semantic.

I think we could keep the split we have today and either use the parameter index ignoring the content, or ass vk-specific attributes for component/location.

void main([[hlsl_user_value("MYSEMANTIC"), vk::location(0), vk::component(1)]] float a)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I had been thinking of this as just replacing the semantic as captured in reflection for SPIRV. That's more or less all it really is for DX too although it is expressed into the signatures rather than shader reflection.

I'd like to think more about how to handle location and component packing.


The new `hlsl::user_value` attribute replaces user-defined semantics. The first
argument to the attribute is a string which can contain any valid C-string. The
second optional value is an index.

#### hlsl::system_value(enum[, int=0])
Copy link
Collaborator

Choose a reason for hiding this comment

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

@Keenuts Which system values have an index, and how do we handle those for SPIR-V in DXC?

Copy link
Collaborator

Choose a reason for hiding this comment

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

It depends on the system value:
Compute related ones have no index, same for semantic like POINT_SIZE. Those are builtins. But for SV_TARGET, which has an index, we emit a Location decoration.


The new `hlsl::system_value` attribute replaces system value semantics. The
first argument is an enumeration which specifies which system value is being
bound, and the second optional value is an index.

#### hlsl::packoffset(int[, int=0])

The new `hlsl::packoffset` attribute replaces the `packoffset` HLSL annotation.
The attribute takes one required and one optional integer arguments. The second
integer must be greater than or equal to 0 and less than or equal to 3.

The first value specifies the starting row for packing data, and the second
value specifies the starting column. Existing `packoffset` arguments written
`c<row>.<column_letter>` map to the new attribute as `hlsl::packoffset(<row>,
<column_index>)`, where `<column_index>` maps as in the table below.

| column_letter | column_index |
Copy link
Collaborator

Choose a reason for hiding this comment

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

considering payload_access allows identifiers, I'm curious why we are changing to integers here? The previous use of letters is fairly arbitrary and not any easier/harder to understand than integers, but the reverse holds, and this would be asking our users to get used to a new way of writing something

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

My reasoning for getting rid of the prefix character c is that it can only ever be c which has meaning in DirectX (it's a CBV resource), but has no meaning in other binding models.

In terms of the column index. I can see an argument for it remaining a letter. I struggle a bit with it because it is effectively a sub-index to 32-bit offsets, which actually isn't valid for all types. For example you can't use an offset of y today for a 64-bit data member so your offsets available are only x and z, which just feels weird. Not that offsets 0 and 2 is really entirely better.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I guess the other advantage that integers would have is that they can be integer constant expressions not just static values. So using integers would enable someone to do something like:

template <typename T, int X>
constexpr int toOffset() {
    constexpr int Val = (sizeof(T) / 4) * X;
    static_assert(Val <= 3);
    return Val
}
...

double V [[hlsl::packoffset(0, toOffset<double,2>())]];

We could also consider supporting either integers or identifiers.

I'd be curious for @tex3d's thoughts here too.

Copy link
Collaborator

@V-FEXrt V-FEXrt Jul 21, 2025

Choose a reason for hiding this comment

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

My reasoning for getting rid of the prefix character c

Yeah I wasn't concerned about dropping the c. More so that we'd be moving somewhat arbitrarily from hlsl::binding(0,y) to hlsl::binding(0,2).

I think it's not really an issue since we are also rewriting the rest of the attribute but its at least worth considering given the tools and guides written around the notion that the second value is a letter.

I personally thing integer is simpler/clearer and think from a clean slate that it's the right decision, I just want to make sure we aren't negatively impacting users

Copy link
Member

Choose a reason for hiding this comment

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

@V-FEXrt - I think you meant moving from packoffset(c0,y) to hlsl::packoffset(0,2), right? hlsl::binding is described bellow.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah sorry, that's what I meant. I don't think it's an issue worth blocking over. Just something to consider

| ------------- | ------------ |
| x | 0 |
| y | 1 |
| z | 2 |
| w | 3 |

#### hlsl::binding(int[, int=0])

The new `hlsl::binding` attribute replaces the `binding` HLSL annotation. The
attribute takes one required and one optional integer arguments. The first
integer argument specifies the binding index. the second integer specifies
the binding scope of the binding index. The interpretation of this attribute is
defined by the target runtime's binding model.

In DirectX, the first value maps as the register value, and the second the
register space. DirectX scopes bindings by resource class, allowing the same
register and space assignments to be specified for resources of different types
(e.g. a UAV and SRV may have the same register and space values without
aliasing).

In Vulkan, the first value maps as the binding index, and the second maps as the
descriptor set index.

#### hlsl::payload_access(<enum>, ...)

The new `hlsl::payload_access` attribute replaces the `read` and `write` HLSL
annotations for raytracing payload access qualifiers. The attribute takes an
enum value specifying `read` or `write` to denote the type of access and a
variable argument list of enumeration values specifying the stage that the
access qualifier applies to. Consider the following currently valid HLSL:

```hlsl
struct [raypayload] Payload {
float f : read(caller, anyhit) : write(caller, anyhit);
};
```

Under HLSL 202y this code will be rewritten as:

```hlsl
using namespace hlsl;
struct [[raypayload]] Payload {
float f [[payload_access(read, caller, anyhit), payload_access(write, caller, anyhit)]];
};
```
Copy link
Member

Choose a reason for hiding this comment

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

I think it would be good to add similar rewriting examples for all the other attributes above.