I used clang's ftime-trace to profile the compilation of time of my program. Turns out that about 90% of the time is spent parsing the massive vulkan.hpp header provided by the khronos group.
This in turn means that if I minimize the inclusion of this header on header files and put it only on cpp files my compile times should be drastically better.
I face the following problem however.
There's a few objects in the header that I need pretty much everywhere. There's a few error code enumerators, a few enums of other kinds, and a couple of object types, such as
vk::Buffer
, vk::Image
etc...
These ones make less than a fraction of a percent of the total header, but I cannot include them without including the entire header. What can I do to cherry pick only the types that I actually use and avoid including the entire header every time I need my code to interface with an image?
There are some ways to mitigate the issue on your side.
First, there are several headers now (there did not used to be, this was a huge complaint in virtually every vulkan survey). This does not completely mitigate the issues you have (the headers are still massive) but you don't have to include vulkan.hpp, which itself includes every single available header, just to get access to vk::Image and vk::Buffer. Handles are now found in vulkan_handles.hpp ( though it is still 13000 lines long).
You talk about not having classes because of the way vulkan works. Hypothetically, you can avoid having Vulkan.hpp in your header files in a lot of cases.
vk::Buffer, vk::Image can both be forward declared, eliminating the need to include that header, as long as you follow forward declaration rules
You say that you can't use classes etc... That doesn't really make sense. vk::Buffer and vk::Image are both classes. You could hypothetically create wrapper classes for only the types you need doing this, however, in order to eliminate the overhead you'd have to allocate enough space for those types before hand.
Now in a big enterprise library with enterprise defined types, you normally don't do this, because the size of types could change at any moment. However, for vulkan.hpp, the size and declaration of the types vulkan.hpp is using and size of their wrappers is really well defined, and not going to change, as that would cause other incompatibilities on their side.
So you can assume the size of these types and create something like :
struct BufferWrapper{
// vulkan.hpp buffer only wraps buffer directly, does not inherit from anything, it's size should match it exactly
std::array<std::byte, sizeof(VkBuffer)> vk_data;
}
in cpp file....
BufferWrapper(...){
auto buffer_ptr = new vk_data vk::Buffer(...);
}
vk::Buffer* getBuffer(){
//technically undefined behavior IIRC,
return reinterpret_cast<vk::Buffer*>(&vk_data);
}
of course since it only wraps buffer, you could actually just use VkBuffer directly and reinterpret cast it. Then you could make buffer wrapper automatically convert to vk::Buffer with non explicit conversion operators, making it hypothetically pretty seamless.
However this is a lot of work to do if it's more than a couple of types. If you're willing to put in that much work then you might as well...
So if you're really serious about changing things this far, you might as well change the source code.
To start off, Vulkan hpp is not directly written. vulkan.hpp is generated. Inside of the vulan-hpp repo there's the VulkanHppGenerator.hpp and .cpp. Modifying these you make the generator generate source files. You can find prototypes for each generator you need (ie generateHandle). You could hypothetically alter the output to account for a source file.
Doing this however is outside the scope of the question, I only bring it up as the nuclear option.