In our game engine we load assets in a separate thread since we need to load assets and textures dynamically.
Since only one thread can access a GL context at a time, we do all the GL calls in the rendering thread. For shaders this means that we load the shader source in the loader thread, and compile the shader on the first frame it is used in the rendering thread.
This causes a lot of stuttering when many assets are loaded at the same time. It seems that compiling shaders is a quite heavy operation.
Is there any way to do the compilation of shaders in a separate thread? Other ways to alleviate this problem? We are using GLSL shaders, and we have a lot of them…
I guess we could put the shader compilation into a queue where we only process a few shaders each frame, but the best would be if we could do it asynchronously.
How many shaders are you talking about? For me, a shader gets compiled when a certain combination of states are enabled, nothing really to do with assets being loaded. From what I’ve read in various forum posts over the web, it seems people have shaders packaged up with model assets, as if a shader is heavily coupled to a particular model. This is a bad association to make. I blame Cgfx and rendermonkey for this association.
Think of shaders as being state driven renderer code, with the main states being set by the materials used by a model. Once you make this design choice, it becomes trivial to pre-compile your shaders at load time, indeed you can compile the shaders while the main data is being loaded in a separate thread, in a hidden and shared GL context if you like. It’s just driver work, nothing expensive happens on the card during compilation.
I’ve read in an nvidia paper that there is a very coarse lock in the driver that is held when compiling shaders, meaning even if you were to compile in a different thread, the main rendering thread would get held up while you were compiling anyway.
Ofcourse this could have changed since that paper was written.
Well at load time my render thread is either building a shader or uploading Mesh/Texture data passed to it by the loader thread, while the loader thread deals with loading the stuff off disk. This way you get resource loading and shader compilation pretty much in parallel, which minimises the impact of the absence of binary blobs.
This was one of the main reasons we switched from forward to deferred lighting. With forward lighting and a maximum of 8 lights, there were a ton of possible shader variations that had to be loaded. Since it was not feasible to load and compile 500 versions of each shader, I thought we could just compile them on the fly when needed, but then we got the horrible stuttering like you describe.
If you are only using a few different shaders and don’t have a million variations of each, I think it is okay to load them when you need them, or to load them all ahead of time.
Ok, thanks for all the good answers here!
I’ve solved it by queueing the shaders so that only a few are compiled each frame. We get a couple of frames with weird lighting before the shaders are ready, but that’s ok.
Some of the shaders can also be precompiled during startup, so the stuttering isn’t that bad anymore.