1use std::sync::Arc;
2
3use crate::loader::Loader;
4use crate::vk;
5
6pub struct Device {
46 handle: vk::Device,
47 commands: Box<vk::commands::DeviceCommands>,
48 _loader: Option<Arc<dyn Loader>>,
49}
50
51impl Device {
52 pub(crate) unsafe fn load(
60 handle: vk::Device,
61 get_device_proc_addr: vk::commands::PFN_vkGetDeviceProcAddr,
62 loader: Option<Arc<dyn Loader>>,
63 ) -> Self {
64 let get_device_proc_addr_fn = get_device_proc_addr.expect("vkGetDeviceProcAddr not loaded");
65 let commands = Box::new(unsafe {
67 vk::commands::DeviceCommands::load(|name| {
68 std::mem::transmute(get_device_proc_addr_fn(handle, name.as_ptr()))
69 })
70 });
71 Self {
72 handle,
73 commands,
74 _loader: loader,
75 }
76 }
77
78 pub unsafe fn from_raw_parts(
100 handle: vk::Device,
101 get_device_proc_addr: vk::commands::PFN_vkGetDeviceProcAddr,
102 ) -> Self {
103 unsafe { Self::load(handle, get_device_proc_addr, None) }
105 }
106
107 pub fn handle(&self) -> vk::Device {
109 self.handle
110 }
111
112 pub fn commands(&self) -> &vk::commands::DeviceCommands {
117 &self.commands
118 }
119
120 pub unsafe fn create_graphics_pipeline(
129 &self,
130 pipeline_cache: vk::PipelineCache,
131 create_info: &vk::GraphicsPipelineCreateInfo,
132 allocator: Option<&vk::AllocationCallbacks>,
133 ) -> crate::VkResult<vk::Pipeline> {
134 unsafe { self.create_graphics_pipelines(pipeline_cache, &[*create_info], allocator) }
135 .map(|v| v[0])
136 }
137
138 pub unsafe fn create_compute_pipeline(
147 &self,
148 pipeline_cache: vk::PipelineCache,
149 create_info: &vk::ComputePipelineCreateInfo,
150 allocator: Option<&vk::AllocationCallbacks>,
151 ) -> crate::VkResult<vk::Pipeline> {
152 unsafe { self.create_compute_pipelines(pipeline_cache, &[*create_info], allocator) }
153 .map(|v| v[0])
154 }
155
156 pub unsafe fn map_memory(
167 &self,
168 memory: vk::DeviceMemory,
169 offset: u64,
170 size: u64,
171 flags: vk::MemoryMapFlags,
172 ) -> crate::VkResult<*mut core::ffi::c_void> {
173 let fp = self.commands().map_memory.expect("vkMapMemory not loaded");
174 let mut data: *mut core::ffi::c_void = core::ptr::null_mut();
175 crate::error::check(unsafe { fp(self.handle(), memory, offset, size, flags, &mut data) })?;
176 Ok(data)
177 }
178
179 pub unsafe fn map_memory2(
189 &self,
190 p_memory_map_info: &vk::MemoryMapInfo,
191 ) -> crate::VkResult<*mut core::ffi::c_void> {
192 let fp = self
193 .commands()
194 .map_memory2
195 .expect("vkMapMemory2 not loaded");
196 let mut data: *mut core::ffi::c_void = core::ptr::null_mut();
197 crate::error::check(unsafe { fp(self.handle(), p_memory_map_info, &mut data) })?;
198 Ok(data)
199 }
200}
201
202#[cfg(test)]
203mod tests {
204 use super::*;
205 use std::ffi::c_char;
206 use vk::Handle;
207
208 fn fake_handle() -> vk::Device {
209 vk::Device::from_raw(0xBEEF)
210 }
211
212 unsafe extern "system" fn mock_get_device_proc_addr(
214 _device: vk::Device,
215 _name: *const c_char,
216 ) -> vk::PFN_vkVoidFunction {
217 None
218 }
219
220 #[test]
221 fn from_raw_parts_stores_handle() {
222 let device =
223 unsafe { Device::from_raw_parts(fake_handle(), Some(mock_get_device_proc_addr)) };
224 assert_eq!(device.handle().as_raw(), fake_handle().as_raw());
225 }
226
227 #[test]
228 fn handle_returns_value_from_construction() {
229 let device = unsafe { Device::load(fake_handle(), Some(mock_get_device_proc_addr), None) };
230 assert_eq!(device.handle().as_raw(), fake_handle().as_raw());
231 }
232
233 #[test]
234 fn commands_returns_reference() {
235 let device = unsafe { Device::load(fake_handle(), Some(mock_get_device_proc_addr), None) };
236 let _ = device.commands();
239 }
240
241 #[test]
242 fn load_with_loader_reference() {
243 use std::ffi::{CStr, c_void};
244 struct DummyLoader;
245 unsafe impl Loader for DummyLoader {
246 unsafe fn load(&self, _name: &CStr) -> *const c_void {
247 std::ptr::null()
248 }
249 }
250 let loader: Arc<dyn Loader> = Arc::new(DummyLoader);
251 let device = unsafe {
252 Device::load(
253 fake_handle(),
254 Some(mock_get_device_proc_addr),
255 Some(loader.clone()),
256 )
257 };
258 assert_eq!(Arc::strong_count(&loader), 2);
259 assert_eq!(device.handle().as_raw(), fake_handle().as_raw());
260 }
261
262 #[test]
263 fn load_without_loader() {
264 let device = unsafe { Device::load(fake_handle(), Some(mock_get_device_proc_addr), None) };
265 assert_eq!(device.handle().as_raw(), fake_handle().as_raw());
266 assert!(device.commands().device_wait_idle.is_none());
268 }
269
270 #[test]
271 fn commands_all_none_from_null_mock() {
272 let device =
273 unsafe { Device::from_raw_parts(fake_handle(), Some(mock_get_device_proc_addr)) };
274 assert!(device.commands().create_buffer.is_none());
275 assert!(device.commands().destroy_device.is_none());
276 assert!(device.commands().get_device_queue.is_none());
277 }
278
279 unsafe extern "system" fn mock_device_wait_idle(_device: vk::Device) -> vk::Result {
282 vk::Result::SUCCESS
283 }
284
285 unsafe extern "system" fn mock_destroy_device(
286 _device: vk::Device,
287 _p_allocator: *const vk::AllocationCallbacks,
288 ) {
289 }
290
291 unsafe extern "system" fn rich_get_device_proc_addr(
293 _device: vk::Device,
294 name: *const c_char,
295 ) -> vk::PFN_vkVoidFunction {
296 let name = unsafe { std::ffi::CStr::from_ptr(name) };
297 match name.to_bytes() {
298 b"vkDeviceWaitIdle" => Some(unsafe {
299 std::mem::transmute::<
300 unsafe extern "system" fn(vk::Device) -> vk::Result,
301 unsafe extern "system" fn(),
302 >(mock_device_wait_idle)
303 }),
304 b"vkDestroyDevice" => Some(unsafe {
305 std::mem::transmute::<
306 unsafe extern "system" fn(vk::Device, *const vk::AllocationCallbacks),
307 unsafe extern "system" fn(),
308 >(mock_destroy_device)
309 }),
310 _ => None,
311 }
312 }
313
314 #[test]
315 fn load_with_rich_mock_populates_some_commands() {
316 let device = unsafe { Device::load(fake_handle(), Some(rich_get_device_proc_addr), None) };
317 assert!(
318 device.commands().device_wait_idle.is_some(),
319 "device_wait_idle should be loaded"
320 );
321 assert!(
322 device.commands().destroy_device.is_some(),
323 "destroy_device should be loaded"
324 );
325 assert!(device.commands().create_buffer.is_none());
327 }
328
329 #[test]
330 fn from_raw_parts_with_rich_mock_populates_commands() {
331 let device =
332 unsafe { Device::from_raw_parts(fake_handle(), Some(rich_get_device_proc_addr)) };
333 assert!(device.commands().device_wait_idle.is_some());
334 assert!(device.commands().destroy_device.is_some());
335 }
336
337 #[test]
338 fn device_wait_idle_succeeds_with_mock() {
339 let device =
340 unsafe { Device::from_raw_parts(fake_handle(), Some(rich_get_device_proc_addr)) };
341 let result = unsafe { device.device_wait_idle() };
342 assert!(result.is_ok());
343 }
344
345 #[test]
346 fn destroy_device_succeeds_with_mock() {
347 let device =
348 unsafe { Device::from_raw_parts(fake_handle(), Some(rich_get_device_proc_addr)) };
349 unsafe { device.destroy_device(None) };
351 }
352
353 #[test]
354 fn from_raw_parts_stores_no_loader() {
355 let device =
356 unsafe { Device::from_raw_parts(fake_handle(), Some(mock_get_device_proc_addr)) };
357 assert_eq!(device.handle().as_raw(), fake_handle().as_raw());
359 }
360
361 #[test]
362 fn load_with_loader_keeps_arc_alive() {
363 use std::ffi::{CStr, c_void};
364 struct DummyLoader;
365 unsafe impl Loader for DummyLoader {
366 unsafe fn load(&self, _name: &CStr) -> *const c_void {
367 std::ptr::null()
368 }
369 }
370 let loader: Arc<dyn Loader> = Arc::new(DummyLoader);
371 let weak = Arc::downgrade(&loader);
372 let device =
373 unsafe { Device::load(fake_handle(), Some(mock_get_device_proc_addr), Some(loader)) };
374 assert!(weak.upgrade().is_some(), "loader should still be alive");
376 drop(device);
377 assert!(weak.upgrade().is_none(), "loader should be dropped");
379 }
380
381 #[test]
382 #[ignore] fn device_wait_idle_succeeds() {
384 let _vk = crate::VK_TEST_MUTEX.lock().expect("VK_TEST_MUTEX poisoned");
385 let (instance, device) = create_real_device();
386 unsafe { device.device_wait_idle() }.expect("device_wait_idle failed");
387 unsafe { device.destroy_device(None) };
388 unsafe { instance.destroy_instance(None) };
389 }
390
391 #[test]
392 #[ignore] fn get_device_queue_returns_non_null_queue() {
394 let _vk = crate::VK_TEST_MUTEX.lock().expect("VK_TEST_MUTEX poisoned");
395 let (instance, device) = create_real_device();
396 let queue = unsafe { device.get_device_queue(0, 0) };
397 assert!(!queue.is_null(), "expected non-null queue handle");
398 unsafe { device.destroy_device(None) };
399 unsafe { instance.destroy_instance(None) };
400 }
401
402 #[test]
403 #[ignore] fn queue_wait_idle_succeeds() {
405 let _vk = crate::VK_TEST_MUTEX.lock().expect("VK_TEST_MUTEX poisoned");
406 let (instance, device) = create_real_device();
407 let queue = unsafe { device.get_device_queue(0, 0) };
408 unsafe { device.queue_wait_idle(queue) }.expect("queue_wait_idle failed");
409 unsafe { device.destroy_device(None) };
410 unsafe { instance.destroy_instance(None) };
411 }
412
413 fn create_real_device() -> (crate::instance::Instance, Device) {
414 use crate::entry::Entry;
415 use crate::loader::LibloadingLoader;
416
417 let loader = LibloadingLoader::new().expect("failed to load Vulkan");
418 let entry = unsafe { Entry::new(loader) }.expect("failed to create Entry");
419
420 let api_version_1_0 = crate::Version::new(1, 0, 0).to_raw();
421 let app_info = vk::ApplicationInfo {
422 s_type: vk::StructureType::APPLICATION_INFO,
423 p_next: std::ptr::null(),
424 p_application_name: std::ptr::null(),
425 application_version: 0,
426 p_engine_name: std::ptr::null(),
427 engine_version: 0,
428 api_version: api_version_1_0,
429 };
430 let instance_create_info = vk::InstanceCreateInfo {
431 s_type: vk::StructureType::INSTANCE_CREATE_INFO,
432 p_next: std::ptr::null(),
433 flags: vk::InstanceCreateFlagBits::empty(),
434 p_application_info: &app_info,
435 enabled_layer_count: 0,
436 pp_enabled_layer_names: std::ptr::null(),
437 enabled_extension_count: 0,
438 pp_enabled_extension_names: std::ptr::null(),
439 };
440 let instance = unsafe { entry.create_instance(&instance_create_info, None) }
441 .expect("failed to create instance");
442
443 let physical_devices = unsafe { instance.enumerate_physical_devices() }
444 .expect("failed to enumerate physical devices");
445 let physical_device = physical_devices[0];
446
447 let queue_priority = 1.0f32;
448 let queue_create_info = vk::DeviceQueueCreateInfo {
449 s_type: vk::StructureType::DEVICE_QUEUE_CREATE_INFO,
450 p_next: std::ptr::null(),
451 flags: vk::DeviceQueueCreateFlagBits::empty(),
452 queue_family_index: 0,
453 queue_count: 1,
454 p_queue_priorities: &queue_priority,
455 };
456 let device_create_info = vk::DeviceCreateInfo {
457 s_type: vk::StructureType::DEVICE_CREATE_INFO,
458 p_next: std::ptr::null(),
459 flags: 0,
460 queue_create_info_count: 1,
461 p_queue_create_infos: &queue_create_info,
462 enabled_layer_count: 0,
463 pp_enabled_layer_names: std::ptr::null(),
464 enabled_extension_count: 0,
465 pp_enabled_extension_names: std::ptr::null(),
466 p_enabled_features: std::ptr::null(),
467 };
468 let device = unsafe { instance.create_device(physical_device, &device_create_info, None) }
469 .expect("failed to create device");
470
471 (instance, device)
472 }
473}