Added new compression for floats

This commit is contained in:
Kamay Xutax 2024-07-17 08:09:25 +02:00
parent 366edb18a7
commit 4186c83682
10 changed files with 278 additions and 119 deletions

View file

@ -11,6 +11,8 @@
#include "coordsize.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "strtools.h"
#include "sysexternal.h"
#include "tier0/memdbgon.h"
extern void DataTable_Warning( const char *pInMessage, ... );
@ -57,46 +59,110 @@ static inline bool EncodeSpecialFloat( const SendProp *pProp, float fVal, bf_wri
return false;
}
static inline void EncodeFloat( const SendProp *pProp, float fVal, bf_write *pOut, int objectID )
// TODO_ENHANCED:
// Encoding works by sending a normalized float (from range 0.0 to 1.0)
// This works because we know the range we encode the float to
static inline void EncodeFloat(const SendProp* pProp,
float fVal,
bf_write* pOut,
int objectID)
{
// Check for special flags like SPROP_COORD, SPROP_NOSCALE, and SPROP_NORMAL.
if( EncodeSpecialFloat( pProp, fVal, pOut ) )
{
return;
}
const auto WriteNormalizedFloat = [&](double dblValue)
{
bool isPerfectOne = dblValue >= 1.0;
uint32 ulVal;
if( fVal < pProp->m_fLowValue )
{
// clamp < 0
ulVal = 0;
if(!(pProp->GetFlags() & SPROP_ROUNDUP))
{
DataTable_Warning("(class %s): Out-of-range value (%f) in SendPropFloat '%s', clamping.\n", GetObjectClassName( objectID ), fVal, pProp->m_pVarName );
}
}
else if( fVal > pProp->m_fHighValue )
{
// clamp > 1
ulVal = ((1 << pProp->m_nBits) - 1);
if (isPerfectOne)
{
pOut->WriteOneBit(true);
}
else
{
pOut->WriteOneBit(false);
if(!(pProp->GetFlags() & SPROP_ROUNDDOWN))
{
DataTable_Warning("%s: Out-of-range value (%f) in SendPropFloat '%s', clamping.\n", GetObjectClassName( objectID ), fVal, pProp->m_pVarName );
}
}
else
{
float fRangeVal = (fVal - pProp->m_fLowValue) * pProp->m_fHighLowMul;
ulVal = RoundFloatToUnsignedLong( fRangeVal );
}
pOut->WriteUBitLong(ulVal, pProp->m_nBits);
char strNumber[16];
V_memset(strNumber, 0, sizeof(strNumber));
V_sprintf_safe(strNumber, "%f", static_cast<float>(dblValue));
auto sixDigitsValue = V_atoi(&strNumber[2]);
if (sixDigitsValue > 999999)
{
Sys_Error("EncodeFloat: tell to xutaxkamay that he's "
"dumb\n");
}
pOut->WriteUBitLong(sixDigitsValue, 20);
}
};
// Check for special flags like SPROP_COORD, SPROP_NOSCALE, and
// SPROP_NORMAL.
if (EncodeSpecialFloat(pProp, fVal, pOut))
{
return;
}
if ((pProp->m_fHighValue == 0.0f && pProp->m_fLowValue == 0.0f)
|| pProp->m_fHighValue == pProp->m_fLowValue)
{
pOut->WriteBitFloat(fVal);
return;
}
if (fVal > pProp->m_fHighValue)
{
Sys_Error("EncodeFloat: %s has value %f which is too big "
"(%f)",
pProp->GetName(),
fVal,
pProp->m_fHighValue);
}
if (fVal < pProp->m_fLowValue)
{
Sys_Error("EncodeFloat: %s has value %f which is too low "
"(%f)",
pProp->GetName(),
fVal,
pProp->m_fLowValue);
}
if (pProp->m_fLowValue > pProp->m_fHighValue)
{
Sys_Error("EncodeFloat: %s(%f) low value %f is higher than "
"%f",
pProp->GetName(),
fVal,
pProp->m_fLowValue,
pProp->m_fHighValue);
}
double dblVal = static_cast<double>(fVal);
// If low value bigger than zero, we need to substract to the
// lowest value possible, so we can recover it later.
if ((pProp->m_fLowValue > 0.0f && pProp->m_fHighValue > 0.0f)
|| (pProp->m_fHighValue < 0.0f && pProp->m_fLowValue < 0.0f))
{
dblVal -= pProp->m_fLowValue;
}
double dblRange = pProp->m_fHighValue - pProp->m_fLowValue;
bool sign = false;
if (dblVal < 0.0)
{
sign = true;
dblVal = -dblVal;
}
pOut->WriteOneBit(sign);
WriteNormalizedFloat(dblRange > 1.0 ? (dblVal / dblRange) :
dblVal);
}
// Look for special flags like SPROP_COORD, SPROP_NOSCALE, and SPROP_NORMAL and
// decode if they're there. Fills in fVal and returns true if it decodes anything.
static inline bool DecodeSpecialFloat( SendProp const *pProp, bf_read *pIn, float &fVal )
@ -123,7 +189,7 @@ static inline bool DecodeSpecialFloat( SendProp const *pProp, bf_read *pIn, floa
fVal = pIn->ReadBitCoordMP( true, false );
return true;
}
else if ( flags & SPROP_NOSCALE )
if ( flags & SPROP_NOSCALE )
{
fVal = pIn->ReadBitFloat();
return true;
@ -137,28 +203,110 @@ static inline bool DecodeSpecialFloat( SendProp const *pProp, bf_read *pIn, floa
return false;
}
static float DecodeFloat(SendProp const *pProp, bf_read *pIn)
static inline float DecodeNormalizedFloat(const SendProp* pProp, bf_read* pIn)
{
float fVal;
uint32 dwInterp;
bool sign = pIn->ReadOneBit();
double dblVal;
// Check for special flags..
if( DecodeSpecialFloat( pProp, pIn, fVal ) )
{
return fVal;
}
if (!pIn->ReadOneBit())
{
const auto fractionPart = pIn->ReadUBitLong(20);
dwInterp = pIn->ReadUBitLong(pProp->m_nBits);
fVal = (float)dwInterp / ((1 << pProp->m_nBits) - 1);
fVal = pProp->m_fLowValue + (pProp->m_fHighValue - pProp->m_fLowValue) * fVal;
return fVal;
char strNumber[8];
V_memset(strNumber, 0, sizeof(strNumber));
V_sprintf_safe(strNumber, "%i", fractionPart);
int countDigits = 0;
while (strNumber[countDigits])
{
countDigits++;
}
int digitLeft = 6 - countDigits;
if (digitLeft < 0)
{
Sys_Error("DecodeNormalizedFloat: digitLeft < 0\n");
}
char strFraction[16];
strFraction[0] = '0';
strFraction[1] = '.';
for (int i = 0; i < digitLeft; i++)
{
strFraction[2 + i] = '0';
}
for (int i = 0; i < countDigits; i++)
{
strFraction[2 + digitLeft + i] = strNumber[i];
}
dblVal = V_atof(strFraction);
}
else
{
dblVal = 1.0;
}
if (dblVal <= 0.0)
{
return 0.0f;
}
double dblRange = pProp->m_fHighValue - pProp->m_fLowValue;
if (dblRange > 1.0)
{
dblVal *= dblRange;
}
if ((pProp->m_fLowValue > 0.0f && pProp->m_fHighValue > 0.0f)
|| (pProp->m_fHighValue < 0.0f && pProp->m_fLowValue < 0.0f))
{
dblVal += pProp->m_fLowValue;
}
dblVal = sign ? -dblVal : dblVal;
return static_cast<float>(dblVal);
}
static inline float DecodeFloat(const SendProp* pProp, bf_read* pIn)
{
float fVal;
// Check for special flags..
if (DecodeSpecialFloat(pProp, pIn, fVal))
{
return fVal;
}
if ((pProp->m_fHighValue == 0.0f && pProp->m_fLowValue == 0.0f)
|| pProp->m_fHighValue == pProp->m_fLowValue)
{
fVal = pIn->ReadBitFloat();
return fVal;
}
if (pProp->m_fLowValue > pProp->m_fHighValue)
{
Sys_Error("EncodeFloat: %s low value %f is higher than "
"%f",
pProp->GetName(),
pProp->m_fLowValue,
pProp->m_fHighValue);
}
fVal = DecodeNormalizedFloat(pProp, pIn);
return fVal;
}
static inline void DecodeVector(SendProp const *pProp, bf_read *pIn, float *v)
{
v[0] = DecodeFloat(pProp, pIn);
v[1] = DecodeFloat(pProp, pIn);
v[1] = DecodeFloat(pProp, pIn);
// Don't read in the third component for normals
if ((pProp->GetFlags() & SPROP_NORMAL) == 0)
@ -465,7 +613,7 @@ int Float_CompareDeltas( const SendProp *pProp, bf_read *p1, bf_read *p2 )
}
else
{
return p1->ReadUBitLong( pProp->m_nBits ) != p2->ReadUBitLong( pProp->m_nBits );
return DecodeNormalizedFloat(pProp, p1) != DecodeNormalizedFloat(pProp, p2);
}
}
@ -541,7 +689,7 @@ void Float_SkipProp( const SendProp *pProp, bf_read *pIn )
}
else
{
pIn->SeekRelative( pProp->m_nBits );
DecodeNormalizedFloat(pProp, pIn);
}
}

View file

@ -353,11 +353,12 @@ def build(bld):
'audio',
'../public/engine/audio/',
'audio/',
'../thirdparty/ALP/include'
]
defines = []
libs = ['tier0','vgui_controls','dmxloader','tier1','tier2','tier3','bitmap','vstdlib','appframework','datamodel','vtf','mathlib','steam_api','matsys_controls','BZ2','SDL2','JPEG','ZLIB','OPENAL','CURL' ]
libs = ['ALP','tier0','vgui_controls','dmxloader','tier1','tier2','tier3','bitmap','vstdlib','appframework','datamodel','vtf','mathlib','steam_api','matsys_controls','BZ2','SDL2','JPEG','ZLIB','OPENAL','CURL' ]
if bld.env.DEST_OS == 'android':
libs += ['SSL', 'CRYPTO'] # android curl was built with openssl

View file

@ -219,7 +219,7 @@ IMPLEMENT_SERVERCLASS_ST_NOBASE( CBaseEntity, DT_BaseEntity )
#if PREDICTION_ERROR_CHECK_LEVEL > 1
SendPropVector (SENDINFO(m_vecOrigin), -1, SPROP_NOSCALE|SPROP_CHANGES_OFTEN, 0.0f, HIGH_DEFAULT, SendProxy_Origin ),
#else
SendPropVector (SENDINFO(m_vecOrigin), -1, SPROP_COORD|SPROP_CHANGES_OFTEN, 0.0f, HIGH_DEFAULT, SendProxy_Origin ),
SendPropVector (SENDINFO(m_vecOrigin), -1, SPROP_NOSCALE|SPROP_CHANGES_OFTEN, 0.0f, HIGH_DEFAULT, SendProxy_Origin ),
#endif
SendPropInt (SENDINFO( m_ubInterpolationFrame ), NOINTERP_PARITY_MAX_BITS, SPROP_UNSIGNED ),

View file

@ -7,6 +7,7 @@
#include "cbase.h"
#include "const.h"
#include "baseplayer_shared.h"
#include "dt_common.h"
#include "dt_send.h"
#include "trains.h"
#include "soundent.h"
@ -7934,9 +7935,9 @@ void CMovementSpeedMod::InputSpeedMod(inputdata_t &data)
// If HL2_DLL is defined, then baseflex.cpp already sends these.
#ifndef HL2_DLL
SendPropFloat ( SENDINFO_VECTORELEM(m_vecViewOffset, 0), 8, SPROP_ROUNDDOWN, -32.0, 32.0f),
SendPropFloat ( SENDINFO_VECTORELEM(m_vecViewOffset, 1), 8, SPROP_ROUNDDOWN, -32.0, 32.0f),
SendPropFloat ( SENDINFO_VECTORELEM(m_vecViewOffset, 2), 20, SPROP_CHANGES_OFTEN, 0.0f, 256.0f),
SendPropFloat ( SENDINFO_VECTORELEM(m_vecViewOffset, 0), -1, 0, -32.0, 32.0f),
SendPropFloat ( SENDINFO_VECTORELEM(m_vecViewOffset, 1), -1, 0, -32.0, 32.0f),
SendPropFloat ( SENDINFO_VECTORELEM(m_vecViewOffset, 2), -1, 0, 0.0f, 256.0f),
#endif
SendPropFloat ( SENDINFO(m_flFriction), 8, SPROP_ROUNDDOWN, 0.0f, 4.0f),
@ -7956,9 +7957,9 @@ void CMovementSpeedMod::InputSpeedMod(inputdata_t &data)
SendPropFloat ( SENDINFO_VECTORELEM(m_vecVelocity, 2), 32, SPROP_NOSCALE|SPROP_CHANGES_OFTEN ),
#if PREDICTION_ERROR_CHECK_LEVEL > 1
SendPropVector ( SENDINFO( m_vecBaseVelocity ), -1, SPROP_COORD ),
SendPropVector ( SENDINFO( m_vecBaseVelocity ), -1, SPROP_NOSCALE ),
#else
SendPropVector ( SENDINFO( m_vecBaseVelocity ), 20, 0, -1000, 1000 ),
SendPropVector ( SENDINFO( m_vecBaseVelocity ), -1, SPROP_NOSCALE ),
#endif
SendPropEHandle ( SENDINFO( m_hConstraintEntity)),

View file

@ -30,9 +30,9 @@ BEGIN_SEND_TABLE_NOBASE( CPlayerLocalData, DT_Local )
SendPropInt (SENDINFO(m_bDucked), 1, SPROP_UNSIGNED ),
SendPropInt (SENDINFO(m_bDucking), 1, SPROP_UNSIGNED ),
SendPropInt (SENDINFO(m_bInDuckJump), 1, SPROP_UNSIGNED ),
SendPropFloat (SENDINFO(m_flDucktime), 12, SPROP_ROUNDDOWN|SPROP_CHANGES_OFTEN, 0.0f, 2048.0f ),
SendPropFloat (SENDINFO(m_flDuckJumpTime), 12, SPROP_ROUNDDOWN, 0.0f, 2048.0f ),
SendPropFloat (SENDINFO(m_flJumpTime), 12, SPROP_ROUNDDOWN, 0.0f, 2048.0f ),
SendPropFloat (SENDINFO(m_flDucktime), 12, 0, 0.0f, 1000.0f ),
SendPropFloat (SENDINFO(m_flDuckJumpTime), 12, 0, 0.0f, 1000.0f ),
SendPropFloat (SENDINFO(m_flJumpTime), 12, 0, 0.0f, 1000.0f ),
#if PREDICTION_ERROR_CHECK_LEVEL > 1
SendPropFloat (SENDINFO(m_flFallVelocity), 32, SPROP_NOSCALE ),
@ -45,7 +45,7 @@ BEGIN_SEND_TABLE_NOBASE( CPlayerLocalData, DT_Local )
SendPropFloat ( SENDINFO_VECTORELEM(m_vecPunchAngleVel, 2), 32, SPROP_NOSCALE|SPROP_CHANGES_OFTEN ),
#else
SendPropFloat (SENDINFO(m_flFallVelocity), 17, SPROP_CHANGES_OFTEN, -4096.0f, 4096.0f ),
SendPropFloat (SENDINFO(m_flFallVelocity), -1, SPROP_COORD|SPROP_CHANGES_OFTEN ),
SendPropVector (SENDINFO(m_vecPunchAngle), -1, SPROP_COORD|SPROP_CHANGES_OFTEN),
SendPropVector (SENDINFO(m_vecPunchAngleVel), -1, SPROP_COORD),
#endif

View file

@ -24,7 +24,7 @@
#define MAX_ARRAY_ELEMENTS 2048 // a network array should have more that 1024 elements
#define HIGH_DEFAULT -121121.121121f
#define HIGH_DEFAULT 2048.0f * 2048.0f
#define BITS_FULLRES -1 // Use the full resolution of the type being encoded.
#define BITS_WORLDCOORD -2 // Encode as a world coordinate.

View file

@ -8,6 +8,7 @@
#include "dt_send.h"
#include "dt_common.h"
#include "mathlib/mathlib.h"
#include "mathlib/vector.h"
#include "tier0/dbg.h"
@ -374,10 +375,6 @@ SendProp SendPropFloat(
)
{
SendProp ret;
// Remove float compression to avoid errors
flags = SPROP_NOSCALE;
nBits = 32;
if ( varProxy == SendProxy_FloatToFloat )
{
@ -389,19 +386,16 @@ SendProp SendPropFloat(
flags |= SPROP_NOSCALE;
fLowValue = 0.f;
fHighValue = 0.f;
}
else
{
if(fHighValue == HIGH_DEFAULT)
fHighValue = (1 << nBits);
}
else
{
if (fHighValue == HIGH_DEFAULT)
{
fLowValue = -HIGH_DEFAULT;
}
}
if (flags & SPROP_ROUNDDOWN)
fHighValue = fHighValue - ((fHighValue - fLowValue) / (1 << nBits));
else if (flags & SPROP_ROUNDUP)
fLowValue = fLowValue + ((fHighValue - fLowValue) / (1 << nBits));
}
ret.m_Type = DPT_Float;
ret.m_Type = DPT_Float;
ret.m_pVarName = pVarName;
ret.SetOffset( offset );
ret.m_nBits = nBits;
@ -429,17 +423,24 @@ SendProp SendPropVector(
{
SendProp ret;
// Remove float compression to avoid errors
flags = SPROP_NOSCALE;
nBits = 32;
if(varProxy == SendProxy_VectorToVector)
{
Assert(sizeofVar == sizeof(Vector));
}
if ( nBits == 32 )
if ( nBits <= 0 || nBits == 32 )
{
flags |= SPROP_NOSCALE;
fLowValue = 0.f;
fHighValue = 0.f;
}
else
{
if (fHighValue == HIGH_DEFAULT)
{
fLowValue = -HIGH_DEFAULT;
}
}
ret.m_Type = DPT_Vector;
ret.m_pVarName = pVarName;
@ -469,17 +470,24 @@ SendProp SendPropVectorXY(
{
SendProp ret;
// Remove float compression to avoid errors
flags = SPROP_NOSCALE;
nBits = 32;
if(varProxy == SendProxy_VectorXYToVectorXY)
{
Assert(sizeofVar == sizeof(Vector));
}
if ( nBits == 32 )
if ( nBits <= 0 || nBits == 32 )
{
flags |= SPROP_NOSCALE;
fLowValue = 0.f;
fHighValue = 0.f;
}
else
{
if (fHighValue == HIGH_DEFAULT)
{
fLowValue = -HIGH_DEFAULT;
}
}
ret.m_Type = DPT_VectorXY;
ret.m_pVarName = pVarName;
@ -515,8 +523,19 @@ SendProp SendPropQuaternion(
Assert(sizeofVar == sizeof(Quaternion));
}
if ( nBits == 32 )
if ( nBits <= 0 || nBits == 32 )
{
flags |= SPROP_NOSCALE;
fLowValue = 0.f;
fHighValue = 0.f;
}
else
{
if (fHighValue == HIGH_DEFAULT)
{
fLowValue = -HIGH_DEFAULT;
}
}
ret.m_Type = DPT_Quaternion;
ret.m_pVarName = pVarName;
@ -550,10 +569,6 @@ SendProp SendPropAngle(
Assert(sizeofVar == 4);
}
// Remove float compression to avoid errors
flags = SPROP_NOSCALE;
nBits = 32;
if ( nBits == 32 )
flags |= SPROP_NOSCALE;
@ -587,10 +602,6 @@ SendProp SendPropQAngles(
Assert(sizeofVar == 4);
}
// Remove float compression to avoid errors
flags = SPROP_NOSCALE;
nBits = 32;
if ( nBits == 32 )
flags |= SPROP_NOSCALE;

@ -1 +1 @@
Subproject commit c5b901ecef515ea068fa8b8a19ca5cd5353905cb
Subproject commit b7d0d33bb36ffbced774c08ff974a073499a67f2

20
waf vendored

File diff suppressed because one or more lines are too long

16
wscript
View file

@ -94,6 +94,7 @@ projects={
'utils/vtex',
'unicode',
'video',
'thirdparty/ALP'
],
'tests': [
'appframework',
@ -145,7 +146,8 @@ projects={
'vpklib',
'vstdlib',
'vtf',
'stub_steam'
'stub_steam',
'thirdparty/ALP'
]
}
@ -297,13 +299,13 @@ def options(opt):
grp.add_option('-D', '--debug-engine', action = 'store_true', dest = 'DEBUG_ENGINE', default = False,
help = 'build with -DDEBUG [default: %default]')
grp.add_option('--use-sdl', action = 'store', dest = 'SDL', type = 'int', default = sys.platform != 'win32',
grp.add_option('--use-sdl', action = 'store', dest = 'SDL', type = int, default = sys.platform != 'win32',
help = 'build engine with SDL [default: %default]')
grp.add_option('--use-togl', action = 'store', dest = 'GL', type = 'int', default = sys.platform != 'win32',
grp.add_option('--use-togl', action = 'store', dest = 'GL', type = int, default = sys.platform != 'win32',
help = 'build engine with ToGL [default: %default]')
grp.add_option('--build-games', action = 'store', dest = 'GAMES', type = 'string', default = 'cstrike',
grp.add_option('--build-games', action = 'store', dest = 'GAMES', type = str, default = 'cstrike',
help = 'build games [default: %default]')
grp.add_option('--use-ccache', action = 'store_true', dest = 'CCACHE', default = False,
@ -321,10 +323,7 @@ def options(opt):
grp.add_option('--sanitize', action = 'store', dest = 'SANITIZE', default = '',
help = 'build with sanitizers [default: %default]')
opt.load('compiler_optimizations subproject')
opt.load('xcompile compiler_cxx compiler_c sdl2 clang_compilation_database strip_on_install_v2 waf_unit_test subproject')
opt.load('xcompile compiler_cxx compiler_c compiler_optimizations sdl2 clang_compilation_database strip_on_install waf_unit_test subproject')
if sys.platform == 'win32':
opt.load('msvc msdev msvs')
opt.load('reconfigure')
@ -435,7 +434,6 @@ def check_deps(conf):
def configure(conf):
conf.load('fwgslib reconfigure compiler_optimizations')
# Force XP compability, all build targets should add
# subsystem=bld.env.MSVC_SUBSYSTEM
# TODO: wrapper around bld.stlib, bld.shlib and so on?