added gl_nopartialtextureupdates cvar which disables use of
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Mon, 21 Dec 2009 07:38:14 +0000 (07:38 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Mon, 21 Dec 2009 07:38:14 +0000 (07:38 +0000)
glTexSubImage2D calls (in case this hits bad paths on some drivers?)
added R_Mesh_TexBound function which returns the currently bound texture
on a given unit (avoids a slow qglGetIntegerv)

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@9615 d7cf8633-e32d-0410-b094-e92efae38249

cl_gecko.c
cl_video.c
gl_backend.c
gl_backend.h
gl_draw.c
gl_rmain.c
gl_textures.c
model_brush.c
r_textures.h

index 72764ef..9da615c 100644 (file)
@@ -449,7 +449,7 @@ static void cl_gecko_updatecallback( rtexture_t *texture, void* callbackData ) {
 static void cl_gecko_linktexture( clgecko_t *instance ) {
        // TODO: assert that instance->texture == NULL
        instance->texture = R_LoadTexture2D( cl_geckotexturepool, instance->name, 
-               instance->texWidth, instance->texHeight, NULL, TEXTYPE_BGRA, TEXF_ALWAYSPRECACHE | TEXF_ALPHA | TEXF_PERSISTENT, NULL );
+               instance->texWidth, instance->texHeight, NULL, TEXTYPE_BGRA, TEXF_ALWAYSPRECACHE | TEXF_ALPHA | TEXF_PERSISTENT | TEXF_ALLOWUPDATES, NULL );
        R_MakeTextureDynamic( instance->texture, cl_gecko_updatecallback, instance );
        CL_LinkDynTexture( instance->name, instance->texture );
 }
index 4eac2ae..f97872c 100644 (file)
@@ -42,7 +42,7 @@ static void VideoUpdateCallback(rtexture_t *rt, void *data) {
 
 static void LinkVideoTexture( clvideo_t *video ) {
        video->cpif.tex = R_LoadTexture2D( cl_videotexturepool, video->cpif.name,
-               video->cpif.width, video->cpif.height, NULL, TEXTYPE_BGRA, TEXF_ALWAYSPRECACHE | TEXF_PERSISTENT, NULL );
+               video->cpif.width, video->cpif.height, NULL, TEXTYPE_BGRA, TEXF_ALWAYSPRECACHE | TEXF_PERSISTENT | TEXF_ALLOWUPDATES, NULL );
        R_MakeTextureDynamic( video->cpif.tex, VideoUpdateCallback, video );
        CL_LinkDynTexture( video->cpif.name, video->cpif.tex );
 }
index a0e336a..adf0ad1 100644 (file)
@@ -1545,6 +1545,22 @@ void R_Mesh_TexCoordPointer(unsigned int unitnum, unsigned int numcomponents, co
        }
 }
 
+int R_Mesh_TexBound(unsigned int unitnum, int id)
+{
+       gltextureunit_t *unit = gl_state.units + unitnum;
+       if (unitnum >= vid.teximageunits)
+               return 0;
+       if (id == GL_TEXTURE_2D)
+               return unit->t2d;
+       if (id == GL_TEXTURE_3D)
+               return unit->t3d;
+       if (id == GL_TEXTURE_CUBE_MAP_ARB)
+               return unit->tcubemap;
+       if (id == GL_TEXTURE_RECTANGLE_ARB)
+               return unit->trectangle;
+       return 0;
+}
+
 void R_Mesh_TexBindAll(unsigned int unitnum, int tex2d, int tex3d, int texcubemap, int texrectangle)
 {
        gltextureunit_t *unit = gl_state.units + unitnum;
index 63261cd..be97481 100644 (file)
@@ -74,6 +74,8 @@ void R_Mesh_VertexPointer(const float *vertex3f, int bufferobject, size_t buffer
 void R_Mesh_ColorPointer(const float *color4f, int bufferobject, size_t bufferoffset);
 // sets the texcoord array pointer for an array unit
 void R_Mesh_TexCoordPointer(unsigned int unitnum, unsigned int numcomponents, const float *texcoord, int bufferobject, size_t bufferoffset);
+// returns current texture bound to this identifier
+int R_Mesh_TexBound(unsigned int unitnum, int id);
 // sets all textures bound to an image unit (multiple can be non-zero at once, according to OpenGL rules the highest one overrides the others)
 void R_Mesh_TexBindAll(unsigned int unitnum, int tex2d, int tex3d, int texcubemap, int texrectangle);
 // equivalent to R_Mesh_TexBindAll(unitnum,tex2d,0,0,0)
index 348b70a..af30269 100644 (file)
--- a/gl_draw.c
+++ b/gl_draw.c
@@ -504,7 +504,7 @@ cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, u
        pic->height = height;
        if (pic->tex)
                R_FreeTexture(pic->tex);
-       pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, TEXF_PRECACHE | (alpha ? TEXF_ALPHA : 0), NULL);
+       pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, TEXF_PRECACHE | (alpha ? TEXF_ALPHA : 0) | TEXF_ALLOWUPDATES, NULL);
        return pic;
 }
 
index e7c97c0..4906552 100644 (file)
@@ -450,8 +450,8 @@ static void R_BuildFogTexture(void)
        }
        else
        {
-               r_texture_fogattenuation = R_LoadTexture2D(r_main_texturepool, "fogattenuation", FOGWIDTH, 1, &data1[0][0], TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_PERSISTENT, NULL);
-               //r_texture_fogintensity = R_LoadTexture2D(r_main_texturepool, "fogintensity", FOGWIDTH, 1, &data2[0][0], TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_FORCELINEAR | TEXF_CLAMP, NULL);
+               r_texture_fogattenuation = R_LoadTexture2D(r_main_texturepool, "fogattenuation", FOGWIDTH, 1, &data1[0][0], TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_PERSISTENT | TEXF_ALLOWUPDATES, NULL);
+               //r_texture_fogintensity = R_LoadTexture2D(r_main_texturepool, "fogintensity", FOGWIDTH, 1, &data2[0][0], TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_ALLOWUPDATES, NULL);
        }
 }
 
@@ -5136,7 +5136,7 @@ void R_UpdateVariables(void)
                                }
                                else
                                {
-                                       r_texture_gammaramps = R_LoadTexture2D(r_main_texturepool, "gammaramps", RAMPWIDTH, 1, &rampbgr[0][0], TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_PERSISTENT, NULL);
+                                       r_texture_gammaramps = R_LoadTexture2D(r_main_texturepool, "gammaramps", RAMPWIDTH, 1, &rampbgr[0][0], TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_PERSISTENT | TEXF_ALLOWUPDATES, NULL);
                                }
                        }
                }
@@ -9426,10 +9426,31 @@ void R_DrawWorldSurfaces(qboolean skysurfaces, qboolean writedepth, qboolean dep
        }
        // update lightmaps if needed
        if (update)
+       {
+               int updated = 0;
                for (j = model->firstmodelsurface, endj = model->firstmodelsurface + model->nummodelsurfaces;j < endj;j++)
+               {
                        if (r_refdef.viewcache.world_surfacevisible[j])
+                       {
                                if (update[j])
+                               {
+                                       updated++;
                                        R_BuildLightMap(r_refdef.scene.worldentity, surfaces + j);
+                               }
+                       }
+               }
+               if (updated)
+               {
+                       int count = model->brushq3.num_mergedlightmaps;
+                       for (i = 0;i < count;i++)
+                       {
+                               if (model->brushq3.data_deluxemaps[i])
+                                       R_FlushTexture(model->brushq3.data_deluxemaps[i]);
+                               if (model->brushq3.data_lightmaps[i])
+                                       R_FlushTexture(model->brushq3.data_lightmaps[i]);
+                       }
+               }
+       }
        // don't do anything if there were no surfaces
        if (!numsurfacelist)
        {
@@ -9538,6 +9559,29 @@ void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces, qboolean wr
                return;
        }
        // update lightmaps if needed
+       if (update)
+       {
+               int updated = 0;
+               for (j = model->firstmodelsurface, endj = model->firstmodelsurface + model->nummodelsurfaces;j < endj;j++)
+               {
+                       if (update[j])
+                       {
+                               updated++;
+                               R_BuildLightMap(ent, surfaces + j);
+                       }
+               }
+               if (updated)
+               {
+                       int count = model->brushq3.num_mergedlightmaps;
+                       for (i = 0;i < count;i++)
+                       {
+                               if (model->brushq3.data_deluxemaps[i])
+                                       R_FlushTexture(model->brushq3.data_deluxemaps[i]);
+                               if (model->brushq3.data_lightmaps[i])
+                                       R_FlushTexture(model->brushq3.data_lightmaps[i]);
+                       }
+               }
+       }
        if (update)
                for (j = model->firstmodelsurface, endj = model->firstmodelsurface + model->nummodelsurfaces;j < endj;j++)
                        if (update[j])
index 083f4f3..6e2cb7f 100644 (file)
@@ -20,6 +20,7 @@ cvar_t gl_texturecompression_q3bsplightmaps = {CVAR_SAVE, "gl_texturecompression
 cvar_t gl_texturecompression_q3bspdeluxemaps = {CVAR_SAVE, "gl_texturecompression_q3bspdeluxemaps", "0", "whether to compress deluxemaps in q3bsp format levels (only levels compiled with q3map2 -deluxe have these)"};
 cvar_t gl_texturecompression_sky = {CVAR_SAVE, "gl_texturecompression_sky", "0", "whether to compress sky textures"};
 cvar_t gl_texturecompression_lightcubemaps = {CVAR_SAVE, "gl_texturecompression_lightcubemaps", "1", "whether to compress light cubemaps (spotlights and other light projection images)"};
+cvar_t gl_nopartialtextureupdates = {CVAR_SAVE, "gl_nopartialtextureupdates", "0", "use alternate path for dynamic lightmap updates"};
 
 int            gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
 int            gl_filter_mag = GL_LINEAR;
@@ -100,6 +101,10 @@ typedef struct gltexture_s
        void *updatacallback_data;
        // --- [11/22/2007 Black]
 
+       // stores backup copy of texture for deferred texture updates (r_nopartialtextureupdates cvar)
+       unsigned char *bufferpixels;
+       qboolean buffermodified;
+
        // pointer to texturepool (check this to see if the texture is allocated)
        struct gltexturepool_s *pool;
        // pointer to next texture in texturepool chain
@@ -386,6 +391,7 @@ static void GL_TextureMode_f (void)
        // change all the existing mipmap texture objects
        // FIXME: force renderer(/client/something?) restart instead?
        CHECKGLERROR
+       GL_ActiveTexture(0);
        for (pool = gltexturepoolchain;pool;pool = pool->next)
        {
                for (glt = pool->gltchain;glt;glt = glt->chain)
@@ -393,7 +399,7 @@ static void GL_TextureMode_f (void)
                        // only update already uploaded images
                        if (!(glt->flags & (GLTEXF_UPLOAD | TEXF_FORCENEAREST | TEXF_FORCELINEAR)))
                        {
-                               qglGetIntegerv(gltexturetypebindingenums[glt->texturetype], &oldbindtexnum);CHECKGLERROR
+                               oldbindtexnum = R_Mesh_TexBound(0, gltexturetypebindingenums[glt->texturetype]);
                                qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
                                if (glt->flags & TEXF_MIPMAP)
                                {
@@ -606,6 +612,7 @@ void R_Textures_Init (void)
        Cvar_RegisterVariable (&gl_texturecompression_q3bspdeluxemaps);
        Cvar_RegisterVariable (&gl_texturecompression_sky);
        Cvar_RegisterVariable (&gl_texturecompression_lightcubemaps);
+       Cvar_RegisterVariable (&gl_nopartialtextureupdates);
 
        R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
 }
@@ -641,6 +648,7 @@ void R_Textures_Frame (void)
                Cvar_SetValueQuick(&gl_texture_anisotropy, old_aniso);
 
                CHECKGLERROR
+               GL_ActiveTexture(0);
                for (pool = gltexturepoolchain;pool;pool = pool->next)
                {
                        for (glt = pool->gltchain;glt;glt = glt->chain)
@@ -648,7 +656,7 @@ void R_Textures_Frame (void)
                                // only update already uploaded images
                                if ((glt->flags & (GLTEXF_UPLOAD | TEXF_MIPMAP)) == TEXF_MIPMAP)
                                {
-                                       qglGetIntegerv(gltexturetypebindingenums[glt->texturetype], &oldbindtexnum);CHECKGLERROR
+                                       oldbindtexnum = R_Mesh_TexBound(0, gltexturetypebindingenums[glt->texturetype]);
 
                                        qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
                                        qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_ANISOTROPY_EXT, old_aniso);CHECKGLERROR
@@ -772,7 +780,8 @@ static void R_Upload(gltexture_t *glt, const unsigned char *data, int fragx, int
        CHECKGLERROR
 
        // we need to restore the texture binding after finishing the upload
-       qglGetIntegerv(gltexturetypebindingenums[glt->texturetype], &oldbindtexnum);CHECKGLERROR
+       GL_ActiveTexture(0);
+       oldbindtexnum = R_Mesh_TexBound(0, gltexturetypebindingenums[glt->texturetype]);
        qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
 
        // these are rounded up versions of the size to do better resampling
@@ -804,7 +813,7 @@ static void R_Upload(gltexture_t *glt, const unsigned char *data, int fragx, int
                prevbuffer = colorconvertbuffer;
        }
 
-       if ((glt->flags & (TEXF_MIPMAP | TEXF_PICMIP | GLTEXF_UPLOAD)) == 0 && glt->inputwidth == glt->tilewidth && glt->inputheight == glt->tileheight && glt->inputdepth == glt->tiledepth)
+       if ((glt->flags & (TEXF_MIPMAP | TEXF_PICMIP | GLTEXF_UPLOAD)) == 0 && glt->inputwidth == glt->tilewidth && glt->inputheight == glt->tileheight && glt->inputdepth == glt->tiledepth && (fragx != 0 || fragy != 0 || fragwidth != glt->tilewidth || fragheight != glt->tileheight))
        {
                // update a portion of the image
                switch(glt->texturetype)
@@ -1070,6 +1079,8 @@ static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *iden
                CHECKGLERROR
                qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
                R_Upload(glt, data, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
+               if ((glt->flags & TEXF_ALLOWUPDATES) && gl_nopartialtextureupdates.integer)
+                       glt->bufferpixels = Mem_Alloc(texturemempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
        }
        else if (data)
        {
@@ -1165,7 +1176,56 @@ void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, in
        if (!glt->texnum)
                Host_Error("R_UpdateTexture: texture has not been uploaded yet");
        // update part of the texture
-       R_Upload(glt, data, x, y, 0, width, height, 1);
+       if (glt->bufferpixels)
+       {
+               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;
+               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;
+               glt->buffermodified = true;
+               output += y*outputskip + x*bpp;
+               for (j = 0;j < height;j++, output += outputskip, input += inputskip)
+                       memcpy(output, input, width*bpp);
+               if (!(glt->flags & TEXF_MANUALFLUSHUPDATES))
+                       R_FlushTexture(rt);
+       }
+       else
+               R_Upload(glt, data, x, y, 0, width, height, 1);
+}
+
+void R_FlushTexture(rtexture_t *rt)
+{
+       gltexture_t *glt;
+       if (rt == NULL)
+               Host_Error("R_FlushTexture: no texture supplied");
+
+       // update part of the texture
+       glt = (gltexture_t *)rt;
+
+       if (!glt->buffermodified || !glt->bufferpixels)
+               return;
+       glt->buffermodified = false;
+       R_Upload(glt, glt->bufferpixels, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth);
 }
 
 void R_ClearTexture (rtexture_t *rt)
index 6fbad62..5cfae15 100644 (file)
@@ -2263,7 +2263,7 @@ static void Mod_Q1BSP_LoadFaces(lump_t *l)
 
        lightmaptexture = NULL;
        deluxemaptexture = r_texture_blanknormalmap;
-       lightmapnumber = 1;
+       lightmapnumber = 0;
        lightmapsize = bound(256, gl_max_lightmapsize.integer, (int)vid.maxtexturesize_2d);
        totallightmapsamples = 0;
 
@@ -2441,9 +2441,12 @@ static void Mod_Q1BSP_LoadFaces(lump_t *l)
                                if (loadmodel->texturepool == NULL)
                                        loadmodel->texturepool = R_AllocTexturePool();
                                // could not find room, make a new lightmap
-                               lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%i", lightmapnumber), lightmapsize, lightmapsize, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
+                               loadmodel->brushq3.num_mergedlightmaps = lightmapnumber + 1;
+                               loadmodel->brushq3.data_lightmaps = Mem_Realloc(loadmodel->mempool, loadmodel->brushq3.data_lightmaps, loadmodel->brushq3.num_mergedlightmaps * sizeof(loadmodel->brushq3.data_lightmaps[0]));
+                               loadmodel->brushq3.data_deluxemaps = Mem_Realloc(loadmodel->mempool, loadmodel->brushq3.data_deluxemaps, loadmodel->brushq3.num_mergedlightmaps * sizeof(loadmodel->brushq3.data_deluxemaps[0]));
+                               loadmodel->brushq3.data_lightmaps[lightmapnumber] = lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%i", lightmapnumber), lightmapsize, lightmapsize, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_PRECACHE | TEXF_ALLOWUPDATES | TEXF_MANUALFLUSHUPDATES, NULL);
                                if (loadmodel->brushq1.nmaplightdata)
-                                       deluxemaptexture = R_LoadTexture2D(loadmodel->texturepool, va("deluxemap%i", lightmapnumber), lightmapsize, lightmapsize, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
+                                       loadmodel->brushq3.data_deluxemaps[lightmapnumber] = deluxemaptexture = R_LoadTexture2D(loadmodel->texturepool, va("deluxemap%i", lightmapnumber), lightmapsize, lightmapsize, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_PRECACHE | TEXF_ALLOWUPDATES | TEXF_MANUALFLUSHUPDATES, NULL);
                                lightmapnumber++;
                                Mod_AllocLightmap_Reset(&allocState);
                                Mod_AllocLightmap_Block(&allocState, ssize, tsize, &lightmapx, &lightmapy);
@@ -4690,9 +4693,9 @@ static void Mod_Q3BSP_LoadLightmaps(lump_t *l, lump_t *faceslump)
                                        ;
                                if (developer_loading.integer)
                                        Con_Printf("lightmap merge texture #%i is %ix%i (%i of %i used)\n", lightmapindex, mergewidth*size, mergeheight*size, min(j, mergewidth*mergeheight), mergewidth*mergeheight);
-                               loadmodel->brushq3.data_lightmaps[lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", lightmapindex), mergewidth * size, mergeheight * size, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_PRECACHE | (gl_texturecompression_q3bsplightmaps.integer ? TEXF_COMPRESS : 0), NULL);
+                               loadmodel->brushq3.data_lightmaps[lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", lightmapindex), mergewidth * size, mergeheight * size, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_PRECACHE | (gl_texturecompression_q3bsplightmaps.integer ? TEXF_COMPRESS : TEXF_ALLOWUPDATES | TEXF_MANUALFLUSHUPDATES), NULL);
                                if (loadmodel->brushq3.data_deluxemaps)
-                                       loadmodel->brushq3.data_deluxemaps[lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("deluxemap%04i", lightmapindex), mergewidth * size, mergeheight * size, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_PRECACHE | (gl_texturecompression_q3bspdeluxemaps.integer ? TEXF_COMPRESS : 0), NULL);
+                                       loadmodel->brushq3.data_deluxemaps[lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("deluxemap%04i", lightmapindex), mergewidth * size, mergeheight * size, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_PRECACHE | (gl_texturecompression_q3bspdeluxemaps.integer ? TEXF_COMPRESS : TEXF_ALLOWUPDATES | TEXF_MANUALFLUSHUPDATES), NULL);
                        }
                        mergewidth = R_TextureWidth(loadmodel->brushq3.data_lightmaps[lightmapindex]) / size;
                        mergeheight = R_TextureHeight(loadmodel->brushq3.data_lightmaps[lightmapindex]) / size;
@@ -4712,6 +4715,14 @@ static void Mod_Q3BSP_LoadLightmaps(lump_t *l, lump_t *faceslump)
                }
        }
 
+       for (i = 0;i < loadmodel->brushq3.num_mergedlightmaps;i++)
+       {
+               if (loadmodel->brushq3.data_deluxemaps[i])
+                       R_FlushTexture(loadmodel->brushq3.data_deluxemaps[i]);
+               if (loadmodel->brushq3.data_lightmaps[i])
+                       R_FlushTexture(loadmodel->brushq3.data_lightmaps[i]);
+       }
+
        Mem_Free(convertedpixels);
        if(external)
        {
index 516394a..edd7d9e 100644 (file)
 #define TEXF_COMPARE 0x00000800
 // indicates texture should use lower precision where supported
 #define TEXF_LOWPRECISION 0x00001000
+// indicates texture should support R_UpdateTexture
+#define TEXF_ALLOWUPDATES 0x00002000
+// indicates texture should support R_FlushTexture (improving speed on multiple partial updates per draw)
+#define TEXF_MANUALFLUSHUPDATES 0x00004000
 // used for checking if textures mismatch
 #define TEXF_IMPORTANTBITS (TEXF_ALPHA | TEXF_MIPMAP | TEXF_CLAMP | TEXF_FORCENEAREST | TEXF_FORCELINEAR | TEXF_PICMIP | TEXF_COMPRESS | TEXF_COMPARE | TEXF_LOWPRECISION)
 
@@ -92,7 +96,11 @@ 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.
+// if TEXF_MANUALFLUSHUPDATES is used, you MUST call R_FlushTexture to apply the updates
 void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int width, int height);
+// if TEXF_MANUALFLUSHUPDATES is used, call this to apply the updates,
+// otherwise this function does nothing
+void R_FlushTexture(rtexture_t *rt);
 
 // returns the renderer dependent texture slot number (call this before each
 // use, as a texture might not have been precached)