Team Fortress 2 Source Code as on 22/4/2020
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.

3924 lines
119 KiB

  1. //========= Copyright 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. #include <memory.h>
  12. #include "tier0/vprof.h"
  13. #include "tier0/icommandline.h"
  14. #include "tier1/utllinkedlist.h"
  15. #include "tier1/utlmap.h"
  16. #include "datacache/imdlcache.h"
  17. #include "istudiorender.h"
  18. #include "filesystem.h"
  19. #include "optimize.h"
  20. #include "materialsystem/imaterialsystemhardwareconfig.h"
  21. #include "materialsystem/imesh.h"
  22. #include "datacache/idatacache.h"
  23. #include "studio.h"
  24. #include "vcollide.h"
  25. #include "utldict.h"
  26. #include "convar.h"
  27. #include "datacache_common.h"
  28. #include "mempool.h"
  29. #include "vphysics_interface.h"
  30. #include "phyfile.h"
  31. #include "studiobyteswap.h"
  32. #include "tier2/fileutils.h"
  33. #include "filesystem/IQueuedLoader.h"
  34. #include "tier1/lzmaDecoder.h"
  35. #include "functors.h"
  36. // XXX remove this later. (henryg)
  37. #if 0 && defined(_DEBUG) && defined(_WIN32) && !defined(_X360)
  38. typedef struct LARGE_INTEGER { unsigned long long QuadPart; } LARGE_INTEGER;
  39. extern "C" void __stdcall OutputDebugStringA( const char *lpOutputString );
  40. extern "C" long __stdcall QueryPerformanceCounter( LARGE_INTEGER *lpPerformanceCount );
  41. extern "C" long __stdcall QueryPerformanceFrequency( LARGE_INTEGER *lpPerformanceCount );
  42. namespace {
  43. class CDebugMicroTimer
  44. {
  45. public:
  46. CDebugMicroTimer(const char* n) : name(n) { QueryPerformanceCounter(&start); }
  47. ~CDebugMicroTimer() {
  48. LARGE_INTEGER end;
  49. char outbuf[128];
  50. QueryPerformanceCounter(&end);
  51. if (!freq) QueryPerformanceFrequency((LARGE_INTEGER*)&freq);
  52. V_snprintf(outbuf, 128, "%s %6d us\n", name, (int)((end.QuadPart - start.QuadPart) * 1000000 / freq));
  53. OutputDebugStringA(outbuf);
  54. }
  55. LARGE_INTEGER start;
  56. const char* name;
  57. static long long freq;
  58. };
  59. long long CDebugMicroTimer::freq = 0;
  60. }
  61. #define DEBUG_SCOPE_TIMER(name) CDebugMicroTimer dbgLocalTimer(#name)
  62. #else
  63. #define DEBUG_SCOPE_TIMER(name) (void)0
  64. #endif
  65. #ifdef _RETAIL
  66. #define NO_LOG_MDLCACHE 1
  67. #endif
  68. #ifdef NO_LOG_MDLCACHE
  69. #define LogMdlCache() 0
  70. #else
  71. #define LogMdlCache() mod_trace_load.GetBool()
  72. #endif
  73. #define MdlCacheMsg if ( !LogMdlCache() ) ; else Msg
  74. #define MdlCacheWarning if ( !LogMdlCache() ) ; else Warning
  75. #if defined( _X360 )
  76. #define AsyncMdlCache() 0 // Explicitly OFF for 360 (incompatible)
  77. #else
  78. #define AsyncMdlCache() 0
  79. #endif
  80. #define ERROR_MODEL "models/error.mdl"
  81. #define IDSTUDIOHEADER (('T'<<24)+('S'<<16)+('D'<<8)+'I')
  82. #define MakeCacheID( handle, type ) ( ( (uint)(handle) << 16 ) | (uint)(type) )
  83. #define HandleFromCacheID( id) ( (MDLHandle_t)((id) >> 16) )
  84. #define TypeFromCacheID( id ) ( (MDLCacheDataType_t)((id) & 0xffff) )
  85. enum
  86. {
  87. STUDIODATA_FLAGS_STUDIOMESH_LOADED = 0x0001,
  88. STUDIODATA_FLAGS_VCOLLISION_LOADED = 0x0002,
  89. STUDIODATA_ERROR_MODEL = 0x0004,
  90. STUDIODATA_FLAGS_NO_STUDIOMESH = 0x0008,
  91. STUDIODATA_FLAGS_NO_VERTEX_DATA = 0x0010,
  92. STUDIODATA_FLAGS_VCOLLISION_SHARED = 0x0020,
  93. STUDIODATA_FLAGS_LOCKED_MDL = 0x0040,
  94. };
  95. // only models with type "mod_studio" have this data
  96. struct studiodata_t
  97. {
  98. // The .mdl file
  99. DataCacheHandle_t m_MDLCache;
  100. // the vphysics.dll collision model
  101. vcollide_t m_VCollisionData;
  102. studiohwdata_t m_HardwareData;
  103. #if defined( USE_HARDWARE_CACHE )
  104. DataCacheHandle_t m_HardwareDataCache;
  105. #endif
  106. unsigned short m_nFlags;
  107. short m_nRefCount;
  108. // pointer to the virtual version of the model
  109. virtualmodel_t *m_pVirtualModel;
  110. // array of cache handles to demand loaded virtual model data
  111. int m_nAnimBlockCount;
  112. DataCacheHandle_t *m_pAnimBlock;
  113. unsigned long *m_iFakeAnimBlockStall;
  114. // vertex data is usually compressed to save memory (model decal code only needs some data)
  115. DataCacheHandle_t m_VertexCache;
  116. bool m_VertexDataIsCompressed;
  117. int m_nAutoplaySequenceCount;
  118. unsigned short *m_pAutoplaySequenceList;
  119. void *m_pUserData;
  120. DECLARE_FIXEDSIZE_ALLOCATOR_MT( studiodata_t );
  121. };
  122. DEFINE_FIXEDSIZE_ALLOCATOR_MT( studiodata_t, 128, CUtlMemoryPool::GROW_SLOW );
  123. // memdbgon must be the last include file in a .cpp file!!!
  124. #include "tier0/memdbgon.h"
  125. class CTempAllocHelper
  126. {
  127. public:
  128. CTempAllocHelper()
  129. {
  130. m_pData = NULL;
  131. }
  132. ~CTempAllocHelper()
  133. {
  134. Free();
  135. }
  136. void *Get()
  137. {
  138. return m_pData;
  139. }
  140. void Alloc( int nSize )
  141. {
  142. m_pData = malloc( nSize );
  143. }
  144. void Free()
  145. {
  146. if ( m_pData )
  147. {
  148. free( m_pData );
  149. m_pData = NULL;
  150. }
  151. }
  152. private:
  153. void *m_pData;
  154. };
  155. //-----------------------------------------------------------------------------
  156. // ConVars
  157. //-----------------------------------------------------------------------------
  158. static ConVar r_rootlod( "r_rootlod", "0", FCVAR_ARCHIVE );
  159. static ConVar mod_forcedata( "mod_forcedata", ( AsyncMdlCache() ) ? "0" : "1", 0, "Forces all model file data into cache on model load." );
  160. static ConVar mod_test_not_available( "mod_test_not_available", "0", FCVAR_CHEAT );
  161. static ConVar mod_test_mesh_not_available( "mod_test_mesh_not_available", "0", FCVAR_CHEAT );
  162. static ConVar mod_test_verts_not_available( "mod_test_verts_not_available", "0", FCVAR_CHEAT );
  163. static ConVar mod_load_mesh_async( "mod_load_mesh_async", ( AsyncMdlCache() ) ? "1" : "0" );
  164. static ConVar mod_load_anims_async( "mod_load_anims_async", ( IsX360() || AsyncMdlCache() ) ? "1" : "0" );
  165. static ConVar mod_load_vcollide_async( "mod_load_vcollide_async", ( AsyncMdlCache() ) ? "1" : "0" );
  166. static ConVar mod_trace_load( "mod_trace_load", "0" );
  167. static ConVar mod_lock_mdls_on_load( "mod_lock_mdls_on_load", ( IsX360() ) ? "1" : "0" );
  168. static ConVar mod_load_fakestall( "mod_load_fakestall", "0", 0, "Forces all ANI file loading to stall for specified ms\n");
  169. //-----------------------------------------------------------------------------
  170. // Utility functions
  171. //-----------------------------------------------------------------------------
  172. #if defined( USE_HARDWARE_CACHE )
  173. unsigned ComputeHardwareDataSize( studiohwdata_t *pData )
  174. {
  175. unsigned size = 0;
  176. for ( int i = pData->m_RootLOD; i < pData->m_NumLODs; i++ )
  177. {
  178. studioloddata_t *pLOD = &pData->m_pLODs[i];
  179. for ( int j = 0; j < pData->m_NumStudioMeshes; j++ )
  180. {
  181. studiomeshdata_t *pMeshData = &pLOD->m_pMeshData[j];
  182. for ( int k = 0; k < pMeshData->m_NumGroup; k++ )
  183. {
  184. size += pMeshData->m_pMeshGroup[k].m_pMesh->ComputeMemoryUsed();
  185. }
  186. }
  187. }
  188. return size;
  189. }
  190. #endif
  191. //-----------------------------------------------------------------------------
  192. // Async support
  193. //-----------------------------------------------------------------------------
  194. #define MDLCACHE_NONE ((MDLCacheDataType_t)-1)
  195. struct AsyncInfo_t
  196. {
  197. AsyncInfo_t() : hControl( NULL ), hModel( MDLHANDLE_INVALID ), type( MDLCACHE_NONE ), iAnimBlock( 0 ) {}
  198. FSAsyncControl_t hControl;
  199. MDLHandle_t hModel;
  200. MDLCacheDataType_t type;
  201. int iAnimBlock;
  202. };
  203. const int NO_ASYNC = CUtlLinkedList< AsyncInfo_t >::InvalidIndex();
  204. //-------------------------------------
  205. CUtlMap<int, int> g_AsyncInfoMap( DefLessFunc( int ) );
  206. CThreadFastMutex g_AsyncInfoMapMutex;
  207. inline int MakeAsyncInfoKey( MDLHandle_t hModel, MDLCacheDataType_t type, int iAnimBlock )
  208. {
  209. Assert( type <= 7 && iAnimBlock < 8*1024 );
  210. return ( ( ( (int)hModel) << 16 ) | ( (int)type << 13 ) | iAnimBlock );
  211. }
  212. inline int GetAsyncInfoIndex( MDLHandle_t hModel, MDLCacheDataType_t type, int iAnimBlock = 0 )
  213. {
  214. AUTO_LOCK( g_AsyncInfoMapMutex );
  215. int key = MakeAsyncInfoKey( hModel, type, iAnimBlock );
  216. int i = g_AsyncInfoMap.Find( key );
  217. if ( i == g_AsyncInfoMap.InvalidIndex() )
  218. {
  219. return NO_ASYNC;
  220. }
  221. return g_AsyncInfoMap[i];
  222. }
  223. inline int SetAsyncInfoIndex( MDLHandle_t hModel, MDLCacheDataType_t type, int iAnimBlock, int index )
  224. {
  225. AUTO_LOCK( g_AsyncInfoMapMutex );
  226. Assert( index == NO_ASYNC || GetAsyncInfoIndex( hModel, type, iAnimBlock ) == NO_ASYNC );
  227. int key = MakeAsyncInfoKey( hModel, type, iAnimBlock );
  228. if ( index == NO_ASYNC )
  229. {
  230. g_AsyncInfoMap.Remove( key );
  231. }
  232. else
  233. {
  234. g_AsyncInfoMap.Insert( key, index );
  235. }
  236. return index;
  237. }
  238. inline int SetAsyncInfoIndex( MDLHandle_t hModel, MDLCacheDataType_t type, int index )
  239. {
  240. return SetAsyncInfoIndex( hModel, type, 0, index );
  241. }
  242. //-----------------------------------------------------------------------------
  243. // QUEUED LOADING
  244. // Populates the cache by pushing expected MDL's (and all of their data).
  245. // The Model cache i/o behavior is unchanged during gameplay, ideally the cache
  246. // should yield miss free behaviour.
  247. //-----------------------------------------------------------------------------
  248. struct ModelParts_t
  249. {
  250. enum BufferType_t
  251. {
  252. BUFFER_MDL = 0,
  253. BUFFER_VTX = 1,
  254. BUFFER_VVD = 2,
  255. BUFFER_PHY = 3,
  256. BUFFER_MAXPARTS,
  257. };
  258. ModelParts_t()
  259. {
  260. nLoadedParts = 0;
  261. nExpectedParts = 0;
  262. hMDL = MDLHANDLE_INVALID;
  263. hFileCache = 0;
  264. bHeaderLoaded = false;
  265. bMaterialsPending = false;
  266. bTexturesPending = false;
  267. }
  268. // thread safe, only one thread will get a positive result
  269. bool DoFinalProcessing()
  270. {
  271. // indicates that all buffers have arrived
  272. // when all parts are present, returns true ( guaranteed once ), and marked as completed
  273. return nLoadedParts.AssignIf( nExpectedParts, nExpectedParts | 0x80000000 );
  274. }
  275. CUtlBuffer Buffers[BUFFER_MAXPARTS];
  276. MDLHandle_t hMDL;
  277. // async material loading on PC
  278. FileCacheHandle_t hFileCache;
  279. bool bHeaderLoaded;
  280. bool bMaterialsPending;
  281. bool bTexturesPending;
  282. CUtlVector< IMaterial* > Materials;
  283. // bit flags
  284. CInterlockedInt nLoadedParts;
  285. int nExpectedParts;
  286. private:
  287. ModelParts_t(const ModelParts_t&); // no impl
  288. ModelParts_t& operator=(const ModelParts_t&); // no impl
  289. };
  290. struct CleanupModelParts_t
  291. {
  292. FileCacheHandle_t hFileCache;
  293. CUtlVector< IMaterial* > Materials;
  294. };
  295. //-----------------------------------------------------------------------------
  296. // Implementation of the simple studio data cache (no caching)
  297. //-----------------------------------------------------------------------------
  298. class CMDLCache : public CTier3AppSystem< IMDLCache >, public IStudioDataCache, public CDefaultDataCacheClient
  299. {
  300. typedef CTier3AppSystem< IMDLCache > BaseClass;
  301. public:
  302. CMDLCache();
  303. // Inherited from IAppSystem
  304. virtual bool Connect( CreateInterfaceFn factory );
  305. virtual void Disconnect();
  306. virtual void *QueryInterface( const char *pInterfaceName );
  307. virtual InitReturnVal_t Init();
  308. virtual void Shutdown();
  309. // Inherited from IStudioDataCache
  310. bool VerifyHeaders( studiohdr_t *pStudioHdr );
  311. vertexFileHeader_t *CacheVertexData( studiohdr_t *pStudioHdr );
  312. // Inherited from IMDLCache
  313. virtual MDLHandle_t FindMDL( const char *pMDLRelativePath );
  314. virtual int AddRef( MDLHandle_t handle );
  315. virtual int Release( MDLHandle_t handle );
  316. virtual int GetRef( MDLHandle_t handle );
  317. virtual void MarkAsLoaded(MDLHandle_t handle);
  318. virtual studiohdr_t *GetStudioHdr( MDLHandle_t handle );
  319. virtual studiohwdata_t *GetHardwareData( MDLHandle_t handle );
  320. virtual vcollide_t *GetVCollide( MDLHandle_t handle ) { return GetVCollideEx( handle, true); }
  321. virtual vcollide_t *GetVCollideEx( MDLHandle_t handle, bool synchronousLoad = true );
  322. virtual unsigned char *GetAnimBlock( MDLHandle_t handle, int nBlock );
  323. virtual virtualmodel_t *GetVirtualModel( MDLHandle_t handle );
  324. virtual virtualmodel_t *GetVirtualModelFast( const studiohdr_t *pStudioHdr, MDLHandle_t handle );
  325. virtual int GetAutoplayList( MDLHandle_t handle, unsigned short **pOut );
  326. virtual void TouchAllData( MDLHandle_t handle );
  327. virtual void SetUserData( MDLHandle_t handle, void* pData );
  328. virtual void *GetUserData( MDLHandle_t handle );
  329. virtual bool IsErrorModel( MDLHandle_t handle );
  330. virtual void SetCacheNotify( IMDLCacheNotify *pNotify );
  331. virtual vertexFileHeader_t *GetVertexData( MDLHandle_t handle );
  332. virtual void Flush( MDLCacheFlush_t nFlushFlags = MDLCACHE_FLUSH_ALL );
  333. virtual void Flush( MDLHandle_t handle, int nFlushFlags = MDLCACHE_FLUSH_ALL );
  334. virtual const char *GetModelName( MDLHandle_t handle );
  335. IDataCacheSection *GetCacheSection( MDLCacheDataType_t type )
  336. {
  337. switch ( type )
  338. {
  339. case MDLCACHE_STUDIOHWDATA:
  340. case MDLCACHE_VERTEXES:
  341. // meshes and vertexes are isolated to their own section
  342. return m_pMeshCacheSection;
  343. case MDLCACHE_ANIMBLOCK:
  344. // anim blocks have their own section
  345. return m_pAnimBlockCacheSection;
  346. default:
  347. // everybody else
  348. return m_pModelCacheSection;
  349. }
  350. }
  351. void *AllocData( MDLCacheDataType_t type, int size );
  352. void FreeData( MDLCacheDataType_t type, void *pData );
  353. void CacheData( DataCacheHandle_t *c, void *pData, int size, const char *name, MDLCacheDataType_t type, DataCacheClientID_t id = (DataCacheClientID_t)-1 );
  354. void *CheckData( DataCacheHandle_t c, MDLCacheDataType_t type );
  355. void *CheckDataNoTouch( DataCacheHandle_t c, MDLCacheDataType_t type );
  356. void UncacheData( DataCacheHandle_t c, MDLCacheDataType_t type, bool bLockedOk = false );
  357. void DisableAsync() { mod_load_mesh_async.SetValue( 0 ); mod_load_anims_async.SetValue( 0 ); }
  358. virtual void BeginLock();
  359. virtual void EndLock();
  360. virtual int *GetFrameUnlockCounterPtrOLD();
  361. virtual int *GetFrameUnlockCounterPtr( MDLCacheDataType_t type );
  362. virtual void FinishPendingLoads();
  363. // Task switch
  364. void ReleaseMaterialSystemObjects();
  365. void RestoreMaterialSystemObjects( int nChangeFlags );
  366. virtual bool GetVCollideSize( MDLHandle_t handle, int *pVCollideSize );
  367. virtual void BeginMapLoad();
  368. virtual void EndMapLoad();
  369. virtual void InitPreloadData( bool rebuild );
  370. virtual void ShutdownPreloadData();
  371. virtual bool IsDataLoaded( MDLHandle_t handle, MDLCacheDataType_t type );
  372. virtual studiohdr_t *LockStudioHdr( MDLHandle_t handle );
  373. virtual void UnlockStudioHdr( MDLHandle_t handle );
  374. virtual bool PreloadModel( MDLHandle_t handle );
  375. virtual void ResetErrorModelStatus( MDLHandle_t handle );
  376. virtual void MarkFrame();
  377. // Queued loading
  378. void ProcessQueuedData( ModelParts_t *pModelParts, bool bHeaderOnly = false );
  379. static void QueuedLoaderCallback_MDL( void *pContext, void *pContext2, const void *pData, int nSize, LoaderError_t loaderError );
  380. static void ProcessDynamicLoad( ModelParts_t *pModelParts );
  381. static void CleanupDynamicLoad( CleanupModelParts_t *pCleanup );
  382. private:
  383. // Inits, shuts downs studiodata_t
  384. void InitStudioData( MDLHandle_t handle );
  385. void ShutdownStudioData( MDLHandle_t handle );
  386. // Returns the *actual* name of the model (could be an error model if the requested model didn't load)
  387. const char *GetActualModelName( MDLHandle_t handle );
  388. // Constructs a filename based on a model handle
  389. void MakeFilename( MDLHandle_t handle, const char *pszExtension, char *pszFileName, int nMaxLength );
  390. // Inform filesystem that we unloaded a particular file
  391. void NotifyFileUnloaded( MDLHandle_t handle, const char *pszExtension );
  392. // Attempts to load a MDL file, validates that it's ok.
  393. bool ReadMDLFile( MDLHandle_t handle, const char *pMDLFileName, CUtlBuffer &buf );
  394. // Unserializes the VCollide file associated w/ models (the vphysics representation)
  395. void UnserializeVCollide( MDLHandle_t handle, bool synchronousLoad );
  396. // Destroys the VCollide associated w/ models
  397. void DestroyVCollide( MDLHandle_t handle );
  398. // Unserializes the MDL
  399. studiohdr_t *UnserializeMDL( MDLHandle_t handle, void *pData, int nDataSize, bool bDataValid );
  400. // Unserializes an animation block from disk
  401. unsigned char *UnserializeAnimBlock( MDLHandle_t handle, int nBlock );
  402. // Allocates/frees the anim blocks
  403. void AllocateAnimBlocks( studiodata_t *pStudioData, int nCount );
  404. void FreeAnimBlocks( MDLHandle_t handle );
  405. // Allocates/frees the virtual model
  406. void AllocateVirtualModel( MDLHandle_t handle );
  407. void FreeVirtualModel( MDLHandle_t handle );
  408. // Purpose: Pulls all submodels/.ani file models into the cache
  409. void UnserializeAllVirtualModelsAndAnimBlocks( MDLHandle_t handle );
  410. // Loads/unloads the static meshes
  411. bool LoadHardwareData( MDLHandle_t handle ); // returns false if not ready
  412. void UnloadHardwareData( MDLHandle_t handle, bool bCacheRemove = true, bool bLockedOk = false );
  413. // Allocates/frees autoplay sequence list
  414. void AllocateAutoplaySequences( studiodata_t *pStudioData, int nCount );
  415. void FreeAutoplaySequences( studiodata_t *pStudioData );
  416. FSAsyncStatus_t LoadData( const char *pszFilename, const char *pszPathID, bool bAsync, FSAsyncControl_t *pControl ) { return LoadData( pszFilename, pszPathID, NULL, 0, 0, bAsync, pControl ); }
  417. FSAsyncStatus_t LoadData( const char *pszFilename, const char *pszPathID, void *pDest, int nBytes, int nOffset, bool bAsync, FSAsyncControl_t *pControl );
  418. vertexFileHeader_t *LoadVertexData( studiohdr_t *pStudioHdr );
  419. vertexFileHeader_t *BuildAndCacheVertexData( studiohdr_t *pStudioHdr, vertexFileHeader_t *pRawVvdHdr );
  420. bool BuildHardwareData( MDLHandle_t handle, studiodata_t *pStudioData, studiohdr_t *pStudioHdr, OptimizedModel::FileHeader_t *pVtxHdr );
  421. void ConvertFlexData( studiohdr_t *pStudioHdr );
  422. int ProcessPendingAsync( int iAsync );
  423. void ProcessPendingAsyncs( MDLCacheDataType_t type = MDLCACHE_NONE );
  424. bool ClearAsync( MDLHandle_t handle, MDLCacheDataType_t type, int iAnimBlock, bool bAbort = false );
  425. const char *GetVTXExtension();
  426. virtual bool HandleCacheNotification( const DataCacheNotification_t &notification );
  427. virtual bool GetItemName( DataCacheClientID_t clientId, const void *pItem, char *pDest, unsigned nMaxLen );
  428. virtual bool GetAsyncLoad( MDLCacheDataType_t type );
  429. virtual bool SetAsyncLoad( MDLCacheDataType_t type, bool bAsync );
  430. // Creates the 360 file if it doesn't exist or is out of date
  431. int UpdateOrCreate( studiohdr_t *pHdr, const char *pFilename, char *pX360Filename, int maxLen, const char *pPathID, bool bForce = false );
  432. // Attempts to read the platform native file - on 360 it can read and swap Win32 file as a fallback
  433. bool ReadFileNative( char *pFileName, const char *pPath, CUtlBuffer &buf, int nMaxBytes = 0 );
  434. // Creates a thin cache entry (to be used for model decals) from fat vertex data
  435. vertexFileHeader_t * CreateThinVertexes( vertexFileHeader_t * originalData, const studiohdr_t * pStudioHdr, int * cacheLength );
  436. // Processes raw data (from an I/O source) into the cache. Sets the cache state as expected for bad data.
  437. bool ProcessDataIntoCache( MDLHandle_t handle, MDLCacheDataType_t type, int iAnimBlock, void *pData, int nDataSize, bool bDataValid );
  438. void BreakFrameLock( bool bModels = true, bool bMesh = true );
  439. void RestoreFrameLock();
  440. private:
  441. IDataCacheSection *m_pModelCacheSection;
  442. IDataCacheSection *m_pMeshCacheSection;
  443. IDataCacheSection *m_pAnimBlockCacheSection;
  444. int m_nModelCacheFrameLocks;
  445. int m_nMeshCacheFrameLocks;
  446. CUtlDict< studiodata_t*, MDLHandle_t > m_MDLDict;
  447. IMDLCacheNotify *m_pCacheNotify;
  448. CUtlFixedLinkedList< AsyncInfo_t > m_PendingAsyncs;
  449. CThreadFastMutex m_QueuedLoadingMutex;
  450. CThreadFastMutex m_AsyncMutex;
  451. bool m_bLostVideoMemory : 1;
  452. bool m_bConnected : 1;
  453. bool m_bInitialized : 1;
  454. };
  455. //-----------------------------------------------------------------------------
  456. // Singleton interface
  457. //-----------------------------------------------------------------------------
  458. static CMDLCache g_MDLCache;
  459. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CMDLCache, IMDLCache, MDLCACHE_INTERFACE_VERSION, g_MDLCache );
  460. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CMDLCache, IStudioDataCache, STUDIO_DATA_CACHE_INTERFACE_VERSION, g_MDLCache );
  461. //-----------------------------------------------------------------------------
  462. // Task switch
  463. //-----------------------------------------------------------------------------
  464. static void ReleaseMaterialSystemObjects( )
  465. {
  466. g_MDLCache.ReleaseMaterialSystemObjects();
  467. }
  468. static void RestoreMaterialSystemObjects( int nChangeFlags )
  469. {
  470. g_MDLCache.RestoreMaterialSystemObjects( nChangeFlags );
  471. }
  472. //-----------------------------------------------------------------------------
  473. // Constructor
  474. //-----------------------------------------------------------------------------
  475. CMDLCache::CMDLCache() : BaseClass( false )
  476. {
  477. m_bLostVideoMemory = false;
  478. m_bConnected = false;
  479. m_bInitialized = false;
  480. m_pCacheNotify = NULL;
  481. m_pModelCacheSection = NULL;
  482. m_pMeshCacheSection = NULL;
  483. m_pAnimBlockCacheSection = NULL;
  484. m_nModelCacheFrameLocks = 0;
  485. m_nMeshCacheFrameLocks = 0;
  486. }
  487. //-----------------------------------------------------------------------------
  488. // Connect, disconnect
  489. //-----------------------------------------------------------------------------
  490. bool CMDLCache::Connect( CreateInterfaceFn factory )
  491. {
  492. // Connect can be called twice, because this inherits from 2 appsystems.
  493. if ( m_bConnected )
  494. return true;
  495. if ( !BaseClass::Connect( factory ) )
  496. return false;
  497. if ( !g_pMaterialSystemHardwareConfig || !g_pPhysicsCollision || !g_pStudioRender || !g_pMaterialSystem )
  498. return false;
  499. m_bConnected = true;
  500. if( g_pMaterialSystem )
  501. {
  502. g_pMaterialSystem->AddReleaseFunc( ::ReleaseMaterialSystemObjects );
  503. g_pMaterialSystem->AddRestoreFunc( ::RestoreMaterialSystemObjects );
  504. }
  505. return true;
  506. }
  507. void CMDLCache::Disconnect()
  508. {
  509. if ( g_pMaterialSystem && m_bConnected )
  510. {
  511. g_pMaterialSystem->RemoveReleaseFunc( ::ReleaseMaterialSystemObjects );
  512. g_pMaterialSystem->RemoveRestoreFunc( ::RestoreMaterialSystemObjects );
  513. m_bConnected = false;
  514. }
  515. BaseClass::Disconnect();
  516. }
  517. //-----------------------------------------------------------------------------
  518. // Query Interface
  519. //-----------------------------------------------------------------------------
  520. void *CMDLCache::QueryInterface( const char *pInterfaceName )
  521. {
  522. if (!Q_strncmp( pInterfaceName, STUDIO_DATA_CACHE_INTERFACE_VERSION, Q_strlen(STUDIO_DATA_CACHE_INTERFACE_VERSION) + 1))
  523. return (IStudioDataCache*)this;
  524. if (!Q_strncmp( pInterfaceName, MDLCACHE_INTERFACE_VERSION, Q_strlen(MDLCACHE_INTERFACE_VERSION) + 1))
  525. return (IMDLCache*)this;
  526. return NULL;
  527. }
  528. //-----------------------------------------------------------------------------
  529. // Init/Shutdown
  530. //-----------------------------------------------------------------------------
  531. #define MODEL_CACHE_MODEL_SECTION_NAME "ModelData"
  532. #define MODEL_CACHE_MESH_SECTION_NAME "ModelMesh"
  533. #define MODEL_CACHE_ANIMBLOCK_SECTION_NAME "AnimBlock"
  534. // #define ENABLE_CACHE_WATCH 1
  535. #if defined( ENABLE_CACHE_WATCH )
  536. static ConVar cache_watch( "cache_watch", "", 0 );
  537. static void CacheLog( const char *fileName, const char *accessType )
  538. {
  539. if ( Q_stristr( fileName, cache_watch.GetString() ) )
  540. {
  541. Msg( "%s access to %s\n", accessType, fileName );
  542. }
  543. }
  544. #endif
  545. InitReturnVal_t CMDLCache::Init()
  546. {
  547. // Can be called twice since it inherits from 2 appsystems
  548. if ( m_bInitialized )
  549. return INIT_OK;
  550. InitReturnVal_t nRetVal = BaseClass::Init();
  551. if ( nRetVal != INIT_OK )
  552. return nRetVal;
  553. if ( !m_pModelCacheSection )
  554. {
  555. m_pModelCacheSection = g_pDataCache->AddSection( this, MODEL_CACHE_MODEL_SECTION_NAME );
  556. }
  557. if ( !m_pMeshCacheSection )
  558. {
  559. unsigned int meshLimit = (unsigned)-1;
  560. DataCacheLimits_t limits( meshLimit, (unsigned)-1, 0, 0 );
  561. m_pMeshCacheSection = g_pDataCache->AddSection( this, MODEL_CACHE_MESH_SECTION_NAME, limits );
  562. }
  563. if ( !m_pAnimBlockCacheSection )
  564. {
  565. // 360 tuned to worst case, ep_outland_12a, less than 6 MB is not a viable working set
  566. unsigned int animBlockLimit = IsX360() ? 6*1024*1024 : (unsigned)-1;
  567. DataCacheLimits_t limits( animBlockLimit, (unsigned)-1, 0, 0 );
  568. m_pAnimBlockCacheSection = g_pDataCache->AddSection( this, MODEL_CACHE_ANIMBLOCK_SECTION_NAME, limits );
  569. }
  570. if ( IsX360() )
  571. {
  572. // By default, source data is assumed to be non-native to the 360.
  573. StudioByteSwap::ActivateByteSwapping( true );
  574. StudioByteSwap::SetCollisionInterface( g_pPhysicsCollision );
  575. }
  576. m_bLostVideoMemory = false;
  577. m_bInitialized = true;
  578. #if defined( ENABLE_CACHE_WATCH )
  579. g_pFullFileSystem->AddLoggingFunc( &CacheLog );
  580. #endif
  581. return INIT_OK;
  582. }
  583. void CMDLCache::Shutdown()
  584. {
  585. if ( !m_bInitialized )
  586. return;
  587. #if defined( ENABLE_CACHE_WATCH )
  588. g_pFullFileSystem->RemoveLoggingFunc( CacheLog );
  589. #endif
  590. m_bInitialized = false;
  591. if ( m_pModelCacheSection || m_pMeshCacheSection )
  592. {
  593. // Free all MDLs that haven't been cleaned up
  594. MDLHandle_t i = m_MDLDict.First();
  595. while ( i != m_MDLDict.InvalidIndex() )
  596. {
  597. ShutdownStudioData( i );
  598. i = m_MDLDict.Next( i );
  599. }
  600. m_MDLDict.Purge();
  601. if ( m_pModelCacheSection )
  602. {
  603. g_pDataCache->RemoveSection( MODEL_CACHE_MODEL_SECTION_NAME );
  604. m_pModelCacheSection = NULL;
  605. }
  606. if ( m_pMeshCacheSection )
  607. {
  608. g_pDataCache->RemoveSection( MODEL_CACHE_MESH_SECTION_NAME );
  609. m_pMeshCacheSection = NULL;
  610. }
  611. }
  612. if ( m_pAnimBlockCacheSection )
  613. {
  614. g_pDataCache->RemoveSection( MODEL_CACHE_ANIMBLOCK_SECTION_NAME );
  615. m_pAnimBlockCacheSection = NULL;
  616. }
  617. BaseClass::Shutdown();
  618. }
  619. //-----------------------------------------------------------------------------
  620. // Flushes an MDLHandle_t
  621. //-----------------------------------------------------------------------------
  622. void CMDLCache::Flush( MDLHandle_t handle, int nFlushFlags )
  623. {
  624. studiodata_t *pStudioData = m_MDLDict[handle];
  625. Assert( pStudioData != NULL );
  626. bool bIgnoreLock = ( nFlushFlags & MDLCACHE_FLUSH_IGNORELOCK ) != 0;
  627. // release the hardware portion
  628. if ( nFlushFlags & MDLCACHE_FLUSH_STUDIOHWDATA )
  629. {
  630. if ( ClearAsync( handle, MDLCACHE_STUDIOHWDATA, 0, true ) )
  631. {
  632. m_pMeshCacheSection->Unlock( pStudioData->m_VertexCache );
  633. }
  634. UnloadHardwareData( handle, true, bIgnoreLock );
  635. }
  636. // free collision
  637. if ( nFlushFlags & MDLCACHE_FLUSH_VCOLLIDE )
  638. {
  639. DestroyVCollide( handle );
  640. }
  641. // Free animations
  642. if ( nFlushFlags & MDLCACHE_FLUSH_VIRTUALMODEL )
  643. {
  644. FreeVirtualModel( handle );
  645. }
  646. if ( nFlushFlags & MDLCACHE_FLUSH_ANIMBLOCK )
  647. {
  648. FreeAnimBlocks( handle );
  649. }
  650. if ( nFlushFlags & MDLCACHE_FLUSH_AUTOPLAY )
  651. {
  652. // Free autoplay sequences
  653. FreeAutoplaySequences( pStudioData );
  654. }
  655. if ( nFlushFlags & MDLCACHE_FLUSH_STUDIOHDR )
  656. {
  657. MdlCacheMsg( "MDLCache: Free studiohdr %s\n", GetModelName( handle ) );
  658. if ( pStudioData->m_nFlags & STUDIODATA_FLAGS_LOCKED_MDL )
  659. {
  660. GetCacheSection( MDLCACHE_STUDIOHDR )->Unlock( pStudioData->m_MDLCache );
  661. pStudioData->m_nFlags &= ~STUDIODATA_FLAGS_LOCKED_MDL;
  662. }
  663. UncacheData( pStudioData->m_MDLCache, MDLCACHE_STUDIOHDR, bIgnoreLock );
  664. pStudioData->m_MDLCache = NULL;
  665. }
  666. if ( nFlushFlags & MDLCACHE_FLUSH_VERTEXES )
  667. {
  668. MdlCacheMsg( "MDLCache: Free VVD %s\n", GetModelName( handle ) );
  669. ClearAsync( handle, MDLCACHE_VERTEXES, 0, true );
  670. UncacheData( pStudioData->m_VertexCache, MDLCACHE_VERTEXES, bIgnoreLock );
  671. pStudioData->m_VertexCache = NULL;
  672. }
  673. // Now check whatever files are not loaded, make sure file system knows
  674. // that we don't have them loaded.
  675. if ( !IsDataLoaded( handle, MDLCACHE_STUDIOHDR ) )
  676. NotifyFileUnloaded( handle, ".mdl" );
  677. if ( !IsDataLoaded( handle, MDLCACHE_STUDIOHWDATA ) )
  678. NotifyFileUnloaded( handle, GetVTXExtension() );
  679. if ( !IsDataLoaded( handle, MDLCACHE_VERTEXES ) )
  680. NotifyFileUnloaded( handle, ".vvd" );
  681. if ( !IsDataLoaded( handle, MDLCACHE_VCOLLIDE ) )
  682. NotifyFileUnloaded( handle, ".phy" );
  683. }
  684. //-----------------------------------------------------------------------------
  685. // Inits, shuts downs studiodata_t
  686. //-----------------------------------------------------------------------------
  687. void CMDLCache::InitStudioData( MDLHandle_t handle )
  688. {
  689. Assert( m_MDLDict[handle] == NULL );
  690. studiodata_t *pStudioData = new studiodata_t;
  691. m_MDLDict[handle] = pStudioData;
  692. memset( pStudioData, 0, sizeof( studiodata_t ) );
  693. }
  694. void CMDLCache::ShutdownStudioData( MDLHandle_t handle )
  695. {
  696. Flush( handle );
  697. studiodata_t *pStudioData = m_MDLDict[handle];
  698. Assert( pStudioData != NULL );
  699. delete pStudioData;
  700. m_MDLDict[handle] = NULL;
  701. }
  702. //-----------------------------------------------------------------------------
  703. // Sets the cache notify
  704. //-----------------------------------------------------------------------------
  705. void CMDLCache::SetCacheNotify( IMDLCacheNotify *pNotify )
  706. {
  707. m_pCacheNotify = pNotify;
  708. }
  709. //-----------------------------------------------------------------------------
  710. // Returns the name of the model
  711. //-----------------------------------------------------------------------------
  712. const char *CMDLCache::GetModelName( MDLHandle_t handle )
  713. {
  714. if ( handle == MDLHANDLE_INVALID )
  715. return ERROR_MODEL;
  716. return m_MDLDict.GetElementName( handle );
  717. }
  718. //-----------------------------------------------------------------------------
  719. // Returns the *actual* name of the model (could be an error model)
  720. //-----------------------------------------------------------------------------
  721. const char *CMDLCache::GetActualModelName( MDLHandle_t handle )
  722. {
  723. if ( handle == MDLHANDLE_INVALID )
  724. return ERROR_MODEL;
  725. if ( m_MDLDict[handle]->m_nFlags & STUDIODATA_ERROR_MODEL )
  726. return ERROR_MODEL;
  727. return m_MDLDict.GetElementName( handle );
  728. }
  729. //-----------------------------------------------------------------------------
  730. // Constructs a filename based on a model handle
  731. //-----------------------------------------------------------------------------
  732. void CMDLCache::MakeFilename( MDLHandle_t handle, const char *pszExtension, char *pszFileName, int nMaxLength )
  733. {
  734. Q_strncpy( pszFileName, GetActualModelName( handle ), nMaxLength );
  735. Q_SetExtension( pszFileName, pszExtension, nMaxLength );
  736. Q_FixSlashes( pszFileName );
  737. #ifdef _LINUX
  738. Q_strlower( pszFileName );
  739. #endif
  740. }
  741. //-----------------------------------------------------------------------------
  742. void CMDLCache::NotifyFileUnloaded( MDLHandle_t handle, const char *pszExtension )
  743. {
  744. if ( handle == MDLHANDLE_INVALID )
  745. return;
  746. if ( !m_MDLDict.IsValidIndex( handle ) )
  747. return;
  748. char szFilename[MAX_PATH];
  749. V_strcpy_safe( szFilename, m_MDLDict.GetElementName( handle ) );
  750. V_SetExtension( szFilename, pszExtension, sizeof(szFilename) );
  751. V_FixSlashes( szFilename );
  752. g_pFullFileSystem->NotifyFileUnloaded( szFilename, "game" );
  753. }
  754. //-----------------------------------------------------------------------------
  755. // Finds an MDL
  756. //-----------------------------------------------------------------------------
  757. MDLHandle_t CMDLCache::FindMDL( const char *pMDLRelativePath )
  758. {
  759. // can't trust provided path
  760. // ensure provided path correctly resolves (Dictionary is case-insensitive)
  761. char szFixedName[MAX_PATH];
  762. V_strncpy( szFixedName, pMDLRelativePath, sizeof( szFixedName ) );
  763. V_RemoveDotSlashes( szFixedName, '/' );
  764. MDLHandle_t handle = m_MDLDict.Find( szFixedName );
  765. if ( handle == m_MDLDict.InvalidIndex() )
  766. {
  767. handle = m_MDLDict.Insert( szFixedName, NULL );
  768. InitStudioData( handle );
  769. }
  770. AddRef( handle );
  771. return handle;
  772. }
  773. //-----------------------------------------------------------------------------
  774. // Reference counting
  775. //-----------------------------------------------------------------------------
  776. int CMDLCache::AddRef( MDLHandle_t handle )
  777. {
  778. return ++m_MDLDict[handle]->m_nRefCount;
  779. }
  780. int CMDLCache::Release( MDLHandle_t handle )
  781. {
  782. // Deal with shutdown order issues (i.e. datamodel shutting down after mdlcache)
  783. if ( !m_bInitialized )
  784. return 0;
  785. // NOTE: It can be null during shutdown because multiple studiomdls
  786. // could be referencing the same virtual model
  787. if ( !m_MDLDict[handle] )
  788. return 0;
  789. Assert( m_MDLDict[handle]->m_nRefCount > 0 );
  790. int nRefCount = --m_MDLDict[handle]->m_nRefCount;
  791. if ( nRefCount <= 0 )
  792. {
  793. ShutdownStudioData( handle );
  794. m_MDLDict.RemoveAt( handle );
  795. }
  796. return nRefCount;
  797. }
  798. int CMDLCache::GetRef( MDLHandle_t handle )
  799. {
  800. if ( !m_bInitialized )
  801. return 0;
  802. if ( !m_MDLDict[handle] )
  803. return 0;
  804. return m_MDLDict[handle]->m_nRefCount;
  805. }
  806. //-----------------------------------------------------------------------------
  807. // Unserializes the PHY file associated w/ models (the vphysics representation)
  808. //-----------------------------------------------------------------------------
  809. void CMDLCache::UnserializeVCollide( MDLHandle_t handle, bool synchronousLoad )
  810. {
  811. VPROF( "CMDLCache::UnserializeVCollide" );
  812. // FIXME: Should the vcollde be played into cacheable memory?
  813. studiodata_t *pStudioData = m_MDLDict[handle];
  814. int iAsync = GetAsyncInfoIndex( handle, MDLCACHE_VCOLLIDE );
  815. if ( iAsync == NO_ASYNC )
  816. {
  817. // clear existing data
  818. pStudioData->m_nFlags &= ~STUDIODATA_FLAGS_VCOLLISION_LOADED;
  819. memset( &pStudioData->m_VCollisionData, 0, sizeof( pStudioData->m_VCollisionData ) );
  820. #if 0
  821. // FIXME: ywb
  822. // If we don't ask for the virtual model to load, then we can get a hitch later on after startup
  823. // Should we async load the sub .mdls during startup assuming they'll all be resident by the time the level can actually
  824. // start drawing?
  825. if ( pStudioData->m_pVirtualModel || synchronousLoad )
  826. #endif
  827. {
  828. virtualmodel_t *pVirtualModel = GetVirtualModel( handle );
  829. if ( pVirtualModel )
  830. {
  831. for ( int i = 1; i < pVirtualModel->m_group.Count(); i++ )
  832. {
  833. MDLHandle_t sharedHandle = (MDLHandle_t) (int)pVirtualModel->m_group[i].cache & 0xffff;
  834. studiodata_t *pData = m_MDLDict[sharedHandle];
  835. if ( !(pData->m_nFlags & STUDIODATA_FLAGS_VCOLLISION_LOADED) )
  836. {
  837. UnserializeVCollide( sharedHandle, synchronousLoad );
  838. }
  839. if ( pData->m_VCollisionData.solidCount > 0 )
  840. {
  841. pStudioData->m_VCollisionData = pData->m_VCollisionData;
  842. pStudioData->m_nFlags |= STUDIODATA_FLAGS_VCOLLISION_SHARED;
  843. return;
  844. }
  845. }
  846. }
  847. }
  848. char pFileName[MAX_PATH];
  849. MakeFilename( handle, ".phy", pFileName, sizeof(pFileName) );
  850. if ( IsX360() )
  851. {
  852. char pX360Filename[MAX_PATH];
  853. UpdateOrCreate( NULL, pFileName, pX360Filename, sizeof( pX360Filename ), "GAME" );
  854. Q_strncpy( pFileName, pX360Filename, sizeof(pX360Filename) );
  855. }
  856. bool bAsyncLoad = mod_load_vcollide_async.GetBool() && !synchronousLoad;
  857. MdlCacheMsg( "MDLCache: %s load vcollide %s\n", bAsyncLoad ? "Async" : "Sync", GetModelName( handle ) );
  858. AsyncInfo_t info;
  859. if ( IsDebug() )
  860. {
  861. memset( &info, 0xdd, sizeof( AsyncInfo_t ) );
  862. }
  863. info.hModel = handle;
  864. info.type = MDLCACHE_VCOLLIDE;
  865. info.iAnimBlock = 0;
  866. info.hControl = NULL;
  867. LoadData( pFileName, "GAME", bAsyncLoad, &info.hControl );
  868. {
  869. AUTO_LOCK( m_AsyncMutex );
  870. iAsync = SetAsyncInfoIndex( handle, MDLCACHE_VCOLLIDE, m_PendingAsyncs.AddToTail( info ) );
  871. }
  872. }
  873. else if ( synchronousLoad )
  874. {
  875. AsyncInfo_t *pInfo;
  876. {
  877. AUTO_LOCK( m_AsyncMutex );
  878. pInfo = &m_PendingAsyncs[iAsync];
  879. }
  880. if ( pInfo->hControl )
  881. {
  882. g_pFullFileSystem->AsyncFinish( pInfo->hControl, true );
  883. }
  884. }
  885. ProcessPendingAsync( iAsync );
  886. }
  887. //-----------------------------------------------------------------------------
  888. // Free model's collision data
  889. //-----------------------------------------------------------------------------
  890. void CMDLCache::DestroyVCollide( MDLHandle_t handle )
  891. {
  892. studiodata_t *pStudioData = m_MDLDict[handle];
  893. if ( pStudioData->m_nFlags & STUDIODATA_FLAGS_VCOLLISION_SHARED )
  894. return;
  895. if ( pStudioData->m_nFlags & STUDIODATA_FLAGS_VCOLLISION_LOADED )
  896. {
  897. pStudioData->m_nFlags &= ~STUDIODATA_FLAGS_VCOLLISION_LOADED;
  898. if ( pStudioData->m_VCollisionData.solidCount )
  899. {
  900. if ( m_pCacheNotify )
  901. {
  902. m_pCacheNotify->OnDataUnloaded( MDLCACHE_VCOLLIDE, handle );
  903. }
  904. MdlCacheMsg("MDLCache: Unload vcollide %s\n", GetModelName( handle ) );
  905. g_pPhysicsCollision->VCollideUnload( &pStudioData->m_VCollisionData );
  906. }
  907. }
  908. }
  909. //-----------------------------------------------------------------------------
  910. // Unserializes the PHY file associated w/ models (the vphysics representation)
  911. //-----------------------------------------------------------------------------
  912. vcollide_t *CMDLCache::GetVCollideEx( MDLHandle_t handle, bool synchronousLoad /*= true*/ )
  913. {
  914. if ( mod_test_not_available.GetBool() )
  915. return NULL;
  916. if ( handle == MDLHANDLE_INVALID )
  917. return NULL;
  918. studiodata_t *pStudioData = m_MDLDict[handle];
  919. if ( ( pStudioData->m_nFlags & STUDIODATA_FLAGS_VCOLLISION_LOADED ) == 0 )
  920. {
  921. UnserializeVCollide( handle, synchronousLoad );
  922. }
  923. // We've loaded an empty collision file or no file was found, so return NULL
  924. if ( !pStudioData->m_VCollisionData.solidCount )
  925. return NULL;
  926. return &pStudioData->m_VCollisionData;
  927. }
  928. bool CMDLCache::GetVCollideSize( MDLHandle_t handle, int *pVCollideSize )
  929. {
  930. *pVCollideSize = 0;
  931. studiodata_t *pStudioData = m_MDLDict[handle];
  932. if ( ( pStudioData->m_nFlags & STUDIODATA_FLAGS_VCOLLISION_LOADED ) == 0 )
  933. return false;
  934. vcollide_t *pCollide = &pStudioData->m_VCollisionData;
  935. for ( int j = 0; j < pCollide->solidCount; j++ )
  936. {
  937. *pVCollideSize += g_pPhysicsCollision->CollideSize( pCollide->solids[j] );
  938. }
  939. *pVCollideSize += pCollide->descSize;
  940. return true;
  941. }
  942. //-----------------------------------------------------------------------------
  943. // Allocates/frees the anim blocks
  944. //-----------------------------------------------------------------------------
  945. void CMDLCache::AllocateAnimBlocks( studiodata_t *pStudioData, int nCount )
  946. {
  947. Assert( pStudioData->m_pAnimBlock == NULL );
  948. pStudioData->m_nAnimBlockCount = nCount;
  949. pStudioData->m_pAnimBlock = new DataCacheHandle_t[pStudioData->m_nAnimBlockCount];
  950. memset( pStudioData->m_pAnimBlock, 0, sizeof(DataCacheHandle_t) * pStudioData->m_nAnimBlockCount );
  951. pStudioData->m_iFakeAnimBlockStall = new unsigned long [pStudioData->m_nAnimBlockCount];
  952. memset( pStudioData->m_iFakeAnimBlockStall, 0, sizeof( unsigned long ) * pStudioData->m_nAnimBlockCount );
  953. }
  954. void CMDLCache::FreeAnimBlocks( MDLHandle_t handle )
  955. {
  956. studiodata_t *pStudioData = m_MDLDict[handle];
  957. if ( pStudioData->m_pAnimBlock )
  958. {
  959. for (int i = 0; i < pStudioData->m_nAnimBlockCount; ++i )
  960. {
  961. MdlCacheMsg( "MDLCache: Free Anim block: %d\n", i );
  962. ClearAsync( handle, MDLCACHE_ANIMBLOCK, i, true );
  963. if ( pStudioData->m_pAnimBlock[i] )
  964. {
  965. UncacheData( pStudioData->m_pAnimBlock[i], MDLCACHE_ANIMBLOCK, true );
  966. }
  967. }
  968. delete[] pStudioData->m_pAnimBlock;
  969. pStudioData->m_pAnimBlock = NULL;
  970. delete[] pStudioData->m_iFakeAnimBlockStall;
  971. pStudioData->m_iFakeAnimBlockStall = NULL;
  972. }
  973. pStudioData->m_nAnimBlockCount = 0;
  974. }
  975. //-----------------------------------------------------------------------------
  976. // Unserializes an animation block from disk
  977. //-----------------------------------------------------------------------------
  978. unsigned char *CMDLCache::UnserializeAnimBlock( MDLHandle_t handle, int nBlock )
  979. {
  980. VPROF( "CMDLCache::UnserializeAnimBlock" );
  981. if ( IsX360() && g_pQueuedLoader->IsMapLoading() )
  982. {
  983. // anim block i/o is not allowed at this stage
  984. return NULL;
  985. }
  986. // Block 0 is never used!!!
  987. Assert( nBlock > 0 );
  988. studiodata_t *pStudioData = m_MDLDict[handle];
  989. int iAsync = GetAsyncInfoIndex( handle, MDLCACHE_ANIMBLOCK, nBlock );
  990. if ( iAsync == NO_ASYNC )
  991. {
  992. studiohdr_t *pStudioHdr = GetStudioHdr( handle );
  993. // FIXME: For consistency, the block name maybe shouldn't have 'model' in it.
  994. char const *pModelName = pStudioHdr->pszAnimBlockName();
  995. mstudioanimblock_t *pBlock = pStudioHdr->pAnimBlock( nBlock );
  996. int nSize = pBlock->dataend - pBlock->datastart;
  997. if ( nSize == 0 )
  998. return NULL;
  999. // allocate space in the cache
  1000. pStudioData->m_pAnimBlock[nBlock] = NULL;
  1001. char pFileName[MAX_PATH];
  1002. Q_strncpy( pFileName, pModelName, sizeof(pFileName) );
  1003. Q_FixSlashes( pFileName );
  1004. #ifdef _LINUX
  1005. Q_strlower( pFileName );
  1006. #endif
  1007. if ( IsX360() )
  1008. {
  1009. char pX360Filename[MAX_PATH];
  1010. UpdateOrCreate( pStudioHdr, pFileName, pX360Filename, sizeof( pX360Filename ), "GAME" );
  1011. Q_strncpy( pFileName, pX360Filename, sizeof(pX360Filename) );
  1012. }
  1013. MdlCacheMsg( "MDLCache: Begin load Anim Block %s (block %i)\n", GetModelName( handle ), nBlock );
  1014. AsyncInfo_t info;
  1015. if ( IsDebug() )
  1016. {
  1017. memset( &info, 0xdd, sizeof( AsyncInfo_t ) );
  1018. }
  1019. info.hModel = handle;
  1020. info.type = MDLCACHE_ANIMBLOCK;
  1021. info.iAnimBlock = nBlock;
  1022. info.hControl = NULL;
  1023. LoadData( pFileName, "GAME", NULL, nSize, pBlock->datastart, mod_load_anims_async.GetBool(), &info.hControl );
  1024. {
  1025. AUTO_LOCK( m_AsyncMutex );
  1026. iAsync = SetAsyncInfoIndex( handle, MDLCACHE_ANIMBLOCK, nBlock, m_PendingAsyncs.AddToTail( info ) );
  1027. }
  1028. }
  1029. ProcessPendingAsync( iAsync );
  1030. return ( unsigned char * )CheckData( pStudioData->m_pAnimBlock[nBlock], MDLCACHE_ANIMBLOCK );
  1031. }
  1032. //-----------------------------------------------------------------------------
  1033. // Gets at an animation block associated with an MDL
  1034. //-----------------------------------------------------------------------------
  1035. unsigned char *CMDLCache::GetAnimBlock( MDLHandle_t handle, int nBlock )
  1036. {
  1037. if ( mod_test_not_available.GetBool() )
  1038. return NULL;
  1039. if ( handle == MDLHANDLE_INVALID )
  1040. return NULL;
  1041. // Allocate animation blocks if we don't have them yet
  1042. studiodata_t *pStudioData = m_MDLDict[handle];
  1043. if ( pStudioData->m_pAnimBlock == NULL )
  1044. {
  1045. studiohdr_t *pStudioHdr = GetStudioHdr( handle );
  1046. AllocateAnimBlocks( pStudioData, pStudioHdr->numanimblocks );
  1047. }
  1048. // check for request being in range
  1049. if ( nBlock < 0 || nBlock >= pStudioData->m_nAnimBlockCount)
  1050. return NULL;
  1051. // Check the cache to see if the animation is in memory
  1052. unsigned char *pData = ( unsigned char * )CheckData( pStudioData->m_pAnimBlock[nBlock], MDLCACHE_ANIMBLOCK );
  1053. if ( !pData )
  1054. {
  1055. pStudioData->m_pAnimBlock[nBlock] = NULL;
  1056. // It's not in memory, read it off of disk
  1057. pData = UnserializeAnimBlock( handle, nBlock );
  1058. }
  1059. if (mod_load_fakestall.GetInt())
  1060. {
  1061. unsigned int t = Plat_MSTime();
  1062. if (pStudioData->m_iFakeAnimBlockStall[nBlock] == 0 || pStudioData->m_iFakeAnimBlockStall[nBlock] > t)
  1063. {
  1064. pStudioData->m_iFakeAnimBlockStall[nBlock] = t;
  1065. }
  1066. if ((int)(t - pStudioData->m_iFakeAnimBlockStall[nBlock]) < mod_load_fakestall.GetInt())
  1067. {
  1068. return NULL;
  1069. }
  1070. }
  1071. return pData;
  1072. }
  1073. //-----------------------------------------------------------------------------
  1074. // Allocates/frees autoplay sequence list
  1075. //-----------------------------------------------------------------------------
  1076. void CMDLCache::AllocateAutoplaySequences( studiodata_t *pStudioData, int nCount )
  1077. {
  1078. FreeAutoplaySequences( pStudioData );
  1079. pStudioData->m_nAutoplaySequenceCount = nCount;
  1080. pStudioData->m_pAutoplaySequenceList = new unsigned short[nCount];
  1081. }
  1082. void CMDLCache::FreeAutoplaySequences( studiodata_t *pStudioData )
  1083. {
  1084. if ( pStudioData->m_pAutoplaySequenceList )
  1085. {
  1086. delete[] pStudioData->m_pAutoplaySequenceList;
  1087. pStudioData->m_pAutoplaySequenceList = NULL;
  1088. }
  1089. pStudioData->m_nAutoplaySequenceCount = 0;
  1090. }
  1091. //-----------------------------------------------------------------------------
  1092. // Gets the autoplay list
  1093. //-----------------------------------------------------------------------------
  1094. int CMDLCache::GetAutoplayList( MDLHandle_t handle, unsigned short **pAutoplayList )
  1095. {
  1096. if ( pAutoplayList )
  1097. {
  1098. *pAutoplayList = NULL;
  1099. }
  1100. if ( handle == MDLHANDLE_INVALID )
  1101. return 0;
  1102. virtualmodel_t *pVirtualModel = GetVirtualModel( handle );
  1103. if ( pVirtualModel )
  1104. {
  1105. if ( pAutoplayList && pVirtualModel->m_autoplaySequences.Count() )
  1106. {
  1107. *pAutoplayList = pVirtualModel->m_autoplaySequences.Base();
  1108. }
  1109. return pVirtualModel->m_autoplaySequences.Count();
  1110. }
  1111. // FIXME: Should we cache autoplay info here on demand instead of in unserializeMDL?
  1112. studiodata_t *pStudioData = m_MDLDict[handle];
  1113. if ( pAutoplayList )
  1114. {
  1115. *pAutoplayList = pStudioData->m_pAutoplaySequenceList;
  1116. }
  1117. return pStudioData->m_nAutoplaySequenceCount;
  1118. }
  1119. //-----------------------------------------------------------------------------
  1120. // Allocates/frees the virtual model
  1121. //-----------------------------------------------------------------------------
  1122. void CMDLCache::AllocateVirtualModel( MDLHandle_t handle )
  1123. {
  1124. studiodata_t *pStudioData = m_MDLDict[handle];
  1125. Assert( pStudioData->m_pVirtualModel == NULL );
  1126. pStudioData->m_pVirtualModel = new virtualmodel_t;
  1127. // FIXME: The old code slammed these; could have leaked memory?
  1128. Assert( pStudioData->m_nAnimBlockCount == 0 );
  1129. Assert( pStudioData->m_pAnimBlock == NULL );
  1130. }
  1131. void CMDLCache::FreeVirtualModel( MDLHandle_t handle )
  1132. {
  1133. studiodata_t *pStudioData = m_MDLDict[handle];
  1134. if ( pStudioData && pStudioData->m_pVirtualModel )
  1135. {
  1136. int nGroupCount = pStudioData->m_pVirtualModel->m_group.Count();
  1137. Assert( (nGroupCount >= 1) && pStudioData->m_pVirtualModel->m_group[0].cache == (void*)(uintp)handle );
  1138. // NOTE: Start at *1* here because the 0th element contains a reference to *this* handle
  1139. for ( int i = 1; i < nGroupCount; ++i )
  1140. {
  1141. MDLHandle_t h = (MDLHandle_t)(int)pStudioData->m_pVirtualModel->m_group[i].cache&0xffff;
  1142. FreeVirtualModel( h );
  1143. Release( h );
  1144. }
  1145. delete pStudioData->m_pVirtualModel;
  1146. pStudioData->m_pVirtualModel = NULL;
  1147. }
  1148. }
  1149. //-----------------------------------------------------------------------------
  1150. // Returns the virtual model
  1151. //-----------------------------------------------------------------------------
  1152. virtualmodel_t *CMDLCache::GetVirtualModel( MDLHandle_t handle )
  1153. {
  1154. if ( mod_test_not_available.GetBool() )
  1155. return NULL;
  1156. if ( handle == MDLHANDLE_INVALID )
  1157. return NULL;
  1158. studiohdr_t *pStudioHdr = GetStudioHdr( handle );
  1159. if ( pStudioHdr == NULL )
  1160. return NULL;
  1161. return GetVirtualModelFast( pStudioHdr, handle );
  1162. }
  1163. virtualmodel_t *CMDLCache::GetVirtualModelFast( const studiohdr_t *pStudioHdr, MDLHandle_t handle )
  1164. {
  1165. if (pStudioHdr->numincludemodels == 0)
  1166. return NULL;
  1167. studiodata_t *pStudioData = m_MDLDict[handle];
  1168. if ( !pStudioData )
  1169. return NULL;
  1170. if ( !pStudioData->m_pVirtualModel )
  1171. {
  1172. DevMsg( 2, "Loading virtual model for %s\n", pStudioHdr->pszName() );
  1173. CMDLCacheCriticalSection criticalSection( this );
  1174. AllocateVirtualModel( handle );
  1175. // Group has to be zero to ensure refcounting is correct
  1176. int nGroup = pStudioData->m_pVirtualModel->m_group.AddToTail( );
  1177. Assert( nGroup == 0 );
  1178. pStudioData->m_pVirtualModel->m_group[nGroup].cache = (void *)(uintp)handle;
  1179. // Add all dependent data
  1180. pStudioData->m_pVirtualModel->AppendModels( 0, pStudioHdr );
  1181. }
  1182. return pStudioData->m_pVirtualModel;
  1183. }
  1184. //-----------------------------------------------------------------------------
  1185. // Purpose: Pulls all submodels/.ani file models into the cache
  1186. // to avoid runtime hitches and load animations at load time, set mod_forcedata to be 1
  1187. //-----------------------------------------------------------------------------
  1188. void CMDLCache::UnserializeAllVirtualModelsAndAnimBlocks( MDLHandle_t handle )
  1189. {
  1190. if ( handle == MDLHANDLE_INVALID )
  1191. return;
  1192. // might be re-loading, discard old virtualmodel to force rebuild
  1193. // unfortunately, the virtualmodel does build data into the cacheable studiohdr
  1194. FreeVirtualModel( handle );
  1195. if ( IsX360() && g_pQueuedLoader->IsMapLoading() )
  1196. {
  1197. // queued loading has to do it
  1198. return;
  1199. }
  1200. // don't load the submodel data
  1201. if ( !mod_forcedata.GetBool() )
  1202. return;
  1203. // if not present, will instance and load the submodels
  1204. GetVirtualModel( handle );
  1205. if ( IsX360() )
  1206. {
  1207. // 360 does not drive the anims into its small cache section
  1208. return;
  1209. }
  1210. // Note that the animblocks start at 1!!!
  1211. studiohdr_t *pStudioHdr = GetStudioHdr( handle );
  1212. for ( int i = 1 ; i < (int)pStudioHdr->numanimblocks; ++i )
  1213. {
  1214. GetAnimBlock( handle, i );
  1215. }
  1216. ProcessPendingAsyncs( MDLCACHE_ANIMBLOCK );
  1217. }
  1218. //-----------------------------------------------------------------------------
  1219. // Loads the static meshes
  1220. //-----------------------------------------------------------------------------
  1221. bool CMDLCache::LoadHardwareData( MDLHandle_t handle )
  1222. {
  1223. Assert( handle != MDLHANDLE_INVALID );
  1224. // Don't try to load VTX files if we don't have focus...
  1225. if ( m_bLostVideoMemory )
  1226. return false;
  1227. studiodata_t *pStudioData = m_MDLDict[handle];
  1228. CMDLCacheCriticalSection criticalSection( this );
  1229. // Load up the model
  1230. studiohdr_t *pStudioHdr = GetStudioHdr( handle );
  1231. if ( !pStudioHdr || !pStudioHdr->numbodyparts )
  1232. {
  1233. pStudioData->m_nFlags |= STUDIODATA_FLAGS_NO_STUDIOMESH;
  1234. return true;
  1235. }
  1236. if ( pStudioData->m_nFlags & STUDIODATA_FLAGS_NO_STUDIOMESH )
  1237. {
  1238. return false;
  1239. }
  1240. if ( LogMdlCache() &&
  1241. GetAsyncInfoIndex( handle, MDLCACHE_STUDIOHWDATA ) == NO_ASYNC &&
  1242. GetAsyncInfoIndex( handle, MDLCACHE_VERTEXES ) == NO_ASYNC )
  1243. {
  1244. MdlCacheMsg( "MDLCache: Begin load studiomdl %s\n", GetModelName( handle ) );
  1245. }
  1246. // Vertex data is required to call LoadModel(), so make sure that's ready
  1247. if ( !GetVertexData( handle ) )
  1248. {
  1249. if ( pStudioData->m_nFlags & STUDIODATA_FLAGS_NO_VERTEX_DATA )
  1250. {
  1251. pStudioData->m_nFlags |= STUDIODATA_FLAGS_NO_STUDIOMESH;
  1252. }
  1253. return false;
  1254. }
  1255. int iAsync = GetAsyncInfoIndex( handle, MDLCACHE_STUDIOHWDATA );
  1256. if ( iAsync == NO_ASYNC )
  1257. {
  1258. m_pMeshCacheSection->Lock( pStudioData->m_VertexCache );
  1259. // load and persist the vtx file
  1260. // use model name for correct path
  1261. char pFileName[MAX_PATH];
  1262. MakeFilename( handle, GetVTXExtension(), pFileName, sizeof(pFileName) );
  1263. if ( IsX360() )
  1264. {
  1265. char pX360Filename[MAX_PATH];
  1266. UpdateOrCreate( pStudioHdr, pFileName, pX360Filename, sizeof( pX360Filename ), "GAME" );
  1267. Q_strncpy( pFileName, pX360Filename, sizeof(pX360Filename) );
  1268. }
  1269. MdlCacheMsg("MDLCache: Begin load VTX %s\n", GetModelName( handle ) );
  1270. AsyncInfo_t info;
  1271. if ( IsDebug() )
  1272. {
  1273. memset( &info, 0xdd, sizeof( AsyncInfo_t ) );
  1274. }
  1275. info.hModel = handle;
  1276. info.type = MDLCACHE_STUDIOHWDATA;
  1277. info.iAnimBlock = 0;
  1278. info.hControl = NULL;
  1279. LoadData( pFileName, "GAME", mod_load_mesh_async.GetBool(), &info.hControl );
  1280. {
  1281. AUTO_LOCK( m_AsyncMutex );
  1282. iAsync = SetAsyncInfoIndex( handle, MDLCACHE_STUDIOHWDATA, m_PendingAsyncs.AddToTail( info ) );
  1283. }
  1284. }
  1285. if ( ProcessPendingAsync( iAsync ) > 0 )
  1286. {
  1287. if ( pStudioData->m_nFlags & STUDIODATA_FLAGS_NO_STUDIOMESH )
  1288. {
  1289. return false;
  1290. }
  1291. return ( pStudioData->m_HardwareData.m_NumStudioMeshes != 0 );
  1292. }
  1293. return false;
  1294. }
  1295. void CMDLCache::ConvertFlexData( studiohdr_t *pStudioHdr )
  1296. {
  1297. float flVertAnimFixedPointScale = pStudioHdr->VertAnimFixedPointScale();
  1298. for ( int i = 0; i < pStudioHdr->numbodyparts; i++ )
  1299. {
  1300. mstudiobodyparts_t *pBody = pStudioHdr->pBodypart( i );
  1301. for ( int j = 0; j < pBody->nummodels; j++ )
  1302. {
  1303. mstudiomodel_t *pModel = pBody->pModel( j );
  1304. for ( int k = 0; k < pModel->nummeshes; k++ )
  1305. {
  1306. mstudiomesh_t *pMesh = pModel->pMesh( k );
  1307. for ( int l = 0; l < pMesh->numflexes; l++ )
  1308. {
  1309. mstudioflex_t *pFlex = pMesh->pFlex( l );
  1310. bool bIsWrinkleAnim = ( pFlex->vertanimtype == STUDIO_VERT_ANIM_WRINKLE );
  1311. for ( int m = 0; m < pFlex->numverts; m++ )
  1312. {
  1313. mstudiovertanim_t *pVAnim = bIsWrinkleAnim ?
  1314. pFlex->pVertanimWrinkle( m ) : pFlex->pVertanim( m );
  1315. pVAnim->ConvertToFixed( flVertAnimFixedPointScale );
  1316. }
  1317. }
  1318. }
  1319. }
  1320. }
  1321. }
  1322. //-----------------------------------------------------------------------------
  1323. //
  1324. //-----------------------------------------------------------------------------
  1325. bool CMDLCache::BuildHardwareData( MDLHandle_t handle, studiodata_t *pStudioData, studiohdr_t *pStudioHdr, OptimizedModel::FileHeader_t *pVtxHdr )
  1326. {
  1327. if ( pVtxHdr )
  1328. {
  1329. MdlCacheMsg("MDLCache: Alloc VTX %s\n", pStudioHdr->pszName() );
  1330. // check header
  1331. if ( pVtxHdr->version != OPTIMIZED_MODEL_FILE_VERSION )
  1332. {
  1333. Warning( "Error Index File for '%s' version %d should be %d\n", pStudioHdr->pszName(), pVtxHdr->version, OPTIMIZED_MODEL_FILE_VERSION );
  1334. pVtxHdr = NULL;
  1335. }
  1336. else if ( pVtxHdr->checkSum != pStudioHdr->checksum )
  1337. {
  1338. Warning( "Error Index File for '%s' checksum %d should be %d\n", pStudioHdr->pszName(), pVtxHdr->checkSum, pStudioHdr->checksum );
  1339. pVtxHdr = NULL;
  1340. }
  1341. }
  1342. if ( !pVtxHdr )
  1343. {
  1344. pStudioData->m_nFlags |= STUDIODATA_FLAGS_NO_STUDIOMESH;
  1345. return false;
  1346. }
  1347. CTempAllocHelper pOriginalData;
  1348. if ( IsX360() )
  1349. {
  1350. unsigned char *pInputData = (unsigned char *)pVtxHdr + sizeof( OptimizedModel::FileHeader_t );
  1351. if ( CLZMA::IsCompressed( pInputData ) )
  1352. {
  1353. // vtx arrives compressed, decode and cache the results
  1354. unsigned int nOriginalSize = CLZMA::GetActualSize( pInputData );
  1355. pOriginalData.Alloc( sizeof( OptimizedModel::FileHeader_t ) + nOriginalSize );
  1356. V_memcpy( pOriginalData.Get(), pVtxHdr, sizeof( OptimizedModel::FileHeader_t ) );
  1357. unsigned int nOutputSize = CLZMA::Uncompress( pInputData, sizeof( OptimizedModel::FileHeader_t ) + (unsigned char *)pOriginalData.Get() );
  1358. if ( nOutputSize != nOriginalSize )
  1359. {
  1360. // decoder failure
  1361. return false;
  1362. }
  1363. pVtxHdr = (OptimizedModel::FileHeader_t *)pOriginalData.Get();
  1364. }
  1365. }
  1366. MdlCacheMsg( "MDLCache: Load studiomdl %s\n", pStudioHdr->pszName() );
  1367. Assert( GetVertexData( handle ) );
  1368. BeginLock();
  1369. bool bLoaded = g_pStudioRender->LoadModel( pStudioHdr, pVtxHdr, &pStudioData->m_HardwareData );
  1370. EndLock();
  1371. if ( bLoaded )
  1372. {
  1373. pStudioData->m_nFlags |= STUDIODATA_FLAGS_STUDIOMESH_LOADED;
  1374. }
  1375. else
  1376. {
  1377. pStudioData->m_nFlags |= STUDIODATA_FLAGS_NO_STUDIOMESH;
  1378. }
  1379. if ( m_pCacheNotify )
  1380. {
  1381. m_pCacheNotify->OnDataLoaded( MDLCACHE_STUDIOHWDATA, handle );
  1382. }
  1383. #if defined( USE_HARDWARE_CACHE )
  1384. GetCacheSection( MDLCACHE_STUDIOHWDATA )->Add( MakeCacheID( handle, MDLCACHE_STUDIOHWDATA ), &pStudioData->m_HardwareData, ComputeHardwareDataSize( &pStudioData->m_HardwareData ), &pStudioData->m_HardwareDataCache );
  1385. #endif
  1386. return true;
  1387. }
  1388. //-----------------------------------------------------------------------------
  1389. // Loads the static meshes
  1390. //-----------------------------------------------------------------------------
  1391. void CMDLCache::UnloadHardwareData( MDLHandle_t handle, bool bCacheRemove, bool bLockedOk )
  1392. {
  1393. if ( handle == MDLHANDLE_INVALID )
  1394. return;
  1395. // Don't load it if it's loaded
  1396. studiodata_t *pStudioData = m_MDLDict[handle];
  1397. if ( pStudioData->m_nFlags & STUDIODATA_FLAGS_STUDIOMESH_LOADED )
  1398. {
  1399. #if defined( USE_HARDWARE_CACHE )
  1400. if ( bCacheRemove )
  1401. {
  1402. if ( GetCacheSection( MDLCACHE_STUDIOHWDATA )->BreakLock( pStudioData->m_HardwareDataCache ) && !bLockedOk )
  1403. {
  1404. DevMsg( "Warning: freed a locked resource\n" );
  1405. Assert( 0 );
  1406. }
  1407. GetCacheSection( MDLCACHE_STUDIOHWDATA )->Remove( pStudioData->m_HardwareDataCache );
  1408. }
  1409. #endif
  1410. if ( m_pCacheNotify )
  1411. {
  1412. m_pCacheNotify->OnDataUnloaded( MDLCACHE_STUDIOHWDATA, handle );
  1413. }
  1414. MdlCacheMsg("MDLCache: Unload studiomdl %s\n", GetModelName( handle ) );
  1415. g_pStudioRender->UnloadModel( &pStudioData->m_HardwareData );
  1416. memset( &pStudioData->m_HardwareData, 0, sizeof( pStudioData->m_HardwareData ) );
  1417. pStudioData->m_nFlags &= ~STUDIODATA_FLAGS_STUDIOMESH_LOADED;
  1418. NotifyFileUnloaded( handle, ".mdl" );
  1419. }
  1420. }
  1421. //-----------------------------------------------------------------------------
  1422. // Returns the hardware data associated with an MDL
  1423. //-----------------------------------------------------------------------------
  1424. studiohwdata_t *CMDLCache::GetHardwareData( MDLHandle_t handle )
  1425. {
  1426. if ( mod_test_not_available.GetBool() )
  1427. return NULL;
  1428. if ( mod_test_mesh_not_available.GetBool() )
  1429. return NULL;
  1430. studiodata_t *pStudioData = m_MDLDict[handle];
  1431. m_pMeshCacheSection->LockMutex();
  1432. if ( ( pStudioData->m_nFlags & (STUDIODATA_FLAGS_STUDIOMESH_LOADED | STUDIODATA_FLAGS_NO_STUDIOMESH) ) == 0 )
  1433. {
  1434. m_pMeshCacheSection->UnlockMutex();
  1435. if ( !LoadHardwareData( handle ) )
  1436. {
  1437. return NULL;
  1438. }
  1439. }
  1440. else
  1441. {
  1442. #if defined( USE_HARDWARE_CACHE )
  1443. CheckData( pStudioData->m_HardwareDataCache, MDLCACHE_STUDIOHWDATA );
  1444. #endif
  1445. m_pMeshCacheSection->UnlockMutex();
  1446. }
  1447. // didn't load, don't return an empty pointer
  1448. if ( pStudioData->m_nFlags & STUDIODATA_FLAGS_NO_STUDIOMESH )
  1449. return NULL;
  1450. return &pStudioData->m_HardwareData;
  1451. }
  1452. //-----------------------------------------------------------------------------
  1453. // Task switch
  1454. //-----------------------------------------------------------------------------
  1455. void CMDLCache::ReleaseMaterialSystemObjects()
  1456. {
  1457. Assert( !m_bLostVideoMemory );
  1458. m_bLostVideoMemory = true;
  1459. BreakFrameLock( false );
  1460. // Free all hardware data
  1461. MDLHandle_t i = m_MDLDict.First();
  1462. while ( i != m_MDLDict.InvalidIndex() )
  1463. {
  1464. UnloadHardwareData( i );
  1465. i = m_MDLDict.Next( i );
  1466. }
  1467. RestoreFrameLock();
  1468. }
  1469. void CMDLCache::RestoreMaterialSystemObjects( int nChangeFlags )
  1470. {
  1471. Assert( m_bLostVideoMemory );
  1472. m_bLostVideoMemory = false;
  1473. BreakFrameLock( false );
  1474. // Restore all hardware data
  1475. MDLHandle_t i = m_MDLDict.First();
  1476. while ( i != m_MDLDict.InvalidIndex() )
  1477. {
  1478. studiodata_t *pStudioData = m_MDLDict[i];
  1479. bool bIsMDLInMemory = GetCacheSection( MDLCACHE_STUDIOHDR )->IsPresent( pStudioData->m_MDLCache );
  1480. // If the vertex format changed, we have to free the data because we may be using different .vtx files.
  1481. if ( nChangeFlags & MATERIAL_RESTORE_VERTEX_FORMAT_CHANGED )
  1482. {
  1483. MdlCacheMsg( "MDLCache: Free studiohdr\n" );
  1484. MdlCacheMsg( "MDLCache: Free VVD\n" );
  1485. MdlCacheMsg( "MDLCache: Free VTX\n" );
  1486. // FIXME: Do we have to free m_MDLCache + m_VertexCache?
  1487. // Certainly we have to free m_IndexCache, cause that's a dx-level specific vtx file.
  1488. ClearAsync( i, MDLCACHE_STUDIOHWDATA, 0, true );
  1489. Flush( i, MDLCACHE_FLUSH_VERTEXES );
  1490. }
  1491. // Only restore the hardware data of those studiohdrs which are currently in memory
  1492. if ( bIsMDLInMemory )
  1493. {
  1494. GetHardwareData( i );
  1495. }
  1496. i = m_MDLDict.Next( i );
  1497. }
  1498. RestoreFrameLock();
  1499. }
  1500. void CMDLCache::MarkAsLoaded(MDLHandle_t handle)
  1501. {
  1502. if ( mod_lock_mdls_on_load.GetBool() )
  1503. {
  1504. g_MDLCache.GetStudioHdr(handle);
  1505. if ( !( m_MDLDict[handle]->m_nFlags & STUDIODATA_FLAGS_LOCKED_MDL ) )
  1506. {
  1507. m_MDLDict[handle]->m_nFlags |= STUDIODATA_FLAGS_LOCKED_MDL;
  1508. GetCacheSection( MDLCACHE_STUDIOHDR )->Lock( m_MDLDict[handle]->m_MDLCache );
  1509. }
  1510. }
  1511. }
  1512. //-----------------------------------------------------------------------------
  1513. // Callback for UpdateOrCreate utility function - swaps any studiomdl file type.
  1514. //-----------------------------------------------------------------------------
  1515. static bool MdlcacheCreateCallback( const char *pSourceName, const char *pTargetName, const char *pPathID, void *pHdr )
  1516. {
  1517. // Missing studio files are permissible and not spewed as errors
  1518. bool retval = false;
  1519. CUtlBuffer sourceBuf;
  1520. bool bOk = g_pFullFileSystem->ReadFile( pSourceName, NULL, sourceBuf );
  1521. if ( bOk )
  1522. {
  1523. CUtlBuffer targetBuf;
  1524. targetBuf.EnsureCapacity( sourceBuf.TellPut() + BYTESWAP_ALIGNMENT_PADDING );
  1525. int bytes = StudioByteSwap::ByteswapStudioFile( pTargetName, targetBuf.Base(), sourceBuf.Base(), sourceBuf.TellPut(), (studiohdr_t*)pHdr );
  1526. if ( bytes )
  1527. {
  1528. // If the file was an .mdl, attempt to swap the .ani as well
  1529. if ( Q_stristr( pSourceName, ".mdl" ) )
  1530. {
  1531. char szANISourceName[ MAX_PATH ];
  1532. Q_StripExtension( pSourceName, szANISourceName, sizeof( szANISourceName ) );
  1533. Q_strncat( szANISourceName, ".ani", sizeof( szANISourceName ), COPY_ALL_CHARACTERS );
  1534. UpdateOrCreate( szANISourceName, NULL, 0, pPathID, MdlcacheCreateCallback, true, targetBuf.Base() );
  1535. }
  1536. targetBuf.SeekPut( CUtlBuffer::SEEK_HEAD, bytes );
  1537. g_pFullFileSystem->WriteFile( pTargetName, pPathID, targetBuf );
  1538. retval = true;
  1539. }
  1540. else
  1541. {
  1542. Warning( "Failed to create %s\n", pTargetName );
  1543. }
  1544. }
  1545. return retval;
  1546. }
  1547. //-----------------------------------------------------------------------------
  1548. // Calls utility function to create .360 version of a file.
  1549. //-----------------------------------------------------------------------------
  1550. int CMDLCache::UpdateOrCreate( studiohdr_t *pHdr, const char *pSourceName, char *pTargetName, int targetLen, const char *pPathID, bool bForce )
  1551. {
  1552. return ::UpdateOrCreate( pSourceName, pTargetName, targetLen, pPathID, MdlcacheCreateCallback, bForce, pHdr );
  1553. }
  1554. //-----------------------------------------------------------------------------
  1555. // Purpose: Attempts to read a file native to the current platform
  1556. //-----------------------------------------------------------------------------
  1557. bool CMDLCache::ReadFileNative( char *pFileName, const char *pPath, CUtlBuffer &buf, int nMaxBytes )
  1558. {
  1559. bool bOk = false;
  1560. if ( IsX360() )
  1561. {
  1562. // Read the 360 version
  1563. char pX360Filename[ MAX_PATH ];
  1564. UpdateOrCreate( NULL, pFileName, pX360Filename, sizeof( pX360Filename ), pPath );
  1565. bOk = g_pFullFileSystem->ReadFile( pX360Filename, pPath, buf, nMaxBytes );
  1566. }
  1567. else
  1568. {
  1569. // Read the PC version
  1570. bOk = g_pFullFileSystem->ReadFile( pFileName, pPath, buf, nMaxBytes );
  1571. }
  1572. return bOk;
  1573. }
  1574. //-----------------------------------------------------------------------------
  1575. // Purpose:
  1576. //-----------------------------------------------------------------------------
  1577. studiohdr_t *CMDLCache::UnserializeMDL( MDLHandle_t handle, void *pData, int nDataSize, bool bDataValid )
  1578. {
  1579. if ( !bDataValid || nDataSize <= 0 || pData == NULL)
  1580. {
  1581. return NULL;
  1582. }
  1583. CTempAllocHelper pOriginalData;
  1584. if ( IsX360() )
  1585. {
  1586. if ( CLZMA::IsCompressed( (unsigned char *)pData ) )
  1587. {
  1588. // mdl arrives compressed, decode and cache the results
  1589. unsigned int nOriginalSize = CLZMA::GetActualSize( (unsigned char *)pData );
  1590. pOriginalData.Alloc( nOriginalSize );
  1591. unsigned int nOutputSize = CLZMA::Uncompress( (unsigned char *)pData, (unsigned char *)pOriginalData.Get() );
  1592. if ( nOutputSize != nOriginalSize )
  1593. {
  1594. // decoder failure
  1595. return NULL;
  1596. }
  1597. pData = pOriginalData.Get();
  1598. nDataSize = nOriginalSize;
  1599. }
  1600. }
  1601. studiohdr_t *pStudioHdrIn = (studiohdr_t *)pData;
  1602. if ( r_rootlod.GetInt() > 0 )
  1603. {
  1604. // raw data is already setup for lod 0, override otherwise
  1605. Studio_SetRootLOD( pStudioHdrIn, r_rootlod.GetInt() );
  1606. }
  1607. // critical! store a back link to our data
  1608. // this is fetched when re-establishing dependent cached data (vtx/vvd)
  1609. pStudioHdrIn->virtualModel = (void *)(uintp)handle;
  1610. MdlCacheMsg( "MDLCache: Alloc studiohdr %s\n", GetModelName( handle ) );
  1611. // allocate cache space
  1612. MemAlloc_PushAllocDbgInfo( "Models:StudioHdr", 0);
  1613. studiohdr_t *pHdr = (studiohdr_t *)AllocData( MDLCACHE_STUDIOHDR, pStudioHdrIn->length );
  1614. MemAlloc_PopAllocDbgInfo();
  1615. if ( !pHdr )
  1616. return NULL;
  1617. CacheData( &m_MDLDict[handle]->m_MDLCache, pHdr, pStudioHdrIn->length, GetModelName( handle ), MDLCACHE_STUDIOHDR, MakeCacheID( handle, MDLCACHE_STUDIOHDR) );
  1618. if ( mod_lock_mdls_on_load.GetBool() )
  1619. {
  1620. GetCacheSection( MDLCACHE_STUDIOHDR )->Lock( m_MDLDict[handle]->m_MDLCache );
  1621. m_MDLDict[handle]->m_nFlags |= STUDIODATA_FLAGS_LOCKED_MDL;
  1622. }
  1623. // FIXME: Is there any way we can compute the size to load *before* loading in
  1624. // and read directly into cache memory? It would be nice to reduce cache overhead here.
  1625. // move the complete, relocatable model to the cache
  1626. memcpy( pHdr, pStudioHdrIn, pStudioHdrIn->length );
  1627. // On first load, convert the flex deltas from fp16 to 16-bit fixed-point
  1628. if ( (pHdr->flags & STUDIOHDR_FLAGS_FLEXES_CONVERTED) == 0 )
  1629. {
  1630. ConvertFlexData( pHdr );
  1631. // Mark as converted so it only happens once
  1632. pHdr->flags |= STUDIOHDR_FLAGS_FLEXES_CONVERTED;
  1633. }
  1634. if ( m_pCacheNotify )
  1635. {
  1636. m_pCacheNotify->OnDataLoaded( MDLCACHE_STUDIOHDR, handle );
  1637. }
  1638. return pHdr;
  1639. }
  1640. //-----------------------------------------------------------------------------
  1641. // Attempts to load a MDL file, validates that it's ok.
  1642. //-----------------------------------------------------------------------------
  1643. bool CMDLCache::ReadMDLFile( MDLHandle_t handle, const char *pMDLFileName, CUtlBuffer &buf )
  1644. {
  1645. VPROF( "CMDLCache::ReadMDLFile" );
  1646. char pFileName[ MAX_PATH ];
  1647. Q_strncpy( pFileName, pMDLFileName, sizeof( pFileName ) );
  1648. Q_FixSlashes( pFileName );
  1649. #ifdef _LINUX
  1650. Q_strlower( pFileName );
  1651. #endif
  1652. MdlCacheMsg( "MDLCache: Load studiohdr %s\n", pFileName );
  1653. MEM_ALLOC_CREDIT();
  1654. bool bOk = ReadFileNative( pFileName, "GAME", buf );
  1655. if ( !bOk )
  1656. {
  1657. DevWarning( "Failed to load %s!\n", pMDLFileName );
  1658. return false;
  1659. }
  1660. if ( IsX360() )
  1661. {
  1662. if ( CLZMA::IsCompressed( (unsigned char *)buf.PeekGet() ) )
  1663. {
  1664. // mdl arrives compressed, decode and cache the results
  1665. unsigned int nOriginalSize = CLZMA::GetActualSize( (unsigned char *)buf.PeekGet() );
  1666. void *pOriginalData = malloc( nOriginalSize );
  1667. unsigned int nOutputSize = CLZMA::Uncompress( (unsigned char *)buf.PeekGet(), (unsigned char *)pOriginalData );
  1668. if ( nOutputSize != nOriginalSize )
  1669. {
  1670. // decoder failure
  1671. free( pOriginalData );
  1672. return false;
  1673. }
  1674. // replace caller's buffer
  1675. buf.Purge();
  1676. buf.Put( pOriginalData, nOriginalSize );
  1677. free( pOriginalData );
  1678. }
  1679. }
  1680. studiohdr_t *pStudioHdr = (studiohdr_t*)buf.PeekGet();
  1681. if ( !pStudioHdr )
  1682. {
  1683. DevWarning( "Failed to read model %s from buffer!\n", pMDLFileName );
  1684. return false;
  1685. }
  1686. if ( pStudioHdr->id != IDSTUDIOHEADER )
  1687. {
  1688. DevWarning( "Model %s not a .MDL format file!\n", pMDLFileName );
  1689. return false;
  1690. }
  1691. // critical! store a back link to our data
  1692. // this is fetched when re-establishing dependent cached data (vtx/vvd)
  1693. pStudioHdr->virtualModel = (void*)(uintp)handle;
  1694. // Make sure all dependent files are valid
  1695. if ( !VerifyHeaders( pStudioHdr ) )
  1696. {
  1697. DevWarning( "Model %s has mismatched .vvd + .vtx files!\n", pMDLFileName );
  1698. return false;
  1699. }
  1700. return true;
  1701. }
  1702. //-----------------------------------------------------------------------------
  1703. //
  1704. //-----------------------------------------------------------------------------
  1705. studiohdr_t *CMDLCache::LockStudioHdr( MDLHandle_t handle )
  1706. {
  1707. if ( handle == MDLHANDLE_INVALID )
  1708. {
  1709. return NULL;
  1710. }
  1711. CMDLCacheCriticalSection cacheCriticalSection( this );
  1712. studiohdr_t *pStdioHdr = GetStudioHdr( handle );
  1713. // @TODO (toml 9/12/2006) need this?: AddRef( handle );
  1714. if ( !pStdioHdr )
  1715. {
  1716. return NULL;
  1717. }
  1718. GetCacheSection( MDLCACHE_STUDIOHDR )->Lock( m_MDLDict[handle]->m_MDLCache );
  1719. return pStdioHdr;
  1720. }
  1721. void CMDLCache::UnlockStudioHdr( MDLHandle_t handle )
  1722. {
  1723. if ( handle == MDLHANDLE_INVALID )
  1724. {
  1725. return;
  1726. }
  1727. CMDLCacheCriticalSection cacheCriticalSection( this );
  1728. studiohdr_t *pStdioHdr = GetStudioHdr( handle );
  1729. if ( pStdioHdr )
  1730. {
  1731. GetCacheSection( MDLCACHE_STUDIOHDR )->Unlock( m_MDLDict[handle]->m_MDLCache );
  1732. }
  1733. // @TODO (toml 9/12/2006) need this?: Release( handle );
  1734. }
  1735. //-----------------------------------------------------------------------------
  1736. // Loading the data in
  1737. //-----------------------------------------------------------------------------
  1738. studiohdr_t *CMDLCache::GetStudioHdr( MDLHandle_t handle )
  1739. {
  1740. if ( handle == MDLHANDLE_INVALID )
  1741. return NULL;
  1742. // Returning a pointer to data inside the cache when it's unlocked is just a bad idea.
  1743. // It's technically legal, but the pointer can get invalidated if anything else looks at the cache.
  1744. // Don't do that.
  1745. // Assert( m_pModelCacheSection->IsFrameLocking() );
  1746. // Assert( m_pMeshCacheSection->IsFrameLocking() );
  1747. #if _DEBUG
  1748. VPROF_INCREMENT_COUNTER( "GetStudioHdr", 1 );
  1749. #endif
  1750. studiohdr_t *pHdr = (studiohdr_t*)CheckData( m_MDLDict[handle]->m_MDLCache, MDLCACHE_STUDIOHDR );
  1751. if ( !pHdr )
  1752. {
  1753. m_MDLDict[handle]->m_MDLCache = NULL;
  1754. CMDLCacheCriticalSection cacheCriticalSection( this );
  1755. // load the file
  1756. const char *pModelName = GetActualModelName( handle );
  1757. if ( developer.GetInt() > 1 )
  1758. {
  1759. DevMsg( "Loading %s\n", pModelName );
  1760. }
  1761. // Load file to temporary space
  1762. CUtlBuffer buf;
  1763. if ( !ReadMDLFile( handle, pModelName, buf ) )
  1764. {
  1765. bool bOk = false;
  1766. if ( ( m_MDLDict[handle]->m_nFlags & STUDIODATA_ERROR_MODEL ) == 0 )
  1767. {
  1768. buf.Clear(); // clear buffer for next file read
  1769. m_MDLDict[handle]->m_nFlags |= STUDIODATA_ERROR_MODEL;
  1770. bOk = ReadMDLFile( handle, ERROR_MODEL, buf );
  1771. }
  1772. if ( !bOk )
  1773. {
  1774. if (IsOSX())
  1775. {
  1776. // rbarris wants this to go somewhere like the console.log prior to crashing, which is what the Error call will do next
  1777. printf("\n ##### Model %s not found and %s couldn't be loaded", pModelName, ERROR_MODEL );
  1778. fflush( stdout );
  1779. }
  1780. Error( "Model %s not found and %s couldn't be loaded", pModelName, ERROR_MODEL );
  1781. return NULL;
  1782. }
  1783. }
  1784. // put it in the cache
  1785. if ( ProcessDataIntoCache( handle, MDLCACHE_STUDIOHDR, 0, buf.Base(), buf.TellMaxPut(), true ) )
  1786. {
  1787. pHdr = (studiohdr_t*)CheckData( m_MDLDict[handle]->m_MDLCache, MDLCACHE_STUDIOHDR );
  1788. }
  1789. }
  1790. return pHdr;
  1791. }
  1792. //-----------------------------------------------------------------------------
  1793. // Gets/sets user data associated with the MDL
  1794. //-----------------------------------------------------------------------------
  1795. void CMDLCache::SetUserData( MDLHandle_t handle, void* pData )
  1796. {
  1797. if ( handle == MDLHANDLE_INVALID )
  1798. return;
  1799. m_MDLDict[handle]->m_pUserData = pData;
  1800. }
  1801. void *CMDLCache::GetUserData( MDLHandle_t handle )
  1802. {
  1803. if ( handle == MDLHANDLE_INVALID )
  1804. return NULL;
  1805. return m_MDLDict[handle]->m_pUserData;
  1806. }
  1807. //-----------------------------------------------------------------------------
  1808. // Polls information about a particular mdl
  1809. //-----------------------------------------------------------------------------
  1810. bool CMDLCache::IsErrorModel( MDLHandle_t handle )
  1811. {
  1812. if ( handle == MDLHANDLE_INVALID )
  1813. return false;
  1814. return (m_MDLDict[handle]->m_nFlags & STUDIODATA_ERROR_MODEL) != 0;
  1815. }
  1816. //-----------------------------------------------------------------------------
  1817. // Brings all data associated with an MDL into memory
  1818. //-----------------------------------------------------------------------------
  1819. void CMDLCache::TouchAllData( MDLHandle_t handle )
  1820. {
  1821. studiohdr_t *pStudioHdr = GetStudioHdr( handle );
  1822. virtualmodel_t *pVModel = GetVirtualModel( handle );
  1823. if ( pVModel )
  1824. {
  1825. // skip self, start at children
  1826. // ensure all sub models are cached
  1827. for ( int i=1; i<pVModel->m_group.Count(); ++i )
  1828. {
  1829. MDLHandle_t childHandle = (MDLHandle_t)(int)pVModel->m_group[i].cache&0xffff;
  1830. if ( childHandle != MDLHANDLE_INVALID )
  1831. {
  1832. // FIXME: Should this be calling TouchAllData on the child?
  1833. GetStudioHdr( childHandle );
  1834. }
  1835. }
  1836. }
  1837. if ( !IsX360() )
  1838. {
  1839. // cache the anims
  1840. // Note that the animblocks start at 1!!!
  1841. for ( int i=1; i< (int)pStudioHdr->numanimblocks; ++i )
  1842. {
  1843. pStudioHdr->GetAnimBlock( i );
  1844. }
  1845. }
  1846. // cache the vertexes
  1847. if ( pStudioHdr->numbodyparts )
  1848. {
  1849. CacheVertexData( pStudioHdr );
  1850. GetHardwareData( handle );
  1851. }
  1852. }
  1853. //-----------------------------------------------------------------------------
  1854. // Flushes all data
  1855. //-----------------------------------------------------------------------------
  1856. void CMDLCache::Flush( MDLCacheFlush_t nFlushFlags )
  1857. {
  1858. // Free all MDLs that haven't been cleaned up
  1859. MDLHandle_t i = m_MDLDict.First();
  1860. while ( i != m_MDLDict.InvalidIndex() )
  1861. {
  1862. Flush( i, nFlushFlags );
  1863. i = m_MDLDict.Next( i );
  1864. }
  1865. }
  1866. //-----------------------------------------------------------------------------
  1867. // Cache handlers
  1868. //-----------------------------------------------------------------------------
  1869. static const char *g_ppszTypes[] =
  1870. {
  1871. "studiohdr", // MDLCACHE_STUDIOHDR
  1872. "studiohwdata", // MDLCACHE_STUDIOHWDATA
  1873. "vcollide", // MDLCACHE_VCOLLIDE
  1874. "animblock", // MDLCACHE_ANIMBLOCK
  1875. "virtualmodel", // MDLCACHE_VIRTUALMODEL
  1876. "vertexes", // MDLCACHE_VERTEXES
  1877. };
  1878. bool CMDLCache::HandleCacheNotification( const DataCacheNotification_t &notification )
  1879. {
  1880. switch ( notification.type )
  1881. {
  1882. case DC_AGE_DISCARD:
  1883. case DC_FLUSH_DISCARD:
  1884. case DC_REMOVED:
  1885. {
  1886. MdlCacheMsg( "MDLCache: Data cache discard %s %s\n", g_ppszTypes[TypeFromCacheID( notification.clientId )], GetModelName( HandleFromCacheID( notification.clientId ) ) );
  1887. if ( (DataCacheClientID_t)notification.pItemData == notification.clientId ||
  1888. TypeFromCacheID(notification.clientId) != MDLCACHE_STUDIOHWDATA )
  1889. {
  1890. Assert( notification.pItemData );
  1891. FreeData( TypeFromCacheID(notification.clientId), (void *)notification.pItemData );
  1892. }
  1893. else
  1894. {
  1895. UnloadHardwareData( HandleFromCacheID( notification.clientId ), false );
  1896. }
  1897. return true;
  1898. }
  1899. }
  1900. return CDefaultDataCacheClient::HandleCacheNotification( notification );
  1901. }
  1902. bool CMDLCache::GetItemName( DataCacheClientID_t clientId, const void *pItem, char *pDest, unsigned nMaxLen )
  1903. {
  1904. if ( (DataCacheClientID_t)pItem == clientId )
  1905. {
  1906. return false;
  1907. }
  1908. MDLHandle_t handle = HandleFromCacheID( clientId );
  1909. MDLCacheDataType_t type = TypeFromCacheID( clientId );
  1910. Q_snprintf( pDest, nMaxLen, "%s - %s", g_ppszTypes[type], GetModelName( handle ) );
  1911. return false;
  1912. }
  1913. //-----------------------------------------------------------------------------
  1914. // Flushes all data
  1915. //-----------------------------------------------------------------------------
  1916. void CMDLCache::BeginLock()
  1917. {
  1918. m_pModelCacheSection->BeginFrameLocking();
  1919. m_pMeshCacheSection->BeginFrameLocking();
  1920. }
  1921. //-----------------------------------------------------------------------------
  1922. // Flushes all data
  1923. //-----------------------------------------------------------------------------
  1924. void CMDLCache::EndLock()
  1925. {
  1926. m_pModelCacheSection->EndFrameLocking();
  1927. m_pMeshCacheSection->EndFrameLocking();
  1928. }
  1929. //-----------------------------------------------------------------------------
  1930. //
  1931. //-----------------------------------------------------------------------------
  1932. void CMDLCache::BreakFrameLock( bool bModels, bool bMesh )
  1933. {
  1934. if ( bModels )
  1935. {
  1936. if ( m_pModelCacheSection->IsFrameLocking() )
  1937. {
  1938. Assert( !m_nModelCacheFrameLocks );
  1939. m_nModelCacheFrameLocks = 0;
  1940. do
  1941. {
  1942. m_nModelCacheFrameLocks++;
  1943. } while ( m_pModelCacheSection->EndFrameLocking() );
  1944. }
  1945. }
  1946. if ( bMesh )
  1947. {
  1948. if ( m_pMeshCacheSection->IsFrameLocking() )
  1949. {
  1950. Assert( !m_nMeshCacheFrameLocks );
  1951. m_nMeshCacheFrameLocks = 0;
  1952. do
  1953. {
  1954. m_nMeshCacheFrameLocks++;
  1955. } while ( m_pMeshCacheSection->EndFrameLocking() );
  1956. }
  1957. }
  1958. }
  1959. void CMDLCache::RestoreFrameLock()
  1960. {
  1961. while ( m_nModelCacheFrameLocks )
  1962. {
  1963. m_pModelCacheSection->BeginFrameLocking();
  1964. m_nModelCacheFrameLocks--;
  1965. }
  1966. while ( m_nMeshCacheFrameLocks )
  1967. {
  1968. m_pMeshCacheSection->BeginFrameLocking();
  1969. m_nMeshCacheFrameLocks--;
  1970. }
  1971. }
  1972. //-----------------------------------------------------------------------------
  1973. //
  1974. //-----------------------------------------------------------------------------
  1975. int *CMDLCache::GetFrameUnlockCounterPtrOLD()
  1976. {
  1977. return GetCacheSection( MDLCACHE_STUDIOHDR )->GetFrameUnlockCounterPtr();
  1978. }
  1979. int *CMDLCache::GetFrameUnlockCounterPtr( MDLCacheDataType_t type )
  1980. {
  1981. return GetCacheSection( type )->GetFrameUnlockCounterPtr();
  1982. }
  1983. //-----------------------------------------------------------------------------
  1984. // Completes all pending async operations
  1985. //-----------------------------------------------------------------------------
  1986. void CMDLCache::FinishPendingLoads()
  1987. {
  1988. if ( !ThreadInMainThread() )
  1989. {
  1990. return;
  1991. }
  1992. AUTO_LOCK( m_AsyncMutex );
  1993. // finish just our known jobs
  1994. int iAsync = m_PendingAsyncs.Head();
  1995. while ( iAsync != m_PendingAsyncs.InvalidIndex() )
  1996. {
  1997. AsyncInfo_t &info = m_PendingAsyncs[iAsync];
  1998. if ( info.hControl )
  1999. {
  2000. g_pFullFileSystem->AsyncFinish( info.hControl, true );
  2001. }
  2002. iAsync = m_PendingAsyncs.Next( iAsync );
  2003. }
  2004. ProcessPendingAsyncs();
  2005. }
  2006. //-----------------------------------------------------------------------------
  2007. // Notify map load has started
  2008. //-----------------------------------------------------------------------------
  2009. void CMDLCache::BeginMapLoad()
  2010. {
  2011. BreakFrameLock();
  2012. studiodata_t *pStudioData;
  2013. // Unlock prior map MDLs prior to load
  2014. MDLHandle_t i = m_MDLDict.First();
  2015. while ( i != m_MDLDict.InvalidIndex() )
  2016. {
  2017. pStudioData = m_MDLDict[i];
  2018. if ( pStudioData->m_nFlags & STUDIODATA_FLAGS_LOCKED_MDL )
  2019. {
  2020. GetCacheSection( MDLCACHE_STUDIOHDR )->Unlock( pStudioData->m_MDLCache );
  2021. pStudioData->m_nFlags &= ~STUDIODATA_FLAGS_LOCKED_MDL;
  2022. }
  2023. i = m_MDLDict.Next( i );
  2024. }
  2025. }
  2026. //-----------------------------------------------------------------------------
  2027. // Notify map load is complete
  2028. //-----------------------------------------------------------------------------
  2029. void CMDLCache::EndMapLoad()
  2030. {
  2031. FinishPendingLoads();
  2032. // Remove all stray MDLs not referenced during load
  2033. if ( mod_lock_mdls_on_load.GetBool() )
  2034. {
  2035. studiodata_t *pStudioData;
  2036. MDLHandle_t i = m_MDLDict.First();
  2037. while ( i != m_MDLDict.InvalidIndex() )
  2038. {
  2039. pStudioData = m_MDLDict[i];
  2040. if ( !(pStudioData->m_nFlags & STUDIODATA_FLAGS_LOCKED_MDL) )
  2041. {
  2042. Flush( i, MDLCACHE_FLUSH_STUDIOHDR );
  2043. }
  2044. i = m_MDLDict.Next( i );
  2045. }
  2046. }
  2047. RestoreFrameLock();
  2048. }
  2049. //-----------------------------------------------------------------------------
  2050. // Is a particular part of the model data loaded?
  2051. //-----------------------------------------------------------------------------
  2052. bool CMDLCache::IsDataLoaded( MDLHandle_t handle, MDLCacheDataType_t type )
  2053. {
  2054. if ( handle == MDLHANDLE_INVALID || !m_MDLDict.IsValidIndex( handle ) )
  2055. return false;
  2056. studiodata_t *pData = m_MDLDict[ handle ];
  2057. switch( type )
  2058. {
  2059. case MDLCACHE_STUDIOHDR:
  2060. return GetCacheSection( MDLCACHE_STUDIOHDR )->IsPresent( pData->m_MDLCache );
  2061. case MDLCACHE_STUDIOHWDATA:
  2062. return ( pData->m_nFlags & STUDIODATA_FLAGS_STUDIOMESH_LOADED ) != 0;
  2063. case MDLCACHE_VCOLLIDE:
  2064. return ( pData->m_nFlags & STUDIODATA_FLAGS_VCOLLISION_LOADED ) != 0;
  2065. case MDLCACHE_ANIMBLOCK:
  2066. {
  2067. if ( !pData->m_pAnimBlock )
  2068. return false;
  2069. for (int i = 0; i < pData->m_nAnimBlockCount; ++i )
  2070. {
  2071. if ( !pData->m_pAnimBlock[i] )
  2072. return false;
  2073. if ( !GetCacheSection( type )->IsPresent( pData->m_pAnimBlock[i] ) )
  2074. return false;
  2075. }
  2076. return true;
  2077. }
  2078. case MDLCACHE_VIRTUALMODEL:
  2079. return ( pData->m_pVirtualModel != 0 );
  2080. case MDLCACHE_VERTEXES:
  2081. return m_pMeshCacheSection->IsPresent( pData->m_VertexCache );
  2082. }
  2083. return false;
  2084. }
  2085. //-----------------------------------------------------------------------------
  2086. // Get the correct extension for our dx
  2087. //-----------------------------------------------------------------------------
  2088. const char *CMDLCache::GetVTXExtension()
  2089. {
  2090. if ( IsPC() )
  2091. {
  2092. if ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 90 )
  2093. {
  2094. return ".dx90.vtx";
  2095. }
  2096. else if ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 80 )
  2097. {
  2098. return ".dx80.vtx";
  2099. }
  2100. else
  2101. {
  2102. return ".sw.vtx";
  2103. }
  2104. }
  2105. return ".dx90.vtx";
  2106. }
  2107. //-----------------------------------------------------------------------------
  2108. // Minimal presence and header validation, no data loads
  2109. // Return true if successful, false otherwise.
  2110. //-----------------------------------------------------------------------------
  2111. bool CMDLCache::VerifyHeaders( studiohdr_t *pStudioHdr )
  2112. {
  2113. VPROF( "CMDLCache::VerifyHeaders" );
  2114. if ( developer.GetInt() < 2 )
  2115. {
  2116. return true;
  2117. }
  2118. // model has no vertex data
  2119. if ( !pStudioHdr->numbodyparts )
  2120. {
  2121. // valid
  2122. return true;
  2123. }
  2124. char pFileName[ MAX_PATH ];
  2125. MDLHandle_t handle = (MDLHandle_t)(int)pStudioHdr->virtualModel&0xffff;
  2126. MakeFilename( handle, ".vvd", pFileName, sizeof(pFileName) );
  2127. MdlCacheMsg("MDLCache: Load VVD (verify) %s\n", pFileName );
  2128. // vvd header only
  2129. CUtlBuffer vvdHeader( 0, sizeof(vertexFileHeader_t) );
  2130. if ( !ReadFileNative( pFileName, "GAME", vvdHeader, sizeof(vertexFileHeader_t) ) )
  2131. {
  2132. return false;
  2133. }
  2134. vertexFileHeader_t *pVertexHdr = (vertexFileHeader_t*)vvdHeader.PeekGet();
  2135. // check
  2136. if (( pVertexHdr->id != MODEL_VERTEX_FILE_ID ) ||
  2137. ( pVertexHdr->version != MODEL_VERTEX_FILE_VERSION ) ||
  2138. ( pVertexHdr->checksum != pStudioHdr->checksum ))
  2139. {
  2140. return false;
  2141. }
  2142. // load the VTX file
  2143. // use model name for correct path
  2144. MakeFilename( handle, GetVTXExtension(), pFileName, sizeof(pFileName) );
  2145. MdlCacheMsg("MDLCache: Load VTX (verify) %s\n", pFileName );
  2146. // vtx header only
  2147. CUtlBuffer vtxHeader( 0, sizeof(OptimizedModel::FileHeader_t) );
  2148. if ( !ReadFileNative( pFileName, "GAME", vtxHeader, sizeof(OptimizedModel::FileHeader_t) ) )
  2149. {
  2150. return false;
  2151. }
  2152. // check
  2153. OptimizedModel::FileHeader_t *pVtxHdr = (OptimizedModel::FileHeader_t*)vtxHeader.PeekGet();
  2154. if (( pVtxHdr->version != OPTIMIZED_MODEL_FILE_VERSION ) ||
  2155. ( pVtxHdr->checkSum != pStudioHdr->checksum ))
  2156. {
  2157. return false;
  2158. }
  2159. // valid
  2160. return true;
  2161. }
  2162. //-----------------------------------------------------------------------------
  2163. // Cache model's specified dynamic data
  2164. //-----------------------------------------------------------------------------
  2165. vertexFileHeader_t *CMDLCache::CacheVertexData( studiohdr_t *pStudioHdr )
  2166. {
  2167. VPROF( "CMDLCache::CacheVertexData" );
  2168. vertexFileHeader_t *pVvdHdr;
  2169. MDLHandle_t handle;
  2170. Assert( pStudioHdr );
  2171. handle = (MDLHandle_t)(int)pStudioHdr->virtualModel&0xffff;
  2172. Assert( handle != MDLHANDLE_INVALID );
  2173. pVvdHdr = (vertexFileHeader_t *)CheckData( m_MDLDict[handle]->m_VertexCache, MDLCACHE_VERTEXES );
  2174. if ( pVvdHdr )
  2175. {
  2176. return pVvdHdr;
  2177. }
  2178. m_MDLDict[handle]->m_VertexCache = NULL;
  2179. return LoadVertexData( pStudioHdr );
  2180. }
  2181. //-----------------------------------------------------------------------------
  2182. // Start an async transfer
  2183. //-----------------------------------------------------------------------------
  2184. FSAsyncStatus_t CMDLCache::LoadData( const char *pszFilename, const char *pszPathID, void *pDest, int nBytes, int nOffset, bool bAsync, FSAsyncControl_t *pControl )
  2185. {
  2186. if ( !*pControl )
  2187. {
  2188. if ( IsX360() && g_pQueuedLoader->IsMapLoading() )
  2189. {
  2190. DevWarning( "CMDLCache: Non-Optimal loading path for %s\n", pszFilename );
  2191. }
  2192. FileAsyncRequest_t asyncRequest;
  2193. asyncRequest.pszFilename = pszFilename;
  2194. asyncRequest.pszPathID = pszPathID;
  2195. asyncRequest.pData = pDest;
  2196. asyncRequest.nBytes = nBytes;
  2197. asyncRequest.nOffset = nOffset;
  2198. if ( !pDest )
  2199. {
  2200. asyncRequest.flags = FSASYNC_FLAGS_ALLOCNOFREE;
  2201. }
  2202. if ( !bAsync )
  2203. {
  2204. asyncRequest.flags |= FSASYNC_FLAGS_SYNC;
  2205. }
  2206. MEM_ALLOC_CREDIT();
  2207. return g_pFullFileSystem->AsyncRead( asyncRequest, pControl );
  2208. }
  2209. return FSASYNC_ERR_FAILURE;
  2210. }
  2211. //-----------------------------------------------------------------------------
  2212. // Determine the maximum number of 'real' bone influences used by any vertex in a model
  2213. // (100% binding to bone zero doesn't count)
  2214. //-----------------------------------------------------------------------------
  2215. int ComputeMaxRealBoneInfluences( vertexFileHeader_t * vertexFile, int lod )
  2216. {
  2217. const mstudiovertex_t * verts = vertexFile->GetVertexData();
  2218. int numVerts = vertexFile->numLODVertexes[ lod ];
  2219. Assert(verts);
  2220. int maxWeights = 0;
  2221. for (int i = 0;i < numVerts;i++)
  2222. {
  2223. if ( verts[i].m_BoneWeights.numbones > 0 )
  2224. {
  2225. int numWeights = 0;
  2226. for (int j = 0;j < MAX_NUM_BONES_PER_VERT;j++)
  2227. {
  2228. if ( verts[i].m_BoneWeights.weight[j] > 0 )
  2229. numWeights = j + 1;
  2230. }
  2231. if ( ( numWeights == 1 ) && ( verts[i].m_BoneWeights.bone[0] == 0 ) )
  2232. {
  2233. // 100% binding to first bone - not really skinned (the first bone is just the model transform)
  2234. numWeights = 0;
  2235. }
  2236. maxWeights = max( numWeights, maxWeights );
  2237. }
  2238. }
  2239. return maxWeights;
  2240. }
  2241. //-----------------------------------------------------------------------------
  2242. // Generate thin vertices (containing just the data needed to do model decals)
  2243. //-----------------------------------------------------------------------------
  2244. vertexFileHeader_t * CMDLCache::CreateThinVertexes( vertexFileHeader_t * originalData, const studiohdr_t * pStudioHdr, int * cacheLength )
  2245. {
  2246. int rootLod = min( (int)pStudioHdr->rootLOD, ( originalData->numLODs - 1 ) );
  2247. int numVerts = originalData->numLODVertexes[ rootLod ] + 1; // Add 1 vert to support prefetch during array access
  2248. int numBoneInfluences = ComputeMaxRealBoneInfluences( originalData, rootLod );
  2249. // Only store (N-1) weights (all N weights sum to 1, so we can re-compute the Nth weight later)
  2250. int numStoredWeights = max( 0, ( numBoneInfluences - 1 ) );
  2251. int vertexSize = 2*sizeof( Vector ) + numBoneInfluences*sizeof( unsigned char ) + numStoredWeights*sizeof( float );
  2252. *cacheLength = sizeof( vertexFileHeader_t ) + sizeof( thinModelVertices_t ) + numVerts*vertexSize;
  2253. // Allocate cache space for the thin data
  2254. MemAlloc_PushAllocDbgInfo( "Models:Vertex data", 0);
  2255. vertexFileHeader_t * pNewVvdHdr = (vertexFileHeader_t *)AllocData( MDLCACHE_VERTEXES, *cacheLength );
  2256. MemAlloc_PopAllocDbgInfo();
  2257. Assert( pNewVvdHdr );
  2258. if ( pNewVvdHdr )
  2259. {
  2260. // Copy the header and set it up to hold thin vertex data
  2261. memcpy( (void *)pNewVvdHdr, (void *)originalData, sizeof( vertexFileHeader_t ) );
  2262. pNewVvdHdr->id = MODEL_VERTEX_FILE_THIN_ID;
  2263. pNewVvdHdr->numFixups = 0;
  2264. pNewVvdHdr->fixupTableStart = 0;
  2265. pNewVvdHdr->tangentDataStart = 0;
  2266. pNewVvdHdr->vertexDataStart = sizeof( vertexFileHeader_t );
  2267. // Set up the thin vertex structure
  2268. thinModelVertices_t * pNewThinVerts = (thinModelVertices_t *)( pNewVvdHdr + 1 );
  2269. Vector * pPositions = (Vector *)( pNewThinVerts + 1 );
  2270. float * pBoneWeights = (float *)( pPositions + numVerts );
  2271. // Alloc the (short) normals here to avoid mis-aligning the float data
  2272. unsigned short * pNormals = (unsigned short *)( pBoneWeights + numVerts*numStoredWeights );
  2273. // Alloc the (char) indices here to avoid mis-aligning the float/short data
  2274. char * pBoneIndices = (char *)( pNormals + numVerts );
  2275. if ( numStoredWeights == 0 )
  2276. pBoneWeights = NULL;
  2277. if ( numBoneInfluences == 0 )
  2278. pBoneIndices = NULL;
  2279. pNewThinVerts->Init( numBoneInfluences, pPositions, pNormals, pBoneWeights, pBoneIndices );
  2280. // Copy over the original data
  2281. const mstudiovertex_t * srcVertexData = originalData->GetVertexData();
  2282. for ( int i = 0; i < numVerts; i++ )
  2283. {
  2284. pNewThinVerts->SetPosition( i, srcVertexData[ i ].m_vecPosition );
  2285. pNewThinVerts->SetNormal( i, srcVertexData[ i ].m_vecNormal );
  2286. if ( numBoneInfluences > 0 )
  2287. {
  2288. mstudioboneweight_t boneWeights;
  2289. boneWeights.numbones = numBoneInfluences;
  2290. for ( int j = 0; j < numStoredWeights; j++ )
  2291. {
  2292. boneWeights.weight[ j ] = srcVertexData[ i ].m_BoneWeights.weight[ j ];
  2293. }
  2294. for ( int j = 0; j < numBoneInfluences; j++ )
  2295. {
  2296. boneWeights.bone[ j ] = srcVertexData[ i ].m_BoneWeights.bone[ j ];
  2297. }
  2298. pNewThinVerts->SetBoneWeights( i, boneWeights );
  2299. }
  2300. }
  2301. }
  2302. return pNewVvdHdr;
  2303. }
  2304. //-----------------------------------------------------------------------------
  2305. // Process the provided raw data into the cache. Distributes to low level
  2306. // unserialization or build methods.
  2307. //-----------------------------------------------------------------------------
  2308. bool CMDLCache::ProcessDataIntoCache( MDLHandle_t handle, MDLCacheDataType_t type, int iAnimBlock, void *pData, int nDataSize, bool bDataValid )
  2309. {
  2310. studiohdr_t *pStudioHdrCurrent = NULL;
  2311. if ( type != MDLCACHE_STUDIOHDR )
  2312. {
  2313. // can only get the studiohdr once the header has been processed successfully into the cache
  2314. // causes a ProcessDataIntoCache() with the studiohdr data
  2315. pStudioHdrCurrent = GetStudioHdr( handle );
  2316. if ( !pStudioHdrCurrent )
  2317. {
  2318. return false;
  2319. }
  2320. }
  2321. studiodata_t *pStudioDataCurrent = m_MDLDict[handle];
  2322. if ( !pStudioDataCurrent )
  2323. {
  2324. return false;
  2325. }
  2326. switch ( type )
  2327. {
  2328. case MDLCACHE_STUDIOHDR:
  2329. {
  2330. pStudioHdrCurrent = UnserializeMDL( handle, pData, nDataSize, bDataValid );
  2331. if ( !pStudioHdrCurrent )
  2332. {
  2333. return false;
  2334. }
  2335. if (!Studio_ConvertStudioHdrToNewVersion( pStudioHdrCurrent ))
  2336. {
  2337. Warning( "MDLCache: %s needs to be recompiled\n", pStudioHdrCurrent->pszName() );
  2338. }
  2339. if ( pStudioHdrCurrent->numincludemodels == 0 )
  2340. {
  2341. // perf optimization, calculate once and cache off the autoplay sequences
  2342. int nCount = pStudioHdrCurrent->CountAutoplaySequences();
  2343. if ( nCount )
  2344. {
  2345. AllocateAutoplaySequences( m_MDLDict[handle], nCount );
  2346. pStudioHdrCurrent->CopyAutoplaySequences( m_MDLDict[handle]->m_pAutoplaySequenceList, nCount );
  2347. }
  2348. }
  2349. // Load animations
  2350. UnserializeAllVirtualModelsAndAnimBlocks( handle );
  2351. break;
  2352. }
  2353. case MDLCACHE_VERTEXES:
  2354. {
  2355. if ( bDataValid )
  2356. {
  2357. BuildAndCacheVertexData( pStudioHdrCurrent, (vertexFileHeader_t *)pData );
  2358. }
  2359. else
  2360. {
  2361. pStudioDataCurrent->m_nFlags |= STUDIODATA_FLAGS_NO_VERTEX_DATA;
  2362. if ( pStudioHdrCurrent->numbodyparts )
  2363. {
  2364. // expected data not valid
  2365. Warning( "MDLCache: Failed load of .VVD data for %s\n", pStudioHdrCurrent->pszName() );
  2366. return false;
  2367. }
  2368. }
  2369. break;
  2370. }
  2371. case MDLCACHE_STUDIOHWDATA:
  2372. {
  2373. if ( bDataValid )
  2374. {
  2375. BuildHardwareData( handle, pStudioDataCurrent, pStudioHdrCurrent, (OptimizedModel::FileHeader_t *)pData );
  2376. }
  2377. else
  2378. {
  2379. pStudioDataCurrent->m_nFlags |= STUDIODATA_FLAGS_NO_STUDIOMESH;
  2380. if ( pStudioHdrCurrent->numbodyparts )
  2381. {
  2382. // expected data not valid
  2383. Warning( "MDLCache: Failed load of .VTX data for %s\n", pStudioHdrCurrent->pszName() );
  2384. return false;
  2385. }
  2386. }
  2387. m_pMeshCacheSection->Unlock( pStudioDataCurrent->m_VertexCache );
  2388. m_pMeshCacheSection->Age( pStudioDataCurrent->m_VertexCache );
  2389. // FIXME: thin VVD data on PC too (have to address alt-tab, various DX8/DX7/debug software paths in studiorender, tools, etc)
  2390. static bool bCompressedVVDs = CommandLine()->CheckParm( "-no_compressed_vvds" ) == NULL;
  2391. if ( IsX360() && !( pStudioDataCurrent->m_nFlags & STUDIODATA_FLAGS_NO_STUDIOMESH ) && bCompressedVVDs )
  2392. {
  2393. // Replace the cached vertex data with a thin version (used for model decals).
  2394. // Flexed meshes require the fat data to remain, for CPU mesh anim.
  2395. if ( pStudioHdrCurrent->numflexdesc == 0 )
  2396. {
  2397. vertexFileHeader_t *originalVertexData = GetVertexData( handle );
  2398. Assert( originalVertexData );
  2399. if ( originalVertexData )
  2400. {
  2401. int thinVertexDataSize = 0;
  2402. vertexFileHeader_t *thinVertexData = CreateThinVertexes( originalVertexData, pStudioHdrCurrent, &thinVertexDataSize );
  2403. Assert( thinVertexData && ( thinVertexDataSize > 0 ) );
  2404. if ( thinVertexData && ( thinVertexDataSize > 0 ) )
  2405. {
  2406. // Remove the original cache entry (and free it)
  2407. Flush( handle, MDLCACHE_FLUSH_VERTEXES | MDLCACHE_FLUSH_IGNORELOCK );
  2408. // Add the new one
  2409. CacheData( &pStudioDataCurrent->m_VertexCache, thinVertexData, thinVertexDataSize, pStudioHdrCurrent->pszName(), MDLCACHE_VERTEXES, MakeCacheID( handle, MDLCACHE_VERTEXES) );
  2410. }
  2411. }
  2412. }
  2413. }
  2414. break;
  2415. }
  2416. case MDLCACHE_ANIMBLOCK:
  2417. {
  2418. MEM_ALLOC_CREDIT_( __FILE__ ": Anim Blocks" );
  2419. if ( bDataValid )
  2420. {
  2421. MdlCacheMsg( "MDLCache: Finish load anim block %s (block %i)\n", pStudioHdrCurrent->pszName(), iAnimBlock );
  2422. char pCacheName[MAX_PATH];
  2423. Q_snprintf( pCacheName, MAX_PATH, "%s (block %i)", pStudioHdrCurrent->pszName(), iAnimBlock );
  2424. if ( IsX360() )
  2425. {
  2426. if ( CLZMA::IsCompressed( (unsigned char *)pData ) )
  2427. {
  2428. // anim block arrives compressed, decode and cache the results
  2429. unsigned int nOriginalSize = CLZMA::GetActualSize( (unsigned char *)pData );
  2430. // get a "fake" (not really aligned) optimal read buffer, as expected by the free logic
  2431. void *pOriginalData = g_pFullFileSystem->AllocOptimalReadBuffer( FILESYSTEM_INVALID_HANDLE, nOriginalSize, 0 );
  2432. unsigned int nOutputSize = CLZMA::Uncompress( (unsigned char *)pData, (unsigned char *)pOriginalData );
  2433. if ( nOutputSize != nOriginalSize )
  2434. {
  2435. // decoder failure
  2436. g_pFullFileSystem->FreeOptimalReadBuffer( pOriginalData );
  2437. return false;
  2438. }
  2439. // input i/o buffer is now unused
  2440. g_pFullFileSystem->FreeOptimalReadBuffer( pData );
  2441. // datacache will now own the data
  2442. pData = pOriginalData;
  2443. nDataSize = nOriginalSize;
  2444. }
  2445. }
  2446. CacheData( &pStudioDataCurrent->m_pAnimBlock[iAnimBlock], pData, nDataSize, pCacheName, MDLCACHE_ANIMBLOCK, MakeCacheID( handle, MDLCACHE_ANIMBLOCK) );
  2447. }
  2448. else
  2449. {
  2450. MdlCacheMsg( "MDLCache: Failed load anim block %s (block %i)\n", pStudioHdrCurrent->pszName(), iAnimBlock );
  2451. if ( pStudioDataCurrent->m_pAnimBlock )
  2452. {
  2453. pStudioDataCurrent->m_pAnimBlock[iAnimBlock] = NULL;
  2454. }
  2455. return false;
  2456. }
  2457. break;
  2458. }
  2459. case MDLCACHE_VCOLLIDE:
  2460. {
  2461. // always marked as loaded, vcollides are not present for every model
  2462. pStudioDataCurrent->m_nFlags |= STUDIODATA_FLAGS_VCOLLISION_LOADED;
  2463. if ( bDataValid )
  2464. {
  2465. MdlCacheMsg( "MDLCache: Finish load vcollide for %s\n", pStudioHdrCurrent->pszName() );
  2466. CTempAllocHelper pOriginalData;
  2467. if ( IsX360() )
  2468. {
  2469. if ( CLZMA::IsCompressed( (unsigned char *)pData ) )
  2470. {
  2471. // phy arrives compressed, decode and cache the results
  2472. unsigned int nOriginalSize = CLZMA::GetActualSize( (unsigned char *)pData );
  2473. pOriginalData.Alloc( nOriginalSize );
  2474. unsigned int nOutputSize = CLZMA::Uncompress( (unsigned char *)pData, (unsigned char *)pOriginalData.Get() );
  2475. if ( nOutputSize != nOriginalSize )
  2476. {
  2477. // decoder failure
  2478. return NULL;
  2479. }
  2480. pData = pOriginalData.Get();
  2481. nDataSize = nOriginalSize;
  2482. }
  2483. }
  2484. CUtlBuffer buf( pData, nDataSize, CUtlBuffer::READ_ONLY );
  2485. buf.SeekPut( CUtlBuffer::SEEK_HEAD, nDataSize );
  2486. phyheader_t header;
  2487. buf.Get( &header, sizeof( phyheader_t ) );
  2488. if ( ( header.size == sizeof( header ) ) && header.solidCount > 0 )
  2489. {
  2490. int nBufSize = buf.TellMaxPut() - buf.TellGet();
  2491. vcollide_t *pCollide = &pStudioDataCurrent->m_VCollisionData;
  2492. g_pPhysicsCollision->VCollideLoad( pCollide, header.solidCount, (const char*)buf.PeekGet(), nBufSize );
  2493. if ( m_pCacheNotify )
  2494. {
  2495. m_pCacheNotify->OnDataLoaded( MDLCACHE_VCOLLIDE, handle );
  2496. }
  2497. }
  2498. }
  2499. else
  2500. {
  2501. MdlCacheWarning( "MDLCache: Failed load of .PHY data for %s\n", pStudioHdrCurrent->pszName() );
  2502. return false;
  2503. }
  2504. break;
  2505. }
  2506. default:
  2507. Assert( 0 );
  2508. }
  2509. // success
  2510. return true;
  2511. }
  2512. //-----------------------------------------------------------------------------
  2513. // Returns:
  2514. // <0: indeterminate at this time
  2515. // =0: pending
  2516. // >0: completed
  2517. //-----------------------------------------------------------------------------
  2518. int CMDLCache::ProcessPendingAsync( int iAsync )
  2519. {
  2520. if ( !ThreadInMainThread() )
  2521. {
  2522. return -1;
  2523. }
  2524. ASSERT_NO_REENTRY();
  2525. void *pData = NULL;
  2526. int nBytesRead = 0;
  2527. AsyncInfo_t *pInfo;
  2528. {
  2529. AUTO_LOCK( m_AsyncMutex );
  2530. pInfo = &m_PendingAsyncs[iAsync];
  2531. }
  2532. Assert( pInfo->hControl );
  2533. FSAsyncStatus_t status = g_pFullFileSystem->AsyncGetResult( pInfo->hControl, &pData, &nBytesRead );
  2534. if ( status == FSASYNC_STATUS_PENDING )
  2535. {
  2536. return 0;
  2537. }
  2538. AsyncInfo_t info = *pInfo;
  2539. pInfo = &info;
  2540. ClearAsync( pInfo->hModel, pInfo->type, pInfo->iAnimBlock );
  2541. switch ( pInfo->type )
  2542. {
  2543. case MDLCACHE_VERTEXES:
  2544. case MDLCACHE_STUDIOHWDATA:
  2545. case MDLCACHE_VCOLLIDE:
  2546. {
  2547. ProcessDataIntoCache( pInfo->hModel, pInfo->type, 0, pData, nBytesRead, status == FSASYNC_OK );
  2548. g_pFullFileSystem->FreeOptimalReadBuffer( pData );
  2549. break;
  2550. }
  2551. case MDLCACHE_ANIMBLOCK:
  2552. {
  2553. // cache assumes ownership of valid async'd data
  2554. if ( !ProcessDataIntoCache( pInfo->hModel, MDLCACHE_ANIMBLOCK, pInfo->iAnimBlock, pData, nBytesRead, status == FSASYNC_OK ) )
  2555. {
  2556. g_pFullFileSystem->FreeOptimalReadBuffer( pData );
  2557. }
  2558. break;
  2559. }
  2560. default:
  2561. Assert( 0 );
  2562. }
  2563. return 1;
  2564. }
  2565. //-----------------------------------------------------------------------------
  2566. //
  2567. //-----------------------------------------------------------------------------
  2568. void CMDLCache::ProcessPendingAsyncs( MDLCacheDataType_t type )
  2569. {
  2570. if ( !ThreadInMainThread() )
  2571. {
  2572. return;
  2573. }
  2574. if ( !m_PendingAsyncs.Count() )
  2575. {
  2576. return;
  2577. }
  2578. static bool bReentering;
  2579. if ( bReentering )
  2580. {
  2581. return;
  2582. }
  2583. bReentering = true;
  2584. AUTO_LOCK( m_AsyncMutex );
  2585. // Process all of the completed loads that were requested before a new one. This ensures two
  2586. // things -- the LRU is in correct order, and it catches precached items lurking
  2587. // in the async queue that have only been requested once (thus aren't being cached
  2588. // and might lurk forever, e.g., wood gibs in the citadel)
  2589. int current = m_PendingAsyncs.Head();
  2590. while ( current != m_PendingAsyncs.InvalidIndex() )
  2591. {
  2592. int next = m_PendingAsyncs.Next( current );
  2593. if ( type == MDLCACHE_NONE || m_PendingAsyncs[current].type == type )
  2594. {
  2595. // process, also removes from list
  2596. if ( ProcessPendingAsync( current ) <= 0 )
  2597. {
  2598. // indeterminate or pending
  2599. break;
  2600. }
  2601. }
  2602. current = next;
  2603. }
  2604. bReentering = false;
  2605. }
  2606. //-----------------------------------------------------------------------------
  2607. // Cache model's specified dynamic data
  2608. //-----------------------------------------------------------------------------
  2609. bool CMDLCache::ClearAsync( MDLHandle_t handle, MDLCacheDataType_t type, int iAnimBlock, bool bAbort )
  2610. {
  2611. int iAsyncInfo = GetAsyncInfoIndex( handle, type, iAnimBlock );
  2612. if ( iAsyncInfo != NO_ASYNC )
  2613. {
  2614. AsyncInfo_t *pInfo;
  2615. {
  2616. AUTO_LOCK( m_AsyncMutex );
  2617. pInfo = &m_PendingAsyncs[iAsyncInfo];
  2618. }
  2619. if ( pInfo->hControl )
  2620. {
  2621. if ( bAbort )
  2622. {
  2623. g_pFullFileSystem->AsyncAbort( pInfo->hControl );
  2624. void *pData;
  2625. int ignored;
  2626. if ( g_pFullFileSystem->AsyncGetResult( pInfo->hControl, &pData, &ignored ) == FSASYNC_OK )
  2627. {
  2628. g_pFullFileSystem->FreeOptimalReadBuffer( pData );
  2629. }
  2630. }
  2631. g_pFullFileSystem->AsyncRelease( pInfo->hControl );
  2632. pInfo->hControl = NULL;
  2633. }
  2634. SetAsyncInfoIndex( handle, type, iAnimBlock, NO_ASYNC );
  2635. {
  2636. AUTO_LOCK( m_AsyncMutex );
  2637. m_PendingAsyncs.Remove( iAsyncInfo );
  2638. }
  2639. return true;
  2640. }
  2641. return false;
  2642. }
  2643. //-----------------------------------------------------------------------------
  2644. //-----------------------------------------------------------------------------
  2645. bool CMDLCache::GetAsyncLoad( MDLCacheDataType_t type )
  2646. {
  2647. switch ( type )
  2648. {
  2649. case MDLCACHE_STUDIOHDR:
  2650. return false;
  2651. case MDLCACHE_STUDIOHWDATA:
  2652. return mod_load_mesh_async.GetBool();
  2653. case MDLCACHE_VCOLLIDE:
  2654. return mod_load_vcollide_async.GetBool();
  2655. case MDLCACHE_ANIMBLOCK:
  2656. return mod_load_anims_async.GetBool();
  2657. case MDLCACHE_VIRTUALMODEL:
  2658. return false;
  2659. case MDLCACHE_VERTEXES:
  2660. return mod_load_mesh_async.GetBool();
  2661. }
  2662. return false;
  2663. }
  2664. //-----------------------------------------------------------------------------
  2665. //-----------------------------------------------------------------------------
  2666. bool CMDLCache::SetAsyncLoad( MDLCacheDataType_t type, bool bAsync )
  2667. {
  2668. bool bRetVal = false;
  2669. switch ( type )
  2670. {
  2671. case MDLCACHE_STUDIOHDR:
  2672. break;
  2673. case MDLCACHE_STUDIOHWDATA:
  2674. bRetVal = mod_load_mesh_async.GetBool();
  2675. mod_load_mesh_async.SetValue( bAsync );
  2676. break;
  2677. case MDLCACHE_VCOLLIDE:
  2678. bRetVal = mod_load_vcollide_async.GetBool();
  2679. mod_load_vcollide_async.SetValue( bAsync );
  2680. break;
  2681. case MDLCACHE_ANIMBLOCK:
  2682. bRetVal = mod_load_anims_async.GetBool();
  2683. mod_load_anims_async.SetValue( bAsync );
  2684. break;
  2685. case MDLCACHE_VIRTUALMODEL:
  2686. return false;
  2687. break;
  2688. case MDLCACHE_VERTEXES:
  2689. bRetVal = mod_load_mesh_async.GetBool();
  2690. mod_load_mesh_async.SetValue( bAsync );
  2691. break;
  2692. }
  2693. return bRetVal;
  2694. }
  2695. //-----------------------------------------------------------------------------
  2696. // Cache model's specified dynamic data
  2697. //-----------------------------------------------------------------------------
  2698. vertexFileHeader_t *CMDLCache::BuildAndCacheVertexData( studiohdr_t *pStudioHdr, vertexFileHeader_t *pRawVvdHdr )
  2699. {
  2700. MDLHandle_t handle = (MDLHandle_t)(int)pStudioHdr->virtualModel&0xffff;
  2701. vertexFileHeader_t *pVvdHdr;
  2702. MdlCacheMsg( "MDLCache: Load VVD for %s\n", pStudioHdr->pszName() );
  2703. Assert( pRawVvdHdr );
  2704. // check header
  2705. if ( pRawVvdHdr->id != MODEL_VERTEX_FILE_ID )
  2706. {
  2707. Warning( "Error Vertex File for '%s' id %d should be %d\n", pStudioHdr->pszName(), pRawVvdHdr->id, MODEL_VERTEX_FILE_ID );
  2708. return NULL;
  2709. }
  2710. if ( pRawVvdHdr->version != MODEL_VERTEX_FILE_VERSION )
  2711. {
  2712. Warning( "Error Vertex File for '%s' version %d should be %d\n", pStudioHdr->pszName(), pRawVvdHdr->version, MODEL_VERTEX_FILE_VERSION );
  2713. return NULL;
  2714. }
  2715. if ( pRawVvdHdr->checksum != pStudioHdr->checksum )
  2716. {
  2717. Warning( "Error Vertex File for '%s' checksum %d should be %d\n", pStudioHdr->pszName(), pRawVvdHdr->checksum, pStudioHdr->checksum );
  2718. return NULL;
  2719. }
  2720. Assert( pRawVvdHdr->numLODs );
  2721. if ( !pRawVvdHdr->numLODs )
  2722. {
  2723. return NULL;
  2724. }
  2725. CTempAllocHelper pOriginalData;
  2726. if ( IsX360() )
  2727. {
  2728. unsigned char *pInput = (unsigned char *)pRawVvdHdr + sizeof( vertexFileHeader_t );
  2729. if ( CLZMA::IsCompressed( pInput ) )
  2730. {
  2731. // vvd arrives compressed, decode and cache the results
  2732. unsigned int nOriginalSize = CLZMA::GetActualSize( pInput );
  2733. pOriginalData.Alloc( sizeof( vertexFileHeader_t ) + nOriginalSize );
  2734. V_memcpy( pOriginalData.Get(), pRawVvdHdr, sizeof( vertexFileHeader_t ) );
  2735. unsigned int nOutputSize = CLZMA::Uncompress( pInput, sizeof( vertexFileHeader_t ) + (unsigned char *)pOriginalData.Get() );
  2736. if ( nOutputSize != nOriginalSize )
  2737. {
  2738. // decoder failure
  2739. return NULL;
  2740. }
  2741. pRawVvdHdr = (vertexFileHeader_t *)pOriginalData.Get();
  2742. }
  2743. }
  2744. bool bNeedsTangentS = IsX360() || (g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 80);
  2745. int rootLOD = min( (int)pStudioHdr->rootLOD, pRawVvdHdr->numLODs - 1 );
  2746. // determine final cache footprint, possibly truncated due to lod
  2747. int cacheLength = Studio_VertexDataSize( pRawVvdHdr, rootLOD, bNeedsTangentS );
  2748. MdlCacheMsg("MDLCache: Alloc VVD %s\n", GetModelName( handle ) );
  2749. // allocate cache space
  2750. MemAlloc_PushAllocDbgInfo( "Models:Vertex data", 0);
  2751. pVvdHdr = (vertexFileHeader_t *)AllocData( MDLCACHE_VERTEXES, cacheLength );
  2752. MemAlloc_PopAllocDbgInfo();
  2753. GetCacheSection( MDLCACHE_VERTEXES )->BeginFrameLocking();
  2754. CacheData( &m_MDLDict[handle]->m_VertexCache, pVvdHdr, cacheLength, pStudioHdr->pszName(), MDLCACHE_VERTEXES, MakeCacheID( handle, MDLCACHE_VERTEXES) );
  2755. // expected 32 byte alignment
  2756. Assert( ((int64)pVvdHdr & 0x1F) == 0 );
  2757. // load minimum vertexes and fixup
  2758. Studio_LoadVertexes( pRawVvdHdr, pVvdHdr, rootLOD, bNeedsTangentS );
  2759. GetCacheSection( MDLCACHE_VERTEXES )->EndFrameLocking();
  2760. return pVvdHdr;
  2761. }
  2762. //-----------------------------------------------------------------------------
  2763. // Load and cache model's specified dynamic data
  2764. //-----------------------------------------------------------------------------
  2765. vertexFileHeader_t *CMDLCache::LoadVertexData( studiohdr_t *pStudioHdr )
  2766. {
  2767. char pFileName[MAX_PATH];
  2768. MDLHandle_t handle;
  2769. Assert( pStudioHdr );
  2770. handle = (MDLHandle_t)(int)pStudioHdr->virtualModel&0xffff;
  2771. Assert( !m_MDLDict[handle]->m_VertexCache );
  2772. studiodata_t *pStudioData = m_MDLDict[handle];
  2773. if ( pStudioData->m_nFlags & STUDIODATA_FLAGS_NO_VERTEX_DATA )
  2774. {
  2775. return NULL;
  2776. }
  2777. int iAsync = GetAsyncInfoIndex( handle, MDLCACHE_VERTEXES );
  2778. if ( iAsync == NO_ASYNC )
  2779. {
  2780. // load the VVD file
  2781. // use model name for correct path
  2782. MakeFilename( handle, ".vvd", pFileName, sizeof(pFileName) );
  2783. if ( IsX360() )
  2784. {
  2785. char pX360Filename[MAX_PATH];
  2786. UpdateOrCreate( pStudioHdr, pFileName, pX360Filename, sizeof( pX360Filename ), "GAME" );
  2787. Q_strncpy( pFileName, pX360Filename, sizeof(pX360Filename) );
  2788. }
  2789. MdlCacheMsg( "MDLCache: Begin load VVD %s\n", pFileName );
  2790. AsyncInfo_t info;
  2791. if ( IsDebug() )
  2792. {
  2793. memset( &info, 0xdd, sizeof( AsyncInfo_t ) );
  2794. }
  2795. info.hModel = handle;
  2796. info.type = MDLCACHE_VERTEXES;
  2797. info.iAnimBlock = 0;
  2798. info.hControl = NULL;
  2799. LoadData( pFileName, "GAME", mod_load_mesh_async.GetBool(), &info.hControl );
  2800. {
  2801. AUTO_LOCK( m_AsyncMutex );
  2802. iAsync = SetAsyncInfoIndex( handle, MDLCACHE_VERTEXES, m_PendingAsyncs.AddToTail( info ) );
  2803. }
  2804. }
  2805. ProcessPendingAsync( iAsync );
  2806. return (vertexFileHeader_t *)CheckData( m_MDLDict[handle]->m_VertexCache, MDLCACHE_VERTEXES );
  2807. }
  2808. //-----------------------------------------------------------------------------
  2809. //
  2810. //-----------------------------------------------------------------------------
  2811. vertexFileHeader_t *CMDLCache::GetVertexData( MDLHandle_t handle )
  2812. {
  2813. if ( mod_test_not_available.GetBool() )
  2814. return NULL;
  2815. if ( mod_test_verts_not_available.GetBool() )
  2816. return NULL;
  2817. return CacheVertexData( GetStudioHdr( handle ) );
  2818. }
  2819. //-----------------------------------------------------------------------------
  2820. // Allocates a cacheable item
  2821. //-----------------------------------------------------------------------------
  2822. void *CMDLCache::AllocData( MDLCacheDataType_t type, int size )
  2823. {
  2824. void *pData = _aligned_malloc( size, 32 );
  2825. if ( !pData )
  2826. {
  2827. Error( "CMDLCache:: Out of memory" );
  2828. return NULL;
  2829. }
  2830. return pData;
  2831. }
  2832. //-----------------------------------------------------------------------------
  2833. // Caches an item
  2834. //-----------------------------------------------------------------------------
  2835. void CMDLCache::CacheData( DataCacheHandle_t *c, void *pData, int size, const char *name, MDLCacheDataType_t type, DataCacheClientID_t id )
  2836. {
  2837. if ( !pData )
  2838. {
  2839. return;
  2840. }
  2841. if ( id == (DataCacheClientID_t)-1 )
  2842. id = (DataCacheClientID_t)pData;
  2843. GetCacheSection( type )->Add(id, pData, size, c );
  2844. }
  2845. //-----------------------------------------------------------------------------
  2846. // returns the cached data, and moves to the head of the LRU list
  2847. // if present, otherwise returns NULL
  2848. //-----------------------------------------------------------------------------
  2849. void *CMDLCache::CheckData( DataCacheHandle_t c, MDLCacheDataType_t type )
  2850. {
  2851. return GetCacheSection( type )->Get( c, true );
  2852. }
  2853. //-----------------------------------------------------------------------------
  2854. // returns the cached data, if present, otherwise returns NULL
  2855. //-----------------------------------------------------------------------------
  2856. void *CMDLCache::CheckDataNoTouch( DataCacheHandle_t c, MDLCacheDataType_t type )
  2857. {
  2858. return GetCacheSection( type )->GetNoTouch( c, true );
  2859. }
  2860. //-----------------------------------------------------------------------------
  2861. // Frees a cache item
  2862. //-----------------------------------------------------------------------------
  2863. void CMDLCache::UncacheData( DataCacheHandle_t c, MDLCacheDataType_t type, bool bLockedOk )
  2864. {
  2865. if ( c == DC_INVALID_HANDLE )
  2866. return;
  2867. if ( !GetCacheSection( type )->IsPresent( c ) )
  2868. return;
  2869. if ( GetCacheSection( type )->BreakLock( c ) && !bLockedOk )
  2870. {
  2871. DevMsg( "Warning: freed a locked resource\n" );
  2872. Assert( 0 );
  2873. }
  2874. const void *pItemData;
  2875. GetCacheSection( type )->Remove( c, &pItemData );
  2876. FreeData( type, (void *)pItemData );
  2877. }
  2878. //-----------------------------------------------------------------------------
  2879. // Frees memory for an item
  2880. //-----------------------------------------------------------------------------
  2881. void CMDLCache::FreeData( MDLCacheDataType_t type, void *pData )
  2882. {
  2883. if ( type != MDLCACHE_ANIMBLOCK )
  2884. {
  2885. _aligned_free( (void *)pData );
  2886. }
  2887. else
  2888. {
  2889. g_pFullFileSystem->FreeOptimalReadBuffer( pData );
  2890. }
  2891. }
  2892. void CMDLCache::InitPreloadData( bool rebuild )
  2893. {
  2894. }
  2895. void CMDLCache::ShutdownPreloadData()
  2896. {
  2897. }
  2898. //-----------------------------------------------------------------------------
  2899. // Work function for processing a model delivered by the queued loader.
  2900. // ProcessDataIntoCache() is invoked for each MDL datum.
  2901. //-----------------------------------------------------------------------------
  2902. void CMDLCache::ProcessQueuedData( ModelParts_t *pModelParts, bool bHeaderOnly )
  2903. {
  2904. void *pData;
  2905. int nSize;
  2906. // the studiohdr is critical, ensure it's setup as expected
  2907. MDLHandle_t handle = pModelParts->hMDL;
  2908. studiohdr_t *pStudioHdr = NULL;
  2909. if ( !pModelParts->bHeaderLoaded && ( pModelParts->nLoadedParts & ( 1 << ModelParts_t::BUFFER_MDL ) ) )
  2910. {
  2911. DEBUG_SCOPE_TIMER(mdl);
  2912. pData = pModelParts->Buffers[ModelParts_t::BUFFER_MDL].Base();
  2913. nSize = pModelParts->Buffers[ModelParts_t::BUFFER_MDL].TellMaxPut();
  2914. ProcessDataIntoCache( handle, MDLCACHE_STUDIOHDR, 0, pData, nSize, nSize != 0 );
  2915. LockStudioHdr( handle );
  2916. g_pFullFileSystem->FreeOptimalReadBuffer( pData );
  2917. pModelParts->bHeaderLoaded = true;
  2918. }
  2919. if ( bHeaderOnly )
  2920. {
  2921. return;
  2922. }
  2923. bool bAbort = false;
  2924. pStudioHdr = (studiohdr_t *)CheckDataNoTouch( m_MDLDict[handle]->m_MDLCache, MDLCACHE_STUDIOHDR );
  2925. if ( !pStudioHdr )
  2926. {
  2927. // The header is expected to be loaded and locked, everything depends on it!
  2928. // but if the async read fails, we might not have it
  2929. //Assert( 0 );
  2930. DevWarning( "CMDLCache:: Error MDLCACHE_STUDIOHDR not present for '%s'\n", GetModelName( handle ) );
  2931. // cannot unravel any of this model's dependant data, abort any further processing
  2932. bAbort = true;
  2933. }
  2934. if ( pModelParts->nLoadedParts & ( 1 << ModelParts_t::BUFFER_PHY ) )
  2935. {
  2936. DEBUG_SCOPE_TIMER(phy);
  2937. // regardless of error, call job callback so caller can do cleanup of their context
  2938. pData = pModelParts->Buffers[ModelParts_t::BUFFER_PHY].Base();
  2939. nSize = bAbort ? 0 : pModelParts->Buffers[ModelParts_t::BUFFER_PHY].TellMaxPut();
  2940. ProcessDataIntoCache( handle, MDLCACHE_VCOLLIDE, 0, pData, nSize, nSize != 0 );
  2941. g_pFullFileSystem->FreeOptimalReadBuffer( pData );
  2942. }
  2943. // vvd vertexes before vtx
  2944. if ( pModelParts->nLoadedParts & ( 1 << ModelParts_t::BUFFER_VVD ) )
  2945. {
  2946. DEBUG_SCOPE_TIMER(vvd);
  2947. pData = pModelParts->Buffers[ModelParts_t::BUFFER_VVD].Base();
  2948. nSize = bAbort ? 0 : pModelParts->Buffers[ModelParts_t::BUFFER_VVD].TellMaxPut();
  2949. ProcessDataIntoCache( handle, MDLCACHE_VERTEXES, 0, pData, nSize, nSize != 0 );
  2950. g_pFullFileSystem->FreeOptimalReadBuffer( pData );
  2951. }
  2952. // can construct meshes after vvd and vtx vertexes arrive
  2953. if ( pModelParts->nLoadedParts & ( 1 << ModelParts_t::BUFFER_VTX ) )
  2954. {
  2955. DEBUG_SCOPE_TIMER(vtx);
  2956. pData = pModelParts->Buffers[ModelParts_t::BUFFER_VTX].Base();
  2957. nSize = bAbort ? 0 : pModelParts->Buffers[ModelParts_t::BUFFER_VTX].TellMaxPut();
  2958. // ProcessDataIntoCache() will do an unlock, so lock
  2959. studiodata_t *pStudioData = m_MDLDict[handle];
  2960. GetCacheSection( MDLCACHE_STUDIOHWDATA )->Lock( pStudioData->m_VertexCache );
  2961. {
  2962. // constructing the static meshes isn't thread safe
  2963. AUTO_LOCK( m_QueuedLoadingMutex );
  2964. ProcessDataIntoCache( handle, MDLCACHE_STUDIOHWDATA, 0, pData, nSize, nSize != 0 );
  2965. }
  2966. g_pFullFileSystem->FreeOptimalReadBuffer( pData );
  2967. }
  2968. UnlockStudioHdr( handle );
  2969. delete pModelParts;
  2970. }
  2971. //-----------------------------------------------------------------------------
  2972. // Journals each of the incoming MDL components until all arrive (or error).
  2973. // Not all components exist, but that information is not known at job submission.
  2974. //-----------------------------------------------------------------------------
  2975. void CMDLCache::QueuedLoaderCallback_MDL( void *pContext, void *pContext2, const void *pData, int nSize, LoaderError_t loaderError )
  2976. {
  2977. // validity is denoted by a nonzero buffer
  2978. nSize = ( loaderError == LOADERERROR_NONE ) ? nSize : 0;
  2979. // journal each incoming buffer
  2980. ModelParts_t *pModelParts = (ModelParts_t *)pContext;
  2981. ModelParts_t::BufferType_t bufferType = static_cast< ModelParts_t::BufferType_t >((int)pContext2);
  2982. pModelParts->Buffers[bufferType].SetExternalBuffer( (void *)pData, nSize, nSize, CUtlBuffer::READ_ONLY );
  2983. pModelParts->nLoadedParts += (1 << bufferType);
  2984. // wait for all components
  2985. if ( pModelParts->DoFinalProcessing() )
  2986. {
  2987. if ( !IsPC() )
  2988. {
  2989. // now have all components, process the raw data into the cache
  2990. g_MDLCache.ProcessQueuedData( pModelParts );
  2991. }
  2992. else
  2993. {
  2994. // PC background load path. pull in material dependencies on the fly.
  2995. Assert( ThreadInMainThread() );
  2996. g_MDLCache.ProcessQueuedData( pModelParts, true );
  2997. // preload all possible paths to VMTs
  2998. {
  2999. DEBUG_SCOPE_TIMER(findvmt);
  3000. MaterialLock_t hMatLock = materials->Lock();
  3001. if ( studiohdr_t * pHdr = g_MDLCache.GetStudioHdr( pModelParts->hMDL ) )
  3002. {
  3003. if ( !(pHdr->flags & STUDIOHDR_FLAGS_OBSOLETE) )
  3004. {
  3005. char buf[MAX_PATH];
  3006. V_strcpy( buf, "materials/" );
  3007. int prefixLen = V_strlen( buf );
  3008. for ( int t = 0; t < pHdr->numtextures; ++t )
  3009. {
  3010. // XXX this does not take remaps from vtxdata into account;
  3011. // right now i am not caring about that. we will hitch if any
  3012. // LODs remap to materials that are not in the header. (henryg)
  3013. const char *pTexture = pHdr->pTexture(t)->pszName();
  3014. pTexture += ( pTexture[0] == CORRECT_PATH_SEPARATOR || pTexture[0] == INCORRECT_PATH_SEPARATOR );
  3015. for ( int cd = 0; cd < pHdr->numcdtextures; ++cd )
  3016. {
  3017. const char *pCdTexture = pHdr->pCdtexture( cd );
  3018. pCdTexture += ( pCdTexture[0] == CORRECT_PATH_SEPARATOR || pCdTexture[0] == INCORRECT_PATH_SEPARATOR );
  3019. V_ComposeFileName( pCdTexture, pTexture, buf + prefixLen, MAX_PATH - prefixLen );
  3020. V_strncat( buf, ".vmt", MAX_PATH, COPY_ALL_CHARACTERS );
  3021. pModelParts->bMaterialsPending = true;
  3022. const char *pbuf = buf;
  3023. g_pFullFileSystem->AddFilesToFileCache( pModelParts->hFileCache, &pbuf, 1, "GAME" );
  3024. if ( materials->IsMaterialLoaded( buf + prefixLen ) )
  3025. {
  3026. // found a loaded one. still cache it in case it unloads,
  3027. // but we can stop adding more potential paths to the cache
  3028. // since this one is known to be valid.
  3029. break;
  3030. }
  3031. }
  3032. }
  3033. }
  3034. }
  3035. materials->Unlock(hMatLock);
  3036. }
  3037. // queue functor which will start polling every frame by re-queuing itself
  3038. g_pQueuedLoader->QueueDynamicLoadFunctor( CreateFunctor( ProcessDynamicLoad, pModelParts ) );
  3039. }
  3040. }
  3041. }
  3042. void CMDLCache::ProcessDynamicLoad( ModelParts_t *pModelParts )
  3043. {
  3044. Assert( IsPC() && ThreadInMainThread() );
  3045. if ( !g_pFullFileSystem->IsFileCacheLoaded( pModelParts->hFileCache ) )
  3046. {
  3047. // poll again next frame...
  3048. g_pQueuedLoader->QueueDynamicLoadFunctor( CreateFunctor( ProcessDynamicLoad, pModelParts ) );
  3049. return;
  3050. }
  3051. if ( pModelParts->bMaterialsPending )
  3052. {
  3053. DEBUG_SCOPE_TIMER(processvmt);
  3054. pModelParts->bMaterialsPending = false;
  3055. pModelParts->bTexturesPending = true;
  3056. MaterialLock_t hMatLock = materials->Lock();
  3057. materials->SetAsyncTextureLoadCache( pModelParts->hFileCache );
  3058. // Load all the materials
  3059. studiohdr_t * pHdr = g_MDLCache.GetStudioHdr( pModelParts->hMDL );
  3060. if ( pHdr && !(pHdr->flags & STUDIOHDR_FLAGS_OBSOLETE) )
  3061. {
  3062. // build strings inside a buffer that already contains a materials/ prefix
  3063. char buf[MAX_PATH];
  3064. V_strcpy( buf, "materials/" );
  3065. int prefixLen = V_strlen( buf );
  3066. // XXX this does not take remaps from vtxdata into account;
  3067. // right now i am not caring about that. we will hitch if any
  3068. // LODs remap to materials that are not in the header. (henryg)
  3069. for ( int t = 0; t < pHdr->numtextures; ++t )
  3070. {
  3071. const char *pTexture = pHdr->pTexture(t)->pszName();
  3072. pTexture += ( pTexture[0] == CORRECT_PATH_SEPARATOR || pTexture[0] == INCORRECT_PATH_SEPARATOR );
  3073. for ( int cd = 0; cd < pHdr->numcdtextures; ++cd )
  3074. {
  3075. const char *pCdTexture = pHdr->pCdtexture( cd );
  3076. pCdTexture += ( pCdTexture[0] == CORRECT_PATH_SEPARATOR || pCdTexture[0] == INCORRECT_PATH_SEPARATOR );
  3077. V_ComposeFileName( pCdTexture, pTexture, buf + prefixLen, MAX_PATH - prefixLen );
  3078. IMaterial* pMaterial = materials->FindMaterial( buf + prefixLen, TEXTURE_GROUP_MODEL, false );
  3079. if ( !IsErrorMaterial( pMaterial ) && !pMaterial->IsPrecached() )
  3080. {
  3081. pModelParts->Materials.AddToTail( pMaterial );
  3082. pMaterial->IncrementReferenceCount();
  3083. // Force texture loads while material system is set to capture
  3084. // them and redirect to an error texture... this will populate
  3085. // the file cache with all the requested textures
  3086. pMaterial->RefreshPreservingMaterialVars();
  3087. break;
  3088. }
  3089. }
  3090. }
  3091. }
  3092. materials->SetAsyncTextureLoadCache( NULL );
  3093. materials->Unlock( hMatLock );
  3094. // poll again next frame... dont want to do too much work right now
  3095. g_pQueuedLoader->QueueDynamicLoadFunctor( CreateFunctor( ProcessDynamicLoad, pModelParts ) );
  3096. return;
  3097. }
  3098. if ( pModelParts->bTexturesPending )
  3099. {
  3100. DEBUG_SCOPE_TIMER(matrefresh);
  3101. pModelParts->bTexturesPending = false;
  3102. // Perform the real material loads now while raw texture files are cached.
  3103. FOR_EACH_VEC( pModelParts->Materials, i )
  3104. {
  3105. IMaterial* pMaterial = pModelParts->Materials[i];
  3106. if ( !IsErrorMaterial( pMaterial ) && pMaterial->IsPrecached() )
  3107. {
  3108. // Do a full reload to get the correct textures and computed flags
  3109. pMaterial->Refresh();
  3110. }
  3111. }
  3112. // poll again next frame... dont want to do too much work right now
  3113. g_pQueuedLoader->QueueDynamicLoadFunctor( CreateFunctor( ProcessDynamicLoad, pModelParts ) );
  3114. return;
  3115. }
  3116. // done. finish and clean up.
  3117. Assert( !pModelParts->bTexturesPending && !pModelParts->bMaterialsPending );
  3118. // pull out cached items we want to overlap with final processing
  3119. CleanupModelParts_t *pCleanup = new CleanupModelParts_t;
  3120. pCleanup->hFileCache = pModelParts->hFileCache;
  3121. pCleanup->Materials.Swap( pModelParts->Materials );
  3122. g_pQueuedLoader->QueueCleanupDynamicLoadFunctor( CreateFunctor( CleanupDynamicLoad, pCleanup ) );
  3123. {
  3124. DEBUG_SCOPE_TIMER(processall);
  3125. g_MDLCache.ProcessQueuedData( pModelParts ); // pModelParts is deleted here
  3126. }
  3127. }
  3128. void CMDLCache::CleanupDynamicLoad( CleanupModelParts_t *pCleanup )
  3129. {
  3130. Assert( IsPC() && ThreadInMainThread() );
  3131. // remove extra material refs, unload cached files
  3132. FOR_EACH_VEC( pCleanup->Materials, i )
  3133. {
  3134. pCleanup->Materials[i]->DecrementReferenceCount();
  3135. }
  3136. g_pFullFileSystem->DestroyFileCache( pCleanup->hFileCache );
  3137. delete pCleanup;
  3138. }
  3139. //-----------------------------------------------------------------------------
  3140. // Build a queued loader job to get the MDL ant all of its components into the cache.
  3141. //-----------------------------------------------------------------------------
  3142. bool CMDLCache::PreloadModel( MDLHandle_t handle )
  3143. {
  3144. if ( g_pQueuedLoader->IsDynamic() == false )
  3145. {
  3146. if ( !IsX360() )
  3147. {
  3148. return false;
  3149. }
  3150. if ( !g_pQueuedLoader->IsMapLoading() || handle == MDLHANDLE_INVALID )
  3151. {
  3152. return false;
  3153. }
  3154. }
  3155. if ( !g_pQueuedLoader->IsBatching() )
  3156. {
  3157. // batching must be active, following code depends on its behavior
  3158. DevWarning( "CMDLCache:: Late preload of model '%s'\n", GetModelName( handle ) );
  3159. return false;
  3160. }
  3161. // determine existing presence
  3162. // actual necessity is not established here, allowable absent files need their i/o error to occur
  3163. bool bNeedsMDL = !IsDataLoaded( handle, MDLCACHE_STUDIOHDR );
  3164. bool bNeedsVTX = !IsDataLoaded( handle, MDLCACHE_STUDIOHWDATA );
  3165. bool bNeedsVVD = !IsDataLoaded( handle, MDLCACHE_VERTEXES );
  3166. bool bNeedsPHY = !IsDataLoaded( handle, MDLCACHE_VCOLLIDE );
  3167. if ( !bNeedsMDL && !bNeedsVTX && !bNeedsVVD && !bNeedsPHY )
  3168. {
  3169. // nothing to do
  3170. return true;
  3171. }
  3172. char szFilename[MAX_PATH];
  3173. char szNameOnDisk[MAX_PATH];
  3174. V_strncpy( szFilename, GetActualModelName( handle ), sizeof( szFilename ) );
  3175. V_StripExtension( szFilename, szFilename, sizeof( szFilename ) );
  3176. // need to gather all model parts (mdl, vtx, vvd, phy, ani)
  3177. ModelParts_t *pModelParts = new ModelParts_t;
  3178. pModelParts->hMDL = handle;
  3179. pModelParts->hFileCache = g_pFullFileSystem->CreateFileCache();
  3180. // create multiple loader jobs to perform gathering i/o operations
  3181. LoaderJob_t loaderJob;
  3182. loaderJob.m_pPathID = "GAME";
  3183. loaderJob.m_pCallback = QueuedLoaderCallback_MDL;
  3184. loaderJob.m_pContext = (void *)pModelParts;
  3185. loaderJob.m_Priority = LOADERPRIORITY_DURINGPRELOAD;
  3186. loaderJob.m_bPersistTargetData = true;
  3187. if ( bNeedsMDL )
  3188. {
  3189. V_snprintf( szNameOnDisk, sizeof( szNameOnDisk ), "%s%s.mdl", szFilename, GetPlatformExt() );
  3190. loaderJob.m_pFilename = szNameOnDisk;
  3191. loaderJob.m_pContext2 = (void *)ModelParts_t::BUFFER_MDL;
  3192. g_pQueuedLoader->AddJob( &loaderJob );
  3193. pModelParts->nExpectedParts |= 1 << ModelParts_t::BUFFER_MDL;
  3194. }
  3195. if ( bNeedsVTX )
  3196. {
  3197. // vtx extensions are .xxx.vtx, need to re-form as, ???.xxx.yyy.vtx
  3198. char szTempName[MAX_PATH];
  3199. V_snprintf( szNameOnDisk, sizeof( szNameOnDisk ), "%s%s", szFilename, GetVTXExtension() );
  3200. V_StripExtension( szNameOnDisk, szTempName, sizeof( szTempName ) );
  3201. V_snprintf( szNameOnDisk, sizeof( szNameOnDisk ), "%s%s.vtx", szTempName, GetPlatformExt() );
  3202. loaderJob.m_pFilename = szNameOnDisk;
  3203. loaderJob.m_pContext2 = (void *)ModelParts_t::BUFFER_VTX;
  3204. g_pQueuedLoader->AddJob( &loaderJob );
  3205. pModelParts->nExpectedParts |= 1 << ModelParts_t::BUFFER_VTX;
  3206. }
  3207. if ( bNeedsVVD )
  3208. {
  3209. V_snprintf( szNameOnDisk, sizeof( szNameOnDisk ), "%s%s.vvd", szFilename, GetPlatformExt() );
  3210. loaderJob.m_pFilename = szNameOnDisk;
  3211. loaderJob.m_pContext2 = (void *)ModelParts_t::BUFFER_VVD;
  3212. g_pQueuedLoader->AddJob( &loaderJob );
  3213. pModelParts->nExpectedParts |= 1 << ModelParts_t::BUFFER_VVD;
  3214. }
  3215. if ( bNeedsPHY )
  3216. {
  3217. V_snprintf( szNameOnDisk, sizeof( szNameOnDisk ), "%s%s.phy", szFilename, GetPlatformExt() );
  3218. loaderJob.m_pFilename = szNameOnDisk;
  3219. loaderJob.m_pContext2 = (void *)ModelParts_t::BUFFER_PHY;
  3220. g_pQueuedLoader->AddJob( &loaderJob );
  3221. pModelParts->nExpectedParts |= 1 << ModelParts_t::BUFFER_PHY;
  3222. }
  3223. if ( !pModelParts->nExpectedParts )
  3224. {
  3225. g_pFullFileSystem->DestroyFileCache( pModelParts->hFileCache );
  3226. delete pModelParts;
  3227. }
  3228. return true;
  3229. }
  3230. //-----------------------------------------------------------------------------
  3231. // Purpose: Clear the STUDIODATA_ERROR_MODEL flag.
  3232. //-----------------------------------------------------------------------------
  3233. void CMDLCache::ResetErrorModelStatus( MDLHandle_t handle )
  3234. {
  3235. if ( handle == MDLHANDLE_INVALID )
  3236. return;
  3237. m_MDLDict[handle]->m_nFlags &= ~STUDIODATA_ERROR_MODEL;
  3238. }
  3239. //-----------------------------------------------------------------------------
  3240. //
  3241. //-----------------------------------------------------------------------------
  3242. void CMDLCache::MarkFrame()
  3243. {
  3244. ProcessPendingAsyncs();
  3245. }
  3246. //-----------------------------------------------------------------------------
  3247. // Purpose: bind studiohdr_t support functions to the mdlcacher
  3248. //-----------------------------------------------------------------------------
  3249. const studiohdr_t *studiohdr_t::FindModel( void **cache, char const *pModelName ) const
  3250. {
  3251. MDLHandle_t handle = g_MDLCache.FindMDL( pModelName );
  3252. *cache = (void*)(uintp)handle;
  3253. return g_MDLCache.GetStudioHdr( handle );
  3254. }
  3255. virtualmodel_t *studiohdr_t::GetVirtualModel( void ) const
  3256. {
  3257. if (numincludemodels == 0)
  3258. return NULL;
  3259. return g_MDLCache.GetVirtualModelFast( this, (MDLHandle_t)(int)virtualModel&0xffff );
  3260. }
  3261. byte *studiohdr_t::GetAnimBlock( int i ) const
  3262. {
  3263. return g_MDLCache.GetAnimBlock( (MDLHandle_t)(int)virtualModel&0xffff, i );
  3264. }
  3265. int studiohdr_t::GetAutoplayList( unsigned short **pOut ) const
  3266. {
  3267. return g_MDLCache.GetAutoplayList( (MDLHandle_t)(int)virtualModel&0xffff, pOut );
  3268. }
  3269. const studiohdr_t *virtualgroup_t::GetStudioHdr( void ) const
  3270. {
  3271. return g_MDLCache.GetStudioHdr( (MDLHandle_t)(int)cache&0xffff );
  3272. }