Skip to content

library: Split capabilities creation from init - #924

Open
GrinlexGH wants to merge 3 commits into
KhronosGroup:mainfrom
GrinlexGH:function-loading
Open

library: Split capabilities creation from init#924
GrinlexGH wants to merge 3 commits into
KhronosGroup:mainfrom
GrinlexGH:function-loading

Conversation

@GrinlexGH

@GrinlexGH GrinlexGH commented Aug 1, 2026

Copy link
Copy Markdown

Fixes: #734

Previously vpCreateCapabilities() both allocated the VpCapabilities object and resolved all Vulkan function pointers via a VpCapabilitiesCreateInfo argument, including apiVersion, which does not make sense outside of an instance/device context.

Split this into three explicit steps:

  • vpCreateCapabilities() now only allocates the object.
  • vpInitialize() resolves the global-level Vulkan functions (vkCreateInstance, vkEnumerateInstanceExtensionProperties, etc.), either statically or via an application-supplied GetInstanceProcAddr.
  • vpLoadInstance() resolves the instance-level functions (vkCreateDevice, vkGetPhysicalDevice*2, etc.) once a VkInstance exists, with an optional flag to fall back to the VK_KHR_get_physical_device_properties2 entry points on Vulkan 1.0 implementations.

This removes the apiVersion field, which is no longer needed for validation, and drops GetDeviceProcAddr from VpVulkanFunctions since device functions are now loaded through the instance.

VP_PROFILE_CREATE_STATIC_BIT is renamed to VP_CAPABILITIES_CREATE_STATIC_BIT and is only available
when static loader linking is possible; a new VP_CAPABILITIES_CREATE_DYNAMIC_BIT flag covers the dynamic loading case.

Update tests, the mock Vulkan API, and the generated library template to use the new two-step init/load flow.

AI Free

@ci-tester-lunarg

Copy link
Copy Markdown
Collaborator

Author GrinlexGH not on autobuild list. Waiting for curator authorization before starting CI build.

1 similar comment
@ci-tester-lunarg

Copy link
Copy Markdown
Collaborator

Author GrinlexGH not on autobuild list. Waiting for curator authorization before starting CI build.

@CLAassistant

CLAassistant commented Aug 1, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

Apply custom functions first, and change the static import to
only fill members that are still null, matching the pattern
already used by the dynamic global and instance import
functions. This makes custom function precedence a property
of the code itself instead of a side effect of call order.
@ci-tester-lunarg

Copy link
Copy Markdown
Collaborator

Author GrinlexGH not on autobuild list. Waiting for curator authorization before starting CI build.

1 similar comment
@ci-tester-lunarg

Copy link
Copy Markdown
Collaborator

Author GrinlexGH not on autobuild list. Waiting for curator authorization before starting CI build.

@christophe-lunarg

Copy link
Copy Markdown
Collaborator

I pull the branch to #926 to trigger Internal C.I. which fails on macOS run of TEST(no_prototypes, create_instance_with_dynamic_pointers).

consoleText.txt

Considering that on macOS we typically expect the Vulkan Loader and the Vulkan driver (MoltenVK or KosmicKrips) to be packaged with the Vulkan application, I can imagine DispatchLoaderDynamic would work pretty differently. Maybe it's just fine to disable the test on macOS

@ci-tester-lunarg

Copy link
Copy Markdown
Collaborator

Author GrinlexGH not on autobuild list. Waiting for curator authorization before starting CI build.

1 similar comment
@ci-tester-lunarg

Copy link
Copy Markdown
Collaborator

Author GrinlexGH not on autobuild list. Waiting for curator authorization before starting CI build.

@GrinlexGH

Copy link
Copy Markdown
Author

As stated here: https://github.com/KhronosGroup/MoltenVK#using-the-vulkan-sdk, on macOS you have to create an instance with VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR, if this fix doesn't work, you can disable this test for macOS

@christophe-lunarg christophe-lunarg left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Open to here what you think about these comments.

Comment thread library/TUTORIAL.md
Once a Vulkan instance has been created, the instance-level Vulkan functions the library needs for device-level queries and device creation (such as `vkCreateDevice` and `vkGetPhysicalDeviceFeatures2`) must be loaded by calling the following command:

```C++
VkResult vpLoadInstance(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Did you consider automatically calling this function in vpCreateInstance to load the Vulkan device functions?

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.

I copied the behavior of these functions from volk and thought about it. I initially did this because I thought it was always explicit > implicit, plus, architecturally, instance creation shouldn't be tied to initialization. In theory, if a library user decides to create multiple VpCapabilities, their initialization won't be tied to instance creation, but will the user actually do that? Also, in theory, the user could get an instance from something other than vpCreateInstance, which would also allow for proper initialization of the VpCapabilities object, but again, it's unlikely the user will do this, although they can. I don't know whether to allow this option, because the valid use of the library is a single VpCapabilities and sequential creation of instance and devices using vulkan profiles functions. In fact, this library is quite high-level and architecturally it does not harm in any way, so I can implicitly call vpLoadInstance inside vpCreateInstance, but let the user call vpLoadInstance before vpCreateInstance if he needs to

VpCapabilitiesCreateInfo createInfo{};
createInfo.flags = VP_CAPABILITIES_CREATE_STATIC_BIT;
vpInitialize(&createInfo);
vpLoadInstance(::scaffold->instance, {});

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is a little confuzing to me as removing or not this code in this test has no impact on this test.

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.

I wrote this before the implementation of possible implicit static initialization of a singleton, so it can indeed be removed

Comment thread library/TUTORIAL.md
vpInitialize(capabilities, &createInfo);
```

`VP_CAPABILITIES_CREATE_STATIC_BIT` tells the library to resolve the Vulkan functions it needs from the statically linked Vulkan loader; this flag is only available when the application links against the Vulkan loader directly (i.e. `VK_NO_PROTOTYPES` and `VP_DISABLE_STATIC_LINKING` are not defined). Applications that load Vulkan dynamically should instead use `VP_CAPABILITIES_CREATE_DYNAMIC_BIT` and provide a `VpVulkanFunctions::GetInstanceProcAddr` pointer through `pVulkanFunctions`, from which the library will resolve the remaining global-level functions it needs.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It's not clear to me what's the purpose of VP_DISABLE_STATIC_LINKING. Maybe it's just a documentation request.

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.

I created this in case the user doesn't want to use function declarations even if VK_NO_PROTOTYPES isn't defined. Although it's unlikely to be useful anywhere, I think it's best to remove it

@@ -66,11 +67,9 @@ struct Capabilities {
vulkanFunctions.CreateDevice = vkCreateDevice;

VpCapabilitiesCreateInfo createInfo;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It would have be great to keep the API backward compatible.

I created VP_USE_OBJECT only for this purpose but it's something I'd like to remove and have the Vulkan developer responsible for the loading and the Vulkan API.


#include "mock_vulkan_api.hpp"
#include "test_vulkan_profiles.hpp"
#include "mock_vulkan_api.hpp"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Any specific reason for this switch in the lines?

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.

The tests use different versions of the vulkan_profiles header, so mock_vulkan_api.hpp intentionally does not include them, although it is required for mocking functions

Comment thread library/TUTORIAL.md
- [API reference](#api-reference)
- [Preprocessor definitions](#preprocessor-definitions)
- [Profile support and usage](#profile-support-and-usage)
- [Initializing capabilities](#initializing-capabilities)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think the vpLoadInstance and vpInitialize functions are a little be confusing to me. Probably just an naming issue.

Maybe the VpCapabilities_T should be completely redesigned and considering it's not enabled by default I think it would be ok. Probably VpCapabilities is not a great name either and in a first place. Maybe VpInstance?

For example, we could create a per VkInstance table to store the function loaded with ImportInstanceVulkanFunctions_Dynamic.

I could picture something like vpLoadGlobalFunc replacing vpInitialize:

VpVulkanFunctions vulkanFunctions;
vpLoadGlobalFunc(dl.vkGetInstanceProcAddr, vulkanFunctions);

Where vpLoadGlobalFunc is just a helper function to fill global functions using dl.vkGetInstanceProcAddr. This leave the posibility for Vulkan developer to fill either function manually.

vpCreateInstance would return VK_ERROR_INITIALIZATION_FAILED if pVulkanFunctions is not initialized/validated when VK_NO_PROTOTYPES and VP_DISABLE_STATIC_LINKING are not defined.

Otherwise, vpCreateInstance would use the static function automatically.

@GrinlexGH GrinlexGH Aug 3, 2026

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.

I think a table for instance -> VpCapabilities object would be an unnecessarily complex functionality
It's a good idea to use the vpLoadGlobalFunc helper. This could eliminate the _STATIC/_DYNAMIC_BIT altogether.
I think it could look something like this:

// VK_NO_PROTOTYPES may be defined

VpVulkanFunctions vulkanFunctions;

// Loads only global dynamic functions
vpLoadGlobalFunc(dl.vkGetInstanceProcAddr, &vulkanFunctions);

VpCapabilities capabilities {};
VpCapabilitiesCreateInfo createInfo;

createInfo.pVulkanFunctions = vulkanFunctions;

// If global functions have not been loaded, it will try to load static versions, else will return an error

// If global functions have been loaded, it will load the remaining functions in vpCreateInstance via vkGetInstanceProcAddr, and will not try to load static functions

// by the way if you rename VpCapabilities to VpInstance, you will create a conflict in function names :(
vpCreateCapabilities(&createInfo, nullptr, &capabilities);

By the way, this will also solve the problem of backward compatibility, so apiVersion and flags can be deprecated

but how to dynamically initialize a singleton in this case?

@christophe-lunarg

Copy link
Copy Markdown
Collaborator

As stated here: https://github.com/KhronosGroup/MoltenVK#using-the-vulkan-sdk, on macOS you have to create an instance with VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR, if this fix doesn't work, you can disable this test for macOS

Ah yes very good point. I ran again the internal C.I. tests.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

vulkan_profiles.hpp does not compile when VK_NO_PROTOTYPES is defined

4 participants