1 /* 2 * Copyright 2011 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 8 #include "GrGpuResource.h" 9 #include "GrContext.h" 10 #include "GrContextPriv.h" 11 #include "GrResourceCache.h" 12 #include "GrGpu.h" 13 #include "GrGpuResourcePriv.h" 14 #include "SkTraceMemoryDump.h" 15 #include <atomic> 16 17 static inline GrResourceCache* get_resource_cache(GrGpu* gpu) { 18 SkASSERT(gpu); 19 SkASSERT(gpu->getContext()); 20 SkASSERT(gpu->getContext()->contextPriv().getResourceCache()); 21 return gpu->getContext()->contextPriv().getResourceCache(); 22 } 23 24 GrGpuResource::GrGpuResource(GrGpu* gpu) : fGpu(gpu), fUniqueID(CreateUniqueID()) { 25 SkDEBUGCODE(fCacheArrayIndex = -1); 26 } 27 28 void GrGpuResource::registerWithCache(SkBudgeted budgeted) { 29 SkASSERT(fBudgetedType == GrBudgetedType::kUnbudgetedUncacheable); 30 fBudgetedType = budgeted == SkBudgeted::kYes ? GrBudgetedType::kBudgeted 31 : GrBudgetedType::kUnbudgetedUncacheable; 32 this->computeScratchKey(&fScratchKey); 33 get_resource_cache(fGpu)->resourceAccess().insertResource(this); 34 } 35 36 void GrGpuResource::registerWithCacheWrapped(GrWrapCacheable wrapType) { 37 SkASSERT(fBudgetedType == GrBudgetedType::kUnbudgetedUncacheable); 38 // Resources referencing wrapped objects are never budgeted. They may be cached or uncached. 39 fBudgetedType = wrapType == GrWrapCacheable::kNo ? GrBudgetedType::kUnbudgetedUncacheable 40 : GrBudgetedType::kUnbudgetedCacheable; 41 fRefsWrappedObjects = true; 42 get_resource_cache(fGpu)->resourceAccess().insertResource(this); 43 } 44 45 GrGpuResource::~GrGpuResource() { 46 // The cache should have released or destroyed this resource. 47 SkASSERT(this->wasDestroyed()); 48 } 49 50 void GrGpuResource::release() { 51 SkASSERT(fGpu); 52 this->onRelease(); 53 get_resource_cache(fGpu)->resourceAccess().removeResource(this); 54 fGpu = nullptr; 55 fGpuMemorySize = 0; 56 } 57 58 void GrGpuResource::abandon() { 59 if (this->wasDestroyed()) { 60 return; 61 } 62 SkASSERT(fGpu); 63 this->onAbandon(); 64 get_resource_cache(fGpu)->resourceAccess().removeResource(this); 65 fGpu = nullptr; 66 fGpuMemorySize = 0; 67 } 68 69 void GrGpuResource::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const { 70 if (this->fRefsWrappedObjects && !traceMemoryDump->shouldDumpWrappedObjects()) { 71 return; 72 } 73 74 this->dumpMemoryStatisticsPriv(traceMemoryDump, this->getResourceName(), 75 this->getResourceType(), this->gpuMemorySize()); 76 } 77 78 void GrGpuResource::dumpMemoryStatisticsPriv(SkTraceMemoryDump* traceMemoryDump, 79 const SkString& resourceName, 80 const char* type, size_t size) const { 81 const char* tag = "Scratch"; 82 if (fUniqueKey.isValid()) { 83 tag = (fUniqueKey.tag() != nullptr) ? fUniqueKey.tag() : "Other"; 84 } 85 86 traceMemoryDump->dumpNumericValue(resourceName.c_str(), "size", "bytes", size); 87 traceMemoryDump->dumpStringValue(resourceName.c_str(), "type", type); 88 traceMemoryDump->dumpStringValue(resourceName.c_str(), "category", tag); 89 if (this->isPurgeable()) { 90 traceMemoryDump->dumpNumericValue(resourceName.c_str(), "purgeable_size", "bytes", size); 91 } 92 93 this->setMemoryBacking(traceMemoryDump, resourceName); 94 } 95 96 bool GrGpuResource::isPurgeable() const { 97 // Resources in the kUnbudgetedCacheable state are never purgeable when they have a unique 98 // key. The key must be removed/invalidated to make them purgeable. 99 return !this->hasRefOrPendingIO() && 100 !(fBudgetedType == GrBudgetedType::kUnbudgetedCacheable && fUniqueKey.isValid()); 101 } 102 103 bool GrGpuResource::hasRefOrPendingIO() const { 104 return this->internalHasRef() || this->internalHasPendingIO(); 105 } 106 107 SkString GrGpuResource::getResourceName() const { 108 // Dump resource as "skia/gpu_resources/resource_#". 109 SkString resourceName("skia/gpu_resources/resource_"); 110 resourceName.appendU32(this->uniqueID().asUInt()); 111 return resourceName; 112 } 113 114 const GrContext* GrGpuResource::getContext() const { 115 if (fGpu) { 116 return fGpu->getContext(); 117 } else { 118 return nullptr; 119 } 120 } 121 122 GrContext* GrGpuResource::getContext() { 123 if (fGpu) { 124 return fGpu->getContext(); 125 } else { 126 return nullptr; 127 } 128 } 129 130 void GrGpuResource::removeUniqueKey() { 131 if (this->wasDestroyed()) { 132 return; 133 } 134 SkASSERT(fUniqueKey.isValid()); 135 get_resource_cache(fGpu)->resourceAccess().removeUniqueKey(this); 136 } 137 138 void GrGpuResource::setUniqueKey(const GrUniqueKey& key) { 139 SkASSERT(this->internalHasRef()); 140 SkASSERT(key.isValid()); 141 142 // Uncached resources can never have a unique key, unless they're wrapped resources. Wrapped 143 // resources are a special case: the unique keys give us a weak ref so that we can reuse the 144 // same resource (rather than re-wrapping). When a wrapped resource is no longer referenced, 145 // it will always be released - it is never converted to a scratch resource. 146 if (this->resourcePriv().budgetedType() != GrBudgetedType::kBudgeted && 147 !this->fRefsWrappedObjects) { 148 return; 149 } 150 151 if (this->wasDestroyed()) { 152 return; 153 } 154 155 get_resource_cache(fGpu)->resourceAccess().changeUniqueKey(this, key); 156 } 157 158 void GrGpuResource::notifyAllCntsAreZero(CntType lastCntTypeToReachZero) const { 159 GrGpuResource* mutableThis = const_cast<GrGpuResource*>(this); 160 mutableThis->removedLastRefOrPendingIO(); 161 if (this->wasDestroyed()) { 162 // We've already been removed from the cache. Goodbye cruel world! 163 delete this; 164 return; 165 } 166 167 // We should have already handled this fully in notifyRefCntIsZero(). 168 SkASSERT(kRef_CntType != lastCntTypeToReachZero); 169 170 static const uint32_t kFlag = 171 GrResourceCache::ResourceAccess::kAllCntsReachedZero_RefNotificationFlag; 172 get_resource_cache(fGpu)->resourceAccess().notifyCntReachedZero(mutableThis, kFlag); 173 } 174 175 bool GrGpuResource::notifyRefCountIsZero() const { 176 if (this->wasDestroyed()) { 177 // handle this in notifyAllCntsAreZero(). 178 return true; 179 } 180 181 GrGpuResource* mutableThis = const_cast<GrGpuResource*>(this); 182 uint32_t flags = GrResourceCache::ResourceAccess::kRefCntReachedZero_RefNotificationFlag; 183 if (!this->internalHasPendingIO()) { 184 flags |= GrResourceCache::ResourceAccess::kAllCntsReachedZero_RefNotificationFlag; 185 mutableThis->removedLastRefOrPendingIO(); 186 } 187 get_resource_cache(fGpu)->resourceAccess().notifyCntReachedZero(mutableThis, flags); 188 189 // There is no need to call our notifyAllCntsAreZero function at this point since we already 190 // told the cache about the state of cnts. 191 return false; 192 } 193 194 void GrGpuResource::removeScratchKey() { 195 if (!this->wasDestroyed() && fScratchKey.isValid()) { 196 get_resource_cache(fGpu)->resourceAccess().willRemoveScratchKey(this); 197 fScratchKey.reset(); 198 } 199 } 200 201 void GrGpuResource::makeBudgeted() { 202 // We should never make a wrapped resource budgeted. 203 SkASSERT(!fRefsWrappedObjects); 204 // Only wrapped resources can be in the kUnbudgetedCacheable state. 205 SkASSERT(fBudgetedType != GrBudgetedType::kUnbudgetedCacheable); 206 if (!this->wasDestroyed() && fBudgetedType == GrBudgetedType::kUnbudgetedUncacheable) { 207 // Currently resources referencing wrapped objects are not budgeted. 208 fBudgetedType = GrBudgetedType::kBudgeted; 209 get_resource_cache(fGpu)->resourceAccess().didChangeBudgetStatus(this); 210 } 211 } 212 213 void GrGpuResource::makeUnbudgeted() { 214 if (!this->wasDestroyed() && fBudgetedType == GrBudgetedType::kBudgeted && 215 !fUniqueKey.isValid()) { 216 fBudgetedType = GrBudgetedType::kUnbudgetedUncacheable; 217 get_resource_cache(fGpu)->resourceAccess().didChangeBudgetStatus(this); 218 } 219 } 220 221 uint32_t GrGpuResource::CreateUniqueID() { 222 static std::atomic<uint32_t> nextID{1}; 223 uint32_t id; 224 do { 225 id = nextID++; 226 } while (id == SK_InvalidUniqueID); 227 return id; 228 } 229