mirror of
https://github.com/EmbarkStudios/rust-gpu.git
synced 2024-11-25 08:14:12 +00:00
Add debug_printf!
and debug_printfln!
macros that uses the DebugPrintf extension (#768)
This commit is contained in:
parent
28313a2029
commit
e5c2953ea6
@ -350,3 +350,266 @@ fn path_from_ident(ident: Ident) -> syn::Type {
|
|||||||
path: syn::Path::from(ident),
|
path: syn::Path::from(ident),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Print a formatted string with a newline using the debug printf extension.
|
||||||
|
///
|
||||||
|
/// Examples:
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// debug_printfln!("uv: %v2f", uv);
|
||||||
|
/// debug_printfln!("pos.x: %f, pos.z: %f, int: %i", pos.x, pos.z, int);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// See <https://github.com/KhronosGroup/Vulkan-ValidationLayers/blob/master/docs/debug_printf.md#debug-printf-format-string> for formatting rules.
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn debug_printf(input: TokenStream) -> TokenStream {
|
||||||
|
debug_printf_inner(syn::parse_macro_input!(input as DebugPrintfInput))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Similar to `debug_printf` but appends a newline to the format string.
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn debug_printfln(input: TokenStream) -> TokenStream {
|
||||||
|
let mut input = syn::parse_macro_input!(input as DebugPrintfInput);
|
||||||
|
input.format_string.push('\n');
|
||||||
|
debug_printf_inner(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DebugPrintfInput {
|
||||||
|
span: proc_macro2::Span,
|
||||||
|
format_string: String,
|
||||||
|
variables: Vec<syn::Expr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl syn::parse::Parse for DebugPrintfInput {
|
||||||
|
fn parse(input: syn::parse::ParseStream<'_>) -> syn::parse::Result<Self> {
|
||||||
|
let span = input.span();
|
||||||
|
|
||||||
|
if input.is_empty() {
|
||||||
|
return Ok(Self {
|
||||||
|
span,
|
||||||
|
format_string: Default::default(),
|
||||||
|
variables: Default::default(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let format_string = input.parse::<syn::LitStr>()?;
|
||||||
|
if !input.is_empty() {
|
||||||
|
input.parse::<syn::token::Comma>()?;
|
||||||
|
}
|
||||||
|
let variables =
|
||||||
|
syn::punctuated::Punctuated::<syn::Expr, syn::token::Comma>::parse_terminated(input)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
span,
|
||||||
|
format_string: format_string.value(),
|
||||||
|
variables: variables.into_iter().collect(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parsing_error(message: &str, span: proc_macro2::Span) -> TokenStream {
|
||||||
|
syn::Error::new(span, message).to_compile_error().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
enum FormatType {
|
||||||
|
Scalar {
|
||||||
|
ty: proc_macro2::TokenStream,
|
||||||
|
},
|
||||||
|
Vector {
|
||||||
|
ty: proc_macro2::TokenStream,
|
||||||
|
width: usize,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
fn debug_printf_inner(input: DebugPrintfInput) -> TokenStream {
|
||||||
|
let DebugPrintfInput {
|
||||||
|
format_string,
|
||||||
|
variables,
|
||||||
|
span,
|
||||||
|
} = input;
|
||||||
|
|
||||||
|
fn map_specifier_to_type(
|
||||||
|
specifier: char,
|
||||||
|
chars: &mut std::str::Chars<'_>,
|
||||||
|
) -> Option<proc_macro2::TokenStream> {
|
||||||
|
let mut peekable = chars.peekable();
|
||||||
|
|
||||||
|
Some(match specifier {
|
||||||
|
'd' | 'i' => quote::quote! { i32 },
|
||||||
|
'o' | 'x' | 'X' => quote::quote! { u32 },
|
||||||
|
'a' | 'A' | 'e' | 'E' | 'f' | 'F' | 'g' | 'G' => quote::quote! { f32 },
|
||||||
|
'u' => {
|
||||||
|
if matches!(peekable.peek(), Some('l')) {
|
||||||
|
chars.next();
|
||||||
|
quote::quote! { u64 }
|
||||||
|
} else {
|
||||||
|
quote::quote! { u32 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'l' => {
|
||||||
|
if matches!(peekable.peek(), Some('u' | 'x')) {
|
||||||
|
chars.next();
|
||||||
|
quote::quote! { u64 }
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut chars = format_string.chars();
|
||||||
|
let mut format_arguments = Vec::new();
|
||||||
|
|
||||||
|
while let Some(mut ch) = chars.next() {
|
||||||
|
if ch == '%' {
|
||||||
|
ch = match chars.next() {
|
||||||
|
Some('%') => continue,
|
||||||
|
None => return parsing_error("Unterminated format specifier", span),
|
||||||
|
Some(ch) => ch,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut has_precision = false;
|
||||||
|
|
||||||
|
while matches!(ch, '0'..='9') {
|
||||||
|
ch = match chars.next() {
|
||||||
|
Some(ch) => ch,
|
||||||
|
None => {
|
||||||
|
return parsing_error(
|
||||||
|
"Unterminated format specifier: missing type after precision",
|
||||||
|
span,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
has_precision = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if has_precision && ch == '.' {
|
||||||
|
ch = match chars.next() {
|
||||||
|
Some(ch) => ch,
|
||||||
|
None => {
|
||||||
|
return parsing_error(
|
||||||
|
"Unterminated format specifier: missing type after decimal point",
|
||||||
|
span,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
while matches!(ch, '0'..='9') {
|
||||||
|
ch = match chars.next() {
|
||||||
|
Some(ch) => ch,
|
||||||
|
None => return parsing_error(
|
||||||
|
"Unterminated format specifier: missing type after fraction precision",
|
||||||
|
span,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ch == 'v' {
|
||||||
|
let width = match chars.next() {
|
||||||
|
Some('2') => 2,
|
||||||
|
Some('3') => 3,
|
||||||
|
Some('4') => 4,
|
||||||
|
Some(ch) => {
|
||||||
|
return parsing_error(&format!("Invalid width for vector: {}", ch), span)
|
||||||
|
}
|
||||||
|
None => return parsing_error("Missing vector dimensions specifier", span),
|
||||||
|
};
|
||||||
|
|
||||||
|
ch = match chars.next() {
|
||||||
|
Some(ch) => ch,
|
||||||
|
None => return parsing_error("Missing vector type specifier", span),
|
||||||
|
};
|
||||||
|
|
||||||
|
let ty = match map_specifier_to_type(ch, &mut chars) {
|
||||||
|
Some(ty) => ty,
|
||||||
|
_ => {
|
||||||
|
return parsing_error(
|
||||||
|
&format!("Unrecognised vector type specifier: '{}'", ch),
|
||||||
|
span,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
format_arguments.push(FormatType::Vector { ty, width });
|
||||||
|
} else {
|
||||||
|
let ty = match map_specifier_to_type(ch, &mut chars) {
|
||||||
|
Some(ty) => ty,
|
||||||
|
_ => {
|
||||||
|
return parsing_error(
|
||||||
|
&format!("Unrecognised format specifier: '{}'", ch),
|
||||||
|
span,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
format_arguments.push(FormatType::Scalar { ty });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if format_arguments.len() != variables.len() {
|
||||||
|
return syn::Error::new(
|
||||||
|
span,
|
||||||
|
&format!(
|
||||||
|
"{} % arguments were found, but {} variables were given",
|
||||||
|
format_arguments.len(),
|
||||||
|
variables.len()
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.to_compile_error()
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut variable_idents = String::new();
|
||||||
|
let mut input_registers = Vec::new();
|
||||||
|
let mut op_loads = Vec::new();
|
||||||
|
|
||||||
|
for (i, (variable, format_argument)) in variables.into_iter().zip(format_arguments).enumerate()
|
||||||
|
{
|
||||||
|
let ident = quote::format_ident!("_{}", i);
|
||||||
|
|
||||||
|
variable_idents.push_str(&format!("%{} ", ident));
|
||||||
|
|
||||||
|
let assert_fn = match format_argument {
|
||||||
|
FormatType::Scalar { ty } => {
|
||||||
|
quote::quote! { spirv_std::debug_printf_assert_is_type::<#ty> }
|
||||||
|
}
|
||||||
|
FormatType::Vector { ty, width } => {
|
||||||
|
quote::quote! { spirv_std::debug_printf_assert_is_vector::<#ty, _, #width> }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
input_registers.push(quote::quote! {
|
||||||
|
#ident = in(reg) &#assert_fn(#variable),
|
||||||
|
});
|
||||||
|
|
||||||
|
let op_load = format!("%{ident} = OpLoad _ {{{ident}}}", ident = ident);
|
||||||
|
|
||||||
|
op_loads.push(quote::quote! {
|
||||||
|
#op_load,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let input_registers = input_registers
|
||||||
|
.into_iter()
|
||||||
|
.collect::<proc_macro2::TokenStream>();
|
||||||
|
let op_loads = op_loads.into_iter().collect::<proc_macro2::TokenStream>();
|
||||||
|
|
||||||
|
let op_string = format!("%string = OpString {:?}", format_string);
|
||||||
|
|
||||||
|
let output = quote::quote! {
|
||||||
|
asm!(
|
||||||
|
"%void = OpTypeVoid",
|
||||||
|
#op_string,
|
||||||
|
"%debug_printf = OpExtInstImport \"NonSemantic.DebugPrintf\"",
|
||||||
|
#op_loads
|
||||||
|
concat!("%result = OpExtInst %void %debug_printf 1 %string ", #variable_idents),
|
||||||
|
#input_registers
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
output.into()
|
||||||
|
}
|
||||||
|
@ -130,3 +130,19 @@ extern "C" fn rust_eh_personality() {}
|
|||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
/// [spirv_types]
|
/// [spirv_types]
|
||||||
pub fn workaround_rustdoc_ice_84738() {}
|
pub fn workaround_rustdoc_ice_84738() {}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn debug_printf_assert_is_type<T>(ty: T) -> T {
|
||||||
|
ty
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn debug_printf_assert_is_vector<
|
||||||
|
TY: crate::scalar::Scalar,
|
||||||
|
V: crate::vector::Vector<TY, SIZE>,
|
||||||
|
const SIZE: usize,
|
||||||
|
>(
|
||||||
|
vec: V,
|
||||||
|
) -> V {
|
||||||
|
vec
|
||||||
|
}
|
||||||
|
52
tests/ui/arch/debug_printf.rs
Normal file
52
tests/ui/arch/debug_printf.rs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// build-pass
|
||||||
|
// compile-flags: -Ctarget-feature=+ext:SPV_KHR_non_semantic_info
|
||||||
|
|
||||||
|
use spirv_std::{
|
||||||
|
glam::{IVec2, UVec2, Vec2, Vec3, Vec4},
|
||||||
|
macros::{debug_printf, debug_printfln},
|
||||||
|
};
|
||||||
|
|
||||||
|
fn func(a: f32, b: f32) -> f32 {
|
||||||
|
a * b + 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Struct {
|
||||||
|
a: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Struct {
|
||||||
|
fn method(&self, b: f32, c: f32) -> f32 {
|
||||||
|
self.a * b + c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[spirv(fragment)]
|
||||||
|
pub fn main() {
|
||||||
|
unsafe {
|
||||||
|
debug_printf!();
|
||||||
|
debug_printfln!();
|
||||||
|
debug_printfln!("Hello World");
|
||||||
|
debug_printfln!("Hello World",);
|
||||||
|
debug_printfln!(r#"Hello "World""#);
|
||||||
|
debug_printfln!(
|
||||||
|
r#"Hello "World"
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
debug_printfln!("Hello \"World\"\n\n");
|
||||||
|
debug_printfln!("%%r %%f %%%%f %%%%%u", 77);
|
||||||
|
}
|
||||||
|
|
||||||
|
let vec = Vec2::new(1.52, 25.1);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
debug_printfln!("%v2f", vec);
|
||||||
|
debug_printfln!("%1v2f", { vec * 2.0 });
|
||||||
|
debug_printfln!("%1.2v2f", vec * 3.0);
|
||||||
|
debug_printfln!("%% %v2f %%", vec * 4.0);
|
||||||
|
debug_printfln!("%u %i %f 🐉", 11_u32, -11_i32, 11.0_f32);
|
||||||
|
debug_printfln!("%f", func(33.0, 44.0));
|
||||||
|
debug_printfln!("%f", Struct { a: 33.0 }.method(44.0, 55.0));
|
||||||
|
debug_printfln!("%v3f %v4f", Vec3::new(1.0, 1.0, 1.0), Vec4::splat(5.0));
|
||||||
|
debug_printfln!("%v2u %v2i", UVec2::new(1, 1), IVec2::splat(-5));
|
||||||
|
}
|
||||||
|
}
|
25
tests/ui/arch/debug_printf_type_checking.rs
Normal file
25
tests/ui/arch/debug_printf_type_checking.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// build-fail
|
||||||
|
// normalize-stderr-test "\S*/crates/spirv-std/src/" -> "$$SPIRV_STD_SRC/"
|
||||||
|
// compile-flags: -Ctarget-feature=+ext:SPV_KHR_non_semantic_info
|
||||||
|
|
||||||
|
use spirv_std::{glam::Vec2, macros::debug_printf};
|
||||||
|
|
||||||
|
#[spirv(fragment)]
|
||||||
|
pub fn main() {
|
||||||
|
unsafe {
|
||||||
|
debug_printf!("%1");
|
||||||
|
debug_printf!("%1.");
|
||||||
|
debug_printf!("%.");
|
||||||
|
debug_printf!("%.1");
|
||||||
|
debug_printf!("%1.1");
|
||||||
|
debug_printf!("%1.1v");
|
||||||
|
debug_printf!("%1.1v5");
|
||||||
|
debug_printf!("%1.1v2");
|
||||||
|
debug_printf!("%1.1v2r");
|
||||||
|
debug_printf!("%r", 11_i32);
|
||||||
|
debug_printf!("%f", 11_u32);
|
||||||
|
debug_printf!("%u", 11.0_f32);
|
||||||
|
debug_printf!("%v2f", 11.0);
|
||||||
|
debug_printf!("%f", Vec2::splat(33.3));
|
||||||
|
}
|
||||||
|
}
|
113
tests/ui/arch/debug_printf_type_checking.stderr
Normal file
113
tests/ui/arch/debug_printf_type_checking.stderr
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
error: Unterminated format specifier: missing type after precision
|
||||||
|
--> $DIR/debug_printf_type_checking.rs:10:23
|
||||||
|
|
|
||||||
|
10 | debug_printf!("%1");
|
||||||
|
| ^^^^
|
||||||
|
|
||||||
|
error: Unterminated format specifier: missing type after decimal point
|
||||||
|
--> $DIR/debug_printf_type_checking.rs:11:23
|
||||||
|
|
|
||||||
|
11 | debug_printf!("%1.");
|
||||||
|
| ^^^^^
|
||||||
|
|
||||||
|
error: Unrecognised format specifier: '.'
|
||||||
|
--> $DIR/debug_printf_type_checking.rs:12:23
|
||||||
|
|
|
||||||
|
12 | debug_printf!("%.");
|
||||||
|
| ^^^^
|
||||||
|
|
||||||
|
error: Unrecognised format specifier: '.'
|
||||||
|
--> $DIR/debug_printf_type_checking.rs:13:23
|
||||||
|
|
|
||||||
|
13 | debug_printf!("%.1");
|
||||||
|
| ^^^^^
|
||||||
|
|
||||||
|
error: Unterminated format specifier: missing type after fraction precision
|
||||||
|
--> $DIR/debug_printf_type_checking.rs:14:23
|
||||||
|
|
|
||||||
|
14 | debug_printf!("%1.1");
|
||||||
|
| ^^^^^^
|
||||||
|
|
||||||
|
error: Missing vector dimensions specifier
|
||||||
|
--> $DIR/debug_printf_type_checking.rs:15:23
|
||||||
|
|
|
||||||
|
15 | debug_printf!("%1.1v");
|
||||||
|
| ^^^^^^^
|
||||||
|
|
||||||
|
error: Invalid width for vector: 5
|
||||||
|
--> $DIR/debug_printf_type_checking.rs:16:23
|
||||||
|
|
|
||||||
|
16 | debug_printf!("%1.1v5");
|
||||||
|
| ^^^^^^^^
|
||||||
|
|
||||||
|
error: Missing vector type specifier
|
||||||
|
--> $DIR/debug_printf_type_checking.rs:17:23
|
||||||
|
|
|
||||||
|
17 | debug_printf!("%1.1v2");
|
||||||
|
| ^^^^^^^^
|
||||||
|
|
||||||
|
error: Unrecognised vector type specifier: 'r'
|
||||||
|
--> $DIR/debug_printf_type_checking.rs:18:23
|
||||||
|
|
|
||||||
|
18 | debug_printf!("%1.1v2r");
|
||||||
|
| ^^^^^^^^^
|
||||||
|
|
||||||
|
error: Unrecognised format specifier: 'r'
|
||||||
|
--> $DIR/debug_printf_type_checking.rs:19:23
|
||||||
|
|
|
||||||
|
19 | debug_printf!("%r", 11_i32);
|
||||||
|
| ^^^^
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/debug_printf_type_checking.rs:20:29
|
||||||
|
|
|
||||||
|
20 | debug_printf!("%f", 11_u32);
|
||||||
|
| ^^^^^^ expected `f32`, found `u32`
|
||||||
|
|
|
||||||
|
help: change the type of the numeric literal from `u32` to `f32`
|
||||||
|
|
|
||||||
|
20 | debug_printf!("%f", 11_f32);
|
||||||
|
| ~~~
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/debug_printf_type_checking.rs:21:29
|
||||||
|
|
|
||||||
|
21 | debug_printf!("%u", 11.0_f32);
|
||||||
|
| ^^^^^^^^ expected `u32`, found `f32`
|
||||||
|
|
|
||||||
|
help: change the type of the numeric literal from `f32` to `u32`
|
||||||
|
|
|
||||||
|
21 | debug_printf!("%u", 11u32);
|
||||||
|
| ~~~
|
||||||
|
|
||||||
|
error[E0277]: the trait bound `{float}: Vector<f32, 2_usize>` is not satisfied
|
||||||
|
--> $DIR/debug_printf_type_checking.rs:22:31
|
||||||
|
|
|
||||||
|
22 | debug_printf!("%v2f", 11.0);
|
||||||
|
| ----------------------^^^^--
|
||||||
|
| | |
|
||||||
|
| | the trait `Vector<f32, 2_usize>` is not implemented for `{float}`
|
||||||
|
| required by a bound introduced by this call
|
||||||
|
|
|
||||||
|
= help: the following implementations were found:
|
||||||
|
<BVec2 as Vector<bool, 2_usize>>
|
||||||
|
<BVec3 as Vector<bool, 3_usize>>
|
||||||
|
<BVec4 as Vector<bool, 4_usize>>
|
||||||
|
<DVec2 as Vector<f64, 2_usize>>
|
||||||
|
and 13 others
|
||||||
|
note: required by a bound in `debug_printf_assert_is_vector`
|
||||||
|
--> $SPIRV_STD_SRC/lib.rs:142:8
|
||||||
|
|
|
||||||
|
142 | V: crate::vector::Vector<TY, SIZE>,
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `debug_printf_assert_is_vector`
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/debug_printf_type_checking.rs:23:29
|
||||||
|
|
|
||||||
|
23 | debug_printf!("%f", Vec2::splat(33.3));
|
||||||
|
| ^^^^^^^^^^^^^^^^^ expected `f32`, found struct `Vec2`
|
||||||
|
|
||||||
|
error: aborting due to 14 previous errors
|
||||||
|
|
||||||
|
Some errors have detailed explanations: E0277, E0308.
|
||||||
|
For more information about an error, try `rustc --explain E0277`.
|
Loading…
Reference in New Issue
Block a user