From 8f93dcae0082bb60d1e699e430df6e3a2a013e7f Mon Sep 17 00:00:00 2001 From: Firestar99 Date: Thu, 10 Apr 2025 16:16:19 +0200 Subject: [PATCH 1/7] derivative: refactor derivatives, allows f32, Vec2, Vec3, Vec4 and Vec3A to be derived disallow f64 as spec expects only 32bit floats --- crates/spirv-std/src/arch/derivative.rs | 220 +++++++++++++++--------- crates/spirv-std/src/sealed.rs | 15 ++ 2 files changed, 150 insertions(+), 85 deletions(-) diff --git a/crates/spirv-std/src/arch/derivative.rs b/crates/spirv-std/src/arch/derivative.rs index 3ab5c2d1fe..9add59a385 100644 --- a/crates/spirv-std/src/arch/derivative.rs +++ b/crates/spirv-std/src/arch/derivative.rs @@ -1,102 +1,152 @@ -use crate::float::Float; +use crate::sealed::Sealed; +use glam::{Vec2, Vec3, Vec3A, Vec4}; + +macro_rules! cap_deriv_control { + () => { + unsafe { + core::arch::asm!("OpCapability DerivativeControl"); + } + }; +} -#[cfg(target_arch = "spirv")] macro_rules! deriv_fn { - ($p:ident, $inst:ident) => { + ($inst:ident, $param:expr) => { unsafe { - let mut o = Default::default(); - ::core::arch::asm!( - "%input = OpLoad _ {0}", - concat!("%result = ", stringify!($inst), " _ %input"), - "OpStore {1} %result", - in(reg) &$p, - in(reg) &mut o, + let mut result = Default::default(); + core::arch::asm!( + "%input = OpLoad typeof*{1} {1}", + concat!("%result = ", stringify!($inst), " typeof*{1} %input"), + "OpStore {0} %result", + in(reg) &mut result, + in(reg) &$param, ); - o + result } }; } -/// Returns the partial derivative of `component` with respect to the window's X -/// coordinate. Returns the same result as either [`ddx_fine`] or -/// [`ddx_coarse`], selection of which one is dependent on external factors. -#[crate::macros::vectorized] -#[crate::macros::gpu_only] -pub fn ddx(component: F) -> F { - deriv_fn!(component, OpDPdx) -} +/// Types that can be derived by partial derivatives +pub unsafe trait Derivative: Sealed + Default { + /// Result is the partial derivative of `Self` with respect to the window x coordinate. Uses local differencing + /// based on the value of `Self`. Same result as either [`ddx_fine`] or [`ddx_coarse`] on `Self`. Selection of which + /// one is based on external factors. + /// + /// An invocation will not execute a dynamic instance of this instruction (X') until all invocations in its + /// derivative group have executed all dynamic instances that are program-ordered before X'. + /// + /// This instruction is only valid in the Fragment Execution Model. + #[crate::macros::gpu_only] + fn ddx(self) -> Self { + deriv_fn!(OpDPdx, self) + } -/// Returns the partial derivative of `component` with respect to the window's X -/// coordinate. Uses local differencing based on the value of `component` for -/// the current fragment and its immediate neighbor(s). -#[crate::macros::vectorized] -#[crate::macros::gpu_only] -pub fn ddx_fine(component: F) -> F { - deriv_fn!(component, OpDPdxFine) -} + /// Result is the partial derivative of `Self` with respect to the window x coordinate. Uses local differencing + /// based on the value of `Self` for the current fragment and its immediate neighbor(s). + /// + /// An invocation will not execute a dynamic instance of this instruction (X') until all invocations in its + /// derivative group have executed all dynamic instances that are program-ordered before X'. + /// + /// This instruction is only valid in the Fragment Execution Model. + #[crate::macros::gpu_only] + fn ddx_fine(self) -> Self { + cap_deriv_control!(); + deriv_fn!(OpDPdxFine, self) + } -/// Returns the partial derivative of `component` with respect to the window's X -/// coordinate. Uses local differencing based on the value of `component` for -/// the current fragment’s neighbors, and possibly, but not necessarily, -/// includes the value of `component` for the current fragment. That is, over a -/// given area, the implementation can compute X derivatives in fewer unique -/// locations than would be allowed by [`ddx_fine`]. -#[crate::macros::vectorized] -#[crate::macros::gpu_only] -pub fn ddx_coarse(component: F) -> F { - deriv_fn!(component, OpDPdxCoarse) -} + /// Result is the partial derivative of `Self` with respect to the window x coordinate. Uses local differencing + /// based on the value of `Self` for the current fragment’s neighbors, and possibly, but not necessarily, includes + /// the value of `Self` for the current fragment. That is, over a given area, the implementation can compute x + /// derivatives in fewer unique locations than would be allowed for [`ddx_fine`]. + /// + /// An invocation will not execute a dynamic instance of this instruction (X') until all invocations in its + /// derivative group have executed all dynamic instances that are program-ordered before X'. + /// + /// This instruction is only valid in the Fragment Execution Model. + #[crate::macros::gpu_only] + fn ddx_coarse(self) -> Self { + cap_deriv_control!(); + deriv_fn!(OpDPdxCoarse, self) + } -/// Returns the partial derivative of `component` with respect to the window's Y -/// coordinate. Returns the same result as either [`ddy_fine`] or -/// [`ddy_coarse`], selection of which one is dependent on external factors. -#[crate::macros::vectorized] -#[crate::macros::gpu_only] -pub fn ddy(component: F) -> F { - deriv_fn!(component, OpDPdy) -} + /// Result is the partial derivative of `Self` with respect to the window y coordinate. Uses local differencing + /// based on the value of `Self`. Same result as either [`ddy_fine`] or [`ddy_coarse`] on `Self`. Selection of which + /// one is based on external factors. + /// + /// An invocation will not execute a dynamic instance of this instruction (X') until all invocations in its + /// derivative group have executed all dynamic instances that are program-ordered before X'. + /// + /// This instruction is only valid in the Fragment Execution Model. + #[crate::macros::gpu_only] + fn ddy(self) -> Self { + deriv_fn!(OpDPdy, self) + } -/// Returns the partial derivative of `component` with respect to the window's Y -/// coordinate. Uses local differencing based on the value of `component` for -/// the current fragment and its immediate neighbor(s). -#[crate::macros::vectorized] -#[crate::macros::gpu_only] -pub fn ddy_fine(component: F) -> F { - deriv_fn!(component, OpDPdyFine) -} + /// Result is the partial derivative of `Self` with respect to the window y coordinate. Uses local differencing + /// based on the value of `Self` for the current fragment and its immediate neighbor(s). + /// + /// An invocation will not execute a dynamic instance of this instruction (X') until all invocations in its + /// derivative group have executed all dynamic instances that are program-ordered before X'. + /// + /// This instruction is only valid in the Fragment Execution Model. + #[crate::macros::gpu_only] + fn ddy_fine(self) -> Self { + cap_deriv_control!(); + deriv_fn!(OpDPdyFine, self) + } -/// Returns the partial derivative of `component` with respect to the window's Y -/// coordinate. Uses local differencing based on the value of `component` for -/// the current fragment’s neighbors, and possibly, but not necessarily, -/// includes the value of `component` for the current fragment. That is, over a -/// given area, the implementation can compute Y derivatives in fewer unique -/// locations than would be allowed by [`ddy_fine`]. -#[crate::macros::vectorized] -#[crate::macros::gpu_only] -pub fn ddy_coarse(component: F) -> F { - deriv_fn!(component, OpDPdyCoarse) -} + /// Result is the partial derivative of `Self` with respect to the window y coordinate. Uses local differencing + /// based on the value of `Self` for the current fragment’s neighbors, and possibly, but not necessarily, includes + /// the value of `Self` for the current fragment. That is, over a given area, the implementation can compute y + /// derivatives in fewer unique locations than would be allowed for [`ddy_fine`]. + /// + /// An invocation will not execute a dynamic instance of this instruction (X') until all invocations in its + /// derivative group have executed all dynamic instances that are program-ordered before X'. + /// + /// This instruction is only valid in the Fragment Execution Model. + #[crate::macros::gpu_only] + fn ddy_coarse(self) -> Self { + cap_deriv_control!(); + deriv_fn!(OpDPdyCoarse, self) + } -/// Returns the sum of the absolute values of [`ddx`] and [`ddy`] as a single -/// operation. -#[crate::macros::vectorized] -#[crate::macros::gpu_only] -pub fn fwidth(component: F) -> F { - deriv_fn!(component, OpFwidth) -} + /// Result is the same as computing the sum of the absolute values of [`ddx`] and [`ddy`] on P. + /// + /// An invocation will not execute a dynamic instance of this instruction (X') until all invocations in its + /// derivative group have executed all dynamic instances that are program-ordered before X'. + /// + /// This instruction is only valid in the Fragment Execution Model. + #[crate::macros::gpu_only] + fn fwidth(self) -> Self { + deriv_fn!(OpFwidth, self) + } -/// Returns the sum of the absolute values of [`ddx_fine`] and [`ddy_fine`] as a -/// single operation. -#[crate::macros::vectorized] -#[crate::macros::gpu_only] -pub fn fwidth_fine(component: F) -> F { - deriv_fn!(component, OpFwidthFine) -} + /// Result is the same as computing the sum of the absolute values of [`ddx_fine`] and [`ddy_fine`] on P. + /// + /// An invocation will not execute a dynamic instance of this instruction (X') until all invocations in its + /// derivative group have executed all dynamic instances that are program-ordered before X'. + /// + /// This instruction is only valid in the Fragment Execution Model. + #[crate::macros::gpu_only] + fn fwidth_fine(self) -> Self { + cap_deriv_control!(); + deriv_fn!(OpFwidthFine, self) + } -/// Returns the sum of the absolute values of [`ddx_coarse`] and [`ddy_coarse`] -/// as a single operation. -#[crate::macros::vectorized] -#[crate::macros::gpu_only] -pub fn fwidth_coarse(component: F) -> F { - deriv_fn!(component, OpFwidthCoarse) + /// Result is the same as computing the sum of the absolute values of [`ddx_coarse`] and [`ddy_coarse`] on P. + /// + /// An invocation will not execute a dynamic instance of this instruction (X') until all invocations in its + /// derivative group have executed all dynamic instances that are program-ordered before X'. + /// + /// This instruction is only valid in the Fragment Execution Model. + #[crate::macros::gpu_only] + fn fwidth_coarse(self) -> Self { + cap_deriv_control!(); + deriv_fn!(OpFwidthCoarse, self) + } } + +unsafe impl Derivative for f32 {} +unsafe impl Derivative for Vec2 {} +unsafe impl Derivative for Vec3 {} +unsafe impl Derivative for Vec4 {} +unsafe impl Derivative for Vec3A {} diff --git a/crates/spirv-std/src/sealed.rs b/crates/spirv-std/src/sealed.rs index 53f0821721..7c8cdb5789 100644 --- a/crates/spirv-std/src/sealed.rs +++ b/crates/spirv-std/src/sealed.rs @@ -13,3 +13,18 @@ impl Sealed for i8 {} impl Sealed for i16 {} impl Sealed for i32 {} impl Sealed for i64 {} + +impl Sealed for glam::Vec2 {} +impl Sealed for glam::Vec3 {} +impl Sealed for glam::Vec4 {} +impl Sealed for glam::DVec2 {} +impl Sealed for glam::DVec3 {} +impl Sealed for glam::DVec4 {} +impl Sealed for glam::UVec2 {} +impl Sealed for glam::UVec3 {} +impl Sealed for glam::UVec4 {} +impl Sealed for glam::IVec2 {} +impl Sealed for glam::IVec3 {} +impl Sealed for glam::IVec4 {} + +impl Sealed for glam::Vec3A {} From e83037439a06c2972d45303f75f137f695988590 Mon Sep 17 00:00:00 2001 From: Firestar99 Date: Thu, 10 Apr 2025 16:17:22 +0200 Subject: [PATCH 2/7] derivative: remove vectorized macro, no longer needed --- crates/spirv-std/macros/src/lib.rs | 154 +---------------------------- 1 file changed, 4 insertions(+), 150 deletions(-) diff --git a/crates/spirv-std/macros/src/lib.rs b/crates/spirv-std/macros/src/lib.rs index 589dfcd296..f9f447004b 100644 --- a/crates/spirv-std/macros/src/lib.rs +++ b/crates/spirv-std/macros/src/lib.rs @@ -75,11 +75,9 @@ mod image; use proc_macro::TokenStream; -use proc_macro2::{Delimiter, Group, Ident, Span, TokenTree}; +use proc_macro2::{Delimiter, Group, Span, TokenTree}; -use syn::{ - ImplItemFn, ItemFn, Token, punctuated::Punctuated, spanned::Spanned, visit_mut::VisitMut, -}; +use syn::{ImplItemFn, visit_mut::VisitMut}; use quote::{ToTokens, quote}; use std::fmt::Write; @@ -224,150 +222,6 @@ pub fn gpu_only(_attr: TokenStream, item: TokenStream) -> TokenStream { output.into() } -/// Accepts a function with an argument named `component`, and outputs the -/// function plus a vectorized version of the function which accepts a vector -/// of `component`. This is mostly useful when you have the same impl body for -/// a scalar and vector versions of the same operation. -#[proc_macro_attribute] -#[doc(hidden)] -pub fn vectorized(_attr: TokenStream, item: TokenStream) -> TokenStream { - let function = syn::parse_macro_input!(item as syn::ItemFn); - let vectored_function = match create_vectored_fn(function.clone()) { - Ok(val) => val, - Err(err) => return err.to_compile_error().into(), - }; - - let output = quote::quote!( - #function - - #vectored_function - ); - - output.into() -} - -fn create_vectored_fn( - ItemFn { - attrs, - vis, - mut sig, - block, - }: ItemFn, -) -> Result { - const COMPONENT_ARG_NAME: &str = "component"; - let trait_bound_name = Ident::new("VECTOR", Span::mixed_site()); - let const_bound_name = Ident::new("LENGTH", Span::mixed_site()); - - sig.ident = Ident::new(&format!("{}_vector", sig.ident), Span::mixed_site()); - sig.output = syn::ReturnType::Type( - Default::default(), - Box::new(path_from_ident(trait_bound_name.clone())), - ); - - let component_type = sig.inputs.iter_mut().find_map(|x| match x { - syn::FnArg::Typed(ty) => match &*ty.pat { - syn::Pat::Ident(pat) if pat.ident == COMPONENT_ARG_NAME => Some(&mut ty.ty), - _ => None, - }, - syn::FnArg::Receiver(_) => None, - }); - - if component_type.is_none() { - return Err(syn::Error::new( - sig.inputs.span(), - "#[vectorized] requires an argument named `component`.", - )); - } - let component_type = component_type.unwrap(); - - let vector_path = { - let mut path = syn::Path { - leading_colon: None, - segments: Punctuated::new(), - }; - - for segment in &["crate", "vector"] { - path.segments - .push(Ident::new(segment, Span::mixed_site()).into()); - } - - path.segments.push(syn::PathSegment { - ident: Ident::new("Vector", Span::mixed_site()), - arguments: syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { - colon2_token: None, - lt_token: Default::default(), - args: { - let mut punct = Punctuated::new(); - - punct.push(syn::GenericArgument::Type(*component_type.clone())); - punct.push(syn::GenericArgument::Type(path_from_ident( - const_bound_name.clone(), - ))); - - punct - }, - gt_token: Default::default(), - }), - }); - - path - }; - - // Replace the original component type with vector version. - **component_type = path_from_ident(trait_bound_name.clone()); - - let trait_bounds = { - let mut punct = Punctuated::new(); - punct.push(syn::TypeParamBound::Trait(syn::TraitBound { - paren_token: None, - modifier: syn::TraitBoundModifier::None, - lifetimes: None, - path: vector_path, - })); - punct - }; - - sig.generics - .params - .push(syn::GenericParam::Type(syn::TypeParam { - attrs: Vec::new(), - ident: trait_bound_name, - colon_token: Some(Token![:](Span::mixed_site())), - bounds: trait_bounds, - eq_token: None, - default: None, - })); - - sig.generics - .params - .push(syn::GenericParam::Const(syn::ConstParam { - attrs: Vec::default(), - const_token: Default::default(), - ident: const_bound_name, - colon_token: Default::default(), - ty: syn::Type::Path(syn::TypePath { - qself: None, - path: Ident::new("usize", Span::mixed_site()).into(), - }), - eq_token: None, - default: None, - })); - - Ok(ItemFn { - attrs, - vis, - sig, - block, - }) -} - -fn path_from_ident(ident: Ident) -> syn::Type { - syn::Type::Path(syn::TypePath { - qself: None, - path: syn::Path::from(ident), - }) -} - /// Print a formatted string with a newline using the debug printf extension. /// /// Examples: @@ -392,7 +246,7 @@ pub fn debug_printfln(input: TokenStream) -> TokenStream { } struct DebugPrintfInput { - span: proc_macro2::Span, + span: Span, format_string: String, variables: Vec, } @@ -424,7 +278,7 @@ impl syn::parse::Parse for DebugPrintfInput { } } -fn parsing_error(message: &str, span: proc_macro2::Span) -> TokenStream { +fn parsing_error(message: &str, span: Span) -> TokenStream { syn::Error::new(span, message).to_compile_error().into() } From d8aba1c8cb0c9debc864f7bb95a3db22519a7d41 Mon Sep 17 00:00:00 2001 From: Firestar99 Date: Thu, 10 Apr 2025 16:43:52 +0200 Subject: [PATCH 3/7] derivative: add compiletests --- crates/spirv-std/src/arch/derivative.rs | 9 +++++++++ tests/ui/arch/derivative.rs | 16 ++++++++++++++++ tests/ui/arch/derivative.stderr | 11 +++++++++++ tests/ui/arch/derivative_control.rs | 21 +++++++++++++++++++++ tests/ui/arch/derivative_control.stderr | 17 +++++++++++++++++ 5 files changed, 74 insertions(+) create mode 100644 tests/ui/arch/derivative.rs create mode 100644 tests/ui/arch/derivative.stderr create mode 100644 tests/ui/arch/derivative_control.rs create mode 100644 tests/ui/arch/derivative_control.stderr diff --git a/crates/spirv-std/src/arch/derivative.rs b/crates/spirv-std/src/arch/derivative.rs index 9add59a385..8199fc2473 100644 --- a/crates/spirv-std/src/arch/derivative.rs +++ b/crates/spirv-std/src/arch/derivative.rs @@ -36,6 +36,7 @@ pub unsafe trait Derivative: Sealed + Default { /// /// This instruction is only valid in the Fragment Execution Model. #[crate::macros::gpu_only] + #[inline] fn ddx(self) -> Self { deriv_fn!(OpDPdx, self) } @@ -48,6 +49,7 @@ pub unsafe trait Derivative: Sealed + Default { /// /// This instruction is only valid in the Fragment Execution Model. #[crate::macros::gpu_only] + #[inline] fn ddx_fine(self) -> Self { cap_deriv_control!(); deriv_fn!(OpDPdxFine, self) @@ -63,6 +65,7 @@ pub unsafe trait Derivative: Sealed + Default { /// /// This instruction is only valid in the Fragment Execution Model. #[crate::macros::gpu_only] + #[inline] fn ddx_coarse(self) -> Self { cap_deriv_control!(); deriv_fn!(OpDPdxCoarse, self) @@ -77,6 +80,7 @@ pub unsafe trait Derivative: Sealed + Default { /// /// This instruction is only valid in the Fragment Execution Model. #[crate::macros::gpu_only] + #[inline] fn ddy(self) -> Self { deriv_fn!(OpDPdy, self) } @@ -89,6 +93,7 @@ pub unsafe trait Derivative: Sealed + Default { /// /// This instruction is only valid in the Fragment Execution Model. #[crate::macros::gpu_only] + #[inline] fn ddy_fine(self) -> Self { cap_deriv_control!(); deriv_fn!(OpDPdyFine, self) @@ -104,6 +109,7 @@ pub unsafe trait Derivative: Sealed + Default { /// /// This instruction is only valid in the Fragment Execution Model. #[crate::macros::gpu_only] + #[inline] fn ddy_coarse(self) -> Self { cap_deriv_control!(); deriv_fn!(OpDPdyCoarse, self) @@ -116,6 +122,7 @@ pub unsafe trait Derivative: Sealed + Default { /// /// This instruction is only valid in the Fragment Execution Model. #[crate::macros::gpu_only] + #[inline] fn fwidth(self) -> Self { deriv_fn!(OpFwidth, self) } @@ -127,6 +134,7 @@ pub unsafe trait Derivative: Sealed + Default { /// /// This instruction is only valid in the Fragment Execution Model. #[crate::macros::gpu_only] + #[inline] fn fwidth_fine(self) -> Self { cap_deriv_control!(); deriv_fn!(OpFwidthFine, self) @@ -139,6 +147,7 @@ pub unsafe trait Derivative: Sealed + Default { /// /// This instruction is only valid in the Fragment Execution Model. #[crate::macros::gpu_only] + #[inline] fn fwidth_coarse(self) -> Self { cap_deriv_control!(); deriv_fn!(OpFwidthCoarse, self) diff --git a/tests/ui/arch/derivative.rs b/tests/ui/arch/derivative.rs new file mode 100644 index 0000000000..4230673548 --- /dev/null +++ b/tests/ui/arch/derivative.rs @@ -0,0 +1,16 @@ +// build-pass +// compile-flags: -C llvm-args=--disassemble-fn=derivative::derivative + +use spirv_std::arch::Derivative; +use spirv_std::spirv; + +#[spirv(fragment)] +pub fn main() { + derivative(); +} + +pub fn derivative() { + Derivative::ddx(0.); + Derivative::ddy(0.); + Derivative::fwidth(0.); +} diff --git a/tests/ui/arch/derivative.stderr b/tests/ui/arch/derivative.stderr new file mode 100644 index 0000000000..7328d6c9db --- /dev/null +++ b/tests/ui/arch/derivative.stderr @@ -0,0 +1,11 @@ +%1 = OpFunction %2 None %3 +%4 = OpLabel +OpLine %5 41 8 +%6 = OpDPdx %7 %8 +OpLine %5 85 8 +%9 = OpDPdy %7 %8 +OpLine %5 127 8 +%10 = OpFwidth %7 %8 +OpNoLine +OpReturn +OpFunctionEnd diff --git a/tests/ui/arch/derivative_control.rs b/tests/ui/arch/derivative_control.rs new file mode 100644 index 0000000000..08626e3bbb --- /dev/null +++ b/tests/ui/arch/derivative_control.rs @@ -0,0 +1,21 @@ +// build-pass +// compile-flags: -C target-feature=+DerivativeControl +// compile-flags: -C llvm-args=--disassemble-fn=derivative_control::derivative + +use spirv_std::arch::Derivative; +use spirv_std::spirv; + +#[spirv(fragment)] +pub fn main() { + derivative(); +} + +pub fn derivative() { + Derivative::ddx_fine(0.); + Derivative::ddy_fine(0.); + Derivative::fwidth_fine(0.); + + Derivative::ddx_coarse(0.); + Derivative::ddy_coarse(0.); + Derivative::fwidth_coarse(0.); +} diff --git a/tests/ui/arch/derivative_control.stderr b/tests/ui/arch/derivative_control.stderr new file mode 100644 index 0000000000..107f655aac --- /dev/null +++ b/tests/ui/arch/derivative_control.stderr @@ -0,0 +1,17 @@ +%1 = OpFunction %2 None %3 +%4 = OpLabel +OpLine %5 55 8 +%6 = OpDPdxFine %7 %8 +OpLine %5 99 8 +%9 = OpDPdyFine %7 %8 +OpLine %5 140 8 +%10 = OpFwidthFine %7 %8 +OpLine %5 71 8 +%11 = OpDPdxCoarse %7 %8 +OpLine %5 115 8 +%12 = OpDPdyCoarse %7 %8 +OpLine %5 153 8 +%13 = OpFwidthCoarse %7 %8 +OpNoLine +OpReturn +OpFunctionEnd From 5efe4108c446dea0b463800ec3912505ff2c4609 Mon Sep 17 00:00:00 2001 From: Firestar99 Date: Thu, 10 Apr 2025 17:00:21 +0200 Subject: [PATCH 4/7] derivative: do not implicitly turn on `OpCapability DerivativeControl`, fix warnings --- crates/spirv-std/src/arch/derivative.rs | 15 +-------------- tests/ui/arch/derivative.stderr | 6 +++--- tests/ui/arch/derivative_control.stderr | 12 ++++++------ 3 files changed, 10 insertions(+), 23 deletions(-) diff --git a/crates/spirv-std/src/arch/derivative.rs b/crates/spirv-std/src/arch/derivative.rs index 8199fc2473..51adb99518 100644 --- a/crates/spirv-std/src/arch/derivative.rs +++ b/crates/spirv-std/src/arch/derivative.rs @@ -1,14 +1,7 @@ use crate::sealed::Sealed; use glam::{Vec2, Vec3, Vec3A, Vec4}; -macro_rules! cap_deriv_control { - () => { - unsafe { - core::arch::asm!("OpCapability DerivativeControl"); - } - }; -} - +#[cfg(target_arch = "spirv")] macro_rules! deriv_fn { ($inst:ident, $param:expr) => { unsafe { @@ -51,7 +44,6 @@ pub unsafe trait Derivative: Sealed + Default { #[crate::macros::gpu_only] #[inline] fn ddx_fine(self) -> Self { - cap_deriv_control!(); deriv_fn!(OpDPdxFine, self) } @@ -67,7 +59,6 @@ pub unsafe trait Derivative: Sealed + Default { #[crate::macros::gpu_only] #[inline] fn ddx_coarse(self) -> Self { - cap_deriv_control!(); deriv_fn!(OpDPdxCoarse, self) } @@ -95,7 +86,6 @@ pub unsafe trait Derivative: Sealed + Default { #[crate::macros::gpu_only] #[inline] fn ddy_fine(self) -> Self { - cap_deriv_control!(); deriv_fn!(OpDPdyFine, self) } @@ -111,7 +101,6 @@ pub unsafe trait Derivative: Sealed + Default { #[crate::macros::gpu_only] #[inline] fn ddy_coarse(self) -> Self { - cap_deriv_control!(); deriv_fn!(OpDPdyCoarse, self) } @@ -136,7 +125,6 @@ pub unsafe trait Derivative: Sealed + Default { #[crate::macros::gpu_only] #[inline] fn fwidth_fine(self) -> Self { - cap_deriv_control!(); deriv_fn!(OpFwidthFine, self) } @@ -149,7 +137,6 @@ pub unsafe trait Derivative: Sealed + Default { #[crate::macros::gpu_only] #[inline] fn fwidth_coarse(self) -> Self { - cap_deriv_control!(); deriv_fn!(OpFwidthCoarse, self) } } diff --git a/tests/ui/arch/derivative.stderr b/tests/ui/arch/derivative.stderr index 7328d6c9db..7b49e04ad2 100644 --- a/tests/ui/arch/derivative.stderr +++ b/tests/ui/arch/derivative.stderr @@ -1,10 +1,10 @@ %1 = OpFunction %2 None %3 %4 = OpLabel -OpLine %5 41 8 +OpLine %5 34 8 %6 = OpDPdx %7 %8 -OpLine %5 85 8 +OpLine %5 76 8 %9 = OpDPdy %7 %8 -OpLine %5 127 8 +OpLine %5 116 8 %10 = OpFwidth %7 %8 OpNoLine OpReturn diff --git a/tests/ui/arch/derivative_control.stderr b/tests/ui/arch/derivative_control.stderr index 107f655aac..6ca4a40238 100644 --- a/tests/ui/arch/derivative_control.stderr +++ b/tests/ui/arch/derivative_control.stderr @@ -1,16 +1,16 @@ %1 = OpFunction %2 None %3 %4 = OpLabel -OpLine %5 55 8 +OpLine %5 47 8 %6 = OpDPdxFine %7 %8 -OpLine %5 99 8 +OpLine %5 89 8 %9 = OpDPdyFine %7 %8 -OpLine %5 140 8 +OpLine %5 128 8 %10 = OpFwidthFine %7 %8 -OpLine %5 71 8 +OpLine %5 62 8 %11 = OpDPdxCoarse %7 %8 -OpLine %5 115 8 +OpLine %5 104 8 %12 = OpDPdyCoarse %7 %8 -OpLine %5 153 8 +OpLine %5 140 8 %13 = OpFwidthCoarse %7 %8 OpNoLine OpReturn From 3d2c03a0b3174943ffce9ff5cec10fa883ed79ed Mon Sep 17 00:00:00 2001 From: Firestar99 Date: Thu, 10 Apr 2025 17:06:50 +0200 Subject: [PATCH 5/7] derivative: rename derivative functions to match GLSL --- crates/spirv-std/src/arch/derivative.rs | 26 ++++++++++++------------- tests/ui/arch/derivative.rs | 4 ++-- tests/ui/arch/derivative_control.rs | 8 ++++---- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/crates/spirv-std/src/arch/derivative.rs b/crates/spirv-std/src/arch/derivative.rs index 51adb99518..933b94be51 100644 --- a/crates/spirv-std/src/arch/derivative.rs +++ b/crates/spirv-std/src/arch/derivative.rs @@ -21,7 +21,7 @@ macro_rules! deriv_fn { /// Types that can be derived by partial derivatives pub unsafe trait Derivative: Sealed + Default { /// Result is the partial derivative of `Self` with respect to the window x coordinate. Uses local differencing - /// based on the value of `Self`. Same result as either [`ddx_fine`] or [`ddx_coarse`] on `Self`. Selection of which + /// based on the value of `Self`. Same result as either [`dfdx_fine`] or [`dfdx_coarse`] on `Self`. Selection of which /// one is based on external factors. /// /// An invocation will not execute a dynamic instance of this instruction (X') until all invocations in its @@ -30,7 +30,7 @@ pub unsafe trait Derivative: Sealed + Default { /// This instruction is only valid in the Fragment Execution Model. #[crate::macros::gpu_only] #[inline] - fn ddx(self) -> Self { + fn dfdx(self) -> Self { deriv_fn!(OpDPdx, self) } @@ -43,14 +43,14 @@ pub unsafe trait Derivative: Sealed + Default { /// This instruction is only valid in the Fragment Execution Model. #[crate::macros::gpu_only] #[inline] - fn ddx_fine(self) -> Self { + fn dfdx_fine(self) -> Self { deriv_fn!(OpDPdxFine, self) } /// Result is the partial derivative of `Self` with respect to the window x coordinate. Uses local differencing /// based on the value of `Self` for the current fragment’s neighbors, and possibly, but not necessarily, includes /// the value of `Self` for the current fragment. That is, over a given area, the implementation can compute x - /// derivatives in fewer unique locations than would be allowed for [`ddx_fine`]. + /// derivatives in fewer unique locations than would be allowed for [`dfdx_fine`]. /// /// An invocation will not execute a dynamic instance of this instruction (X') until all invocations in its /// derivative group have executed all dynamic instances that are program-ordered before X'. @@ -58,12 +58,12 @@ pub unsafe trait Derivative: Sealed + Default { /// This instruction is only valid in the Fragment Execution Model. #[crate::macros::gpu_only] #[inline] - fn ddx_coarse(self) -> Self { + fn dfdx_coarse(self) -> Self { deriv_fn!(OpDPdxCoarse, self) } /// Result is the partial derivative of `Self` with respect to the window y coordinate. Uses local differencing - /// based on the value of `Self`. Same result as either [`ddy_fine`] or [`ddy_coarse`] on `Self`. Selection of which + /// based on the value of `Self`. Same result as either [`dfdy_fine`] or [`dfdy_coarse`] on `Self`. Selection of which /// one is based on external factors. /// /// An invocation will not execute a dynamic instance of this instruction (X') until all invocations in its @@ -72,7 +72,7 @@ pub unsafe trait Derivative: Sealed + Default { /// This instruction is only valid in the Fragment Execution Model. #[crate::macros::gpu_only] #[inline] - fn ddy(self) -> Self { + fn dfdy(self) -> Self { deriv_fn!(OpDPdy, self) } @@ -85,14 +85,14 @@ pub unsafe trait Derivative: Sealed + Default { /// This instruction is only valid in the Fragment Execution Model. #[crate::macros::gpu_only] #[inline] - fn ddy_fine(self) -> Self { + fn dfdy_fine(self) -> Self { deriv_fn!(OpDPdyFine, self) } /// Result is the partial derivative of `Self` with respect to the window y coordinate. Uses local differencing /// based on the value of `Self` for the current fragment’s neighbors, and possibly, but not necessarily, includes /// the value of `Self` for the current fragment. That is, over a given area, the implementation can compute y - /// derivatives in fewer unique locations than would be allowed for [`ddy_fine`]. + /// derivatives in fewer unique locations than would be allowed for [`dfdy_fine`]. /// /// An invocation will not execute a dynamic instance of this instruction (X') until all invocations in its /// derivative group have executed all dynamic instances that are program-ordered before X'. @@ -100,11 +100,11 @@ pub unsafe trait Derivative: Sealed + Default { /// This instruction is only valid in the Fragment Execution Model. #[crate::macros::gpu_only] #[inline] - fn ddy_coarse(self) -> Self { + fn dfdy_coarse(self) -> Self { deriv_fn!(OpDPdyCoarse, self) } - /// Result is the same as computing the sum of the absolute values of [`ddx`] and [`ddy`] on P. + /// Result is the same as computing the sum of the absolute values of [`dfdx`] and [`dfdy`] on P. /// /// An invocation will not execute a dynamic instance of this instruction (X') until all invocations in its /// derivative group have executed all dynamic instances that are program-ordered before X'. @@ -116,7 +116,7 @@ pub unsafe trait Derivative: Sealed + Default { deriv_fn!(OpFwidth, self) } - /// Result is the same as computing the sum of the absolute values of [`ddx_fine`] and [`ddy_fine`] on P. + /// Result is the same as computing the sum of the absolute values of [`dfdx_fine`] and [`dfdy_fine`] on P. /// /// An invocation will not execute a dynamic instance of this instruction (X') until all invocations in its /// derivative group have executed all dynamic instances that are program-ordered before X'. @@ -128,7 +128,7 @@ pub unsafe trait Derivative: Sealed + Default { deriv_fn!(OpFwidthFine, self) } - /// Result is the same as computing the sum of the absolute values of [`ddx_coarse`] and [`ddy_coarse`] on P. + /// Result is the same as computing the sum of the absolute values of [`dfdx_coarse`] and [`dfdy_coarse`] on P. /// /// An invocation will not execute a dynamic instance of this instruction (X') until all invocations in its /// derivative group have executed all dynamic instances that are program-ordered before X'. diff --git a/tests/ui/arch/derivative.rs b/tests/ui/arch/derivative.rs index 4230673548..06b63ab154 100644 --- a/tests/ui/arch/derivative.rs +++ b/tests/ui/arch/derivative.rs @@ -10,7 +10,7 @@ pub fn main() { } pub fn derivative() { - Derivative::ddx(0.); - Derivative::ddy(0.); + Derivative::dfdx(0.); + Derivative::dfdy(0.); Derivative::fwidth(0.); } diff --git a/tests/ui/arch/derivative_control.rs b/tests/ui/arch/derivative_control.rs index 08626e3bbb..3ff44dc4b4 100644 --- a/tests/ui/arch/derivative_control.rs +++ b/tests/ui/arch/derivative_control.rs @@ -11,11 +11,11 @@ pub fn main() { } pub fn derivative() { - Derivative::ddx_fine(0.); - Derivative::ddy_fine(0.); + Derivative::dfdx_fine(0.); + Derivative::dfdy_fine(0.); Derivative::fwidth_fine(0.); - Derivative::ddx_coarse(0.); - Derivative::ddy_coarse(0.); + Derivative::dfdx_coarse(0.); + Derivative::dfdy_coarse(0.); Derivative::fwidth_coarse(0.); } From d6aa374c2c2eb9a1dbb91e69534ea2c7abcc98a4 Mon Sep 17 00:00:00 2001 From: Firestar99 Date: Thu, 10 Apr 2025 18:09:52 +0200 Subject: [PATCH 6/7] derivative: fix doc links --- crates/spirv-std/src/arch/derivative.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/spirv-std/src/arch/derivative.rs b/crates/spirv-std/src/arch/derivative.rs index 933b94be51..e2f7381fa4 100644 --- a/crates/spirv-std/src/arch/derivative.rs +++ b/crates/spirv-std/src/arch/derivative.rs @@ -21,7 +21,7 @@ macro_rules! deriv_fn { /// Types that can be derived by partial derivatives pub unsafe trait Derivative: Sealed + Default { /// Result is the partial derivative of `Self` with respect to the window x coordinate. Uses local differencing - /// based on the value of `Self`. Same result as either [`dfdx_fine`] or [`dfdx_coarse`] on `Self`. Selection of which + /// based on the value of `Self`. Same result as either [`Self::dfdx_fine`] or [`Self::dfdx_coarse`] on `Self`. Selection of which /// one is based on external factors. /// /// An invocation will not execute a dynamic instance of this instruction (X') until all invocations in its @@ -50,7 +50,7 @@ pub unsafe trait Derivative: Sealed + Default { /// Result is the partial derivative of `Self` with respect to the window x coordinate. Uses local differencing /// based on the value of `Self` for the current fragment’s neighbors, and possibly, but not necessarily, includes /// the value of `Self` for the current fragment. That is, over a given area, the implementation can compute x - /// derivatives in fewer unique locations than would be allowed for [`dfdx_fine`]. + /// derivatives in fewer unique locations than would be allowed for [`Self::dfdx_fine`]. /// /// An invocation will not execute a dynamic instance of this instruction (X') until all invocations in its /// derivative group have executed all dynamic instances that are program-ordered before X'. @@ -63,7 +63,7 @@ pub unsafe trait Derivative: Sealed + Default { } /// Result is the partial derivative of `Self` with respect to the window y coordinate. Uses local differencing - /// based on the value of `Self`. Same result as either [`dfdy_fine`] or [`dfdy_coarse`] on `Self`. Selection of which + /// based on the value of `Self`. Same result as either [`Self::dfdy_fine`] or [`Self::dfdy_coarse`] on `Self`. Selection of which /// one is based on external factors. /// /// An invocation will not execute a dynamic instance of this instruction (X') until all invocations in its @@ -92,7 +92,7 @@ pub unsafe trait Derivative: Sealed + Default { /// Result is the partial derivative of `Self` with respect to the window y coordinate. Uses local differencing /// based on the value of `Self` for the current fragment’s neighbors, and possibly, but not necessarily, includes /// the value of `Self` for the current fragment. That is, over a given area, the implementation can compute y - /// derivatives in fewer unique locations than would be allowed for [`dfdy_fine`]. + /// derivatives in fewer unique locations than would be allowed for [`Self::dfdy_fine`]. /// /// An invocation will not execute a dynamic instance of this instruction (X') until all invocations in its /// derivative group have executed all dynamic instances that are program-ordered before X'. @@ -104,7 +104,7 @@ pub unsafe trait Derivative: Sealed + Default { deriv_fn!(OpDPdyCoarse, self) } - /// Result is the same as computing the sum of the absolute values of [`dfdx`] and [`dfdy`] on P. + /// Result is the same as computing the sum of the absolute values of [`Self::dfdx`] and [`Self::dfdy`] on P. /// /// An invocation will not execute a dynamic instance of this instruction (X') until all invocations in its /// derivative group have executed all dynamic instances that are program-ordered before X'. @@ -116,7 +116,7 @@ pub unsafe trait Derivative: Sealed + Default { deriv_fn!(OpFwidth, self) } - /// Result is the same as computing the sum of the absolute values of [`dfdx_fine`] and [`dfdy_fine`] on P. + /// Result is the same as computing the sum of the absolute values of [`Self::dfdx_fine`] and [`Self::dfdy_fine`] on P. /// /// An invocation will not execute a dynamic instance of this instruction (X') until all invocations in its /// derivative group have executed all dynamic instances that are program-ordered before X'. @@ -128,7 +128,7 @@ pub unsafe trait Derivative: Sealed + Default { deriv_fn!(OpFwidthFine, self) } - /// Result is the same as computing the sum of the absolute values of [`dfdx_coarse`] and [`dfdy_coarse`] on P. + /// Result is the same as computing the sum of the absolute values of [`Self::dfdx_coarse`] and [`Self::dfdy_coarse`] on P. /// /// An invocation will not execute a dynamic instance of this instruction (X') until all invocations in its /// derivative group have executed all dynamic instances that are program-ordered before X'. From 23313da6ffd8abbee6b6aaa1cffbba71f1bdbb8e Mon Sep 17 00:00:00 2001 From: Firestar99 Date: Thu, 10 Apr 2025 19:13:51 +0200 Subject: [PATCH 7/7] derivative: fix missing safety doc --- crates/spirv-std/src/arch/derivative.rs | 3 +++ tests/ui/arch/derivative.stderr | 6 +++--- tests/ui/arch/derivative_control.stderr | 12 ++++++------ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/crates/spirv-std/src/arch/derivative.rs b/crates/spirv-std/src/arch/derivative.rs index e2f7381fa4..050fdf2da8 100644 --- a/crates/spirv-std/src/arch/derivative.rs +++ b/crates/spirv-std/src/arch/derivative.rs @@ -19,6 +19,9 @@ macro_rules! deriv_fn { } /// Types that can be derived by partial derivatives +/// +/// # Safety +/// Result Type must be a scalar or vector of floating-point type using the IEEE 754 encoding. The component width must be 32 bits. pub unsafe trait Derivative: Sealed + Default { /// Result is the partial derivative of `Self` with respect to the window x coordinate. Uses local differencing /// based on the value of `Self`. Same result as either [`Self::dfdx_fine`] or [`Self::dfdx_coarse`] on `Self`. Selection of which diff --git a/tests/ui/arch/derivative.stderr b/tests/ui/arch/derivative.stderr index 7b49e04ad2..5a4404b1b3 100644 --- a/tests/ui/arch/derivative.stderr +++ b/tests/ui/arch/derivative.stderr @@ -1,10 +1,10 @@ %1 = OpFunction %2 None %3 %4 = OpLabel -OpLine %5 34 8 +OpLine %5 37 8 %6 = OpDPdx %7 %8 -OpLine %5 76 8 +OpLine %5 79 8 %9 = OpDPdy %7 %8 -OpLine %5 116 8 +OpLine %5 119 8 %10 = OpFwidth %7 %8 OpNoLine OpReturn diff --git a/tests/ui/arch/derivative_control.stderr b/tests/ui/arch/derivative_control.stderr index 6ca4a40238..0085d26025 100644 --- a/tests/ui/arch/derivative_control.stderr +++ b/tests/ui/arch/derivative_control.stderr @@ -1,16 +1,16 @@ %1 = OpFunction %2 None %3 %4 = OpLabel -OpLine %5 47 8 +OpLine %5 50 8 %6 = OpDPdxFine %7 %8 -OpLine %5 89 8 +OpLine %5 92 8 %9 = OpDPdyFine %7 %8 -OpLine %5 128 8 +OpLine %5 131 8 %10 = OpFwidthFine %7 %8 -OpLine %5 62 8 +OpLine %5 65 8 %11 = OpDPdxCoarse %7 %8 -OpLine %5 104 8 +OpLine %5 107 8 %12 = OpDPdyCoarse %7 %8 -OpLine %5 140 8 +OpLine %5 143 8 %13 = OpFwidthCoarse %7 %8 OpNoLine OpReturn