diff --git a/Cargo.toml b/Cargo.toml index 562e33e..cd58fd0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ description = "An easy-to-use Vulkan rendering engine in the spirit of QBasic." [features] default = [] +macos-dynamic-molten-vk=[] profile-with-puffin = ["profiling/profile-with-puffin"] profile-with-optick = ["profiling/profile-with-optick"] profile-with-superluminal = ["profiling/profile-with-superluminal"] diff --git a/src/driver/device.rs b/src/driver/device.rs index 7ea91c1..5b4b05b 100644 --- a/src/driver/device.rs +++ b/src/driver/device.rs @@ -75,7 +75,7 @@ impl Device { where F: FnOnce(vk::DeviceCreateInfo) -> ash::prelude::VkResult, { - let mut enabled_ext_names = Vec::with_capacity(6); + let mut enabled_ext_names = Vec::with_capacity(7); if display_window { enabled_ext_names.push(khr::swapchain::NAME.as_ptr()); @@ -98,6 +98,12 @@ impl Device { enabled_ext_names.push(ext::index_type_uint8::NAME.as_ptr()); } + // Molten-vk doesn't support the full Vulkan feature set, hence the portability subset extension must be enabled. + #[cfg(all(target_os = "macos", feature = "macos-dynamic-molten-vk"))] + { + enabled_ext_names.push(khr::portability_subset::NAME.as_ptr()); + } + let priorities = repeat_n( 1.0, physical_device diff --git a/src/driver/instance.rs b/src/driver/instance.rs index 2974df4..3506aa7 100644 --- a/src/driver/instance.rs +++ b/src/driver/instance.rs @@ -11,7 +11,7 @@ use { }, }; -#[cfg(not(target_os = "macos"))] +#[cfg(any(not(target_os = "macos"), feature = "macos-dynamic-molten-vk"))] use { log::{Level, Metadata, info, logger}, std::{ @@ -25,7 +25,7 @@ use { #[cfg(target_os = "macos")] use std::env::set_var; -#[cfg(not(target_os = "macos"))] +#[cfg(any(not(target_os = "macos"), feature = "macos-dynamic-molten-vk"))] unsafe extern "system" fn vulkan_debug_callback( _flags: vk::DebugReportFlagsEXT, _obj_type: vk::DebugReportObjectTypeEXT, @@ -136,7 +136,8 @@ impl Instance { set_var("MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS", "1"); } - #[cfg(not(target_os = "macos"))] + // Link molten-vk dynamically if not on MacOS, or if explicitly requested. + #[cfg(any(not(target_os = "macos"), feature = "macos-dynamic-molten-vk"))] let entry = unsafe { Entry::load().map_err(|err| { error!("Vulkan driver not found: {err}"); @@ -144,11 +145,22 @@ impl Instance { DriverError::Unsupported })? }; - - #[cfg(target_os = "macos")] + // On MacOS, by default link molten-vk statically using ash-molten. + #[cfg(all(target_os = "macos", not(feature = "macos-dynamic-molten-vk")))] let entry = ash_molten::load(); - let required_extensions = required_extensions.collect::>(); + #[allow(unused_mut)] + let mut required_extensions = required_extensions.collect::>(); + + // If linking dynamically on MacOS, we require a few additional extensions. + // Based on "Encountered VK_ERROR_INCOMPATIBLE_DRIVER" section in: + // https://vulkan.lunarg.com/doc/view/latest/mac/getting_started.html + #[cfg(all(target_os = "macos", feature = "macos-dynamic-molten-vk"))] + { + required_extensions.push(ash::khr::get_physical_device_properties2::NAME); + required_extensions.push(ash::khr::portability_enumeration::NAME); + } + let instance_extensions = required_extensions .iter() .map(|ext| ext.as_ptr()) @@ -165,32 +177,38 @@ impl Instance { .enabled_layer_names(&layer_names) .enabled_extension_names(&instance_extensions); - let instance = unsafe { - entry.create_instance(&instance_desc, None).map_err(|_| { - if debug { - warn!("debug may only be enabled with a valid Vulkan SDK installation"); - } + // Molten-vk doesn't support the full Vulkan feature set, hence the portability flag needs to be set. + #[cfg(all(target_os = "macos", feature = "macos-dynamic-molten-vk"))] + let instance_desc = instance_desc.flags(vk::InstanceCreateFlags::ENUMERATE_PORTABILITY_KHR); - error!("Vulkan driver does not support API v1.2"); + let instance = unsafe { + entry + .create_instance(&instance_desc, None) + .map_err(|vkerr| { + if debug { + warn!("debug may only be enabled with a valid Vulkan SDK installation"); + } + error!("Vulkan Error: {}", vkerr); + error!("Vulkan driver does not support API v1.2"); - for layer_name in Self::layer_names(debug) { - debug!("Layer: {:?}", layer_name); - } + for layer_name in Self::layer_names(debug) { + debug!("Layer: {:?}", layer_name); + } - for extension_name in required_extensions { - debug!("Extension: {:?}", extension_name); - } + for extension_name in required_extensions { + debug!("Extension: {:?}", extension_name); + } - DriverError::Unsupported - })? + DriverError::Unsupported + })? }; trace!("created a Vulkan instance"); - #[cfg(target_os = "macos")] + #[cfg(all(target_os = "macos", not(feature = "macos-dynamic-molten-vk")))] let (debug_loader, debug_callback, debug_utils) = (None, None, None); - #[cfg(not(target_os = "macos"))] + #[cfg(any(not(target_os = "macos"), feature = "macos-dynamic-molten-vk"))] let (debug_loader, debug_callback, debug_utils) = if debug { let debug_info = vk::DebugReportCallbackCreateInfoEXT { flags: vk::DebugReportFlagsEXT::ERROR @@ -255,17 +273,20 @@ impl Instance { unsafe fn extension_names( #[cfg_attr(target_os = "macos", allow(unused_variables))] debug: bool, ) -> Vec<*const c_char> { - #[cfg_attr(target_os = "macos", allow(unused_mut))] - let mut res = vec![]; - - #[cfg(not(target_os = "macos"))] - if debug { - #[allow(deprecated)] - res.push(ext::debug_report::NAME.as_ptr()); - res.push(ext::debug_utils::NAME.as_ptr()); + if cfg!(all( + target_os = "macos", + not(feature = "macos-dynamic-molten-vk") + )) { + vec![] + } else { + let mut res = vec![]; + if debug { + #[allow(deprecated)] + res.push(ext::debug_report::NAME.as_ptr()); + res.push(ext::debug_utils::NAME.as_ptr()); + } + res } - - res } /// Returns `true` if this instance was created with debug layers enabled. @@ -276,15 +297,18 @@ impl Instance { fn layer_names( #[cfg_attr(target_os = "macos", allow(unused_variables))] debug: bool, ) -> Vec { - #[cfg_attr(target_os = "macos", allow(unused_mut))] - let mut res = vec![]; - - #[cfg(not(target_os = "macos"))] - if debug { - res.push(CString::new("VK_LAYER_KHRONOS_validation").unwrap()); + if cfg!(all( + target_os = "macos", + not(feature = "macos-dynamic-molten-vk") + )) { + vec![] + } else { + let mut res = vec![]; + if debug { + res.push(CString::new("VK_LAYER_KHRONOS_validation").unwrap()); + } + res } - - res } /// Returns the available physical devices of this instance.