Skip to main content

vk/core/
instance.rs

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