diff --git a/doc/040_cvarlist.md b/doc/040_cvarlist.md index 531521c8..eb7bc7d1 100644 --- a/doc/040_cvarlist.md +++ b/doc/040_cvarlist.md @@ -460,7 +460,7 @@ Set `0` by default. ## Graphics (OpenGL 1.4 only) -* **gl1_biglightmaps**: Enables lightmaps to use a bigger +* **gl1_biglightmaps**: Enables lightmaps and scrap to use a bigger texture size, which means fewer texture switches, improving performance. Default is `1` (enabled). Requires a `vid_restart`. diff --git a/src/client/refresh/gl1/gl1_image.c b/src/client/refresh/gl1/gl1_image.c index 1bfd886f..83061cd2 100644 --- a/src/client/refresh/gl1/gl1_image.c +++ b/src/client/refresh/gl1/gl1_image.c @@ -31,7 +31,7 @@ int numgltextures; static int image_max = 0; int base_textureid; /* gltextures[i] = base_textureid+i */ extern qboolean scrap_dirty; -extern byte scrap_texels[MAX_SCRAPS][BLOCK_WIDTH * BLOCK_HEIGHT]; +extern byte *scrap_texels[MAX_SCRAPS]; static byte intensitytable[256]; static unsigned char gammatable[256]; @@ -1040,17 +1040,17 @@ R_LoadPic(const char *name, byte *pic, int width, int realwidth, { for (j = 0; j < image->width; j++, k++) { - scrap_texels[texnum][(y + i) * BLOCK_WIDTH + x + j] = pic[k]; + scrap_texels[texnum][(y + i) * gl_state.scrap_width + x + j] = pic[k]; } } image->texnum = TEXNUM_SCRAPS + texnum; image->scrap = true; image->has_alpha = true; - image->sl = (x + 0.01) / (float)BLOCK_WIDTH; - image->sh = (x + image->width - 0.01) / (float)BLOCK_WIDTH; - image->tl = (y + 0.01) / (float)BLOCK_WIDTH; - image->th = (y + image->height - 0.01) / (float)BLOCK_WIDTH; + image->sl = (float)x / gl_state.scrap_width; + image->sh = (float)(x + image->width) / gl_state.scrap_width; + image->tl = (float)y / gl_state.scrap_height; + image->th = (float)(y + image->height) / gl_state.scrap_height; } else { diff --git a/src/client/refresh/gl1/gl1_main.c b/src/client/refresh/gl1/gl1_main.c index 914590b9..b9f21f39 100644 --- a/src/client/refresh/gl1/gl1_main.c +++ b/src/client/refresh/gl1/gl1_main.c @@ -146,6 +146,8 @@ cvar_t *gl1_stereo_convergence; refimport_t ri; void LM_FreeLightmapBuffers(void); +void Scrap_Free(void); +void Scrap_Init(void); void R_RotateForEntity(entity_t *e) @@ -1647,6 +1649,8 @@ RI_Init(void) gl_state.block_width = BLOCK_WIDTH; gl_state.block_height = BLOCK_HEIGHT; gl_state.max_lightmaps = MAX_LIGHTMAPS; + gl_state.scrap_width = BLOCK_WIDTH; + gl_state.scrap_height = BLOCK_HEIGHT; glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_tex_size); if (max_tex_size > BLOCK_WIDTH) { @@ -1655,6 +1659,8 @@ RI_Init(void) gl_state.block_width = gl_state.block_height = Q_min(max_tex_size, 512); gl_state.max_lightmaps = (BLOCK_WIDTH * BLOCK_HEIGHT * MAX_LIGHTMAPS) / (gl_state.block_width * gl_state.block_height); + gl_state.scrap_width = gl_state.scrap_height = + (gl_config.npottextures)? Q_min(max_tex_size, 384) : Q_min(max_tex_size, 256); R_Printf(PRINT_ALL, "Okay\n"); } else @@ -1671,6 +1677,7 @@ RI_Init(void) R_SetDefaultState(); + Scrap_Init(); R_InitImages(); Mod_Init(); R_InitParticleTexture(); @@ -1688,6 +1695,7 @@ RI_Shutdown(void) ri.Cmd_RemoveCommand("gl_strings"); LM_FreeLightmapBuffers(); + Scrap_Free(); Mod_FreeAll(); R_ShutdownImages(); diff --git a/src/client/refresh/gl1/gl1_scrap.c b/src/client/refresh/gl1/gl1_scrap.c index 3a769300..0f729683 100644 --- a/src/client/refresh/gl1/gl1_scrap.c +++ b/src/client/refresh/gl1/gl1_scrap.c @@ -27,8 +27,8 @@ #include "header/local.h" -int scrap_allocated[MAX_SCRAPS][BLOCK_WIDTH]; -byte scrap_texels[MAX_SCRAPS][BLOCK_WIDTH * BLOCK_HEIGHT]; +int *scrap_allocated[MAX_SCRAPS]; +byte *scrap_texels[MAX_SCRAPS]; qboolean scrap_dirty; qboolean R_Upload8(byte *data, @@ -44,12 +44,14 @@ Scrap_AllocBlock(int w, int h, int *x, int *y) int i, j; int best, best2; int texnum; + w += 2; // add an empty border to all sides + h += 2; for (texnum = 0; texnum < MAX_SCRAPS; texnum++) { - best = BLOCK_HEIGHT; + best = gl_state.scrap_height; - for (i = 0; i < BLOCK_WIDTH - w; i++) + for (i = 0; i < gl_state.scrap_width - w; i++) { best2 = 0; @@ -73,7 +75,7 @@ Scrap_AllocBlock(int w, int h, int *x, int *y) } } - if (best + h > BLOCK_HEIGHT) + if (best + h > gl_state.scrap_height) { continue; } @@ -82,6 +84,8 @@ Scrap_AllocBlock(int w, int h, int *x, int *y) { scrap_allocated[texnum][*x + i] = best + h; } + (*x)++; // jump the border + (*y)++; return texnum; } @@ -93,7 +97,57 @@ void Scrap_Upload(void) { R_Bind(TEXNUM_SCRAPS); - R_Upload8(scrap_texels[0], BLOCK_WIDTH, BLOCK_HEIGHT, false, false); + R_Upload8(scrap_texels[0], gl_state.scrap_width, + gl_state.scrap_height, false, false); scrap_dirty = false; } +void +Scrap_Free(void) +{ + for (int i = 0; i < MAX_SCRAPS; i++) + { + if (scrap_allocated[i]) + { + free(scrap_allocated[i]); + } + scrap_allocated[i] = NULL; + + if (scrap_texels[i]) + { + free(scrap_texels[i]); + } + scrap_texels[i] = NULL; + } +} + +void +Scrap_Init(void) +{ + const unsigned int allocd_size = gl_state.scrap_width * sizeof(int); + const unsigned int texels_size = gl_state.scrap_width + * gl_state.scrap_height * sizeof(byte); + int i; + + Scrap_Free(); + + for (i = 0; i < MAX_SCRAPS; i++) + { + if (!scrap_allocated[i]) + { + scrap_allocated[i] = malloc (allocd_size) ; + } + if (!scrap_texels[i]) + { + scrap_texels[i] = malloc (texels_size) ; + } + + if (!scrap_allocated[i] || !scrap_texels[i]) + { + ri.Sys_Error(ERR_FATAL, "Could not allocate scrap memory.\n"); + } + memset (scrap_allocated[i], 0, allocd_size); // empty + memset (scrap_texels[i], 255, texels_size); // transparent + } +} + diff --git a/src/client/refresh/gl1/header/local.h b/src/client/refresh/gl1/header/local.h index b912543b..03cfe3d4 100644 --- a/src/client/refresh/gl1/header/local.h +++ b/src/client/refresh/gl1/header/local.h @@ -394,7 +394,9 @@ typedef struct int block_width, // replaces BLOCK_WIDTH block_height, // replaces BLOCK_HEIGHT - max_lightmaps; // the larger the lightmaps, the fewer the max lightmaps + max_lightmaps, // the larger the lightmaps, the fewer the max lightmaps + scrap_width, // size for scrap (atlas of 2D elements) + scrap_height; } glstate_t; typedef struct