vulkan_layer_macros/lib.rs
1// Copyright 2023 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#![warn(missing_docs)]
16
17//! Macros for `vulkan-layer`.
18
19use proc_macro::TokenStream;
20use proc_macro2::TokenStream as TokenStream2;
21use quote::quote;
22use syn::{parse_macro_input, ItemImpl, Type};
23
24mod details;
25mod dummy;
26
27/// Derive the implementation of the `vulkan_layer::GlobalHooksInfo` trait from the implementation
28/// of the `vulkan_layer::GlobalHooks` trait.
29///
30/// This attribute macro should be used over an implementation item of the
31/// `vulkan_layer::GlobalHooks` trait, and will implement the `vulkan_layer::GlobalHooksInfo` trait
32/// for the type:
33/// * `GlobalHooksInfo::hooked_commands` returns a list of the overridden methods that appear in the
34/// implementation item.
35/// * `GlobalHooksInfo::HooksType` and `GlobalHooksInfo::HooksRefType` are defined as `Self` and
36/// `&Self`.
37/// * `GlobalHooksInfo::hooks` returns `self`.
38///
39/// # Examples
40///
41/// ```
42/// use ash::vk;
43/// use vulkan_layer::{
44/// auto_globalhooksinfo_impl, GlobalHooks, GlobalHooksInfo, LayerResult, LayerVulkanCommand,
45/// VkLayerInstanceLink,
46/// };
47///
48/// #[derive(Default)]
49/// struct MyGlobalHooks;
50///
51/// #[auto_globalhooksinfo_impl]
52/// impl GlobalHooks for MyGlobalHooks {
53/// fn create_instance(
54/// &self,
55/// _p_create_info: &vk::InstanceCreateInfo,
56/// _layer_instance_link: &VkLayerInstanceLink,
57/// _p_allocator: Option<&vk::AllocationCallbacks>,
58/// _p_instance: *mut vk::Instance,
59/// ) -> LayerResult<ash::prelude::VkResult<()>> {
60/// LayerResult::Unhandled
61/// }
62/// }
63///
64/// let my_global_hooks: MyGlobalHooks = Default::default();
65/// assert!(std::ptr::eq(&my_global_hooks, my_global_hooks.hooks()));
66/// assert_eq!(
67/// MyGlobalHooks::hooked_commands(),
68/// [LayerVulkanCommand::CreateInstance]
69/// );
70/// ```
71#[proc_macro_attribute]
72pub fn auto_globalhooksinfo_impl(_: TokenStream, item: TokenStream) -> TokenStream {
73 let original_item: TokenStream2 = item.clone().into();
74 let input = parse_macro_input!(item as ItemImpl);
75 let target_trait = quote!(::vulkan_layer::GlobalHooksInfo);
76 let to_append = details::autoinfo(&input, &target_trait).unwrap_or_else(|e| {
77 let dummy = dummy::dummy_autoinfo_impl(&input.self_ty, &target_trait);
78 let compile_error = e.to_compile_error();
79 quote! {
80 #dummy
81 #compile_error
82 }
83 });
84 quote! {
85 #original_item
86 #to_append
87 }
88 .into()
89}
90
91/// Derive the implementation of the `vulkan_layer::InstanceInfo` trait from the implementation of
92/// the `vulkan_layer::InstanceHooks`.
93///
94/// This attribute macro should be used over an implementation item of the
95/// `vulkan_layer::InstanceHooks` trait, and will implement the `vulkan_layer::InstanceInfo` trait
96/// for the type:
97/// * `InstanceInfo::hooked_commands` returns a list of the overridden methods that appear in the
98/// implementation item.
99/// * `InstanceInfo::HooksType` and `InstanceInfo::HooksRefType` are defined as `Self` and `&Self`.
100/// * `InstanceInfo::hooks` returns `self`.
101///
102/// # Examples
103///
104/// ```
105/// use ash::vk;
106/// use std::mem::MaybeUninit;
107/// use vulkan_layer::{
108/// auto_instanceinfo_impl, InstanceHooks, InstanceInfo, LayerResult, LayerVulkanCommand,
109/// };
110///
111/// #[derive(Default)]
112/// struct MyInstanceHooks;
113///
114/// #[auto_instanceinfo_impl]
115/// impl InstanceHooks for MyInstanceHooks {
116/// fn get_physical_device_features(
117/// &self,
118/// _physical_device: vk::PhysicalDevice,
119/// _p_features: &mut MaybeUninit<vk::PhysicalDeviceFeatures>,
120/// ) -> LayerResult<()> {
121/// LayerResult::Unhandled
122/// }
123/// }
124///
125/// let my_instance_hooks: MyInstanceHooks = Default::default();
126/// assert!(std::ptr::eq(my_instance_hooks.hooks(), &my_instance_hooks));
127/// assert_eq!(
128/// MyInstanceHooks::hooked_commands(),
129/// [LayerVulkanCommand::GetPhysicalDeviceFeatures]
130/// );
131/// ```
132#[proc_macro_attribute]
133pub fn auto_instanceinfo_impl(_: TokenStream, item: TokenStream) -> TokenStream {
134 let original_item: TokenStream2 = item.clone().into();
135 let input = parse_macro_input!(item as ItemImpl);
136 let target_trait = quote!(::vulkan_layer::InstanceInfo);
137 let to_append = details::autoinfo(&input, &target_trait).unwrap_or_else(|e| {
138 let dummy = dummy::dummy_autoinfo_impl(&input.self_ty, &target_trait);
139 let compile_error = e.to_compile_error();
140 quote! {
141 #dummy
142 #compile_error
143 }
144 });
145 quote! {
146 #original_item
147 #to_append
148 }
149 .into()
150}
151
152/// Derive the implementation of the `vulkan_layer::DeviceInfo` trait from the implementation of the
153/// `vulkan_layer::DeviceHooks` trait.
154///
155/// This attribute macro should be used over an implementation item of the
156/// `vulkan_layer::DeviceHooks` trait, and will implement the `vulkan_layer::DeviceInfo` trait for
157/// the type:
158/// * `DeviceInfo::hooked_commands` returns a list of the overridden methods that appear in the
159/// implementation item.
160/// * `DeviceInfo::HooksType` and `DeviceInfo::HooksRefType` are defined as `Self` and `&Self`.
161/// * `DeviceInfo::hooks` returns `self`.
162///
163/// # Examples
164///
165/// ```
166/// use ash::vk;
167/// use vulkan_layer::{
168/// auto_deviceinfo_impl, DeviceHooks, DeviceInfo, LayerResult, LayerVulkanCommand,
169/// };
170///
171/// #[derive(Default)]
172/// struct MyDeviceHooks;
173///
174/// #[auto_deviceinfo_impl]
175/// impl DeviceHooks for MyDeviceHooks {
176/// fn create_image(
177/// &self,
178/// _p_create_info: &vk::ImageCreateInfo,
179/// _p_allocator: Option<&vk::AllocationCallbacks>,
180/// ) -> LayerResult<ash::prelude::VkResult<vk::Image>> {
181/// LayerResult::Unhandled
182/// }
183/// }
184///
185/// let my_device_hooks: MyDeviceHooks = Default::default();
186/// assert!(std::ptr::eq(my_device_hooks.hooks(), &my_device_hooks));
187/// assert_eq!(
188/// MyDeviceHooks::hooked_commands(),
189/// [LayerVulkanCommand::CreateImage]
190/// );
191/// ```
192#[proc_macro_attribute]
193pub fn auto_deviceinfo_impl(_: TokenStream, item: TokenStream) -> TokenStream {
194 let original_item: TokenStream2 = item.clone().into();
195 let input = parse_macro_input!(item as ItemImpl);
196 let target_trait = quote!(::vulkan_layer::DeviceInfo);
197 let to_append = details::autoinfo(&input, &target_trait).unwrap_or_else(|e| {
198 let dummy = dummy::dummy_autoinfo_impl(&input.self_ty, &target_trait);
199 let compile_error = e.to_compile_error();
200 quote! {
201 #dummy
202 #compile_error
203 }
204 });
205 quote! {
206 #original_item
207 #to_append
208 }
209 .into()
210}
211
212/// Declare the required introspection queries for Android given an instantiated
213/// `vulkan_layer::Global` type.
214///
215/// All functions are defined without name mangling, so that they are exported as C symbols in the
216/// generated dynamic library. This is recommended by
217/// [the Vulkan loader doc](https://github.com/KhronosGroup/Vulkan-Loader/blob/280997da523951c4016f4ca6af66d58a31e36ab3/docs/LoaderLayerInterface.md#layer-manifest-file-usage:~:text=These%20introspection%20functions%20are%20not%20used%20by%20the%20Khronos%20loader%20but%20should%20be%20present%20in%20layers%20to%20maintain%20consistency.%20The%20specific%20%22introspection%22%20functions%20are%20called%20out%20in%20the%20Layer%20Manifest%20File%20Format%20table):
218///
219/// > These introspection functions are not used by the Khronos loader but should be present in
220/// > layers to maintain consistency. The specific "introspection" functions are called out in the
221/// > Layer Manifest File Format table.
222///
223/// According to the
224/// [the Vulkan loader doc](https://github.com/KhronosGroup/Vulkan-Loader/blob/280997da523951c4016f4ca6af66d58a31e36ab3/docs/LoaderLayerInterface.md#layer-manifest-file-format), introspection queries include:
225/// * `vkEnumerateInstanceLayerProperties`
226/// * `vkEnumerateInstanceExtensionProperties`
227/// * `vkEnumerateDeviceLayerProperties`
228/// * `vkEnumerateDeviceExtensionProperties`
229/// * `vkGetInstanceProcAddr`
230/// * `vkGetDeviceProcAddr`
231/// # Examples
232///
233/// ```
234/// # use std::sync::Arc;
235/// # use vulkan_layer::{StubGlobalHooks, StubInstanceInfo, StubDeviceInfo, Layer, Global, declare_introspection_queries, LayerManifest};
236/// # use once_cell::sync::Lazy;
237/// # use ash::{vk, self};
238/// #
239/// #[derive(Default)]
240/// struct MyLayer(StubGlobalHooks);
241///
242/// impl Layer for MyLayer {
243/// // ...
244/// # type GlobalHooksInfo = StubGlobalHooks;
245/// # type InstanceInfo = StubInstanceInfo;
246/// # type DeviceInfo = StubDeviceInfo;
247/// # type InstanceInfoContainer = StubInstanceInfo;
248/// # type DeviceInfoContainer = StubDeviceInfo;
249/// #
250/// # fn global_instance() -> impl std::ops::Deref<Target = Global<Self>> + 'static {
251/// # static GLOBAL: Lazy<Global<MyLayer>> = Lazy::new(Default::default);
252/// # &*GLOBAL
253/// # }
254/// #
255/// # fn manifest() -> LayerManifest {
256/// # Default::default()
257/// # }
258/// #
259/// # fn global_hooks_info(&self) -> &Self::GlobalHooksInfo {
260/// # &self.0
261/// # }
262/// #
263/// # fn create_instance_info(
264/// # &self,
265/// # _: &vk::InstanceCreateInfo,
266/// # _: Option<&vk::AllocationCallbacks>,
267/// # _: Arc<ash::Instance>,
268/// # _next_get_instance_proc_addr: vk::PFN_vkGetInstanceProcAddr,
269/// # ) -> Self::InstanceInfoContainer {
270/// # Default::default()
271/// # }
272/// #
273/// # fn create_device_info(
274/// # &self,
275/// # _: vk::PhysicalDevice,
276/// # _: &vk::DeviceCreateInfo,
277/// # _: Option<&vk::AllocationCallbacks>,
278/// # _: Arc<ash::Device>,
279/// # _next_get_device_proc_addr: vk::PFN_vkGetDeviceProcAddr,
280/// # ) -> Self::DeviceInfoContainer {
281/// # Default::default()
282/// # }
283/// }
284///
285/// type MyGlobal = Global::<MyLayer>;
286/// declare_introspection_queries!(MyGlobal);
287/// # let _: vk::PFN_vkEnumerateInstanceLayerProperties = vkEnumerateInstanceLayerProperties;
288/// # let _: vk::PFN_vkEnumerateInstanceExtensionProperties = vkEnumerateInstanceExtensionProperties;
289/// # let _: vk::PFN_vkEnumerateDeviceLayerProperties = vkEnumerateDeviceLayerProperties;
290/// # let _: vk::PFN_vkEnumerateDeviceExtensionProperties = vkEnumerateDeviceExtensionProperties;
291/// # let _: vk::PFN_vkGetInstanceProcAddr = vkGetInstanceProcAddr;
292/// # let _: vk::PFN_vkGetDeviceProcAddr = vkGetDeviceProcAddr;
293/// ```
294#[proc_macro]
295pub fn declare_introspection_queries(item: TokenStream) -> TokenStream {
296 let global_type = parse_macro_input!(item as Type);
297 details::declare_introspection_queries_impl(&global_type)
298 .unwrap_or_else(|e| e.to_compile_error())
299 .into()
300}