vulkan_layer/
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#![cfg_attr(all(doc, RUSTC_NIGHTLY), feature(doc_auto_cfg))]
17
18//! This crate provides a convenient framework to develop
19//! [Vulkan layers](https://github.com/KhronosGroup/Vulkan-Loader/blob/main/docs/LoaderLayerInterface.md)
20//! in Rust on top of the [ash](https://crates.io/crates/ash) crate. If you are not familiar how to
21//! write a Vulkan layer, [this C++ tutorial](https://renderdoc.org/vulkan-layer-guide.html) by
22//! Baldur Karlsson is a good reference to start with.
23//!
24//! Key features provided by this crate includes:
25//!
26//! * Support the look-up map fashion of implementing a Vulkan layer.
27//!
28//!   The look-up maps for `VkDevice` and `VkInstance` are handled by this crate.
29//!
30//! * Implement `vkGet*ProcAddr` automatically.
31//!
32//!   This is a non-trivial work to comply with the spec, because of extensions and the
33//!   required/supported Vulkan API level. See the
34//!   [spec](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetInstanceProcAddr.html)
35//!   of `vkGetInstanceProc` for details.
36//!
37//! * Handle dispatch tables, `vkCreateInstance` and `vkCreateDevice`.
38//!
39//!   This mainly includes using the `vkGet*ProcAddr` function pointer correctly, and advancing the
40//!   `VkLayer*CreateInfo::u::pLayerInfo` link list. One common mistake in layer implementation
41//!   in `vkCreateDevice` is to call `getInstanceProcAddr(VK_NULL_HANDLE, "vkCreateDevice")` to
42//!   obtain the function pointer to `vkCreateDevice`. According to
43//!   [the spec](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetInstanceProcAddr.html#_description),
44//!   `getInstanceProcAddr` should return `NULL`. This framework helps you avoid bugs like this.
45//!
46//! Note that object wrapping is not supported by this crate, and we don't plan such support,
47//! because object wrapping requires us to intercept ALL Vulkan commands related to one handle type,
48//! so it can't handle unknown commands. In addition, object wrapping is more complicated because it
49//! is required to call loader callback on every dispatchable handle creation and destruction.
50//!
51//! # Overview
52//!
53//! The primary types in this crate are the [`Layer`] trait and the [`Global`] struct. The user
54//! implements the [`Layer`] trait for a type `T`, and the [`Global<T>`] will include all necessary
55//! functions needed to export from this dynamic link library. With the help of the
56//! [`declare_introspection_queries`] macro, the user can export those functions in oneline.
57//!
58//! ```
59//! use ash::{self, vk};
60//! use once_cell::sync::Lazy;
61//! use std::sync::Arc;
62//! use vulkan_layer::{
63//!     declare_introspection_queries, Global, Layer, LayerManifest, StubDeviceInfo,
64//!     StubGlobalHooks, StubInstanceInfo,
65//! };
66//!
67//! // Define the layer type.
68//! #[derive(Default)]
69//! struct MyLayer(StubGlobalHooks);
70//!
71//! // Implement the Layer trait.
72//! impl Layer for MyLayer {
73//!     type GlobalHooksInfo = StubGlobalHooks;
74//!     type InstanceInfo = StubInstanceInfo;
75//!     type DeviceInfo = StubDeviceInfo;
76//!     type InstanceInfoContainer = StubInstanceInfo;
77//!     type DeviceInfoContainer = StubDeviceInfo;
78//!
79//!     fn global_instance() -> impl std::ops::Deref<Target = Global<Self>> + 'static {
80//!         static GLOBAL: Lazy<Global<MyLayer>> = Lazy::new(Default::default);
81//!         &*GLOBAL
82//!     }
83//!
84//!     fn manifest() -> LayerManifest {
85//!         Default::default()
86//!     }
87//!
88//!     fn global_hooks_info(&self) -> &Self::GlobalHooksInfo {
89//!         &self.0
90//!     }
91//!
92//!     fn create_instance_info(
93//!         &self,
94//!         _: &vk::InstanceCreateInfo,
95//!         _: Option<&vk::AllocationCallbacks>,
96//!         _: Arc<ash::Instance>,
97//!         _next_get_instance_proc_addr: vk::PFN_vkGetInstanceProcAddr,
98//!     ) -> Self::InstanceInfoContainer {
99//!         Default::default()
100//!     }
101//!
102//!     fn create_device_info(
103//!         &self,
104//!         _: vk::PhysicalDevice,
105//!         _: &vk::DeviceCreateInfo,
106//!         _: Option<&vk::AllocationCallbacks>,
107//!         _: Arc<ash::Device>,
108//!         _next_get_device_proc_addr: vk::PFN_vkGetDeviceProcAddr,
109//!     ) -> Self::DeviceInfoContainer {
110//!         Default::default()
111//!     }
112//! }
113//!
114//! // Define the global type from the layer type.
115//! type MyGlobal = Global<MyLayer>;
116//! // Export C functions.
117//! declare_introspection_queries!(MyGlobal);
118//! ```
119//! The user can provide their own types that implement different traits to intercept different
120//! commands and custom the layer behavior:
121//! * [`GlobalHooks`], [`InstanceHooks`], [`DeviceHooks`]: Implements the interception to different
122//!   Vulkan commands based on the dispatch types.
123//! * [`GlobalHooksInfo`], [`InstanceInfo`], [`DeviceInfo`]: Container of [`GlobalHooks`],
124//!   [`InstanceHooks`], [`DeviceHooks`]. Provide the list of commands to intercept for the
125//!   framework. Can be automatically implemented through the [`auto_deviceinfo_impl`],
126//!   [`auto_globalhooksinfo_impl`], [`auto_instanceinfo_impl`] macros from the [`GlobalHooks`],
127//!   [`InstanceHooks`], [`DeviceHooks`] implementation respectively.
128//! * [`Layer`]: Provide the metadata of a layer through [`LayerManifest`], e.g. the name, version,
129//!   exported extensions of the layer. Provide the storage for the [`Global`] object. Container of
130//!   [`GlobalHooksInfo`] type. The factory type of the [`InstanceInfo`], [`DeviceInfo`] types.
131//!
132//! # Usage
133//!
134//! You can check a live example in `examples/hello-world`.
135//!
136//! First, create a Rust lib crate.
137//! ```bash
138//! $ mkdir vulkan-layer-rust-example
139//! $ cd vulkan-layer-rust-example
140//! $ cargo init --lib
141//!      Created library package
142//! ```
143//!
144//! Second, modify the crate type to `cdylib` and set the panic behavior to abort in `Cargo.toml`.
145//! ```toml
146//! [lib]
147//! crate-type = ["cdylib"]
148//!
149//! [profile.dev]
150//! panic = "abort"
151//!
152//! [profile.release]
153//! panic = "abort"
154//! ```
155//! We need to set panic to abort to avoid unwinding from Rust to the caller(most likely C/C++),
156//! because unwinding into other language from Rust is
157//! [undefined behavior](https://doc.rust-lang.org/beta/nomicon/unwinding.html).
158//!
159//! If you want to try the layer on Android, also modify the crate name to
160//! `VkLayer_vendor_rust_example`, because Android requires that the layer shared object library
161//! follow a specific name convention.
162//! ```toml
163//! [lib]
164//! name = "VkLayer_vendor_rust_example"
165//! ```
166//!
167//! Third, set up the dependency in `Cargo.toml`. In my case, I checkout the project repository in
168//! the same directory where the `vulkan-layer-rust-example` folder lives.
169//! ```toml
170//! [dependencies]
171//! vulkan-layer = { path = "../vk-layer-for-rust/vulkan-layer" }
172//! ```
173//! Other dependencies.
174//! ```bash
175//! cargo add ash once_cell
176//! ```
177//!
178//! Fourth, implement the layer trait in `lib.rs`.
179//! ```
180//! use ash::{self, vk};
181//! use once_cell::sync::Lazy;
182//! use std::sync::Arc;
183//! use vulkan_layer::{
184//!     declare_introspection_queries, Global, Layer, LayerManifest, StubDeviceInfo,
185//!     StubGlobalHooks, StubInstanceInfo,
186//! };
187//!
188//! #[derive(Default)]
189//! struct MyLayer(StubGlobalHooks);
190//!
191//! impl Layer for MyLayer {
192//!     type GlobalHooksInfo = StubGlobalHooks;
193//!     type InstanceInfo = StubInstanceInfo;
194//!     type DeviceInfo = StubDeviceInfo;
195//!     type InstanceInfoContainer = StubInstanceInfo;
196//!     type DeviceInfoContainer = StubDeviceInfo;
197//!
198//!     fn global_instance() -> impl std::ops::Deref<Target = Global<Self>> + 'static {
199//!         static GLOBAL: Lazy<Global<MyLayer>> = Lazy::new(Default::default);
200//!         &*GLOBAL
201//!     }
202//!
203//!     fn manifest() -> LayerManifest {
204//!         let mut manifest = LayerManifest::default();
205//!         manifest.name = "VK_LAYER_VENDOR_rust_example";
206//!         manifest.spec_version = vk::API_VERSION_1_1;
207//!         manifest.description = "Rust test layer";
208//!         manifest
209//!     }
210//!
211//!     fn global_hooks_info(&self) -> &Self::GlobalHooksInfo {
212//!         &self.0
213//!     }
214//!
215//!     fn create_instance_info(
216//!         &self,
217//!         _: &vk::InstanceCreateInfo,
218//!         _: Option<&vk::AllocationCallbacks>,
219//!         _: Arc<ash::Instance>,
220//!         _next_get_instance_proc_addr: vk::PFN_vkGetInstanceProcAddr,
221//!     ) -> Self::InstanceInfoContainer {
222//!         Default::default()
223//!     }
224//!
225//!     fn create_device_info(
226//!         &self,
227//!         _: vk::PhysicalDevice,
228//!         _: &vk::DeviceCreateInfo,
229//!         _: Option<&vk::AllocationCallbacks>,
230//!         _: Arc<ash::Device>,
231//!         _next_get_device_proc_addr: vk::PFN_vkGetDeviceProcAddr,
232//!     ) -> Self::DeviceInfoContainer {
233//!         println!("Hello from the Rust Vulkan layer!");
234//!         Default::default()
235//!     }
236//! }
237//! ```
238//!
239//! Fifth, export functions through the [`declare_introspection_queries`] macro
240//! ```
241//! # use ash::{self, vk};
242//! # use once_cell::sync::Lazy;
243//! # use std::sync::Arc;
244//! # use vulkan_layer::{
245//! #     declare_introspection_queries, Global, Layer, LayerManifest, StubDeviceInfo,
246//! #     StubGlobalHooks, StubInstanceInfo,
247//! # };
248//! #
249//! # #[derive(Default)]
250//! # struct MyLayer(StubGlobalHooks);
251//! #
252//! # impl Layer for MyLayer {
253//! #     type GlobalHooksInfo = StubGlobalHooks;
254//! #     type InstanceInfo = StubInstanceInfo;
255//! #     type DeviceInfo = StubDeviceInfo;
256//! #     type InstanceInfoContainer = StubInstanceInfo;
257//! #     type DeviceInfoContainer = StubDeviceInfo;
258//! #
259//! #     fn global_instance() -> impl std::ops::Deref<Target = Global<Self>> + 'static {
260//! #         static GLOBAL: Lazy<Global<MyLayer>> = Lazy::new(Default::default);
261//! #         &*GLOBAL
262//! #     }
263//! #
264//! #     fn manifest() -> LayerManifest {
265//! #         let mut manifest = LayerManifest::default();
266//! #         manifest.name = "VK_LAYER_VENDOR_rust_example";
267//! #         manifest.spec_version = vk::API_VERSION_1_1;
268//! #         manifest.description = "Rust test layer";
269//! #         manifest
270//! #     }
271//! #
272//! #     fn global_hooks_info(&self) -> &Self::GlobalHooksInfo {
273//! #         &self.0
274//! #     }
275//! #
276//! #     fn create_instance_info(
277//! #         &self,
278//! #         _: &vk::InstanceCreateInfo,
279//! #         _: Option<&vk::AllocationCallbacks>,
280//! #         _: Arc<ash::Instance>,
281//! #         _next_get_instance_proc_addr: vk::PFN_vkGetInstanceProcAddr,
282//! #     ) -> Self::InstanceInfoContainer {
283//! #         Default::default()
284//! #     }
285//! #
286//! #     fn create_device_info(
287//! #         &self,
288//! #         _: vk::PhysicalDevice,
289//! #         _: &vk::DeviceCreateInfo,
290//! #         _: Option<&vk::AllocationCallbacks>,
291//! #         _: Arc<ash::Device>,
292//! #         _next_get_device_proc_addr: vk::PFN_vkGetDeviceProcAddr,
293//! #     ) -> Self::DeviceInfoContainer {
294//! #         println!("Hello from the Rust Vulkan layer!");
295//! #         Default::default()
296//! #     }
297//! # }
298//! declare_introspection_queries!(Global::<MyLayer>);
299//! ```
300//!
301//! Sixth, build and check the exported symbol of the build artifacts, and we should see Vulkan
302//! introspection APIs are exported.
303//!
304//! On Windows, use
305//! [`dumpbin`](https://learn.microsoft.com/en-us/cpp/build/reference/dumpbin-reference?view=msvc-170)
306//! ```cmd
307//! $ dumpbin /exports .\target\debug\VkLayer_vendor_rust_example.dll
308//! Microsoft (R) COFF/PE Dumper Version 14.36.32537.0
309//! Copyright (C) Microsoft Corporation.  All rights reserved.
310//!
311//!
312//! Dump of file .\target\debug\VkLayer_vendor_rust_example.dll
313//!
314//! File Type: DLL
315//!
316//!   Section contains the following exports for VkLayer_vendor_rust_example.dll
317//!
318//!     00000000 characteristics
319//!     FFFFFFFF time date stamp
320//!         0.00 version
321//!            1 ordinal base
322//!            6 number of functions
323//!            6 number of names
324//!
325//!     ordinal hint RVA      name
326//!
327//!           1    0 000F1840 vkEnumerateDeviceExtensionProperties = vkEnumerateDeviceExtensionProperties
328//!           2    1 000F1820 vkEnumerateDeviceLayerProperties = vkEnumerateDeviceLayerProperties
329//!           3    2 000F1800 vkEnumerateInstanceExtensionProperties = vkEnumerateInstanceExtensionProperties
330//!           4    3 000F17E0 vkEnumerateInstanceLayerProperties = vkEnumerateInstanceLayerProperties
331//!           5    4 000F1890 vkGetDeviceProcAddr = vkGetDeviceProcAddr
332//!           6    5 000F1870 vkGetInstanceProcAddr = vkGetInstanceProcAddr
333//!
334//!   Summary
335//!
336//!         1000 .data
337//!        11000 .pdata
338//!        37000 .rdata
339//!         2000 .reloc
340//!       171000 .text
341//! ```
342//! On Linux, use [`objdump`](https://linux.die.net/man/1/objdump).
343//! ```bat
344//! $ objdump -TC target/debug/libVkLayer_vendor_rust_example.so
345//!
346//! target/debug/libVkLayer_vendor_rust_example.so:     file format elf64-x86-64
347//!
348//! DYNAMIC SYMBOL TABLE:
349//! (omit some irrelevant symbols...)
350//! 00000000000fad20 g    DF .text  0000000000000022  Base        vkEnumerateDeviceExtensionProperties
351//! 00000000000face0 g    DF .text  000000000000001c  Base        vkEnumerateInstanceExtensionProperties
352//! 00000000000fad50 g    DF .text  0000000000000018  Base        vkGetInstanceProcAddr
353//! 00000000000facc0 g    DF .text  0000000000000018  Base        vkEnumerateInstanceLayerProperties
354//! 00000000000fad00 g    DF .text  000000000000001c  Base        vkEnumerateDeviceLayerProperties
355//! 00000000000fad70 g    DF .text  0000000000000018  Base        vkGetDeviceProcAddr
356//! ```
357//!
358//! Seventh, create the layer manifest file named `rust_example_layer.json` right beside the built
359//! artifact. If targeting Android, the json manifest file is not needed.
360//!
361//! For Windows,
362//! ```json
363//! {
364//!     "file_format_version" : "1.2.1",
365//!     "layer": {
366//!         "name": "VK_LAYER_VENDOR_rust_example",
367//!         "type": "INSTANCE",
368//!         "library_path": ".\\VkLayer_vendor_rust_example.dll",
369//!         "library_arch" : "64",
370//!         "api_version" : "1.1.0",
371//!         "implementation_version" : "0",
372//!         "description" : "Rust test layer"
373//!     }
374//! }
375//! ```
376//!
377//! For Linux,
378//! ```json
379//! {
380//!     "file_format_version" : "1.2.1",
381//!     "layer": {
382//!         "name": "VK_LAYER_VENDOR_rust_example",
383//!         "type": "INSTANCE",
384//!         "library_path": "./libVkLayer_vendor_rust_example.so",
385//!         "library_arch" : "64",
386//!         "api_version" : "1.1.0",
387//!         "implementation_version" : "0",
388//!         "description" : "Rust test layer"
389//!     }
390//! }
391//! ```
392//! This json file will define an explicit layer named `VK_LAYER_VENDOR_rust_example`.
393//!
394//! Eighth, use [`VkConfig`](https://github.com/LunarG/VulkanTools/blob/main/vkconfig/README.md)
395//! (i.e. Vulkan Configurator) to force enable this explicit layer, and launch the vkcube
396//! application through `VkConfig`. In the log view, we should see the
397//! `"Hello from the Rust Vulkan layer!"` log line. For Android, follow
398//! [this instruction](https://developer.android.com/ndk/guides/graphics/validation-layer) to enable
399//! the layer. [`println!`] won't write to logcat, so one needs to change the layer implementation
400//! to write to the logcat.
401//!
402//! # Global initialization and clean up
403//!
404//! See [the][Layer#initialization] [document][Layer#global-clean-up] of [`Layer`] for details.
405//!
406//! # Extensions
407//!
408//! TODO
409//!
410//! # Synchronization
411//!
412//! TODO
413//!
414//! # Safety
415//!
416//! TODO
417//!
418//! # API stability
419//!
420//! TODO
421
422use ash::vk::{self, Handle};
423use bytemuck::cast_slice;
424use log::{error, warn};
425use std::{
426    borrow::Borrow,
427    collections::{BTreeMap, BTreeSet},
428    ffi::{c_char, c_void, CStr, CString},
429    ptr::{null, null_mut, NonNull},
430    sync::{Arc, Mutex},
431};
432extern crate self as vulkan_layer;
433
434mod bindings;
435mod global_simple_intercept;
436mod layer_trait;
437mod lazy_collection;
438#[cfg(any(feature = "_test", test))]
439pub mod test_utils;
440
441#[cfg(feature = "unstable")]
442pub mod unstable_api;
443#[cfg(not(feature = "unstable"))]
444mod unstable_api;
445mod vk_utils;
446
447use bindings::vk_layer::{VkLayerDeviceCreateInfo, VkLayerFunction, VkLayerInstanceCreateInfo};
448pub use bindings::vk_layer::{VkLayerDeviceLink, VkLayerInstanceLink};
449pub use global_simple_intercept::Extension;
450use global_simple_intercept::{DeviceDispatchTable, InstanceDispatchTable, VulkanCommand};
451pub use layer_trait::{
452    DeviceHooks, DeviceInfo, ExtensionProperties, GlobalHooks, GlobalHooksInfo, InstanceHooks,
453    InstanceInfo, Layer, LayerManifest, LayerResult, VulkanCommand as LayerVulkanCommand,
454};
455use unstable_api::{ApiVersion, IsCommandEnabled, LazyCollection};
456pub use vk_utils::{fill_vk_out_array, VulkanBaseInStructChain, VulkanBaseOutStructChain};
457use vk_utils::{ptr_as_uninit_mut, slice_from_raw_parts, slice_to_owned_strings};
458pub use vulkan_layer_macros::{
459    auto_deviceinfo_impl, auto_globalhooksinfo_impl, auto_instanceinfo_impl,
460    declare_introspection_queries,
461};
462
463trait DispatchableObject: vk::Handle + Copy {
464    type DispatchKey: From<usize>;
465
466    fn get_dispatch_key(&self) -> Self::DispatchKey {
467        assert_eq!(
468            std::mem::size_of::<Self>(),
469            std::mem::size_of::<*const *const c_void>()
470        );
471        // Safe, because all dispatchable objects can be cast to void **. See details at
472        // https://github.com/KhronosGroup/Vulkan-Loader/blob/35b005a5792f6e4c2931d62a37324923f1a71c79/docs/LoaderDriverInterface.md#driver-dispatchable-object-creation.
473        let key = unsafe {
474            // We use transmute instead of Handle::as_raw here to avoid integer to pointer cast, and
475            // allow the miri tests with tree borrows to work with this test. See
476            // https://github.com/ash-rs/ash/issues/996 for details.
477            let dispatch_table_ptr = std::mem::transmute_copy::<Self, *const *const c_void>(self);
478            std::ptr::read(dispatch_table_ptr)
479        };
480        (key as usize).into()
481    }
482}
483
484#[repr(transparent)]
485#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
486struct InstanceDispatchKey(usize);
487
488impl From<usize> for InstanceDispatchKey {
489    fn from(value: usize) -> Self {
490        Self(value)
491    }
492}
493
494impl DispatchableObject for vk::Instance {
495    type DispatchKey = InstanceDispatchKey;
496}
497
498impl DispatchableObject for vk::PhysicalDevice {
499    type DispatchKey = InstanceDispatchKey;
500}
501
502#[repr(transparent)]
503#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
504struct DeviceDispatchKey(usize);
505
506impl From<usize> for DeviceDispatchKey {
507    fn from(value: usize) -> Self {
508        Self(value)
509    }
510}
511
512impl DispatchableObject for vk::Device {
513    type DispatchKey = DeviceDispatchKey;
514}
515
516impl DispatchableObject for vk::CommandBuffer {
517    type DispatchKey = DeviceDispatchKey;
518}
519
520impl DispatchableObject for vk::Queue {
521    type DispatchKey = DeviceDispatchKey;
522}
523
524struct InstanceInfoWrapper<T: Layer> {
525    get_instance_proc_addr: vk::PFN_vkGetInstanceProcAddr,
526    dispatch_table: InstanceDispatchTable,
527    api_version: ApiVersion,
528    enabled_extensions: BTreeSet<Extension>,
529    // instance_commands and device_commands are recalculated on every vkCreateInstance, so that
530    // the layer can decide which commands to intercept dynamically.
531    instance_commands: Box<[VulkanCommand]>,
532    device_commands: Box<[VulkanCommand]>,
533    is_create_device_hooked: bool,
534    customized_info: T::InstanceInfoContainer,
535}
536
537struct PhysicalDeviceInfoWrapper {
538    owner_instance: vk::Instance,
539    properties: vk::PhysicalDeviceProperties,
540}
541
542struct DeviceInfoWrapper<T: Layer> {
543    dispatch_table: DeviceDispatchTable,
544    get_device_proc_addr: vk::PFN_vkGetDeviceProcAddr,
545    api_version: ApiVersion,
546    enabled_extensions: BTreeSet<Extension>,
547    // device_commands are recalculated on every vkCreateDevice, so that the layer can decide
548    // which commands to intercept dynamically.
549    device_commands: Box<[VulkanCommand]>,
550    customized_info: T::DeviceInfoContainer,
551}
552
553/// A struct that implements all necessarily functions for a layer given a type that implements
554/// [`Layer`].
555///
556/// The layer implementation should use [`Global::default`] to construct a [`Global`] object.
557/// When all `VkInstance`s and `VkDevice`s are destroyed, the drop of [`Global`] is no-op if the
558/// drop of `T` is no-op under such circumstances. See [the][Layer#initialization]
559/// [document][Layer#global-clean-up] of [`Layer`] for details on global initialization and
560/// clean-up.
561///
562/// This is supposed to be a global singleton for a layer to store all necessary data to implement a
563/// Vulkan layer including dispatch tables, maps between the Vulkan objects and their wrappers, etc.
564pub struct Global<T: Layer> {
565    instance_map: Mutex<LazyCollection<BTreeMap<InstanceDispatchKey, Arc<InstanceInfoWrapper<T>>>>>,
566    physical_device_map:
567        Mutex<LazyCollection<BTreeMap<vk::PhysicalDevice, Arc<PhysicalDeviceInfoWrapper>>>>,
568    device_map: Mutex<LazyCollection<BTreeMap<DeviceDispatchKey, Arc<DeviceInfoWrapper<T>>>>>,
569    /// Access to the underlying `T`.
570    // layer_info can't be lazily constructed when the first VkInstance is created, because we want
571    // to guarantee that T::default is only called once during the lifetime of Global, so that the
572    // client can put global state in T, and can perform global initialization(e.g. the global
573    // logger) in T::default. If the client needs Global to be trivially destructible, the client
574    // must guarantee that T is also trivially destructible.
575    pub layer_info: T,
576    get_instance_addr_proc_hooked: bool,
577}
578
579impl<T: Layer> Global<T> {
580    fn instance() -> impl std::ops::Deref<Target = Self> + 'static {
581        T::global_instance()
582    }
583
584    fn get_instance_info(
585        &self,
586        instance: impl DispatchableObject<DispatchKey = InstanceDispatchKey>,
587    ) -> Option<Arc<InstanceInfoWrapper<T>>> {
588        self.instance_map
589            .lock()
590            .unwrap()
591            .get()
592            .get(&instance.get_dispatch_key())
593            .map(Arc::clone)
594    }
595
596    fn create_physical_device_infos<U: Borrow<vk::PhysicalDevice>>(
597        &self,
598        instance: vk::Instance,
599        physical_devices: impl IntoIterator<Item = U>,
600    ) {
601        for physical_device in physical_devices {
602            let mut physical_device_map = self.physical_device_map.lock().unwrap();
603            let mut physical_device_map = physical_device_map.get_mut_or_default();
604            if let Some(physical_device_info) = physical_device_map.get(physical_device.borrow()) {
605                assert_eq!(physical_device_info.owner_instance, instance);
606                continue;
607            }
608            let instance_info = self.get_instance_info(instance).unwrap_or_else(|| {
609                panic!("Unknown VkInstance handle: {:#018x}", instance.as_raw())
610            });
611            let properties = unsafe {
612                instance_info
613                    .dispatch_table
614                    .core
615                    .get_physical_device_properties(*physical_device.borrow())
616            };
617            physical_device_map.insert(
618                *physical_device.borrow(),
619                Arc::new(PhysicalDeviceInfoWrapper {
620                    owner_instance: instance,
621                    properties,
622                }),
623            );
624        }
625    }
626
627    fn get_device_info(
628        &self,
629        device: impl DispatchableObject<DispatchKey = DeviceDispatchKey>,
630    ) -> Option<Arc<DeviceInfoWrapper<T>>> {
631        self.device_map
632            .lock()
633            .unwrap()
634            .get()
635            .get(&device.get_dispatch_key())
636            .map(Arc::clone)
637    }
638
639    fn get_physical_info(
640        &self,
641        physical_device: vk::PhysicalDevice,
642    ) -> Option<Arc<PhysicalDeviceInfoWrapper>> {
643        self.physical_device_map
644            .lock()
645            .unwrap()
646            .get()
647            .get(&physical_device)
648            .map(Arc::clone)
649    }
650
651    extern "system" fn create_instance(
652        create_info: *const vk::InstanceCreateInfo,
653        allocator: *const vk::AllocationCallbacks,
654        p_instance: *mut vk::Instance,
655    ) -> vk::Result {
656        let p_next_chain = unsafe { create_info.as_ref() }
657            .map(|create_info| create_info.p_next as *mut vk::BaseOutStructure)
658            .unwrap_or(null_mut());
659        let mut p_next_chain: VulkanBaseOutStructChain = unsafe { p_next_chain.as_mut() }.into();
660        let layer_create_info = p_next_chain.find_map(|out_struct| {
661            let out_struct = out_struct as *mut vk::BaseOutStructure;
662            let layer_create_info = unsafe {
663                ash::match_out_struct!(match out_struct {
664                    out_struct @ VkLayerInstanceCreateInfo => {
665                        out_struct
666                    }
667                    _ => {
668                        return None;
669                    }
670                })
671            };
672            if layer_create_info.function == VkLayerFunction::VK_LAYER_LINK_INFO {
673                Some(layer_create_info)
674            } else {
675                None
676            }
677        });
678        let layer_create_info = match layer_create_info {
679            Some(layer_create_info) => layer_create_info,
680            None => {
681                error!("failed to find the VkLayerInstanceCreateInfo struct in the chain.");
682                return vk::Result::ERROR_INITIALIZATION_FAILED;
683            }
684        };
685        let layer_info = unsafe { layer_create_info.u.pLayerInfo.as_ref() }.unwrap();
686        layer_create_info.u.pLayerInfo = layer_info.pNext;
687
688        let get_instance_proc_addr: vk::PFN_vkGetInstanceProcAddr =
689            layer_info.pfnNextGetInstanceProcAddr;
690
691        let global = Self::instance();
692        let hooked =
693            T::GlobalHooksInfo::hooked_commands().contains(&LayerVulkanCommand::CreateInstance);
694        let layer_result = if hooked {
695            global.layer_info.global_hooks().create_instance(
696                unsafe { create_info.as_ref() }.unwrap(),
697                layer_info,
698                unsafe { allocator.as_ref() },
699                p_instance,
700            )
701        } else {
702            LayerResult::Unhandled
703        };
704
705        match layer_result {
706            LayerResult::Handled(Ok(())) => {}
707            LayerResult::Handled(Err(e)) => return e,
708            LayerResult::Unhandled => {
709                let get_proc_addr = |name: &CStr| -> *const c_void {
710                    let fp = unsafe { get_instance_proc_addr(vk::Instance::null(), name.as_ptr()) };
711                    if let Some(fp) = fp {
712                        return fp as *const _;
713                    }
714                    std::ptr::null()
715                };
716                let entry = vk::EntryFnV1_0::load(get_proc_addr);
717
718                let ret: vk::Result =
719                    unsafe { (entry.create_instance)(create_info, allocator, p_instance) };
720                if !matches!(ret, vk::Result::SUCCESS) {
721                    return ret;
722                }
723            }
724        };
725
726        let instance = *unsafe { p_instance.as_ref() }.unwrap();
727        let ash_instance = unsafe {
728            ash::Instance::load(
729                &ash::vk::StaticFn {
730                    get_instance_proc_addr,
731                },
732                instance,
733            )
734        };
735
736        let create_info = unsafe { create_info.as_ref() }.unwrap();
737        let enabled_extensions = unsafe {
738            slice_from_raw_parts(
739                create_info.pp_enabled_extension_names,
740                create_info.enabled_extension_count,
741            )
742        };
743        let enabled_extensions = unsafe { slice_to_owned_strings(enabled_extensions) }
744            .filter_map(|extension_name| match extension_name.as_str().try_into() {
745                Ok(extension) => Some(extension),
746                Err(e) => {
747                    warn!(
748                        "Failed to recognize the extension {}: {}",
749                        extension_name, e
750                    );
751                    None
752                }
753            })
754            .collect();
755        let api_version = unsafe { create_info.p_application_info.as_ref() }
756            .map(|app_info| app_info.api_version)
757            .unwrap_or(0);
758        let api_version = if api_version == 0 {
759            ApiVersion { major: 1, minor: 0 }
760        } else {
761            api_version.into()
762        };
763        let ash_instance = Arc::new(ash_instance);
764        let customized_info = global.layer_info.create_instance_info(
765            create_info,
766            unsafe { allocator.as_ref() },
767            Arc::clone(&ash_instance),
768            get_instance_proc_addr,
769        );
770        let is_create_device_hooked = global
771            .layer_info
772            .hooked_instance_commands(customized_info.borrow())
773            .any(|command| command == LayerVulkanCommand::CreateDevice);
774        let instance_commands = global.create_instance_commands(customized_info.borrow());
775        let device_commands = global.create_device_commands(customized_info.borrow(), None);
776        {
777            let key = instance.get_dispatch_key();
778            let mut instance_map = global.instance_map.lock().unwrap();
779            let mut instance_map = instance_map.get_mut_or_default();
780            if instance_map.contains_key(&key) {
781                error!(
782                    "duplicate instances: instance {:?} already exists",
783                    instance
784                );
785                return vk::Result::ERROR_INITIALIZATION_FAILED;
786            }
787            instance_map.insert(
788                key,
789                Arc::new(InstanceInfoWrapper {
790                    get_instance_proc_addr,
791                    dispatch_table: InstanceDispatchTable::load(
792                        get_instance_proc_addr,
793                        ash_instance,
794                    ),
795                    api_version,
796                    enabled_extensions,
797                    instance_commands,
798                    device_commands,
799                    is_create_device_hooked,
800                    customized_info,
801                }),
802            );
803        }
804        vk::Result::SUCCESS
805    }
806
807    extern "system" fn destroy_instance(
808        instance: vk::Instance,
809        allocator: *const vk::AllocationCallbacks,
810    ) {
811        if instance == vk::Instance::null() {
812            return;
813        }
814        let dispatch_key = instance.get_dispatch_key();
815        let global = Self::instance();
816        let instance_info = global
817            .instance_map
818            .lock()
819            .unwrap()
820            .get_mut_or_default()
821            .remove(&dispatch_key);
822        let instance_info = instance_info.unwrap();
823        let instance_info = match Arc::try_unwrap(instance_info) {
824            Ok(instance_info) => instance_info,
825            Err(_) => panic!(
826                "This should be the sole instance dispatch table reference for instance {:?}.",
827                instance
828            ),
829        };
830        global
831            .physical_device_map
832            .lock()
833            .unwrap()
834            .get_mut_or_default()
835            .retain(|_, physical_device_info| physical_device_info.owner_instance != instance);
836        unsafe {
837            (instance_info.dispatch_table.core.fp_v1_0().destroy_instance)(instance, allocator)
838        };
839    }
840
841    extern "system" fn enumerate_physical_devices(
842        instance: vk::Instance,
843        p_physical_device_count: *mut u32,
844        p_physical_devices: *mut vk::PhysicalDevice,
845    ) -> vk::Result {
846        let global = Self::instance();
847        let ash_instance = &global
848            .get_instance_info(instance)
849            .expect("Must be a valid VkInstance")
850            .dispatch_table
851            .core;
852        let res = unsafe {
853            (ash_instance.fp_v1_0().enumerate_physical_devices)(
854                instance,
855                p_physical_device_count,
856                p_physical_devices,
857            )
858        };
859        if (res == vk::Result::SUCCESS || res == vk::Result::INCOMPLETE)
860            && !p_physical_devices.is_null()
861        {
862            let physical_device_count = unsafe { p_physical_device_count.as_ref() }.unwrap();
863            let physical_devices =
864                unsafe { slice_from_raw_parts(p_physical_devices, *physical_device_count) };
865            global.create_physical_device_infos(instance, physical_devices);
866        }
867        res
868    }
869
870    extern "system" fn enumerate_physical_device_groups(
871        instance: vk::Instance,
872        p_physical_device_group_count: *mut u32,
873        p_physical_device_group_properties: *mut vk::PhysicalDeviceGroupProperties,
874    ) -> vk::Result {
875        let global = Self::instance();
876        let ash_instance = &global
877            .get_instance_info(instance)
878            .expect("Must be a valid VkInstance")
879            .dispatch_table
880            .core;
881        let res = unsafe {
882            (ash_instance.fp_v1_1().enumerate_physical_device_groups)(
883                instance,
884                p_physical_device_group_count,
885                p_physical_device_group_properties,
886            )
887        };
888        if (res == vk::Result::SUCCESS || res == vk::Result::INCOMPLETE)
889            && !p_physical_device_group_count.is_null()
890        {
891            let physical_device_group_count =
892                *unsafe { p_physical_device_group_count.as_ref() }.unwrap();
893            let physical_device_groups = unsafe {
894                slice_from_raw_parts(
895                    p_physical_device_group_properties,
896                    physical_device_group_count,
897                )
898            };
899            let physical_devices =
900                physical_device_groups
901                    .iter()
902                    .flat_map(|physical_device_group| {
903                        &physical_device_group.physical_devices[..physical_device_group
904                            .physical_device_count
905                            .try_into()
906                            .unwrap()]
907                    });
908            global.create_physical_device_infos(instance, physical_devices);
909        }
910        res
911    }
912
913    extern "system" fn create_device(
914        physical_device: vk::PhysicalDevice,
915        create_info: *const vk::DeviceCreateInfo,
916        p_allocator: *const vk::AllocationCallbacks,
917        p_device: *mut vk::Device,
918    ) -> vk::Result {
919        let create_info = unsafe { create_info.as_ref() }.unwrap();
920        let mut p_next_chain: VulkanBaseOutStructChain =
921            unsafe { (create_info.p_next as *mut vk::BaseOutStructure).as_mut() }.into();
922        let layer_create_info = p_next_chain.find_map(|out_struct| {
923            let out_struct = out_struct as *mut vk::BaseOutStructure;
924            let layer_create_info = unsafe {
925                ash::match_out_struct!(match out_struct {
926                    out_struct @ VkLayerDeviceCreateInfo => {
927                        out_struct
928                    }
929                    _ => {
930                        return None;
931                    }
932                })
933            };
934            if layer_create_info.function == VkLayerFunction::VK_LAYER_LINK_INFO {
935                Some(layer_create_info)
936            } else {
937                None
938            }
939        });
940        let layer_create_info = match layer_create_info {
941            Some(layer_create_info) => layer_create_info,
942            None => {
943                error!("failed to find the VkLayerDeviceCreateInfo struct in the chain.");
944                return vk::Result::ERROR_INITIALIZATION_FAILED;
945            }
946        };
947        let layer_link = unsafe { layer_create_info.u.pLayerInfo.as_ref() }.unwrap();
948        layer_create_info.u.pLayerInfo = layer_link.pNext;
949
950        let get_instance_proc_addr: vk::PFN_vkGetInstanceProcAddr =
951            layer_link.pfnNextGetInstanceProcAddr;
952        let get_device_proc_addr: vk::PFN_vkGetDeviceProcAddr = layer_link.pfnNextGetDeviceProcAddr;
953
954        let global = Self::instance();
955        let physical_device_info = global
956            .get_physical_info(physical_device)
957            .expect("physical device must be a valid VkPhysicalDevice.");
958        let instance_info = global
959            .get_instance_info(physical_device_info.owner_instance)
960            .expect("The owner instance of this physical device must be registered.");
961
962        let create_device_name = CString::new("vkCreateDevice").unwrap();
963        let next_create_device = unsafe {
964            get_instance_proc_addr(
965                instance_info.dispatch_table.core.handle(),
966                create_device_name.as_ptr(),
967            )
968        };
969        let next_create_device: vk::PFN_vkCreateDevice =
970            unsafe { std::mem::transmute(next_create_device) };
971        let mut create_info = *create_info;
972        let requested_extensions = unsafe {
973            slice_from_raw_parts(
974                create_info.pp_enabled_extension_names,
975                create_info.enabled_extension_count,
976            )
977        };
978        let requested_extensions =
979            unsafe { slice_to_owned_strings(requested_extensions) }.collect::<Vec<_>>();
980        let create_device_res = if instance_info.is_create_device_hooked {
981            instance_info
982                .customized_info
983                .borrow()
984                .hooks()
985                .create_device(
986                    physical_device,
987                    &create_info,
988                    layer_link,
989                    unsafe { p_allocator.as_ref() },
990                    unsafe { ptr_as_uninit_mut(p_device) }.unwrap(),
991                )
992        } else {
993            LayerResult::Unhandled
994        };
995        let layer_manifest = T::manifest();
996        match create_device_res {
997            LayerResult::Handled(Ok(())) => {}
998            LayerResult::Handled(Err(e)) => return e,
999            LayerResult::Unhandled => {
1000                let enabled_extensions = requested_extensions
1001                    .iter()
1002                    .filter_map(|extension_name| {
1003                        let extension_name_cstring = CString::new(extension_name.clone())
1004                            .unwrap_or_else(|e| {
1005                                panic!("Failed to create CString from {}: {}", extension_name, e)
1006                            });
1007                        let extension: Extension = match extension_name.as_str().try_into() {
1008                            Ok(extension) => extension,
1009                            Err(_) => return Some(extension_name_cstring),
1010                        };
1011                        if layer_manifest
1012                            .device_extensions
1013                            .iter()
1014                            .any(|layer_extension| layer_extension.name == extension)
1015                        {
1016                            None
1017                        } else {
1018                            Some(extension_name_cstring)
1019                        }
1020                    })
1021                    .collect::<Vec<_>>();
1022                let enabled_extensions = enabled_extensions
1023                    .iter()
1024                    .map(|extension_name| extension_name.as_ptr())
1025                    .collect::<Vec<_>>();
1026                create_info.enabled_extension_count = enabled_extensions.len().try_into().unwrap();
1027                create_info.pp_enabled_extension_names = if enabled_extensions.is_empty() {
1028                    null()
1029                } else {
1030                    enabled_extensions.as_ptr()
1031                };
1032                let res = unsafe {
1033                    next_create_device(physical_device, &create_info, p_allocator, p_device)
1034                };
1035                if res != vk::Result::SUCCESS {
1036                    return res;
1037                }
1038            }
1039        }
1040
1041        let device = *unsafe { p_device.as_ref() }.unwrap();
1042        let ash_instance = Arc::clone(&instance_info.dispatch_table.core);
1043        let ash_device = unsafe {
1044            // ash will also try to load instance-level dispatchable commands with
1045            // vkGetDeviceProcAddr. Although that won't end up with undefined behavior, and we
1046            // should always expect NULL returned according to the spec, we may see Android vulkan
1047            // loader complaining about internal vkGetDeviceProcAddr called for <function name>,
1048            // which should be benign.
1049            ash::Device::load(
1050                &vk::InstanceFnV1_0 {
1051                    get_device_proc_addr,
1052                    ..ash_instance.fp_v1_0().clone()
1053                },
1054                device,
1055            )
1056        };
1057        let ash_device = Arc::new(ash_device);
1058        let enabled_extensions = requested_extensions
1059            .iter()
1060            .filter_map(|name| {
1061                let extension_name: Option<Extension> = name.as_str().try_into().ok();
1062                extension_name
1063            })
1064            .collect::<BTreeSet<_>>();
1065        let api_version = instance_info
1066            .api_version
1067            .min(physical_device_info.properties.api_version.into());
1068        let customized_info = global.layer_info.create_device_info(
1069            physical_device,
1070            &create_info,
1071            unsafe { p_allocator.as_ref() },
1072            Arc::clone(&ash_device),
1073            get_device_proc_addr,
1074        );
1075        let device_commands = global.create_device_commands(
1076            instance_info.customized_info.borrow(),
1077            Some(customized_info.borrow()),
1078        );
1079        {
1080            let mut device_map = global.device_map.lock().unwrap();
1081            let mut device_map = device_map.get_mut_or_default();
1082            assert!(
1083                !device_map.contains_key(&device.get_dispatch_key()),
1084                "duplicate VkDevice: {:?}",
1085                device
1086            );
1087            device_map.insert(
1088                device.get_dispatch_key(),
1089                Arc::new(DeviceInfoWrapper {
1090                    dispatch_table: DeviceDispatchTable::load(get_device_proc_addr, ash_device),
1091                    get_device_proc_addr,
1092                    api_version,
1093                    enabled_extensions,
1094                    device_commands,
1095                    customized_info,
1096                }),
1097            );
1098        }
1099        vk::Result::SUCCESS
1100    }
1101
1102    extern "system" fn destroy_device(
1103        device: vk::Device,
1104        p_allocator: *const vk::AllocationCallbacks,
1105    ) {
1106        if device == vk::Device::null() {
1107            return;
1108        }
1109        let global = Self::instance();
1110        let device_info = match Arc::try_unwrap(
1111            global
1112                .device_map
1113                .lock()
1114                .unwrap()
1115                .get_mut_or_default()
1116                .remove(&device.get_dispatch_key())
1117                .expect("device must be registered"),
1118        ) {
1119            Ok(device_info) => device_info,
1120            Err(_) => panic!("This should be the sole owner of the device {:?}", device),
1121        };
1122        let allocation_callback = unsafe { p_allocator.as_ref() };
1123        unsafe {
1124            device_info
1125                .dispatch_table
1126                .core
1127                .destroy_device(allocation_callback)
1128        };
1129    }
1130
1131    fn layer_properties() -> Vec<vk::LayerProperties> {
1132        let layer_manifest = T::manifest();
1133        // layer_name and description will be set later
1134        let mut layer_property: vk::LayerProperties = vk::LayerProperties::builder()
1135            .spec_version(layer_manifest.spec_version)
1136            .implementation_version(layer_manifest.implementation_version)
1137            .build();
1138        assert!(layer_manifest.name.len() < vk::MAX_EXTENSION_NAME_SIZE);
1139        let layer_name = CString::new(layer_manifest.name).unwrap();
1140        let layer_name = cast_slice(layer_name.as_bytes());
1141        layer_property.layer_name[..layer_name.len()].copy_from_slice(layer_name);
1142
1143        let layer_description = CString::new(layer_manifest.description).unwrap();
1144        let layer_description = cast_slice(layer_description.as_bytes());
1145        assert!(layer_description.len() < vk::MAX_DESCRIPTION_SIZE);
1146        layer_property.description[..layer_description.len()].copy_from_slice(layer_description);
1147        vec![layer_property]
1148    }
1149
1150    // Introspection queries required by Android, so we need to expose them as public functions so
1151    // that the user can further expose them as functions exported by the dynamic link library.
1152
1153    /// The `vkEnumerateInstanceLayerProperties` entry point provided by the layer framework.
1154    ///
1155    /// The return value is decided by [`Layer::manifest`]. Only enumerate the layer itself
1156    /// according to the
1157    /// [layer rule](<https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderLayerInterface.md#:~:text=vkEnumerateInstanceLayerProperties%20must%20enumerate%20and%20only%20enumerate%20the%20layer%20itself.>).
1158    ///
1159    /// # Safety
1160    /// See valid usage of `vkEnumerateInstanceLayerProperties` at
1161    /// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkEnumerateInstanceLayerProperties.html>.
1162    #[deny(unsafe_op_in_unsafe_fn)]
1163    pub unsafe extern "system" fn enumerate_instance_layer_properties(
1164        property_count: *mut u32,
1165        properties: *mut vk::LayerProperties,
1166    ) -> vk::Result {
1167        let ret_properties = Self::layer_properties();
1168        // Safe, because the caller guarantees that `property_count` is a valid pointer to u32, and
1169        // if the value referenced by property_count is not 0, and properties is not NULL,
1170        // properties must be a valid pointer to an array of property_count vk::LayerProperties
1171        // structures. See details in
1172        // VUID-vkEnumerateInstanceLayerProperties-pPropertyCount-parameter and
1173        // VUID-vkEnumerateInstanceLayerProperties-pProperties-parameter.
1174        unsafe {
1175            fill_vk_out_array(
1176                &ret_properties,
1177                NonNull::new(property_count).unwrap(),
1178                properties,
1179            )
1180        }
1181    }
1182
1183    /// The `vkEnumerateInstanceExtensionProperties` entry point provided by the layer framework.
1184    ///
1185    /// Returns an empty list with `VK_SUCCESS` if the layer name matches; returns
1186    /// `VK_ERROR_LAYER_NOT_PRESENT` with all the out pointers untouched, according to the
1187    /// [`LLP_LAYER_15`](<https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderLayerInterface.md#:~:text=LLP_LAYER_15,Conventions%20and%20Rules>)
1188    /// rule.
1189    ///
1190    /// # Safety
1191    /// See valid usage of `vkEnumerateInstanceExtensionProperties` at
1192    /// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkEnumerateInstanceExtensionProperties.html>.
1193    #[deny(unsafe_op_in_unsafe_fn)]
1194    pub unsafe extern "system" fn enumerate_instance_extension_properties(
1195        layer_name: *const c_char,
1196        property_count: *mut u32,
1197        _: *mut vk::ExtensionProperties,
1198    ) -> vk::Result {
1199        if !layer_name.is_null() {
1200            let layer_name = unsafe { CStr::from_ptr(layer_name) }
1201                .to_str()
1202                .expect(concat!(
1203                    "According to \
1204                     VUID-vkEnumerateInstanceExtensionProperties-pLayerName-parameter, ",
1205                    "if p_layer_name is not NULL, p_layer_name must be a null-terminated UTF-8 \
1206                     string."
1207                ));
1208            if layer_name == T::manifest().name {
1209                // Safe, because the caller guarantees that if the passed in `property_count` is not
1210                // null, it's a valid pointer to u32.
1211                if let Some(property_count) = unsafe { property_count.as_mut() } {
1212                    *property_count = 0;
1213                }
1214                return vk::Result::SUCCESS;
1215            }
1216        }
1217        vk::Result::ERROR_LAYER_NOT_PRESENT
1218    }
1219
1220    /// The `vkEnumerateDeviceLayerProperties` entry point provided by the layer framework.
1221    ///
1222    /// The return value is decided by [`Layer::manifest`]. Only enumerate the layer itself
1223    /// according to the
1224    /// [layer interface version 0 requirement](<https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderLayerInterface.md#:~:text=vkEnumerateDeviceLayerProperties%20enumerates%20a,device%20function%20interception.>).
1225    /// Note that although this function
1226    /// [is deprecated](<https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderLayerInterface.md#:~:text=vkEnumerateDeviceLayerProperties%20is%20deprecated,in%20undefined%20behavior.>)
1227    /// in the Khronos Vulkan loader, it is still used in the Android Vulkan loader.
1228    ///
1229    /// # Safety
1230    /// See valid usage of `vkEnumerateDeviceLayerProperties` at
1231    /// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkEnumerateDeviceLayerProperties.html>.
1232    #[deny(unsafe_op_in_unsafe_fn)]
1233    pub unsafe extern "system" fn enumerate_device_layer_properties(
1234        _: vk::PhysicalDevice,
1235        p_property_count: *mut u32,
1236        p_properties: *mut vk::LayerProperties,
1237    ) -> vk::Result {
1238        // TODO: call into the customized instance data with the vk::PhysicalDevice to provide more
1239        // flexibility.
1240        let ret_properties = Self::layer_properties();
1241        // Safe, because the caller guarantees that `p_property_count` is a valid pointer to u32,
1242        // and if the value referenced by `p_property_count` is not 0, and `p_properties` is not
1243        // NULL, `p_properties` must be a valid pointer to an array of `p_property_count`
1244        // vk::LayerProperties structures. See details in
1245        // VUID-vkEnumerateDeviceLayerProperties-pPropertyCount-parameter
1246        // and VUID-vkEnumerateDeviceLayerProperties-pProperties-parameter.
1247        unsafe {
1248            fill_vk_out_array(
1249                &ret_properties,
1250                NonNull::new(p_property_count).expect(concat!(
1251                    "p_property_count must be a valid pointer to u32 according to ",
1252                    "VUID-vkEnumerateDeviceLayerProperties-pPropertyCount-parameter"
1253                )),
1254                p_properties,
1255            )
1256        }
1257    }
1258
1259    /// The `vkEnumerateDeviceExtensionProperties` entry point provided by the layer framework.
1260    ///
1261    /// The return value is decided by [`Layer::manifest`] where `p_layer_name` is itself. If the
1262    /// `p_layer_name` doesn't match, the behavior depends on whether the `physical_device` argument
1263    /// is `VK_NULL_HANDLE` or not: if `physical_device` argument is `VK_NULL_HANDLE`,
1264    /// `VK_ERROR_LAYER_NOT_PRESENT` is returned; if `physical_device` is not `VK_NULL_HANDLE`, the
1265    /// layer framework calls into the next layer of the call chain. This behavior is defined by the [layer interface version 0](<https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderLayerInterface.md#:~:text=vkEnumerateDeviceExtensionProperties%20enumerates%20device,function%20never%20fails.>)
1266    /// and
1267    /// [`LLP_LAYER_16`](<https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderLayerInterface.md#:~:text=LLP_LAYER_16,Conventions%20and%20Rules>).
1268    ///
1269    /// # Safety
1270    /// See valid usage of `vkEnumerateDeviceExtensionProperties` at
1271    /// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkEnumerateDeviceExtensionProperties.html>.
1272    /// A NULL `VkPhysicalDevice` can also be used to call this interface, according to the
1273    /// [layer interface version 0](<https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderLayerInterface.md#:~:text=vkEnumerateDeviceExtensionProperties%20enumerates%20device,function%20never%20fails.>).
1274    #[deny(unsafe_op_in_unsafe_fn)]
1275    pub unsafe extern "system" fn enumerate_device_extension_properties(
1276        physical_device: vk::PhysicalDevice,
1277        p_layer_name: *const c_char,
1278        p_property_count: *mut u32,
1279        p_properties: *mut vk::ExtensionProperties,
1280    ) -> vk::Result {
1281        let layer_manifest = T::manifest();
1282        let is_this_layer = if p_layer_name.is_null() {
1283            false
1284        } else {
1285            unsafe { CStr::from_ptr(p_layer_name) }
1286                .to_str()
1287                .map(|layer_name| layer_name == layer_manifest.name)
1288                .unwrap_or(false)
1289        };
1290        if !is_this_layer {
1291            if physical_device == vk::PhysicalDevice::null() {
1292                return vk::Result::ERROR_LAYER_NOT_PRESENT;
1293            }
1294            let global = Self::instance();
1295            let physical_device_info = global
1296                .get_physical_info(physical_device)
1297                .expect("physical device must be a valid VkPhysicalDevice.");
1298            let instance_info = global
1299                .get_instance_info(physical_device_info.owner_instance)
1300                .expect("The owner instance of this physical device must be registered.");
1301            return unsafe {
1302                (instance_info
1303                    .dispatch_table
1304                    .core
1305                    .fp_v1_0()
1306                    .enumerate_device_extension_properties)(
1307                    physical_device,
1308                    p_layer_name,
1309                    p_property_count,
1310                    p_properties,
1311                )
1312            };
1313        }
1314        let property_count = NonNull::new(p_property_count).expect(concat!(
1315            "`p_property_count` must be a valid pointer to u32 according to ",
1316            "VUID-vkEnumerateDeviceExtensionProperties-pPropertyCount-parameter."
1317        ));
1318        // Safe because the caller guarantees `p_property_count` is a valid pointer to u32 according
1319        // to VUID-vkEnumerateDeviceExtensionProperties-pPropertyCount-parameter, and if
1320        // `p_property_count` doesn't point to 0, p_properties is either NULL or a valid pointer to
1321        // an array of `p_property_count` `vk::ExtensionProperties` structures according to
1322        // VUID-vkEnumerateDeviceExtensionProperties-pProperties-parameter.
1323        let device_extensions = layer_manifest
1324            .device_extensions
1325            .iter()
1326            .cloned()
1327            .map(Into::<vk::ExtensionProperties>::into)
1328            .collect::<Vec<_>>();
1329        unsafe { fill_vk_out_array(&device_extensions, property_count, p_properties) }
1330    }
1331
1332    /// The `vkGetInstanceProcAddr` entry point provided by the layer framework.
1333    ///
1334    /// The layer framework will make use of [`Layer::hooked_instance_commands`] and
1335    /// [`Layer::hooked_device_commands`] (with the `device_info` argument being [`None`]) to decide
1336    /// whether a local function pointer should be returned or should just passthrough to the next
1337    /// layer in the call chain. Note that the layer implementation doesn't have to take care of
1338    /// whether the command is enabled/available, and only needs to use the interfaces to express
1339    /// the intent. The layer framework should handle most of the logic(e.g. the requirement of
1340    /// [`LLP_LAYER_18`](<https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderLayerInterface.md#pre-instance-functions:~:text=LLP_LAYER_18,Conventions%20and%20Rules>))
1341    /// here unless the layer implementation decides to intercept `vkGetInstanceProcAddr`.
1342    ///
1343    /// The actual behavior is defined as follow:
1344    ///
1345    /// 1. If the `instance` parameter is `VK_NULL_HANDLE`, returns the local function pointers of
1346    ///    the current layer for all
1347    ///    [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.)
1348    ///    and `vkGetInstanceProcAddr`; `NULL` is returned for other Vulkan commands. One culprit
1349    ///    for now is that currently the layer framework always returns `NULL` for
1350    ///    `vkEnumerateInstanceVersion`, but that should be Ok, because the layer framework
1351    ///    currently doesn't allow to intercept pre-instance functions anyway, so this function
1352    ///    should be completely handled by the Vulkan loader.
1353    ///
1354    /// 1. If instance is not null, the situation is more complicated:
1355    ///
1356    ///    If the layer implementation intercepts the `vkGetInstanceProcAddr` function(i.e.
1357    ///    [`Layer::hooked_instance_commands`] returns `vkGetInstanceProcAddr`), the layer framework
1358    ///    just calls [`InstanceHooks::get_instance_proc_addr`] and return the return value if
1359    ///    [`LayerResult::Handled`] is returned.
1360    ///
1361    ///    Otherwise, the layer framework handles most of the logic according to
1362    ///    [`Layer::hooked_instance_commands`] and [`Layer::hooked_device_commands`]:
1363    ///
1364    ///     1. For dispatchable commands that are introspection queries:
1365    ///        `vkEnumerateDeviceExtensionProperties`, `vkEnumerateDeviceLayerProperties`, always
1366    ///        returns a local function pointer of the current layer.
1367    ///
1368    ///     1. For `vkDestroyInstance`, `vkCreateDevice`, `vkDestroyDevice`, always returns a local
1369    ///        function pointer. The layer framework needs to intercept those functions to build the
1370    ///        dispatch table and perform instance/device specific initialization. Note that
1371    ///        `vkCreateInstance` is a
1372    ///        [global command](<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.>),
1373    ///        so `NULL` will be returned if the `instance` argument is not `VK_NULL_HANDLE`.
1374    ///
1375    ///     1. For `vkEnumeratePhysicalDevices`, `vkEnumeratePhysicalDeviceGroups`, always returns a
1376    ///        local function pointer. The layer framework needs to intercept those functions to
1377    ///        build a map between `VkPhysicalDevice` and `VkInstance` so that the layer framework
1378    ///        can find the correspondent `VkInstance` in `vkCreateDevice`.
1379    ///
1380    ///     1. For other core dispatchable commands and enabled instance extension dispatchable
1381    ///        commands, if the layer implementation decides to intercept(according to
1382    ///        [`Layer::hooked_instance_commands`] and [`Layer::hooked_device_commands`]), returns a
1383    ///        local function pointer, otherwise returns the function pointer from the next layer.
1384    ///
1385    ///     1. For disabled instance extension dispatchable commands(either because the API version
1386    ///        is not high enough or the extension is not enabled), directly call to the next layer
1387    ///        regardless of whether the layer implementation wants to intercept the command or not,
1388    ///        and rely on the next layer to return `NULL`.
1389    ///
1390    ///     1. For device extension dispatchable commands directly supported by the layer
1391    ///        implementation according to [`LayerManifest::device_extensions`], always returns a
1392    ///        local function pointer if the layer implementation intercepts the command. Otherwise,
1393    ///        returns the function pointer of the next layer.
1394    ///
1395    ///     1. For device extension dispatchable commands not directly supported by the layer(i.e.
1396    ///        the extension doesn't appear in the [`LayerManifest::device_extensions`] of the
1397    ///        current layer):
1398    ///
1399    ///        * If the next layer returns `NULL`, `NULL` is always returned regardless of whether
1400    ///          the layer implementation wants to intercept the command. This indicates that this
1401    ///          device extension dispathcable command is not available for the instance.
1402    ///
1403    ///        * if the next layer returns non-NULL, a local function pointer is returned if the
1404    ///          layer implementation wants to intercept the command, otherwise returns the function
1405    ///          pointer of the next layer This indicates that this device extension dispathcable
1406    ///          command is available for the instance.
1407    ///
1408    ///     1. Passthrough to the next layer if the command is not recognized.
1409    ///
1410    /// # Safety
1411    /// See valid usage of `vkGetInstanceProcAddr` at
1412    /// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetInstanceProcAddr.html>.
1413    #[deny(unsafe_op_in_unsafe_fn)]
1414    pub unsafe extern "system" fn get_instance_proc_addr(
1415        instance: vk::Instance,
1416        p_name: *const c_char,
1417    ) -> vk::PFN_vkVoidFunction {
1418        // Make sure Global is initialized.
1419        let global = Self::instance();
1420        // Safe because the caller is expected to pass in a C string in the name parameter. See
1421        // VUID-vkGetInstanceProcAddr-pName-parameter.
1422        let name = unsafe { CStr::from_ptr(p_name) };
1423        let name = name.to_str().expect(concat!(
1424            "According to VUID-vkGetInstanceProcAddr-pName-parameter, p_name must be a null-",
1425            "terminated UTF-8 string."
1426        ));
1427        // Returns a valid function pointer for global commands all the time, although this behavior
1428        // doesn't match the spec. See https://github.com/KhronosGroup/Vulkan-Loader/issues/1537 for
1429        // details.
1430        match name {
1431            "vkGetInstanceProcAddr" => {
1432                return unsafe {
1433                    std::mem::transmute::<vk::PFN_vkGetInstanceProcAddr, vk::PFN_vkVoidFunction>(
1434                        Self::get_instance_proc_addr,
1435                    )
1436                }
1437            }
1438            "vkEnumerateInstanceExtensionProperties" => {
1439                return unsafe {
1440                    std::mem::transmute::<
1441                        vk::PFN_vkEnumerateInstanceExtensionProperties,
1442                        vk::PFN_vkVoidFunction,
1443                    >(Self::enumerate_instance_extension_properties)
1444                }
1445            }
1446            "vkEnumerateInstanceLayerProperties" => {
1447                return unsafe {
1448                    std::mem::transmute::<
1449                        vk::PFN_vkEnumerateInstanceLayerProperties,
1450                        vk::PFN_vkVoidFunction,
1451                    >(Self::enumerate_instance_layer_properties)
1452                }
1453            }
1454            "vkCreateInstance" => {
1455                return unsafe {
1456                    std::mem::transmute::<vk::PFN_vkCreateInstance, vk::PFN_vkVoidFunction>(
1457                        Self::create_instance,
1458                    )
1459                }
1460            }
1461            _ => {}
1462        }
1463        if instance == vk::Instance::null() {
1464            // TODO: allow inteception of vkEnumerateInstanceVersion and handle
1465            // vkEnumerateInstanceVersion properly. For now, we still return NULL for
1466            // vkEnumerateInstanceVersion. The Vulkan loader should cover us for this case.
1467            // Per spec, if instance is NULL, and pName is neither NULL nor a global command, NULL
1468            // should be returned.
1469            return None;
1470        }
1471        let instance_info = global.get_instance_info(instance)?;
1472        if global.get_instance_addr_proc_hooked {
1473            if let LayerResult::Handled(res) = instance_info
1474                .customized_info
1475                .borrow()
1476                .hooks()
1477                .get_instance_proc_addr(name)
1478            {
1479                return res;
1480            }
1481        }
1482        let get_next_proc_addr =
1483            || unsafe { (instance_info.get_instance_proc_addr)(instance, p_name) };
1484        let instance_commands = &instance_info.instance_commands;
1485        let instance_command = match instance_commands
1486            .binary_search_by_key(&name, |VulkanCommand { name, .. }| name)
1487        {
1488            Ok(index) => Some(&instance_commands[index]),
1489            Err(_) => None,
1490        };
1491        if let Some(instance_command) = instance_command {
1492            if !instance_command.hooked {
1493                return get_next_proc_addr();
1494            }
1495            let enabled = instance_command.features.is_command_enabled(
1496                &instance_info.api_version,
1497                &instance_info.enabled_extensions,
1498            );
1499            if !enabled {
1500                return get_next_proc_addr();
1501            }
1502            return instance_command.proc;
1503        }
1504        let device_commands = &instance_info.device_commands;
1505        let device_command =
1506            match device_commands.binary_search_by_key(&name, |VulkanCommand { name, .. }| name) {
1507                Ok(index) => Some(&device_commands[index]),
1508                Err(_) => None,
1509            };
1510        if let Some(device_command) = device_command {
1511            let next_proc_addr = get_next_proc_addr();
1512            if !device_command.hooked {
1513                return next_proc_addr;
1514            }
1515            let layer_device_extensions: BTreeSet<Extension> = T::manifest()
1516                .device_extensions
1517                .iter()
1518                .map(|ExtensionProperties { name, .. }| name.clone())
1519                .collect();
1520            // If the layer supports the command or the next proc addr can find it, this is an
1521            // available device command.
1522            let command_available = device_command
1523                .features
1524                .is_command_enabled(&instance_info.api_version, &layer_device_extensions)
1525                || next_proc_addr.is_some();
1526            if command_available {
1527                return device_command.proc;
1528            } else {
1529                return next_proc_addr;
1530            }
1531        }
1532        // We don't recognize this command.
1533        get_next_proc_addr()
1534    }
1535
1536    /// The `vkGetDeviceProcAddr` entry point provided by the layer framework.
1537    ///
1538    /// The layer framework will make use of [`Layer::hooked_device_commands`] to decide whether a
1539    /// local function pointer should be returned or should passthrough to the next layer in the
1540    /// call chain. Note that the layer implementation doesn't have to take care of whether the
1541    /// command is enabled(either included in the requested core version or enabled extension), and
1542    /// only needs to use the [`Layer::hooked_device_commands`] interface to express the intent. The
1543    /// layer framework should handle most of the logic(e.g. the requirement of
1544    /// [`LLP_LAYER_18`](<https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderLayerInterface.md#pre-instance-functions:~:text=LLP_LAYER_18,Conventions%20and%20Rules>))
1545    /// here unless the layer implementation decides to intercept `vkGetDeviceProcAddr`.
1546    ///
1547    /// # Safety
1548    /// See valid usage of `vkGetDeviceProcAddr` at
1549    /// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetDeviceProcAddr.html>.
1550    #[deny(unsafe_op_in_unsafe_fn)]
1551    pub unsafe extern "system" fn get_device_proc_addr(
1552        device: vk::Device,
1553        p_name: *const c_char,
1554    ) -> vk::PFN_vkVoidFunction {
1555        let name = unsafe { CStr::from_ptr(p_name) };
1556        let name = name.to_str().expect("name should be a valid UTF-8 string.");
1557        let global = Self::instance();
1558        let device_info = global.get_device_info(device)?;
1559        if let LayerResult::Handled(res) = device_info
1560            .customized_info
1561            .borrow()
1562            .hooks()
1563            .get_device_proc_addr(name)
1564        {
1565            return res;
1566        }
1567        let get_next_device_proc_addr =
1568            || unsafe { (device_info.get_device_proc_addr)(device, p_name) };
1569        let device_commands = &device_info.device_commands;
1570        let command = if let Ok(index) =
1571            device_commands.binary_search_by_key(&name, |VulkanCommand { name, .. }| name)
1572        {
1573            &device_commands[index]
1574        } else {
1575            return get_next_device_proc_addr();
1576        };
1577        if !command
1578            .features
1579            .is_command_enabled(&device_info.api_version, &device_info.enabled_extensions)
1580        {
1581            return get_next_device_proc_addr();
1582        }
1583        if !command.hooked {
1584            return get_next_device_proc_addr();
1585        }
1586        command.proc
1587    }
1588}
1589
1590impl<T: Layer> Default for Global<T> {
1591    fn default() -> Self {
1592        let layer_info: T = Default::default();
1593        let get_instance_addr_proc_hooked = T::GlobalHooksInfo::hooked_commands()
1594            .contains(&LayerVulkanCommand::GetInstanceProcAddr);
1595        Self {
1596            instance_map: Default::default(),
1597            physical_device_map: Default::default(),
1598            device_map: Default::default(),
1599            layer_info,
1600            get_instance_addr_proc_hooked,
1601        }
1602    }
1603}
1604
1605/// A stub struct that intercept no commands, which implements [`GlobalHooks`] and
1606/// [`GlobalHooksInfo`].
1607#[derive(Default)]
1608pub struct StubGlobalHooks;
1609
1610#[auto_globalhooksinfo_impl]
1611impl GlobalHooks for StubGlobalHooks {}
1612
1613/// A stub struct that intercept no commands, which implements [`InstanceHooks`] and
1614/// [`InstanceInfo`].
1615#[derive(Default)]
1616pub struct StubInstanceInfo;
1617
1618#[auto_instanceinfo_impl]
1619impl InstanceHooks for StubInstanceInfo {}
1620
1621/// A stub struct that intercept no commands, which implements [`DeviceHooks`] and [`DeviceInfo`].
1622#[derive(Default)]
1623pub struct StubDeviceInfo;
1624
1625#[auto_deviceinfo_impl]
1626impl DeviceHooks for StubDeviceInfo {}
1627
1628#[cfg(test)]
1629mod tests {
1630    use once_cell::sync::Lazy;
1631
1632    use crate::test_utils::LayerManifestExt;
1633    use std::{cmp::Ordering, mem::MaybeUninit};
1634
1635    use super::*;
1636
1637    #[test]
1638    fn commands_must_be_sorted() {
1639        #[derive(Default)]
1640        struct TestLayer(StubGlobalHooks);
1641
1642        impl Layer for TestLayer {
1643            type GlobalHooksInfo = StubGlobalHooks;
1644            type InstanceInfo = StubInstanceInfo;
1645            type DeviceInfo = StubDeviceInfo;
1646            type InstanceInfoContainer = StubInstanceInfo;
1647            type DeviceInfoContainer = StubDeviceInfo;
1648
1649            fn global_instance() -> impl std::ops::Deref<Target = Global<Self>> + 'static {
1650                &*GLOBAL
1651            }
1652
1653            fn manifest() -> LayerManifest {
1654                LayerManifest::test_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                _: &vk::DeviceCreateInfo,
1675                _: Option<&vk::AllocationCallbacks>,
1676                _: Arc<ash::Device>,
1677                _next_get_device_proc_addr: vk::PFN_vkGetDeviceProcAddr,
1678            ) -> Self::DeviceInfoContainer {
1679                Default::default()
1680            }
1681        }
1682
1683        static GLOBAL: Lazy<Global<TestLayer>> = Lazy::new(Default::default);
1684
1685        #[inline]
1686        fn check<T: PartialOrd>(last: &mut T) -> impl FnMut(T) -> bool + '_ {
1687            move |curr| {
1688                if let Some(Ordering::Greater) | None = (*last).partial_cmp(&curr) {
1689                    return false;
1690                }
1691                *last = curr;
1692                true
1693            }
1694        }
1695
1696        let instance_info = Default::default();
1697        let device_commands =
1698            Global::<TestLayer>::instance().create_device_commands(&instance_info, None);
1699        let mut name_iter = device_commands
1700            .iter()
1701            .map(|VulkanCommand { name, .. }| *name);
1702        let mut last = name_iter.next().unwrap();
1703
1704        assert!(name_iter.all(check(&mut last)));
1705
1706        let instance_commands =
1707            Global::<TestLayer>::instance().create_instance_commands(&instance_info);
1708        let mut name_iter = instance_commands
1709            .iter()
1710            .map(|VulkanCommand { name, .. }| *name);
1711        let mut last = name_iter.next().unwrap();
1712
1713        assert!(name_iter.all(check(&mut last)));
1714    }
1715
1716    #[test]
1717    fn fill_vk_out_array_should_handle_zero_count_correctly() {
1718        let extensions = [ExtensionProperties {
1719            name: Extension::KHRSwapchain,
1720            spec_version: 1,
1721        }]
1722        .into_iter()
1723        .map(Into::<vk::ExtensionProperties>::into)
1724        .collect::<Vec<_>>();
1725
1726        let mut count = 0;
1727        assert_eq!(
1728            unsafe { fill_vk_out_array(&extensions, (&mut count).into(), null_mut()) },
1729            vk::Result::SUCCESS
1730        );
1731        assert_eq!(count, extensions.len());
1732
1733        count = 0;
1734        let mut extension_property = MaybeUninit::<vk::ExtensionProperties>::uninit();
1735        assert_eq!(
1736            unsafe {
1737                fill_vk_out_array(
1738                    &extensions,
1739                    (&mut count).into(),
1740                    extension_property.as_mut_ptr(),
1741                )
1742            },
1743            vk::Result::INCOMPLETE
1744        );
1745        assert_eq!(count, 0);
1746
1747        count = 0;
1748        assert_eq!(
1749            unsafe { fill_vk_out_array(&[], (&mut count).into(), extension_property.as_mut_ptr()) },
1750            vk::Result::SUCCESS
1751        );
1752    }
1753
1754    #[test]
1755    fn fill_vk_out_array_should_return_incomplete_with_short_out_array() {
1756        let extensions = [
1757            ExtensionProperties {
1758                name: Extension::KHRSwapchain,
1759                spec_version: 1,
1760            },
1761            ExtensionProperties {
1762                name: Extension::KHRSamplerYcbcrConversion,
1763                spec_version: 1,
1764            },
1765        ]
1766        .into_iter()
1767        .map(Into::<vk::ExtensionProperties>::into)
1768        .collect::<Vec<_>>();
1769
1770        let mut count = 1;
1771        let mut extension_property = MaybeUninit::<vk::ExtensionProperties>::uninit();
1772        assert_eq!(
1773            unsafe {
1774                fill_vk_out_array(
1775                    &extensions,
1776                    (&mut count).into(),
1777                    extension_property.as_mut_ptr(),
1778                )
1779            },
1780            vk::Result::INCOMPLETE
1781        );
1782        assert_eq!(count, 1);
1783    }
1784}