How to properly test for Vulkan version with preprocessor

How do I test using the preprocessor if my application links to either:

  • Vulkan version 1.3 or higher
  • or to an older Vulkan version?

Attempt 1:

#ifdef VK_VERSION_1_3
#else
#endif

This ^ has the problem that it won’t catch Vulkan version 1.4.

Attempt 2:
I could use the VK_HEADER_VERSION, but which header version is the right one for Vulkan 1.3?

Attempt 3:

#if defined(VK_HEADER_VERSION_COMPLETE) && VK_HEADER_VERSION_COMPLETE >= VK_MAKE_API_VERSION(0, 1, 3, 0)
#else
#endif

This ^ seems right, but I can’t get my head around the compiler errors I’m getting, which there are:

E0059 function call is not allowed in a constant expression
C1012 unmatched parenthesis: missing ')'

What am I missing/not seeing?

This ^ has the problem that it won’t catch Vulkan version 1.4.

It will.

What am I missing/not seeing?

#if and preprocessor comparison operators cannot eval typecasts, which are a compiler entities

You don’t.

Your application does not link (statically) to Vulkan of any version. It may link to the Vulkan SDK of some version, but that’s not what you want to test. There’s no need to test what version of the SDK you link to because… you are linking to it. You know what version you compiled with because it’s your build system.

Well, you might want some kind of static-assert-equivalent to make sure you didn’t accidentally link to an older SDK version, but otherwise, you know what you linked to because you asked to link to it.

When you do #ifdef VK_VERSION_1_3, what you’re doing is asking if you are compiling against a version of the Vulkan SDK that exposes Vulkan 1.3 directly. That says nothing about whether the implementation you’re running on actually supports Vulkan 1.3. That is a runtime question, and the preprocessor has long-since been done with your code by the time you can ask it.

And note that, because all Vulkan versions of the same major version number are required to be backwards-compatible with lower minor versions, #ifdef VK_VERSION_1_3 will be valid even if the SDK also exposes Vulkan 1.4 directly.

Hmm, okay then maybe I’m following the wrong approach. ^^

I thought it would be a good idea to write code like the following:

#ifdef VK_VERSION_1_3
vkCmdWriteTimestamp2(...);
#else
vkCmdWriteTimestamp2KHR(...);
#endif

in order to keep supporting older SDKs. In the example, vkCmdWriteTimestamp2 is available with Vulkan 1.3. With older SDK versions, one has to use vkCmdWriteTimestamp2KHR and the VK_KHR_synchronization2 extension.

Anything wrong with that approach of mine?
Is it better to only support the latest SDKs always? :thinking:
What about desktop + Android support—isn’t it the case that Android supports only up to Vulkan 1.2 or so?

Writing code that way does not buy you much in my opinion. Think about what this means for someone compiling with a new SDK: your application now requires as a minimum Vulkan 1.3 support at runtime - so execution on hardware (or drivers) that do not provide that will fail.
If on the other hand that same person compiles with an old SDK your app only requires some older Vulkan version at runtime and it can run on that same hardware it failed on with the new SDK.
Instead, if you always require an SDK that is at least as new as the minimum Vulkan version you require at runtime you can still check for availability of a newer version and take advantage of it - but it will be a runtime check.
After all, the hardware you build the app on matters far less than the hardware the app runs on and the latter you can only test/branch for at runtime.

Hmm, I’m trying to find out the best practice here.

Maybe I should explain the situation:
I’m developing a framework that shall be usable by other developers. It would be cool if those developers were able to build the framework using different SDK versions, e.g., SDK 1.2 and SDK 1.3 (especially interesting/important around the time of major version changes).
So, I thought it would be a good idea to #if either on the VK_VERSION_..., or the VK_HEADER_VERSION, or something similar (hence, this question).
Is it not?

One more general question:
If I link with SDK 1.3 and use, e.g., vkCmdWriteTimestamp2, but then I run my application on hardware that only supports Vulkan 1.2, that automatically translates to—so to say—enabling VK_KHR_synchronization2 and calling vkCmdWriteTimestamp2KHR, right?
I.e., I can use SDK 1.3 to build an application, use all the SDK 1.3 core features, and run that just fine on, e.g., an Android device that supports only Vulkan 1.2 or 1.1, if that device supports all the used features.
Is this :point_up_2: assumption accurate?

No, it does not.

You cannot equate the SDK version with the Vulkan implementation version. The SDK version is compile-time; the Vulkan version is runtime.

The SDK version does not require a Vulkan implementation to support any particular Vulkan version. You can link to the 1.3 SDK and use a Vulkan 1.0 implementation just fine, as long as you detect this and don’t call functions which Vulkan 1.0 doesn’t support.

This also means that the Vulkan SDK does not impose any expectation that any particular Vulkan interface function will actually work. You statically link to those APIs, but the backend of any particular function will only actually work if the implementation supports it.

To my knowledge, the SDK does not include any automatic switchover support for command aliases. Therefore, it is on you to detect that Vulkan 1.3 is not supported, detect that KHR_synchronization2 is supported, enable that extension, and then use the KHR functions instead of the core functions. None of this is automatic.

I see. Thanks a lot!
I.e., that would also mean that I cannot just put an #if somewhere and invoke either vkCmdWriteTimestamp2 or vkCmdWriteTimestamp2KHR, because if I linked to the 1.3 SDK but then ran it on a device that supports only up to Vulkan 1.2, I would invoke vkCmdWriteTimestamp2, which I must expect to fail.

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.