Multithreaded rendering (Part 2)
So in last post on this topic I described a very general look about what is happening inside threads and show some problems that I found in meantime of prototyping. This time we will look a little more on solution that is use in White Rabbit Engine. And few things here:
- Starting from this post I will change a little convention of naming rendering Api. I will use name OpenGL in situation when problem will be 100% connected to it (i.e. extensions, bugs etc.) for everything else I will use rApi so text be a little more general.
- I don't claim that this solution is something really innovative and I will surprise you with it.
- I still work on this system and there is really big probably that some part of it will somehow change in future.
So after this short intro let's move to main topic. As was mentioned earlier engine slowly switched from single thread solution to multiple. Initial loop looked something like that:
First thing that changed was [IO update] that was moved on separate thread. This step was easy other wasn't. There is of course possibility to move [game update] to other threads and left rendering the way it is on main thread. I didn't decide on this mostly because I hadn't any idea on solution which don't have a lot of objects synchronizations (gameplay <-> rendering). And I wanted to avoid it because it connects with big number of critical sections which aren't help later in debugging of code. ->
So I searched more and decided to left only rApi calls and [System Events] on main thread. The most intuitive solution to it was to create some kind of rApi render list which will store all rendering calls and after filling it is passed to main thread. This step changed a little so everything looked almost the same:
Even if this step look so simple it shown some nice gains. Below some of it:
- White Rabbit Engine rAPI render list work on continuous memory (it's size is defined on initialization) so it's a lot more CPU cache friendly.
- Only one place where rApi calls can be done.
- Nice separation between rendering ([Gathering Data]) and rApi calls ([Render Data]),
- Code cleanup to remove some hacks that used rApi directly.
- Tracing of bugs in rendering was a lot easier and there was option to repeat frame rendering.
After this I knew that it was good change. But still it only worked on one thread and simplest solution to this problem would be something like this.
For simple framework it would probably work. But in case of White Rabbit Engine don't. Mostly because:
- It use dynamic geometry that update rApi buffers in meantime of game loop.
- Textures and meshes can be load in any moment of game (preparation for streaming).
- There is no separation between rendering of load screen and rendering of game.
So there are some problems and a lot places for performance gain ( right now [Render Data] need to wait for rApi render list from [Gather Data] ). Of course this is only begin and a lot things need to change so it could be fully usable. But about them next time.
Till the next post.