GL3: Make gl3drawCmd_t smaller. Step 2: Move transModelMat elsewhere

Most of the time the transformation matrix is the identity matrix
anyway, so let's not waste 64 bytes in every gl3drawCmd_t...

Now it's just an index into a hmm_mat4 array.
Index 0 is always the identity matrix, if a different one is required,
GL3_SetDrawCmdTransMatrix(&drawCmd, mat) can be used to set it - it will
be added to that array and its index is set in drawCmd.

Bonus: This doesn't only reduce the gl3drawCmd_t to 36 bytes but also
makes comparing two of them a lot cheaper: Instead of comparing the
values of the matrices, we can just compare their index.
Also frees up one flag (`flag & DCFlag_IsIdentityMat` is replaced by
`transModelMatIdx == 0`).
This commit is contained in:
Daniel Gibson 2026-07-17 01:07:13 +02:00
parent 2013aecee3
commit 964492e0fd
4 changed files with 49 additions and 64 deletions

View file

@ -136,11 +136,12 @@ static cvar_t *gl_znear;
DA_TYPEDEF(gl3_3D_vtx_t, Vtx3DArray_t);
DA_TYPEDEF(GLushort, UShortArray_t);
DA_TYPEDEF(gl3drawCmd_t, DrawCommandArray_t);
DA_TYPEDEF(hmm_mat4, Mat4Array_t);
// dynamic arrays to batch all consecutive 3D draws (with gl3_3D_vtx_t) with same texture to reduce drawcalls
static Vtx3DArray_t vtxBuf = {0};
static UShortArray_t idxBuf = {0};
static DrawCommandArray_t drawCmds = {0};
static Mat4Array_t transModelMats = {0};
// Yaw-Pitch-Roll
// equivalent to R_z * R_y * R_x where R_x is the trans matrix for rotating around X axis for aroundXdeg
@ -190,7 +191,7 @@ GL3_RotateUni3DforEntity(entity_t *e)
// if replaceTransModelMat is true, the existing value of drawCmd->transModelMat
// is ignored and it's replaced with the one calculated here (otherwise they're multiplied)
void
GL3_RotateForEntity(entity_t *e, gl3drawCmd_t* drawCmd, qboolean replaceTransModelMat)
GL3_RotateForEntity(entity_t *e, gl3drawCmd_t* drawCmd)
{
// TODO: shortcut for "not rotated at all"?
@ -203,11 +204,7 @@ GL3_RotateForEntity(entity_t *e, gl3drawCmd_t* drawCmd, qboolean replaceTransMod
transMat.Elements[3][i] = e->origin[i]; // set translation
}
if (replaceTransModelMat)
drawCmd->transModelMat = transMat;
else
drawCmd->transModelMat = HMM_MultiplyMat4(drawCmd->transModelMat, transMat);
drawCmd->flags &= ~DCFlag_IsIdentityMat;
GL3_SetDrawCmdTransMatrix(drawCmd, transMat);
}
@ -551,6 +548,9 @@ GL3_Init(void)
GetPCXPalette (&colormap, d_8to24table);
free(colormap);
da_clear(transModelMats);
da_add(transModelMats, gl3_identityMat4); // element 0 is always the identity matrix
GL3_Register();
/* set our "safe" mode */
@ -694,6 +694,7 @@ GL3_Shutdown(void)
da_free(vtxBuf);
da_free(idxBuf);
da_free(drawCmds);
da_free(transModelMats);
/* shutdown OS specific OpenGL stuff like contexts, etc. */
GL3_ShutdownContext();
@ -765,21 +766,21 @@ void GL3_Draw3DBatchesNow()
// set curState to something that reflects the actual state, as far as possible,
// and otherwise makes sure it'll be set in the loop
gl3drawCmd_t curState = GL3_CreateDrawCmd(true);
curState.flags = ~drawCmds.p[0].flags; // just the opposite flags of the first element
gl3drawCmd_t curState = GL3_CreateDrawCmd();
curState.texnum = gl3state.currenttexture;
curState.lmtexnum = gl3state.currentlightmap;
curState.alpha = gl3state.uni3DData.alpha;
curState.scroll = gl3state.uni3DData.scroll;
curState.lightScaleForTurb = gl3state.uni3DData.lightScaleForTurb;
gl3ShaderInfo_t* shader = NULL;
// for the next two the approach is setting a value that
// is different from the one in the first drawCmd, to make sure
// the corresponding state is set in the first iteration
curState.flags = ~drawCmds.p[0].flags; // just the opposite flags of the first element
curState.transModelMatIdx = drawCmds.p[0].transModelMatIdx + 1;
// .. but at least set the identity mat so DCFlag_IsIdentityMat can be set
// as a sane default
curState.flags |= DCFlag_IsIdentityMat;
gl3state.uni3DData.transModelMat4 = gl3_identityMat4;
GL3_UpdateUBO3D();
gl3ShaderInfo_t* shader = NULL;
for(int i=0, n=da_count(drawCmds); i < n; ++i)
{
@ -862,20 +863,11 @@ void GL3_Draw3DBatchesNow()
updateUni3D = true;
}
if((flags & DCFlag_IsIdentityMat) != (curFlags & DCFlag_IsIdentityMat))
if(cmd->transModelMatIdx != curState.transModelMatIdx)
{
// one is the identity matrix, the other isn't => we need to update
gl3state.uni3DData.transModelMat4 = cmd->transModelMat;
gl3state.uni3DData.transModelMat4 = da_get(transModelMats, cmd->transModelMatIdx);
updateUni3D = true;
}
else if( !(flags & DCFlag_IsIdentityMat) )
{
// neither is an identity matrix
// TODO: could check if they're identical and only update if not
gl3state.uni3DData.transModelMat4 = cmd->transModelMat;
updateUni3D = true;
}
// else both are the identity matrix - nothing to do
if(updateUni3D)
GL3_UpdateUBO3D();
@ -891,13 +883,15 @@ void GL3_Draw3DBatchesNow()
da_clear(idxBuf);
da_clear(drawCmds);
da_setcount(transModelMats, 1); // keep index 0 (identity matrix)
// restore sane default for other draw operations (models, particles, 2D)
int curFlags = curState.flags;
if( !(curFlags & DCFlag_IsIdentityMat) )
if(curState.transModelMatIdx != 0)
{
gl3state.uni3DData.transModelMat4 = gl3_identityMat4;
GL3_UpdateUBO3D();
}
int curFlags = curState.flags;
if(curFlags & DCFlag_Blend)
glDisable(GL_BLEND);
if(curFlags & DCFlag_DisableDepthMask)
@ -910,7 +904,8 @@ void GL3_Draw3DBatchesNow()
static qboolean drawStateEqual(const gl3drawCmd_t* a, const gl3drawCmd_t* b)
{
if( a->flags != b->flags || a->shaderIdx != b->shaderIdx
|| a->texnum != b->texnum || a->lmtexnum != b->lmtexnum )
|| a->texnum != b->texnum || a->lmtexnum != b->lmtexnum
|| a->transModelMatIdx != b->transModelMatIdx )
return false;
int flags = a->flags; // at this point we know the flags are identical
@ -928,24 +923,19 @@ static qboolean drawStateEqual(const gl3drawCmd_t* a, const gl3drawCmd_t* b)
return false;
if((flags & DCFlag_UseLmStyles) && !lmstylesEqual(a->styles, b->styles))
{
return false;
}
if( !(flags & DCFlag_IsIdentityMat) )
{
const float* atm = a->transModelMat.Elements[0];
const float* btm = b->transModelMat.Elements[0];
for(int i=0; i<16; ++i)
{
if(atm[i] != btm[i]) // TODO: tolerance?
return false;
}
}
return true;
}
void
GL3_SetDrawCmdTransMatrix(gl3drawCmd_t* drawCmd, hmm_mat4 mat)
{
int idx = da_count(transModelMats);
da_add(transModelMats, mat);
drawCmd->transModelMatIdx = idx;
}
// assumes gl3state.v[ab]o3D are bound
// buffers and draws gl3_3D_vtx_t vertices
// drawMode is something like GL_TRIANGLE_STRIP or GL_TRIANGLE_FAN or whatever
@ -1033,7 +1023,7 @@ GL3_DrawBeam(entity_t *e)
int i;
enum { NUM_BEAM_SEGS = 6 };
gl3drawCmd_t drawCmd = GL3_CreateDrawCmd(true);
gl3drawCmd_t drawCmd = GL3_CreateDrawCmd();
vec3_t perpvec;
vec3_t direction, normalized_direction;
@ -1109,7 +1099,7 @@ GL3_DrawSpriteModel(entity_t *e, gl3model_t *currentmodel)
dsprite_t *psprite;
gl3image_t *skin;
gl3drawCmd_t drawCmd = GL3_CreateDrawCmd(true);
gl3drawCmd_t drawCmd = GL3_CreateDrawCmd();
/* don't even bother culling, because it's just
a single polygon without a surface cache */
@ -1180,7 +1170,7 @@ static void
GL3_DrawNullModel(entity_t *currententity)
{
vec3_t shadelight;
gl3drawCmd_t drawCmd = GL3_CreateDrawCmd(true);
gl3drawCmd_t drawCmd = GL3_CreateDrawCmd();
if(currententity->flags & RF_TRANSLUCENT)
drawCmd.flags |= DCFlag_DisableDepthMask;
@ -1194,7 +1184,7 @@ GL3_DrawNullModel(entity_t *currententity)
GL3_LightPoint(currententity, currententity->origin, shadelight);
}
GL3_RotateForEntity(currententity, &drawCmd, true);
GL3_RotateForEntity(currententity, &drawCmd);
drawCmd.alpha = 1.0f;
for(int i=0; i<3; ++i)

View file

@ -164,7 +164,6 @@ GL3_DrawGLPoly(msurface_t *fa, gl3drawCmd_t drawCmd)
{
glpoly_t *p = fa->polys;
GL3_BufferAndDraw3D(p->vertices, p->numverts, GL_TRIANGLE_FAN, drawCmd);
}
@ -296,7 +295,7 @@ GL3_DrawAlphaSurfaces(void)
msurface_t *s;
/* go back to the world matrix */
gl3drawCmd_t drawCmd = GL3_CreateDrawCmd(true);
gl3drawCmd_t drawCmd = GL3_CreateDrawCmd();
drawCmd.flags |= DCFlag_Blend;
for (s = gl3_alpha_surfaces; s != NULL; s = s->texturechain)
@ -344,7 +343,7 @@ DrawTextureChains(entity_t *currententity)
c_visible_textures = 0;
gl3drawCmd_t drawCmd = GL3_CreateDrawCmd(true);
gl3drawCmd_t drawCmd = GL3_CreateDrawCmd();
for (i = 0, image = gl3textures; i < numgl3textures; i++, image++)
{
@ -477,7 +476,7 @@ GL3_DrawBrushModel(entity_t *e, gl3model_t *currentmodel)
return;
}
gl3drawCmd_t drawCmd = GL3_CreateDrawCmd(false);
gl3drawCmd_t drawCmd = GL3_CreateDrawCmd();
if(e->flags & RF_TRANSLUCENT)
drawCmd.flags |= DCFlag_DisableDepthMask;
@ -527,7 +526,7 @@ GL3_DrawBrushModel(entity_t *e, gl3model_t *currentmodel)
e->angles[0] = -e->angles[0];
e->angles[2] = -e->angles[2];
GL3_RotateForEntity(e, &drawCmd, true);
GL3_RotateForEntity(e, &drawCmd);
e->angles[0] = -e->angles[0];
e->angles[2] = -e->angles[2];

View file

@ -253,8 +253,6 @@ GL3_EmitWaterPolys(msurface_t *fa, gl3drawCmd_t drawCmd)
drawCmd.lightScaleForTurb = fa->texinfo->image->is_lava ? 1.0f : 0.5f;
drawCmd.flags |= DCFlag_UseLightScaleForTurb;
GL3_SetDrawCmdShader(&drawCmd, &gl3state.si3Dturb);
for (bp = fa->polys; bp != NULL; bp = bp->next)
@ -684,7 +682,7 @@ GL3_DrawSkyBox(void)
}
}
gl3drawCmd_t drawCmd = GL3_CreateDrawCmd(false);
gl3drawCmd_t drawCmd = GL3_CreateDrawCmd();
// glPushMatrix();
@ -697,7 +695,8 @@ GL3_DrawSkyBox(void)
hmm_vec3 rotAxis = HMM_Vec3(skyaxis[0], skyaxis[1], skyaxis[2]);
modMVmat = HMM_MultiplyMat4(modMVmat, HMM_Rotate(r_newrefdef.time * skyrotate, rotAxis));
}
drawCmd.transModelMat = modMVmat;
GL3_SetDrawCmdTransMatrix(&drawCmd, modMVmat);
GL3_SetDrawCmdShader(&drawCmd, &gl3state.si3Dsky);

View file

@ -278,12 +278,13 @@ typedef struct
// drawcommands using gl3_3D_vtx_t, for batching
typedef struct gl3drawCmd_s {
hmm_mat4 transModelMat;
GLuint texnum;
signed char lmtexnum;
signed char shaderIdx;
// index into gl3_main.c transModelMats; 0 always is identity matrix
unsigned short transModelMatIdx;
float scroll; // for gl3state.uni3DData.scroll
float lightScaleForTurb; // for gl3state.uni3DData.lightScaleForTurb
float alpha; // either part of color or for gl3state.uni3DData.alpha
@ -305,20 +306,15 @@ enum gl3drawCmd_Flags {
DCFlag_UseScroll = 16,
DCFlag_UseLmStyles = 32,
DCFlag_UseLightScaleForTurb = 64,
DCFlag_IsIdentityMat = 128, // avoids comparing the matrix in many cases
// TODO: DCFlag_SameAsPrevious = 255 for "don't check, just merge into previous command"?
};
// create an "empty" gl3drawCmd_t with sane defaults
static inline gl3drawCmd_t
GL3_CreateDrawCmd(qboolean identityTrans)
GL3_CreateDrawCmd(void)
{
gl3drawCmd_t ret = {0};
if(identityTrans) {
ret.transModelMat = gl3_identityMat4;
ret.flags = DCFlag_IsIdentityMat;
}
ret.alpha = 1.0f;
ret.styles[0] = 255;
ret.lmtexnum = -1;
@ -468,9 +464,10 @@ GL3_BindEBO(GLuint ebo)
extern void GL3_BufferAndDraw3D(const gl3_3D_vtx_t* verts, int numVerts, GLenum drawMode, gl3drawCmd_t drawCmd);
extern void GL3_Draw3DBatchesNow(void);
extern void GL3_SetDrawCmdTransMatrix(gl3drawCmd_t* drawCmd, hmm_mat4 mat);
extern void GL3_RotateUni3DforEntity(entity_t *e);
extern void GL3_RotateForEntity(entity_t *e, gl3drawCmd_t* drawCmd, qboolean replaceTransModelMat);
extern void GL3_RotateForEntity(entity_t *e, gl3drawCmd_t* drawCmd);
// gl3_sdl.c
extern int GL3_InitContext(void* win);