michael@0: /* michael@0: * Copyright 2012 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #ifndef GrEffect_DEFINED michael@0: #define GrEffect_DEFINED michael@0: michael@0: #include "GrColor.h" michael@0: #include "GrEffectUnitTest.h" michael@0: #include "GrTexture.h" michael@0: #include "GrTextureAccess.h" michael@0: #include "GrTypesPriv.h" michael@0: michael@0: class GrBackendEffectFactory; michael@0: class GrContext; michael@0: class GrCoordTransform; michael@0: class GrEffect; michael@0: class GrVertexEffect; michael@0: class SkString; michael@0: michael@0: /** michael@0: * A Wrapper class for GrEffect. Its ref-count will track owners that may use effects to enqueue michael@0: * new draw operations separately from ownership within a deferred drawing queue. When the michael@0: * GrEffectRef ref count reaches zero the scratch GrResources owned by the effect can be recycled michael@0: * in service of later draws. However, the deferred draw queue may still own direct references to michael@0: * the underlying GrEffect. michael@0: * michael@0: * GrEffectRefs created by new are placed in a per-thread managed pool. The pool is destroyed when michael@0: * the thread ends. Therefore, all dynamically allocated GrEffectRefs must be unreffed before thread michael@0: * termination. michael@0: */ michael@0: class GrEffectRef : public SkRefCnt { michael@0: public: michael@0: SK_DECLARE_INST_COUNT(GrEffectRef); michael@0: virtual ~GrEffectRef(); michael@0: michael@0: GrEffect* get() { return fEffect; } michael@0: const GrEffect* get() const { return fEffect; } michael@0: michael@0: const GrEffect* operator-> () { return fEffect; } michael@0: const GrEffect* operator-> () const { return fEffect; } michael@0: michael@0: void* operator new(size_t size); michael@0: void operator delete(void* target); michael@0: michael@0: void* operator new(size_t size, void* placement) { michael@0: return ::operator new(size, placement); michael@0: } michael@0: void operator delete(void* target, void* placement) { michael@0: ::operator delete(target, placement); michael@0: } michael@0: michael@0: private: michael@0: friend class GrEffect; // to construct these michael@0: michael@0: explicit GrEffectRef(GrEffect* effect); michael@0: michael@0: GrEffect* fEffect; michael@0: michael@0: typedef SkRefCnt INHERITED; michael@0: }; michael@0: michael@0: /** Provides custom vertex shader, fragment shader, uniform data for a particular stage of the michael@0: Ganesh shading pipeline. michael@0: Subclasses must have a function that produces a human-readable name: michael@0: static const char* Name(); michael@0: GrEffect objects *must* be immutable: after being constructed, their fields may not change. michael@0: michael@0: GrEffect subclass objects should be created by factory functions that return GrEffectRef. michael@0: There is no public way to wrap a GrEffect in a GrEffectRef. Thus, a factory should be a static michael@0: member function of a GrEffect subclass. michael@0: michael@0: Because almost no code should ever handle a GrEffect directly outside of a GrEffectRef, we michael@0: privately inherit from SkRefCnt to help prevent accidental direct ref'ing/unref'ing of effects. michael@0: michael@0: Dynamically allocated GrEffects and their corresponding GrEffectRefs are managed by a per-thread michael@0: memory pool. The ref count of an effect must reach 0 before the thread terminates and the pool michael@0: is destroyed. To create a static effect use the macro GR_CREATE_STATIC_EFFECT declared below. michael@0: */ michael@0: class GrEffect : private SkRefCnt { michael@0: public: michael@0: SK_DECLARE_INST_COUNT(GrEffect) michael@0: michael@0: virtual ~GrEffect(); michael@0: michael@0: /** michael@0: * This function is used to perform optimizations. When called the color and validFlags params michael@0: * indicate whether the input components to this effect in the FS will have known values. michael@0: * validFlags is a bitfield of GrColorComponentFlags. The function updates both params to michael@0: * indicate known values of its output. A component of the color param only has meaning if the michael@0: * corresponding bit in validFlags is set. michael@0: */ michael@0: virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const = 0; michael@0: michael@0: /** Will this effect read the source color value? */ michael@0: bool willUseInputColor() const { return fWillUseInputColor; } michael@0: michael@0: /** This object, besides creating back-end-specific helper objects, is used for run-time-type- michael@0: identification. The factory should be an instance of templated class, michael@0: GrTBackendEffectFactory. It is templated on the subclass of GrEffect. The subclass must have michael@0: a nested type (or typedef) named GLEffect which will be the subclass of GrGLEffect created michael@0: by the factory. michael@0: michael@0: Example: michael@0: class MyCustomEffect : public GrEffect { michael@0: ... michael@0: virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { michael@0: return GrTBackendEffectFactory::getInstance(); michael@0: } michael@0: ... michael@0: }; michael@0: */ michael@0: virtual const GrBackendEffectFactory& getFactory() const = 0; michael@0: michael@0: /** Returns true if this and other effect conservatively draw identically. It can only return michael@0: true when the two effects are of the same subclass (i.e. they return the same object from michael@0: from getFactory()). michael@0: michael@0: A return value of true from isEqual() should not be used to test whether the effects would michael@0: generate the same shader code. To test for identical code generation use the EffectKey michael@0: computed by the GrBackendEffectFactory: michael@0: effectA.getFactory().glEffectKey(effectA) == effectB.getFactory().glEffectKey(effectB). michael@0: */ michael@0: bool isEqual(const GrEffectRef& other) const { michael@0: return this->isEqual(*other.get()); michael@0: } michael@0: michael@0: /** Human-meaningful string to identify this effect; may be embedded michael@0: in generated shader code. */ michael@0: const char* name() const; michael@0: michael@0: int numTransforms() const { return fCoordTransforms.count(); } michael@0: michael@0: /** Returns the coordinate transformation at index. index must be valid according to michael@0: numTransforms(). */ michael@0: const GrCoordTransform& coordTransform(int index) const { return *fCoordTransforms[index]; } michael@0: michael@0: int numTextures() const { return fTextureAccesses.count(); } michael@0: michael@0: /** Returns the access pattern for the texture at index. index must be valid according to michael@0: numTextures(). */ michael@0: const GrTextureAccess& textureAccess(int index) const { return *fTextureAccesses[index]; } michael@0: michael@0: /** Shortcut for textureAccess(index).texture(); */ michael@0: GrTexture* texture(int index) const { return this->textureAccess(index).getTexture(); } michael@0: michael@0: /** Will this effect read the destination pixel value? */ michael@0: bool willReadDstColor() const { return fWillReadDstColor; } michael@0: michael@0: /** Will this effect read the fragment position? */ michael@0: bool willReadFragmentPosition() const { return fWillReadFragmentPosition; } michael@0: michael@0: /** Will this effect emit custom vertex shader code? michael@0: (To set this value the effect must inherit from GrVertexEffect.) */ michael@0: bool hasVertexCode() const { return fHasVertexCode; } michael@0: michael@0: int numVertexAttribs() const { michael@0: SkASSERT(0 == fVertexAttribTypes.count() || fHasVertexCode); michael@0: return fVertexAttribTypes.count(); michael@0: } michael@0: michael@0: GrSLType vertexAttribType(int index) const { return fVertexAttribTypes[index]; } michael@0: michael@0: static const int kMaxVertexAttribs = 2; michael@0: michael@0: /** Useful for effects that want to insert a texture matrix that is implied by the texture michael@0: dimensions */ michael@0: static inline SkMatrix MakeDivByTextureWHMatrix(const GrTexture* texture) { michael@0: SkASSERT(NULL != texture); michael@0: SkMatrix mat; michael@0: mat.setIDiv(texture->width(), texture->height()); michael@0: return mat; michael@0: } michael@0: michael@0: void* operator new(size_t size); michael@0: void operator delete(void* target); michael@0: michael@0: void* operator new(size_t size, void* placement) { michael@0: return ::operator new(size, placement); michael@0: } michael@0: void operator delete(void* target, void* placement) { michael@0: ::operator delete(target, placement); michael@0: } michael@0: michael@0: /** These functions are used when recording effects into a deferred drawing queue. The inc call michael@0: keeps the effect alive outside of GrEffectRef while allowing any resources owned by the michael@0: effect to be returned to the cache for reuse. The dec call must balance the inc call. */ michael@0: void incDeferredRefCounts() const { michael@0: this->ref(); michael@0: int count = fTextureAccesses.count(); michael@0: for (int t = 0; t < count; ++t) { michael@0: fTextureAccesses[t]->getTexture()->incDeferredRefCount(); michael@0: } michael@0: } michael@0: void decDeferredRefCounts() const { michael@0: int count = fTextureAccesses.count(); michael@0: for (int t = 0; t < count; ++t) { michael@0: fTextureAccesses[t]->getTexture()->decDeferredRefCount(); michael@0: } michael@0: this->unref(); michael@0: } michael@0: michael@0: protected: michael@0: /** michael@0: * Subclasses call this from their constructor to register coordinate transformations. The michael@0: * effect subclass manages the lifetime of the transformations (this function only stores a michael@0: * pointer). The GrCoordTransform is typically a member field of the GrEffect subclass. When the michael@0: * matrix has perspective, the transformed coordinates will have 3 components. Otherwise they'll michael@0: * have 2. This must only be called from the constructor because GrEffects are immutable. michael@0: */ michael@0: void addCoordTransform(const GrCoordTransform* coordTransform); michael@0: michael@0: /** michael@0: * Subclasses call this from their constructor to register GrTextureAccesses. The effect michael@0: * subclass manages the lifetime of the accesses (this function only stores a pointer). The michael@0: * GrTextureAccess is typically a member field of the GrEffect subclass. This must only be michael@0: * called from the constructor because GrEffects are immutable. michael@0: */ michael@0: void addTextureAccess(const GrTextureAccess* textureAccess); michael@0: michael@0: GrEffect() michael@0: : fWillReadDstColor(false) michael@0: , fWillReadFragmentPosition(false) michael@0: , fWillUseInputColor(true) michael@0: , fHasVertexCode(false) michael@0: , fEffectRef(NULL) {} michael@0: michael@0: /** This should be called by GrEffect subclass factories. See the comment on AutoEffectUnref for michael@0: an example factory function. */ michael@0: static GrEffectRef* CreateEffectRef(GrEffect* effect) { michael@0: if (NULL == effect->fEffectRef) { michael@0: effect->fEffectRef = SkNEW_ARGS(GrEffectRef, (effect)); michael@0: } else { michael@0: effect->fEffectRef->ref(); michael@0: } michael@0: return effect->fEffectRef; michael@0: } michael@0: michael@0: static const GrEffectRef* CreateEffectRef(const GrEffect* effect) { michael@0: return CreateEffectRef(const_cast(effect)); michael@0: } michael@0: michael@0: /** Used by GR_CREATE_STATIC_EFFECT below */ michael@0: static GrEffectRef* CreateStaticEffectRef(void* refStorage, GrEffect* effect) { michael@0: SkASSERT(NULL == effect->fEffectRef); michael@0: effect->fEffectRef = SkNEW_PLACEMENT_ARGS(refStorage, GrEffectRef, (effect)); michael@0: return effect->fEffectRef; michael@0: } michael@0: michael@0: michael@0: /** Helper used in subclass factory functions to unref the effect after it has been wrapped in a michael@0: GrEffectRef. E.g.: michael@0: michael@0: class EffectSubclass : public GrEffect { michael@0: public: michael@0: GrEffectRef* Create(ParamType1 param1, ParamType2 param2, ...) { michael@0: AutoEffectUnref effect(SkNEW_ARGS(EffectSubclass, (param1, param2, ...))); michael@0: return CreateEffectRef(effect); michael@0: } michael@0: */ michael@0: class AutoEffectUnref { michael@0: public: michael@0: AutoEffectUnref(GrEffect* effect) : fEffect(effect) { } michael@0: ~AutoEffectUnref() { fEffect->unref(); } michael@0: operator GrEffect*() { return fEffect; } michael@0: private: michael@0: GrEffect* fEffect; michael@0: }; michael@0: michael@0: /** Helper for getting the GrEffect out of a GrEffectRef and down-casting to a GrEffect subclass michael@0: */ michael@0: template michael@0: static const T& CastEffect(const GrEffect& effectRef) { michael@0: return *static_cast(&effectRef); michael@0: } michael@0: michael@0: /** michael@0: * If the effect subclass will read the destination pixel value then it must call this function michael@0: * from its constructor. Otherwise, when its generated backend-specific effect class attempts michael@0: * to generate code that reads the destination pixel it will fail. michael@0: */ michael@0: void setWillReadDstColor() { fWillReadDstColor = true; } michael@0: michael@0: /** michael@0: * If the effect will generate a backend-specific effect that will read the fragment position michael@0: * in the FS then it must call this method from its constructor. Otherwise, the request to michael@0: * access the fragment position will be denied. michael@0: */ michael@0: void setWillReadFragmentPosition() { fWillReadFragmentPosition = true; } michael@0: michael@0: /** michael@0: * If the effect will generate a result that does not depend on the input color value then it must michael@0: * call this function from its constructor. Otherwise, when its generated backend-specific code michael@0: * might fail during variable binding due to unused variables. michael@0: */ michael@0: void setWillNotUseInputColor() { fWillUseInputColor = false; } michael@0: michael@0: private: michael@0: bool isEqual(const GrEffect& other) const { michael@0: if (&this->getFactory() != &other.getFactory()) { michael@0: return false; michael@0: } michael@0: bool result = this->onIsEqual(other); michael@0: #ifdef SK_DEBUG michael@0: if (result) { michael@0: this->assertEquality(other); michael@0: } michael@0: #endif michael@0: return result; michael@0: } michael@0: michael@0: SkDEBUGCODE(void assertEquality(const GrEffect& other) const;) michael@0: michael@0: /** Subclass implements this to support isEqual(). It will only be called if it is known that michael@0: the two effects are of the same subclass (i.e. they return the same object from michael@0: getFactory()).*/ michael@0: virtual bool onIsEqual(const GrEffect& other) const = 0; michael@0: michael@0: void EffectRefDestroyed() { fEffectRef = NULL; } michael@0: michael@0: friend class GrEffectRef; // to call EffectRefDestroyed() michael@0: friend class GrEffectStage; // to rewrap GrEffect in GrEffectRef when restoring an effect-stage michael@0: // from deferred state, to call isEqual on naked GrEffects, and michael@0: // to inc/dec deferred ref counts. michael@0: friend class GrVertexEffect; // to set fHasVertexCode and build fVertexAttribTypes. michael@0: michael@0: SkSTArray<4, const GrCoordTransform*, true> fCoordTransforms; michael@0: SkSTArray<4, const GrTextureAccess*, true> fTextureAccesses; michael@0: SkSTArray fVertexAttribTypes; michael@0: bool fWillReadDstColor; michael@0: bool fWillReadFragmentPosition; michael@0: bool fWillUseInputColor; michael@0: bool fHasVertexCode; michael@0: GrEffectRef* fEffectRef; michael@0: michael@0: typedef SkRefCnt INHERITED; michael@0: }; michael@0: michael@0: inline GrEffectRef::GrEffectRef(GrEffect* effect) { michael@0: SkASSERT(NULL != effect); michael@0: effect->ref(); michael@0: fEffect = effect; michael@0: } michael@0: michael@0: /** michael@0: * This creates an effect outside of the effect memory pool. The effect's destructor will be called michael@0: * at global destruction time. NAME will be the name of the created GrEffectRef. michael@0: */ michael@0: #define GR_CREATE_STATIC_EFFECT(NAME, EFFECT_CLASS, ARGS) \ michael@0: enum { \ michael@0: k_##NAME##_EffectRefOffset = GR_CT_ALIGN_UP(sizeof(EFFECT_CLASS), 8), \ michael@0: k_##NAME##_StorageSize = k_##NAME##_EffectRefOffset + sizeof(GrEffectRef) \ michael@0: }; \ michael@0: static SkAlignedSStorage g_##NAME##_Storage; \ michael@0: static void* NAME##_RefLocation = (char*)g_##NAME##_Storage.get() + k_##NAME##_EffectRefOffset; \ michael@0: static GrEffect* NAME##_Effect SkNEW_PLACEMENT_ARGS(g_##NAME##_Storage.get(), EFFECT_CLASS, ARGS);\ michael@0: static SkAutoTDestroy NAME##_ad(NAME##_Effect); \ michael@0: static GrEffectRef* NAME(GrEffect::CreateStaticEffectRef(NAME##_RefLocation, NAME##_Effect)); \ michael@0: static SkAutoTDestroy NAME##_Ref_ad(NAME) michael@0: michael@0: michael@0: #endif