1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
use std::{borrow::BorrowMut, error::Error};

use ash::{Entry, extensions::ext::DebugUtils, version::{EntryV1_0, InstanceV1_0}, vk};

use crate::{compute::Compute, debug_layer::{DebugLayer, DebugOption}, gpu::GPU};


pub struct Vulkan {
    // Entry: Loads the Vulkan library.
    // Needs to outlive Instance and Device.
    _entry: ash::Entry,
    // Instance: Loads instance level functions.
    // Needs to outlive the Devices it has created.
    instance: ash::Instance,
    debug_layer: Option<DebugLayer>,
}

impl Vulkan {

    pub fn new(
        debug: DebugOption
    ) -> Result<Vulkan, Box<dyn Error>> {

        let (layers, mut info) = debug.cons()?;
        let vk_layers: Vec<_> = layers
            .iter()
            .map(|raw_name| raw_name.as_ptr())
            .collect();

        let _entry = unsafe { Entry::new()? };

        let instance = unsafe { _entry
            .create_instance(&vk::InstanceCreateInfo::builder()
                .push_next(info.borrow_mut())
                .application_info(&vk::ApplicationInfo {
                    api_version: vk::make_version(1, 2, 0),
                    engine_version: 0,
                    ..Default::default()
                })
                .enabled_layer_names(&vk_layers)
                .enabled_extension_names(&[DebugUtils::name().as_ptr()])
            , None)? };

        println!("Instance created");
        match _entry.try_enumerate_instance_version()? {
            Some(v) => println!("Using Vulkan {}.{}.{}", vk::version_major(v), vk::version_minor(v), vk::version_patch(v)),
            None => println!("Using Vulkan 1.0"),
        };

        let debug_layer = DebugLayer::new(debug, &info, &_entry, &instance)?;
        Ok(Vulkan{_entry, instance, debug_layer})
    }

    pub(crate) unsafe fn gpus(
        &self
    ) -> Result<Vec<GPU>, Box<dyn Error>> {
        let gpus = self.instance
            .enumerate_physical_devices()?
            .into_iter()
            .filter_map(|pdevice| GPU::new(&self.instance, pdevice))
            .collect::<Vec<GPU>>();

        match gpus.is_empty() {
            false => Ok(gpus),
            true => Err("No compute capable GPUs".to_string().into()),
        }
    }

    pub(crate) unsafe fn logical_devices(
        &self,
        gpus: Vec<GPU>
    ) -> Vec<Compute> {
        gpus
            .iter()
            .filter_map(|gpu| match gpu.device(&self.instance) {
                Ok(gpu) => Some(gpu),
                Err(_) => None,
            })
            .collect::<Vec<Compute>>()
    }
}

impl Drop for Vulkan {
    fn drop(
        &mut self
    ) {
        self.debug_layer = None;
        unsafe { self.instance.destroy_instance(None) }
    }
}

#[cfg(test)]
mod tests {
    use std::time::Instant;

    use crate::{debug_layer::DebugOption, new};

    #[test]
    fn app_new() {
        let init_timer = Instant::now();
        let res = new(DebugOption::None);
        assert!(res.is_ok());
        let (_app, devices) = res.unwrap();
        println!("Found {} logical device(s)", devices.len());
        println!("Found {} thread(s)", devices.iter().map(|f| f.fences.len()).sum::<usize>());
        println!("App new {}ms", init_timer.elapsed().as_millis());
    }

}