AZSL is a thin extension of HLSL.
For the most part everything that works in HLSL SM 6.0 and above should work in AZSL too, with a few additions and exceptions.
The easiest way to get started is to check the test suite, which is a collection of 100+ tests that are also meant to be samples of what is allowed and what is disallowed in the grammar.
tests\Samples // More advanced samples
tests\Semantic // Gramatically and semantically correct tests - you can write such code
tests\Semantic\AsError // Gramatically correct, but semantically incorrect - these samples don't work because of higher level language rules
tests\Syntax // Gramatically correct language
tests\Syntax\AsError // Gibberish - you shouldn't even write this, AZSLc can't make any sense of such files
Classes can hold methods, while Structs cannot. Only Structs may be defined within ShaderResourceGroups. Both of them have public visibility members, since there is no notion of visibility at all.
HLSL6 supports them, but HLSL5 did not. AZSL does support: enum typedef
Class may inherit from Interfaces, to be forced to respect a contract (the implementation of functions).
Not generally supported in AZSL 1.6 nor under. At the exception of intrinsic HLSL types like vector, matrix or StructuredBuffer...
Not supported in AZSL 1.6 nor under.
In addition to HLSL attributes, we also use attribute specifier sequences as a non-intrusive way of providing API specific information in AZSL.
Although not strictly related to C++ standards, currently we don't support using
(C++17 standard) or contract-attributes (C++20 standard) specified in the linked document.
Example of usage are:
[[namespace::attribute]]
[[namespace::attribute(arg1, arg2), attribute]]
[[namespace::attribute(arg1, arg2), namespace::attribute(arg), namespace::attribute, attribute]]
Namespace is used to filter out attributes and has no correlation to the namespaces used in the shader code. Attribute is the name of the attribute specified. It can have no arguments or one or more arguments, which are literals - boolean, integer, float or string. You can have more than one attribute in a sequence, separated by a comma.
The attributes function the way they are described in the C++ standard and affect the next declaration, expression or scope.
Attributes with no namespace are always enabled.
Attributes with a namespace are only enabled if the namespace is enabled when compiling the shader (--namespace=<nspc>
).
The void
namespace is always disabled. (This feature is still WIP, the grammar doesn't accept keywords like void
as namespace identifiers yet.)
Attribute sequences without a namespace are always enabled, for example [[location(1)]]
.
Attribute sequences with a namespace (for example [[vk::location(1)]]
) have to be enabled when compiling by passing the --namespace=vk
attribute.
Multiple namespaces can be specified, for example --namespace=vk,mobile,debug
, comma separated with no whitespaces. Note that only one such namespace can be a graphics API. For example --namespace=vk,dx
is not allowed - you compile the file either for DirectX or Vulkan, but not both at the same time!
Throughout this document we use some naming conventions for namespaces - for example [[dx::]]
for DirectX12, [[vk::]]
for Vulkan, etc.
However, AZSLc doesn't restrict the choice of namespace. It is left to the shader authors and the consumer application.
To differentiate between attributes which belong to the global scope and attributes attached to the next declaration, we use the root namespace global::
.
This is required since both cases appear in the same space! Think about attribute sequence before a function declaration in the global scope.
Thus [[global::attribute]]
is a global attribute with no namespace (global
is ignored as a namespace in this case) and [[global::dx::atttribute]]
is a global attribute in the dx
namespace.
AZSLc uses some attributes and passes others. The special attributes are listed below.
[[global::verbatim("// Text will be re-emitted as its!")]]
Verbatim attributes will emit all their arguments as-is, on a single line with a single whitespace between them. AZSLc makes no sense of what's in the verbatim block. You can use macros and includes here, but those macros will have to target the next compiler (dxc, spriv-cross, etc.), AZSLc will make no sense of what's being included.
It is possible to include files with the #include
directive this way, refer to tests/Advanced/simple-surface.azsl
for an example.
[[global::output_format("R16G16B16A16_FLOAT")]]
The output_format
specifies pixel shader entry output format hint. If no index is provided, it affects all render targets, otherwise you can specify a render target (0 to 7) before the format.
Refer to tests/Samples/PixelShaderOutputAttributes.azsl
for details.
AZSLc supports enumerations, both unscoped (enum
) and scoped (enum class
and enum struct
).
The usage is the same as in C++ for the most part with the exception that explicit declaration of underlying type is not supported. Also, unlike in Microsoft C++ Compiler (cl), non-class enumerators cannot be refered to using explicit qualification.
Refer to tests/Samples/Enumeration.azsl
for details.