A Graphics engine with Vulkan: code, libraries, architecture

Back to the main page of this topic.

Let’s talk about the source code. Well, it does the job, but everything else about it seems bad. I tried to be overly clever by using templates and the constexpr keyword which seem completely unnecessary now. But at least it’s not heavily OOP. I spent some time to implement memory allocators, which were a good exercise by themselves. I also made some general containers, which looking back now seem unnecessary and have a really bad interface, probably because I tried to be overly clever. After watching hundreds of episodes of Handmade Hero, I probably wouldn’t implement containers in a vacuum anymore, it really seems a waste of time. On the other hand, I’m fine with the memory allocators, some of them, for example the free-list and the monotonic buffer allocator turned out to be useful in the project (although I implemented them by following the concept of polymorphic memory resources from C++17). The project contains lots of files: every enum, and most of the functions are in different files. I decided to do this separation at the end of the project, because… I don’t remember… Maybe I thought that’s prettier? I don’t know. This was an absolutely horrible idea… This was the worst idea I’ve ever had in programming. The number of compilation units increased heavily, and the number of includes also increased, so the same, template heavy headers had to be compiled multiple times. Therefore, the compilation time increased to a painfully high value. I did a clean build now, it’s one and a half minutes.

About the libraries… I used GLFW for window management and Vulkan, GLM for linear algebra, and stb_image for loading images. I used GLFW because the tutorial from which I learned the basics of Vulkan used it, and to be honest I didn’t know anything about the Windows API back then and didn’t want to spend time learning it. I still don’t know much about it, only what I saw in Handmade Hero, but that is already enough knowledge for me to not have to depend on libraries for window and input management. About GLM… it’s not that I didn’t know how vectors and matrices work, or how to put together a transform or a projection matrix, or what the relation between rotations and quaternions is. I used GLM because I thought it’s somehow magically better than the linalg functions I would write. And also, I thought it would have taken a significant amount of time to write the linalg functions and structs I needed. Now I think neither of the arguments make any sense, and using GLM just unnecessarily increased the code base. Using stb_image was definitely a good idea, because I didn’t want to bother at all with reading about image format specifications and implementing a loader for them, this wasn’t the goal of the project.

About the architecture… Again, I think it’s bad. This is a rendering engine in the vacuum. It has an interface which can be used to create materials, meshes, transforms, provide data for the terrain, sky and ocean, and create opaque objects from a mesh, material and transform. The engine works in a retained mode, so each created renderable things (opaque objects, ocean, terrain, sky) are rendered automatically when the render function is called once per frame. Back then I didn’t know what retained mode means, I chose this techique because I thought that in the majority of cases if something was rendered in a frame, that it will probably be rendered in the next frame as well, so it made sense to cache the render commands. Also, in this case I didn’t have to record the Vulkan command buffers in every frame, only when a change occurred (a renderable was added or removed). I guess this optimization makes sense, if can be applied without too much pain, but only AFTER the game is practically finished, otherwise it would just make the development harder. In addition, the renderer is also the asset manager: it takes the filenames of the textures, obj meshes, and uploads them to GPU memory (on a worker thread). In a real scenario I guess this design would be very unfortunate, since maybe other systems would use the assets as well. So I don’t think that a rendering engine with this API would be useful for developing a game. I guess it’s a good example of a bad API design. But again, it was just a project for learning graphics. I definitely didn’t write the usage code first, but there was no motivation behind writing any usage code at all.