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}