Now that we have a platform independent Graphics implementation, we now get to generalize it. Right now, we have the Graphics.cpp class instantiate and bind/draw two sets of effects and sprites. Ideally we’d want our Graphics implementation to be able to handle any number of effects and sprites that will be passed to it by the game. The objective now is to enable the game thread to submit sprite and effect data to the render thread so that they can be rendered.
The engine code that JP (our professor) has given us runs separate threads for the game (or application) and the rendering engine. So transferring data between these threads will not be easy. Luckily the engine exposes useful APIs (like the “SubmitDataToBeRendered()” virtual function) that inform the game when the render thread is ready to accept new data.
The first thing I did was add function to the Graphics.cpp that let the clear color be driven by the game instead of having it hardcoded in the Graphics implementation. I created a simple Color struct which holds the RGBA values and added a function called “SubmitClearColor” in the Graphics implementation which takes in a color value and caches it for when it is ready to render.
The next step was to figure out a way to submit the Sprite and Effect data. However, since we are going to be sharing pointers between threads in this case, we need to implement reference counting first. The engine, being the ever present savior, allows us to do so easily using the “ReferenceCountedAssets.h” interface. By adding in a few macros, hiding the default constructors and setting up factory functions for the Sprite and Effect classes, I was able to make them reference counted. I remember spending a whole week implementing reference counting in my own engine, I’m glad we didn’t have to do it again. The size of the Sprite and Effect class after making these changes ended up being 56 and 48 bytes in x64 and 48 and 20 bytes in x86. I don’t think I could have reduced the size further as I made sure to order the member variables so that they appear in decreasing order of size in memory, this allows the class to fit optimally into a 4 byte aligned row of memory. I could increase the size by rearranging the variables but who wants to do that?
Once reference counting was in it was pretty straightforward to expose a function in Graphics through which the application thread could submit the Sprite and Effect data for all the Sprites that need to rendered. I used two std::vector objects to store the list of sprites and effects to be rendered. These get cleared at the end of each render cycle.
From what I can make out from JP’s engine code, the application thread waits for a signal from the render thread that tells it that it can submit the data for the next frame. The render thread maintains two pointers that hold the same type of struct which specifies the data required to render a frame. One pointer holds the data that is submitted by the application thread (that needs to be rendered) and the other one holds data that is currently being rendered. Once rendering is completed, the render thread clears the active render pointer and swaps the two pointers (so that the submit pointer is now being rendered and the render pointer can accept new submissions) and signals to the application thread that it can now submit more data. Things are done this way to maximize concurrency between the two threads.
Now that we have a mechanism to submit sprite and effect data, we can now move the Sprite and Effect data out of our Graphics implementation and into the game class. I initialize 3 sprites and effects in the game’s “Initialize” function and add them to their respective vectors. Whenever the application thread calls “SubmitDataToBeRendered()” I submit the Sprite and Effect vectors.
To have a little fun I decided to manipulate the clear color based on time so that the final product ends up looking like this.