From 95740981c36266e4595ddde2264aa38e3c7e2d02 Mon Sep 17 00:00:00 2001 From: bsalomon Date: Thu, 4 Sep 2014 13:12:37 -0700 Subject: [PATCH] Add GrProgramElement base class for GrEffect with deferred exec ref. BUG=skia:2889 R=robertphillips@google.com Author: bsalomon@google.com Review URL: https://codereview.chromium.org/537773004 --- gyp/gpu.gypi | 5 ++ include/gpu/GrEffect.h | 11 ++- include/gpu/GrGpuResource.h | 2 +- include/gpu/GrProgramElement.h | 94 ++++++++++++++++++++++++++ include/gpu/GrProgramElementRef.h | 73 ++++++++++++++++++++ include/gpu/GrProgramResource.h | 73 ++++++++++++++++++++ src/gpu/GrProgramElement.cpp | 30 ++++++++ src/gpu/GrProgramResource.cpp | 109 ++++++++++++++++++++++++++++++ 8 files changed, 389 insertions(+), 8 deletions(-) create mode 100644 include/gpu/GrProgramElement.h create mode 100644 include/gpu/GrProgramElementRef.h create mode 100644 include/gpu/GrProgramResource.h create mode 100644 src/gpu/GrProgramElement.cpp create mode 100644 src/gpu/GrProgramResource.cpp diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi index 6945eed5c1..343bcbafa7 100644 --- a/gyp/gpu.gypi +++ b/gyp/gpu.gypi @@ -24,6 +24,9 @@ '<(skia_include_path)/gpu/GrGpuResource.h', '<(skia_include_path)/gpu/GrPaint.h', '<(skia_include_path)/gpu/GrPathRendererChain.h', + '<(skia_include_path)/gpu/GrProgramElement.h', + '<(skia_include_path)/gpu/GrProgramElementRef.h', + '<(skia_include_path)/gpu/GrProgramResource.h', '<(skia_include_path)/gpu/GrRect.h', '<(skia_include_path)/gpu/GrRenderTarget.h', '<(skia_include_path)/gpu/GrResourceKey.h', @@ -104,6 +107,8 @@ '<(skia_src_path)/gpu/GrPathRendering.h', '<(skia_src_path)/gpu/GrPathUtils.cpp', '<(skia_src_path)/gpu/GrPathUtils.h', + '<(skia_src_path)/gpu/GrProgramElement.cpp', + '<(skia_src_path)/gpu/GrProgramResource.cpp', '<(skia_src_path)/gpu/GrPictureUtils.h', '<(skia_src_path)/gpu/GrPictureUtils.cpp', '<(skia_src_path)/gpu/GrPlotMgr.h', diff --git a/include/gpu/GrEffect.h b/include/gpu/GrEffect.h index ec1b0ba636..b7a6d17af9 100644 --- a/include/gpu/GrEffect.h +++ b/include/gpu/GrEffect.h @@ -10,16 +10,14 @@ #include "GrColor.h" #include "GrEffectUnitTest.h" -#include "GrTexture.h" +#include "GrProgramElement.h" #include "GrTextureAccess.h" #include "GrTypesPriv.h" class GrBackendEffectFactory; class GrContext; class GrCoordTransform; -class GrEffect; -class GrVertexEffect; -class SkString; + /** Provides custom vertex shader, fragment shader, uniform data for a particular stage of the Ganesh shading pipeline. @@ -31,7 +29,7 @@ class SkString; effect must reach 0 before the thread terminates and the pool is destroyed. To create a static effect use the macro GR_CREATE_STATIC_EFFECT declared below. */ -class GrEffect : public SkRefCnt { +class GrEffect : public GrProgramElement { public: SK_DECLARE_INST_COUNT(GrEffect) @@ -205,7 +203,7 @@ private: bool fWillUseInputColor; bool fRequiresVertexShader; - typedef SkRefCnt INHERITED; + typedef GrProgramElement INHERITED; }; /** @@ -217,5 +215,4 @@ static SkAlignedSStorage g_##NAME##_Storage; static GrEffect* NAME SkNEW_PLACEMENT_ARGS(g_##NAME##_Storage.get(), EFFECT_CLASS, ARGS); \ static SkAutoTDestroy NAME##_ad(NAME); - #endif diff --git a/include/gpu/GrGpuResource.h b/include/gpu/GrGpuResource.h index f18f5c5050..189237a944 100644 --- a/include/gpu/GrGpuResource.h +++ b/include/gpu/GrGpuResource.h @@ -110,7 +110,7 @@ private: mutable int32_t fPendingWrites; // These functions need access to the pending read/write member functions. - friend class GrDrawState; + friend class GrRODrawState; friend class GrProgramResource; }; diff --git a/include/gpu/GrProgramElement.h b/include/gpu/GrProgramElement.h new file mode 100644 index 0000000000..67135a3ddb --- /dev/null +++ b/include/gpu/GrProgramElement.h @@ -0,0 +1,94 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrProgramElement_DEFINED +#define GrProgramElement_DEFINED + +#include "SkRefCnt.h" +#include "SkTArray.h" + +class GrProgramResource; + +/** + * Base class for GrEffect (and future GrGeometryProcessor). GrDrawState uses this to manage + * transitioning a GrEffect from being owned by a client to being scheduled for execution. It + * converts resources owned by the effect from being ref'ed to having pending reads/writes. + * + * All GrGpuResource objects owned by a GrProgramElement or derived classes (either directly or + * indirectly) must be wrapped in a GrProgramResource and registered with the GrProgramElement using + * addGrProgramResource(). This allows the regular refs to be converted to pending IO events + * when the program element is scheduled for deferred execution. + */ +class GrProgramElement : public SkNoncopyable { +public: + SK_DECLARE_INST_COUNT_ROOT(GrProgramElement) + + virtual ~GrProgramElement() { + // fRefCnt can be one when an effect is created statically using GR_CREATE_STATIC_EFFECT + SkASSERT((0 == fRefCnt || 1 == fRefCnt) && 0 == fPendingExecutions); + // Set to invalid values. + SkDEBUGCODE(fRefCnt = fPendingExecutions = -10;) + } + + void ref() const { + // Once the ref cnt reaches zero it should never be ref'ed again. + SkASSERT(fRefCnt > 0); + this->validate(); + ++fRefCnt; + } + + void unref() const { + this->validate(); + --fRefCnt; + if (0 == fRefCnt && 0 == fPendingExecutions) { + SkDELETE(this); + } + } + + void validate() const { +#ifdef SK_DEBUG + SkASSERT(fRefCnt >= 0); + SkASSERT(fPendingExecutions >= 0); + SkASSERT(fRefCnt + fPendingExecutions > 0); +#endif + } + +protected: + GrProgramElement() : fRefCnt(1), fPendingExecutions(0) {} + + /** Subclasses registers their resources using this function. It is assumed the GrProgramResouce + is and will remain owned by the subclass and this function will retain a raw ptr. Once a + GrProgramResource is registered its setResource must not be called. + */ + void addProgramResource(const GrProgramResource* res) { + fProgramResources.push_back(res); + } + +private: + void convertRefToPendingExecution() const; + + void completedExecution() const { + this->validate(); + --fPendingExecutions; + if (0 == fRefCnt && 0 == fPendingExecutions) { + SkDELETE(this); + } + } + + mutable int32_t fRefCnt; + // Count of deferred executions not yet issued to the 3D API. + mutable int32_t fPendingExecutions; + + SkSTArray<4, const GrProgramResource*, true> fProgramResources; + + // Only this class can access convertRefToPendingExecution() and completedExecution(). + template friend class GrProgramElementRef; + + typedef SkNoncopyable INHERITED; +}; + +#endif diff --git a/include/gpu/GrProgramElementRef.h b/include/gpu/GrProgramElementRef.h new file mode 100644 index 0000000000..ea11f4446c --- /dev/null +++ b/include/gpu/GrProgramElementRef.h @@ -0,0 +1,73 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrProgramElementRef_DEFINED +#define GrProgramElementRef_DEFINED + +#include "SkRefCnt.h" +#include "GrTypes.h" + +/** + * Helper for owning a GrProgramElement subclass and being able to convert a ref to pending + * execution. It is like an SkAutoTUnref for program elements whose execution can be deferred. Once + * in the pending execution state it is illegal to change the object that is owned by the + * GrProgramElementRef. Its destructor will either unref the GrProgramElement or signal that + * the pending execution has completed, depending on whether convertToPendingExec() was called. + */ +template class GrProgramElementRef : SkNoncopyable { +public: + GrProgramElementRef() : fOwnPendingExec(false), fObj(NULL) {}; + + // Adopts a ref from the caller. + explicit GrProgramElementRef(T* obj) : fOwnPendingExec(false), fObj(obj) {} + + // Adopts a ref from the caller. Do not call after convertToPendingExec. + void reset(T* obj) { + SkASSERT(!fOwnPendingExec); + SkSafeUnref(fObj); + fObj = obj; + } + + void convertToPendingExec() { + SkASSERT(!fOwnPendingExec); + fObj->convertRefToPendingExecution(); + fOwnPendingExec = true; + } + + T* get() const { return fObj; } + operator T*() { return fObj; } + + /** If T is const, the type returned from operator-> will also be const. */ + typedef typename SkTConstType::BlockRef, + SkTIsConst::value>::type BlockRefType; + + /** + * GrProgramElementRef assumes ownership of the ref and manages converting the ref to a + * pending execution. As a result, it is an error for the user to ref or unref through + * GrProgramElementRef. Therefore operator-> returns BlockRef*. + */ + BlockRefType *operator->() const { + return static_cast(fObj); + } + + ~GrProgramElementRef() { + if (NULL != fObj) { + if (fOwnPendingExec) { + fObj->completedExecution(); + } else { + fObj->unref(); + } + } + } + +private: + bool fOwnPendingExec; + T* fObj; + + typedef SkNoncopyable INHERITED; +}; +#endif diff --git a/include/gpu/GrProgramResource.h b/include/gpu/GrProgramResource.h new file mode 100644 index 0000000000..277ae9dc62 --- /dev/null +++ b/include/gpu/GrProgramResource.h @@ -0,0 +1,73 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrProgramResource_DEFINED +#define GrProgramResource_DEFINED + +#include "SkRefCnt.h" + +class GrGpuResource; + +/** + * Class that wraps a resource referenced by a GrProgramElement or GrDrawState. It manages + * converting refs to pending io operations. Like SkAutoTUnref, its constructor and setter adopt + * a ref from their caller. This class is intended only for internal use in core Gr code. + */ +class GrProgramResource : SkNoncopyable { +public: + enum IOType { + kRead_IOType, + kWrite_IOType, + kRW_IOType, + + kNone_IOType, // For internal use only, don't specify to constructor or setResource(). + }; + + SK_DECLARE_INST_COUNT_ROOT(GrProgramResource); + GrProgramResource(); + + /** Adopts a ref from the caller. ioType expresses what type of IO operations will be marked as + pending on the resource when markPendingIO is called. */ + explicit GrProgramResource(GrGpuResource*, IOType ioType); + + ~GrProgramResource(); + + GrGpuResource* getResource() const { return fResource; } + + /** Adopts a ref from the caller. ioType expresses what type of IO operations will be marked as + pending on the resource when markPendingIO is called. */ + void setResource(GrGpuResource*, IOType ioType); + + /** Does this object own a pending read or write on the resource it is wrapping. */ + bool ownsPendingIO() const { return fPendingIO; } + + /** Shortcut for calling setResource() with NULL. It cannot be called after markingPendingIO + is called. */ + void reset(); + +private: + /** Called by owning GrProgramElement when the program element is first scheduled for + execution. */ + void markPendingIO() const; + + /** Called when the program element/draw state is no longer owned by GrDrawTarget-client code. + This lets the cache know that the drawing code will no longer schedule additional reads or + writes to the resource using the program element or draw state. */ + void removeRef() const; + + friend class GrDrawState; + friend class GrProgramElement; + + GrGpuResource* fResource; + mutable bool fOwnRef; + mutable bool fPendingIO; + IOType fIOType; + + typedef SkNoncopyable INHERITED; +}; + +#endif diff --git a/src/gpu/GrProgramElement.cpp b/src/gpu/GrProgramElement.cpp new file mode 100644 index 0000000000..e14a85b5f4 --- /dev/null +++ b/src/gpu/GrProgramElement.cpp @@ -0,0 +1,30 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + +#include "GrProgramElement.h" +#include "GrProgramResource.h" + +void GrProgramElement::convertRefToPendingExecution() const { + // This function makes it so that all the GrProgramResources own a single ref to their + // underlying GrGpuResource if there are any refs to the GrProgramElement and a single + // pending read/write if there are any pending executions of the GrProgramElement. The + // GrProgramResource will give up its single ref and/or pending read/write in its destructor. + SkASSERT(fRefCnt > 0); + if (0 == fPendingExecutions) { + for (int i = 0; i < fProgramResources.count(); ++i) { + fProgramResources[i]->markPendingIO(); + } + } + ++fPendingExecutions; + this->unref(); + if (0 == fRefCnt) { + for (int i = 0; i < fProgramResources.count(); ++i) { + fProgramResources[i]->removeRef(); + } + } +} diff --git a/src/gpu/GrProgramResource.cpp b/src/gpu/GrProgramResource.cpp new file mode 100644 index 0000000000..08975ae041 --- /dev/null +++ b/src/gpu/GrProgramResource.cpp @@ -0,0 +1,109 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrProgramResource.h" +#include "GrGpuResource.h" + +GrProgramResource::GrProgramResource() { + fResource = NULL; + fOwnRef = false; + fPendingIO = false; + fIOType = kNone_IOType; +} + +GrProgramResource::GrProgramResource(GrGpuResource* resource, IOType ioType) { + fResource = NULL; + fOwnRef = false; + fPendingIO = false; + this->setResource(resource, ioType); +} + +GrProgramResource::~GrProgramResource() { + if (fOwnRef) { + SkASSERT(NULL != fResource); + fResource->unref(); + } + if (fPendingIO) { + switch (fIOType) { + case kNone_IOType: + SkFAIL("Shouldn't get here if fIOType is kNone."); + break; + case kRead_IOType: + fResource->completedRead(); + break; + case kWrite_IOType: + fResource->completedWrite(); + break; + case kRW_IOType: + fResource->completedRead(); + fResource->completedWrite(); + break; + } + } +} + +void GrProgramResource::reset() { + SkASSERT(!fPendingIO); + SkASSERT((NULL != fResource) == fOwnRef); + if (fOwnRef) { + fResource->unref(); + fOwnRef = false; + fResource = NULL; + fIOType = kNone_IOType; + } +} + +void GrProgramResource::setResource(GrGpuResource* resource, IOType ioType) { + SkASSERT(!fPendingIO); + SkASSERT((NULL != fResource) == fOwnRef); + SkSafeUnref(fResource); + if (NULL == resource) { + fResource = NULL; + fOwnRef = false; + fIOType = kNone_IOType; + } else { + SkASSERT(kNone_IOType != ioType); + fResource = resource; + fOwnRef = true; + fIOType = ioType; + } +} + +void GrProgramResource::markPendingIO() const { + // This should only be called once, when the owning GrProgramElement gets its first + // pendingExecution ref. + SkASSERT(!fPendingIO); + SkASSERT(NULL != fResource); + fPendingIO = true; + switch (fIOType) { + case kNone_IOType: + SkFAIL("GrProgramResource with neither reads nor writes?"); + break; + case kRead_IOType: + fResource->addPendingRead(); + break; + case kWrite_IOType: + fResource->addPendingWrite(); + break; + case kRW_IOType: + fResource->addPendingRead(); + fResource->addPendingWrite(); + break; + + } +} + +void GrProgramResource::removeRef() const { + // This should only be called once, when the owners last ref goes away and + // there is a pending execution. + SkASSERT(fOwnRef); + SkASSERT(fPendingIO); + SkASSERT(kNone_IOType != fIOType); + SkASSERT(NULL != fResource); + fResource->unref(); + fOwnRef = false; +}