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() } diff --git a/crates/spirv-std/src/arch/derivative.rs b/crates/spirv-std/src/arch/derivative.rs index 3ab5c2d1fe..050fdf2da8 100644 --- a/crates/spirv-std/src/arch/derivative.rs +++ b/crates/spirv-std/src/arch/derivative.rs @@ -1,102 +1,151 @@ -use crate::float::Float; +use crate::sealed::Sealed; +use glam::{Vec2, Vec3, Vec3A, Vec4}; #[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 +/// +/// # 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 + /// 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] + #[inline] + fn dfdx(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] + #[inline] + fn dfdx_fine(self) -> Self { + 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 [`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'. + /// + /// This instruction is only valid in the Fragment Execution Model. + #[crate::macros::gpu_only] + #[inline] + fn dfdx_coarse(self) -> Self { + 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 [`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 + /// 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] + #[inline] + fn dfdy(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] + #[inline] + fn dfdy_fine(self) -> Self { + 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 [`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'. + /// + /// This instruction is only valid in the Fragment Execution Model. + #[crate::macros::gpu_only] + #[inline] + fn dfdy_coarse(self) -> Self { + 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 [`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'. + /// + /// This instruction is only valid in the Fragment Execution Model. + #[crate::macros::gpu_only] + #[inline] + 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 [`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'. + /// + /// This instruction is only valid in the Fragment Execution Model. + #[crate::macros::gpu_only] + #[inline] + fn fwidth_fine(self) -> Self { + 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 [`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'. + /// + /// This instruction is only valid in the Fragment Execution Model. + #[crate::macros::gpu_only] + #[inline] + fn fwidth_coarse(self) -> Self { + 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 {} diff --git a/tests/ui/arch/derivative.rs b/tests/ui/arch/derivative.rs new file mode 100644 index 0000000000..06b63ab154 --- /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::dfdx(0.); + Derivative::dfdy(0.); + Derivative::fwidth(0.); +} diff --git a/tests/ui/arch/derivative.stderr b/tests/ui/arch/derivative.stderr new file mode 100644 index 0000000000..5a4404b1b3 --- /dev/null +++ b/tests/ui/arch/derivative.stderr @@ -0,0 +1,11 @@ +%1 = OpFunction %2 None %3 +%4 = OpLabel +OpLine %5 37 8 +%6 = OpDPdx %7 %8 +OpLine %5 79 8 +%9 = OpDPdy %7 %8 +OpLine %5 119 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..3ff44dc4b4 --- /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::dfdx_fine(0.); + Derivative::dfdy_fine(0.); + Derivative::fwidth_fine(0.); + + Derivative::dfdx_coarse(0.); + Derivative::dfdy_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..0085d26025 --- /dev/null +++ b/tests/ui/arch/derivative_control.stderr @@ -0,0 +1,17 @@ +%1 = OpFunction %2 None %3 +%4 = OpLabel +OpLine %5 50 8 +%6 = OpDPdxFine %7 %8 +OpLine %5 92 8 +%9 = OpDPdyFine %7 %8 +OpLine %5 131 8 +%10 = OpFwidthFine %7 %8 +OpLine %5 65 8 +%11 = OpDPdxCoarse %7 %8 +OpLine %5 107 8 +%12 = OpDPdyCoarse %7 %8 +OpLine %5 143 8 +%13 = OpFwidthCoarse %7 %8 +OpNoLine +OpReturn +OpFunctionEnd