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