From 4ba615858bc88fb2467a16c97126462d4dfc3b53 Mon Sep 17 00:00:00 2001 From: havoc Date: Mon, 21 Dec 2009 07:38:14 +0000 Subject: [PATCH] added gl_nopartialtextureupdates cvar which disables use of 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 | 2 +- cl_video.c | 2 +- gl_backend.c | 16 ++++++++++++ gl_backend.h | 2 ++ gl_draw.c | 2 +- gl_rmain.c | 50 +++++++++++++++++++++++++++++++++--- gl_textures.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++---- model_brush.c | 21 ++++++++++++---- r_textures.h | 8 ++++++ 9 files changed, 157 insertions(+), 16 deletions(-) diff --git a/cl_gecko.c b/cl_gecko.c index 72764efe..9da615c6 100644 --- a/cl_gecko.c +++ b/cl_gecko.c @@ -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 ); } diff --git a/cl_video.c b/cl_video.c index 4eac2aea..f97872cd 100644 --- a/cl_video.c +++ b/cl_video.c @@ -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 ); } diff --git a/gl_backend.c b/gl_backend.c index a0e336ac..adf0ad1c 100644 --- a/gl_backend.c +++ b/gl_backend.c @@ -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; diff --git a/gl_backend.h b/gl_backend.h index 63261cd3..be97481d 100644 --- a/gl_backend.h +++ b/gl_backend.h @@ -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) diff --git a/gl_draw.c b/gl_draw.c index 348b70af..af302694 100644 --- 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; } diff --git a/gl_rmain.c b/gl_rmain.c index e7c97c03..49065524 100644 --- a/gl_rmain.c +++ b/gl_rmain.c @@ -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]) diff --git a/gl_textures.c b/gl_textures.c index 083f4f3c..6e2cb7fd 100644 --- a/gl_textures.c +++ b/gl_textures.c @@ -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) diff --git a/model_brush.c b/model_brush.c index 6fbad625..5cfae153 100644 --- a/model_brush.c +++ b/model_brush.c @@ -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) { diff --git a/r_textures.h b/r_textures.h index 516394ae..edd7d9e5 100644 --- a/r_textures.h +++ b/r_textures.h @@ -26,6 +26,10 @@ #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) -- 2.39.2