From 0406d64e716af8e93af0638e1942d576f9fa94d9 Mon Sep 17 00:00:00 2001 From: havoc Date: Mon, 30 Nov 2020 06:40:43 +0000 Subject: [PATCH] Refactored lightmap update handling and added cvars to replace gl_nopartialtextureupdates cvar with finer-grained control: * r_q1bsp_lightmap_updates_enabled (default 1 - shouldn't turn this off) * r_q1bsp_lightmap_updates_combine (default 2 - upload full lightmap texture) * r_q1bsp_lightmap_updates_hidden_surfaces (default 0 - if turned on it may improve fps when the view is moving/turning by only uploading lightmaps on certain frames) Previously there was a bug that made hidden surfaces get lightmap updates, which was never intended, but a cvar was added to allow the behavior to be toggled in case it has some use. Overall this change performs better on AMD drivers for Windows, and is expected to perform a lot better on all Mesa drivers for Linux as they have even worse performance with partial texture update locking, it may reduce performance on NVIDIA drivers for Windows as they have always had good handling of thousands of small partial texture updates. git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@13056 d7cf8633-e32d-0410-b094-e92efae38249 --- gl_draw.c | 2 +- gl_rmain.c | 108 ++++++++++++++++++++++-------------------- gl_rsurf.c | 6 +-- gl_textures.c | 128 +++++++++++++++++++++++--------------------------- r_shadow.c | 6 +-- r_textures.h | 3 +- render.h | 7 ++- 7 files changed, 130 insertions(+), 130 deletions(-) diff --git a/gl_draw.c b/gl_draw.c index 3de01e19..d625740e 100644 --- a/gl_draw.c +++ b/gl_draw.c @@ -267,7 +267,7 @@ cachepic_t *Draw_NewPic(const char *picname, int width, int height, unsigned cha if (pic->flags & CACHEPICFLAG_NEWPIC && pic->skinframe && pic->skinframe->base && pic->width == width && pic->height == height) { Con_DPrintf("Draw_NewPic(\"%s\"): frame %i: updating texture\n", picname, draw_frame); - R_UpdateTexture(pic->skinframe->base, pixels_bgra, 0, 0, 0, width, height, 1); + R_UpdateTexture(pic->skinframe->base, pixels_bgra, 0, 0, 0, width, height, 1, 0); R_SkinFrame_MarkUsed(pic->skinframe); pic->lastusedframe = draw_frame; return pic; diff --git a/gl_rmain.c b/gl_rmain.c index 8e273c8f..ebebd6c3 100644 --- a/gl_rmain.c +++ b/gl_rmain.c @@ -256,6 +256,10 @@ cvar_t r_buffermegs[R_BUFFERDATA_COUNT] = {CF_CLIENT | CF_ARCHIVE, "r_buffermegs_uniform", "0.25", "uniform buffer size for one frame"}, }; +cvar_t r_q1bsp_lightmap_updates_enabled = {CF_CLIENT | CF_ARCHIVE, "r_q1bsp_lightmap_updates_enabled", "1", "allow lightmaps to be updated on Q1BSP maps (don't turn this off except for debugging)"}; +cvar_t r_q1bsp_lightmap_updates_combine = {CF_CLIENT | CF_ARCHIVE, "r_q1bsp_lightmap_updates_combine", "2", "combine lightmap texture updates to make fewer glTexSubImage2D calls, modes: 0 = immediately upload lightmaps (may be thousands of small 3x3 updates), 1 = combine to one call, 2 = combine to one full texture update (glTexImage2D) which tells the driver it does not need to lock the resource (faster on most drivers)"}; +cvar_t r_q1bsp_lightmap_updates_hidden_surfaces = {CF_CLIENT | CF_ARCHIVE, "r_q1bsp_lightmap_updates_hidden_surfaces", "0", "update lightmaps on surfaces that are not visible, so that updates only occur on frames where lightstyles changed value (animation or light switches), only makes sense with combine = 2"}; + extern cvar_t v_glslgamma_2d; extern qbool v_flipped_state; @@ -511,8 +515,8 @@ static void R_BuildFogTexture(void) } if (r_texture_fogattenuation) { - R_UpdateTexture(r_texture_fogattenuation, &data1[0][0], 0, 0, 0, FOGWIDTH, 1, 1); - //R_UpdateTexture(r_texture_fogattenuation, &data2[0][0], 0, 0, 0, FOGWIDTH, 1, 1); + R_UpdateTexture(r_texture_fogattenuation, &data1[0][0], 0, 0, 0, FOGWIDTH, 1, 1, 0); + //R_UpdateTexture(r_texture_fogattenuation, &data2[0][0], 0, 0, 0, FOGWIDTH, 1, 1, 0); } else { @@ -3409,6 +3413,9 @@ void GL_Main_Init(void) for (i = 0;i < R_BUFFERDATA_COUNT;i++) Cvar_RegisterVariable(&r_buffermegs[i]); Cvar_RegisterVariable(&r_batch_dynamicbuffer); + Cvar_RegisterVariable(&r_q1bsp_lightmap_updates_enabled); + Cvar_RegisterVariable(&r_q1bsp_lightmap_updates_combine); + Cvar_RegisterVariable(&r_q1bsp_lightmap_updates_hidden_surfaces); if (gamemode == GAME_NEHAHRA || gamemode == GAME_TENEBRAE) Cvar_SetValue(&cvars_all, "r_fullbrights", 0); #ifdef DP_MOBILETOUCH @@ -5577,7 +5584,7 @@ void R_UpdateVariables(void) } if (r_texture_gammaramps) { - R_UpdateTexture(r_texture_gammaramps, &rampbgr[0][0], 0, 0, 0, RAMPWIDTH, 1, 1); + R_UpdateTexture(r_texture_gammaramps, &rampbgr[0][0], 0, 0, 0, RAMPWIDTH, 1, 1, 0); } else { @@ -9989,7 +9996,7 @@ int r_maxsurfacelist = 0; const msurface_t **r_surfacelist = NULL; void R_DrawModelSurfaces(entity_render_t *ent, qbool skysurfaces, qbool writedepth, qbool depthonly, qbool debug, qbool prepass, qbool ui) { - int i, j, endj, flagsmask; + int i, j, flagsmask; model_t *model = ent->model; msurface_t *surfaces; unsigned char *update; @@ -10026,34 +10033,20 @@ void R_DrawModelSurfaces(entity_render_t *ent, qbool skysurfaces, qbool writedep return; } + // check if this is an empty model + if (model->nummodelsurfaces == 0) + return; + rsurface.lightmaptexture = NULL; rsurface.deluxemaptexture = NULL; rsurface.uselightmaptexture = false; rsurface.texture = NULL; rsurface.rtlight = NULL; numsurfacelist = 0; + // add visible surfaces to draw list if (ent == r_refdef.scene.worldentity) { - // update light styles - if (!skysurfaces && !depthonly && !prepass && model->brushq1.num_lightstyles && r_refdef.scene.lightmapintensity > 0) - { - model_brush_lightstyleinfo_t *style; - // Iterate over each active style - for (i = 0, style = model->brushq1.data_lightstyleinfo;i < model->brushq1.num_lightstyles;i++, style++) - { - if (style->value != r_refdef.scene.lightstylevalue[style->style]) - { - int *list = style->surfacelist; - style->value = r_refdef.scene.lightstylevalue[style->style]; - // Iterate over every surface this style applies to - for (j = 0;j < style->numsurfaces;j++) - // Update brush entities even if not visible otherwise they'll render solid black. - if(r_refdef.viewcache.world_surfacevisible[list[j]]) - update[list[j]] = true; - } - } - } // for the world entity, check surfacevisible for (i = 0;i < model->nummodelsurfaces;i++) { @@ -10061,49 +10054,60 @@ void R_DrawModelSurfaces(entity_render_t *ent, qbool skysurfaces, qbool writedep if (r_refdef.viewcache.world_surfacevisible[j]) r_surfacelist[numsurfacelist++] = surfaces + j; } + + // don't do anything if there were no surfaces added (none of the world entity is visible) + if (!numsurfacelist) + { + rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveModelEntity + return; + } } else if (ui) { - // for ui we have to preserve the order of surfaces + // for ui we have to preserve the order of surfaces (not using sortedmodelsurfaces) for (i = 0; i < model->nummodelsurfaces; i++) r_surfacelist[numsurfacelist++] = surfaces + model->firstmodelsurface + i; } else { - // update light styles - if (!skysurfaces && !depthonly && !prepass && model->brushq1.num_lightstyles && r_refdef.scene.lightmapintensity > 0) - { - model_brush_lightstyleinfo_t *style; - // Iterate over each active style - for (i = 0, style = model->brushq1.data_lightstyleinfo;i < model->brushq1.num_lightstyles;i++, style++) - { - if (style->value != r_refdef.scene.lightstylevalue[style->style]) - { - int *list = style->surfacelist; - style->value = r_refdef.scene.lightstylevalue[style->style]; - // Iterate over every surface this style applies to - for (j = 0;j < style->numsurfaces;j++) - update[list[j]] = true; - } - } - } // add all surfaces for (i = 0; i < model->nummodelsurfaces; i++) r_surfacelist[numsurfacelist++] = surfaces + model->sortedmodelsurfaces[i]; } - // don't do anything if there were no surfaces - if (!numsurfacelist) - { - rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveModelEntity - return; - } - // update lightmaps if needed - if (update) + + // mark lightmaps as dirty if their lightstyle's value changed + // we do this by using style chains because most styles do not change on most frames, + // and most surfaces do not have styles on them + // map packs like Arcane Dimensions (e.g. ad_sepulcher) break this rule and animate most surfaces + if (update && !skysurfaces && !depthonly && !prepass && model->brushq1.num_lightstyles && r_refdef.scene.lightmapintensity > 0 && r_q1bsp_lightmap_updates_enabled.integer) { - for (j = model->firstmodelsurface, endj = model->firstmodelsurface + model->nummodelsurfaces;j < endj;j++) + model_brush_lightstyleinfo_t* style; + // for each lightstyle, check if its value changed and mark the lightmaps as dirty if so + for (i = 0, style = model->brushq1.data_lightstyleinfo; i < model->brushq1.num_lightstyles; i++, style++) + { + if (style->value != r_refdef.scene.lightstylevalue[style->style]) + { + int* list = style->surfacelist; + style->value = r_refdef.scene.lightstylevalue[style->style]; + // value changed - mark the surfaces belonging to this style chain as dirty + for (j = 0; j < style->numsurfaces; j++) + update[list[j]] = true; + } + } + // now check if update flags are set on any surfaces that are visible + if (r_q1bsp_lightmap_updates_hidden_surfaces.integer) + { + // we can do less frequent texture uploads (approximately 10hz for animated lightstyles) by rebuilding lightmaps on surfaces that are not currently visible + // for optimal efficiency, this includes the submodels of the worldmodel, so we use model->num_surfaces, not nummodelsurfaces + for (i = 0; i < model->num_surfaces;i++) + if (update[i]) + R_BuildLightMap(ent, surfaces + i, r_q1bsp_lightmap_updates_combine.integer); + } + else { - if (update[j]) - R_BuildLightMap(ent, surfaces + j); + for (i = 0; i < numsurfacelist; i++) + if (update[r_surfacelist[i] - surfaces]) + R_BuildLightMap(ent, (msurface_t *)r_surfacelist[i], r_q1bsp_lightmap_updates_combine.integer); } } diff --git a/gl_rsurf.c b/gl_rsurf.c index a832e4fa..6ac291c9 100644 --- a/gl_rsurf.c +++ b/gl_rsurf.c @@ -47,7 +47,7 @@ R_BuildLightMap Combine and scale multiple lightmaps into the 8.8 format in blocklights =============== */ -void R_BuildLightMap (const entity_render_t *ent, msurface_t *surface) +void R_BuildLightMap (const entity_render_t *ent, msurface_t *surface, int combine) { int smax, tmax, i, size, size3, maps, l; int *bl, scale; @@ -131,7 +131,7 @@ void R_BuildLightMap (const entity_render_t *ent, msurface_t *surface) if(vid_sRGB.integer && vid_sRGB_fallback.integer && !vid.sRGB3D) Image_MakesRGBColorsFromLinear_Lightmap(templight, templight, size); - R_UpdateTexture(surface->lightmaptexture, templight, surface->lightmapinfo->lightmaporigin[0], surface->lightmapinfo->lightmaporigin[1], 0, smax, tmax, 1); + R_UpdateTexture(surface->lightmaptexture, templight, surface->lightmapinfo->lightmaporigin[0], surface->lightmapinfo->lightmaporigin[1], 0, smax, tmax, 1, combine); // update the surface's deluxemap if it has one if (surface->deluxemaptexture != r_texture_blanknormalmap) @@ -169,7 +169,7 @@ void R_BuildLightMap (const entity_render_t *ent, msurface_t *surface) l = (int)(n[2] * 128 + 128);out[0] = bound(0, l, 255); out[3] = 255; } - R_UpdateTexture(surface->deluxemaptexture, templight, surface->lightmapinfo->lightmaporigin[0], surface->lightmapinfo->lightmaporigin[1], 0, smax, tmax, 1); + R_UpdateTexture(surface->deluxemaptexture, templight, surface->lightmapinfo->lightmaporigin[0], surface->lightmapinfo->lightmaporigin[1], 0, smax, tmax, 1, r_q1bsp_lightmap_updates_combine.integer); } } diff --git a/gl_textures.c b/gl_textures.c index 0bb0a293..fcb0bac7 100644 --- a/gl_textures.c +++ b/gl_textures.c @@ -27,7 +27,6 @@ cvar_t gl_texturecompression_sky = {CF_CLIENT | CF_ARCHIVE, "gl_texturecompressi cvar_t gl_texturecompression_lightcubemaps = {CF_CLIENT | CF_ARCHIVE, "gl_texturecompression_lightcubemaps", "1", "whether to compress light cubemaps (spotlights and other light projection images)"}; cvar_t gl_texturecompression_reflectmask = {CF_CLIENT | CF_ARCHIVE, "gl_texturecompression_reflectmask", "1", "whether to compress reflection cubemap masks (mask of which areas of the texture should reflect the generic shiny cubemap)"}; cvar_t gl_texturecompression_sprites = {CF_CLIENT | CF_ARCHIVE, "gl_texturecompression_sprites", "1", "whether to compress sprites"}; -cvar_t gl_nopartialtextureupdates = {CF_CLIENT | CF_ARCHIVE, "gl_nopartialtextureupdates", "0", "use alternate path for dynamic lightmap updates that avoids a possibly slow code path in the driver"}; cvar_t r_texture_dds_load_alphamode = {CF_CLIENT, "r_texture_dds_load_alphamode", "1", "0: trust DDPF_ALPHAPIXELS flag, 1: texture format and brute force search if ambiguous, 2: texture format only"}; cvar_t r_texture_dds_load_logfailure = {CF_CLIENT, "r_texture_dds_load_logfailure", "0", "log missing DDS textures to ddstexturefailures.log, 0: done log, 1: log with no optional textures (_norm, glow etc.). 2: log all"}; cvar_t r_texture_dds_swdecode = {CF_CLIENT, "r_texture_dds_swdecode", "0", "0: don't software decode DDS, 1: software decode DDS if unsupported, 2: always software decode DDS"}; @@ -172,11 +171,9 @@ typedef struct gltexture_s void *updatecallback_data; // --- [11/22/2007 Black] - // stores backup copy of texture for deferred texture updates (gl_nopartialtextureupdates cvar) + // stores backup copy of texture for deferred texture updates (R_UpdateTexture when combine = true) unsigned char *bufferpixels; - unsigned char *modifiedpixels; - int modified_width, modified_height, modified_depth; - int modified_offset_x, modified_offset_y, modified_offset_z; + int modified_mins[3], modified_maxs[3]; qbool buffermodified; // pointer to texturepool (check this to see if the texture is allocated) @@ -727,7 +724,6 @@ void R_Textures_Init (void) Cvar_RegisterVariable (&gl_texturecompression_lightcubemaps); Cvar_RegisterVariable (&gl_texturecompression_reflectmask); Cvar_RegisterVariable (&gl_texturecompression_sprites); - Cvar_RegisterVariable (&gl_nopartialtextureupdates); Cvar_RegisterVariable (&r_texture_dds_load_alphamode); Cvar_RegisterVariable (&r_texture_dds_load_logfailure); Cvar_RegisterVariable (&r_texture_dds_swdecode); @@ -1319,8 +1315,9 @@ static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *iden if (glt->flags & TEXF_ALLOWUPDATES) glt->bufferpixels = (unsigned char *)Mem_Alloc(texturemempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel); - glt->modifiedpixels = NULL; - glt->modified_width = glt->modified_height = glt->modified_depth = glt->modified_offset_x = glt->modified_offset_y = glt->modified_offset_z = 0; + glt->buffermodified = false; + VectorClear(glt->modified_mins); + VectorClear(glt->modified_maxs); // free any temporary processing buffer we allocated... if (temppixels) @@ -2264,7 +2261,7 @@ int R_TextureFlags(rtexture_t *rt) return rt ? ((gltexture_t *)rt)->flags : 0; } -void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int z, int width, int height, int depth) +void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int z, int width, int height, int depth, int combine) { gltexture_t *glt = (gltexture_t *)rt; if (data == NULL) @@ -2279,70 +2276,59 @@ void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, in // update part of the texture if (glt->bufferpixels || (glt->bufferpixels && (x || y || z || width != glt->inputwidth || height != glt->inputheight || depth != glt->inputdepth))) { - int j; - int bpp = glt->bytesperpixel; - int inputskip = width*bpp; - int outputskip = glt->tilewidth*bpp; - const unsigned char *input = data; - unsigned char *output = glt->bufferpixels; + size_t j, bpp = glt->bytesperpixel; + + // depth and sides are not fully implemented here - can still do full updates but not partial. if (glt->inputdepth != 1 || glt->sides != 1) - Sys_Error("R_UpdateTexture on buffered texture that is not 2D\n"); - if (x < 0) - { - width += x; - input -= x*bpp; - x = 0; - } - if (y < 0) - { - height += y; - input -= y*inputskip; - y = 0; - } - if (width > glt->tilewidth - x) - width = glt->tilewidth - x; - if (height > glt->tileheight - y) - height = glt->tileheight - y; - if (width < 1 || height < 1) - return; - - output += y*outputskip + x*bpp; - /* Cloudwalk FIXME: Broken shit, disabled for now. - // TODO: Optimize this further. - if(!gl_nopartialtextureupdates.integer) + Host_Error("R_UpdateTexture on buffered texture that is not 2D\n"); + if (x < 0 || y < 0 || z < 0 || glt->tilewidth < x + width || glt->tileheight < y + height || glt->tiledepth < z + depth) + Host_Error("R_UpdateTexture on buffered texture with out of bounds coordinates (%i %i %i to %i %i %i is not within 0 0 0 to %i %i %i)", x, y, z, x + width, y + height, z + depth, glt->tilewidth, glt->tileheight, glt->tiledepth); + + for (j = 0; j < height; j++) + memcpy(glt->bufferpixels + ((y + j) * glt->tilewidth + x) * bpp, data + j * width * bpp, width * bpp); + + switch(combine) { - // Calculate the modified region, and resize it as it gets bigger. - if(!glt->buffermodified) + case 0: + // immediately update the part of the texture, no combining + R_UploadPartialTexture(glt, data, x, y, z, width, height, depth); + break; + case 1: + // keep track of the region that is modified, decide later how big the partial update area is + if (glt->buffermodified) { - glt->modified_offset_x = x; - glt->modified_offset_y = y; - glt->modified_offset_z = z; - - glt->modified_width = width; - glt->modified_height = height; - glt->modified_depth = depth; + glt->modified_mins[0] = min(glt->modified_mins[0], x); + glt->modified_mins[1] = min(glt->modified_mins[1], y); + glt->modified_mins[2] = min(glt->modified_mins[2], z); + glt->modified_maxs[0] = max(glt->modified_maxs[0], x + width); + glt->modified_maxs[1] = max(glt->modified_maxs[1], y + height); + glt->modified_maxs[2] = max(glt->modified_maxs[2], z + depth); } else { - if(x < glt->modified_offset_x) glt->modified_offset_x = x; - if(y < glt->modified_offset_y) glt->modified_offset_y = y; - if(z < glt->modified_offset_z) glt->modified_offset_z = z; - - if(width + x > glt->modified_width) glt->modified_width = width + x; - if(height + y > glt->modified_height) glt->modified_height = height + y; - if(depth + z > glt->modified_depth) glt->modified_depth = depth + z; + glt->buffermodified = true; + glt->modified_mins[0] = x; + glt->modified_mins[1] = y; + glt->modified_mins[2] = z; + glt->modified_maxs[0] = x + width; + glt->modified_maxs[1] = y + height; + glt->modified_maxs[2] = z + depth; } - - if(!&glt->modifiedpixels || &output < &glt->modifiedpixels) - glt->modifiedpixels = output; + glt->dirty = true; + break; + default: + case 2: + // mark the entire texture as dirty, it will be uploaded later + glt->buffermodified = true; + glt->modified_mins[0] = 0; + glt->modified_mins[1] = 0; + glt->modified_mins[2] = 0; + glt->modified_maxs[0] = glt->tilewidth; + glt->modified_maxs[1] = glt->tileheight; + glt->modified_maxs[2] = glt->tiledepth; + glt->dirty = true; + break; } - */ - - for (j = 0;j < height;j++, output += outputskip, input += inputskip) - memcpy(output, input, width*bpp); - - glt->dirty = true; - glt->buffermodified = true; } else R_UploadFullTexture(glt, data); @@ -2359,13 +2345,17 @@ int R_RealGetTexture(rtexture_t *rt) if (glt->buffermodified && glt->bufferpixels) { glt->buffermodified = false; - if(!glt->modifiedpixels) + // Because we currently don't set the relevant upload stride parameters, just make it full width. + glt->modified_mins[0] = 0; + glt->modified_maxs[0] = glt->tilewidth; + // Check also if it's updating at least half the height of the texture. + if (glt->modified_maxs[1] - glt->modified_mins[1] > glt->tileheight / 2) R_UploadFullTexture(glt, glt->bufferpixels); else - R_UploadPartialTexture(glt, glt->modifiedpixels, glt->modified_offset_x, glt->modified_offset_y, glt->modified_offset_z, glt->modified_width, glt->modified_height, glt->modified_depth); + R_UploadPartialTexture(glt, glt->bufferpixels + (size_t)glt->modified_mins[1] * glt->tilewidth * glt->bytesperpixel, glt->modified_mins[0], glt->modified_mins[1], glt->modified_mins[2], glt->modified_maxs[0] - glt->modified_mins[0], glt->modified_maxs[1] - glt->modified_mins[1], glt->modified_maxs[2] - glt->modified_mins[2]); } - glt->modified_offset_x = glt->modified_offset_y = glt->modified_offset_z = glt->modified_width = glt->modified_height = glt->modified_depth = 0; - glt->modifiedpixels = NULL; + VectorClear(glt->modified_mins); + VectorClear(glt->modified_maxs); glt->dirty = false; return glt->texnum; } diff --git a/r_shadow.c b/r_shadow.c index 67cf27ce..192f9064 100644 --- a/r_shadow.c +++ b/r_shadow.c @@ -2530,7 +2530,7 @@ static void R_Shadow_BounceGrid_ConvertPixelsAndUpload(void) } if (!r_shadow_bouncegrid_state.createtexture) - R_UpdateTexture(r_shadow_bouncegrid_state.texture, pixelsbgra8, 0, 0, 0, resolution[0], resolution[1], resolution[2]*pixelbands); + R_UpdateTexture(r_shadow_bouncegrid_state.texture, pixelsbgra8, 0, 0, 0, resolution[0], resolution[1], resolution[2]*pixelbands, 0); else r_shadow_bouncegrid_state.texture = R_LoadTexture3D(r_shadow_texturepool, "bouncegrid", resolution[0], resolution[1], resolution[2]*pixelbands, pixelsbgra8, TEXTYPE_BGRA, TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCELINEAR, 0, NULL); break; @@ -2581,7 +2581,7 @@ static void R_Shadow_BounceGrid_ConvertPixelsAndUpload(void) } if (!r_shadow_bouncegrid_state.createtexture) - R_UpdateTexture(r_shadow_bouncegrid_state.texture, (const unsigned char *)pixelsrgba16f, 0, 0, 0, resolution[0], resolution[1], resolution[2]*pixelbands); + R_UpdateTexture(r_shadow_bouncegrid_state.texture, (const unsigned char *)pixelsrgba16f, 0, 0, 0, resolution[0], resolution[1], resolution[2]*pixelbands, 0); else r_shadow_bouncegrid_state.texture = R_LoadTexture3D(r_shadow_texturepool, "bouncegrid", resolution[0], resolution[1], resolution[2]*pixelbands, (const unsigned char *)pixelsrgba16f, TEXTYPE_COLORBUFFER16F, TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCELINEAR, 0, NULL); break; @@ -2590,7 +2590,7 @@ static void R_Shadow_BounceGrid_ConvertPixelsAndUpload(void) pixelsrgba32f = highpixels; if (!r_shadow_bouncegrid_state.createtexture) - R_UpdateTexture(r_shadow_bouncegrid_state.texture, (const unsigned char *)pixelsrgba32f, 0, 0, 0, resolution[0], resolution[1], resolution[2]*pixelbands); + R_UpdateTexture(r_shadow_bouncegrid_state.texture, (const unsigned char *)pixelsrgba32f, 0, 0, 0, resolution[0], resolution[1], resolution[2]*pixelbands, 0); else r_shadow_bouncegrid_state.texture = R_LoadTexture3D(r_shadow_texturepool, "bouncegrid", resolution[0], resolution[1], resolution[2]*pixelbands, (const unsigned char *)pixelsrgba32f, TEXTYPE_COLORBUFFER32F, TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCELINEAR, 0, NULL); break; diff --git a/r_textures.h b/r_textures.h index 1e2b1044..cf2c0f3f 100644 --- a/r_textures.h +++ b/r_textures.h @@ -206,7 +206,8 @@ void R_FreeTexture(rtexture_t *rt); // update a portion of the image data of a texture, used by lightmap updates // and procedural textures such as video playback, actual uploads may be // delayed by gl_nopartialtextureupdates cvar until R_Mesh_TexBind uses it -void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int z, int width, int height, int depth); +// combine has 3 values: 0 = immediately upload (glTexSubImage2D), 1 = combine with other updates (glTexSubImage2D on next draw), 2 = combine with other updates and never upload partial images (glTexImage2D on next draw) +void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int z, int width, int height, int depth, int combine); // returns the renderer dependent texture slot number (call this before each // use, as a texture might not have been precached) diff --git a/render.h b/render.h index db5f9e85..eb9d5ef9 100644 --- a/render.h +++ b/render.h @@ -205,6 +205,11 @@ extern cvar_t r_fullbright; extern cvar_t r_wateralpha; extern cvar_t r_dynamic; +extern cvar_t r_q1bsp_lightmap_updates_enabled; +extern cvar_t r_q1bsp_lightmap_updates_combine; +extern cvar_t r_q1bsp_lightmap_updates_combine_full_texture; +extern cvar_t r_q1bsp_lightmap_updates_hidden_surfaces; + void R_NewExplosion(const vec3_t org); void R_UpdateVariables(void); // must call after setting up most of r_refdef, but before calling R_RenderView void R_RenderView(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture, int x, int y, int width, int height); // must set r_refdef and call R_UpdateVariables and CL_UpdateEntityShading first @@ -976,7 +981,7 @@ void R_Shadow_EditLights_DrawSelectedLightProperties(void); void R_DecalSystem_Reset(decalsystem_t *decalsystem); void R_Shadow_UpdateBounceGridTexture(void); void R_DrawPortals(void); -void R_BuildLightMap(const entity_render_t *ent, msurface_t *surface); +void R_BuildLightMap(const entity_render_t *ent, msurface_t *surface, int combine); void R_Water_AddWaterPlane(msurface_t *surface, int entno); int R_Shadow_GetRTLightInfo(unsigned int lightindex, float *origin, float *radius, float *color); dp_font_t *FindFont(const char *title, qbool allocate_new); -- 2.39.2