Project Spock Vulkan framework

Submitted by zach on Sat, 10/10/2020 - 04:02
Vulkan Demo Image

Spock is a project I've been working on for a while now to make using Vulkan in smaller projects a lot easier. I've been fascinated with Vulkan since I first dived into the API in a graphics class. The option to submit multiple queues at once in a parallel fashion was enticing for performance and overall very interesting. However after about the first 3 weeks of the class I was finding that setting up Vulkan for use in a project was a serious undertaking. I wanted a way that I could set up Vulkan pipelines as simply as: 

Pipeline myPipeline = CreatePipeline(ShaderInfo);

And then to be able to render objects as simply as:

myPipeline.Render(myObject);

In reality I wanted a way that I could use Vulkan as easily as OpenGL but also have a way to fine tune the underpinnings for specific projects. What I ended up with is something somewhere in the middle but it still makes using Vulkan for basic projects drastically easier.

The project frameworks we were given in my class did all the basic setup as a set of about 20 functions working on global variables. This was great for demonstration because each function was numbered and we could walk through the process step by step and see what was going on. But this was very cumbersome and involved a lot of explicit function calls to setup the pipeline, load vertex buffers, and ultimately render the buffers to the screen. I wanted a solution that would allow all of the setup to be hidden behind one or two object instantiations and then allow all of the other functionality to work as methods to those few objects.

To get started and to really understand the interactions between all the pieces of Vulkan I went through all of the steps in each of the functions in one of our example frameworks and inlined everything. Instead of having multiple functions I explicitly did everything in the main procedure. This allowed me to do several things, first and foremost it allowed me to see everything in order without having to jump between functions. Second it allowed me to organize the code into logical sections based on what piece of the pipeline it worked on. Lastly it allowed me to break away from the example framework we were given in class and ensure that my code worked the way I expected (and in some cases to better understand why some things worked and didn't work).

With the code now working and organized into logical sections I was left with several major groupings: Instance, Device, View, Mesh, Buffer, DescriptorSet, and Pipeline. Each one of these groupings became a class that has primary ownership of one major piece of the Vulkan pipeline. Each progressive class built upon the classes that came before it. For example a Device requires an Instance, and a Pipeline requires an Instance, a Device, and a View (although it can get the Instance from the device). Each class feeds through the pipeline to the ultimate goal of creating a Pipeline object that can be used to render objects to the screen.

I ended up taking an interesting approach to the actual rendering process to try and achieve the simplicity goal of being able to simply call something like "myPipeline.Render(myObject)". I created the concept of a "Render Agent" that could simply take in some class that implements IRenderable and jam it into the pipeline. The way this works is that the Render Agent is actually responsible for all of the Vulkan setup and is capable of producing buffers, descriptor sets, and even the pipelines. As a side note I initially planned to be able to use multiple graphics APIs in a modular manor so the base class would be IRenderAgent and for Vulkan I created a class called VKRenderAgent. VKRenderAgent has a method called Render that takes in a pointer to an object of class IRenderable, and in this case VKRenderAgents tries to dynamic_cast the IRenderable to a VKRenderable to determine if the object was set up using Vulkan or another API. It can then gather pipeline and descriptor set info from the object itself which makes it trivial to draw the object.

I'm not quite ready to call this project "Done" with a capital D but it does work! There are a few things I want to change and make better, and the VKRenderable class is very messy but these things can be fixed with some time. If you want to see it in action check out the video demo below and if you want to browse the code check it out on the github repo here: https://github.com/zachmakesgames/Spock.

This project has been a great learning experience and I plan to use it as the graphical layer in a basic game engine.

 

Tags