The Assignment:
This time I made progress on separating data from the Graphics library. All rendering jobs now are being submitted to the Graphics loop from the game code. By doing this I’ve created a very basic version of a job system.
The Results:
A Windows application that renders a square-shaped mesh and a triangle-shaped mesh in a window where a fragment shader changes the color of the triangle continuously over time, and the square stays white.
Control of the game: F1 to hide triangle, F2 to hide square, F3 to change the effect of the triangle, F4 to change the effect of the square.
The Game at the default state
The Game when the square is hidden
The Game when the square uses a different effect
How Submitting Works:
Submit background color
Submit a mesh-effect pair
The SetBackgroundColor function takes a Color struct instance as input and passes the rgba values to the frame constant buffer.
The SubmitMeshData function takes a Mesh pointer and an Effect pointer as input and passes them to the frame constant buffer. One thing should be noticed is that before passing in the pointers, make sure they have been assigned using the factory functions defined in the Mesh class and the Effect class.
Why Submitting Data Like This?
Caching data for a single frame adds some parallelism to the application and the graphics engine. Essentially it allows the application to fill the data needed for rendering the next frame when the graphics engine is rendering the current frame. This is done by having two buffers to be swapped back and forth. When the data in one buffer is being rendered by the graphics, the other buffer is being populated with new data for the next frame by the application, such that it reduces the wait time between the two threads.
Data Size:
A mesh instance takes 32 bytes in the x64 implementation and 20 bytes in the x86 implementation. At first my x64 mesh instance took 40 bytes. But later I realized I could reorder the variables to group up a uint32 and a uint16 so that the two of them took 8 bytes in total instead of 16.
An effect instance takes 56 bytes in the x64 implementation and 16 bytes in the x86 implementation. I applied the same trick I did for the Mesh class so that the size of an effect instance in x86 is reduced by 4 bytes. (Before reordering, an effect in x86 will take 20 bytes.)
Total Memory:
For now, I have set my sDataRequiredToRenderAFrame struct to have the space to hold ten mesh-effect pointer pairs. So, the total budgeted memory in my graphic project should be 2*sizeof(sDataRequiredToRenderAFrame) + 10*sizeof(mesh) + 10*sizeof(effect).
sizeof(sDataRequiredToRenderAFrame) is 328 in x64, 244 in x86.
Using this formula to calculate, I got the memory size 1536 bytes for x64, 848 bytes for x86.
Good Practice Note:
Make asserting and logging on any function calls that may fail.
Thoughts:
The assignment provided me with a practical solution on data sharing between threads. After adding some parallelism to this program, it finally shows some signature as a game engine. I’m getting excited to see how far it can eventually go.