4

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?

Makogan
  • 8,208
  • 7
  • 44
  • 112
  • Maybe building a wrapper dll as a middle layer with static link could work? Maybe like debug.dll as a temporary solution until release? – huseyin tugrul buyukisik Feb 28 '22 at 08:54
  • maybe this helps a [compiler cache](https://ccache.dev/) -> *"Ccache is a compiler cache. It speeds up recompilation by caching previous compilations and detecting when the same compilation is being done again. Ccache is free software,"* – Raymond Nijland Feb 28 '22 at 08:58
  • @RaymondNijland That tool seems interesting. I will look into it. – Makogan Feb 28 '22 at 09:21
  • @huseyintugrulbuyukisik How would the consumers of that shared library be aware of the definitions of the vulkan objects? – Makogan Feb 28 '22 at 09:26
  • @Makogan then consumers need more than just a few parts of the header? Isn't there a way to insert adapter class? – huseyin tugrul buyukisik Feb 28 '22 at 09:28
  • Doing it as a class would not make sense as vulkan is not relaly an OOP interface, I would need to somehow alias all the underlying objects (which are close to fundamental types). It's likely doable but will make readability painful. – Makogan Feb 28 '22 at 20:10
  • Have you tried precompiled headers? – Sedenion Feb 28 '22 at 20:59

2 Answers2

3

There are some ways to mitigate the issue on your side.

vulkan_handles.hpp exists

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).

Forward declaration

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

Stack PIMPLE wrapping

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...

Use Vulkan.hpp/s generator to just generate cpp/hpp instead of header only

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.

Krupip
  • 4,404
  • 2
  • 32
  • 54
1

Just adding my own advice to the existing answer:

  • Try using precompiled headers. Precompiling vulkan.hpp helped a lot in my case.
  • Try opting out of unused features by defining preprocessor directives such as VULKAN_HPP_NO_STRUCT_SETTERS, VULKAN_HPP_NO_STRUCT_CONSTRUCTORS VULKAN_HPP_NO_SMART_HANDLE.
Elad Maimoni
  • 3,703
  • 3
  • 20
  • 37