From 50aebd2de45b8dbcf18e459b38796864534d0fc5 Mon Sep 17 00:00:00 2001 From: Jaime Moreira Date: Mon, 15 Jul 2024 06:56:02 -0400 Subject: [PATCH] GL1 unified draw calls, init Implemented a batching procedure, to try to group meshes in a buffer and use a final GL call to draw them all in one step, instead of the many GL draw calls existing today. For now, only 2D textures are included, especifically console text ("conchars"), scrap and tiles. It's not worth doing this for individual 2D elements (e.g. crosshair). --- CMakeLists.txt | 1 + Makefile | 1 + src/client/refresh/gl1/gl1_buffer.c | 171 ++++++++++++++++++++++++++ src/client/refresh/gl1/gl1_draw.c | 63 +++------- src/client/refresh/gl1/gl1_sdl.c | 1 + src/client/refresh/gl1/header/local.h | 30 +++-- 6 files changed, 206 insertions(+), 61 deletions(-) create mode 100644 src/client/refresh/gl1/gl1_buffer.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ff24654..72377b13 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -590,6 +590,7 @@ set(GL1-Source ${REF_SRC_DIR}/gl1/gl1_surf.c ${REF_SRC_DIR}/gl1/gl1_warp.c ${REF_SRC_DIR}/gl1/gl1_sdl.c + ${REF_SRC_DIR}/gl1/gl1_buffer.c ${REF_SRC_DIR}/files/models.c ${REF_SRC_DIR}/files/pcx.c ${REF_SRC_DIR}/files/stb.c diff --git a/Makefile b/Makefile index 58407ceb..898f5623 100644 --- a/Makefile +++ b/Makefile @@ -959,6 +959,7 @@ REFGL1_OBJS_ := \ src/client/refresh/gl1/gl1_surf.o \ src/client/refresh/gl1/gl1_warp.o \ src/client/refresh/gl1/gl1_sdl.o \ + src/client/refresh/gl1/gl1_buffer.o \ src/client/refresh/files/surf.o \ src/client/refresh/files/models.o \ src/client/refresh/files/pcx.o \ diff --git a/src/client/refresh/gl1/gl1_buffer.c b/src/client/refresh/gl1/gl1_buffer.c new file mode 100644 index 00000000..3e591e5e --- /dev/null +++ b/src/client/refresh/gl1/gl1_buffer.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) 1997-2001 Id Software, Inc. + * Copyright (C) 2024 Jaime Moreira + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * ======================================================================= + * + * Drawing buffer: sort of a "Q3A shader" handler, allows to join multiple + * draw calls into one, by grouping those which share the same + * characteristics (mostly the same texture). + * + * ======================================================================= + */ + +#include "header/local.h" + +#define MAX_VERTICES 16384 +#define MAX_INDICES (MAX_VERTICES * 4) + +typedef struct // 832k aprox. +{ + buffered_draw_t type; + + GLfloat + vtx[MAX_VERTICES * 3], // vertexes + tex[MAX_TEXTURE_UNITS][MAX_VERTICES * 2], // texture coords + clr[MAX_VERTICES * 4]; // color components + + GLushort + idx[MAX_INDICES], // indices + vtx_ptr, idx_ptr; // pointers for array positions + + int texture[MAX_TEXTURE_UNITS]; + int flags; // entity flags + float alpha; +} glbuffer_t; + +glbuffer_t gl_buf; + +GLuint vt, tx, cl; // indices for arrays in gl_buf + +extern void R_MYgluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar); + +void +R_ApplyGLBuffer(void) +{ + // Properties of batched draws here + GLint vtx_size; + qboolean texture; + + if (gl_buf.vtx_ptr == 0 || gl_buf.idx_ptr == 0) + { + return; + } + + // defaults for drawing + vtx_size = 3; + texture = true; + + // choosing features by type + switch (gl_buf.type) + { + case buf_2d: + vtx_size = 2; + break; + default: + break; + } + + glEnableClientState( GL_VERTEX_ARRAY ); + glVertexPointer (vtx_size, GL_FLOAT, 0, gl_buf.vtx); + + if (texture) + { + R_Bind(gl_buf.texture[0]); + + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, 0, gl_buf.tex[0]); + } + + // All set, we can finally draw + glDrawElements(GL_TRIANGLES, gl_buf.idx_ptr, GL_UNSIGNED_SHORT, gl_buf.idx); + // ... and now, turn back everything as it was + + if (texture) + { + glDisableClientState( GL_TEXTURE_COORD_ARRAY ); + } + + glDisableClientState( GL_VERTEX_ARRAY ); + + gl_buf.vtx_ptr = gl_buf.idx_ptr = 0; +} + +void +R_UpdateGLBuffer(buffered_draw_t type, int colortex, int lighttex, int flags, float alpha) +{ + if ( gl_buf.type != type || gl_buf.texture[0] != colortex ) + { + R_ApplyGLBuffer(); + + gl_buf.type = type; + gl_buf.texture[0] = colortex; + gl_buf.texture[1] = lighttex; + gl_buf.flags = flags; + gl_buf.alpha = alpha; + } +} + +void +R_Buffer2DQuad(GLfloat ul_vx, GLfloat ul_vy, GLfloat dr_vx, GLfloat dr_vy, + GLfloat ul_tx, GLfloat ul_ty, GLfloat dr_tx, GLfloat dr_ty) +{ + static const GLushort idx_max = MAX_INDICES - 7; + static const GLushort vtx_max = MAX_VERTICES - 5; + unsigned int i; + + if (gl_buf.idx_ptr > idx_max || gl_buf.vtx_ptr > vtx_max) + { + R_ApplyGLBuffer(); + } + + i = gl_buf.vtx_ptr * 2; // vertex index + + // "Quad" = 2-triangle GL_TRIANGLE_FAN + gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr; + gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr+1; + gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr+2; + gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr; + gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr+2; + gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr+3; + + // up left corner coords + gl_buf.vtx[i] = ul_vx; + gl_buf.vtx[i+1] = ul_vy; + // up right + gl_buf.vtx[i+2] = dr_vx; + gl_buf.vtx[i+3] = ul_vy; + // down right + gl_buf.vtx[i+4] = dr_vx; + gl_buf.vtx[i+5] = dr_vy; + // and finally, down left + gl_buf.vtx[i+6] = ul_vx; + gl_buf.vtx[i+7] = dr_vy; + + gl_buf.tex[0][i] = ul_tx; + gl_buf.tex[0][i+1] = ul_ty; + gl_buf.tex[0][i+2] = dr_tx; + gl_buf.tex[0][i+3] = ul_ty; + gl_buf.tex[0][i+4] = dr_tx; + gl_buf.tex[0][i+5] = dr_ty; + gl_buf.tex[0][i+6] = ul_tx; + gl_buf.tex[0][i+7] = dr_ty; + + gl_buf.vtx_ptr += 4; +} diff --git a/src/client/refresh/gl1/gl1_draw.c b/src/client/refresh/gl1/gl1_draw.c index c8093301..6fe553da 100644 --- a/src/client/refresh/gl1/gl1_draw.c +++ b/src/client/refresh/gl1/gl1_draw.c @@ -77,31 +77,10 @@ RDraw_CharScaled(int x, int y, int num, float scale) scaledSize = 8*scale; - R_Bind(draw_chars->texnum); + R_UpdateGLBuffer(buf_2d, draw_chars->texnum, 0, 0, 1); - GLfloat vtx[] = { - x, y, - x + scaledSize, y, - x + scaledSize, y + scaledSize, - x, y + scaledSize - }; - - GLfloat tex[] = { - fcol, frow, - fcol + size, frow, - fcol + size, frow + size, - fcol, frow + size - }; - - glEnableClientState( GL_VERTEX_ARRAY ); - glEnableClientState( GL_TEXTURE_COORD_ARRAY ); - - glVertexPointer( 2, GL_FLOAT, 0, vtx ); - glTexCoordPointer( 2, GL_FLOAT, 0, tex ); - glDrawArrays( GL_TRIANGLE_FAN, 0, 4 ); - - glDisableClientState( GL_VERTEX_ARRAY ); - glDisableClientState( GL_TEXTURE_COORD_ARRAY ); + R_Buffer2DQuad(x, y, x + scaledSize, y + scaledSize, + fcol, frow, fcol + size, frow + size); } image_t * @@ -190,6 +169,14 @@ RDraw_PicScaled(int x, int y, char *pic, float factor) Scrap_Upload(); } + if (gl->texnum == TEXNUM_SCRAPS) + { + R_UpdateGLBuffer(buf_2d, TEXNUM_SCRAPS, 0, 0, 1); + R_Buffer2DQuad(x, y, x + gl->width * factor, y + gl->height * factor, + gl->sl, gl->tl, gl->sh, gl->th); + return; + } + R_Bind(gl->texnum); GLfloat vtx[] = { @@ -235,31 +222,10 @@ RDraw_TileClear(int x, int y, int w, int h, char *pic) return; } - R_Bind(image->texnum); + R_UpdateGLBuffer(buf_2d, image->texnum, 0, 0, 1); - GLfloat vtx[] = { - x, y, - x + w, y, - x + w, y + h, - x, y + h - }; - - GLfloat tex[] = { - x / 64.0, y / 64.0, - ( x + w ) / 64.0, y / 64.0, - ( x + w ) / 64.0, ( y + h ) / 64.0, - x / 64.0, ( y + h ) / 64.0 - }; - - glEnableClientState( GL_VERTEX_ARRAY ); - glEnableClientState( GL_TEXTURE_COORD_ARRAY ); - - glVertexPointer( 2, GL_FLOAT, 0, vtx ); - glTexCoordPointer( 2, GL_FLOAT, 0, tex ); - glDrawArrays( GL_TRIANGLE_FAN, 0, 4 ); - - glDisableClientState( GL_VERTEX_ARRAY ); - glDisableClientState( GL_TEXTURE_COORD_ARRAY ); + R_Buffer2DQuad(x, y, x + w, y + h, x / 64.0, y / 64.0, + ( x + w ) / 64.0, ( y + h ) / 64.0); } /* @@ -306,6 +272,7 @@ RDraw_Fill(int x, int y, int w, int h, int c) void RDraw_FadeScreen(void) { + R_ApplyGLBuffer(); // draw what needs to be hidden glEnable(GL_BLEND); glDisable(GL_TEXTURE_2D); glColor4f(0, 0, 0, 0.8); diff --git a/src/client/refresh/gl1/gl1_sdl.c b/src/client/refresh/gl1/gl1_sdl.c index 7623ea32..707b9154 100644 --- a/src/client/refresh/gl1/gl1_sdl.c +++ b/src/client/refresh/gl1/gl1_sdl.c @@ -46,6 +46,7 @@ static qboolean vsyncActive = false; void RI_EndFrame(void) { + R_ApplyGLBuffer(); // to draw buffered 2D text SDL_GL_SwapWindow(window); } diff --git a/src/client/refresh/gl1/header/local.h b/src/client/refresh/gl1/header/local.h index 2740df4a..554b3b4a 100644 --- a/src/client/refresh/gl1/header/local.h +++ b/src/client/refresh/gl1/header/local.h @@ -39,17 +39,18 @@ #define GL_COLOR_INDEX8_EXT GL_COLOR_INDEX #endif -#define TEXNUM_LIGHTMAPS 1024 -#define TEXNUM_SCRAPS 1152 -#define TEXNUM_IMAGES 1153 -#define MAX_GLTEXTURES 1024 +#define MAX_LIGHTMAPS 128 #define MAX_SCRAPS 1 +#define TEXNUM_LIGHTMAPS 1024 +#define TEXNUM_SCRAPS (TEXNUM_LIGHTMAPS + MAX_LIGHTMAPS) +#define TEXNUM_IMAGES (TEXNUM_SCRAPS + MAX_SCRAPS) +#define MAX_GLTEXTURES 1024 #define BLOCK_WIDTH 128 // default values; now defined in glstate_t #define BLOCK_HEIGHT 128 #define REF_VERSION "Yamagi Quake II OpenGL Refresher" #define BACKFACE_EPSILON 0.01 #define LIGHTMAP_BYTES 4 -#define MAX_LIGHTMAPS 128 +#define MAX_TEXTURE_UNITS 2 #define GL_LIGHTMAP_FORMAT GL_RGBA /* up / down */ @@ -106,19 +107,17 @@ typedef enum rserr_unknown } rserr_t; +typedef enum +{ + buf_2d +} buffered_draw_t; + #include "model.h" void R_SetDefaultState(void); extern float gldepthmin, gldepthmax; -typedef struct -{ - float x, y, z; - float s, t; - float r, g, b; -} glvert_t; - extern image_t gltextures[MAX_GLTEXTURES]; extern int numgltextures; @@ -287,6 +286,11 @@ void R_TextureAlphaMode(char *string); void R_TextureSolidMode(char *string); int Scrap_AllocBlock(int w, int h, int *x, int *y); +void R_ApplyGLBuffer(void); +void R_UpdateGLBuffer(buffered_draw_t type, int colortex, int lighttex, int flags, float alpha); +void R_Buffer2DQuad(GLfloat ul_vx, GLfloat ul_vy, GLfloat dr_vx, GLfloat dr_vy, + GLfloat ul_tx, GLfloat ul_ty, GLfloat dr_tx, GLfloat dr_ty); + #ifdef DEBUG void glCheckError_(const char *file, const char *function, int line); // Ideally, the following list should contain all OpenGL calls. @@ -380,7 +384,7 @@ typedef struct int lightmap_textures; - int currenttextures[2]; + int currenttextures[MAX_TEXTURE_UNITS]; int currenttmu; GLenum currenttarget;