1use proc_macro::TokenStream;
2use proc_macro2::Span;
3use quote::*;
4use syn::{parse_macro_input, spanned::Spanned, Expr};
5
6macro_rules! try_compile_error {
7 ($x: expr) => {
8 match $x {
9 Ok(x) => x,
10 Err(e) => return e.into_compile_error().into(),
11 }
12 };
13}
14
15mod promote;
16mod vulkan_structure;
17
18fn find_vkhandle_source(fields: &syn::Fields) -> (proc_macro2::TokenStream, &syn::Type) {
19 #[inline]
20 fn has_handle_attribute(f: &syn::Field) -> bool {
21 f.attrs
22 .iter()
23 .any(|a| matches!(a.meta, syn::Meta::Path(ref path) if path.is_ident("handle")))
24 }
25
26 match fields {
27 syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed, .. }) => match unnamed.iter().position(has_handle_attribute)
28 {
29 Some(n) => (quote! { self.#n }, &unnamed[n].ty),
30 None => (quote! { self.0 }, &unnamed[0].ty),
31 },
32 syn::Fields::Named(syn::FieldsNamed { named, .. }) => {
33 let Some(f) = named.iter().find(|&f| has_handle_attribute(f)) else {
34 panic!("Named fields struct must have one field that marked by #[handle]");
35 };
36 let n = f.ident.as_ref().unwrap();
37
38 (quote! { self.#n }, &f.ty)
39 }
40 syn::Fields::Unit => panic!("Unit struct cannot auto-derive VkHandle"),
41 }
42}
43
44fn find_parent_field(fields: &syn::Fields) -> (usize, &syn::Field) {
45 #[inline]
46 fn has_parent_attribute(f: &syn::Field) -> bool {
47 f.attrs
48 .iter()
49 .any(|a| matches!(a.meta, syn::Meta::Path(ref path) if path.is_ident("parent")))
50 }
51
52 match fields {
53 syn::Fields::Named(ref n) => {
54 let mut parents = n.named.iter().enumerate().filter(|(_, n)| has_parent_attribute(n));
55
56 let Some(p) = parents.next() else {
57 panic!("could not find parent field");
58 };
59 if parents.next().is_some() {
60 panic!("too many parent fields");
61 }
62
63 p
64 }
65 syn::Fields::Unnamed(ref u) => {
66 let mut parents = u.unnamed.iter().enumerate().filter(|(_, n)| has_parent_attribute(n));
67
68 let Some(p) = parents.next() else {
69 panic!("could not find parent field");
70 };
71 if parents.next().is_some() {
72 panic!("too many parent fields");
73 }
74
75 p
76 }
77 syn::Fields::Unit => panic!("unit structure has no parent field"),
78 }
79}
80
81#[proc_macro_derive(VkHandle, attributes(handle))]
82pub fn derive_handle(tok: TokenStream) -> TokenStream {
83 let input = parse_macro_input!(tok as syn::DeriveInput);
84 let name = &input.ident;
85 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
86 let syn::Data::Struct(syn::DataStruct { ref fields, .. }) = input.data else {
87 panic!("AutoDerive VkHandle can only be applied for structs");
88 };
89 let (handle_field_ref, handle_ty) = find_vkhandle_source(fields);
90
91 quote! {
92 impl #impl_generics crate::VkHandle for #name #ty_generics #where_clause {
93 type Handle = #handle_ty;
94
95 #[inline(always)]
96 fn native_ptr(&self) -> Self::Handle {
97 #handle_field_ref
98 }
99 }
100 impl #impl_generics crate::VkHandleMut for #name #ty_generics #where_clause {
101 #[inline(always)]
102 fn native_ptr_mut(&mut self) -> Self::Handle {
103 #handle_field_ref
104 }
105 }
106 }
107 .into()
108}
109
110struct VkObjectAttributes {
111 span: Span,
112 r#type: Option<Expr>,
113}
114impl VkObjectAttributes {
115 #[inline]
116 pub fn find_and_parse(input: &syn::DeriveInput) -> syn::Result<Option<Self>> {
117 input
118 .attrs
119 .iter()
120 .find(|a| a.path().is_ident("VkObject"))
121 .map(Self::parse)
122 .transpose()
123 }
124
125 pub fn parse(attr: &syn::Attribute) -> syn::Result<Self> {
126 let mut r#type = None;
127 attr.parse_nested_meta(|meta| {
128 if meta.path.is_ident("type") {
129 r#type = Some(meta.value()?.parse::<Expr>()?);
130 return Ok(());
131 }
132
133 Err(meta.error("unkown attribute"))
134 })?;
135
136 Ok(Self {
137 span: attr.span(),
138 r#type,
139 })
140 }
141}
142
143#[proc_macro_derive(VkObject, attributes(VkObject))]
144pub fn derive_object(tok: TokenStream) -> TokenStream {
145 let input = parse_macro_input!(tok as syn::DeriveInput);
146 let name = &input.ident;
147 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
148
149 let object_attr = match VkObjectAttributes::find_and_parse(&input) {
150 Ok(Some(x)) => x,
151 Ok(None) => {
152 return syn::Error::new(Span::call_site(), "VkObject attribute required")
153 .into_compile_error()
154 .into()
155 }
156 Err(e) => return e.into_compile_error().into(),
157 };
158 let Some(object_type) = object_attr.r#type else {
159 return syn::Error::new(object_attr.span, "No type specified")
160 .into_compile_error()
161 .into();
162 };
163
164 quote! {
165 impl #impl_generics crate::VkObject for #name #ty_generics #where_clause {
166 const TYPE: crate::vk::VkObjectType = #object_type;
167 }
168 }
169 .into()
170}
171
172#[proc_macro_derive(InstanceChild, attributes(parent))]
173pub fn derive_instance_child(tok: TokenStream) -> TokenStream {
174 let input = parse_macro_input!(tok as syn::DeriveInput);
175 let name = &input.ident;
176 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
177
178 let syn::Data::Struct(ref s) = input.data else {
179 return syn::Error::new(Span::call_site(), "No type except structure can derive InstanceChild")
180 .into_compile_error()
181 .into();
182 };
183 let (parent_index, parent_field) = find_parent_field(&s.fields);
184 let parent_ty = &parent_field.ty;
185 let parent_field = match parent_field.ident {
186 Some(ref f) => quote! { &self.#f },
187 None => {
188 let x = syn::Index::from(parent_index);
189 quote! { &self.#x }
190 }
191 };
192
193 quote! {
194 impl #impl_generics crate::InstanceChild for #name #ty_generics #where_clause {
195 type ConcreteInstance = #parent_ty;
196
197 fn instance(&self) -> &Self::ConcreteInstance { #parent_field }
198 }
199 }
200 .into()
201}
202
203#[proc_macro_derive(InstanceChildTransferrable, attributes(parent))]
204pub fn derive_instance_child_transferrable(tok: TokenStream) -> TokenStream {
205 let input = parse_macro_input!(tok as syn::DeriveInput);
206 let name = &input.ident;
207 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
208 let syn::Data::Struct(ref s) = input.data else {
209 panic!("no type except structure can derive InstanceChild");
210 };
211 let (parent_index, parent_field) = find_parent_field(&s.fields);
212 let parent_field = match parent_field.ident {
213 Some(ref f) => quote! { self.#f },
214 None => {
215 let x = syn::Index::from(parent_index);
216 quote! { self.#x }
217 }
218 };
219
220 quote! {
221 impl #impl_generics crate::InstanceChildTransferrable for #name #ty_generics #where_clause {
222 fn transfer_instance(self) -> Self::ConcreteInstance { #parent_field }
223 }
224 }
225 .into()
226}
227
228#[proc_macro_derive(DeviceChild, attributes(parent))]
229pub fn derive_device_child(tok: TokenStream) -> TokenStream {
230 let input = parse_macro_input!(tok as syn::DeriveInput);
231 let name = &input.ident;
232 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
233 let syn::Data::Struct(ref s) = input.data else {
234 panic!("no type except structure can derive DeviceChild");
235 };
236 let (parent_index, parent_field) = find_parent_field(&s.fields);
237 let parent_ty = &parent_field.ty;
238 let parent_field = match parent_field.ident {
239 Some(ref f) => quote! { &self.#f },
240 None => {
241 let x = syn::Index::from(parent_index);
242 quote! { &self.#x }
243 }
244 };
245
246 quote! {
247 impl #impl_generics crate::DeviceChild for #name #ty_generics #where_clause {
248 type ConcreteDevice = #parent_ty;
249
250 fn device(&self) -> &Self::ConcreteDevice { #parent_field }
251 }
252 }
253 .into()
254}
255
256#[proc_macro_derive(DeviceChildTransferrable, attributes(parent))]
257pub fn derive_device_child_transferrable(tok: TokenStream) -> TokenStream {
258 let input = parse_macro_input!(tok as syn::DeriveInput);
259 let name = &input.ident;
260 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
261 let syn::Data::Struct(ref s) = input.data else {
262 panic!("no type except structure can derive DeviceChild");
263 };
264 let (parent_index, parent_field) = find_parent_field(&s.fields);
265 let parent_field = match parent_field.ident {
266 Some(ref f) => quote! { self.#f },
267 None => {
268 let x = syn::Index::from(parent_index);
269 quote! { self.#x }
270 }
271 };
272
273 quote! {
274 impl #impl_generics crate::DeviceChildTransferrable for #name #ty_generics #where_clause {
275 fn transfer_device(self) -> Self::ConcreteDevice { #parent_field }
276 }
277 }
278 .into()
279}
280
281#[proc_macro_derive(TypedVulkanStructure, attributes(VulkanStructure))]
282#[inline(always)]
283pub fn derive_vulkan_structure(input: TokenStream) -> TokenStream {
284 vulkan_structure::derive(input)
285}
286
287#[proc_macro_derive(TypedVulkanSinkStructure, attributes(VulkanSinkStructure))]
288#[inline(always)]
289pub fn derive_vulkan_sink_structure(input: TokenStream) -> TokenStream {
290 vulkan_structure::derive_sink(input)
291}
292
293#[proc_macro_attribute]
294#[inline(always)]
295pub fn promote_1_1(args: TokenStream, item: TokenStream) -> TokenStream {
296 promote::core(
297 parse_macro_input!(item as syn::Item),
298 args,
299 syn::LitStr::new("Allow1_1APIs", Span::call_site()),
300 )
301}
302#[proc_macro_attribute]
303#[inline(always)]
304pub fn promote_1_2(args: TokenStream, item: TokenStream) -> TokenStream {
305 promote::core(
306 parse_macro_input!(item as syn::Item),
307 args,
308 syn::LitStr::new("Allow1_2APIs", Span::call_site()),
309 )
310}
311#[proc_macro_attribute]
312#[inline(always)]
313pub fn promote_1_3(args: TokenStream, item: TokenStream) -> TokenStream {
314 promote::core(
315 parse_macro_input!(item as syn::Item),
316 args,
317 syn::LitStr::new("Allow1_3APIs", Span::call_site()),
318 )
319}
320#[proc_macro_attribute]
321#[inline(always)]
322pub fn promote_1_4(args: TokenStream, item: TokenStream) -> TokenStream {
323 promote::core(
324 parse_macro_input!(item as syn::Item),
325 args,
326 syn::LitStr::new("Allow1_4APIs", Span::call_site()),
327 )
328}
329
330#[inline]
331fn newtype_struct_org_type(d: &syn::DataStruct) -> syn::Result<&syn::Type> {
332 match d.fields {
333 syn::Fields::Unnamed(ref f) if f.unnamed.len() == 1 => Ok(&unsafe { f.unnamed.first().unwrap_unchecked() }.ty),
334 _ => Err(syn::Error::new_spanned(&d.fields, "not a newtype struct")),
335 }
336}
337
338#[proc_macro_attribute]
339pub fn vk_raw_handle(args: TokenStream, input: TokenStream) -> TokenStream {
340 let input = parse_macro_input!(input as syn::DeriveInput);
341 let name = &input.ident;
342 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
343 let syn::Data::Struct(ref s) = input.data else {
344 panic!("cannot derive VkRawHandle from this type");
345 };
346
347 let mut object_type = None::<Expr>;
348 let parser = syn::meta::parser(|p| {
349 if p.path.is_ident("object_type") {
350 object_type = Some(p.value()?.parse()?);
351 Ok(())
352 } else {
353 Err(p.error("unknown attr"))
354 }
355 });
356 parse_macro_input!(args with parser);
357
358 let dispatchable = matches!(try_compile_error!(newtype_struct_org_type(s)), syn::Type::Ptr(_));
359 let (null_def, raw_handle_conversion);
360 if dispatchable {
361 null_def = quote! { Self(std::ptr::null_mut()) };
362 raw_handle_conversion = quote! { self.0 as usize as _ };
363 } else {
364 null_def = quote! { Self(0) };
365 raw_handle_conversion = quote! { self.0 };
366 }
367
368 quote! {
369 #input
370 impl #impl_generics crate::handle::VkRawHandle for #name #ty_generics #where_clause {
371 const OBJECT_TYPE: VkObjectType = #object_type;
372 const NULL: Self = #null_def;
373
374 #[inline]
375 fn raw_handle_value(&self) -> u64 {
376 #raw_handle_conversion
377 }
378 }
379 }
380 .into()
381}
382
383#[proc_macro_derive(PFN, attributes(pfn_of))]
384pub fn derive_pfn(input: TokenStream) -> TokenStream {
385 let input = parse_macro_input!(input as syn::DeriveInput);
386 let impl_name = &input.ident;
387
388 let Some(org_attr) = input.attrs.iter().find_map(|a| match a.meta {
389 syn::Meta::List(ref ml) if ml.path.is_ident("pfn_of") => Some(ml),
390 _ => None,
391 }) else {
392 return syn::Error::new_spanned(&input, "no #[pfn_of(...)] found")
393 .into_compile_error()
394 .into();
395 };
396 let org_fn: syn::Path = try_compile_error!(org_attr.parse_args());
397 let Some(org_fn_name) = org_fn.get_ident().or_else(|| org_fn.segments.last().map(|l| &l.ident)) else {
398 return syn::Error::new_spanned(org_attr, "invalid pfn_of fn path")
399 .into_compile_error()
400 .into();
401 };
402 let org_fn_cstr = syn::LitCStr::new(
403 &unsafe { std::ffi::CString::from_vec_unchecked(org_fn_name.to_string().into_bytes()) },
404 org_fn.span(),
405 );
406
407 quote! {
408 unsafe impl crate::resolver::PFN for #impl_name {
409 const NAME_CSTR: &'static core::ffi::CStr = #org_fn_cstr;
410
411 #[inline(always)]
412 unsafe fn from_ptr(p: *const core::ffi::c_void) -> Self {
413 core::mem::transmute(p)
414 }
415 #[inline(always)]
416 unsafe fn from_void_fn(p: crate::vk::PFN_vkVoidFunction) -> Self {
417 core::mem::transmute(p)
418 }
419 }
420 }
421 .into()
422}
423
424#[proc_macro_derive(StaticCallable)]
425pub fn derive_static_callable(input: TokenStream) -> TokenStream {
426 let input = parse_macro_input!(input as syn::DeriveInput);
427 let impl_name = &input.ident;
428 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
429 let org_attr = &input
430 .attrs
431 .iter()
432 .filter_map(|a| a.meta.require_list().ok())
433 .find(|a| a.path.is_ident("pfn_of"))
434 .expect("no #[pfn_of] found");
435 let org_fn: syn::Path = org_attr.parse_args().expect("invalid arg for pfn_of");
436
437 quote! {
438 #[cfg(all(not(feature = "DynamicLoaded"), feature = "Implements"))]
439 impl #impl_generics crate::resolver::StaticCallable for #impl_name #ty_generics #where_clause {
440 const STATIC: Self = Self(#org_fn);
441 }
442 }
443 .into()
444}
445
446#[proc_macro_attribute]
448pub fn implements(args: TokenStream, target: TokenStream) -> TokenStream {
449 let mut out: TokenStream = quote! { #[cfg(feature = "Implements")] }.into();
450
451 if !args.is_empty() {
452 let extra_feature_requirements =
453 parse_macro_input!(args with syn::punctuated::Punctuated<syn::LitStr, syn::Token![,]>::parse_terminated);
454
455 out.extend(
456 extra_feature_requirements
457 .into_iter()
458 .flat_map(|x| TokenStream::from(quote! { #[cfg(feature = #x)] })),
459 );
460 }
461
462 out.extend(target);
463 out
464}
465
466#[proc_macro_attribute]
467pub fn bitflags_newtype(_args: TokenStream, target: TokenStream) -> TokenStream {
468 let input = parse_macro_input!(target as syn::DeriveInput);
469 let name = &input.ident;
470 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
471
472 let syn::Data::Struct(ref s) = input.data else {
473 return syn::Error::new(input.span(), "cannot use as bitflags struct")
474 .into_compile_error()
475 .into();
476 };
477 let org_type = try_compile_error!(newtype_struct_org_type(s));
478
479 quote! {
480 #[repr(transparent)]
481 #input
482 impl #impl_generics core::ops::BitOr for #name #ty_generics #where_clause {
483 type Output = Self;
484
485 #[inline(always)]
486 fn bitor(self, rhs: Self) -> Self {
487 Self(self.0 | rhs.0)
488 }
489 }
490 impl #impl_generics core::ops::BitAnd for #name #ty_generics #where_clause {
491 type Output = Self;
492
493 #[inline(always)]
494 fn bitand(self, rhs: Self) -> Self {
495 Self(self.0 & rhs.0)
496 }
497 }
498 impl #impl_generics core::ops::Not for #name #ty_generics #where_clause {
499 type Output = Self;
500
501 #[inline(always)]
502 fn not(self) -> Self {
503 Self(!self.0)
504 }
505 }
506 impl #impl_generics core::ops::BitOrAssign for #name #ty_generics #where_clause {
507 #[inline(always)]
508 fn bitor_assign(&mut self, rhs: Self) {
509 self.0 |= rhs.0;
510 }
511 }
512 impl #impl_generics core::ops::BitAndAssign for #name #ty_generics #where_clause {
513 #[inline(always)]
514 fn bitand_assign(&mut self, rhs: Self) {
515 self.0 &= rhs.0;
516 }
517 }
518
519 impl #impl_generics From<#name #ty_generics> for #org_type #where_clause {
520 #[inline(always)]
521 fn from(value: #name #ty_generics) -> Self {
522 value.0
523 }
524 }
525
526 impl #impl_generics #name #ty_generics #where_clause {
527 #[inline(always)]
529 pub const fn bits(&self) -> #org_type {
530 self.0
531 }
532
533 #[inline(always)]
535 pub const fn has_any(self, other: Self) -> bool {
536 (self.0 & other.0) != 0
537 }
538
539 #[inline(always)]
541 pub const fn has_all(self, other: Self) -> bool {
542 (self.0 & other.0) == other.0
543 }
544
545 #[inline(always)]
547 pub const fn merge(self, other: Self) -> Self {
548 Self(self.0 | other.0)
549 }
550 }
551 }
552 .into()
553}
554
555struct VkExtCommandInput {
556 span: Span,
557 base_define: syn::ForeignItemFn,
558 suffix: Option<syn::LitStr>,
559 promote: Option<syn::LitStr>,
560 static_callable: bool,
561}
562impl syn::parse::Parse for VkExtCommandInput {
563 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
564 let span = input.span();
565 let base_define = syn::ForeignItemFn::parse(input)?;
566
567 let (mut suffix, mut promote, mut static_callable) = (None, None, false);
568 while input.peek(syn::Ident) {
569 let extra_ident = syn::Ident::parse(input)?;
570
571 match &extra_ident.to_string() as &str {
572 "suffix" => {
573 if suffix.is_some() {
574 return Err(syn::Error::new(input.span(), "duplicated suffix extra"));
575 }
576
577 input.parse::<syn::Token![=]>()?;
578 suffix = Some(input.parse()?);
579 input.parse::<syn::Token![;]>()?;
580 }
581 "promote" => {
582 if promote.is_some() {
583 return Err(syn::Error::new(input.span(), "duplicated promote extra"));
584 }
585
586 input.parse::<syn::Token![=]>()?;
587 promote = Some(input.parse()?);
588 input.parse::<syn::Token![;]>()?;
589 }
590 "static_callable" => {
591 input.parse::<syn::Token![;]>()?;
592
593 static_callable = true;
594 }
595 unknown => return Err(syn::Error::new(input.span(), &format!("unknown extra: {unknown}"))),
596 }
597 }
598
599 Ok(Self {
600 span,
601 base_define,
602 suffix,
603 promote,
604 static_callable,
605 })
606 }
607}
608
609#[proc_macro]
610pub fn vk_ext_command(input: TokenStream) -> TokenStream {
611 let input = parse_macro_input!(input as VkExtCommandInput);
612
613 let base_vis = &input.base_define.vis;
614 let pfn_name = syn::Ident::new(
615 &format!("PFN_{}", input.base_define.sig.ident.to_string()),
616 input.base_define.sig.ident.span(),
617 );
618 let pfn_ty = syn::TypeBareFn {
619 lifetimes: None,
620 unsafety: Some(syn::Token)),
621 abi: Some(syn::Abi {
622 extern_token: syn::Token),
623 name: Some(syn::LitStr::new("system", Span::call_site())),
624 }),
625 fn_token: input.base_define.sig.fn_token.clone(),
626 paren_token: input.base_define.sig.paren_token.clone(),
627 inputs: input
628 .base_define
629 .sig
630 .inputs
631 .iter()
632 .map(|a| match a {
633 syn::FnArg::Receiver(_) => unreachable!("vk command cannot have receivers"),
634 syn::FnArg::Typed(t) => syn::BareFnArg {
635 attrs: t.attrs.clone(),
636 name: match *t.pat {
637 syn::Pat::Ident(ref x) => Some((x.ident.clone(), t.colon_token.clone())),
638 _ => None,
639 },
640 ty: *t.ty.clone(),
641 },
642 })
643 .collect(),
644 variadic: input.base_define.sig.variadic.as_ref().map(|v| syn::BareVariadic {
645 attrs: v.attrs.clone(),
646 name: match v.pat {
647 Some((ref p, c)) => match &**p {
648 &syn::Pat::Ident(ref x) => Some((x.ident.clone(), c.clone())),
649 _ => None,
650 },
651 _ => None,
652 },
653 dots: v.dots.clone(),
654 comma: v.comma.clone(),
655 }),
656 output: input.base_define.sig.output.clone(),
657 };
658 let fname_cstr = syn::LitCStr::new(
659 &unsafe { std::ffi::CString::from_vec_unchecked(input.base_define.sig.ident.to_string().into_bytes()) },
660 input.base_define.sig.ident.span(),
661 );
662
663 let ext_static_link_impl = if input.static_callable {
664 let fn_ident = &input.base_define.sig.ident;
665 let define = &input.base_define;
666
667 Some(quote! {
668 #[cfg(all(feature = "Implements", not(feature = "DynamicLoaded")))]
669 impl crate::resolver::StaticCallable for #pfn_name {
670 const STATIC: Self = Self(#fn_ident);
671 }
672
673 #[cfg(all(feature = "Implements", not(feature = "DynamicLoaded")))]
674 extern "system" {
675 #define
676 }
677 })
678 } else {
679 None
680 };
681
682 let promoted_pfn_impl = if let Some(ref p) = input.promote {
683 let base_fn_name = input.base_define.sig.ident.to_string();
684 let suffix = try_compile_error!(input
685 .suffix
686 .ok_or_else(|| syn::Error::new(input.span, "suffix extra required for promote")));
687 let Some(promoted_fn_name) = base_fn_name.strip_suffix(&suffix.value()) else {
688 return syn::Error::new_spanned(&input.base_define.sig.ident, "not suffixed")
689 .into_compile_error()
690 .into();
691 };
692 let promoted_fn_ident = syn::Ident::new(promoted_fn_name, input.base_define.sig.ident.span());
693 let pfn_name = syn::Ident::new(&format!("PFN_{promoted_fn_name}"), input.base_define.sig.ident.span());
694 let promoted_fn_cstr = syn::LitCStr::new(
695 &unsafe { std::ffi::CString::from_vec_unchecked(promoted_fn_name.as_bytes().to_vec()) },
696 input.base_define.sig.ident.span(),
697 );
698 let promoted_fn = syn::ForeignItemFn {
699 sig: syn::Signature {
700 ident: promoted_fn_ident.clone(),
701 ..input.base_define.sig.clone()
702 },
703 ..input.base_define.clone()
704 };
705
706 let promote_feature_name = syn::LitStr::new(&format!("Allow{}APIs", p.value().replace('.', "_")), p.span());
707
708 Some(quote! {
709 #[cfg(feature = #promote_feature_name)]
710 #[repr(transparent)]
711 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
712 #base_vis struct #pfn_name(pub #pfn_ty);
713 #[cfg(feature = #promote_feature_name)]
714 unsafe impl crate::resolver::PFN for #pfn_name {
715 const NAME_CSTR: &'static core::ffi::CStr = #promoted_fn_cstr;
716
717 #[inline(always)]
718 unsafe fn from_ptr(p: *const core::ffi::c_void) -> Self {
719 core::mem::transmute(p)
720 }
721 #[inline(always)]
722 unsafe fn from_void_fn(p: crate::vk::PFN_vkVoidFunction) -> Self {
723 core::mem::transmute(p)
724 }
725 }
726
727 #[cfg(all(feature = "Implements", feature = #promote_feature_name, not(feature = "DynamicLoaded")))]
728 impl crate::resolver::StaticCallable for #pfn_name {
729 const STATIC: Self = Self(#promoted_fn_ident);
730 }
731
732 #[cfg(all(feature = "Implements", feature = #promote_feature_name, not(feature = "DynamicLoaded")))]
733 extern "system" {
734 #promoted_fn
735 }
736 })
737 } else {
738 None
739 };
740
741 quote! {
742 #[repr(transparent)]
743 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
744 #base_vis struct #pfn_name(pub #pfn_ty);
745 unsafe impl crate::resolver::PFN for #pfn_name {
746 const NAME_CSTR: &'static core::ffi::CStr = #fname_cstr;
747
748 #[inline(always)]
749 unsafe fn from_ptr(p: *const core::ffi::c_void) -> Self {
750 core::mem::transmute(p)
751 }
752 #[inline(always)]
753 unsafe fn from_void_fn(p: crate::vk::PFN_vkVoidFunction) -> Self {
754 core::mem::transmute(p)
755 }
756 }
757
758 #ext_static_link_impl
759 #promoted_pfn_impl
760 }
761 .into()
762}
763
764#[proc_macro_derive(SpecializationConstants, attributes(constant_id))]
766pub fn safe_derive_spec_constant(tok: TokenStream) -> TokenStream {
767 let input = parse_macro_input!(tok as syn::DeriveInput);
768 let name = &input.ident;
769 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
770
771 match input.data {
772 syn::Data::Struct(syn::DataStruct { ref fields, .. }) => {
773 let mut entries = Vec::with_capacity(fields.len());
774 for f in fields {
775 let mut constant_ids = f.attrs.iter().filter_map(|a| match a {
776 syn::Attribute {
777 style: syn::AttrStyle::Outer,
778 meta: syn::Meta::NameValue(ref nv),
779 ..
780 } if nv.path.is_ident("constant_id") => Some(Ok(nv.value.clone())),
781 syn::Attribute {
782 style: syn::AttrStyle::Outer,
783 meta: syn::Meta::List(ref ml),
784 ..
785 } if ml.path.is_ident("constant_id") => Some(ml.parse_args::<syn::Expr>()),
786 _ => None,
787 });
788 let Some(first_cid) = constant_ids.next() else {
789 return syn::Error::new_spanned(f, "Missing constant_id attribute")
790 .into_compile_error()
791 .into();
792 };
793 if constant_ids.next().is_some() {
794 return syn::Error::new_spanned(f, "One or more constant_id attributes found on same field")
795 .into_compile_error()
796 .into();
797 }
798 let constant_id = try_compile_error!(first_cid);
799
800 let ty = &f.ty;
801 let ident = &f.ident;
802 entries.push(quote! { bedrock::SpecializationMapEntry {
803 constantID: #constant_id,
804 offset: core::mem::offset_of!(Self, #ident) as _,
805 size: core::mem::size_of::<#ty>(),
806 } });
807 }
808
809 quote! {
810 unsafe impl<#impl_generics> bedrock::SpecializationConstants for #name #ty_generics #where_clause {
811 const ENTRIES: &'static [bedrock::SpecializationMapEntry] = &[#(#entries),*];
812
813 #[inline(always)]
814 fn as_ptr(&self) -> *const core::ffi::c_void {
815 self as *const _ as _
816 }
817 }
818 }
819 .into()
820 }
821 _ => unimplemented!("unsupported"),
822 }
823}