Skip to content

Commit

Permalink
Optimize squirrel function_stub() Vector/QAngle return value
Browse files Browse the repository at this point in the history
Instead of temporary allocating dynamic memory, which subsequently is
copied into another dynamically allocated piece of memory, provide a
temporary buffer on the stack.
The script value inserted into the VM still needs to allocate
dynamically though...

A few sites are adapted to not create a ScriptVariant_t from a temporary
Vector to avoid dynamic allocation there too.

cf. mapbase-source#321
  • Loading branch information
z33ky committed Nov 13, 2024
1 parent f2b2e28 commit 8ed0ae9
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 89 deletions.
15 changes: 9 additions & 6 deletions sp/src/game/server/hl2/proto_sniper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2645,16 +2645,19 @@ Vector CProtoSniper::DesiredBodyTarget( CBaseEntity *pTarget )
{
// By default, aim for the center
Vector vecTarget = pTarget->WorldSpaceCenter();
const Vector vecBulletOrigin = GetBulletOrigin();

#ifdef MAPBASE_VSCRIPT
if (m_ScriptScope.IsInitialized() && g_Hook_GetActualShootPosition.CanRunInScope(m_ScriptScope))
if ( m_ScriptScope.IsInitialized() && g_Hook_GetActualShootPosition.CanRunInScope( m_ScriptScope ) )
{
ScriptVariant_t functionReturn;
ScriptVariant_t args[] = { GetBulletOrigin(), ToHScript( pTarget ) };
if (g_Hook_GetActualShootPosition.Call( m_ScriptScope, &functionReturn, args ))
ScriptVariant_t args[] = { vecBulletOrigin, ToHScript( pTarget ) };
if ( g_Hook_GetActualShootPosition.Call( m_ScriptScope, &functionReturn, args ) )
{
if (functionReturn.m_type == FIELD_VECTOR && functionReturn.m_pVector->LengthSqr() != 0.0f)
if ( functionReturn.m_type == FIELD_VECTOR && functionReturn.m_pVector->LengthSqr() != 0.0f )
{
return *functionReturn.m_pVector;
}
}
}
#endif
Expand Down Expand Up @@ -2682,12 +2685,12 @@ Vector CProtoSniper::DesiredBodyTarget( CBaseEntity *pTarget )
{
if( flTimeSinceLastMiss > 0.0f && flTimeSinceLastMiss < 4.0f && hl2_episodic.GetBool() )
{
vecTarget = pTarget->BodyTarget( GetBulletOrigin(), false );
vecTarget = pTarget->BodyTarget( vecBulletOrigin, false );
}
else
{
// Shoot zombies in the headcrab
vecTarget = pTarget->HeadTarget( GetBulletOrigin() );
vecTarget = pTarget->HeadTarget( vecBulletOrigin );
}
}
else if( pTarget->Classify() == CLASS_ANTLION )
Expand Down
3 changes: 2 additions & 1 deletion sp/src/game/shared/mapbase/vscript_consts_shared.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,8 @@ void RegisterSharedScriptConstants()
ScriptRegisterConstant( g_pScriptVM, ROPE_NO_GRAVITY, "Disable gravity on this rope. (for use in rope flags)" );
ScriptRegisterConstant( g_pScriptVM, ROPE_NUMFLAGS, "The number of rope flags recognized by the game." );

ScriptRegisterConstantNamed( g_pScriptVM, Vector( ROPE_GRAVITY ), "ROPE_GRAVITY", "Default rope gravity vector." );
static Vector vecRopeGravity( ROPE_GRAVITY );
ScriptRegisterConstantNamed( g_pScriptVM, vecRopeGravity, "ROPE_GRAVITY", "Default rope gravity vector." );

//
// Sounds
Expand Down
104 changes: 33 additions & 71 deletions sp/src/public/vscript/ivscript.h
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,8 @@ enum ScriptFuncBindingFlags_t
SF_MEMBER_FUNC = 0x01,
};

typedef bool (*ScriptBindingFunc_t)( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn );
union ScriptVariantTemporaryStorage_t;
typedef bool (*ScriptBindingFunc_t)( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn, ScriptVariantTemporaryStorage_t &temporaryReturnStorage );

struct ScriptFunctionBinding_t
{
Expand Down Expand Up @@ -389,93 +390,44 @@ struct ScriptVariant_t
ScriptVariant_t( bool val ) : m_flags( 0 ), m_type( FIELD_BOOLEAN ) { m_bool = val; }
ScriptVariant_t( HSCRIPT val ) : m_flags( 0 ), m_type( FIELD_HSCRIPT ) { m_hScript = val; }

ScriptVariant_t( const Vector &val, bool bCopy = false ) : m_flags( 0 ), m_type( FIELD_VECTOR )
{
if ( !bCopy )
{
m_pVector = &val;
}
else
{
m_pVector = (Vector*)malloc( sizeof( Vector ) );
new ( (Vector*)m_pVector ) Vector( val );
m_flags |= SV_FREE;
}
}
ScriptVariant_t( const Vector *val, bool bCopy = false ) : ScriptVariant_t( *val ) { }
ScriptVariant_t( const Vector &val ) : m_flags( 0 ), m_type( FIELD_VECTOR ) { m_pVector = &val; }
ScriptVariant_t( const Vector *val ) : ScriptVariant_t( *val ) { }

ScriptVariant_t( const char *val , bool bCopy = false ) : m_flags( 0 ), m_type( FIELD_CSTRING )
{
if ( !bCopy )
{
m_pszString = val;
}
else
{
m_pszString = strdup( val );
m_flags |= SV_FREE;
}
}
ScriptVariant_t( const char *val ) : m_flags( 0 ), m_type( FIELD_CSTRING ) { m_pszString = val; }

#ifdef MAPBASE_VSCRIPT
ScriptVariant_t( const QAngle &val, bool bCopy = false ) : m_flags( 0 ), m_type( FIELD_VECTOR )
{
if ( !bCopy )
{
m_pAngle = &val;
}
else
{
m_pAngle = (QAngle*)malloc( sizeof( QAngle ) );
new ( (QAngle*)m_pAngle ) QAngle( val );
m_flags |= SV_FREE;
}
}
ScriptVariant_t( const QAngle *val, bool bCopy = false ) : ScriptVariant_t( *val ) { }
ScriptVariant_t( const QAngle &val ) : m_flags( 0 ), m_type( FIELD_VECTOR ) { m_pAngle = &val; }
ScriptVariant_t( const QAngle *val ) : ScriptVariant_t( *val ) { }

ScriptVariant_t( Vector &&val ) : ScriptVariant_t( val, true ) { }
ScriptVariant_t( QAngle &&val ) : ScriptVariant_t( val, true ) { }
ScriptVariant_t( Vector &&val ) = delete;
ScriptVariant_t( QAngle &&val ) = delete;
#endif

bool IsNull() const { return (m_type == FIELD_VOID ); }

operator int() const { Assert( m_type == FIELD_INTEGER ); return m_int; }
operator float() const { Assert( m_type == FIELD_FLOAT ); return m_float; }
operator const char *() const { Assert( m_type == FIELD_CSTRING ); return ( m_pszString ) ? m_pszString : ""; }
operator const Vector &() const { Assert( m_type == FIELD_VECTOR ); static Vector vecNull(0, 0, 0); return (m_pVector) ? *m_pVector : vecNull; }
operator const Vector &() const { Assert( m_type == FIELD_VECTOR ); return (m_pVector) ? *m_pVector : vec3_origin; }
operator char() const { Assert( m_type == FIELD_CHARACTER ); return m_char; }
operator bool() const { Assert( m_type == FIELD_BOOLEAN ); return m_bool; }
operator HSCRIPT() const { Assert( m_type == FIELD_HSCRIPT ); return m_hScript; }
#ifdef MAPBASE_VSCRIPT
operator const QAngle &() const { Assert( m_type == FIELD_VECTOR ); static QAngle vecNull(0, 0, 0); return (m_pAngle) ? *m_pAngle : vecNull; }
operator const QAngle &() const { Assert( m_type == FIELD_VECTOR ); return (m_pAngle) ? *m_pAngle : vec3_angle; }
#endif

void operator=( int i ) { m_type = FIELD_INTEGER; m_int = i; }
void operator=( float f ) { m_type = FIELD_FLOAT; m_float = f; }
void operator=( double f ) { m_type = FIELD_FLOAT; m_float = (float)f; }
void operator=( const Vector &vec ) { m_type = FIELD_VECTOR; m_pVector = &vec; }
void operator=( const Vector *vec ) { m_type = FIELD_VECTOR; m_pVector = vec; }
void operator=( const char *psz ) { m_type = FIELD_CSTRING; m_pszString = psz; }
void operator=( char c ) { m_type = FIELD_CHARACTER; m_char = c; }
void operator=( bool b ) { m_type = FIELD_BOOLEAN; m_bool = b; }
void operator=( HSCRIPT h ) { m_type = FIELD_HSCRIPT; m_hScript = h; }
void operator=( int i ) { m_type = FIELD_INTEGER; m_flags = 0; m_int = i; }
void operator=( float f ) { m_type = FIELD_FLOAT; m_flags = 0; m_float = f; }
void operator=( double f ) { m_type = FIELD_FLOAT; m_flags = 0; m_float = (float)f; }
void operator=( const Vector &vec ) { m_type = FIELD_VECTOR; m_flags = 0; m_pVector = &vec; }
void operator=( const Vector *vec ) { m_type = FIELD_VECTOR; m_flags = 0; m_pVector = vec; }
void operator=( const char *psz ) { m_type = FIELD_CSTRING; m_flags = 0; m_pszString = psz; }
void operator=( char c ) { m_type = FIELD_CHARACTER; m_flags = 0; m_char = c; }
void operator=( bool b ) { m_type = FIELD_BOOLEAN; m_flags = 0; m_bool = b; }
void operator=( HSCRIPT h ) { m_type = FIELD_HSCRIPT; m_flags = 0; m_hScript = h; }
#ifdef MAPBASE_VSCRIPT
void operator=( const QAngle &vec ) { m_type = FIELD_VECTOR; m_pAngle = &vec; }
void operator=( const QAngle *vec ) { m_type = FIELD_VECTOR; m_pAngle = vec; }

void operator=( Vector &&vec )
{
m_pVector = (Vector*)malloc( sizeof( Vector ) );
new ( (Vector*)m_pVector ) Vector( vec );
m_flags |= SV_FREE;
}

void operator=( QAngle &&ang )
{
m_pAngle = (QAngle*)malloc( sizeof( QAngle ) );
new ( (QAngle*)m_pAngle ) Vector( ang );
m_flags |= SV_FREE;
}
void operator=( const QAngle &vec ) { m_type = FIELD_VECTOR; m_flags = 0; m_pAngle = &vec; }
void operator=( const QAngle *vec ) { m_type = FIELD_VECTOR; m_flags = 0; m_pAngle = vec; }
#endif

void Free()
Expand Down Expand Up @@ -649,6 +601,16 @@ inline void ScriptVariant_t::EmplaceAllocedVector( const Vector &vec )

#define SCRIPT_VARIANT_NULL ScriptVariant_t()

union ScriptVariantTemporaryStorage_t
{
// members must be initialized via placement-new
ScriptVariantTemporaryStorage_t() { }

// members must have trivial destructor, since no destructor will be invoked
Vector m_vec;
QAngle m_ang;
};

#ifdef MAPBASE_VSCRIPT
//---------------------------------------------------------
struct ScriptConstantBinding_t
Expand Down Expand Up @@ -741,7 +703,7 @@ static inline int ToConstantVariant(int value)
// This is used for registering variants (particularly vectors) not tied to existing variables.
// The principal difference is that m_data is initted with bCopy set to true.
#define ScriptRegisterConstantFromTemp( pVM, constant, description ) ScriptRegisterConstantFromTempNamed( pVM, constant, #constant, description )
#define ScriptRegisterConstantFromTempNamed( pVM, constant, scriptName, description ) do { static ScriptConstantBinding_t binding; binding.m_pszScriptName = scriptName; binding.m_pszDescription = description; binding.m_data = ScriptVariant_t( constant, true ); pVM->RegisterConstant( &binding ); } while (0)
#define ScriptRegisterConstantFromTempNamed( pVM, constant, scriptName, description ) do { static const auto constantStorage = constant; static ScriptConstantBinding_t binding; binding.m_pszScriptName = scriptName; binding.m_pszDescription = description; binding.m_data = ScriptVariant_t( constantStorage ); pVM->RegisterConstant( &binding ); } while (0)

//-----------------------------------------------------------------------------
//
Expand Down
100 changes: 90 additions & 10 deletions sp/src/public/vscript/vscript_templates.h
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,8 @@ inline FUNCPTR_TYPE ScriptConvertFuncPtrFromVoid( void *p )
class CNonMemberScriptBinding##N \
{ \
public: \
static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ) \
{ \
static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn, ScriptVariantTemporaryStorage_t &temporaryReturnStorage ) \
{ \
Assert( nArguments == N ); \
Assert( pReturn ); \
Assert( !pContext ); \
Expand All @@ -337,15 +337,15 @@ inline FUNCPTR_TYPE ScriptConvertFuncPtrFromVoid( void *p )
return false; \
} \
*pReturn = ((FUNC_TYPE)pFunction)( SCRIPT_BINDING_ARGS_##N ); \
return true; \
} \
return true; \
} \
}; \
\
template <typename FUNC_TYPE FUNC_TEMPLATE_FUNC_PARAMS_##N> \
class CNonMemberScriptBinding##N<FUNC_TYPE, void FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_##N> \
{ \
public: \
static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ) \
static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn, ScriptVariantTemporaryStorage_t &temporaryReturnStorage ) \
{ \
Assert( nArguments == N ); \
Assert( !pReturn ); \
Expand All @@ -360,12 +360,52 @@ inline FUNCPTR_TYPE ScriptConvertFuncPtrFromVoid( void *p )
} \
}; \
\
template <typename FUNC_TYPE FUNC_TEMPLATE_FUNC_PARAMS_##N> \
class CNonMemberScriptBinding##N<FUNC_TYPE, Vector FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_##N> \
{ \
public: \
static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn, ScriptVariantTemporaryStorage_t &temporaryReturnStorage ) \
{ \
Assert( nArguments == N ); \
Assert( pReturn ); \
Assert( !pContext ); \
\
if ( nArguments != N || !pReturn || pContext ) \
{ \
return false; \
} \
new ( &temporaryReturnStorage.m_vec ) Vector( ((FUNC_TYPE)pFunction)( SCRIPT_BINDING_ARGS_##N ) ); \
*pReturn = temporaryReturnStorage.m_vec; \
return true; \
} \
}; \
\
template <typename FUNC_TYPE FUNC_TEMPLATE_FUNC_PARAMS_##N> \
class CNonMemberScriptBinding##N<FUNC_TYPE, QAngle FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_##N> \
{ \
public: \
static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn, ScriptVariantTemporaryStorage_t &temporaryReturnStorage ) \
{ \
Assert( nArguments == N ); \
Assert( pReturn ); \
Assert( !pContext ); \
\
if ( nArguments != N || !pReturn || pContext ) \
{ \
return false; \
} \
new ( &temporaryReturnStorage.m_ang ) QAngle( ((FUNC_TYPE)pFunction)( SCRIPT_BINDING_ARGS_##N ) ); \
*pReturn = temporaryReturnStorage.m_ang; \
return true; \
} \
}; \
\
template <class OBJECT_TYPE_PTR, typename FUNC_TYPE, typename FUNCTION_RETTYPE FUNC_TEMPLATE_FUNC_PARAMS_##N> \
class CMemberScriptBinding##N \
{ \
public: \
static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ) \
{ \
static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn, ScriptVariantTemporaryStorage_t &temporaryReturnStorage ) \
{ \
Assert( nArguments == N ); \
Assert( pReturn ); \
Assert( pContext ); \
Expand All @@ -375,15 +415,15 @@ inline FUNCPTR_TYPE ScriptConvertFuncPtrFromVoid( void *p )
return false; \
} \
*pReturn = (((OBJECT_TYPE_PTR)(pContext))->*ScriptConvertFuncPtrFromVoid<FUNC_TYPE>(pFunction))( SCRIPT_BINDING_ARGS_##N ); \
return true; \
} \
return true; \
} \
}; \
\
template <class OBJECT_TYPE_PTR, typename FUNC_TYPE FUNC_TEMPLATE_FUNC_PARAMS_##N> \
class CMemberScriptBinding##N<OBJECT_TYPE_PTR, FUNC_TYPE, void FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_##N> \
{ \
public: \
static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ) \
static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn, ScriptVariantTemporaryStorage_t &temporaryReturnStorage ) \
{ \
Assert( nArguments == N ); \
Assert( !pReturn ); \
Expand All @@ -398,6 +438,46 @@ inline FUNCPTR_TYPE ScriptConvertFuncPtrFromVoid( void *p )
} \
}; \
\
template <class OBJECT_TYPE_PTR, typename FUNC_TYPE FUNC_TEMPLATE_FUNC_PARAMS_##N> \
class CMemberScriptBinding##N<OBJECT_TYPE_PTR, FUNC_TYPE, Vector FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_##N> \
{ \
public: \
static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn, ScriptVariantTemporaryStorage_t &temporaryReturnStorage ) \
{ \
Assert( nArguments == N ); \
Assert( pReturn ); \
Assert( pContext ); \
\
if ( nArguments != N || !pReturn || !pContext ) \
{ \
return false; \
} \
new ( &temporaryReturnStorage.m_vec ) Vector( (((OBJECT_TYPE_PTR)(pContext))->*ScriptConvertFuncPtrFromVoid<FUNC_TYPE>(pFunction))( SCRIPT_BINDING_ARGS_##N ) ); \
*pReturn = temporaryReturnStorage.m_vec; \
return true; \
} \
}; \
\
template <class OBJECT_TYPE_PTR, typename FUNC_TYPE FUNC_TEMPLATE_FUNC_PARAMS_##N> \
class CMemberScriptBinding##N<OBJECT_TYPE_PTR, FUNC_TYPE, QAngle FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_##N> \
{ \
public: \
static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn, ScriptVariantTemporaryStorage_t &temporaryReturnStorage ) \
{ \
Assert( nArguments == N ); \
Assert( pReturn ); \
Assert( pContext ); \
\
if ( nArguments != N || !pReturn || !pContext ) \
{ \
return false; \
} \
new ( &temporaryReturnStorage.m_ang ) QAngle( (((OBJECT_TYPE_PTR)(pContext))->*ScriptConvertFuncPtrFromVoid<FUNC_TYPE>(pFunction))( SCRIPT_BINDING_ARGS_##N ) ); \
*pReturn = temporaryReturnStorage.m_ang; \
return true; \
} \
}; \
\
template <typename FUNCTION_RETTYPE FUNC_TEMPLATE_FUNC_PARAMS_##N> \
inline ScriptBindingFunc_t ScriptCreateBinding(FUNCTION_RETTYPE (*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) ) \
{ \
Expand Down
4 changes: 3 additions & 1 deletion sp/src/vscript/vscript_squirrel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1399,14 +1399,15 @@ SQInteger function_stub(HSQUIRRELVM vm)
}

ScriptVariant_t retval;
ScriptVariantTemporaryStorage_t retval_storage;

SquirrelVM* pSquirrelVM = (SquirrelVM*)sq_getforeignptr(vm);
assert(pSquirrelVM);

sq_resetobject(&pSquirrelVM->lastError_);

(*pFunc->m_pfnBinding)(pFunc->m_pFunction, instance, params.Base(), nargs,
pFunc->m_desc.m_ReturnType == FIELD_VOID ? nullptr : &retval);
pFunc->m_desc.m_ReturnType == FIELD_VOID ? nullptr : &retval, retval_storage);

if (!sq_isnull(pSquirrelVM->lastError_))
{
Expand All @@ -1417,6 +1418,7 @@ SQInteger function_stub(HSQUIRRELVM vm)

PushVariant(vm, retval);

Assert(!(retval.m_flags & SV_FREE));
retval.Free();

return pFunc->m_desc.m_ReturnType != FIELD_VOID;
Expand Down

0 comments on commit 8ed0ae9

Please sign in to comment.