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: bedrock_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 raw_handle_conversion = if dispatchable {
360 quote! { self.0.as_ptr().addr() as _ }
361 } else {
362 quote! { self.0.get() }
363 };
364
365 quote! {
366 #input
367 impl #impl_generics crate::handle::VkRawHandle for #name #ty_generics #where_clause {
368 const OBJECT_TYPE: VkObjectType = #object_type;
369
370 #[inline]
371 fn raw_handle_value(&self) -> u64 {
372 #raw_handle_conversion
373 }
374 }
375 }
376 .into()
377}
378
379#[proc_macro_derive(PFN, attributes(pfn_of))]
380pub fn derive_pfn(input: TokenStream) -> TokenStream {
381 let input = parse_macro_input!(input as syn::DeriveInput);
382 let impl_name = &input.ident;
383
384 let Some(org_attr) = input.attrs.iter().find_map(|a| match a.meta {
385 syn::Meta::List(ref ml) if ml.path.is_ident("pfn_of") => Some(ml),
386 _ => None,
387 }) else {
388 return syn::Error::new_spanned(&input, "no #[pfn_of(...)] found")
389 .into_compile_error()
390 .into();
391 };
392 let org_fn: syn::Path = try_compile_error!(org_attr.parse_args());
393 let Some(org_fn_name) = org_fn.get_ident().or_else(|| org_fn.segments.last().map(|l| &l.ident)) else {
394 return syn::Error::new_spanned(org_attr, "invalid pfn_of fn path")
395 .into_compile_error()
396 .into();
397 };
398 let org_fn_cstr = syn::LitCStr::new(
399 &unsafe { std::ffi::CString::from_vec_unchecked(org_fn_name.to_string().into_bytes()) },
400 org_fn.span(),
401 );
402
403 quote! {
404 unsafe impl crate::resolver::PFN for #impl_name {
405 const NAME_CSTR: &'static core::ffi::CStr = #org_fn_cstr;
406
407 #[inline(always)]
408 unsafe fn from_ptr(p: *const core::ffi::c_void) -> Self {
409 core::mem::transmute(p)
410 }
411 #[inline(always)]
412 unsafe fn from_void_fn(p: crate::vk::PFN_vkVoidFunction) -> Self {
413 core::mem::transmute(p)
414 }
415 }
416 }
417 .into()
418}
419
420#[proc_macro_derive(StaticCallable)]
421pub fn derive_static_callable(input: TokenStream) -> TokenStream {
422 let input = parse_macro_input!(input as syn::DeriveInput);
423 let impl_name = &input.ident;
424 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
425 let org_attr = &input
426 .attrs
427 .iter()
428 .filter_map(|a| a.meta.require_list().ok())
429 .find(|a| a.path.is_ident("pfn_of"))
430 .expect("no #[pfn_of] found");
431 let org_fn: syn::Path = org_attr.parse_args().expect("invalid arg for pfn_of");
432
433 quote! {
434 #[cfg(all(not(feature = "DynamicLoaded"), feature = "Implements"))]
435 impl #impl_generics crate::resolver::StaticCallable for #impl_name #ty_generics #where_clause {
436 const STATIC: Self = Self(#org_fn);
437 }
438 }
439 .into()
440}
441
442#[proc_macro_attribute]
444pub fn implements(args: TokenStream, target: TokenStream) -> TokenStream {
445 let mut out: TokenStream = quote! { #[cfg(feature = "Implements")] }.into();
446
447 if !args.is_empty() {
448 let extra_feature_requirements =
449 parse_macro_input!(args with syn::punctuated::Punctuated<syn::LitStr, syn::Token![,]>::parse_terminated);
450
451 out.extend(
452 extra_feature_requirements
453 .into_iter()
454 .flat_map(|x| TokenStream::from(quote! { #[cfg(feature = #x)] })),
455 );
456 }
457
458 out.extend(target);
459 out
460}
461
462#[proc_macro_attribute]
463pub fn bitflags_newtype(_args: TokenStream, target: TokenStream) -> TokenStream {
464 let input = parse_macro_input!(target as syn::DeriveInput);
465 let name = &input.ident;
466 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
467
468 let syn::Data::Struct(ref s) = input.data else {
469 return syn::Error::new(input.span(), "cannot use as bitflags struct")
470 .into_compile_error()
471 .into();
472 };
473 let org_type = try_compile_error!(newtype_struct_org_type(s));
474
475 quote! {
476 #[repr(transparent)]
477 #input
478 impl #impl_generics core::ops::BitOr for #name #ty_generics #where_clause {
479 type Output = Self;
480
481 #[inline(always)]
482 fn bitor(self, rhs: Self) -> Self {
483 Self(self.0 | rhs.0)
484 }
485 }
486 impl #impl_generics core::ops::BitAnd for #name #ty_generics #where_clause {
487 type Output = Self;
488
489 #[inline(always)]
490 fn bitand(self, rhs: Self) -> Self {
491 Self(self.0 & rhs.0)
492 }
493 }
494 impl #impl_generics core::ops::Not for #name #ty_generics #where_clause {
495 type Output = Self;
496
497 #[inline(always)]
498 fn not(self) -> Self {
499 Self(!self.0)
500 }
501 }
502 impl #impl_generics core::ops::BitOrAssign for #name #ty_generics #where_clause {
503 #[inline(always)]
504 fn bitor_assign(&mut self, rhs: Self) {
505 self.0 |= rhs.0;
506 }
507 }
508 impl #impl_generics core::ops::BitAndAssign for #name #ty_generics #where_clause {
509 #[inline(always)]
510 fn bitand_assign(&mut self, rhs: Self) {
511 self.0 &= rhs.0;
512 }
513 }
514
515 impl #impl_generics From<#name #ty_generics> for #org_type #where_clause {
516 #[inline(always)]
517 fn from(value: #name #ty_generics) -> Self {
518 value.0
519 }
520 }
521
522 impl #impl_generics #name #ty_generics #where_clause {
523 #[inline(always)]
525 pub const fn bits(&self) -> #org_type {
526 self.0
527 }
528
529 #[inline(always)]
531 pub const fn has_any(self, other: Self) -> bool {
532 (self.0 & other.0) != 0
533 }
534
535 #[inline(always)]
537 pub const fn has_all(self, other: Self) -> bool {
538 (self.0 & other.0) == other.0
539 }
540
541 #[inline(always)]
543 pub const fn merge(self, other: Self) -> Self {
544 Self(self.0 | other.0)
545 }
546 }
547 }
548 .into()
549}
550
551struct VkExtCommandInput {
552 span: Span,
553 base_define: syn::ForeignItemFn,
554 suffix: Option<syn::LitStr>,
555 promote: Option<syn::LitStr>,
556 static_callable: bool,
557}
558impl syn::parse::Parse for VkExtCommandInput {
559 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
560 let span = input.span();
561 let base_define = syn::ForeignItemFn::parse(input)?;
562
563 let (mut suffix, mut promote, mut static_callable) = (None, None, false);
564 while input.peek(syn::Ident) {
565 let extra_ident = syn::Ident::parse(input)?;
566
567 match &extra_ident.to_string() as &str {
568 "suffix" => {
569 if suffix.is_some() {
570 return Err(syn::Error::new(input.span(), "duplicated suffix extra"));
571 }
572
573 input.parse::<syn::Token![=]>()?;
574 suffix = Some(input.parse()?);
575 input.parse::<syn::Token![;]>()?;
576 }
577 "promote" => {
578 if promote.is_some() {
579 return Err(syn::Error::new(input.span(), "duplicated promote extra"));
580 }
581
582 input.parse::<syn::Token![=]>()?;
583 promote = Some(input.parse()?);
584 input.parse::<syn::Token![;]>()?;
585 }
586 "static_callable" => {
587 input.parse::<syn::Token![;]>()?;
588
589 static_callable = true;
590 }
591 unknown => return Err(syn::Error::new(input.span(), format!("unknown extra: {unknown}"))),
592 }
593 }
594
595 Ok(Self {
596 span,
597 base_define,
598 suffix,
599 promote,
600 static_callable,
601 })
602 }
603}
604
605#[proc_macro]
606pub fn vk_ext_command(input: TokenStream) -> TokenStream {
607 let input = parse_macro_input!(input as VkExtCommandInput);
608
609 let base_vis = &input.base_define.vis;
610 let pfn_name = syn::Ident::new(
611 &format!("PFN_{}", input.base_define.sig.ident),
612 input.base_define.sig.ident.span(),
613 );
614 let pfn_ty = syn::TypeFnPtr {
615 attrs: Vec::new(),
616 lifetimes: None,
617 unsafety: Some(syn::Token)),
618 abi: Some(syn::Abi {
619 extern_token: syn::Token),
620 name: Some(syn::LitStr::new("system", Span::call_site())),
621 }),
622 fn_token: input.base_define.sig.fn_token,
623 paren_token: input.base_define.sig.paren_token,
624 inputs: input
625 .base_define
626 .sig
627 .inputs
628 .iter()
629 .map(|a| match a {
630 syn::FnArg::Receiver(_) => unreachable!("vk command cannot have receivers"),
631 syn::FnArg::Typed(t) => syn::NamedArg {
632 attrs: t.attrs.clone(),
633 name: match *t.pat {
634 syn::Pat::Ident(ref x) => Some((x.ident.clone(), t.colon_token)),
635 _ => None,
636 },
637 ty: *t.ty.clone(),
638 },
639 })
640 .collect(),
641 variadic: input.base_define.sig.variadic.as_ref().map(|v| syn::FnPtrVariadic {
642 attrs: v.attrs.clone(),
643 name: match v.pat {
644 Some((ref p, c)) => match &**p {
645 syn::Pat::Ident(x) => Some((x.ident.clone(), c)),
646 _ => None,
647 },
648 _ => None,
649 },
650 dots: v.dots,
651 comma: v.comma,
652 }),
653 output: input.base_define.sig.output.clone(),
654 };
655 let fname_cstr = syn::LitCStr::new(
656 &unsafe { std::ffi::CString::from_vec_unchecked(input.base_define.sig.ident.to_string().into_bytes()) },
657 input.base_define.sig.ident.span(),
658 );
659
660 let ext_static_link_impl = if input.static_callable {
661 let fn_ident = &input.base_define.sig.ident;
662 let define = &input.base_define;
663
664 Some(quote! {
665 #[cfg(all(feature = "Implements", not(feature = "DynamicLoaded")))]
666 impl crate::StaticCallable for #pfn_name {
667 const STATIC: Self = Self(#fn_ident);
668 }
669
670 #[cfg(all(feature = "Implements", not(feature = "DynamicLoaded")))]
671 extern "system" {
672 #define
673 }
674 })
675 } else {
676 None
677 };
678
679 let promoted_pfn_impl = if let Some(ref p) = input.promote {
680 let base_fn_name = input.base_define.sig.ident.to_string();
681 let suffix = try_compile_error!(input
682 .suffix
683 .ok_or_else(|| syn::Error::new(input.span, "suffix extra required for promote")));
684 let Some(promoted_fn_name) = base_fn_name.strip_suffix(&suffix.value()) else {
685 return syn::Error::new_spanned(&input.base_define.sig.ident, "not suffixed")
686 .into_compile_error()
687 .into();
688 };
689 let promoted_fn_ident = syn::Ident::new(promoted_fn_name, input.base_define.sig.ident.span());
690 let pfn_name = syn::Ident::new(&format!("PFN_{promoted_fn_name}"), input.base_define.sig.ident.span());
691 let promoted_fn_cstr = syn::LitCStr::new(
692 &unsafe { std::ffi::CString::from_vec_unchecked(promoted_fn_name.as_bytes().to_vec()) },
693 input.base_define.sig.ident.span(),
694 );
695 let promoted_fn = syn::ForeignItemFn {
696 sig: syn::Signature {
697 ident: promoted_fn_ident.clone(),
698 ..input.base_define.sig.clone()
699 },
700 ..input.base_define.clone()
701 };
702
703 let promote_feature_name = syn::LitStr::new(&format!("Allow{}APIs", p.value().replace('.', "_")), p.span());
704
705 Some(quote! {
706 #[cfg(feature = #promote_feature_name)]
707 #[repr(transparent)]
708 #[derive(Clone, Copy, Debug)]
709 #base_vis struct #pfn_name(pub #pfn_ty);
710 #[cfg(feature = #promote_feature_name)]
711 unsafe impl crate::FromPtr for #pfn_name {
712 #[inline(always)]
713 unsafe fn from_ptr(p: *const core::ffi::c_void) -> Self {
714 core::mem::transmute(p)
715 }
716 }
717 #[cfg(feature = #promote_feature_name)]
718 unsafe impl crate::PFN for #pfn_name {
719 const NAME_CSTR: &core::ffi::CStr = #promoted_fn_cstr;
720
721 #[inline(always)]
722 unsafe fn from_void_fn(p: crate::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::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)]
744 #base_vis struct #pfn_name(pub #pfn_ty);
745 unsafe impl crate::FromPtr for #pfn_name {
746 #[inline(always)]
747 unsafe fn from_ptr(p: *const core::ffi::c_void) -> Self {
748 core::mem::transmute(p)
749 }
750 }
751 unsafe impl crate::PFN for #pfn_name {
752 const NAME_CSTR: &core::ffi::CStr = #fname_cstr;
753
754 #[inline(always)]
755 unsafe fn from_void_fn(p: crate::PFN_vkVoidFunction) -> Self {
756 core::mem::transmute(p)
757 }
758 }
759
760 #ext_static_link_impl
761 #promoted_pfn_impl
762 }
763 .into()
764}
765
766struct SpecializationConstantsFieldAttr {
767 constant_id: syn::Expr,
768}
769impl SpecializationConstantsFieldAttr {
770 fn parse(field: &syn::Field) -> Result<Self, syn::Error> {
771 let mut constant_ids = field.attrs.iter().filter_map(|a| match a {
772 syn::Attribute {
773 style: syn::AttrStyle::Outer,
774 meta: syn::Meta::NameValue(ref nv),
775 ..
776 } if nv.path.is_ident("constant_id") => Some(Ok(nv.value.clone())),
777 syn::Attribute {
778 style: syn::AttrStyle::Outer,
779 meta: syn::Meta::List(ref ml),
780 ..
781 } if ml.path.is_ident("constant_id") => Some(ml.parse_args::<syn::Expr>()),
782 _ => None,
783 });
784 let Some(first_cid) = constant_ids.next() else {
785 return Err(syn::Error::new_spanned(field, "Missing constant_id attribute"));
786 };
787 if constant_ids.next().is_some() {
788 return Err(syn::Error::new_spanned(
789 field,
790 "One or more constant_id attributes found on same field",
791 ));
792 }
793
794 Ok(Self {
795 constant_id: first_cid?,
796 })
797 }
798}
799
800#[proc_macro_derive(SpecializationConstants, attributes(constant_id))]
802pub fn safe_derive_spec_constant(tok: TokenStream) -> TokenStream {
803 let input = parse_macro_input!(tok as syn::DeriveInput);
804 let name = &input.ident;
805 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
806
807 match input.data {
808 syn::Data::Struct(syn::DataStruct { ref fields, .. }) => {
809 let mut entries = Vec::with_capacity(fields.len());
810 for f in fields {
811 let SpecializationConstantsFieldAttr { constant_id } =
812 try_compile_error!(SpecializationConstantsFieldAttr::parse(f));
813
814 let ty = &f.ty;
815 let ident = &f.ident;
816 entries.push(
817 quote! { bedrock::SpecializationMapEntry(bedrock::vk::VkSpecializationMapEntry {
818 constantID: #constant_id,
819 offset: core::mem::offset_of!(Self, #ident) as _,
820 size: core::mem::size_of::<#ty>(),
821 }) },
822 );
823 }
824
825 quote! {
826 unsafe impl<#impl_generics> bedrock::SpecializationConstants for #name #ty_generics #where_clause {
827 const ENTRIES: &'static [bedrock::SpecializationMapEntry] = &[#(#entries),*];
828
829 #[inline(always)]
830 fn as_ptr(&self) -> *const core::ffi::c_void {
831 core::ptr::from_ref(self).cast()
832 }
833 }
834 }
835 .into()
836 }
837 _ => unimplemented!("unsupported"),
838 }
839}