diff -r 000000000000 -r 6474c204b198 gfx/skia/trunk/src/gpu/GrResourceCache.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gfx/skia/trunk/src/gpu/GrResourceCache.h Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,386 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + + +#ifndef GrResourceCache_DEFINED +#define GrResourceCache_DEFINED + +#include "GrConfig.h" +#include "GrTypes.h" +#include "GrTMultiMap.h" +#include "GrBinHashKey.h" +#include "SkMessageBus.h" +#include "SkTInternalLList.h" + +class GrResource; +class GrResourceEntry; + +class GrResourceKey { +public: + static GrCacheID::Domain ScratchDomain() { + static const GrCacheID::Domain gDomain = GrCacheID::GenerateDomain(); + return gDomain; + } + + /** Uniquely identifies the GrResource subclass in the key to avoid collisions + across resource types. */ + typedef uint8_t ResourceType; + + /** Flags set by the GrResource subclass. */ + typedef uint8_t ResourceFlags; + + /** Generate a unique ResourceType */ + static ResourceType GenerateResourceType(); + + /** Creates a key for resource */ + GrResourceKey(const GrCacheID& id, ResourceType type, ResourceFlags flags) { + this->init(id.getDomain(), id.getKey(), type, flags); + }; + + GrResourceKey(const GrResourceKey& src) { + fKey = src.fKey; + } + + GrResourceKey() { + fKey.reset(); + } + + void reset(const GrCacheID& id, ResourceType type, ResourceFlags flags) { + this->init(id.getDomain(), id.getKey(), type, flags); + } + + uint32_t getHash() const { + return fKey.getHash(); + } + + bool isScratch() const { + return ScratchDomain() == + *reinterpret_cast(fKey.getData() + + kCacheIDDomainOffset); + } + + ResourceType getResourceType() const { + return *reinterpret_cast(fKey.getData() + + kResourceTypeOffset); + } + + ResourceFlags getResourceFlags() const { + return *reinterpret_cast(fKey.getData() + + kResourceFlagsOffset); + } + + bool operator==(const GrResourceKey& other) const { return fKey == other.fKey; } + +private: + enum { + kCacheIDKeyOffset = 0, + kCacheIDDomainOffset = kCacheIDKeyOffset + sizeof(GrCacheID::Key), + kResourceTypeOffset = kCacheIDDomainOffset + sizeof(GrCacheID::Domain), + kResourceFlagsOffset = kResourceTypeOffset + sizeof(ResourceType), + kPadOffset = kResourceFlagsOffset + sizeof(ResourceFlags), + kKeySize = SkAlign4(kPadOffset), + kPadSize = kKeySize - kPadOffset + }; + + void init(const GrCacheID::Domain domain, + const GrCacheID::Key& key, + ResourceType type, + ResourceFlags flags) { + union { + uint8_t fKey8[kKeySize]; + uint32_t fKey32[kKeySize / 4]; + } keyData; + + uint8_t* k = keyData.fKey8; + memcpy(k + kCacheIDKeyOffset, key.fData8, sizeof(GrCacheID::Key)); + memcpy(k + kCacheIDDomainOffset, &domain, sizeof(GrCacheID::Domain)); + memcpy(k + kResourceTypeOffset, &type, sizeof(ResourceType)); + memcpy(k + kResourceFlagsOffset, &flags, sizeof(ResourceFlags)); + memset(k + kPadOffset, 0, kPadSize); + fKey.setKeyData(keyData.fKey32); + } + GrBinHashKey fKey; +}; + +// The cache listens for these messages to purge junk resources proactively. +struct GrResourceInvalidatedMessage { + GrResourceKey key; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class GrResourceEntry { +public: + GrResource* resource() const { return fResource; } + const GrResourceKey& key() const { return fKey; } + + static const GrResourceKey& GetKey(const GrResourceEntry& e) { return e.key(); } + static uint32_t Hash(const GrResourceKey& key) { return key.getHash(); } + static bool Equal(const GrResourceEntry& a, const GrResourceKey& b) { + return a.key() == b; + } +#ifdef SK_DEBUG + void validate() const; +#else + void validate() const {} +#endif + +private: + GrResourceEntry(const GrResourceKey& key, GrResource* resource); + ~GrResourceEntry(); + + GrResourceKey fKey; + GrResource* fResource; + + // Linked list for the LRU ordering. + SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrResourceEntry); + + friend class GrResourceCache; +}; + +/////////////////////////////////////////////////////////////////////////////// + +/** + * Cache of GrResource objects. + * + * These have a corresponding GrResourceKey, built from 128bits identifying the + * resource. Multiple resources can map to same GrResourceKey. + * + * The cache stores the entries in a double-linked list, which is its LRU. + * When an entry is "locked" (i.e. given to the caller), it is moved to the + * head of the list. If/when we must purge some of the entries, we walk the + * list backwards from the tail, since those are the least recently used. + * + * For fast searches, we maintain a hash map based on the GrResourceKey. + * + * It is a goal to make the GrResourceCache the central repository and bookkeeper + * of all resources. It should replace the linked list of GrResources that + * GrGpu uses to call abandon/release. + */ +class GrResourceCache { +public: + GrResourceCache(int maxCount, size_t maxBytes); + ~GrResourceCache(); + + /** + * Return the current resource cache limits. + * + * @param maxResource If non-null, returns maximum number of resources + * that can be held in the cache. + * @param maxBytes If non-null, returns maximum number of bytes of + * gpu memory that can be held in the cache. + */ + void getLimits(int* maxResources, size_t* maxBytes) const; + + /** + * Specify the resource cache limits. If the current cache exceeds either + * of these, it will be purged (LRU) to keep the cache within these limits. + * + * @param maxResources The maximum number of resources that can be held in + * the cache. + * @param maxBytes The maximum number of bytes of resource memory that + * can be held in the cache. + */ + void setLimits(int maxResources, size_t maxResourceBytes); + + /** + * The callback function used by the cache when it is still over budget + * after a purge. The passed in 'data' is the same 'data' handed to + * setOverbudgetCallback. The callback returns true if some resources + * have been freed. + */ + typedef bool (*PFOverbudgetCB)(void* data); + + /** + * Set the callback the cache should use when it is still over budget + * after a purge. The 'data' provided here will be passed back to the + * callback. Note that the cache will attempt to purge any resources newly + * freed by the callback. + */ + void setOverbudgetCallback(PFOverbudgetCB overbudgetCB, void* data) { + fOverbudgetCB = overbudgetCB; + fOverbudgetData = data; + } + + /** + * Returns the number of bytes consumed by cached resources. + */ + size_t getCachedResourceBytes() const { return fEntryBytes; } + + // For a found or added resource to be completely exclusive to the caller + // both the kNoOtherOwners and kHide flags need to be specified + enum OwnershipFlags { + kNoOtherOwners_OwnershipFlag = 0x1, // found/added resource has no other owners + kHide_OwnershipFlag = 0x2 // found/added resource is hidden from future 'find's + }; + + /** + * Search for an entry with the same Key. If found, return it. + * If not found, return null. + * If ownershipFlags includes kNoOtherOwners and a resource is returned + * then that resource has no other refs to it. + * If ownershipFlags includes kHide and a resource is returned then that + * resource will not be returned from future 'find' calls until it is + * 'freed' (and recycled) or makeNonExclusive is called. + * For a resource to be completely exclusive to a caller both kNoOtherOwners + * and kHide must be specified. + */ + GrResource* find(const GrResourceKey& key, + uint32_t ownershipFlags = 0); + + /** + * Add the new resource to the cache (by creating a new cache entry based + * on the provided key and resource). + * + * Ownership of the resource is transferred to the resource cache, + * which will unref() it when it is purged or deleted. + * + * If ownershipFlags includes kHide, subsequent calls to 'find' will not + * return 'resource' until it is 'freed' (and recycled) or makeNonExclusive + * is called. + */ + void addResource(const GrResourceKey& key, + GrResource* resource, + uint32_t ownershipFlags = 0); + + /** + * Determines if the cache contains an entry matching a key. If a matching + * entry exists but was detached then it will not be found. + */ + bool hasKey(const GrResourceKey& key) const { return NULL != fCache.find(key); } + + /** + * Hide 'entry' so that future searches will not find it. Such + * hidden entries will not be purged. The entry still counts against + * the cache's budget and should be made non-exclusive when exclusive access + * is no longer needed. + */ + void makeExclusive(GrResourceEntry* entry); + + /** + * Restore 'entry' so that it can be found by future searches. 'entry' + * will also be purgeable (provided its lock count is now 0.) + */ + void makeNonExclusive(GrResourceEntry* entry); + + /** + * Remove a resource from the cache and delete it! + */ + void deleteResource(GrResourceEntry* entry); + + /** + * Removes every resource in the cache that isn't locked. + */ + void purgeAllUnlocked(); + + /** + * Allow cache to purge unused resources to obey resource limitations + * Note: this entry point will be hidden (again) once totally ref-driven + * cache maintenance is implemented. Note that the overbudget callback + * will be called if the initial purge doesn't get the cache under + * its budget. + * + * extraCount and extraBytes are added to the current resource allocation + * to make sure enough room is available for future additions (e.g, + * 10MB across 10 textures is about to be added). + */ + void purgeAsNeeded(int extraCount = 0, size_t extraBytes = 0); + +#ifdef SK_DEBUG + void validate() const; +#else + void validate() const {} +#endif + +#if GR_CACHE_STATS + void printStats(); +#endif + +private: + enum BudgetBehaviors { + kAccountFor_BudgetBehavior, + kIgnore_BudgetBehavior + }; + + void internalDetach(GrResourceEntry*, BudgetBehaviors behavior = kAccountFor_BudgetBehavior); + void attachToHead(GrResourceEntry*, BudgetBehaviors behavior = kAccountFor_BudgetBehavior); + + void removeInvalidResource(GrResourceEntry* entry); + + GrTMultiMap fCache; + + // We're an internal doubly linked list + typedef SkTInternalLList EntryList; + EntryList fList; + +#ifdef SK_DEBUG + // These objects cannot be returned by a search + EntryList fExclusiveList; +#endif + + // our budget, used in purgeAsNeeded() + int fMaxCount; + size_t fMaxBytes; + + // our current stats, related to our budget +#if GR_CACHE_STATS + int fHighWaterEntryCount; + size_t fHighWaterEntryBytes; + int fHighWaterClientDetachedCount; + size_t fHighWaterClientDetachedBytes; +#endif + + int fEntryCount; + size_t fEntryBytes; + int fClientDetachedCount; + size_t fClientDetachedBytes; + + // prevents recursive purging + bool fPurging; + + PFOverbudgetCB fOverbudgetCB; + void* fOverbudgetData; + + void internalPurge(int extraCount, size_t extraBytes); + + // Listen for messages that a resource has been invalidated and purge cached junk proactively. + SkMessageBus::Inbox fInvalidationInbox; + void purgeInvalidated(); + +#ifdef SK_DEBUG + static size_t countBytes(const SkTInternalLList& list); +#endif +}; + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + class GrAutoResourceCacheValidate { + public: + GrAutoResourceCacheValidate(GrResourceCache* cache) : fCache(cache) { + cache->validate(); + } + ~GrAutoResourceCacheValidate() { + fCache->validate(); + } + private: + GrResourceCache* fCache; + }; +#else + class GrAutoResourceCacheValidate { + public: + GrAutoResourceCacheValidate(GrResourceCache*) {} + }; +#endif + +#endif