It’s… been a while. You historian from the future (NO ONE is reading this right now) may be wondering why its been such a long time with no new commits, and I am here to tell you why. Vulkan is meant as an OpenGL replacement, it is sometimes refered to as OpenGL 5.0, but its API is nothing like OpenGL… well, except maybe for using the ‘vk’ prefix on function names the way OpenGL used ‘gl’. Needless to say, a lot of kruft was dropped, and I though that even though the API is completelly diferent, the inherent nature would be fairly similar, new stuff was expected, but I never though some things taken for granted would be gone. And in general that’s true, you get vertex buffers, index buffers, programs are now pipelines and you have to compile your shaders by hand, but it really gets you in the minute things.
Not what? 2, 3 commits ago I had to add code to upcast byte size index buffers into shorts (two byte indices) at runtime because Vulkan no longer supports any signed indices, which make sence, or any index size other than 16 or 32 bits, which I am not entirely sure makes sence. If you had a small mesh (less or equal to 255 vertices), an unsigned byte is all you need to index it, and OpenGL does support it. But that’s fine, on with the times I guess, push game developers to use something other than boxes and crates to fill empty environments in games just to make use of that extra byte per vertex.
Now, we come to the topic that left me scratching my head for a fortnight. In both OpenGL and Vulkan you can have two types of vertex buffers, interleaved and non-interleaved, where the first is basically an array of structs and the second is a structure of arrays. I was under the impression that using interleaved buffers was optimal, and it may well be on some dispositives, so I wrote the mesh format with interleaved data in mind (or I think I did, I need to go back at the code and make sure). The format while flexible, does have some limitations, you cannot just make up posible attributes, instead you get position, normal, tangent, bitangent, uv, and skeletal weight data. All of them are technically optional (position is mandatory in practice), this means the interleaved buffer stride is variable depending on the mesh. Now, in OpenGL you specify a stride on a per vertex attribute basis, so you can have variable buffer strides, you can/must also enable or disable attributes, so it is no big deal if a mesh is missing tangent or skeletal information for example, you just adjust the stride on the mesh side of things before rendering and you’re done. In Vulkan this is not so (at least at the moment), here, stride is handled at the pipeline side and pipelines once created are pretty much static, and since you may run any vertex buffer through the pipeline, you cannot just asume that all will have all the data nor can you asume that because the pipeline does not make use of an attribute, the attribute is not part of all buffers making their way through the pipeline. Surely this was supported in OpenGL, so it must be supported in Vulkan I though, where is the magical command that gives me that? I looked and looked around and found no answer, for some reason no one seems to have stumbled across this problem but myself yet, so either I am doing things wrong, everyone moved to non-interleaved buffers or wasting memory and processing time generating the empty voids in interleaved buffers is no big deal to anyone… or maybe we have not reached that point for more people to wonder why this is no longer posible, what’s the complexity? what’s the advantage?.
Anyway, I discovered this was the reason nothing is showing on screen right now, then I entered the denial stage, and the bargaining stage, ‘there must be some way that doesn’t waste resources!’, and finaly, yesterday afternoon, I reached acceptance. I will inflate buffers at runtime at the moment to keep them interleaved, then I’ll review the format to see what’s the least resistance path to allow for non-interleaved buffers, and finaly I’ll leave the option to the user to use either, each being converted at runtime if required.