The design allows structures to be chained together so that extensions can create additional parameters to existing calls without interfering with the original API structures and without interfering with each other.
Nearly every struct in Vulkan has sType
as it's first member, and pNext
as it's second member. That means that if you have a void*
and all you know is that it is some kind of Vulkan API struct, you can safely read the first 32 bits and it will be a VkStructureType
and read the next 32 or 64 bits and it will tell you if there's another structure in the chain.
So for instance, there's a VkMemoryAllocateInfo
structure for allocating memory that has (aside from sType
and pNext
the size of the allocation and the heap index it should come from. But what if I want to use the "dedicated allocation" extension. Then I also need to fill out a VkMemoryDedicatedAllocateInfo
structure with extra information. But I still need to call the same vkAllocateMemory
function that only takes a VkMemoryAllocateInfo
... so where do I put the VkMemoryDedicatedAllocateInfo
structure I filled out? I put a pointer to it in the pNext
field of VkMemoryAllocateInfo
.
Maybe I also want to share this memory with some OpenGL code. There's an extension that lets you do that, but you need to fill out a VkExportMemoryAllocateInfo
structure and pass it in during the allocation as well. Well, I can do that by putting it in the pNext
field of my VkMemoryDedicatedAllocateInfo
structure. I can create a chain of structures like that as long as I want.
Here's the really important part. Since all structures have sType
as their first field, an extension can navigate along this chain of structures and find the ones it cares about without knowing anything about the structures other than that they always start with sType
and pNext
.
All of this means that Vulkan can be extended in ways that alter the behavior of existing functions, but without changing the function itself, or the structures that are passed to it.
You might ask why all of the core structures have sType
and pNext
, even though you're passing them to functions with typed pointers, rather than void pointers. The reason is consistency, and because you never know when an existing structure might be needed as part of the chain for some new extension.
If we dont have any options anyway, why is this parameter not just set implicitly upon creation of the type?
Because C isn't C++. There's no way to declare a structure in C and say that this portion of the structure will always have this value. In C++ you can, by declaring something as const and providing the initial default value. In fact, one of the things I like about the Vulkan C++ bindings is that you can basically forget about sType
forever. If you're using extensions you still need to populate pNext
as appropriate.