About the author, or why you should care about what I do or do not do with the Vulkan API
I am a professional programmer, but I do not develop games for a living. In the past, I have brought you the fine small3d game development library, open source titles like Gloom, Avoid the Bug and Frog Remixed (the latter two are now archived and offline). More recently I have used the aforementioned small3d game development library to release the fine masterpiece Islet Hell across Windows, Linux, MacOS, Android and iOS.
What is this small3d game development library?
I wanted my titles to be cross-platform and open source ever since I started out with 3D game development (as a hobby). I had heard about “writing games not engines”, a popular concept at the time (you can read more about that here) and I started out trying to make a game involving a bug, chasing a goat. Given my modest 3D modeling skills the bug ended up looking like a crow but, in any case somehow I got it done while learning OpenGL.
While working on this Avoid the Bug title, I could not help but notice that 3D game development without a game engine is a lot of work. There is so much time that needs to be spent writing the code that loads models, renders them on the screen and animates them that it would not be very efficient to be doing it again and again every time one codes a new title. I think that it is for that reason that a lot of developers tend to use an engine like Unity or Unreal these days. Wanting to do things almost from scratch (I say almost because I still use libraries like glfw or libpng) was getting out of fashion. But since I went down that road I decided to extract an engine from that first game I made so that I would not need to code everything again when I would produce my next game. This is how small3d came to be and, as a matter of fact, if you read the above mentioned article your will find that that was pretty much the idea it was supporting too. I was calling small3d an “engine” in the beginning, but it is really just a library that I have been maintaining and extending ever since while using it to make my games.
Where does Vulkan fit into all of this?
To cut a long story short, soon after small3d was launched and I was happily working on my open source game projects, I came across the news about Vulkan in 2016. It sounded great. I learned that it was a brand new API, forged in the industry, low level and “clean”, here to save us from the hot mess that OpenGL had become.
To be perfectly honest, I was not thrilled with the news that there was more to learn about cross platform 3D development APIs beyond OpenGL, with which I had only recently managed to familiarise myself. But the way Vulkan was presented was quite compelling. It was the future of Android, it allowed multi-threaded programming, known developers were praising its many qualities. Also it was a bit of a challenge. “Forget about the hand-holding OpenGL has gotten you used to”, they said. With Vulkan you would have to totally control the GPU. Vulkan would make you a grown-up. And just in case anyone had any doubts about how soon we should get our hands dirty with it, companies were asserting that “your device is ready”. Presumably they were already supporting it since before we even knew it existed. I am exaggerating but that was the feeling I was getting.
Granted, Khronos and anybody who was somebody in the open 3D API standards universe were reassuring about OpenGL. We were told that OpenGL would be around for a long time, if not forever. But there was always a sign that we should not remain too comfortable with it. Apple deprecated it (in favour not of Vulkan but Metal, but Vulkan comes with MoltenVK, which translates its commands to Metal, allowing it to run on MacOS and iOS). Google Stadia (don’t laugh) was going to have Vulkan center stage (pun intended). A bunch of companies were laying out plans to migrate to Vulkan “soon”. Interviews about the woes of OpenGL would just not stop. There was also a testimonial somewhere about driver developers having to code around known game bugs from the driver side because game developers just could not learn to use OpenGL properly.
At the same time, I could not help but notice that a lot of fellow hobbyist developers were not too eager to learn Vulkan. It seemed like too much work and some were even accusing it of being unrealistically cumbersome to the programmer. Nonetheless, given the fact that my game library is quite limited in features and because one of my priorities is to ensure that it runs on as many different hardware platforms as possible, I decided to get into it. I worked through the Vulkan Tutorial, I wrote a C helper library for Vulkan to facilitate plugging it into small3d and I implemented all my existing graphics features that were already developed in OpenGL, also in Vulkan.
Things went pretty much ok over the next couple of years. I was maintaining my OpenGL renderer and my Vulkan renderer in parallel, making sure that they are at feature parity. It was easy to switch a game build from OpenGL to Vulkan and vice versa without any changes in the code. I always did have a feeling that maybe I was doing a bit too much work but I thought it was worth it. OpenGL would keep my titles running on older devices and Vulkan would be great for newer ones and would ensure that my games run everywhere in the future, if OpenGL stops being supported someday. I was noticing that Vulkan would just not get tamed though and that was not because of its complexity with regard to OpenGL. Time and time again, some GPU would freeze and would require a trivial change in the code to sidestep the issue, or the image rendered on some screen would be corrupted and, once again, a change of the code from one way of doing things into another one, both correct, would allow me to move on. I found this a bit contradictory. Vulkan was supposed to give us responsibility for our GPU programming, thus making it easy for vendors to avoid driver bugs and quirks.
What went wrong?
A big realisation came in the summer of 2022. It was at that time that I actually released a game, not as open source on GitHub, but as a commercial title on Steam, the Apple App Store and Google Play. Don’t be dazzled by the “commercial title” characterisation. It is still just a little hobby game, for which I am charging a modest amount, just to get an experience of actually selling something.
No matter how unambitious I am about my game development career, I am quite interested in doing the few things I actually do reasonably well. So, I hate the thought of somebody paying 1 euro or dollar for a game only to have it crash on their computer or phone. So I have tried, as much as possible, to ensure that Islet Hell, the game I have released, would run properly on as many devices I could get my hands on. I have provided free copies to friends and strangers and I have also collected old phones and laptops to install and test the game myself. The experience was devastating as far as Vulkan is concerned. I started out only providing an OpenGL version for the PC for Windows and Linux because I had seen more than enough desktop machines missing a Vulkan driver. But for MacOS, iOS and Android I provided only Vulkan binaries, encouraged by the support for the API I had witnessed on my own devices and by how well MoltenVK (the Vulkan to Metal translation layer) worked at home. I assumed that if I made my title available only for Androids running the version of the operating system that is supposed to support Vulkan, all would be fine.
The glitches I witnessed on many machines I tested the game on made me seriously reconsider. I have to say that the first system to go was the Android. I do not want to name specific vendors in this text, but the support for Vulkan in many cases, even if “officially” certified, is abysmal, to the point of shaders crashing just because I have put an extra variable somewhere or because I have not used a data type that the driver likes (even though it is correct according to the documentation). There was even one feature (skeletal animation) which I was just not able to code in GLSL for Vulkan. Granted, my game is not actually using it, but I still had to deactivate it in the small3d library because just by being available it would make the driver crash. The situation with Apple devices was marginally better. MoltenVK is quite decent. But I still got a sticky issue with all graphics disappearing from the screen sometimes that I just could not resolve.
Even though I am not providing many technical details in this article, I can assure you that I have tried very hard to get to the bottom of all of these problems that I am mentioning. I have managed to resolve some and for others I have found evidence that they actually are unresolved issues of the implementation of Vulkan on some devices. I am not blaming anyone for this, I understand that Khronos is trying to do a good job formulating the standard and that the vendors will get it right at some point. But it is just not something I feel I can trust right now. If you would like to check whether or not I know what I am doing, please feel free to review the last commit in small3d’s source code that actually supports Vulkan.
Also please feel free to contact me, or add a comment here below if you would like specific examples of the issues I am mentioning above and I will provide them. I would consider a swarm of more capable developers coming here and proving me wrong on every point a happy outcome that will make me reconsider my position.
In any case, all of the above mentioned problems would just magically disappear, every time I replaced the Vulkan renderer with OpenGL (or OpenGL ES on mobile). The OpenGL(ES) renderer was super easy to get right and it just would not crash under any circumstance, not even on Apple where it is supposedly deprecated. So my commercial title ended up running on OpenGL everywhere by the time it got to be stable (or on OpenGL ES in the case of mobile).
The Finishing Blow
Despite all these troubles, I did not even think of removing Vulkan from small3d a few months ago. I thought it was still a good investment in the future. However, lately I have seen some announcements about the newly released VK_EXT_shader_object extension for Vulkan. It was followed by Vulkan heavyweights expressing their opinion that programming with pipelines was not good (yes, pipelines, that thing I have spent hours and hours learning how to program). On top of that there are some comments, again from developers much more important than myself, that we need a new Vulkan version to clean up the mess that has accumulated in the API so far, and that for what was a “new clean API” a few years ago. There is also an announcement about an effort to improve the documentation.
All of this kind of makes me think that what we have here is not at all a new future-proof API that we just need to get used to before we can get on with efficiently coding more robust applications. We have an API just like the others, with its pros and cons, and issues that need resolving.
Considering the time I have been spending on trying to maintain the Vulkan renderer in parallel with the OpenGL one, and the complexity managing the two has added to my codebase, I have decided that the best thing to do for now is to just stop supporting Vulkan.
I would like to make it clear that I am not saying that Vulkan is a bad API. Neither that it does not have a future. A hobbyist graphics / game developer like me could not even pretend to be able to make such a statement with any credibility. I have seen what teams of professionals have done with Vulkan, in Red Dead Redemption II for example. I am just saying that it is not something that can be used for my purposes. small3d is supposed to let C++ enthusiasts code games in the language they love, using a minimalist support infrastructure that allows those games to run anywhere. The problem is that, with Vulkan, the games will just not do that (run everywhere). A lot of old or low budget devices do not have implementations that are good enough yet. And with the new effort to reorganise the API, I predict that it will be very long before devices like these will get any reasonable amount of stability.
I could keep Vulkan around indefinitely but I would rather use the time that would have taken to do other things. For example I could focus on writing another game, or work on improving my lighting (I am only using Gouraud shading for the time being), and that without having to implement or test everything I do on two APIs, one of which is guaranteed to fail on many pieces of hardware. And since it does fail, that means that even if I keep the Vulkan renderer functional only in some testing environments, I cannot use it in production for the foreseeable future. To quote a character from an 80s movie that I still remember, “a weapon unused is a useless weapon”.
Yes, rendering pipelines suck. You end up just putting all your settings into a sortable container and then using an STL map to retrieve them each time they are used, something the driver could easily do. Technically it makes the driver faster, but it's just shifting that load onto your application.
Thank god they remove render passes or I would not have been able to finish my engine.