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()->priv().getResourceCache()); 21 return gpu->getContext()->priv().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::notifyAllCntsWillBeZero() const { 159 GrGpuResource* mutableThis = const_cast<GrGpuResource*>(this); 160 mutableThis->willRemoveLastRefOrPendingIO(); 161 } 162 163 void GrGpuResource::notifyAllCntsAreZero(CntType lastCntTypeToReachZero) const { 164 if (this->wasDestroyed()) { 165 // We've already been removed from the cache. Goodbye cruel world! 166 delete this; 167 return; 168 } 169 170 // We should have already handled this fully in notifyRefCntIsZero(). 171 SkASSERT(kRef_CntType != lastCntTypeToReachZero); 172 173 static const uint32_t kFlag = 174 GrResourceCache::ResourceAccess::kAllCntsReachedZero_RefNotificationFlag; 175 GrGpuResource* mutableThis = const_cast<GrGpuResource*>(this); 176 get_resource_cache(fGpu)->resourceAccess().notifyCntReachedZero(mutableThis, kFlag); 177 } 178 179 bool GrGpuResource::notifyRefCountIsZero() const { 180 if (this->wasDestroyed()) { 181 // handle this in notifyAllCntsAreZero(). 182 return true; 183 } 184 185 GrGpuResource* mutableThis = const_cast<GrGpuResource*>(this); 186 uint32_t flags = GrResourceCache::ResourceAccess::kRefCntReachedZero_RefNotificationFlag; 187 if (!this->internalHasPendingIO()) { 188 flags |= GrResourceCache::ResourceAccess::kAllCntsReachedZero_RefNotificationFlag; 189 } 190 get_resource_cache(fGpu)->resourceAccess().notifyCntReachedZero(mutableThis, flags); 191 192 // There is no need to call our notifyAllCntsAreZero function at this point since we already 193 // told the cache about the state of cnts. 194 return false; 195 } 196 197 void GrGpuResource::removeScratchKey() { 198 if (!this->wasDestroyed() && fScratchKey.isValid()) { 199 get_resource_cache(fGpu)->resourceAccess().willRemoveScratchKey(this); 200 fScratchKey.reset(); 201 } 202 } 203 204 void GrGpuResource::makeBudgeted() { 205 // We should never make a wrapped resource budgeted. 206 SkASSERT(!fRefsWrappedObjects); 207 // Only wrapped resources can be in the kUnbudgetedCacheable state. 208 SkASSERT(fBudgetedType != GrBudgetedType::kUnbudgetedCacheable); 209 if (!this->wasDestroyed() && fBudgetedType == GrBudgetedType::kUnbudgetedUncacheable) { 210 // Currently resources referencing wrapped objects are not budgeted. 211 fBudgetedType = GrBudgetedType::kBudgeted; 212 get_resource_cache(fGpu)->resourceAccess().didChangeBudgetStatus(this); 213 } 214 } 215 216 void GrGpuResource::makeUnbudgeted() { 217 if (!this->wasDestroyed() && fBudgetedType == GrBudgetedType::kBudgeted && 218 !fUniqueKey.isValid()) { 219 fBudgetedType = GrBudgetedType::kUnbudgetedUncacheable; 220 get_resource_cache(fGpu)->resourceAccess().didChangeBudgetStatus(this); 221 } 222 } 223 224 uint32_t GrGpuResource::CreateUniqueID() { 225 static std::atomic<uint32_t> nextID{1}; 226 uint32_t id; 227 do { 228 id = nextID++; 229 } while (id == SK_InvalidUniqueID); 230 return id; 231 } 232