Series of Articles
Problem Overview
Let’s say we have a game engine that uses OpenGL and we need to load textures asynchronously so that we don’t block the main thread, and we can load the editor or game much faster.
Now as previously demonstrated, I could launch a new set of threads and have them load up the textures (I’m using stb_image for that) and generate textures with glGenTextures
. The main issue here is that OpenGL context is only available in the main thread so if we want to take advantage of multi-threading texture loading, we need to split the loading and generating for textures.
Loading is going to be done in worker threads, and generating textures will be done in the main thread. The following diagram shows a simplified workflow of what we’ll achieve.
Simplified execution of loading textures in worker threads, and processing them in the main thread.
In our main thread, we have a method that will check our processing textures queue for a job. If it finds one, it generates the OpengGL texture and assigns it back to the material.
void AssetManager::Update()
{
if (!m_processingTexturesQueue.Empty())
{
TextureLoadJob assetJob;
if (m_processingTexturesQueue.TryPop(assetJob))
{
Texture outputTexture = GenerateTexture(assetJob.loadedData, assetJob.textureType);
assetJob.materialOwner->AddTexture(outputTexture);
}
}
}
The loader thread will continuously run and check the loading textures queue for jobs. In this case, I load the texture from a file path and assign the result into the loaded data.
void AssetManager::LoaderThread()
{
while (m_loadingThreadActive)
{
if (!m_loadingTexturesQueue.Empty())
{
TextureLoadJob assetJob;
if (m_loadingTexturesQueue.TryPop(assetJob))
{
assetJob.loadedData = LoadTextureData(assetJob.texturePath);
m_processingTexturesQueue.Push(assetJob);
}
}
}
}
In game sponza scene.
This architecture allows me to load textures while the game is running without blocking the main thread. It's a bit pointless to compare times here since I’m using my own sandbox instead of a sample program to test only this matter. See Part 1 and Part 2 for more information and code you can follow along.
In the next part, we’ll parallelize a toy Ray Tracer. This a different problem on its own where we need to use the resulting values of multiple threads or jobs to build a final image.
Continue Reading