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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
mod physical_device_features;
mod physical_device_properties;

use {
    crate::{ffi, InstanceResult, VulkanHandle, VulkanInstance},
    ash::vk,
    indoc::indoc,
};

pub use self::{
    physical_device_features::PhysicalDeviceFeatures,
    physical_device_properties::PhysicalDeviceProperties,
};

/// A Vulkan physical device along with its properties and requested features.
///
/// Physical devices are purely descriptive and can be cloned without concern
/// for underlying GPU resources.
#[derive(Clone)]
pub struct PhysicalDevice {
    properties: PhysicalDeviceProperties,
    features: PhysicalDeviceFeatures,
    available_extensions: Vec<vk::ExtensionProperties>,
    available_extension_names: Vec<String>,
    queue_family_properties: Vec<vk::QueueFamilyProperties>,
    physical_device: vk::PhysicalDevice,
}

impl PhysicalDevice {
    /// Properties for all queue families supported by this device.
    pub fn queue_family_properties(&self) -> &[vk::QueueFamilyProperties] {
        &self.queue_family_properties
    }

    /// The set of all extensions available on this device.
    pub fn available_extensions(&self) -> &[vk::ExtensionProperties] {
        &self.available_extensions
    }

    /// The set of all extension names available on this device.
    pub fn available_extension_names(&self) -> &[String] {
        &self.available_extension_names
    }

    /// The properties for this physical device.
    pub fn properties(&self) -> &PhysicalDeviceProperties {
        &self.properties
    }

    /// The features requested when picking this device.
    pub fn features(&self) -> &PhysicalDeviceFeatures {
        &self.features
    }

    /// The physical device name from the device properties struct.
    pub fn name(&self) -> String {
        ffi::string_from_i8(&self.properties().properties().device_name)
            .unwrap()
    }

    /// Enumerate all physical devices which support the required featuers.
    ///
    /// # Params
    ///
    /// * `instance` - the Vulkan instance which will provide access to the
    ///   physical devices.
    /// * `features` - the features the device must support.
    pub fn enumerate_supported_devices(
        instance: &VulkanInstance,
        required_features: &PhysicalDeviceFeatures,
    ) -> InstanceResult<Vec<Self>> {
        let all_supported_devices: Vec<vk::PhysicalDevice> =
            unsafe { instance.ash().enumerate_physical_devices()? }
                .into_iter()
                .filter(|physical_device| {
                    required_features.is_supported_by(
                        &PhysicalDeviceFeatures::from_physical_device(
                            instance,
                            physical_device,
                        ),
                    )
                })
                .collect();

        let mut devices_with_requested_features = vec![];
        for physical_device in all_supported_devices {
            let properties = PhysicalDeviceProperties::from_physical_device(
                instance,
                &physical_device,
            );
            let extension_properties = unsafe {
                instance
                    .ash()
                    .enumerate_device_extension_properties(physical_device)?
            };
            let extension_names: Vec<String> = extension_properties
                .iter()
                .map(|props| ffi::string_from_i8(&props.extension_name))
                .filter_map(|name| name.ok())
                .collect();
            let queue_family_properties = unsafe {
                instance.ash().get_physical_device_queue_family_properties(
                    physical_device,
                )
            };
            devices_with_requested_features.push(Self {
                properties,
                features: *required_features,
                available_extensions: extension_properties,
                available_extension_names: extension_names,
                queue_family_properties,
                physical_device,
            });
        }

        Ok(devices_with_requested_features)
    }
}

impl std::fmt::Debug for PhysicalDevice {
    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if formatter.alternate() {
            formatter
                .debug_struct("PhysicalDevice")
                .field("properties", &self.properties)
                .field("features", &self.features)
                .field("available_extensions", &self.available_extensions)
                .field("queue_family_properties", &self.queue_family_properties)
                .finish()
        } else {
            formatter.write_str(&self.name())
        }
    }
}

impl std::fmt::Display for PhysicalDevice {
    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        formatter.write_fmt(format_args!(indoc!("{:?}"), self.name(),))?;
        Ok(())
    }
}

impl VulkanHandle for PhysicalDevice {
    type Handle = vk::PhysicalDevice;

    unsafe fn raw(&self) -> &Self::Handle {
        &self.physical_device
    }
}