/* * Copyright 2016 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef GrSurfaceProxy_DEFINED #define GrSurfaceProxy_DEFINED #include "include/core/SkRect.h" #include "include/gpu/GrBackendSurface.h" #include "include/gpu/GrGpuResource.h" #include "include/gpu/GrSurface.h" #include "include/gpu/GrTexture.h" #include "include/private/SkNoncopyable.h" #include "src/gpu/GrSwizzle.h" class GrCaps; class GrContext_Base; class GrOpList; class GrRecordingContext; class GrRenderTargetOpList; class GrRenderTargetProxy; class GrResourceProvider; class GrSurfaceContext; class GrSurfaceProxyPriv; class GrTextureOpList; class GrTextureProxy; // This is basically SkRefCntBase except Ganesh uses internalGetProxyRefCnt for more than asserts. class GrIORefProxy : public SkNoncopyable { public: GrIORefProxy() : fRefCnt(1) {} virtual ~GrIORefProxy() {} bool unique() const { SkASSERT(fRefCnt > 0); return 1 == fRefCnt; } void ref() const { SkASSERT(fRefCnt > 0); ++fRefCnt; } void unref() const { SkASSERT(fRefCnt > 0); --fRefCnt; if (0 == fRefCnt) { delete this; } } protected: int32_t internalGetProxyRefCnt() const { return fRefCnt; } private: mutable int32_t fRefCnt; }; class GrSurfaceProxy : public GrIORefProxy { public: /** * Some lazy proxy callbacks want to set their own (or no key) on the GrSurfaces they return. * Others want the GrSurface's key to be kept in sync with the proxy's key. This enum controls * the key relationship between proxies and their targets. */ enum class LazyInstantiationKeyMode { /** * Don't key the GrSurface with the proxy's key. The lazy instantiation callback is free to * return a GrSurface that already has a unique key unrelated to the proxy's key. */ kUnsynced, /** * Keep the GrSurface's unique key in sync with the proxy's unique key. The GrSurface * returned from the lazy instantiation callback must not have a unique key or have the same * same unique key as the proxy. If the proxy is later assigned a key it is in turn assigned * to the GrSurface. */ kSynced }; struct LazyInstantiationResult { LazyInstantiationResult() = default; LazyInstantiationResult(const LazyInstantiationResult&) = default; LazyInstantiationResult(LazyInstantiationResult&& that) = default; LazyInstantiationResult(sk_sp surf, LazyInstantiationKeyMode mode = LazyInstantiationKeyMode::kSynced) : fSurface(std::move(surf)), fKeyMode(mode) {} LazyInstantiationResult(sk_sp tex) : LazyInstantiationResult(sk_sp(std::move(tex))) {} LazyInstantiationResult& operator=(const LazyInstantiationResult&) = default; LazyInstantiationResult& operator=(LazyInstantiationResult&&) = default; sk_sp fSurface; LazyInstantiationKeyMode fKeyMode = LazyInstantiationKeyMode::kSynced; }; using LazyInstantiateCallback = std::function; enum class LazyInstantiationType { kSingleUse, // Instantiation callback is allowed to be called only once. kMultipleUse, // Instantiation callback can be called multiple times. kDeinstantiate, // Instantiation callback can be called multiple times, // but we will deinstantiate the proxy after every flush. }; enum class LazyState { kNot, // The proxy is instantiated or does not have a lazy callback kPartially, // The proxy has a lazy callback but knows basic information about itself. kFully, // The proxy has a lazy callback and also doesn't know its width, height, etc. }; LazyState lazyInstantiationState() const { if (this->isInstantiated() || !SkToBool(fLazyInstantiateCallback)) { return LazyState::kNot; } else { if (fWidth <= 0) { SkASSERT(fHeight <= 0); return LazyState::kFully; } else { SkASSERT(fHeight > 0); return LazyState::kPartially; } } } GrPixelConfig config() const { return fConfig; } int width() const { SkASSERT(LazyState::kFully != this->lazyInstantiationState()); return fWidth; } int height() const { SkASSERT(LazyState::kFully != this->lazyInstantiationState()); return fHeight; } SkISize isize() const { return {fWidth, fHeight}; } int worstCaseWidth() const; int worstCaseHeight() const; /** * Helper that gets the width and height of the surface as a bounding rectangle. */ SkRect getBoundsRect() const { SkASSERT(LazyState::kFully != this->lazyInstantiationState()); return SkRect::MakeIWH(this->width(), this->height()); } /** * Helper that gets the worst case width and height of the surface as a bounding rectangle. */ SkRect getWorstCaseBoundsRect() const { SkASSERT(LazyState::kFully != this->lazyInstantiationState()); return SkRect::MakeIWH(this->worstCaseWidth(), this->worstCaseHeight()); } GrSurfaceOrigin origin() const { SkASSERT(kTopLeft_GrSurfaceOrigin == fOrigin || kBottomLeft_GrSurfaceOrigin == fOrigin); return fOrigin; } const GrSwizzle& textureSwizzle() const { return fTextureSwizzle; } const GrBackendFormat& backendFormat() const { return fFormat; } class UniqueID { public: static UniqueID InvalidID() { return UniqueID(uint32_t(SK_InvalidUniqueID)); } // wrapped explicit UniqueID(const GrGpuResource::UniqueID& id) : fID(id.asUInt()) { } // deferred and lazy-callback UniqueID() : fID(GrGpuResource::CreateUniqueID()) { } uint32_t asUInt() const { return fID; } bool operator==(const UniqueID& other) const { return fID == other.fID; } bool operator!=(const UniqueID& other) const { return !(*this == other); } void makeInvalid() { fID = SK_InvalidUniqueID; } bool isInvalid() const { return SK_InvalidUniqueID == fID; } private: explicit UniqueID(uint32_t id) : fID(id) {} uint32_t fID; }; /* * The contract for the uniqueID is: * for wrapped resources: * the uniqueID will match that of the wrapped resource * * for deferred resources: * the uniqueID will be different from the real resource, when it is allocated * the proxy's uniqueID will not change across the instantiate call * * the uniqueIDs of the proxies and the resources draw from the same pool * * What this boils down to is that the uniqueID of a proxy can be used to consistently * track/identify a proxy but should never be used to distinguish between * resources and proxies - beware! */ UniqueID uniqueID() const { return fUniqueID; } UniqueID underlyingUniqueID() const { if (fTarget) { return UniqueID(fTarget->uniqueID()); } return fUniqueID; } virtual bool instantiate(GrResourceProvider*) = 0; void deinstantiate(); /** * Proxies that are already instantiated and whose backing surface cannot be recycled to * instantiate other proxies do not need to be considered by GrResourceAllocator. */ bool canSkipResourceAllocator() const; /** * @return the texture proxy associated with the surface proxy, may be NULL. */ virtual GrTextureProxy* asTextureProxy() { return nullptr; } virtual const GrTextureProxy* asTextureProxy() const { return nullptr; } /** * @return the render target proxy associated with the surface proxy, may be NULL. */ virtual GrRenderTargetProxy* asRenderTargetProxy() { return nullptr; } virtual const GrRenderTargetProxy* asRenderTargetProxy() const { return nullptr; } bool isInstantiated() const { return SkToBool(fTarget); } // If the proxy is already instantiated, return its backing GrTexture; if not, return null. GrSurface* peekSurface() const { return fTarget.get(); } // If this is a texture proxy and the proxy is already instantiated, return its backing // GrTexture; if not, return null. GrTexture* peekTexture() const { return fTarget ? fTarget->asTexture() : nullptr; } // If this is a render target proxy and the proxy is already instantiated, return its backing // GrRenderTarget; if not, return null. GrRenderTarget* peekRenderTarget() const { return fTarget ? fTarget->asRenderTarget() : nullptr; } /** * Does the resource count against the resource budget? */ SkBudgeted isBudgeted() const { return fBudgeted; } /** * The pixel values of this proxy's surface cannot be modified (e.g. doesn't support write * pixels or MIP map level regen). Read-only proxies also bypass interval tracking and * assignment in GrResourceAllocator. */ bool readOnly() const { return fSurfaceFlags & GrInternalSurfaceFlags::kReadOnly; } void setLastOpList(GrOpList* opList); GrOpList* getLastOpList() { return fLastOpList; } GrRenderTargetOpList* getLastRenderTargetOpList(); GrTextureOpList* getLastTextureOpList(); /** * Retrieves the amount of GPU memory that will be or currently is used by this resource * in bytes. It is approximate since we aren't aware of additional padding or copies made * by the driver. * * @return the amount of GPU memory used in bytes */ size_t gpuMemorySize() const { SkASSERT(LazyState::kFully != this->lazyInstantiationState()); if (fTarget) { return fTarget->gpuMemorySize(); } if (kInvalidGpuMemorySize == fGpuMemorySize) { fGpuMemorySize = this->onUninstantiatedGpuMemorySize(); SkASSERT(kInvalidGpuMemorySize != fGpuMemorySize); } return fGpuMemorySize; } enum class RectsMustMatch : bool { kNo = false, kYes = true }; // Helper function that creates a temporary SurfaceContext to perform the copy // The copy is is not a render target and not multisampled. static sk_sp Copy(GrRecordingContext*, GrSurfaceProxy* src, GrMipMapped, SkIRect srcRect, SkBackingFit, SkBudgeted, RectsMustMatch = RectsMustMatch::kNo); // Copy the entire 'src' static sk_sp Copy(GrRecordingContext*, GrSurfaceProxy* src, GrMipMapped, SkBackingFit, SkBudgeted); #if GR_TEST_UTILS int32_t testingOnly_getBackingRefCnt() const; GrInternalSurfaceFlags testingOnly_getFlags() const; #endif SkDEBUGCODE(void validate(GrContext_Base*) const;) // Provides access to functions that aren't part of the public API. inline GrSurfaceProxyPriv priv(); inline const GrSurfaceProxyPriv priv() const; // Returns true if we are working with protected content. bool isProtected() const { return fIsProtected == GrProtected::kYes; } protected: // Deferred version GrSurfaceProxy(const GrBackendFormat& format, const GrSurfaceDesc& desc, GrRenderable renderable, GrSurfaceOrigin origin, const GrSwizzle& textureSwizzle, SkBackingFit fit, SkBudgeted budgeted, GrProtected isProtected, GrInternalSurfaceFlags surfaceFlags) : GrSurfaceProxy(nullptr, LazyInstantiationType::kSingleUse, format, desc, renderable, origin, textureSwizzle, fit, budgeted, isProtected, surfaceFlags) { // Note: this ctor pulls a new uniqueID from the same pool at the GrGpuResources } // Lazy-callback version GrSurfaceProxy(LazyInstantiateCallback&&, LazyInstantiationType, const GrBackendFormat& format, const GrSurfaceDesc&, GrRenderable, GrSurfaceOrigin, const GrSwizzle& textureSwizzle, SkBackingFit, SkBudgeted, GrProtected, GrInternalSurfaceFlags); // Wrapped version. GrSurfaceProxy(sk_sp, GrSurfaceOrigin, const GrSwizzle& textureSwizzle, SkBackingFit); ~GrSurfaceProxy() override; friend class GrSurfaceProxyPriv; // Methods made available via GrSurfaceProxyPriv bool ignoredByResourceAllocator() const { return fIgnoredByResourceAllocator; } void setIgnoredByResourceAllocator() { fIgnoredByResourceAllocator = true; } int32_t getProxyRefCnt() const { return this->internalGetProxyRefCnt(); } void computeScratchKey(GrScratchKey*) const; virtual sk_sp createSurface(GrResourceProvider*) const = 0; void assign(sk_sp surface); sk_sp createSurfaceImpl(GrResourceProvider*, int sampleCnt, int minStencilSampleCount, GrRenderable, GrMipMapped) const; // Once the size of a fully-lazy proxy is decided, and before it gets instantiated, the client // can use this optional method to specify the proxy's size. (A proxy's size can be less than // the GPU surface that backs it. e.g., SkBackingFit::kApprox.) Otherwise, the proxy's size will // be set to match the underlying GPU surface upon instantiation. void setLazySize(int width, int height) { SkASSERT(GrSurfaceProxy::LazyState::kFully == this->lazyInstantiationState()); SkASSERT(width > 0 && height > 0); fWidth = width; fHeight = height; } bool instantiateImpl(GrResourceProvider* resourceProvider, int sampleCnt, int minStencilSampleCount, GrRenderable, GrMipMapped, const GrUniqueKey*); // For deferred proxies this will be null until the proxy is instantiated. // For wrapped proxies it will point to the wrapped resource. sk_sp fTarget; // In many cases these flags aren't actually known until the proxy has been instantiated. // However, Ganesh frequently needs to change its behavior based on these settings. For // internally create proxies we will know these properties ahead of time. For wrapped // proxies we will copy the properties off of the GrSurface. For lazy proxies we force the // call sites to provide the required information ahead of time. At instantiation time // we verify that the assumed properties match the actual properties. GrInternalSurfaceFlags fSurfaceFlags; private: // For wrapped resources, 'fFormat', 'fConfig', 'fWidth', 'fHeight', and 'fOrigin; will always // be filled in from the wrapped resource. GrBackendFormat fFormat; GrPixelConfig fConfig; int fWidth; int fHeight; GrSurfaceOrigin fOrigin; GrSwizzle fTextureSwizzle; SkBackingFit fFit; // always kApprox for lazy-callback resources // always kExact for wrapped resources mutable SkBudgeted fBudgeted; // always kYes for lazy-callback resources // set from the backing resource for wrapped resources // mutable bc of SkSurface/SkImage wishy-washiness const UniqueID fUniqueID; // set from the backing resource for wrapped resources LazyInstantiateCallback fLazyInstantiateCallback; // If this is set to kSingleuse, then after one call to fLazyInstantiateCallback we will cleanup // the lazy callback and then delete it. This will allow for any refs and resources being held // by the standard function to be released. This is specifically useful in non-dll cases where // we make lazy proxies and instantiate them immediately. // Note: This is ignored if fLazyInstantiateCallback is null. LazyInstantiationType fLazyInstantiationType; SkDEBUGCODE(void validateSurface(const GrSurface*);) SkDEBUGCODE(virtual void onValidateSurface(const GrSurface*) = 0;) static const size_t kInvalidGpuMemorySize = ~static_cast(0); SkDEBUGCODE(size_t getRawGpuMemorySize_debugOnly() const { return fGpuMemorySize; }) virtual size_t onUninstantiatedGpuMemorySize() const = 0; bool fIgnoredByResourceAllocator = false; GrProtected fIsProtected; // This entry is lazily evaluated so, when the proxy wraps a resource, the resource // will be called but, when the proxy is deferred, it will compute the answer itself. // If the proxy computes its own answer that answer is checked (in debug mode) in // the instantiation method. mutable size_t fGpuMemorySize; // The last opList that wrote to or is currently going to write to this surface // The opList can be closed (e.g., no surface context is currently bound // to this proxy). // This back-pointer is required so that we can add a dependancy between // the opList used to create the current contents of this surface // and the opList of a destination surface to which this one is being drawn or copied. // This pointer is unreffed. OpLists own a ref on their surface proxies. GrOpList* fLastOpList; typedef GrIORefProxy INHERITED; }; #endif