Skip to content

vk: enable portability enumeration on Apple platforms#2324

Open
JoeMatt wants to merge 2 commits intoflyinghead:masterfrom
Provenance-Emu:vulkan/apple-portability
Open

vk: enable portability enumeration on Apple platforms#2324
JoeMatt wants to merge 2 commits intoflyinghead:masterfrom
Provenance-Emu:vulkan/apple-portability

Conversation

@JoeMatt
Copy link
Copy Markdown

@JoeMatt JoeMatt commented Apr 22, 2026

Summary

MoltenVK is a portability driver. Starting with Vulkan loader 1.3.216 the loader no longer enumerates portability ICDs unless the application explicitly opts in via:

  • VK_KHR_portability_enumeration instance extension
  • VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR flag on VkInstanceCreateInfo

Without this, vkCreateInstance returns VK_ERROR_INCOMPATIBLE_DRIVER on macOS / iOS where MoltenVK is the only available ICD, and Flycast falls back / fails to start the Vulkan renderer at all on a fresh install with the current SDK.

The matching VK_KHR_portability_subset device extension is already enabled on device creation (under VK_ENABLE_BETA_EXTENSIONS), so this PR only fills in the missing instance side.

Scope

  • Gated on defined(__APPLE__) || defined(VK_USE_PLATFORM_METAL_EXT), so non-portability platforms are not affected.
  • Also gated on defined(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME) so older Vulkan headers continue to compile.
  • No behavior change when not using a portability ICD: the flag and extension are inert against conformant drivers.

Test plan

  • macOS / MoltenVK with current Vulkan SDK: confirm Flycast's Vulkan renderer starts cleanly (previously: VK_ERROR_INCOMPATIBLE_DRIVER).
  • Linux + RADV / NVIDIA: confirm no regression — vext does not gain the portability extension and instanceFlags stays {}.
  • Windows + NVIDIA: confirm no regression.

Made with Cursor

MoltenVK is a portability driver. Since Vulkan loader 1.3.216 the
loader no longer enumerates portability ICDs unless the application
sets VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR and enables the
VK_KHR_portability_enumeration instance extension. Without this,
vkCreateInstance returns VK_ERROR_INCOMPATIBLE_DRIVER on macOS / iOS
where MoltenVK is the only available ICD, and Flycast fails to start
the Vulkan renderer entirely.

The opt-in is gated on __APPLE__ / VK_USE_PLATFORM_METAL_EXT so that
non-portability platforms are unaffected, and on the extension macro
being defined so older Vulkan headers continue to build. The matching
VK_KHR_portability_subset device extension is already enabled on
device creation under VK_ENABLE_BETA_EXTENSIONS.

Made-with: Cursor
Copilot AI review requested due to automatic review settings April 22, 2026 05:06
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Enables Vulkan portability enumeration on Apple/Metal builds so the Vulkan loader can enumerate MoltenVK (portability) ICDs and avoid VK_ERROR_INCOMPATIBLE_DRIVER during instance creation.

Changes:

  • Adds VK_KHR_portability_enumeration to the instance extension list on Apple/Metal builds (when available in headers).
  • Sets vk::InstanceCreateFlagBits::eEnumeratePortabilityKHR on instance creation for those builds.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread core/rend/vulkan/vulkan_context.cpp Outdated
Comment on lines +191 to +197
// MoltenVK is a portability driver. Vulkan loaders >= 1.3.216
// will not enumerate portability ICDs unless the application
// opts in via VK_KHR_portability_enumeration. Without this
// instance creation fails with VK_ERROR_INCOMPATIBLE_DRIVER
// on macOS / iOS where MoltenVK is the only available ICD.
vext.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
instanceFlags |= vk::InstanceCreateFlagBits::eEnumeratePortabilityKHR;
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

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

Enabling VK_KHR_portability_enumeration unconditionally (based only on compile-time macros) can regress on systems where the app is built with newer headers but runs against an older Vulkan loader that doesn’t advertise this instance extension: vkCreateInstance would fail with VK_ERROR_EXTENSION_NOT_PRESENT even though the old loader would otherwise work without the opt-in. Consider querying available instance extensions via vkEnumerateInstanceExtensionProperties (after VULKAN_HPP_DEFAULT_DISPATCHER.init) and only appending the extension + setting eEnumeratePortabilityKHR when it’s actually reported, and also avoid adding it if it’s already present in vext to prevent duplicate extension names.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good point — applied in 530bd90. Now queries vk::enumerateInstanceExtensionProperties() and only opts in (both pushing the extension and setting eEnumeratePortabilityKHR) when the loader actually advertises VK_KHR_portability_enumeration. So a binary built against newer headers but running against a pre-1.3.216 loader stays on the old code path instead of flipping to VK_ERROR_EXTENSION_NOT_PRESENT. Also added an std::any_of check against the host-provided vext to avoid duplicate names in case the surface backend (SDL etc.) ever starts requesting it itself.

Address Copilot review feedback. Compile-time gating alone is not
enough: a binary built with newer Vulkan headers can run against an
older loader that does not advertise VK_KHR_portability_enumeration,
in which case unconditionally requesting it would flip the failure
mode from "no MoltenVK ICD enumerated" to VK_ERROR_EXTENSION_NOT_PRESENT
on otherwise-working setups.

Query vk::enumerateInstanceExtensionProperties() and only push the
extension + set eEnumeratePortabilityKHR when the loader reports it.
Also skip re-pushing if the host (SDL etc.) already requested it, so
we don't end up with duplicate names in vext.

Made-with: Cursor
@flyinghead
Copy link
Copy Markdown
Owner

Please target the dev branch . Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants