Optimize squirrel function_stub() Vector/QAngle return value

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/source-sdk-2013#321
This commit is contained in:
Alexander 'z33ky' Hirsch 2024-09-07 22:50:28 +02:00
parent 879b9e8d25
commit 99858e63f7
5 changed files with 145 additions and 91 deletions

View File

@ -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
@ -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 )

View File

@ -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

View File

@ -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
{
@ -389,52 +390,17 @@ 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 ); }
@ -442,42 +408,29 @@ struct ScriptVariant_t
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=( const QAngle &ang ) { m_type = FIELD_VECTOR; m_flags = 0; m_pAngle = &ang; }
void operator=( const QAngle *ang ) { m_type = FIELD_VECTOR; m_flags = 0; m_pAngle = ang; }
void operator=( Vector &&vec )
{
m_type = FIELD_VECTOR;
m_pVector = (Vector*)malloc( sizeof( Vector ) );
new ( (Vector*)m_pVector ) Vector( vec );
m_flags |= SV_FREE;
}
void operator=( QAngle &&ang )
{
m_type = FIELD_VECTOR;
m_pAngle = (QAngle*)malloc( sizeof( QAngle ) );
new ( (QAngle*)m_pAngle ) QAngle( ang );
m_flags |= SV_FREE;
}
void operator=( Vector &&vec ) = delete;
void operator=( QAngle &&ang ) = delete;
#endif
void Free()
@ -651,6 +604,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
@ -743,7 +706,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)
//-----------------------------------------------------------------------------
//

View File

@ -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 ); \
@ -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 ); \
@ -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 ); \
@ -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 ); \
@ -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 ) ) \
{ \
@ -419,7 +499,11 @@ inline FUNCPTR_TYPE ScriptConvertFuncPtrFromVoid( void *p )
return &CMemberScriptBinding##N<OBJECT_TYPE_PTR, Func_t, FUNCTION_RETTYPE FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_##N>::Call; \
}
//note: no memory is actually allocated in the functions that get defined,
// it merely uses placement-new for which we need to disable this
#include "tier0/memdbgoff.h"
FUNC_GENERATE_ALL( DEFINE_SCRIPT_BINDINGS );
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
//

View File

@ -1399,6 +1399,7 @@ SQInteger function_stub(HSQUIRRELVM vm)
}
ScriptVariant_t retval;
ScriptVariantTemporaryStorage_t retval_storage;
SquirrelVM* pSquirrelVM = (SquirrelVM*)sq_getforeignptr(vm);
assert(pSquirrelVM);
@ -1406,7 +1407,7 @@ SQInteger function_stub(HSQUIRRELVM vm)
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_))
{
@ -1417,7 +1418,9 @@ SQInteger function_stub(HSQUIRRELVM vm)
PushVariant(vm, retval);
retval.Free();
// strings never get copied here, Vector and QAngle are stored in script_retval_storage
// everything else is stored inline, so there should be no memory to free
Assert(!(retval.m_flags & SV_FREE));
return pFunc->m_desc.m_ReturnType != FIELD_VOID;
}