Raven Framework (Vulkan, Cross-Platform)

PROJECT NAME:

Raven

SHORT DESCRIPTION:

Raven is a rendering/game framework which allows a programmer to create scenes on the Windows and Linux platforms. The project features cross-platform development, Vulkan rendering, a rendering abstraction layer and fiber-based multithreading.

ENGINE/LANGUAGES/FRAMEWORKS:

Custom Framework (C++), Vulkan, CMake.

PLATFORMS:

Windows, Linux

DATE:

February 2018 – July 2018

Project Details

I wrote Raven as part of my graduation project. For this project I set several goals. One of the goals was to support multi-platform development because the market is continuously evolving towards having games released on multiple platforms. It seemed beneficial to familiarize myself with creating and managing projects using CMake and learning how to create Linux applications. Another goal was to allows the developer to easily implement new platforms if needed, therefore a considerable amount time during the pre-production phase was dedicated to designing an engine and project architecture which allows for this. This was achieved by abstractions and modularization of each part of the game engine.

Since the project has to build and execute on various platforms (Windows & Linux), I wrote a rendering abstraction API to separate the game rendering code from the rendering implementation. By doing this the developer can implement new graphics API’s without having to touch the game rendering code. Additionally this further improves the portability of the project for multiple platforms. Since in my previous project I wrote a renderer using the DirectX12 API, I wanted to continue the trend of learning modern rendering API’s so I decided use the Vulkan rendering API. Vulkan is supported on many different platforms including the target platforms I set for this project.

The last point of focus for this project is to maximize core utilization of the CPU by using Fibers. Fibers allow the developer to take control over when threads should switch between contexts by allowing the developer to save the current executing thread’s stack in memory and switch it for a new stack. By doing this you can maximize core utilization by switching to a different task if the currently executing task has dependencies. Writing implementations for both Windows and Linux helped me better understand how multithreading works and how to design the game architecture in such a way that it benefits from using multiple threads.

Challenges

While working on the project I encountered several challenges:
  • Designing the project architecture in such a way that there are little to no circular dependencies. If all the modules rely on each other to work, splitting them up will be a waste. One key point I learned here was that planning ahead the systems and their interactions are crucial.
  • Writing a rendering API which works for multiple API’s. The current API is written with Vulkan and DirectX12 compatibility in mind and continuously has been iterated upon as I learned which features needed to be exposed. The hard part is determining how much freedom you want to give the developer and how much can be determined by using heuristics. In my implementation I tried to keep the API as low-level as possible, so any high-level systems can be written in api-independent code, resulting in more portable systems.
  • Writing implementations of Fiber behaviour on Linux when it is not natively supported. My solution was using boost’s fiber context implementation which is written in assembly.
  • Developing for multiple platforms resulted in different bugs for each platform, therefore it was essential to continuously test on both platforms. In the future unit testing could be a better solution to this challenge.

Rendering

The game rendering code uses a low-level, API-agnostic interface to render objects and effects to the screen. To test the renderer I wrote multiple effects to test the API:

SSAO

The SSAO algorithm is quite simple; for each fragment the shader samples the depth in a certain radius around the fragment and increases the occlusion factor if the sample is in front of the fragment. By using this occlusion factor in account when performing lighting calculations, ambient occlusion can be faked to a certain degree.


Volumetric Light

The volumetric fog algorithm uses ray marching techniques to simulate mie scattering. By using raymarching to sample from multiple positions in a scene and determining if that point in space is occluded by shadows and additively blending the result of the mie scattering formula the effect is produced. Due to the heavy performance cost of raymarching, I apply a dithering pattern to get more detail in less steps and blend the buffer to hide any visual artefacts. Since the effect does not need high accuracy, I also render the fog at half-resolution.