]> git.xonotic.org Git - xonotic/darkplaces.git/commitdiff
Refactored lightmap update handling and added cvars to replace gl_nopartialtextureupd...
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Mon, 30 Nov 2020 06:40:43 +0000 (06:40 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Mon, 30 Nov 2020 06:40:43 +0000 (06:40 +0000)
* 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
gl_rmain.c
gl_rsurf.c
gl_textures.c
r_shadow.c
r_textures.h
render.h

index 3de01e19d5c3443f6989a24851d893f1a3b3ef1d..d625740e48c6996a1fc27ded907866c924fdeb2e 100644 (file)
--- 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;
index 8e273c8f4fefa2f5b728eeaf54c6b5ad8638e3c6..ebebd6c3eaea67dcbcb2adce069024d5e75e5ee0 100644 (file)
@@ -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);
                }
        }
 
index a832e4fa3cbc6d5f355574750c83a56b1fd68f06..6ac291c910f6385a9c7e19ecad3109de85fca207 100644 (file)
@@ -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);
        }
 }
 
index 0bb0a2939f12d194d9d44a63c25a41d5995a9e0f..fcb0bac7892c57e59e318de442358cb42d4a3827 100644 (file)
@@ -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;
        }
index 67cf27ced1aae04be8ed88ff1914e160278096d2..192f9064731cf2d00d04ed0e59739179f0c490bd 100644 (file)
@@ -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;
index 1e2b104416b2bd42f9bd1a2f9167b758a9c3c83b..cf2c0f3f3fb9974f5da2dd28b2ee6ae47c5ee65c 100644 (file)
@@ -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)
index db5f9e85cc2a6de54f27decf11a51a244e30f5bd..eb9d5ef98c137f37d9437f9bbe0677f85395d80d 100644 (file)
--- 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);