Submitting the Background Color
Within cExampleGame.h owns a Color type variable titled bgColor. This variable is then initialized with the appropriate RGB values to construct a color in the Initialize() function of the game code. Than, it is submitted along with other data to the Graphics Library within the SubmitDataToBeRendered() function of the game class.
Submitting Renderable Objects: Sprites and Effects
Sprites, or the representation of the polygons on screen, are created by specifying the height, width, and center of their representation (centerX and centerY). As the programmer, you can initialize these objects as pointers in your header file, for example, making sure to keep them as nullptr. Then, in the Initialize() function of your game you can factory load them with the Load() function.
Effects are the visual effects like color and transformation of a given Sprite in the game. These are initialized similarly to Sprites via their declaration as nullptrs and then loaded via a factory Load() function. Effects require a path to the location of the Fragment and Vertex .shd files. The initial render state must also be specified.
Packaging Sprites and Effects for Submission to the Graphics Library
In the main loop of the game application, the programmer must submit this data to Graphics in order to be rendered. To combine Sprites with their appropriate Effects, we use the std::pair<> collection. Which takes both the Sprite and Effect as pointers. This can be seen on line 9 of the Snippet2.cpp sample posted above.
Submission and Note about Cleanup
The actual submission call is quite simple! You can do this simply within the SubmitDataToBeRendered function of your game loop with a SubmitRenderableToRender() call with your pertinent pair(s). NOTE: The programmer is responsible for taking care of Decrementing the Reference Count of these pairs. This is done like so:
You’re probably wondering why you have to cache all of the data for a single frame rather than immediately sending over the objects as they’re initialized. For our purposes, in a simple game such as this, we want to make sure that we are ready with all the required render data for a given frame before actually beginning the render process. It makes sense that if we wanted something to show on screen together that we also would like to ensure that all of the required data is ready to go at the same time.
On the topic of Multiple Sprites and/or Effects for a Given Frame
This is made possible via how the Graphics library handles the data, or std::pair<>’s that we used to package up our Sprite and Effect. Briefly, the collection used to store this data is a std::Vector behind the scenes, they are rendered in order of their placement into said vector.
sizeof(Sprite) and sizeof(Effect)
Below are the results of a sizeof() call on the two classes priorto improvements:
Concerning the topic of how to make either classes “Smaller” in their byte size. This can be done in a few ways:
- Considering the probable size of a given member variable and adjusting to what’s needed
- For example, using a uint8_t to represent a value instead of an entire int.
- Using a bitfield instead of a Boolean to store a True or False value
- Taking advantage of a given architecture’s data alignment and making sure that your member variables align with those specifications:
- Some SSE2 instructions on x86 require 128-bit/16-byte aligned data, and padding may be inserted in order for data to fall on those bounds. Making sure that your variables are structured in such a way to minimize the padding required could make a large difference in the overall size of a given class
- Prioritizing the use of pointers, which are merely an address, over actual references to objects in memory.
- The con here being that now the programmer must take into consideration proper memory allocation, pointer management, and other pitfalls when using a pointer heavy architecture. Especially how accessing data via a pointer could lead to worse performance due to the possible lack of cache coherency.
Some thoughts however on our specific implementation:
- Examining the cRenderState class, it could benefit from being a pointer. More specifically, it doesn’t exactly need to be contained whole in our Effect. Of course, there’s the considerations you must make when it comes to working with pointers but it should be fairly easy.
- File Path Manipulation
- Currently we require the entire path of given fragment/vertex data in the creation of an Effect. This is because I’m using char of a fixed size to hold the path of such. This could make more sense as a char* and size management. The goal being able to get rid of the MAX_PATH_LENGTH definition in Effect.h. There may be moments when this is too much or more common, perhaps not enough to store the entire path depending on where the programmer would like to store this data.
- Currently we require the entire path of a given fragment/vertex data in the creation of said effect. Instead, we could make it more efficient and user friendly by allowing the programmer to only specify the file name of the given files. The actual file paths could then be hard-coded directly into the loading function itself.
- By omitting the use of a char array in the Effect class, we were able to bring down the size even more because we can use the directly strings passed into the load function:
With this stage we began to move away from hardcoded objects from within the Graphics library and moving them to the more sensible location of within the game code. I found this assignment actually interesting and satisfying not just because it’s working towards a sensible goal, but it was structured in such a way that made sense.
You can try out these games via the below links. The only difference is that Direct3D will be used in the x64 version, with OpenGL in the other. They have been built and verified to work on Windows.