vkobject_rs/
device.rs

1
2use crate::prelude::*;
3use std::{
4	ffi::CStr,
5	fmt::{self, Debug, Formatter},
6	mem::MaybeUninit,
7	ptr::{null, null_mut},
8	sync::Arc,
9};
10
11/// The physical device info
12#[derive(Debug, Clone)]
13pub struct VulkanGpuInfo {
14	/// The physical device
15	pub(crate) gpu: VkPhysicalDevice,
16
17	/// The properties of the physical device
18	pub(crate) properties: VkPhysicalDeviceProperties,
19
20	/// The properties of the physical device memory
21	pub(crate) mem_properties: VkPhysicalDeviceMemoryProperties,
22
23	/// The queue families
24	pub(crate) queue_families: Vec<VkQueueFamilyProperties>,
25
26	/// The extension properties
27	pub(crate) extension_properties: Vec<VkExtensionProperties>,
28}
29
30impl VulkanGpuInfo {
31	/// Create a list of all the GPUs info
32	pub fn get_gpu_info(vkcore: &VkCore) -> Result<Vec<VulkanGpuInfo>, VulkanError> {
33		let mut num_gpus = 0u32;
34		vkcore.vkEnumeratePhysicalDevices(vkcore.get_instance(), &mut num_gpus, null_mut())?;
35		let mut gpus = Vec::<VkPhysicalDevice>::with_capacity(num_gpus as usize);
36		vkcore.vkEnumeratePhysicalDevices(vkcore.get_instance(), &mut num_gpus, gpus.as_mut_ptr())?;
37		unsafe {gpus.set_len(num_gpus as usize)};
38		let mut ret = Vec::<VulkanGpuInfo>::with_capacity(num_gpus as usize);
39		for gpu in gpus {
40			let mut properties: VkPhysicalDeviceProperties = unsafe {MaybeUninit::zeroed().assume_init()};
41			vkcore.vkGetPhysicalDeviceProperties(gpu, &mut properties)?;
42			let mut mem_properties: VkPhysicalDeviceMemoryProperties = unsafe {MaybeUninit::zeroed().assume_init()};
43			vkcore.vkGetPhysicalDeviceMemoryProperties(gpu, &mut mem_properties)?;
44			let mut num_queue_families = 0u32;
45			vkcore.vkGetPhysicalDeviceQueueFamilyProperties(gpu, &mut num_queue_families, null_mut())?;
46			let mut queue_families = Vec::<VkQueueFamilyProperties>::with_capacity(num_queue_families as usize);
47			vkcore.vkGetPhysicalDeviceQueueFamilyProperties(gpu, &mut num_queue_families, queue_families.as_mut_ptr())?;
48			unsafe {queue_families.set_len(num_queue_families as usize)};
49			let mut num_extension_properties = 0u32;
50			vkcore.vkEnumerateDeviceExtensionProperties(gpu, null(), &mut num_extension_properties, null_mut())?;
51			let mut extension_properties = Vec::<VkExtensionProperties>::with_capacity(num_extension_properties as usize);
52			vkcore.vkEnumerateDeviceExtensionProperties(gpu, null(), &mut num_extension_properties, extension_properties.as_mut_ptr())?;
53			unsafe {extension_properties.set_len(num_extension_properties as usize)};
54			ret.push(VulkanGpuInfo {
55				gpu,
56				properties,
57				mem_properties,
58				queue_families,
59				extension_properties,
60			});
61		}
62		Ok(ret)
63	}
64
65	/// Get the `VkPhysicalDevice`
66	pub(crate) fn get_vk_physical_device(&self) -> VkPhysicalDevice {
67		self.gpu
68	}
69
70	/// Get the `VkQueueFamilyProperties` list
71	pub fn get_queue_families(&self) -> &[VkQueueFamilyProperties] {
72		self.queue_families.as_ref()
73	}
74
75	/// Find a queue family index that matches the flags
76	pub fn get_queue_family_index_by_flags(&self, queue_flag_match: u32) -> u32 {
77		for i in 0..self.queue_families.len() {
78			if (self.queue_families[i].queueFlags & queue_flag_match) == queue_flag_match {
79				return i as u32;
80			}
81		}
82		u32::MAX
83	}
84
85	/// Get the `VkPhysicalDeviceProperties`
86	pub fn get_properties(&self) -> &VkPhysicalDeviceProperties {
87		&self.properties
88	}
89
90	/// Get the list of the `VkExtensionProperties`
91	pub fn get_extension_properties(&self) -> &[VkExtensionProperties] {
92		self.extension_properties.as_ref()
93	}
94
95	/// Get memory type index by the memory properties flags
96	pub fn get_memory_type_index(&self, mut type_bits: u32, properties: VkMemoryPropertyFlags) -> Result<u32, VulkanError> {
97		for i in 0..self.mem_properties.memoryTypeCount {
98			if (type_bits & 1) == 1
99				&& (self.mem_properties.memoryTypes[i as usize].propertyFlags & properties) == properties {
100				return Ok(i)
101			}
102			type_bits >>= 1;
103		}
104		Err(VulkanError::NoSuitableMemoryType)
105	}
106}
107
108unsafe impl Send for VulkanGpuInfo {}
109unsafe impl Sync for VulkanGpuInfo {}
110
111/// The Vulkan device with its queues to submit the rendering commands
112pub struct VulkanDevice {
113	/// The Vulkan driver
114	pub(crate) vkcore: Arc<VkCore>,
115
116	/// The current queue family index
117	queue_family_index: u32,
118
119	/// The info of the GPU
120	gpu: VulkanGpuInfo,
121
122	/// The handle to the device
123	device: VkDevice,
124
125	/// The queues of the device. Submit commands to the queue to control GPU.
126	pub(crate) queue: VkQueue,
127}
128
129impl VulkanDevice {
130	/// Create the `VulkanDevice` by the given `VkPhysicalDevice` and the queue family index
131	pub fn new(vkcore: Arc<VkCore>, gpu: VulkanGpuInfo, queue_family_index: u32) -> Result<Self, VulkanError> {
132		fn remove_c_string_from_vec(v: &mut Vec<*const i8>, to_remove: &str) {
133			let mut found_index = None;
134			for (i, &ptr) in v.iter().enumerate() {
135				unsafe {
136					let c_str = CStr::from_ptr(ptr);
137					// Compare without assuming it's UTF-8
138					if c_str.to_bytes() == to_remove.as_bytes() {
139						found_index = Some(i);
140						break;
141					}
142				}
143			}
144			found_index.map(|i| v.swap_remove(i));
145		}
146		let priorities = [1.0_f32];
147		let queue_ci = VkDeviceQueueCreateInfo {
148			sType: VkStructureType::VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
149			pNext: null(),
150			flags: 0,
151			queueFamilyIndex: queue_family_index,
152			queueCount: 1,
153			pQueuePriorities: priorities.as_ptr(),
154		};
155		let mut extensions: Vec<*const i8> = Vec::with_capacity(gpu.extension_properties.len());
156		let mut has_vk_khr_buffer_device_address = false;
157		let mut has_vk_ext_buffer_device_address = false;
158		for ext in gpu.extension_properties.iter() {
159			let ext_ptr = ext.extensionName.as_ptr();
160			let ext_str = unsafe {CStr::from_ptr(ext_ptr)}.to_string_lossy();
161			if ext_str == "VK_KHR_buffer_device_address" {
162				has_vk_khr_buffer_device_address = true;
163			} else if ext_str == "VK_EXT_buffer_device_address" {
164				has_vk_ext_buffer_device_address = true;
165			} else if ext_str == "VK_NV_disk_cache_utils" || ext_str == "VK_NV_internal_nvpresent" {
166				// The validation layer doesn't know what these extensions are. It will say you are asking for some unknown extensions. Also, we are not going to use these.
167				continue;
168			}
169			extensions.push(ext_ptr)
170		}
171		if has_vk_khr_buffer_device_address && has_vk_ext_buffer_device_address {
172			remove_c_string_from_vec(&mut extensions, "VK_EXT_buffer_device_address");
173		}
174
175		let device_ci = VkDeviceCreateInfo {
176			sType: VkStructureType::VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
177			pNext: null(),
178			flags: 0,
179			queueCreateInfoCount: 1,
180			pQueueCreateInfos: &queue_ci,
181			enabledLayerCount: 0,
182			ppEnabledLayerNames: null(),
183			enabledExtensionCount: extensions.len() as u32,
184			ppEnabledExtensionNames: extensions.as_ptr(),
185			pEnabledFeatures: null(),
186		};
187
188		let mut device: VkDevice = null();
189		vkcore.vkCreateDevice(gpu.get_vk_physical_device(), &device_ci, null(), &mut device)?;
190		let device = ResourceGuard::new(device, |&d|proceed_run(vkcore.vkDestroyDevice(d, null())));
191
192		let mut queue: VkQueue = null();
193		vkcore.vkGetDeviceQueue(*device, queue_family_index, 0, &mut queue)?;
194		let device = device.release();
195
196		Ok(Self {
197			vkcore,
198			queue_family_index,
199			gpu,
200			device,
201			queue,
202		})
203	}
204
205	/// Choose a GPU that matches the `VkQueueFlags`
206	/// * `flags`: The flags you want to match
207	pub fn choose_gpu_by_flags(vkcore: Arc<VkCore>, flags: VkQueueFlags, name_substr: &str) -> Result<Self, VulkanError> {
208		for gpu in VulkanGpuInfo::get_gpu_info(&vkcore)?.iter() {
209			if !unsafe{CStr::from_ptr(gpu.properties.deviceName.as_ptr())}.to_str().unwrap().contains(name_substr) {
210				continue;
211			}
212			let index = gpu.get_queue_family_index_by_flags(flags);
213			if index != u32::MAX {
214				return Self::new(vkcore, gpu.clone(), index);
215			}
216		}
217		Err(VulkanError::ChooseGpuFailed)
218	}
219
220	/// Choose a GPU that must support graphics
221	/// * `queue_count`: see `VulkanDevice::new()`
222	pub fn choose_gpu_with_graphics(vkcore: Arc<VkCore>, name_substr: &str) -> Result<Self, VulkanError> {
223		Self::choose_gpu_by_flags(vkcore, VkQueueFlagBits::VK_QUEUE_GRAPHICS_BIT as VkQueueFlags, name_substr)
224	}
225
226	/// Choose a GPU that must support compute
227	/// * `queue_count`: see `VulkanDevice::new()`
228	pub fn choose_gpu_with_compute(vkcore: Arc<VkCore>, name_substr: &str) -> Result<Self, VulkanError> {
229		Self::choose_gpu_by_flags(vkcore, VkQueueFlagBits::VK_QUEUE_COMPUTE_BIT as VkQueueFlags, name_substr)
230	}
231
232	/// Choose a GPU that must support both graphics and compute
233	/// * `queue_count`: see `VulkanDevice::new()`
234	pub fn choose_gpu_with_graphics_and_compute(vkcore: Arc<VkCore>, name_substr: &str) -> Result<Self, VulkanError> {
235		Self::choose_gpu_by_flags(vkcore,
236			VkQueueFlagBits::VK_QUEUE_GRAPHICS_BIT as VkQueueFlags |
237			VkQueueFlagBits::VK_QUEUE_COMPUTE_BIT as VkQueueFlags, name_substr)
238	}
239
240	/// Choose a GPU that is, anyway, a GPU regardless of can do graphics/compute or not.
241	/// * `queue_count`: see `VulkanDevice::new()`
242	pub fn choose_gpu_anyway(vkcore: Arc<VkCore>, name_substr: &str) -> Result<Self, VulkanError> {
243		Self::choose_gpu_by_flags(vkcore, 0, name_substr)
244	}
245
246	/// Get the current queue family index
247	pub fn get_queue_family_index(&self) -> u32 {
248		self.queue_family_index
249	}
250
251	/// Get the GPU info
252	pub fn get_gpu(&self) -> &VulkanGpuInfo {
253		&self.gpu
254	}
255
256	/// Check if the `queue_index` and the `VkSurfaceKHR` were supported by the `VkPhysicalDevice`
257	pub fn get_supported_by_surface(&self, queue_index: usize, surface: VkSurfaceKHR) -> Result<bool, VulkanError> {
258		let mut result: VkBool32 = 0;
259		self.vkcore.vkGetPhysicalDeviceSurfaceSupportKHR(self.get_vk_physical_device(), queue_index as u32, surface, &mut result)?;
260		Ok(result != 0)
261	}
262
263	/// A wrapper for `vkDeviceWaitIdle`
264	pub fn wait_idle(&self) -> Result<(), VulkanError> {
265		self.vkcore.vkDeviceWaitIdle(self.device)?;
266		Ok(())
267	}
268
269	/// Get the `VkPhysicalDevice`
270	pub(crate) fn get_vk_physical_device(&self) -> VkPhysicalDevice {
271		self.gpu.get_vk_physical_device()
272	}
273
274	/// Get the `VkDevice`
275	pub(crate) fn get_vk_device(&self) -> VkDevice {
276		self.device
277	}
278
279	/// Get a queue for the current device
280	pub(crate) fn get_vk_queue(&self) -> VkQueue {
281		self.queue
282	}
283}
284
285impl Debug for VulkanDevice {
286	fn fmt(&self, f: &mut Formatter) -> fmt::Result {
287		f.debug_struct("VulkanDevice")
288		.field("queue_family_index", &self.queue_family_index)
289		.field("gpu", &self.gpu)
290		.field("device", &self.device)
291		.field("queue", &self.queue)
292		.finish()
293	}
294}
295
296impl Clone for VulkanDevice {
297	fn clone(&self) -> Self {
298		Self::new(self.vkcore.clone(), self.gpu.clone(), self.queue_family_index).unwrap()
299	}
300}
301
302impl Drop for VulkanDevice {
303	fn drop(&mut self) {
304		proceed_run(self.vkcore.vkDestroyDevice(self.device, null()))
305	}
306}
307
308unsafe impl Send for VulkanDevice {}
309unsafe impl Sync for VulkanDevice {}