-
Notifications
You must be signed in to change notification settings - Fork 98
spirv-std functions to query compute builtins #535
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: move_mod
Are you sure you want to change the base?
Changes from all commits
7a3a8d7
970cd1b
44502a4
b8a7704
6bcdbdc
f382cfb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| //! Functionality to declare builtins, mostly proc macros | ||
| //! | ||
| //! # Making built-in functions for `spirv-std` | ||
| //! | ||
| //! Usually, built-ins are implemented as freestanding functions in `spirv-std`. We like to keep function declaration | ||
| //! outside the macro to make it easier for users to browse the source code. | ||
| //! | ||
| //! Example on how to declare an Input Built-in: | ||
| //! ```no_run | ||
| //! # use spirv_std_macros::gpu_only; | ||
| //! # | ||
| //! /// GLSL docs short description in #Name section. Remove the first "Contains " since we're using getters instead | ||
| //! /// of globals, capitalize and add a dot to the end. | ||
| //! /// | ||
| //! /// GLSL docs full #Description section. | ||
| //! /// | ||
| //! /// We're using GLSL documentation of this built-in, which is usually more descriptive than the SPIR-V or WGSL docs. | ||
| //! /// Change all references to link with rust-gpu intrinsics. | ||
| //! /// | ||
| //! /// Update the links of GLSL and WGSL to reference the correct page, keep SPIR-V as is. GLSL may link to the | ||
| //! /// [reference](https://registry.khronos.org/OpenGL-Refpages/gl4/) or to the | ||
| //! /// [glsl extensions github repo](https://github.com/KhronosGroup/GLSL/tree/main/extensions). | ||
| //! /// * GLSL: [`gl_MyBuiltIn`](https://registry.khronos.org/OpenGL-Refpages/gl4/html/gl_LocalInvocationID.xhtml) | ||
| //! /// * WGSL: [`my_built_in`](https://www.w3.org/TR/WGSL/#local-invocation-id-builtin-value) | ||
| //! /// * SPIRV: [`MyBuiltIn`](https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#_builtin) | ||
| //! #[doc(alias = "gl_MyBuiltIn")] | ||
| //! #[doc(alias = "MyBuiltIn")] | ||
| //! #[inline] | ||
| //! #[gpu_only] | ||
| //! pub fn my_built_in() -> u32 { | ||
| //! crate::load_builtin!(MyBuiltIn) | ||
| //! } | ||
| //! ``` | ||
| //! | ||
| //! Reference links: | ||
| //! * [WGSL specification describing builtins](https://www.w3.org/TR/WGSL/#builtin-inputs-outputs) | ||
| //! * [SPIR-V specification for builtins](https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#_builtin) | ||
| //! * [GLSL reference](https://registry.khronos.org/OpenGL-Refpages/gl4/) | ||
| //! * [GLSL reference source code](https://github.com/KhronosGroup/OpenGL-Refpages/tree/main/gl4) | ||
| //! * [GLSL extensions](https://github.com/KhronosGroup/GLSL/tree/main/extensions) | ||
|
|
||
| /// Query SPIR-V (read-only global) built-in values | ||
| /// | ||
| /// See [module level documentation] on how to use these. | ||
| #[macro_export] | ||
| macro_rules! load_builtin { | ||
| ($name:ident $(: $ty:ty)?) => { | ||
| unsafe { | ||
| let mut result $(: $ty)? = Default::default(); | ||
| ::core::arch::asm! { | ||
| "%builtin = OpVariable typeof{result_ref} Input", | ||
| concat!("OpDecorate %builtin BuiltIn ", stringify!($name)), | ||
| "%result = OpLoad typeof*{result_ref} %builtin", | ||
| "OpStore {result_ref} %result", | ||
| result_ref = in(reg) &mut result, | ||
| } | ||
| result | ||
| } | ||
| }; | ||
| } | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Separating out all the builtins into modules in the root makes it a little harder to find them all. I think (especially for compute), someone using the subgroup builtins are also likely to use the compute builtins, so keeping these modules nested under In Any higher-level abstraction on top such as safe collections (
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This won't work for the graphics builtins. Here's a message I sent internally last week about this:
If you split them into one module per shader type (like in #540), you can define the builtin for each shader separately and exactly like is required.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes I meant 1 module per shader type, so As I mentioned elsewhere, but you may have missed, I think output builtin setters would be confusing, as it will be difficult to see in their code when and where they are set. For compute shaders, I think you always have all the input builtins, whereas e.g. for vertex what is available depends more on what has been configured, is that correct? I also think it would be confusing to not be able to see all the inputs params in one place.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moving just the subgroup builtins from On output setters: I also think they need more design work, which is why I separated out the compute parts and we'll do graphics later. But have you looked at the current function declaration? Cause it's very much not a setter: So I'm currently thinking of having these functions marked as unsafe and hide them a little, so they're only used via the new proc macro (to be designed). Another idea I just had: We may also be better off with a wrapper type like
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Vulkan 1.1 (released 2018) included the subgroup capabilities I believe, so I doubt they are rare enough to be packaged away separately. From a quick check on vulkan.gpuinfo.org reports (from the last 2 years, excluding CPU implementations), 0.4% of reports supported none of the subgroup features. Output Fine in small examples, potentially very nasty in a framework. It's the difference between finding usages of a local variable and a global variable across your code base.
Sounds fine if they're
Like my first PR? Sounds good to me for a typed unique output variable :)
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For setting a variable at most once, the output wrapper can be opaque and consumed when you set the output:
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wait
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know you have that now, I was proposing low level stuff is grouped somewhere (as But thinking again I prefer grouping by shader type and use case, as you describe. As you probably know, ray tracing shaders also have subgroup builtins but with different semantics as the subgroup IDs can change during the invocation. I think I would like the module docs for |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| //! compute shader built-ins | ||
|
|
||
| use glam::UVec3; | ||
|
|
||
| /// The index of work item currently being operated on by a compute shader. | ||
| /// | ||
| /// In the compute language, [`local_invocation_id`] is an input variable containing the n-dimensional index of the | ||
| /// local work invocation within the work group that the current shader is executing in. The possible values for this | ||
| /// variable range across the local work group size, i.e., `(0,0,0)` to | ||
| /// `(workgroup_size.x - 1, workgroup_size.y - 1, workgroup_size.z - 1)`. | ||
| /// | ||
| /// * GLSL: [`gl_LocalInvocationID`](https://registry.khronos.org/OpenGL-Refpages/gl4/html/gl_LocalInvocationID.xhtml) | ||
| /// * WGSL: [`local_invocation_id`](https://www.w3.org/TR/WGSL/#local-invocation-id-builtin-value) | ||
| /// * SPIR-V: [`LocalInvocationId`](https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#_builtin) | ||
| #[doc(alias = "gl_LocalInvocationID")] | ||
| #[doc(alias = "LocalInvocationId")] | ||
| #[inline] | ||
| #[gpu_only] | ||
| pub fn local_invocation_id() -> UVec3 { | ||
| crate::load_builtin!(LocalInvocationId) | ||
| } | ||
|
|
||
| /// The local linear index of work item currently being operated on by a compute shader. | ||
| /// | ||
| /// In the compute language, [`local_invocation_index`] is a derived input variable containing the 1-dimensional | ||
| /// linearized index of the work invocation within the work group that the current shader is executing on. The value of | ||
| /// [`local_invocation_index`] is equal to [`local_invocation_id`]`.z * workgroup_size.x * workgroup_size.y` | ||
| /// `+ `[`local_invocation_id`]`.y * workgroup_size.x + `[`local_invocation_id`]`.x`. | ||
| /// | ||
| /// * GLSL: [`gl_LocalInvocationIndex`](https://registry.khronos.org/OpenGL-Refpages/gl4/html/gl_LocalInvocationIndex.xhtml) | ||
| /// * WGSL: [`local_invocation_index`](https://www.w3.org/TR/WGSL/#local-invocation-index-builtin-value) | ||
| /// * SPIR-V: [`LocalInvocationIndex`](https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#_builtin) | ||
| #[doc(alias = "gl_LocalInvocationIndex")] | ||
| #[doc(alias = "LocalInvocationIndex")] | ||
| #[inline] | ||
| #[gpu_only] | ||
| pub fn local_invocation_index() -> u32 { | ||
| crate::load_builtin!(LocalInvocationIndex) | ||
| } | ||
|
|
||
| /// The global index of work item currently being operated on by a compute shader. | ||
| /// | ||
| /// In the compute language, [`global_invocation_id`] is a derived input variable containing the n-dimensional index of | ||
| /// the work invocation within the global work group that the current shader is executing on. The value of | ||
| /// [`global_invocation_id`] is equal to [`workgroup_id`]` * workgroup_size + `[`local_invocation_id`]. | ||
| /// | ||
| /// * GLSL: [`gl_GlobalInvocationID`](https://registry.khronos.org/OpenGL-Refpages/gl4/html/gl_GlobalInvocationID.xhtml) | ||
| /// * WGSL: [`global_invocation_id`](https://www.w3.org/TR/WGSL/#global-invocation-index-builtin-value) | ||
| /// * SPIR-V: [`GlobalInvocationId`](https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#_builtin) | ||
| #[doc(alias = "gl_GlobalInvocationID")] | ||
| #[doc(alias = "GlobalInvocationId")] | ||
| #[inline] | ||
| #[gpu_only] | ||
| pub fn global_invocation_id() -> UVec3 { | ||
| crate::load_builtin!(GlobalInvocationId) | ||
| } | ||
|
|
||
| // custom: do not mention `glDispatchCompute` directly, be more general across APIs | ||
| /// The number of workgroups that have been dispatched to a compute shader. | ||
| /// | ||
| /// In the compute language, [`num_workgroups`] contains the total number of work groups that will execute the compute | ||
| /// shader. The components of [`num_workgroups`] are equal to the `x`, `y`, and `z` parameters passed to the dispatch | ||
| /// command. | ||
| /// | ||
| /// * GLSL: [`gl_NumWorkGroups`](https://registry.khronos.org/OpenGL-Refpages/gl4/html/gl_NumWorkGroups.xhtml) | ||
| /// * WGSL: [`num_workgroups`](https://www.w3.org/TR/WGSL/#num-workgroups-builtin-value) | ||
| /// * SPIR-V: [`NumWorkgroups`](https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#_builtin) | ||
| #[doc(alias = "gl_NumWorkGroups")] | ||
| #[doc(alias = "NumWorkgroups")] | ||
| #[inline] | ||
| #[gpu_only] | ||
| pub fn num_workgroups() -> UVec3 { | ||
| crate::load_builtin!(NumWorkgroups) | ||
| } | ||
|
|
||
| // custom: do not mention `glDispatchCompute` directly, be more general across APIs | ||
| /// The index of the workgroup currently being operated on by a compute shader. | ||
| /// | ||
| /// In the compute language, [`workgroup_id`] contains the 3-dimensional index of the global work group that the current | ||
| /// compute shader invocation is executing within. The possible values range across the parameters passed into the | ||
| /// dispatch command, i.e., from `(0, 0, 0)` to | ||
| /// `(`[`num_workgroups`]`.x - 1, `[`num_workgroups`]`.y - 1, `[`num_workgroups`]`.z - 1)`. | ||
| /// | ||
| /// * GLSL: [`gl_WorkGroupID`](https://registry.khronos.org/OpenGL-Refpages/gl4/html/gl_WorkGroupID.xhtml) | ||
| /// * WGSL: [`workgroup_id`](https://www.w3.org/TR/WGSL/#workgroup-id-builtin-value) | ||
| /// * SPIR-V: [`WorkgroupId`](https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#_builtin) | ||
| #[doc(alias = "gl_WorkGroupID")] | ||
| #[doc(alias = "WorkgroupId")] | ||
| #[inline] | ||
| #[gpu_only] | ||
| pub fn workgroup_id() -> UVec3 { | ||
| crate::load_builtin!(WorkgroupId) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I deliberately kept
load_builtin!private as an implementation detail. As is, it's very unsafe. Also easy to make something that won't compile but will have an inscrutable error message, or won't validate.I think this should remain an internal implementation detail.