Skip to main content

rotex_vulkan/core/
mod.rs

1use std::ffi::{CStr, CString, c_void};
2
3use ash::vk;
4
5use crate::backend::vulkan::Adapter;
6use crate::error::{Error, ErrorKind, vk_error};
7
8#[derive(Debug, Clone)]
9pub struct InstanceOptions {
10    pub application_name: &'static str,
11    pub engine_name: &'static str,
12    pub enable_validation: bool,
13    pub enable_debug_utils: bool,
14}
15
16impl Default for InstanceOptions {
17    fn default() -> Self {
18        Self {
19            application_name: "rotex_vulkan",
20            engine_name: "rotex_vulkan",
21            enable_validation: false,
22            enable_debug_utils: false,
23        }
24    }
25}
26
27pub struct Instance {
28    entry: ash::Entry,
29    instance: ash::Instance,
30}
31
32impl Instance {
33    pub fn new_with_options(
34        options: &InstanceOptions,
35        extensions: &[*const i8],
36    ) -> Result<(Self, Option<DebugMessenger>), Error> {
37        let entry = unsafe { ash::Entry::load() }
38            .map_err(|_| Error::fatal(ErrorKind::Unsupported("Failed to load Vulkan entry")))?;
39
40        let app_name = CString::new(options.application_name)
41            .map_err(|_| Error::fatal(ErrorKind::Unsupported("Application name contains NUL")))?;
42        let engine_name = CString::new(options.engine_name)
43            .map_err(|_| Error::fatal(ErrorKind::Unsupported("Engine name contains NUL")))?;
44
45        let app_info = vk::ApplicationInfo::default()
46            .application_name(&app_name)
47            .engine_name(&engine_name)
48            .api_version(vk::API_VERSION_1_0);
49
50        let mut enabled_extensions = extensions.to_vec();
51        if options.enable_debug_utils
52            && !enabled_extensions.contains(&ash::ext::debug_utils::NAME.as_ptr())
53        {
54            enabled_extensions.push(ash::ext::debug_utils::NAME.as_ptr());
55        }
56
57        let mut enabled_layers = Vec::new();
58        if options.enable_validation {
59            enabled_layers.push(c"VK_LAYER_KHRONOS_validation".as_ptr());
60        }
61
62        let create_info = vk::InstanceCreateInfo::default()
63            .application_info(&app_info)
64            .enabled_extension_names(&enabled_extensions)
65            .enabled_layer_names(&enabled_layers);
66
67        let instance = unsafe { entry.create_instance(&create_info, None) }.map_err(vk_error)?;
68
69        let debug = if options.enable_debug_utils {
70            Some(DebugMessenger::new(&entry, &instance)?)
71        } else {
72            None
73        };
74
75        Ok((Self { entry, instance }, debug))
76    }
77
78    pub fn entry(&self) -> &ash::Entry {
79        &self.entry
80    }
81
82    pub fn instance(&self) -> &ash::Instance {
83        &self.instance
84    }
85
86    pub fn enumerate_adapters(&self) -> Vec<Adapter> {
87        let Ok(physical_devices) = (unsafe { self.instance.enumerate_physical_devices() }) else {
88            return Vec::new();
89        };
90
91        physical_devices
92            .into_iter()
93            .map(|handle| {
94                let properties =
95                    unsafe { self.instance.get_physical_device_properties(handle) };
96                let name = unsafe { CStr::from_ptr(properties.device_name.as_ptr()) }
97                    .to_string_lossy()
98                    .into_owned();
99                Adapter::new(handle, name, properties.device_type, properties.limits)
100            })
101            .collect()
102    }
103
104    pub fn destroy(self) {
105        unsafe {
106            self.instance.destroy_instance(None);
107        }
108    }
109}
110
111pub struct DebugMessenger {
112    loader: ash::ext::debug_utils::Instance,
113    messenger: vk::DebugUtilsMessengerEXT,
114}
115
116impl DebugMessenger {
117    fn new(entry: &ash::Entry, instance: &ash::Instance) -> Result<Self, Error> {
118        let loader = ash::ext::debug_utils::Instance::new(entry, instance);
119        let create_info = vk::DebugUtilsMessengerCreateInfoEXT::default()
120            .message_severity(
121                vk::DebugUtilsMessageSeverityFlagsEXT::WARNING
122                    | vk::DebugUtilsMessageSeverityFlagsEXT::ERROR,
123            )
124            .message_type(
125                vk::DebugUtilsMessageTypeFlagsEXT::GENERAL
126                    | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION
127                    | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE,
128            )
129            .pfn_user_callback(Some(vulkan_debug_callback));
130        let messenger =
131            unsafe { loader.create_debug_utils_messenger(&create_info, None) }.map_err(vk_error)?;
132        Ok(Self { loader, messenger })
133    }
134
135    pub fn destroy(self) {
136        unsafe {
137            self.loader
138                .destroy_debug_utils_messenger(self.messenger, None);
139        }
140    }
141}
142
143unsafe extern "system" fn vulkan_debug_callback(
144    _message_severity: vk::DebugUtilsMessageSeverityFlagsEXT,
145    _message_type: vk::DebugUtilsMessageTypeFlagsEXT,
146    callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT<'_>,
147    _user_data: *mut c_void,
148) -> vk::Bool32 {
149    if !callback_data.is_null() {
150        let message_ptr = unsafe { (*callback_data).p_message };
151        if !message_ptr.is_null() {
152            let message = unsafe { CStr::from_ptr(message_ptr) };
153            eprintln!("[vulkan] {}", message.to_string_lossy());
154        }
155    }
156    vk::FALSE
157}