GrResourceCache.cpp 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983
  1. /*
  2. * Copyright 2014 Google Inc.
  3. *
  4. * Use of this source code is governed by a BSD-style license that can be
  5. * found in the LICENSE file.
  6. */
  7. #include "src/gpu/GrResourceCache.h"
  8. #include <atomic>
  9. #include "include/gpu/GrContext.h"
  10. #include "include/gpu/GrTexture.h"
  11. #include "include/private/GrSingleOwner.h"
  12. #include "include/private/SkTo.h"
  13. #include "include/utils/SkRandom.h"
  14. #include "src/core/SkExchange.h"
  15. #include "src/core/SkMessageBus.h"
  16. #include "src/core/SkOpts.h"
  17. #include "src/core/SkScopeExit.h"
  18. #include "src/core/SkTSort.h"
  19. #include "src/gpu/GrCaps.h"
  20. #include "src/gpu/GrContextPriv.h"
  21. #include "src/gpu/GrGpuResourceCacheAccess.h"
  22. #include "src/gpu/GrProxyProvider.h"
  23. #include "src/gpu/GrTextureProxyCacheAccess.h"
  24. #include "src/gpu/GrTracing.h"
  25. #include "src/gpu/SkGr.h"
  26. DECLARE_SKMESSAGEBUS_MESSAGE(GrUniqueKeyInvalidatedMessage);
  27. DECLARE_SKMESSAGEBUS_MESSAGE(GrGpuResourceFreedMessage);
  28. #define ASSERT_SINGLE_OWNER \
  29. SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(fSingleOwner);)
  30. //////////////////////////////////////////////////////////////////////////////
  31. GrScratchKey::ResourceType GrScratchKey::GenerateResourceType() {
  32. static std::atomic<int32_t> nextType{INHERITED::kInvalidDomain + 1};
  33. int32_t type = nextType++;
  34. if (type > SkTo<int32_t>(UINT16_MAX)) {
  35. SK_ABORT("Too many Resource Types");
  36. }
  37. return static_cast<ResourceType>(type);
  38. }
  39. GrUniqueKey::Domain GrUniqueKey::GenerateDomain() {
  40. static std::atomic<int32_t> nextDomain{INHERITED::kInvalidDomain + 1};
  41. int32_t domain = nextDomain++;
  42. if (domain > SkTo<int32_t>(UINT16_MAX)) {
  43. SK_ABORT("Too many GrUniqueKey Domains");
  44. }
  45. return static_cast<Domain>(domain);
  46. }
  47. uint32_t GrResourceKeyHash(const uint32_t* data, size_t size) {
  48. return SkOpts::hash(data, size);
  49. }
  50. //////////////////////////////////////////////////////////////////////////////
  51. class GrResourceCache::AutoValidate : ::SkNoncopyable {
  52. public:
  53. AutoValidate(GrResourceCache* cache) : fCache(cache) { cache->validate(); }
  54. ~AutoValidate() { fCache->validate(); }
  55. private:
  56. GrResourceCache* fCache;
  57. };
  58. //////////////////////////////////////////////////////////////////////////////
  59. inline GrResourceCache::ResourceAwaitingUnref::ResourceAwaitingUnref() = default;
  60. inline GrResourceCache::ResourceAwaitingUnref::ResourceAwaitingUnref(GrGpuResource* resource)
  61. : fResource(resource), fNumUnrefs(1) {}
  62. inline GrResourceCache::ResourceAwaitingUnref::ResourceAwaitingUnref(ResourceAwaitingUnref&& that) {
  63. fResource = skstd::exchange(that.fResource, nullptr);
  64. fNumUnrefs = skstd::exchange(that.fNumUnrefs, 0);
  65. }
  66. inline GrResourceCache::ResourceAwaitingUnref& GrResourceCache::ResourceAwaitingUnref::operator=(
  67. ResourceAwaitingUnref&& that) {
  68. fResource = skstd::exchange(that.fResource, nullptr);
  69. fNumUnrefs = skstd::exchange(that.fNumUnrefs, 0);
  70. return *this;
  71. }
  72. inline GrResourceCache::ResourceAwaitingUnref::~ResourceAwaitingUnref() {
  73. if (fResource) {
  74. for (int i = 0; i < fNumUnrefs; ++i) {
  75. fResource->unref();
  76. }
  77. }
  78. }
  79. inline void GrResourceCache::ResourceAwaitingUnref::addRef() { ++fNumUnrefs; }
  80. inline void GrResourceCache::ResourceAwaitingUnref::unref() {
  81. SkASSERT(fNumUnrefs > 0);
  82. fResource->unref();
  83. --fNumUnrefs;
  84. }
  85. inline bool GrResourceCache::ResourceAwaitingUnref::finished() { return !fNumUnrefs; }
  86. //////////////////////////////////////////////////////////////////////////////
  87. GrResourceCache::GrResourceCache(const GrCaps* caps, GrSingleOwner* singleOwner,
  88. uint32_t contextUniqueID)
  89. : fInvalidUniqueKeyInbox(contextUniqueID)
  90. , fFreedGpuResourceInbox(contextUniqueID)
  91. , fContextUniqueID(contextUniqueID)
  92. , fSingleOwner(singleOwner)
  93. , fPreferVRAMUseOverFlushes(caps->preferVRAMUseOverFlushes()) {
  94. SkASSERT(contextUniqueID != SK_InvalidUniqueID);
  95. }
  96. GrResourceCache::~GrResourceCache() {
  97. this->releaseAll();
  98. }
  99. void GrResourceCache::setLimits(int count, size_t bytes) {
  100. fMaxCount = count;
  101. fMaxBytes = bytes;
  102. this->purgeAsNeeded();
  103. }
  104. void GrResourceCache::insertResource(GrGpuResource* resource) {
  105. ASSERT_SINGLE_OWNER
  106. SkASSERT(resource);
  107. SkASSERT(!this->isInCache(resource));
  108. SkASSERT(!resource->wasDestroyed());
  109. SkASSERT(!resource->resourcePriv().isPurgeable());
  110. // We must set the timestamp before adding to the array in case the timestamp wraps and we wind
  111. // up iterating over all the resources that already have timestamps.
  112. resource->cacheAccess().setTimestamp(this->getNextTimestamp());
  113. this->addToNonpurgeableArray(resource);
  114. size_t size = resource->gpuMemorySize();
  115. SkDEBUGCODE(++fCount;)
  116. fBytes += size;
  117. #if GR_CACHE_STATS
  118. fHighWaterCount = SkTMax(this->getResourceCount(), fHighWaterCount);
  119. fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
  120. #endif
  121. if (GrBudgetedType::kBudgeted == resource->resourcePriv().budgetedType()) {
  122. ++fBudgetedCount;
  123. fBudgetedBytes += size;
  124. TRACE_COUNTER2("skia.gpu.cache", "skia budget", "used",
  125. fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
  126. #if GR_CACHE_STATS
  127. fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
  128. fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
  129. #endif
  130. }
  131. if (resource->resourcePriv().getScratchKey().isValid() &&
  132. !resource->getUniqueKey().isValid()) {
  133. SkASSERT(!resource->resourcePriv().refsWrappedObjects());
  134. fScratchMap.insert(resource->resourcePriv().getScratchKey(), resource);
  135. }
  136. this->purgeAsNeeded();
  137. }
  138. void GrResourceCache::removeResource(GrGpuResource* resource) {
  139. ASSERT_SINGLE_OWNER
  140. this->validate();
  141. SkASSERT(this->isInCache(resource));
  142. size_t size = resource->gpuMemorySize();
  143. if (resource->resourcePriv().isPurgeable()) {
  144. fPurgeableQueue.remove(resource);
  145. fPurgeableBytes -= size;
  146. } else {
  147. this->removeFromNonpurgeableArray(resource);
  148. }
  149. SkDEBUGCODE(--fCount;)
  150. fBytes -= size;
  151. if (GrBudgetedType::kBudgeted == resource->resourcePriv().budgetedType()) {
  152. --fBudgetedCount;
  153. fBudgetedBytes -= size;
  154. TRACE_COUNTER2("skia.gpu.cache", "skia budget", "used",
  155. fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
  156. }
  157. if (resource->resourcePriv().getScratchKey().isValid() &&
  158. !resource->getUniqueKey().isValid()) {
  159. fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
  160. }
  161. if (resource->getUniqueKey().isValid()) {
  162. fUniqueHash.remove(resource->getUniqueKey());
  163. }
  164. this->validate();
  165. }
  166. void GrResourceCache::abandonAll() {
  167. AutoValidate av(this);
  168. // We need to make sure to free any resources that were waiting on a free message but never
  169. // received one.
  170. fResourcesAwaitingUnref.reset();
  171. while (fNonpurgeableResources.count()) {
  172. GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
  173. SkASSERT(!back->wasDestroyed());
  174. back->cacheAccess().abandon();
  175. }
  176. while (fPurgeableQueue.count()) {
  177. GrGpuResource* top = fPurgeableQueue.peek();
  178. SkASSERT(!top->wasDestroyed());
  179. top->cacheAccess().abandon();
  180. }
  181. SkASSERT(!fScratchMap.count());
  182. SkASSERT(!fUniqueHash.count());
  183. SkASSERT(!fCount);
  184. SkASSERT(!this->getResourceCount());
  185. SkASSERT(!fBytes);
  186. SkASSERT(!fBudgetedCount);
  187. SkASSERT(!fBudgetedBytes);
  188. SkASSERT(!fPurgeableBytes);
  189. SkASSERT(!fResourcesAwaitingUnref.count());
  190. }
  191. void GrResourceCache::releaseAll() {
  192. AutoValidate av(this);
  193. this->processFreedGpuResources();
  194. // We need to make sure to free any resources that were waiting on a free message but never
  195. // received one.
  196. fResourcesAwaitingUnref.reset();
  197. SkASSERT(fProxyProvider); // better have called setProxyProvider
  198. // We must remove the uniqueKeys from the proxies here. While they possess a uniqueKey
  199. // they also have a raw pointer back to this class (which is presumably going away)!
  200. fProxyProvider->removeAllUniqueKeys();
  201. while (fNonpurgeableResources.count()) {
  202. GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
  203. SkASSERT(!back->wasDestroyed());
  204. back->cacheAccess().release();
  205. }
  206. while (fPurgeableQueue.count()) {
  207. GrGpuResource* top = fPurgeableQueue.peek();
  208. SkASSERT(!top->wasDestroyed());
  209. top->cacheAccess().release();
  210. }
  211. SkASSERT(!fScratchMap.count());
  212. SkASSERT(!fUniqueHash.count());
  213. SkASSERT(!fCount);
  214. SkASSERT(!this->getResourceCount());
  215. SkASSERT(!fBytes);
  216. SkASSERT(!fBudgetedCount);
  217. SkASSERT(!fBudgetedBytes);
  218. SkASSERT(!fPurgeableBytes);
  219. SkASSERT(!fResourcesAwaitingUnref.count());
  220. }
  221. void GrResourceCache::refResource(GrGpuResource* resource) {
  222. SkASSERT(resource);
  223. SkASSERT(resource->getContext()->priv().getResourceCache() == this);
  224. if (resource->cacheAccess().hasRef()) {
  225. resource->ref();
  226. } else {
  227. this->refAndMakeResourceMRU(resource);
  228. }
  229. this->validate();
  230. }
  231. class GrResourceCache::AvailableForScratchUse {
  232. public:
  233. AvailableForScratchUse(bool rejectPendingIO) : fRejectPendingIO(rejectPendingIO) { }
  234. bool operator()(const GrGpuResource* resource) const {
  235. SkASSERT(!resource->getUniqueKey().isValid() &&
  236. resource->resourcePriv().getScratchKey().isValid());
  237. if (resource->internalHasRef() || !resource->cacheAccess().isScratch()) {
  238. return false;
  239. }
  240. return !fRejectPendingIO || !resource->internalHasPendingIO();
  241. }
  242. private:
  243. bool fRejectPendingIO;
  244. };
  245. GrGpuResource* GrResourceCache::findAndRefScratchResource(const GrScratchKey& scratchKey,
  246. size_t resourceSize,
  247. ScratchFlags flags) {
  248. SkASSERT(scratchKey.isValid());
  249. GrGpuResource* resource;
  250. if (flags & (ScratchFlags::kPreferNoPendingIO | ScratchFlags::kRequireNoPendingIO)) {
  251. resource = fScratchMap.find(scratchKey, AvailableForScratchUse(true));
  252. if (resource) {
  253. this->refAndMakeResourceMRU(resource);
  254. this->validate();
  255. return resource;
  256. } else if (flags & ScratchFlags::kRequireNoPendingIO) {
  257. return nullptr;
  258. }
  259. // We would prefer to consume more available VRAM rather than flushing
  260. // immediately, but on ANGLE this can lead to starving of the GPU.
  261. if (fPreferVRAMUseOverFlushes && this->wouldFit(resourceSize)) {
  262. // kPrefer is specified, we didn't find a resource without pending io,
  263. // but there is still space in our budget for the resource so force
  264. // the caller to allocate a new resource.
  265. return nullptr;
  266. }
  267. }
  268. resource = fScratchMap.find(scratchKey, AvailableForScratchUse(false));
  269. if (resource) {
  270. this->refAndMakeResourceMRU(resource);
  271. this->validate();
  272. }
  273. return resource;
  274. }
  275. void GrResourceCache::willRemoveScratchKey(const GrGpuResource* resource) {
  276. ASSERT_SINGLE_OWNER
  277. SkASSERT(resource->resourcePriv().getScratchKey().isValid());
  278. if (!resource->getUniqueKey().isValid()) {
  279. fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
  280. }
  281. }
  282. void GrResourceCache::removeUniqueKey(GrGpuResource* resource) {
  283. ASSERT_SINGLE_OWNER
  284. // Someone has a ref to this resource in order to have removed the key. When the ref count
  285. // reaches zero we will get a ref cnt notification and figure out what to do with it.
  286. if (resource->getUniqueKey().isValid()) {
  287. SkASSERT(resource == fUniqueHash.find(resource->getUniqueKey()));
  288. fUniqueHash.remove(resource->getUniqueKey());
  289. }
  290. resource->cacheAccess().removeUniqueKey();
  291. if (resource->resourcePriv().getScratchKey().isValid()) {
  292. fScratchMap.insert(resource->resourcePriv().getScratchKey(), resource);
  293. }
  294. // Removing a unique key from a kUnbudgetedCacheable resource would make the resource
  295. // require purging. However, the resource must be ref'ed to get here and therefore can't
  296. // be purgeable. We'll purge it when the refs reach zero.
  297. SkASSERT(!resource->resourcePriv().isPurgeable());
  298. this->validate();
  299. }
  300. void GrResourceCache::changeUniqueKey(GrGpuResource* resource, const GrUniqueKey& newKey) {
  301. ASSERT_SINGLE_OWNER
  302. SkASSERT(resource);
  303. SkASSERT(this->isInCache(resource));
  304. // If another resource has the new key, remove its key then install the key on this resource.
  305. if (newKey.isValid()) {
  306. if (GrGpuResource* old = fUniqueHash.find(newKey)) {
  307. // If the old resource using the key is purgeable and is unreachable, then remove it.
  308. if (!old->resourcePriv().getScratchKey().isValid() &&
  309. old->resourcePriv().isPurgeable()) {
  310. old->cacheAccess().release();
  311. } else {
  312. // removeUniqueKey expects an external owner of the resource.
  313. this->removeUniqueKey(sk_ref_sp(old).get());
  314. }
  315. }
  316. SkASSERT(nullptr == fUniqueHash.find(newKey));
  317. // Remove the entry for this resource if it already has a unique key.
  318. if (resource->getUniqueKey().isValid()) {
  319. SkASSERT(resource == fUniqueHash.find(resource->getUniqueKey()));
  320. fUniqueHash.remove(resource->getUniqueKey());
  321. SkASSERT(nullptr == fUniqueHash.find(resource->getUniqueKey()));
  322. } else {
  323. // 'resource' didn't have a valid unique key before so it is switching sides. Remove it
  324. // from the ScratchMap
  325. if (resource->resourcePriv().getScratchKey().isValid()) {
  326. fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
  327. }
  328. }
  329. resource->cacheAccess().setUniqueKey(newKey);
  330. fUniqueHash.add(resource);
  331. } else {
  332. this->removeUniqueKey(resource);
  333. }
  334. this->validate();
  335. }
  336. void GrResourceCache::refAndMakeResourceMRU(GrGpuResource* resource) {
  337. ASSERT_SINGLE_OWNER
  338. SkASSERT(resource);
  339. SkASSERT(this->isInCache(resource));
  340. if (resource->resourcePriv().isPurgeable()) {
  341. // It's about to become unpurgeable.
  342. fPurgeableBytes -= resource->gpuMemorySize();
  343. fPurgeableQueue.remove(resource);
  344. this->addToNonpurgeableArray(resource);
  345. } else if (!resource->cacheAccess().hasRef() &&
  346. resource->resourcePriv().budgetedType() == GrBudgetedType::kBudgeted) {
  347. SkASSERT(fNumBudgetedResourcesFlushWillMakePurgeable > 0);
  348. fNumBudgetedResourcesFlushWillMakePurgeable--;
  349. }
  350. resource->cacheAccess().ref();
  351. resource->cacheAccess().setTimestamp(this->getNextTimestamp());
  352. this->validate();
  353. }
  354. void GrResourceCache::notifyCntReachedZero(GrGpuResource* resource, uint32_t flags) {
  355. ASSERT_SINGLE_OWNER
  356. SkASSERT(resource);
  357. SkASSERT(!resource->wasDestroyed());
  358. SkASSERT(flags);
  359. SkASSERT(this->isInCache(resource));
  360. // This resource should always be in the nonpurgeable array when this function is called. It
  361. // will be moved to the queue if it is newly purgeable.
  362. SkASSERT(fNonpurgeableResources[*resource->cacheAccess().accessCacheIndex()] == resource);
  363. if (SkToBool(ResourceAccess::kRefCntReachedZero_RefNotificationFlag & flags)) {
  364. #ifdef SK_DEBUG
  365. // When the timestamp overflows validate() is called. validate() checks that resources in
  366. // the nonpurgeable array are indeed not purgeable. However, the movement from the array to
  367. // the purgeable queue happens just below in this function. So we mark it as an exception.
  368. if (resource->resourcePriv().isPurgeable()) {
  369. fNewlyPurgeableResourceForValidation = resource;
  370. }
  371. #endif
  372. resource->cacheAccess().setTimestamp(this->getNextTimestamp());
  373. SkDEBUGCODE(fNewlyPurgeableResourceForValidation = nullptr);
  374. if (!resource->resourcePriv().isPurgeable() &&
  375. resource->resourcePriv().budgetedType() == GrBudgetedType::kBudgeted) {
  376. SkASSERT(resource->resourcePriv().hasPendingIO_debugOnly());
  377. ++fNumBudgetedResourcesFlushWillMakePurgeable;
  378. }
  379. } else {
  380. // If this is budgeted and just became purgeable by dropping the last pending IO
  381. // then it clearly no longer needs a flush to become purgeable.
  382. if (resource->resourcePriv().budgetedType() == GrBudgetedType::kBudgeted &&
  383. resource->resourcePriv().isPurgeable()) {
  384. SkASSERT(fNumBudgetedResourcesFlushWillMakePurgeable > 0);
  385. fNumBudgetedResourcesFlushWillMakePurgeable--;
  386. }
  387. }
  388. if (!SkToBool(ResourceAccess::kAllCntsReachedZero_RefNotificationFlag & flags)) {
  389. SkASSERT(!resource->resourcePriv().isPurgeable());
  390. return;
  391. }
  392. if (!resource->resourcePriv().isPurgeable()) {
  393. this->validate();
  394. return;
  395. }
  396. this->removeFromNonpurgeableArray(resource);
  397. fPurgeableQueue.insert(resource);
  398. resource->cacheAccess().setTimeWhenResourceBecomePurgeable();
  399. fPurgeableBytes += resource->gpuMemorySize();
  400. bool hasUniqueKey = resource->getUniqueKey().isValid();
  401. GrBudgetedType budgetedType = resource->resourcePriv().budgetedType();
  402. if (budgetedType == GrBudgetedType::kBudgeted) {
  403. // Purge the resource immediately if we're over budget
  404. // Also purge if the resource has neither a valid scratch key nor a unique key.
  405. bool hasKey = resource->resourcePriv().getScratchKey().isValid() || hasUniqueKey;
  406. if (!this->overBudget() && hasKey) {
  407. return;
  408. }
  409. } else {
  410. // We keep unbudgeted resources with a unique key in the purgeable queue of the cache so
  411. // they can be reused again by the image connected to the unique key.
  412. if (hasUniqueKey && budgetedType == GrBudgetedType::kUnbudgetedCacheable) {
  413. return;
  414. }
  415. // Check whether this resource could still be used as a scratch resource.
  416. if (!resource->resourcePriv().refsWrappedObjects() &&
  417. resource->resourcePriv().getScratchKey().isValid()) {
  418. // We won't purge an existing resource to make room for this one.
  419. if (fBudgetedCount < fMaxCount &&
  420. fBudgetedBytes + resource->gpuMemorySize() <= fMaxBytes) {
  421. resource->resourcePriv().makeBudgeted();
  422. return;
  423. }
  424. }
  425. }
  426. SkDEBUGCODE(int beforeCount = this->getResourceCount();)
  427. resource->cacheAccess().release();
  428. // We should at least free this resource, perhaps dependent resources as well.
  429. SkASSERT(this->getResourceCount() < beforeCount);
  430. this->validate();
  431. }
  432. void GrResourceCache::didChangeBudgetStatus(GrGpuResource* resource) {
  433. ASSERT_SINGLE_OWNER
  434. SkASSERT(resource);
  435. SkASSERT(this->isInCache(resource));
  436. size_t size = resource->gpuMemorySize();
  437. // Changing from BudgetedType::kUnbudgetedCacheable to another budgeted type could make
  438. // resource become purgeable. However, we should never allow that transition. Wrapped
  439. // resources are the only resources that can be in that state and they aren't allowed to
  440. // transition from one budgeted state to another.
  441. SkDEBUGCODE(bool wasPurgeable = resource->resourcePriv().isPurgeable());
  442. if (resource->resourcePriv().budgetedType() == GrBudgetedType::kBudgeted) {
  443. ++fBudgetedCount;
  444. fBudgetedBytes += size;
  445. #if GR_CACHE_STATS
  446. fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
  447. fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
  448. #endif
  449. if (!resource->resourcePriv().isPurgeable() && !resource->cacheAccess().hasRef()) {
  450. ++fNumBudgetedResourcesFlushWillMakePurgeable;
  451. }
  452. this->purgeAsNeeded();
  453. } else {
  454. SkASSERT(resource->resourcePriv().budgetedType() != GrBudgetedType::kUnbudgetedCacheable);
  455. --fBudgetedCount;
  456. fBudgetedBytes -= size;
  457. if (!resource->resourcePriv().isPurgeable() && !resource->cacheAccess().hasRef()) {
  458. --fNumBudgetedResourcesFlushWillMakePurgeable;
  459. }
  460. }
  461. SkASSERT(wasPurgeable == resource->resourcePriv().isPurgeable());
  462. TRACE_COUNTER2("skia.gpu.cache", "skia budget", "used",
  463. fBudgetedBytes, "free", fMaxBytes - fBudgetedBytes);
  464. this->validate();
  465. }
  466. void GrResourceCache::purgeAsNeeded() {
  467. SkTArray<GrUniqueKeyInvalidatedMessage> invalidKeyMsgs;
  468. fInvalidUniqueKeyInbox.poll(&invalidKeyMsgs);
  469. if (invalidKeyMsgs.count()) {
  470. SkASSERT(fProxyProvider);
  471. for (int i = 0; i < invalidKeyMsgs.count(); ++i) {
  472. fProxyProvider->processInvalidUniqueKey(invalidKeyMsgs[i].key(), nullptr,
  473. GrProxyProvider::InvalidateGPUResource::kYes);
  474. SkASSERT(!this->findAndRefUniqueResource(invalidKeyMsgs[i].key()));
  475. }
  476. }
  477. this->processFreedGpuResources();
  478. bool stillOverbudget = this->overBudget();
  479. while (stillOverbudget && fPurgeableQueue.count()) {
  480. GrGpuResource* resource = fPurgeableQueue.peek();
  481. SkASSERT(resource->resourcePriv().isPurgeable());
  482. resource->cacheAccess().release();
  483. stillOverbudget = this->overBudget();
  484. }
  485. this->validate();
  486. }
  487. void GrResourceCache::purgeUnlockedResources(bool scratchResourcesOnly) {
  488. if (!scratchResourcesOnly) {
  489. // We could disable maintaining the heap property here, but it would add a lot of
  490. // complexity. Moreover, this is rarely called.
  491. while (fPurgeableQueue.count()) {
  492. GrGpuResource* resource = fPurgeableQueue.peek();
  493. SkASSERT(resource->resourcePriv().isPurgeable());
  494. resource->cacheAccess().release();
  495. }
  496. } else {
  497. // Sort the queue
  498. fPurgeableQueue.sort();
  499. // Make a list of the scratch resources to delete
  500. SkTDArray<GrGpuResource*> scratchResources;
  501. for (int i = 0; i < fPurgeableQueue.count(); i++) {
  502. GrGpuResource* resource = fPurgeableQueue.at(i);
  503. SkASSERT(resource->resourcePriv().isPurgeable());
  504. if (!resource->getUniqueKey().isValid()) {
  505. *scratchResources.append() = resource;
  506. }
  507. }
  508. // Delete the scratch resources. This must be done as a separate pass
  509. // to avoid messing up the sorted order of the queue
  510. for (int i = 0; i < scratchResources.count(); i++) {
  511. scratchResources.getAt(i)->cacheAccess().release();
  512. }
  513. }
  514. this->validate();
  515. }
  516. void GrResourceCache::purgeResourcesNotUsedSince(GrStdSteadyClock::time_point purgeTime) {
  517. while (fPurgeableQueue.count()) {
  518. const GrStdSteadyClock::time_point resourceTime =
  519. fPurgeableQueue.peek()->cacheAccess().timeWhenResourceBecamePurgeable();
  520. if (resourceTime >= purgeTime) {
  521. // Resources were given both LRU timestamps and tagged with a frame number when
  522. // they first became purgeable. The LRU timestamp won't change again until the
  523. // resource is made non-purgeable again. So, at this point all the remaining
  524. // resources in the timestamp-sorted queue will have a frame number >= to this
  525. // one.
  526. break;
  527. }
  528. GrGpuResource* resource = fPurgeableQueue.peek();
  529. SkASSERT(resource->resourcePriv().isPurgeable());
  530. resource->cacheAccess().release();
  531. }
  532. }
  533. void GrResourceCache::purgeUnlockedResources(size_t bytesToPurge, bool preferScratchResources) {
  534. const size_t tmpByteBudget = SkTMax((size_t)0, fBytes - bytesToPurge);
  535. bool stillOverbudget = tmpByteBudget < fBytes;
  536. if (preferScratchResources && bytesToPurge < fPurgeableBytes) {
  537. // Sort the queue
  538. fPurgeableQueue.sort();
  539. // Make a list of the scratch resources to delete
  540. SkTDArray<GrGpuResource*> scratchResources;
  541. size_t scratchByteCount = 0;
  542. for (int i = 0; i < fPurgeableQueue.count() && stillOverbudget; i++) {
  543. GrGpuResource* resource = fPurgeableQueue.at(i);
  544. SkASSERT(resource->resourcePriv().isPurgeable());
  545. if (!resource->getUniqueKey().isValid()) {
  546. *scratchResources.append() = resource;
  547. scratchByteCount += resource->gpuMemorySize();
  548. stillOverbudget = tmpByteBudget < fBytes - scratchByteCount;
  549. }
  550. }
  551. // Delete the scratch resources. This must be done as a separate pass
  552. // to avoid messing up the sorted order of the queue
  553. for (int i = 0; i < scratchResources.count(); i++) {
  554. scratchResources.getAt(i)->cacheAccess().release();
  555. }
  556. stillOverbudget = tmpByteBudget < fBytes;
  557. this->validate();
  558. }
  559. // Purge any remaining resources in LRU order
  560. if (stillOverbudget) {
  561. const size_t cachedByteCount = fMaxBytes;
  562. fMaxBytes = tmpByteBudget;
  563. this->purgeAsNeeded();
  564. fMaxBytes = cachedByteCount;
  565. }
  566. }
  567. bool GrResourceCache::requestsFlush() const {
  568. return this->overBudget() && !fPurgeableQueue.count() &&
  569. fNumBudgetedResourcesFlushWillMakePurgeable > 0;
  570. }
  571. void GrResourceCache::insertDelayedResourceUnref(GrGpuResource* resource) {
  572. resource->ref();
  573. uint32_t id = resource->uniqueID().asUInt();
  574. if (auto* data = fResourcesAwaitingUnref.find(id)) {
  575. data->addRef();
  576. } else {
  577. fResourcesAwaitingUnref.set(id, {resource});
  578. }
  579. }
  580. void GrResourceCache::processFreedGpuResources() {
  581. SkTArray<GrGpuResourceFreedMessage> msgs;
  582. fFreedGpuResourceInbox.poll(&msgs);
  583. for (int i = 0; i < msgs.count(); ++i) {
  584. SkASSERT(msgs[i].fOwningUniqueID == fContextUniqueID);
  585. uint32_t id = msgs[i].fResource->uniqueID().asUInt();
  586. ResourceAwaitingUnref* info = fResourcesAwaitingUnref.find(id);
  587. // If we called release or abandon on the GrContext we will have already released our ref on
  588. // the GrGpuResource. If then the message arrives before the actual GrContext gets destroyed
  589. // we will try to process the message when we destroy the GrContext. This protects us from
  590. // trying to unref the resource twice.
  591. if (info) {
  592. info->unref();
  593. if (info->finished()) {
  594. fResourcesAwaitingUnref.remove(id);
  595. }
  596. }
  597. }
  598. }
  599. void GrResourceCache::addToNonpurgeableArray(GrGpuResource* resource) {
  600. int index = fNonpurgeableResources.count();
  601. *fNonpurgeableResources.append() = resource;
  602. *resource->cacheAccess().accessCacheIndex() = index;
  603. }
  604. void GrResourceCache::removeFromNonpurgeableArray(GrGpuResource* resource) {
  605. int* index = resource->cacheAccess().accessCacheIndex();
  606. // Fill the whole we will create in the array with the tail object, adjust its index, and
  607. // then pop the array
  608. GrGpuResource* tail = *(fNonpurgeableResources.end() - 1);
  609. SkASSERT(fNonpurgeableResources[*index] == resource);
  610. fNonpurgeableResources[*index] = tail;
  611. *tail->cacheAccess().accessCacheIndex() = *index;
  612. fNonpurgeableResources.pop();
  613. SkDEBUGCODE(*index = -1);
  614. }
  615. uint32_t GrResourceCache::getNextTimestamp() {
  616. // If we wrap then all the existing resources will appear older than any resources that get
  617. // a timestamp after the wrap.
  618. if (0 == fTimestamp) {
  619. int count = this->getResourceCount();
  620. if (count) {
  621. // Reset all the timestamps. We sort the resources by timestamp and then assign
  622. // sequential timestamps beginning with 0. This is O(n*lg(n)) but it should be extremely
  623. // rare.
  624. SkTDArray<GrGpuResource*> sortedPurgeableResources;
  625. sortedPurgeableResources.setReserve(fPurgeableQueue.count());
  626. while (fPurgeableQueue.count()) {
  627. *sortedPurgeableResources.append() = fPurgeableQueue.peek();
  628. fPurgeableQueue.pop();
  629. }
  630. SkTQSort(fNonpurgeableResources.begin(), fNonpurgeableResources.end() - 1,
  631. CompareTimestamp);
  632. // Pick resources out of the purgeable and non-purgeable arrays based on lowest
  633. // timestamp and assign new timestamps.
  634. int currP = 0;
  635. int currNP = 0;
  636. while (currP < sortedPurgeableResources.count() &&
  637. currNP < fNonpurgeableResources.count()) {
  638. uint32_t tsP = sortedPurgeableResources[currP]->cacheAccess().timestamp();
  639. uint32_t tsNP = fNonpurgeableResources[currNP]->cacheAccess().timestamp();
  640. SkASSERT(tsP != tsNP);
  641. if (tsP < tsNP) {
  642. sortedPurgeableResources[currP++]->cacheAccess().setTimestamp(fTimestamp++);
  643. } else {
  644. // Correct the index in the nonpurgeable array stored on the resource post-sort.
  645. *fNonpurgeableResources[currNP]->cacheAccess().accessCacheIndex() = currNP;
  646. fNonpurgeableResources[currNP++]->cacheAccess().setTimestamp(fTimestamp++);
  647. }
  648. }
  649. // The above loop ended when we hit the end of one array. Finish the other one.
  650. while (currP < sortedPurgeableResources.count()) {
  651. sortedPurgeableResources[currP++]->cacheAccess().setTimestamp(fTimestamp++);
  652. }
  653. while (currNP < fNonpurgeableResources.count()) {
  654. *fNonpurgeableResources[currNP]->cacheAccess().accessCacheIndex() = currNP;
  655. fNonpurgeableResources[currNP++]->cacheAccess().setTimestamp(fTimestamp++);
  656. }
  657. // Rebuild the queue.
  658. for (int i = 0; i < sortedPurgeableResources.count(); ++i) {
  659. fPurgeableQueue.insert(sortedPurgeableResources[i]);
  660. }
  661. this->validate();
  662. SkASSERT(count == this->getResourceCount());
  663. // count should be the next timestamp we return.
  664. SkASSERT(fTimestamp == SkToU32(count));
  665. }
  666. }
  667. return fTimestamp++;
  668. }
  669. void GrResourceCache::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const {
  670. for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
  671. fNonpurgeableResources[i]->dumpMemoryStatistics(traceMemoryDump);
  672. }
  673. for (int i = 0; i < fPurgeableQueue.count(); ++i) {
  674. fPurgeableQueue.at(i)->dumpMemoryStatistics(traceMemoryDump);
  675. }
  676. }
  677. #if GR_CACHE_STATS
  678. void GrResourceCache::getStats(Stats* stats) const {
  679. stats->reset();
  680. stats->fTotal = this->getResourceCount();
  681. stats->fNumNonPurgeable = fNonpurgeableResources.count();
  682. stats->fNumPurgeable = fPurgeableQueue.count();
  683. for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
  684. stats->update(fNonpurgeableResources[i]);
  685. }
  686. for (int i = 0; i < fPurgeableQueue.count(); ++i) {
  687. stats->update(fPurgeableQueue.at(i));
  688. }
  689. }
  690. #if GR_TEST_UTILS
  691. void GrResourceCache::dumpStats(SkString* out) const {
  692. this->validate();
  693. Stats stats;
  694. this->getStats(&stats);
  695. float countUtilization = (100.f * fBudgetedCount) / fMaxCount;
  696. float byteUtilization = (100.f * fBudgetedBytes) / fMaxBytes;
  697. out->appendf("Budget: %d items %d bytes\n", fMaxCount, (int)fMaxBytes);
  698. out->appendf("\t\tEntry Count: current %d"
  699. " (%d budgeted, %d wrapped, %d locked, %d scratch %.2g%% full), high %d\n",
  700. stats.fTotal, fBudgetedCount, stats.fWrapped, stats.fNumNonPurgeable,
  701. stats.fScratch, countUtilization, fHighWaterCount);
  702. out->appendf("\t\tEntry Bytes: current %d (budgeted %d, %.2g%% full, %d unbudgeted) high %d\n",
  703. SkToInt(fBytes), SkToInt(fBudgetedBytes), byteUtilization,
  704. SkToInt(stats.fUnbudgetedSize), SkToInt(fHighWaterBytes));
  705. }
  706. void GrResourceCache::dumpStatsKeyValuePairs(SkTArray<SkString>* keys,
  707. SkTArray<double>* values) const {
  708. this->validate();
  709. Stats stats;
  710. this->getStats(&stats);
  711. keys->push_back(SkString("gpu_cache_purgable_entries")); values->push_back(stats.fNumPurgeable);
  712. }
  713. #endif
  714. #endif
  715. #ifdef SK_DEBUG
  716. void GrResourceCache::validate() const {
  717. // Reduce the frequency of validations for large resource counts.
  718. static SkRandom gRandom;
  719. int mask = (SkNextPow2(fCount + 1) >> 5) - 1;
  720. if (~mask && (gRandom.nextU() & mask)) {
  721. return;
  722. }
  723. struct Stats {
  724. size_t fBytes;
  725. int fBudgetedCount;
  726. size_t fBudgetedBytes;
  727. int fLocked;
  728. int fScratch;
  729. int fCouldBeScratch;
  730. int fContent;
  731. const ScratchMap* fScratchMap;
  732. const UniqueHash* fUniqueHash;
  733. Stats(const GrResourceCache* cache) {
  734. memset(this, 0, sizeof(*this));
  735. fScratchMap = &cache->fScratchMap;
  736. fUniqueHash = &cache->fUniqueHash;
  737. }
  738. void update(GrGpuResource* resource) {
  739. fBytes += resource->gpuMemorySize();
  740. if (!resource->resourcePriv().isPurgeable()) {
  741. ++fLocked;
  742. }
  743. const GrScratchKey& scratchKey = resource->resourcePriv().getScratchKey();
  744. const GrUniqueKey& uniqueKey = resource->getUniqueKey();
  745. if (resource->cacheAccess().isScratch()) {
  746. SkASSERT(!uniqueKey.isValid());
  747. ++fScratch;
  748. SkASSERT(fScratchMap->countForKey(scratchKey));
  749. SkASSERT(!resource->resourcePriv().refsWrappedObjects());
  750. } else if (scratchKey.isValid()) {
  751. SkASSERT(GrBudgetedType::kBudgeted != resource->resourcePriv().budgetedType() ||
  752. uniqueKey.isValid());
  753. if (!uniqueKey.isValid()) {
  754. ++fCouldBeScratch;
  755. SkASSERT(fScratchMap->countForKey(scratchKey));
  756. }
  757. SkASSERT(!resource->resourcePriv().refsWrappedObjects());
  758. }
  759. if (uniqueKey.isValid()) {
  760. ++fContent;
  761. SkASSERT(fUniqueHash->find(uniqueKey) == resource);
  762. SkASSERT(GrBudgetedType::kBudgeted == resource->resourcePriv().budgetedType() ||
  763. resource->resourcePriv().refsWrappedObjects());
  764. if (scratchKey.isValid()) {
  765. SkASSERT(!fScratchMap->has(resource, scratchKey));
  766. }
  767. }
  768. if (GrBudgetedType::kBudgeted == resource->resourcePriv().budgetedType()) {
  769. ++fBudgetedCount;
  770. fBudgetedBytes += resource->gpuMemorySize();
  771. }
  772. }
  773. };
  774. {
  775. ScratchMap::ConstIter iter(&fScratchMap);
  776. int count = 0;
  777. for ( ; !iter.done(); ++iter) {
  778. const GrGpuResource* resource = *iter;
  779. SkASSERT(resource->resourcePriv().getScratchKey().isValid());
  780. SkASSERT(!resource->getUniqueKey().isValid());
  781. count++;
  782. }
  783. SkASSERT(count == fScratchMap.count()); // ensure the iterator is working correctly
  784. }
  785. Stats stats(this);
  786. size_t purgeableBytes = 0;
  787. int numBudgetedResourcesFlushWillMakePurgeable = 0;
  788. for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
  789. SkASSERT(!fNonpurgeableResources[i]->resourcePriv().isPurgeable() ||
  790. fNewlyPurgeableResourceForValidation == fNonpurgeableResources[i]);
  791. SkASSERT(*fNonpurgeableResources[i]->cacheAccess().accessCacheIndex() == i);
  792. SkASSERT(!fNonpurgeableResources[i]->wasDestroyed());
  793. if (fNonpurgeableResources[i]->resourcePriv().budgetedType() == GrBudgetedType::kBudgeted &&
  794. !fNonpurgeableResources[i]->cacheAccess().hasRef() &&
  795. fNewlyPurgeableResourceForValidation != fNonpurgeableResources[i]) {
  796. SkASSERT(fNonpurgeableResources[i]->resourcePriv().hasPendingIO_debugOnly());
  797. ++numBudgetedResourcesFlushWillMakePurgeable;
  798. }
  799. stats.update(fNonpurgeableResources[i]);
  800. }
  801. for (int i = 0; i < fPurgeableQueue.count(); ++i) {
  802. SkASSERT(fPurgeableQueue.at(i)->resourcePriv().isPurgeable());
  803. SkASSERT(*fPurgeableQueue.at(i)->cacheAccess().accessCacheIndex() == i);
  804. SkASSERT(!fPurgeableQueue.at(i)->wasDestroyed());
  805. stats.update(fPurgeableQueue.at(i));
  806. purgeableBytes += fPurgeableQueue.at(i)->gpuMemorySize();
  807. }
  808. SkASSERT(fCount == this->getResourceCount());
  809. SkASSERT(fBudgetedCount <= fCount);
  810. SkASSERT(fBudgetedBytes <= fBytes);
  811. SkASSERT(stats.fBytes == fBytes);
  812. SkASSERT(fNumBudgetedResourcesFlushWillMakePurgeable ==
  813. numBudgetedResourcesFlushWillMakePurgeable);
  814. SkASSERT(stats.fBudgetedBytes == fBudgetedBytes);
  815. SkASSERT(stats.fBudgetedCount == fBudgetedCount);
  816. SkASSERT(purgeableBytes == fPurgeableBytes);
  817. #if GR_CACHE_STATS
  818. SkASSERT(fBudgetedHighWaterCount <= fHighWaterCount);
  819. SkASSERT(fBudgetedHighWaterBytes <= fHighWaterBytes);
  820. SkASSERT(fBytes <= fHighWaterBytes);
  821. SkASSERT(fCount <= fHighWaterCount);
  822. SkASSERT(fBudgetedBytes <= fBudgetedHighWaterBytes);
  823. SkASSERT(fBudgetedCount <= fBudgetedHighWaterCount);
  824. #endif
  825. SkASSERT(stats.fContent == fUniqueHash.count());
  826. SkASSERT(stats.fScratch + stats.fCouldBeScratch == fScratchMap.count());
  827. // This assertion is not currently valid because we can be in recursive notifyCntReachedZero()
  828. // calls. This will be fixed when subresource registration is explicit.
  829. // bool overBudget = budgetedBytes > fMaxBytes || budgetedCount > fMaxCount;
  830. // SkASSERT(!overBudget || locked == count || fPurging);
  831. }
  832. bool GrResourceCache::isInCache(const GrGpuResource* resource) const {
  833. int index = *resource->cacheAccess().accessCacheIndex();
  834. if (index < 0) {
  835. return false;
  836. }
  837. if (index < fPurgeableQueue.count() && fPurgeableQueue.at(index) == resource) {
  838. return true;
  839. }
  840. if (index < fNonpurgeableResources.count() && fNonpurgeableResources[index] == resource) {
  841. return true;
  842. }
  843. SkDEBUGFAIL("Resource index should be -1 or the resource should be in the cache.");
  844. return false;
  845. }
  846. #endif