vulkanalia_bootstrap/
instance.rs

1use crate::system_info::{DEBUG_UTILS_EXT_NAME, SystemInfo, VALIDATION_LAYER_NAME};
2use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
3use std::borrow::Cow;
4use std::ffi;
5use std::ffi::c_void;
6use std::fmt::Debug;
7use std::sync::Arc;
8use vulkanalia::vk::{
9    self, EntryV1_1, ExtDebugUtilsExtensionInstanceCommands, HasBuilder, InstanceV1_0,
10    KhrSurfaceExtensionInstanceCommands,
11};
12use vulkanalia::vk::{AllocationCallbacks, DebugUtilsMessengerEXT};
13use vulkanalia::{Version, window as vk_window};
14
15pub trait WindowTraits: HasDisplayHandle + HasWindowHandle + Debug {}
16impl<T> WindowTraits for T where T: HasDisplayHandle + HasWindowHandle + Debug {}
17
18unsafe extern "system" fn vulkan_debug_callback(
19    message_severity: vk::DebugUtilsMessageSeverityFlagsEXT,
20    message_type: vk::DebugUtilsMessageTypeFlagsEXT,
21    p_callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT,
22    _user_data: *mut std::os::raw::c_void,
23) -> vk::Bool32 {
24    unsafe {
25        let callback_data = *p_callback_data;
26        let message_id_number = callback_data.message_id_number;
27
28        let message_id_name = if callback_data.message_id_name.is_null() {
29            Cow::from("")
30        } else {
31            ffi::CStr::from_ptr(callback_data.message_id_name).to_string_lossy()
32        };
33
34        let message = if callback_data.message.is_null() {
35            Cow::from("")
36        } else {
37            ffi::CStr::from_ptr(callback_data.message).to_string_lossy()
38        };
39
40        println!(
41            "{message_severity:?}:\n{message_type:?} [{message_id_name} ({message_id_number})] : {message}\n",
42        );
43
44        vk::FALSE
45    }
46}
47
48#[derive(Debug)]
49pub struct DebugUserData(*mut c_void);
50
51impl Default for DebugUserData {
52    fn default() -> Self {
53        Self(std::ptr::null_mut())
54    }
55}
56
57impl DebugUserData {
58    /// Caller must ensure that data pointer points to valid memory.
59    pub unsafe fn new(data: *mut c_void) -> Self {
60        Self(data)
61    }
62}
63
64impl DebugUserData {
65    pub fn into_inner(self) -> *mut c_void {
66        self.0
67    }
68}
69
70#[derive(Debug)]
71pub struct InstanceBuilder {
72    // VkApplicationInfo
73    app_name: String,
74    engine_name: String,
75    application_version: Version,
76    engine_version: Version,
77    minimum_instance_version: Version,
78    required_instance_version: Version,
79
80    // VkInstanceCreateInfo
81    layers: Vec<vk::ExtensionName>,
82    extensions: Vec<vk::ExtensionName>,
83    flags: vk::InstanceCreateFlags,
84
85    // debug callback
86    debug_callback: vk::PFN_vkDebugUtilsMessengerCallbackEXT,
87    debug_message_severity: vk::DebugUtilsMessageSeverityFlagsEXT,
88    debug_message_type: vk::DebugUtilsMessageTypeFlagsEXT,
89    debug_user_data: DebugUserData,
90
91    // validation checks
92    disabled_validation_checks: Vec<vk::ValidationCheckEXT>,
93    enabled_validation_features: Vec<vk::ValidationFeatureEnableEXT>,
94    disabled_validation_features: Vec<vk::ValidationFeatureDisableEXT>,
95
96    allocation_callbacks: Option<vk::AllocationCallbacks>,
97
98    request_validation_layers: bool,
99    enable_validation_layers: bool,
100    // TODO: make typesafe
101    use_debug_messenger: bool,
102    headless_context: bool,
103
104    window: Option<Arc<dyn WindowTraits>>,
105}
106
107impl InstanceBuilder {
108    pub fn new(window: Option<Arc<dyn WindowTraits>>) -> Self {
109        Self {
110            app_name: "".to_string(),
111            engine_name: "".to_string(),
112            application_version: Version::new(0, 0, 0),
113            engine_version: Version::new(0, 0, 0),
114            minimum_instance_version: Version::new(0, 0, 0),
115            required_instance_version: Version::new(0, 0, 0),
116            layers: vec![],
117            extensions: vec![],
118            flags: Default::default(),
119            debug_callback: None,
120            debug_message_severity: vk::DebugUtilsMessageSeverityFlagsEXT::WARNING
121                | vk::DebugUtilsMessageSeverityFlagsEXT::ERROR,
122            debug_message_type: vk::DebugUtilsMessageTypeFlagsEXT::GENERAL
123                | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION
124                | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE,
125            debug_user_data: Default::default(),
126            disabled_validation_checks: vec![],
127            enabled_validation_features: vec![],
128            disabled_validation_features: vec![],
129            allocation_callbacks: None,
130            request_validation_layers: false,
131            enable_validation_layers: false,
132            use_debug_messenger: false,
133            headless_context: false,
134            window,
135        }
136    }
137
138    /// Set the application name that will be passed to Vulkan via VkApplicationInfo.
139    pub fn app_name(mut self, app_name: impl Into<String>) -> Self {
140        self.app_name = app_name.into();
141        self
142    }
143
144    /// Set the engine name that will be passed to Vulkan via VkApplicationInfo.
145    pub fn engine_name(mut self, engine_name: impl Into<String>) -> Self {
146        self.engine_name = engine_name.into();
147        self
148    }
149
150    /// Set the application version reported to Vulkan.
151    pub fn app_version(mut self, version: Version) -> Self {
152        self.application_version = version;
153        self
154    }
155
156    /// Set the engine version reported to Vulkan.
157    pub fn engine_version(mut self, version: Version) -> Self {
158        self.engine_version = version;
159        self
160    }
161
162    /// Require a minimum Vulkan API version for instance creation.
163    pub fn require_api_version(mut self, version: Version) -> Self {
164        self.required_instance_version = version;
165        self
166    }
167
168    /// Set the minimum instance API version that must be supported by the system.
169    pub fn minimum_instance_version(mut self, version: Version) -> Self {
170        self.minimum_instance_version = version;
171        self
172    }
173
174    /// Enable the given instance layer for creation (e.g. validation layers).
175    pub fn enable_layer(mut self, layer: vk::ExtensionName) -> Self {
176        self.layers.push(layer.into());
177        self
178    }
179
180    /// Enable the given Vulkan instance extension for creation.
181    pub fn enable_extension(mut self, extension: vk::ExtensionName) -> Self {
182        self.extensions.push(extension);
183        self
184    }
185
186    /// Explicitly enable or disable validation layers.
187    pub fn enable_validation_layers(mut self, enable: bool) -> Self {
188        self.enable_validation_layers = enable;
189        self
190    }
191
192    /// Request validation layers when available on the system (will be used if present).
193    pub fn request_validation_layers(mut self, request: bool) -> Self {
194        self.request_validation_layers = request;
195        self
196    }
197
198    /// Use the default debug messenger which prints messages to stdout.
199    pub fn use_default_debug_messenger(mut self) -> Self {
200        self.use_debug_messenger = true;
201        self.debug_callback = Some(vulkan_debug_callback);
202        self
203    }
204
205    #[cfg(feature = "enable_tracing")]
206    pub fn use_default_tracing_messenger(mut self) -> Self {
207        self.use_debug_messenger = true;
208        self.debug_callback = Some(crate::tracing::vulkan_tracing_callback);
209        self
210    }
211
212    /// Set a custom debug messenger callback function.
213    pub fn set_debug_messenger(
214        mut self,
215        callback: vk::PFN_vkDebugUtilsMessengerCallbackEXT,
216    ) -> Self {
217        self.use_debug_messenger = true;
218        self.debug_callback = callback;
219        self
220    }
221
222    /// Provide a user data pointer that will be passed to the debug callback.
223    pub fn debug_user_data(mut self, debug_user_data: DebugUserData) -> Self {
224        self.debug_user_data = debug_user_data;
225        self
226    }
227
228    /// Indicate that no windowing surface will be created (headless mode).
229    pub fn headless(mut self, headless: bool) -> Self {
230        self.headless_context = headless;
231        self
232    }
233
234    /// Set the severity flags for the debug messenger (e.g. WARNING | ERROR).
235    pub fn debug_messenger_severity(
236        mut self,
237        severity: vk::DebugUtilsMessageSeverityFlagsEXT,
238    ) -> Self {
239        self.debug_message_severity = severity;
240        self
241    }
242
243    /// Add additional severity flags to the debug messenger.
244    pub fn add_debug_messenger_severity(
245        mut self,
246        severity: vk::DebugUtilsMessageSeverityFlagsEXT,
247    ) -> Self {
248        self.debug_message_severity |= severity;
249        self
250    }
251
252    /// Set the types of debug messages the messenger should receive (general/validation/perf).
253    pub fn debug_messenger_type(mut self, message_type: vk::DebugUtilsMessageTypeFlagsEXT) -> Self {
254        self.debug_message_type = message_type;
255        self
256    }
257
258    /// Add additional debug message types to the messenger configuration.
259    pub fn add_debug_messenger_type(
260        mut self,
261        message_type: vk::DebugUtilsMessageTypeFlagsEXT,
262    ) -> Self {
263        self.debug_message_type |= message_type;
264        self
265    }
266
267    #[cfg_attr(feature = "enable_tracing", tracing::instrument(skip(self)))]
268    /// Build and return an `Instance` according to the configured options.
269    ///
270    /// Performs validation of available layers/extensions and creates the Vulkan instance
271    /// and optional debug messenger and surface.
272    pub fn build(self) -> crate::Result<Arc<Instance>> {
273        let system_info = SystemInfo::get_system_info()?;
274
275        let instance_version = {
276            if self.minimum_instance_version > Version::V1_0_0
277                || self.required_instance_version > Version::V1_0_0
278            {
279                let version = unsafe { system_info.entry.enumerate_instance_version() }
280                    .map_or(Version::V1_0_0, Version::from);
281
282                if version < self.minimum_instance_version
283                    || (self.minimum_instance_version == Version::V1_0_0
284                        && version < self.required_instance_version)
285                {
286                    return match self
287                        .required_instance_version
288                        .max(self.minimum_instance_version)
289                        .minor
290                    {
291                        3 => Err(crate::InstanceError::VulkanVersion13Unavailable.into()),
292                        2 => Err(crate::InstanceError::VulkanVersion12Unavailable.into()),
293                        1 => Err(crate::InstanceError::VulkanVersion11Unavailable.into()),
294                        minor => Err(crate::InstanceError::VulkanVersionUnavailable(format!(
295                            "1.{minor}"
296                        ))
297                        .into()),
298                    };
299                } else {
300                    version
301                }
302            } else {
303                Version::V1_0_0
304            }
305        };
306
307        #[cfg(feature = "enable_tracing")]
308        {
309            tracing::info!(
310                "Instance version: {}.{}.{}",
311                instance_version.major,
312                instance_version.minor,
313                instance_version.patch
314            );
315        }
316
317        let api_version = if instance_version < Version::V1_1_0
318            || self.required_instance_version < self.minimum_instance_version
319        {
320            instance_version
321        } else {
322            self.required_instance_version
323                .max(self.minimum_instance_version)
324        };
325        #[cfg(feature = "enable_tracing")]
326        {
327            tracing::info!("api_version: {}", api_version);
328        }
329
330        let app_name = self.app_name;
331        let engine_name = self.engine_name;
332
333        let app_info = vk::ApplicationInfo {
334            application_name: app_name.as_bytes().as_ptr() as _,
335            application_version: self.application_version.into(),
336            engine_name: engine_name.as_bytes().as_ptr() as _,
337            engine_version: self.engine_version.into(),
338            api_version: api_version.into(),
339            ..Default::default()
340        };
341
342        #[cfg(feature = "enable_tracing")]
343        {
344            tracing::info!("Creating vkInstance with application info...");
345            tracing::debug!(
346                r#"
347Application info: {{
348    name: {:?},
349    version: {}.{}.{},
350    engine_name: {:?},
351    engine_version: {}.{}.{},
352    api_version: {}.{}.{},
353}}
354            "#,
355                app_name,
356                self.application_version.major,
357                self.application_version.minor,
358                self.application_version.patch,
359                engine_name,
360                self.engine_version.major,
361                self.engine_version.minor,
362                self.engine_version.patch,
363                api_version.major,
364                api_version.minor,
365                api_version.patch,
366            )
367        }
368
369        let mut enabled_extensions: Vec<vk::ExtensionName> = vec![];
370        let mut enabled_layers: Vec<vk::ExtensionName> = vec![];
371
372        enabled_extensions.extend_from_slice(self.extensions.as_slice());
373
374        if self.debug_callback.is_some()
375            && self.use_debug_messenger
376            && system_info.debug_utils_available
377        {
378            enabled_extensions.push(DEBUG_UTILS_EXT_NAME);
379        }
380
381        let properties2_ext_enabled = api_version < Version::V1_1_0
382            && system_info
383                .is_extension_available(&vk::KHR_GET_PHYSICAL_DEVICE_PROPERTIES2_EXTENSION.name)?;
384
385        if properties2_ext_enabled {
386            enabled_extensions.push(vk::KHR_GET_PHYSICAL_DEVICE_PROPERTIES2_EXTENSION.name);
387        }
388
389        #[cfg(feature = "portability")]
390        let portability_enumeration_support =
391            system_info.is_extension_available(&vk::KHR_PORTABILITY_ENUMERATION_EXTENSION.name)?;
392        #[cfg(feature = "portability")]
393        if portability_enumeration_support {
394            enabled_extensions.push(vk::KHR_PORTABILITY_ENUMERATION_EXTENSION.name);
395        }
396
397        if !self.headless_context {
398            if let Some(window) = self.window.clone() {
399                let surface_extensions: Vec<vk::ExtensionName> =
400                    vk_window::get_required_instance_extensions(window.as_ref())
401                        .into_iter()
402                        .map(|ext| **ext)
403                        .collect();
404
405                if !system_info.are_extensions_available(&surface_extensions)? {
406                    return Err(crate::InstanceError::WindowingExtensionsNotPresent(
407                        surface_extensions,
408                    )
409                    .into());
410                };
411
412                enabled_extensions.extend_from_slice(&surface_extensions);
413            }
414        }
415
416        #[cfg(feature = "enable_tracing")]
417        tracing::trace!(?enabled_extensions);
418
419        let all_extensions_supported = system_info.are_extensions_available(&enabled_extensions)?;
420        if !all_extensions_supported {
421            return Err(
422                crate::InstanceError::RequestedExtensionsNotPresent(enabled_extensions).into(),
423            );
424        };
425
426        enabled_layers.extend_from_slice(&self.layers);
427
428        if self.enable_validation_layers
429            || (self.request_validation_layers && system_info.validation_layers_available)
430        {
431            enabled_layers.push(VALIDATION_LAYER_NAME)
432        };
433
434        let all_layers_supported = system_info.are_layers_available(self.layers)?;
435
436        if !all_layers_supported {
437            return Err(crate::InstanceError::RequestedLayersNotPresent(enabled_layers).into());
438        };
439
440        let instance_create_flags = if cfg!(feature = "portability") {
441            self.flags | vk::InstanceCreateFlags::ENUMERATE_PORTABILITY_KHR
442        } else {
443            self.flags
444        };
445
446        let enabled_extension_ptr = enabled_extensions
447            .iter()
448            .map(|e| e.as_ptr())
449            .collect::<Vec<_>>();
450
451        let enabled_layers_ptr = enabled_layers
452            .iter()
453            .map(|e| e.as_ptr())
454            .collect::<Vec<_>>();
455
456        let mut instance_create_info = vk::InstanceCreateInfo::builder()
457            .flags(instance_create_flags)
458            .application_info(&app_info)
459            .enabled_extension_names(&enabled_extension_ptr)
460            .enabled_layer_names(&enabled_layers_ptr);
461
462        let mut features = vk::ValidationFeaturesEXT::builder()
463            .disabled_validation_features(&self.disabled_validation_features)
464            .enabled_validation_features(&self.enabled_validation_features);
465
466        if !self.enabled_validation_features.is_empty()
467            || !self.disabled_validation_features.is_empty()
468        {
469            instance_create_info = instance_create_info.push_next(&mut features);
470        };
471
472        let mut checks = vk::ValidationFlagsEXT::builder();
473        if !self.disabled_validation_checks.is_empty() {
474            checks = checks.disabled_validation_checks(&self.disabled_validation_checks);
475
476            instance_create_info = instance_create_info.push_next(&mut checks);
477        };
478
479        let instance = unsafe {
480            system_info
481                .entry
482                .create_instance(&instance_create_info, self.allocation_callbacks.as_ref())
483        }
484        .map_err(|_| crate::InstanceError::FailedCreateInstance)?;
485
486        #[cfg(feature = "enable_tracing")]
487        tracing::info!("Created vkInstance");
488
489        let mut debug_messenger = None;
490        let mut debug_user_data = self.debug_user_data.into_inner();
491
492        if self.use_debug_messenger {
493            let messenger_create_info = vk::DebugUtilsMessengerCreateInfoEXT::builder()
494                .message_severity(self.debug_message_severity)
495                .message_type(self.debug_message_type)
496                .user_callback(self.debug_callback)
497                .user_data(&mut debug_user_data);
498
499            #[cfg(feature = "enable_tracing")]
500            tracing::trace!(?self.debug_callback, "Using debug messenger");
501
502            let messenger =
503                unsafe { instance.create_debug_utils_messenger_ext(&messenger_create_info, None) }?;
504
505            debug_messenger.replace(messenger);
506        };
507
508        let mut surface = None;
509        if let Some(window) = self.window.clone() {
510            surface = Some(unsafe {
511                vk_window::create_surface(&instance, window.as_ref(), window.as_ref())?
512            });
513            #[cfg(feature = "enable_tracing")]
514            tracing::info!("Created vkSurfaceKhr")
515        };
516
517        Ok(Arc::new(Instance {
518            instance,
519            surface,
520            allocation_callbacks: self.allocation_callbacks,
521            instance_version,
522            api_version,
523            properties2_ext_enabled,
524            debug_messenger,
525            _system_info: system_info,
526        }))
527    }
528}
529
530#[derive(Debug)]
531pub struct Instance {
532    pub(crate) instance: vulkanalia::Instance,
533    pub(crate) allocation_callbacks: Option<AllocationCallbacks>,
534    pub(crate) surface: Option<vk::SurfaceKHR>,
535    pub(crate) instance_version: Version,
536    pub api_version: Version,
537    pub(crate) properties2_ext_enabled: bool,
538    pub(crate) debug_messenger: Option<DebugUtilsMessengerEXT>,
539    _system_info: SystemInfo,
540}
541
542impl Instance {
543    pub fn destroy(&self) {
544        unsafe {
545            if let Some(debug_messenger) = self.debug_messenger {
546                self.instance.destroy_debug_utils_messenger_ext(
547                    debug_messenger,
548                    self.allocation_callbacks.as_ref(),
549                );
550            }
551            if let Some(surface) = self.surface {
552                self.instance
553                    .destroy_surface_khr(surface, self.allocation_callbacks.as_ref());
554            }
555            self.instance
556                .destroy_instance(self.allocation_callbacks.as_ref());
557        }
558    }
559}
560
561impl AsRef<vulkanalia::Instance> for Instance {
562    fn as_ref(&self) -> &vulkanalia::Instance {
563        &self.instance
564    }
565}
566
567#[cfg(test)]
568mod tests {
569
570    #[test]
571    fn compiles() {}
572}