From 30fdd88c9fecd152080044360fc33bbc65500b5d Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 2 Aug 2017 22:39:27 -0500 Subject: [PATCH] Fix floating point precision loss in renderer [part 1] Patch for https://bugzilla.icculus.org/show_bug.cgi?id=5931 by Eugene C. from 2013 plus recent fix for tcMod rotate. I merged the changes into the OpenGL2 renderer though the fix for tcMod turb doesn't translate. --- code/renderergl1/tr_backend.c | 9 ++++++--- code/renderergl1/tr_local.h | 18 +++++++++--------- code/renderergl1/tr_shade.c | 9 ++++++--- code/renderergl1/tr_shade_calc.c | 20 +++++++++++--------- code/renderergl2/tr_backend.c | 9 ++++++--- code/renderergl2/tr_local.h | 18 +++++++++--------- code/renderergl2/tr_shade.c | 9 ++++++--- code/renderergl2/tr_shade_calc.c | 18 ++++++++++-------- 8 files changed, 63 insertions(+), 47 deletions(-) diff --git a/code/renderergl1/tr_backend.c b/code/renderergl1/tr_backend.c index 41fe47ac..22ec0480 100644 --- a/code/renderergl1/tr_backend.c +++ b/code/renderergl1/tr_backend.c @@ -509,7 +509,7 @@ void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) { int i; drawSurf_t *drawSurf; int oldSort; - float originalTime; + double originalTime; // save original time for entity shader offsets originalTime = backEnd.refdef.floatTime; @@ -562,7 +562,10 @@ void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) { if ( entityNum != REFENTITYNUM_WORLD ) { backEnd.currentEntity = &backEnd.refdef.entities[entityNum]; - backEnd.refdef.floatTime = originalTime - backEnd.currentEntity->e.shaderTime; + + // FIXME: e.shaderTime must be passed as int to avoid fp-precision loss issues + backEnd.refdef.floatTime = originalTime - (double)backEnd.currentEntity->e.shaderTime; + // we have to reset the shaderTime as well otherwise image animations start // from the wrong frame tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset; @@ -713,7 +716,7 @@ void RB_SetGL2D (void) { // set time for 2D shaders backEnd.refdef.time = ri.Milliseconds(); - backEnd.refdef.floatTime = backEnd.refdef.time * 0.001f; + backEnd.refdef.floatTime = (double)backEnd.refdef.time * 0.001f; } diff --git a/code/renderergl1/tr_local.h b/code/renderergl1/tr_local.h index 37e0e070..54279a6b 100644 --- a/code/renderergl1/tr_local.h +++ b/code/renderergl1/tr_local.h @@ -189,10 +189,10 @@ typedef enum { typedef struct { genFunc_t func; - float base; - float amplitude; - float phase; - float frequency; + double base; + double amplitude; + double phase; + double frequency; } waveForm_t; #define TR_MAX_TEXMODS 4 @@ -252,7 +252,7 @@ typedef struct { typedef struct { image_t *image[MAX_IMAGE_ANIMATIONS]; int numImageAnimations; - float imageAnimationSpeed; + double imageAnimationSpeed; texCoordGen_t tcGen; vec3_t tcGenVectors[2]; @@ -362,8 +362,8 @@ typedef struct shader_s { void (*optimalStageIteratorFunc)( void ); - float clampTime; // time this shader is clamped to - float timeOffset; // current time offset for this shader + double clampTime; // time this shader is clamped to + double timeOffset; // current time offset for this shader struct shader_s *remappedShader; // current shader this one is remapped too @@ -388,7 +388,7 @@ typedef struct { byte areamask[MAX_MAP_AREA_BYTES]; qboolean areamaskModified; // qtrue if areamask changed since last scene - float floatTime; // tr.refdef.time / 1000.0 + double floatTime; // tr.refdef.time / 1000.0 // text messages for deform text shaders char text[MAX_RENDER_STRINGS][MAX_RENDER_STRING_LENGTH]; @@ -1217,7 +1217,7 @@ typedef struct shaderCommands_s color4ub_t constantColor255[SHADER_MAX_VERTEXES] QALIGN(16); shader_t *shader; - float shaderTime; + double shaderTime; int fogNum; int dlightBits; // or together of all vertexDlightBits diff --git a/code/renderergl1/tr_shade.c b/code/renderergl1/tr_shade.c index d26cb1ac..ab5813f0 100644 --- a/code/renderergl1/tr_shade.c +++ b/code/renderergl1/tr_shade.c @@ -218,7 +218,8 @@ R_BindAnimatedImage ================= */ static void R_BindAnimatedImage( textureBundle_t *bundle ) { - int index; + int64_t index; + double v; if ( bundle->isVideoMap ) { ri.CIN_RunCinematic(bundle->videoMapHandle); @@ -233,8 +234,10 @@ static void R_BindAnimatedImage( textureBundle_t *bundle ) { // it is necessary to do this messy calc to make sure animations line up // exactly with waveforms of the same frequency - index = ri.ftol(tess.shaderTime * bundle->imageAnimationSpeed * FUNCTABLE_SIZE); - index >>= FUNCTABLE_SIZE2; + //index = ri.ftol(tess.shaderTime * bundle->imageAnimationSpeed * FUNCTABLE_SIZE); + //index >>= FUNCTABLE_SIZE2; + v = tess.shaderTime * bundle->imageAnimationSpeed; + index = v; if ( index < 0 ) { index = 0; // may happen with shader time offsets diff --git a/code/renderergl1/tr_shade_calc.c b/code/renderergl1/tr_shade_calc.c index 82f7c0cc..34975e25 100644 --- a/code/renderergl1/tr_shade_calc.c +++ b/code/renderergl1/tr_shade_calc.c @@ -27,7 +27,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #endif -#define WAVEVALUE( table, base, amplitude, phase, freq ) ((base) + table[ ri.ftol( ( ( (phase) + tess.shaderTime * (freq) ) * FUNCTABLE_SIZE ) ) & FUNCTABLE_MASK ] * (amplitude)) +#define WAVEVALUE( table, base, amplitude, phase, freq ) ((base) + table[ (int)( ( ( (phase) + tess.shaderTime * (freq) ) * FUNCTABLE_SIZE ) ) & FUNCTABLE_MASK ] * (amplitude)) static float *TableForFunc( genFunc_t func ) { @@ -920,7 +920,7 @@ void RB_CalcEnvironmentTexCoords( float *st ) void RB_CalcTurbulentTexCoords( const waveForm_t *wf, float *st ) { int i; - float now; + double now; now = ( wf->phase + tess.shaderTime * wf->frequency ); @@ -954,11 +954,13 @@ void RB_CalcScaleTexCoords( const float scale[2], float *st ) void RB_CalcScrollTexCoords( const float scrollSpeed[2], float *st ) { int i; - float timeScale = tess.shaderTime; - float adjustedScrollS, adjustedScrollT; + double timeScale; + double adjustedScrollS, adjustedScrollT; - adjustedScrollS = scrollSpeed[0] * timeScale; - adjustedScrollT = scrollSpeed[1] * timeScale; + timeScale = tess.shaderTime; + + adjustedScrollS = (double)scrollSpeed[0] * timeScale; + adjustedScrollT = (double)scrollSpeed[1] * timeScale; // clamp so coordinates don't continuously get larger, causing problems // with hardware limits @@ -994,9 +996,9 @@ void RB_CalcTransformTexCoords( const texModInfo_t *tmi, float *st ) */ void RB_CalcRotateTexCoords( float degsPerSecond, float *st ) { - float timeScale = tess.shaderTime; - float degs; - int index; + double timeScale = tess.shaderTime; + double degs; + int64_t index; float sinValue, cosValue; texModInfo_t tmi; diff --git a/code/renderergl2/tr_backend.c b/code/renderergl2/tr_backend.c index d6ff1226..4f8f3f4f 100644 --- a/code/renderergl2/tr_backend.c +++ b/code/renderergl2/tr_backend.c @@ -433,7 +433,7 @@ void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) { int i; drawSurf_t *drawSurf; int oldSort; - float originalTime; + double originalTime; FBO_t* fbo = NULL; qboolean inQuery = qfalse; @@ -498,7 +498,10 @@ void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) { if ( entityNum != REFENTITYNUM_WORLD ) { backEnd.currentEntity = &backEnd.refdef.entities[entityNum]; - backEnd.refdef.floatTime = originalTime - backEnd.currentEntity->e.shaderTime; + + // FIXME: e.shaderTime must be passed as int to avoid fp-precision loss issues + backEnd.refdef.floatTime = originalTime - (double)backEnd.currentEntity->e.shaderTime; + // we have to reset the shaderTime as well otherwise image animations start // from the wrong frame tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset; @@ -658,7 +661,7 @@ void RB_SetGL2D (void) { // set time for 2D shaders backEnd.refdef.time = ri.Milliseconds(); - backEnd.refdef.floatTime = backEnd.refdef.time * 0.001f; + backEnd.refdef.floatTime = (double)backEnd.refdef.time * 0.001f; } diff --git a/code/renderergl2/tr_local.h b/code/renderergl2/tr_local.h index dd959d5c..9426ede6 100644 --- a/code/renderergl2/tr_local.h +++ b/code/renderergl2/tr_local.h @@ -268,10 +268,10 @@ typedef enum { typedef struct { genFunc_t func; - float base; - float amplitude; - float phase; - float frequency; + double base; + double amplitude; + double phase; + double frequency; } waveForm_t; #define TR_MAX_TEXMODS 4 @@ -331,7 +331,7 @@ typedef struct { typedef struct { image_t *image[MAX_IMAGE_ANIMATIONS]; int numImageAnimations; - float imageAnimationSpeed; + double imageAnimationSpeed; texCoordGen_t tcGen; vec3_t tcGenVectors[2]; @@ -471,8 +471,8 @@ typedef struct shader_s { void (*optimalStageIteratorFunc)( void ); - float clampTime; // time this shader is clamped to - float timeOffset; // current time offset for this shader + double clampTime; // time this shader is clamped to + double timeOffset; // current time offset for this shader struct shader_s *remappedShader; // current shader this one is remapped too @@ -733,7 +733,7 @@ typedef struct { byte areamask[MAX_MAP_AREA_BYTES]; qboolean areamaskModified; // qtrue if areamask changed since last scene - float floatTime; // tr.refdef.time / 1000.0 + double floatTime; // tr.refdef.time / 1000.0 float blurFactor; @@ -1978,7 +1978,7 @@ typedef struct shaderCommands_s //color4ub_t constantColor255[SHADER_MAX_VERTEXES] QALIGN(16); shader_t *shader; - float shaderTime; + double shaderTime; int fogNum; int cubemapIndex; diff --git a/code/renderergl2/tr_shade.c b/code/renderergl2/tr_shade.c index 657f5073..0fd15486 100644 --- a/code/renderergl2/tr_shade.c +++ b/code/renderergl2/tr_shade.c @@ -65,7 +65,8 @@ R_BindAnimatedImageToTMU ================= */ static void R_BindAnimatedImageToTMU( textureBundle_t *bundle, int tmu ) { - int index; + int64_t index; + double v; if ( bundle->isVideoMap ) { ri.CIN_RunCinematic(bundle->videoMapHandle); @@ -81,8 +82,10 @@ static void R_BindAnimatedImageToTMU( textureBundle_t *bundle, int tmu ) { // it is necessary to do this messy calc to make sure animations line up // exactly with waveforms of the same frequency - index = ri.ftol(tess.shaderTime * bundle->imageAnimationSpeed * FUNCTABLE_SIZE); - index >>= FUNCTABLE_SIZE2; + //index = ri.ftol(tess.shaderTime * bundle->imageAnimationSpeed * FUNCTABLE_SIZE); + //index >>= FUNCTABLE_SIZE2; + v = tess.shaderTime * bundle->imageAnimationSpeed; + index = v; if ( index < 0 ) { index = 0; // may happen with shader time offsets diff --git a/code/renderergl2/tr_shade_calc.c b/code/renderergl2/tr_shade_calc.c index cb7bcf74..c411e03f 100644 --- a/code/renderergl2/tr_shade_calc.c +++ b/code/renderergl2/tr_shade_calc.c @@ -27,7 +27,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #endif -#define WAVEVALUE( table, base, amplitude, phase, freq ) ((base) + table[ ri.ftol( ( ( (phase) + tess.shaderTime * (freq) ) * FUNCTABLE_SIZE ) ) & FUNCTABLE_MASK ] * (amplitude)) +#define WAVEVALUE( table, base, amplitude, phase, freq ) ((base) + table[ (int)( ( ( (phase) + tess.shaderTime * (freq) ) * FUNCTABLE_SIZE ) ) & FUNCTABLE_MASK ] * (amplitude)) static float *TableForFunc( genFunc_t func ) { @@ -776,16 +776,18 @@ void RB_CalcScaleTexMatrix( const float scale[2], float *matrix ) */ void RB_CalcScrollTexMatrix( const float scrollSpeed[2], float *matrix ) { - float timeScale = tess.shaderTime; - float adjustedScrollS, adjustedScrollT; + double timeScale; + double adjustedScrollS, adjustedScrollT; + + timeScale = tess.shaderTime; adjustedScrollS = scrollSpeed[0] * timeScale; adjustedScrollT = scrollSpeed[1] * timeScale; // clamp so coordinates don't continuously get larger, causing problems // with hardware limits - adjustedScrollS = adjustedScrollS - floor( adjustedScrollS ); - adjustedScrollT = adjustedScrollT - floor( adjustedScrollT ); + adjustedScrollS = (double)adjustedScrollS - floor( adjustedScrollS ); + adjustedScrollT = (double)adjustedScrollT - floor( adjustedScrollT ); matrix[0] = 1.0f; matrix[2] = 0.0f; matrix[4] = adjustedScrollS; matrix[1] = 0.0f; matrix[3] = 1.0f; matrix[5] = adjustedScrollT; @@ -805,9 +807,9 @@ void RB_CalcTransformTexMatrix( const texModInfo_t *tmi, float *matrix ) */ void RB_CalcRotateTexMatrix( float degsPerSecond, float *matrix ) { - float timeScale = tess.shaderTime; - float degs; - int index; + double timeScale = tess.shaderTime; + double degs; + int64_t index; float sinValue, cosValue; degs = -degsPerSecond * timeScale;