Skip to main content

vk/
instance.rs

1//! Explicit instance destruction with retained native-library ownership.
2use crate as vk;
3use crate::load::{
4	CallError, Capabilities, CommandSupport, CreatedHandle, Entry, InitializationFailure, LoadError,
5};
6use libloading::Library;
7use std::sync::Arc;
8
9/// Vulkan instance with cached commands and optional creation-capability metadata.
10///
11/// The raw native identity is [`InstanceHandle`](crate::InstanceHandle), available
12/// through [`Self::handle`]. This wrapper is not a native ABI handle.
13/// Call [`Self::destroy`] explicitly; Drop never destroys Vulkan objects.
14///
15/// [Khronos object reference](https://docs.vulkan.org/refpages/latest/refpages/source/VkInstance.html)
16pub struct Instance {
17	handle: vk::InstanceHandle,
18	fp: vk::FnInstance,
19	pub(super) gipa: unsafe extern "system" fn(
20		vk::InstanceHandle,
21		*const std::ffi::c_char,
22	) -> vk::PFN_vkVoidFunction,
23	pub(crate) library: Option<Arc<Library>>,
24	pub(crate) capabilities: Option<Capabilities>,
25}
26
27impl Instance {
28	/// Associate a live raw instance with the entry that created it.
29	///
30	/// # Safety
31	/// `handle` must be live and belong to `entry`. The caller transfers the
32	/// responsibility to destroy it once and must not create duplicate owners.
33	/// Ownership remains with the caller on error. Creation metadata is unknown
34	/// on this import path; command admission remains the caller's obligation.
35	pub unsafe fn new_from_handle(
36		entry: &Entry,
37		handle: vk::InstanceHandle,
38	) -> Result<Self, LoadError> {
39		if handle.is_null() {
40			return Err(LoadError::NullHandle("instance"));
41		}
42		// SAFETY: caller guarantees a compatible live instance; gipa resolves by name.
43		let fp = unsafe { vk::FnInstance::new_from_resolver(entry.gipa, handle) };
44		if fp.destroy_instance.is_none() {
45			return Err(LoadError::MissingSymbol("vkDestroyInstance"));
46		}
47		Ok(Self {
48			handle,
49			fp,
50			gipa: entry.gipa,
51			library: entry.library.clone(),
52			capabilities: None,
53		})
54	}
55
56	/// Import a native instance with an owned creation snapshot and filtered dispatch.
57	///
58	/// # Errors
59	/// Reports wrong snapshot scope, a null handle or unavailable destruction.
60	/// Every error leaves native ownership with the caller.
61	///
62	/// # Safety
63	/// The handle must be live, belong to entry and have no other wrapper owner.
64	/// Capabilities must truthfully describe this exact instance's creation and
65	/// implementation. Caller retains parent/callback lifetime obligations.
66	pub unsafe fn new_from_handle_with_capabilities(
67		entry: &Entry,
68		handle: vk::InstanceHandle,
69		capabilities: Capabilities,
70	) -> Result<Self, LoadError> {
71		if capabilities.is_device() {
72			return Err(LoadError::InvalidMetadata(
73				"instance import needs instance capabilities",
74			));
75		}
76		// SAFETY: caller guarantees the live compatible handle and exclusive import.
77		let mut instance = unsafe { Self::new_from_handle(entry, handle) }?;
78		instance
79			.fp
80			.retain_enabled(|command| capabilities.support(command) != CommandSupport::Disabled);
81		instance.capabilities = Some(capabilities);
82		Ok(instance)
83	}
84
85	/// Return the raw handle. Its use must respect this instance's lifetime.
86	pub fn handle(&self) -> vk::InstanceHandle {
87		self.handle
88	}
89
90	/// Borrow the full instance dispatch table.
91	pub fn fp(&self) -> &vk::FnInstance {
92		&self.fp
93	}
94
95	/// Creation version/extension provenance, or None for an imported raw handle.
96	pub fn capabilities(&self) -> Option<&Capabilities> {
97		self.capabilities.as_ref()
98	}
99
100	/// Destroy once, using callbacks compatible with instance creation.
101	///
102	/// # Safety
103	/// All children and outstanding instance operations must be complete. The
104	/// allocator must match creation, with its user data and functions still live.
105	#[doc(alias = "vkDestroyInstance")]
106	#[doc = "\n\n[Khronos reference: `vkDestroyInstance`](https://docs.vulkan.org/refpages/latest/refpages/source/vkDestroyInstance.html)"]
107	pub unsafe fn destroy(self, allocator: Option<&vk::AllocationCallbacks>) {
108		// SAFETY: caller supplies the canonical destruction guarantees.
109		unsafe { destroy_instance(self, allocator) };
110	}
111}
112
113/// Create a headless or extension-configured Vulkan instance.
114///
115/// # Errors
116/// Reports host metadata/version-query failures before creation, driver errors,
117/// invalid native outputs and unavailable destruction. If rollback is impossible,
118/// `CallError::InitializationFailed` retains the native object for explicit recovery.
119///
120/// # Safety
121/// The create info, its reachable pointers and allocator callbacks must meet
122/// Vulkan valid usage. Allocator state must remain alive until destruction.
123#[doc(alias = "vkCreateInstance")]
124#[doc = "\n\n[Khronos reference: `vkCreateInstance`](https://docs.vulkan.org/refpages/latest/refpages/source/vkCreateInstance.html)"]
125pub unsafe fn create_instance(
126	entry: &Entry,
127	create_info: &vk::InstanceCreateInfo,
128	allocator: Option<&vk::AllocationCallbacks>,
129) -> Result<Instance, CallError> {
130	// Capture all fallible host metadata before creating the native object.
131	// SAFETY: caller guarantees validity of all reachable create-info pointers.
132	let mut capabilities = unsafe { Capabilities::instance(create_info) }?;
133	capabilities.limit_instance_version(entry.enumerate_instance_version()?)?;
134	let allocator_ptr = allocator.map_or(std::ptr::null(), |a| a);
135	let mut handle = vk::InstanceHandle::NULL;
136	// SAFETY: caller guarantees create-info validity; global PFN is validated.
137	let result =
138		unsafe { entry.fp().create_instance.unwrap()(create_info, allocator_ptr, &mut handle) };
139	if result != vk::Result::SUCCESS {
140		return Err(CallError::Vulkan(result));
141	}
142	if handle.is_null() {
143		return Err(CallError::InvalidOutput(
144			"vkCreateInstance returned a null handle",
145		));
146	}
147	// SAFETY: native success supplied a non-null instance from this entry.
148	let mut fp = unsafe { vk::FnInstance::new_from_resolver(entry.gipa, handle) };
149	if fp.destroy_instance.is_none() {
150		let failure = InitializationFailure {
151			handle: CreatedHandle::Instance(handle),
152			missing_command: "vkDestroyInstance",
153			library: entry.library.clone(),
154			instance: handle,
155			gipa: entry.gipa,
156			gdpa: None,
157		};
158		// SAFETY: the new instance has no children/work; callbacks match creation.
159		return match unsafe { failure.destroy(allocator) } {
160			Ok(()) => Err(CallError::MissingCommand("vkDestroyInstance")),
161			Err(failure) => Err(CallError::InitializationFailed(failure)),
162		};
163	}
164	// Device-extension physical-device queries cannot be admitted from an
165	// instance's enabled extensions alone. Retain them for the unsafe query API.
166	fp.retain_enabled(|command| capabilities.support(command) != CommandSupport::Disabled);
167	Ok(Instance {
168		handle,
169		fp,
170		gipa: entry.gipa,
171		library: entry.library.clone(),
172		capabilities: Some(capabilities),
173	})
174}
175
176impl Instance {
177	/// Create an instance using cached entry dispatch.
178	///
179	/// # Safety
180	/// The create info and allocator must satisfy Vulkan instance creation and destruction requirements.
181	#[doc(alias = "vkCreateInstance")]
182	#[doc = "\n\n[Khronos reference: `vkCreateInstance`](https://docs.vulkan.org/refpages/latest/refpages/source/vkCreateInstance.html)"]
183	pub unsafe fn new(
184		entry: &Entry,
185		create_info: &vk::InstanceCreateInfo,
186		allocator: Option<&vk::AllocationCallbacks>,
187	) -> Result<Self, CallError> {
188		// SAFETY: caller provides the canonical creation guarantees.
189		unsafe { create_instance(entry, create_info, allocator) }
190	}
191}
192
193use super::command::commands;
194commands! { Instance, instance;
195	/// Resolve an instance command using this instance's retained loader.
196	///
197	/// # Safety
198	/// Cast to the command's exact ABI, obey its enabled-version/extension
199	/// requirements and keep the instance and native library alive during use.
200	#[doc(alias = "vkGetInstanceProcAddr")]
201	#[doc = "\n\n[Khronos reference: `vkGetInstanceProcAddr`](https://docs.vulkan.org/refpages/latest/refpages/source/vkGetInstanceProcAddr.html)"]
202	pub unsafe fn get_instance_proc_addr(name: &std::ffi::CStr) -> vk::PFN_vkVoidFunction {
203		// SAFETY: this wrapper retains the library and caller guarantees instance lifetime.
204		unsafe { (instance.gipa)(instance.handle, name.as_ptr()) }
205	}
206
207	/// Enumerate physical devices available to this instance.
208	///
209	/// # Safety
210	/// The instance must be live and not destroyed.
211	#[doc(alias = "vkEnumeratePhysicalDevices")]
212	#[doc = "\n\n[Khronos reference: `vkEnumeratePhysicalDevices`](https://docs.vulkan.org/refpages/latest/refpages/source/vkEnumeratePhysicalDevices.html)"]
213	pub unsafe fn enumerate_physical_devices() -> Result<Vec<vk::PhysicalDevice>, CallError> {
214		let f = instance
215			.fp
216			.enumerate_physical_devices
217			.ok_or(CallError::MissingCommand("vkEnumeratePhysicalDevices"))?;
218		// SAFETY: caller guarantees a live instance; PFN implements enumeration.
219		unsafe { crate::load::enumerate::enumerate(|count, data| f(instance.handle, count, data)) }
220	}
221
222	/// Query properties of a physical device (name, driver version, limits, …).
223	///
224	/// # Safety
225	/// The instance must be live and `physical_device` must belong to it.
226	#[doc(alias = "vkGetPhysicalDeviceProperties")]
227	#[doc = "\n\n[Khronos reference: `vkGetPhysicalDeviceProperties`](https://docs.vulkan.org/refpages/latest/refpages/source/vkGetPhysicalDeviceProperties.html)"]
228	pub unsafe fn get_physical_device_properties(
229		physical_device: vk::PhysicalDevice,
230	) -> Result<vk::PhysicalDeviceProperties, CallError> {
231		let f = instance
232			.fp
233			.get_physical_device_properties
234			.ok_or(CallError::MissingCommand("vkGetPhysicalDeviceProperties"))?;
235		// SAFETY: PhysicalDeviceProperties is a C struct of integers, enum and arrays; zeroed is valid.
236		let mut props = unsafe { std::mem::zeroed::<vk::PhysicalDeviceProperties>() };
237		// SAFETY: caller guarantees the physical device and instance are live.
238		unsafe { f(physical_device, &mut props) };
239		Ok(props)
240	}
241
242	/// Query the optional features supported by a physical device.
243	///
244	/// # Safety
245	/// The instance must be live and `physical_device` must belong to it.
246	#[doc(alias = "vkGetPhysicalDeviceFeatures")]
247	#[doc = "\n\n[Khronos reference: `vkGetPhysicalDeviceFeatures`](https://docs.vulkan.org/refpages/latest/refpages/source/vkGetPhysicalDeviceFeatures.html)"]
248	pub unsafe fn get_physical_device_features(
249		physical_device: vk::PhysicalDevice,
250	) -> Result<vk::PhysicalDeviceFeatures, CallError> {
251		let f = instance
252			.fp
253			.get_physical_device_features
254			.ok_or(CallError::MissingCommand("vkGetPhysicalDeviceFeatures"))?;
255		// SAFETY: PhysicalDeviceFeatures is a C struct of VkBool32 (u32) fields; zeroed is valid.
256		let mut features = unsafe { std::mem::zeroed::<vk::PhysicalDeviceFeatures>() };
257		// SAFETY: caller guarantees the physical device and instance are live.
258		unsafe { f(physical_device, &mut features) };
259		Ok(features)
260	}
261
262	/// Enumerate queue families and their properties for a physical device.
263	///
264	/// # Safety
265	/// The instance must be live and `physical_device` must belong to it.
266	#[doc(alias = "vkGetPhysicalDeviceQueueFamilyProperties")]
267	#[doc = "\n\n[Khronos reference: `vkGetPhysicalDeviceQueueFamilyProperties`](https://docs.vulkan.org/refpages/latest/refpages/source/vkGetPhysicalDeviceQueueFamilyProperties.html)"]
268	pub unsafe fn get_physical_device_queue_family_properties(
269		physical_device: vk::PhysicalDevice,
270	) -> Result<Vec<vk::QueueFamilyProperties>, CallError> {
271		let f = instance
272			.fp
273			.get_physical_device_queue_family_properties
274			.ok_or(CallError::MissingCommand(
275				"vkGetPhysicalDeviceQueueFamilyProperties",
276			))?;
277		// SAFETY: the live physical device and PFN obey the count/data protocol.
278		unsafe { crate::load::enumerate::enumerate_void(|count, data| f(physical_device, count, data)) }
279	}
280
281	/// Enumerate extensions supported by a physical device (optionally filtered by layer).
282	///
283	/// Pass `layer_name = None` to query all extensions from the ICD.
284	///
285	/// # Safety
286	/// The instance must be live and `physical_device` must belong to it.
287	#[doc(alias = "vkEnumerateDeviceExtensionProperties")]
288	#[doc = "\n\n[Khronos reference: `vkEnumerateDeviceExtensionProperties`](https://docs.vulkan.org/refpages/latest/refpages/source/vkEnumerateDeviceExtensionProperties.html)"]
289	pub unsafe fn enumerate_device_extension_properties(
290		physical_device: vk::PhysicalDevice,
291		layer_name: Option<&std::ffi::CStr>,
292	) -> Result<Vec<vk::ExtensionProperties>, CallError> {
293		let f = instance
294			.fp
295			.enumerate_device_extension_properties
296			.ok_or(CallError::MissingCommand(
297				"vkEnumerateDeviceExtensionProperties",
298			))?;
299		let layer_ptr = layer_name.map_or(std::ptr::null(), |l| l.as_ptr());
300		// SAFETY: caller guarantees a live device and layer name; PFN implements enumeration.
301		unsafe {
302			crate::load::enumerate::enumerate(|count, data| f(physical_device, layer_ptr, count, data))
303		}
304	}
305
306	/// Query the memory heaps and memory types available to a physical device.
307	///
308	/// # Safety
309	/// The instance must be live and `physical_device` must belong to it.
310	#[doc(alias = "vkGetPhysicalDeviceMemoryProperties")]
311	#[doc = "\n\n[Khronos reference: `vkGetPhysicalDeviceMemoryProperties`](https://docs.vulkan.org/refpages/latest/refpages/source/vkGetPhysicalDeviceMemoryProperties.html)"]
312	pub unsafe fn get_physical_device_memory_properties(
313		physical_device: vk::PhysicalDevice,
314	) -> Result<vk::PhysicalDeviceMemoryProperties, CallError> {
315		let f = instance
316			.fp
317			.get_physical_device_memory_properties
318			.ok_or(CallError::MissingCommand(
319				"vkGetPhysicalDeviceMemoryProperties",
320			))?;
321		// SAFETY: PhysicalDeviceMemoryProperties is a C struct of integers and arrays; zeroed is valid.
322		let mut props = unsafe { std::mem::zeroed::<vk::PhysicalDeviceMemoryProperties>() };
323		// SAFETY: caller guarantees the physical device and instance are live.
324		unsafe { f(physical_device, &mut props) };
325		Ok(props)
326	}
327
328}
329
330/// Destroy once, using callbacks compatible with instance creation.
331///
332/// # Safety
333/// All children and outstanding instance operations must be complete. The
334/// allocator must match creation, with its user data and functions still live.
335#[doc(alias = "vkDestroyInstance")]
336#[doc = "\n\n[Khronos reference: `vkDestroyInstance`](https://docs.vulkan.org/refpages/latest/refpages/source/vkDestroyInstance.html)"]
337pub unsafe fn destroy_instance(instance: Instance, allocator: Option<&vk::AllocationCallbacks>) {
338	// SAFETY: caller guarantees all destruction and allocator requirements.
339	unsafe {
340		instance.fp.destroy_instance.unwrap()(
341			instance.handle,
342			allocator.map_or(std::ptr::null(), |a| a),
343		)
344	};
345}