/* * 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 "GrGpuResource.h" #include "GrSurface.h" #include "SkRect.h" class GrCaps; class GrOpList; class GrTextureProvider; class GrTextureProxy; class GrRenderTargetProxy; class GrTextureProvider; // This class replicates the functionality GrIORef but tracks the // utilitization for later resource allocation (for the deferred case) and // forwards on the utilization in the wrapped case class GrIORefProxy : public SkNoncopyable { public: void ref() const { this->validate(); ++fRefCnt; if (fTarget) { fTarget->ref(); } } void unref() const { this->validate(); if (fTarget) { fTarget->unref(); } if (!(--fRefCnt)) { delete this; return; } this->validate(); } void validate() const { #ifdef SK_DEBUG SkASSERT(fRefCnt >= 0); #endif } protected: GrIORefProxy() : fRefCnt(1), fTarget(nullptr) {} GrIORefProxy(sk_sp surface) : fRefCnt(1) { // Since we're manually forwarding on refs & unrefs we don't want sk_sp doing // anything extra. fTarget = surface.release(); } virtual ~GrIORefProxy() { // We don't unref 'fTarget' here since the 'unref' method will already // have forwarded on the unref call that got use here. } // TODO: add the IO ref counts. Although if we can delay shader creation to flush time // we may not even need to do that. mutable int32_t fRefCnt; // For deferred proxies this will be null. For wrapped proxies it will point to the // wrapped resource. GrSurface* fTarget; }; class GrSurfaceProxy : public GrIORefProxy { public: static sk_sp MakeWrapped(sk_sp); static sk_sp MakeDeferred(const GrCaps&, const GrSurfaceDesc&, SkBackingFit, SkBudgeted); // TODO: need to refine ownership semantics of 'srcData' if we're in completely // deferred mode static sk_sp MakeDeferred(const GrCaps&, GrTextureProvider*, const GrSurfaceDesc&, SkBudgeted, const void* srcData, size_t rowBytes); const GrSurfaceDesc& desc() const { return fDesc; } GrSurfaceOrigin origin() const { SkASSERT(kTopLeft_GrSurfaceOrigin == fDesc.fOrigin || kBottomLeft_GrSurfaceOrigin == fDesc.fOrigin); return fDesc.fOrigin; } int width() const { return fDesc.fWidth; } int height() const { return fDesc.fHeight; } GrPixelConfig config() const { return fDesc.fConfig; } class UniqueID { public: // wrapped explicit UniqueID(const GrGpuResource::UniqueID& id) : fID(id.asUInt()) { } // deferred 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); } bool isInvalid() const { return SK_InvalidUniqueID == fID; } private: const 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; } GrSurface* instantiate(GrTextureProvider* texProvider); /** * Helper that gets the width and height of the surface as a bounding rectangle. */ SkRect getBoundsRect() const { return SkRect::MakeIWH(this->width(), this->height()); } /** * @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; } /** * Does the resource count against the resource budget? */ SkBudgeted isBudgeted() const { return fBudgeted; } void setLastOpList(GrOpList* opList); GrOpList* getLastOpList() { return fLastOpList; } /** * 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 { if (kInvalidGpuMemorySize == fGpuMemorySize) { fGpuMemorySize = this->onGpuMemorySize(); SkASSERT(kInvalidGpuMemorySize != fGpuMemorySize); } return fGpuMemorySize; } bool isWrapped_ForTesting() const; protected: // Deferred version GrSurfaceProxy(const GrSurfaceDesc& desc, SkBackingFit fit, SkBudgeted budgeted) : fDesc(desc) , fFit(fit) , fBudgeted(budgeted) , fGpuMemorySize(kInvalidGpuMemorySize) , fLastOpList(nullptr) { // Note: this ctor pulls a new uniqueID from the same pool at the GrGpuResources } // Wrapped version GrSurfaceProxy(sk_sp surface, SkBackingFit fit); virtual ~GrSurfaceProxy(); // For wrapped resources, 'fDesc' will always be filled in from the wrapped resource. const GrSurfaceDesc fDesc; const SkBackingFit fFit; // always exact for wrapped resources const SkBudgeted fBudgeted; // set from the backing resource for wrapped resources const UniqueID fUniqueID; // set from the backing resource for wrapped resources static const size_t kInvalidGpuMemorySize = ~static_cast(0); SkDEBUGCODE(size_t getRawGpuMemorySize_debugOnly() const { return fGpuMemorySize; }) private: virtual size_t onGpuMemorySize() const = 0; // 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 render target context is currently bound // to this renderTarget). // 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. GrOpList* fLastOpList; typedef GrIORefProxy INHERITED; }; #endif