Post

Assignment 04

September 19, 2026

Objective

This week, I improved upon the game engine system design by moving instances of cMesh and cEffect from the Graphics project to the main MyGame application. This involved setting up reference counting without the use of smart pointers, an limitation set in place for learning purposes. This also means that main application has to submit data to the Graphics project. Therefore, I created gameplay programming friendly interfaces that allow the main application to submit background colors and cMesh/cEffect reference pairs.

Gameplay

DallinFrank_assignment04_x64

DallinFrank_assignment04_x86

Gameplay Footage

The running game features a dark blue background with two separate meshes with unique effects. When the space key is held, the mesh in the top right corner is hidden. When the shift key is pressed, the mesh in the bottom left corner uses the animated color shader.

Project Updates

Data Submission To Render A Frame

SubmitBackgroundColor

The primary goal of the improvements made this week was to allow for a way for the MyGame project to interact with the Graphics project without knowing or caring about the internals. First, I provided an interface for the main application to set the background color. I considered creating an interface that used a color enum that would then reconstruct the color later, but I figured the flexibility of RGBA values was too important, and it’s not unreasonable to ask a gameplay programmer to provide specific values. The signature and usage for submitting the background color can be seen below.

1
2
3
4
5
// Signature in the Graphics project
void SubmitBackgroundColor(float r, float g, float b, float a = 1.0f);

// Usage from the MyGame project
eae6320::Graphics::SubmitBackgroundColor(0.0f, 0.0f, 0.2f);

SubmitPair

It was also important that the Graphics project no longer contained mesh instances, but rather references to meshes and effects. Therefore I created the SubmitPair interface, which takes in a pointer to a cMesh and a pointer to a cEffect. I considered created a struct to track this pairing, but decided that I would make this decision in a future update when we create some sort of actor representation. Instead, the Graphics project populates a static array with std::pair<cMesh*, cEffect*> pairings.

1
2
3
4
5
6
7
8
// MeshEffectPair typedef
typedef std::pair<eae6320::Graphics::cMesh*, eae6320::Graphics::cEffect*> MeshEffectPair;

// Signature in the Graphics project
void SubmitPair(eae6320::Graphics::cMesh* mesh, eae6320::Graphics::cEffect* effect);

// Usage from the MyGame project
eae6320::Graphics::SubmitPair(s_mesh, s_animatedColorEffect);

cMesh

The logging message below was used to determine the size of cMesh on both platforms. On the x64 architecture, cMesh is 32 bytes and on the x86 architecture, cMesh is 16 bytes.

1
eae6320::Logging::OutputMessage(std::to_string(sizeof(eae6320::Graphics::cMesh)).c_str());

Now wait a minute! You might recall that the size of cMesh was the same exact size last week, but we’ve now introducing a reference counter, so the size should in fact be bigger, right? Last week, m_triangleCount was an unsigned int that took 4 bytes in total. I decided to use a uint16_t instead since it’s smaller (2 bytes) and should cover the same range as the positive range on a signed int. Since the new m_referenceCount from the EAE6320_ASSETS_DECLAREREFERENCECOUNT() is also uint16_t, the total size is the same as last week’s. The size of the struct cannot be made any smaller, size these variables are necessary to represent a mesh, and the ordering cannot be optimized further. The variables with the largest sizes are placed at the top, with smaller variables at the bottom. If the two uint16_t variables were not placed next to each other, the size would increase by another 4 bytes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct cMesh
{
#if defined( EAE6320_PLATFORM_D3D )
    cVertexFormat* m_vertexFormat = nullptr;
    ID3D11Buffer* m_vertexBuffer = nullptr;
    ID3D11Buffer* m_indexBuffer = nullptr;
#elif defined( EAE6320_PLATFORM_GL )
    GLuint m_vertexBufferId = 0;
    GLuint m_vertexArrayId = 0;
    GLuint m_indexBufferId = 0;
#endif
    uint16_t m_triangleCount;
    uint16_t m_referenceCount = 1;
};

cEffect

The logging message below was used to determine the size of cEffect on both platforms. On the x64 architecture, cEffect is 56 bytes and on the x86 architecture, cEffect is 16 bytes.

Last week, cEffect class was 16 bytes on x86 architecture, and 48 bytes on x64 architecture. Similar to the cMesh class,I took advantage of the fact that m_referenceCount is only 2 bytes and that m_renderState is only 1 byte on the x86 architecture. Since this class is properly ordered, the packed size is only 16 bytes. And like before, ordering them incorrectly could result in a larger size.

For the x64 architecture, addresses are twice the size. Unfortunately, m_renderState includes 3 additional pointers and the 1 byte member variable, giving it a size of 25 bytes rounded up to 32 bytes. Because the largest variable within m_renderState is 8 bytes, there must be 7 bytes of padding after the 1 byte member variable and that space cannot be used for our new reference counter. Since m_referenceCount must align with an 8 byte address now, there is an additional 6 bytes of padding following it, no matter where it is placed in the class. This brings the x64 implementation to have a total of 15 unused bytes..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class cEffect
{
    cShader* m_vertexShader;
    cShader* m_fragmentShader;

#if defined ( EAE6320_PLATFORM_D3D )
    cRenderState m_renderState;
    uint16_t m_referenceCount = 1;
#elif defined( EAE6320_PLATFORM_GL )
    GLuint m_programId = 0;
    uint16_t m_referenceCount = 1;
    cRenderState m_renderState;
#endif
}

sDataRequiredToRenderAFrame

Below is the struct sDataRequiredToRenderAFrame. On x64, the struct is 2216 bytes and on x86, the struct is 1188 bytes. Since there are two of these structs, the total memory budget if 4432 on x64 and 2376 on x86. These values are easily changed with the variable N, which sets the max size of the MeshEffectPair array. I arbitrarily set it to 128, but I’m sure many values could work.

I am also choosing not to include the memory for the objects that the MeshEffectPair pointers reference because it is not actually stored in the frame cache, and doesn’t really effect the frame itself much.

1
2
3
4
5
6
7
8
9
10
constexpr size_t N = 128;
typedef std::pair<eae6320::Graphics::cMesh*, eae6320::Graphics::cEffect*> MeshEffectPair;

struct sDataRequiredToRenderAFrame
{
    eae6320::Graphics::ConstantBufferFormats::sFrame constantData_frame;
    float r, g, b, a;
    uint16_t meshEffectPairIndex = 0;
    std::array<MeshEffectPair, N> meshEffectPairs{};
}