Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5807 lines
178 KiB

  1. //===== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // The copyright to the contents herein is the property of Valve, L.L.C.
  4. // The contents may be used and/or copied only with the written permission of
  5. // Valve, L.L.C., or in accordance with the terms and conditions stipulated in
  6. // the agreement/contract under which the contents have been supplied.
  7. //
  8. // model loading and caching
  9. //
  10. //===========================================================================//
  11. #ifndef _PS3
  12. #include <memory.h>
  13. #endif
  14. #include "tier0/vprof.h"
  15. #include "tier0/icommandline.h"
  16. #include "tier1/utllinkedlist.h"
  17. #include "tier1/utlmap.h"
  18. #include "datacache/imdlcache.h"
  19. #include "istudiorender.h"
  20. #include "filesystem.h"
  21. #include "optimize.h"
  22. #include "materialsystem/imaterialsystemhardwareconfig.h"
  23. #include "materialsystem/imesh.h"
  24. #include "datacache/idatacache.h"
  25. #include "studio.h"
  26. #include "vcollide.h"
  27. #include "utldict.h"
  28. #include "convar.h"
  29. #include "datacache_common.h"
  30. #include "mempool.h"
  31. #include "vphysics_interface.h"
  32. #include "phyfile.h"
  33. #include "studiobyteswap.h"
  34. #include "tier2/fileutils.h"
  35. #include "filesystem/IQueuedLoader.h"
  36. #include "tier1/lzmaDecoder.h"
  37. #include "datacache/iresourceaccesscontrol.h"
  38. #include "tier0/miniprofiler.h"
  39. #include <algorithm>
  40. #include "mdlcombine.h"
  41. #include "vtfcombine.h"
  42. #include "keyvalues.h"
  43. #ifdef _CERT
  44. #define NO_LOG_MDLCACHE 1
  45. #endif
  46. //#define DEBUG_ANIM_STALLS
  47. #ifdef NO_LOG_MDLCACHE
  48. #define LogMdlCache() 0
  49. #else
  50. #define LogMdlCache() mod_trace_load.GetBool()
  51. #endif
  52. #define MdlCacheMsg if ( !LogMdlCache() ) ; else Msg
  53. #define MdlCacheWarning if ( !LogMdlCache() ) ; else Warning
  54. #if defined( _X360 )
  55. #define AsyncMdlCache() 0 // Explicitly !!!OFF!!! for 360 (incompatible), specific compatible resources opt in individually.
  56. #else
  57. #define AsyncMdlCache() 0
  58. #endif
  59. #define ERROR_MODEL "models/error.mdl"
  60. #define IDSTUDIOHEADER (('T'<<24)+('S'<<16)+('D'<<8)+'I')
  61. #define MakeCacheID( handle, type ) ( ( (uint)(handle) << 16 ) | (uint)(type) )
  62. #define HandleFromCacheID( id) ( (MDLHandle_t)((id) >> 16) )
  63. #define TypeFromCacheID( id ) ( (MDLCacheDataType_t)((id) & 0xffff) )
  64. enum
  65. {
  66. STUDIODATA_FLAGS_STUDIOMESH_LOADED = 0x0001,
  67. STUDIODATA_FLAGS_VCOLLISION_LOADED = 0x0002,
  68. STUDIODATA_ERROR_MODEL = 0x0004,
  69. STUDIODATA_FLAGS_NO_STUDIOMESH = 0x0008,
  70. STUDIODATA_FLAGS_NO_VERTEX_DATA = 0x0010,
  71. // = 0x0020, // unused
  72. STUDIODATA_FLAGS_PHYSICS2COLLISION_LOADED = 0x0040,
  73. STUDIODATA_FLAGS_VCOLLISION_SCANNED = 0x0080,
  74. STUDIODATA_FLAGS_COMBINED_PLACEHOLDER = 0x0100,
  75. STUDIODATA_FLAGS_COMBINED = 0x0200,
  76. STUDIODATA_FLAGS_COMBINED_UNAVAILABLE = 0x0400,
  77. STUDIODATA_FLAGS_COMBINED_ASSET = 0x0800,
  78. };
  79. static IPhysicsSurfaceProps *physprops = NULL;
  80. class CStudioVCollide : public CRefCounted<>
  81. {
  82. public:
  83. ~CStudioVCollide()
  84. {
  85. g_pPhysicsCollision->VCollideUnload( &m_vcollide );
  86. }
  87. vcollide_t *GetVCollide()
  88. {
  89. return &m_vcollide;
  90. }
  91. private:
  92. vcollide_t m_vcollide;
  93. };
  94. // #define DEBUG_COMBINER 1
  95. enum
  96. {
  97. COMBINED_REFERENCE_PLACEHOLDER = 0x00000001,
  98. COMBINED_REFERENCE_PRIMARY = 0x00000002,
  99. COMBINED_REFERENCE_COMBINER = 0x00000004,
  100. };
  101. // only models with type "mod_studio" have this data
  102. struct studiodata_t
  103. {
  104. // The .mdl file
  105. DataCacheHandle_t m_MDLCache;
  106. // Reference count
  107. unsigned short m_nRefCount;
  108. // User data associated with handle
  109. void *m_pUserData;
  110. // the VPhysics collision model
  111. CStudioVCollide *m_pVCollide;
  112. // Hardware & LOD data
  113. studiohwdata_t m_HardwareData;
  114. // STUDIODATA_FLAGS_STUDIOMESH_LOADED, etc. from above
  115. unsigned short m_nFlags;
  116. // Pointer to the virtual version of the model
  117. virtualmodel_t *m_pVirtualModel;
  118. // Array of handles to animation blocks
  119. CUtlVector< DataCacheHandle_t > m_vecAnimBlocks;
  120. CUtlVector< unsigned long > m_vecFakeAnimBlockStall;
  121. #ifdef DEBUG_ANIM_STALLS
  122. CUtlVector< unsigned long > m_vecFirstRequest;
  123. #endif
  124. // vertex data is usually compressed to save memory (model decal code only needs some data)
  125. DataCacheHandle_t m_VertexCache;
  126. CUtlVector< unsigned short > m_vecAutoplaySequenceList;
  127. studiohdr_t *m_pForceLockedStudioHdr; // only non-null if mod_lock_mdls_on_load is set
  128. vertexFileHeader_t *m_pForceLockedVertexFileHeader; // only non-null if mod_lock_meshs_on_load is set and not async loading
  129. CInterlockedInt m_iStudioHdrVirtualLock; // keeps count while mdlcache lock is held, lock counts fixed up for transition
  130. CThreadFastMutex m_ForceLockMutex;
  131. MDLHandle_t m_Handle;
  132. TCombinedStudioData *m_pCombinedStudioData;
  133. DECLARE_FIXEDSIZE_ALLOCATOR_MT( studiodata_t );
  134. };
  135. DEFINE_FIXEDSIZE_ALLOCATOR_MT( studiodata_t, 128, CUtlMemoryPool::GROW_SLOW );
  136. // memdbgon must be the last include file in a .cpp file!!!
  137. #include "tier0/memdbgon.h"
  138. #define MODEL_SUBSTITUTION_FILENAME "cfg/model_substitution.txt"
  139. static const char *s_ModelSwapperExtensions[] =
  140. {
  141. ".mdl",
  142. ".dx90.vtx",
  143. ".vvd",
  144. ".ani",
  145. };
  146. //
  147. // Class to swap out models based on GPU level (PC-only)
  148. //
  149. class CModelSwapper
  150. {
  151. public:
  152. CModelSwapper() :
  153. // Hash with 256 buckets
  154. m_ModelLookup( 256, 0, 0, ModelSubstitution_t::AreEqual, ModelSubstitution_t::Hash ),
  155. m_nMaxExtensionLength( 0 ),
  156. m_nGPULevel( -1 )
  157. {
  158. for ( int i = 0; i < ARRAYSIZE( s_ModelSwapperExtensions ); ++ i )
  159. {
  160. int nLen = Q_strlen( s_ModelSwapperExtensions[ i ] );
  161. if ( m_nMaxExtensionLength < nLen )
  162. {
  163. m_nMaxExtensionLength = nLen;
  164. }
  165. }
  166. }
  167. ~CModelSwapper()
  168. {
  169. Cleanup();
  170. }
  171. void LoadSubstitutionFile( const char *pSubstitutionDefinitionFile )
  172. {
  173. Cleanup();
  174. KeyValues *pKV = new KeyValues( "ModelSubstitution" );
  175. if ( pKV->LoadFromFile( g_pFullFileSystem, pSubstitutionDefinitionFile ) )
  176. {
  177. for ( KeyValues *pSubKV = pKV->GetFirstSubKey(); pSubKV != NULL; pSubKV = pSubKV->GetNextKey() )
  178. {
  179. if ( Q_stricmp( pSubKV->GetName(), "sub" ) == 0 )
  180. {
  181. // max GPU level for which this substitution will be performed (default: 1)
  182. int nMaxGPULevel = pSubKV->GetInt( "maxgpulevel", 1 );
  183. const char *pOriginalModelName = pSubKV->GetString( "original", "" );
  184. const char *pSubstituteModelName = pSubKV->GetString( "substitute", "" );
  185. int nOriginalModelNameLength = Q_strlen( pOriginalModelName );
  186. int nSubstituteModelNameLength = Q_strlen( pSubstituteModelName );
  187. if ( nOriginalModelNameLength >= ( MAX_PATH - m_nMaxExtensionLength ) ||
  188. nSubstituteModelNameLength >= ( MAX_PATH - m_nMaxExtensionLength ) )
  189. {
  190. Warning( "PERF WARNING: error parsing " MODEL_SUBSTITUTION_FILENAME "\n" );
  191. continue;
  192. }
  193. // Create substitution entries for .mdl, .ani, .vvd, .dx90.vtx
  194. ModelSubstitution_t modelSubstitution;
  195. modelSubstitution.nMaxGPULevel = nMaxGPULevel;
  196. for ( int i = 0; i < ARRAYSIZE( s_ModelSwapperExtensions ); ++ i )
  197. {
  198. char *pName;
  199. pName = new char[ MAX_PATH ];
  200. modelSubstitution.pOriginalModelName = pName;
  201. Q_strncpy( pName, pOriginalModelName, MAX_PATH );
  202. Q_strncat( pName, s_ModelSwapperExtensions[ i ], MAX_PATH );
  203. V_FixSlashes( pName );
  204. m_Strings.AddToTail( modelSubstitution.pOriginalModelName );
  205. pName = new char[ MAX_PATH ];
  206. modelSubstitution.pSubstituteModelName = pName;
  207. Q_strncpy( pName, pSubstituteModelName, MAX_PATH );
  208. Q_strncat( pName, s_ModelSwapperExtensions[ i ], MAX_PATH );
  209. V_FixSlashes( pName );
  210. m_Strings.AddToTail( modelSubstitution.pSubstituteModelName );
  211. m_ModelLookup.Insert( modelSubstitution );
  212. }
  213. }
  214. }
  215. }
  216. else
  217. {
  218. Warning( "PERF WARNING: Failed to open model substitution file, cannot swap models out based on gpu_level!\n" );
  219. }
  220. pKV->deleteThis();
  221. }
  222. void Cleanup()
  223. {
  224. for ( int i = 0; i < m_Strings.Count(); ++ i )
  225. {
  226. delete[] m_Strings[i];
  227. }
  228. m_Strings.RemoveAll();
  229. m_ModelLookup.RemoveAll();
  230. }
  231. const char * TranslateModelName( const char *pOriginalModelName )
  232. {
  233. ModelSubstitution_t searchData;
  234. searchData.pOriginalModelName = pOriginalModelName;
  235. UtlHashHandle_t handle = m_ModelLookup.Find( searchData );
  236. if ( handle != m_ModelLookup.InvalidHandle() && GetEffectiveGPULevel() <= m_ModelLookup[ handle ].nMaxGPULevel )
  237. {
  238. DevMsg( "Substituting model %s for %s because gpu_level is %d\n", m_ModelLookup[ handle ].pSubstituteModelName, pOriginalModelName, GetEffectiveGPULevel() );
  239. return m_ModelLookup[ handle ].pSubstituteModelName;
  240. }
  241. return pOriginalModelName;
  242. }
  243. // We explicitly set and get the cached GPU level because
  244. // if it changes mid-level, bad things can happen to the model swapping logic.
  245. // Specifically, we'll try and re-load mesh data from the wrong models and
  246. // likely crash and/or corrupt memory.
  247. void LatchEffectiveGPULevel()
  248. {
  249. static ConVarRef gpu_level( "gpu_level" );
  250. m_nGPULevel = gpu_level.GetInt();
  251. }
  252. int GetEffectiveGPULevel()
  253. {
  254. // Latch on first request if this has not yet been initialized
  255. if ( m_nGPULevel == -1 )
  256. {
  257. LatchEffectiveGPULevel();
  258. }
  259. return m_nGPULevel;
  260. }
  261. private:
  262. struct ModelSubstitution_t
  263. {
  264. const char *pOriginalModelName;
  265. const char *pSubstituteModelName;
  266. int nMaxGPULevel;
  267. static bool AreEqual( const ModelSubstitution_t &lhs, const ModelSubstitution_t &rhs )
  268. {
  269. return Q_stricmp( lhs.pOriginalModelName, rhs.pOriginalModelName ) == 0;
  270. }
  271. static unsigned int Hash( const ModelSubstitution_t &value )
  272. {
  273. return HashStringCaseless( value.pOriginalModelName );
  274. }
  275. };
  276. CUtlVector< const char * > m_Strings;
  277. CUtlHash< ModelSubstitution_t > m_ModelLookup;
  278. int m_nMaxExtensionLength;
  279. int m_nGPULevel;
  280. };
  281. //-----------------------------------------------------------------------------
  282. // AnimBlock allocator - Provides a fixed block pooling strategy for anim blocks
  283. // to ease fragmentation due to streaming
  284. //-----------------------------------------------------------------------------
  285. #if defined( CSTRIKE15 )
  286. // CS:GO currently loads 518 anim blocks (most of which are in the 24-32K size range), I've made this 530 to allow for some additional animations that are coming soon.
  287. #define MAX_ANIMBLOCKS 530
  288. #define ANIMBLOCK_SIZE 33*1024 // this is set to 33K for now, because one animation is over 32K by a smidge and spews a message and ends up allocating outside the pool, reduce back to 32K when that anim is fixed
  289. #else
  290. // Portal 2 has different requirements. There are a lot of big animations seeking, which creates some issues in term of DVD latencies.
  291. // On the other hand, there are not a lot of different animations. So we use the 9 MB buffer differently.
  292. #define MAX_ANIMBLOCKS 137
  293. #define ANIMBLOCK_SIZE 64*1024
  294. #endif
  295. CFixedBudgetMemoryPool<ANIMBLOCK_SIZE, MAX_ANIMBLOCKS> g_AnimBlockAllocator;
  296. void FreeAnimBlock( void *p )
  297. {
  298. // anim blocks can be allocated from different providers
  299. if ( g_AnimBlockAllocator.Owns( p ) )
  300. {
  301. g_AnimBlockAllocator.Free( p );
  302. }
  303. else
  304. {
  305. g_pFullFileSystem->FreeOptimalReadBuffer( p );
  306. }
  307. }
  308. //-----------------------------------------------------------------------------
  309. // ConVars
  310. //-----------------------------------------------------------------------------
  311. static ConVar r_rootlod( "r_rootlod", "0" );
  312. static ConVar mod_forcedata( "mod_forcedata", ( AsyncMdlCache() ) ? "0" : "1", 0, "Forces all model file data into cache on model load." );
  313. static ConVar mod_test_not_available( "mod_test_not_available", "0" );
  314. static ConVar mod_test_mesh_not_available( "mod_test_mesh_not_available", "0" );
  315. static ConVar mod_test_verts_not_available( "mod_test_verts_not_available", "0" );
  316. static ConVar mod_load_mesh_async( "mod_load_mesh_async", ( AsyncMdlCache() ) ? "1" : "0" );
  317. static ConVar mod_load_anims_async( "mod_load_anims_async", ( IsGameConsole() || AsyncMdlCache() ) ? "1" : "0" );
  318. static ConVar mod_load_vcollide_async( "mod_load_vcollide_async", ( AsyncMdlCache() ) ? "1" : "0" );
  319. static ConVar mod_trace_load( "mod_trace_load", "0" );
  320. static ConVar mod_lock_mdls_on_load( "mod_lock_mdls_on_load", "1" );
  321. static ConVar mod_lock_meshes_on_load( "mod_lock_meshes_on_load", "1" );
  322. static ConVar mod_load_fakestall( "mod_load_fakestall", "0", 0, "Forces all ANI file loading to stall for specified ms\n");
  323. static ConVar mod_check_vcollide("mod_check_vcollide","0", 0, "Check all vcollides on load");
  324. #ifdef DEBUG_ANIM_STALLS
  325. static ConVar mod_load_showasync( "mod_load_showasync", "0", 0, "Shows the time to load an async animblock\n");
  326. #endif
  327. #ifdef DEDICATED
  328. static ConVar mod_dont_load_vertices("mod_dont_load_vertices", "1", 0, "For the dedicated server, don't load model vertex data" );
  329. #else
  330. static ConVar mod_dont_load_vertices("mod_dont_load_vertices", "0", 0, "For the dedicated server, supress loading model vertex data" );
  331. #endif
  332. //-----------------------------------------------------------------------------
  333. // Utility functions
  334. //-----------------------------------------------------------------------------
  335. static void MakeFilename( char szFileName[MAX_PATH], studiohdr_t *pStudioHdr, const char *pszExtension )
  336. {
  337. char szBaseModelName[MAX_PATH];
  338. Q_StripExtension( pStudioHdr->pszName(), szBaseModelName, MAX_PATH );
  339. Q_snprintf( szFileName, MAX_PATH, "models/%s%s", szBaseModelName, pszExtension );
  340. Q_FixSlashes( szFileName );
  341. #ifdef POSIX
  342. Q_strlower( szFileName );
  343. #endif
  344. }
  345. // cache off the surface prop indices for each bone or model prop
  346. static void StudioHdrLookupSurfaceProps( studiohdr_t *pStudioHdrIn )
  347. {
  348. pStudioHdrIn->surfacepropLookup = physprops->GetSurfaceIndex( pStudioHdrIn->pszSurfaceProp() );
  349. for ( int i = 0; i < pStudioHdrIn->numbones; i++ )
  350. {
  351. mstudiobone_t *pBone = (mstudiobone_t *)pStudioHdrIn->pBone(i);
  352. pBone->surfacepropLookup = physprops->GetSurfaceIndex( pBone->pszSurfaceProp() );
  353. }
  354. }
  355. static void StudioHdrSetAnimEventFlag( studiohdr_t *pStudioHdrIn )
  356. {
  357. for ( int i = 0; i < pStudioHdrIn->numlocalseq; i++ )
  358. {
  359. if ( pStudioHdrIn->pLocalSeqdesc(i)->numevents )
  360. return;
  361. }
  362. pStudioHdrIn->flags |= STUDIOHDR_FLAGS_NO_ANIM_EVENTS;
  363. }
  364. //-----------------------------------------------------------------------------
  365. // Async support
  366. //-----------------------------------------------------------------------------
  367. struct AsyncInfo_t
  368. {
  369. AsyncInfo_t() : hControl( NULL ), hModel( MDLHANDLE_INVALID ), type( MDLCACHE_NONE ), iAnimBlock( 0 ) {}
  370. FSAsyncControl_t hControl;
  371. MDLHandle_t hModel;
  372. MDLCacheDataType_t type;
  373. int iAnimBlock;
  374. };
  375. const int NO_ASYNC = CUtlLinkedList< AsyncInfo_t >::InvalidIndex();
  376. //-------------------------------------
  377. CUtlMap<int, intp> g_AsyncInfoMap( DefLessFunc( int ) );
  378. CThreadFastMutex g_AsyncInfoMapMutex;
  379. inline int MakeAsyncInfoKey( MDLHandle_t hModel, MDLCacheDataType_t type, int iAnimBlock )
  380. {
  381. Assert( type <= 7 && iAnimBlock < 8*1024 );
  382. return ( ( ( (int)hModel) << 16 ) | ( (int)type << 13 ) | iAnimBlock );
  383. }
  384. inline intp GetAsyncInfoIndex( MDLHandle_t hModel, MDLCacheDataType_t type, int iAnimBlock = 0 )
  385. {
  386. AUTO_LOCK( g_AsyncInfoMapMutex );
  387. int key = MakeAsyncInfoKey( hModel, type, iAnimBlock );
  388. int i = g_AsyncInfoMap.Find( key );
  389. if ( i == g_AsyncInfoMap.InvalidIndex() )
  390. {
  391. return NO_ASYNC;
  392. }
  393. return g_AsyncInfoMap[i];
  394. }
  395. inline intp SetAsyncInfoIndex( MDLHandle_t hModel, MDLCacheDataType_t type, int iAnimBlock, intp index )
  396. {
  397. AUTO_LOCK( g_AsyncInfoMapMutex );
  398. Assert( index == NO_ASYNC || GetAsyncInfoIndex( hModel, type, iAnimBlock ) == NO_ASYNC );
  399. int key = MakeAsyncInfoKey( hModel, type, iAnimBlock );
  400. if ( index == NO_ASYNC )
  401. {
  402. g_AsyncInfoMap.Remove( key );
  403. }
  404. else
  405. {
  406. g_AsyncInfoMap.Insert( key, index );
  407. }
  408. return index;
  409. }
  410. inline intp SetAsyncInfoIndex( MDLHandle_t hModel, MDLCacheDataType_t type, intp index )
  411. {
  412. return SetAsyncInfoIndex( hModel, type, 0, index );
  413. }
  414. //-----------------------------------------------------------------------------
  415. // QUEUED LOADING
  416. // Populates the cache by pushing expected MDL's (and all of their data).
  417. // The Model cache i/o behavior is unchanged during gameplay, ideally the cache
  418. // should yield miss free behaviour.
  419. //-----------------------------------------------------------------------------
  420. struct ModelParts_t
  421. {
  422. enum BufferType_t
  423. {
  424. BUFFER_MDL = 0,
  425. BUFFER_VTX = 1,
  426. BUFFER_VVD = 2,
  427. BUFFER_PHY = 3,
  428. BUFFER_MAXPARTS,
  429. };
  430. ModelParts_t()
  431. {
  432. nLoadedParts = 0;
  433. nExpectedParts = 0;
  434. hMDL = MDLHANDLE_INVALID;
  435. }
  436. // thread safe, only one thread will get a positive result
  437. bool DoFinalProcessing()
  438. {
  439. // indicates that all buffers have arrived
  440. // when all parts are present, returns true ( guaranteed once ), and marked as completed
  441. return nLoadedParts.AssignIf( nExpectedParts, nExpectedParts | 0x80000000 );
  442. }
  443. CUtlBuffer Buffers[BUFFER_MAXPARTS];
  444. MDLHandle_t hMDL;
  445. // bit flags
  446. CInterlockedInt nLoadedParts;
  447. int nExpectedParts;
  448. };
  449. struct AsyncHardwareLoad_t
  450. {
  451. ModelParts_t *m_pModelParts;
  452. };
  453. class CMDLCacheData;
  454. //-----------------------------------------------------------------------------
  455. // Implementation of the simple studio data cache (no caching)
  456. //-----------------------------------------------------------------------------
  457. class CMDLCache : public CTier3AppSystem< IMDLCache >, public IStudioDataCache, public CDefaultDataCacheClient
  458. {
  459. typedef CTier3AppSystem< IMDLCache > BaseClass;
  460. public:
  461. CMDLCache();
  462. // Inherited from IAppSystem
  463. virtual bool Connect( CreateInterfaceFn factory );
  464. virtual void Disconnect();
  465. virtual void *QueryInterface( const char *pInterfaceName );
  466. virtual InitReturnVal_t Init();
  467. virtual void Shutdown();
  468. virtual const AppSystemInfo_t* GetDependencies() { return NULL; }
  469. virtual AppSystemTier_t GetTier() { return APP_SYSTEM_TIER3; }
  470. virtual void Reconnect( CreateInterfaceFn factory, const char *pInterfaceName ) { BaseClass::Reconnect( factory, pInterfaceName ); }
  471. // Inherited from IStudioDataCache
  472. bool VerifyHeaders( studiohdr_t *pStudioHdr );
  473. vertexFileHeader_t *CacheVertexData( studiohdr_t *pStudioHdr );
  474. // Inherited from IMDLCache
  475. virtual MDLHandle_t FindMDL( const char *pMDLRelativePath );
  476. virtual int AddRef( MDLHandle_t handle );
  477. virtual int Release( MDLHandle_t handle );
  478. virtual int GetRef( MDLHandle_t handle );
  479. virtual void MarkAsLoaded(MDLHandle_t handle);
  480. virtual studiohdr_t *GetStudioHdr( MDLHandle_t handle );
  481. virtual studiohwdata_t *GetHardwareData( MDLHandle_t handle );
  482. virtual vcollide_t *GetVCollide( MDLHandle_t handle ) { return GetVCollideEx( handle, true); }
  483. virtual vcollide_t *GetVCollideEx( MDLHandle_t handle, bool synchronousLoad = true );
  484. virtual unsigned char *GetAnimBlock( MDLHandle_t handle, int nBlock, bool preloadIfMissing );
  485. virtual bool HasAnimBlockBeenPreloaded( MDLHandle_t handle, int nBlock );
  486. virtual virtualmodel_t *GetVirtualModel( MDLHandle_t handle );
  487. virtual virtualmodel_t *GetVirtualModelFast( const studiohdr_t *pStudioHdr, MDLHandle_t handle );
  488. virtual int GetAutoplayList( MDLHandle_t handle, unsigned short **pOut );
  489. virtual void TouchAllData( MDLHandle_t handle );
  490. virtual void SetUserData( MDLHandle_t handle, void* pData );
  491. virtual void *GetUserData( MDLHandle_t handle );
  492. virtual bool IsErrorModel( MDLHandle_t handle );
  493. virtual bool IsOverBudget( MDLHandle_t handle );
  494. virtual void SetCacheNotify( IMDLCacheNotify *pNotify );
  495. virtual vertexFileHeader_t *GetVertexData( MDLHandle_t handle );
  496. virtual void Flush( MDLCacheFlush_t nFlushFlags = MDLCACHE_FLUSH_ALL );
  497. virtual void Flush( MDLHandle_t handle, int nFlushFlags = MDLCACHE_FLUSH_ALL );
  498. virtual const char *GetModelName( MDLHandle_t handle );
  499. IDataCacheSection *GetCacheSection( MDLCacheDataType_t type )
  500. {
  501. switch ( type )
  502. {
  503. case MDLCACHE_STUDIOHWDATA:
  504. case MDLCACHE_VERTEXES:
  505. // meshes and vertexes are isolated to their own section
  506. return m_pMeshCacheSection;
  507. case MDLCACHE_ANIMBLOCK:
  508. // anim blocks have their own section
  509. return m_pAnimBlocksCacheSection;
  510. default:
  511. // everybody else
  512. return m_pModelCacheSection;
  513. }
  514. }
  515. void *AllocData( MDLCacheDataType_t type, int size );
  516. void FreeData( MDLCacheDataType_t type, void *pData );
  517. void CacheData( DataCacheHandle_t *c, void *pData, int size, const char *name, MDLCacheDataType_t type, DataCacheClientID_t id = (DataCacheClientID_t)-1 );
  518. void *CheckData( DataCacheHandle_t c, MDLCacheDataType_t type );
  519. void *CheckDataNoTouch( DataCacheHandle_t c, MDLCacheDataType_t type );
  520. void UncacheData( DataCacheHandle_t c, MDLCacheDataType_t type, bool bLockedOk = false );
  521. void DisableAsync() { mod_load_mesh_async.SetValue( 0 ); mod_load_anims_async.SetValue( 0 ); }
  522. virtual void BeginLock();
  523. virtual void EndLock();
  524. virtual void BeginCoarseLock();
  525. virtual void EndCoarseLock();
  526. virtual int *GetFrameUnlockCounterPtrOLD();
  527. virtual int *GetFrameUnlockCounterPtr( MDLCacheDataType_t type );
  528. virtual void FinishPendingLoads();
  529. // Task switch
  530. void ReleaseMaterialSystemObjects( int nChangeFlags );
  531. void RestoreMaterialSystemObjects( int nChangeFlags );
  532. virtual bool GetVCollideSize( MDLHandle_t handle, int *pVCollideSize );
  533. virtual void BeginMapLoad();
  534. virtual void EndMapLoad();
  535. virtual void InitPreloadData( bool rebuild );
  536. virtual void ShutdownPreloadData();
  537. virtual bool IsDataLoaded( MDLHandle_t handle, MDLCacheDataType_t type );
  538. virtual studiohdr_t *LockStudioHdr( MDLHandle_t handle );
  539. virtual void UnlockStudioHdr( MDLHandle_t handle );
  540. virtual void UnloadQueuedHardwareData( );
  541. virtual bool PreloadModel( MDLHandle_t handle );
  542. virtual void ResetErrorModelStatus( MDLHandle_t handle );
  543. virtual void MarkFrame();
  544. // Queued loading
  545. void ProcessQueuedData( ModelParts_t *pModelParts );
  546. static void QueuedLoaderCallback_MDL( void *pContext, void *pContext2, const void *pData, int nSize, LoaderError_t loaderError );
  547. // combined models
  548. virtual MDLHandle_t CreateCombinedModel( const char *pszModelName );
  549. virtual bool CreateCombinedModel( MDLHandle_t handle );
  550. virtual bool SetCombineModels( MDLHandle_t handle, const CUtlVector< SCombinerModelInput_t > &vecModelsToCombine );
  551. virtual bool FinishCombinedModel( MDLHandle_t handle, CombinedModelLoadedCallback pFunc, void *pUserData );
  552. virtual bool IsCombinedPlaceholder( MDLHandle_t handle );
  553. virtual bool IsCombinedModel( MDLHandle_t handle );
  554. virtual int GetNumCombinedSubModels( MDLHandle_t handle );
  555. virtual void GetCombinedSubModelFilename( MDLHandle_t handle, int nSubModelIndex, char *pszResult, int nResultSize );
  556. virtual KeyValues *GetCombinedMaterialKV( MDLHandle_t handle, int nAtlasGroup = 0 );
  557. virtual void UpdateCombiner( );
  558. virtual void *GetCombinedInternalAsset( ECombinedAsset AssetType, const char *pszAssetID, int *nSize );
  559. virtual void SetCombinerFlags( unsigned nFlags );
  560. virtual void ClearCombinerFlags( unsigned nFlags );
  561. virtual void DebugCombinerInfo( );
  562. virtual bool ReleaseAnimBlockAllocator();
  563. virtual bool RestoreHardwareData( MDLHandle_t handle, FSAsyncControl_t *pAsyncVTXControl, FSAsyncControl_t *pAsyncVVDControl );
  564. virtual bool ProcessPendingHardwareRestore();
  565. void OnAsyncHardwareDataComplete( ModelParts_t::BufferType_t bufferType, ModelParts_t *pContext, void *pData, int nNumReadBytes, FSAsyncStatus_t asyncStatus );
  566. virtual void DumpDictionaryState();
  567. private:
  568. // Inits, shuts downs studiodata_t
  569. void InitStudioData( MDLHandle_t handle );
  570. void ShutdownStudioData( MDLHandle_t handle, bool bImmediate );
  571. // Returns the *actual* name of the model (could be an error model if the requested model didn't load)
  572. const char *GetActualModelName( MDLHandle_t handle );
  573. // Attempts to load a MDL file, validates that it's ok.
  574. bool ReadMDLFile( MDLHandle_t handle, const char *pMDLFileName, CMDLCacheData &cacheData );
  575. void FlushImmediate( studiodata_t *pStudioData, MDLCacheFlush_t nFlushFlags = MDLCACHE_FLUSH_ALL );
  576. void Flush( studiodata_t *pStudioData, MDLCacheFlush_t nFlushFlags = MDLCACHE_FLUSH_ALL );
  577. // Unserializes the VCollide file associated w/ models (the vphysics representation)
  578. void UnserializeVCollide( MDLHandle_t handle, bool bUseAsync, bool synchronousLoad );
  579. void LoadPhysics2Collision( MDLHandle_t handle, bool synchronousLoad );
  580. // Destroys the VCollide associated w/ models
  581. void DestroyVCollide( studiodata_t *pStudioData );
  582. // Unserializes the MDL
  583. studiohdr_t *UnserializeMDL( MDLHandle_t handle, CMDLCacheData &cacheData );
  584. // Unserializes an animation block from disk
  585. unsigned char *UnserializeAnimBlock( MDLHandle_t handle, bool bUseAsync, int nBlock );
  586. // Allocates/frees the anim blocks
  587. void AllocateAnimBlocks( studiodata_t *pStudioData, int nCount );
  588. void FreeAnimBlocks( studiodata_t *pStudioData );
  589. // Allocates/frees the virtual model
  590. void AllocateVirtualModel( MDLHandle_t handle );
  591. void FreeVirtualModel( studiodata_t *pStudioData );
  592. // Purpose: Pulls all submodels/.ani file models into the cache
  593. void UnserializeAllVirtualModelsAndAnimBlocks( MDLHandle_t handle );
  594. // Loads/unloads the static meshes
  595. bool UnserializeHardwareData( MDLHandle_t handle, bool bUseAsync ); // returns false if not ready
  596. void UnloadHardwareData( studiodata_t *pStudioData );
  597. // Allocates/frees autoplay sequence list
  598. void AllocateAutoplaySequences( studiodata_t *pStudioData, int nCount );
  599. void FreeAutoplaySequences( studiodata_t *pStudioData );
  600. FSAsyncStatus_t LoadData( const char *pszFilename, const char *pszPathID, bool bAsync, FSAsyncControl_t *pControl, MDLHandle_t hModel ) { return LoadData( pszFilename, pszPathID, NULL, 0, 0, bAsync, pControl, hModel ); }
  601. FSAsyncStatus_t LoadData( const char *pszFilename, const char *pszPathID, void *pDest, int nBytes, int nOffset, bool bAsync, FSAsyncControl_t *pControl, MDLHandle_t hModel );
  602. vertexFileHeader_t *LoadVertexData( studiohdr_t *pStudioHdr );
  603. vertexFileHeader_t *BuildAndCacheVertexData( studiohdr_t *pStudioHdr, CMDLCacheData &cacheData );
  604. bool BuildHardwareData( MDLHandle_t handle, studiodata_t *pStudioData, studiohdr_t *pStudioHdr, CMDLCacheData &cacheData );
  605. void ConvertFlexData( studiohdr_t *pStudioHdr );
  606. int ProcessPendingAsync( intp iAsync );
  607. void ProcessPendingAsyncs( MDLCacheDataType_t type = MDLCACHE_NONE );
  608. bool ClearAsync( MDLHandle_t handle, MDLCacheDataType_t type, int iAnimBlock, bool bAbort = false );
  609. const char *GetVTXExtension();
  610. virtual bool HandleCacheNotification( const DataCacheNotification_t &notification );
  611. virtual bool GetItemName( DataCacheClientID_t clientId, const void *pItem, char *pDest, unsigned nMaxLen );
  612. virtual bool GetAsyncLoad( MDLCacheDataType_t type );
  613. virtual bool SetAsyncLoad( MDLCacheDataType_t type, bool bAsync );
  614. // Creates the 360 file if it doesn't exist or is out of date
  615. int UpdateOrCreate( studiohdr_t *pHdr, const char *pFilename, char *pX360Filename, int maxLen, const char *pPathID, bool bForce = false );
  616. // Attempts to read the platform native file - on 360 it can read and swap Win32 file as a fallback
  617. bool ReadFileNative( char *pFileName, const char *pPath, CUtlBuffer &buf, int nMaxBytes = 0 );
  618. // Creates a thin cache entry (to be used for model decals) from fat vertex data
  619. vertexFileHeader_t * CreateThinVertexes( vertexFileHeader_t * originalData, const studiohdr_t * pStudioHdr, int * cacheLength );
  620. // Creates a null cache entry (showing that vertex data has been loaded, turned into VBs/IBs, and discarded)
  621. vertexFileHeader_t * CreateNullVertexes( vertexFileHeader_t * originalData, const studiohdr_t * pStudioHdr, int * cacheLength );
  622. // Processes raw data (from an I/O source) into the cache. Sets the cache state as expected for bad data.
  623. bool ProcessDataIntoCache( MDLHandle_t handle, CMDLCacheData &cacheData, int iAnimBlock = 0 );
  624. void BreakFrameLock( bool bModels = true, bool bMesh = true, bool bAnimBlock = true );
  625. void RestoreFrameLock();
  626. void ReloadVCollide( MDLHandle_t handle );
  627. virtual void DisableVCollideLoad( void ) {m_bDisableVCollideLoad = true;}
  628. virtual void EnableVCollideLoad( void ) {m_bDisableVCollideLoad = false;}
  629. virtual void DisableFileNotFoundWarnings( void ) {m_bFileNotFoundAllowed = true;}
  630. virtual void EnableFileNotFoundWarnings( void ) {m_bFileNotFoundAllowed = false;}
  631. // combined models
  632. TCombinedStudioData *GetCombinedData( MDLHandle_t handle );
  633. void CheckCombinerFlagChanges( int nNewFlags );
  634. void InitCombiner( );
  635. void ShutdownCombiner( );
  636. void CombinerThread( );
  637. static uintp StaticCombinerThread( void *pParam );
  638. bool UnserializeCombinedHardwareData( MDLHandle_t handle );
  639. void FreeCombinedGeneratedData( studiodata_t *pStudioData );
  640. private:
  641. IDataCacheSection *m_pModelCacheSection;
  642. IDataCacheSection *m_pMeshCacheSection;
  643. IDataCacheSection *m_pAnimBlocksCacheSection;
  644. int m_nModelCacheFrameLocks;
  645. int m_nMeshCacheFrameLocks;
  646. int m_nAnimBlockCacheFrameLocks;
  647. CUtlDict< studiodata_t*, MDLHandle_t > m_MDLDict;
  648. IMDLCacheNotify *m_pCacheNotify;
  649. CUtlFixedLinkedList< AsyncInfo_t > m_PendingAsyncs;
  650. CThreadFastMutex m_QueuedLoadingMutex;
  651. CThreadFastMutex m_AsyncMutex;
  652. CTSQueue< studiodata_t * > m_UnloadHandles;
  653. // combined functionality
  654. CTSQueue< TCombinedStudioData * > m_CombinerToBeCombined;
  655. bool m_bCombinerReady;
  656. unsigned m_nCombinerFlags;
  657. volatile bool m_bCombinerShutdown;
  658. ThreadHandle_t m_hCombinerThread;
  659. CThreadEvent m_CombinerEvent;
  660. CThreadEvent m_CombinerShutdownEvent;
  661. CInterlockedPtr< TCombinedStudioData > m_pToBeCombined;
  662. CInterlockedPtr< TCombinedStudioData > m_pCombinedCompleted;
  663. bool m_bLostVideoMemory : 1;
  664. bool m_bConnected : 1;
  665. bool m_bInitialized : 1;
  666. bool m_bDisableVCollideLoad : 1;
  667. bool m_bFileNotFoundAllowed : 1;
  668. CModelSwapper m_ModelSwapper;
  669. CTSQueue< AsyncHardwareLoad_t > m_QueuedAsyncHardwareLoads;
  670. friend class CMDLCacheData; // Needs to access ReadFileNative
  671. };
  672. //-----------------------------------------------------------------------------
  673. // Singleton interface
  674. //-----------------------------------------------------------------------------
  675. static CMDLCache g_MDLCache;
  676. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CMDLCache, IMDLCache, MDLCACHE_INTERFACE_VERSION, g_MDLCache );
  677. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CMDLCache, IStudioDataCache, STUDIO_DATA_CACHE_INTERFACE_VERSION, g_MDLCache );
  678. //-----------------------------------------------------------------------------
  679. // Task switch
  680. //-----------------------------------------------------------------------------
  681. static void ReleaseMaterialSystemObjects( int nChangeFlags )
  682. {
  683. g_MDLCache.ReleaseMaterialSystemObjects( nChangeFlags );
  684. }
  685. static void RestoreMaterialSystemObjects( int nChangeFlags )
  686. {
  687. g_MDLCache.RestoreMaterialSystemObjects( nChangeFlags );
  688. }
  689. static void CleanupMaterialSystemObjects( )
  690. {
  691. // g_MDLCache.UpdateCombiner();
  692. g_MDLCache.UnloadQueuedHardwareData();
  693. }
  694. //-----------------------------------------------------------------------------
  695. // CMDLCacheData manages data being processed into the cache:
  696. // - it handles LZMA decompression (minimizing concurrent memory allocations)
  697. // - it makes sure all original+intermediate data get allocated/freed in the appropriate fashion
  698. //-----------------------------------------------------------------------------
  699. class CMDLCacheData
  700. {
  701. public:
  702. enum AllocType_t // Specifies how
  703. {
  704. ALLOC_MALLOC = 0, // The input data (and decompressed data) are allocated via malloc
  705. ALLOC_OPTIMALREADBUFFER = 1, // The input buffer uses external memory allocated via g_pFullFileSystem->AllocOptimalReadBuffer
  706. ALLOC_ANIMBLOCK = 2, // The input buffer uses external memory allocated via g_AnimBlockAllocator.Alloc()
  707. };
  708. // NOTE: On construction, the given CUtlBuffer has its memory detached, so the CMDLCacheData now owns it.
  709. CMDLCacheData( MDLCacheDataType_t dataType, AllocType_t allocType, CUtlBuffer *pDataBuffer = NULL )
  710. : m_DataType( dataType ), m_AllocType( allocType ), m_pData( NULL ), m_nDataSize( 0 )
  711. {
  712. Assert( ( m_AllocType != ALLOC_MALLOC ) == ( pDataBuffer ? pDataBuffer->IsExternallyAllocated() : false ) );
  713. if ( pDataBuffer && pDataBuffer->TellMaxPut() )
  714. {
  715. m_pData = pDataBuffer->Base();
  716. m_nDataSize = pDataBuffer->TellMaxPut();
  717. if ( pDataBuffer->IsExternallyAllocated() )
  718. pDataBuffer->SetExternalBuffer( NULL, 0, 0 );
  719. else
  720. pDataBuffer->Detach();
  721. Decompress();
  722. }
  723. }
  724. ~CMDLCacheData() { Purge(); }
  725. // Get the data (if the data is invalid or absent, this will return NULL)
  726. void *Data( void ) { return m_pData; }
  727. // Get the size of the data (if the data is invalid, this will return zero)
  728. int DataSize( void ) { return m_nDataSize; }
  729. MDLCacheDataType_t DataType( void ) { return m_DataType; }
  730. // The caller may ask to discard the data (i.e. don't use it)
  731. void Purge( void )
  732. {
  733. if ( m_pData )
  734. {
  735. Deallocate( m_pData );
  736. m_pData = NULL;
  737. m_nDataSize = 0;
  738. }
  739. }
  740. // Transfer ownership of the memory to the caller
  741. void *Detach( void )
  742. {
  743. if ( !m_pData )
  744. {
  745. // Paranoid usage check
  746. Warning( "ERROR: CMDLCacheData::Detach used incorrectly (there is no data to return!)\n" );
  747. Assert( 0 );
  748. return NULL;
  749. }
  750. void *pResult = m_pData;
  751. m_pData = NULL;
  752. m_nDataSize = 0;
  753. return pResult;
  754. }
  755. // Read a file into the CMDLCacheData's internal buffer (replaces any existing data)
  756. bool ReadFileNative( char *pFileName, const char *pPath )
  757. {
  758. // Clear out any existing data
  759. Purge();
  760. // Read a file into memory
  761. bool bSuccess = false;
  762. if ( m_AllocType == ALLOC_MALLOC )
  763. {
  764. CUtlBuffer buf;
  765. bSuccess = g_MDLCache.ReadFileNative( pFileName, pPath, buf );
  766. if ( bSuccess )
  767. {
  768. if ( m_DataType == MDLCACHE_STUDIOHDR )
  769. {
  770. studiohdr_t* pStudioHdr = ( studiohdr_t* ) buf.Base();
  771. if ( pStudioHdr->studiohdr2index == 0 )
  772. {
  773. // We always need this now, so make room for it in the buffer now.
  774. int bufferContentsEnd = buf.TellMaxPut();
  775. int maskBits = VALIGNOF( studiohdr2_t ) - 1;
  776. int offsetStudiohdr2 = ( bufferContentsEnd + maskBits ) & ~maskBits;
  777. int sizeIncrease = ( offsetStudiohdr2 - bufferContentsEnd ) + sizeof( studiohdr2_t );
  778. buf.SeekPut( CUtlBuffer::SEEK_CURRENT, sizeIncrease );
  779. // Re-get the pointer after resizing, because it has probably moved.
  780. pStudioHdr = ( studiohdr_t* ) buf.Base();
  781. studiohdr2_t* pStudioHdr2 = ( studiohdr2_t* ) ( ( byte * ) pStudioHdr + offsetStudiohdr2 );
  782. memset( pStudioHdr2, 0, sizeof( studiohdr2_t ) );
  783. pStudioHdr2->flMaxEyeDeflection = 0.866f; // Matches studio.h.
  784. pStudioHdr->studiohdr2index = offsetStudiohdr2;
  785. // Also make sure the structure knows about the extra bytes
  786. // we've added so they get copied around.
  787. pStudioHdr->length += sizeIncrease;
  788. }
  789. }
  790. m_nDataSize = buf.TellMaxPut();
  791. m_pData = buf.Detach();
  792. Decompress();
  793. }
  794. }
  795. else
  796. {
  797. // We don't support this pattern (if we need to, we could pass an alloc callback to g_pFullFileSystem->ReadFile)
  798. Warning( "ERROR: CMDLCacheData::ReadFileNative is only supported when using ALLOC_MALLOC\n" );
  799. Assert( 0 );
  800. }
  801. return bSuccess;
  802. }
  803. private:
  804. void *Allocate( int nDataSize )
  805. {
  806. switch( m_AllocType )
  807. {
  808. case ALLOC_MALLOC:
  809. return malloc( nDataSize );
  810. case ALLOC_OPTIMALREADBUFFER:
  811. return g_pFullFileSystem->AllocOptimalReadBuffer( FILESYSTEM_INVALID_HANDLE, nDataSize );
  812. case ALLOC_ANIMBLOCK:
  813. if ( nDataSize <= ANIMBLOCK_SIZE )
  814. {
  815. return g_AnimBlockAllocator.Alloc();
  816. }
  817. else
  818. {
  819. Warning( "%s(%d): MDL Cache allocation outside the pool. Size allocated: %d.\n", __FILE__, __LINE__, nDataSize );
  820. // If an animblock was compressed, its decompressed size could exceed ANIMBLOCK_SIZE,
  821. // so deal with that the same manner as CMDLCache::UnserializeAnimBlock():
  822. m_AllocType = ALLOC_OPTIMALREADBUFFER;
  823. return Allocate( nDataSize );
  824. }
  825. }
  826. Assert(0);
  827. return NULL;
  828. }
  829. void Deallocate( void *pData )
  830. {
  831. switch( m_AllocType )
  832. {
  833. case ALLOC_MALLOC:
  834. return free( pData );
  835. case ALLOC_OPTIMALREADBUFFER:
  836. return g_pFullFileSystem->FreeOptimalReadBuffer( pData );
  837. case ALLOC_ANIMBLOCK:
  838. return FreeAnimBlock( pData ); // NOTE: this handles large animblocks allocated by the caller via AllocOptimalReadBuffer
  839. }
  840. Assert(0);
  841. }
  842. bool Decompress( void )
  843. {
  844. CLZMA lzma;
  845. // Trivial early-outs - make sure we have valid data, and are on a game console (no LZMA on PC):
  846. if ( !IsGameConsole() || !m_pData )
  847. return true;
  848. // Some asset types have an uncompressed header before the compressed data starts:
  849. int nHeaderSize = 0;
  850. if ( m_DataType == MDLCACHE_VERTEXES )
  851. nHeaderSize = sizeof( vertexFileHeader_t );
  852. if ( m_DataType == MDLCACHE_STUDIOHWDATA )
  853. nHeaderSize = sizeof( OptimizedModel::FileHeader_t );
  854. // Check that the data is actually compressed (only happens for game consoles)
  855. if ( !lzma.IsCompressed( nHeaderSize + (unsigned char *)m_pData ) )
  856. return true;
  857. // Allocate a new buffer for the uncompressed data
  858. unsigned int nUncompressedSize = lzma.GetActualSize( nHeaderSize + (unsigned char *)m_pData );
  859. void * pUncompressedData = Allocate( nUncompressedSize + nHeaderSize );
  860. // Copy the uncompressed header verbatim
  861. memcpy( pUncompressedData, m_pData, nHeaderSize );
  862. // Decompress the rest
  863. if ( lzma.Uncompress( nHeaderSize + (unsigned char *)m_pData, nHeaderSize + (unsigned char *)pUncompressedData ) != nUncompressedSize )
  864. {
  865. // Decompression failed!
  866. Msg( "ERROR: LZMA decompression failed - corrupt data!!\n" );
  867. Deallocate( pUncompressedData );
  868. Purge();
  869. return false;
  870. }
  871. // Free the original memory (to minimize concurrent memory allocations - important on game consoles!)
  872. Deallocate( m_pData );
  873. // Expose the decompressed data to the user from now on
  874. m_pData = pUncompressedData;
  875. m_nDataSize = nUncompressedSize + nHeaderSize;
  876. return true;
  877. }
  878. void *m_pData; // The original source data, replaced with decompressed data by 'Decompress'
  879. int m_nDataSize; // Size of the data, updated by 'Decompress'
  880. MDLCacheDataType_t m_DataType; // The type of the data (determines how memory is decompressed)
  881. AllocType_t m_AllocType; // Determines how data is [de]allocated.
  882. };
  883. //-----------------------------------------------------------------------------
  884. // Constructor
  885. //-----------------------------------------------------------------------------
  886. CMDLCache::CMDLCache()
  887. {
  888. m_bLostVideoMemory = false;
  889. m_bConnected = false;
  890. m_bInitialized = false;
  891. m_bDisableVCollideLoad = false;
  892. m_bFileNotFoundAllowed = false;
  893. m_pCacheNotify = NULL;
  894. m_pModelCacheSection = NULL;
  895. m_pMeshCacheSection = NULL;
  896. m_pAnimBlocksCacheSection = NULL;
  897. m_nModelCacheFrameLocks = 0;
  898. m_nMeshCacheFrameLocks = 0;
  899. m_nAnimBlockCacheFrameLocks = 0;
  900. m_bCombinerReady = false;
  901. m_nCombinerFlags = COMBINER_FLAG_THREADING;
  902. m_bCombinerShutdown = false;
  903. m_hCombinerThread = NULL;
  904. m_pToBeCombined = NULL;
  905. m_pCombinedCompleted = NULL;
  906. m_CombinerEvent.Reset();
  907. m_CombinerShutdownEvent.Reset();
  908. }
  909. //-----------------------------------------------------------------------------
  910. // Connect, disconnect
  911. //-----------------------------------------------------------------------------
  912. bool CMDLCache::Connect( CreateInterfaceFn factory )
  913. {
  914. if ( !BaseClass::Connect( factory ) )
  915. return false;
  916. // Connect can be called twice, because this inherits from 2 appsystems.
  917. if ( m_bConnected )
  918. return true;
  919. physprops = (IPhysicsSurfaceProps *)factory( VPHYSICS_SURFACEPROPS_INTERFACE_VERSION, NULL );
  920. //if ( !physprops )
  921. // return false;
  922. //if ( !g_pMaterialSystemHardwareConfig || !g_pPhysicsCollision || !g_pStudioRender || !g_pMaterialSystem )
  923. // return false;
  924. m_bConnected = true;
  925. if( g_pMaterialSystem )
  926. {
  927. g_pMaterialSystem->AddReleaseFunc( ::ReleaseMaterialSystemObjects );
  928. g_pMaterialSystem->AddRestoreFunc( ::RestoreMaterialSystemObjects );
  929. #ifdef PLATFORM_WINDOWS_PC
  930. g_pMaterialSystem->AddEndFrameCleanupFunc( ::CleanupMaterialSystemObjects );
  931. #endif
  932. }
  933. return true;
  934. }
  935. void CMDLCache::Disconnect()
  936. {
  937. if ( g_pMaterialSystem && m_bConnected )
  938. {
  939. g_pMaterialSystem->RemoveReleaseFunc( ::ReleaseMaterialSystemObjects );
  940. g_pMaterialSystem->RemoveRestoreFunc( ::RestoreMaterialSystemObjects );
  941. #ifdef PLATFORM_WINDOWS_PC
  942. g_pMaterialSystem->RemoveEndFrameCleanupFunc( ::CleanupMaterialSystemObjects );
  943. #endif
  944. ShutdownCombiner();
  945. m_bConnected = false;
  946. }
  947. BaseClass::Disconnect();
  948. }
  949. //-----------------------------------------------------------------------------
  950. // Query Interface
  951. //-----------------------------------------------------------------------------
  952. void *CMDLCache::QueryInterface( const char *pInterfaceName )
  953. {
  954. if (!Q_strncmp( pInterfaceName, STUDIO_DATA_CACHE_INTERFACE_VERSION, Q_strlen(STUDIO_DATA_CACHE_INTERFACE_VERSION) + 1))
  955. return (IStudioDataCache*)this;
  956. if (!Q_strncmp( pInterfaceName, MDLCACHE_INTERFACE_VERSION, Q_strlen(MDLCACHE_INTERFACE_VERSION) + 1))
  957. return (IMDLCache*)this;
  958. return NULL;
  959. }
  960. //-----------------------------------------------------------------------------
  961. // Init/Shutdown
  962. //-----------------------------------------------------------------------------
  963. #define MODEL_CACHE_MODEL_SECTION_NAME "ModelData"
  964. #define MODEL_CACHE_MESH_SECTION_NAME "ModelMesh"
  965. #define MODEL_CACHE_ANIMBLOCK_SECTION_NAME "AnimBlock"
  966. // #define ENABLE_CACHE_WATCH 1
  967. #if defined( ENABLE_CACHE_WATCH )
  968. static ConVar cache_watch( "cache_watch", "", 0 );
  969. static void CacheLog( const char *fileName, const char *accessType )
  970. {
  971. if ( Q_stristr( fileName, cache_watch.GetString() ) )
  972. {
  973. Msg( "%s access to %s\n", accessType, fileName );
  974. }
  975. }
  976. #endif
  977. InitReturnVal_t CMDLCache::Init()
  978. {
  979. // Can be called twice since it inherits from 2 appsystems
  980. if ( m_bInitialized )
  981. return INIT_OK;
  982. InitReturnVal_t nRetVal = BaseClass::Init();
  983. if ( nRetVal != INIT_OK )
  984. return nRetVal;
  985. if ( !m_pModelCacheSection )
  986. {
  987. m_pModelCacheSection = g_pDataCache->AddSection( this, MODEL_CACHE_MODEL_SECTION_NAME );
  988. }
  989. if ( !m_pMeshCacheSection )
  990. {
  991. unsigned int meshLimit = (unsigned)-1;
  992. DataCacheLimits_t limits( meshLimit, (unsigned)-1, 0, 0 );
  993. m_pMeshCacheSection = g_pDataCache->AddSection( this, MODEL_CACHE_MESH_SECTION_NAME, limits );
  994. // model meshes do not participate in LRU pruge due to -1 max bytes limit
  995. // not allowing console mem_force_flush to unexpectedly flush, which would otherwise destabilize model meshes
  996. m_pMeshCacheSection->SetOptions( m_pMeshCacheSection->GetOptions() | DC_NO_USER_FORCE_FLUSH );
  997. }
  998. if ( !m_pAnimBlocksCacheSection )
  999. {
  1000. unsigned int animBlockLimit = (unsigned)-1;
  1001. if ( IsGameConsole() )
  1002. {
  1003. // consoles limit the anim cache, tuned to worst case
  1004. // Use the amount of memory allocated by g_AnimBlockAllocator
  1005. animBlockLimit = ANIMBLOCK_SIZE*MAX_ANIMBLOCKS;
  1006. }
  1007. DataCacheLimits_t limits( animBlockLimit, (unsigned)-1, 0, 0 );
  1008. m_pAnimBlocksCacheSection = g_pDataCache->AddSection( this, MODEL_CACHE_ANIMBLOCK_SECTION_NAME, limits );
  1009. }
  1010. if ( IsGameConsole() )
  1011. {
  1012. // By default, source data is assumed to be non-native to the 360.
  1013. StudioByteSwap::ActivateByteSwapping( true );
  1014. StudioByteSwap::SetCollisionInterface( g_pPhysicsCollision );
  1015. }
  1016. m_bLostVideoMemory = false;
  1017. m_bInitialized = true;
  1018. #if defined( ENABLE_CACHE_WATCH )
  1019. g_pFullFileSystem->AddLoggingFunc( &CacheLog );
  1020. #endif
  1021. if ( IsPC() )
  1022. {
  1023. //UNDONE: This opens up a whole fun realm of cheating for multiplayer games!
  1024. //m_ModelSwapper.LoadSubstitutionFile( MODEL_SUBSTITUTION_FILENAME );
  1025. }
  1026. return INIT_OK;
  1027. }
  1028. void CMDLCache::Shutdown()
  1029. {
  1030. if ( !m_bInitialized )
  1031. return;
  1032. #if defined( ENABLE_CACHE_WATCH )
  1033. g_pFullFileSystem->RemoveLoggingFunc( CacheLog );
  1034. #endif
  1035. m_bInitialized = false;
  1036. if ( m_pModelCacheSection || m_pMeshCacheSection )
  1037. {
  1038. // Free all MDLs that haven't been cleaned up
  1039. MDLHandle_t i = m_MDLDict.First();
  1040. while ( i != m_MDLDict.InvalidIndex() )
  1041. {
  1042. ShutdownStudioData( i, true );
  1043. i = m_MDLDict.Next( i );
  1044. }
  1045. m_MDLDict.Purge();
  1046. if ( m_pModelCacheSection )
  1047. {
  1048. g_pDataCache->RemoveSection( MODEL_CACHE_MODEL_SECTION_NAME );
  1049. m_pModelCacheSection = NULL;
  1050. }
  1051. if ( m_pMeshCacheSection )
  1052. {
  1053. g_pDataCache->RemoveSection( MODEL_CACHE_MESH_SECTION_NAME );
  1054. m_pMeshCacheSection = NULL;
  1055. }
  1056. }
  1057. if ( m_pAnimBlocksCacheSection )
  1058. {
  1059. g_pDataCache->RemoveSection( MODEL_CACHE_ANIMBLOCK_SECTION_NAME );
  1060. m_pAnimBlocksCacheSection = NULL;
  1061. }
  1062. BaseClass::Shutdown();
  1063. }
  1064. void CMDLCache::FlushImmediate( studiodata_t *pStudioData, MDLCacheFlush_t nFlushFlags )
  1065. {
  1066. if ( nFlushFlags & MDLCACHE_FLUSH_STUDIOHWDATA )
  1067. {
  1068. if ( ClearAsync( pStudioData->m_Handle, MDLCACHE_STUDIOHWDATA, 0, true ) )
  1069. {
  1070. m_pMeshCacheSection->Unlock( pStudioData->m_VertexCache );
  1071. }
  1072. }
  1073. if ( nFlushFlags & MDLCACHE_FLUSH_VERTEXES )
  1074. {
  1075. ClearAsync( pStudioData->m_Handle, MDLCACHE_VERTEXES, 0, true );
  1076. }
  1077. }
  1078. void CMDLCache::Flush( studiodata_t *pStudioData, MDLCacheFlush_t nFlushFlags )
  1079. {
  1080. Assert( pStudioData != NULL );
  1081. bool bIgnoreLock = ( nFlushFlags & MDLCACHE_FLUSH_IGNORELOCK ) != 0;
  1082. // release the hardware portion
  1083. if ( nFlushFlags & MDLCACHE_FLUSH_STUDIOHWDATA )
  1084. {
  1085. UnloadHardwareData( pStudioData );
  1086. }
  1087. // free collision
  1088. if ( nFlushFlags & MDLCACHE_FLUSH_VCOLLIDE )
  1089. {
  1090. DestroyVCollide( pStudioData );
  1091. }
  1092. // Free animations
  1093. if ( nFlushFlags & MDLCACHE_FLUSH_VIRTUALMODEL )
  1094. {
  1095. FreeVirtualModel( pStudioData );
  1096. }
  1097. if ( nFlushFlags & MDLCACHE_FLUSH_ANIMBLOCK )
  1098. {
  1099. FreeAnimBlocks( pStudioData );
  1100. }
  1101. if ( nFlushFlags & MDLCACHE_FLUSH_AUTOPLAY )
  1102. {
  1103. // Free autoplay sequences
  1104. FreeAutoplaySequences( pStudioData );
  1105. }
  1106. if ( nFlushFlags & MDLCACHE_FLUSH_COMBINED_DATA )
  1107. {
  1108. #ifdef DEBUG_COMBINER
  1109. if ( ( pStudioData->m_nFlags & STUDIODATA_FLAGS_COMBINED_ASSET ) != 0 )
  1110. {
  1111. Msg( "%p Flush: pStudioData=%p Flags=%08x", pStudioData->m_pCombinedStudioData, pStudioData, pStudioData->m_nFlags );
  1112. if ( pStudioData->m_pCombinedStudioData )
  1113. {
  1114. Msg( " Reference=%08x", pStudioData->m_pCombinedStudioData->m_nReferenceFlags );
  1115. }
  1116. Msg( "\n" );
  1117. }
  1118. #endif
  1119. if ( pStudioData->m_pCombinedStudioData != NULL )
  1120. {
  1121. if ( ( pStudioData->m_nFlags & STUDIODATA_FLAGS_COMBINED_PLACEHOLDER ) != 0 )
  1122. {
  1123. pStudioData->m_pCombinedStudioData->m_nReferenceFlags &= ~COMBINED_REFERENCE_PLACEHOLDER;
  1124. }
  1125. if ( ( pStudioData->m_nFlags & STUDIODATA_FLAGS_COMBINED ) != 0 )
  1126. {
  1127. pStudioData->m_pCombinedStudioData->m_nReferenceFlags &= ~COMBINED_REFERENCE_PRIMARY;
  1128. }
  1129. if ( pStudioData->m_pCombinedStudioData->m_nReferenceFlags == 0 )
  1130. {
  1131. #if 0
  1132. if ( pStudioData->m_pCombinedStudioData->m_pCombineData != NULL )
  1133. {
  1134. // Assert( 0 ); // is this currently in flight in the combiner thread?
  1135. delete pStudioData->m_pCombinedStudioData->m_pCombineData;
  1136. }
  1137. #endif
  1138. #ifdef DEBUG_COMBINER
  1139. Msg( "%p Free: pStudioData=%p\n", pStudioData->m_pCombinedStudioData, pStudioData );
  1140. #endif
  1141. FreeCombinedGeneratedData( pStudioData );
  1142. free( pStudioData->m_pCombinedStudioData );
  1143. }
  1144. #ifdef DEBUG_COMBINER
  1145. else if ( pStudioData->m_pCombinedStudioData->m_nReferenceFlags == COMBINED_REFERENCE_COMBINER )
  1146. {
  1147. Msg( "%p Combiner Reference: pStudioData=%p\n", pStudioData->m_pCombinedStudioData, pStudioData );
  1148. Assert( 0 );
  1149. // Error( "CMDLCache::UpdateCombiner - freeing model handles before combiner finishes" );
  1150. }
  1151. #endif
  1152. pStudioData->m_pCombinedStudioData = NULL;
  1153. }
  1154. #ifdef DEBUG_COMBINER
  1155. else if ( ( pStudioData->m_nFlags & STUDIODATA_FLAGS_COMBINED_ASSET ) != 0 )
  1156. {
  1157. Msg( "%p Nothing to Free: pStudioData=%p\n", pStudioData->m_pCombinedStudioData, pStudioData );
  1158. Assert( 0 );
  1159. }
  1160. #endif
  1161. }
  1162. if ( nFlushFlags & MDLCACHE_FLUSH_STUDIOHDR )
  1163. {
  1164. MdlCacheMsg( "MDLCache: Free studiohdr %s\n", GetModelName( pStudioData->m_Handle ) );
  1165. if ( pStudioData->m_pForceLockedStudioHdr )
  1166. {
  1167. GetCacheSection( MDLCACHE_STUDIOHDR )->Unlock( pStudioData->m_MDLCache );
  1168. pStudioData->m_pForceLockedStudioHdr = NULL;
  1169. }
  1170. UncacheData( pStudioData->m_MDLCache, MDLCACHE_STUDIOHDR, bIgnoreLock );
  1171. pStudioData->m_MDLCache = NULL;
  1172. }
  1173. if ( nFlushFlags & MDLCACHE_FLUSH_VERTEXES )
  1174. {
  1175. MdlCacheMsg( "MDLCache: Free VVD %s\n", GetModelName( pStudioData->m_Handle ) );
  1176. if ( pStudioData->m_pForceLockedVertexFileHeader )
  1177. {
  1178. GetCacheSection( MDLCACHE_VERTEXES )->Unlock( pStudioData->m_VertexCache );
  1179. pStudioData->m_pForceLockedVertexFileHeader = NULL;
  1180. }
  1181. ClearAsync( pStudioData->m_Handle, MDLCACHE_VERTEXES, 0, true );
  1182. UncacheData( pStudioData->m_VertexCache, MDLCACHE_VERTEXES, bIgnoreLock );
  1183. pStudioData->m_VertexCache = NULL;
  1184. }
  1185. }
  1186. //-----------------------------------------------------------------------------
  1187. // Flushes an MDLHandle_t
  1188. //-----------------------------------------------------------------------------
  1189. void CMDLCache::Flush( MDLHandle_t handle, int nFlushFlags )
  1190. {
  1191. studiodata_t *pStudioData = m_MDLDict[handle];
  1192. Flush( pStudioData, ( MDLCacheFlush_t )nFlushFlags );
  1193. }
  1194. //-----------------------------------------------------------------------------
  1195. // Inits, shuts downs studiodata_t
  1196. //-----------------------------------------------------------------------------
  1197. void CMDLCache::InitStudioData( MDLHandle_t handle )
  1198. {
  1199. Assert( m_MDLDict[handle] == NULL );
  1200. studiodata_t *pStudioData = new studiodata_t;
  1201. m_MDLDict[handle] = pStudioData;
  1202. memset( pStudioData, 0, sizeof( studiodata_t ) );
  1203. pStudioData->m_Handle = handle;
  1204. }
  1205. void CMDLCache::ShutdownStudioData( MDLHandle_t handle, bool bImmediate )
  1206. {
  1207. #ifdef PLATFORM_WINDOWS_PC
  1208. BeginLock();
  1209. if ( bImmediate == false )
  1210. {
  1211. #ifdef DEBUG_COMBINER
  1212. studiodata_t *pStudioData = m_MDLDict[handle];
  1213. if ( ( pStudioData->m_nFlags & ( STUDIODATA_FLAGS_COMBINED_ASSET ) ) == STUDIODATA_FLAGS_COMBINED_ASSET )
  1214. {
  1215. Msg( "%p ShutdownStudioData: pStudioData=%p\n", pStudioData->m_pCombinedStudioData, pStudioData );
  1216. }
  1217. #endif
  1218. m_UnloadHandles.PushItem( m_MDLDict[ handle ] );
  1219. FlushImmediate( m_MDLDict[ handle ] );
  1220. m_MDLDict[handle] = NULL;
  1221. }
  1222. else
  1223. {
  1224. FlushImmediate( m_MDLDict[ handle ] );
  1225. Flush( handle );
  1226. studiodata_t *pStudioData = m_MDLDict[handle];
  1227. Assert( pStudioData != NULL );
  1228. delete pStudioData;
  1229. m_MDLDict[handle] = NULL;
  1230. }
  1231. EndLock();
  1232. #else
  1233. FlushImmediate( m_MDLDict[ handle ] );
  1234. Flush( handle );
  1235. studiodata_t *pStudioData = m_MDLDict[handle];
  1236. Assert( pStudioData != NULL );
  1237. delete pStudioData;
  1238. m_MDLDict[handle] = NULL;
  1239. #endif // PLATFORM_WINDOWS
  1240. }
  1241. //-----------------------------------------------------------------------------
  1242. // Sets the cache notify
  1243. //-----------------------------------------------------------------------------
  1244. void CMDLCache::SetCacheNotify( IMDLCacheNotify *pNotify )
  1245. {
  1246. m_pCacheNotify = pNotify;
  1247. }
  1248. //-----------------------------------------------------------------------------
  1249. // Returns the name of the model
  1250. //-----------------------------------------------------------------------------
  1251. const char *CMDLCache::GetModelName( MDLHandle_t handle )
  1252. {
  1253. if ( handle == MDLHANDLE_INVALID || !m_MDLDict.IsValidIndex( handle ) )
  1254. return ERROR_MODEL;
  1255. return m_MDLDict.GetElementName( handle );
  1256. }
  1257. //-----------------------------------------------------------------------------
  1258. // Returns the *actual* name of the model (could be an error model)
  1259. //-----------------------------------------------------------------------------
  1260. const char *CMDLCache::GetActualModelName( MDLHandle_t handle )
  1261. {
  1262. if ( handle == MDLHANDLE_INVALID )
  1263. return ERROR_MODEL;
  1264. if ( m_MDLDict[handle]->m_nFlags & STUDIODATA_ERROR_MODEL )
  1265. return ERROR_MODEL;
  1266. return m_MDLDict.GetElementName( handle );
  1267. }
  1268. //-----------------------------------------------------------------------------
  1269. // Finds an MDL
  1270. //-----------------------------------------------------------------------------
  1271. MDLHandle_t CMDLCache::FindMDL( const char *pMDLRelativePath )
  1272. {
  1273. // can't trust provided path
  1274. // ensure provided path correctly resolves (Dictionary is case-insensitive)
  1275. char szFixedName[MAX_PATH];
  1276. V_strncpy( szFixedName, pMDLRelativePath, sizeof( szFixedName ) );
  1277. V_RemoveDotSlashes( szFixedName, '/' );
  1278. if ( g_pResourceAccessControl )
  1279. {
  1280. if ( !g_pResourceAccessControl->IsAccessAllowed( RESOURCE_MODEL, szFixedName ) )
  1281. {
  1282. Q_strncpy( szFixedName, ERROR_MODEL, sizeof(szFixedName) );
  1283. }
  1284. }
  1285. MDLHandle_t handle = m_MDLDict.Find( szFixedName );
  1286. if ( handle == m_MDLDict.InvalidIndex() )
  1287. {
  1288. handle = m_MDLDict.Insert( szFixedName, NULL );
  1289. InitStudioData( handle );
  1290. }
  1291. AddRef( handle );
  1292. return handle;
  1293. }
  1294. //-----------------------------------------------------------------------------
  1295. // Reference counting
  1296. //-----------------------------------------------------------------------------
  1297. int CMDLCache::AddRef( MDLHandle_t handle )
  1298. {
  1299. return ++m_MDLDict[handle]->m_nRefCount;
  1300. }
  1301. int CMDLCache::Release( MDLHandle_t handle )
  1302. {
  1303. // Deal with shutdown order issues (i.e. datamodel shutting down after mdlcache)
  1304. if ( !m_bInitialized )
  1305. return 0;
  1306. // NOTE: It can be null during shutdown because multiple studiomdls
  1307. // could be referencing the same virtual model
  1308. if ( !m_MDLDict[handle] )
  1309. return 0;
  1310. Assert( m_MDLDict[handle]->m_nRefCount > 0 );
  1311. int nRefCount = --m_MDLDict[handle]->m_nRefCount;
  1312. if ( nRefCount <= 0 )
  1313. {
  1314. ShutdownStudioData( handle, false );
  1315. m_MDLDict.RemoveAt( handle );
  1316. }
  1317. return nRefCount;
  1318. }
  1319. int CMDLCache::GetRef( MDLHandle_t handle )
  1320. {
  1321. if ( !m_bInitialized )
  1322. return 0;
  1323. if ( !m_MDLDict[handle] )
  1324. return 0;
  1325. return m_MDLDict[handle]->m_nRefCount;
  1326. }
  1327. //-----------------------------------------------------------------------------
  1328. // Unserializes the PHY file associated w/ models (the vphysics representation)
  1329. //-----------------------------------------------------------------------------
  1330. void CMDLCache::UnserializeVCollide( MDLHandle_t handle, bool bUseAsync, bool synchronousLoad )
  1331. {
  1332. VPROF( "CMDLCache::UnserializeVCollide" );
  1333. // FIXME: Should the vcollde be played into cacheable memory?
  1334. studiodata_t *pStudioData = m_MDLDict[handle];
  1335. intp iAsync = GetAsyncInfoIndex( handle, MDLCACHE_VCOLLIDE );
  1336. if ( iAsync == NO_ASYNC )
  1337. {
  1338. // clear existing data
  1339. pStudioData->m_nFlags &= ~STUDIODATA_FLAGS_VCOLLISION_LOADED;
  1340. Assert( pStudioData->m_pVCollide == NULL);
  1341. pStudioData->m_pVCollide = NULL;
  1342. #if 0
  1343. // FIXME: ywb
  1344. // If we don't ask for the virtual model to load, then we can get a hitch later on after startup
  1345. // Should we async load the sub .mdls during startup assuming they'll all be resident by the time the level can actually
  1346. // start drawing?
  1347. if ( pStudioData->m_pVirtualModel || synchronousLoad )
  1348. #endif
  1349. {
  1350. pStudioData->m_nFlags |= STUDIODATA_FLAGS_VCOLLISION_SCANNED;
  1351. virtualmodel_t *pVirtualModel = GetVirtualModel( handle );
  1352. if ( pVirtualModel )
  1353. {
  1354. for ( int i = 1; i < pVirtualModel->m_group.Count(); i++ )
  1355. {
  1356. MDLHandle_t sharedHandle = VoidPtrToMDLHandle( pVirtualModel->m_group[i].cache );
  1357. studiodata_t *pData = m_MDLDict[sharedHandle];
  1358. if ( !(pData->m_nFlags & STUDIODATA_FLAGS_VCOLLISION_LOADED) )
  1359. {
  1360. UnserializeVCollide( sharedHandle, bUseAsync, synchronousLoad );
  1361. }
  1362. if ( pData->m_pVCollide != NULL )
  1363. {
  1364. pData->m_pVCollide->AddRef();
  1365. pStudioData->m_pVCollide = pData->m_pVCollide;
  1366. pStudioData->m_nFlags |= STUDIODATA_FLAGS_VCOLLISION_LOADED;
  1367. return;
  1368. }
  1369. }
  1370. }
  1371. }
  1372. char pFileName[MAX_PATH];
  1373. Q_strncpy( pFileName, GetActualModelName( handle ), MAX_PATH );
  1374. Q_SetExtension( pFileName, ".phy", sizeof( pFileName ) );
  1375. Q_FixSlashes( pFileName );
  1376. #ifdef POSIX
  1377. Q_strlower( pFileName );
  1378. #endif
  1379. if ( IsGameConsole() )
  1380. {
  1381. char pX360Filename[MAX_PATH];
  1382. UpdateOrCreate( NULL, pFileName, pX360Filename, sizeof( pX360Filename ), "GAME" );
  1383. Q_strncpy( pFileName, pX360Filename, sizeof(pX360Filename) );
  1384. }
  1385. bool bAsyncLoad = bUseAsync && !synchronousLoad;
  1386. MdlCacheMsg( "MDLCache: %s load vcollide %s\n", bAsyncLoad ? "Async" : "Sync", GetModelName( handle ) );
  1387. AsyncInfo_t info;
  1388. if ( IsDebug() )
  1389. {
  1390. memset( &info, 0xdd, sizeof( AsyncInfo_t ) );
  1391. }
  1392. info.hModel = handle;
  1393. info.type = MDLCACHE_VCOLLIDE;
  1394. info.iAnimBlock = 0;
  1395. info.hControl = NULL;
  1396. LoadData( pFileName, "GAME", bAsyncLoad, &info.hControl, handle );
  1397. {
  1398. AUTO_LOCK_FM( m_AsyncMutex );
  1399. iAsync = SetAsyncInfoIndex( handle, MDLCACHE_VCOLLIDE, m_PendingAsyncs.AddToTail( info ) );
  1400. }
  1401. }
  1402. else if ( synchronousLoad )
  1403. {
  1404. AsyncInfo_t *pInfo;
  1405. {
  1406. AUTO_LOCK_FM( m_AsyncMutex );
  1407. pInfo = &m_PendingAsyncs[iAsync];
  1408. }
  1409. if ( pInfo->hControl )
  1410. {
  1411. g_pFullFileSystem->AsyncFinish( pInfo->hControl, true );
  1412. }
  1413. }
  1414. ProcessPendingAsync( iAsync );
  1415. }
  1416. //-----------------------------------------------------------------------------
  1417. // Free model's collision data
  1418. //-----------------------------------------------------------------------------
  1419. void CMDLCache::DestroyVCollide( studiodata_t *pStudioData )
  1420. {
  1421. if ( pStudioData->m_nFlags & STUDIODATA_FLAGS_VCOLLISION_LOADED )
  1422. {
  1423. pStudioData->m_nFlags &= ~STUDIODATA_FLAGS_VCOLLISION_LOADED;
  1424. if ( pStudioData->m_pVCollide )
  1425. {
  1426. if ( m_pCacheNotify )
  1427. {
  1428. m_pCacheNotify->OnDataUnloaded( MDLCACHE_VCOLLIDE, pStudioData->m_Handle );
  1429. }
  1430. MdlCacheMsg( "MDLCache: Unload vcollide %s\n", GetModelName( pStudioData->m_Handle ) );
  1431. pStudioData->m_pVCollide->Release();
  1432. pStudioData->m_pVCollide = NULL;
  1433. }
  1434. }
  1435. }
  1436. void CMDLCache::ReloadVCollide( MDLHandle_t handle )
  1437. {
  1438. studiodata_t *pStudioData = m_MDLDict[handle];
  1439. ExecuteNTimes(1, Warning( "ReloadVCollide invoked and will leak memory\n" ) );
  1440. pStudioData->m_nFlags &= ~( STUDIODATA_FLAGS_VCOLLISION_LOADED | STUDIODATA_FLAGS_VCOLLISION_SCANNED );
  1441. // this is where we leak the memory
  1442. pStudioData->m_pVCollide = NULL;
  1443. virtualmodel_t *pVirtualModel = GetVirtualModel( handle );
  1444. if ( pVirtualModel )
  1445. {
  1446. for ( int i = 1; i < pVirtualModel->m_group.Count(); i++ )
  1447. {
  1448. MDLHandle_t sharedHandle = VoidPtrToMDLHandle( pVirtualModel->m_group[i].cache );
  1449. ReloadVCollide( sharedHandle );
  1450. }
  1451. }
  1452. // now, reload
  1453. GetVCollideEx( handle, true );
  1454. }
  1455. //-----------------------------------------------------------------------------
  1456. // Unserializes the PHY file associated w/ models (the vphysics representation)
  1457. //-----------------------------------------------------------------------------
  1458. vcollide_t *CMDLCache::GetVCollideEx( MDLHandle_t handle, bool synchronousLoad /*= true*/ )
  1459. {
  1460. if ( mod_test_not_available.GetBool() )
  1461. return NULL;
  1462. if ( handle == MDLHANDLE_INVALID )
  1463. return NULL;
  1464. if ( m_MDLDict[handle]->m_nFlags & STUDIODATA_ERROR_MODEL )
  1465. return NULL;
  1466. if ( m_bDisableVCollideLoad )
  1467. return NULL;
  1468. studiodata_t *pStudioData = m_MDLDict[handle];
  1469. if ( ( pStudioData->m_nFlags & STUDIODATA_FLAGS_VCOLLISION_LOADED ) == 0 )
  1470. {
  1471. UnserializeVCollide( handle, mod_load_vcollide_async.GetBool(), synchronousLoad );
  1472. }
  1473. // in queued mode we need to scan the virtual model for shared vcollides
  1474. if ( ( pStudioData->m_nFlags & STUDIODATA_FLAGS_VCOLLISION_SCANNED ) == 0 )
  1475. {
  1476. pStudioData->m_nFlags |= STUDIODATA_FLAGS_VCOLLISION_SCANNED;
  1477. if ( !pStudioData->m_pVCollide )
  1478. {
  1479. virtualmodel_t *pVirtualModel = GetVirtualModel( handle );
  1480. if ( pVirtualModel )
  1481. {
  1482. for ( int i = 1; i < pVirtualModel->m_group.Count(); i++ )
  1483. {
  1484. MDLHandle_t sharedHandle = VoidPtrToMDLHandle( pVirtualModel->m_group[i].cache );
  1485. studiodata_t *pData = m_MDLDict[sharedHandle];
  1486. if ( pData->m_pVCollide )
  1487. {
  1488. pStudioData->m_pVCollide = pData->m_pVCollide;
  1489. pData->m_pVCollide->AddRef();
  1490. break;
  1491. }
  1492. }
  1493. }
  1494. }
  1495. }
  1496. // We've loaded an empty collision file or no file was found, so return NULL
  1497. if ( !pStudioData->m_pVCollide )
  1498. return NULL;
  1499. // returned pointer to shared vcollide
  1500. return pStudioData->m_pVCollide->GetVCollide();
  1501. }
  1502. bool CMDLCache::GetVCollideSize( MDLHandle_t handle, int *pVCollideSize )
  1503. {
  1504. *pVCollideSize = 0;
  1505. studiodata_t *pStudioData = m_MDLDict[handle];
  1506. if ( !pStudioData->m_pVCollide )
  1507. return false;
  1508. vcollide_t *pCollide = pStudioData->m_pVCollide->GetVCollide();
  1509. for ( int j = 0; j < pCollide->solidCount; j++ )
  1510. {
  1511. *pVCollideSize += g_pPhysicsCollision->CollideSize( pCollide->solids[j] );
  1512. }
  1513. *pVCollideSize += pCollide->descSize;
  1514. return true;
  1515. }
  1516. //-----------------------------------------------------------------------------
  1517. // Allocates/frees the anim blocks
  1518. //-----------------------------------------------------------------------------
  1519. void CMDLCache::AllocateAnimBlocks( studiodata_t *pStudioData, int nCount )
  1520. {
  1521. Assert( pStudioData->m_vecAnimBlocks.Count() == 0 );
  1522. pStudioData->m_vecAnimBlocks.EnsureCount( nCount );
  1523. memset( pStudioData->m_vecAnimBlocks.Base(), 0, sizeof(DataCacheHandle_t) * nCount );
  1524. pStudioData->m_vecFakeAnimBlockStall.EnsureCount( nCount );
  1525. memset( pStudioData->m_vecFakeAnimBlockStall.Base(), 0, sizeof( unsigned long ) * nCount );
  1526. #ifdef DEBUG_ANIM_STALLS
  1527. pStudioData->m_vecFirstRequest.EnsureCount( nCount );
  1528. memset( pStudioData->m_vecFirstRequest.Base(), 0, sizeof( unsigned long ) * nCount );
  1529. #endif
  1530. }
  1531. void CMDLCache::FreeAnimBlocks( studiodata_t *pStudioData )
  1532. {
  1533. for ( int i = 0; i < pStudioData->m_vecAnimBlocks.Count(); ++i )
  1534. {
  1535. MdlCacheMsg( "MDLCache: Free Anim block %s (block: %d)\n", GetModelName( pStudioData->m_Handle ), i );
  1536. ClearAsync( pStudioData->m_Handle, MDLCACHE_ANIMBLOCK, i, true );
  1537. if ( pStudioData->m_vecAnimBlocks[i] )
  1538. {
  1539. UncacheData( pStudioData->m_vecAnimBlocks[i], MDLCACHE_ANIMBLOCK, true );
  1540. }
  1541. }
  1542. pStudioData->m_vecAnimBlocks.Purge();
  1543. pStudioData->m_vecFakeAnimBlockStall.Purge();
  1544. #ifdef DEBUG_ANIM_STALLS
  1545. pStudioData->m_vecFirstRequest.Purge();
  1546. #endif
  1547. }
  1548. //-----------------------------------------------------------------------------
  1549. // Unserializes an animation block from disk
  1550. //-----------------------------------------------------------------------------
  1551. unsigned char *CMDLCache::UnserializeAnimBlock( MDLHandle_t handle, bool bUseAsync, int nBlock )
  1552. {
  1553. VPROF( "CMDLCache::UnserializeAnimBlock" );
  1554. if ( IsGameConsole() && g_pQueuedLoader->IsMapLoading() )
  1555. {
  1556. // anim block i/o is not allowed at this stage
  1557. return NULL;
  1558. }
  1559. // Block 0 is never used!!!
  1560. Assert( nBlock > 0 );
  1561. studiodata_t *pStudioData = m_MDLDict[handle];
  1562. intp iAsync = GetAsyncInfoIndex( handle, MDLCACHE_ANIMBLOCK, nBlock );
  1563. if ( iAsync == NO_ASYNC )
  1564. {
  1565. studiohdr_t *pStudioHdr = GetStudioHdr( handle );
  1566. // FIXME: For consistency, the block name maybe shouldn't have 'model' in it.
  1567. char const *pModelName = pStudioHdr->pszAnimBlockName();
  1568. mstudioanimblock_t *pBlock = pStudioHdr->pAnimBlock( nBlock );
  1569. int nSize = pBlock->dataend - pBlock->datastart;
  1570. if ( nSize == 0 )
  1571. return NULL;
  1572. // allocate space in the cache
  1573. pStudioData->m_vecAnimBlocks[nBlock] = NULL;
  1574. char pFileName[MAX_PATH];
  1575. Q_strncpy( pFileName, pModelName, sizeof(pFileName) );
  1576. Q_FixSlashes( pFileName );
  1577. #ifdef POSIX
  1578. Q_strlower( pFileName );
  1579. #endif
  1580. if ( IsGameConsole() )
  1581. {
  1582. char pX360Filename[MAX_PATH];
  1583. UpdateOrCreate( pStudioHdr, pFileName, pX360Filename, sizeof( pX360Filename ), "GAME" );
  1584. Q_strncpy( pFileName, pX360Filename, sizeof(pX360Filename) );
  1585. }
  1586. MdlCacheMsg( "MDLCache: Begin load Anim Block %s (block %i, bytes %d)\n", GetModelName( handle ), nBlock, nSize );
  1587. AsyncInfo_t info;
  1588. if ( IsDebug() )
  1589. {
  1590. memset( &info, 0xdd, sizeof( AsyncInfo_t ) );
  1591. }
  1592. info.hModel = handle;
  1593. info.type = MDLCACHE_ANIMBLOCK;
  1594. info.iAnimBlock = nBlock;
  1595. info.hControl = NULL;
  1596. void *pData;
  1597. if ( nSize <= ANIMBLOCK_SIZE )
  1598. {
  1599. // using the pool
  1600. pData = g_AnimBlockAllocator.Alloc();
  1601. }
  1602. else
  1603. {
  1604. // Null will yield file system allocating an optimal read buffer
  1605. pData = NULL;
  1606. Warning( "%s(%d): MDL Cache allocation outside the pool. %s : %d.\n", __FILE__, __LINE__, pStudioHdr->pszName(), nSize );
  1607. }
  1608. LoadData( pFileName, "GAME", pData, nSize, pBlock->datastart, bUseAsync, &info.hControl, handle );
  1609. Assert( m_AsyncMutex.GetOwnerId() == ThreadGetCurrentId() );
  1610. iAsync = SetAsyncInfoIndex( handle, MDLCACHE_ANIMBLOCK, nBlock, m_PendingAsyncs.AddToTail( info ) );
  1611. #ifdef DEBUG_ANIM_STALLS
  1612. // keep track of when it was requested
  1613. pStudioData->m_vecFirstRequest[nBlock] = Plat_MSTime();
  1614. #endif
  1615. #ifdef DEBUG_ANIM_BLOCKS_LOADED
  1616. mstudioanimdesc_t &animdesc = pStudioHdr->pAnimdesc( nBlock );
  1617. Msg( "AnimBlock: %s (%s) %d \n", animdesc.pszName(), pStudioHdr->pszName(), nSize );
  1618. #endif
  1619. }
  1620. ProcessPendingAsync( iAsync );
  1621. return ( unsigned char * )CheckData( pStudioData->m_vecAnimBlocks[nBlock], MDLCACHE_ANIMBLOCK );
  1622. }
  1623. //-----------------------------------------------------------------------------
  1624. // Gets at an animation block associated with an MDL
  1625. //-----------------------------------------------------------------------------
  1626. unsigned char *CMDLCache::GetAnimBlock( MDLHandle_t handle, int nBlock, bool preloadIfMissing )
  1627. {
  1628. if ( mod_test_not_available.GetBool() )
  1629. return NULL;
  1630. if ( handle == MDLHANDLE_INVALID )
  1631. return NULL;
  1632. if ( m_MDLDict[handle]->m_nFlags & STUDIODATA_ERROR_MODEL )
  1633. return NULL;
  1634. // Allocate animation blocks if we don't have them yet
  1635. studiodata_t *pStudioData = m_MDLDict[handle];
  1636. if ( pStudioData->m_vecAnimBlocks.Count() == 0 )
  1637. {
  1638. AUTO_LOCK_FM( m_AsyncMutex );
  1639. if ( pStudioData->m_vecAnimBlocks.Count() == 0 )
  1640. {
  1641. studiohdr_t *pStudioHdr = GetStudioHdr( handle );
  1642. AllocateAnimBlocks( pStudioData, pStudioHdr->numanimblocks );
  1643. }
  1644. }
  1645. // check for request being in range
  1646. if ( nBlock < 0 || nBlock >= pStudioData->m_vecAnimBlocks.Count())
  1647. return NULL;
  1648. // Check the cache to see if the animation is in memory
  1649. unsigned char *pData = ( unsigned char * )CheckData( pStudioData->m_vecAnimBlocks[nBlock], MDLCACHE_ANIMBLOCK );
  1650. if ( !pData )
  1651. {
  1652. AUTO_LOCK_FM( m_AsyncMutex );
  1653. pData = ( unsigned char * )CheckData( pStudioData->m_vecAnimBlocks[nBlock], MDLCACHE_ANIMBLOCK );
  1654. if ( !pData )
  1655. {
  1656. pStudioData->m_vecAnimBlocks[nBlock] = NULL;
  1657. if ( preloadIfMissing )
  1658. {
  1659. // It's not in memory, read it off of disk
  1660. pData = UnserializeAnimBlock( handle, mod_load_anims_async.GetBool(), nBlock );
  1661. }
  1662. }
  1663. }
  1664. if (mod_load_fakestall.GetInt())
  1665. {
  1666. unsigned long t = Plat_MSTime();
  1667. if (pStudioData->m_vecFakeAnimBlockStall[nBlock] == 0 || pStudioData->m_vecFakeAnimBlockStall[nBlock] > t)
  1668. {
  1669. pStudioData->m_vecFakeAnimBlockStall[nBlock] = t;
  1670. }
  1671. if ((int)(t - pStudioData->m_vecFakeAnimBlockStall[nBlock]) < mod_load_fakestall.GetInt())
  1672. {
  1673. return NULL;
  1674. }
  1675. }
  1676. return pData;
  1677. }
  1678. //-----------------------------------------------------------------------------
  1679. // Indicates if an anim block has been preloaded (either already in memory or in asynchronous loading).
  1680. //-----------------------------------------------------------------------------
  1681. bool CMDLCache::HasAnimBlockBeenPreloaded( MDLHandle_t handle, int nBlock )
  1682. {
  1683. if ( mod_test_not_available.GetBool() )
  1684. return false;
  1685. if ( handle == MDLHANDLE_INVALID )
  1686. return false;
  1687. if ( m_MDLDict[handle]->m_nFlags & STUDIODATA_ERROR_MODEL )
  1688. return false;
  1689. studiodata_t *pStudioData = m_MDLDict[handle];
  1690. if ( nBlock <= 0 )
  1691. return true;
  1692. // check for request being in range
  1693. if ( nBlock >= pStudioData->m_vecAnimBlocks.Count() )
  1694. return false;
  1695. // Check the cache to see if the animation is in memory
  1696. // TODO: Investigate if the double testing is really necessary here.
  1697. unsigned char *pData = ( unsigned char * )CheckData( pStudioData->m_vecAnimBlocks[nBlock], MDLCACHE_ANIMBLOCK );
  1698. if ( pData == NULL )
  1699. {
  1700. AUTO_LOCK_FM( m_AsyncMutex );
  1701. pData = ( unsigned char * )CheckData( pStudioData->m_vecAnimBlocks[nBlock], MDLCACHE_ANIMBLOCK );
  1702. if ( pData != NULL )
  1703. {
  1704. return true;
  1705. }
  1706. }
  1707. else
  1708. {
  1709. return true;
  1710. }
  1711. // Data was not cached already, but maybe in async loading
  1712. int iAsync = GetAsyncInfoIndex( handle, MDLCACHE_ANIMBLOCK, nBlock );
  1713. return ( iAsync != NO_ASYNC );
  1714. }
  1715. //-----------------------------------------------------------------------------
  1716. // Allocates/frees autoplay sequence list
  1717. //-----------------------------------------------------------------------------
  1718. void CMDLCache::AllocateAutoplaySequences( studiodata_t *pStudioData, int nCount )
  1719. {
  1720. FreeAutoplaySequences( pStudioData );
  1721. pStudioData->m_vecAutoplaySequenceList.EnsureCount( nCount );
  1722. }
  1723. void CMDLCache::FreeAutoplaySequences( studiodata_t *pStudioData )
  1724. {
  1725. pStudioData->m_vecAutoplaySequenceList.Purge();
  1726. }
  1727. //-----------------------------------------------------------------------------
  1728. // Gets the autoplay list
  1729. //-----------------------------------------------------------------------------
  1730. int CMDLCache::GetAutoplayList( MDLHandle_t handle, unsigned short **pAutoplayList )
  1731. {
  1732. if ( pAutoplayList )
  1733. {
  1734. *pAutoplayList = NULL;
  1735. }
  1736. if ( handle == MDLHANDLE_INVALID )
  1737. return 0;
  1738. virtualmodel_t *pVirtualModel = GetVirtualModel( handle );
  1739. if ( pVirtualModel )
  1740. {
  1741. if ( pAutoplayList && pVirtualModel->m_autoplaySequences.Count() )
  1742. {
  1743. *pAutoplayList = pVirtualModel->m_autoplaySequences.Base();
  1744. }
  1745. return pVirtualModel->m_autoplaySequences.Count();
  1746. }
  1747. // FIXME: Should we cache autoplay info here on demand instead of in unserializeMDL?
  1748. studiodata_t *pStudioData = m_MDLDict[handle];
  1749. if ( pAutoplayList )
  1750. {
  1751. *pAutoplayList = pStudioData->m_vecAutoplaySequenceList.Base();
  1752. }
  1753. return pStudioData->m_vecAutoplaySequenceList.Count();
  1754. }
  1755. //-----------------------------------------------------------------------------
  1756. // Allocates/frees the virtual model
  1757. //-----------------------------------------------------------------------------
  1758. void CMDLCache::AllocateVirtualModel( MDLHandle_t handle )
  1759. {
  1760. studiodata_t *pStudioData = m_MDLDict[handle];
  1761. Assert( pStudioData->m_pVirtualModel == NULL );
  1762. pStudioData->m_pVirtualModel = new virtualmodel_t;
  1763. // FIXME: The old code slammed these; could have leaked memory?
  1764. Assert( pStudioData->m_vecAnimBlocks.Count() == 0 );
  1765. Assert( pStudioData->m_vecAnimBlocks.Count() == 0 );
  1766. }
  1767. void CMDLCache::FreeVirtualModel( studiodata_t *pStudioData )
  1768. {
  1769. if ( pStudioData && pStudioData->m_pVirtualModel )
  1770. {
  1771. int nGroupCount = pStudioData->m_pVirtualModel->m_group.Count();
  1772. Assert( (nGroupCount >= 1) && pStudioData->m_pVirtualModel->m_group[0].cache == (void*)(uintp)pStudioData->m_Handle );
  1773. // NOTE: Start at *1* here because the 0th element contains a reference to *this* handle
  1774. for ( int i = 1; i < nGroupCount; ++i )
  1775. {
  1776. MDLHandle_t h = VoidPtrToMDLHandle( pStudioData->m_pVirtualModel->m_group[i].cache );
  1777. FreeVirtualModel( m_MDLDict[ h ] );
  1778. Release( h );
  1779. }
  1780. delete pStudioData->m_pVirtualModel;
  1781. pStudioData->m_pVirtualModel = NULL;
  1782. }
  1783. }
  1784. //-----------------------------------------------------------------------------
  1785. // Returns the virtual model
  1786. //-----------------------------------------------------------------------------
  1787. virtualmodel_t *CMDLCache::GetVirtualModel( MDLHandle_t handle )
  1788. {
  1789. if ( mod_test_not_available.GetBool() )
  1790. return NULL;
  1791. if ( handle == MDLHANDLE_INVALID )
  1792. return NULL;
  1793. studiohdr_t *pStudioHdr = GetStudioHdr( handle );
  1794. if ( pStudioHdr == NULL )
  1795. return NULL;
  1796. return GetVirtualModelFast( pStudioHdr, handle );
  1797. }
  1798. virtualmodel_t *CMDLCache::GetVirtualModelFast( const studiohdr_t *pStudioHdr, MDLHandle_t handle )
  1799. {
  1800. if (pStudioHdr->numincludemodels == 0)
  1801. return NULL;
  1802. studiodata_t *pStudioData = m_MDLDict[handle];
  1803. if ( !pStudioData )
  1804. return NULL;
  1805. // These exist just so we can get some valid pointers when we're trying to catch a crash here
  1806. static const studiohdr_t *pDebugStudioHdr = pStudioHdr;
  1807. static const MDLHandle_t pDebugHandle = handle;
  1808. static const studiodata_t *pDebugStudioData = m_MDLDict[handle];
  1809. if ( !pStudioData->m_pVirtualModel )
  1810. {
  1811. DevMsg( 2, "Loading virtual model for %s\n", pStudioHdr->pszName() );
  1812. CMDLCacheCriticalSection criticalSection( this );
  1813. AllocateVirtualModel( handle );
  1814. // Group has to be zero to ensure refcounting is correct
  1815. int nGroup = pStudioData->m_pVirtualModel->m_group.AddToTail( );
  1816. Assert( nGroup == 0 );
  1817. pStudioData->m_pVirtualModel->m_group[nGroup].cache = (void *)(uintp)handle;
  1818. // Add all dependent data
  1819. pStudioData->m_pVirtualModel->AppendModels( 0, pStudioHdr );
  1820. }
  1821. return pStudioData->m_pVirtualModel;
  1822. }
  1823. //-----------------------------------------------------------------------------
  1824. // Purpose: Pulls all submodels/.ani file models into the cache
  1825. // to avoid runtime hitches and load animations at load time, set mod_forcedata to be 1
  1826. //-----------------------------------------------------------------------------
  1827. void CMDLCache::UnserializeAllVirtualModelsAndAnimBlocks( MDLHandle_t handle )
  1828. {
  1829. if ( handle == MDLHANDLE_INVALID )
  1830. return;
  1831. // might be re-loading, discard old virtualmodel to force rebuild
  1832. // unfortunately, the virtualmodel does build data into the cacheable studiohdr
  1833. FreeVirtualModel( m_MDLDict[handle] );
  1834. if ( IsGameConsole() && g_pQueuedLoader->IsMapLoading() )
  1835. {
  1836. // queued loading has to do it
  1837. return;
  1838. }
  1839. // don't load the submodel data
  1840. if ( !mod_forcedata.GetBool() )
  1841. return;
  1842. // if not present, will instance and load the submodels
  1843. GetVirtualModel( handle );
  1844. if ( IsGameConsole() )
  1845. {
  1846. // 360 does not drive the anims into its small cache section
  1847. return;
  1848. }
  1849. // Note that the animblocks start at 1!!!
  1850. studiohdr_t *pStudioHdr = GetStudioHdr( handle );
  1851. for ( int i = 1 ; i < (int)pStudioHdr->numanimblocks; ++i )
  1852. {
  1853. GetAnimBlock( handle, i, true );
  1854. }
  1855. ProcessPendingAsyncs( MDLCACHE_ANIMBLOCK );
  1856. }
  1857. //-----------------------------------------------------------------------------
  1858. // Loads the static meshes
  1859. //-----------------------------------------------------------------------------
  1860. bool CMDLCache::UnserializeHardwareData( MDLHandle_t handle, bool bUseAsync )
  1861. {
  1862. Assert( handle != MDLHANDLE_INVALID );
  1863. // Don't try to load VTX files if we don't have focus...
  1864. if ( m_bLostVideoMemory )
  1865. return false;
  1866. studiodata_t *pStudioData = m_MDLDict[handle];
  1867. if ( pStudioData->m_pCombinedStudioData && pStudioData->m_pCombinedStudioData->m_FinalHandle == handle )
  1868. {
  1869. return UnserializeCombinedHardwareData( handle );
  1870. }
  1871. CMDLCacheCriticalSection criticalSection( this );
  1872. // Load up the model
  1873. studiohdr_t *pStudioHdr = GetStudioHdr( handle );
  1874. if ( !pStudioHdr || !pStudioHdr->numbodyparts )
  1875. {
  1876. pStudioData->m_nFlags |= STUDIODATA_FLAGS_NO_STUDIOMESH;
  1877. return true;
  1878. }
  1879. if ( pStudioData->m_nFlags & STUDIODATA_FLAGS_NO_STUDIOMESH )
  1880. {
  1881. return false;
  1882. }
  1883. if ( LogMdlCache() &&
  1884. GetAsyncInfoIndex( handle, MDLCACHE_STUDIOHWDATA ) == NO_ASYNC &&
  1885. GetAsyncInfoIndex( handle, MDLCACHE_VERTEXES ) == NO_ASYNC )
  1886. {
  1887. MdlCacheMsg( "MDLCache: Begin load studiomdl %s\n", GetModelName( handle ) );
  1888. }
  1889. // Vertex data is required to call LoadModel(), so make sure that's ready
  1890. if ( !GetVertexData( handle ) )
  1891. {
  1892. if ( pStudioData->m_nFlags & STUDIODATA_FLAGS_NO_VERTEX_DATA )
  1893. {
  1894. pStudioData->m_nFlags |= STUDIODATA_FLAGS_NO_STUDIOMESH;
  1895. }
  1896. return false;
  1897. }
  1898. intp iAsync = GetAsyncInfoIndex( handle, MDLCACHE_STUDIOHWDATA );
  1899. if ( iAsync == NO_ASYNC )
  1900. {
  1901. m_pMeshCacheSection->Lock( pStudioData->m_VertexCache );
  1902. // load and persist the vtx file
  1903. // use model name for correct path
  1904. char pFileName[MAX_PATH];
  1905. MakeFilename( pFileName, pStudioHdr, GetVTXExtension() );
  1906. if ( IsGameConsole() )
  1907. {
  1908. char pX360Filename[MAX_PATH];
  1909. UpdateOrCreate( pStudioHdr, pFileName, pX360Filename, sizeof( pX360Filename ), "GAME" );
  1910. Q_strncpy( pFileName, pX360Filename, sizeof(pX360Filename) );
  1911. }
  1912. MdlCacheMsg( "MDLCache: Begin load VTX %s\n", GetModelName( handle ) );
  1913. AsyncInfo_t info;
  1914. if ( IsDebug() )
  1915. {
  1916. memset( &info, 0xdd, sizeof( AsyncInfo_t ) );
  1917. }
  1918. info.hModel = handle;
  1919. info.type = MDLCACHE_STUDIOHWDATA;
  1920. info.iAnimBlock = 0;
  1921. info.hControl = NULL;
  1922. LoadData( pFileName, "GAME", bUseAsync, &info.hControl, handle );
  1923. {
  1924. AUTO_LOCK_FM( m_AsyncMutex );
  1925. iAsync = SetAsyncInfoIndex( handle, MDLCACHE_STUDIOHWDATA, m_PendingAsyncs.AddToTail( info ) );
  1926. }
  1927. }
  1928. if ( ProcessPendingAsync( iAsync ) > 0 )
  1929. {
  1930. if ( pStudioData->m_nFlags & STUDIODATA_FLAGS_NO_STUDIOMESH )
  1931. {
  1932. return false;
  1933. }
  1934. return ( pStudioData->m_HardwareData.m_NumStudioMeshes != 0 );
  1935. }
  1936. return false;
  1937. }
  1938. static bool SortLessFunc(const mstudiovertanim_t &left, const mstudiovertanim_t & right)
  1939. {
  1940. return left.index < right.index;
  1941. }
  1942. static bool SortLessFuncWrinkle(const mstudiovertanim_wrinkle_t &left, const mstudiovertanim_wrinkle_t & right)
  1943. {
  1944. return left.index < right.index;
  1945. }
  1946. CMiniProfiler s_mp_SortFlexData;
  1947. void CMDLCache::ConvertFlexData( studiohdr_t *pStudioHdr )
  1948. {
  1949. float flVertAnimFixedPointScale = pStudioHdr->VertAnimFixedPointScale();
  1950. for ( int i = 0; i < pStudioHdr->numbodyparts; i++ )
  1951. {
  1952. mstudiobodyparts_t *pBody = pStudioHdr->pBodypart( i );
  1953. for ( int j = 0; j < pBody->nummodels; j++ )
  1954. {
  1955. mstudiomodel_t *pModel = pBody->pModel( j );
  1956. for ( int k = 0; k < pModel->nummeshes; k++ )
  1957. {
  1958. mstudiomesh_t *pMesh = pModel->pMesh( k );
  1959. for ( int l = 0; l < pMesh->numflexes; l++ )
  1960. {
  1961. mstudioflex_t *pFlex = pMesh->pFlex( l );
  1962. bool bIsWrinkleAnim = ( pFlex->vertanimtype == STUDIO_VERT_ANIM_WRINKLE );
  1963. for ( int m = 0; m < pFlex->numverts; m++ )
  1964. {
  1965. mstudiovertanim_t *pVAnim = bIsWrinkleAnim ?
  1966. pFlex->pVertanimWrinkle( m ) : pFlex->pVertanim( m );
  1967. pVAnim->ConvertToFixed( flVertAnimFixedPointScale );
  1968. }
  1969. CMiniProfilerGuard mpguard(&s_mp_SortFlexData);
  1970. switch ( pFlex->vertanimtype )
  1971. {
  1972. case STUDIO_VERT_ANIM_NORMAL:
  1973. {
  1974. mstudiovertanim_t *pvanim = pFlex->pVertanim( 0 );
  1975. mstudiovertanim_t *pvanimEnd = pvanim + pFlex->numverts;
  1976. std::make_heap( pvanim, pvanimEnd, SortLessFunc );
  1977. std::sort_heap( pvanim, pvanimEnd, SortLessFunc );
  1978. }
  1979. break;
  1980. case STUDIO_VERT_ANIM_WRINKLE:
  1981. {
  1982. mstudiovertanim_wrinkle_t *pvanim = pFlex->pVertanimWrinkle( 0 );
  1983. mstudiovertanim_wrinkle_t *pvanimEnd = pvanim + pFlex->numverts;
  1984. std::make_heap( pvanim, pvanimEnd, SortLessFuncWrinkle );
  1985. std::sort_heap( pvanim, pvanimEnd, SortLessFuncWrinkle );
  1986. }
  1987. break;
  1988. }
  1989. }
  1990. }
  1991. }
  1992. }
  1993. }
  1994. //-----------------------------------------------------------------------------
  1995. //
  1996. //-----------------------------------------------------------------------------
  1997. bool CMDLCache::BuildHardwareData( MDLHandle_t handle, studiodata_t *pStudioData, studiohdr_t *pStudioHdr, CMDLCacheData &cacheData )
  1998. {
  1999. OptimizedModel::FileHeader_t *pVtxHdr = (OptimizedModel::FileHeader_t *)cacheData.Data();
  2000. if ( pVtxHdr )
  2001. {
  2002. MdlCacheMsg("MDLCache: Alloc VTX %s\n", pStudioHdr->pszName() );
  2003. // check header
  2004. if ( pVtxHdr->version != OPTIMIZED_MODEL_FILE_VERSION )
  2005. {
  2006. Warning( "Error Index File for '%s' version %d should be %d\n", pStudioHdr->pszName(), pVtxHdr->version, OPTIMIZED_MODEL_FILE_VERSION );
  2007. pVtxHdr = NULL;
  2008. }
  2009. else if ( pVtxHdr->checkSum != pStudioHdr->checksum )
  2010. {
  2011. Warning( "Error Index File for '%s' checksum %ld should be %ld\n", pStudioHdr->pszName(), pVtxHdr->checkSum, pStudioHdr->checksum );
  2012. pVtxHdr = NULL;
  2013. }
  2014. }
  2015. if ( !pVtxHdr )
  2016. {
  2017. pStudioData->m_nFlags |= STUDIODATA_FLAGS_NO_STUDIOMESH;
  2018. return false;
  2019. }
  2020. MdlCacheMsg( "MDLCache: Load studiohwdata %s\n", pStudioHdr->pszName() );
  2021. Assert( GetVertexData( handle ) );
  2022. BeginCoarseLock();
  2023. BeginLock();
  2024. bool bLoaded = g_pStudioRender->LoadModel( pStudioHdr, pVtxHdr, &pStudioData->m_HardwareData );
  2025. EndLock();
  2026. EndCoarseLock();
  2027. if ( bLoaded )
  2028. {
  2029. pStudioData->m_nFlags |= STUDIODATA_FLAGS_STUDIOMESH_LOADED;
  2030. }
  2031. else
  2032. {
  2033. pStudioData->m_nFlags |= STUDIODATA_FLAGS_NO_STUDIOMESH;
  2034. }
  2035. if ( m_pCacheNotify )
  2036. {
  2037. m_pCacheNotify->OnDataLoaded( MDLCACHE_STUDIOHWDATA, handle );
  2038. }
  2039. return true;
  2040. }
  2041. //-----------------------------------------------------------------------------
  2042. // Loads the static meshes
  2043. //-----------------------------------------------------------------------------
  2044. void CMDLCache::UnloadHardwareData( studiodata_t *pStudioData )
  2045. {
  2046. // Don't load it if it's loaded
  2047. if ( pStudioData && pStudioData->m_nFlags & STUDIODATA_FLAGS_STUDIOMESH_LOADED )
  2048. {
  2049. if ( m_pCacheNotify )
  2050. {
  2051. m_pCacheNotify->OnDataUnloaded( MDLCACHE_STUDIOHWDATA, pStudioData->m_Handle );
  2052. }
  2053. MdlCacheMsg( "MDLCache: Unload studiohwdata %s\n", GetModelName( pStudioData->m_Handle ) );
  2054. g_pStudioRender->UnloadModel( &pStudioData->m_HardwareData );
  2055. memset( &pStudioData->m_HardwareData, 0, sizeof( pStudioData->m_HardwareData ) );
  2056. pStudioData->m_nFlags &= ~STUDIODATA_FLAGS_STUDIOMESH_LOADED;
  2057. if ( ( pStudioData->m_nFlags & STUDIODATA_FLAGS_COMBINED ) != 0 )
  2058. {
  2059. g_pMaterialSystem->UncacheUnusedMaterials();
  2060. }
  2061. }
  2062. }
  2063. void CMDLCache::UnloadQueuedHardwareData( )
  2064. {
  2065. studiodata_t *handle = NULL;
  2066. BeginLock();
  2067. while( m_UnloadHandles.PopItem( &handle ) == true )
  2068. {
  2069. Flush( handle );
  2070. delete handle;
  2071. }
  2072. EndLock();
  2073. }
  2074. //-----------------------------------------------------------------------------
  2075. // Returns the hardware data associated with an MDL
  2076. //-----------------------------------------------------------------------------
  2077. studiohwdata_t *CMDLCache::GetHardwareData( MDLHandle_t handle )
  2078. {
  2079. if ( mod_test_not_available.GetBool() )
  2080. return NULL;
  2081. if ( mod_test_mesh_not_available.GetBool() )
  2082. return NULL;
  2083. studiodata_t *pStudioData = m_MDLDict[handle];
  2084. if ( ( pStudioData->m_nFlags & (STUDIODATA_FLAGS_STUDIOMESH_LOADED | STUDIODATA_FLAGS_NO_STUDIOMESH) ) == 0 )
  2085. {
  2086. m_pMeshCacheSection->LockMutex();
  2087. if ( ( pStudioData->m_nFlags & (STUDIODATA_FLAGS_STUDIOMESH_LOADED | STUDIODATA_FLAGS_NO_STUDIOMESH) ) == 0 )
  2088. {
  2089. m_pMeshCacheSection->UnlockMutex();
  2090. if ( !UnserializeHardwareData( handle, mod_load_mesh_async.GetBool() ) )
  2091. {
  2092. return NULL;
  2093. }
  2094. }
  2095. else
  2096. {
  2097. m_pMeshCacheSection->UnlockMutex();
  2098. }
  2099. }
  2100. return &pStudioData->m_HardwareData;
  2101. }
  2102. //-----------------------------------------------------------------------------
  2103. // Task switch
  2104. //-----------------------------------------------------------------------------
  2105. void CMDLCache::ReleaseMaterialSystemObjects( int nChangeFlags )
  2106. {
  2107. Assert( !m_bLostVideoMemory );
  2108. m_bLostVideoMemory = true;
  2109. ShutdownCombiner();
  2110. BreakFrameLock( false );
  2111. // Free all hardware data
  2112. MDLHandle_t i = m_MDLDict.First();
  2113. while ( i != m_MDLDict.InvalidIndex() )
  2114. {
  2115. UnloadHardwareData( m_MDLDict[ i ] );
  2116. i = m_MDLDict.Next( i );
  2117. }
  2118. RestoreFrameLock();
  2119. }
  2120. void CMDLCache::RestoreMaterialSystemObjects( int nChangeFlags )
  2121. {
  2122. Assert( m_bLostVideoMemory );
  2123. m_bLostVideoMemory = false;
  2124. BreakFrameLock( false );
  2125. // Restore all hardware data
  2126. MDLHandle_t i = m_MDLDict.First();
  2127. while ( i != m_MDLDict.InvalidIndex() )
  2128. {
  2129. studiodata_t *pStudioData = m_MDLDict[i];
  2130. bool bIsMDLInMemory = GetCacheSection( MDLCACHE_STUDIOHDR )->IsPresent( pStudioData->m_MDLCache );
  2131. // If the vertex format changed, we have to free the data because we may be using different .vtx files.
  2132. if ( nChangeFlags & MATERIAL_RESTORE_VERTEX_FORMAT_CHANGED )
  2133. {
  2134. MdlCacheMsg( "MDLCache: Free studiohdr\n" );
  2135. MdlCacheMsg( "MDLCache: Free VVD\n" );
  2136. MdlCacheMsg( "MDLCache: Free VTX\n" );
  2137. // FIXME: Do we have to free m_MDLCache + m_VertexCache?
  2138. // Certainly we have to free m_IndexCache, cause that's a dx-level specific vtx file.
  2139. ClearAsync( i, MDLCACHE_STUDIOHWDATA, 0, true );
  2140. Flush( i, MDLCACHE_FLUSH_VERTEXES );
  2141. }
  2142. // Only restore the hardware data of those studiohdrs which are currently in memory
  2143. if ( bIsMDLInMemory )
  2144. {
  2145. GetHardwareData( i );
  2146. }
  2147. i = m_MDLDict.Next( i );
  2148. }
  2149. RestoreFrameLock();
  2150. }
  2151. //-----------------------------------------------------------------------------
  2152. // Finalization step of loading. Part of an intricate loading control flow.
  2153. //-----------------------------------------------------------------------------
  2154. void CMDLCache::MarkAsLoaded( MDLHandle_t handle )
  2155. {
  2156. // For the 360...
  2157. // This should only be occurring at the very end of loading. The queued loader will have
  2158. // either left the data in the datacache or populated it. This just re-hooks the
  2159. // aliased pointers to data that should be in the cache. If the queued loader is
  2160. // working properly, no i/o should be evented.
  2161. if ( mod_lock_mdls_on_load.GetBool() )
  2162. {
  2163. // re-establish the cached model header, if the cache doesn't have it, it will cause i/o and get loaded now
  2164. g_MDLCache.GetStudioHdr( handle );
  2165. if ( !m_MDLDict[handle]->m_pForceLockedStudioHdr )
  2166. {
  2167. m_MDLDict[handle]->m_pForceLockedStudioHdr = (studiohdr_t *)GetCacheSection( MDLCACHE_STUDIOHDR )->Lock( m_MDLDict[handle]->m_MDLCache );
  2168. }
  2169. }
  2170. if ( !mod_dont_load_vertices.GetInt() )
  2171. {
  2172. // re-establish the cached vertex header, if the cache doesn't have it, it will cause i/o and get loaded now
  2173. if ( !mod_load_mesh_async.GetBool() && mod_lock_meshes_on_load.GetBool() && !( m_MDLDict[handle]->m_nFlags & STUDIODATA_FLAGS_NO_VERTEX_DATA ) )
  2174. {
  2175. GetVertexData( handle );
  2176. if ( !m_MDLDict[handle]->m_pForceLockedVertexFileHeader )
  2177. {
  2178. m_MDLDict[handle]->m_pForceLockedVertexFileHeader = (vertexFileHeader_t *)GetCacheSection( MDLCACHE_VERTEXES )->Lock( m_MDLDict[handle]->m_VertexCache );
  2179. }
  2180. }
  2181. }
  2182. }
  2183. //-----------------------------------------------------------------------------
  2184. // Callback for UpdateOrCreate utility function - swaps any studiomdl file type.
  2185. //-----------------------------------------------------------------------------
  2186. static bool MdlcacheCreateCallback( const char *pSourceName, const char *pTargetName, const char *pPathID, void *pHdr )
  2187. {
  2188. // Missing studio files are permissible and not spewed as errors
  2189. bool retval = false;
  2190. CUtlBuffer sourceBuf;
  2191. bool bOk = g_pFullFileSystem->ReadFile( pSourceName, NULL, sourceBuf );
  2192. if ( bOk )
  2193. {
  2194. CUtlBuffer targetBuf;
  2195. targetBuf.EnsureCapacity( sourceBuf.TellPut() + BYTESWAP_ALIGNMENT_PADDING );
  2196. int bytes = StudioByteSwap::ByteswapStudioFile( pTargetName, targetBuf.Base(), targetBuf.Size(), sourceBuf.Base(), sourceBuf.TellPut(), (studiohdr_t*)pHdr );
  2197. if ( bytes )
  2198. {
  2199. // If the file was an .mdl, attempt to swap the .ani as well
  2200. if ( Q_stristr( pSourceName, ".mdl" ) )
  2201. {
  2202. char szANISourceName[ MAX_PATH ];
  2203. Q_StripExtension( pSourceName, szANISourceName, sizeof( szANISourceName ) );
  2204. Q_strncat( szANISourceName, ".ani", sizeof( szANISourceName ), COPY_ALL_CHARACTERS );
  2205. UpdateOrCreate( szANISourceName, NULL, 0, pPathID, MdlcacheCreateCallback, true, targetBuf.Base() );
  2206. }
  2207. targetBuf.SeekPut( CUtlBuffer::SEEK_HEAD, bytes );
  2208. g_pFullFileSystem->WriteFile( pTargetName, pPathID, targetBuf );
  2209. retval = true;
  2210. }
  2211. else
  2212. {
  2213. Warning( "Failed to create %s\n", pTargetName );
  2214. }
  2215. }
  2216. return retval;
  2217. }
  2218. //-----------------------------------------------------------------------------
  2219. // Calls utility function to create .360 version of a file.
  2220. //-----------------------------------------------------------------------------
  2221. int CMDLCache::UpdateOrCreate( studiohdr_t *pHdr, const char *pSourceName, char *pTargetName, int targetLen, const char *pPathID, bool bForce )
  2222. {
  2223. return ::UpdateOrCreate( pSourceName, pTargetName, targetLen, pPathID, MdlcacheCreateCallback, bForce, pHdr );
  2224. }
  2225. //-----------------------------------------------------------------------------
  2226. // Purpose: Attempts to read a file native to the current platform
  2227. //-----------------------------------------------------------------------------
  2228. bool CMDLCache::ReadFileNative( char *pFileName, const char *pPath, CUtlBuffer &buf, int nMaxBytes )
  2229. {
  2230. bool bOk = false;
  2231. if ( IsGameConsole() )
  2232. {
  2233. // Read the 360 version
  2234. char pX360Filename[ MAX_PATH ];
  2235. UpdateOrCreate( NULL, pFileName, pX360Filename, sizeof( pX360Filename ), pPath );
  2236. bOk = g_pFullFileSystem->ReadFile( pX360Filename, pPath, buf, nMaxBytes );
  2237. }
  2238. else
  2239. {
  2240. const char *pActualFilename = pFileName;
  2241. if ( IsPC() )
  2242. {
  2243. pActualFilename = m_ModelSwapper.TranslateModelName( pFileName );
  2244. }
  2245. // Read the PC version
  2246. bOk = g_pFullFileSystem->ReadFile( pActualFilename, pPath, buf, nMaxBytes );
  2247. }
  2248. return bOk;
  2249. }
  2250. //-----------------------------------------------------------------------------
  2251. // Purpose:
  2252. //-----------------------------------------------------------------------------
  2253. studiohdr_t *CMDLCache::UnserializeMDL( MDLHandle_t handle, CMDLCacheData &cacheData )
  2254. {
  2255. studiohdr_t *pStudioHdrIn = (studiohdr_t *)cacheData.Data();
  2256. Assert( pStudioHdrIn );
  2257. if ( !pStudioHdrIn )
  2258. return NULL;
  2259. #ifdef CSTRIKE15
  2260. // Slamp root LOD to 0 for CS:GO
  2261. int nRootLOD = 0;
  2262. #else
  2263. int nRootLOD = r_rootlod.GetInt();
  2264. #endif
  2265. if ( nRootLOD > 0 )
  2266. {
  2267. // raw data is already setup for lod 0, override otherwise
  2268. static bool s_bTempRootLodEnable = !!CommandLine()->FindParm( "-r_rootlod_enable" );
  2269. if ( s_bTempRootLodEnable )
  2270. {
  2271. Studio_SetRootLOD( pStudioHdrIn, nRootLOD );
  2272. }
  2273. else
  2274. {
  2275. ExecuteNTimes( 5, Warning( "r_rootlod is temporarily unsupported: bugbait#70052" ) );
  2276. }
  2277. }
  2278. StudioHdrLookupSurfaceProps( pStudioHdrIn );
  2279. if ( pStudioHdrIn->numincludemodels == 0 )
  2280. {
  2281. StudioHdrSetAnimEventFlag( pStudioHdrIn );
  2282. }
  2283. // critical! store a back link to our data
  2284. // this is fetched when re-establishing dependent cached data (vtx/vvd)
  2285. pStudioHdrIn->SetVirtualModel( MDLHandleToVirtual( handle ) );
  2286. MdlCacheMsg( "MDLCache: Alloc studiohdr %s\n", GetModelName( handle ) );
  2287. // allocate cache space
  2288. MemAlloc_PushAllocDbgInfo( "Models:StudioHdr", 0);
  2289. studiohdr_t *pHdr = (studiohdr_t *)AllocData( MDLCACHE_STUDIOHDR, pStudioHdrIn->length );
  2290. MemAlloc_PopAllocDbgInfo();
  2291. if ( !pHdr )
  2292. return NULL;
  2293. CacheData( &m_MDLDict[handle]->m_MDLCache, pHdr, pStudioHdrIn->length, GetModelName( handle ), MDLCACHE_STUDIOHDR, MakeCacheID( handle, MDLCACHE_STUDIOHDR) );
  2294. if ( mod_lock_mdls_on_load.GetBool() )
  2295. {
  2296. m_MDLDict[handle]->m_pForceLockedStudioHdr = (studiohdr_t *)GetCacheSection( MDLCACHE_STUDIOHDR )->Lock( m_MDLDict[handle]->m_MDLCache );
  2297. }
  2298. // FIXME: Is there any way we can compute the size to load *before* loading in
  2299. // and read directly into cache memory? It would be nice to reduce cache overhead here.
  2300. // move the complete, relocatable model to the cache
  2301. memcpy( pHdr, pStudioHdrIn, pStudioHdrIn->length );
  2302. // On first load, convert the flex deltas from fp16 to 16-bit fixed-point
  2303. if ( (pHdr->flags & STUDIOHDR_FLAGS_FLEXES_CONVERTED) == 0 )
  2304. {
  2305. ConvertFlexData( pHdr );
  2306. // Mark as converted so it only happens once
  2307. pHdr->flags |= STUDIOHDR_FLAGS_FLEXES_CONVERTED;
  2308. }
  2309. if ( m_pCacheNotify )
  2310. {
  2311. m_pCacheNotify->OnDataLoaded( MDLCACHE_STUDIOHDR, handle );
  2312. }
  2313. return pHdr;
  2314. }
  2315. //-----------------------------------------------------------------------------
  2316. // Attempts to load a MDL file, validates that it's ok.
  2317. //-----------------------------------------------------------------------------
  2318. bool CMDLCache::ReadMDLFile( MDLHandle_t handle, const char *pMDLFileName, CMDLCacheData &cacheData )
  2319. {
  2320. VPROF( "CMDLCache::ReadMDLFile" );
  2321. char pFileName[ MAX_PATH ];
  2322. Q_strncpy( pFileName, pMDLFileName, sizeof( pFileName ) );
  2323. Q_FixSlashes( pFileName );
  2324. #ifdef POSIX
  2325. Q_strlower( pFileName );
  2326. #endif
  2327. MdlCacheMsg( "MDLCache: Load studiohdr %s\n", pFileName );
  2328. MEM_ALLOC_CREDIT();
  2329. bool bOk = cacheData.ReadFileNative( pFileName, "GAME" );
  2330. if ( !bOk || !cacheData.Data() )
  2331. {
  2332. DevWarning( "Failed to load %s!\n", pMDLFileName );
  2333. return false;
  2334. }
  2335. if ( cacheData.DataSize() < sizeof(studiohdr_t) )
  2336. {
  2337. DevWarning( "Empty model %s\n", pMDLFileName );
  2338. return false;
  2339. }
  2340. studiohdr_t *pStudioHdr = (studiohdr_t*)cacheData.Data();
  2341. if ( pStudioHdr->id != IDSTUDIOHEADER )
  2342. {
  2343. DevWarning( "Model %s not a .MDL format file!\n", pMDLFileName );
  2344. return false;
  2345. }
  2346. // We need this, but older file formats don't have it.
  2347. if ( pStudioHdr->studiohdr2index == 0 )
  2348. {
  2349. DevWarning( "Model %s doesn't have a studiohdr2, which should've been fixed. This is required of all models now.\n", pMDLFileName );
  2350. return false;
  2351. }
  2352. // critical! store a back link to our data
  2353. // this is fetched when re-establishing dependent cached data (vtx/vvd)
  2354. pStudioHdr->SetVirtualModel( MDLHandleToVirtual( handle ) );
  2355. static ConVarRef developer( "developer" );
  2356. // Make sure all dependent files are valid (blocking loads and checks them in developer mode 2)
  2357. if ( ( developer.IsValid() && developer.GetInt() >= 2 ) &&
  2358. !VerifyHeaders( pStudioHdr ) )
  2359. {
  2360. DevWarning( "Model %s has mismatched .vvd + .vtx files!\n", pMDLFileName );
  2361. return false;
  2362. }
  2363. return true;
  2364. }
  2365. //-----------------------------------------------------------------------------
  2366. //
  2367. //-----------------------------------------------------------------------------
  2368. studiohdr_t *CMDLCache::LockStudioHdr( MDLHandle_t handle )
  2369. {
  2370. if ( handle == MDLHANDLE_INVALID )
  2371. {
  2372. return NULL;
  2373. }
  2374. studiodata_t *pStudioData = m_MDLDict[handle];
  2375. if ( pStudioData->m_pForceLockedStudioHdr )
  2376. {
  2377. ++pStudioData->m_iStudioHdrVirtualLock;
  2378. return pStudioData->m_pForceLockedStudioHdr;
  2379. }
  2380. CMDLCacheCriticalSection cacheCriticalSection( this );
  2381. studiohdr_t *pStdioHdr = GetStudioHdr( handle );
  2382. // @TODO (toml 9/12/2006) need this?: AddRef( handle );
  2383. if ( !pStdioHdr )
  2384. {
  2385. return NULL;
  2386. }
  2387. if ( pStudioData->m_pForceLockedStudioHdr )
  2388. {
  2389. ++pStudioData->m_iStudioHdrVirtualLock;
  2390. return pStudioData->m_pForceLockedStudioHdr;
  2391. }
  2392. GetCacheSection( MDLCACHE_STUDIOHDR )->Lock( m_MDLDict[handle]->m_MDLCache );
  2393. return pStdioHdr;
  2394. }
  2395. void CMDLCache::UnlockStudioHdr( MDLHandle_t handle )
  2396. {
  2397. if ( handle == MDLHANDLE_INVALID )
  2398. {
  2399. return;
  2400. }
  2401. studiodata_t *pStudioData = m_MDLDict[handle];
  2402. if ( pStudioData->m_pForceLockedStudioHdr )
  2403. {
  2404. --pStudioData->m_iStudioHdrVirtualLock;
  2405. return;
  2406. }
  2407. if ( pStudioData->m_MDLCache != DC_INVALID_HANDLE )
  2408. {
  2409. GetCacheSection( MDLCACHE_STUDIOHDR )->Unlock( m_MDLDict[handle]->m_MDLCache );
  2410. }
  2411. // @TODO (toml 9/12/2006) need this?: Release( handle );
  2412. }
  2413. //-----------------------------------------------------------------------------
  2414. // Loading the data in
  2415. //-----------------------------------------------------------------------------
  2416. studiohdr_t *CMDLCache::GetStudioHdr( MDLHandle_t handle )
  2417. {
  2418. if ( handle == MDLHANDLE_INVALID )
  2419. return NULL;
  2420. studiodata_t *pStudioData = m_MDLDict[handle];
  2421. if( !pStudioData )
  2422. {
  2423. // <sergiy> this happens on quit during map load. Safeguarding to prevent immediate crash, as the cause is unknown.
  2424. Warning(
  2425. "-------------------------------------------------------------------------------\n"
  2426. "CMDLCache::GetStudioHdr(0x%X) : invalid handle, not in dictionary (of size %u).\n"
  2427. "-------------------------------------------------------------------------------\n",
  2428. (int)handle, m_MDLDict.Count() );
  2429. return NULL;
  2430. }
  2431. if ( pStudioData->m_pForceLockedStudioHdr )
  2432. {
  2433. return pStudioData->m_pForceLockedStudioHdr;
  2434. }
  2435. if ( mod_lock_mdls_on_load.GetBool() )
  2436. {
  2437. pStudioData->m_ForceLockMutex.Lock();
  2438. if ( pStudioData->m_pForceLockedStudioHdr )
  2439. {
  2440. pStudioData->m_ForceLockMutex.Unlock();
  2441. return pStudioData->m_pForceLockedStudioHdr;
  2442. }
  2443. }
  2444. // Returning a pointer to data inside the cache when it's unlocked is just a bad idea.
  2445. // It's technically legal, but the pointer can get invalidated if anything else looks at the cache.
  2446. // Don't do that.
  2447. // Assert( m_pModelCacheSection->IsFrameLocking() );
  2448. // Assert( m_pMeshCacheSection->IsFrameLocking() );
  2449. #if _DEBUG
  2450. VPROF_INCREMENT_COUNTER( "GetStudioHdr", 1 );
  2451. #endif
  2452. studiohdr_t *pHdr = (studiohdr_t*)CheckData( m_MDLDict[handle]->m_MDLCache, MDLCACHE_STUDIOHDR );
  2453. if ( !pHdr )
  2454. {
  2455. m_MDLDict[handle]->m_MDLCache = NULL;
  2456. CMDLCacheCriticalSection cacheCriticalSection( this );
  2457. static ConVarRef developer( "developer" );
  2458. // load the file
  2459. const char *pModelName = GetActualModelName( handle );
  2460. if ( developer.IsValid() && developer.GetInt() > 1 )
  2461. {
  2462. DevMsg( "Loading %s\n", pModelName );
  2463. }
  2464. // Load file to temporary space
  2465. CMDLCacheData cacheData( MDLCACHE_STUDIOHDR, CMDLCacheData::ALLOC_MALLOC );
  2466. if ( !ReadMDLFile( handle, pModelName, cacheData ) )
  2467. {
  2468. bool bOk = false;
  2469. if ( ( m_MDLDict[handle]->m_nFlags & STUDIODATA_ERROR_MODEL ) == 0 )
  2470. {
  2471. m_MDLDict[handle]->m_nFlags |= STUDIODATA_ERROR_MODEL;
  2472. bOk = ReadMDLFile( handle, ERROR_MODEL, cacheData );
  2473. }
  2474. if ( !bOk )
  2475. {
  2476. if (IsOSX())
  2477. {
  2478. // rbarris wants this to go somewhere like the console.log prior to crashing, which is what the Error call will do next
  2479. printf("\n ##### Model %s not found and %s couldn't be loaded", pModelName, ERROR_MODEL );
  2480. fflush( stdout );
  2481. }
  2482. Error( "Model %s not found and %s couldn't be loaded", pModelName, ERROR_MODEL );
  2483. if ( mod_lock_mdls_on_load.GetBool() )
  2484. {
  2485. pStudioData->m_ForceLockMutex.Unlock();
  2486. }
  2487. return NULL;
  2488. }
  2489. }
  2490. // put it in the cache
  2491. if ( ProcessDataIntoCache( handle, cacheData ) )
  2492. {
  2493. pHdr = (studiohdr_t*)CheckData( m_MDLDict[handle]->m_MDLCache, MDLCACHE_STUDIOHDR );
  2494. }
  2495. }
  2496. else
  2497. {
  2498. if ( mod_lock_mdls_on_load.GetBool() )
  2499. {
  2500. GetCacheSection( MDLCACHE_STUDIOHDR )->Lock( m_MDLDict[handle]->m_MDLCache ); // add an explicit lock
  2501. m_MDLDict[handle]->m_pForceLockedStudioHdr = pHdr;
  2502. }
  2503. }
  2504. if ( mod_lock_mdls_on_load.GetBool() )
  2505. {
  2506. pStudioData->m_ForceLockMutex.Unlock();
  2507. }
  2508. return pHdr;
  2509. }
  2510. //-----------------------------------------------------------------------------
  2511. // Gets/sets user data associated with the MDL
  2512. //-----------------------------------------------------------------------------
  2513. void CMDLCache::SetUserData( MDLHandle_t handle, void* pData )
  2514. {
  2515. if ( handle == MDLHANDLE_INVALID )
  2516. return;
  2517. m_MDLDict[handle]->m_pUserData = pData;
  2518. }
  2519. void *CMDLCache::GetUserData( MDLHandle_t handle )
  2520. {
  2521. if ( handle == MDLHANDLE_INVALID )
  2522. return NULL;
  2523. return m_MDLDict[handle]->m_pUserData;
  2524. }
  2525. //-----------------------------------------------------------------------------
  2526. // Polls information about a particular mdl
  2527. //-----------------------------------------------------------------------------
  2528. bool CMDLCache::IsErrorModel( MDLHandle_t handle )
  2529. {
  2530. if ( handle == MDLHANDLE_INVALID )
  2531. return false;
  2532. return (m_MDLDict[handle]->m_nFlags & STUDIODATA_ERROR_MODEL) != 0;
  2533. }
  2534. bool CMDLCache::IsOverBudget( MDLHandle_t handle )
  2535. {
  2536. if ( handle == MDLHANDLE_INVALID )
  2537. {
  2538. return false;
  2539. }
  2540. studiohdr_t *pStdioHdr = GetStudioHdr( handle );
  2541. return ( ( pStdioHdr->flags & STUDIOHDR_FLAGS_OVER_BUDGET ) != 0 );
  2542. }
  2543. //-----------------------------------------------------------------------------
  2544. // Brings all data associated with an MDL into memory
  2545. //-----------------------------------------------------------------------------
  2546. void CMDLCache::TouchAllData( MDLHandle_t handle )
  2547. {
  2548. studiohdr_t *pStudioHdr = GetStudioHdr( handle );
  2549. virtualmodel_t *pVModel = GetVirtualModel( handle );
  2550. if ( pVModel )
  2551. {
  2552. // skip self, start at children
  2553. // ensure all sub models are cached
  2554. for ( int i=1; i<pVModel->m_group.Count(); ++i )
  2555. {
  2556. MDLHandle_t childHandle = VoidPtrToMDLHandle( pVModel->m_group[i].cache );
  2557. if ( childHandle != MDLHANDLE_INVALID )
  2558. {
  2559. // FIXME: Should this be calling TouchAllData on the child?
  2560. GetStudioHdr( childHandle );
  2561. }
  2562. }
  2563. }
  2564. if ( !IsGameConsole() )
  2565. {
  2566. // cache the anims
  2567. // Note that the animblocks start at 1!!!
  2568. for ( int i=1; i< (int)pStudioHdr->numanimblocks; ++i )
  2569. {
  2570. pStudioHdr->GetAnimBlock( i );
  2571. }
  2572. }
  2573. // cache the vertexes
  2574. if ( pStudioHdr->numbodyparts )
  2575. {
  2576. if ( !mod_dont_load_vertices.GetInt() )
  2577. {
  2578. CacheVertexData( pStudioHdr );
  2579. GetHardwareData( handle );
  2580. }
  2581. }
  2582. }
  2583. //-----------------------------------------------------------------------------
  2584. // Flushes all data
  2585. //-----------------------------------------------------------------------------
  2586. void CMDLCache::Flush( MDLCacheFlush_t nFlushFlags )
  2587. {
  2588. // Free all MDLs that haven't been cleaned up
  2589. MDLHandle_t i = m_MDLDict.First();
  2590. while ( i != m_MDLDict.InvalidIndex() )
  2591. {
  2592. Flush( i, nFlushFlags );
  2593. i = m_MDLDict.Next( i );
  2594. }
  2595. }
  2596. //-----------------------------------------------------------------------------
  2597. // Cache handlers
  2598. //-----------------------------------------------------------------------------
  2599. static const char *g_ppszTypes[] =
  2600. {
  2601. "studiohdr", // MDLCACHE_STUDIOHDR
  2602. "studiohwdata", // MDLCACHE_STUDIOHWDATA
  2603. "vcollide", // MDLCACHE_VCOLLIDE
  2604. "animblock", // MDLCACHE_ANIMBLOCK
  2605. "virtualmodel", // MDLCACHE_VIRTUALMODEL
  2606. "vertexes", // MDLCACHE_VERTEXES
  2607. "decodedanim", // MDLCACHE_DECODEDANIMBLOCK
  2608. };
  2609. bool CMDLCache::HandleCacheNotification( const DataCacheNotification_t &notification )
  2610. {
  2611. switch ( notification.type )
  2612. {
  2613. case DC_AGE_DISCARD:
  2614. case DC_FLUSH_DISCARD:
  2615. case DC_REMOVED:
  2616. {
  2617. // This message can cause a crash on debug builds with "mod_trace_load 1"
  2618. MdlCacheMsg( "MDLCache: Data cache discard %s %s\n", g_ppszTypes[TypeFromCacheID( notification.clientId )], GetModelName( HandleFromCacheID( notification.clientId ) ) );
  2619. if ( (DataCacheClientID_t)notification.pItemData == notification.clientId ||
  2620. TypeFromCacheID(notification.clientId) != MDLCACHE_STUDIOHWDATA )
  2621. {
  2622. Assert( notification.pItemData );
  2623. FreeData( TypeFromCacheID(notification.clientId), (void *)notification.pItemData );
  2624. }
  2625. else
  2626. {
  2627. UnloadHardwareData( m_MDLDict[ HandleFromCacheID( notification.clientId ) ] );
  2628. }
  2629. return true;
  2630. }
  2631. }
  2632. return CDefaultDataCacheClient::HandleCacheNotification( notification );
  2633. }
  2634. bool CMDLCache::GetItemName( DataCacheClientID_t clientId, const void *pItem, char *pDest, unsigned nMaxLen )
  2635. {
  2636. if ( (DataCacheClientID_t)pItem == clientId )
  2637. {
  2638. return false;
  2639. }
  2640. MDLHandle_t handle = HandleFromCacheID( clientId );
  2641. MDLCacheDataType_t type = TypeFromCacheID( clientId );
  2642. Assert( type >= 0 && type < ARRAYSIZE(g_ppszTypes) );
  2643. Q_snprintf( pDest, nMaxLen, "%s - %s", g_ppszTypes[type], GetModelName( handle ) );
  2644. return false;
  2645. }
  2646. //-----------------------------------------------------------------------------
  2647. // Flushes all data
  2648. //-----------------------------------------------------------------------------
  2649. void CMDLCache::BeginCoarseLock()
  2650. {
  2651. if ( IsGameConsole() )
  2652. {
  2653. m_pModelCacheSection->BeginFrameLocking();
  2654. m_pMeshCacheSection->BeginFrameLocking();
  2655. }
  2656. }
  2657. //-----------------------------------------------------------------------------
  2658. // Flushes all data
  2659. //-----------------------------------------------------------------------------
  2660. void CMDLCache::EndCoarseLock()
  2661. {
  2662. if ( IsGameConsole() )
  2663. {
  2664. m_pModelCacheSection->EndFrameLocking();
  2665. m_pMeshCacheSection->EndFrameLocking();
  2666. }
  2667. }
  2668. //-----------------------------------------------------------------------------
  2669. // Flushes all data
  2670. //-----------------------------------------------------------------------------
  2671. void CMDLCache::BeginLock()
  2672. {
  2673. if ( !IsGameConsole() )
  2674. {
  2675. m_pModelCacheSection->BeginFrameLocking();
  2676. m_pMeshCacheSection->BeginFrameLocking();
  2677. }
  2678. m_pAnimBlocksCacheSection->BeginFrameLocking();
  2679. }
  2680. //-----------------------------------------------------------------------------
  2681. // Flushes all data
  2682. //-----------------------------------------------------------------------------
  2683. void CMDLCache::EndLock()
  2684. {
  2685. if ( !IsGameConsole() )
  2686. {
  2687. m_pModelCacheSection->EndFrameLocking();
  2688. m_pMeshCacheSection->EndFrameLocking();
  2689. }
  2690. m_pAnimBlocksCacheSection->EndFrameLocking();
  2691. }
  2692. //-----------------------------------------------------------------------------
  2693. //
  2694. //-----------------------------------------------------------------------------
  2695. void CMDLCache::BreakFrameLock( bool bModels, bool bMesh, bool bAnimBlock )
  2696. {
  2697. if ( bModels )
  2698. {
  2699. if ( m_pModelCacheSection->IsFrameLocking() )
  2700. {
  2701. Assert( !m_nModelCacheFrameLocks );
  2702. m_nModelCacheFrameLocks = 0;
  2703. do
  2704. {
  2705. m_nModelCacheFrameLocks++;
  2706. } while ( m_pModelCacheSection->EndFrameLocking() );
  2707. }
  2708. }
  2709. if ( bMesh )
  2710. {
  2711. if ( m_pMeshCacheSection->IsFrameLocking() )
  2712. {
  2713. Assert( !m_nMeshCacheFrameLocks );
  2714. m_nMeshCacheFrameLocks = 0;
  2715. do
  2716. {
  2717. m_nMeshCacheFrameLocks++;
  2718. } while ( m_pMeshCacheSection->EndFrameLocking() );
  2719. }
  2720. }
  2721. if ( bAnimBlock )
  2722. {
  2723. if ( m_pAnimBlocksCacheSection->IsFrameLocking() )
  2724. {
  2725. Assert( !m_nAnimBlockCacheFrameLocks );
  2726. m_nAnimBlockCacheFrameLocks = 0;
  2727. do
  2728. {
  2729. m_nAnimBlockCacheFrameLocks++;
  2730. } while ( m_pAnimBlocksCacheSection->EndFrameLocking() );
  2731. }
  2732. }
  2733. }
  2734. void CMDLCache::RestoreFrameLock()
  2735. {
  2736. while ( m_nModelCacheFrameLocks )
  2737. {
  2738. m_pModelCacheSection->BeginFrameLocking();
  2739. m_nModelCacheFrameLocks--;
  2740. }
  2741. while ( m_nMeshCacheFrameLocks )
  2742. {
  2743. m_pMeshCacheSection->BeginFrameLocking();
  2744. m_nMeshCacheFrameLocks--;
  2745. }
  2746. while ( m_nAnimBlockCacheFrameLocks )
  2747. {
  2748. m_pAnimBlocksCacheSection->BeginFrameLocking();
  2749. m_nAnimBlockCacheFrameLocks--;
  2750. }
  2751. }
  2752. //-----------------------------------------------------------------------------
  2753. //
  2754. //-----------------------------------------------------------------------------
  2755. int *CMDLCache::GetFrameUnlockCounterPtrOLD()
  2756. {
  2757. return GetCacheSection( MDLCACHE_STUDIOHDR )->GetFrameUnlockCounterPtr();
  2758. }
  2759. int *CMDLCache::GetFrameUnlockCounterPtr( MDLCacheDataType_t type )
  2760. {
  2761. return GetCacheSection( type )->GetFrameUnlockCounterPtr();
  2762. }
  2763. //-----------------------------------------------------------------------------
  2764. // Completes all pending async operations
  2765. //-----------------------------------------------------------------------------
  2766. void CMDLCache::FinishPendingLoads()
  2767. {
  2768. if ( !ThreadInMainThread() )
  2769. {
  2770. return;
  2771. }
  2772. AUTO_LOCK_FM( m_AsyncMutex );
  2773. // finish just our known jobs
  2774. intp iAsync = m_PendingAsyncs.Head();
  2775. while ( iAsync != m_PendingAsyncs.InvalidIndex() )
  2776. {
  2777. AsyncInfo_t &info = m_PendingAsyncs[iAsync];
  2778. if ( info.hControl )
  2779. {
  2780. g_pFullFileSystem->AsyncFinish( info.hControl, true );
  2781. }
  2782. iAsync = m_PendingAsyncs.Next( iAsync );
  2783. }
  2784. ProcessPendingAsyncs();
  2785. }
  2786. //-----------------------------------------------------------------------------
  2787. // Notify map load has started
  2788. //-----------------------------------------------------------------------------
  2789. void CMDLCache::BeginMapLoad()
  2790. {
  2791. BreakFrameLock();
  2792. studiodata_t *pStudioData;
  2793. m_ModelSwapper.LatchEffectiveGPULevel();
  2794. // Unlock prior map MDLs prior to load
  2795. MDLHandle_t i = m_MDLDict.First();
  2796. while ( i != m_MDLDict.InvalidIndex() )
  2797. {
  2798. pStudioData = m_MDLDict[i];
  2799. if ( pStudioData->m_pForceLockedStudioHdr )
  2800. {
  2801. // Reset the lock counts to where they need to be while the mdlcache lock is not active
  2802. while ( pStudioData->m_iStudioHdrVirtualLock > 0 )
  2803. {
  2804. --pStudioData->m_iStudioHdrVirtualLock;
  2805. GetCacheSection( MDLCACHE_STUDIOHDR )->Lock( pStudioData->m_MDLCache );
  2806. }
  2807. while ( pStudioData->m_iStudioHdrVirtualLock < 0 )
  2808. {
  2809. ++pStudioData->m_iStudioHdrVirtualLock;
  2810. GetCacheSection( MDLCACHE_STUDIOHDR )->Unlock( pStudioData->m_MDLCache );
  2811. }
  2812. GetCacheSection( MDLCACHE_STUDIOHDR )->Unlock( pStudioData->m_MDLCache );
  2813. pStudioData->m_pForceLockedStudioHdr = NULL;
  2814. }
  2815. if ( pStudioData->m_pForceLockedVertexFileHeader )
  2816. {
  2817. GetCacheSection( MDLCACHE_VERTEXES )->Unlock( pStudioData->m_VertexCache );
  2818. pStudioData->m_pForceLockedVertexFileHeader = NULL;
  2819. }
  2820. i = m_MDLDict.Next( i );
  2821. }
  2822. }
  2823. //-----------------------------------------------------------------------------
  2824. // Notify map load is complete
  2825. //-----------------------------------------------------------------------------
  2826. void CMDLCache::EndMapLoad()
  2827. {
  2828. FinishPendingLoads();
  2829. bool bLockMdls = mod_lock_mdls_on_load.GetBool();
  2830. bool bLockMeshes = ( !mod_load_mesh_async.GetBool() && mod_lock_meshes_on_load.GetBool() );
  2831. // Remove all stray MDLs not referenced during load
  2832. if ( bLockMdls || bLockMeshes )
  2833. {
  2834. studiodata_t *pStudioData;
  2835. MDLHandle_t i = m_MDLDict.First();
  2836. while ( i != m_MDLDict.InvalidIndex() )
  2837. {
  2838. pStudioData = m_MDLDict[i];
  2839. if ( bLockMdls && !m_MDLDict[i]->m_pForceLockedStudioHdr )
  2840. {
  2841. Flush( i, MDLCACHE_FLUSH_STUDIOHDR | MDLCACHE_FLUSH_COMBINED_DATA );
  2842. }
  2843. if ( bLockMeshes && !( m_MDLDict[i]->m_nFlags & STUDIODATA_FLAGS_NO_VERTEX_DATA ) && !m_MDLDict[i]->m_pForceLockedVertexFileHeader )
  2844. {
  2845. Flush( i, MDLCACHE_FLUSH_VERTEXES );
  2846. }
  2847. i = m_MDLDict.Next( i );
  2848. }
  2849. }
  2850. RestoreFrameLock();
  2851. }
  2852. //-----------------------------------------------------------------------------
  2853. // Is a particular part of the model data loaded?
  2854. //-----------------------------------------------------------------------------
  2855. bool CMDLCache::IsDataLoaded( MDLHandle_t handle, MDLCacheDataType_t type )
  2856. {
  2857. if ( handle == MDLHANDLE_INVALID || !m_MDLDict.IsValidIndex( handle ) )
  2858. return false;
  2859. studiodata_t *pData = m_MDLDict[ handle ];
  2860. switch( type )
  2861. {
  2862. case MDLCACHE_STUDIOHDR:
  2863. return GetCacheSection( MDLCACHE_STUDIOHDR )->IsPresent( pData->m_MDLCache );
  2864. case MDLCACHE_STUDIOHWDATA:
  2865. return ( pData->m_nFlags & STUDIODATA_FLAGS_STUDIOMESH_LOADED ) != 0;
  2866. case MDLCACHE_VCOLLIDE:
  2867. return ( pData->m_nFlags & STUDIODATA_FLAGS_VCOLLISION_LOADED ) != 0;
  2868. case MDLCACHE_ANIMBLOCK:
  2869. {
  2870. if ( !pData->m_vecAnimBlocks.Count() )
  2871. return false;
  2872. for (int i = 0; i < pData->m_vecAnimBlocks.Count(); ++i )
  2873. {
  2874. if ( !pData->m_vecAnimBlocks[i] )
  2875. return false;
  2876. if ( !GetCacheSection( type )->IsPresent( pData->m_vecAnimBlocks[i] ) )
  2877. return false;
  2878. }
  2879. return true;
  2880. }
  2881. case MDLCACHE_VIRTUALMODEL:
  2882. return ( pData->m_pVirtualModel != 0 );
  2883. case MDLCACHE_VERTEXES:
  2884. return m_pMeshCacheSection->IsPresent( pData->m_VertexCache );
  2885. }
  2886. return false;
  2887. }
  2888. //-----------------------------------------------------------------------------
  2889. // Get the correct extension for our dx
  2890. //-----------------------------------------------------------------------------
  2891. const char *CMDLCache::GetVTXExtension()
  2892. {
  2893. return ".dx90.vtx";
  2894. }
  2895. //-----------------------------------------------------------------------------
  2896. // Minimal presence and header validation, no data loads
  2897. // Return true if successful, false otherwise.
  2898. //-----------------------------------------------------------------------------
  2899. bool CMDLCache::VerifyHeaders( studiohdr_t *pStudioHdr )
  2900. {
  2901. VPROF( "CMDLCache::VerifyHeaders" );
  2902. // model has no vertex data
  2903. if ( !pStudioHdr->numbodyparts )
  2904. {
  2905. // valid
  2906. return true;
  2907. }
  2908. char pFileName[ MAX_PATH ];
  2909. MakeFilename( pFileName, pStudioHdr, ".vvd" );
  2910. MdlCacheMsg("MDLCache: Load VVD (verify) %s\n", pFileName );
  2911. // vvd header only
  2912. CUtlBuffer vvdHeader( 0, sizeof(vertexFileHeader_t) );
  2913. if ( !ReadFileNative( pFileName, "GAME", vvdHeader, sizeof(vertexFileHeader_t) ) )
  2914. {
  2915. return false;
  2916. }
  2917. vertexFileHeader_t *pVertexHdr = (vertexFileHeader_t*)vvdHeader.PeekGet();
  2918. // check
  2919. if (( pVertexHdr->id != MODEL_VERTEX_FILE_ID ) ||
  2920. ( pVertexHdr->version != MODEL_VERTEX_FILE_VERSION ) ||
  2921. ( pVertexHdr->checksum != pStudioHdr->checksum ))
  2922. {
  2923. return false;
  2924. }
  2925. // load the VTX file
  2926. // use model name for correct path
  2927. MakeFilename( pFileName, pStudioHdr, GetVTXExtension() );
  2928. MdlCacheMsg("MDLCache: Load VTX (verify) %s\n", pFileName );
  2929. // vtx header only
  2930. CUtlBuffer vtxHeader( 0, sizeof(OptimizedModel::FileHeader_t) );
  2931. if ( !ReadFileNative( pFileName, "GAME", vtxHeader, sizeof(OptimizedModel::FileHeader_t) ) )
  2932. {
  2933. return false;
  2934. }
  2935. // check
  2936. OptimizedModel::FileHeader_t *pVtxHdr = (OptimizedModel::FileHeader_t*)vtxHeader.PeekGet();
  2937. if (( pVtxHdr->version != OPTIMIZED_MODEL_FILE_VERSION ) ||
  2938. ( pVtxHdr->checkSum != pStudioHdr->checksum ))
  2939. {
  2940. return false;
  2941. }
  2942. // valid
  2943. return true;
  2944. }
  2945. //-----------------------------------------------------------------------------
  2946. // Cache model's specified dynamic data
  2947. //-----------------------------------------------------------------------------
  2948. vertexFileHeader_t *CMDLCache::CacheVertexData( studiohdr_t *pStudioHdr )
  2949. {
  2950. VPROF( "CMDLCache::CacheVertexData" );
  2951. vertexFileHeader_t *pVvdHdr;
  2952. MDLHandle_t handle;
  2953. Assert( pStudioHdr );
  2954. handle = VoidPtrToMDLHandle( pStudioHdr->VirtualModel() );
  2955. Assert( handle != MDLHANDLE_INVALID );
  2956. if ( m_MDLDict[handle]->m_pForceLockedVertexFileHeader )
  2957. {
  2958. return m_MDLDict[handle]->m_pForceLockedVertexFileHeader;
  2959. }
  2960. pVvdHdr = (vertexFileHeader_t *)CheckData( m_MDLDict[handle]->m_VertexCache, MDLCACHE_VERTEXES );
  2961. if ( pVvdHdr )
  2962. {
  2963. return pVvdHdr;
  2964. }
  2965. m_MDLDict[handle]->m_VertexCache = NULL;
  2966. return LoadVertexData( pStudioHdr );
  2967. }
  2968. //-----------------------------------------------------------------------------
  2969. // Start an async transfer
  2970. //-----------------------------------------------------------------------------
  2971. FSAsyncStatus_t CMDLCache::LoadData( const char *pszFilename, const char *pszPathID, void *pDest, int nBytes, int nOffset, bool bAsync, FSAsyncControl_t *pControl, MDLHandle_t hModel )
  2972. {
  2973. if ( !*pControl )
  2974. {
  2975. if ( IsGameConsole() && g_pQueuedLoader->IsMapLoading() )
  2976. {
  2977. // the weapon model cache explicitly bypasses the QL causing beingin warnings
  2978. // per request, these need to get suppressed from the log which is causing undesired noise
  2979. if ( !m_pCacheNotify || !m_pCacheNotify->ShouldSupressLoadWarning( hModel ) )
  2980. {
  2981. DevWarning( "CMDLCache: Non-Optimal loading path for %s\n", pszFilename );
  2982. }
  2983. }
  2984. const char *pActualFilename = pszFilename;
  2985. if ( IsPC() )
  2986. {
  2987. pActualFilename = m_ModelSwapper.TranslateModelName( pszFilename );
  2988. }
  2989. FileAsyncRequest_t asyncRequest;
  2990. asyncRequest.pszFilename = pActualFilename;
  2991. asyncRequest.pszPathID = pszPathID;
  2992. asyncRequest.pData = pDest;
  2993. asyncRequest.nBytes = nBytes;
  2994. asyncRequest.nOffset = nOffset;
  2995. if ( !pDest )
  2996. {
  2997. asyncRequest.flags = FSASYNC_FLAGS_ALLOCNOFREE;
  2998. }
  2999. if ( !bAsync )
  3000. {
  3001. asyncRequest.flags |= FSASYNC_FLAGS_SYNC;
  3002. }
  3003. MEM_ALLOC_CREDIT();
  3004. return g_pFullFileSystem->AsyncRead( asyncRequest, pControl );
  3005. }
  3006. return FSASYNC_ERR_FAILURE;
  3007. }
  3008. //-----------------------------------------------------------------------------
  3009. // Determine the maximum number of 'real' bone influences used by any vertex in a model
  3010. // (100% binding to bone zero doesn't count)
  3011. //-----------------------------------------------------------------------------
  3012. int ComputeMaxRealBoneInfluences( vertexFileHeader_t * vertexFile, int lod )
  3013. {
  3014. const mstudiovertex_t * verts = vertexFile->GetVertexData();
  3015. int numVerts = vertexFile->numLODVertexes[ lod ];
  3016. Assert(verts);
  3017. int maxWeights = 0;
  3018. for (int i = 0;i < numVerts;i++)
  3019. {
  3020. if ( verts[i].m_BoneWeights.numbones > 0 )
  3021. {
  3022. int numWeights = 0;
  3023. for (int j = 0;j < MAX_NUM_BONES_PER_VERT;j++)
  3024. {
  3025. if ( verts[i].m_BoneWeights.weight[j] > 0 )
  3026. numWeights = j + 1;
  3027. }
  3028. if ( ( numWeights == 1 ) && ( verts[i].m_BoneWeights.bone[0] == 0 ) )
  3029. {
  3030. // 100% binding to first bone - not really skinned (the first bone is just the model transform)
  3031. numWeights = 0;
  3032. }
  3033. maxWeights = MAX( numWeights, maxWeights );
  3034. }
  3035. }
  3036. return maxWeights;
  3037. }
  3038. //-----------------------------------------------------------------------------
  3039. // Generate thin vertices (containing just the data needed to do model decals)
  3040. //-----------------------------------------------------------------------------
  3041. vertexFileHeader_t * CMDLCache::CreateThinVertexes( vertexFileHeader_t * originalData, const studiohdr_t * pStudioHdr, int * cacheLength )
  3042. {
  3043. int rootLod = MIN( pStudioHdr->rootLOD, ( originalData->numLODs - 1 ) );
  3044. Assert( rootLod >= 0 && rootLod < ARRAYSIZE(originalData->numLODVertexes) );
  3045. int numVerts = originalData->numLODVertexes[ rootLod ] + 1; // Add 1 vert to support prefetch during array access
  3046. int numBoneInfluences = ComputeMaxRealBoneInfluences( originalData, rootLod );
  3047. // Only store (N-1) weights (all N weights sum to 1, so we can re-compute the Nth weight later)
  3048. int numStoredWeights = MAX( 0, ( numBoneInfluences - 1 ) );
  3049. int vertexSize = 2*sizeof( Vector ) + numBoneInfluences*sizeof( unsigned char ) + numStoredWeights*sizeof( float );
  3050. *cacheLength = sizeof( vertexFileHeader_t ) + sizeof( thinModelVertices_t ) + numVerts*vertexSize;
  3051. // Allocate cache space for the thin data
  3052. MemAlloc_PushAllocDbgInfo( "Models:Vertex data", 0);
  3053. vertexFileHeader_t * pNewVvdHdr = (vertexFileHeader_t *)AllocData( MDLCACHE_VERTEXES, *cacheLength );
  3054. MemAlloc_PopAllocDbgInfo();
  3055. Assert( pNewVvdHdr );
  3056. if ( pNewVvdHdr )
  3057. {
  3058. // Copy the header and set it up to hold thin vertex data
  3059. memcpy( (void *)pNewVvdHdr, (void *)originalData, sizeof( vertexFileHeader_t ) );
  3060. pNewVvdHdr->id = MODEL_VERTEX_FILE_THIN_ID;
  3061. pNewVvdHdr->numFixups = 0;
  3062. pNewVvdHdr->fixupTableStart = 0;
  3063. pNewVvdHdr->tangentDataStart = 0;
  3064. pNewVvdHdr->vertexDataStart = sizeof( vertexFileHeader_t );
  3065. // Set up the thin vertex structure
  3066. thinModelVertices_t * pNewThinVerts = (thinModelVertices_t *)( pNewVvdHdr + 1 );
  3067. Vector * pPositions = (Vector *)( pNewThinVerts + 1 );
  3068. float * pBoneWeights = (float *)( pPositions + numVerts );
  3069. // Alloc the (short) normals here to avoid mis-aligning the float data
  3070. unsigned short * pNormals = (unsigned short *)( pBoneWeights + numVerts*numStoredWeights );
  3071. // Alloc the (char) indices here to avoid mis-aligning the float/short data
  3072. byte * pBoneIndices = (byte *)( pNormals + numVerts );
  3073. if ( numStoredWeights == 0 )
  3074. pBoneWeights = NULL;
  3075. if ( numBoneInfluences == 0 )
  3076. pBoneIndices = NULL;
  3077. pNewThinVerts->Init( numBoneInfluences, pPositions, pNormals, pBoneWeights, pBoneIndices );
  3078. // Copy over the original data
  3079. const mstudiovertex_t * srcVertexData = originalData->GetVertexData();
  3080. for ( int i = 0; i < numVerts; i++ )
  3081. {
  3082. pNewThinVerts->SetPosition( i, srcVertexData[ i ].m_vecPosition );
  3083. pNewThinVerts->SetNormal( i, srcVertexData[ i ].m_vecNormal );
  3084. if ( numBoneInfluences > 0 )
  3085. {
  3086. mstudioboneweight_t boneWeights;
  3087. boneWeights.numbones = numBoneInfluences;
  3088. for ( int j = 0; j < numStoredWeights; j++ )
  3089. {
  3090. boneWeights.weight[ j ] = srcVertexData[ i ].m_BoneWeights.weight[ j ];
  3091. }
  3092. for ( int j = 0; j < numBoneInfluences; j++ )
  3093. {
  3094. boneWeights.bone[ j ] = srcVertexData[ i ].m_BoneWeights.bone[ j ];
  3095. }
  3096. pNewThinVerts->SetBoneWeights( i, boneWeights );
  3097. }
  3098. }
  3099. }
  3100. return pNewVvdHdr;
  3101. }
  3102. //-----------------------------------------------------------------------------
  3103. // Generate null vertices (containing no data - just a header to say verts have been loaded, converted into VBs/IBs and discarded)
  3104. //-----------------------------------------------------------------------------
  3105. vertexFileHeader_t * CMDLCache::CreateNullVertexes( vertexFileHeader_t * originalData, const studiohdr_t * pStudioHdr, int * cacheLength )
  3106. {
  3107. // Allocate cache space for the thin data
  3108. *cacheLength = sizeof( vertexFileHeader_t );
  3109. MemAlloc_PushAllocDbgInfo( "Models:Vertex data", 0);
  3110. vertexFileHeader_t * pNewVvdHdr = (vertexFileHeader_t *)AllocData( MDLCACHE_VERTEXES, *cacheLength );
  3111. MemAlloc_PopAllocDbgInfo();
  3112. Assert( pNewVvdHdr );
  3113. if ( pNewVvdHdr )
  3114. {
  3115. // Copy the header and blank out any references to data - which will now be discarded
  3116. memcpy( (void *)pNewVvdHdr, (void *)originalData, sizeof( vertexFileHeader_t ) );
  3117. pNewVvdHdr->id = MODEL_VERTEX_FILE_NULL_ID;
  3118. pNewVvdHdr->numFixups = 0;
  3119. pNewVvdHdr->fixupTableStart = 0;
  3120. pNewVvdHdr->tangentDataStart = 0;
  3121. pNewVvdHdr->vertexDataStart = 0;
  3122. }
  3123. return pNewVvdHdr;
  3124. }
  3125. //-----------------------------------------------------------------------------
  3126. // Process the provided raw data into the cache. Distributes to low level
  3127. // unserialization or build methods.
  3128. //-----------------------------------------------------------------------------
  3129. bool CMDLCache::ProcessDataIntoCache( MDLHandle_t handle, CMDLCacheData &cacheData, int iAnimBlock )
  3130. {
  3131. studiohdr_t *pStudioHdrCurrent = NULL;
  3132. if ( cacheData.DataType() != MDLCACHE_STUDIOHDR )
  3133. {
  3134. // can only get the studiohdr once the header has been processed successfully into the cache
  3135. // causes a ProcessDataIntoCache() with the studiohdr data
  3136. pStudioHdrCurrent = GetStudioHdr( handle );
  3137. if ( !pStudioHdrCurrent )
  3138. {
  3139. return false;
  3140. }
  3141. }
  3142. studiodata_t *pStudioDataCurrent = m_MDLDict[handle];
  3143. if ( !pStudioDataCurrent )
  3144. {
  3145. return false;
  3146. }
  3147. switch ( cacheData.DataType() )
  3148. {
  3149. case MDLCACHE_STUDIOHDR:
  3150. {
  3151. pStudioHdrCurrent = UnserializeMDL( handle, cacheData );
  3152. if ( !pStudioHdrCurrent )
  3153. {
  3154. return false;
  3155. }
  3156. if (!Studio_ConvertStudioHdrToNewVersion( pStudioHdrCurrent ))
  3157. {
  3158. Warning( "MDLCache: %s needs to be recompiled\n", pStudioHdrCurrent->pszName() );
  3159. }
  3160. if ( pStudioHdrCurrent->numincludemodels == 0 )
  3161. {
  3162. // perf optimization, calculate once and cache off the autoplay sequences
  3163. int nCount = pStudioHdrCurrent->CountAutoplaySequences();
  3164. if ( nCount )
  3165. {
  3166. AllocateAutoplaySequences( m_MDLDict[handle], nCount );
  3167. pStudioHdrCurrent->CopyAutoplaySequences( m_MDLDict[handle]->m_vecAutoplaySequenceList.Base(), nCount );
  3168. }
  3169. }
  3170. // Load animations
  3171. UnserializeAllVirtualModelsAndAnimBlocks( handle );
  3172. break;
  3173. }
  3174. case MDLCACHE_VERTEXES:
  3175. {
  3176. if ( cacheData.Data() )
  3177. {
  3178. BuildAndCacheVertexData( pStudioHdrCurrent, cacheData );
  3179. }
  3180. else
  3181. {
  3182. pStudioDataCurrent->m_nFlags |= STUDIODATA_FLAGS_NO_VERTEX_DATA;
  3183. if ( pStudioHdrCurrent->numbodyparts )
  3184. {
  3185. // expected data not valid
  3186. Warning( "MDLCache: Failed load of .VVD data for %s\n", pStudioHdrCurrent->pszName() );
  3187. return false;
  3188. }
  3189. }
  3190. break;
  3191. }
  3192. case MDLCACHE_STUDIOHWDATA:
  3193. {
  3194. if ( cacheData.Data() )
  3195. {
  3196. BuildHardwareData( handle, pStudioDataCurrent, pStudioHdrCurrent, cacheData );
  3197. }
  3198. else
  3199. {
  3200. pStudioDataCurrent->m_nFlags |= STUDIODATA_FLAGS_NO_STUDIOMESH;
  3201. if ( pStudioHdrCurrent->numbodyparts )
  3202. {
  3203. // expected data not valid
  3204. Warning( "MDLCache: Failed load of .VTX data for %s\n", pStudioHdrCurrent->pszName() );
  3205. return false;
  3206. }
  3207. }
  3208. m_pMeshCacheSection->Unlock( pStudioDataCurrent->m_VertexCache );
  3209. m_pMeshCacheSection->Age( pStudioDataCurrent->m_VertexCache );
  3210. if ( !( pStudioDataCurrent->m_nFlags & STUDIODATA_FLAGS_NO_STUDIOMESH ) )
  3211. {
  3212. vertexFileHeader_t *originalVertexData = GetVertexData( handle );
  3213. Assert( originalVertexData );
  3214. if ( originalVertexData && IsGameConsole() )
  3215. {
  3216. // PORTAL2 CONSOLE: Vertex/Index data will never be read again (no model decals or load-time lighting), so discard the VVD data and create a new header
  3217. int nullVertexDataSize = 0;
  3218. vertexFileHeader_t *nullVertexData = CreateNullVertexes( originalVertexData, pStudioHdrCurrent, &nullVertexDataSize );
  3219. Assert( nullVertexData && ( nullVertexDataSize > 0 ) );
  3220. if ( nullVertexData && ( nullVertexDataSize > 0 ) )
  3221. {
  3222. // Remove and free the original cache entry, and add the new one
  3223. // This causes the aliased "forced" locked vertex pointer to be nulled
  3224. // which trips MarkAsLoaded() to re-establish it during CL_FullyConnected(), thus the alias is maintained.
  3225. Flush( handle, MDLCACHE_FLUSH_VERTEXES | MDLCACHE_FLUSH_IGNORELOCK );
  3226. CacheData( &pStudioDataCurrent->m_VertexCache, nullVertexData, nullVertexDataSize, pStudioHdrCurrent->pszName(), MDLCACHE_VERTEXES, MakeCacheID( handle, MDLCACHE_VERTEXES) );
  3227. }
  3228. }
  3229. }
  3230. break;
  3231. }
  3232. case MDLCACHE_ANIMBLOCK:
  3233. {
  3234. MEM_ALLOC_CREDIT_( __FILE__ ": Anim Blocks" );
  3235. if ( cacheData.Data() )
  3236. {
  3237. MdlCacheMsg( "MDLCache: Finish load anim block %s (block %i)\n", pStudioHdrCurrent->pszName(), iAnimBlock );
  3238. char pCacheName[MAX_PATH];
  3239. Q_snprintf( pCacheName, MAX_PATH, "%s (block %i)", pStudioHdrCurrent->pszName(), iAnimBlock );
  3240. CacheData( &pStudioDataCurrent->m_vecAnimBlocks[iAnimBlock], cacheData.Data(), cacheData.DataSize(), pCacheName, MDLCACHE_ANIMBLOCK, MakeCacheID( handle, MDLCACHE_ANIMBLOCK) );
  3241. // The cache now owns the data, so detach it from 'cacheData':
  3242. cacheData.Detach();
  3243. #ifdef DEBUG_ANIM_STALLS
  3244. if ( mod_load_showasync.GetBool() && pStudioDataCurrent->m_vecFirstRequest && pStudioHdrCurrent )
  3245. {
  3246. Msg("[%5.3f] async model load %s:%d\n", (Plat_MSTime() - pStudioDataCurrent->m_vecFirstRequest[iAnimBlock]) / 1000.0f, pStudioHdrCurrent->pszName(), iAnimBlock );
  3247. }
  3248. #endif
  3249. }
  3250. else
  3251. {
  3252. MdlCacheMsg( "MDLCache: Failed load anim block %s (block %i)\n", pStudioHdrCurrent->pszName(), iAnimBlock );
  3253. if ( pStudioDataCurrent->m_vecAnimBlocks.Count() > iAnimBlock )
  3254. {
  3255. pStudioDataCurrent->m_vecAnimBlocks[iAnimBlock] = NULL;
  3256. }
  3257. return false;
  3258. }
  3259. break;
  3260. }
  3261. case MDLCACHE_VCOLLIDE:
  3262. {
  3263. // always marked as loaded, vcollides are not present for every model
  3264. pStudioDataCurrent->m_nFlags |= STUDIODATA_FLAGS_VCOLLISION_LOADED;
  3265. if ( cacheData.Data() )
  3266. {
  3267. MdlCacheMsg( "MDLCache: Finish load vcollide for %s\n", pStudioHdrCurrent->pszName() );
  3268. CUtlBuffer buf( cacheData.Data(), cacheData.DataSize(), CUtlBuffer::READ_ONLY );
  3269. buf.SeekPut( CUtlBuffer::SEEK_HEAD, cacheData.DataSize() );
  3270. phyheader_t header;
  3271. buf.Get( &header, sizeof( phyheader_t ) );
  3272. if ( ( header.size == sizeof( header ) ) && header.solidCount > 0 )
  3273. {
  3274. int nBufSize = buf.TellMaxPut() - buf.TellGet();
  3275. pStudioDataCurrent->m_pVCollide = new CStudioVCollide;
  3276. vcollide_t *pCollide = pStudioDataCurrent->m_pVCollide->GetVCollide();
  3277. g_pPhysicsCollision->VCollideLoad( pCollide, header.solidCount, (const char*)buf.PeekGet(), nBufSize );
  3278. if ( mod_check_vcollide.GetBool() )
  3279. {
  3280. g_pPhysicsCollision->VCollideCheck( pCollide, pStudioHdrCurrent->pszName() );
  3281. }
  3282. if ( m_pCacheNotify )
  3283. {
  3284. m_pCacheNotify->OnDataLoaded( MDLCACHE_VCOLLIDE, handle );
  3285. }
  3286. }
  3287. }
  3288. else
  3289. {
  3290. // Since it is legitimate to not have PHY data for a model, we only note this as a message and not a warning.
  3291. MdlCacheMsg( "MDLCache: Failed load of .PHY data for %s\n", pStudioHdrCurrent->pszName() );
  3292. return false;
  3293. }
  3294. break;
  3295. }
  3296. default:
  3297. Assert( 0 );
  3298. }
  3299. // success
  3300. return true;
  3301. }
  3302. //-----------------------------------------------------------------------------
  3303. // Returns:
  3304. // <0: indeterminate at this time
  3305. // =0: pending
  3306. // >0: completed
  3307. //-----------------------------------------------------------------------------
  3308. int CMDLCache::ProcessPendingAsync( intp iAsync )
  3309. {
  3310. if ( !ThreadInMainThread() || iAsync == NO_ASYNC )
  3311. {
  3312. return -1;
  3313. }
  3314. ASSERT_NO_REENTRY();
  3315. void *pData = NULL;
  3316. int nBytesRead = 0;
  3317. AsyncInfo_t *pInfo;
  3318. {
  3319. AUTO_LOCK_FM( m_AsyncMutex );
  3320. pInfo = &m_PendingAsyncs[iAsync];
  3321. }
  3322. Assert( pInfo->hControl );
  3323. FSAsyncStatus_t status = g_pFullFileSystem->AsyncGetResult( pInfo->hControl, &pData, &nBytesRead );
  3324. if ( status == FSASYNC_STATUS_PENDING )
  3325. {
  3326. return 0;
  3327. }
  3328. AsyncInfo_t info = *pInfo;
  3329. pInfo = &info;
  3330. ClearAsync( pInfo->hModel, pInfo->type, pInfo->iAnimBlock );
  3331. if( m_bFileNotFoundAllowed && status == FSASYNC_ERR_FILEOPEN )
  3332. {
  3333. // file not found here is valid so return complete
  3334. return 1;
  3335. }
  3336. //Assert( nBytesRead > 0 );
  3337. if ( nBytesRead <= 0 )
  3338. {
  3339. return 1;
  3340. }
  3341. switch ( pInfo->type )
  3342. {
  3343. case MDLCACHE_VERTEXES:
  3344. case MDLCACHE_STUDIOHWDATA:
  3345. case MDLCACHE_VCOLLIDE:
  3346. {
  3347. // NOTE: 'cacheData' deals with decompression/freeing of the incoming data
  3348. CUtlBuffer buf( pData, nBytesRead, CUtlBuffer::READ_ONLY );
  3349. CMDLCacheData cacheData( pInfo->type, CMDLCacheData::ALLOC_OPTIMALREADBUFFER, &buf );
  3350. if ( status != FSASYNC_OK )
  3351. cacheData.Purge();
  3352. ProcessDataIntoCache( pInfo->hModel, cacheData );
  3353. }
  3354. break;
  3355. case MDLCACHE_ANIMBLOCK:
  3356. {
  3357. // NOTES: - 'cacheData' deals with decompression/freeing of the incoming data
  3358. // - the cache assumes ownership of valid async'd data (invalid data gets freed)
  3359. // - see CMDLCache::UnserializeAnimBlock for how the incoming data was allocated (FreeAnimBlock will work in either case)
  3360. CUtlBuffer buf( pData, nBytesRead, CUtlBuffer::READ_ONLY );
  3361. CMDLCacheData cacheData( pInfo->type, CMDLCacheData::ALLOC_ANIMBLOCK, &buf );
  3362. if ( status != FSASYNC_OK )
  3363. cacheData.Purge();
  3364. ProcessDataIntoCache( pInfo->hModel, cacheData, pInfo->iAnimBlock );
  3365. }
  3366. break;
  3367. default:
  3368. {
  3369. Assert( 0 );
  3370. }
  3371. break;
  3372. }
  3373. return 1;
  3374. }
  3375. //-----------------------------------------------------------------------------
  3376. //
  3377. //-----------------------------------------------------------------------------
  3378. void CMDLCache::ProcessPendingAsyncs( MDLCacheDataType_t type )
  3379. {
  3380. if ( !ThreadInMainThread() )
  3381. {
  3382. return;
  3383. }
  3384. if ( !m_PendingAsyncs.Count() )
  3385. {
  3386. return;
  3387. }
  3388. static bool bReentering;
  3389. if ( bReentering )
  3390. {
  3391. return;
  3392. }
  3393. bReentering = true;
  3394. AUTO_LOCK_FM( m_AsyncMutex );
  3395. // Process all of the completed loads that were requested before a new one. This ensures two
  3396. // things -- the LRU is in correct order, and it catches precached items lurking
  3397. // in the async queue that have only been requested once (thus aren't being cached
  3398. // and might lurk forever, e.g., wood gibs in the citadel)
  3399. intp current = m_PendingAsyncs.Head();
  3400. while ( current != m_PendingAsyncs.InvalidIndex() )
  3401. {
  3402. intp next = m_PendingAsyncs.Next( current );
  3403. if ( type == MDLCACHE_NONE || m_PendingAsyncs[current].type == type )
  3404. {
  3405. // process, also removes from list
  3406. if ( ProcessPendingAsync( current ) <= 0 )
  3407. {
  3408. // indeterminate or pending
  3409. break;
  3410. }
  3411. }
  3412. current = next;
  3413. }
  3414. bReentering = false;
  3415. }
  3416. //-----------------------------------------------------------------------------
  3417. //
  3418. //-----------------------------------------------------------------------------
  3419. bool CMDLCache::ClearAsync( MDLHandle_t handle, MDLCacheDataType_t type, int iAnimBlock, bool bAbort )
  3420. {
  3421. intp iAsyncInfo = GetAsyncInfoIndex( handle, type, iAnimBlock );
  3422. if ( iAsyncInfo != NO_ASYNC )
  3423. {
  3424. AsyncInfo_t *pInfo;
  3425. {
  3426. AUTO_LOCK_FM( m_AsyncMutex );
  3427. pInfo = &m_PendingAsyncs[iAsyncInfo];
  3428. }
  3429. if ( pInfo->hControl )
  3430. {
  3431. if ( bAbort )
  3432. {
  3433. g_pFullFileSystem->AsyncAbort( pInfo->hControl );
  3434. void *pData;
  3435. int ignored;
  3436. if ( g_pFullFileSystem->AsyncGetResult( pInfo->hControl, &pData, &ignored ) == FSASYNC_OK )
  3437. {
  3438. if ( type != MDLCACHE_ANIMBLOCK )
  3439. {
  3440. g_pFullFileSystem->FreeOptimalReadBuffer( pData );
  3441. }
  3442. else
  3443. {
  3444. FreeAnimBlock( pData );
  3445. }
  3446. }
  3447. }
  3448. g_pFullFileSystem->AsyncRelease( pInfo->hControl );
  3449. pInfo->hControl = NULL;
  3450. }
  3451. SetAsyncInfoIndex( handle, type, iAnimBlock, NO_ASYNC );
  3452. {
  3453. AUTO_LOCK_FM( m_AsyncMutex );
  3454. m_PendingAsyncs.Remove( iAsyncInfo );
  3455. }
  3456. return true;
  3457. }
  3458. return false;
  3459. }
  3460. //-----------------------------------------------------------------------------
  3461. //-----------------------------------------------------------------------------
  3462. bool CMDLCache::GetAsyncLoad( MDLCacheDataType_t type )
  3463. {
  3464. switch ( type )
  3465. {
  3466. case MDLCACHE_STUDIOHDR:
  3467. return false;
  3468. case MDLCACHE_STUDIOHWDATA:
  3469. return mod_load_mesh_async.GetBool();
  3470. case MDLCACHE_VCOLLIDE:
  3471. return mod_load_vcollide_async.GetBool();
  3472. case MDLCACHE_ANIMBLOCK:
  3473. return mod_load_anims_async.GetBool();
  3474. case MDLCACHE_VIRTUALMODEL:
  3475. return false;
  3476. case MDLCACHE_VERTEXES:
  3477. return mod_load_mesh_async.GetBool();
  3478. }
  3479. return false;
  3480. }
  3481. //-----------------------------------------------------------------------------
  3482. //-----------------------------------------------------------------------------
  3483. bool CMDLCache::SetAsyncLoad( MDLCacheDataType_t type, bool bAsync )
  3484. {
  3485. bool bRetVal = false;
  3486. switch ( type )
  3487. {
  3488. case MDLCACHE_STUDIOHDR:
  3489. break;
  3490. case MDLCACHE_STUDIOHWDATA:
  3491. bRetVal = mod_load_mesh_async.GetBool();
  3492. mod_load_mesh_async.SetValue( bAsync );
  3493. break;
  3494. case MDLCACHE_VCOLLIDE:
  3495. bRetVal = mod_load_vcollide_async.GetBool();
  3496. mod_load_vcollide_async.SetValue( bAsync );
  3497. break;
  3498. case MDLCACHE_ANIMBLOCK:
  3499. bRetVal = mod_load_anims_async.GetBool();
  3500. mod_load_anims_async.SetValue( bAsync );
  3501. break;
  3502. case MDLCACHE_VIRTUALMODEL:
  3503. return false;
  3504. break;
  3505. case MDLCACHE_VERTEXES:
  3506. bRetVal = mod_load_mesh_async.GetBool();
  3507. mod_load_mesh_async.SetValue( bAsync );
  3508. break;
  3509. }
  3510. return bRetVal;
  3511. }
  3512. //-----------------------------------------------------------------------------
  3513. // Cache model's specified dynamic data
  3514. //-----------------------------------------------------------------------------
  3515. vertexFileHeader_t *CMDLCache::BuildAndCacheVertexData( studiohdr_t *pStudioHdr, CMDLCacheData &cacheData )
  3516. {
  3517. MDLHandle_t handle = VoidPtrToMDLHandle( pStudioHdr->VirtualModel() );
  3518. vertexFileHeader_t *pRawVvdHdr, *pVvdHdr;
  3519. MdlCacheMsg( "MDLCache: Load VVD for %s\n", pStudioHdr->pszName() );
  3520. pRawVvdHdr = (vertexFileHeader_t *)cacheData.Data();
  3521. Assert( pRawVvdHdr );
  3522. // check header
  3523. if ( pRawVvdHdr->id != MODEL_VERTEX_FILE_ID )
  3524. {
  3525. Warning( "Error Vertex File for '%s' id %d should be %d\n", pStudioHdr->pszName(), pRawVvdHdr->id, MODEL_VERTEX_FILE_ID );
  3526. return NULL;
  3527. }
  3528. if ( pRawVvdHdr->version != MODEL_VERTEX_FILE_VERSION )
  3529. {
  3530. Warning( "Error Vertex File for '%s' version %d should be %d\n", pStudioHdr->pszName(), pRawVvdHdr->version, MODEL_VERTEX_FILE_VERSION );
  3531. return NULL;
  3532. }
  3533. if ( pRawVvdHdr->checksum != pStudioHdr->checksum )
  3534. {
  3535. Warning( "Error Vertex File for '%s' checksum %ld should be %ld\n", pStudioHdr->pszName(), pRawVvdHdr->checksum, pStudioHdr->checksum );
  3536. return NULL;
  3537. }
  3538. Assert( pRawVvdHdr->numLODs );
  3539. if ( !pRawVvdHdr->numLODs )
  3540. {
  3541. return NULL;
  3542. }
  3543. bool bNeedsTangentS = true;
  3544. int rootLOD = MIN( pStudioHdr->rootLOD, pRawVvdHdr->numLODs - 1 );
  3545. bool bHasExtraData = (pStudioHdr->flags & STUDIOHDR_FLAGS_EXTRA_VERTEX_DATA) != 0;
  3546. // determine final cache footprint, possibly truncated due to lod
  3547. int cacheLength = Studio_VertexDataSize( pRawVvdHdr, rootLOD, bNeedsTangentS, bHasExtraData );
  3548. MdlCacheMsg( "MDLCache: Alloc VVD %s\n", GetModelName( handle ) );
  3549. // allocate cache space
  3550. MemAlloc_PushAllocDbgInfo( "Models:Vertex data", 0);
  3551. pVvdHdr = (vertexFileHeader_t *)AllocData( MDLCACHE_VERTEXES, cacheLength );
  3552. MemAlloc_PopAllocDbgInfo();
  3553. GetCacheSection( MDLCACHE_VERTEXES )->BeginFrameLocking();
  3554. CacheData( &m_MDLDict[handle]->m_VertexCache, pVvdHdr, cacheLength, pStudioHdr->pszName(), MDLCACHE_VERTEXES, MakeCacheID( handle, MDLCACHE_VERTEXES) );
  3555. // expected 32 byte alignment
  3556. Assert( ((uintp)pVvdHdr & 0x1F) == 0 );
  3557. // load minimum vertexes and fixup
  3558. Studio_LoadVertexes( pRawVvdHdr, pVvdHdr, rootLOD, bNeedsTangentS, bHasExtraData );
  3559. GetCacheSection( MDLCACHE_VERTEXES )->EndFrameLocking();
  3560. return pVvdHdr;
  3561. }
  3562. //-----------------------------------------------------------------------------
  3563. // Load and cache model's specified dynamic data
  3564. //-----------------------------------------------------------------------------
  3565. vertexFileHeader_t *CMDLCache::LoadVertexData( studiohdr_t *pStudioHdr )
  3566. {
  3567. char pFileName[MAX_PATH];
  3568. MDLHandle_t handle;
  3569. Assert( pStudioHdr );
  3570. handle = VoidPtrToMDLHandle( pStudioHdr->VirtualModel() );
  3571. Assert( !m_MDLDict[handle]->m_VertexCache );
  3572. studiodata_t *pStudioData = m_MDLDict[handle];
  3573. if ( pStudioData->m_nFlags & STUDIODATA_FLAGS_NO_VERTEX_DATA )
  3574. {
  3575. return NULL;
  3576. }
  3577. intp iAsync = GetAsyncInfoIndex( handle, MDLCACHE_VERTEXES );
  3578. if ( iAsync == NO_ASYNC )
  3579. {
  3580. // load the VVD file
  3581. // use model name for correct path
  3582. MakeFilename( pFileName, pStudioHdr, ".vvd" );
  3583. if ( IsGameConsole() )
  3584. {
  3585. char pX360Filename[MAX_PATH];
  3586. UpdateOrCreate( pStudioHdr, pFileName, pX360Filename, sizeof( pX360Filename ), "GAME" );
  3587. Q_strncpy( pFileName, pX360Filename, sizeof(pX360Filename) );
  3588. }
  3589. MdlCacheMsg( "MDLCache: Begin load VVD %s\n", pFileName );
  3590. AsyncInfo_t info;
  3591. if ( IsDebug() )
  3592. {
  3593. memset( &info, 0xdd, sizeof( AsyncInfo_t ) );
  3594. }
  3595. info.hModel = handle;
  3596. info.type = MDLCACHE_VERTEXES;
  3597. info.iAnimBlock = 0;
  3598. info.hControl = NULL;
  3599. LoadData( pFileName, "GAME", mod_load_mesh_async.GetBool(), &info.hControl, handle );
  3600. {
  3601. AUTO_LOCK_FM( m_AsyncMutex );
  3602. iAsync = SetAsyncInfoIndex( handle, MDLCACHE_VERTEXES, m_PendingAsyncs.AddToTail( info ) );
  3603. }
  3604. }
  3605. ProcessPendingAsync( iAsync );
  3606. if ( !mod_load_mesh_async.GetBool() && mod_lock_meshes_on_load.GetBool() )
  3607. {
  3608. m_MDLDict[handle]->m_pForceLockedVertexFileHeader = (vertexFileHeader_t *)GetCacheSection( MDLCACHE_VERTEXES )->Lock( m_MDLDict[handle]->m_VertexCache );
  3609. }
  3610. return (vertexFileHeader_t *)CheckData( m_MDLDict[handle]->m_VertexCache, MDLCACHE_VERTEXES );
  3611. }
  3612. //-----------------------------------------------------------------------------
  3613. //
  3614. //-----------------------------------------------------------------------------
  3615. vertexFileHeader_t *CMDLCache::GetVertexData( MDLHandle_t handle )
  3616. {
  3617. if ( mod_test_not_available.GetBool() )
  3618. return NULL;
  3619. if ( mod_test_verts_not_available.GetBool() )
  3620. return NULL;
  3621. if ( m_MDLDict[handle]->m_pForceLockedVertexFileHeader )
  3622. {
  3623. return m_MDLDict[handle]->m_pForceLockedVertexFileHeader;
  3624. }
  3625. return CacheVertexData( GetStudioHdr( handle ) );
  3626. }
  3627. //-----------------------------------------------------------------------------
  3628. // Allocates a cacheable item
  3629. //-----------------------------------------------------------------------------
  3630. void *CMDLCache::AllocData( MDLCacheDataType_t type, int size )
  3631. {
  3632. void *pData = _aligned_malloc( size, 32 );
  3633. if ( !pData )
  3634. {
  3635. Error( "CMDLCache:: Out of memory" );
  3636. return NULL;
  3637. }
  3638. return pData;
  3639. }
  3640. //-----------------------------------------------------------------------------
  3641. // Caches an item
  3642. //-----------------------------------------------------------------------------
  3643. void CMDLCache::CacheData( DataCacheHandle_t *c, void *pData, int size, const char *name, MDLCacheDataType_t type, DataCacheClientID_t id )
  3644. {
  3645. if ( !pData )
  3646. {
  3647. return;
  3648. }
  3649. if ( id == (DataCacheClientID_t)-1 )
  3650. id = (DataCacheClientID_t)pData;
  3651. GetCacheSection( type )->Add(id, pData, size, c );
  3652. }
  3653. //-----------------------------------------------------------------------------
  3654. // returns the cached data, and moves to the head of the LRU list
  3655. // if present, otherwise returns NULL
  3656. //-----------------------------------------------------------------------------
  3657. void *CMDLCache::CheckData( DataCacheHandle_t c, MDLCacheDataType_t type )
  3658. {
  3659. return GetCacheSection( type )->Get( c, true );
  3660. }
  3661. //-----------------------------------------------------------------------------
  3662. // returns the cached data, if present, otherwise returns NULL
  3663. //-----------------------------------------------------------------------------
  3664. void *CMDLCache::CheckDataNoTouch( DataCacheHandle_t c, MDLCacheDataType_t type )
  3665. {
  3666. return GetCacheSection( type )->GetNoTouch( c, true );
  3667. }
  3668. //-----------------------------------------------------------------------------
  3669. // Frees a cache item
  3670. //-----------------------------------------------------------------------------
  3671. void CMDLCache::UncacheData( DataCacheHandle_t c, MDLCacheDataType_t type, bool bLockedOk )
  3672. {
  3673. if ( c == DC_INVALID_HANDLE )
  3674. return;
  3675. IDataCacheSection *pSection = GetCacheSection( type );
  3676. if ( !pSection->IsPresent( c ) )
  3677. return;
  3678. if ( !bLockedOk )
  3679. {
  3680. if ( pSection->GetLockCount( c ) > 0 )
  3681. {
  3682. return;
  3683. }
  3684. }
  3685. pSection->BreakLock( c );
  3686. const void *pItemData;
  3687. pSection->Remove( c, &pItemData );
  3688. FreeData( type, (void *)pItemData );
  3689. }
  3690. //-----------------------------------------------------------------------------
  3691. // Frees memory for an item
  3692. //-----------------------------------------------------------------------------
  3693. void CMDLCache::FreeData( MDLCacheDataType_t type, void *pData )
  3694. {
  3695. if ( type != MDLCACHE_ANIMBLOCK )
  3696. {
  3697. _aligned_free( (void *)pData );
  3698. }
  3699. else
  3700. {
  3701. FreeAnimBlock( pData );
  3702. }
  3703. }
  3704. void CMDLCache::InitPreloadData( bool rebuild )
  3705. {
  3706. }
  3707. void CMDLCache::ShutdownPreloadData()
  3708. {
  3709. }
  3710. //-----------------------------------------------------------------------------
  3711. // Work function for processing a model delivered by the queued loader.
  3712. // ProcessDataIntoCache() is invoked for each MDL datum.
  3713. //-----------------------------------------------------------------------------
  3714. void CMDLCache::ProcessQueuedData( ModelParts_t *pModelParts )
  3715. {
  3716. // the studiohdr is critical, ensure it's setup as expected
  3717. MDLHandle_t handle = pModelParts->hMDL;
  3718. studiohdr_t *pStudioHdr = NULL;
  3719. if ( pModelParts->nLoadedParts & ( 1 << ModelParts_t::BUFFER_MDL ) )
  3720. {
  3721. CMDLCacheData cacheData( MDLCACHE_STUDIOHDR, CMDLCacheData::ALLOC_OPTIMALREADBUFFER, &pModelParts->Buffers[ModelParts_t::BUFFER_MDL] );
  3722. if ( cacheData.Data() )
  3723. {
  3724. ProcessDataIntoCache( handle, cacheData );
  3725. }
  3726. }
  3727. bool bAbort = false;
  3728. pStudioHdr = (studiohdr_t *)CheckDataNoTouch( m_MDLDict[handle]->m_MDLCache, MDLCACHE_STUDIOHDR );
  3729. if ( !pStudioHdr )
  3730. {
  3731. // huh?, the header is expected to be loaded and locked, everything depends on it!
  3732. Assert( 0 );
  3733. DevWarning( "CMDLCache:: Error MDLCACHE_STUDIOHDR not present for '%s'\n", GetModelName( handle ) );
  3734. // cannot unravel any of this model's dependant data, abort any further processing
  3735. bAbort = true;
  3736. }
  3737. if ( pModelParts->nLoadedParts & ( 1 << ModelParts_t::BUFFER_PHY ) )
  3738. {
  3739. CMDLCacheData cacheData( MDLCACHE_VCOLLIDE, CMDLCacheData::ALLOC_OPTIMALREADBUFFER, &pModelParts->Buffers[ModelParts_t::BUFFER_PHY] );
  3740. if ( bAbort )
  3741. cacheData.Purge();
  3742. ProcessDataIntoCache( handle, cacheData );
  3743. }
  3744. // vvd vertexes before vtx
  3745. if ( pModelParts->nLoadedParts & ( 1 << ModelParts_t::BUFFER_VVD ) )
  3746. {
  3747. CMDLCacheData cacheData( MDLCACHE_VERTEXES, CMDLCacheData::ALLOC_OPTIMALREADBUFFER, &pModelParts->Buffers[ModelParts_t::BUFFER_VVD] );
  3748. if ( bAbort )
  3749. cacheData.Purge();
  3750. ProcessDataIntoCache( handle, cacheData );
  3751. }
  3752. // can construct meshes after vvd and vtx vertexes arrive
  3753. if ( pModelParts->nLoadedParts & ( 1 << ModelParts_t::BUFFER_VTX ) )
  3754. {
  3755. CMDLCacheData cacheData( MDLCACHE_STUDIOHWDATA, CMDLCacheData::ALLOC_OPTIMALREADBUFFER, &pModelParts->Buffers[ModelParts_t::BUFFER_VTX] );
  3756. if ( bAbort )
  3757. cacheData.Purge();
  3758. // ProcessDataIntoCache() will do an unlock, so lock
  3759. studiodata_t *pStudioData = m_MDLDict[handle];
  3760. GetCacheSection( MDLCACHE_STUDIOHWDATA )->Lock( pStudioData->m_VertexCache );
  3761. {
  3762. // constructing the static meshes isn't thread safe
  3763. AUTO_LOCK_FM( m_QueuedLoadingMutex );
  3764. ProcessDataIntoCache( handle, cacheData );
  3765. }
  3766. }
  3767. delete pModelParts;
  3768. }
  3769. //-----------------------------------------------------------------------------
  3770. // Journals each of the incoming MDL components until all arrive (or error).
  3771. // Not all components exist, but that information is not known at job submission.
  3772. //-----------------------------------------------------------------------------
  3773. void CMDLCache::QueuedLoaderCallback_MDL( void *pContext, void *pContext2, const void *pData, int nSize, LoaderError_t loaderError )
  3774. {
  3775. // validity is denoted by a nonzero buffer
  3776. nSize = ( loaderError == LOADERERROR_NONE ) ? nSize : 0;
  3777. // journal each incoming buffer
  3778. ModelParts_t *pModelParts = (ModelParts_t *)pContext;
  3779. ModelParts_t::BufferType_t bufferType = static_cast< ModelParts_t::BufferType_t >(size_cast<int>( (intp) pContext2 ) );
  3780. pModelParts->Buffers[bufferType].SetExternalBuffer( (void *)pData, nSize, nSize, CUtlBuffer::READ_ONLY );
  3781. pModelParts->nLoadedParts += (1 << bufferType);
  3782. // wait for all components
  3783. if ( pModelParts->DoFinalProcessing() )
  3784. {
  3785. // now have all components, process the raw data into the cache
  3786. g_MDLCache.ProcessQueuedData( pModelParts );
  3787. }
  3788. }
  3789. //-----------------------------------------------------------------------------
  3790. // Build a queued loader job to get the MDL ant all of its components into the cache.
  3791. //-----------------------------------------------------------------------------
  3792. bool CMDLCache::PreloadModel( MDLHandle_t handle )
  3793. {
  3794. if ( !IsGameConsole() )
  3795. {
  3796. return false;
  3797. }
  3798. if ( !g_pQueuedLoader->IsMapLoading() || handle == MDLHANDLE_INVALID )
  3799. {
  3800. return false;
  3801. }
  3802. if ( !g_pQueuedLoader->IsBatching() )
  3803. {
  3804. // batching must be active, following code depends on its behavior
  3805. DevWarning( "CMDLCache:: Late preload of model '%s'\n", GetModelName( handle ) );
  3806. return false;
  3807. }
  3808. // determine existing presence
  3809. // actual necessity is not established here, allowable absent files need their i/o error to occur
  3810. // queued loader has additional info and may inhibit some specific model's types
  3811. bool bNeedsMDL = !IsDataLoaded( handle, MDLCACHE_STUDIOHDR );
  3812. bool bNeedsVTX = !IsDataLoaded( handle, MDLCACHE_STUDIOHWDATA );
  3813. bool bNeedsVVD = !IsDataLoaded( handle, MDLCACHE_VERTEXES );
  3814. bool bNeedsPHY = !IsDataLoaded( handle, MDLCACHE_VCOLLIDE );
  3815. if ( !bNeedsMDL && !bNeedsVTX && !bNeedsVVD && !bNeedsPHY )
  3816. {
  3817. // already in cache, nothing to do
  3818. return true;
  3819. }
  3820. char szFilename[MAX_PATH];
  3821. char szNameOnDisk[MAX_PATH];
  3822. V_strncpy( szFilename, GetActualModelName( handle ), sizeof( szFilename ) );
  3823. V_StripExtension( szFilename, szFilename, sizeof( szFilename ) );
  3824. // need to gather all model parts (mdl, vtx, vvd, phy, ani)
  3825. ModelParts_t *pModelParts = new ModelParts_t;
  3826. pModelParts->hMDL = handle;
  3827. // create multiple loader jobs to perform gathering i/o operations
  3828. LoaderJob_t loaderJob;
  3829. loaderJob.m_pPathID = "GAME";
  3830. loaderJob.m_pCallback = QueuedLoaderCallback_MDL;
  3831. loaderJob.m_pContext = (void *)pModelParts;
  3832. loaderJob.m_Priority = LOADERPRIORITY_DURINGPRELOAD;
  3833. loaderJob.m_bPersistTargetData = true;
  3834. if ( bNeedsMDL )
  3835. {
  3836. V_snprintf( szNameOnDisk, sizeof( szNameOnDisk ), "%s%s.mdl", szFilename, GetPlatformExt() );
  3837. loaderJob.m_pFilename = szNameOnDisk;
  3838. loaderJob.m_pContext2 = (void *)ModelParts_t::BUFFER_MDL;
  3839. if ( g_pQueuedLoader->AddJob( &loaderJob ) )
  3840. {
  3841. pModelParts->nExpectedParts |= 1 << ModelParts_t::BUFFER_MDL;
  3842. }
  3843. }
  3844. if ( bNeedsVTX )
  3845. {
  3846. // vtx extensions are .xxx.vtx, need to re-form as, ???.xxx.yyy.vtx
  3847. char szTempName[MAX_PATH];
  3848. V_snprintf( szNameOnDisk, sizeof( szNameOnDisk ), "%s%s", szFilename, GetVTXExtension() );
  3849. V_StripExtension( szNameOnDisk, szTempName, sizeof( szTempName ) );
  3850. V_snprintf( szNameOnDisk, sizeof( szNameOnDisk ), "%s%s.vtx", szTempName, GetPlatformExt() );
  3851. loaderJob.m_pFilename = szNameOnDisk;
  3852. loaderJob.m_pContext2 = (void *)ModelParts_t::BUFFER_VTX;
  3853. if ( g_pQueuedLoader->AddJob( &loaderJob ) )
  3854. {
  3855. pModelParts->nExpectedParts |= 1 << ModelParts_t::BUFFER_VTX;
  3856. }
  3857. }
  3858. if ( bNeedsVVD )
  3859. {
  3860. V_snprintf( szNameOnDisk, sizeof( szNameOnDisk ), "%s%s.vvd", szFilename, GetPlatformExt() );
  3861. loaderJob.m_pFilename = szNameOnDisk;
  3862. loaderJob.m_pContext2 = (void *)ModelParts_t::BUFFER_VVD;
  3863. if ( g_pQueuedLoader->AddJob( &loaderJob ) )
  3864. {
  3865. pModelParts->nExpectedParts |= 1 << ModelParts_t::BUFFER_VVD;
  3866. }
  3867. }
  3868. if ( bNeedsPHY )
  3869. {
  3870. V_snprintf( szNameOnDisk, sizeof( szNameOnDisk ), "%s%s.phy", szFilename, GetPlatformExt() );
  3871. loaderJob.m_pFilename = szNameOnDisk;
  3872. loaderJob.m_pContext2 = (void *)ModelParts_t::BUFFER_PHY;
  3873. if ( g_pQueuedLoader->AddJob( &loaderJob ) )
  3874. {
  3875. pModelParts->nExpectedParts |= 1 << ModelParts_t::BUFFER_PHY;
  3876. }
  3877. }
  3878. if ( !pModelParts->nExpectedParts )
  3879. {
  3880. // further logic showed that no components are actually needed
  3881. delete pModelParts;
  3882. }
  3883. return true;
  3884. }
  3885. bool CMDLCache::ProcessPendingHardwareRestore()
  3886. {
  3887. if ( !m_QueuedAsyncHardwareLoads.Count() )
  3888. {
  3889. // nothing to do
  3890. return false;
  3891. }
  3892. bool bDataProcessed = false;
  3893. AsyncHardwareLoad_t asyncHardwareLoad;
  3894. while ( m_QueuedAsyncHardwareLoads.PopItem( &asyncHardwareLoad ) )
  3895. {
  3896. ModelParts_t *pModelParts = asyncHardwareLoad.m_pModelParts;
  3897. // the studiohdr should still be there, otherwise the restoration has become invalid
  3898. bool bError = !IsDataLoaded( pModelParts->hMDL, MDLCACHE_STUDIOHDR );
  3899. // all i/o for data components must have succeeded or they all get purged
  3900. bError |= ( ( pModelParts->nExpectedParts & ( 1 << ModelParts_t::BUFFER_VTX ) ) && ( pModelParts->Buffers[ModelParts_t::BUFFER_VTX].Size() == 0 ) );
  3901. bError |= ( ( pModelParts->nExpectedParts & ( 1 << ModelParts_t::BUFFER_VVD ) ) && ( pModelParts->Buffers[ModelParts_t::BUFFER_VVD].Size() == 0 ) );
  3902. if ( bError )
  3903. {
  3904. // unexpected error, purge all
  3905. if ( pModelParts->nExpectedParts & ( 1 << ModelParts_t::BUFFER_VTX ) )
  3906. {
  3907. void *pVTXData = asyncHardwareLoad.m_pModelParts->Buffers[ModelParts_t::BUFFER_VTX].Detach();
  3908. if ( pVTXData )
  3909. {
  3910. g_pFullFileSystem->FreeOptimalReadBuffer( pVTXData );
  3911. }
  3912. }
  3913. if ( pModelParts->nExpectedParts & ( 1 << ModelParts_t::BUFFER_VVD ) )
  3914. {
  3915. void *pVVDData = asyncHardwareLoad.m_pModelParts->Buffers[ModelParts_t::BUFFER_VVD].Detach();
  3916. if ( pVVDData )
  3917. {
  3918. g_pFullFileSystem->FreeOptimalReadBuffer( pVVDData );
  3919. }
  3920. }
  3921. delete pModelParts;
  3922. }
  3923. else
  3924. {
  3925. DevMsg( "*** Restoring: %s\n", GetModelName( pModelParts->hMDL ) );
  3926. // now have all components, process the raw data into the cache
  3927. g_MDLCache.ProcessQueuedData( pModelParts );
  3928. // a non trivial operation (static mesh buildout) occurred
  3929. bDataProcessed = true;
  3930. }
  3931. }
  3932. return bDataProcessed;
  3933. }
  3934. void CMDLCache::OnAsyncHardwareDataComplete( ModelParts_t::BufferType_t bufferType, ModelParts_t *pModelParts, void *pData, int nNumReadBytes, FSAsyncStatus_t asyncStatus )
  3935. {
  3936. // validity is denoted by a nonzero buffer
  3937. // error handling is deferred until the processing stage drains the queue
  3938. int nSize = ( asyncStatus == FSASYNC_OK ) ? nNumReadBytes : 0;
  3939. // journal each incoming buffer
  3940. pModelParts->Buffers[bufferType].SetExternalBuffer( pData, nSize, nSize, CUtlBuffer::READ_ONLY );
  3941. pModelParts->nLoadedParts += (1 << bufferType);
  3942. // wait for all components
  3943. if ( pModelParts->DoFinalProcessing() )
  3944. {
  3945. // queue the async loaded hardware data, cannot deal with updating the hardware data until the main thread
  3946. AsyncHardwareLoad_t asyncHardwareLoad;
  3947. asyncHardwareLoad.m_pModelParts = pModelParts;
  3948. m_QueuedAsyncHardwareLoads.PushItem( asyncHardwareLoad );
  3949. }
  3950. }
  3951. static void IOAsyncVTXCallback( const FileAsyncRequest_t &asyncRequest, int numReadBytes, FSAsyncStatus_t asyncStatus )
  3952. {
  3953. g_MDLCache.OnAsyncHardwareDataComplete( ModelParts_t::BUFFER_VTX, (ModelParts_t *)asyncRequest.pContext, asyncRequest.pData, numReadBytes, asyncStatus );
  3954. }
  3955. static void IOAsyncVVDCallback( const FileAsyncRequest_t &asyncRequest, int numReadBytes, FSAsyncStatus_t asyncStatus )
  3956. {
  3957. g_MDLCache.OnAsyncHardwareDataComplete( ModelParts_t::BUFFER_VVD, (ModelParts_t *)asyncRequest.pContext, asyncRequest.pData, numReadBytes, asyncStatus );
  3958. }
  3959. //-----------------------------------------------------------------------------
  3960. // Very specialized back door for the weapon model cache to restore the HW data it evicted.
  3961. //-----------------------------------------------------------------------------
  3962. bool CMDLCache::RestoreHardwareData( MDLHandle_t handle, FSAsyncControl_t *pAsyncVTXControl, FSAsyncControl_t *pAsyncVVDControl )
  3963. {
  3964. if ( !IsGameConsole() )
  3965. {
  3966. return false;
  3967. }
  3968. if ( *pAsyncVTXControl || *pAsyncVVDControl )
  3969. {
  3970. // already scheduled
  3971. return false;
  3972. }
  3973. bool bNeedsVTX = !IsDataLoaded( handle, MDLCACHE_STUDIOHWDATA );
  3974. if ( !bNeedsVTX )
  3975. {
  3976. // already in cache, nothing to do
  3977. return false;
  3978. }
  3979. bool bNeedsVVD = !IsDataLoaded( handle, MDLCACHE_VERTEXES );
  3980. char szFilename[MAX_PATH];
  3981. char szNameOnDisk[MAX_PATH];
  3982. V_strncpy( szFilename, GetActualModelName( handle ), sizeof( szFilename ) );
  3983. V_StripExtension( szFilename, szFilename, sizeof( szFilename ) );
  3984. // need to gather all model parts (vtx, vvd)
  3985. ModelParts_t *pModelParts = new ModelParts_t;
  3986. pModelParts->hMDL = handle;
  3987. if ( bNeedsVTX )
  3988. {
  3989. // vtx extensions are .xxx.vtx, need to re-form as, ???.xxx.yyy.vtx
  3990. char szTempName[MAX_PATH];
  3991. V_snprintf( szNameOnDisk, sizeof( szNameOnDisk ), "%s%s", szFilename, GetVTXExtension() );
  3992. V_StripExtension( szNameOnDisk, szTempName, sizeof( szTempName ) );
  3993. V_snprintf( szNameOnDisk, sizeof( szNameOnDisk ), "%s%s.vtx", szTempName, GetPlatformExt() );
  3994. // schedule the async
  3995. FileAsyncRequest_t asyncRequest;
  3996. asyncRequest.pszFilename = szNameOnDisk;
  3997. asyncRequest.pszPathID = "GAME";
  3998. asyncRequest.priority = -1;
  3999. asyncRequest.flags = FSASYNC_FLAGS_ALLOCNOFREE;
  4000. asyncRequest.pContext = (void *)pModelParts;
  4001. asyncRequest.pfnCallback = IOAsyncVTXCallback;
  4002. g_pFullFileSystem->AsyncRead( asyncRequest, pAsyncVTXControl );
  4003. pModelParts->nExpectedParts |= 1 << ModelParts_t::BUFFER_VTX;
  4004. }
  4005. if ( bNeedsVVD )
  4006. {
  4007. V_snprintf( szNameOnDisk, sizeof( szNameOnDisk ), "%s%s.vvd", szFilename, GetPlatformExt() );
  4008. // schedule the async
  4009. FileAsyncRequest_t asyncRequest;
  4010. asyncRequest.pszFilename = szNameOnDisk;
  4011. asyncRequest.pszPathID = "GAME";
  4012. asyncRequest.priority = -1;
  4013. asyncRequest.flags = FSASYNC_FLAGS_ALLOCNOFREE;
  4014. asyncRequest.pContext = (void *)pModelParts;
  4015. asyncRequest.pfnCallback = IOAsyncVVDCallback;
  4016. g_pFullFileSystem->AsyncRead( asyncRequest, pAsyncVVDControl );
  4017. pModelParts->nExpectedParts |= 1 << ModelParts_t::BUFFER_VVD;
  4018. }
  4019. if ( !pModelParts->nExpectedParts )
  4020. {
  4021. // further logic showed that no components are actually needed
  4022. delete pModelParts;
  4023. return false;
  4024. }
  4025. return true;
  4026. }
  4027. //-----------------------------------------------------------------------------
  4028. // Purpose: Clear the STUDIODATA_ERROR_MODEL flag.
  4029. //-----------------------------------------------------------------------------
  4030. void CMDLCache::ResetErrorModelStatus( MDLHandle_t handle )
  4031. {
  4032. if ( handle == MDLHANDLE_INVALID )
  4033. return;
  4034. // added STUDIODATA_FLAGS_NO_STUDIOMESH for hammer when the dir watching catches a file in mid-processing.
  4035. // otherwise, this flag is permanently set and no future loading will happen, even once the model is valid.
  4036. m_MDLDict[handle]->m_nFlags &= ~( STUDIODATA_ERROR_MODEL | STUDIODATA_FLAGS_NO_STUDIOMESH );
  4037. }
  4038. //-----------------------------------------------------------------------------
  4039. //
  4040. //-----------------------------------------------------------------------------
  4041. void CMDLCache::MarkFrame()
  4042. {
  4043. ProcessPendingAsyncs();
  4044. }
  4045. bool CMDLCache::ReleaseAnimBlockAllocator()
  4046. {
  4047. if ( !g_AnimBlockAllocator.IsEmpty() )
  4048. {
  4049. Warning( "Failure to release anim block allocator, unexpected remaining allocations\n" );
  4050. return false;
  4051. }
  4052. g_AnimBlockAllocator.Clear();
  4053. return true;
  4054. }
  4055. //-----------------------------------------------------------------------------
  4056. // Purpose: bind studiohdr_t support functions to the mdlcacher
  4057. //-----------------------------------------------------------------------------
  4058. const studiohdr_t *studiohdr_t::FindModel( void **cache, char const *pModelName ) const
  4059. {
  4060. MDLHandle_t handle = g_MDLCache.FindMDL( pModelName );
  4061. *cache = (void*)(uintp)handle;
  4062. return g_MDLCache.GetStudioHdr( handle );
  4063. }
  4064. virtualmodel_t *studiohdr_t::GetVirtualModel( void ) const
  4065. {
  4066. if (numincludemodels == 0)
  4067. return NULL;
  4068. return g_MDLCache.GetVirtualModelFast( this, VoidPtrToMDLHandle( VirtualModel() ) );
  4069. }
  4070. byte *studiohdr_t::GetAnimBlock( int i, bool preloadIfMissing ) const
  4071. {
  4072. return g_MDLCache.GetAnimBlock( VoidPtrToMDLHandle( VirtualModel() ), i, preloadIfMissing );
  4073. }
  4074. // Shame that this code is duplicated... :(
  4075. bool studiohdr_t::hasAnimBlockBeenPreloaded( int i ) const
  4076. {
  4077. return g_pMDLCache->HasAnimBlockBeenPreloaded( VoidPtrToMDLHandle( VirtualModel() ), i );
  4078. }
  4079. int studiohdr_t::GetAutoplayList( unsigned short **pOut ) const
  4080. {
  4081. return g_MDLCache.GetAutoplayList( VoidPtrToMDLHandle( VirtualModel() ), pOut );
  4082. }
  4083. const studiohdr_t *virtualgroup_t::GetStudioHdr( void ) const
  4084. {
  4085. return g_MDLCache.GetStudioHdr( VoidPtrToMDLHandle( cache ) );
  4086. }
  4087. // combined models
  4088. MDLHandle_t CMDLCache::CreateCombinedModel( const char *pszModelName )
  4089. {
  4090. char szPlaceholderName[ MAX_PATH ];
  4091. char szFinalName[ MAX_PATH ];
  4092. V_strcpy_safe( szFinalName, pszModelName );
  4093. V_RemoveDotSlashes( szFinalName, '/' );
  4094. V_sprintf_safe( szPlaceholderName, "combined_placeholder_%s", pszModelName );
  4095. V_RemoveDotSlashes( szPlaceholderName, '/' );
  4096. MDLHandle_t FinalHandle = m_MDLDict.Find( szFinalName );
  4097. MDLHandle_t PlaceholderHandle = m_MDLDict.Find( szPlaceholderName );
  4098. if ( FinalHandle == m_MDLDict.InvalidIndex() && PlaceholderHandle == m_MDLDict.InvalidIndex() )
  4099. {
  4100. TCombinedStudioData *pCombinedStudioData = ( TCombinedStudioData * )malloc( sizeof( TCombinedStudioData ) );
  4101. memset( pCombinedStudioData, 0, sizeof( *pCombinedStudioData ) );
  4102. pCombinedStudioData->m_nReferenceFlags = COMBINED_REFERENCE_PLACEHOLDER | COMBINED_REFERENCE_PRIMARY | COMBINED_REFERENCE_COMBINER;
  4103. FinalHandle = m_MDLDict.Insert( szFinalName, NULL );
  4104. InitStudioData( FinalHandle );
  4105. studiodata_t *pFinalStudioData = m_MDLDict[ FinalHandle ];
  4106. PlaceholderHandle = m_MDLDict.Insert( szPlaceholderName, NULL );
  4107. InitStudioData( PlaceholderHandle );
  4108. studiodata_t *pPlaceholderStudioData = m_MDLDict[ PlaceholderHandle ];
  4109. V_strcpy_safe( pCombinedStudioData->m_szCombinedModelName, pszModelName );
  4110. pCombinedStudioData->m_pPlaceholderStudioData = pPlaceholderStudioData;
  4111. pCombinedStudioData->m_PlaceholderHandle = PlaceholderHandle;
  4112. pCombinedStudioData->m_pFinalStudioData = pFinalStudioData;
  4113. pCombinedStudioData->m_FinalHandle = FinalHandle;
  4114. pCombinedStudioData->m_pCombinedUserData = 0;
  4115. pCombinedStudioData->m_CallbackFunc = 0;
  4116. pPlaceholderStudioData->m_pCombinedStudioData = pCombinedStudioData;
  4117. pPlaceholderStudioData->m_nFlags |= STUDIODATA_FLAGS_COMBINED_PLACEHOLDER | STUDIODATA_FLAGS_COMBINED_ASSET;
  4118. pFinalStudioData->m_pCombinedStudioData = pCombinedStudioData;
  4119. pFinalStudioData->m_nFlags |= STUDIODATA_FLAGS_COMBINED | STUDIODATA_FLAGS_COMBINED_UNAVAILABLE | STUDIODATA_FLAGS_COMBINED_ASSET;
  4120. #ifdef DEBUG_COMBINER
  4121. Msg( "%p Alloc: pPlaceholderStudioData=%p, pFinalStudioData=%p\n", pCombinedStudioData, pPlaceholderStudioData, pFinalStudioData );
  4122. #endif
  4123. }
  4124. else
  4125. {
  4126. AssertMsg1( false, "Asking to combine model '%s' when it already has a placeholder or final model handle", pszModelName );
  4127. return MDLHANDLE_INVALID;
  4128. }
  4129. InitCombiner();
  4130. AddRef( FinalHandle );
  4131. AddRef( PlaceholderHandle );
  4132. return PlaceholderHandle;
  4133. }
  4134. bool CMDLCache::CreateCombinedModel( MDLHandle_t handle )
  4135. {
  4136. studiodata_t *pFinalStudioData = m_MDLDict[ handle ];
  4137. if ( pFinalStudioData == NULL )
  4138. {
  4139. return false;
  4140. }
  4141. TCombinedStudioData *pCombinedStudioData = pFinalStudioData->m_pCombinedStudioData;
  4142. if ( pCombinedStudioData == NULL )
  4143. {
  4144. pCombinedStudioData = ( TCombinedStudioData * )malloc( sizeof( TCombinedStudioData ) );
  4145. }
  4146. else
  4147. {
  4148. /*
  4149. if ( ( pCombinedStudioData->m_nReferenceFlags & COMBINED_REFERENCE_PLACEHOLDER ) != 0 )
  4150. { // are we trying to replace a combined model too quickly
  4151. Assert( 0 );
  4152. return false;
  4153. }
  4154. */
  4155. if ( ( pFinalStudioData->m_nFlags & STUDIODATA_FLAGS_COMBINED_UNAVAILABLE ) != 0 )
  4156. { // are we trying to replace a combined model too quickly
  4157. Assert( 0 );
  4158. return false;
  4159. }
  4160. FreeCombinedGeneratedData( pFinalStudioData );
  4161. }
  4162. InitCombiner();
  4163. memset( pCombinedStudioData, 0, sizeof( *pCombinedStudioData ) );
  4164. pCombinedStudioData->m_nReferenceFlags |= COMBINED_REFERENCE_PLACEHOLDER; // the replace will nuke away a ref count
  4165. V_strcpy_safe( pCombinedStudioData->m_szCombinedModelName, GetModelName( handle ) );
  4166. pCombinedStudioData->m_pPlaceholderStudioData = NULL;
  4167. pCombinedStudioData->m_PlaceholderHandle = MDLHANDLE_INVALID;
  4168. pCombinedStudioData->m_pFinalStudioData = pFinalStudioData;
  4169. pCombinedStudioData->m_FinalHandle = handle;
  4170. pFinalStudioData->m_pCombinedStudioData = pCombinedStudioData;
  4171. pFinalStudioData->m_nFlags |= STUDIODATA_FLAGS_COMBINED | STUDIODATA_FLAGS_COMBINED_UNAVAILABLE;
  4172. return true;
  4173. }
  4174. bool CMDLCache::SetCombineModels( MDLHandle_t handle, const CUtlVector< SCombinerModelInput_t > &vecModelsToCombine )
  4175. {
  4176. if ( handle == MDLHANDLE_INVALID || vecModelsToCombine[ 0 ].m_iszModelName == NULL_STRING || STRING( vecModelsToCombine[ 0 ].m_iszModelName )[ 0 ] == '\0' )
  4177. {
  4178. return false;
  4179. }
  4180. studiodata_t *pTempStudioData = m_MDLDict[ handle ];
  4181. TCombinedStudioData *pCombinedStudioData = pTempStudioData->m_pCombinedStudioData;
  4182. FOR_EACH_VEC( vecModelsToCombine, i )
  4183. {
  4184. pCombinedStudioData->m_ModelInputData[ i ] = vecModelsToCombine.Element( i );
  4185. }
  4186. pCombinedStudioData->m_nNumModels = vecModelsToCombine.Count();
  4187. return true;
  4188. }
  4189. bool CMDLCache::FinishCombinedModel( MDLHandle_t handle, CombinedModelLoadedCallback pFunc, void *pUserData )
  4190. {
  4191. if ( handle == MDLHANDLE_INVALID )
  4192. {
  4193. return false;
  4194. }
  4195. studiodata_t *pTempStudioData = m_MDLDict[ handle ];
  4196. TCombinedStudioData *pCombinedStudioData = pTempStudioData->m_pCombinedStudioData;
  4197. pCombinedStudioData->m_pCombinedUserData = pUserData;
  4198. pCombinedStudioData->m_CallbackFunc = pFunc;
  4199. m_CombinerToBeCombined.PushItem( pCombinedStudioData );
  4200. return true;
  4201. }
  4202. bool CMDLCache::IsCombinedPlaceholder( MDLHandle_t handle )
  4203. {
  4204. studiodata_t *pTempStudioData = m_MDLDict[ handle ];
  4205. return ( ( pTempStudioData->m_nFlags & STUDIODATA_FLAGS_COMBINED_PLACEHOLDER ) != 0 );
  4206. }
  4207. bool CMDLCache::IsCombinedModel( MDLHandle_t handle )
  4208. {
  4209. studiodata_t *pTempStudioData = m_MDLDict[ handle ];
  4210. return ( ( pTempStudioData->m_nFlags & STUDIODATA_FLAGS_COMBINED ) != 0 );
  4211. }
  4212. TCombinedStudioData *CMDLCache::GetCombinedData( MDLHandle_t handle )
  4213. {
  4214. if ( handle == MDLHANDLE_INVALID )
  4215. {
  4216. return NULL;
  4217. }
  4218. studiodata_t *pTempStudioData = m_MDLDict[ handle ];
  4219. if ( ( pTempStudioData->m_nFlags & ( STUDIODATA_FLAGS_COMBINED_PLACEHOLDER | STUDIODATA_FLAGS_COMBINED ) ) == 0 )
  4220. {
  4221. return NULL;
  4222. }
  4223. return pTempStudioData->m_pCombinedStudioData;
  4224. }
  4225. int CMDLCache::GetNumCombinedSubModels( MDLHandle_t handle )
  4226. {
  4227. TCombinedStudioData *pCombinedStudioData = GetCombinedData( handle );
  4228. if ( !pCombinedStudioData )
  4229. {
  4230. return 0;
  4231. }
  4232. return pCombinedStudioData->m_nNumModels;
  4233. }
  4234. void CMDLCache::GetCombinedSubModelFilename( MDLHandle_t handle, int nSubModelIndex, char *pszResult, int nResultSize )
  4235. {
  4236. pszResult[ 0 ] = 0;
  4237. TCombinedStudioData *pCombinedStudioData = GetCombinedData( handle );
  4238. if ( !pCombinedStudioData )
  4239. {
  4240. return;
  4241. }
  4242. if ( nSubModelIndex < 0 || nSubModelIndex > pCombinedStudioData->m_nNumModels )
  4243. {
  4244. return;
  4245. }
  4246. V_strncpy( pszResult, STRING( pCombinedStudioData->m_ModelInputData[ nSubModelIndex ].m_iszModelName ), nResultSize );
  4247. }
  4248. KeyValues *CMDLCache::GetCombinedMaterialKV( MDLHandle_t handle, int nAtlasGroup )
  4249. {
  4250. TCombinedStudioData *pCombinedStudioData = GetCombinedData( handle );
  4251. if ( !pCombinedStudioData )
  4252. {
  4253. return NULL;
  4254. }
  4255. if ( !pCombinedStudioData->m_AtlasGroups[ nAtlasGroup ].m_pCombinedMaterial )
  4256. {
  4257. return NULL;
  4258. }
  4259. return pCombinedStudioData->m_AtlasGroups[ nAtlasGroup ].m_pCombinedMaterial->MakeCopy();
  4260. }
  4261. void CMDLCache::CheckCombinerFlagChanges( int nNewFlags )
  4262. {
  4263. unsigned nFlagsChanged = m_nCombinerFlags ^ nNewFlags;
  4264. if ( ( nFlagsChanged & COMBINER_FLAG_THREADING ) != 0 )
  4265. {
  4266. ShutdownCombiner();
  4267. }
  4268. m_nCombinerFlags = nNewFlags;
  4269. if ( ( nFlagsChanged & COMBINER_FLAG_THREADING ) != 0 )
  4270. {
  4271. InitCombiner();
  4272. }
  4273. }
  4274. void CMDLCache::SetCombinerFlags( unsigned nFlags )
  4275. {
  4276. CheckCombinerFlagChanges( m_nCombinerFlags | nFlags );
  4277. }
  4278. void CMDLCache::ClearCombinerFlags( unsigned nFlags )
  4279. {
  4280. CheckCombinerFlagChanges( m_nCombinerFlags & ( ~nFlags ) );
  4281. }
  4282. void CMDLCache::UpdateCombiner( )
  4283. {
  4284. // non-threaded approach
  4285. BeginLock();
  4286. TCombinedStudioData *pCombinedStudioData;
  4287. if ( ( m_nCombinerFlags & COMBINER_FLAG_THREADING ) == 0 )
  4288. {
  4289. if ( m_CombinerToBeCombined.PopItem( &pCombinedStudioData ) )
  4290. {
  4291. pCombinedStudioData->m_pCombineData = &g_ModelCombiner;
  4292. pCombinedStudioData->m_pCombineData->Init( pCombinedStudioData );
  4293. pCombinedStudioData->m_pCombineData->Resolve();
  4294. m_pCombinedCompleted = pCombinedStudioData;
  4295. }
  4296. }
  4297. if ( m_pCombinedCompleted )
  4298. {
  4299. pCombinedStudioData = ( TCombinedStudioData * )m_pCombinedCompleted;
  4300. if ( pCombinedStudioData->m_Results.m_nCombinedResults == COMBINE_RESULT_FLAG_OK )
  4301. {
  4302. double flStartEngineTime = Plat_FloatTime();
  4303. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  4304. if ( pCombinedStudioData->m_PlaceholderHandle == MDLHANDLE_INVALID )
  4305. { // we are doing a replace, this will reduce the ref count that we artificially inflated for this pathway
  4306. FlushImmediate( m_MDLDict[ pCombinedStudioData->m_FinalHandle ] );
  4307. Flush( pCombinedStudioData->m_FinalHandle );
  4308. }
  4309. else
  4310. { // user is responsible for cleanup of the placeholder
  4311. // Release( pCombinedStudioData->m_PlaceholderHandle );
  4312. }
  4313. if ( ( m_nCombinerFlags & COMBINER_FLAG_NO_DATA_PROCESSING ) == 0 )
  4314. {
  4315. Flush( pCombinedStudioData->m_FinalHandle, MDLCACHE_FLUSH_STUDIOHDR | MDLCACHE_FLUSH_STUDIOHWDATA | MDLCACHE_FLUSH_VERTEXES );
  4316. if ( m_pCacheNotify && pCombinedStudioData->m_PlaceholderHandle != MDLHANDLE_INVALID )
  4317. {
  4318. m_pCacheNotify->OnCombinerPreCache( pCombinedStudioData->m_PlaceholderHandle, pCombinedStudioData->m_FinalHandle );
  4319. }
  4320. {
  4321. CUtlBuffer buf( pCombinedStudioData->m_pCombineData->GetCombinedMDLPtr(), pCombinedStudioData->m_pCombineData->GetCombinedMDLSize(), CUtlBuffer::READ_ONLY );
  4322. CMDLCacheData cacheData( MDLCACHE_STUDIOHDR, CMDLCacheData::ALLOC_OPTIMALREADBUFFER, &buf );
  4323. ProcessDataIntoCache( pCombinedStudioData->m_FinalHandle, cacheData );
  4324. }
  4325. {
  4326. CUtlBuffer buf( pCombinedStudioData->m_pCombineData->GetCombinedVVDPtr(), pCombinedStudioData->m_pCombineData->GetCombinedVVDSize(), CUtlBuffer::READ_ONLY );
  4327. CMDLCacheData cacheData( MDLCACHE_VERTEXES, CMDLCacheData::ALLOC_OPTIMALREADBUFFER, &buf );
  4328. ProcessDataIntoCache( pCombinedStudioData->m_FinalHandle, cacheData );
  4329. }
  4330. {
  4331. CUtlBuffer buf( pCombinedStudioData->m_pCombineData->GetCombinedVTXPtr(), pCombinedStudioData->m_pCombineData->GetCombinedVTXSize(), CUtlBuffer::READ_ONLY );
  4332. CMDLCacheData cacheData( MDLCACHE_STUDIOHWDATA, CMDLCacheData::ALLOC_OPTIMALREADBUFFER, &buf );
  4333. ProcessDataIntoCache( pCombinedStudioData->m_FinalHandle, cacheData );
  4334. }
  4335. //ProcessDataIntoCache( pCombinedStudioData->m_FinalHandle, MDLCACHE_STUDIOHDR, 0, pCombinedStudioData->m_pCombineData->GetCombinedMDLPtr(), pCombinedStudioData->m_pCombineData->GetCombinedMDLSize(),
  4336. // pCombinedStudioData->m_pCombineData->GetCombinedMDLAvailability() );
  4337. //ProcessDataIntoCache( pCombinedStudioData->m_FinalHandle, MDLCACHE_VERTEXES, 0, pCombinedStudioData->m_pCombineData->GetCombinedVVDPtr(), pCombinedStudioData->m_pCombineData->GetCombinedVVDSize(),
  4338. // pCombinedStudioData->m_pCombineData->GetCombinedVVDAvailability() );
  4339. //ProcessDataIntoCache( pCombinedStudioData->m_FinalHandle, MDLCACHE_STUDIOHWDATA, 0, pCombinedStudioData->m_pCombineData->GetCombinedVTXPtr(), pCombinedStudioData->m_pCombineData->GetCombinedVTXSize(),
  4340. // pCombinedStudioData->m_pCombineData->GetCombinedVTXAvailability() );
  4341. }
  4342. else
  4343. {
  4344. FreeCombinedGeneratedData( m_MDLDict[ pCombinedStudioData->m_FinalHandle ] );
  4345. }
  4346. studiodata_t *pStudioDataCurrent = m_MDLDict[ pCombinedStudioData->m_FinalHandle ];
  4347. pStudioDataCurrent->m_nFlags &= ~STUDIODATA_FLAGS_COMBINED_UNAVAILABLE;
  4348. pCombinedStudioData->m_Results.m_flEngineProcessingDuration = ( float )( Plat_FloatTime() - flStartEngineTime );
  4349. pCombinedStudioData->m_CallbackFunc( pCombinedStudioData->m_pCombinedUserData, pCombinedStudioData->m_PlaceholderHandle, pCombinedStudioData->m_FinalHandle, pCombinedStudioData->m_Results );
  4350. }
  4351. else
  4352. {
  4353. FreeCombinedGeneratedData( m_MDLDict[ pCombinedStudioData->m_FinalHandle ] );
  4354. Release( pCombinedStudioData->m_FinalHandle );
  4355. pCombinedStudioData->m_CallbackFunc( pCombinedStudioData->m_pCombinedUserData, pCombinedStudioData->m_PlaceholderHandle, MDLHANDLE_INVALID, pCombinedStudioData->m_Results );
  4356. // user is responsible for cleanup of the placeholder
  4357. // Release( pCombinedStudioData->m_PlaceholderHandle );
  4358. }
  4359. if ( ( m_nCombinerFlags & COMBINER_FLAG_NO_DATA_PROCESSING ) != 0 )
  4360. {
  4361. GetTextureCombiner().FreeCombinedMaterials();
  4362. }
  4363. pCombinedStudioData->m_nReferenceFlags &= ~COMBINED_REFERENCE_COMBINER;
  4364. if ( pCombinedStudioData->m_nReferenceFlags == 0 )
  4365. {
  4366. Assert( 0 );
  4367. Error( "CMDLCache::UpdateCombiner - model handles have been freed" );
  4368. }
  4369. m_pCombinedCompleted = NULL;
  4370. // delete pCombinedStudioData->m_pCombineData;
  4371. // pCombinedStudioData->m_pCombineData = NULL;
  4372. }
  4373. // do the scheduling of the next item here, so that we have a full frame to process it
  4374. if ( ( m_nCombinerFlags & COMBINER_FLAG_THREADING ) != 0 )
  4375. {
  4376. if ( m_pToBeCombined == NULL && m_pCombinedCompleted == NULL )
  4377. {
  4378. if ( m_CombinerToBeCombined.PopItem( &pCombinedStudioData ) )
  4379. {
  4380. m_pToBeCombined = pCombinedStudioData;
  4381. m_CombinerEvent.Set();
  4382. }
  4383. }
  4384. }
  4385. EndLock();
  4386. }
  4387. void *CMDLCache::GetCombinedInternalAsset( ECombinedAsset AssetType, const char *pszAssetID, int *nSize )
  4388. {
  4389. if ( nSize != NULL )
  4390. {
  4391. *nSize = 0;
  4392. }
  4393. switch( AssetType )
  4394. {
  4395. case COMBINED_ASSET_MATERIAL:
  4396. {
  4397. char szAssetName[ MAX_PATH ];
  4398. MDLHandle_t nHandleID;
  4399. int nAssetID;
  4400. int nAtlasGroup;
  4401. // expecting "!%s|%hu|%d!"
  4402. const char *pStartPos = pszAssetID;
  4403. if ( *pStartPos != '!' )
  4404. {
  4405. Assert( 0 );
  4406. return NULL;
  4407. }
  4408. pStartPos++;
  4409. const char *pEndPos = strchr( pStartPos, '|' );
  4410. if ( pEndPos == NULL )
  4411. {
  4412. Assert( 0 );
  4413. return NULL;
  4414. }
  4415. int nLength = pEndPos - pStartPos;
  4416. if ( ( nLength + 1 ) > sizeof( szAssetName ) )
  4417. {
  4418. Assert( 0 );
  4419. return NULL;
  4420. }
  4421. strncpy( szAssetName, pStartPos, nLength );
  4422. szAssetName[ nLength ] = 0;
  4423. pStartPos = pEndPos + 1;
  4424. nAtlasGroup = atoi( pStartPos );
  4425. if ( nAtlasGroup < 0 || nAtlasGroup >= COMBINER_MAX_ATLAS_GROUPS )
  4426. {
  4427. Assert( 0 );
  4428. return NULL;
  4429. }
  4430. pEndPos = strchr( pStartPos, '|' );
  4431. if ( pEndPos == NULL )
  4432. {
  4433. Assert( 0 );
  4434. return NULL;
  4435. }
  4436. pStartPos = pEndPos + 1;
  4437. nHandleID = atol( pStartPos );
  4438. pEndPos = strchr( pStartPos, '|' );
  4439. if ( pEndPos == NULL )
  4440. {
  4441. Assert( 0 );
  4442. return NULL;
  4443. }
  4444. pStartPos = pEndPos + 1;
  4445. nAssetID = atoi( pStartPos );
  4446. pEndPos = strchr( pStartPos, '!' );
  4447. if ( pEndPos == NULL )
  4448. {
  4449. Assert( 0 );
  4450. return NULL;
  4451. }
  4452. studiodata_t *pFinalStudioData = m_MDLDict[ nHandleID ];
  4453. if ( pFinalStudioData == NULL )
  4454. {
  4455. Assert( 0 );
  4456. return NULL;
  4457. }
  4458. TCombinedStudioData *pCombinedStudioData = pFinalStudioData->m_pCombinedStudioData;
  4459. if ( pCombinedStudioData == NULL )
  4460. {
  4461. Assert( 0 );
  4462. return NULL;
  4463. }
  4464. if ( pCombinedStudioData->m_AtlasGroups[ nAtlasGroup ].m_pCombinedMaterial == NULL )
  4465. {
  4466. Assert( 0 );
  4467. return NULL;
  4468. }
  4469. return pCombinedStudioData->m_AtlasGroups[ nAtlasGroup ].m_pCombinedMaterial;
  4470. }
  4471. break;
  4472. case COMBINED_ASSET_TEXTURE:
  4473. {
  4474. char szAssetName[ MAX_PATH ];
  4475. int nAtlasGroup;
  4476. int nTexture;
  4477. MDLHandle_t nHandleID;
  4478. int nAssetID;
  4479. // expecting "!%s|%d|%hu|%d!"
  4480. const char *pStartPos = pszAssetID;
  4481. if ( *pStartPos != '!' )
  4482. {
  4483. Assert( 0 );
  4484. return NULL;
  4485. }
  4486. pStartPos++;
  4487. const char *pEndPos = strchr( pStartPos, '|' );
  4488. if ( pEndPos == NULL )
  4489. {
  4490. Assert( 0 );
  4491. return NULL;
  4492. }
  4493. int nLength = pEndPos - pStartPos;
  4494. if ( ( nLength + 1 ) > sizeof( szAssetName ) )
  4495. {
  4496. Assert( 0 );
  4497. return NULL;
  4498. }
  4499. strncpy( szAssetName, pStartPos, nLength );
  4500. szAssetName[ nLength ] = 0;
  4501. pStartPos = pEndPos + 1;
  4502. nAtlasGroup = atoi( pStartPos );
  4503. if ( nAtlasGroup < 0 || nAtlasGroup >= COMBINER_MAX_ATLAS_GROUPS )
  4504. {
  4505. Assert( 0 );
  4506. return NULL;
  4507. }
  4508. pEndPos = strchr( pStartPos, '|' );
  4509. if ( pEndPos == NULL )
  4510. {
  4511. Assert( 0 );
  4512. return NULL;
  4513. }
  4514. pStartPos = pEndPos + 1;
  4515. nTexture = atoi( pStartPos );
  4516. if ( nTexture < 0 || nTexture >= COMBINER_MAX_TEXTURES_PER_MATERIAL )
  4517. {
  4518. Assert( 0 );
  4519. return NULL;
  4520. }
  4521. pEndPos = strchr( pStartPos, '|' );
  4522. if ( pEndPos == NULL )
  4523. {
  4524. Assert( 0 );
  4525. return NULL;
  4526. }
  4527. pStartPos = pEndPos + 1;
  4528. nHandleID = atol( pStartPos );
  4529. pEndPos = strchr( pStartPos, '|' );
  4530. if ( pEndPos == NULL )
  4531. {
  4532. Assert( 0 );
  4533. return NULL;
  4534. }
  4535. pStartPos = pEndPos + 1;
  4536. nAssetID = atoi( pStartPos );
  4537. pEndPos = strchr( pStartPos, '!' );
  4538. if ( pEndPos == NULL )
  4539. {
  4540. Assert( 0 );
  4541. return NULL;
  4542. }
  4543. studiodata_t *pFinalStudioData = m_MDLDict[ nHandleID ];
  4544. if ( pFinalStudioData == NULL )
  4545. {
  4546. Assert( 0 );
  4547. return NULL;
  4548. }
  4549. TCombinedStudioData *pCombinedStudioData = pFinalStudioData->m_pCombinedStudioData;
  4550. if ( pCombinedStudioData == NULL )
  4551. {
  4552. Assert( 0 );
  4553. return NULL;
  4554. }
  4555. if ( pCombinedStudioData->m_AtlasGroups[ nAtlasGroup ].m_pCombinedTextures[ nTexture ] == NULL )
  4556. {
  4557. Assert( 0 );
  4558. return NULL;
  4559. }
  4560. *nSize = pCombinedStudioData->m_AtlasGroups[ nAtlasGroup ].m_nCombinedTextureSizes[ nTexture ];
  4561. return pCombinedStudioData->m_AtlasGroups[ nAtlasGroup ].m_pCombinedTextures[ nTexture ];
  4562. }
  4563. break;
  4564. }
  4565. return NULL;
  4566. }
  4567. void CMDLCache::CombinerThread( )
  4568. {
  4569. while( !m_bCombinerShutdown )
  4570. {
  4571. m_CombinerEvent.Wait();
  4572. m_CombinerEvent.Reset();
  4573. if ( m_pToBeCombined )
  4574. {
  4575. TCombinedStudioData *pCombinedStudioData = ( TCombinedStudioData * )m_pToBeCombined;
  4576. pCombinedStudioData->m_pCombineData = &g_ModelCombiner;
  4577. pCombinedStudioData->m_pCombineData->Init( pCombinedStudioData );
  4578. pCombinedStudioData->m_pCombineData->Resolve();
  4579. m_pCombinedCompleted = m_pToBeCombined;
  4580. m_pToBeCombined = NULL;
  4581. }
  4582. }
  4583. m_CombinerShutdownEvent.Set();
  4584. }
  4585. uintp CMDLCache::StaticCombinerThread( void *pParam )
  4586. {
  4587. g_MDLCache.CombinerThread();
  4588. return 0;
  4589. }
  4590. void CMDLCache::InitCombiner( )
  4591. {
  4592. if ( m_bCombinerReady )
  4593. {
  4594. return;
  4595. }
  4596. if ( ( m_nCombinerFlags & COMBINER_FLAG_THREADING ) != 0 )
  4597. {
  4598. m_bCombinerShutdown = false;
  4599. m_CombinerShutdownEvent.Reset();
  4600. m_hCombinerThread = CreateSimpleThread( CMDLCache::StaticCombinerThread, NULL, 10240 );
  4601. ThreadSetDebugName( m_hCombinerThread, "Combiner" );
  4602. }
  4603. m_bCombinerReady = true;
  4604. }
  4605. void CMDLCache::ShutdownCombiner( )
  4606. {
  4607. if ( !m_bCombinerReady )
  4608. {
  4609. return;
  4610. }
  4611. if ( ( m_nCombinerFlags & COMBINER_FLAG_THREADING ) != 0 && m_hCombinerThread != NULL )
  4612. {
  4613. m_bCombinerShutdown = true;
  4614. m_CombinerEvent.Set();
  4615. m_CombinerShutdownEvent.Wait();
  4616. #if 0
  4617. // how to kill this guy off?
  4618. ReleaseThreadHandle( m_hCombinerThread );
  4619. #endif
  4620. m_hCombinerThread = NULL;
  4621. }
  4622. m_bCombinerReady = false;
  4623. }
  4624. void CMDLCache::FreeCombinedGeneratedData( studiodata_t *pStudioData )
  4625. {
  4626. for ( int nGroup = 0; nGroup < pStudioData->m_pCombinedStudioData->m_nNumAtlasGroups; nGroup++ )
  4627. {
  4628. for( int nTexture = 0; nTexture < COMBINER_MAX_TEXTURES_PER_MATERIAL; nTexture++ )
  4629. {
  4630. if ( pStudioData->m_pCombinedStudioData->m_AtlasGroups[ nGroup ].m_pCombinedTextures[ nTexture ] )
  4631. {
  4632. free( pStudioData->m_pCombinedStudioData->m_AtlasGroups[ nGroup ].m_pCombinedTextures[ nTexture ] );
  4633. pStudioData->m_pCombinedStudioData->m_AtlasGroups[ nGroup ].m_pCombinedTextures[ nTexture ] = NULL;
  4634. pStudioData->m_pCombinedStudioData->m_AtlasGroups[ nGroup ].m_nCombinedTextureSizes[ nTexture ] = 0;
  4635. pStudioData->m_pCombinedStudioData->m_AtlasGroups[ nGroup ].m_szCombinedMaterialName[ 0 ] = 0;
  4636. }
  4637. }
  4638. if ( pStudioData->m_pCombinedStudioData->m_AtlasGroups[ nGroup ].m_pCombinedMaterial )
  4639. {
  4640. pStudioData->m_pCombinedStudioData->m_AtlasGroups[ nGroup ].m_pCombinedMaterial->deleteThis();
  4641. pStudioData->m_pCombinedStudioData->m_AtlasGroups[ nGroup ].m_pCombinedMaterial = NULL;
  4642. }
  4643. }
  4644. }
  4645. bool CMDLCache::UnserializeCombinedHardwareData( MDLHandle_t handle )
  4646. {
  4647. if ( m_bCombinerReady )
  4648. { // we should be doing this only when the combiner is shut down
  4649. Assert( 0 );
  4650. return false;
  4651. }
  4652. studiodata_t *pStudioData = m_MDLDict[ handle ];
  4653. if ( !pStudioData )
  4654. {
  4655. return false;
  4656. }
  4657. // we should have the lock already if we are in here!
  4658. TCombinedStudioData *pCombinedStudioData = pStudioData->m_pCombinedStudioData;
  4659. if ( !pCombinedStudioData )
  4660. {
  4661. return false;
  4662. }
  4663. FreeCombinedGeneratedData( pStudioData );
  4664. pCombinedStudioData->m_pCombineData = &g_ModelCombiner;
  4665. pCombinedStudioData->m_pCombineData->Init( pCombinedStudioData );
  4666. pCombinedStudioData->m_pCombineData->Resolve();
  4667. m_pCombinedCompleted = pCombinedStudioData;
  4668. if ( pCombinedStudioData->m_Results.m_nCombinedResults == COMBINE_RESULT_FLAG_OK )
  4669. {
  4670. Flush( handle, MDLCACHE_FLUSH_STUDIOHDR | MDLCACHE_FLUSH_STUDIOHWDATA | MDLCACHE_FLUSH_VERTEXES );
  4671. // we need to process all of the data as the internal asset id's have been updated ( for material and texture references )
  4672. {
  4673. CUtlBuffer buf( pCombinedStudioData->m_pCombineData->GetCombinedMDLPtr(), pCombinedStudioData->m_pCombineData->GetCombinedMDLSize(), CUtlBuffer::READ_ONLY );
  4674. CMDLCacheData cacheData( MDLCACHE_STUDIOHDR, CMDLCacheData::ALLOC_OPTIMALREADBUFFER, &buf );
  4675. ProcessDataIntoCache( pCombinedStudioData->m_FinalHandle, cacheData );
  4676. }
  4677. {
  4678. CUtlBuffer buf( pCombinedStudioData->m_pCombineData->GetCombinedVVDPtr(), pCombinedStudioData->m_pCombineData->GetCombinedVVDSize(), CUtlBuffer::READ_ONLY );
  4679. CMDLCacheData cacheData( MDLCACHE_VERTEXES, CMDLCacheData::ALLOC_OPTIMALREADBUFFER, &buf );
  4680. ProcessDataIntoCache( pCombinedStudioData->m_FinalHandle, cacheData );
  4681. }
  4682. {
  4683. CUtlBuffer buf( pCombinedStudioData->m_pCombineData->GetCombinedVTXPtr(), pCombinedStudioData->m_pCombineData->GetCombinedVTXSize(), CUtlBuffer::READ_ONLY );
  4684. CMDLCacheData cacheData( MDLCACHE_STUDIOHWDATA, CMDLCacheData::ALLOC_OPTIMALREADBUFFER, &buf );
  4685. ProcessDataIntoCache( pCombinedStudioData->m_FinalHandle, cacheData );
  4686. }
  4687. //ProcessDataIntoCache( pCombinedStudioData->m_FinalHandle, MDLCACHE_STUDIOHDR, 0, pCombinedStudioData->m_pCombineData->GetCombinedMDLPtr(), pCombinedStudioData->m_pCombineData->GetCombinedMDLSize(),
  4688. // pCombinedStudioData->m_pCombineData->GetCombinedMDLAvailability() );
  4689. //ProcessDataIntoCache( pCombinedStudioData->m_FinalHandle, MDLCACHE_VERTEXES, 0, pCombinedStudioData->m_pCombineData->GetCombinedVVDPtr(), pCombinedStudioData->m_pCombineData->GetCombinedVVDSize(),
  4690. // pCombinedStudioData->m_pCombineData->GetCombinedVVDAvailability() );
  4691. //ProcessDataIntoCache( pCombinedStudioData->m_FinalHandle, MDLCACHE_STUDIOHWDATA, 0, pCombinedStudioData->m_pCombineData->GetCombinedVTXPtr(), pCombinedStudioData->m_pCombineData->GetCombinedVTXSize(),
  4692. // pCombinedStudioData->m_pCombineData->GetCombinedVTXAvailability() );
  4693. return true;
  4694. }
  4695. return false;
  4696. }
  4697. void CMDLCache::DebugCombinerInfo( )
  4698. {
  4699. Msg( "MDLCache:\n" );
  4700. for( MDLHandle_t nHandle = m_MDLDict.First(); nHandle != m_MDLDict.InvalidIndex(); nHandle = m_MDLDict.Next( nHandle ) )
  4701. {
  4702. studiodata_t *pStudioData = m_MDLDict[ nHandle ];
  4703. if ( pStudioData == NULL || ( pStudioData->m_nFlags & STUDIODATA_FLAGS_COMBINED_ASSET ) == 0 )
  4704. {
  4705. continue;
  4706. }
  4707. Msg( " %d: ", nHandle );
  4708. if ( ( pStudioData->m_nFlags & STUDIODATA_FLAGS_STUDIOMESH_LOADED ) != 0 )
  4709. {
  4710. Msg( "STUDIODATA_FLAGS_STUDIOMESH_LOADED " );
  4711. }
  4712. if ( ( pStudioData->m_nFlags & STUDIODATA_FLAGS_VCOLLISION_LOADED ) != 0 )
  4713. {
  4714. Msg( "STUDIODATA_FLAGS_VCOLLISION_LOADED " );
  4715. }
  4716. if ( ( pStudioData->m_nFlags & STUDIODATA_ERROR_MODEL ) != 0 )
  4717. {
  4718. Msg( "STUDIODATA_ERROR_MODEL " );
  4719. }
  4720. if ( ( pStudioData->m_nFlags & STUDIODATA_FLAGS_NO_STUDIOMESH ) != 0 )
  4721. {
  4722. Msg( "STUDIODATA_FLAGS_NO_STUDIOMESH " );
  4723. }
  4724. if ( ( pStudioData->m_nFlags & STUDIODATA_FLAGS_NO_VERTEX_DATA ) != 0 )
  4725. {
  4726. Msg( "STUDIODATA_FLAGS_NO_VERTEX_DATA " );
  4727. }
  4728. if ( ( pStudioData->m_nFlags & STUDIODATA_FLAGS_PHYSICS2COLLISION_LOADED ) != 0 )
  4729. {
  4730. Msg( "STUDIODATA_FLAGS_PHYSICS2COLLISION_LOADED " );
  4731. }
  4732. if ( ( pStudioData->m_nFlags & STUDIODATA_FLAGS_VCOLLISION_SCANNED ) != 0 )
  4733. {
  4734. Msg( "STUDIODATA_FLAGS_VCOLLISION_SCANNED " );
  4735. }
  4736. if ( ( pStudioData->m_nFlags & STUDIODATA_FLAGS_COMBINED_PLACEHOLDER ) != 0 )
  4737. {
  4738. Msg( "STUDIODATA_FLAGS_COMBINED_PLACEHOLDER " );
  4739. }
  4740. if ( ( pStudioData->m_nFlags & STUDIODATA_FLAGS_COMBINED ) != 0 )
  4741. {
  4742. Msg( "STUDIODATA_FLAGS_COMBINED " );
  4743. }
  4744. if ( ( pStudioData->m_nFlags & STUDIODATA_FLAGS_COMBINED_UNAVAILABLE ) != 0 )
  4745. {
  4746. Msg( "STUDIODATA_FLAGS_COMBINED_UNAVAILABLE " );
  4747. }
  4748. if ( ( pStudioData->m_nFlags & STUDIODATA_FLAGS_COMBINED_ASSET ) != 0 )
  4749. {
  4750. Msg( "STUDIODATA_FLAGS_COMBINED_ASSET " );
  4751. }
  4752. Msg( "\n" );
  4753. if ( pStudioData->m_pCombinedStudioData )
  4754. {
  4755. Msg( " Combined Name: %s\n", pStudioData->m_pCombinedStudioData->m_szCombinedModelName );
  4756. Msg( " Primary Model: %s\n", STRING( pStudioData->m_pCombinedStudioData->m_ModelInputData[ 0 ].m_iszModelName ) );
  4757. for( int i = 1; i < pStudioData->m_pCombinedStudioData->m_nNumModels; i++ )
  4758. {
  4759. Msg( " Secondary Model %d: %s\n", i, STRING( pStudioData->m_pCombinedStudioData->m_ModelInputData[ i ].m_iszModelName ) );
  4760. }
  4761. }
  4762. }
  4763. }
  4764. void CMDLCache::DumpDictionaryState()
  4765. {
  4766. int nDataCount = 0;
  4767. int nMeshCount = 0;
  4768. int nAnimCount = 0;
  4769. MDLHandle_t i = m_MDLDict.First();
  4770. while ( i != m_MDLDict.InvalidIndex() )
  4771. {
  4772. Msg("0x%08x : %p : %s \n",
  4773. m_MDLDict.Element( i )->m_Handle, // MDLHandle_t (should = i)
  4774. m_MDLDict.Element( i )->m_MDLCache, // DataCacheHandle_t
  4775. m_MDLDict.GetElementName( i ) );
  4776. if ( m_MDLDict.Element( i )->m_VertexCache != NULL )
  4777. {
  4778. Msg("0x%08x : %p : %s \n",
  4779. m_MDLDict.Element( i )->m_Handle,
  4780. m_MDLDict.Element( i )->m_VertexCache,
  4781. "MeshData");
  4782. nMeshCount++;
  4783. }
  4784. for ( int j = 0; j < m_MDLDict.Element( i )->m_vecAnimBlocks.Count(); j++ )
  4785. {
  4786. if ( m_MDLDict.Element( i )->m_vecAnimBlocks.Element( j ) != NULL )
  4787. {
  4788. Msg("0x%08x : %p : %s \n",
  4789. m_MDLDict.Element( i )->m_Handle,
  4790. m_MDLDict.Element( i )->m_vecAnimBlocks.Element( j ),
  4791. "AnimBlock");
  4792. nAnimCount++;
  4793. }
  4794. }
  4795. i = m_MDLDict.Next( i );
  4796. nDataCount++;
  4797. }
  4798. Msg( "DataCount: %d MeshCount: %d AnimCount: %d \n", nDataCount, nMeshCount, nAnimCount );
  4799. Msg( "Total: %d \n", nDataCount + nMeshCount + nAnimCount );
  4800. }
  4801. CON_COMMAND( mdlcache_dump_dictionary_state, "Dump the state of the MDLCache Dictionary." )
  4802. {
  4803. g_pMDLCache->DumpDictionaryState();
  4804. }
  4805. //-----------------------------------------------------------------------------
  4806. // Clears the anim cache, freeing its memory
  4807. //
  4808. // NOTE: this is only useful if no more entities will animate after this point in the map,
  4809. // since any further ANIMBLOCK load requests will cause the cache to be recreated.
  4810. // Entirely resident (non-streaming) animations will not have this effect.
  4811. //-----------------------------------------------------------------------------
  4812. CON_COMMAND( clear_anim_cache, "Clears the animation cache, freeing the memory (until the next time a streaming animblock is requested)." )
  4813. {
  4814. IDataCacheSection* pSection = g_MDLCache.GetCacheSection( MDLCACHE_ANIMBLOCK );
  4815. pSection->Purge( 512*1024*1024 ); // "purge everything"
  4816. if ( g_AnimBlockAllocator.IsEmpty() )
  4817. {
  4818. Msg( "Animblock cache successfully cleared\n" );
  4819. g_AnimBlockAllocator.Clear();
  4820. }
  4821. else
  4822. {
  4823. Warning( "Cannot clear animblock cache - %d blocks still in use!\n", MAX_ANIMBLOCKS - g_AnimBlockAllocator.m_freeList.Count() );
  4824. }
  4825. }