1use {
2 super::{DriverError, physical_device::PhysicalDevice},
3 ash::{Entry, ext, vk},
4 log::{debug, error, trace, warn},
5 std::{
6 ffi::{CStr, CString},
7 fmt::{Debug, Formatter},
8 ops::Deref,
9 os::raw::c_char,
10 thread::panicking,
11 },
12};
13
14#[cfg(not(target_os = "macos"))]
15use {
16 log::{Level, Metadata, info, logger},
17 std::{
18 env::var,
19 ffi::c_void,
20 process::{abort, id},
21 thread::{current, park},
22 },
23};
24
25#[cfg(target_os = "macos")]
26use std::env::set_var;
27
28#[cfg(not(target_os = "macos"))]
29unsafe extern "system" fn vulkan_debug_callback(
30 _flags: vk::DebugReportFlagsEXT,
31 _obj_type: vk::DebugReportObjectTypeEXT,
32 _src_obj: u64,
33 _location: usize,
34 _msg_code: i32,
35 _layer_prefix: *const c_char,
36 message: *const c_char,
37 _user_data: *mut c_void,
38) -> u32 {
39 if panicking() {
40 return vk::FALSE;
41 }
42
43 assert!(!message.is_null());
44
45 let mut found_null = false;
46 for i in 0..u16::MAX as _ {
47 if unsafe { *message.add(i) } == 0 {
48 found_null = true;
49 break;
50 }
51 }
52
53 assert!(found_null);
54
55 let message = unsafe { CStr::from_ptr(message) }.to_str().unwrap();
56
57 if message.starts_with("Validation Warning: [ UNASSIGNED-BestPractices-pipeline-stage-flags ]")
58 {
59 warn!("{}", message);
61 } else {
62 let prefix = "Validation Error: [ ";
63
64 let (vuid, message) = if message.starts_with(prefix) {
65 let (vuid, message) = message
66 .trim_start_matches(prefix)
67 .split_once(" ]")
68 .unwrap_or_default();
69 let message = message.split(" | ").nth(2).unwrap_or(message);
70
71 (Some(vuid.trim()), message)
72 } else {
73 (None, message)
74 };
75
76 if let Some(vuid) = vuid {
77 info!("{vuid}");
78 }
79
80 error!("🆘 {message}");
81
82 if !logger().enabled(&Metadata::builder().level(Level::Debug).build())
83 || var("RUST_LOG")
84 .map(|rust_log| rust_log.is_empty())
85 .unwrap_or(true)
86 {
87 eprintln!(
88 "note: run with `RUST_LOG=trace` environment variable to display more information"
89 );
90 eprintln!("note: see https://github.com/rust-lang/log#in-executables");
91 abort()
92 }
93
94 if current().name() != Some("main") {
95 warn!("executing on a child thread!")
96 }
97
98 debug!(
99 "🛑 PARKING THREAD `{}` -> attach debugger to pid {}!",
100 current().name().unwrap_or_default(),
101 id()
102 );
103
104 logger().flush();
105
106 park();
107 }
108
109 vk::FALSE
110}
111
112pub struct Instance {
118 _debug_callback: Option<vk::DebugReportCallbackEXT>,
119 #[allow(deprecated)] _debug_loader: Option<ext::debug_report::Instance>,
121 debug_utils: Option<ext::debug_utils::Instance>,
122 entry: Entry,
123 instance: ash::Instance,
124}
125
126impl Instance {
127 #[profiling::function]
129 pub fn create<'a>(
130 debug: bool,
131 required_extensions: impl Iterator<Item = &'a CStr>,
132 ) -> Result<Self, DriverError> {
133 #[cfg(target_os = "macos")]
135 unsafe {
136 set_var("MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS", "1");
137 }
138
139 #[cfg(not(target_os = "macos"))]
140 let entry = unsafe {
141 Entry::load().map_err(|err| {
142 error!("Vulkan driver not found: {err}");
143
144 DriverError::Unsupported
145 })?
146 };
147
148 #[cfg(target_os = "macos")]
149 let entry = ash_molten::load();
150
151 let required_extensions = required_extensions.collect::<Vec<_>>();
152 let instance_extensions = required_extensions
153 .iter()
154 .map(|ext| ext.as_ptr())
155 .chain(unsafe { Self::extension_names(debug).into_iter() })
156 .collect::<Box<[_]>>();
157 let layer_names = Self::layer_names(debug);
158 let layer_names: Vec<*const c_char> = layer_names
159 .iter()
160 .map(|raw_name| raw_name.as_ptr())
161 .collect();
162 let app_desc = vk::ApplicationInfo::default().api_version(vk::API_VERSION_1_2);
163 let instance_desc = vk::InstanceCreateInfo::default()
164 .application_info(&app_desc)
165 .enabled_layer_names(&layer_names)
166 .enabled_extension_names(&instance_extensions);
167
168 let instance = unsafe {
169 entry.create_instance(&instance_desc, None).map_err(|_| {
170 if debug {
171 warn!("debug may only be enabled with a valid Vulkan SDK installation");
172 }
173
174 error!("Vulkan driver does not support API v1.2");
175
176 for layer_name in Self::layer_names(debug) {
177 debug!("Layer: {:?}", layer_name);
178 }
179
180 for extension_name in required_extensions {
181 debug!("Extension: {:?}", extension_name);
182 }
183
184 DriverError::Unsupported
185 })?
186 };
187
188 trace!("created a Vulkan instance");
189
190 #[cfg(target_os = "macos")]
191 let (debug_loader, debug_callback, debug_utils) = (None, None, None);
192
193 #[cfg(not(target_os = "macos"))]
194 let (debug_loader, debug_callback, debug_utils) = if debug {
195 let debug_info = vk::DebugReportCallbackCreateInfoEXT {
196 flags: vk::DebugReportFlagsEXT::ERROR
197 | vk::DebugReportFlagsEXT::WARNING
198 | vk::DebugReportFlagsEXT::PERFORMANCE_WARNING,
199 pfn_callback: Some(vulkan_debug_callback),
200 ..Default::default()
201 };
202
203 #[allow(deprecated)]
204 let debug_loader = ext::debug_report::Instance::new(&entry, &instance);
205
206 let debug_callback = unsafe {
207 #[allow(deprecated)]
208 debug_loader
209 .create_debug_report_callback(&debug_info, None)
210 .unwrap()
211 };
212
213 let debug_utils = ext::debug_utils::Instance::new(&entry, &instance);
214
215 (Some(debug_loader), Some(debug_callback), Some(debug_utils))
216 } else {
217 (None, None, None)
218 };
219
220 Ok(Self {
221 _debug_callback: debug_callback,
222 _debug_loader: debug_loader,
223 debug_utils,
224 entry,
225 instance,
226 })
227 }
228
229 #[profiling::function]
234 pub fn load(entry: Entry, instance: vk::Instance) -> Result<Self, DriverError> {
235 if instance == vk::Instance::null() {
236 return Err(DriverError::InvalidData);
237 }
238
239 let instance = unsafe { ash::Instance::load(entry.static_fn(), instance) };
240
241 Ok(Self {
242 _debug_callback: None,
243 _debug_loader: None,
244 debug_utils: None,
245 entry,
246 instance,
247 })
248 }
249
250 pub fn entry(this: &Self) -> &Entry {
252 &this.entry
253 }
254
255 unsafe fn extension_names(
256 #[cfg_attr(target_os = "macos", allow(unused_variables))] debug: bool,
257 ) -> Vec<*const c_char> {
258 #[cfg_attr(target_os = "macos", allow(unused_mut))]
259 let mut res = vec![];
260
261 #[cfg(not(target_os = "macos"))]
262 if debug {
263 #[allow(deprecated)]
264 res.push(ext::debug_report::NAME.as_ptr());
265 res.push(ext::debug_utils::NAME.as_ptr());
266 }
267
268 res
269 }
270
271 pub fn is_debug(this: &Self) -> bool {
273 this.debug_utils.is_some()
274 }
275
276 fn layer_names(
277 #[cfg_attr(target_os = "macos", allow(unused_variables))] debug: bool,
278 ) -> Vec<CString> {
279 #[cfg_attr(target_os = "macos", allow(unused_mut))]
280 let mut res = vec![];
281
282 #[cfg(not(target_os = "macos"))]
283 if debug {
284 res.push(CString::new("VK_LAYER_KHRONOS_validation").unwrap());
285 }
286
287 res
288 }
289
290 #[profiling::function]
292 pub fn physical_devices(this: &Self) -> Result<Vec<PhysicalDevice>, DriverError> {
293 let physical_devices = unsafe { this.enumerate_physical_devices() };
294
295 Ok(physical_devices
296 .map_err(|err| {
297 error!("unable to enumerate physical devices: {err}");
298
299 DriverError::Unsupported
300 })?
301 .into_iter()
302 .enumerate()
303 .filter_map(|(idx, physical_device)| {
304 let res = PhysicalDevice::new(this, physical_device);
305
306 if let Err(err) = &res {
307 warn!("unable to create physical device at index {idx}: {err}");
308 }
309
310 res.ok().filter(|physical_device| {
311 let major = vk::api_version_major(physical_device.properties_v1_0.api_version);
312 let minor = vk::api_version_minor(physical_device.properties_v1_0.api_version);
313 let supports_vulkan_1_2 = major > 1 || (major == 1 && minor >= 2);
314
315 if !supports_vulkan_1_2 {
316 warn!(
317 "physical device `{}` does not support Vulkan v1.2",
318 physical_device.properties_v1_0.device_name
319 );
320 }
321
322 supports_vulkan_1_2
323 })
324 })
325 .collect())
326 }
327}
328
329impl Debug for Instance {
330 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
331 f.write_str("Instance")
332 }
333}
334
335impl Deref for Instance {
336 type Target = ash::Instance;
337
338 fn deref(&self) -> &Self::Target {
339 &self.instance
340 }
341}
342
343impl Drop for Instance {
344 #[profiling::function]
345 fn drop(&mut self) {
346 if panicking() {
347 return;
348 }
349
350 unsafe {
351 #[allow(deprecated)]
352 if let Some(debug_loader) = &self._debug_loader {
353 let debug_callback = self._debug_callback.unwrap();
354 debug_loader.destroy_debug_report_callback(debug_callback, None);
355 }
356
357 self.instance.destroy_instance(None);
358 }
359 }
360}