Skip to content

Conversation

@sharadhr
Copy link
Contributor

@sharadhr sharadhr commented Jan 17, 2026

Resolves #2366 and resolves #2379.

Also resolves #2447 and resolves #2448.

Starts with an initial experiment with vulkan:video, which seems to work pretty well with the MODULE_Video test.

The only issue is the duplicated #includes in both the .hpp and the .module, but I would rather duplicated headers than all the issues with the using statements. The module interface file is also so much shorter now.

@sharadhr sharadhr force-pushed the vulkan-cppm-abi-breaking-style branch from a7c5fbb to 3527223 Compare January 17, 2026 00:35
Copy link
Contributor

@M2-TE M2-TE left a comment

Choose a reason for hiding this comment

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

Simply VULKAN_HPP_EXPORTing the entire namespace is a good solution!

The real test would be to run the Handles and ArrayWrapper module tests (once you have the vulkan module changes), as those currently don't work due to lack of exported operators.

@sharadhr
Copy link
Contributor Author

I've actually got to the next step and passed all module tests locally, but it's a bit of a mess because vulkan.hpp is a common header between module vulkan and the video partition.

We would have to drop the partition altogether, directly include vulkan_video.hpp in the main cppm, and guard the include of vulkan.hpp.

Now, this is OK when manually editing the files (I should commit and push what I have, coming shortly...), but generating the headers is a problem, especially when it comes to the has_include stuff.

@M2-TE
Copy link
Contributor

M2-TE commented Jan 18, 2026

I've actually got to the next step and passed all module tests locally

The tests I mentioned before are currently disabled, you have to manually uncomment them in tests/CMakeLists.txt. Unless you already did that, of course.

We would have to drop the partition altogether, directly include vulkan_video.hpp in the main cppm, and guard the include of vulkan.hpp

I don't think that would be too much of a problem. The video module is kind of unnecessary when all the export statements are in the hpp headers anyways.

@sharadhr
Copy link
Contributor Author

sharadhr commented Jan 18, 2026

I don't think that would be too much of a problem.

That's not the biggest issue, but it's generating video-related stuff in the VulkanHppGenerator.cpp that is more problematic. I suppose I can instantiate the object of VideoHppGenerator.cpp in the former, but I'd like to adhere to single-responsibility, and having the normal generator depend on the video stuff is... not that.

In fact it might be better for vulkan_video.cppm to return to being a separate module and export import vulkan rather than the other way round, because that is the dependency order.

@sharadhr
Copy link
Contributor Author

sharadhr commented Jan 18, 2026

OK, this latest set of patches implements that; we are back to two modules: vulkan, and vulkan_video. I am open to changing the name of the latter to vulkan.video, or vulkan-video, or any other delimiter.

IMO this is the most logical separation, as it is clear the video module depends on Vulkan-Hpp core functionality, but the normal Vulkan module doesn't need any video-related functionality.

As a stretch goal I would also like to get rid of the using PFN... stuff, but that will require interacting with the C headers, which will probably be much more difficult than this.

@sharadhr sharadhr marked this pull request as ready for review January 18, 2026 22:58
Copilot AI review requested due to automatic review settings January 18, 2026 22:58
Copy link

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

This PR adopts the "ABI-breaking style" for C++ modules as documented in the Clang migration guide, simplifying the module interface generation by using a VULKAN_HPP_EXPORT macro instead of explicit using statements. This resolves issues with maintaining hardcoded using statements and simplifies the code generator.

Changes:

  • Introduced VULKAN_HPP_EXPORT macro that expands to export in module context
  • Simplified vulkan_video.cppm by replacing 300+ lines of using statements with direct namespace exports
  • Updated all header files to use VULKAN_HPP_EXPORT on namespace declarations

Reviewed changes

Copilot reviewed 30 out of 34 changed files in this pull request and generated no comments.

Show a summary per file
File Description
vulkan/vulkan_video.cppm Simplified from ~320 lines to ~72 lines by removing explicit using statements, now imports vulkan module and includes headers with export
vulkan/vulkan_video.hpp Wrapped includes in !defined(VULKAN_HPP_CXX_MODULE) guard, added VULKAN_HPP_EXPORT to namespace, formatting improvements
vulkan/vulkan_hpp_macros.hpp Added VULKAN_HPP_EXPORT macro definition
snippets/*.hpp Updated templates to apply VULKAN_HPP_EXPORT pattern
VulkanHppGenerator.cpp Removed generation of using statements for modules

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

@M2-TE
Copy link
Contributor

M2-TE commented Jan 19, 2026

Currently, the module breaks for me (Clang 21) when using the dynamic dispatcher. The DispatchLoaderDynamic is declared in vulkan/vulkan_hpp_macros.hpp within the global module fragment and defined in vulkan/vulkan.hpp in the module body. This leads to the error seen below:

[build] In file included from vulkan/vulkan.cppm:37:
[build] vulkan/vulkan.hpp:21063:11: error: declaration of 'DispatchLoaderDynamic' in module vulkan follows declaration in the global module
[build]  21063 |     class DispatchLoaderDynamic : public DispatchLoaderBase
[build]        |           ^
[build] vulkan/vulkan_hpp_macros.hpp:288:11: note: previous declaration is here
[build]   288 |     class DispatchLoaderDynamic;
[build]       |           ^

Simply moving vulkan/vulkan_hpp_macros.hpp from the GMF to the module body does not work either, as it is declared with internal linkage and defined with export in the module body.

[build] In file included from vulkan/vulkan.cppm:38:
[build] vulkan/vulkan.hpp:21063:11: error: cannot export redeclaration 'DispatchLoaderDynamic' here since the previous declaration has module linkage
[build]  21063 |     class DispatchLoaderDynamic : public DispatchLoaderBase
[build]        |           ^
[build] vulkan/vulkan_hpp_macros.hpp:288:11: note: previous declaration is here
[build]   288 |     class DispatchLoaderDynamic;
[build]       |           ^

Could we use this opportunity to think about removing the need to have external storage for the dynamic dispatcher when using the module? It makes sense for the normal headers, but the module is its own TU.

@sharadhr
Copy link
Contributor Author

sharadhr commented Jan 19, 2026

Could we use this opportunity to think about removing the need to have external storage for the dynamic dispatcher when using the module? It makes sense for the normal headers, but the module is its own TU.

Yes; if the module is being used we can probably opt into having the storage directly in the module.

I've been thinking about the vk::detail namespace; this should be obviated with modules. And more importantly I'm not sure why the dispatcher is in a nested namespace at all; it's an advertised feature of Vulkan that should be in the top-level.

@M2-TE
Copy link
Contributor

M2-TE commented Jan 19, 2026

You forgot to export the namespace with vulkan_to_string.hpp. I'll create a test for that one, since it should not have passed those.

I've been thinking about the vk::detail namespace; this should be obviated with modules

Agreed, users often interact with it directly (initialization and storage in particular, sometimes even passing it to functions).

Easiest way I can get the dynamic dispatcher compiled within the module is by:

  1. Remove the forward declarations from vulkan_hpp_macros.hpp. Only the macros should be there.
  2. Add the forward declarations for DispatchLoaderDynamic and defaultDispatchLoaderDynamic to start of vulkan.hpp.
  3. Create a DispatchLoaderDynamic defaultDispatchLoaderDynamic right after the definition of DispatchLoaderDynamic.

Of course, these should be hidden by similar #if gates as in the current macro header. Step 3) should also only apply to the module, so that's another gate to check for.

@sharadhr
Copy link
Contributor Author

I don't really use the dynamic dispatcher (I predominantly use vk::raii, which has its own dispatcher), so forgive me for the dumb question. Exactly what are the forward declarations for? Can we get by with simply not defining them for the module case, and just exporting the definitions, like we have done for everything else?

@M2-TE
Copy link
Contributor

M2-TE commented Jan 19, 2026

The order declarations/definitions is a bit odd. As far as I can tell, it is a sort of circular dependency?
Certain functions and handles (predominantly vulkan_handles.hpp) need the defined dispatcher, whereas the dispatcher definition needs some handles like vk::Instance and vk::Device for its respective initialization phases.

As an example if I do not forward declare defaultDispatchLoaderDynamic with extern:

[build] vulkan/vulkan_handles.hpp:5119:71: error: no member named 'defaultDispatchLoaderDynamic' in namespace 'vk::detail'; did you mean 'DispatchLoaderDynamic'?
[build]  5119 |                                        Dispatch const & d             VULKAN_HPP_DEFAULT_DISPATCHER_ASSIGNMENT ) const VULKAN_HPP_NOEXCEPT;
[build]       |                                                                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[build] vulkan/vulkan_hpp_macros.hpp:335:81: note: expanded from macro 'VULKAN_HPP_DEFAULT_DISPATCHER_ASSIGNMENT'
[build]   335 | #define VULKAN_HPP_DEFAULT_DISPATCHER_ASSIGNMENT VULKAN_HPP_DEFAULT_ASSIGNMENT( VULKAN_HPP_DEFAULT_DISPATCHER )
[build]       |                                                                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[build] vulkan/vulkan_hpp_macros.hpp:315:75: note: expanded from macro 'VULKAN_HPP_DEFAULT_DISPATCHER'
[build]   315 | #    define VULKAN_HPP_DEFAULT_DISPATCHER ::VULKAN_HPP_NAMESPACE::detail::defaultDispatchLoaderDynamic
[build]       |                                                                           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
[build] vulkan/vulkan.hpp:91:11: note: 'DispatchLoaderDynamic' declared here
[build]    91 |     class DispatchLoaderDynamic;
[build]       |           ^

I can create a patch off of your branch with a working implementation of what I mentioned, if you want?

@sharadhr
Copy link
Contributor Author

sharadhr commented Jan 19, 2026

I can create a patch off of your branch with a working implementation of what I mentioned, if you want?

By all means; I can accept that in when you think it's ready. Cheers!

@M2-TE
Copy link
Contributor

M2-TE commented Jan 19, 2026

The patch: dyndis.patch

What this does is three things:

  1. Moves forward declarations out of vulkan_hpp_macros.hpp and into vulkan.hpp.
  2. Places storage for the default dynamic dispatcher into the module (so long as users have not defined a custom one).
  3. Updates tests to use the storage macro and also remove storage entirely when the module version is being tested.

These changes honestly belong into their own PR, as they touch on the normal headers as well. I don't want such a change to drown in an already big PR like this. Especially since it should also work with the current main branch modules (just gotta add the export keyword).

What do you think?

@sharadhr
Copy link
Contributor Author

sharadhr commented Jan 19, 2026

I reckon if we want to separate it out, we should merge that in first, and then I can rebase my work on top of that. Although I suspect the rebase will be quite painful, and it might be easier to just add this patch in, it's not the biggest, really.

I absolutely want to avoid a 1.4.335 situation where we had a broken master branch, and it seems this might be such a case, because we haven't enabled those tests.

@M2-TE
Copy link
Contributor

M2-TE commented Jan 19, 2026

Oh yea, the rebase might be painful.. in that case, just add it in now.
I've created issues #2447 and #2448 that would be closed by this PR (if you could add them to your "resolves" list).
Just so we have something to track that specific change in case it causes issues!

- Move forward declarations to `vulkan.hpp`
- Declare storage in the module
- Fix tests
@sharadhr
Copy link
Contributor Author

Side question...

Could we make the module tests reuse the compiled module? As they are now, the test timings don't look very flattering because the module is recompiled for every test.

@M2-TE
Copy link
Contributor

M2-TE commented Jan 19, 2026

I had separated them via ctest, because some tests need special VULKAN_HPP_* flags.

Previously I had made a sort of "hash map" in the root Vulkan-Hpp CMake, where tests with the same flags would share the same Vulkan module dependency.. but it was incredibly messy and error prone (leaking flags, etc..).

Perhaps this is worth revisiting..

- Also enable tests
@sharadhr
Copy link
Contributor Author

Fair enough. Although I'd really like to prepare some header-vs-module timings. Hopefully I can migrate the modern Khronos samples to this repository... some day.

@M2-TE
Copy link
Contributor

M2-TE commented Jan 19, 2026

The tests would be a bad fit for timings either way. You would need an atleast somewhat larger project (like those samples) where multiple TUs otherwise include vulkan.hpp and std headers.

By the way, the current state of this PR is now fully working for me (Clang 21).

@sharadhr
Copy link
Contributor Author

sharadhr commented Jan 19, 2026

By the way, the current state of this PR is now fully working for me (Clang 21).

Woohoo! Works for me on MSVC too.

@asuessenbach this is ready to run CI and for your review...

@SaschaWillems
Copy link

I sadly don't have much experience with modules and rarely use them.

Tried to get this work on our version of the Vulkan Tutorial with MSVC 2022, but I'm stuck with the following error:

"vulkan.cppm(8,1): error C7577: a global module fragment can only appear at the start of a translation unit"

I have enabled both the ISO C++23 langauge preview and Build ISO C++23 Standard Library modules

@M2-TE
Copy link
Contributor

M2-TE commented Jan 19, 2026

@SaschaWillems that sounds like you have precompiled headers enabled. I think I had that issue for that reason before, since it places the pch at the beginning where the global module fragment is supposed to be.

@SaschaWillems
Copy link

Just checked, precompiled headers are explicitly disable (option is set to "Not Using Precompiled Headers").

@M2-TE
Copy link
Contributor

M2-TE commented Jan 19, 2026

Hm.. something is either being placed at the start of the module automatically, or you perhaps included vulkan.cppm somewhere on accident instead of using import? If it's none of those, @sharadhr compiles modules with MSVC so he might have an idea.

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

Labels

None yet

Projects

None yet

3 participants