vulkan_layer/layer_trait.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//! This module includes all the traits that require the layer implementation to implement.
16
17use crate::{bindings::vk_layer::VkLayerInstanceLink, global_simple_intercept::Extension, Global};
18use ash::vk;
19use std::{borrow::Borrow, ffi::CString, ops::Deref, sync::Arc};
20use thiserror::Error;
21
22pub mod generated;
23pub use generated::*;
24
25/// The return value of an intercepted Vulkan command by the layer implementation.
26///
27/// The layer framework uses the value of this type to decide whether to call into the next call
28/// chain after calling into the layer implementation. For an intercepted Vulkan command, if the
29/// layer implementation returns [`LayerResult::Handled`], the layer framework won't call into the
30/// next layer call chain. Otherwise, if [`LayerResult::Unhandled`] is returned, the layer framework
31/// will call into the next layer call chain with the original parameters.
32#[must_use]
33#[derive(Clone)]
34pub enum LayerResult<T> {
35 /// The layer implementation has handled this Vulkan command, so the layer framework shouldn't
36 /// try to call into the next call chain.
37 ///
38 /// This can be useful if the layer implementation wants to modify the parameters passed to the
39 /// next call chain, or wants to use some other Vulkan commands to fake/emulate the intercepted
40 /// Vulkan command. This is useful when the layer wants to emulate an unsupported extension.
41 /// `T` is usually `ash::prelude::VkResult<U>`.
42 Handled(T),
43 /// The layer implementation doesn't handle this Vulkan command, so the layer framework should
44 /// call into the next call chain with the original parameters.
45 ///
46 /// If the layer implementation doesn't want to bother calling into the next call chain, and
47 /// wants the layer framework to do the job, this variant can be used. Note that if the layer
48 /// implementation needs to inform the layer framework that there is an error and needs to
49 /// return early, [`LayerResult::Handled`] should be used.
50 Unhandled,
51}
52
53#[derive(Error, Debug)]
54pub enum TryFromVulkanCommandError {
55 #[error("unknown command `{0}`")]
56 UnknownCommand(String),
57}
58
59/// A trait for the layer implementation to provide metadata of [`DeviceHooks`] for the layer
60/// framework.
61///
62/// This trait serves as the container of the [`DeviceHooks`] type and provides the list of
63/// intercepted
64/// [Vulkan device functions](https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderInterfaceArchitecture.md#device-functions)
65/// for the layer framework. This trait and [`DeviceHooks`] can be implemented by the same type.
66///
67/// This trait can also be automatically implemented with the help of the
68/// [`auto_deviceinfo_impl`][crate::auto_deviceinfo_impl] attribute macro.
69///
70/// # Examples
71///
72/// A layer that needs to intercept the `vkCreateImage` function.
73/// ```
74/// # use ash::{vk, prelude::VkResult};
75/// # use vulkan_layer::{DeviceHooks, DeviceInfo, LayerVulkanCommand as VulkanCommand, LayerResult};
76/// #
77/// struct MyLayerDeviceInfo;
78///
79/// impl DeviceHooks for MyLayerDeviceInfo {
80/// fn create_image(
81/// &self,
82/// _p_create_info: &vk::ImageCreateInfo,
83/// _p_allocator: Option<&vk::AllocationCallbacks>,
84/// ) -> LayerResult<VkResult<vk::Image>> {
85/// LayerResult::Unhandled
86/// }
87/// }
88///
89/// impl DeviceInfo for MyLayerDeviceInfo {
90/// type HooksType = Self;
91/// type HooksRefType<'a> = &'a Self;
92///
93/// fn hooked_commands() -> &'static [VulkanCommand] {
94/// &[VulkanCommand::CreateImage]
95/// }
96///
97/// fn hooks(&self) -> Self::HooksRefType<'_> {
98/// self
99/// }
100/// }
101/// ```
102pub trait DeviceInfo: Send + Sync {
103 /// The underlying [`DeviceHooks`] type that implements the core logic to intercept
104 /// [Vulkan device functions](https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderInterfaceArchitecture.md#device-functions).
105 ///
106 /// If a type implements both this trait and the [`DeviceHooks`] trait simultaneously,
107 /// [`DeviceInfo::HooksType`] can be set to `Self`.
108 type HooksType: DeviceHooks;
109
110 /// A type that can be dereferencing to [`DeviceInfo::HooksType`]. Usually just
111 /// `&DeviceInfo::HooksType`.
112 ///
113 /// This extra associated type allows the layer implementation to use a smart pointer like
114 /// [`Arc`][std::sync::Arc] or [`MutexGuard`][std::sync::MutexGuard] to hold the reference.
115 ///
116 /// # Examples
117 ///
118 /// A layer implementation that returns a [`Arc`][std::sync::Arc].
119 /// ```
120 /// # use std::sync::Arc;
121 /// # use vulkan_layer::{DeviceHooks, DeviceInfo, LayerVulkanCommand as VulkanCommand};
122 /// #
123 /// # struct MyLayerDeviceHooks;
124 /// #
125 /// # impl DeviceHooks for MyLayerDeviceHooks {}
126 /// #
127 /// struct MyLayerDeviceInfo(Arc<MyLayerDeviceHooks>);
128 ///
129 /// impl DeviceInfo for MyLayerDeviceInfo {
130 /// type HooksType = MyLayerDeviceHooks;
131 /// type HooksRefType<'a> = Arc<MyLayerDeviceHooks>;
132 /// #
133 /// # fn hooked_commands() -> &'static [VulkanCommand] {
134 /// # &[]
135 /// # }
136 ///
137 /// // Other required methods omitted.
138 ///
139 /// fn hooks(&self) -> Self::HooksRefType<'_> {
140 /// Arc::clone(&self.0)
141 /// }
142 /// }
143 /// ```
144 type HooksRefType<'a>: Deref<Target = Self::HooksType> + 'a
145 where
146 Self: 'a;
147
148 /// Returns a slice of
149 /// [Vulkan device functions](https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderInterfaceArchitecture.md#device-functions)
150 /// that the layer implementation needs to intercept.
151 ///
152 /// This function should normally return the methods implemented by the associated
153 /// [`DeviceHooks`] type. If the layer implementation needs to decide the functions to intercept
154 /// dynamically at runtime, use the [`Layer::hooked_device_commands`] method to override the
155 /// list of intercepted device functions.
156 ///
157 /// Avoid returning commands other than
158 /// [Vulkan device functions](https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderInterfaceArchitecture.md#device-functions).
159 /// Otherwise, those commands may or may not be intercepted.
160 ///
161 /// For Vulkan commands that have multiple names, e.g. `vkCreateSamplerYcbcrConversion` and
162 /// `vkCreateSamplerYcbcrConversionKHR`, only the shortest name should be
163 /// used([`VulkanCommand::CreateSamplerYcbcrConversion`] in this example). The layer framework
164 /// automatically wires both entries to the layer implementation.
165 ///
166 /// # Examples
167 ///
168 /// A layer that intercepts the `vkCreateImage` only if the ASTC LDR feature is enabled.
169 /// ```
170 /// use ash::{self, prelude::VkResult, vk};
171 /// use once_cell::sync::Lazy;
172 /// use std::sync::Arc;
173 /// use vulkan_layer::{
174 /// DeviceHooks, DeviceInfo, Global, Layer, LayerManifest, LayerResult,
175 /// LayerVulkanCommand as VulkanCommand, StubGlobalHooks, StubInstanceInfo,
176 /// VulkanBaseInStructChain,
177 /// };
178 ///
179 /// struct MyLayerDeviceInfo {
180 /// is_astc_enabled: bool,
181 /// }
182 ///
183 /// impl DeviceHooks for MyLayerDeviceInfo {
184 /// fn create_image(
185 /// &self,
186 /// create_info: &vk::ImageCreateInfo,
187 /// _p_allocator: Option<&vk::AllocationCallbacks>,
188 /// ) -> LayerResult<VkResult<vk::Image>> {
189 /// if create_info.format == vk::Format::ASTC_4X4_UNORM_BLOCK {
190 /// println!("ASTC 4x4 UNORM image created.");
191 /// }
192 /// LayerResult::Unhandled
193 /// }
194 /// }
195 ///
196 /// impl DeviceInfo for MyLayerDeviceInfo {
197 /// type HooksType = Self;
198 /// type HooksRefType<'a> = &'a Self;
199 ///
200 /// fn hooked_commands() -> &'static [VulkanCommand] {
201 /// &[VulkanCommand::CreateImage]
202 /// }
203 ///
204 /// fn hooks(&self) -> Self::HooksRefType<'_> {
205 /// self
206 /// }
207 /// }
208 ///
209 /// #[derive(Default)]
210 /// struct MyLayer(StubGlobalHooks);
211 ///
212 /// impl Layer for MyLayer {
213 /// // ...
214 /// # type GlobalHooksInfo = StubGlobalHooks;
215 /// # type InstanceInfo = StubInstanceInfo;
216 /// type DeviceInfo = MyLayerDeviceInfo;
217 /// # type InstanceInfoContainer = StubInstanceInfo;
218 /// type DeviceInfoContainer = MyLayerDeviceInfo;
219 /// #
220 /// # fn global_instance() -> impl std::ops::Deref<Target = Global<Self>> + 'static {
221 /// # static GLOBAL: Lazy<Global<MyLayer>> = Lazy::new(Default::default);
222 /// # &*GLOBAL
223 /// # }
224 /// #
225 /// # fn manifest() -> LayerManifest {
226 /// # Default::default()
227 /// # }
228 /// #
229 /// # fn global_hooks_info(&self) -> &Self::GlobalHooksInfo {
230 /// # &self.0
231 /// # }
232 /// #
233 /// # fn create_instance_info(
234 /// # &self,
235 /// # _: &vk::InstanceCreateInfo,
236 /// # _: Option<&vk::AllocationCallbacks>,
237 /// # _: Arc<ash::Instance>,
238 /// # _next_get_instance_proc_addr: vk::PFN_vkGetInstanceProcAddr,
239 /// # ) -> Self::InstanceInfoContainer {
240 /// # Default::default()
241 /// # }
242 /// #
243 /// fn create_device_info(
244 /// &self,
245 /// _: vk::PhysicalDevice,
246 /// create_info: &vk::DeviceCreateInfo,
247 /// _: Option<&vk::AllocationCallbacks>,
248 /// _: Arc<ash::Device>,
249 /// _next_get_device_proc_addr: vk::PFN_vkGetDeviceProcAddr,
250 /// ) -> Self::DeviceInfoContainer {
251 /// let mut p_next_chain: VulkanBaseInStructChain =
252 /// unsafe { (create_info.p_next as *const vk::BaseInStructure).as_ref() }.into();
253 /// let is_astc_enabled = p_next_chain
254 /// .find_map(|p_next| {
255 /// let p_next = p_next as *const vk::BaseInStructure;
256 /// unsafe {
257 /// ash::match_in_struct!(match p_next {
258 /// features2 @ vk::PhysicalDeviceFeatures2 => {
259 /// Some(&features2.features)
260 /// }
261 /// _ => {
262 /// None
263 /// }
264 /// })
265 /// }
266 /// })
267 /// .or_else(|| unsafe { create_info.p_enabled_features.as_ref() })
268 /// .map(|features| features.texture_compression_astc_ldr == vk::TRUE)
269 /// .unwrap_or(false);
270 /// MyLayerDeviceInfo { is_astc_enabled }
271 /// }
272 ///
273 /// fn hooked_device_commands(
274 /// &self,
275 /// _instance_info: &Self::InstanceInfo,
276 /// device_info: Option<&Self::DeviceInfo>,
277 /// ) -> Box<dyn Iterator<Item = VulkanCommand>> {
278 /// let should_hook_create_image = device_info
279 /// .map(|device_info| device_info.is_astc_enabled)
280 /// // Always hook the vkCreateImage function in the function pointers returned by
281 /// // vkGetInstanceProcAddr: we don't know if the to-be-created VkDevice will be
282 /// // created with the ASTC feature enabled.
283 /// .unwrap_or(true);
284 /// Box::new(
285 /// Self::DeviceInfo::hooked_commands()
286 /// .iter()
287 /// .cloned()
288 /// .filter(move |command| match command {
289 /// VulkanCommand::CreateImage => should_hook_create_image,
290 /// _ => true,
291 /// }),
292 /// )
293 /// }
294 /// }
295 /// ```
296 fn hooked_commands() -> &'static [VulkanCommand];
297
298 /// Returns the reference of the [`DeviceInfo::HooksType`].
299 ///
300 /// The layer framework uses the returned value to call into the layer implementation of
301 /// intercepted commands.
302 fn hooks(&self) -> Self::HooksRefType<'_>;
303}
304
305/// A trait for the layer implementation to provide metadata of [`InstanceHooks`] for the layer
306/// framework.
307///
308/// This trait serves as the container of the [`InstanceHooks`] type and provides the list of
309/// intercepted
310/// [Vulkan instance functions](https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderInterfaceArchitecture.md#instance-functions)
311/// ([global commands](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetInstanceProcAddr.html#:~:text=The%20global%20commands%20are%3A%20vkEnumerateInstanceVersion%2C%20vkEnumerateInstanceExtensionProperties%2C%20vkEnumerateInstanceLayerProperties%2C%20and%20vkCreateInstance.)
312/// not included) for the layer framework. This trait and [`InstanceHooks`] can be implemented by
313/// the same type. Avoid returning either
314/// [Vulkan device functions](https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderInterfaceArchitecture.md#device-functions)
315/// or
316/// [global commands](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetInstanceProcAddr.html#:~:text=The%20global%20commands%20are%3A%20vkEnumerateInstanceVersion%2C%20vkEnumerateInstanceExtensionProperties%2C%20vkEnumerateInstanceLayerProperties%2C%20and%20vkCreateInstance.).
317///
318/// This trait can also be automatically implemented with the help of the
319/// [`auto_instanceinfo_impl`][crate::auto_instanceinfo_impl] attribute macro.
320///
321/// # Examples
322///
323/// A layer that needs to intercept the `vkCreateWin32SurfaceKHR` function.
324/// ```
325/// use ash::{prelude::VkResult, vk};
326/// use vulkan_layer::{
327/// InstanceHooks, InstanceInfo, LayerResult, LayerVulkanCommand as VulkanCommand,
328/// };
329///
330/// struct MyLayerInstanceHooks;
331///
332/// impl InstanceHooks for MyLayerInstanceHooks {
333/// fn create_win32_surface_khr(
334/// &self,
335/// _p_create_info: &vk::Win32SurfaceCreateInfoKHR,
336/// _p_allocator: Option<&vk::AllocationCallbacks>,
337/// ) -> LayerResult<VkResult<vk::SurfaceKHR>> {
338/// LayerResult::Unhandled
339/// }
340/// }
341///
342/// impl InstanceInfo for MyLayerInstanceHooks {
343/// type HooksType = Self;
344/// type HooksRefType<'a> = &'a Self;
345///
346/// fn hooked_commands() -> &'static [VulkanCommand] {
347/// &[VulkanCommand::CreateWin32SurfaceKhr]
348/// }
349///
350/// fn hooks(&self) -> Self::HooksRefType<'_> {
351/// self
352/// }
353/// }
354/// ```
355pub trait InstanceInfo: Send + Sync {
356 /// The underlying [`InstanceHooks`] type that implements the core logic to intercept
357 /// [Vulkan instance functions](https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderInterfaceArchitecture.md#instance-functions)
358 /// apart from
359 /// [global commands](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetInstanceProcAddr.html#:~:text=The%20global%20commands%20are%3A%20vkEnumerateInstanceVersion%2C%20vkEnumerateInstanceExtensionProperties%2C%20vkEnumerateInstanceLayerProperties%2C%20and%20vkCreateInstance.).
360 ///
361 /// If a type implements both this trait and the [`InstanceHooks`] trait simultaneously,
362 /// [`InstanceInfo::HooksType`] can be set to `Self`.
363 type HooksType: InstanceHooks;
364
365 /// A type that can be dereferencing to [`InstanceInfo::HooksType`]. Usually
366 /// `&InstanceInfo::HooksType`.
367 ///
368 /// This extra associated type allows the layer implementation to use a smart pointer like
369 /// [`Arc`][std::sync::Arc] or [`MutexGuard`][std::sync::MutexGuard] to hold the reference.
370 ///
371 /// # Examples
372 ///
373 /// A layer implementation that returns a [`Arc`][std::sync::Arc].
374 /// ```
375 /// # use std::sync::Arc;
376 /// # use vulkan_layer::{InstanceHooks, InstanceInfo, LayerVulkanCommand as VulkanCommand};
377 /// #
378 /// # struct MyLayerInstanceHooks;
379 /// #
380 /// # impl InstanceHooks for MyLayerInstanceHooks {}
381 /// #
382 /// struct MyLayerInstanceInfo(Arc<MyLayerInstanceHooks>);
383 ///
384 /// impl InstanceInfo for MyLayerInstanceInfo {
385 /// type HooksType = MyLayerInstanceHooks;
386 /// type HooksRefType<'a> = Arc<MyLayerInstanceHooks>;
387 /// #
388 /// # fn hooked_commands() -> &'static [VulkanCommand] {
389 /// # &[]
390 /// # }
391 ///
392 /// // Other required methods omitted.
393 ///
394 /// fn hooks(&self) -> Self::HooksRefType<'_> {
395 /// Arc::clone(&self.0)
396 /// }
397 /// }
398 /// ```
399 type HooksRefType<'a>: Deref<Target = Self::HooksType> + 'a
400 where
401 Self: 'a;
402
403 /// Returns a slice of
404 /// [Vulkan instance functions](https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderInterfaceArchitecture.md#instance-functions)
405 /// ([global commands](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetInstanceProcAddr.html#:~:text=The%20global%20commands%20are%3A%20vkEnumerateInstanceVersion%2C%20vkEnumerateInstanceExtensionProperties%2C%20vkEnumerateInstanceLayerProperties%2C%20and%20vkCreateInstance.)
406 /// not included) that the layer implementation needs to intercept.
407 ///
408 /// This function should normally return the methods implemented by the associated
409 /// [`InstanceHooks`] type. If the layer implementation needs to decide the function to
410 /// intercept dynamically at runtime, use the [`Layer::hooked_instance_commands`] method to
411 /// override the list of intercepted instance functions.
412 ///
413 /// Avoid returning unexpected commands(i.e.
414 /// [Vulkan device functions](https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderInterfaceArchitecture.md#device-functions)
415 /// or
416 /// [global commands](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetInstanceProcAddr.html#:~:text=The%20global%20commands%20are%3A%20vkEnumerateInstanceVersion%2C%20vkEnumerateInstanceExtensionProperties%2C%20vkEnumerateInstanceLayerProperties%2C%20and%20vkCreateInstance.)).
417 /// Otherwise, those commands may or may not be intercepted.
418 ///
419 /// For Vulkan commands that have multiple names, e.g. `vkGetPhysicalDeviceProperties2` and
420 /// `vkGetPhysicalDeviceProperties2KHR`, only the shortest name should be
421 /// used([`VulkanCommand::GetPhysicalDeviceProperties2`] in this example). The layer framework
422 /// automatically wires both entries to the layer implementation.
423 ///
424 /// # Examples
425 ///
426 /// A layer that intercepts `vkDestroySurfaceKHR` only if the `VK_KHR_win32_surface` extension
427 /// is enabled.
428 /// ```
429 /// use ash::vk;
430 /// use once_cell::sync::Lazy;
431 /// use std::{ffi::CStr, sync::Arc};
432 /// use vulkan_layer::{
433 /// Global, InstanceHooks, InstanceInfo, Layer, LayerManifest, LayerResult,
434 /// LayerVulkanCommand as VulkanCommand, StubDeviceInfo, StubGlobalHooks,
435 /// };
436 ///
437 /// struct MyLayerInstanceInfo {
438 /// is_win32_surface_enabled: bool,
439 /// }
440 ///
441 /// impl InstanceHooks for MyLayerInstanceInfo {
442 /// fn destroy_surface_khr(
443 /// &self,
444 /// _surface: vk::SurfaceKHR,
445 /// _p_allocator: Option<&vk::AllocationCallbacks>,
446 /// ) -> LayerResult<()> {
447 /// LayerResult::Unhandled
448 /// }
449 /// }
450 ///
451 /// impl InstanceInfo for MyLayerInstanceInfo {
452 /// type HooksType = Self;
453 /// type HooksRefType<'a> = &'a Self;
454 ///
455 /// fn hooked_commands() -> &'static [VulkanCommand] {
456 /// &[VulkanCommand::DestroySurfaceKhr]
457 /// }
458 ///
459 /// fn hooks(&self) -> Self::HooksRefType<'_> {
460 /// self
461 /// }
462 /// }
463 ///
464 /// #[derive(Default)]
465 /// struct MyLayer(StubGlobalHooks);
466 ///
467 /// impl Layer for MyLayer {
468 /// // ...
469 /// # type GlobalHooksInfo = StubGlobalHooks;
470 /// type InstanceInfo = MyLayerInstanceInfo;
471 /// # type DeviceInfo = StubDeviceInfo;
472 /// type InstanceInfoContainer = MyLayerInstanceInfo;
473 /// # type DeviceInfoContainer = StubDeviceInfo;
474 /// #
475 /// # fn global_instance() -> impl std::ops::Deref<Target = Global<Self>> + 'static {
476 /// # static GLOBAL: Lazy<Global<MyLayer>> = Lazy::new(Default::default);
477 /// # &*GLOBAL
478 /// # }
479 /// #
480 /// # fn manifest() -> LayerManifest {
481 /// # Default::default()
482 /// # }
483 /// #
484 /// # fn global_hooks_info(&self) -> &Self::GlobalHooksInfo {
485 /// # &self.0
486 /// # }
487 ///
488 /// fn create_instance_info(
489 /// &self,
490 /// create_info: &vk::InstanceCreateInfo,
491 /// _: Option<&vk::AllocationCallbacks>,
492 /// _: Arc<ash::Instance>,
493 /// _next_get_instance_proc_addr: vk::PFN_vkGetInstanceProcAddr,
494 /// ) -> Self::InstanceInfoContainer {
495 /// let enabled_extensions = if create_info.enabled_extension_count > 0 {
496 /// unsafe {
497 /// std::slice::from_raw_parts(
498 /// create_info.pp_enabled_extension_names,
499 /// create_info.enabled_extension_count as usize,
500 /// )
501 /// }
502 /// } else {
503 /// &[]
504 /// };
505 /// let is_win32_surface_enabled = enabled_extensions
506 /// .iter()
507 /// .find(|extension_name| {
508 /// (unsafe { CStr::from_ptr(**extension_name) })
509 /// == ash::extensions::khr::Win32Surface::name()
510 /// })
511 /// .is_some();
512 /// MyLayerInstanceInfo {
513 /// is_win32_surface_enabled,
514 /// }
515 /// }
516 ///
517 /// fn hooked_instance_commands(
518 /// &self,
519 /// instance_info: &Self::InstanceInfo,
520 /// ) -> Box<dyn Iterator<Item = VulkanCommand>> {
521 /// let should_hook_destroy_surface = instance_info.is_win32_surface_enabled;
522 /// Box::new(
523 /// Self::InstanceInfo::hooked_commands()
524 /// .iter()
525 /// .cloned()
526 /// .filter(move |command| match command {
527 /// VulkanCommand::DestroySurfaceKhr => should_hook_destroy_surface,
528 /// _ => true,
529 /// }),
530 /// )
531 /// }
532 /// #
533 /// # fn create_device_info(
534 /// # &self,
535 /// # _: vk::PhysicalDevice,
536 /// # _: &vk::DeviceCreateInfo,
537 /// # _: Option<&vk::AllocationCallbacks>,
538 /// # _: Arc<ash::Device>,
539 /// # _next_get_device_proc_addr: vk::PFN_vkGetDeviceProcAddr,
540 /// # ) -> Self::DeviceInfoContainer {
541 /// # Default::default()
542 /// # }
543 /// }
544 /// ```
545 fn hooked_commands() -> &'static [VulkanCommand];
546
547 /// Returns the reference of the [`InstanceInfo::HooksType`].
548 ///
549 /// The layer framework uses the returned value to call into the layer implementation of
550 /// intercepted commands.
551 fn hooks(&self) -> Self::HooksRefType<'_>;
552}
553
554/// A trait for the layer implementation to provide the implementation of the intercepted
555/// [global commands](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetInstanceProcAddr.html#:~:text=The%20global%20commands%20are%3A%20vkEnumerateInstanceVersion%2C%20vkEnumerateInstanceExtensionProperties%2C%20vkEnumerateInstanceLayerProperties%2C%20and%20vkCreateInstance.).
556///
557/// The global commands are copied here for convenience:
558/// * `vkEnumerateInstanceVersion`
559/// * `vkEnumerateInstanceExtensionProperties`
560/// * `vkEnumerateInstanceLayerProperties`
561/// * `vkCreateInstance`
562///
563/// Currently, it is only supported to intercept the `vkCreateInstance` function.
564pub trait GlobalHooks: Send + Sync {
565 /// The logic to intercept the `vkCreateInstance` function.
566 ///
567 /// The implementations should return [`LayerResult::Unhandled`] if the layer implementation
568 /// wants the layer framework to handle the task to call into the next layer, otherwise should
569 /// return [`LayerResult::Handled`].
570 ///
571 /// This function is usually intercepted if the layer needs to modify the `VkInstanceCreateInfo`
572 /// passed to the next layer. Otherwise, [`Layer::create_instance_info`] is almost always
573 /// better. This function is called before [`Layer::create_instance_info`] to create the
574 /// `VkInstance`.
575 ///
576 /// # Arguments
577 ///
578 /// * `_p_create_info` is a pointer to a VkInstanceCreateInfo structure controlling creation of
579 /// the instance. The `VkLayerInstanceLink` linked list in the `pNext` chain is already
580 /// advanced by the layer framework.
581 /// * `_layer_instance_link` is the `VkLayerInstanceLink` linked list node pointing to the next
582 /// layer.
583 /// * `_p_allocator` controls host memory allocation as described in the Memory Allocation
584 /// chapter of the Vulkan spec.
585 /// * `_p_instance` points a VkInstance handle in which the resulting instance is returned.
586 /// If the layer implementation wants to handle the call to the next layer, according to
587 /// [the `LLP_LAYER_21` requirement](https://github.com/KhronosGroup/Vulkan-Loader/blob/main/docs/LoaderLayerInterface.md#example-code-for-createinstance:~:text=LLP_LAYER_21,N/A),
588 /// implementations must guarantee that the original pointer should be passed to the lower
589 /// layers.
590 ///
591 /// # Examples
592 /// A layer that always enables the `VK_KHR_surface` extension.
593 /// ```
594 /// use ash::vk;
595 /// use std::{ffi::CStr, ptr::null};
596 /// use vulkan_layer::{GlobalHooks, LayerResult, VkLayerInstanceLink};
597 ///
598 /// struct MyGlobalHooks;
599 ///
600 /// impl GlobalHooks for MyGlobalHooks {
601 /// fn create_instance(
602 /// &self,
603 /// p_create_info: &vk::InstanceCreateInfo,
604 /// layer_instance_link: &VkLayerInstanceLink,
605 /// p_allocator: Option<&vk::AllocationCallbacks>,
606 /// p_instance: *mut vk::Instance,
607 /// ) -> LayerResult<ash::prelude::VkResult<()>> {
608 /// // Clone the input enabled extension names as a Vec.
609 /// let mut enabled_extensions = if p_create_info.enabled_extension_count == 0 {
610 /// vec![]
611 /// } else {
612 /// unsafe {
613 /// std::slice::from_raw_parts(
614 /// p_create_info.pp_enabled_extension_names,
615 /// p_create_info.enabled_extension_count as usize,
616 /// )
617 /// }
618 /// .to_vec()
619 /// };
620 /// // Push the VK_KHR_surface extension name only if it doesn't exist.
621 /// if enabled_extensions
622 /// .iter()
623 /// .find(|extension_name| {
624 /// (unsafe { CStr::from_ptr(**extension_name) })
625 /// == ash::extensions::khr::Win32Surface::name()
626 /// })
627 /// .is_none()
628 /// {
629 /// enabled_extensions.push(ash::extensions::khr::Win32Surface::name().as_ptr());
630 /// }
631 /// // Create a new VkInstanceCreateInfo with the new extensions. The passed in
632 /// // VkInstanceCreateInfo is const, so we have to create another VkInstanceCreateInfo
633 /// // to mutate the field we need to avoid possible undefined behavior in Rust.
634 /// let mut create_info = p_create_info.clone();
635 /// create_info.enabled_extension_count = enabled_extensions.len() as u32;
636 /// create_info.pp_enabled_extension_names = enabled_extensions.as_ptr();
637 /// // Convert p_allocator to a raw pointer that can be directly passed to
638 /// // vkCreateInstance.
639 /// let p_allocator = p_allocator
640 /// .map(|allocator| allocator as *const _)
641 /// .unwrap_or_else(null);
642 /// // Find the vkCreateInstance entry of the next layer.
643 /// let create_instance = unsafe {
644 /// (layer_instance_link.pfnNextGetInstanceProcAddr)(
645 /// vk::Instance::null(),
646 /// c"vkCreateInstance".as_ptr(),
647 /// )
648 /// };
649 /// let create_instance: vk::PFN_vkCreateInstance = match create_instance {
650 /// Some(fp) => unsafe { std::mem::transmute(fp) },
651 /// None => return LayerResult::Handled(Err(vk::Result::ERROR_INITIALIZATION_FAILED)),
652 /// };
653 /// // Call into the vkCreateInstance of the next layer with the original p_instance
654 /// // pointer.
655 /// LayerResult::Handled(
656 /// unsafe { create_instance(&create_info, p_allocator, p_instance) }.result(),
657 /// )
658 /// }
659 /// }
660 /// ```
661 fn create_instance(
662 &self,
663 _p_create_info: &vk::InstanceCreateInfo,
664 _layer_instance_link: &VkLayerInstanceLink,
665 _p_allocator: Option<&vk::AllocationCallbacks>,
666 _p_instance: *mut vk::Instance,
667 ) -> LayerResult<ash::prelude::VkResult<()>> {
668 LayerResult::Unhandled
669 }
670}
671
672/// A trait for the layer implementation to provide metadata of [`GlobalHooks`] for the layer
673/// framework.
674///
675/// This trait serves as the container of the [`GlobalHooks`] type and provides the list of
676/// intercepted
677/// [global commands](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetInstanceProcAddr.html#:~:text=The%20global%20commands%20are%3A%20vkEnumerateInstanceVersion%2C%20vkEnumerateInstanceExtensionProperties%2C%20vkEnumerateInstanceLayerProperties%2C%20and%20vkCreateInstance.)
678/// for the layer framework. This trait and [`GlobalHooks`] can be implemented by the same type.
679///
680/// This trait can also be automatically implemented with the help of the
681/// [`auto_globalhooksinfo_impl`][`crate::auto_globalhooksinfo_impl`] attribute macro.
682///
683/// # Examples
684///
685/// A layer that needs to intercept the `vkCreateInstance` function.
686///
687/// ```
688/// # use ash::vk;
689/// # use vulkan_layer::{
690/// # GlobalHooks, GlobalHooksInfo, LayerResult, LayerVulkanCommand as VulkanCommand,
691/// # VkLayerInstanceLink,
692/// # };
693/// #
694/// struct MyLayerGlobalHooks;
695///
696/// impl GlobalHooks for MyLayerGlobalHooks {
697/// fn create_instance(
698/// &self,
699/// _p_create_info: &vk::InstanceCreateInfo,
700/// _layer_instance_link: &VkLayerInstanceLink,
701/// _p_allocator: Option<&vk::AllocationCallbacks>,
702/// _p_instance: *mut vk::Instance,
703/// ) -> LayerResult<ash::prelude::VkResult<()>> {
704/// LayerResult::Unhandled
705/// }
706/// }
707///
708/// impl GlobalHooksInfo for MyLayerGlobalHooks {
709/// type HooksType = Self;
710/// type HooksRefType<'a> = &'a Self;
711///
712/// fn hooked_commands() -> &'static [VulkanCommand] {
713/// &[VulkanCommand::CreateInstance]
714/// }
715///
716/// fn hooks(&self) -> Self::HooksRefType<'_> {
717/// self
718/// }
719/// }
720/// ```
721pub trait GlobalHooksInfo: Send + Sync {
722 /// The underlying [`GlobalHooks`] type that implements the core logic to intercept
723 /// [global commands](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetInstanceProcAddr.html#:~:text=The%20global%20commands%20are%3A%20vkEnumerateInstanceVersion%2C%20vkEnumerateInstanceExtensionProperties%2C%20vkEnumerateInstanceLayerProperties%2C%20and%20vkCreateInstance.).
724 ///
725 /// If a type implements both this trait and the [`GlobalHooks`] trait simultaneously,
726 /// [`GlobalHooksInfo::HooksType`] can be set to `Self`.
727 type HooksType: GlobalHooks;
728
729 /// A type that can be dereferencing to [`GlobalHooksInfo::HooksType`]. Usually just
730 /// `&GlobalHooksInfo::HooksType`.
731 ///
732 /// This extra associated type allows the layer implementation to use a smart pointer like
733 /// [`Arc`][std::sync::Arc] or [`MutexGuard`][std::sync::MutexGuard] to hold the reference.
734 ///
735 /// # Examples
736 ///
737 /// A layer implementation that returns a [`Arc`][std::sync::Arc].
738 /// ```
739 /// # use std::sync::Arc;
740 /// # use vulkan_layer::{GlobalHooks, GlobalHooksInfo, LayerVulkanCommand as VulkanCommand};
741 /// #
742 /// # struct MyLayerGlobalHooks;
743 /// #
744 /// # impl GlobalHooks for MyLayerGlobalHooks {}
745 /// #
746 /// struct MyLayerGlobalHooksInfo(Arc<MyLayerGlobalHooks>);
747 ///
748 /// impl GlobalHooksInfo for MyLayerGlobalHooksInfo {
749 /// type HooksType = MyLayerGlobalHooks;
750 /// type HooksRefType<'a> = Arc<MyLayerGlobalHooks>;
751 /// #
752 /// # fn hooked_commands() -> &'static [VulkanCommand] {
753 /// # &[]
754 /// # }
755 ///
756 /// // Other required methods omitted.
757 ///
758 /// fn hooks(&self) -> Self::HooksRefType<'_> {
759 /// Arc::clone(&self.0)
760 /// }
761 /// }
762 /// ```
763 type HooksRefType<'a>: Deref<Target = Self::HooksType> + 'a
764 where
765 Self: 'a;
766
767 /// Returns a slice of
768 /// [global commands](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetInstanceProcAddr.html#:~:text=The%20global%20commands%20are%3A%20vkEnumerateInstanceVersion%2C%20vkEnumerateInstanceExtensionProperties%2C%20vkEnumerateInstanceLayerProperties%2C%20and%20vkCreateInstance.)
769 /// that the layer implementation needs to intercept.
770 ///
771 /// Avoid returning commands other than
772 /// [global commands](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetInstanceProcAddr.html#:~:text=The%20global%20commands%20are%3A%20vkEnumerateInstanceVersion%2C%20vkEnumerateInstanceExtensionProperties%2C%20vkEnumerateInstanceLayerProperties%2C%20and%20vkCreateInstance.).
773 /// Otherwise, those commands may or may not be intercepted.
774 ///
775 /// Because this function is called in [`Global::default`] to initialize the singleton
776 /// [`Global`] instance, implementations must not call into [`Global::default`] directly or
777 /// indirectly including:
778 /// * [`Layer::global_instance`]: this function will call [`Global::default`] under certain
779 /// circumstances.
780 /// * Any [`Global`] static methods: These functions may call into [`Layer::global_instance`] to
781 /// obtain the global singleton.
782 /// * Any Vulkan APIs exposed by the Vulkan loader: These functions will almost certainly
783 /// trigger the layer initialization logic.
784 fn hooked_commands() -> &'static [VulkanCommand];
785
786 /// Returns the reference of the [`GlobalHooksInfo::HooksType`].
787 ///
788 /// The layer framework uses the returned value to call into the layer implementation of
789 /// intercepted commands.
790 fn hooks(&self) -> Self::HooksRefType<'_>;
791}
792
793/// A wrapper for
794/// [`VkExtensionProperties`](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkExtensionProperties.html).
795/// Structure specifying an extension properties.
796///
797/// Can convert to the `ash::vk::ExtensionProperties` C binding with [`From`] or [`Into`].
798///
799/// # Examples
800///
801/// ```
802/// use ash::vk;
803/// use std::ffi::CStr;
804/// use vulkan_layer::{Extension, ExtensionProperties};
805///
806/// let extension_properties = ExtensionProperties {
807/// name: Extension::KHRSurface,
808/// spec_version: 25,
809/// };
810/// let extension_properties_ffi: vk::ExtensionProperties = extension_properties.clone().into();
811/// assert_eq!(
812/// unsafe { CStr::from_ptr(extension_properties_ffi.extension_name.as_ptr()) },
813/// ash::vk::KhrSurfaceFn::name()
814/// );
815/// assert_eq!(extension_properties_ffi.spec_version, 25);
816/// ```
817#[derive(Clone)]
818pub struct ExtensionProperties {
819 /// The name of the extension.
820 pub name: Extension,
821
822 /// `spec_version` is the version of this extension. It is an integer, incremented with
823 /// backward compatible changes.
824 pub spec_version: u32,
825}
826
827impl From<ExtensionProperties> for vk::ExtensionProperties {
828 fn from(ExtensionProperties { name, spec_version }: ExtensionProperties) -> Self {
829 let name: &str = name.into();
830 let name = CString::new(name).unwrap();
831 let byte_len = name.as_bytes().len();
832 let name = unsafe { std::slice::from_raw_parts(name.as_ptr(), byte_len) };
833 let mut res = Self::builder()
834 .extension_name([0; vk::MAX_EXTENSION_NAME_SIZE])
835 .spec_version(spec_version)
836 .build();
837 res.extension_name[0..byte_len].copy_from_slice(name);
838 res
839 }
840}
841
842/// A Rust bindings of the
843/// [layer manifest file](https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderLayerInterface.md#layer-manifest-file-format).
844///
845/// The return type of [`Layer::manifest`]. This type is marked as `non_exhaustive`, because it will
846/// evolve with the [layer manifest schema](https://github.com/LunarG/VulkanTools/blob/v1.3.261/vkconfig_core/layers/layers_schema.json).
847/// The user should initialize the value of this type in a way that will still compile if new fields
848/// are added. [`LayerManifest::default`] will initialize all fields to empty.
849/// ```
850/// # use ash::vk;
851/// # use vulkan_layer::LayerManifest;
852/// let mut manifest = LayerManifest::default();
853/// manifest.name = "VK_LAYER_VENDOR_rust_example";
854/// manifest.spec_version = vk::API_VERSION_1_1;
855/// ```
856// TODO: add the capability to serialize to json.
857#[non_exhaustive]
858#[derive(Default, Clone)]
859pub struct LayerManifest {
860 /// The string used to uniquely identify this layer to applications.
861 ///
862 /// The layer name should follow the
863 /// [layer name convention](https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderLayerInterface.md#layer-conventions-and-rules)
864 /// according to
865 /// [`LLP_LAYER_3`](<https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderLayerInterface.md#requirements-of-well-behaved-layers:~:text=Conventions%20and%20Rules-,LLP_LAYER_3,-Any%20new%20layer>).
866 pub name: &'static str,
867
868 /// The `VkLayerProperties::specVersion` field. The `"api_version"` JSON node.
869 ///
870 /// The major.minor.patch version number of the Vulkan API that the layer supports encoded as
871 /// described in
872 /// <https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#extendingvulkan-coreversions-versionnumbers>.
873 /// It does not require the application to make use of that API version. It simply is an
874 /// indication that the layer can support Vulkan API instance and device functions up to and
875 /// including that API version.
876 pub spec_version: u32,
877
878 /// The version of the layer implemented.
879 ///
880 /// It is an integer, increasing with backward compatible changes. If the layer itself has any
881 /// major changes, this number should change so the loader and/or application can identify it
882 /// properly.
883 pub implementation_version: u32,
884
885 /// A high-level description of the layer and its intended use which provides additional
886 /// details that can be used by the application to identify the layer.
887 ///
888 /// The description shouldn't be longer than `VK_MAX_DESCRIPTION_SIZE` bytes after encoded as a
889 /// null-terminated UTF-8 string. Otherwise, the layer framework will panic at runtime.
890 pub description: &'static str,
891
892 /// Contains the list of device extension names supported by this layer.
893 ///
894 /// An array of one or more elements is required if any device extensions are supported by a
895 /// layer; otherwise the array should be empty. In `vkCreateDevice`, the layer framework
896 /// removes the extensions mentioned here from the
897 /// `VkDeviceCreateInfo::ppEnabledExtensionNames` list.
898 pub device_extensions: &'static [ExtensionProperties],
899}
900
901/// The [`Layer`] trait provides all layer implementation information for the layer framework.
902///
903/// The type that implements [`Layer`] provides the following functionalities:
904/// * Global initialization for the layer.
905/// * The factory of [`LayerManifest`], [`InstanceInfo`], and [`DeviceInfo`].
906/// * The container of [`GlobalHooksInfo`].
907/// * Providing the storage of [`Global`].
908/// * Overriding the intercepted Vulkan commands at runtime.
909///
910/// # Initialization
911///
912/// The layer implementation can rely on `Layer::default` to perform the global initialization. e.g.
913/// initialize the logger.
914///
915/// The layer framework guarantees that `Layer::default` is only called once when
916/// [`Global::default`] is called, and won't be called anywhere else. The layer framework doesn't
917/// call [`Global::default`] to initialize. Instead, [`Layer::global_instance`] is called instead on
918/// every Vulkan function entry when the global singleton is needed, and the layer implementation is
919/// expected to lazily create the static [`Global`] singleton with [`Global::default`] in
920/// [`Layer::global_instance`]. Therefore, if the layer implementation can guarantee that
921/// [`Global::default`] is called once during the lifetime of the whole process, it is also
922/// guaranteed that `Layer::default` is called once during the lifetime of the whole process. Note
923/// that it may be difficult to realize such guarantee, because the Vulkan loader my load and unload
924/// the layer library multiple times, and the application my load and unload the Vulkan loader
925/// multiple times.
926///
927/// `Layer::default` must not call into any [`Global`] methods and any Vulkan commands exported by
928/// the Vulkan loader to avoid an endless recursion.
929///
930/// # Global clean up
931///
932/// It is recommended that the implementor of the [`Layer`] trait should have a no-op drop if all
933/// `VkInstance` intercepted by the layer is destroyed. If so, [`Global`] has a no-op drop when all
934/// `VkInstance` is destroyed. If the [`Layer`] implementor is always
935/// trivially-destructible[^trivial_dtor], it's great.
936///
937/// It is guaranteed that when `Global::drop` is called, `Layer::drop` is called exactly once.
938/// However, as a dynamic link library that may be loaded and unloaded to/from the process multiple
939/// times, there is no easy way to guarantee that `Global::drop` is called on every unload. In
940/// additoon, in Rust, static items do not call drop at the end of the program[^static_drop]. We
941/// can't expect drop to be called on dynamic link library unload as well. Therefore, we should
942/// avoid using `Layer::drop` to do clean-up to avoid resource leak.
943///
944/// If it is really necessary to store some not trivially-destructible types(note that all Rust
945/// [`std::collections`] are not trivially-destructible) in side the type that implements [`Layer`],
946/// the following techniques may be helpful.
947///
948/// One way to test if a type `T` leaks heap memory is to run Miri tests with [`std::mem::forget`].
949///
950/// ```
951/// #[derive(Default)]
952/// struct TriviallyDestructible;
953///
954/// #[derive(Default)]
955/// struct NotTriviallyDestructible(Box<u32>);
956///
957/// // This passes the Miri test, so TriviallyDestructible won't leak heap memory if drop is not
958/// // called.
959/// {
960/// let x: TriviallyDestructible = Default::default();
961/// std::mem::forget(x);
962/// }
963///
964/// // This fails the Miri test, so NotTriviallyDestructible will leak heap memory if drop is not
965/// // called.
966/// {
967/// let x: NotTriviallyDestructible = Default::default();
968/// std::mem::forget(x);
969/// }
970/// ```
971///
972/// The drop of Rust [`std::collections`] is not no-op even if the collection is empty, so avoid
973/// using them directly in the type that implements [`Layer`].
974///
975/// The drop of [`std::sync::Weak`] is not no-op even if all strong references to the underlying
976/// object don't exist, so we should avoid using it to construct a type that needs a no-op drop.
977///
978/// The drop of [`Option::None`] is no-op even if the underlying `T` is not trivially destructible,
979/// and the drop of [`Mutex`][std::sync::Mutex] is no-op if the drop of the wrapped `T` is no-op.
980/// Therefore `Mutex<Option<T>>` is a good basic block to build a type that needs a no-op drop.
981///
982/// # Examples
983///
984/// A simple layer that prints "Hello from the Rust Vulkan layer!" to `stdout` on every
985/// `vkCreateDevice`.
986/// ```
987/// use ash::{self, vk};
988/// use once_cell::sync::Lazy;
989/// use std::sync::Arc;
990/// use vulkan_layer::{
991/// Global, Layer, LayerManifest, StubDeviceInfo, StubGlobalHooks, StubInstanceInfo,
992/// };
993///
994/// #[derive(Default)]
995/// struct MyLayer(StubGlobalHooks);
996///
997/// impl Layer for MyLayer {
998/// type GlobalHooksInfo = StubGlobalHooks;
999/// type InstanceInfo = StubInstanceInfo;
1000/// type DeviceInfo = StubDeviceInfo;
1001/// type InstanceInfoContainer = StubInstanceInfo;
1002/// type DeviceInfoContainer = StubDeviceInfo;
1003///
1004/// fn global_instance() -> impl std::ops::Deref<Target = Global<Self>> + 'static {
1005/// static GLOBAL: Lazy<Global<MyLayer>> = Lazy::new(Default::default);
1006/// &*GLOBAL
1007/// }
1008///
1009/// fn manifest() -> LayerManifest {
1010/// let mut manifest = LayerManifest::default();
1011/// manifest.name = "VK_LAYER_VENDOR_rust_example";
1012/// manifest.spec_version = vk::API_VERSION_1_1;
1013/// manifest
1014/// }
1015///
1016/// fn global_hooks_info(&self) -> &Self::GlobalHooksInfo {
1017/// &self.0
1018/// }
1019///
1020/// fn create_instance_info(
1021/// &self,
1022/// _: &vk::InstanceCreateInfo,
1023/// _: Option<&vk::AllocationCallbacks>,
1024/// _: Arc<ash::Instance>,
1025/// _next_get_instance_proc_addr: vk::PFN_vkGetInstanceProcAddr,
1026/// ) -> Self::InstanceInfoContainer {
1027/// Default::default()
1028/// }
1029///
1030/// fn create_device_info(
1031/// &self,
1032/// _: vk::PhysicalDevice,
1033/// _: &vk::DeviceCreateInfo,
1034/// _: Option<&vk::AllocationCallbacks>,
1035/// _: Arc<ash::Device>,
1036/// _next_get_device_proc_addr: vk::PFN_vkGetDeviceProcAddr,
1037/// ) -> Self::DeviceInfoContainer {
1038/// println!("Hello from the Rust Vulkan layer!");
1039/// Default::default()
1040/// }
1041/// }
1042/// ```
1043///
1044/// A layer that initializes the logging infrastructure with `env_logger`.
1045/// ```
1046/// use ash::{self, vk};
1047/// use log::info;
1048/// use once_cell::sync::Lazy;
1049/// use std::sync::Arc;
1050/// use vulkan_layer::{
1051/// Global, Layer, LayerManifest, StubDeviceInfo, StubGlobalHooks, StubInstanceInfo,
1052/// };
1053///
1054/// struct MyLayer(StubGlobalHooks);
1055///
1056/// impl Default for MyLayer {
1057/// fn default() -> Self {
1058/// env_logger::init();
1059/// Self(Default::default())
1060/// }
1061/// }
1062///
1063/// impl Layer for MyLayer {
1064/// type GlobalHooksInfo = StubGlobalHooks;
1065/// type InstanceInfo = StubInstanceInfo;
1066/// type DeviceInfo = StubDeviceInfo;
1067/// type InstanceInfoContainer = StubInstanceInfo;
1068/// type DeviceInfoContainer = StubDeviceInfo;
1069///
1070/// fn global_instance() -> impl std::ops::Deref<Target = Global<Self>> + 'static {
1071/// static GLOBAL: Lazy<Global<MyLayer>> = Lazy::new(Default::default);
1072/// &*GLOBAL
1073/// }
1074///
1075/// fn manifest() -> LayerManifest {
1076/// let mut manifest = LayerManifest::default();
1077/// manifest.name = "VK_LAYER_VENDOR_rust_example";
1078/// manifest.spec_version = vk::API_VERSION_1_1;
1079/// manifest
1080/// }
1081///
1082/// fn global_hooks_info(&self) -> &Self::GlobalHooksInfo {
1083/// &self.0
1084/// }
1085///
1086/// fn create_instance_info(
1087/// &self,
1088/// _: &vk::InstanceCreateInfo,
1089/// _: Option<&vk::AllocationCallbacks>,
1090/// _: Arc<ash::Instance>,
1091/// _next_get_instance_proc_addr: vk::PFN_vkGetInstanceProcAddr,
1092/// ) -> Self::InstanceInfoContainer {
1093/// Default::default()
1094/// }
1095///
1096/// fn create_device_info(
1097/// &self,
1098/// _: vk::PhysicalDevice,
1099/// _: &vk::DeviceCreateInfo,
1100/// _: Option<&vk::AllocationCallbacks>,
1101/// _: Arc<ash::Device>,
1102/// _next_get_device_proc_addr: vk::PFN_vkGetDeviceProcAddr,
1103/// ) -> Self::DeviceInfoContainer {
1104/// info!("Hello from the Rust Vulkan layer!");
1105/// Default::default()
1106/// }
1107/// }
1108/// ```
1109///
1110/// [^trivial_dtor]: This term is borrowed from
1111/// [C++](<https://en.cppreference.com/w/cpp/language/destructor#:~:text=since%20C%2B%2B20)-,Trivial%20destructor,-The%20destructor%20for>):
1112/// if the type itself doesn't have a destructor and the types of all fields are trivially
1113/// destructible, this type is trivially destructible. Rust has [`std::mem::needs_drop`], but this
1114/// function doesn't have a strong guarantee.
1115///
1116/// [^static_drop]: <https://doc.rust-lang.org/reference/items/static-items.html#:~:text=Static%20items%20do%20not%20call%20drop%20at%20the%20end%20of%20the%20program.>
1117pub trait Layer: Sync + Default + 'static {
1118 /// The type that provides information about interception of
1119 /// [global commands](<https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetInstanceProcAddr.html#:~:text=The%20global%20commands%20are%3A%20vkEnumerateInstanceVersion%2C%20vkEnumerateInstanceExtensionProperties%2C%20vkEnumerateInstanceLayerProperties%2C%20and%20vkCreateInstance>).
1120 ///
1121 /// If the layer implementation is not interested in intercepting any
1122 /// [global commands](<https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetInstanceProcAddr.html#:~:text=The%20global%20commands%20are%3A%20vkEnumerateInstanceVersion%2C%20vkEnumerateInstanceExtensionProperties%2C%20vkEnumerateInstanceLayerProperties%2C%20and%20vkCreateInstance>),
1123 /// [`StubGlobalHooks`][crate::StubGlobalHooks] can be used.
1124 type GlobalHooksInfo: GlobalHooksInfo;
1125
1126 /// The type that provides information about interception of
1127 /// [Vulkan instance functions](https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderInterfaceArchitecture.md#instance-functions),
1128 /// [global commands](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetInstanceProcAddr.html#:~:text=The%20global%20commands%20are%3A%20vkEnumerateInstanceVersion%2C%20vkEnumerateInstanceExtensionProperties%2C%20vkEnumerateInstanceLayerProperties%2C%20and%20vkCreateInstance.)
1129 /// not included.
1130 ///
1131 /// If the layer implementation is not interested in intercepting any
1132 /// [Vulkan instance functions](https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderInterfaceArchitecture.md#instance-functions)
1133 /// ([global commands](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetInstanceProcAddr.html#:~:text=The%20global%20commands%20are%3A%20vkEnumerateInstanceVersion%2C%20vkEnumerateInstanceExtensionProperties%2C%20vkEnumerateInstanceLayerProperties%2C%20and%20vkCreateInstance.)
1134 /// not included), [`StubInstanceInfo`][crate::StubInstanceInfo] can be used.
1135 type InstanceInfo: InstanceInfo;
1136
1137 /// The type that provides information about interception of
1138 /// [Vulkan device functions](https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderInterfaceArchitecture.md#device-functions).
1139 ///
1140 /// If the layer implementation is not interested in intercepting any
1141 /// [Vulkan device functions](https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderInterfaceArchitecture.md#device-functions),
1142 /// [`StubDeviceInfo`][crate::StubDeviceInfo] can be used.
1143 type DeviceInfo: DeviceInfo;
1144
1145 /// The type that holds a [`Layer::InstanceInfo`] type. Usually just `Self::InstanceInfo`.
1146 ///
1147 /// This extra associated type allows the layer to use a smart pointer like [`Arc`] or [`Box`]
1148 /// to hold the [`Layer::InstanceInfo`].
1149 ///
1150 /// # Examples
1151 ///
1152 /// Use [`Arc`] to hold the [`Layer::InstanceInfo`].
1153 /// ```
1154 /// use ash::vk;
1155 /// use once_cell::sync::Lazy;
1156 /// use std::sync::Arc;
1157 /// use vulkan_layer::{
1158 /// Global, Layer, LayerManifest, StubDeviceInfo, StubGlobalHooks, StubInstanceInfo,
1159 /// };
1160 ///
1161 /// #[derive(Default)]
1162 /// struct MyLayer(StubGlobalHooks);
1163 ///
1164 /// impl Layer for MyLayer {
1165 /// type InstanceInfo = StubInstanceInfo;
1166 /// type InstanceInfoContainer = Arc<StubInstanceInfo>;
1167 ///
1168 /// fn create_instance_info(
1169 /// &self,
1170 /// _: &vk::InstanceCreateInfo,
1171 /// _: Option<&vk::AllocationCallbacks>,
1172 /// _: Arc<ash::Instance>,
1173 /// _next_get_instance_proc_addr: vk::PFN_vkGetInstanceProcAddr,
1174 /// ) -> Self::InstanceInfoContainer {
1175 /// Arc::default()
1176 /// }
1177 ///
1178 /// // Unrelated required items are omitted.
1179 /// # type GlobalHooksInfo = StubGlobalHooks;
1180 /// # type DeviceInfo = StubDeviceInfo;
1181 /// # type DeviceInfoContainer = StubDeviceInfo;
1182 /// #
1183 /// # fn global_instance() -> impl std::ops::Deref<Target = Global<Self>> + 'static {
1184 /// # static GLOBAL: Lazy<Global<MyLayer>> = Lazy::new(Default::default);
1185 /// # &*GLOBAL
1186 /// # }
1187 /// #
1188 /// # fn manifest() -> LayerManifest {
1189 /// # Default::default()
1190 /// # }
1191 /// #
1192 /// # fn global_hooks_info(&self) -> &Self::GlobalHooksInfo {
1193 /// # &self.0
1194 /// # }
1195 /// #
1196 /// # fn create_device_info(
1197 /// # &self,
1198 /// # _: vk::PhysicalDevice,
1199 /// # _: &vk::DeviceCreateInfo,
1200 /// # _: Option<&vk::AllocationCallbacks>,
1201 /// # _: Arc<ash::Device>,
1202 /// # _next_get_device_proc_addr: vk::PFN_vkGetDeviceProcAddr,
1203 /// # ) -> Self::DeviceInfoContainer {
1204 /// # Default::default()
1205 /// # }
1206 /// }
1207 /// ```
1208 type InstanceInfoContainer: Borrow<Self::InstanceInfo> + Sync + Send;
1209
1210 /// The type that holds a [`Layer::DeviceInfo`] type. Usually just `Self::DeviceInfo`.
1211 ///
1212 /// This extra associated type allows the layer to use a smart pointer like [`Arc`] to hold the
1213 /// [`Layer::DeviceInfo`].
1214 ///
1215 /// # Examples
1216 ///
1217 /// Use [`Arc`] to hold the [`Layer::DeviceInfo`].
1218 /// ```
1219 /// use ash::vk;
1220 /// use once_cell::sync::Lazy;
1221 /// use std::sync::Arc;
1222 /// use vulkan_layer::{
1223 /// Global, Layer, LayerManifest, StubDeviceInfo, StubGlobalHooks, StubInstanceInfo,
1224 /// };
1225 ///
1226 /// #[derive(Default)]
1227 /// struct MyLayer(StubGlobalHooks);
1228 ///
1229 /// impl Layer for MyLayer {
1230 /// type DeviceInfo = StubDeviceInfo;
1231 /// type DeviceInfoContainer = Arc<StubDeviceInfo>;
1232 ///
1233 /// fn create_device_info(
1234 /// &self,
1235 /// _: vk::PhysicalDevice,
1236 /// _: &vk::DeviceCreateInfo,
1237 /// _: Option<&vk::AllocationCallbacks>,
1238 /// _: Arc<ash::Device>,
1239 /// _next_get_device_proc_addr: vk::PFN_vkGetDeviceProcAddr,
1240 /// ) -> Self::DeviceInfoContainer {
1241 /// Arc::default()
1242 /// }
1243 ///
1244 /// // Unrelated required items are omitted.
1245 /// # type GlobalHooksInfo = StubGlobalHooks;
1246 /// # type InstanceInfo = StubInstanceInfo;
1247 /// # type InstanceInfoContainer = StubInstanceInfo;
1248 /// #
1249 /// # fn global_instance() -> impl std::ops::Deref<Target = Global<Self>> + 'static {
1250 /// # static GLOBAL: Lazy<Global<MyLayer>> = Lazy::new(Default::default);
1251 /// # &*GLOBAL
1252 /// # }
1253 /// #
1254 /// # fn manifest() -> LayerManifest {
1255 /// # Default::default()
1256 /// # }
1257 /// #
1258 /// # fn global_hooks_info(&self) -> &Self::GlobalHooksInfo {
1259 /// # &self.0
1260 /// # }
1261 /// #
1262 /// # fn create_instance_info(
1263 /// # &self,
1264 /// # _: &vk::InstanceCreateInfo,
1265 /// # _: Option<&vk::AllocationCallbacks>,
1266 /// # _: Arc<ash::Instance>,
1267 /// # _next_get_instance_proc_addr: vk::PFN_vkGetInstanceProcAddr,
1268 /// # ) -> Self::InstanceInfoContainer {
1269 /// # Default::default()
1270 /// # }
1271 /// }
1272 /// ```
1273 type DeviceInfoContainer: Borrow<Self::DeviceInfo> + Sync + Send;
1274
1275 /// Returns the
1276 /// [layer manifest](https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderLayerInterface.md#layer-manifest-file-format)
1277 /// to the layer framework.
1278 ///
1279 /// Implementors should always return the same value during the lifetime of the process, and
1280 /// must match the manifest json file with the layer. Implementors should avoid calling into any
1281 /// [`Global`] methods because this function can be called to implement Vulkan introspection
1282 /// queries before any `VkInstance` is created.
1283 ///
1284 /// The layer framework will use this value to:
1285 /// * Implement [Vulkan introspection queries](https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderLayerInterface.md#requirements-of-well-behaved-layers:~:text=Parent-,Introspection%20Query,-%22api_version%22),
1286 /// including `vkEnumerateInstanceLayerProperties`, `vkEnumerateDeviceLayerProperties`,
1287 /// `vkEnumerateInstanceExtensionProperties` and `vkEnumerateDeviceExtensionProperties`.
1288 /// * Remove the layer device extensions from `VkDeviceCreateInfo::ppEnabledExtensionNames` in
1289 /// `vkCreateDevice` if present.
1290 fn manifest() -> LayerManifest;
1291
1292 /// Provide the reference to the global singleton of [`Global`].
1293 ///
1294 /// Implementors must always return the reference pointing to the same [`Global`] object.
1295 /// Implementors can use [`LazyLock`][std::sync::LazyLock], [`OnceLock`][std::sync::OnceLock],
1296 /// or `Lazy` from the `once_cell` crate to implement. Implementors should use
1297 /// [`Global::default`] to create the [`Global`] object. More information on initialization can
1298 /// be found [here][Layer#initialization].
1299 ///
1300 /// # Examples
1301 ///
1302 /// Use `Lazy` provided by the `once_cell` crate to implement.
1303 /// ```
1304 /// use ash::{self, vk};
1305 /// use once_cell::sync::Lazy;
1306 /// use std::sync::Arc;
1307 /// use vulkan_layer::{
1308 /// Global, Layer, LayerManifest, StubDeviceInfo, StubGlobalHooks, StubInstanceInfo,
1309 /// };
1310 ///
1311 /// #[derive(Default)]
1312 /// struct MyLayer(StubGlobalHooks);
1313 ///
1314 /// impl Layer for MyLayer {
1315 /// fn global_instance() -> impl std::ops::Deref<Target = Global<Self>> + 'static {
1316 /// static GLOBAL: Lazy<Global<MyLayer>> = Lazy::new(Default::default);
1317 /// &*GLOBAL
1318 /// }
1319 ///
1320 /// // Unrelated required items are omitted.
1321 /// # type GlobalHooksInfo = StubGlobalHooks;
1322 /// # type InstanceInfo = StubInstanceInfo;
1323 /// # type DeviceInfo = StubDeviceInfo;
1324 /// # type InstanceInfoContainer = StubInstanceInfo;
1325 /// # type DeviceInfoContainer = StubDeviceInfo;
1326 /// #
1327 /// # fn manifest() -> LayerManifest {
1328 /// # let mut manifest = LayerManifest::default();
1329 /// # manifest.name = "VK_LAYER_VENDOR_rust_example";
1330 /// # manifest.spec_version = vk::API_VERSION_1_1;
1331 /// # manifest
1332 /// # }
1333 /// #
1334 /// # fn global_hooks_info(&self) -> &Self::GlobalHooksInfo {
1335 /// # &self.0
1336 /// # }
1337 /// #
1338 /// # fn create_instance_info(
1339 /// # &self,
1340 /// # _: &vk::InstanceCreateInfo,
1341 /// # _: Option<&vk::AllocationCallbacks>,
1342 /// # _: Arc<ash::Instance>,
1343 /// # _next_get_instance_proc_addr: vk::PFN_vkGetInstanceProcAddr,
1344 /// # ) -> Self::InstanceInfoContainer {
1345 /// # Default::default()
1346 /// # }
1347 /// #
1348 /// # fn create_device_info(
1349 /// # &self,
1350 /// # _: vk::PhysicalDevice,
1351 /// # _: &vk::DeviceCreateInfo,
1352 /// # _: Option<&vk::AllocationCallbacks>,
1353 /// # _: Arc<ash::Device>,
1354 /// # _next_get_device_proc_addr: vk::PFN_vkGetDeviceProcAddr,
1355 /// # ) -> Self::DeviceInfoContainer {
1356 /// # println!("Hello from the Rust Vulkan layer!");
1357 /// # Default::default()
1358 /// # }
1359 /// }
1360 /// ```
1361 fn global_instance() -> impl std::ops::Deref<Target = Global<Self>> + 'static;
1362
1363 /// Returns a reference of the underlying [`GlobalHooksInfo`] object.
1364 fn global_hooks_info(&self) -> &Self::GlobalHooksInfo;
1365
1366 /// Returns a reference of the underlying [`GlobalHooks`] object.
1367 ///
1368 /// The layer framework relies on this function to obtain [`GlobalHooks`], and implementors
1369 /// should avoid overriding this method.
1370 fn global_hooks(&self) -> <Self::GlobalHooksInfo as GlobalHooksInfo>::HooksRefType<'_> {
1371 self.global_hooks_info().hooks()
1372 }
1373
1374 /// The factory method for the [`InstanceInfo`] type.
1375 ///
1376 /// This function is called by the layer framework in `vkCreateInstance`, after the
1377 /// `vkCreateInstance` of the next layer returns with success.
1378 ///
1379 /// # Arguments
1380 ///
1381 /// * `create_info` is a pointer to a VkInstanceCreateInfo structure controlling creation of the
1382 /// instance. The [`VkLayerInstanceLink`] linked list in the `pNext` chain is already advanced
1383 /// by the layer framework. This is the original `VkInstanceCreateInfo` passed in by the
1384 /// application or the previous layer(except the advanced [`VkLayerInstanceLink`] linked
1385 /// list), even if the layer implementation passes a different `VkInstanceCreateInfo` to the
1386 /// next layer in [`GlobalHooks::create_instance`].
1387 /// * `allocator` controls host memory allocation as described in the [Memory Allocation](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#memory-allocation)
1388 /// chapter of the Vulkan spec.
1389 /// * `instance` contains the loaded instance dispatch table of the next layer. The layer
1390 /// implementation can just clone this [`Arc`] if it is needed to keep the instance dispatch
1391 /// table.
1392 /// * `next_get_instance_proc_addr` is the `vkGetInstanceProcAddr` function pointer of the next
1393 /// layer obtained from the [`VkLayerInstanceLink`] linked list.
1394 fn create_instance_info(
1395 &self,
1396 create_info: &vk::InstanceCreateInfo,
1397 allocator: Option<&vk::AllocationCallbacks>,
1398 instance: Arc<ash::Instance>,
1399 next_get_instance_proc_addr: vk::PFN_vkGetInstanceProcAddr,
1400 ) -> Self::InstanceInfoContainer;
1401
1402 /// The factory method for the [`DeviceInfo`] type.
1403 ///
1404 /// This function is called by the layer framework in `vkCreateDevice`, after the
1405 /// `vkCreateDevice` of the next layer returns with success.
1406 ///
1407 /// # Arguments
1408 /// * `physical_device` is one of the device handles returned from a call to
1409 /// `vkEnumeratePhysicalDevices` that is passed to `vkCreateDevice` by the application or the
1410 /// previous layer.
1411 /// * `create_info` is a pointer to a `VkDeviceCreateInfo` structure containing information
1412 /// about how to create the device. The [`VkLayerDeviceLink`][crate::VkLayerDeviceLink] linked
1413 /// list in the `pNext` chain is already advanced by the layer framework. This is the original
1414 /// `VkDeviceCreateInfo` passed in by the application or the previous layer(except the
1415 /// advanced [`VkLayerDeviceLink`][crate::VkLayerDeviceLink] linked list), even if the layer
1416 /// implementation passes a different `VkDeviceCreateInfo` to the next layer in
1417 /// [`InstanceHooks::create_device`].
1418 /// * `allocator` controls host memory allocation as described in the [Memory Allocation](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#memory-allocation)
1419 /// chapter of the Vulkan spec.
1420 /// * `device` contains the loaded device dispatch table of the next layer. The layer
1421 /// implementation can just clone this [`Arc`] if it is needed to keep the device dispatch
1422 /// table.
1423 /// * `next_get_device_proc_addr` is the `vkGetDeviceProcAddr` function pointer of the next
1424 /// layer obtained from the [`VkLayerDeviceLink`][crate::VkLayerDeviceLink] linked list.
1425 fn create_device_info(
1426 &self,
1427 physical_device: vk::PhysicalDevice,
1428 create_info: &vk::DeviceCreateInfo,
1429 allocator: Option<&vk::AllocationCallbacks>,
1430 device: Arc<ash::Device>,
1431 next_get_device_proc_addr: vk::PFN_vkGetDeviceProcAddr,
1432 ) -> Self::DeviceInfoContainer;
1433
1434 /// Returns an iterator of
1435 /// [Vulkan instance functions](https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderInterfaceArchitecture.md#instance-functions)
1436 /// ([global commands](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetInstanceProcAddr.html#:~:text=The%20global%20commands%20are%3A%20vkEnumerateInstanceVersion%2C%20vkEnumerateInstanceExtensionProperties%2C%20vkEnumerateInstanceLayerProperties%2C%20and%20vkCreateInstance.)
1437 /// not included) that the layer implementation needs to intercept.
1438 ///
1439 /// This function allows the layer implementation to decide the commands to intercept
1440 /// dynamically after the `VkInstance` is created. By default it just returns all commands
1441 /// returned by [`InstanceInfo::hooked_commands`].
1442 ///
1443 /// # Arguments
1444 /// * `_instance_info` is the relevant instance.
1445 ///
1446 /// # Examples
1447 ///
1448 /// A layer that intercepts `vkDestroySurfaceKHR` only if the `VK_KHR_win32_surface` extension
1449 /// is enabled.
1450 /// ```
1451 /// use ash::vk;
1452 /// use once_cell::sync::Lazy;
1453 /// use std::{ffi::CStr, sync::Arc};
1454 /// use vulkan_layer::{
1455 /// Global, InstanceHooks, InstanceInfo, Layer, LayerManifest, LayerResult,
1456 /// LayerVulkanCommand as VulkanCommand, StubDeviceInfo, StubGlobalHooks,
1457 /// };
1458 ///
1459 /// struct MyLayerInstanceInfo {
1460 /// is_win32_surface_enabled: bool,
1461 /// }
1462 ///
1463 /// impl InstanceHooks for MyLayerInstanceInfo {
1464 /// fn destroy_surface_khr(
1465 /// &self,
1466 /// _surface: vk::SurfaceKHR,
1467 /// _p_allocator: Option<&vk::AllocationCallbacks>,
1468 /// ) -> LayerResult<()> {
1469 /// LayerResult::Unhandled
1470 /// }
1471 /// }
1472 ///
1473 /// impl InstanceInfo for MyLayerInstanceInfo {
1474 /// type HooksType = Self;
1475 /// type HooksRefType<'a> = &'a Self;
1476 ///
1477 /// fn hooked_commands() -> &'static [VulkanCommand] {
1478 /// &[VulkanCommand::DestroySurfaceKhr]
1479 /// }
1480 ///
1481 /// fn hooks(&self) -> Self::HooksRefType<'_> {
1482 /// self
1483 /// }
1484 /// }
1485 ///
1486 /// #[derive(Default)]
1487 /// struct MyLayer(StubGlobalHooks);
1488 ///
1489 /// impl Layer for MyLayer {
1490 /// // ...
1491 /// # type GlobalHooksInfo = StubGlobalHooks;
1492 /// type InstanceInfo = MyLayerInstanceInfo;
1493 /// # type DeviceInfo = StubDeviceInfo;
1494 /// type InstanceInfoContainer = MyLayerInstanceInfo;
1495 /// # type DeviceInfoContainer = StubDeviceInfo;
1496 /// #
1497 /// # fn global_instance() -> impl std::ops::Deref<Target = Global<Self>> + 'static {
1498 /// # static GLOBAL: Lazy<Global<MyLayer>> = Lazy::new(Default::default);
1499 /// # &*GLOBAL
1500 /// # }
1501 /// #
1502 /// # fn manifest() -> LayerManifest {
1503 /// # Default::default()
1504 /// # }
1505 /// #
1506 /// # fn global_hooks_info(&self) -> &Self::GlobalHooksInfo {
1507 /// # &self.0
1508 /// # }
1509 ///
1510 /// fn create_instance_info(
1511 /// &self,
1512 /// create_info: &vk::InstanceCreateInfo,
1513 /// _: Option<&vk::AllocationCallbacks>,
1514 /// _: Arc<ash::Instance>,
1515 /// _next_get_instance_proc_addr: vk::PFN_vkGetInstanceProcAddr,
1516 /// ) -> Self::InstanceInfoContainer {
1517 /// let enabled_extensions = if create_info.enabled_extension_count > 0 {
1518 /// unsafe {
1519 /// std::slice::from_raw_parts(
1520 /// create_info.pp_enabled_extension_names,
1521 /// create_info.enabled_extension_count as usize,
1522 /// )
1523 /// }
1524 /// } else {
1525 /// &[]
1526 /// };
1527 /// let is_win32_surface_enabled = enabled_extensions
1528 /// .iter()
1529 /// .find(|extension_name| {
1530 /// (unsafe { CStr::from_ptr(**extension_name) })
1531 /// == ash::extensions::khr::Win32Surface::name()
1532 /// })
1533 /// .is_some();
1534 /// MyLayerInstanceInfo {
1535 /// is_win32_surface_enabled,
1536 /// }
1537 /// }
1538 ///
1539 /// fn hooked_instance_commands(
1540 /// &self,
1541 /// instance_info: &Self::InstanceInfo,
1542 /// ) -> Box<dyn Iterator<Item = VulkanCommand>> {
1543 /// let should_hook_destroy_surface = instance_info.is_win32_surface_enabled;
1544 /// Box::new(
1545 /// Self::InstanceInfo::hooked_commands()
1546 /// .iter()
1547 /// .cloned()
1548 /// .filter(move |command| match command {
1549 /// VulkanCommand::DestroySurfaceKhr => should_hook_destroy_surface,
1550 /// _ => true,
1551 /// }),
1552 /// )
1553 /// }
1554 /// #
1555 /// # fn create_device_info(
1556 /// # &self,
1557 /// # _: vk::PhysicalDevice,
1558 /// # _: &vk::DeviceCreateInfo,
1559 /// # _: Option<&vk::AllocationCallbacks>,
1560 /// # _: Arc<ash::Device>,
1561 /// # _next_get_device_proc_addr: vk::PFN_vkGetDeviceProcAddr,
1562 /// # ) -> Self::DeviceInfoContainer {
1563 /// # Default::default()
1564 /// # }
1565 /// }
1566 /// ```
1567 fn hooked_instance_commands(
1568 &self,
1569 _instance_info: &Self::InstanceInfo,
1570 ) -> Box<dyn Iterator<Item = VulkanCommand>> {
1571 Box::new(Self::InstanceInfo::hooked_commands().iter().cloned())
1572 }
1573
1574 /// Returns an iterator of
1575 /// [Vulkan device functions](https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderInterfaceArchitecture.md#device-functions)
1576 /// that the layer implementation needs to intercept.
1577 ///
1578 /// This function allows the layer implementation to decide the commands to intercept
1579 /// dynamically after the `VkInstance` or `VkDevice` is created. By default it just returns all
1580 /// commands returned by [`DeviceInfo::hooked_commands`].
1581 ///
1582 /// Arguments
1583 /// * `_instance_info` is the relevant instance.
1584 /// * `_device_info` is an optional relevant device. If `_device_info` is [`None`], the layer
1585 /// framework is querying all possible intercepted
1586 /// [device functions](https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderInterfaceArchitecture.md#device-functions)
1587 /// to implement `vkGetInstanceProcAddr` with the `pName` parameter referring to a
1588 /// [device functions](https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderInterfaceArchitecture.md#device-functions),
1589 /// i.e. implementations must guarantee that the returned commands with a [`None`]
1590 /// `_device_info` is the superset of all possible `_device_info`. Otherwise, the application
1591 /// that just uses the function pointers returned by `vkGetInstanceProcAddr` may skip the
1592 /// current layer.
1593 ///
1594 /// # Examples
1595 ///
1596 /// A layer that intercepts the `vkCreateImage` only if the ASTC LDR feature is enabled.
1597 /// ```
1598 /// use ash::{self, prelude::VkResult, vk};
1599 /// use once_cell::sync::Lazy;
1600 /// use std::sync::Arc;
1601 /// use vulkan_layer::{
1602 /// DeviceHooks, DeviceInfo, Global, Layer, LayerManifest, LayerResult,
1603 /// LayerVulkanCommand as VulkanCommand, StubGlobalHooks, StubInstanceInfo,
1604 /// VulkanBaseInStructChain,
1605 /// };
1606 ///
1607 /// struct MyLayerDeviceInfo {
1608 /// is_astc_enabled: bool,
1609 /// }
1610 ///
1611 /// impl DeviceHooks for MyLayerDeviceInfo {
1612 /// fn create_image(
1613 /// &self,
1614 /// create_info: &vk::ImageCreateInfo,
1615 /// _p_allocator: Option<&vk::AllocationCallbacks>,
1616 /// ) -> LayerResult<VkResult<vk::Image>> {
1617 /// if create_info.format == vk::Format::ASTC_4X4_UNORM_BLOCK {
1618 /// println!("ASTC 4x4 UNORM image created.");
1619 /// }
1620 /// LayerResult::Unhandled
1621 /// }
1622 /// }
1623 ///
1624 /// impl DeviceInfo for MyLayerDeviceInfo {
1625 /// type HooksType = Self;
1626 /// type HooksRefType<'a> = &'a Self;
1627 ///
1628 /// fn hooked_commands() -> &'static [VulkanCommand] {
1629 /// &[VulkanCommand::CreateImage]
1630 /// }
1631 ///
1632 /// fn hooks(&self) -> Self::HooksRefType<'_> {
1633 /// self
1634 /// }
1635 /// }
1636 ///
1637 /// #[derive(Default)]
1638 /// struct MyLayer(StubGlobalHooks);
1639 ///
1640 /// impl Layer for MyLayer {
1641 /// // ...
1642 /// # type GlobalHooksInfo = StubGlobalHooks;
1643 /// # type InstanceInfo = StubInstanceInfo;
1644 /// type DeviceInfo = MyLayerDeviceInfo;
1645 /// # type InstanceInfoContainer = StubInstanceInfo;
1646 /// type DeviceInfoContainer = MyLayerDeviceInfo;
1647 /// #
1648 /// # fn global_instance() -> impl std::ops::Deref<Target = Global<Self>> + 'static {
1649 /// # static GLOBAL: Lazy<Global<MyLayer>> = Lazy::new(Default::default);
1650 /// # &*GLOBAL
1651 /// # }
1652 /// #
1653 /// # fn manifest() -> LayerManifest {
1654 /// # Default::default()
1655 /// # }
1656 /// #
1657 /// # fn global_hooks_info(&self) -> &Self::GlobalHooksInfo {
1658 /// # &self.0
1659 /// # }
1660 /// #
1661 /// # fn create_instance_info(
1662 /// # &self,
1663 /// # _: &vk::InstanceCreateInfo,
1664 /// # _: Option<&vk::AllocationCallbacks>,
1665 /// # _: Arc<ash::Instance>,
1666 /// # _next_get_instance_proc_addr: vk::PFN_vkGetInstanceProcAddr,
1667 /// # ) -> Self::InstanceInfoContainer {
1668 /// # Default::default()
1669 /// # }
1670 /// #
1671 /// fn create_device_info(
1672 /// &self,
1673 /// _: vk::PhysicalDevice,
1674 /// create_info: &vk::DeviceCreateInfo,
1675 /// _: Option<&vk::AllocationCallbacks>,
1676 /// _: Arc<ash::Device>,
1677 /// _next_get_device_proc_addr: vk::PFN_vkGetDeviceProcAddr,
1678 /// ) -> Self::DeviceInfoContainer {
1679 /// let mut p_next_chain: VulkanBaseInStructChain =
1680 /// unsafe { (create_info.p_next as *const vk::BaseInStructure).as_ref() }.into();
1681 /// let is_astc_enabled = p_next_chain
1682 /// .find_map(|p_next| {
1683 /// let p_next = p_next as *const vk::BaseInStructure;
1684 /// unsafe {
1685 /// ash::match_in_struct!(match p_next {
1686 /// features2 @ vk::PhysicalDeviceFeatures2 => {
1687 /// Some(&features2.features)
1688 /// }
1689 /// _ => {
1690 /// None
1691 /// }
1692 /// })
1693 /// }
1694 /// })
1695 /// .or_else(|| unsafe { create_info.p_enabled_features.as_ref() })
1696 /// .map(|features| features.texture_compression_astc_ldr == vk::TRUE)
1697 /// .unwrap_or(false);
1698 /// MyLayerDeviceInfo { is_astc_enabled }
1699 /// }
1700 ///
1701 /// fn hooked_device_commands(
1702 /// &self,
1703 /// _instance_info: &Self::InstanceInfo,
1704 /// device_info: Option<&Self::DeviceInfo>,
1705 /// ) -> Box<dyn Iterator<Item = VulkanCommand>> {
1706 /// let should_hook_create_image = device_info
1707 /// .map(|device_info| device_info.is_astc_enabled)
1708 /// // Always hook the vkCreateImage function in the function pointers returned by
1709 /// // vkGetInstanceProcAddr: we don't know if the to-be-created VkDevice will be
1710 /// // created with the ASTC feature enabled.
1711 /// .unwrap_or(true);
1712 /// Box::new(
1713 /// Self::DeviceInfo::hooked_commands()
1714 /// .iter()
1715 /// .cloned()
1716 /// .filter(move |command| match command {
1717 /// VulkanCommand::CreateImage => should_hook_create_image,
1718 /// _ => true,
1719 /// }),
1720 /// )
1721 /// }
1722 /// }
1723 /// ```
1724 fn hooked_device_commands(
1725 &self,
1726 _instance_info: &Self::InstanceInfo,
1727 _device_info: Option<&Self::DeviceInfo>,
1728 ) -> Box<dyn Iterator<Item = VulkanCommand>> {
1729 Box::new(Self::DeviceInfo::hooked_commands().iter().cloned())
1730 }
1731}
1732
1733#[cfg(test)]
1734mod tests {
1735 use super::*;
1736
1737 #[test]
1738 fn vulkan_command_try_from_should_end_up_error_with_unknown_commands() {
1739 let unknown_command = "vkTestCommandTEST";
1740 let err = VulkanCommand::try_from(unknown_command).unwrap_err();
1741 assert!(err.to_string().contains(unknown_command));
1742 }
1743}