//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $Workfile: $ // $Date: $ // $NoKeywords: $ //=============================================================================// #ifndef IDATACACHE_H #define IDATACACHE_H #ifdef _WIN32 #pragma once #endif #include "appframework/IAppSystem.h" #include "tier0/dbg.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 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