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}