Blog banner

Beyond the GUI

Understanding what is going underneath the hood never hurts, right? Let's see how was my first contact with the API for computer graphics, Vulkan.

  • Erick Frederick
  • 9 months ago
  • Game Development

Taking my last blog post where I quickly introduced myself and wrote about how I got into my hobby, game development, while being able to build what I wanted at the time with Unreal Engine something was missing.

Since the start of my software development career I've always had an weird approach when learning completely new fields, that is, choosing the easier path (frameworks / libraries) first then going under the hood to figure out what's actually going on, for me taking this approach was pretty effective because you know roughly what each block of code is trying to do by knowing its expected result.

Context

On the surface, these are the computer components responsible for everything displayed on your screen:

  • Central Processing Unit (CPU):

    • Executes commands very quickly.
  • Graphics Processing Unit (GPU):

    • Supports the CPU but at a slower pace;
    • Can handle larger datasets due to a greater core count.

To control the CPU, you need to communicate using machine language. Nowadays, there are tools that translate human language to machine language. Now the question arises: how can you send commands to the GPU?

It turns out CPUs (as far as my understanding goes on x86/x64) don't use the GPU automatically to help with task execution; that relies entirely on each application. You can either do this directly using low-level programming or use APIs that abstract that for you.

Currently, these are the most common APIs used in game development:

  • Vulkan;
  • DirectX 11 / 12;
  • Metal;
  • OpenGL.

Even though I've seen multiple times that Vulkan is not a good choice to start with, I went ahead and chose it anyway.

Starting out

Starting with Vulkan was a really overwhelming experience, and at the time (1 month ago), I thought it would get a little better once I saw something on my screen. I was right, but my lack of understanding of some computer graphics concepts made it worse for a little while. This was my first time learning a new technology that would take roughly 1k lines of code to finally see the product of my work in action. Weirdly enough, I wasn't really discouraged by it.

Firstly I was following the Vulkan Tutorial and I was able to make "Hello World" of game dev A Triangle

I'm really fond of figuring my way around a new technology, but when it comes to a new field, I'm inclined to take a more academic approach that teaches the basics so that I can have a solid foundation.

Running on automatic

My lack of knowledge in computer graphics caught up with me, and I found myself mindlessly copying and pasting code around without truly understanding what I was doing. This became evident when I struggled to draw a square and couldn't understand why my approach wasn't working.

Even though the current material is great at explaining what and why it was doing something, one resource can only go so far. At times, it would link up to a computer graphics resource that I would ignore due to my fixation on achieving something tangible. So, I decided to search on YouTube how drawing works on Vulkan. That's when I came in contact with GetIntoGameDev's YouTube channel and its Vulkan CPP series.

For me, it was perfect. Firstly, I wasn't aware that Vulkan had a C++ variant; by then, I was only using C. The GetIntoGameDev way of teaching also included quick explanations on the computer graphics side.

Restarting

With that, I had a new approach in mind: erase all my current progress and follow the material's code snippets, but making sure I write it in my own way. Lately, I've been really into Simulation Racing (Sim Racing), and the project name is themed after it, which is Chicane.

So before I decided to roughly design my Engine with the knowledge I had at the time so I don't get lost on what is doing what:

Even though it's a basic application design, it's also pretty bold considering that I will not build a game first, but an Engine Editor, which is essentially a visually heavy tool to make games.

This design would centralize all the Vulkan API usage inside the Runtime, leaving the Editor to only add, modify, and delete instances inside the rendering space, also known as Scenes. And so, I went on. Now understanding my codebase a little better, I wasn't getting lost anymore. By that time, I had already surpassed what I had done previously with the implementation of Texture, Perspective, and Cameras.

That doesn't mean it was all roses; there were a few hiccups along the way, such as trying to render 3D models.

Without even knowing the expected result, one can clearly see its rough shape, but why those triangles were misshaped or entirely missing was the bane of my existence. The culprit?

    outAllocationSize =+ sizeof(*mesh[0]) * allocationInfo.vertexCount;

This snippet of code would not trigger any compiler errors or warnings because it's valid both semantically and syntactically. The fix?

From

    =+

To

    +=

Debugging and refactoring those were the sole activities I was doing for nearly three days. With my now greater knowledge of my codebase, I was able to find and fix this pesky bug. With that fixed, I moved on with the tutorial. There are plenty of things that I deemed sub-optimal that I did my own way, such as not using a single float vector to declare vertex data. Instead, I used a more human-readable approach due to my commitment with the editor.

His

    std::vector<float> vertexData = {
        texturePosX, texturePosY,
        positionX, positionY, positionZ,
        colorR, colorG, colorB,
        normalPositionX, normalPositionY, normalPositionZ
    };

Mine

    struct Instance
    {
        glm::vec3 position;
        glm::vec3 color;
        glm::vec2 texturePosition;
        glm::vec3 normal;
    };

Granted, it made some implementations a little more difficult, but changes like those proved to be really useful as I moved on with the tutorial. Some of those had a similar refactoring later on in the tutorial series, and I'm really proud of myself because of that.

The Future

As of today, this is the current state of the project.

Since the beginning, I was mildly checking the performance changes with every major code change, and recently, I've been stuck trying to fix a memory leak related to the handling of window events such as resizing, minimizing, and maximizing.

I don't really know if I will treat my project as a experiment or really focus on it, only the time will tell...

That's pretty much it for now, there are still a few things I need to implement in order to "complete" the tutorial such as cubemaps and so on. The year is coming to a end and I need some needed off time.

Happy New Years!