Custom Engine Blightspire

In my third-year at BUas I had the opportunity of working on a custom tech project for the entire school year (4x 8 weeks). The brief for this project was to create a game for the Steam Deck, that had a hard requirement of having to run at 60fps. Making use of Vulkan as our graphics API, we created all the necessary parts for an engine. We aim to create a first-person shooter, and our engine design tries to reflect this.

My responsibility on the project was both graphics programmer and programmer lead. This meant I was mostly in contact with the rendering back-end/architecture, and worked on the main optimizations happening on the GPU. As programmer lead, I helped the team come to conclusions and make sure communication was happening between everyone.

Notable Contributions

Graphics pipeline

To give context to the following features I've worked on, below I've created an overview of some important steps in our graphics pipeline.

  • Deferred rendering: I designed and optimized our deferred rendering pipeline approach.
  • Bindless textures: through Vulkan's descriptor indexing we implement bindless textures, so textures can be sampled through indices and without binding resources.
  • GPU-driven: with indirect draw calls we draw the entire scene by batching the draws in indirect draw calls.
  • Draw generation: by generating our draws on the GPU we can apply culling on a hardware level.
  • Diffuse IBL PBR: we use PBR for our lighting model, and derive an irradiance map from our HDRI skydome for our lighting.
  • Skydome: we draw our sky HDRI on a UV-sphere for a simple sky backdrop.
  • Post-processes: I implemented a number of post-processes to improve the look of our final image.
    • Tonemapping (several options to play around with)
    • Color grading
    • Distance based fog

G-Buffer Layout

I helped implement and optimize our g-buffer attachments to receive desirable performance on our target hardware (Steam Deck).

  • Analyze requirements of our engine/game
  • Review limitations of target hardware
  • Use of smart optimizations to reduce bandwidth
    • Depth reprojection
    • Float packing (for roughness and metallics)
    • Octahedron packing for normals
  • Profiling to verify optimizations
    • Use of Renderdoc and NSight

GPU Driven Rendering

I designed and implemented a gpu-driven rendering approach for our renderer to reduce CPU bandwidth and allow for gpu-drive optimizations. This was implemented in this project, but was part of a solo self-study.

  • Research modern rendering techniques and apply findings
  • Design rendering architecture abstractions
  • Implemented bindless textures to reduce resource binding
  • Implemented a singular index and vertex buffer to reduce resource binding
  • Single draw call per pipeline

Draw Generation and Culling

Draw generation happens on the GPU level to make use of hardware level culling.

  • Two-pass HZB method (source)
  • Implemented frustum and HZB occlusion culling
  • Optimized the approach by producing draws into a contiguous buffer
  • Optimized tracking of visibility through bits, instead of booleans

Animations

I was responsible for designing and implementing the animation system. This allowed us to make use of skeletal animation and vertex skinning.

  • Design a skeletal animation and vertex skinning rendering system
  • Integration in our gpu-driven rendering pipeline
  • Optimized calculation of joint world transforms
  • Usage of dual quaternion transforms for optimized bandwidth and improved rotations
  • Implement an API to use and transition between animations from a scripting language

Shader Reflection System

The native Vulkan pipeline system can be laborious to set up and use. To simplify this process I set up a SPIR-V reflection system. This makes use of SPIRV Reflect to deduce information about our shader binaries.

  • Process shaders through SPIRV Reflect
  • Use information to simplify pipeline creation
    • Inferred vertex attributes
    • Inferred push constants
    • Inferred descriptor set layouts

This system can also be extended to produce C++ code during the build process, so we have compile-time equivalents of our shader structures.