From e930140428dc69c521bdb9480bb49e03979fecaa Mon Sep 17 00:00:00 2001 From: RaphaelIT7 <64648134+RaphaelIT7@users.noreply.github.com> Date: Mon, 17 Nov 2025 00:15:10 +0100 Subject: [PATCH] engine: make multiple things more threadsafe --- engine/audio/private/snd_dma.cpp | 7 ++++--- engine/common.cpp | 4 ++-- engine/decals.cpp | 17 +++++++++++++++++ engine/l_studio.cpp | 20 ++++++++++++++++---- engine/lightcache.cpp | 3 +++ 5 files changed, 42 insertions(+), 9 deletions(-) diff --git a/engine/audio/private/snd_dma.cpp b/engine/audio/private/snd_dma.cpp index c04c3fb00..c2ef4ed48 100644 --- a/engine/audio/private/snd_dma.cpp +++ b/engine/audio/private/snd_dma.cpp @@ -22,6 +22,7 @@ #include "../../client.h" #include "../../cl_main.h" #include "tier1/utldict.h" +#include "tier1/utlmapmt.h" #include "mempool.h" #include "../../enginetrace.h" // for traceline #include "../../public/bspflags.h" // for traceline @@ -299,13 +300,13 @@ static float s_lastsoundtime = 0.0f; bool s_bOnLoadScreen = false; -static CClassMemoryPool< CSfxTable > s_SoundPool( MAX_SFX ); +static CClassMemoryPoolMT< CSfxTable > s_SoundPool( MAX_SFX ); struct SfxDictEntry { CSfxTable *pSfx; }; -static CUtlMap< FileNameHandle_t, SfxDictEntry > s_Sounds( 0, 0, DefLessFunc( FileNameHandle_t ) ); +static CUtlMapMT< FileNameHandle_t, SfxDictEntry > s_Sounds( 0, 0, DefLessFunc( FileNameHandle_t ) ); class CDummySfx : public CSfxTable { @@ -578,7 +579,7 @@ class CResourcePreloadSound : public CResourcePreload } private: - CUtlSymbolTable m_SoundNames; + CUtlSymbolTableMT m_SoundNames; }; static CResourcePreloadSound s_ResourcePreloadSound; diff --git a/engine/common.cpp b/engine/common.cpp index 55d85242e..d502ca84c 100644 --- a/engine/common.cpp +++ b/engine/common.cpp @@ -316,8 +316,8 @@ rotates through a bunch of string buffers of 512 bytes each */ char *tmpstr512() { - static char string[32][512]; - static int curstring = 0; + static thread_local char string[32][512]; + static thread_local int curstring = 0; curstring = ( curstring + 1 ) & 31; return string[curstring]; } diff --git a/engine/decals.cpp b/engine/decals.cpp index deadf0b48..7f1b94629 100644 --- a/engine/decals.cpp +++ b/engine/decals.cpp @@ -32,6 +32,13 @@ struct DecalEntry // This stores the list of all decals CUtlMap< FileNameHandle_t, DecalEntry > g_DecalDictionary( 0, 0, DefLessFunc( FileNameHandle_t ) ); +// We could now use the CUtlMapMT but g_DecalLookup also exists and its easier to lock both and also reduces how many locks we create/destroy +#if defined(WIN32) || defined(_WIN32) +CThreadSpinRWLock g_DecalMutex; +#else +CThreadRWLock g_DecalMutex; +#endif + // This is a list of indices into the dictionary. // This list is indexed by network id, so it maps network ids to decal dictionary entries CUtlVector< int > g_DecalLookup; @@ -56,6 +63,8 @@ int Draw_DecalMax( void ) // called from gl_rsurf.cpp IMaterial *Draw_DecalMaterial( int index ) { + AUTO_LOCK_READ( g_DecalMutex ); + if ( index < 0 || index >= g_DecalLookup.Count() ) return NULL; @@ -70,6 +79,8 @@ IMaterial *Draw_DecalMaterial( int index ) #ifndef SWDS void Draw_DecalSetName( int decal, const char *name ) { + AUTO_LOCK_WRITE( g_DecalMutex ); + while ( decal >= g_DecalLookup.Count() ) { MEM_ALLOC_CREDIT(); @@ -105,6 +116,8 @@ void Draw_DecalSetName( int decal, const char *name ) // used for save/restore int Draw_DecalIndexFromName( const char *name, bool *found ) { + AUTO_LOCK_READ( g_DecalMutex ); + Assert( found ); FileNameHandle_t fnHandle = g_pFileSystem->FindOrAddFileName( name ); @@ -129,6 +142,8 @@ int Draw_DecalIndexFromName( const char *name, bool *found ) const char *Draw_DecalNameFromIndex( int index ) { + AUTO_LOCK_READ( g_DecalMutex ); + #if !defined(SWDS) return g_DecalDictionary[index].material ? g_DecalDictionary[index].material->GetName() : ""; #else @@ -148,6 +163,8 @@ void Decal_Init( void ) //----------------------------------------------------------------------------- void Decal_Shutdown( void ) { + AUTO_LOCK_WRITE( g_DecalMutex ); + for ( int index = g_DecalDictionary.FirstInorder(); index != g_DecalDictionary.InvalidIndex(); index = g_DecalDictionary.NextInorder(index) ) { IMaterial *mat = g_DecalDictionary[index].material; diff --git a/engine/l_studio.cpp b/engine/l_studio.cpp index 57230472c..4c2fceaa5 100644 --- a/engine/l_studio.cpp +++ b/engine/l_studio.cpp @@ -991,6 +991,7 @@ class CModelRender : public IVModelRender, // Model instance data CUtlLinkedList< ModelInstance_t, ModelInstanceHandle_t > m_ModelInstances; + CThreadFastMutex m_ModelInstancesMutex; // current active model studiohdr_t *m_pStudioHdr; @@ -1250,8 +1251,8 @@ LightingState_t *CModelRender::TimeAverageLightingState( ModelInstanceHandle_t h inst.m_flLightingTime = cl.GetTime(); } - static LightingState_t actualLightingState; - static dworldlight_t s_WorldLights[MAXLOCALLIGHTS]; + static thread_local LightingState_t actualLightingState; + static thread_local dworldlight_t s_WorldLights[MAXLOCALLIGHTS]; // I'm creating the equation v = vf - (vf-vi)e^-at // where vf = this frame's lighting value, vi = current time averaged lighting value @@ -1599,7 +1600,7 @@ void CModelRender::StudioSetupLighting( const DrawModelState_t &state, const Vec { pRenderContext->SetAmbientLight( 1.0, 1.0, 1.0 ); - static Vector white[6] = + static const Vector white[6] = { Vector( 1.0, 1.0, 1.0 ), Vector( 1.0, 1.0, 1.0 ), @@ -1620,7 +1621,7 @@ void CModelRender::StudioSetupLighting( const DrawModelState_t &state, const Vec { float add = r_itemblinkmax.GetFloat() * ( FastCos( r_itemblinkrate.GetFloat() * Sys_FloatTime() ) + 1.0f ); Vector additiveColor( add, add, add ); - static Vector temp[6]; + static thread_local Vector temp[6]; int i; for( i = 0; i < 6; i++ ) { @@ -1868,6 +1869,7 @@ struct ModelDebugOverlayData_t }; static CUtlVector s_SavedModelInfo; +static CThreadFastMutex s_SavedModelInfoMutex; void DrawModelDebugOverlay( const DrawModelInfo_t& info, const DrawModelResults_t &results, const Vector &origin, float r = 1.0f, float g = 1.0f, float b = 1.0f ) { @@ -1941,6 +1943,8 @@ void DrawModelDebugOverlay( const DrawModelInfo_t& info, const DrawModelResults_ void AddModelDebugOverlay( const DrawModelInfo_t& info, const DrawModelResults_t &results, const Vector& origin ) { + AUTO_LOCK( s_SavedModelInfoMutex ); + ModelDebugOverlayData_t &tmp = s_SavedModelInfo[s_SavedModelInfo.AddToTail()]; tmp.m_ModelInfo = info; tmp.m_ModelResults = results; @@ -1949,6 +1953,8 @@ void AddModelDebugOverlay( const DrawModelInfo_t& info, const DrawModelResults_t void ClearSaveModelDebugOverlays( void ) { + AUTO_LOCK( s_SavedModelInfoMutex ); + s_SavedModelInfo.RemoveAll(); } @@ -1957,6 +1963,8 @@ static ConVar r_drawmodelstatsoverlaymax( "r_drawmodelstatsoverlaymax", "1.5", F void DrawSavedModelDebugOverlays( void ) { + AUTO_LOCK( s_SavedModelInfoMutex ); + if( s_SavedModelInfo.Count() == 0 ) { return; @@ -4231,6 +4239,10 @@ ModelInstanceHandle_t CModelRender::CreateInstance( IClientRenderable *pRenderab { Assert( pRenderable ); + // RaphaelIT7: Perferably, we make everything threadsafe so that we only need to lock until instance.m_FirstShadow + // But currently most functions are not threadsafe all and will crash if we don't block to 1 thread at a time. + AUTO_LOCK( m_ModelInstancesMutex ); + // ensure all components are available model_t *pModel = (model_t*)pRenderable->GetModel(); diff --git a/engine/lightcache.cpp b/engine/lightcache.cpp index eabdba5df..f61ecbe82 100644 --- a/engine/lightcache.cpp +++ b/engine/lightcache.cpp @@ -2195,8 +2195,11 @@ static ITexture *FindEnvCubemapForPoint( const Vector& origin ) //----------------------------------------------------------------------------- // Create static light cache entry //----------------------------------------------------------------------------- +static CThreadMutex g_pPropCacheMutex; LightCacheHandle_t CreateStaticLightingCache( const Vector& origin, const Vector& mins, const Vector& maxs ) { + AUTO_LOCK( g_pPropCacheMutex ); + PropLightcache_t* pcache = s_PropCache.Alloc(); pcache->m_LightingOrigin = origin;