//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $Workfile: $ // $Date: $ // $NoKeywords: $ //=============================================================================// #ifndef IDATACACHE_H #define IDATACACHE_H #ifdef _WIN32 #pragma once #endif #include "tier0/dbg.h" #include "appframework/IAppSystem.h" class IDataCache; //----------------------------------------------------------------------------- // // Shared Data Cache API // //----------------------------------------------------------------------------- #define DATACACHE_INTERFACE_VERSION "VDataCache003" //----------------------------------------------------------------------------- // Support types and enums //----------------------------------------------------------------------------- //--------------------------------------------------------- // Unique (per section) identifier for a cache item defined by client //--------------------------------------------------------- typedef uint32 DataCacheClientID_t; //--------------------------------------------------------- // Cache-defined handle for a cache item //--------------------------------------------------------- FORWARD_DECLARE_HANDLE( memhandle_t ); typedef memhandle_t DataCacheHandle_t; #define DC_INVALID_HANDLE ((DataCacheHandle_t)0) //--------------------------------------------------------- // Cache Limits //--------------------------------------------------------- struct DataCacheLimits_t { DataCacheLimits_t( unsigned _nMaxBytes = (unsigned)-1, unsigned _nMaxItems = (unsigned)-1, unsigned _nMinBytes = 0, unsigned _nMinItems = 0 ) : nMaxBytes(_nMaxBytes), nMaxItems(_nMaxItems), nMinBytes(_nMinBytes), nMinItems(_nMinItems) { } // Maximum levels permitted unsigned nMaxBytes; unsigned nMaxItems; // Minimum levels permitted unsigned nMinBytes; unsigned nMinItems; }; //--------------------------------------------------------- // Cache status //--------------------------------------------------------- struct DataCacheStatus_t { // Current state of the cache unsigned nBytes; unsigned nItems; unsigned nBytesLocked; unsigned nItemsLocked; // Diagnostics unsigned nFindRequests; unsigned nFindHits; }; //--------------------------------------------------------- // Cache options //--------------------------------------------------------- enum DataCacheOptions_t { DC_TRACE_ACTIVITY = (1 << 0), DC_FORCE_RELOCATE = (1 << 1), DC_ALWAYS_MISS = (1 << 2), DC_VALIDATE = (1 << 3), }; //--------------------------------------------------------- // Cache report types //--------------------------------------------------------- enum DataCacheReportType_t { DC_SUMMARY_REPORT, DC_DETAIL_REPORT, DC_DETAIL_REPORT_LRU, }; //--------------------------------------------------------- // Notifications to section clients on cache events //--------------------------------------------------------- enum DataCacheNotificationType_t { // Used internally to prohibit notifications DC_NONE, // Item is falling off the LRU and should be deleted, return false to block DC_AGE_DISCARD, // Item is being explicitly flushed and should be deleted, return false to block DC_FLUSH_DISCARD, // Item is being explicitly removed and should be deleted. Failure is not an option DC_REMOVED, // Cache is requesting item be relocated for debugging purposes DC_RELOCATE, // Item info should be output to console, return false to accept default handling DC_PRINT_INF0, }; //------------------------------------- struct DataCacheNotification_t { DataCacheNotificationType_t type; const char * pszSectionName; DataCacheClientID_t clientId; const void * pItemData; unsigned nItemSize; }; //--------------------------------------------------------- const int DC_MAX_CLIENT_NAME = 15; const int DC_MAX_ITEM_NAME = 511; //--------------------------------------------------------- // Result codes //--------------------------------------------------------- enum DataCacheRemoveResult_t { DC_OK, DC_NOT_FOUND, DC_LOCKED, }; //--------------------------------------------------------- // Add flags //--------------------------------------------------------- enum DataCacheAddFlags_t { DCAF_LOCK = ( 1 << 0 ), DCAF_DEFAULT = 0, }; //----------------------------------------------------------------------------- // IDataCacheSection // // Purpose: Implements a sub-section of the global cache. Subsections are // areas of the cache with thier own memory constraints and common // management. //----------------------------------------------------------------------------- abstract_class IDataCacheSection { public: //-------------------------------------------------------- virtual IDataCache *GetSharedCache() = 0; virtual const char *GetName() = 0; //-------------------------------------------------------- // Purpose: Controls cache size & options //-------------------------------------------------------- virtual void SetLimits( const DataCacheLimits_t &limits ) = 0; virtual void SetOptions( unsigned options ) = 0; //-------------------------------------------------------- // Purpose: Get the current state of the section //-------------------------------------------------------- virtual void GetStatus( DataCacheStatus_t *pStatus, DataCacheLimits_t *pLimits = NULL ) = 0; //-------------------------------------------------------- // Purpose: Add an item to the cache. Purges old items if over budget, returns false if item was already in cache. //-------------------------------------------------------- virtual void EnsureCapacity( unsigned nBytes, unsigned nItems = 1 ) = 0; //-------------------------------------------------------- // Purpose: Add an item to the cache. Purges old items if over budget, returns false if item was already in cache. //-------------------------------------------------------- virtual bool Add( DataCacheClientID_t clientId, const void *pItemData, unsigned size, DataCacheHandle_t *pHandle ) = 0; //-------------------------------------------------------- // Purpose: Finds an item in the cache, returns NULL if item is not in cache. Not a cheap operation if section not configured for fast find. //-------------------------------------------------------- virtual DataCacheHandle_t Find( DataCacheClientID_t clientId ) = 0; //-------------------------------------------------------- // Purpose: Get an item out of the cache and remove it. No callbacks are executed unless explicity specified. //-------------------------------------------------------- virtual DataCacheRemoveResult_t Remove( DataCacheHandle_t handle, const void **ppItemData, unsigned *pItemSize = NULL, bool bNotify = false ) = 0; DataCacheRemoveResult_t Remove( DataCacheHandle_t handle, bool bNotify = false ) { return Remove( handle, NULL, NULL, bNotify ); } //-------------------------------------------------------- // Purpose: Returns if the data is currently in memory, but does *not* change its location in the LRU //-------------------------------------------------------- virtual bool IsPresent( DataCacheHandle_t handle ) = 0; //-------------------------------------------------------- // Purpose: Lock an item in the cache, returns NULL if item is not in the cache. //-------------------------------------------------------- virtual void *Lock( DataCacheHandle_t handle ) = 0; //-------------------------------------------------------- // Purpose: Unlock a previous lock. //-------------------------------------------------------- virtual int Unlock( DataCacheHandle_t handle ) = 0; //-------------------------------------------------------- // Purpose: Get an item without locking it, returns NULL if item is not in the cache. Use with care! //-------------------------------------------------------- virtual void *Get( DataCacheHandle_t handle, bool bFrameLock = false ) = 0; virtual void *GetNoTouch( DataCacheHandle_t handle, bool bFrameLock = false ) = 0; //-------------------------------------------------------- // Purpose: "Frame locking" (not game frame). A crude way to manage locks over relatively // short periods. Does not affect normal locks/unlocks //-------------------------------------------------------- virtual int BeginFrameLocking() = 0; virtual bool IsFrameLocking() = 0; virtual void *FrameLock( DataCacheHandle_t handle ) = 0; virtual int EndFrameLocking() = 0; virtual int *GetFrameUnlockCounterPtr() = 0; //-------------------------------------------------------- // Purpose: Lock management, not for the feint of heart //-------------------------------------------------------- virtual int GetLockCount( DataCacheHandle_t handle ) = 0; virtual int BreakLock( DataCacheHandle_t handle ) = 0; //-------------------------------------------------------- // Purpose: Explicitly mark an item as "recently used" //-------------------------------------------------------- virtual bool Touch( DataCacheHandle_t handle ) = 0; //-------------------------------------------------------- // Purpose: Explicitly mark an item as "least recently used". //-------------------------------------------------------- virtual bool Age( DataCacheHandle_t handle ) = 0; //-------------------------------------------------------- // Purpose: Empty the cache. Returns bytes released, will remove locked items if force specified //-------------------------------------------------------- virtual unsigned Flush( bool bUnlockedOnly = true, bool bNotify = true ) = 0; //-------------------------------------------------------- // Purpose: Dump the oldest items to free the specified amount of memory. Returns amount actually freed //-------------------------------------------------------- virtual unsigned Purge( unsigned nBytes ) = 0; //-------------------------------------------------------- // Purpose: Output the state of the section //-------------------------------------------------------- virtual void OutputReport( DataCacheReportType_t reportType = DC_SUMMARY_REPORT ) = 0; //-------------------------------------------------------- // Purpose: Updates the size used by a specific item (locks the item, kicks // other items out to make room as necessary, unlocks the item). //-------------------------------------------------------- virtual void UpdateSize( DataCacheHandle_t handle, unsigned int nNewSize ) = 0; //-------------------------------------------------------- // Purpose: Access to the mutex. More explicit control during get-then-lock sequences // to ensure object stays valid during "then" //-------------------------------------------------------- virtual void LockMutex() = 0; virtual void UnlockMutex() = 0; //-------------------------------------------------------- // Purpose: Add an item to the cache. Purges old items if over budget, returns false if item was already in cache. //-------------------------------------------------------- virtual bool AddEx( DataCacheClientID_t clientId, const void *pItemData, unsigned size, unsigned flags, DataCacheHandle_t *pHandle ) = 0; }; //----------------------------------------------------------------------------- // IDataCacheClient // // Purpose: Connection between the cache and the owner of a cache section // //----------------------------------------------------------------------------- abstract_class IDataCacheClient { public: //-------------------------------------------------------- // //-------------------------------------------------------- virtual bool HandleCacheNotification( const DataCacheNotification_t ¬ification ) = 0; //-------------------------------------------------------- // //-------------------------------------------------------- virtual bool GetItemName( DataCacheClientID_t clientId, const void *pItem, char *pDest, unsigned nMaxLen ) = 0; }; //------------------------------------- class CDefaultDataCacheClient : public IDataCacheClient { public: virtual bool HandleCacheNotification( const DataCacheNotification_t ¬ification ) { switch ( notification.type ) { case DC_AGE_DISCARD: case DC_FLUSH_DISCARD: case DC_REMOVED: default: Assert ( 0 ); return false; } return false; } virtual bool GetItemName( DataCacheClientID_t clientId, const void *pItem, char *pDest, unsigned nMaxLen ) { return false; } }; //----------------------------------------------------------------------------- // IDataCache // // Purpose: The global shared cache. Manages sections and overall budgets. // //----------------------------------------------------------------------------- abstract_class IDataCache : public IAppSystem { public: //-------------------------------------------------------- // Purpose: Controls cache size. //-------------------------------------------------------- virtual void SetSize( int nMaxBytes ) = 0; virtual void SetOptions( unsigned options ) = 0; virtual void SetSectionLimits( const char *pszSectionName, const DataCacheLimits_t &limits ) = 0; //-------------------------------------------------------- // Purpose: Get the current state of the cache //-------------------------------------------------------- virtual void GetStatus( DataCacheStatus_t *pStatus, DataCacheLimits_t *pLimits = NULL ) = 0; //-------------------------------------------------------- // Purpose: Add a section to the cache //-------------------------------------------------------- virtual IDataCacheSection *AddSection( IDataCacheClient *pClient, const char *pszSectionName, const DataCacheLimits_t &limits = DataCacheLimits_t(), bool bSupportFastFind = false ) = 0; //-------------------------------------------------------- // Purpose: Remove a section from the cache //-------------------------------------------------------- virtual void RemoveSection( const char *pszClientName, bool bCallFlush = true ) = 0; void RemoveSection( IDataCacheSection *pSection, bool bCallFlush = true ) { if ( pSection) RemoveSection( pSection->GetName() ); } //-------------------------------------------------------- // Purpose: Find a section of the cache //-------------------------------------------------------- virtual IDataCacheSection *FindSection( const char *pszClientName ) = 0; //-------------------------------------------------------- // Purpose: Dump the oldest items to free the specified amount of memory. Returns amount actually freed //-------------------------------------------------------- virtual unsigned Purge( unsigned nBytes ) = 0; //-------------------------------------------------------- // Purpose: Empty the cache. Returns bytes released, will remove locked items if force specified //-------------------------------------------------------- virtual unsigned Flush( bool bUnlockedOnly = true, bool bNotify = true ) = 0; //-------------------------------------------------------- // Purpose: Output the state of the cache //-------------------------------------------------------- virtual void OutputReport( DataCacheReportType_t reportType = DC_SUMMARY_REPORT, const char *pszSection = NULL ) = 0; }; //----------------------------------------------------------------------------- // Helper class to support usage pattern similar to CDataManager //----------------------------------------------------------------------------- template< class STORAGE_TYPE, class CREATE_PARAMS, class LOCK_TYPE = STORAGE_TYPE * > class CManagedDataCacheClient : public CDefaultDataCacheClient { public: typedef CManagedDataCacheClient CCacheClientBaseClass; CManagedDataCacheClient() : m_pCache( NULL ) { } void Init( IDataCache *pSharedCache, const char *pszSectionName, const DataCacheLimits_t &limits = DataCacheLimits_t(), bool bSupportFastFind = false ) { if ( !m_pCache ) { m_pCache = pSharedCache->AddSection( this, pszSectionName, limits, bSupportFastFind ); } } void Shutdown() { if ( m_pCache ) { m_pCache->GetSharedCache()->RemoveSection( m_pCache ); m_pCache = NULL; } } LOCK_TYPE CacheGet( DataCacheHandle_t handle, bool bFrameLock = true ) { return (LOCK_TYPE)(((STORAGE_TYPE *)m_pCache->Get( handle, bFrameLock ))->GetData()); } LOCK_TYPE CacheGetNoTouch( DataCacheHandle_t handle ) { return (LOCK_TYPE)(((STORAGE_TYPE *)m_pCache->GetNoTouch( handle ))->GetData()); } LOCK_TYPE CacheLock( DataCacheHandle_t handle ) { return (LOCK_TYPE)(((STORAGE_TYPE *)m_pCache->Lock( handle ))->GetData()); } int CacheUnlock( DataCacheHandle_t handle ) { return m_pCache->Unlock( handle ); } void CacheTouch( DataCacheHandle_t handle ) { m_pCache->Touch( handle ); } void CacheRemove( DataCacheHandle_t handle, bool bNotify = true ) { m_pCache->Remove( handle, bNotify ); } void CacheFlush() { m_pCache->Flush(); } DataCacheHandle_t CacheCreate( const CREATE_PARAMS &createParams, unsigned flags = DCAF_DEFAULT ) { m_pCache->EnsureCapacity(STORAGE_TYPE::EstimatedSize(createParams)); STORAGE_TYPE *pStore = STORAGE_TYPE::CreateResource( createParams ); DataCacheHandle_t handle; m_pCache->AddEx( (DataCacheClientID_t)pStore, pStore, pStore->Size(), flags, &handle); return handle; } void CacheLockMutex() { m_pCache->LockMutex(); } void CacheUnlockMutex() { m_pCache->UnlockMutex(); } bool HandleCacheNotification( const DataCacheNotification_t ¬ification ) { switch ( notification.type ) { case DC_AGE_DISCARD: case DC_FLUSH_DISCARD: case DC_REMOVED: { STORAGE_TYPE *p = (STORAGE_TYPE *)notification.clientId; p->DestroyResource(); } return true; default: return CDefaultDataCacheClient::HandleCacheNotification( notification ); } } protected: ~CManagedDataCacheClient() { Shutdown(); } IDataCacheSection *GetCacheSection() { return m_pCache; } private: IDataCacheSection *m_pCache; }; //----------------------------------------------------------------------------- #endif // IDataCache