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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
use {
    crate::error::InstanceResult,
    ash::{extensions::ext::DebugUtils, vk},
    std::fmt::Debug,
};

mod create_instance;
mod debug_callback;

/// The Ash instance, entry, and additional data provided when the instance was
/// created.
pub struct VulkanInstance {
    layers: Vec<String>,
    extensions: Vec<String>,

    debug_messenger: Option<vk::DebugUtilsMessengerEXT>,
    debug_utils: Option<DebugUtils>,

    entry: ash::Entry,
    ash: ash::Instance,
}

impl VulkanInstance {
    /// Create a new Vulkan instance.
    ///
    /// # Params
    ///
    /// * `required_extensions` - All of the extension names required by this
    ///   application. The DebugUtils extension is added automatically when
    ///   compiled with debug assertions enabled.
    /// * `required_layers` - All of the layers required by this application.
    ///
    /// # Returns
    ///
    /// The Vulkan Instance or an InstanceError if any of the extensions or
    /// layers are unavailable.
    ///
    /// # Safety
    ///
    /// Unsafe because:
    ///   - The Application must ensure that all device resources created with
    ///     the instance are destroyed proior to dropping this struct.
    pub unsafe fn new(
        required_extensions: &[String],
        required_layers: &[String],
    ) -> InstanceResult<Self> {
        let actual_required_extensions =
            Self::with_additional_extensions(required_extensions);

        let (entry, ash) = Self::create_instance(
            &actual_required_extensions,
            required_layers,
        )?;

        let mut vulkan_instance = Self {
            layers: required_layers.to_vec(),
            extensions: actual_required_extensions.to_vec(),
            debug_messenger: None,
            debug_utils: None,
            entry,
            ash,
        };

        vulkan_instance.setup_debug_logger()?;

        Ok(vulkan_instance)
    }

    /// The raw Ash Entry.
    pub fn entry(&self) -> &ash::Entry {
        &self.entry
    }

    /// The raw Ash library instance.
    pub fn ash(&self) -> &ash::Instance {
        &self.ash
    }

    /// The layers used to create this Vulkan Instance.
    pub fn layers(&self) -> &[String] {
        &self.layers
    }

    /// The extensions used to creat this Vulkan Instance.
    pub fn extensions(&self) -> &[String] {
        &self.extensions
    }

    /// Set the debug name for an object owned by the provided logical device.
    ///
    /// This is a no-op for release builds.
    ///
    /// # Params
    ///
    /// * `logical_device` - the logical Vulkan device used to create the object
    ///   referenced by the name info struct.
    /// * `name_info` - the name info struct containing the targeted object and
    ///   its new name.
    #[cfg(debug_assertions)]
    pub fn debug_utils_set_object_name(
        &self,
        logical_device: &ash::Device,
        name_info: &vk::DebugUtilsObjectNameInfoEXT,
    ) {
        let result = unsafe {
            self.debug_utils
                .as_ref()
                .unwrap()
                .debug_utils_set_object_name(logical_device.handle(), name_info)
        };
        if result.is_err() {
            log::warn!(
                "Unable to set debug name for device! {:#?} {:#?}",
                name_info,
                result.err().unwrap()
            );
        }
    }

    /// Set the debug name for an object owned by the provided logical device.
    ///
    /// This is a no-op for release builds.
    ///
    /// # Params
    ///
    /// * `logical_device` - the logical Vulkan device used to create the object
    ///   referenced by the name info struct.
    /// * `name_info` - the name info struct containing the targeted object and
    ///   its new name.
    #[cfg(not(debug_assertions))]
    pub fn debug_utils_set_object_name(
        &self,
        _logical_device: &ash::Device,
        _name_info: &vk::DebugUtilsObjectNameInfoEXT,
    ) {
        // no-op
    }
}

impl Drop for VulkanInstance {
    /// Drop the instance.
    ///
    /// # Safety
    ///
    /// Implicitly unsafe because:
    ///   - dropping the instance while Vulkan resources still exist can result
    ///     in undefined behavior.
    ///   - use Vulkan validation layers to verify correct resource management.
    fn drop(&mut self) {
        unsafe {
            if self.debug_utils.is_some() {
                self.debug_utils
                    .as_ref()
                    .unwrap()
                    .destroy_debug_utils_messenger(
                        self.debug_messenger.unwrap(),
                        None,
                    );
            }
            self.ash.destroy_instance(None);
        }
    }
}

impl Debug for VulkanInstance {
    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        formatter
            .debug_struct("VulkanInstance")
            .field("layers", &self.layers)
            .field("extensions", &self.extensions)
            .field("is_debug_enabled", &cfg!(debug_assertions))
            .finish()
    }
}

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