1 /* 2 * Copyright 2016 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 #ifndef GrSurfaceProxy_DEFINED 9 #define GrSurfaceProxy_DEFINED 10 11 #include "include/core/SkRect.h" 12 #include "include/core/SkRefCnt.h" 13 #include "include/core/SkSize.h" 14 #include "include/core/SkString.h" 15 #include "include/core/SkTypes.h" 16 #include "include/gpu/ganesh/GrBackendSurface.h" 17 #include "include/gpu/ganesh/GrTypes.h" 18 #include "include/private/base/SkDebug.h" 19 #include "include/private/base/SkMacros.h" 20 #include "include/private/base/SkTo.h" 21 #include "include/private/gpu/ganesh/GrTypesPriv.h" 22 #include "src/gpu/ResourceKey.h" 23 #include "src/gpu/ganesh/GrGpuResource.h" 24 #include "src/gpu/ganesh/GrSurface.h" 25 26 #include <atomic> 27 #include <cstddef> 28 #include <cstdint> 29 #include <functional> 30 #include <string> 31 #include <string_view> 32 #include <utility> 33 34 class GrCaps; 35 class GrContext_Base; 36 class GrRecordingContext; 37 class GrRenderTarget; 38 class GrRenderTargetProxy; 39 class GrRenderTask; 40 class GrResourceProvider; 41 class GrSurfaceProxyPriv; 42 class GrTexture; 43 class GrTextureProxy; 44 enum class SkBackingFit; 45 namespace skgpu { 46 enum class Budgeted : bool; 47 enum class Mipmapped : bool; 48 } 49 50 class GrSurfaceProxy : public SkNVRefCnt<GrSurfaceProxy> { 51 public: 52 virtual ~GrSurfaceProxy(); 53 54 /** 55 * Indicates "resolutions" that need to be done on a surface before its pixels can be accessed. 56 * If both types of resolve are requested, the MSAA resolve will happen first. 57 */ 58 enum class ResolveFlags { 59 kNone = 0, 60 kMSAA = 1 << 0, // Blit and resolve an internal MSAA render buffer into the texture. 61 kMipMaps = 1 << 1, // Regenerate all mipmap levels. 62 }; 63 64 /** 65 * Some lazy proxy callbacks want to set their own (or no key) on the GrSurfaces they return. 66 * Others want the GrSurface's key to be kept in sync with the proxy's key. This enum controls 67 * the key relationship between proxies and their targets. 68 */ 69 enum class LazyInstantiationKeyMode { 70 /** 71 * Don't key the GrSurface with the proxy's key. The lazy instantiation callback is free to 72 * return a GrSurface that already has a unique key unrelated to the proxy's key. 73 */ 74 kUnsynced, 75 /** 76 * Keep the GrSurface's unique key in sync with the proxy's unique key. The GrSurface 77 * returned from the lazy instantiation callback must not have a unique key or have the same 78 * same unique key as the proxy. If the proxy is later assigned a key it is in turn assigned 79 * to the GrSurface. 80 */ 81 kSynced 82 }; 83 84 /** 85 * Specifies the expected properties of the GrSurface returned by a lazy instantiation 86 * callback. The dimensions will be negative in the case of a fully lazy proxy. 87 */ 88 struct LazySurfaceDesc { 89 SkISize fDimensions; 90 SkBackingFit fFit; 91 GrRenderable fRenderable; 92 skgpu::Mipmapped fMipmapped; 93 int fSampleCnt; 94 const GrBackendFormat& fFormat; 95 GrTextureType fTextureType; 96 GrProtected fProtected; 97 skgpu::Budgeted fBudgeted; 98 std::string_view fLabel; 99 }; 100 101 struct LazyCallbackResult { 102 LazyCallbackResult() = default; 103 LazyCallbackResult(const LazyCallbackResult&) = default; 104 LazyCallbackResult(LazyCallbackResult&& that) = default; 105 LazyCallbackResult(sk_sp<GrSurface> surf, 106 bool releaseCallback = true, 107 LazyInstantiationKeyMode mode = LazyInstantiationKeyMode::kSynced); 108 LazyCallbackResult(sk_sp<GrTexture> tex); 109 110 LazyCallbackResult& operator=(const LazyCallbackResult&) = default; 111 LazyCallbackResult& operator=(LazyCallbackResult&&) = default; 112 113 sk_sp<GrSurface> fSurface; 114 LazyInstantiationKeyMode fKeyMode = LazyInstantiationKeyMode::kSynced; 115 /** 116 * Should the callback be disposed of after it has returned or preserved until the proxy 117 * is freed. Only honored if fSurface is not-null. If it is null the callback is preserved. 118 */ 119 bool fReleaseCallback = true; 120 }; 121 122 using LazyInstantiateCallback = 123 std::function<LazyCallbackResult(GrResourceProvider*, const LazySurfaceDesc&)>; 124 125 enum class UseAllocator { 126 /** 127 * This proxy will be instantiated outside the allocator (e.g. for proxies that are 128 * instantiated in on-flush callbacks). 129 */ 130 kNo = false, 131 /** 132 * GrResourceAllocator should instantiate this proxy. 133 */ 134 kYes = true, 135 }; 136 isLazy()137 bool isLazy() const { return !this->isInstantiated() && SkToBool(fLazyInstantiateCallback); } 138 isFullyLazy()139 bool isFullyLazy() const { 140 bool result = fDimensions.width() < 0; 141 SkASSERT(result == (fDimensions.height() < 0)); 142 SkASSERT(!result || this->isLazy()); 143 return result; 144 } 145 dimensions()146 SkISize dimensions() const { 147 SkASSERT(!this->isFullyLazy()); 148 return fDimensions; 149 } width()150 int width() const { return this->dimensions().width(); } height()151 int height() const { return this->dimensions().height(); } 152 153 SkISize backingStoreDimensions() const; 154 155 /** 156 * Helper that gets the width and height of the proxy as a bounding rectangle. 157 */ getBoundsRect()158 SkRect getBoundsRect() const { return SkRect::Make(this->dimensions()); } 159 160 /* A perhaps faster check for this->dimensions() == this->backingStoreDimensions(). */ 161 bool isFunctionallyExact() const; 162 163 /** 164 * Helper that gets the dimensions the backing GrSurface will have as a bounding rectangle. 165 */ backingStoreBoundsRect()166 SkRect backingStoreBoundsRect() const { 167 return SkRect::Make(this->backingStoreDimensions()); 168 } 169 backingStoreBoundsIRect()170 SkIRect backingStoreBoundsIRect() const { 171 return SkIRect::MakeSize(this->backingStoreDimensions()); 172 } 173 backendFormat()174 const GrBackendFormat& backendFormat() const { return fFormat; } 175 176 bool isFormatCompressed(const GrCaps*) const; 177 178 class UniqueID { 179 public: InvalidID()180 static UniqueID InvalidID() { 181 return UniqueID(uint32_t(SK_InvalidUniqueID)); 182 } 183 184 // wrapped UniqueID(const GrGpuResource::UniqueID & id)185 explicit UniqueID(const GrGpuResource::UniqueID& id) : fID(id.asUInt()) { } 186 // deferred and lazy-callback UniqueID()187 UniqueID() : fID(GrGpuResource::CreateUniqueID()) { } 188 asUInt()189 uint32_t asUInt() const { return fID; } 190 191 bool operator==(const UniqueID& other) const { 192 return fID == other.fID; 193 } 194 bool operator!=(const UniqueID& other) const { 195 return !(*this == other); 196 } 197 makeInvalid()198 void makeInvalid() { fID = SK_InvalidUniqueID; } isInvalid()199 bool isInvalid() const { return SK_InvalidUniqueID == fID; } 200 201 private: UniqueID(uint32_t id)202 explicit UniqueID(uint32_t id) : fID(id) {} 203 204 uint32_t fID; 205 }; 206 207 /* 208 * The contract for the uniqueID is: 209 * for wrapped resources: 210 * the uniqueID will match that of the wrapped resource 211 * 212 * for deferred resources: 213 * the uniqueID will be different from the real resource, when it is allocated 214 * the proxy's uniqueID will not change across the instantiate call 215 * 216 * the uniqueIDs of the proxies and the resources draw from the same pool 217 * 218 * What this boils down to is that the uniqueID of a proxy can be used to consistently 219 * track/identify a proxy but should never be used to distinguish between 220 * resources and proxies - beware! 221 */ uniqueID()222 UniqueID uniqueID() const { return fUniqueID; } 223 underlyingUniqueID()224 UniqueID underlyingUniqueID() const { 225 if (fTarget) { 226 return UniqueID(fTarget->uniqueID()); 227 } 228 229 return fUniqueID; 230 } 231 232 virtual bool instantiate(GrResourceProvider*) = 0; 233 234 void deinstantiate(); 235 236 /** 237 * Proxies that are already instantiated and whose backing surface cannot be recycled to 238 * instantiate other proxies do not need to be considered by GrResourceAllocator. 239 */ 240 bool canSkipResourceAllocator() const; 241 242 /** 243 * @return the texture proxy associated with the surface proxy, may be NULL. 244 */ asTextureProxy()245 virtual GrTextureProxy* asTextureProxy() { return nullptr; } asTextureProxy()246 virtual const GrTextureProxy* asTextureProxy() const { return nullptr; } 247 248 /** 249 * @return the render target proxy associated with the surface proxy, may be NULL. 250 */ asRenderTargetProxy()251 virtual GrRenderTargetProxy* asRenderTargetProxy() { return nullptr; } asRenderTargetProxy()252 virtual const GrRenderTargetProxy* asRenderTargetProxy() const { return nullptr; } 253 254 /** @return The unique key for this proxy. May be invalid. */ getUniqueKey()255 virtual const skgpu::UniqueKey& getUniqueKey() const { 256 // Base class never has a valid unique key. 257 static const skgpu::UniqueKey kInvalidKey; 258 return kInvalidKey; 259 } 260 isInstantiated()261 bool isInstantiated() const { return SkToBool(fTarget); } 262 263 /** Called when this task becomes a target of a GrRenderTask. */ isUsedAsTaskTarget()264 void isUsedAsTaskTarget() { ++fTaskTargetCount; } 265 266 /** How many render tasks has this proxy been the target of? */ getTaskTargetCount()267 int getTaskTargetCount() const { return fTaskTargetCount; } 268 269 // If the proxy is already instantiated, return its backing GrTexture; if not, return null. peekSurface()270 GrSurface* peekSurface() const { return fTarget.get(); } 271 272 // If this is a texture proxy and the proxy is already instantiated, return its backing 273 // GrTexture; if not, return null. peekTexture()274 GrTexture* peekTexture() const { return fTarget ? fTarget->asTexture() : nullptr; } 275 276 // If this is a render target proxy and the proxy is already instantiated, return its backing 277 // GrRenderTarget; if not, return null. peekRenderTarget()278 GrRenderTarget* peekRenderTarget() const { 279 return fTarget ? fTarget->asRenderTarget() : nullptr; 280 } 281 282 /** 283 * Does the resource count against the resource budget? 284 */ isBudgeted()285 skgpu::Budgeted isBudgeted() const { return fBudgeted; } 286 287 /** 288 * The pixel values of this proxy's surface cannot be modified (e.g. doesn't support write 289 * pixels or MIP map level regen). Read-only proxies also bypass interval tracking and 290 * assignment in GrResourceAllocator. 291 */ readOnly()292 bool readOnly() const { return fSurfaceFlags & GrInternalSurfaceFlags::kReadOnly; } framebufferOnly()293 bool framebufferOnly() const { 294 return fSurfaceFlags & GrInternalSurfaceFlags::kFramebufferOnly; 295 } 296 297 /** 298 * This means surface is a multisampled render target, and internally holds a non-msaa texture 299 * for resolving into. The render target resolves itself by blitting into this internal texture. 300 * (asTexture() might or might not return the internal texture, but if it does, we always 301 * resolve the render target before accessing this texture's data.) 302 */ requiresManualMSAAResolve()303 bool requiresManualMSAAResolve() const { 304 return fSurfaceFlags & GrInternalSurfaceFlags::kRequiresManualMSAAResolve; 305 } 306 307 /** 308 * Retrieves the amount of GPU memory that will be or currently is used by this resource 309 * in bytes. It is approximate since we aren't aware of additional padding or copies made 310 * by the driver. 311 * 312 * @return the amount of GPU memory used in bytes 313 */ gpuMemorySize()314 size_t gpuMemorySize() const { 315 SkASSERT(!this->isFullyLazy()); 316 if (kInvalidGpuMemorySize == fGpuMemorySize) { 317 fGpuMemorySize = this->onUninstantiatedGpuMemorySize(); 318 SkASSERT(kInvalidGpuMemorySize != fGpuMemorySize); 319 } 320 return fGpuMemorySize; 321 } 322 getLabel()323 std::string_view getLabel() const { return fLabel; } 324 325 enum class RectsMustMatch : bool { 326 kNo = false, 327 kYes = true 328 }; 329 330 // Helper function that creates a temporary SurfaceContext to perform the copy 331 // The copy is is not a render target and not multisampled. 332 // 333 // The intended use of this copy call is simply to copy exact pixel values from one proxy to a 334 // new one. Thus, there isn't a need for a swizzle when doing the copy. The format of the copy 335 // will be the same as the src. Therefore, the copy can be used in a view with the same swizzle 336 // as the original for use with a given color type. 337 // 338 // Optionally gets the render task that performs the copy. If it is later determined that the 339 // copy is not neccessaru then the task can be marked skippable using GrRenderTask::canSkip() and 340 // the copy will be elided. 341 static sk_sp<GrSurfaceProxy> Copy(GrRecordingContext*, 342 sk_sp<GrSurfaceProxy> src, 343 GrSurfaceOrigin, 344 skgpu::Mipmapped, 345 SkIRect srcRect, 346 SkBackingFit, 347 skgpu::Budgeted, 348 std::string_view label, 349 RectsMustMatch = RectsMustMatch::kNo, 350 sk_sp<GrRenderTask>* outTask = nullptr); 351 352 // Same as above Copy but copies the entire 'src' 353 static sk_sp<GrSurfaceProxy> Copy(GrRecordingContext*, 354 sk_sp<GrSurfaceProxy> src, 355 GrSurfaceOrigin, 356 skgpu::Mipmapped, 357 SkBackingFit, 358 skgpu::Budgeted, 359 std::string_view label, 360 sk_sp<GrRenderTask>* outTask = nullptr); 361 362 #if defined(GPU_TEST_UTILS) 363 int32_t testingOnly_getBackingRefCnt() const; 364 GrInternalSurfaceFlags testingOnly_getFlags() const; 365 SkString dump() const; 366 #endif 367 368 #ifdef SK_DEBUG 369 void validate(GrContext_Base*) const; getDebugName()370 SkString getDebugName() { 371 return fDebugName.isEmpty() ? SkStringPrintf("%u", this->uniqueID().asUInt()) : fDebugName; 372 } setDebugName(SkString name)373 void setDebugName(SkString name) { fDebugName = std::move(name); } 374 #endif 375 376 // Provides access to functions that aren't part of the public API. 377 inline GrSurfaceProxyPriv priv(); 378 inline const GrSurfaceProxyPriv priv() const; // NOLINT(readability-const-return-type) 379 isDDLTarget()380 bool isDDLTarget() const { return fIsDDLTarget; } 381 isProtected()382 GrProtected isProtected() const { return fIsProtected; } 383 isPromiseProxy()384 bool isPromiseProxy() { return fIsPromiseProxy; } 385 386 protected: 387 // Deferred version - takes a new UniqueID from the shared resource/proxy pool. 388 GrSurfaceProxy(const GrBackendFormat&, 389 SkISize, 390 SkBackingFit, 391 skgpu::Budgeted, 392 GrProtected, 393 GrInternalSurfaceFlags, 394 UseAllocator, 395 std::string_view label); 396 // Lazy-callback version - takes a new UniqueID from the shared resource/proxy pool. 397 GrSurfaceProxy(LazyInstantiateCallback&&, 398 const GrBackendFormat&, 399 SkISize, 400 SkBackingFit, 401 skgpu::Budgeted, 402 GrProtected, 403 GrInternalSurfaceFlags, 404 UseAllocator, 405 std::string_view label); 406 407 // Wrapped version - shares the UniqueID of the passed surface. 408 // Takes UseAllocator because even though this is already instantiated it still can participate 409 // in allocation by having its backing resource recycled to other uninstantiated proxies or 410 // not depending on UseAllocator. 411 GrSurfaceProxy(sk_sp<GrSurface>, SkBackingFit, UseAllocator); 412 413 friend class GrSurfaceProxyPriv; 414 415 // Methods made available via GrSurfaceProxyPriv ignoredByResourceAllocator()416 bool ignoredByResourceAllocator() const { return fIgnoredByResourceAllocator; } setIgnoredByResourceAllocator()417 void setIgnoredByResourceAllocator() { fIgnoredByResourceAllocator = true; } 418 419 void computeScratchKey(const GrCaps&, skgpu::ScratchKey*) const; 420 421 virtual sk_sp<GrSurface> createSurface(GrResourceProvider*) const = 0; 422 void assign(sk_sp<GrSurface> surface); 423 424 sk_sp<GrSurface> createSurfaceImpl(GrResourceProvider*, 425 int sampleCnt, 426 GrRenderable, 427 skgpu::Mipmapped) const; 428 429 // Once the dimensions of a fully-lazy proxy are decided, and before it gets instantiated, the 430 // client can use this optional method to specify the proxy's dimensions. (A proxy's dimensions 431 // can be less than the GPU surface that backs it. e.g., SkBackingFit::kApprox.) Otherwise, 432 // the proxy's dimensions will be set to match the underlying GPU surface upon instantiation. setLazyDimensions(SkISize dimensions)433 void setLazyDimensions(SkISize dimensions) { 434 SkASSERT(this->isFullyLazy()); 435 SkASSERT(!dimensions.isEmpty()); 436 fDimensions = dimensions; 437 } 438 439 bool instantiateImpl(GrResourceProvider* resourceProvider, 440 int sampleCnt, 441 GrRenderable, 442 skgpu::Mipmapped, 443 const skgpu::UniqueKey*); 444 445 // For deferred proxies this will be null until the proxy is instantiated. 446 // For wrapped proxies it will point to the wrapped resource. 447 sk_sp<GrSurface> fTarget; 448 449 // In many cases these flags aren't actually known until the proxy has been instantiated. 450 // However, Ganesh frequently needs to change its behavior based on these settings. For 451 // internally create proxies we will know these properties ahead of time. For wrapped 452 // proxies we will copy the properties off of the GrSurface. For lazy proxies we force the 453 // call sites to provide the required information ahead of time. At instantiation time 454 // we verify that the assumed properties match the actual properties. 455 GrInternalSurfaceFlags fSurfaceFlags; 456 457 private: 458 // For wrapped resources, 'fFormat' and 'fDimensions' will always be filled in from the 459 // wrapped resource. 460 const GrBackendFormat fFormat; 461 SkISize fDimensions; 462 463 SkBackingFit fFit; // always kApprox for lazy-callback resources 464 // always kExact for wrapped resources 465 mutable skgpu::Budgeted fBudgeted; // always kYes for lazy-callback resources 466 // set from the backing resource for wrapped resources 467 // mutable bc of SkSurface/SkImage wishy-washiness 468 // Only meaningful if fLazyInstantiateCallback is non-null. 469 UseAllocator fUseAllocator; 470 471 const UniqueID fUniqueID; // set from the backing resource for wrapped resources 472 473 LazyInstantiateCallback fLazyInstantiateCallback; 474 475 SkDEBUGCODE(void validateSurface(const GrSurface*);) 476 SkDEBUGCODE(virtual void onValidateSurface(const GrSurface*) = 0;) 477 478 static const size_t kInvalidGpuMemorySize = ~static_cast<size_t>(0); 479 SkDEBUGCODE(size_t getRawGpuMemorySize_debugOnly() const { return fGpuMemorySize; }) 480 481 virtual size_t onUninstantiatedGpuMemorySize() const = 0; 482 483 virtual LazySurfaceDesc callbackDesc() const = 0; 484 485 bool fIgnoredByResourceAllocator = false; 486 bool fIsDDLTarget = false; 487 bool fIsPromiseProxy = false; 488 GrProtected fIsProtected; 489 490 int fTaskTargetCount = 0; 491 492 const std::string fLabel; 493 494 // This entry is lazily evaluated so, when the proxy wraps a resource, the resource 495 // will be called but, when the proxy is deferred, it will compute the answer itself. 496 // If the proxy computes its own answer that answer is checked (in debug mode) in 497 // the instantiation method. The image may be shared between threads, hence atomic. 498 mutable std::atomic<size_t> fGpuMemorySize{kInvalidGpuMemorySize}; 499 SkDEBUGCODE(SkString fDebugName;) 500 }; 501 502 SK_MAKE_BITFIELD_CLASS_OPS(GrSurfaceProxy::ResolveFlags) 503 504 #endif 505