| /* | |
| * 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 GrRenderTargetProxy_DEFINED | |
| #define GrRenderTargetProxy_DEFINED | |
| #include "include/private/GrTypesPriv.h" | |
| #include "src/core/SkArenaAlloc.h" | |
| #include "src/gpu/GrCaps.h" | |
| #include "src/gpu/GrNativeRect.h" | |
| #include "src/gpu/GrSubRunAllocator.h" | |
| #include "src/gpu/GrSurfaceProxy.h" | |
| #include "src/gpu/GrSwizzle.h" | |
| class GrResourceProvider; | |
| // GrArenas matches the lifetime of a single frame. It is created and held on the | |
| // SurfaceFillContext's RenderTargetProxy with the first call to get an arena. Each GrOpsTask | |
| // takes a ref on it to keep the arenas alive. When the first GrOpsTask's onExecute() is | |
| // completed, the arena ref on the SurfaceFillContext's RenderTargetProxy is nulled out so that | |
| // any new GrOpsTasks will create and ref a new set of arenas. | |
| class GrArenas : public SkNVRefCnt<GrArenas> { | |
| public: | |
| SkArenaAlloc* arenaAlloc() { | |
| SkDEBUGCODE(if (fIsFlushed) SK_ABORT("Using a flushed arena");) | |
| return &fArenaAlloc; | |
| } | |
| void flush() { | |
| SkDEBUGCODE(fIsFlushed = true;) | |
| } | |
| GrSubRunAllocator* subRunAlloc() { return &fSubRunAllocator; } | |
| private: | |
| SkArenaAlloc fArenaAlloc{1024}; | |
| // An allocator specifically designed to minimize the overhead of sub runs. It provides a | |
| // different dtor semantics than SkArenaAlloc. | |
| GrSubRunAllocator fSubRunAllocator{1024}; | |
| SkDEBUGCODE(bool fIsFlushed = false;) | |
| }; | |
| // This class delays the acquisition of RenderTargets until they are actually | |
| // required | |
| // Beware: the uniqueID of the RenderTargetProxy will usually be different than | |
| // the uniqueID of the RenderTarget it represents! | |
| class GrRenderTargetProxy : virtual public GrSurfaceProxy { | |
| public: | |
| GrRenderTargetProxy* asRenderTargetProxy() override { return this; } | |
| const GrRenderTargetProxy* asRenderTargetProxy() const override { return this; } | |
| // Actually instantiate the backing rendertarget, if necessary. | |
| bool instantiate(GrResourceProvider*) override; | |
| // Returns true if this proxy either has a stencil attachment already, or if we can attach one | |
| // during flush. Wrapped render targets without stencil will return false, since we are unable | |
| // to modify their attachments. | |
| bool canUseStencil(const GrCaps& caps) const; | |
| /* | |
| * Indicate that a draw to this proxy requires stencil. | |
| */ | |
| void setNeedsStencil() { fNeedsStencil = true; } | |
| int needsStencil() const { return fNeedsStencil; } | |
| /** | |
| * Returns the number of samples/pixel in the color buffer (One if non-MSAA). | |
| */ | |
| int numSamples() const { return fSampleCnt; } | |
| int maxWindowRectangles(const GrCaps& caps) const; | |
| bool glRTFBOIDIs0() const { return fSurfaceFlags & GrInternalSurfaceFlags::kGLRTFBOIDIs0; } | |
| bool wrapsVkSecondaryCB() const { return fWrapsVkSecondaryCB == WrapsVkSecondaryCB::kYes; } | |
| bool supportsVkInputAttachment() const { | |
| return fSurfaceFlags & GrInternalSurfaceFlags::kVkRTSupportsInputAttachment; | |
| } | |
| void markMSAADirty(SkIRect dirtyRect) { | |
| SkASSERT(SkIRect::MakeSize(this->backingStoreDimensions()).contains(dirtyRect)); | |
| SkASSERT(this->requiresManualMSAAResolve()); | |
| fMSAADirtyRect.join(dirtyRect); | |
| } | |
| void markMSAAResolved() { | |
| SkASSERT(this->requiresManualMSAAResolve()); | |
| fMSAADirtyRect.setEmpty(); | |
| } | |
| bool isMSAADirty() const { | |
| SkASSERT(fMSAADirtyRect.isEmpty() || this->requiresManualMSAAResolve()); | |
| return this->requiresManualMSAAResolve() && !fMSAADirtyRect.isEmpty(); | |
| } | |
| const SkIRect& msaaDirtyRect() const { | |
| SkASSERT(this->requiresManualMSAAResolve()); | |
| return fMSAADirtyRect; | |
| } | |
| // TODO: move this to a priv class! | |
| bool refsWrappedObjects() const; | |
| sk_sp<GrArenas> arenas() { | |
| if (fArenas == nullptr) { | |
| fArenas = sk_make_sp<GrArenas>(); | |
| } | |
| return fArenas; | |
| } | |
| void clearArenas() { | |
| if (fArenas != nullptr) { | |
| fArenas->flush(); | |
| } | |
| fArenas = nullptr; | |
| } | |
| protected: | |
| friend class GrProxyProvider; // for ctors | |
| friend class GrRenderTargetProxyPriv; | |
| // Deferred version | |
| GrRenderTargetProxy(const GrCaps&, | |
| const GrBackendFormat&, | |
| SkISize, | |
| int sampleCount, | |
| SkBackingFit, | |
| SkBudgeted, | |
| GrProtected, | |
| GrInternalSurfaceFlags, | |
| UseAllocator); | |
| enum class WrapsVkSecondaryCB : bool { kNo = false, kYes = true }; | |
| // Lazy-callback version | |
| // There are two main use cases for lazily-instantiated proxies: | |
| // basic knowledge - width, height, config, samples, origin are known | |
| // minimal knowledge - only config is known. | |
| // | |
| // The basic knowledge version is used for DDL where we know the type of proxy we are going to | |
| // use, but we don't have access to the GPU yet to instantiate it. | |
| // | |
| // The minimal knowledge version is used for when we are generating an atlas but we do not know | |
| // the final size until we have finished adding to it. | |
| GrRenderTargetProxy(LazyInstantiateCallback&&, | |
| const GrBackendFormat&, | |
| SkISize, | |
| int sampleCount, | |
| SkBackingFit, | |
| SkBudgeted, | |
| GrProtected, | |
| GrInternalSurfaceFlags, | |
| UseAllocator, | |
| WrapsVkSecondaryCB); | |
| // Wrapped version | |
| GrRenderTargetProxy(sk_sp<GrSurface>, | |
| UseAllocator, | |
| WrapsVkSecondaryCB = WrapsVkSecondaryCB::kNo); | |
| sk_sp<GrSurface> createSurface(GrResourceProvider*) const override; | |
| private: | |
| size_t onUninstantiatedGpuMemorySize() const override; | |
| SkDEBUGCODE(void onValidateSurface(const GrSurface*) override;) | |
| LazySurfaceDesc callbackDesc() const override; | |
| // WARNING: Be careful when adding or removing fields here. ASAN is likely to trigger warnings | |
| // when instantiating GrTextureRenderTargetProxy. The std::function in GrSurfaceProxy makes | |
| // each class in the diamond require 16 byte alignment. Clang appears to layout the fields for | |
| // each class to achieve the necessary alignment. However, ASAN checks the alignment of 'this' | |
| // in the constructors, and always looks for the full 16 byte alignment, even if the fields in | |
| // that particular class don't require it. Changing the size of this object can move the start | |
| // address of other types, leading to this problem. | |
| int8_t fSampleCnt; | |
| int8_t fNeedsStencil = false; | |
| WrapsVkSecondaryCB fWrapsVkSecondaryCB; | |
| SkIRect fMSAADirtyRect = SkIRect::MakeEmpty(); | |
| sk_sp<GrArenas> fArenas{nullptr}; | |
| // This is to fix issue in large comment above. Without the padding we can end up with the | |
| // GrTextureProxy starting 8 byte aligned by not 16. This happens when the RT ends at bytes 1-8. | |
| // Note: with the virtual inheritance an 8 byte pointer is at the start of GrRenderTargetProxy. | |
| // | |
| // In the current world we end the RT proxy at 12 bytes. Technically any padding between 0-4 | |
| // will work, but we use 4 to be more explicit about getting it to 16 byte alignment. | |
| char fPadding[4]; | |
| using INHERITED = GrSurfaceProxy; | |
| }; | |
| #endif |