C++ Rendering Engine I – Abstracting the Render Device

You can obtain the all the code I used for this post from GitHub: https://github.com/amesgames/RenderDevice.

Introduction

In my day job, we have a rendering engine where we have abstracted the graphics library behind our own interface. We deliberated at length on whether or not we should abstract the graphics library away or program directly to it and abstract later, once there is a strong business need for it.

Regardless of where my company ends up, there are definitely some advantages of abstracting the graphics library away. Namely, for platform portability and business agility. Especially in the game industry, this is quite an important advantage. For example, having a working PC engine even if your game is only shipping on Playstation can make programming and content development much easier. Another advantage is that you can hide some details of the graphics library that you never intend to use or raise the level of abstraction slightly in order to eliminate common errors. This can make graphics programming slightly more accessible for junior staff members.

In this article, I will show what a typical graphics library abstraction can look like in a rendering engine. We will call the abstraction a render device.

I was hesitant to start a rendering engine series with a render device abstraction for several reasons.

First, abstractions are difficult. We want the abstraction to have minimal performance overhead; we want it easy to use; we want it easy to maintain; we want it to surface all functionality; we want the abstraction to not “leak”; and so on.

Second, if you are one of those junior programmers beginning your career, starting off with OpenGL or DirectX, and not abstracting them, is a better choice. If you have never seen more than one graphics library, you are not going to know how to abstract it.

Finally, if you work for a professional graphics team, abstracting the render device will depend on your business’ needs. If you are a Windows-only shop building software expected to only ever run on Windows, you may want to choose DirectX and not abstract it away. Abstracting the render device when your business does not want or need it can limit turnaround time for features due to the extra cognitive burden of an extra abstraction.

Assuming you have the desire or need to abstract the render device in your engine, this article can help. To follow along, you should already be familiar with general graphics programming principles and have experience programming with at least one graphics library, preferably in C or C++.

Personally, I prefer having a render device abstraction, even in my hobby engines and projects. That way, I can easily port to a new platform and graphics library and have all of my projects work in that environment.

Dependencies

In this article, we will implement our render device around the OpenGL 4 core profile. We will make use of the GLFW library. GLFW abstracts away the platform-specific concerns of a window, swap chain, and OpenGL context creation.

I am choosing to use GLFW to abstract various platform concerns that are not directly related to the render device, such as input. In a production-ready, cross-platform engine, you will also need to abstract the window and other platform elements. In this article, we will leverage GLFW for those concerns so that the code we build will work on as many platforms as possible.

Porting to DirectX will require us to abstract away more of those platform components using a library other than GLFW. That will not be covered in this article.

I am using CMake for the build system. Again, to ensure the code is easy to run on many different platforms.

I have tested the code on Windows 10 using Microsoft Visual Studio 2015 and on Mac OS X (Sierra) using Xcode 8.

Profiling Multithreaded Unity3d Applications

You can download the entire set of Unity assets I used for this post from BitBucket: https://bitbucket.org/amesgames/multithreaded-profiling-in-unity. I used Unity 5.3.5 to develop and test the sample scenes and code.

Introduction

Hello and welcome to the first software development blog post I have written in a while. This article kicks off my new website with relevant software development challenges that programmers are facing today. Along with the challenges you will also find solutions that can help inform your current and future software projects.

Problem

Recently, I have been spending time in the Unity game engine. Very early on, it became clear that my applications needed to make use of multiple threads in order to achieve their target frame rate. Any computer your Unity application will run on likely has two or more CPU cores. However, all of the Unity MonoBehaviour scripts in a scene run only on the main thread.

Equipped with some knowledge of the C# System.Threading library, you will have no trouble running parts of your Unity application in a thread other than the main thread, so long as you are not directly calling into the Unity APIs or directly accessing GameObjects or their Components.

However, the built-in profiler that comes with the editor has no way of showing what is occurring on these other threads. In fact, making calls to UnityEngine.Profiler.BeginSample or UnityEngine.Profiler.EndSample from threads other than the main thread will generate an exception.

Run the following MonoBehaviour script to see the runtime exception.

using System.Threading;
 
using UnityEngine;
 
namespace Amesgames.Behaviours
{
	public class UnityProfilerExceptionBehaviour : MonoBehaviour
	{
		void Start()
		{
			new Thread(new ThreadStart(Work)).Start();
		}
 
		void Work()
		{
			UnityEngine.Profiler.BeginSample("test");
			UnityEngine.Profiler.EndSample();
		}
	}
}

In this article, I will describe a solution to these problems. I assume that you already have a basic understanding of multi-threading and synchronization patterns. For a comprehensive survey of synchronization patterns in C#, see this tutorial on Threading in C#.

Let’s begin.