mirror of
https://github.com/yquake2/yquake2remaster.git
synced 2024-11-21 20:21:09 +00:00
GLES1 renderer: lightmap copies
Available in both GL1 and GLES1. Keep multiple copies of "the same" lightmap on video memory; they are actually different, because they're used in different frames. This is a workaround for the usage of glTexSubImage2D() for dynamic lighting, since modifying textures used recently causes slowdown in embedded/mobile devices. Controlled by gl1_lightmapcopies cvar; default in GL1 is `0`, while in GLES1 is `1`.
This commit is contained in:
parent
0596d23e4c
commit
b72c465214
6 changed files with 181 additions and 86 deletions
|
@ -492,6 +492,12 @@ it's `+set busywait 0` (setting the `busywait` cvar) and `-portable`
|
|||
* **gl1_stencilshadow**: If `gl_shadows` is set to `1`, this makes them
|
||||
look a bit better (no flickering) by using the stencil buffer.
|
||||
|
||||
* **gl1_lightmapcopies**: When enabled (`1`), keep multiple copies of the
|
||||
same lightmap rotating, shifting to a different one when drawing a new
|
||||
frame. Meant for mobile/embedded devices, where changing textures just
|
||||
displayed (dynamic lighting) causes slowdown. By default in GL1 is
|
||||
disabled, while in GLES1 is enabled. `vid_restart` needed.
|
||||
|
||||
* **gl1_discardfb**: Only available in ES1. If set to `1` (default),
|
||||
send a hint to discard framebuffers after finishing a frame. Useful
|
||||
for GPUs that attempt to reuse them, something Quake 2 doesn't do.
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#define GLBUFFER_RESET vtx_ptr = idx_ptr = 0; gl_buf.vt = gl_buf.tx = gl_buf.cl = 0;
|
||||
|
||||
glbuffer_t gl_buf; // our drawing buffer, used globally
|
||||
int cur_lm_copy; // which lightmap copy to use (when lightmapcopies=on)
|
||||
|
||||
static GLushort vtx_ptr, idx_ptr; // pointers for array positions in gl_buf
|
||||
|
||||
|
@ -181,7 +182,13 @@ R_ApplyGLBuffer(void)
|
|||
if (mtex)
|
||||
{
|
||||
// TMU 1: Lightmap texture
|
||||
R_MBind(GL_TEXTURE1, gl_state.lightmap_textures + gl_buf.texture[1]);
|
||||
int lmtexture = gl_state.lightmap_textures + gl_buf.texture[1];
|
||||
if (gl_config.lightmapcopies)
|
||||
{
|
||||
// Bind appropiate lightmap copy for this frame
|
||||
lmtexture += gl_state.max_lightmaps * cur_lm_copy;
|
||||
}
|
||||
R_MBind(GL_TEXTURE1, lmtexture);
|
||||
|
||||
if (gl1_overbrightbits->value)
|
||||
{
|
||||
|
|
|
@ -87,7 +87,7 @@ LM_UploadBlock(qboolean dynamic)
|
|||
{
|
||||
const int texture = (dynamic)? 0 : gl_lms.current_lightmap_texture;
|
||||
const int buffer = (gl_config.multitexture)? gl_lms.current_lightmap_texture : 0;
|
||||
int height = 0;
|
||||
int height = 0, i;
|
||||
|
||||
R_Bind(gl_state.lightmap_textures + texture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
|
@ -95,8 +95,6 @@ LM_UploadBlock(qboolean dynamic)
|
|||
|
||||
if (dynamic)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < gl_state.block_width; i++)
|
||||
{
|
||||
if (gl_lms.allocated[i] > height)
|
||||
|
@ -117,6 +115,21 @@ LM_UploadBlock(qboolean dynamic)
|
|||
0, GL_LIGHTMAP_FORMAT, GL_UNSIGNED_BYTE,
|
||||
gl_lms.lightmap_buffer[buffer]);
|
||||
|
||||
if (gl_config.lightmapcopies && buffer != 0)
|
||||
{
|
||||
// Upload to all lightmap copies
|
||||
for (i = 1; i < MAX_LIGHTMAP_COPIES; i++)
|
||||
{
|
||||
R_Bind(gl_state.lightmap_textures + (gl_state.max_lightmaps * i) + texture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, gl_lms.internal_format,
|
||||
gl_state.block_width, gl_state.block_height,
|
||||
0, GL_LIGHTMAP_FORMAT, GL_UNSIGNED_BYTE,
|
||||
gl_lms.lightmap_buffer[buffer]);
|
||||
}
|
||||
}
|
||||
|
||||
if (++gl_lms.current_lightmap_texture == gl_state.max_lightmaps)
|
||||
{
|
||||
ri.Sys_Error(ERR_DROP,
|
||||
|
|
|
@ -91,6 +91,7 @@ cvar_t *gl1_particle_square;
|
|||
cvar_t *gl1_palettedtexture;
|
||||
cvar_t *gl1_pointparameters;
|
||||
cvar_t *gl1_multitexture;
|
||||
cvar_t *gl1_lightmapcopies;
|
||||
cvar_t *gl1_discardfb;
|
||||
|
||||
cvar_t *gl_drawbuffer;
|
||||
|
@ -1171,6 +1172,12 @@ RI_RenderFrame(refdef_t *fd)
|
|||
R_SetGL2D();
|
||||
}
|
||||
|
||||
#ifdef YQ2_GL1_GLES
|
||||
#define DEFAULT_LMCOPIES "1"
|
||||
#else
|
||||
#define DEFAULT_LMCOPIES "0"
|
||||
#endif
|
||||
|
||||
void
|
||||
R_Register(void)
|
||||
{
|
||||
|
@ -1225,6 +1232,7 @@ R_Register(void)
|
|||
gl1_palettedtexture = ri.Cvar_Get("r_palettedtextures", "0", CVAR_ARCHIVE);
|
||||
gl1_pointparameters = ri.Cvar_Get("gl1_pointparameters", "1", CVAR_ARCHIVE);
|
||||
gl1_multitexture = ri.Cvar_Get("gl1_multitexture", "1", CVAR_ARCHIVE);
|
||||
gl1_lightmapcopies = ri.Cvar_Get("gl1_lightmapcopies", DEFAULT_LMCOPIES, CVAR_ARCHIVE);
|
||||
#ifdef YQ2_GL1_GLES
|
||||
gl1_discardfb = ri.Cvar_Get("gl1_discardfb", "1", CVAR_ARCHIVE);
|
||||
#endif
|
||||
|
@ -1265,6 +1273,8 @@ R_Register(void)
|
|||
ri.Cmd_AddCommand("gl_strings", R_Strings);
|
||||
}
|
||||
|
||||
#undef DEFAULT_LMCOPIES
|
||||
|
||||
/*
|
||||
* Changes the video mode
|
||||
*/
|
||||
|
@ -1645,6 +1655,28 @@ RI_Init(void)
|
|||
|
||||
// ----
|
||||
|
||||
/* Lightmap copies: keep multiple copies of "the same" lightmap on video memory.
|
||||
* All of them are actually different, because they are affected by different dynamic lighting,
|
||||
* in different frames. This is not meant for Immediate-Mode Rendering systems (desktop),
|
||||
* but for Tile-Based / Deferred Rendering ones (embedded / mobile), since active manipulation
|
||||
* of textures already being used in the last few frames can cause slowdown on these systems.
|
||||
* Needless to say, GPU memory usage is highly increased, so watch out in low memory situations.
|
||||
*/
|
||||
|
||||
R_Printf(PRINT_ALL, " - Lightmap copies: ");
|
||||
gl_config.lightmapcopies = false;
|
||||
if (gl_config.multitexture && gl1_lightmapcopies->value)
|
||||
{
|
||||
gl_config.lightmapcopies = true;
|
||||
R_Printf(PRINT_ALL, "Okay\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
R_Printf(PRINT_ALL, "Disabled\n");
|
||||
}
|
||||
|
||||
// ----
|
||||
|
||||
/* Discard framebuffer: Available only on GLES1, enables the use of a "performance hint"
|
||||
* to the graphic driver, to get rid of the contents of the depth and stencil buffers.
|
||||
* Useful for some GPUs that may attempt to keep them and/or write them back to
|
||||
|
|
|
@ -28,12 +28,18 @@
|
|||
|
||||
#include "header/local.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int top, bottom, left, right;
|
||||
} lmrect_t;
|
||||
|
||||
int c_visible_lightmaps;
|
||||
int c_visible_textures;
|
||||
static vec3_t modelorg; /* relative to viewpoint */
|
||||
msurface_t *r_alpha_surfaces;
|
||||
|
||||
gllightmapstate_t gl_lms;
|
||||
extern int cur_lm_copy;
|
||||
|
||||
void LM_InitBlock(void);
|
||||
void LM_UploadBlock(qboolean dynamic);
|
||||
|
@ -519,52 +525,6 @@ R_DrawAlphaSurfaces(void)
|
|||
r_alpha_surfaces = NULL;
|
||||
}
|
||||
|
||||
static qboolean
|
||||
R_HasDynamicLights(msurface_t *surf, int *mapNum)
|
||||
{
|
||||
int map;
|
||||
qboolean is_dynamic = false;
|
||||
|
||||
if ( r_fullbright->value || !gl1_dynamic->value ||
|
||||
(surf->texinfo->flags & (SURF_SKY | SURF_TRANS33 | SURF_TRANS66 | SURF_WARP)) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Any dynamic lights on this surface?
|
||||
for (map = 0; map < MAXLIGHTMAPS && surf->styles[map] != 255; map++)
|
||||
{
|
||||
if (r_newrefdef.lightstyles[surf->styles[map]].white != surf->cached_light[map])
|
||||
{
|
||||
is_dynamic = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// No matter if it is this frame or was in the previous one: has dynamic lights
|
||||
if ( !is_dynamic && (surf->dlightframe == r_framecount || surf->dirty_lightmap) )
|
||||
{
|
||||
is_dynamic = true;
|
||||
}
|
||||
|
||||
if (mapNum)
|
||||
{
|
||||
*mapNum = map;
|
||||
}
|
||||
return is_dynamic;
|
||||
}
|
||||
|
||||
static void
|
||||
R_UpdateSurfCache(msurface_t *surf, int map)
|
||||
{
|
||||
if ( ((surf->styles[map] >= 32) || (surf->styles[map] == 0))
|
||||
&& (surf->dlightframe != r_framecount) )
|
||||
{
|
||||
R_SetCacheState(surf);
|
||||
}
|
||||
surf->dirty_lightmap = (surf->dlightframe == r_framecount);
|
||||
}
|
||||
|
||||
static void
|
||||
R_RenderLightmappedPoly(entity_t *currententity, msurface_t *surf)
|
||||
{
|
||||
|
@ -599,11 +559,37 @@ R_RenderLightmappedPoly(entity_t *currententity, msurface_t *surf)
|
|||
}
|
||||
}
|
||||
|
||||
/* Add "adding" area to "obj" */
|
||||
static void
|
||||
R_JoinAreas(lmrect_t *adding, lmrect_t *obj)
|
||||
{
|
||||
if (adding->top < obj->top)
|
||||
{
|
||||
obj->top = adding->top;
|
||||
}
|
||||
if (adding->bottom > obj->bottom)
|
||||
{
|
||||
obj->bottom = adding->bottom;
|
||||
}
|
||||
if (adding->left < obj->left)
|
||||
{
|
||||
obj->left = adding->left;
|
||||
}
|
||||
if (adding->right > obj->right)
|
||||
{
|
||||
obj->right = adding->right;
|
||||
}
|
||||
}
|
||||
|
||||
/* Upload dynamic lights to each lightmap texture (multitexture path only) */
|
||||
static void
|
||||
R_RegenAllLightmaps()
|
||||
{
|
||||
int i, map, smax, tmax, top, bottom, left, right, bt, bb, bl, br;
|
||||
static lmrect_t lmchange[MAX_LIGHTMAP_COPIES][MAX_LIGHTMAPS];
|
||||
static qboolean altered[MAX_LIGHTMAP_COPIES][MAX_LIGHTMAPS];
|
||||
|
||||
int i, map, smax, tmax, cc, lmtex;
|
||||
lmrect_t current, best;
|
||||
msurface_t *surf;
|
||||
byte *base;
|
||||
qboolean affected_lightmap;
|
||||
|
@ -611,11 +597,19 @@ R_RegenAllLightmaps()
|
|||
qboolean pixelstore_set = false;
|
||||
#endif
|
||||
|
||||
if ( !gl_config.multitexture )
|
||||
if ( !gl_config.multitexture || r_fullbright->value || !gl1_dynamic->value )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
cc = lmtex = 0;
|
||||
if (gl_config.lightmapcopies)
|
||||
{
|
||||
cur_lm_copy = (cur_lm_copy + 1) % MAX_LIGHTMAP_COPIES; // alternate between calls
|
||||
cc = cur_lm_copy;
|
||||
lmtex = gl_state.max_lightmaps * cc;
|
||||
}
|
||||
|
||||
for (i = 1; i < gl_state.max_lightmaps; i++)
|
||||
{
|
||||
if (!gl_lms.lightmap_surfaces[i] || !gl_lms.lightmap_buffer[i])
|
||||
|
@ -624,57 +618,94 @@ R_RegenAllLightmaps()
|
|||
}
|
||||
|
||||
affected_lightmap = false;
|
||||
bt = gl_state.block_height;
|
||||
bl = gl_state.block_width;
|
||||
bb = br = 0;
|
||||
best.top = gl_state.block_height;
|
||||
best.left = gl_state.block_width;
|
||||
best.bottom = best.right = 0;
|
||||
|
||||
for (surf = gl_lms.lightmap_surfaces[i];
|
||||
surf != 0;
|
||||
surf = surf->lightmapchain)
|
||||
{
|
||||
if ( !R_HasDynamicLights(surf, &map) )
|
||||
if (surf->texinfo->flags & (SURF_SKY | SURF_TRANS33 | SURF_TRANS66 | SURF_WARP))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Any dynamic lights on this surface?
|
||||
for (map = 0; map < MAXLIGHTMAPS && surf->styles[map] != 255; map++)
|
||||
{
|
||||
if (r_newrefdef.lightstyles[surf->styles[map]].white != surf->cached_light[map])
|
||||
{
|
||||
goto dynamic_surf;
|
||||
}
|
||||
}
|
||||
|
||||
// Doesn't matter if it is this frame or was in the previous one: surface has dynamic lights
|
||||
if ( surf->dlightframe == r_framecount || surf->dirty_lightmap )
|
||||
{
|
||||
goto dynamic_surf;
|
||||
}
|
||||
|
||||
continue; // no dynamic lights affect this surface in this frame
|
||||
|
||||
dynamic_surf:
|
||||
affected_lightmap = true;
|
||||
smax = (surf->extents[0] >> 4) + 1;
|
||||
tmax = (surf->extents[1] >> 4) + 1;
|
||||
|
||||
left = surf->light_s;
|
||||
right = surf->light_s + smax;
|
||||
top = surf->light_t;
|
||||
bottom = surf->light_t + tmax;
|
||||
current.left = surf->light_s;
|
||||
current.right = surf->light_s + smax;
|
||||
current.top = surf->light_t;
|
||||
current.bottom = surf->light_t + tmax;
|
||||
|
||||
base = gl_lms.lightmap_buffer[i];
|
||||
base += (top * gl_state.block_width + left) * LIGHTMAP_BYTES;
|
||||
base += (current.top * gl_state.block_width + current.left) * LIGHTMAP_BYTES;
|
||||
|
||||
R_BuildLightMap(surf, base, gl_state.block_width * LIGHTMAP_BYTES);
|
||||
R_UpdateSurfCache(surf, map);
|
||||
|
||||
if (left < bl)
|
||||
if ( ((surf->styles[map] >= 32) || (surf->styles[map] == 0))
|
||||
&& (surf->dlightframe != r_framecount) )
|
||||
{
|
||||
bl = left;
|
||||
}
|
||||
if (right > br)
|
||||
{
|
||||
br = right;
|
||||
}
|
||||
if (top < bt)
|
||||
{
|
||||
bt = top;
|
||||
}
|
||||
if (bottom > bb)
|
||||
{
|
||||
bb = bottom;
|
||||
}
|
||||
R_SetCacheState(surf);
|
||||
}
|
||||
|
||||
if (!affected_lightmap)
|
||||
surf->dirty_lightmap = (surf->dlightframe == r_framecount);
|
||||
R_JoinAreas(¤t, &best);
|
||||
}
|
||||
|
||||
if (!gl_config.lightmapcopies && !affected_lightmap)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (gl_config.lightmapcopies)
|
||||
{
|
||||
// add all the changes that have happened in the last few frames,
|
||||
// at least just for consistency between them...
|
||||
qboolean apply_changes = affected_lightmap;
|
||||
current = best; // save state for next frames... +
|
||||
|
||||
for (int k = 0; k < MAX_LIGHTMAP_COPIES; k++)
|
||||
{
|
||||
if (altered[k][i])
|
||||
{
|
||||
apply_changes = true;
|
||||
R_JoinAreas(&lmchange[k][i], &best);
|
||||
}
|
||||
}
|
||||
|
||||
altered[cc][i] = affected_lightmap;
|
||||
if (affected_lightmap)
|
||||
{
|
||||
lmchange[cc][i] = current; // + ...here
|
||||
}
|
||||
|
||||
if (!apply_changes)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef YQ2_GL1_GLES
|
||||
if (!pixelstore_set)
|
||||
{
|
||||
|
@ -687,16 +718,20 @@ R_RegenAllLightmaps()
|
|||
base = gl_lms.lightmap_buffer[i];
|
||||
|
||||
#ifdef YQ2_GL1_GLES
|
||||
base += (bt * gl_state.block_width) * LIGHTMAP_BYTES;
|
||||
base += (best.top * gl_state.block_width) * LIGHTMAP_BYTES;
|
||||
|
||||
R_Bind(gl_state.lightmap_textures + i);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, bt, gl_state.block_width, bb - bt,
|
||||
R_Bind(gl_state.lightmap_textures + i + lmtex);
|
||||
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, best.top,
|
||||
gl_state.block_width, best.bottom - best.top,
|
||||
GL_LIGHTMAP_FORMAT, GL_UNSIGNED_BYTE, base);
|
||||
#else
|
||||
base += (bt * gl_state.block_width + bl) * LIGHTMAP_BYTES;
|
||||
base += (best.top * gl_state.block_width + best.left) * LIGHTMAP_BYTES;
|
||||
|
||||
R_Bind(gl_state.lightmap_textures + i);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, bl, bt, br - bl, bb - bt,
|
||||
R_Bind(gl_state.lightmap_textures + i + lmtex);
|
||||
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, best.left, best.top,
|
||||
best.right - best.left, best.bottom - best.top,
|
||||
GL_LIGHTMAP_FORMAT, GL_UNSIGNED_BYTE, base);
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -50,9 +50,10 @@
|
|||
#endif
|
||||
|
||||
#define MAX_LIGHTMAPS 128
|
||||
#define MAX_LIGHTMAP_COPIES 3 // Meant for tile / deferred rendering platforms
|
||||
#define MAX_SCRAPS 1
|
||||
#define TEXNUM_LIGHTMAPS 1024
|
||||
#define TEXNUM_SCRAPS (TEXNUM_LIGHTMAPS + MAX_LIGHTMAPS)
|
||||
#define TEXNUM_SCRAPS (TEXNUM_LIGHTMAPS + MAX_LIGHTMAPS * MAX_LIGHTMAP_COPIES)
|
||||
#define TEXNUM_IMAGES (TEXNUM_SCRAPS + MAX_SCRAPS)
|
||||
#define MAX_GLTEXTURES 1024
|
||||
#define BLOCK_WIDTH 128 // default values; now defined in glstate_t
|
||||
|
@ -429,6 +430,7 @@ typedef struct
|
|||
qboolean palettedtexture;
|
||||
qboolean pointparameters;
|
||||
qboolean multitexture;
|
||||
qboolean lightmapcopies; // many copies of same lightmap, for embedded
|
||||
qboolean discardfb;
|
||||
|
||||
// ----
|
||||
|
|
Loading…
Reference in a new issue