Skip to content

Conversation

@SuperRonan
Copy link

@SuperRonan SuperRonan commented Jan 7, 2026

Currently, due to limitations of C99, 64-bit flags (such as VkAccessFlagBits2) are defined as alias of VkFlags64, and global static const variables. More recent standards (such as C++11 and C23) allow enumerations with an explicit underlying type.

This PR proposes to add macros to define 64-bit flags in vulkan_core.h in a flexible way, depending on the langage capabilities, and the application preferences:

#ifndef VK_USE_64_BIT_FLAGS_ENUMS
    #if (defined(__cplusplus) && (__cplusplus >= 201103L)) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L)) || (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 202311L))
        #define VK_USE_64_BIT_FLAGS_ENUMS 1
    #else
        #define VK_USE_64_BIT_FLAGS_ENUMS 0
    #endif
#endif

#ifndef VK_BEGIN_ENUMERATION
    #if VK_USE_64_BIT_FLAGS_ENUMS
        #define VK_BEGIN_ENUMERATION(enum_name, underlying_type) typedef enum enum_name : underlying_type {
    #else
        #define VK_BEGIN_ENUMERATION(enum_name, underlying_type) typedef underlying_type enum_name;
    #endif
#endif

#ifndef VK_DEFINE_ENUM_VALUE
    #if VK_USE_64_BIT_FLAGS_ENUMS
        #define VK_DEFINE_ENUM_VALUE(enum_name, value_name, value) value_name = value,
    #else
        #define VK_DEFINE_ENUM_VALUE(enum_name, value_name, value) static const enum_name value_name = value;
    #endif
#endif

#ifndef VK_END_ENUMERATION
    #if VK_USE_64_BIT_FLAGS_ENUMS
        #define VK_END_ENUMERATION(enum_name, underlying_type) } enum_name;
    #else
        #define VK_END_ENUMERATION(enum_name, underlying_type)
    #endif
#endif

Declaration of 64-bit flags would become:

// Flag bits for VkPipelineStageFlagBits2
VK_BEGIN_ENUMERATION(VkPipelineStageFlagBits2, VkFlags64)
VK_DEFINE_ENUM_VALUE(VkPipelineStageFlagBits2, VK_PIPELINE_STAGE_2_NONE, 0ULL)
VK_DEFINE_ENUM_VALUE(VkPipelineStageFlagBits2, VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT, 0x00000001ULL)
VK_DEFINE_ENUM_VALUE(VkPipelineStageFlagBits2, VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT, 0x00000002ULL)
VK_DEFINE_ENUM_VALUE(VkPipelineStageFlagBits2, VK_PIPELINE_STAGE_2_VERTEX_INPUT_BIT, 0x00000004ULL)
/// ....
VK_END_ENUMERATION(VkPipelineStageFlagBits2, VkFlags64)

The above definition in C is still quite readable, but unfortunately a bit less than directly using enums or static constants.
Each individual macro of VK_BEGIN_ENUMERATION, VK_DEFINE_ENUM_VALUE and VK_END_ENUMERATION can be defined by the application to suit its needs.
An additional macro VK_USE_64_BIT_FLAGS_ENUMS is defined as a shorcut to decide how the above macros are defined by default (similar to how VK_USE_64_BIT_PTR_DEFINES decides the default definition of VK_DEFINE_NON_DISPATCHABLE_HANDLE)

This PR should not bring any API breaking change.
The only issue I would see is if a script parses vulkan_core.h to extract bitfields values, it would probably break.

I am open to feedback and suggestions.


Note:
In appendices/boilerplate.adoc, I used version 338 for when the macros were added to the specification. This will need to be changed if this PR is accepted for a later version.

- Add definitions `VK_USE_64_BIT_FLAGS_ENUMS`, `VK_BEGIN_ENUMERATION`, `VK_DEFINE_ENUM_VALUE` and `VK_END_ENUMERATION` to control definition of 64 bit enum-like constants
@CLAassistant
Copy link

CLAassistant commented Jan 7, 2026

CLA assistant check
All committers have signed the CLA.

@oddhack
Copy link
Contributor

oddhack commented Jan 8, 2026

I'm not clear on how much type safety this would provide. There are relatively few places in the API where a single Vk*FlagBits value is passed around by type, compared to the places where bits are ORed together to generate a mask value (roughly 1:5, I think). Having the underlying enum type does not do much for likely sorts of errors with 32-bit masks today:

// Combine two unrelated bitflags into a mask
VkAccessFlags flags = VK_ACCESS_INDEX_READ_BIT | VK_IMAGE_ASPECT_COLOR_BIT;

or even just

// Assign a bitflag which does not correspond to the mask type
VkAccessFlags flags = VK_IMAGE_ASPECT_COLOR_BIT;

Would there be anything different about compiler behavior using the syntax you're suggesting for 64-bit flags?

Also to note that today, I do not think there are any APIs accepting individual 64-bit flag bits - they are all masks of these bits. Of course that could change in the future and I may have missed something in a cursory grep of the XML. But in general, we introduce the 64-bit flag types when an existing 32-bit type has run out of bits, and to date those are all open-ended things like usage / access / creation / format flags, while the places where individual flag bits are passed around are mostly more constrained stuff like external memory handle types or resolve modes. Coincidentally we are currently looking at which flag types are likely to need new 64-bit types in the relatively near term, and they are also all usage / creation / format flags.

@SuperRonan
Copy link
Author

Indeed this PR is not about type safety. Ensuring it is the points of vulkan_enums.hpp (which extensively uses enum class and operator overloading). Note also that type safety is no more enforced by VkAccessFlagBits than by VkAccessFlagBits2.

Having the possibility of defining 64-bit bitfields as enums is more about consistency with their 32-bit counterpart. It also allows to have a dedicated types for template specialization in C++ (which is not possible for example whith VkAccessFlagBits2 if it is just an alias of VkFlags64).

@SuperRonan
Copy link
Author

SuperRonan commented Jan 8, 2026

Concerning the CI failures:

  • Run consistency checks fails because of out of date copyright. I fixed it locally, but I don't think this PR should push those changes.
  • Generate the vulkan C++ header fails because a hardcoded set of macros to ignore { "VK_DEFINE_NON_DISPATCHABLE_HANDLE", "VK_NULL_HANDLE", "VK_USE_64_BIT_PTR_DEFINES" } does not contain the new macros of this PR. I checked locally that the problem is fixed if I add this PR's new macros to the set. I can create a PR to Vulkan-Hpp once this one is validated.
  • Compile with vulkan.hpp for C++11~20 seem to fail because the C++ header was not generated.

@SuperRonan
Copy link
Author

SuperRonan commented Jan 8, 2026

I also have an alternative version ready:

#ifndef VK_BEGIN_ENUMERATION
    #if VK_USE_64_BIT_FLAGS_ENUMS
        #define VK_BEGIN_ENUMERATION(enum_name, underlying_type) typedef enum enum_name : underlying_type {
    #else
        #define VK_BEGIN_ENUMERATION(enum_name, underlying_type) typedef underlying_type enum_name; static const enum_name 
    #endif
#endif

#ifndef VK_END_ENUMERATION
    #if VK_USE_64_BIT_FLAGS_ENUMS
        #define VK_END_ENUMERATION(enum_name, underlying_type) } enum_name;
    #else
        #define VK_END_ENUMERATION(enum_name, underlying_type) _##enum_name##_CLOSING = 0;
    #endif
#endif

// Flag bits for VkPipelineStageFlagBits2
VK_BEGIN_ENUMERATION(VkPipelineStageFlagBits2, VkFlags64)
    VK_PIPELINE_STAGE_2_NONE = 0ULL,
    VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT = 0x00000001ULL,
    VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT = 0x00000002ULL,
    VK_PIPELINE_STAGE_2_VERTEX_INPUT_BIT = 0x00000004ULL,
    /// ....
VK_END_ENUMERATION(VkPipelineStageFlagBits2, VkFlags64)

It removes macro VK_DEFINE_ENUM_VALUE and makes the definition in C more readable (like any other enum).
But this version has two drawbacks:

  • It is more constrained to declare only a sequence of values of the same type.
  • In the case when VK_USE_64_BIT_FLAGS_ENUMS is 0, VK_END_ENUMERATION is forced to declare a dummy value at the end for syntax correctness (a value must be declared between the ',' of the previous and the ';', or if the enum if empty (such as VkDataGraphPipelineDispatchFlagBitsARM)). We could explicitely declare this last value with something like MAX_ENUM if that is a better solution.

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