diff options
45 files changed, 2301 insertions, 810 deletions
diff --git a/gpu/include/GrClip.h b/gpu/include/GrClip.h index 8e3030c753..414a6d6806 100644 --- a/gpu/include/GrClip.h +++ b/gpu/include/GrClip.h @@ -20,96 +20,116 @@ #include "GrClipIterator.h" #include "GrRect.h" -#include "GrTDArray.h" +#include "GrPath.h" +#include "GrTArray.h" + class GrClip { public: GrClip(); GrClip(const GrClip& src); - GrClip(GrClipIterator* iter); + GrClip(GrClipIterator* iter, const GrRect* bounds = NULL); + GrClip(const GrIRect& rect); + GrClip(const GrRect& rect); + ~GrClip(); GrClip& operator=(const GrClip& src); - bool isEmpty() const { return fBounds.isEmpty(); } - bool isComplex() const { return fList.count() > 0; } - bool isRect() const { - return !this->isEmpty() && !this->isComplex(); - } - - const GrIRect& getBounds() const { return fBounds; } + bool hasBounds() const { return fBoundsValid; } - /** - * Resets this clip to be empty (fBounds is empty, and fList is empty) - */ - void setEmpty(); + const GrRect& getBounds() const { return fBounds; } - /** - * Resets this clip to have fBounds == rect, and fList is empty. - */ - void setRect(const GrIRect& rect); + int getElementCount() const { return fList.count(); } - /** - * Append a rect to an existing clip. The call must ensure that rect does - * not overlap with any previous rect in this clip (either from setRect - * or addRect). fBounds is automatically updated to reflect the union of - * all rects that have been added. - */ - void addRect(const GrIRect&); + GrClipType getElementType(int i) const { return fList[i].fType; } - void setFromIterator(GrClipIterator* iter); + const GrPath& getPath(int i) const { + GrAssert(kPath_ClipType == fList[i].fType); + return fList[i].fPath; + } - friend bool operator==(const GrClip& a, const GrClip& b) { - return a.fBounds == b.fBounds && a.fList == b.fList; + GrPathFill getPathFill(int i) const { + GrAssert(kPath_ClipType == fList[i].fType); + return fList[i].fPathFill; } - friend bool operator!=(const GrClip& a, const GrClip& b) { - return !(a == b); + + const GrRect& getRect(int i) const { + GrAssert(kRect_ClipType == fList[i].fType); + return fList[i].fRect; } - /** - * Return the number of rects in this clip: 0 for empty, 1 for a rect, - * or N for a complex clip. - */ - int countRects() const { - return this->isEmpty() ? 0 : GrMax<int>(1, fList.count()); + const GrSetOp getOp(int i) const { return fList[i].fOp; } + + bool isRect() const { + if (1 == fList.count() && kRect_ClipType == fList[0].fType) { + GrAssert(fBoundsValid); + GrAssert(fBounds == fList[0].fRect); + return true; + } else { + return false; + } } + bool isEmpty() const { return 0 == fList.count(); } + /** - * Return an array of rects for this clip. Use countRects() to know the - * number of entries. + * Resets this clip to be empty */ - const GrIRect* getRects() const { - return fList.count() > 0 ? fList.begin() : &fBounds; - } - -#if GR_DEBUG - void validate() const; -#else - void validate() const {} -#endif + void setEmpty(); + void setFromIterator(GrClipIterator* iter, const GrRect* bounds = NULL); + void setFromRect(const GrRect& rect); + void setFromIRect(const GrIRect& rect); -private: - GrTDArray<GrIRect> fList; - GrIRect fBounds; -}; + friend bool operator==(const GrClip& a, const GrClip& b) { + if (a.fList.count() != b.fList.count()) { + return false; + } + int count = a.fList.count(); + for (int i = 0; i < count; ++i) { + if (a.fList[i] != b.fList[i]) { + return false; + } + } + return true; + } + friend bool operator!=(const GrClip& a, const GrClip& b) { + return !(a == b); + } -class GrClipIter : public GrClipIterator { -public: - GrClipIter(const GrClip& clip) : fClip(&clip), fIndex(0) {} - GrClipIter() : fClip(NULL), fIndex(0) {} - - void reset(const GrClip& clip); - - virtual bool isDone(); - virtual void rewind(); - virtual void getRect(GrIRect* r); - virtual void next(); - virtual void computeBounds(GrIRect* r); - private: - const GrClip* fClip; - int fIndex; + struct Element { + GrClipType fType; + GrRect fRect; + GrPath fPath; + GrPathFill fPathFill; + GrSetOp fOp; + bool operator ==(const Element& e) const { + if (e.fType != fType || e.fOp != fOp) { + return false; + } + switch (fType) { + case kRect_ClipType: + return fRect == e.fRect; + break; + case kPath_ClipType: + return fPath == e.fPath; + default: + GrCrash("Unknown clip element type."); + return false; // suppress warning + } + } + bool operator !=(const Element& e) const { return !(*this == e); } + }; + + GrRect fBounds; + bool fBoundsValid; + + enum { + kPreAllocElements = 4, + }; + uint8_t fListMemory[sizeof(Element) * kPreAllocElements]; + GrTArray<Element> fList; }; - #endif diff --git a/gpu/include/GrClipIterator.h b/gpu/include/GrClipIterator.h index d1fe4dde2f..abad619623 100644 --- a/gpu/include/GrClipIterator.h +++ b/gpu/include/GrClipIterator.h @@ -18,54 +18,60 @@ #ifndef GrClipIterator_DEFINED #define GrClipIterator_DEFINED +#include "GrPath.h" #include "GrRect.h" +/** + * A clip is a list of paths and/or rects with set operations to combine them. + */ class GrClipIterator { public: - GrClipIterator() : fNeedBounds(true) {} virtual ~GrClipIterator() {} /** * Returns true if there are no more rects to process */ - virtual bool isDone() = 0; + virtual bool isDone() const = 0; /** - * Rewind the iterate to replay the set of rects again + * Rewind the iterator to replay the set of clip elements again */ virtual void rewind() = 0; /** - * Return the current rect. It is an error to call this when done() is true + * Get the type of the current clip element */ - virtual void getRect(GrIRect*) = 0; + virtual GrClipType getType() const = 0; /** - * Call to move to the next rect in the set + * Return the current path. It is an error to call this when isDone() is + * true or when getType() is kRect_Type. */ - virtual void next() = 0; + virtual GrPathIter* getPathIter() = 0; /** - * Set bounds to be the bounds of the clip. + * Return the fill rule for the path. It is an error to call this when + * isDone() is true or when getType is kRect_Type. */ - virtual void computeBounds(GrIRect* bounds) = 0; + virtual GrPathFill getPathFill() const = 0; + + /** + * Return the current rect. It is an error to call this when isDone is true + * or when getType() is kPath_Type. + */ + virtual void getRect(GrRect* rect) const = 0; /** - * Subclass should call this whenever their underlying bounds has changed. + * Gets the operation used to apply the current item to previously iterated + * items. Iterators should not produce a Replace op. */ - void invalidateBoundsCache() { fNeedBounds = true; } - - const GrIRect& getBounds() { - if (fNeedBounds) { - this->computeBounds(&fBounds); - fNeedBounds = false; - } - return fBounds; - } + virtual GrSetOp getOp() const = 0; -private: - GrIRect fBounds; - bool fNeedBounds; + /** + * Call to move to the next rect in the set, previous path iter can be made + * invalid. + */ + virtual void next() = 0; }; /** diff --git a/gpu/include/GrContext.h b/gpu/include/GrContext.h index caba6efcec..b43375cb48 100644 --- a/gpu/include/GrContext.h +++ b/gpu/include/GrContext.h @@ -278,16 +278,23 @@ public: const GrMatrix* srcMatrix = NULL); /** - * Tessellates and draws a path. + * Draws a path. * * @param paint describes how to color pixels. - * @param path the path to draw + * @param pathIter the path to draw * @param fill the path filling rule to use. * @param translate optional additional translation applied to the * path. */ void drawPath(const GrPaint& paint, - GrPathIter* path, + GrPathIter* pathIter, + GrPathFill fill, + const GrPoint* translate = NULL); + /** + * Helper version of drawPath that takes a GrPath + */ + void drawPath(const GrPaint& paint, + const GrPath& path, GrPathFill fill, const GrPoint* translate = NULL); /** diff --git a/gpu/include/GrDrawTarget.h b/gpu/include/GrDrawTarget.h index 10c6d48462..576bd7ae41 100644 --- a/gpu/include/GrDrawTarget.h +++ b/gpu/include/GrDrawTarget.h @@ -23,9 +23,10 @@ #include "GrRefCnt.h" #include "GrSamplerState.h" #include "GrClip.h" +#include "GrTexture.h" +#include "GrStencil.h" class GrTexture; -class GrRenderTarget; class GrClipIterator; class GrVertexBuffer; class GrIndexBuffer; @@ -72,56 +73,63 @@ public: kClip_StateBit = 0x4,//<! Controls whether drawing is clipped // against the region specified by // setClip. + kNoColorWrites_StateBit = 0x8,//<! If set it disables writing colors. + // Useful while performing stencil ops. + + // subclass may use additional bits internally + kDummyStateBit, + kLastPublicStateBit = kDummyStateBit-1 + }; + + enum DrawFace { + kBoth_DrawFace, + kCCW_DrawFace, + kCW_DrawFace, }; /** - * StencilPass - * - * Sets the stencil state for subsequent draw calls. Used to fill paths. - * - * Winding requires two passes when the GPU/API doesn't support separate - * stencil. - * - * The color pass for path fill is used to zero out stencil bits used for - * path filling. Every pixel covere by a winding/EO stencil pass must get - * covered by the color pass in order to leave stencil buffer in the correct - * state for the next path draw. - * - * NOTE: Stencil-based Winding fill has alias-to-zero problems. (e.g. A - * winding count of 128,256,512,etc with a 8 bit stencil buffer - * will be unfilled) + * The DrawTarget may reserve some of the high bits of the stencil. The draw + * target will automatically trim reference and mask values so that the + * client doesn't overwrite these bits. + * The number of bits available is relative to the currently set render + *target. + * @return the number of bits usable by the draw target client. */ - enum StencilPass { - kNone_StencilPass, //<! Not drawing a path or clip. - kEvenOddStencil_StencilPass, //<! records in/out in stencil buffer - // using the Even/Odd fill rule. - kEvenOddColor_StencilPass, //<! writes colors to color target in - // pixels marked inside the fill by - // kEOFillStencil_StencilPass. Clears - // stencil in pixels covered by - // geometry. - kWindingStencil1_StencilPass, //<! records in/out in stencil buffer - // using the Winding fill rule. - kWindingStencil2_StencilPass, //<! records in/out in stencil buffer - // using the Winding fill rule. - // Run when single-stencil-pass winding - // not supported (i.e. no separate - // stencil support) - kWindingColor_StencilPass, //<! writes colors to color target in - // pixels marked inside the fill by - // kWindFillStencil_StencilPass. Clears - // stencil in pixels covered by - // geometry. - kDrawTargetCount_StencilPass //<! Subclass may extend this enum to use - // the stencil for other purposes (e.g. - // to do stencil-based clipping) - // This value is provided as basis for - // defining these extended enum values. - }; + int getUsableStencilBits() const { + int bits = fCurrDrawState.fRenderTarget->stencilBits(); + if (bits) { + return bits - 1; + } else { + return 0; + } + } + + /** + * Sets the stencil settings to use for the next draw. + * @param settings the stencil settings to use. + */ + void setStencil(const GrStencilSettings& settings) { + fCurrDrawState.fStencilSettings = settings; + } + + /** + * Shortcut to disable stencil testing and ops. + */ + void disableStencil() { + fCurrDrawState.fStencilSettings.setDisabled(); + } protected: struct DrState { + DrState() { + // make sure any pad is zero for memcmp + // all DrState members should default to something + // valid by the memset + memset(this, 0, sizeof(DrState)); + GrAssert((intptr_t)(void*)NULL == 0LL); + GrAssert(fStencilSettings.isDisabled()); + } uint32_t fFlagBits; GrBlendCoeff fSrcBlend; GrBlendCoeff fDstBlend; @@ -129,8 +137,9 @@ protected: GrSamplerState fSamplerStates[kNumStages]; GrRenderTarget* fRenderTarget; GrColor fColor; - StencilPass fStencilPass; - bool fReverseFill; + DrawFace fDrawFace; + + GrStencilSettings fStencilSettings; GrMatrix fViewMatrix; bool operator ==(const DrState& s) const { return 0 == memcmp(this, &s, sizeof(DrState)); @@ -196,7 +205,7 @@ public: /** * Sets the sampler state for a stage used in subsequent draws. * - * The sampler state determines how texture coordinates are + * The sampler state determines how texture coordinates are * intepretted and used to sample the texture. * * @param stage the stage of the sampler to set @@ -291,19 +300,17 @@ public: void setAlpha(uint8_t alpha); /** - * Sets pass for path rendering - * - * @param pass of path rendering + * Controls whether clockwise, counterclockwise, or both faces are drawn. + * @param face the face(s) to draw. */ - void setStencilPass(StencilPass pass); + void setDrawFace(DrawFace face) { fCurrDrawState.fDrawFace = face; } /** - * Reveses the in/out decision of the fill rule for path rendering. - * Only affects kEOFillColor_StencilPass and kWindingFillColor_StencilPass - * - * @param reverse true to reverse, false otherwise + * Gets whether the target is drawing clockwise, counterclockwise, + * or both faces. + * @return the current draw face(s). */ - void setReverseFill(bool reverse); + DrawFace getDrawFace() const { return fCurrDrawState.fDrawFace; } /** * Enable render state settings. @@ -327,6 +334,10 @@ public: return 0 != (fCurrDrawState.fFlagBits & kClip_StateBit); } + bool isColorWriteDisabled() const { + return 0 != (fCurrDrawState.fFlagBits & kNoColorWrites_StateBit); + } + /** * Sets the blending function coeffecients. * @@ -457,8 +468,8 @@ public: // text [GrGpuTextVertex vs // GrPoint].) // for below assert - kDummy, - kHighVertexLayoutBit = kDummy - 1 + kDummyVertexLayoutBit, + kHighVertexLayoutBit = kDummyVertexLayoutBit - 1 }; // make sure we haven't exceeded the number of bits in GrVertexLayout. GR_STATIC_ASSERT(kHighVertexLayoutBit < (1 << 8*sizeof(GrVertexLayout))); @@ -648,7 +659,7 @@ public: * have changed. They should be reestablished before the next drawIndexed * or drawNonIndexed. This cannot be called between reserving and releasing * geometry. The GrDrawTarget subclass may be able to perform additional - * optimizations if drawRect is used rather than drawIndexed or + * optimizations if drawRect is used rather than drawIndexed or * drawNonIndexed. * @param rect the rect to draw * @param matrix optional matrix applied to rect (before viewMatrix) @@ -665,18 +676,18 @@ public: * srcMatrix[i]. srcMatrices can be NULL when no * srcMatrices are desired. */ - virtual void drawRect(const GrRect& rect, + virtual void drawRect(const GrRect& rect, const GrMatrix* matrix, StageBitfield stageEnableBitfield, const GrRect* srcRects[], const GrMatrix* srcMatrices[]); /** - * Helper for drawRect when the caller doesn't need separate src rects or + * Helper for drawRect when the caller doesn't need separate src rects or * matrices. */ - void drawSimpleRect(const GrRect& rect, - const GrMatrix* matrix, + void drawSimpleRect(const GrRect& rect, + const GrMatrix* matrix, StageBitfield stageEnableBitfield) { drawRect(rect, matrix, stageEnableBitfield, NULL, NULL); } @@ -912,20 +923,20 @@ public: * Defaults to zero (corresponding to vertex position) * @return pointer to the vertex component as a GrPoint */ - static GrPoint* GetVertexPoint(void* vertices, + static GrPoint* GetVertexPoint(void* vertices, int vertexIndex, int vertexSize, int offset = 0) { intptr_t start = GrTCast<intptr_t>(vertices); - return GrTCast<GrPoint*>(start + offset + + return GrTCast<GrPoint*>(start + offset + vertexIndex * vertexSize); } static const GrPoint* GetVertexPoint(const void* vertices, int vertexIndex, - int vertexSize, + int vertexSize, int offset = 0) { intptr_t start = GrTCast<intptr_t>(vertices); - return GrTCast<const GrPoint*>(start + offset + + return GrTCast<const GrPoint*>(start + offset + vertexIndex * vertexSize); } @@ -937,20 +948,20 @@ public: * @param offset the offset in bytes of the vertex color * @return pointer to the vertex component as a GrColor */ - static GrColor* GetVertexColor(void* vertices, + static GrColor* GetVertexColor(void* vertices, int vertexIndex, int vertexSize, int offset) { intptr_t start = GrTCast<intptr_t>(vertices); - return GrTCast<GrColor*>(start + offset + + return GrTCast<GrColor*>(start + offset + vertexIndex * vertexSize); } static const GrColor* GetVertexColor(const void* vertices, int vertexIndex, - int vertexSize, + int vertexSize, int offset) { const intptr_t start = GrTCast<intptr_t>(vertices); - return GrTCast<const GrColor*>(start + offset + + return GrTCast<const GrColor*>(start + offset + vertexIndex * vertexSize); } @@ -985,10 +996,10 @@ protected: const GrRect* srcRects[]); static void SetRectVertices(const GrRect& rect, - const GrMatrix* matrix, - const GrRect* srcRects[], + const GrMatrix* matrix, + const GrRect* srcRects[], const GrMatrix* srcMatrices[], - GrVertexLayout layout, + GrVertexLayout layout, void* vertices); enum GeometrySrcType { @@ -997,7 +1008,7 @@ protected: kBuffer_GeometrySrcType // src was set using set*SourceToBuffer }; - struct { + struct ReservedGeometry { bool fLocked; uint32_t fVertexCount; uint32_t fIndexCount; diff --git a/gpu/include/GrGLTexture.h b/gpu/include/GrGLTexture.h index fc12ee0878..14370abb80 100644 --- a/gpu/include/GrGLTexture.h +++ b/gpu/include/GrGLTexture.h @@ -36,9 +36,6 @@ public: GLuint renderFBOID() const { return fRTFBOID; } GLuint textureFBOID() const { return fTexFBOID; } - GLuint getStencilBits() const { return fStencilBits; } - - const GrGLIRect& viewport() const { return fViewport; } void abandon(); protected: @@ -58,17 +55,13 @@ protected: GrGpuGL* gl); void setViewport(const GrGLIRect& rect) { fViewport = rect; } - - virtual int width() const { return fViewport.fWidth; } - virtual int height() const { return fViewport.fHeight; } - + const GrGLIRect& getViewport() const { return fViewport; } private: GrGpuGL* fGL; GLuint fRTFBOID; GLuint fTexFBOID; GLuint fStencilRenderbufferID; GLuint fMSColorRenderbufferID; - GLuint fStencilBits; // Should this object delete IDs when it is destroyed or does someone // else own them. diff --git a/gpu/include/GrGpu.h b/gpu/include/GrGpu.h index 661708b51a..5a88ef481f 100644 --- a/gpu/include/GrGpu.h +++ b/gpu/include/GrGpu.h @@ -25,6 +25,7 @@ class GrVertexBufferAllocPool; class GrIndexBufferAllocPool; +class GrPathRenderer; class GrGpu : public GrDrawTarget { @@ -229,15 +230,21 @@ public: bool supports8BitPalette() const { return f8bitPaletteSupport; } /** - * If single stencil pass winding is supported then one stencil pass - * (kWindingStencil1_PathPass) is required to do winding rule path filling - * (or inverse winding rule). Otherwise, two passes are required - * (kWindingStencil1_PathPass followed by kWindingStencil2_PathPass). - * + * returns true if two sided stenciling is supported. If false then only + * the front face values of the GrStencilSettings * @return true if only a single stencil pass is needed. */ - bool supportsSingleStencilPassWinding() const - { return fSingleStencilPassForWinding; } + bool supportsTwoSidedStencil() const + { return fTwoSidedStencilSupport; } + + /** + * returns true if stencil wrap is supported. If false then + * kIncWrap_StencilOp and kDecWrap_StencilOp are treated as + * kIncClamp_StencilOp and kDecClamp_StencilOp, respectively. + * @return true if stencil wrap ops are supported. + */ + bool supportsStencilWrapOps() const + { return fStencilWrapOpsSupport; } /** * Checks whether locking vertex and index buffers is supported. @@ -304,7 +311,7 @@ public: const GrIndexBuffer* getQuadIndexBuffer() const; /** - * Returns a vertex buffer with four position-only vertices [(0,0), (1,0), + * Returns a vertex buffer with four position-only vertices [(0,0), (1,0), * (1,1), (0,1)]. * @ return unit square vertex buffer */ @@ -326,16 +333,12 @@ public: void printStats() const; protected: - /** - * Extensions to GrDrawTarget::StencilPass to implement stencil clipping - */ - enum GpuStencilPass { - kSetClip_StencilPass = kDrawTargetCount_StencilPass, - /* rendering a hard clip to the stencil - buffer. Subsequent draws with other - StencilPass values will be clipped - if kClip_StateBit is set. */ - kGpuCount_StencilPass + enum PrivateStateBits { + kFirstBit = (kLastPublicStateBit << 1), + + kModifyStencilClip_StateBit = kFirstBit, // allows draws to modify + // stencil bits used for + // clipping. }; /** @@ -344,7 +347,6 @@ protected: struct ClipState { bool fClipInStencil; bool fClipIsDirty; - GrRenderTarget* fStencilClipTarget; } fClipState; // GrDrawTarget override @@ -353,6 +355,21 @@ protected: // prepares clip flushes gpu state before a draw bool setupClipAndFlushState(GrPrimitiveType type); + // Functions used to map clip-respecting stencil tests into normal + // stencil funcs supported by GPUs. + static GrStencilFunc ConvertStencilFunc(bool stencilInClip, + GrStencilFunc func); + static void ConvertStencilFuncAndMask(GrStencilFunc func, + bool clipInStencil, + unsigned int clipBit, + unsigned int userBits, + unsigned int* ref, + unsigned int* mask); + + // stencil settings to clip drawing when stencil clipping is in effect + // and the client isn't using the stencil test. + static const GrStencilSettings gClipStencilSettings; + // defaults to false, subclass can set true to support palleted textures bool f8bitPaletteSupport; @@ -360,10 +377,8 @@ protected: bool fNPOTTextureSupport; bool fNPOTTextureTileSupport; bool fNPOTRenderTargetSupport; - - // True if only one stencil pass is required to implement the winding path - // fill rule. Subclass responsible for setting this value. - bool fSingleStencilPassForWinding; + bool fTwoSidedStencilSupport; + bool fStencilWrapOpsSupport; // set by subclass to true if index and vertex buffers can be locked, false // otherwise. @@ -427,13 +442,15 @@ protected: virtual void flushScissor(const GrIRect* rect) = 0; // GrGpu subclass removes the clip from the stencil buffer - virtual void eraseStencilClip() = 0; + virtual void eraseStencilClip(const GrIRect& rect) = 0; private: - + // readies the pools to provide vertex/index data. void prepareVertexPool(); void prepareIndexPool(); + GrPathRenderer* getPathRenderer(); + GrVertexBufferAllocPool* fVertexPool; GrIndexBufferAllocPool* fIndexPool; @@ -443,6 +460,61 @@ private: mutable GrVertexBuffer* fUnitSquareVertexBuffer; // mutable so it can be // created on-demand + + GrPathRenderer* fPathRenderer; + + // when in an internal draw these indicate whether the pools are in use + // by one of the outer draws. If false then it is safe to reset the + // pool. + bool fVertexPoolInUse; + bool fIndexPoolInUse; + + // used to save and restore state when the GrGpu needs + // to make its geometry pools available internally + class AutoInternalDrawGeomRestore { + public: + AutoInternalDrawGeomRestore(GrGpu* gpu) : fAgsr(gpu) { + fGpu = gpu; + + fVertexPoolWasInUse = gpu->fVertexPoolInUse; + fIndexPoolWasInUse = gpu->fIndexPoolInUse; + + gpu->fVertexPoolInUse = fVertexPoolWasInUse || + (kBuffer_GeometrySrcType != + gpu->fGeometrySrc.fVertexSrc); + gpu->fIndexPoolInUse = fIndexPoolWasInUse || + (kBuffer_GeometrySrcType != + gpu->fGeometrySrc.fIndexSrc);; + + fSavedPoolVertexBuffer = gpu->fCurrPoolVertexBuffer; + fSavedPoolStartVertex = gpu->fCurrPoolStartVertex; + fSavedPoolIndexBuffer = gpu->fCurrPoolIndexBuffer; + fSavedPoolStartIndex = gpu->fCurrPoolStartIndex; + + fSavedReservedGeometry = gpu->fReservedGeometry; + gpu->fReservedGeometry.fLocked = false; + } + ~AutoInternalDrawGeomRestore() { + fGpu->fCurrPoolVertexBuffer = fSavedPoolVertexBuffer; + fGpu->fCurrPoolStartVertex = fSavedPoolStartVertex; + fGpu->fCurrPoolIndexBuffer = fSavedPoolIndexBuffer; + fGpu->fCurrPoolStartIndex = fSavedPoolStartIndex; + fGpu->fVertexPoolInUse = fVertexPoolWasInUse; + fGpu->fIndexPoolInUse = fIndexPoolWasInUse; + fGpu->fReservedGeometry = fSavedReservedGeometry; + } + private: + AutoGeometrySrcRestore fAgsr; + GrGpu* fGpu; + const GrVertexBuffer* fSavedPoolVertexBuffer; + int fSavedPoolStartVertex; + const GrIndexBuffer* fSavedPoolIndexBuffer; + int fSavedPoolStartIndex; + bool fVertexPoolWasInUse; + bool fIndexPoolWasInUse; + ReservedGeometry fSavedReservedGeometry; + }; + typedef GrDrawTarget INHERITED; }; diff --git a/gpu/include/GrPath.h b/gpu/include/GrPath.h index cf7b97f2d8..23fc4a86b6 100644 --- a/gpu/include/GrPath.h +++ b/gpu/include/GrPath.h @@ -35,6 +35,8 @@ public: void resetFromIter(GrPathIter*); + bool operator ==(const GrPath& path) const; + bool operator !=(const GrPath& path) const { return !(*this == path); } // overrides from GrPathSink virtual void moveTo(GrScalar x, GrScalar y); @@ -50,7 +52,7 @@ public: // overrides from GrPathIter virtual Command next(GrPoint points[]); - virtual ConvexHint hint() const; + virtual ConvexHint convexHint() const; virtual Command next(); virtual void rewind(); private: diff --git a/gpu/include/GrPathIter.h b/gpu/include/GrPathIter.h index 028faaa14d..140cbcba56 100644 --- a/gpu/include/GrPathIter.h +++ b/gpu/include/GrPathIter.h @@ -30,7 +30,7 @@ struct GrPoint; class GrPathIter { public: /** - Returned by next(). Indicates the next piece of the path. + Returned by next(). Indicates the next piece of the path. */ enum Command { kMove_Command, //!< next() returns 1 pt @@ -44,35 +44,35 @@ public: // Adds a cubic segment kClose_Command, //!< next() returns 0 pts kEnd_Command //!< next() returns 0 pts - // Implictly closes the last + // Implictly closes the last // point }; - + enum ConvexHint { - kNone_ConvexHint, //<! No hint about convexity + kNone_ConvexHint, //<! No hint about convexity // of the path kConvex_ConvexHint, //<! Path is one convex piece - kNonOverlappingConvexPieces_ConvexHint, //<! Multiple convex pieces, + kNonOverlappingConvexPieces_ConvexHint, //<! Multiple convex pieces, // pieces are known to be // disjoint - kSameWindingConvexPieces_ConvexHint, //<! Multiple convex pieces, + kSameWindingConvexPieces_ConvexHint, //<! Multiple convex pieces, // may or may not intersect, - // either all wind cw or all + // either all wind cw or all // wind ccw. - kConcave_ConvexHint //<! Path is known to be + kConcave_ConvexHint //<! Path is known to be // concave }; - + static int NumCommandPoints(Command cmd) { static const int numPoints[] = { 1, 2, 3, 4, 0, 0 }; return numPoints[cmd]; } - + virtual ~GrPathIter() {}; - /** + /** Iterates through the path. Should not be called after kEnd_Command has been returned once. This version retrieves the points for the command. @@ -85,14 +85,14 @@ public: /** * If the host API has knowledge of the convexity of the path * it can be communicated by this hint. Ganesh can make these - * determinations itself. So it is not necessary to compute + * determinations itself. So it is not necessary to compute * convexity status if it isn't already determined. * * @return a hint about the convexity of the path. - */ - virtual ConvexHint hint() const { return kNone_ConvexHint; } + */ + virtual ConvexHint convexHint() const { return kNone_ConvexHint; } - /** + /** Iterates through the path. Should not be called after kEnd_Command has been returned once. This version does not retrieve the points for the command. diff --git a/gpu/include/GrRect.h b/gpu/include/GrRect.h index e98913a57c..96d302f53d 100644 --- a/gpu/include/GrRect.h +++ b/gpu/include/GrRect.h @@ -22,7 +22,7 @@ struct GrIRect { int32_t fLeft, fTop, fRight, fBottom; - + GrIRect() {} GrIRect(int32_t left, int32_t top, int32_t right, int32_t bottom) { fLeft = left; @@ -47,23 +47,22 @@ struct GrIRect { fRight = x + w; fBottom = y + h; } - + void setLTRB(int32_t l, int32_t t, int32_t r, int32_t b) { fLeft = l; fTop = t; fRight = r; fBottom = b; } - + /** * Make the largest representable rectangle - */ void setLargest() { fLeft = fTop = GR_Int32Min; fRight = fBottom = GR_Int32Max; } - + bool quickReject(int l, int t, int r, int b) const { return l >= fRight || fLeft >= r || t >= fBottom || fTop >= b; } @@ -75,10 +74,28 @@ struct GrIRect { if (fBottom < r.fBottom) fBottom = r.fBottom; } + /** + * Sets this rect to the intersection with a clip rect. If there is no + * intersection then this rect will be made empty. + */ + void intersectWith(const GrIRect& clipRect) { + if (fRight < clipRect.fLeft || + fLeft > clipRect.fRight || + fBottom < clipRect.fTop || + fTop > clipRect.fBottom) { + this->setEmpty(); + } else { + fLeft = GrMax(fLeft, clipRect.fLeft); + fRight = GrMin(fRight, clipRect.fRight); + fTop = GrMax(fTop, clipRect.fTop); + fBottom = GrMin(fBottom, clipRect.fBottom); + } + } + friend bool operator==(const GrIRect& a, const GrIRect& b) { return 0 == memcmp(&a, &b, sizeof(a)); } - + friend bool operator!=(const GrIRect& a, const GrIRect& b) { return 0 != memcmp(&a, &b, sizeof(a)); } @@ -91,7 +108,7 @@ struct GrIRect { return fLeft == x && fTop == y && this->width() == w && this->height() == h; } - + bool contains(const GrIRect& r) const { return fLeft <= r.fLeft && fRight >= r.fRight && @@ -102,12 +119,12 @@ struct GrIRect { struct GrIRect16 { int16_t fLeft, fTop, fRight, fBottom; - + int width() const { return fRight - fLeft; } int height() const { return fBottom - fTop; } int area() const { return this->width() * this->height(); } bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; } - + void set(const GrIRect& r) { fLeft = GrToS16(r.fLeft); fTop = GrToS16(r.fTop); @@ -116,12 +133,12 @@ struct GrIRect16 { } }; -/** +/** * 2D Rect struct */ struct GrRect { GrScalar fLeft, fTop, fRight, fBottom; - + /** * Uninitialized rectangle. */ @@ -158,7 +175,7 @@ struct GrRect { GrScalar top() const { return fTop; } GrScalar right() const { return fRight; } GrScalar bottom() const { return fBottom; } - + GrScalar diagonalLengthSqd() const { GrScalar w = width(); GrScalar h = height(); @@ -169,18 +186,18 @@ struct GrRect { // TODO: fixed point sqrt return GrFloatToScalar(sqrtf(GrScalarToFloat(diagonalLengthSqd()))); } - + /** * Returns true if the width or height is <= 0 */ bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; } - + void setEmpty() { fLeft = fTop = fRight = fBottom = 0; } - + /** * returns true if the rectangle is inverted either in x or y */ @@ -192,7 +209,7 @@ struct GrRect { return point.fX >= fLeft && point.fX < fRight && point.fY >= fTop && point.fY < fBottom; } - + /** * Initialize a rectangle to a point. * @param pt the point used to initialize the rectangle. @@ -226,16 +243,16 @@ struct GrRect { /** * Make the largest representable rectangle - * Set the rect to fLeft = fTop = GR_ScalarMin and + * Set the rect to fLeft = fTop = GR_ScalarMin and * fRight = fBottom = GR_ScalarMax. */ void setLargest() { fLeft = fTop = GR_ScalarMin; fRight = fBottom = GR_ScalarMax; } - + /** - Set the rect to fLeft = fTop = GR_ScalarMax and + Set the rect to fLeft = fTop = GR_ScalarMax and fRight = fBottom = GR_ScalarMin. Useful for initializing a bounding rectangle. */ @@ -243,24 +260,24 @@ struct GrRect { fLeft = fTop = GR_ScalarMax; fRight = fBottom = GR_ScalarMin; } - - void setLTRB(GrScalar left, - GrScalar top, - GrScalar right, + + void setLTRB(GrScalar left, + GrScalar top, + GrScalar right, GrScalar bottom) { fLeft = left; fTop = top; fRight = right; fBottom = bottom; } - + void setXYWH(GrScalar x, GrScalar y, GrScalar width, GrScalar height) { fLeft = x; fTop = y; fRight = x + width; fBottom = y + height; } - + /** Expand the edges of the rectangle to include a point. Useful for constructing a bounding rectangle. @@ -269,12 +286,43 @@ struct GrRect { void growToInclude(const GrPoint& pt) { fLeft = GrMin(pt.fX, fLeft); fRight = GrMax(pt.fX, fRight); - + fTop = GrMin(pt.fY, fTop); fBottom = GrMax(pt.fY, fBottom); } /** + * Grows a rect to include another rect. + * @param rect the rect to include + */ + void growToInclude(const GrRect& rect) { + GrAssert(!rect.isEmpty()); + fLeft = GrMin(rect.fLeft, fLeft); + fRight = GrMax(rect.fRight, fRight); + + fTop = GrMin(rect.fTop, fTop); + fBottom = GrMax(rect.fBottom, fBottom); + } + + /** + * Sets this rect to the intersection with a clip rect. If there is no + * intersection then this rect will be made empty. + */ + void intersectWith(const GrRect& clipRect) { + if (fRight < clipRect.fLeft || + fLeft > clipRect.fRight || + fBottom < clipRect.fTop || + fTop > clipRect.fBottom) { + this->setEmpty(); + } else { + fLeft = GrMax(fLeft, clipRect.fLeft); + fRight = GrMin(fRight, clipRect.fRight); + fTop = GrMax(fTop, clipRect.fTop); + fBottom = GrMin(fBottom, clipRect.fBottom); + } + } + + /** * Assigns 4 sequential points in order to construct a counter-clockwise * triangle fan, given the corners of this rect. Returns the address of * the next point, treating pts as an array. @@ -283,6 +331,13 @@ struct GrRect { pts->setRectFan(fLeft, fTop, fRight, fBottom); return pts + 4; } + + bool operator ==(const GrRect& r) const { + return fLeft == r.fLeft && + fTop == r.fTop && + fRight == r.fRight && + fBottom == r.fBottom; + } }; #endif diff --git a/gpu/include/GrStencil.h b/gpu/include/GrStencil.h new file mode 100644 index 0000000000..be3e0f66fb --- /dev/null +++ b/gpu/include/GrStencil.h @@ -0,0 +1,211 @@ +/* + Copyright 2011 Google Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#ifndef GrStencil_DEFINED +#define GrStencil_DEFINED + +#include "GrTypes.h" +/** + * Gr uses the stencil buffer to implement complex clipping inside the + * GrDrawTarget class. The GrDrawTarget makes a subset of the stencil buffer + * bits available for other uses by external code (clients). Client code can + * modify these bits. GrDrawTarget will ignore ref, mask, and writemask bits + * provided by clients that overlap the bits used to implement clipping. The + * client can use the getUsableStencilBits() function to find out how many + * client accessible stencil bits are available. + * + * When code outside the GrDrawTarget class uses the stencil buffer the contract + * is as follows: + * + * > Normal stencil funcs allow the GrGpu client to modify the client bits of + * the stencil buffer outside of the clip. + * > Special functions allow a test against the clip. These are more limited + * than the general stencil functions. + * > Client can assume all client bits are zero initially. + * > Client must ensure that after all its passes are finished it has only + * written to the color buffer in the region inside the clip. Furthermore, it + * must zero all client bits that were modifed (both inside and outside the + * clip). + */ + +/** + * Determines which pixels pass / fail the stencil test. + * Stencil test passes if (ref & mask) FUNC (stencil & mask) is true + */ +enum GrStencilFunc { + kAlways_StencilFunc = 0, + kNever_StencilFunc, + kGreater_StencilFunc, + kGEqual_StencilFunc, + kLess_StencilFunc, + kLEqual_StencilFunc, + kEqual_StencilFunc, + kNotEqual_StencilFunc, + + // Gr stores the current clip in the + // stencil buffer in the high bits that + // are not directly accessible modifiable + // via the GrDrawTarget interface. The below + // stencil funcs test against the current + // clip in addition to the GrDrawTarget + // client's stencil bits. + + // pass if inside the clip + kAlwaysIfInClip_StencilFunc, + kEqualIfInClip_StencilFunc, + kLessIfInClip_StencilFunc, + kLEqualIfInClip_StencilFunc, + kNonZeroIfInClip_StencilFunc, // this one forces the ref to be 0 + + // counts + kStencilFuncCount, + kClipStencilFuncCount = kNonZeroIfInClip_StencilFunc - + kAlwaysIfInClip_StencilFunc + 1, + kBasicStencilFuncCount = kStencilFuncCount - kClipStencilFuncCount +}; + +/** + * Operations to perform based on whether stencil test passed failed. + */ +enum GrStencilOp { + kKeep_StencilOp = 0, // preserve existing stencil value + kReplace_StencilOp, // replace with reference value from stencl test + kIncWrap_StencilOp, // increment and wrap at max + kIncClamp_StencilOp, // increment and clamp at max + kDecWrap_StencilOp, // decrement and wrap at 0 + kDecClamp_StencilOp, // decrement and clamp at 0 + kZero_StencilOp, // zero stencil bits + kInvert_StencilOp, // invert stencil bits + + kStencilOpCount +}; + +/** + * Struct representing stencil state. + */ +struct GrStencilSettings { + GrStencilOp fFrontPassOp; // op to perform when front faces pass + GrStencilOp fBackPassOp; // op to perform when back faces pass + GrStencilOp fFrontFailOp; // op to perform when front faces fail + GrStencilOp fBackFailOp; // op to perform when back faces fail + GrStencilFunc fFrontFunc; // test function for front faces + GrStencilFunc fBackFunc; // test function for back faces + unsigned int fFrontFuncMask; // mask for front face test + unsigned int fBackFuncMask; // mask for back face test + unsigned int fFrontFuncRef; // reference value for front face test + unsigned int fBackFuncRef; // reference value for back face test + unsigned int fFrontWriteMask; // stencil write mask for front faces + unsigned int fBackWriteMask; // stencil write mask for back faces + + bool operator == (const GrStencilSettings& s) const { + // make sure this is tightly packed. + GR_STATIC_ASSERT(0 == sizeof(GrStencilOp)%4); + GR_STATIC_ASSERT(0 == sizeof(GrStencilFunc)%4); + GR_STATIC_ASSERT(sizeof(GrStencilSettings) == + 4*sizeof(GrStencilOp) + + 2*sizeof(GrStencilFunc) + + 6*sizeof(unsigned int)); + return 0 == memcmp(this, &s, sizeof(GrStencilSettings)); + } + + bool operator != (const GrStencilSettings& s) const { + return !(*this == s); + } + + GrStencilSettings& operator =(const GrStencilSettings& s) { + memcpy(this, &s, sizeof(GrStencilSettings)); + return *this; + } + + void setSame(GrStencilOp passOp, + GrStencilOp failOp, + GrStencilFunc func, + unsigned int funcMask, + unsigned int funcRef, + unsigned int writeMask) { + fFrontPassOp = passOp; + fBackPassOp = passOp; + fFrontFailOp = failOp; + fBackFailOp = failOp; + fFrontFunc = func; + fBackFunc = func; + fFrontFuncMask = funcMask; + fBackFuncMask = funcMask; + fFrontFuncRef = funcRef; + fBackFuncRef = funcRef; + fFrontWriteMask = writeMask; + fBackWriteMask = writeMask; + } + + // canonical value for disabled stenciling + static const GrStencilSettings gDisabled; + void setDisabled() { + *this = gDisabled; + } + bool isDisabled() const { + return kKeep_StencilOp == fFrontPassOp && + kKeep_StencilOp == fBackPassOp && + kKeep_StencilOp == fFrontFailOp && + kKeep_StencilOp == fFrontFailOp && + kAlways_StencilFunc == fFrontFunc && + kAlways_StencilFunc == fBackFunc; + } + void invalidate() { + // just write an illegal value to the first member + fFrontPassOp = (GrStencilOp)-1; + } + +private: + friend class GrGpu; + + enum { + kMaxStencilClipPasses = 2 // maximum number of passes to add a clip + // element to the stencil buffer. + }; + + /** + * Given a thing to draw into the stencil clip, a fill type, and a set op + * this function determines: + * 1. Whether the thing can be draw directly to the stencil clip or + * needs to be drawn to the client portion of the stencil first. + * 2. How many passes are needed. + * 3. What those passes are. + * 4. The fill rule that should actually be used to render (will + * always be non-inverted). + * + * @param op the set op to combine this element with the + * existing clip + * @param stencilClipMask mask with just the stencil bit used for clipping + * enabled. + * @param fill in: the fill rule of the element to draw. + * out: the fill rule that should be used to draw + * @param numPasses out: the number of passes needed to add the + * element to the clip. + * @param settings out: the stencil settings to use for each pass + * + * @return true if the clip element's geometry can be drawn directly to the + * stencil clip bit. Will only be true if canBeDirect is true. + * numPasses will be 1 if return value is true. + */ + static bool GetClipPasses(GrSetOp op, + bool canBeDirect, + unsigned int stencilClipMask, + GrPathFill* fill, + int* numPasses, + GrStencilSettings settings[kMaxStencilClipPasses]); +}; + +#endif diff --git a/gpu/include/GrTArray.h b/gpu/include/GrTArray.h index c31c820884..6ef1a13558 100644 --- a/gpu/include/GrTArray.h +++ b/gpu/include/GrTArray.h @@ -176,6 +176,8 @@ public: } } + void reset() { this->pop_back_n(fCount); } + int count() const { return fCount; } bool empty() const { return !fCount; } diff --git a/gpu/include/GrTexture.h b/gpu/include/GrTexture.h index 098ac595a2..2d3928cb25 100644 --- a/gpu/include/GrTexture.h +++ b/gpu/include/GrTexture.h @@ -19,6 +19,7 @@ #define GrTexture_DEFINED #include "GrRefCnt.h" +#include "GrClip.h" class GrTexture; @@ -34,11 +35,16 @@ public: /** * @return the width of the rendertarget */ - virtual int width() const = 0; + int width() const { return fWidth; } /** * @return the height of the rendertarget */ - virtual int height() const = 0; + int height() const { return fHeight; } + + /** + * @return the number of stencil bits in the rendertarget + */ + int stencilBits() const { return fStencilBits; } /** * @return the texture associated with the rendertarget, may be NULL. @@ -46,8 +52,28 @@ public: GrTexture* asTexture() {return fTexture;} protected: - GrRenderTarget(GrTexture* texture) : fTexture(texture) {} + GrRenderTarget(GrTexture* texture, + int width, + int height, + int stencilBits) + : fTexture(texture), + fWidth(width), + fHeight(height), + fStencilBits(stencilBits) {} + + GrTexture* fTexture; + int fWidth; + int fHeight; + int fStencilBits; + +private: + // GrGpu keeps a cached clip in the render target to avoid redundantly + // rendering the clip into the same stencil buffer. + friend class GrGpu; + GrClip fLastStencilClip; + + typedef GrRefCnt INHERITED; }; class GrTexture : public GrRefCnt { diff --git a/gpu/include/GrTypes.h b/gpu/include/GrTypes.h index 02a652a4c1..29e847fd09 100644 --- a/gpu/include/GrTypes.h +++ b/gpu/include/GrTypes.h @@ -201,6 +201,26 @@ enum GrBlendCoeff { kIDA_BlendCoeff, //<! one minus dst alpha }; +/** + * Set Operations used to construct clips. + */ +enum GrSetOp { + kReplace_SetOp, + kIntersect_SetOp, + kUnion_SetOp, + kXor_SetOp, + kDifference_SetOp, + kReverseDifference_SetOp, +}; + +/** + * Clips are composed from these objects. + */ +enum GrClipType { + kRect_ClipType, + kPath_ClipType +}; + /////////////////////////////////////////////////////////////////////////////// // this is included only to make it easy to use this debugging facility diff --git a/gpu/src/GrClip.cpp b/gpu/src/GrClip.cpp index b7a839bfd2..924c01b9f8 100644 --- a/gpu/src/GrClip.cpp +++ b/gpu/src/GrClip.cpp @@ -17,18 +17,30 @@ #include "GrClip.h" -GrClip::GrClip() { +GrClip::GrClip() + : fList(fListMemory, kPreAllocElements) { fBounds.setEmpty(); - this->validate(); + fBoundsValid = true; } -GrClip::GrClip(const GrClip& src) { +GrClip::GrClip(const GrClip& src) + : fList(fListMemory, kPreAllocElements) { *this = src; } -GrClip::GrClip(GrClipIterator* iter) { - fBounds.setEmpty(); - this->setFromIterator(iter); +GrClip::GrClip(const GrIRect& rect) + : fList(fListMemory, kPreAllocElements) { + this->setFromIRect(rect); +} + +GrClip::GrClip(const GrRect& rect) + : fList(fListMemory, kPreAllocElements) { + this->setFromRect(rect); +} + +GrClip::GrClip(GrClipIterator* iter, const GrRect* bounds) + : fList(fListMemory, kPreAllocElements) { + this->setFromIterator(iter, bounds); } GrClip::~GrClip() {} @@ -36,101 +48,100 @@ GrClip::~GrClip() {} GrClip& GrClip::operator=(const GrClip& src) { fList = src.fList; fBounds = src.fBounds; - this->validate(); + fBoundsValid = src.fBoundsValid; return *this; } void GrClip::setEmpty() { fList.reset(); fBounds.setEmpty(); - this->validate(); + fBoundsValid = true; } -void GrClip::setRect(const GrIRect& r) { +void GrClip::setFromRect(const GrRect& r) { fList.reset(); - - // we need a canonical "empty" rect, so that our operator== will behave - // correctly with two empty clips. if (r.isEmpty()) { - fBounds.setEmpty(); + // use a canonical empty rect for == testing. + setEmpty(); } else { + fList.push_back(); + fList.back().fRect = r; + fList.back().fType = kRect_ClipType; fBounds = r; - } - this->validate(); -} - -void GrClip::addRect(const GrIRect& r) { - if (!r.isEmpty()) { - this->validate(); - if (this->isEmpty()) { - GrAssert(fList.count() == 0); - fBounds = r; - } else { - if (this->isRect()) { - *fList.append() = fBounds; - } - *fList.append() = r; - fBounds.unionWith(r); - } - this->validate(); + fBoundsValid = true; } } -void GrClip::setFromIterator(GrClipIterator* iter) { - this->setEmpty(); - if (iter) { - for (iter->rewind(); !iter->isDone(); iter->next()) { - GrIRect r; - iter->getRect(&r); - this->addRect(r); - } +void GrClip::setFromIRect(const GrIRect& r) { + fList.reset(); + if (r.isEmpty()) { + // use a canonical empty rect for == testing. + setEmpty(); + } else { + fList.push_back(); + fList.back().fRect.set(r); + fList.back().fType = kRect_ClipType; + fBounds.set(r); + fBoundsValid = true; } } -/////////////////////////////////////////////////////////////////////////////// +void GrClip::setFromIterator(GrClipIterator* iter, const GrRect* bounds) { + fList.reset(); -void GrClipIter::reset(const GrClip& clip) { fClip = &clip; fIndex = 0; } + int rectCount = 0; -bool GrClipIter::isDone() { - return (NULL == fClip) || (fIndex >= fClip->countRects()); -} + // compute bounds for common case of series of intersecting rects. + bool isectRectValid = true; -void GrClipIter::rewind() { fIndex = 0; } -void GrClipIter::getRect(GrIRect* r) { *r = fClip->getRects()[fIndex]; } -void GrClipIter::next() { fIndex += 1; } -void GrClipIter::computeBounds(GrIRect* r) { - if (NULL == fClip) { - r->setEmpty(); - } else { - *r = fClip->getBounds(); + if (iter) { + for (iter->rewind(); !iter->isDone(); iter->next()) { + Element& e = fList.push_back(); + e.fType = iter->getType(); + e.fOp = iter->getOp(); + // iterators should not emit replace + GrAssert(kReplace_SetOp != e.fOp); + switch (e.fType) { + case kRect_ClipType: + iter->getRect(&e.fRect); + ++rectCount; + if (isectRectValid) { + if (1 == rectCount || kIntersect_SetOp == e.fOp) { + GrAssert(fList.count() <= 2); + if (fList.count() > 1) { + GrAssert(2 == rectCount); + rectCount = 1; + fList.pop_back(); + GrAssert(kRect_ClipType == fList.back().fType); + fList.back().fRect.intersectWith(e.fRect); + } + } else { + isectRectValid = false; + } + } + break; + case kPath_ClipType: + e.fPath.resetFromIter(iter->getPathIter()); + e.fPathFill = iter->getPathFill(); + isectRectValid = false; + break; + default: + GrCrash("Unknown clip element type."); + } + } } -} - -/////////////////////////////////////////////////////////////////////////////// - -#if GR_DEBUG - -void GrClip::validate() const { - if (fBounds.isEmpty()) { - GrAssert(0 == fBounds.fLeft); - GrAssert(0 == fBounds.fTop); - GrAssert(0 == fBounds.fRight); - GrAssert(0 == fBounds.fBottom); - GrAssert(0 == fList.count()); - } else { - int count = fList.count(); - if (count > 0) { - GrAssert(count > 1); - GrAssert(!fList[0].isEmpty()); - GrIRect bounds = fList[0]; - for (int i = 1; i < count; i++) { - GrAssert(!fList[i].isEmpty()); - bounds.unionWith(fList[i]); + fBoundsValid = false; + if (NULL == bounds) { + if (isectRectValid) { + fBoundsValid = true; + if (rectCount > 0) { + fBounds = fList[0].fRect; + } else { + fBounds.setEmpty(); } - GrAssert(fBounds == bounds); } + } else { + fBounds = *bounds; + fBoundsValid = true; } } - -#endif - diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp index 840e773313..a047895ced 100644 --- a/gpu/src/GrContext.cpp +++ b/gpu/src/GrContext.cpp @@ -145,7 +145,7 @@ GrTextureEntry* GrContext::createAndLockTexture(GrTextureKey* key, GrDrawTarget::AutoStateRestore asr(fGpu); fGpu->setRenderTarget(texture->asRenderTarget()); fGpu->setTexture(0, clampEntry->texture()); - fGpu->setStencilPass(GrDrawTarget::kNone_StencilPass); + fGpu->disableStencil(); fGpu->setViewMatrix(GrMatrix::I()); fGpu->setAlpha(0xff); fGpu->setBlendFunc(kOne_BlendCoeff, kZero_BlendCoeff); @@ -284,7 +284,7 @@ void GrContext::setClip(const GrClip& clip) { void GrContext::setClip(const GrIRect& rect) { GrClip clip; - clip.setRect(rect); + clip.setFromIRect(rect); fGpu->setClip(clip); } @@ -297,7 +297,10 @@ void GrContext::eraseColor(GrColor color) { void GrContext::drawPaint(const GrPaint& paint) { // set rect to be big enough to fill the space, but not super-huge, so we // don't overflow fixed-point implementations - GrRect r(fGpu->getClip().getBounds()); + GrRect r; + r.setLTRB(0, 0, + GrIntToScalar(getRenderTarget()->width()), + GrIntToScalar(getRenderTarget()->height())); GrMatrix inverse; if (fGpu->getViewInverse(&inverse)) { inverse.mapRect(&r); @@ -546,6 +549,15 @@ void GrContext::drawPath(const GrPaint& paint, fPathRenderer->drawPath(target, enabledStages, path, fill, translate); } +void GrContext::drawPath(const GrPaint& paint, + const GrPath& path, + GrPathFill fill, + const GrPoint* translate) { + GrPath::Iter iter(path); + this->drawPath(paint, &iter, fill, translate); +} + + //////////////////////////////////////////////////////////////////////////////// void GrContext::flush(bool flushRenderTarget) { @@ -751,7 +763,8 @@ GrContext::GrContext(GrGpu* gpu) { #if BATCH_RECT_TO_RECT fDrawBuffer->setQuadIndexBuffer(this->getQuadIndexBuffer()); #endif - fPathRenderer = new GrDefaultPathRenderer(fGpu->supportsSingleStencilPassWinding()); + fPathRenderer = new GrDefaultPathRenderer(fGpu->supportsTwoSidedStencil(), + fGpu->supportsStencilWrapOps()); } bool GrContext::finalizeTextureKey(GrTextureKey* key, diff --git a/gpu/src/GrDrawTarget.cpp b/gpu/src/GrDrawTarget.cpp index ca525b3b02..7973361890 100644 --- a/gpu/src/GrDrawTarget.cpp +++ b/gpu/src/GrDrawTarget.cpp @@ -358,14 +358,6 @@ void GrDrawTarget::setSamplerState(int stage, const GrSamplerState& state) { fCurrDrawState.fSamplerStates[stage] = state; } -void GrDrawTarget::setStencilPass(StencilPass pass) { - fCurrDrawState.fStencilPass = pass; -} - -void GrDrawTarget::setReverseFill(bool reverse) { - fCurrDrawState.fReverseFill = reverse; -} - void GrDrawTarget::enableState(uint32_t bits) { fCurrDrawState.fFlagBits |= bits; } diff --git a/gpu/src/GrGLTexture.cpp b/gpu/src/GrGLTexture.cpp index 9fd1f57edf..ae991e8362 100644 --- a/gpu/src/GrGLTexture.cpp +++ b/gpu/src/GrGLTexture.cpp @@ -22,12 +22,14 @@ GrGLRenderTarget::GrGLRenderTarget(const GLRenderTargetIDs& ids, GLuint stencilBits, const GrGLIRect& viewport, GrGLTexture* texture, - GrGpuGL* gl) : INHERITED(texture) { + GrGpuGL* gl) : INHERITED(texture, + viewport.fWidth, + viewport.fHeight, + stencilBits) { fGL = gl; fRTFBOID = ids.fRTFBOID; fTexFBOID = ids.fTexFBOID; fStencilRenderbufferID = ids.fStencilRenderbufferID; - fStencilBits = stencilBits; fMSColorRenderbufferID = ids.fMSColorRenderbufferID; fNeedsResolve = false; fViewport = viewport; diff --git a/gpu/src/GrGpu.cpp b/gpu/src/GrGpu.cpp index 019c99ff6d..f42e316082 100644 --- a/gpu/src/GrGpu.cpp +++ b/gpu/src/GrGpu.cpp @@ -22,12 +22,13 @@ #include "GrIndexBuffer.h" #include "GrVertexBuffer.h" #include "GrBufferAllocPool.h" +#include "GrPathRenderer.h" // probably makes no sense for this to be less than a page static const size_t VERTEX_POOL_VB_SIZE = 1 << 12; static const int VERTEX_POOL_VB_COUNT = 1; -/////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// size_t GrTexture::BytesPerPixel(PixelConfig config) { switch (config) { @@ -56,7 +57,7 @@ bool GrTexture::PixelConfigIsOpaque(PixelConfig config) { } -/////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// extern void gr_run_unittests(); @@ -68,7 +69,10 @@ GrGpu::GrGpu() : f8bitPaletteSupport(false), fVertexPool(NULL), fIndexPool(NULL), fQuadIndexBuffer(NULL), - fUnitSquareVertexBuffer(NULL) { + fUnitSquareVertexBuffer(NULL), + fPathRenderer(NULL), + fVertexPoolInUse(false), + fIndexPoolInUse(false) { #if GR_DEBUG // gr_run_unittests(); #endif @@ -80,6 +84,7 @@ GrGpu::~GrGpu() { GrSafeUnref(fUnitSquareVertexBuffer); delete fVertexPool; delete fIndexPool; + delete fPathRenderer; } void GrGpu::resetContext() { @@ -91,7 +96,7 @@ void GrGpu::unimpl(const char msg[]) { #endif } -/////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// static const int MAX_QUADS = 1 << 12; // max possible: (1 << 14) - 1; @@ -159,7 +164,7 @@ const GrVertexBuffer* GrGpu::getUnitSquareVertexBuffer() const { return fUnitSquareVertexBuffer; } -/////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// void GrGpu::clipWillBeSet(const GrClip& newClip) { if (newClip != fClip) { @@ -167,8 +172,113 @@ void GrGpu::clipWillBeSet(const GrClip& newClip) { } } +//////////////////////////////////////////////////////////////////////////////// + +// stencil settings to use when clip is in stencil +const GrStencilSettings GrGpu::gClipStencilSettings = { + kKeep_StencilOp, kKeep_StencilOp, + kKeep_StencilOp, kKeep_StencilOp, + kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc, + 0, 0, + 0, 0, + 0, 0 +}; + +// converts special stencil func to +static const GrStencilFunc gGrClipToNormalStencilFunc[2][kClipStencilFuncCount] = { + {// Stencil-Clipping is DISABLED, effectively always inside the clip + // In the Clip Funcs + kAlways_StencilFunc, // kAlwaysIfInClip_StencilFunc + kEqual_StencilFunc, // kEqualIfInClip_StencilFunc + kLess_StencilFunc, // kLessIfInClip_StencilFunc + kLEqual_StencilFunc, // kLEqualIfInClip_StencilFunc + // Special in the clip func that forces user's ref to be 0. + kNotEqual_StencilFunc, // kNonZeroIfInClip_StencilFunc + // make ref 0 and do normal nequal. + }, + {// Stencil-Clipping is ENABLED + // In the Clip Funcs + kEqual_StencilFunc, // kAlwaysIfInClip_StencilFunc + // eq stencil clip bit, mask + // out user bits. + + kEqual_StencilFunc, // kEqualIfInClip_StencilFunc + // add stencil bit to mask and ref + + kLess_StencilFunc, // kLessIfInClip_StencilFunc + kLEqual_StencilFunc, // kLEqualIfInClip_StencilFunc + // for both of these we can add + // the clip bit to the mask and + // ref and compare as normal + // Special in the clip func that forces user's ref to be 0. + kLess_StencilFunc, // kNonZeroIfInClip_StencilFunc + // make ref have only the clip bit set + // and make comparison be less + // 10..0 < 1..user_bits.. + } +}; + +GrStencilFunc GrGpu::ConvertStencilFunc(bool stencilInClip, GrStencilFunc func) { + GrAssert(func >= 0); + if (func >= kBasicStencilFuncCount) { + GrAssert(func < kStencilFuncCount); + func = gGrClipToNormalStencilFunc[stencilInClip ? 1 : 0][func - kBasicStencilFuncCount]; + GrAssert(func >= 0 && func < kBasicStencilFuncCount); + } + return func; +} + +void GrGpu::ConvertStencilFuncAndMask(GrStencilFunc func, + bool clipInStencil, + unsigned int clipBit, + unsigned int userBits, + unsigned int* ref, + unsigned int* mask) { + if (func < kBasicStencilFuncCount) { + *mask &= userBits; + *ref &= userBits; + } else { + if (clipInStencil) { + switch (func) { + case kAlwaysIfInClip_StencilFunc: + *mask = clipBit; + *ref = clipBit; + break; + case kEqualIfInClip_StencilFunc: + case kLessIfInClip_StencilFunc: + case kLEqualIfInClip_StencilFunc: + *mask = (*mask & userBits) | clipBit; + *ref = (*ref & userBits) | clipBit; + break; + case kNonZeroIfInClip_StencilFunc: + *mask = (*mask & userBits) | clipBit; + *ref = clipBit; + break; + default: + GrCrash("Unknown stencil func"); + } + } else { + *mask &= userBits; + *ref &= userBits; + } + } +} + +//////////////////////////////////////////////////////////////////////////////// + +#define VISUALIZE_COMPLEX_CLIP 0 + +#if VISUALIZE_COMPLEX_CLIP + #include "GrRandom.h" + GrRandom gRandom; + #define SET_RANDOM_COLOR this->setColor(0xff000000 | gRandom.nextU()); +#else + #define SET_RANDOM_COLOR +#endif + bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) { const GrIRect* r = NULL; + GrIRect clipRect; // we check this early because we need a valid // render target to setup stencil clipping @@ -179,80 +289,135 @@ bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) { } if (fCurrDrawState.fFlagBits & kClip_StateBit) { - fClipState.fClipInStencil = fClip.countRects() > 1; + GrRenderTarget& rt = *fCurrDrawState.fRenderTarget; + + GrRect bounds; + GrRect rtRect; + rtRect.setLTRB(0, 0, + GrIntToScalar(rt.width()), GrIntToScalar(rt.height())); + if (fClip.hasBounds()) { + bounds = fClip.getBounds(); + bounds.intersectWith(rtRect); + } else { + bounds = rtRect; + } + + bounds.roundOut(&clipRect); + if (clipRect.isEmpty()) { + clipRect.setLTRB(0,0,0,0); + } + r = &clipRect; + + fClipState.fClipInStencil = !fClip.isRect() && + !fClip.isEmpty() && + !bounds.isEmpty(); if (fClipState.fClipInStencil && (fClipState.fClipIsDirty || - fClipState.fStencilClipTarget != fCurrDrawState.fRenderTarget)) { + fClip != rt.fLastStencilClip)) { + + rt.fLastStencilClip = fClip; + // we set the current clip to the bounds so that our recursive + // draws are scissored to them. We use the copy of the complex clip + // in the rt to render + const GrClip& clip = rt.fLastStencilClip; + fClip.setFromRect(bounds); AutoStateRestore asr(this); - AutoGeometrySrcRestore agsr(this); - - // We have to use setVertexSourceToBuffer (and index) in order - // to ensure we correctly restore the client's geom sources. - // We tack the clip verts onto the vertex pool but we don't - // use the various helper functions because of their side effects. - - int rectTotal = fClip.countRects(); - if (NULL == fVertexPool) { - fVertexPool = new GrVertexBufferAllocPool(this, - true, - VERTEX_POOL_VB_SIZE, - VERTEX_POOL_VB_COUNT); - } else if (kBuffer_GeometrySrcType == fGeometrySrc.fVertexSrc) { - // we can't reset if vertex source is array or reserved - // because then the client data is in the pool! - fVertexPool->reset(); - } - const GrVertexBuffer* vertexBuffer; - int vStart; - GrPoint* rectVertices = - reinterpret_cast<GrPoint*>(fVertexPool->makeSpace(0, - rectTotal * 4, - &vertexBuffer, - &vStart)); - for (int r = 0; r < rectTotal; ++r) { - const GrIRect& rect = fClip.getRects()[r]; - rectVertices[4 * r].setIRectFan(rect.fLeft, rect.fTop, - rect.fRight, rect.fBottom); - } - fVertexPool->unlock(); - this->setVertexSourceToBuffer(0, vertexBuffer); - this->setIndexSourceToBuffer(getQuadIndexBuffer()); + AutoInternalDrawGeomRestore aidgr(this); + this->setViewMatrix(GrMatrix::I()); - // don't clip the clip or recurse! - this->disableState(kClip_StateBit); - this->eraseStencilClip(); - this->setStencilPass((GrDrawTarget::StencilPass)kSetClip_StencilPass); - int currRect = 0; - while (currRect < rectTotal) { - int rectCount = GrMin(MAX_QUADS, - rectTotal - currRect); - this->drawIndexed(kTriangles_PrimitiveType, - vStart + currRect * 4, - 0, - rectCount*4, - rectCount*6); - currRect += rectCount; + this->eraseStencilClip(clipRect); + this->flushScissor(NULL); +#if !VISUALIZE_COMPLEX_CLIP + this->enableState(kNoColorWrites_StateBit); +#else + this->disableState(kNoColorWrites_StateBit); +#endif + int count = clip.getElementCount(); + int clipBit = rt.stencilBits(); + clipBit = (1 << (clipBit-1)); + + // walk through each clip element and perform its set op + // with the existing clip. + for (int c = 0; c < count; ++c) { + GrPathFill fill; + // enabled at bottom of loop + this->disableState(kModifyStencilClip_StateBit); + + bool canDrawDirectToClip; + if (kRect_ClipType == clip.getElementType(c)) { + canDrawDirectToClip = true; + fill = kEvenOdd_PathFill; + } else { + fill = clip.getPathFill(c); + canDrawDirectToClip = getPathRenderer()->requiresStencilPass(clip.getPath(c)); + } + + GrSetOp op = 0 == c ? kReplace_SetOp : clip.getOp(c); + int passes; + GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses]; + + canDrawDirectToClip = GrStencilSettings::GetClipPasses(op, canDrawDirectToClip, + clipBit, &fill, + &passes, stencilSettings); + + // draw the element to the client stencil bits if necessary + if (!canDrawDirectToClip) { + if (kRect_ClipType == clip.getElementType(c)) { + static const GrStencilSettings gDrawToStencil = { + kIncClamp_StencilOp, kIncClamp_StencilOp, + kIncClamp_StencilOp, kIncClamp_StencilOp, + kAlways_StencilFunc, kAlways_StencilFunc, + 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, + }; + this->setStencil(gDrawToStencil); + SET_RANDOM_COLOR + this->drawSimpleRect(clip.getRect(c), NULL, 0); + } else { + SET_RANDOM_COLOR + getPathRenderer()->drawPathToStencil(this, clip.getPath(c), fill, NULL); + } + } + + // now we modify the clip bit by rendering either the clip + // element directly or a bounding rect of the entire clip. + this->enableState(kModifyStencilClip_StateBit); + for (int p = 0; p < passes; ++p) { + this->setStencil(stencilSettings[p]); + if (canDrawDirectToClip) { + if (kRect_ClipType == clip.getElementType(c)) { + SET_RANDOM_COLOR + this->drawSimpleRect(clip.getRect(c), NULL, 0); + } else { + SET_RANDOM_COLOR + getPathRenderer()->drawPath(this, 0, clip.getPath(c), fill, NULL); + } + } else { + SET_RANDOM_COLOR + this->drawSimpleRect(bounds, 0, NULL); + } + } } - fClipState.fStencilClipTarget = fCurrDrawState.fRenderTarget; + fClip = clip; + // recusive draws would have disabled this. + fClipState.fClipInStencil = true; } fClipState.fClipIsDirty = false; - if (!fClipState.fClipInStencil) { - r = &fClip.getBounds(); - } } + // Must flush the scissor after graphics state - if (!flushGraphicsState(type)) { + if (!this->flushGraphicsState(type)) { return false; } - flushScissor(r); + this->flushScissor(r); return true; } - -/////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// void GrGpu::drawIndexed(GrPrimitiveType type, int startVertex, @@ -314,10 +479,11 @@ void GrGpu::finalizeReservedIndices() { void GrGpu::prepareVertexPool() { if (NULL == fVertexPool) { - fVertexPool = new GrVertexBufferAllocPool(this, true, - VERTEX_POOL_VB_SIZE, + fVertexPool = new GrVertexBufferAllocPool(this, true, + VERTEX_POOL_VB_SIZE, VERTEX_POOL_VB_COUNT); - } else { + } else if (!fVertexPoolInUse) { + // the client doesn't have valid data in the pool fVertexPool->reset(); } } @@ -325,7 +491,8 @@ void GrGpu::prepareVertexPool() { void GrGpu::prepareIndexPool() { if (NULL == fVertexPool) { fIndexPool = new GrIndexBufferAllocPool(this, true, 0, 1); - } else { + } else if (!fIndexPoolInUse) { + // the client doesn't have valid data in the pool fIndexPool->reset(); } } @@ -339,7 +506,7 @@ bool GrGpu::acquireGeometryHelper(GrVertexLayout vertexLayout, if (fReservedGeometry.fVertexCount) { GrAssert(NULL != vertices); - prepareVertexPool(); + this->prepareVertexPool(); *vertices = fVertexPool->makeSpace(vertexLayout, fReservedGeometry.fVertexCount, @@ -354,7 +521,7 @@ bool GrGpu::acquireGeometryHelper(GrVertexLayout vertexLayout, if (fReservedGeometry.fIndexCount) { GrAssert(NULL != indices); - prepareIndexPool(); + this->prepareIndexPool(); *indices = fIndexPool->makeSpace(fReservedGeometry.fIndexCount, &fCurrPoolIndexBuffer, @@ -397,7 +564,17 @@ void GrGpu::setIndexSourceToArrayHelper(const void* indexArray, int indexCount) GR_DEBUGASSERT(success); } -/////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +GrPathRenderer* GrGpu::getPathRenderer() { + if (NULL == fPathRenderer) { + fPathRenderer = new GrDefaultPathRenderer(this->supportsTwoSidedStencil(), + this->supportsStencilWrapOps()); + } + return fPathRenderer; +} + +//////////////////////////////////////////////////////////////////////////////// const GrGpu::Stats& GrGpu::getStats() const { return fStats; diff --git a/gpu/src/GrGpuGL.cpp b/gpu/src/GrGpuGL.cpp index b30dd027cf..2574b157a6 100644 --- a/gpu/src/GrGpuGL.cpp +++ b/gpu/src/GrGpuGL.cpp @@ -49,8 +49,8 @@ static const GLenum gXfermodeCoeff2Blend[] = { /////////////////////////////////////////////////////////////////////////////// -void GrGpuGL::AdjustTextureMatrix(const GrGLTexture* texture, - GrSamplerState::SampleMode mode, +void GrGpuGL::AdjustTextureMatrix(const GrGLTexture* texture, + GrSamplerState::SampleMode mode, GrMatrix* matrix) { GrAssert(NULL != texture); GrAssert(NULL != matrix); @@ -80,7 +80,7 @@ void GrGpuGL::AdjustTextureMatrix(const GrGLTexture* texture, } } -bool GrGpuGL::TextureMatrixIsIdentity(const GrGLTexture* texture, +bool GrGpuGL::TextureMatrixIsIdentity(const GrGLTexture* texture, const GrSamplerState& sampler) { GrAssert(NULL != texture); if (!sampler.getMatrix().isIdentity()) { @@ -104,7 +104,7 @@ bool GrGpuGL::TextureMatrixIsIdentity(const GrGLTexture* texture, static bool gPrintStartupSpew; -bool fbo_test(GrGLExts exts, int w, int h) { +static bool fbo_test(GrGLExts exts, int w, int h) { GLint savedFBO; GLint savedTexUnit; @@ -169,8 +169,6 @@ GrGpuGL::GrGpuGL() { GrAssert(maxTextureUnits > kNumStages); #endif - fCurrDrawState = fHWDrawState; - //////////////////////////////////////////////////////////////////////////// // Check for supported features. @@ -263,14 +261,24 @@ GrGpuGL::GrGpuGL() { // we could also look for GL_ATI_separate_stencil extension or // GL_EXT_stencil_two_side but they use different function signatures // than GL2.0+ (and than each other). - fSingleStencilPassForWinding = (major >= 2); + fTwoSidedStencilSupport = (major >= 2); + // supported on GL 1.4 and higher or by extension + fStencilWrapOpsSupport = (major > 1) || + (1 == major) && (minor >= 4) || + has_gl_extension("GL_EXT_stencil_wrap"); #else // ES 2 has two sided stencil but 1.1 doesn't. There doesn't seem to be // an ES1 extension. - fSingleStencilPassForWinding = (major >= 2); + fTwoSidedStencilSupport = (major >= 2); + // stencil wrap support is in ES2, ES1 requires extension. + fStencilWrapOpsSupport = (major > 1) || + has_gl_extension("GL_OES_stencil_wrap"); + #endif if (gPrintStartupSpew) { - GrPrintf("Single Stencil Pass For Winding: %s\n", (fSingleStencilPassForWinding ? "YES" : "NO")); + GrPrintf("Stencil Caps: TwoSide: %s, Wrap: %s\n", + (fTwoSidedStencilSupport ? "YES" : "NO"), + (fStencilWrapOpsSupport ? "YES" : "NO")); } #if GR_SUPPORT_GLDESKTOP @@ -424,8 +432,9 @@ void GrGpuGL::resetContextHelper() { fHWBlendDisabled = false; GR_GL(Enable(GL_BLEND)); - // this is always disabled GR_GL(Disable(GL_CULL_FACE)); + GR_GL(FrontFace(GL_CCW)); + fHWDrawState.fDrawFace = kBoth_DrawFace; GR_GL(Disable(GL_DITHER)); #if GR_SUPPORT_GLDESKTOP @@ -434,14 +443,15 @@ void GrGpuGL::resetContextHelper() { GR_GL(Disable(GL_MULTISAMPLE)); #endif + GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)); + fHWDrawState.fFlagBits = 0; + // we only ever use lines in hairline mode GR_GL(LineWidth(1)); // invalid fActiveTextureUnitIdx = -1; - fHWDrawState.fFlagBits = 0; - // illegal values fHWDrawState.fSrcBlend = (GrBlendCoeff)-1; fHWDrawState.fDstBlend = (GrBlendCoeff)-1; @@ -454,7 +464,7 @@ void GrGpuGL::resetContextHelper() { fHWDrawState.fSamplerStates[s].setRadial2Params(-GR_ScalarMax, -GR_ScalarMax, true); - + fHWDrawState.fSamplerStates[s].setMatrix(GrMatrix::InvalidMatrix()); } @@ -463,16 +473,9 @@ void GrGpuGL::resetContextHelper() { GR_GL(Disable(GL_SCISSOR_TEST)); fHWBounds.fViewportRect.invalidate(); - // disabling the stencil test also disables - // stencil buffer writes - GR_GL(Disable(GL_STENCIL_TEST)); - GR_GL(StencilMask(0xffffffff)); - GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)); - fHWDrawState.fReverseFill = false; - fHWDrawState.fStencilPass = kNone_StencilPass; + fHWDrawState.fStencilSettings.invalidate(); fHWStencilClip = false; fClipState.fClipIsDirty = true; - fClipState.fStencilClipTarget = NULL; fHWGeometryState.fIndexBuffer = NULL; fHWGeometryState.fVertexBuffer = NULL; @@ -480,6 +483,7 @@ void GrGpuGL::resetContextHelper() { GR_GL(BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); fHWGeometryState.fArrayPtrsDirty = true; + GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)); fHWDrawState.fRenderTarget = NULL; } @@ -491,7 +495,7 @@ void GrGpuGL::resetContext() { GrRenderTarget* GrGpuGL::createPlatformRenderTarget( intptr_t platformRenderTarget, int stencilBits, - int width, + int width, int height) { GrGLRenderTarget::GLRenderTargetIDs rtIDs; rtIDs.fStencilRenderbufferID = 0; @@ -1030,11 +1034,11 @@ GrIndexBuffer* GrGpuGL::createIndexBuffer(uint32_t size, bool dynamic) { void GrGpuGL::flushScissor(const GrIRect* rect) { GrAssert(NULL != fCurrDrawState.fRenderTarget); const GrGLIRect& vp = - ((GrGLRenderTarget*)fCurrDrawState.fRenderTarget)->viewport(); + ((GrGLRenderTarget*)fCurrDrawState.fRenderTarget)->getViewport(); GrGLIRect scissor; if (NULL != rect) { - scissor.setRelativeTo(vp, rect->fLeft, rect->fTop, + scissor.setRelativeTo(vp, rect->fLeft, rect->fTop, rect->width(), rect->height()); if (scissor.contains(vp)) { rect = NULL; @@ -1068,12 +1072,12 @@ void GrGpuGL::eraseColor(GrColor color) { fHWBounds.fScissorEnabled = false; } GR_GL(ColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE)); + fHWDrawState.fFlagBits &= ~kNoColorWrites_StateBit; GR_GL(ClearColor(GrColorUnpackR(color)/255.f, GrColorUnpackG(color)/255.f, GrColorUnpackB(color)/255.f, GrColorUnpackA(color)/255.f)); GR_GL(Clear(GL_COLOR_BUFFER_BIT)); - fDirtyFlags.fWriteMaskChanged = true; } void GrGpuGL::eraseStencil(uint32_t value, uint32_t mask) { @@ -1088,15 +1092,21 @@ void GrGpuGL::eraseStencil(uint32_t value, uint32_t mask) { GR_GL(StencilMask(mask)); GR_GL(ClearStencil(value)); GR_GL(Clear(GL_STENCIL_BUFFER_BIT)); - fDirtyFlags.fWriteMaskChanged = true; + fHWDrawState.fStencilSettings.invalidate(); } -void GrGpuGL::eraseStencilClip() { +void GrGpuGL::eraseStencilClip(const GrIRect& rect) { GrAssert(NULL != fCurrDrawState.fRenderTarget); - GLint stencilBitCount = ((GrGLRenderTarget*)fCurrDrawState.fRenderTarget)->getStencilBits(); + GLint stencilBitCount = fCurrDrawState.fRenderTarget->stencilBits(); GrAssert(stencilBitCount > 0); GLint clipStencilMask = (1 << (stencilBitCount - 1)); - eraseStencil(0, clipStencilMask); + + flushRenderTarget(); + flushScissor(&rect); + GR_GL(StencilMask(clipStencilMask)); + GR_GL(ClearStencil(0)); + GR_GL(Clear(GL_STENCIL_BUFFER_BIT)); + fHWDrawState.fStencilSettings.invalidate(); } void GrGpuGL::forceRenderTargetFlush() { @@ -1117,13 +1127,13 @@ bool GrGpuGL::readPixels(int left, int top, int width, int height, } flushRenderTarget(); - const GrGLIRect& glvp = ((GrGLRenderTarget*)fCurrDrawState.fRenderTarget)->viewport(); - + const GrGLIRect& glvp = ((GrGLRenderTarget*)fCurrDrawState.fRenderTarget)->getViewport(); + // the read rect is viewport-relative GrGLIRect readRect; readRect.setRelativeTo(glvp, left, top, width, height); GR_GL(ReadPixels(readRect.fLeft, readRect.fBottom, - readRect.fWidth, readRect.fHeight, + readRect.fWidth, readRect.fHeight, format, type, buffer)); // now reverse the order of the rows, since GL's are bottom-to-top, but our @@ -1166,7 +1176,7 @@ void GrGpuGL::flushRenderTarget() { #endif fDirtyFlags.fRenderTargetChanged = true; fHWDrawState.fRenderTarget = fCurrDrawState.fRenderTarget; - const GrGLIRect& vp = rt->viewport(); + const GrGLIRect& vp = rt->getViewport(); if (true || fHWBounds.fViewportRect != vp) { vp.pushToGLViewport(); fHWBounds.fViewportRect = vp; @@ -1183,6 +1193,27 @@ GLenum gPrimitiveType2GLMode[] = { GL_LINE_STRIP }; +#define SWAP_PER_DRAW 0 + +#if SWAP_PER_DRAW + #if GR_MAC_BUILD + #include <AGL/agl.h> + #elif GR_WIN32_BUILD + void SwapBuf() { + DWORD procID = GetCurrentProcessId(); + HWND hwnd = GetTopWindow(GetDesktopWindow()); + while(hwnd) { + DWORD wndProcID = 0; + GetWindowThreadProcessId(hwnd, &wndProcID); + if(wndProcID == procID) { + SwapBuffers(GetDC(hwnd)); + } + hwnd = GetNextWindow(hwnd, GW_HWNDNEXT); + } + } + #endif +#endif + void GrGpuGL::drawIndexedHelper(GrPrimitiveType type, uint32_t startVertex, uint32_t startIndex, @@ -1201,6 +1232,18 @@ void GrGpuGL::drawIndexedHelper(GrPrimitiveType type, GR_GL(DrawElements(gPrimitiveType2GLMode[type], indexCount, GL_UNSIGNED_SHORT, indices)); +#if SWAP_PER_DRAW + glFlush(); + #if GR_MAC_BUILD + aglSwapBuffers(aglGetCurrentContext()); + int set_a_break_pt_here = 9; + aglSwapBuffers(aglGetCurrentContext()); + #elif GR_WIN32_BUILD + SwapBuf(); + int set_a_break_pt_here = 9; + SwapBuf(); + #endif +#endif } void GrGpuGL::drawNonIndexedHelper(GrPrimitiveType type, @@ -1218,6 +1261,18 @@ void GrGpuGL::drawNonIndexedHelper(GrPrimitiveType type, // account for startVertex in the DrawElements case. So we always // rely on setupGeometry to have accounted for startVertex. GR_GL(DrawArrays(gPrimitiveType2GLMode[type], 0, vertexCount)); +#if SWAP_PER_DRAW + glFlush(); + #if GR_MAC_BUILD + aglSwapBuffers(aglGetCurrentContext()); + int set_a_break_pt_here = 9; + aglSwapBuffers(aglGetCurrentContext()); + #elif GR_WIN32_BUILD + SwapBuf(); + int set_a_break_pt_here = 9; + SwapBuf(); + #endif +#endif } void GrGpuGL::resolveTextureRenderTarget(GrGLTexture* texture) { @@ -1257,203 +1312,162 @@ void GrGpuGL::resolveTextureRenderTarget(GrGLTexture* texture) { } } +static const GLenum grToGLStencilFunc[] = { + GL_ALWAYS, // kAlways_StencilFunc + GL_NEVER, // kNever_StencilFunc + GL_GREATER, // kGreater_StencilFunc + GL_GEQUAL, // kGEqual_StencilFunc + GL_LESS, // kLess_StencilFunc + GL_LEQUAL, // kLEqual_StencilFunc, + GL_EQUAL, // kEqual_StencilFunc, + GL_NOTEQUAL, // kNotEqual_StencilFunc, +}; +GR_STATIC_ASSERT(GR_ARRAY_COUNT(grToGLStencilFunc) == kBasicStencilFuncCount); +GR_STATIC_ASSERT(0 == kAlways_StencilFunc); +GR_STATIC_ASSERT(1 == kNever_StencilFunc); +GR_STATIC_ASSERT(2 == kGreater_StencilFunc); +GR_STATIC_ASSERT(3 == kGEqual_StencilFunc); +GR_STATIC_ASSERT(4 == kLess_StencilFunc); +GR_STATIC_ASSERT(5 == kLEqual_StencilFunc); +GR_STATIC_ASSERT(6 == kEqual_StencilFunc); +GR_STATIC_ASSERT(7 == kNotEqual_StencilFunc); + +static const GLenum grToGLStencilOp[] = { + GL_KEEP, // kKeep_StencilOp + GL_REPLACE, // kReplace_StencilOp + GL_INCR_WRAP, // kIncWrap_StencilOp + GL_INCR, // kIncClamp_StencilOp + GL_DECR_WRAP, // kDecWrap_StencilOp + GL_DECR, // kDecClamp_StencilOp + GL_ZERO, // kZero_StencilOp + GL_INVERT, // kInvert_StencilOp +}; +GR_STATIC_ASSERT(GR_ARRAY_COUNT(grToGLStencilOp) == kStencilOpCount); +GR_STATIC_ASSERT(0 == kKeep_StencilOp); +GR_STATIC_ASSERT(1 == kReplace_StencilOp); +GR_STATIC_ASSERT(2 == kIncWrap_StencilOp); +GR_STATIC_ASSERT(3 == kIncClamp_StencilOp); +GR_STATIC_ASSERT(4 == kDecWrap_StencilOp); +GR_STATIC_ASSERT(5 == kDecClamp_StencilOp); +GR_STATIC_ASSERT(6 == kZero_StencilOp); +GR_STATIC_ASSERT(7 == kInvert_StencilOp); + void GrGpuGL::flushStencil() { + const GrStencilSettings* settings = &fCurrDrawState.fStencilSettings; // use stencil for clipping if clipping is enabled and the clip // has been written into the stencil. bool stencilClip = fClipState.fClipInStencil && (kClip_StateBit & fCurrDrawState.fFlagBits); - bool stencilChange = - fDirtyFlags.fWriteMaskChanged || - fHWStencilClip != stencilClip || - fHWDrawState.fStencilPass != fCurrDrawState.fStencilPass || - (kNone_StencilPass != fCurrDrawState.fStencilPass && - (StencilPass)kSetClip_StencilPass != fCurrDrawState.fStencilPass && - fHWDrawState.fReverseFill != fCurrDrawState.fReverseFill); + bool stencilChange = fHWStencilClip != stencilClip || + fHWDrawState.fStencilSettings != *settings || + ((fHWDrawState.fFlagBits & kModifyStencilClip_StateBit) != + (fCurrDrawState.fFlagBits & kModifyStencilClip_StateBit)); if (stencilChange) { - GLint clipStencilMask; - GLint pathStencilMask; - GLint stencilBitCount = ((GrGLRenderTarget*)fCurrDrawState.fRenderTarget)->getStencilBits(); - GrAssert(stencilBitCount > 0 || - kNone_StencilPass == fCurrDrawState.fStencilPass); - clipStencilMask = (1 << (stencilBitCount - 1)); - pathStencilMask = clipStencilMask - 1; - switch (fCurrDrawState.fStencilPass) { - case kNone_StencilPass: - if (stencilClip) { - GR_GL(Enable(GL_STENCIL_TEST)); - GR_GL(StencilFunc(GL_EQUAL, - clipStencilMask, - clipStencilMask)); - GR_GL(StencilOp(GL_KEEP, GL_KEEP, GL_KEEP)); - } else { - GR_GL(Disable(GL_STENCIL_TEST)); - } - GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)); - if (!fSingleStencilPassForWinding) { - GR_GL(Disable(GL_CULL_FACE)); - } - break; - case kEvenOddStencil_StencilPass: - GR_GL(Enable(GL_STENCIL_TEST)); - if (stencilClip) { - GR_GL(StencilFunc(GL_EQUAL, clipStencilMask, clipStencilMask)); - } else { - GR_GL(StencilFunc(GL_ALWAYS, 0x0, 0x0)); - } - GR_GL(StencilMask(pathStencilMask)); - GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)); - GR_GL(StencilOp(GL_KEEP, GL_INVERT, GL_INVERT)); - GR_GL(ColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)); - if (!fSingleStencilPassForWinding) { - GR_GL(Disable(GL_CULL_FACE)); - } - break; - case kEvenOddColor_StencilPass: { - GR_GL(Enable(GL_STENCIL_TEST)); - GLint funcRef = 0; - GLuint funcMask = pathStencilMask; - if (stencilClip) { - funcRef |= clipStencilMask; - funcMask |= clipStencilMask; - } - if (!fCurrDrawState.fReverseFill) { - funcRef |= pathStencilMask; - } - GR_GL(StencilFunc(GL_EQUAL, funcRef, funcMask)); - GR_GL(StencilMask(pathStencilMask)); - GR_GL(StencilOp(GL_ZERO, GL_ZERO, GL_ZERO)); - GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)); - if (!fSingleStencilPassForWinding) { - GR_GL(Disable(GL_CULL_FACE)); - } - } break; - case kWindingStencil1_StencilPass: - GR_GL(Enable(GL_STENCIL_TEST)); - if (fHasStencilWrap) { - if (stencilClip) { - GR_GL(StencilFunc(GL_EQUAL, - clipStencilMask, - clipStencilMask)); - } else { - GR_GL(StencilFunc(GL_ALWAYS, 0x0, 0x0)); - } - if (fSingleStencilPassForWinding) { - GR_GL(StencilOpSeparate(GL_FRONT, GL_KEEP, - GL_INCR_WRAP, GL_INCR_WRAP)); - GR_GL(StencilOpSeparate(GL_BACK, GL_KEEP, - GL_DECR_WRAP, GL_DECR_WRAP)); - } else { - GR_GL(StencilOp(GL_KEEP, GL_INCR_WRAP, GL_INCR_WRAP)); - GR_GL(Enable(GL_CULL_FACE)); - GR_GL(CullFace(GL_BACK)); - } - } else { - // If we don't have wrap then we use the Func to detect - // values that would wrap (0 on decr and mask on incr). We - // make the func fail on these values and use the sfail op - // to effectively wrap by inverting. - // This applies whether we are doing a two-pass (front faces - // followed by back faces) or a single pass (separate func/op) - - // Note that in the case where we are also using stencil to - // clip this means we will write into the path bits in clipped - // out pixels. We still apply the clip bit in the color pass - // stencil func so we don't draw color outside the clip. - // We also will clear the stencil bits in clipped pixels by - // using zero in the sfail op with write mask set to the - // path mask. - GR_GL(Enable(GL_STENCIL_TEST)); - if (fSingleStencilPassForWinding) { - GR_GL(StencilFuncSeparate(GL_FRONT, - GL_NOTEQUAL, - pathStencilMask, - pathStencilMask)); - GR_GL(StencilFuncSeparate(GL_BACK, - GL_NOTEQUAL, - 0x0, - pathStencilMask)); - GR_GL(StencilOpSeparate(GL_FRONT, GL_INVERT, - GL_INCR, GL_INCR)); - GR_GL(StencilOpSeparate(GL_BACK, GL_INVERT, - GL_DECR, GL_DECR)); - } else { - GR_GL(StencilFunc(GL_NOTEQUAL, - pathStencilMask, - pathStencilMask)); - GR_GL(StencilOp(GL_INVERT, GL_INCR, GL_INCR)); - GR_GL(Enable(GL_CULL_FACE)); - GR_GL(CullFace(GL_BACK)); - } - } - GR_GL(StencilMask(pathStencilMask)); - GR_GL(ColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)); - break; - case kWindingStencil2_StencilPass: - GrAssert(!fSingleStencilPassForWinding); - GR_GL(Enable(GL_STENCIL_TEST)); - if (fHasStencilWrap) { - if (stencilClip) { - GR_GL(StencilFunc(GL_EQUAL, + // we can't simultaneously perform stencil-clipping and modify the stencil clip + GrAssert(!stencilClip || !(fCurrDrawState.fFlagBits & kModifyStencilClip_StateBit)); + + if (settings->isDisabled()) { + if (stencilClip) { + settings = &gClipStencilSettings; + } + } + + if (settings->isDisabled()) { + GR_GL(Disable(GL_STENCIL_TEST)); + } else { + GR_GL(Enable(GL_STENCIL_TEST)); + #if GR_DEBUG + if (!fStencilWrapOpsSupport) { + GrAssert(settings->fFrontPassOp != kIncWrap_StencilOp); + GrAssert(settings->fFrontPassOp != kDecWrap_StencilOp); + GrAssert(settings->fFrontFailOp != kIncWrap_StencilOp); + GrAssert(settings->fBackFailOp != kDecWrap_StencilOp); + GrAssert(settings->fBackPassOp != kIncWrap_StencilOp); + GrAssert(settings->fBackPassOp != kDecWrap_StencilOp); + GrAssert(settings->fBackFailOp != kIncWrap_StencilOp); + GrAssert(settings->fFrontFailOp != kDecWrap_StencilOp); + } + #endif + int stencilBits = fCurrDrawState.fRenderTarget->stencilBits(); + GrAssert(stencilBits || + (GrStencilSettings::gDisabled == + fCurrDrawState.fStencilSettings)); + GLuint clipStencilMask = 1 << (stencilBits - 1); + GLuint userStencilMask = clipStencilMask - 1; + + unsigned int frontRef = settings->fFrontFuncRef; + unsigned int frontMask = settings->fFrontFuncMask; + unsigned int frontWriteMask = settings->fFrontWriteMask; + GLenum frontFunc; + + if (fCurrDrawState.fFlagBits & kModifyStencilClip_StateBit) { + + GrAssert(settings->fFrontFunc < kBasicStencilFuncCount); + frontFunc = grToGLStencilFunc[settings->fFrontFunc]; + } else { + frontFunc = grToGLStencilFunc[ConvertStencilFunc(stencilClip, settings->fFrontFunc)]; + + ConvertStencilFuncAndMask(settings->fFrontFunc, + stencilClip, clipStencilMask, - clipStencilMask)); - } else { - GR_GL(StencilFunc(GL_ALWAYS, 0x0, 0x0)); - } - GR_GL(StencilOp(GL_DECR_WRAP, GL_DECR_WRAP, GL_DECR_WRAP)); - } else { - GR_GL(StencilFunc(GL_NOTEQUAL, 0x0, pathStencilMask)); - GR_GL(StencilOp(GL_INVERT, GL_DECR, GL_DECR)); - } - GR_GL(StencilMask(pathStencilMask)); - GR_GL(Enable(GL_CULL_FACE)); - GR_GL(CullFace(GL_FRONT)); - GR_GL(ColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)); - break; - case kWindingColor_StencilPass: { - GR_GL(Enable(GL_STENCIL_TEST)); - GLint funcRef = 0; - GLuint funcMask = pathStencilMask; - GLenum funcFunc; - - if (stencilClip) { - funcRef |= clipStencilMask; - funcMask |= clipStencilMask; - } - if (fCurrDrawState.fReverseFill) { - funcFunc = GL_EQUAL; + userStencilMask, + &frontRef, + &frontMask); + frontWriteMask &= userStencilMask; + } + GrAssert(settings->fFrontFailOp >= 0 && + settings->fFrontFailOp < GR_ARRAY_COUNT(grToGLStencilOp)); + GrAssert(settings->fFrontPassOp >= 0 && + settings->fFrontPassOp < GR_ARRAY_COUNT(grToGLStencilOp)); + GrAssert(settings->fBackFailOp >= 0 && + settings->fBackFailOp < GR_ARRAY_COUNT(grToGLStencilOp)); + GrAssert(settings->fBackPassOp >= 0 && + settings->fBackPassOp < GR_ARRAY_COUNT(grToGLStencilOp)); + if (fTwoSidedStencilSupport) { + GLenum backFunc; + + unsigned int backRef = settings->fBackFuncRef; + unsigned int backMask = settings->fBackFuncMask; + unsigned int backWriteMask = settings->fBackWriteMask; + + + if (fCurrDrawState.fFlagBits & kModifyStencilClip_StateBit) { + GrAssert(settings->fBackFunc < kBasicStencilFuncCount); + backFunc = grToGLStencilFunc[settings->fBackFunc]; } else { - funcFunc = GL_LESS; + backFunc = grToGLStencilFunc[ConvertStencilFunc(stencilClip, settings->fBackFunc)]; + ConvertStencilFuncAndMask(settings->fBackFunc, + stencilClip, + clipStencilMask, + userStencilMask, + &backRef, + &backMask); + backWriteMask &= userStencilMask; } - GR_GL(StencilFunc(funcFunc, funcRef, funcMask)); - GR_GL(StencilMask(pathStencilMask)); - // must zero in sfail because winding w/o wrap will write - // path stencil bits in clipped out pixels - GR_GL(StencilOp(GL_ZERO, GL_ZERO, GL_ZERO)); - GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)); - if (!fSingleStencilPassForWinding) { - GR_GL(Disable(GL_CULL_FACE)); - } - } break; - case kSetClip_StencilPass: - GR_GL(Enable(GL_STENCIL_TEST)); - GR_GL(StencilFunc(GL_ALWAYS, clipStencilMask, clipStencilMask)); - GR_GL(StencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE)); - GR_GL(StencilMask(clipStencilMask)); - GR_GL(ColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)); - if (!fSingleStencilPassForWinding) { - GR_GL(Disable(GL_CULL_FACE)); - } - break; - default: - GrAssert(!"Unexpected stencil pass."); - break; + GR_GL(StencilFuncSeparate(GL_FRONT, frontFunc, frontRef, frontMask)); + GR_GL(StencilMaskSeparate(GL_FRONT, frontWriteMask)); + GR_GL(StencilFuncSeparate(GL_BACK, backFunc, backRef, backMask)); + GR_GL(StencilMaskSeparate(GL_BACK, backWriteMask)); + GR_GL(StencilOpSeparate(GL_FRONT, grToGLStencilOp[settings->fFrontFailOp], + grToGLStencilOp[settings->fFrontPassOp], + grToGLStencilOp[settings->fFrontPassOp])); + + GR_GL(StencilOpSeparate(GL_BACK, grToGLStencilOp[settings->fBackFailOp], + grToGLStencilOp[settings->fBackPassOp], + grToGLStencilOp[settings->fBackPassOp])); + } else { + GR_GL(StencilFunc(frontFunc, frontRef, frontMask)); + GR_GL(StencilMask(frontWriteMask)); + GR_GL(StencilOp(grToGLStencilOp[settings->fFrontFailOp], + grToGLStencilOp[settings->fFrontPassOp], + grToGLStencilOp[settings->fFrontPassOp])); + } } - fHWDrawState.fStencilPass = fCurrDrawState.fStencilPass; - fHWDrawState.fReverseFill = fCurrDrawState.fReverseFill; + fHWDrawState.fStencilSettings = fCurrDrawState.fStencilSettings; fHWStencilClip = stencilClip; } } @@ -1544,6 +1558,17 @@ bool GrGpuGL::flushGLStateCommon(GrPrimitiveType type) { } } + if ((fCurrDrawState.fFlagBits & kNoColorWrites_StateBit) != + (fHWDrawState.fFlagBits & kNoColorWrites_StateBit)) { + GLenum mask; + if (fCurrDrawState.fFlagBits & kNoColorWrites_StateBit) { + mask = GL_FALSE; + } else { + mask = GL_TRUE; + } + GR_GL(ColorMask(mask, mask, mask, mask)); + } + #if GR_SUPPORT_GLDESKTOP // ES doesn't support toggling GL_MULTISAMPLE and doesn't have // smooth lines. @@ -1592,6 +1617,25 @@ bool GrGpuGL::flushGLStateCommon(GrPrimitiveType type) { } } + if (fHWDrawState.fDrawFace != fCurrDrawState.fDrawFace) { + switch (fCurrDrawState.fDrawFace) { + case kCCW_DrawFace: + glEnable(GL_CULL_FACE); + GR_GL(CullFace(GL_BACK)); + break; + case kCW_DrawFace: + GR_GL(Enable(GL_CULL_FACE)); + GR_GL(CullFace(GL_FRONT)); + break; + case kBoth_DrawFace: + GR_GL(Disable(GL_CULL_FACE)); + break; + default: + GrCrash("Unknown draw face."); + } + fHWDrawState.fDrawFace = fCurrDrawState.fDrawFace; + } + #if GR_DEBUG // check for circular rendering for (int s = 0; s < kNumStages; ++s) { @@ -1605,6 +1649,7 @@ bool GrGpuGL::flushGLStateCommon(GrPrimitiveType type) { flushStencil(); + // flushStencil may look at the private state bits, so keep it before this. fHWDrawState.fFlagBits = fCurrDrawState.fFlagBits; return true; } @@ -1654,9 +1699,6 @@ void GrGpuGL::notifyRenderTargetDelete(GrRenderTarget* renderTarget) { if (fHWDrawState.fRenderTarget == renderTarget) { fHWDrawState.fRenderTarget = NULL; } - if (fClipState.fStencilClipTarget == renderTarget) { - fClipState.fStencilClipTarget = NULL; - } } void GrGpuGL::notifyTextureDelete(GrGLTexture* texture) { diff --git a/gpu/src/GrGpuGL.h b/gpu/src/GrGpuGL.h index f6216c5c6e..a2905c5d30 100644 --- a/gpu/src/GrGpuGL.h +++ b/gpu/src/GrGpuGL.h @@ -80,7 +80,6 @@ protected: // call resetDirtyFlags after its flush is complete struct { bool fRenderTargetChanged : 1; - bool fWriteMaskChanged : 1; int fTextureChangedMask; } fDirtyFlags; GR_STATIC_ASSERT(8 * sizeof(int) >= kNumStages); @@ -108,7 +107,7 @@ protected: uint32_t numVertices); virtual void flushScissor(const GrIRect* rect); void eraseStencil(uint32_t value, uint32_t mask); - virtual void eraseStencilClip(); + virtual void eraseStencilClip(const GrIRect& rect); // binds texture unit in GL void setTextureUnit(int unitIdx); diff --git a/gpu/src/GrInOrderDrawBuffer.cpp b/gpu/src/GrInOrderDrawBuffer.cpp index 68590fc8ab..a249364752 100644 --- a/gpu/src/GrInOrderDrawBuffer.cpp +++ b/gpu/src/GrInOrderDrawBuffer.cpp @@ -67,17 +67,17 @@ void GrInOrderDrawBuffer::setQuadIndexBuffer(const GrIndexBuffer* indexBuffer) { fCurrQuad = 0; fMaxQuads = (NULL == indexBuffer) ? 0 : indexBuffer->maxQuads(); } else { - GrAssert((NULL == indexBuffer && 0 == fMaxQuads) || + GrAssert((NULL == indexBuffer && 0 == fMaxQuads) || (indexBuffer->maxQuads() == fMaxQuads)); } } -void GrInOrderDrawBuffer::drawRect(const GrRect& rect, +void GrInOrderDrawBuffer::drawRect(const GrRect& rect, const GrMatrix* matrix, StageBitfield stageEnableBitfield, const GrRect* srcRects[], const GrMatrix* srcMatrices[]) { - + GrAssert(!(NULL == fQuadIndexBuffer && fCurrQuad)); GrAssert(!(fDraws.empty() && fCurrQuad)); GrAssert(!(0 != fMaxQuads && NULL == fQuadIndexBuffer)); @@ -85,7 +85,7 @@ void GrInOrderDrawBuffer::drawRect(const GrRect& rect, // if we have a quad IB then either append to the previous run of // rects or start a new run if (fMaxQuads) { - + bool appendToPreviousDraw = false; GrVertexLayout layout = GetRectVertexLayout(stageEnableBitfield, srcRects); AutoReleaseGeometry geo(this, layout, 4, 0); @@ -103,10 +103,14 @@ void GrInOrderDrawBuffer::drawRect(const GrRect& rect, // the rect. bool disabledClip = false; if (this->isClipState() && fClip.isRect()) { - GrRect clipRect = GrRect(*fClip.getRects()); + + // single rect clip should have bounds + GrAssert(fClip.hasBounds()); + + GrRect clipRect = GrRect(fClip.getBounds()); // If the clip rect touches the edge of the viewport, extended it // out (close) to infinity to avoid bogus intersections. - // We might consider a more exact clip to viewport if this + // We might consider a more exact clip to viewport if this // conservative test fails. const GrRenderTarget* target = this->getRenderTarget(); if (0 >= clipRect.fLeft) { @@ -135,11 +139,11 @@ void GrInOrderDrawBuffer::drawRect(const GrRect& rect, disabledClip = true; } } - if (!needsNewClip() && !needsNewState() && fCurrQuad > 0 && + if (!needsNewClip() && !needsNewState() && fCurrQuad > 0 && fCurrQuad < fMaxQuads && layout == fLastRectVertexLayout) { int vsize = VertexSize(layout); - + Draw& lastDraw = fDraws.back(); GrAssert(lastDraw.fIndexBuffer == fQuadIndexBuffer); @@ -500,7 +504,7 @@ void GrInOrderDrawBuffer::pushState() { } this->saveCurrentDrawState(&fStates.push_back()); } - + bool GrInOrderDrawBuffer::needsNewClip() const { if (fCurrDrawState.fFlagBits & kClip_StateBit) { if (fClips.empty() || (fClipSet && fClips.back() != fClip)) { @@ -509,12 +513,12 @@ bool GrInOrderDrawBuffer::needsNewClip() const { } return false; } - + void GrInOrderDrawBuffer::pushClip() { fClips.push_back() = fClip; fClipSet = false; } - + void GrInOrderDrawBuffer::clipWillBeSet(const GrClip& newClip) { fClipSet = true; } diff --git a/gpu/src/GrPath.cpp b/gpu/src/GrPath.cpp index 554b3b915a..ca5c43baa2 100644 --- a/gpu/src/GrPath.cpp +++ b/gpu/src/GrPath.cpp @@ -3,6 +3,8 @@ GrPath::GrPath() {} GrPath::GrPath(const GrPath& src) : INHERITED() { + GrPath::Iter iter(src); + this->resetFromIter(&iter); } GrPath::GrPath(GrPathIter& iter) { @@ -12,6 +14,26 @@ GrPath::GrPath(GrPathIter& iter) { GrPath::~GrPath() { } +bool GrPath::operator ==(const GrPath& path) const { + if (fVerbs.count() != path.fVerbs.count() || + fPts.count() != path.fPts.count()) { + return false; + } + + for (int v = 0; v < fVerbs.count(); ++v) { + if (fVerbs[v] != path.fVerbs[v]) { + return false; + } + } + + for (int p = 0; p < fPts.count(); ++p) { + if (fPts[p] != path.fPts[p]) { + return false; + } + } + return true; +} + void GrPath::ensureMoveTo() { if (fVerbs.isEmpty() || this->wasLastVerb(kClose)) { *fVerbs.append() = kMove; @@ -90,6 +112,7 @@ void GrPath::resetFromIter(GrPathIter* iter) { break; } } + fConvexHint = iter->convexHint(); } /////////////////////////////////////////////////////////////////////////////// @@ -157,7 +180,7 @@ GrPathIter::Command GrPath::Iter::next(GrPoint points[]) { return (GrPathIter::Command)cmd; } -GrPathIter::ConvexHint GrPath::Iter::hint() const { +GrPathIter::ConvexHint GrPath::Iter::convexHint() const { return fPath.getConvexHint(); } diff --git a/gpu/src/GrPathRenderer.cpp b/gpu/src/GrPathRenderer.cpp index c47b6e522c..3e2b4b35c5 100644 --- a/gpu/src/GrPathRenderer.cpp +++ b/gpu/src/GrPathRenderer.cpp @@ -6,15 +6,145 @@ #include "GrMemory.h" #include "GrTexture.h" - - -GrDefaultPathRenderer::GrDefaultPathRenderer(bool singlePassWindingStencil) - : fSinglePassWindingStencil(singlePassWindingStencil) { +GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport, + bool stencilWrapOpsSupport) + : fSeparateStencil(separateStencilSupport), + fStencilWrapOps(stencilWrapOpsSupport) { } //////////////////////////////////////////////////////////////////////////////// -// Helpers for draw Path +// Stencil rules for paths + +////// Even/Odd + +static const GrStencilSettings gEOStencilPass = { + kInvert_StencilOp, kInvert_StencilOp, + kKeep_StencilOp, kKeep_StencilOp, + kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc, + 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff +}; + +// ok not to check clip b/c stencil pass only wrote inside clip +static const GrStencilSettings gEOColorPass = { + kZero_StencilOp, kZero_StencilOp, + kZero_StencilOp, kZero_StencilOp, + kNotEqual_StencilFunc, kNotEqual_StencilFunc, + 0xffffffff, 0xffffffff, + 0x0, 0x0, + 0xffffffff, 0xffffffff +}; + +// have to check clip b/c outside clip will always be zero. +static const GrStencilSettings gInvEOColorPass = { + kZero_StencilOp, kZero_StencilOp, + kZero_StencilOp, kZero_StencilOp, + kEqualIfInClip_StencilFunc, kEqualIfInClip_StencilFunc, + 0xffffffff, 0xffffffff, + 0x0, 0x0, + 0xffffffff, 0xffffffff +}; + +////// Winding + +// when we have separate stencil we increment front faces / decrement back faces +// when we don't have wrap incr and decr we use the stencil test to simulate +// them. + +static const GrStencilSettings gWindStencilSeparateWithWrap = { + kIncWrap_StencilOp, kDecWrap_StencilOp, + kKeep_StencilOp, kKeep_StencilOp, + kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc, + 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff +}; + +// if inc'ing the max value, invert to make 0 +// if dec'ing zero invert to make all ones. +// we can't avoid touching the stencil on both passing and +// failing, so we can't resctrict ourselves to the clip. +static const GrStencilSettings gWindStencilSeparateNoWrap = { + kInvert_StencilOp, kInvert_StencilOp, + kIncClamp_StencilOp, kDecClamp_StencilOp, + kEqual_StencilFunc, kEqual_StencilFunc, + 0xffffffff, 0xffffffff, + 0xffffffff, 0x0, + 0xffffffff, 0xffffffff +}; + +// When there are no separate faces we do two passes to setup the winding rule +// stencil. First we draw the front faces and inc, then we draw the back faces +// and dec. These are same as the above two split into the incrementing and +// decrementing passes. +static const GrStencilSettings gWindSingleStencilWithWrapInc = { + kIncWrap_StencilOp, kIncWrap_StencilOp, + kKeep_StencilOp, kKeep_StencilOp, + kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc, + 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff +}; +static const GrStencilSettings gWindSingleStencilWithWrapDec = { + kDecWrap_StencilOp, kDecWrap_StencilOp, + kKeep_StencilOp, kKeep_StencilOp, + kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc, + 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff +}; +static const GrStencilSettings gWindSingleStencilNoWrapInc = { + kInvert_StencilOp, kInvert_StencilOp, + kIncClamp_StencilOp, kIncClamp_StencilOp, + kEqual_StencilFunc, kEqual_StencilFunc, + 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff +}; +static const GrStencilSettings gWindSingleStencilNoWrapDec = { + kInvert_StencilOp, kInvert_StencilOp, + kDecClamp_StencilOp, kDecClamp_StencilOp, + kEqual_StencilFunc, kEqual_StencilFunc, + 0xffffffff, 0xffffffff, + 0x0, 0x0, + 0xffffffff, 0xffffffff +}; + +static const GrStencilSettings gWindColorPass = { + kZero_StencilOp, kZero_StencilOp, + kZero_StencilOp, kZero_StencilOp, + kNonZeroIfInClip_StencilFunc, kNonZeroIfInClip_StencilFunc, + 0xffffffff, 0xffffffff, + 0x0, 0x0, + 0xffffffff, 0xffffffff +}; + +static const GrStencilSettings gInvWindColorPass = { + kZero_StencilOp, kZero_StencilOp, + kZero_StencilOp, kZero_StencilOp, + kEqualIfInClip_StencilFunc, kEqualIfInClip_StencilFunc, + 0xffffffff, 0xffffffff, + 0x0, 0x0, + 0xffffffff, 0xffffffff +}; + +////// Normal render to stencil + +// Sometimes the default path renderer can draw a path directly to the stencil +// buffer without having to first resolve the interior / exterior. +static const GrStencilSettings gDirectToStencil = { + kZero_StencilOp, kZero_StencilOp, + kIncClamp_StencilOp, kIncClamp_StencilOp, + kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc, + 0xffffffff, 0xffffffff, + 0x0, 0x0, + 0xffffffff, 0xffffffff +}; + +//////////////////////////////////////////////////////////////////////////////// +// Helpers for drawPath #define STENCIL_OFF 0 // Always disable stencil (even when needed) static const GrScalar gTolerance = GR_Scalar1; @@ -146,11 +276,11 @@ static inline bool single_pass_path(const GrPathIter& path, return true; #else if (kEvenOdd_PathFill == fill) { - GrPathIter::ConvexHint hint = path.hint(); + GrPathIter::ConvexHint hint = path.convexHint(); return hint == GrPathIter::kConvex_ConvexHint || hint == GrPathIter::kNonOverlappingConvexPieces_ConvexHint; } else if (kWinding_PathFill == fill) { - GrPathIter::ConvexHint hint = path.hint(); + GrPathIter::ConvexHint hint = path.convexHint(); return hint == GrPathIter::kConvex_ConvexHint || hint == GrPathIter::kNonOverlappingConvexPieces_ConvexHint || (hint == GrPathIter::kSameWindingConvexPieces_ConvexHint && @@ -161,13 +291,17 @@ static inline bool single_pass_path(const GrPathIter& path, #endif } -void GrDefaultPathRenderer::drawPath(GrDrawTarget* target, - GrDrawTarget::StageBitfield stages, - GrPathIter* path, - GrPathFill fill, - const GrPoint* translate) { +void GrDefaultPathRenderer::drawPathHelper(GrDrawTarget* target, + GrDrawTarget::StageBitfield stages, + GrPathIter* path, + GrPathFill fill, + const GrPoint* translate, + bool stencilOnly) { GrDrawTarget::AutoStateRestore asr(target); + bool colorWritesWereDisabled = target->isColorWriteDisabled(); + // face culling doesn't make sense here + GrAssert(GrDrawTarget::kBoth_DrawFace == target->getDrawFace()); GrMatrix viewM = target->getViewMatrix(); // In order to tesselate the path we get a bound on how much the matrix can @@ -185,6 +319,8 @@ void GrDefaultPathRenderer::drawPath(GrDrawTarget* target, } GrScalar tolSqd = GrMul(tol, tol); + path->rewind(); + int subpathCnt; int maxPts = worst_case_point_count(path, &subpathCnt, @@ -211,42 +347,91 @@ void GrDefaultPathRenderer::drawPath(GrDrawTarget* target, // TODO: use primitve restart if available rather than multiple draws GrPrimitiveType type; int passCount = 0; - GrDrawTarget::StencilPass passes[3]; + const GrStencilSettings* passes[3]; + GrDrawTarget::DrawFace drawFace[3]; bool reverse = false; + bool lastPassIsBounds; if (kHairLine_PathFill == fill) { type = kLineStrip_PrimitiveType; passCount = 1; - passes[0] = GrDrawTarget::kNone_StencilPass; + if (stencilOnly) { + passes[0] = &gDirectToStencil; + } else { + passes[0] = &GrStencilSettings::gDisabled; + } + lastPassIsBounds = false; + drawFace[0] = GrDrawTarget::kBoth_DrawFace; } else { type = kTriangleFan_PrimitiveType; if (single_pass_path(*path, fill, *target)) { passCount = 1; - passes[0] = GrDrawTarget::kNone_StencilPass; + if (stencilOnly) { + passes[0] = &gDirectToStencil; + } else { + passes[0] = &GrStencilSettings::gDisabled; + } + drawFace[0] = GrDrawTarget::kBoth_DrawFace; + lastPassIsBounds = false; } else { switch (fill) { case kInverseEvenOdd_PathFill: reverse = true; // fallthrough case kEvenOdd_PathFill: - passCount = 2; - passes[0] = GrDrawTarget::kEvenOddStencil_StencilPass; - passes[1] = GrDrawTarget::kEvenOddColor_StencilPass; + passes[0] = &gEOStencilPass; + if (stencilOnly) { + passCount = 1; + lastPassIsBounds = false; + } else { + passCount = 2; + lastPassIsBounds = true; + if (reverse) { + passes[1] = &gInvEOColorPass; + } else { + passes[1] = &gEOColorPass; + } + } + drawFace[0] = drawFace[1] = GrDrawTarget::kBoth_DrawFace; break; case kInverseWinding_PathFill: reverse = true; // fallthrough case kWinding_PathFill: - passes[0] = GrDrawTarget::kWindingStencil1_StencilPass; - if (fSinglePassWindingStencil) { - passes[1] = GrDrawTarget::kWindingColor_StencilPass; + if (fSeparateStencil) { + if (fStencilWrapOps) { + passes[0] = &gWindStencilSeparateWithWrap; + } else { + passes[0] = &gWindStencilSeparateNoWrap; + } passCount = 2; + drawFace[0] = GrDrawTarget::kBoth_DrawFace; } else { - passes[1] = GrDrawTarget::kWindingStencil2_StencilPass; - passes[2] = GrDrawTarget::kWindingColor_StencilPass; + if (fStencilWrapOps) { + passes[0] = &gWindSingleStencilWithWrapInc; + passes[1] = &gWindSingleStencilWithWrapDec; + } else { + passes[0] = &gWindSingleStencilNoWrapInc; + passes[1] = &gWindSingleStencilNoWrapDec; + } + // which is cw and which is ccw is arbitrary. + drawFace[0] = GrDrawTarget::kCW_DrawFace; + drawFace[1] = GrDrawTarget::kCCW_DrawFace; passCount = 3; } + if (stencilOnly) { + lastPassIsBounds = false; + --passCount; + } else { + lastPassIsBounds = true; + drawFace[passCount-1] = GrDrawTarget::kBoth_DrawFace; + if (reverse) { + passes[passCount-1] = &gInvWindColorPass; + } else { + passes[passCount-1] = &gWindColorPass; + } + } break; default: GrAssert(!"Unknown path fill!"); @@ -254,7 +439,6 @@ void GrDefaultPathRenderer::drawPath(GrDrawTarget* target, } } } - target->setReverseFill(reverse); GrPoint pts[4]; @@ -309,11 +493,12 @@ FINISHED: } } - // arbitrary path complexity cutoff - bool useBounds = fill != kHairLine_PathFill && - (reverse || (vert - base) > 8); - GrPoint* boundsVerts = base + maxPts; - if (useBounds) { + // if we're stenciling we will follow with a pass that draws + // a bounding rect to set the color. We're stenciling when + // passCount > 1. + const int& boundVertexStart = maxPts; + GrPoint* boundsVerts = base + boundVertexStart; + if (lastPassIsBounds) { GrRect bounds; if (reverse) { GrAssert(NULL != target->getRenderTarget()); @@ -333,20 +518,44 @@ FINISHED: } for (int p = 0; p < passCount; ++p) { - target->setStencilPass(passes[p]); - if (useBounds && (GrDrawTarget::kEvenOddColor_StencilPass == passes[p] || - GrDrawTarget::kWindingColor_StencilPass == passes[p])) { + target->setDrawFace(drawFace[p]); + target->setStencil(*passes[p]); + + if (lastPassIsBounds && (p == passCount-1)) { + if (!colorWritesWereDisabled) { + target->disableState(GrDrawTarget::kNoColorWrites_StateBit); + } target->drawNonIndexed(kTriangleFan_PrimitiveType, - maxPts, 4); + boundVertexStart, 4); } else { + if (passCount > 1) { + target->enableState(GrDrawTarget::kNoColorWrites_StateBit); + } int baseVertex = 0; for (int sp = 0; sp < subpathCnt; ++sp) { target->drawNonIndexed(type, - baseVertex, - subpathVertCount[sp]); + baseVertex, + subpathVertCount[sp]); baseVertex += subpathVertCount[sp]; } } } } + +void GrDefaultPathRenderer::drawPath(GrDrawTarget* target, + GrDrawTarget::StageBitfield stages, + GrPathIter* path, + GrPathFill fill, + const GrPoint* translate) { + this->drawPathHelper(target, stages, path, fill, translate, false); +} + +void GrDefaultPathRenderer::drawPathToStencil(GrDrawTarget* target, + GrPathIter* path, + GrPathFill fill, + const GrPoint* translate) { + GrAssert(kInverseEvenOdd_PathFill != fill); + GrAssert(kInverseWinding_PathFill != fill); + this->drawPathHelper(target, 0, path, fill, translate, true); + } diff --git a/gpu/src/GrPathRenderer.h b/gpu/src/GrPathRenderer.h index e3fd71507a..467b0a04d3 100644 --- a/gpu/src/GrPathRenderer.h +++ b/gpu/src/GrPathRenderer.h @@ -34,31 +34,107 @@ public: * @param stages indicates which stages the are already * in use. All enabled stages expect positions * as texture coordinates. The path renderer - * use the remaining stages for its path + * use the remaining stages for its path * filling algorithm. * @param path the path to draw. * @param fill the fill rule to apply. * @param translate optional additional translation to apply to - * the path NULL means (0,0). + * the path. NULL means (0,0). */ virtual void drawPath(GrDrawTarget* target, GrDrawTarget::StageBitfield stages, GrPathIter* path, GrPathFill fill, const GrPoint* translate) = 0; + + void drawPath(GrDrawTarget* target, + GrDrawTarget::StageBitfield stages, + const GrPath& path, + GrPathFill fill, + const GrPoint* translate) { + GrPath::Iter iter(path); + this->drawPath(target, stages, &iter, fill, translate); + } + + /** + * For complex clips Gr uses the stencil buffer. The path renderer must be + * able to render paths into the stencil buffer. However, the path renderer + * itself may require the stencil buffer to resolve the path fill rule. This + * function queries whether the path render requires its own stencil + * pass. If this returns false then drawPath() should not modify the + * the target's stencil settings. + * + * @return false if this path renderer can generate interior-only fragments + * without changing the stencil settings on the target. If it + * returns true the drawPathToStencil will be used when rendering + * clips. + */ + virtual bool requiresStencilPass(GrPathIter*) const { return false; } + + bool requiresStencilPass(const GrPath& path) const { + GrPath::Iter iter(path); + return requiresStencilPass(&iter); + } + + /** + * Draws a path to the stencil buffer. Assume the writable bits are zero + * prior and write a nonzero value in interior samples. The default + * implementation assumes the path filling algorithm doesn't require a + * separate stencil pass and so just calls drawPath. + * + * Fill will never be an inverse fill rule. + * + * @param target the target to draw into. + * @param path the path to draw. + * @param fill the fill rule to apply. + * @param translate optional additional translation to apply to + * the path. NULL means (0,0). + */ + virtual void drawPathToStencil(GrDrawTarget* target, + GrPathIter* path, + GrPathFill fill, + const GrPoint* translate) { + GrAssert(kInverseEvenOdd_PathFill != fill); + GrAssert(kInverseWinding_PathFill != fill); + + this->drawPath(target, 0, path, fill, translate); + } + + void drawPathToStencil(GrDrawTarget* target, + const GrPath& path, + GrPathFill fill, + const GrPoint* translate) { + GrPath::Iter iter(path); + this->drawPathToStencil(target, &iter, fill, translate); + } }; class GrDefaultPathRenderer : public GrPathRenderer { public: - GrDefaultPathRenderer(bool singlePassWindingStencil); + GrDefaultPathRenderer(bool separateStencilSupport, + bool stencilWrapOpsSupport); virtual void drawPath(GrDrawTarget* target, GrDrawTarget::StageBitfield stages, GrPathIter* path, GrPathFill fill, const GrPoint* translate); + virtual bool requiresStencilPass(GrPath&) const { return true; } + virtual void drawPathToStencil(GrDrawTarget* target, + GrPathIter* path, + GrPathFill fill, + const GrPoint* translate); private: - bool fSinglePassWindingStencil; + + void drawPathHelper(GrDrawTarget* target, + GrDrawTarget::StageBitfield stages, + GrPathIter* path, + GrPathFill fill, + const GrPoint* translate, + bool stencilOnly); + + bool fSeparateStencil; + bool fStencilWrapOps; }; #endif
\ No newline at end of file diff --git a/gpu/src/GrStencil.cpp b/gpu/src/GrStencil.cpp new file mode 100644 index 0000000000..9d68c65ebd --- /dev/null +++ b/gpu/src/GrStencil.cpp @@ -0,0 +1,398 @@ +/* + Copyright 2011 Google Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#include "GrStencil.h" + +const GrStencilSettings GrStencilSettings::gDisabled = {}; +GR_STATIC_ASSERT(0 == kKeep_StencilOp); +GR_STATIC_ASSERT(0 == kAlways_StencilFunc); + +//////////////////////////////////////////////////////////////////////////////// +// Stencil Rules for Merging user stencil space into clip + +// We can't include the clip bit in the ref or mask values because the division +// between user and clip bits in the stencil depends on the number of stencil +// bits in the runtime. Comments below indicate what the code should do to +// incorporate the clip bit into these settings. + +/////// +// Replace + +// set the ref to be the clip bit, but mask it out for the test +static const GrStencilSettings gUserToClipReplace = { + kReplace_StencilOp, kReplace_StencilOp, + kZero_StencilOp, kZero_StencilOp, + kLess_StencilFunc, kLess_StencilFunc, + 0xffffffff, 0xffffffff, // unset clip bit + 0x0, 0x0, // set clip bit + 0xffffffff, 0xffffffff +}; +static const GrStencilSettings gInvUserToClipReplace = { + kReplace_StencilOp, kReplace_StencilOp, + kZero_StencilOp, kZero_StencilOp, + kEqual_StencilFunc, kEqual_StencilFunc, + 0xffffffff, 0xffffffff, // unset clip bit + 0x0, 0x0, // set clip bit + 0xffffffff, 0xffffffff +}; + +/////// +// Intersect +static const GrStencilSettings gUserToClipIsect = { + kReplace_StencilOp, kReplace_StencilOp, + kZero_StencilOp, kZero_StencilOp, + kLess_StencilFunc, kLess_StencilFunc, + 0xffffffff, 0xffffffff, + 0x0, 0x0, // set clip bit + 0xffffffff, 0xffffffff +}; +static const GrStencilSettings gInvUserToClipIsect = { + kReplace_StencilOp, kReplace_StencilOp, + kZero_StencilOp, kZero_StencilOp, + kEqual_StencilFunc, kEqual_StencilFunc, + 0xffffffff, 0xffffffff, + 0x0, 0x0, // set clip bit + 0xffffffff, 0xffffffff +}; + +/////// +// Difference +static const GrStencilSettings gUserToClipDiff = { + kReplace_StencilOp, kReplace_StencilOp, + kZero_StencilOp, kZero_StencilOp, + kEqual_StencilFunc, kEqual_StencilFunc, + 0xffffffff, 0xffffffff, + 0x0, 0x0, // set clip bit + 0xffffffff, 0xffffffff +}; +static const GrStencilSettings gInvUserToClipDiff = { + kReplace_StencilOp, kReplace_StencilOp, + kZero_StencilOp, kZero_StencilOp, + kLess_StencilFunc, kLess_StencilFunc, + 0xffffffff, 0xffffffff, + 0x0, 0x0, // set clip bit + 0xffffffff, 0xffffffff +}; + +/////// +// Union + +// first pass makes all the passing cases >= just clip bit set. +static const GrStencilSettings gUserToClipUnionPass0 = { + kReplace_StencilOp, kReplace_StencilOp, + kKeep_StencilOp, kKeep_StencilOp, + kLEqual_StencilFunc, kLEqual_StencilFunc, + 0xffffffff, 0xffffffff, // unset clip bit + 0x00000001, 0x00000001, // set clip bit + 0xffffffff, 0xffffffff +}; + +// second pass allows anything greater than just clip bit set to pass +static const GrStencilSettings gUserToClipUnionPass1 = { + kReplace_StencilOp, kReplace_StencilOp, + kZero_StencilOp, kZero_StencilOp, + kLEqual_StencilFunc, kLEqual_StencilFunc, + 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, // set clip bit + 0xffffffff, 0xffffffff +}; + +// for inverse first pass finds non-zerp user with clip bit set +// and converts it to just clip bit set +static const GrStencilSettings gInvUserToClipUnionPass0 = { + kReplace_StencilOp, kReplace_StencilOp, + kKeep_StencilOp, kKeep_StencilOp, + kLess_StencilFunc, kLess_StencilFunc, + 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, // set clip bit + 0xffffffff, 0xffffffff +}; + +// second pass lets anything through with a nonzero user portion +// and writes a ref value with just the clip bit set to it. +static const GrStencilSettings gInvUserToClipUnionPass1 = { + kReplace_StencilOp, kReplace_StencilOp, + kZero_StencilOp, kZero_StencilOp, + kLess_StencilFunc, kLess_StencilFunc, + 0xffffffff, 0xffffffff, // unset clip bit + 0x00000000, 0x00000000, // set clip bit + 0xffffffff, 0xffffffff +}; + +/////// +// Xor +static const GrStencilSettings gUserToClipXorPass0 = { + kInvert_StencilOp, kInvert_StencilOp, + kKeep_StencilOp, kKeep_StencilOp, + kEqual_StencilFunc, kEqual_StencilFunc, + 0xffffffff, 0xffffffff, // unset clip bit + 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff +}; + +static const GrStencilSettings gUserToClipXorPass1 = { + kReplace_StencilOp, kReplace_StencilOp, + kZero_StencilOp, kZero_StencilOp, + kGreater_StencilFunc, kGreater_StencilFunc, + 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, // set clip bit + 0xffffffff, 0xffffffff +}; + +static const GrStencilSettings gInvUserToClipXorPass0 = { + kInvert_StencilOp, kInvert_StencilOp, + kKeep_StencilOp, kKeep_StencilOp, + kEqual_StencilFunc, kEqual_StencilFunc, + 0xffffffff, 0xffffffff, // unset clip bit + 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff +}; + +static const GrStencilSettings gInvUserToClipXorPass1 = { + kReplace_StencilOp, kReplace_StencilOp, + kZero_StencilOp, kZero_StencilOp, + kLess_StencilFunc, kLess_StencilFunc, + 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, // set clip bit + 0xffffffff, 0xffffffff +}; + +/////// +// Reverse Diff +static const GrStencilSettings gUserToClipRDiffPass0 = { + kInvert_StencilOp, kInvert_StencilOp, + kZero_StencilOp, kZero_StencilOp, + kLess_StencilFunc, kLess_StencilFunc, + 0xffffffff, 0xffffffff, // unset clip bit + 0x00000000, 0x00000000, // set clip bit + 0xffffffff, 0xffffffff +}; + +static const GrStencilSettings gUserToClipRDiffPass1 = { + kReplace_StencilOp, kReplace_StencilOp, + kZero_StencilOp, kZero_StencilOp, + kEqual_StencilFunc, kEqual_StencilFunc, + 0x00000000, 0x00000000, // set clip bit + 0x00000000, 0x00000000, // set clip bit + 0xffffffff, 0xffffffff +}; + +static const GrStencilSettings gInvUserToClipRDiff = { + kInvert_StencilOp, kInvert_StencilOp, + kZero_StencilOp, kZero_StencilOp, + kEqual_StencilFunc, kEqual_StencilFunc, + 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, + 0x00000000, 0x00000000 // set clip bit +}; +/////// +// Direct to Stencil + +// We can render a clip element directly without first writing to the client +// portion of the clip when the fill is not inverse and the set operation will +// only modify the in/out status of samples covered by the clip element. + +// this one only works if used right after stencil clip was cleared. +// Our GrClip doesn't allow midstream replace ops. +static const GrStencilSettings gReplaceClip = { + kReplace_StencilOp, kReplace_StencilOp, + kReplace_StencilOp, kReplace_StencilOp, + kAlways_StencilFunc, kAlways_StencilFunc, + 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, // set clip bit + 0x00000000, 0x00000000 // set clipBit +}; + +static const GrStencilSettings gUnionClip = { + kReplace_StencilOp, kReplace_StencilOp, + kReplace_StencilOp, kReplace_StencilOp, + kAlways_StencilFunc, kAlways_StencilFunc, + 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, // set clip bit + 0x00000000, 0x00000000 // set clip bit +}; + +static const GrStencilSettings gXorClip = { + kInvert_StencilOp, kInvert_StencilOp, + kInvert_StencilOp, kInvert_StencilOp, + kAlways_StencilFunc, kAlways_StencilFunc, + 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, + 0x00000000, 0x00000000 // set clip bit +}; + +static const GrStencilSettings gDiffClip = { + kZero_StencilOp, kZero_StencilOp, + kZero_StencilOp, kZero_StencilOp, + kAlways_StencilFunc, kAlways_StencilFunc, + 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, + 0x00000000, 0x00000000 // set clip bit +}; + +static const GrPathFill gNonInvertedFills[] = { + kWinding_PathFill, // kWinding_PathFill + kEvenOdd_PathFill, // kEvenOdd_PathFill + kWinding_PathFill, // kInverseWinding_PathFill + kEvenOdd_PathFill, // kInverseEvenOdd_PathFill + kWinding_PathFill, // kHairLine_PathFill +}; + +static const bool gIsFillInverted[] = { + false, // kWinding_PathFill + false, // kEvenOdd_PathFill + true, // kInverseWinding_PathFill + true, // kInverseEvenOdd_PathFill + false, // kHairLine_PathFill +}; +GR_STATIC_ASSERT(0 == kWinding_PathFill); +GR_STATIC_ASSERT(1 == kEvenOdd_PathFill); +GR_STATIC_ASSERT(2 == kInverseWinding_PathFill); +GR_STATIC_ASSERT(3 == kInverseEvenOdd_PathFill); +GR_STATIC_ASSERT(4 == kHairLine_PathFill); +GR_STATIC_ASSERT(5 == kPathFillCount); + +bool GrStencilSettings::GetClipPasses(GrSetOp op, + bool canBeDirect, + unsigned int stencilClipMask, + GrPathFill* fill, + int* numPasses, + GrStencilSettings settings[kMaxStencilClipPasses]) { + if (canBeDirect) { + if (!gIsFillInverted[*fill]) { + *numPasses = 0; + switch (op) { + case kReplace_SetOp: + *numPasses = 1; + settings[0] = gReplaceClip; + break; + case kUnion_SetOp: + *numPasses = 1; + settings[0] = gUnionClip; + break; + case kXor_SetOp: + *numPasses = 1; + settings[0] = gXorClip; + break; + case kDifference_SetOp: + *numPasses = 1; + settings[0] = gDiffClip; + break; + default: // suppress warning + break; + } + if (1 == *numPasses) { + settings[0].fFrontFuncRef |= stencilClipMask; + settings[0].fFrontWriteMask |= stencilClipMask; + settings[0].fBackFuncRef = settings[0].fFrontFuncRef; + settings[0].fBackWriteMask = settings[0].fFrontWriteMask; + return true; + } + } + } + switch (op) { + case kReplace_SetOp: + *numPasses= 1; + settings[0] = gIsFillInverted[*fill] ? gInvUserToClipReplace : gUserToClipReplace; + settings[0].fFrontFuncMask &= ~stencilClipMask; + settings[0].fFrontFuncRef |= stencilClipMask; + settings[0].fBackFuncMask = settings[0].fFrontFuncMask; + settings[0].fBackFuncRef = settings[0].fFrontFuncRef; + + case kIntersect_SetOp: + *numPasses = 1; + settings[0] = gIsFillInverted[*fill] ? gInvUserToClipIsect : gUserToClipIsect; + settings[0].fFrontFuncRef = stencilClipMask; + settings[0].fBackFuncRef = settings[0].fFrontFuncRef; + break; + case kUnion_SetOp: + *numPasses = 2; + if (gIsFillInverted[*fill]) { + settings[0] = gInvUserToClipUnionPass0; + settings[0].fFrontFuncRef |= stencilClipMask; + settings[0].fBackFuncRef = settings[0].fFrontFuncMask; + + settings[1] = gInvUserToClipUnionPass1; + settings[1].fFrontFuncMask &= ~stencilClipMask; + settings[1].fFrontFuncRef |= stencilClipMask; + settings[1].fBackFuncMask = settings[1].fFrontFuncMask; + settings[1].fBackFuncRef = settings[1].fFrontFuncRef; + + } else { + settings[0] = gUserToClipUnionPass0; + settings[0].fFrontFuncMask &= ~stencilClipMask; + settings[0].fFrontFuncRef |= stencilClipMask; + settings[0].fBackFuncMask = settings[0].fFrontFuncMask; + settings[0].fBackFuncRef = settings[0].fFrontFuncRef; + + settings[1] = gUserToClipUnionPass1; + settings[1].fFrontFuncRef |= stencilClipMask; + settings[1].fBackFuncRef = settings[1].fFrontFuncRef; + } + break; + case kXor_SetOp: + *numPasses = 2; + if (gIsFillInverted[*fill]) { + settings[0] = gInvUserToClipXorPass0; + settings[0].fFrontFuncMask &= ~stencilClipMask; + settings[0].fBackFuncMask = settings[0].fFrontFuncMask; + + settings[1] = gInvUserToClipXorPass1; + settings[1].fFrontFuncRef |= stencilClipMask; + settings[1].fBackFuncRef = settings[1].fFrontFuncRef; + } else { + settings[0] = gUserToClipXorPass0; + settings[0].fFrontFuncMask &= ~stencilClipMask; + settings[0].fBackFuncMask = settings[0].fFrontFuncMask; + + settings[1] = gUserToClipXorPass1; + settings[1].fFrontFuncRef |= stencilClipMask; + settings[1].fBackFuncRef = settings[1].fFrontFuncRef; + } + break; + case kDifference_SetOp: + *numPasses = 1; + settings[0] = gIsFillInverted[*fill] ? gInvUserToClipDiff : gUserToClipDiff; + settings[0].fFrontFuncRef |= stencilClipMask; + settings[0].fBackFuncRef = settings[0].fFrontFuncRef; + break; + case kReverseDifference_SetOp: + if (gIsFillInverted[*fill]) { + *numPasses = 1; + settings[0] = gInvUserToClipRDiff; + settings[0].fFrontWriteMask |= stencilClipMask; + settings[0].fBackWriteMask = settings[0].fFrontWriteMask; + } else { + *numPasses = 2; + settings[0] = gUserToClipRDiffPass0; + settings[0].fFrontFuncMask &= ~stencilClipMask; + settings[0].fBackFuncMask = settings[0].fFrontFuncMask; + settings[0].fFrontFuncRef |= stencilClipMask; + settings[0].fBackFuncRef = settings[0].fFrontFuncRef; + + settings[1] = gUserToClipRDiffPass1; + settings[1].fFrontFuncMask |= stencilClipMask; + settings[1].fFrontFuncRef |= stencilClipMask; + settings[1].fBackFuncMask = settings[1].fFrontFuncMask; + settings[1].fBackFuncRef = settings[1].fFrontFuncRef; + } + break; + default: + GrCrash("Unknown set op"); + } + *fill = gNonInvertedFills[*fill]; + return false; +}
\ No newline at end of file diff --git a/gpu/src/GrTextContext.cpp b/gpu/src/GrTextContext.cpp index 8ce45e40eb..802e3e3ab7 100644 --- a/gpu/src/GrTextContext.cpp +++ b/gpu/src/GrTextContext.cpp @@ -67,21 +67,25 @@ GrTextContext::GrTextContext(GrContext* context, fCurrTexture = NULL; fCurrVertex = 0; - fClipRect = context->getClip().getBounds(); if (NULL != extMatrix) { fExtMatrix = *extMatrix; } else { fExtMatrix = GrMatrix::I(); } - if (!fExtMatrix.isIdentity()) { - GrMatrix inverse; - GrRect r; - r.set(fClipRect); - if (fExtMatrix.invert(&inverse)) { - inverse.mapRect(&r); - r.roundOut(&fClipRect); + if (context->getClip().hasBounds()) { + if (!fExtMatrix.isIdentity()) { + GrMatrix inverse; + GrRect r = context->getClip().getBounds(); + if (fExtMatrix.invert(&inverse)) { + inverse.mapRect(&r); + r.roundOut(&fClipRect); + } + } else { + context->getClip().getBounds().roundOut(&fClipRect); } + } else { + fClipRect.setLargest(); } // save the context's original matrix off and restore in destructor diff --git a/gpu/src/gr_files.mk b/gpu/src/gr_files.mk index 88aa84b222..d0f41e748f 100644 --- a/gpu/src/gr_files.mk +++ b/gpu/src/gr_files.mk @@ -22,4 +22,5 @@ SOURCE := \ GrTextContext.cpp \ GrTextStrike.cpp \ GrBufferAllocPool.cpp\ - GrPathRenderer.cpp + GrPathRenderer.cpp \ + GrStencil.cpp diff --git a/gpu/src/gr_unittests.cpp b/gpu/src/gr_unittests.cpp index 8ef61b1605..9caaa1fb2b 100644 --- a/gpu/src/gr_unittests.cpp +++ b/gpu/src/gr_unittests.cpp @@ -73,71 +73,9 @@ static void test_bsearch() { } } -static void dump(const GrClip& clip, const char message[]) { - GrPrintf("--- dump clip %s\n", message); - GrClipIter iter(clip); - while (!iter.isDone()) { - GrIRect r; - iter.getRect(&r); - GrPrintf("--- [%d %d %d %d]\n", r.fLeft, r.fTop, r.fRight, r.fBottom); - iter.next(); - } -} - -static void test_clip() { - GrClip clip; - GrAssert(clip.isEmpty()); - GrAssert(!clip.isRect()); - GrAssert(!clip.isComplex()); - GrAssert(clip.getBounds().equalsLTRB(0, 0, 0, 0)); - GrAssert(0 == clip.countRects()); - - clip.setRect(GrIRect(10, 10, 10, 10)); - GrAssert(clip.isEmpty()); - GrAssert(!clip.isRect()); - GrAssert(!clip.isComplex()); - GrAssert(clip.getBounds().equalsLTRB(0, 0, 0, 0)); - GrAssert(0 == clip.countRects()); - dump(clip, "empty"); - - clip.setRect(GrIRect(10, 10, 20, 20)); - GrAssert(!clip.isEmpty()); - GrAssert(clip.isRect()); - GrAssert(!clip.isComplex()); - GrAssert(clip.getBounds().equalsLTRB(10, 10, 20, 20)); - GrAssert(1 == clip.countRects()); - GrAssert(clip.getRects()[0] == clip.getBounds()); - dump(clip, "rect"); - - clip.addRect(GrIRect(20, 20, 25, 25)); - GrAssert(!clip.isEmpty()); - GrAssert(!clip.isRect()); - GrAssert(clip.isComplex()); - GrAssert(clip.getBounds().equalsLTRB(10, 10, 25, 25)); - GrAssert(2 == clip.countRects()); - dump(clip, "complex"); - - GrClip c1(clip); - GrAssert(c1 == clip); - GrClip c2; - GrAssert(c2 != c1); - c2 = clip; - GrAssert(c2 == clip); - - clip.setEmpty(); - GrAssert(clip.isEmpty()); - GrAssert(!clip.isRect()); - GrAssert(!clip.isComplex()); - GrAssert(clip.getBounds().equalsLTRB(0, 0, 0, 0)); - - GrAssert(c1 != clip); - GrAssert(c2 != clip); -} - void gr_run_unittests() { test_tdarray(); test_bsearch(); - test_clip(); GrMatrix::UnitTest(); GrRedBlackTree<int>::UnitTest(); } diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h index 1e153078c7..eef3aea572 100644 --- a/include/core/SkCanvas.h +++ b/include/core/SkCanvas.h @@ -794,7 +794,8 @@ private: SkDevice* fLastDeviceToGainFocus; SkDeviceFactory* fDeviceFactory; - void prepareForDeviceDraw(SkDevice*, const SkMatrix&, const SkRegion&); + void prepareForDeviceDraw(SkDevice*, const SkMatrix&, const SkRegion&, + const SkClipStack& clipStack); bool fDeviceCMDirty; // cleared by updateDeviceCMCache() void updateDeviceCMCache(); diff --git a/include/core/SkClipStack.h b/include/core/SkClipStack.h index fb941553b3..db42e4df26 100644 --- a/include/core/SkClipStack.h +++ b/include/core/SkClipStack.h @@ -29,6 +29,11 @@ public: class B2FIter { public: + /** + * Creates an uninitialized iterator. Must be reset() + */ + B2FIter(); + B2FIter(const SkClipStack& stack); struct Clip { @@ -48,6 +53,11 @@ public: */ const Clip* next(); + /** + * Restarts the iterator on a clip stack. + */ + void reset(const SkClipStack& stack); + private: Clip fClip; SkDeque::F2BIter fIter; diff --git a/include/core/SkDeque.h b/include/core/SkDeque.h index 99c8dd46ab..92d515300a 100644 --- a/include/core/SkDeque.h +++ b/include/core/SkDeque.h @@ -52,9 +52,16 @@ private: public: class F2BIter { public: + /** + * Creates an uninitialized iterator. Must be reset() + */ + F2BIter(); + F2BIter(const SkDeque& d); void* next(); + void reset(const SkDeque& d); + private: SkDeque::Head* fHead; char* fPos; diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h index a7903993a4..c0d71c3f8e 100644 --- a/include/core/SkDevice.h +++ b/include/core/SkDevice.h @@ -141,7 +141,8 @@ public: /** Called when this device gains focus (i.e becomes the current device for drawing). */ - virtual void gainFocus(SkCanvas*, const SkMatrix&, const SkRegion&) {} + virtual void gainFocus(SkCanvas*, const SkMatrix&, const SkRegion&, + const SkClipStack&) {} /** Causes any deferred drawing to the device to be completed. */ diff --git a/include/core/SkRegion.h b/include/core/SkRegion.h index 8d9ff013ed..58f4f3fb3f 100644 --- a/include/core/SkRegion.h +++ b/include/core/SkRegion.h @@ -260,7 +260,7 @@ public: bool rewind(); // reset the iterator, using the new region void reset(const SkRegion&); - bool done() { return fDone; } + bool done() const { return fDone; } void next(); const SkIRect& rect() const { return fRect; } // may return null diff --git a/include/gpu/SkGpuDevice.h b/include/gpu/SkGpuDevice.h index 2db33800ed..3fed99ab69 100644 --- a/include/gpu/SkGpuDevice.h +++ b/include/gpu/SkGpuDevice.h @@ -68,7 +68,8 @@ public: * Override from SkGpuDevice, so we can set our FBO to be the render target * The canvas parameter must be a SkGpuCanvas */ - virtual void gainFocus(SkCanvas*, const SkMatrix&, const SkRegion&); + virtual void gainFocus(SkCanvas*, const SkMatrix&, const SkRegion&, + const SkClipStack& clipStack); virtual SkGpuTexture* accessTexture() { return (SkGpuTexture*)fTexture; } diff --git a/include/gpu/SkGr.h b/include/gpu/SkGr.h index ccdd400933..d6a3fab8cc 100644 --- a/include/gpu/SkGr.h +++ b/include/gpu/SkGr.h @@ -20,7 +20,7 @@ #include <stddef.h> -// tetrark headers +// Gr headers #include "GrConfig.h" #include "GrContext.h" #include "GrFontScaler.h" @@ -33,6 +33,7 @@ #include "SkPoint.h" #include "SkRegion.h" #include "SkShader.h" +#include "SkClipStack.h" #if (GR_DEBUG && defined(SK_RELEASE)) || (GR_RELEASE && defined(SK_DEBUG)) // #error "inconsistent GR_DEBUG and SK_DEBUG" @@ -170,39 +171,99 @@ public: class SkGrPathIter : public GrPathIter { public: - SkGrPathIter(const SkPath& path) : fIter(path, false), fPath(path) {} + SkGrPathIter() { fPath = NULL; } + SkGrPathIter(const SkPath& path) { reset(path); } virtual Command next(GrPoint pts[]); virtual Command next(); virtual void rewind(); virtual ConvexHint hint() const; + + void reset(const SkPath& path) { + fPath = &path; + fIter.setPath(path, false); + } private: #if !SK_SCALAR_IS_GR_SCALAR SkPoint fPoints[4]; #endif SkPath::Iter fIter; - const SkPath& fPath; + const SkPath* fPath; }; class SkGrClipIterator : public GrClipIterator { public: - void reset(const SkRegion& clip) { - fIter.reset(clip); - this->invalidateBoundsCache(); - } + SkGrClipIterator() { fClipStack = NULL; fCurr = NULL; } + SkGrClipIterator(const SkClipStack& clipStack) { this->reset(clipStack); } + + void reset(const SkClipStack& clipStack); // overrides + virtual bool isDone() const { return NULL == fCurr; } + virtual void next() { fCurr = fIter.next(); } + virtual void rewind() { this->reset(*fClipStack); } + virtual GrClipType getType() const; + + virtual GrSetOp getOp() const; + + virtual void getRect(GrRect* rect) const { + *rect = Sk2Gr(*fCurr->fRect); + } - virtual bool isDone() { return fIter.done(); } - virtual void getRect(GrIRect* rect) { - SkGr::SetIRect(rect, fIter.rect()); + virtual GrPathIter* getPathIter() { + fPathIter.reset(*fCurr->fPath); + return &fPathIter; } + + virtual GrPathFill getPathFill() const; + +private: + const SkClipStack* fClipStack; + SkClipStack::B2FIter fIter; + SkGrPathIter fPathIter; + // SkClipStack's auto advances on each get + // so we store the current pos here. + const SkClipStack::B2FIter::Clip* fCurr; +}; + +class SkGrRegionIterator : public GrClipIterator { +public: + SkGrRegionIterator() {} + SkGrRegionIterator(const SkRegion& region) { this->reset(region); } + + void reset(const SkRegion& region) { + fRegion = ®ion; + fIter.reset(region); + } + + // overrides + virtual bool isDone() const { return fIter.done(); } virtual void next() { fIter.next(); } - virtual void rewind() { fIter.rewind(); } - virtual void computeBounds(GrIRect* bounds); + virtual void rewind() { this->reset(*fRegion); } + virtual GrClipType getType() const { return kRect_ClipType; } + + virtual GrSetOp getOp() const { return kUnion_SetOp; } + virtual void getRect(GrRect* rect) const { + const SkIRect& r = fIter.rect(); + rect->fLeft = GrIntToScalar(r.fLeft); + rect->fTop = GrIntToScalar(r.fTop); + rect->fRight = GrIntToScalar(r.fRight); + rect->fBottom = GrIntToScalar(r.fBottom); + } + + virtual GrPathIter* getPathIter() { + SkASSERT(0); + return NULL; + } + + virtual GrPathFill getPathFill() const { + SkASSERT(0); + return kWinding_PathFill; + } private: - SkRegion::Iterator fIter; + const SkRegion* fRegion; + SkRegion::Iterator fIter; }; class SkGlyphCache; diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp index e2d6af4bec..e636d2b67d 100644 --- a/src/core/SkCanvas.cpp +++ b/src/core/SkCanvas.cpp @@ -264,7 +264,7 @@ public: } // fCurrLayer may be NULL now - fCanvas->prepareForDeviceDraw(fDevice, *fMatrix, *fClip); + fCanvas->prepareForDeviceDraw(fDevice, *fMatrix, *fClip, *fClipStack); return true; } return false; @@ -632,10 +632,11 @@ void SkCanvas::updateDeviceCMCache() { } void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix, - const SkRegion& clip) { + const SkRegion& clip, + const SkClipStack& clipStack) { SkASSERT(device); if (fLastDeviceToGainFocus != device) { - device->gainFocus(this, matrix, clip); + device->gainFocus(this, matrix, clip, clipStack); fLastDeviceToGainFocus = device; } } diff --git a/src/core/SkClipStack.cpp b/src/core/SkClipStack.cpp index 2b63aea9a0..864f23a4b2 100644 --- a/src/core/SkClipStack.cpp +++ b/src/core/SkClipStack.cpp @@ -116,7 +116,11 @@ void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op) { /////////////////////////////////////////////////////////////////////////////// -SkClipStack::B2FIter::B2FIter(const SkClipStack& stack) : fIter(stack.fDeque) { +SkClipStack::B2FIter::B2FIter() { +} + +SkClipStack::B2FIter::B2FIter(const SkClipStack& stack) { + this->reset(stack); } const SkClipStack::B2FIter::Clip* SkClipStack::B2FIter::next() { @@ -142,3 +146,7 @@ const SkClipStack::B2FIter::Clip* SkClipStack::B2FIter::next() { fClip.fOp = rec->fOp; return &fClip; } + +void SkClipStack::B2FIter::reset(const SkClipStack& stack) { + fIter.reset(stack.fDeque); +} diff --git a/src/core/SkDeque.cpp b/src/core/SkDeque.cpp index 2c6ce64fae..9d685eed8b 100644 --- a/src/core/SkDeque.cpp +++ b/src/core/SkDeque.cpp @@ -225,12 +225,12 @@ void SkDeque::pop_back() { /////////////////////////////////////////////////////////////////////////////// -SkDeque::F2BIter::F2BIter(const SkDeque& d) : fElemSize(d.fElemSize) { - fHead = d.fFront; - while (fHead != NULL && fHead->fBegin == NULL) { - fHead = fHead->fNext; - } - fPos = fHead ? fHead->fBegin : NULL; +SkDeque::F2BIter::F2BIter() { + fPos = NULL; +} + +SkDeque::F2BIter::F2BIter(const SkDeque& d) { + this->reset(d); } void* SkDeque::F2BIter::next() { @@ -250,3 +250,11 @@ void* SkDeque::F2BIter::next() { return pos; } +void SkDeque::F2BIter::reset(const SkDeque& d) { + fElemSize = d.fElemSize; + fHead = d.fFront; + while (fHead != NULL && fHead->fBegin == NULL) { + fHead = fHead->fNext; + } + fPos = fHead ? fHead->fBegin : NULL; +} diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index 3dfe02aa14..e925e53074 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -267,19 +267,30 @@ void SkGpuDevice::writePixels(const SkBitmap& bitmap, int x, int y) { /////////////////////////////////////////////////////////////////////////////// +#define USE_CLIP_STACK 0 + static void convert_matrixclip(GrContext* context, const SkMatrix& matrix, - const SkRegion& clip) { + const SkClipStack& clipStack, + const SkRegion& clipRegion) { GrMatrix grmat; SkGr::SkMatrix2GrMatrix(matrix, &grmat); context->setMatrix(grmat); +#if USE_CLIP_STACK SkGrClipIterator iter; - iter.reset(clip); - GrClip grc(&iter); - if (context->getClip() == grc) { - } else { - context->setClip(grc); - } + iter.reset(clipStack); +#else + SkGrRegionIterator iter; + iter.reset(clipRegion); +#endif + const SkIRect& skBounds = clipRegion.getBounds(); + GrRect bounds; + bounds.setLTRB(GrIntToScalar(skBounds.fLeft), + GrIntToScalar(skBounds.fTop), + GrIntToScalar(skBounds.fRight), + GrIntToScalar(skBounds.fBottom)); + GrClip grc(&iter, NULL); + context->setClip(grc); } // call this ever each draw call, to ensure that the context reflects our state, @@ -289,7 +300,9 @@ void SkGpuDevice::prepareRenderTarget(const SkDraw& draw) { fContext->getRenderTarget() != fRenderTarget) { fContext->setRenderTarget(fRenderTarget); - convert_matrixclip(fContext, *draw.fMatrix, *draw.fClip); + SkASSERT(draw.fClipStack); + convert_matrixclip(fContext, *draw.fMatrix, + *draw.fClipStack, *draw.fClip); fNeedPrepareRenderTarget = false; } } @@ -298,16 +311,17 @@ void SkGpuDevice::setMatrixClip(const SkMatrix& matrix, const SkRegion& clip, const SkClipStack& clipStack) { this->INHERITED::setMatrixClip(matrix, clip, clipStack); - convert_matrixclip(fContext, matrix, clip); + convert_matrixclip(fContext, matrix, clipStack, clip); } void SkGpuDevice::gainFocus(SkCanvas* canvas, const SkMatrix& matrix, - const SkRegion& clip) { + const SkRegion& clip, const SkClipStack& clipStack) { + fContext->setRenderTarget(fRenderTarget); - this->INHERITED::gainFocus(canvas, matrix, clip); + this->INHERITED::gainFocus(canvas, matrix, clip, clipStack); - convert_matrixclip(fContext, matrix, clip); + convert_matrixclip(fContext, matrix, clipStack, clip); if (fNeedClear) { fContext->eraseColor(0x0); diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp index 41cf1bd520..4c7bf6c115 100644 --- a/src/gpu/SkGr.cpp +++ b/src/gpu/SkGr.cpp @@ -135,22 +135,80 @@ SkGrPathIter::Command SkGrPathIter::next() { } void SkGrPathIter::rewind() { - fIter.setPath(fPath, false); + fIter.setPath(*fPath, false); } GrPathIter::ConvexHint SkGrPathIter::hint() const { - return fPath.isConvex() ? GrPathIter::kConvex_ConvexHint : - GrPathIter::kNone_ConvexHint; + return fPath->isConvex() ? GrPathIter::kConvex_ConvexHint : + GrPathIter::kNone_ConvexHint; } /////////////////////////////////////////////////////////////////////////////// -void SkGrClipIterator::computeBounds(GrIRect* bounds) { - const SkRegion* rgn = fIter.rgn(); - if (rgn) { - SkGr::SetIRect(bounds, rgn->getBounds()); +void SkGrClipIterator::reset(const SkClipStack& clipStack) { + fClipStack = &clipStack; + fIter.reset(clipStack); + // Gr has no notion of replace, skip to the + // last replace in the clip stack. + int lastReplace = 0; + int curr = 0; + while (NULL != (fCurr = fIter.next())) { + if (SkRegion::kReplace_Op == fCurr->fOp) { + lastReplace = curr; + } + ++curr; + } + fIter.reset(clipStack); + for (int i = 0; i < lastReplace+1; ++i) { + fCurr = fIter.next(); + } +} + +GrClipType SkGrClipIterator::getType() const { + GrAssert(!this->isDone()); + if (NULL != fCurr->fRect) { + return kRect_ClipType; } else { - bounds->setEmpty(); + GrAssert(NULL != fCurr->fPath); + return kPath_ClipType; + } +} + +GrSetOp SkGrClipIterator::getOp() const { + // we skipped to the last "replace" op + // when this iter was reset. + // GrClip doesn't allow replace, so treat it as + // intersect. + GrSetOp skToGrOps[] = { + kDifference_SetOp, // kDifference_Op + kIntersect_SetOp, // kIntersect_Op + kUnion_SetOp, // kUnion_Op + kXor_SetOp, // kXOR_Op + kReverseDifference_SetOp, // kReverseDifference_Op + kIntersect_SetOp // kReplace_op + }; + GR_STATIC_ASSERT(0 == SkRegion::kDifference_Op); + GR_STATIC_ASSERT(1 == SkRegion::kIntersect_Op); + GR_STATIC_ASSERT(2 == SkRegion::kUnion_Op); + GR_STATIC_ASSERT(3 == SkRegion::kXOR_Op); + GR_STATIC_ASSERT(4 == SkRegion::kReverseDifference_Op); + GR_STATIC_ASSERT(5 == SkRegion::kReplace_Op); + return skToGrOps[fCurr->fOp]; +} + +GrPathFill SkGrClipIterator::getPathFill() const { + switch (fCurr->fPath->getFillType()) { + case SkPath::kWinding_FillType: + return kWinding_PathFill; + case SkPath::kEvenOdd_FillType: + return kEvenOdd_PathFill; + case SkPath::kInverseWinding_FillType: + return kInverseWinding_PathFill; + case SkPath::kInverseEvenOdd_FillType: + return kInverseEvenOdd_PathFill; + default: + GrCrash("Unsupported path fill in clip."); + return kWinding_PathFill; // suppress warning } } diff --git a/src/utils/mac/SkOSWindow_Mac.cpp b/src/utils/mac/SkOSWindow_Mac.cpp index 77161854b7..05f97a7fed 100644 --- a/src/utils/mac/SkOSWindow_Mac.cpp +++ b/src/utils/mac/SkOSWindow_Mac.cpp @@ -531,7 +531,6 @@ void SkOSWindow::detachGL() { void SkOSWindow::presentGL() { aglSwapBuffers((AGLContext)fAGLCtx); - glFlush(); } #endif diff --git a/src/utils/win/SkOSWindow_Win.cpp b/src/utils/win/SkOSWindow_Win.cpp index 3594c2fc30..96a026d6a2 100644 --- a/src/utils/win/SkOSWindow_Win.cpp +++ b/src/utils/win/SkOSWindow_Win.cpp @@ -444,10 +444,6 @@ bool SkOSWindow::attachGL(const SkBitmap* offscreen) { } } if (wglMakeCurrent(GetDC((HWND)fHWND), (HGLRC)fHGLRC)) { - glViewport(0, 0, this->width(), this->height()); - glClearColor(0, 0, 0, 0); - glClearStencil(0); - glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); fGLAttached = true; return true; } @@ -462,9 +458,6 @@ void SkOSWindow::detachGL() { void SkOSWindow::presentGL() { glFlush(); SwapBuffers(GetDC((HWND)fHWND)); - glClearColor(0,0,0,0); - glClearStencil(0); - glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); } IDirect3DDevice9* create_d3d9_device(HWND hwnd) { diff --git a/vs/SampleApp/SampleApp.vcxproj b/vs/SampleApp/SampleApp.vcxproj index 8c6fe83ff9..8de34d018f 100644 --- a/vs/SampleApp/SampleApp.vcxproj +++ b/vs/SampleApp/SampleApp.vcxproj @@ -133,6 +133,7 @@ <ClInclude Include="..\..\gpu\include\GrRefCnt.h" />
<ClInclude Include="..\..\gpu\include\GrSamplerState.h" />
<ClInclude Include="..\..\gpu\include\GrScalar.h" />
+ <ClInclude Include="..\..\gpu\include\GrStencil.h" />
<ClInclude Include="..\..\gpu\include\GrStopwatch.h" />
<ClInclude Include="..\..\gpu\include\GrStringBuilder.h" />
<ClInclude Include="..\..\gpu\include\GrTArray.h" />
@@ -233,6 +234,7 @@ <ClCompile Include="..\..\gpu\src\GrPath.cpp" />
<ClCompile Include="..\..\gpu\src\GrPathRenderer.cpp" />
<ClCompile Include="..\..\gpu\src\GrRectanizer.cpp" />
+ <ClCompile Include="..\..\gpu\src\GrStencil.cpp" />
<ClCompile Include="..\..\gpu\src\GrTextContext.cpp" />
<ClCompile Include="..\..\gpu\src\GrTextStrike.cpp" />
<ClCompile Include="..\..\gpu\src\GrTextureCache.cpp" />
@@ -246,6 +248,7 @@ <ClCompile Include="..\..\samplecode\SampleBlur.cpp" />
<ClCompile Include="..\..\samplecode\SampleCamera.cpp" />
<ClCompile Include="..\..\samplecode\SampleCircle.cpp" />
+ <ClCompile Include="..\..\samplecode\SampleComplexClip.cpp" />
<ClCompile Include="..\..\samplecode\SampleCull.cpp" />
<ClCompile Include="..\..\samplecode\SampleDither.cpp" />
<ClCompile Include="..\..\samplecode\SampleDitherBitmap.cpp" />
diff --git a/xcode/gpu/gpu.xcodeproj/project.pbxproj b/xcode/gpu/gpu.xcodeproj/project.pbxproj index 58a6358394..e891f26d3f 100644 --- a/xcode/gpu/gpu.xcodeproj/project.pbxproj +++ b/xcode/gpu/gpu.xcodeproj/project.pbxproj @@ -90,6 +90,8 @@ 00216E5E130F0B03009A2160 /* GrGLIRect.h in Headers */ = {isa = PBXBuildFile; fileRef = 00216E5D130F0B03009A2160 /* GrGLIRect.h */; }; D539049B12EA01E30025F3D6 /* GrContext_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = D539049A12EA01E30025F3D6 /* GrContext_impl.h */; }; D53904A112EA026E0025F3D6 /* GrPaint.h in Headers */ = {isa = PBXBuildFile; fileRef = D53904A012EA026E0025F3D6 /* GrPaint.h */; }; + D542EAAD131C87E90065FC9D /* GrStencil.h in Headers */ = {isa = PBXBuildFile; fileRef = D542EAAC131C87E90065FC9D /* GrStencil.h */; }; + D5558AE3131EB9BB00C71009 /* GrStencil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5558AE2131EB9BB00C71009 /* GrStencil.cpp */; }; D58CAF9A12E7212100CB9277 /* GrGLUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D58CAF9812E7212100CB9277 /* GrGLUtil.cpp */; }; D5ED886F1313F92C00B98D64 /* GrRedBlackTree.h in Headers */ = {isa = PBXBuildFile; fileRef = D5ED886E1313F92C00B98D64 /* GrRedBlackTree.h */; }; D5ED88EB13144FD600B98D64 /* GrPathRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5ED88E913144FD600B98D64 /* GrPathRenderer.cpp */; }; @@ -184,6 +186,8 @@ D2AAC046055464E500DB518D /* libgpu.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libgpu.a; sourceTree = BUILT_PRODUCTS_DIR; }; D539049A12EA01E30025F3D6 /* GrContext_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GrContext_impl.h; path = ../../gpu/include/GrContext_impl.h; sourceTree = SOURCE_ROOT; }; D53904A012EA026E0025F3D6 /* GrPaint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GrPaint.h; path = ../../gpu/include/GrPaint.h; sourceTree = SOURCE_ROOT; }; + D542EAAC131C87E90065FC9D /* GrStencil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GrStencil.h; path = ../../gpu/include/GrStencil.h; sourceTree = SOURCE_ROOT; }; + D5558AE2131EB9BB00C71009 /* GrStencil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GrStencil.cpp; path = ../../gpu/src/GrStencil.cpp; sourceTree = SOURCE_ROOT; }; D58CAF9812E7212100CB9277 /* GrGLUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GrGLUtil.cpp; path = ../../gpu/src/GrGLUtil.cpp; sourceTree = SOURCE_ROOT; }; D5ED886E1313F92C00B98D64 /* GrRedBlackTree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GrRedBlackTree.h; path = ../../gpu/src/GrRedBlackTree.h; sourceTree = SOURCE_ROOT; }; D5ED88E913144FD600B98D64 /* GrPathRenderer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GrPathRenderer.cpp; path = ../../gpu/src/GrPathRenderer.cpp; sourceTree = SOURCE_ROOT; }; @@ -263,6 +267,7 @@ 00115E6D12C116CA008296FE /* GrUserConfig.h */, 00115E6E12C116CA008296FE /* GrVertexBuffer.h */, D53904A012EA026E0025F3D6 /* GrPaint.h */, + D542EAAC131C87E90065FC9D /* GrStencil.h */, ); name = include; sourceTree = "<group>"; @@ -281,8 +286,6 @@ 08FB7795FE84155DC02AAC07 /* Source */ = { isa = PBXGroup; children = ( - D5ED88E913144FD600B98D64 /* GrPathRenderer.cpp */, - D5ED88EA13144FD600B98D64 /* GrPathRenderer.h */, D5ED886E1313F92C00B98D64 /* GrRedBlackTree.h */, D539049A12EA01E30025F3D6 /* GrContext_impl.h */, 00115DD812C1167A008296FE /* gr_unittests.cpp */, @@ -308,8 +311,11 @@ 00115DEE12C1167A008296FE /* GrMatrix.cpp */, 00115DEF12C1167A008296FE /* GrMemory.cpp */, 00115DF012C1167A008296FE /* GrPath.cpp */, + D5ED88EA13144FD600B98D64 /* GrPathRenderer.h */, + D5ED88E913144FD600B98D64 /* GrPathRenderer.cpp */, 00115DF412C1167A008296FE /* GrRectanizer_fifo.cpp */, 00115DF512C1167A008296FE /* GrRectanizer.cpp */, + D5558AE2131EB9BB00C71009 /* GrStencil.cpp */, 00115DF612C1167A008296FE /* GrTextContext.cpp */, 00115DF712C1167A008296FE /* GrTextStrike_impl.h */, 00115DF812C1167A008296FE /* GrTextStrike.cpp */, @@ -405,6 +411,7 @@ 00216E5E130F0B03009A2160 /* GrGLIRect.h in Headers */, D5ED886F1313F92C00B98D64 /* GrRedBlackTree.h in Headers */, D5ED88EC13144FD600B98D64 /* GrPathRenderer.h in Headers */, + D542EAAD131C87E90065FC9D /* GrStencil.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -483,6 +490,7 @@ D58CAF9A12E7212100CB9277 /* GrGLUtil.cpp in Sources */, D5FAF22313072C27001550A4 /* GrBufferAllocPool.cpp in Sources */, D5ED88EB13144FD600B98D64 /* GrPathRenderer.cpp in Sources */, + D5558AE3131EB9BB00C71009 /* GrStencil.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj b/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj index 1a39d9add5..28205469ba 100644 --- a/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj +++ b/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj @@ -30,7 +30,6 @@ 0021F3A21120B29C0062682F /* SkStaticTextView.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0021F3A11120B29C0062682F /* SkStaticTextView.cpp */; }; 0021F3D31120B61F0062682F /* SampleUnitMapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00995E1510A079D80054AD6D /* SampleUnitMapper.cpp */; }; 00244D1B10642BBA00B8F4D8 /* SampleStrokePath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0009E21F1057E96800B0DE6F /* SampleStrokePath.cpp */; }; - 00244D97106A539500B8F4D8 /* SampleXfermodes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 007A7CB20F01658C00A2D6EE /* SampleXfermodes.cpp */; }; 00244DE2106A681600B8F4D8 /* SampleShaders.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 007A7CA80F01658C00A2D6EE /* SampleShaders.cpp */; }; 00281C751083CF7E00BCCB06 /* libAnimator.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00281C711083CF6600BCCB06 /* libAnimator.a */; }; 00281D071084ED1200BCCB06 /* SkXMLParser_expat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00281D061084ED1200BCCB06 /* SkXMLParser_expat.cpp */; }; @@ -81,7 +80,6 @@ 009F9D1A12C3EB2600C7FD4A /* SampleGM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 27C4625412BFB2F300DBB1F6 /* SampleGM.cpp */; }; 00A728270FD43D0400D5051F /* SampleMovie.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2762F6760FCCCB01002BD8B4 /* SampleMovie.cpp */; }; 00A7282F0FD43D3700D5051F /* SkMovie.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00A7282D0FD43D3700D5051F /* SkMovie.cpp */; }; - 00A7295D0FD8397600D5051F /* SampleAll.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2762F6740FCCCB01002BD8B4 /* SampleAll.cpp */; }; 00AF787E0FE94433007F9650 /* SamplePath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00003C640EFC22A8000FF73A /* SamplePath.cpp */; }; 00AF9B18103CD5EB00CBBCB3 /* SampleDitherBitmap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00AF9B17103CD5EB00CBBCB3 /* SampleDitherBitmap.cpp */; }; 00BB289B104781D00057BF7E /* SampleForth.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00BB289A104781D00057BF7E /* SampleForth.cpp */; }; @@ -148,10 +146,13 @@ 8D0C4E8D0486CD37000505A6 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0867D6AAFE840B52C02AAC07 /* InfoPlist.strings */; }; 8D0C4E8E0486CD37000505A6 /* main.nib in Resources */ = {isa = PBXBuildFile; fileRef = 02345980000FD03B11CA0E72 /* main.nib */; }; 8D0C4E920486CD37000505A6 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 20286C33FDCF999611CA2CEA /* Carbon.framework */; }; + D5558AEF131EBA1E00C71009 /* SampleComplexClip.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5558AEE131EBA1E00C71009 /* SampleComplexClip.cpp */; }; + D5558B29131EBC9C00C71009 /* SampleFillType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0041CE270F00A12400695E8C /* SampleFillType.cpp */; }; D55BEE6712EF44B90055D6FD /* shadertext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D55BEE6612EF44B90055D6FD /* shadertext.cpp */; }; D5962B3A12EDFC7600B478DF /* SampleShaderText.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5962B3812EDFC7600B478DF /* SampleShaderText.cpp */; }; D5A682D712E9CE8500CDDDC6 /* SamplePatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0041CE340F00A12400695E8C /* SamplePatch.cpp */; }; - D5F4A21F12E9D75300DE986A /* SampleFillType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0041CE270F00A12400695E8C /* SampleFillType.cpp */; }; + D5BF1C5A131D449500C4B94B /* SampleAll.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2762F6740FCCCB01002BD8B4 /* SampleAll.cpp */; }; + D5BF1CFD131D500A00C4B94B /* SampleXfermodes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 007A7CB20F01658C00A2D6EE /* SampleXfermodes.cpp */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -414,6 +415,7 @@ 4A9504CAFFE6A41611CA0CBA /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = /System/Library/Frameworks/CoreServices.framework; sourceTree = "<absolute>"; }; 8D0C4E960486CD37000505A6 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; }; 8D0C4E970486CD37000505A6 /* CICarbonSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CICarbonSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + D5558AEE131EBA1E00C71009 /* SampleComplexClip.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleComplexClip.cpp; path = ../../samplecode/SampleComplexClip.cpp; sourceTree = SOURCE_ROOT; }; D55BEE6612EF44B90055D6FD /* shadertext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = shadertext.cpp; path = ../../gm/shadertext.cpp; sourceTree = SOURCE_ROOT; }; D5962B3812EDFC7600B478DF /* SampleShaderText.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleShaderText.cpp; path = ../../samplecode/SampleShaderText.cpp; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ @@ -442,6 +444,7 @@ 00003C610EFC2287000FF73A /* samples */ = { isa = PBXGroup; children = ( + D5558AEE131EBA1E00C71009 /* SampleComplexClip.cpp */, 27C4625412BFB2F300DBB1F6 /* SampleGM.cpp */, 003EE651111239D5001AB759 /* SampleWarp.cpp */, 27A34E8B119892DD00860515 /* SampleTextBox.cpp */, @@ -937,7 +940,6 @@ 2762F66E0FCCCABE002BD8B4 /* SkPageFlipper.cpp in Sources */, 00A728270FD43D0400D5051F /* SampleMovie.cpp in Sources */, 00A7282F0FD43D3700D5051F /* SkMovie.cpp in Sources */, - 00A7295D0FD8397600D5051F /* SampleAll.cpp in Sources */, 00AF787E0FE94433007F9650 /* SamplePath.cpp in Sources */, 005E92DC0FF08507008965B9 /* SampleFilter2.cpp in Sources */, 27005D16100903C100E275B6 /* SampleLines.cpp in Sources */, @@ -950,7 +952,6 @@ 00EB4593104DBB18002B413E /* ForthTests.cpp in Sources */, 009887F1106142FC0020D19B /* SampleNinePatch.cpp in Sources */, 00244D1B10642BBA00B8F4D8 /* SampleStrokePath.cpp in Sources */, - 00244D97106A539500B8F4D8 /* SampleXfermodes.cpp in Sources */, 00244DE2106A681600B8F4D8 /* SampleShaders.cpp in Sources */, 00281D071084ED1200BCCB06 /* SkXMLParser_expat.cpp in Sources */, 009230D8109F111F00AD3F12 /* OverView.cpp in Sources */, @@ -1027,9 +1028,12 @@ 009F9D0A12C3E8AF00C7FD4A /* SampleFilter.cpp in Sources */, 009F9D1A12C3EB2600C7FD4A /* SampleGM.cpp in Sources */, D5A682D712E9CE8500CDDDC6 /* SamplePatch.cpp in Sources */, - D5F4A21F12E9D75300DE986A /* SampleFillType.cpp in Sources */, D5962B3A12EDFC7600B478DF /* SampleShaderText.cpp in Sources */, D55BEE6712EF44B90055D6FD /* shadertext.cpp in Sources */, + D5BF1C5A131D449500C4B94B /* SampleAll.cpp in Sources */, + D5BF1CFD131D500A00C4B94B /* SampleXfermodes.cpp in Sources */, + D5558AEF131EBA1E00C71009 /* SampleComplexClip.cpp in Sources */, + D5558B29131EBC9C00C71009 /* SampleFillType.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; |