Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
8 changes: 7 additions & 1 deletion src/driver/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ impl Device {
where
F: FnOnce(vk::DeviceCreateInfo) -> ash::prelude::VkResult<ash::Device>,
{
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());
Expand All @@ -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());
}

Comment on lines +101 to +106
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be enabled for target_os = "macos" regardless of macos-dynamic-molten-vk? Same question below for ash::khr::portability_enumeration, etc...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

@karhu karhu May 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll leave a longer top-level comment with more details, but in short I've tried that, but it errors out at runtime when requesting those extensions, but not linking dynamically. The error states that "Vulkan Error: Extension specified does not exist."

I suspect (not verified though) this is because screen-13 defaults to using ash-molten, which in turn statically links against an outdated version of MoltenVK.

let priorities = repeat_n(
1.0,
physical_device
Expand Down
104 changes: 64 additions & 40 deletions src/driver/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand All @@ -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,
Expand Down Expand Up @@ -136,19 +136,31 @@ 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}");

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::<Vec<_>>();
#[allow(unused_mut)]
let mut required_extensions = required_extensions.collect::<Vec<_>>();

// 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())
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -276,15 +297,18 @@ impl Instance {
fn layer_names(
#[cfg_attr(target_os = "macos", allow(unused_variables))] debug: bool,
) -> Vec<CString> {
#[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.
Expand Down
Loading