diff --git a/include/gpu/GrTypes.h b/include/gpu/GrTypes.h index 80cfca32c5..ff8f442f7d 100644 --- a/include/gpu/GrTypes.h +++ b/include/gpu/GrTypes.h @@ -608,6 +608,7 @@ enum GrGLBackendState { kProgram_GrGLBackendState = 1 << 8, kFixedFunction_GrGLBackendState = 1 << 9, kMisc_GrGLBackendState = 1 << 10, + kPathRendering_GrGLBackendState = 1 << 11, kALL_GrGLBackendState = 0xffff }; diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp index 58b6a4bf61..1a10336172 100644 --- a/src/gpu/GrClipMaskManager.cpp +++ b/src/gpu/GrClipMaskManager.cpp @@ -1012,3 +1012,29 @@ void GrClipMaskManager::setGpu(GrGpu* gpu) { fGpu = gpu; fAACache.setContext(gpu->getContext()); } + +void GrClipMaskManager::adjustPathStencilParams(GrStencilSettings* settings) { + const GrDrawState& drawState = fGpu->getDrawState(); + GrClipMaskManager::StencilClipMode clipMode; + if (this->isClipInStencil() && drawState.isClipState()) { + clipMode = GrClipMaskManager::kRespectClip_StencilClipMode; + // We can't be modifying the clip and respecting it at the same time. + SkASSERT(!drawState.isStateFlagEnabled( + GrGpu::kModifyStencilClip_StateBit)); + } else if (drawState.isStateFlagEnabled( + GrGpu::kModifyStencilClip_StateBit)) { + clipMode = GrClipMaskManager::kModifyClip_StencilClipMode; + } else { + clipMode = GrClipMaskManager::kIgnoreClip_StencilClipMode; + } + + // TODO: dynamically attach a stencil buffer + int stencilBits = 0; + GrStencilBuffer* stencilBuffer = + drawState.getRenderTarget()->getStencilBuffer(); + if (NULL != stencilBuffer) { + stencilBits = stencilBuffer->bits(); + this->adjustStencilParams(settings, clipMode, stencilBits); + } +} + diff --git a/src/gpu/GrClipMaskManager.h b/src/gpu/GrClipMaskManager.h index 54b7892a29..fa9398786c 100644 --- a/src/gpu/GrClipMaskManager.h +++ b/src/gpu/GrClipMaskManager.h @@ -70,6 +70,8 @@ public: } void setGpu(GrGpu* gpu); + + void adjustPathStencilParams(GrStencilSettings* settings); private: /** * Informs the helper function adjustStencilParams() about how the stencil diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp index c2256a84d8..64eedfbef7 100644 --- a/src/gpu/GrDrawTarget.cpp +++ b/src/gpu/GrDrawTarget.cpp @@ -11,6 +11,7 @@ #include "GrDrawTarget.h" #include "GrContext.h" #include "GrDrawTargetCaps.h" +#include "GrPath.h" #include "GrRenderTarget.h" #include "GrTexture.h" #include "GrVertexBuffer.h" @@ -413,17 +414,18 @@ bool GrDrawTarget::checkDraw(GrPrimitiveType type, int startVertex, return true; } -bool GrDrawTarget::setupDstReadIfNecessary(DrawInfo* info) { +bool GrDrawTarget::setupDstReadIfNecessary(GrDeviceCoordTexture* dstCopy, const SkRect* drawBounds) { if (this->caps()->dstReadInShaderSupport() || !this->getDrawState().willEffectReadDstColor()) { return true; } GrRenderTarget* rt = this->drawState()->getRenderTarget(); - - const GrClipData* clip = this->getClip(); SkIRect copyRect; - clip->getConservativeBounds(this->getDrawState().getRenderTarget(), ©Rect); - SkIRect drawIBounds; - if (info->getDevIBounds(&drawIBounds)) { + const GrClipData* clip = this->getClip(); + clip->getConservativeBounds(rt, ©Rect); + + if (NULL != drawBounds) { + SkIRect drawIBounds; + drawBounds->roundOut(&drawIBounds); if (!copyRect.intersect(drawIBounds)) { #ifdef SK_DEBUG GrPrintf("Missed an early reject. Bailing on draw from setupDstReadIfNecessary.\n"); @@ -451,8 +453,8 @@ bool GrDrawTarget::setupDstReadIfNecessary(DrawInfo* info) { } SkIPoint dstPoint = {0, 0}; if (this->copySurface(ast.texture(), rt, copyRect, dstPoint)) { - info->fDstCopy.setTexture(ast.texture()); - info->fDstCopy.setOffset(copyRect.fLeft, copyRect.fTop); + dstCopy->setTexture(ast.texture()); + dstCopy->setOffset(copyRect.fLeft, copyRect.fTop); return true; } else { return false; @@ -518,12 +520,37 @@ void GrDrawTarget::drawNonIndexed(GrPrimitiveType type, void GrDrawTarget::stencilPath(const GrPath* path, const SkStrokeRec& stroke, SkPath::FillType fill) { // TODO: extract portions of checkDraw that are relevant to path stenciling. SkASSERT(NULL != path); - SkASSERT(this->caps()->pathStencilingSupport()); + SkASSERT(this->caps()->pathRenderingSupport()); SkASSERT(!stroke.isHairlineStyle()); SkASSERT(!SkPath::IsInverseFillType(fill)); this->onStencilPath(path, stroke, fill); } +void GrDrawTarget::fillPath(const GrPath* path, const SkStrokeRec& stroke, SkPath::FillType fill) { + // TODO: extract portions of checkDraw that are relevant to path rendering. + SkASSERT(NULL != path); + SkASSERT(this->caps()->pathRenderingSupport()); + SkASSERT(!stroke.isHairlineStyle()); + const GrDrawState* drawState = &getDrawState(); + + SkRect devBounds; + if (SkPath::IsInverseFillType(fill)) { + devBounds = SkRect::MakeWH(SkIntToScalar(drawState->getRenderTarget()->width()), + SkIntToScalar(drawState->getRenderTarget()->height())); + } else { + devBounds = path->getBounds(); + } + SkMatrix viewM = drawState->getViewMatrix(); + viewM.mapRect(&devBounds); + + GrDeviceCoordTexture dstCopy; + if (!this->setupDstReadIfNecessary(&dstCopy, &devBounds)) { + return; + } + + this->onFillPath(path, stroke, fill, dstCopy.texture() ? &dstCopy : NULL); +} + //////////////////////////////////////////////////////////////////////////////// bool GrDrawTarget::willUseHWAALines() const { @@ -949,7 +976,7 @@ void GrDrawTargetCaps::reset() { fGeometryShaderSupport = false; fDualSourceBlendingSupport = false; fBufferLockSupport = false; - fPathStencilingSupport = false; + fPathRenderingSupport = false; fDstReadInShaderSupport = false; fReuseScratchTextures = true; @@ -968,7 +995,7 @@ GrDrawTargetCaps& GrDrawTargetCaps::operator=(const GrDrawTargetCaps& other) { fGeometryShaderSupport = other.fGeometryShaderSupport; fDualSourceBlendingSupport = other.fDualSourceBlendingSupport; fBufferLockSupport = other.fBufferLockSupport; - fPathStencilingSupport = other.fPathStencilingSupport; + fPathRenderingSupport = other.fPathRenderingSupport; fDstReadInShaderSupport = other.fDstReadInShaderSupport; fReuseScratchTextures = other.fReuseScratchTextures; @@ -990,7 +1017,7 @@ void GrDrawTargetCaps::print() const { GrPrintf("Geometry Shader Support : %s\n", gNY[fGeometryShaderSupport]); GrPrintf("Dual Source Blending Support: %s\n", gNY[fDualSourceBlendingSupport]); GrPrintf("Buffer Lock Support : %s\n", gNY[fBufferLockSupport]); - GrPrintf("Path Stenciling Support : %s\n", gNY[fPathStencilingSupport]); + GrPrintf("Path Rendering Support : %s\n", gNY[fPathRenderingSupport]); GrPrintf("Dst Read In Shader Support : %s\n", gNY[fDstReadInShaderSupport]); GrPrintf("Reuse Scratch Textures : %s\n", gNY[fReuseScratchTextures]); GrPrintf("Max Texture Size : %d\n", fMaxTextureSize); diff --git a/src/gpu/GrDrawTarget.h b/src/gpu/GrDrawTarget.h index 47460ac527..49dd3880c8 100644 --- a/src/gpu/GrDrawTarget.h +++ b/src/gpu/GrDrawTarget.h @@ -328,6 +328,12 @@ public: */ void stencilPath(const GrPath*, const SkStrokeRec& stroke, SkPath::FillType fill); + /** + * Fills a path. Fill must not be a hairline. It will respect the HW + * antialias flag on the draw state (if possible in the 3D API). + */ + void fillPath(const GrPath*, const SkStrokeRec& stroke, SkPath::FillType fill); + /** * Helper function for drawing rects. It performs a geometry src push and pop * and thus will finalize any reserved geometry. @@ -448,6 +454,14 @@ public: */ void executeDraw(const DrawInfo& info) { this->onDraw(info); } + /** + * For subclass internal use to invoke a call to onFillPath(). + */ + void executeFillPath(const GrPath* path, const SkStrokeRec& stroke, + SkPath::FillType fill, const GrDeviceCoordTexture* dstCopy) { + this->onFillPath(path, stroke, fill, dstCopy); + } + //////////////////////////////////////////////////////////////////////////// /** @@ -761,15 +775,6 @@ protected: } const SkRect* getDevBounds() const { return fDevBounds; } - bool getDevIBounds(SkIRect* bounds) const { - if (NULL != fDevBounds) { - fDevBounds->roundOut(bounds); - return true; - } else { - return false; - } - } - // NULL if no copy of the dst is needed for the draw. const GrDeviceCoordTexture* getDstCopy() const { if (NULL != fDstCopy.texture()) { @@ -834,6 +839,8 @@ private: const SkRect* localRect, const SkMatrix* localMatrix); virtual void onStencilPath(const GrPath*, const SkStrokeRec& stroke, SkPath::FillType fill) = 0; + virtual void onFillPath(const GrPath*, const SkStrokeRec& stroke, SkPath::FillType fill, + const GrDeviceCoordTexture* dstCopy) = 0; // helpers for reserving vertex and index space. bool reserveVertexSpace(size_t vertexSize, @@ -852,7 +859,10 @@ private: // Makes a copy of the dst if it is necessary for the draw. Returns false if a copy is required // but couldn't be made. Otherwise, returns true. - bool setupDstReadIfNecessary(DrawInfo* info); + bool setupDstReadIfNecessary(DrawInfo* info) { + return this->setupDstReadIfNecessary(&info->fDstCopy, info->getDevBounds()); + } + bool setupDstReadIfNecessary(GrDeviceCoordTexture* dstCopy, const SkRect* drawBounds); // Check to see if this set of draw commands has been sent out virtual bool isIssued(uint32_t drawID) { return true; } diff --git a/src/gpu/GrDrawTargetCaps.h b/src/gpu/GrDrawTargetCaps.h index 61c9fede08..111b35bf74 100644 --- a/src/gpu/GrDrawTargetCaps.h +++ b/src/gpu/GrDrawTargetCaps.h @@ -34,7 +34,7 @@ public: bool geometryShaderSupport() const { return fGeometryShaderSupport; } bool dualSourceBlendingSupport() const { return fDualSourceBlendingSupport; } bool bufferLockSupport() const { return fBufferLockSupport; } - bool pathStencilingSupport() const { return fPathStencilingSupport; } + bool pathRenderingSupport() const { return fPathRenderingSupport; } bool dstReadInShaderSupport() const { return fDstReadInShaderSupport; } bool reuseScratchTextures() const { return fReuseScratchTextures; } @@ -53,7 +53,7 @@ protected: bool fGeometryShaderSupport : 1; bool fDualSourceBlendingSupport : 1; bool fBufferLockSupport : 1; - bool fPathStencilingSupport : 1; + bool fPathRenderingSupport : 1; bool fDstReadInShaderSupport : 1; bool fReuseScratchTextures : 1; diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp index 910c57146b..5b1c23afe4 100644 --- a/src/gpu/GrGpu.cpp +++ b/src/gpu/GrGpu.cpp @@ -198,7 +198,7 @@ GrIndexBuffer* GrGpu::createIndexBuffer(uint32_t size, bool dynamic) { } GrPath* GrGpu::createPath(const SkPath& path) { - SkASSERT(this->caps()->pathStencilingSupport()); + SkASSERT(this->caps()->pathRenderingSupport()); this->handleDirtyContext(); return this->onCreatePath(path); } @@ -247,6 +247,42 @@ void GrGpu::resolveRenderTarget(GrRenderTarget* target) { this->onResolveRenderTarget(target); } +static const GrStencilSettings& winding_path_stencil_settings() { + GR_STATIC_CONST_SAME_STENCIL_STRUCT(gSettings, + kIncClamp_StencilOp, + kIncClamp_StencilOp, + kAlwaysIfInClip_StencilFunc, + 0xFFFF, 0xFFFF, 0xFFFF); + return *GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(&gSettings); +} + +static const GrStencilSettings& even_odd_path_stencil_settings() { + GR_STATIC_CONST_SAME_STENCIL_STRUCT(gSettings, + kInvert_StencilOp, + kInvert_StencilOp, + kAlwaysIfInClip_StencilFunc, + 0xFFFF, 0xFFFF, 0xFFFF); + return *GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(&gSettings); +} + +void GrGpu::getPathStencilSettingsForFillType(SkPath::FillType fill, GrStencilSettings* outStencilSettings) { + + switch (fill) { + default: + GrCrash("Unexpected path fill."); + /* fallthrough */; + case SkPath::kWinding_FillType: + case SkPath::kInverseWinding_FillType: + *outStencilSettings = winding_path_stencil_settings(); + break; + case SkPath::kEvenOdd_FillType: + case SkPath::kInverseEvenOdd_FillType: + *outStencilSettings = even_odd_path_stencil_settings(); + break; + } + fClipMaskManager.adjustPathStencilParams(outStencilSettings); +} + //////////////////////////////////////////////////////////////////////////////// @@ -349,10 +385,6 @@ void GrGpu::onDraw(const DrawInfo& info) { void GrGpu::onStencilPath(const GrPath* path, const SkStrokeRec&, SkPath::FillType fill) { this->handleDirtyContext(); - // TODO: make this more efficient (don't copy and copy back) - GrAutoTRestore asr(this->drawState()->stencil()); - - this->setStencilPathSettings(*path, fill, this->drawState()->stencil()); GrDrawState::AutoRestoreEffects are; if (!this->setupClipAndFlushState(kStencilPath_DrawType, NULL, &are)) { return; @@ -361,6 +393,20 @@ void GrGpu::onStencilPath(const GrPath* path, const SkStrokeRec&, SkPath::FillTy this->onGpuStencilPath(path, fill); } +void GrGpu::onFillPath(const GrPath* path, const SkStrokeRec& stroke, SkPath::FillType fill, + const GrDeviceCoordTexture* dstCopy) { + this->handleDirtyContext(); + + drawState()->setDefaultVertexAttribs(); + + GrDrawState::AutoRestoreEffects are; + if (!this->setupClipAndFlushState(kFillPath_DrawType, dstCopy, &are)) { + return; + } + + this->onGpuFillPath(path, fill); +} + void GrGpu::finalizeReservedVertices() { SkASSERT(NULL != fVertexPool); fVertexPool->unlock(); diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h index 239ae6bd83..6a9dfa2b58 100644 --- a/src/gpu/GrGpu.h +++ b/src/gpu/GrGpu.h @@ -335,12 +335,15 @@ public: // clipping. }; + void getPathStencilSettingsForFillType(SkPath::FillType fill, GrStencilSettings* outStencilSettings); + protected: enum DrawType { kDrawPoints_DrawType, kDrawLines_DrawType, kDrawTriangles_DrawType, kStencilPath_DrawType, + kFillPath_DrawType, }; DrawType PrimTypeToDrawType(GrPrimitiveType type) { @@ -440,15 +443,10 @@ private: // overridden by backend-specific derived class to perform the draw call. virtual void onGpuDraw(const DrawInfo&) = 0; - // when GrDrawTarget::stencilPath is called the draw state's current stencil - // settings are ignored. Instead the GrGpu decides the stencil rules - // necessary to stencil the path. These are still subject to filtering by - // the clip mask manager. - virtual void setStencilPathSettings(const GrPath&, - SkPath::FillType, - GrStencilSettings* settings) = 0; + // overridden by backend-specific derived class to perform the path stenciling. virtual void onGpuStencilPath(const GrPath*, SkPath::FillType) = 0; + virtual void onGpuFillPath(const GrPath*, SkPath::FillType) = 0; // overridden by backend-specific derived class to perform flush virtual void onForceRenderTargetFlush() = 0; @@ -494,6 +492,9 @@ private: virtual void onStencilPath(const GrPath* path, const SkStrokeRec& stroke, SkPath::FillType) SK_OVERRIDE; + virtual void onFillPath(const GrPath* path, const SkStrokeRec& stroke, SkPath::FillType, + const GrDeviceCoordTexture* dstCopy) SK_OVERRIDE; + // readies the pools to provide vertex/index data. void prepareVertexPool(); void prepareIndexPool(); diff --git a/src/gpu/GrInOrderDrawBuffer.cpp b/src/gpu/GrInOrderDrawBuffer.cpp index 8c8fbfc4ce..dbbdf163e6 100644 --- a/src/gpu/GrInOrderDrawBuffer.cpp +++ b/src/gpu/GrInOrderDrawBuffer.cpp @@ -385,6 +385,7 @@ void GrInOrderDrawBuffer::onDraw(const DrawInfo& info) { } GrInOrderDrawBuffer::StencilPath::StencilPath() : fStroke(SkStrokeRec::kFill_InitStyle) {} +GrInOrderDrawBuffer::FillPath::FillPath() : fStroke(SkStrokeRec::kFill_InitStyle) {} void GrInOrderDrawBuffer::onStencilPath(const GrPath* path, const SkStrokeRec& stroke, SkPath::FillType fill) { @@ -402,6 +403,25 @@ void GrInOrderDrawBuffer::onStencilPath(const GrPath* path, const SkStrokeRec& s sp->fStroke = stroke; } +void GrInOrderDrawBuffer::onFillPath(const GrPath* path, const SkStrokeRec& stroke, + SkPath::FillType fill, const GrDeviceCoordTexture* dstCopy) { + if (this->needsNewClip()) { + this->recordClip(); + } + // TODO: Only compare the subset of GrDrawState relevant to path covering? + if (this->needsNewState()) { + this->recordState(); + } + FillPath* cp = this->recordFillPath(); + cp->fPath.reset(path); + path->ref(); + cp->fFill = fill; + cp->fStroke = stroke; + if (NULL != dstCopy) { + cp->fDstCopy = *dstCopy; + } +} + void GrInOrderDrawBuffer::clear(const SkIRect* rect, GrColor color, GrRenderTarget* renderTarget) { SkIRect r; if (NULL == renderTarget) { @@ -436,6 +456,7 @@ void GrInOrderDrawBuffer::reset() { fCmds.reset(); fDraws.reset(); fStencilPaths.reset(); + fFillPaths.reset(); fStates.reset(); fClears.reset(); fVertexPool.reset(); @@ -480,6 +501,7 @@ void GrInOrderDrawBuffer::flush() { int currClear = 0; int currDraw = 0; int currStencilPath = 0; + int currFillPath = 0; int currCopySurface = 0; for (int c = 0; c < numCmds; ++c) { @@ -501,6 +523,13 @@ void GrInOrderDrawBuffer::flush() { ++currStencilPath; break; } + case kFillPath_Cmd: { + const FillPath& cp = fFillPaths[currFillPath]; + fDstGpu->executeFillPath(cp.fPath.get(), cp.fStroke, cp.fFill, + NULL != cp.fDstCopy.texture() ? &cp.fDstCopy : NULL); + ++currFillPath; + break; + } case kSetState_Cmd: fStates[currState].restoreTo(&playbackState); ++currState; @@ -810,6 +839,11 @@ GrInOrderDrawBuffer::StencilPath* GrInOrderDrawBuffer::recordStencilPath() { return &fStencilPaths.push_back(); } +GrInOrderDrawBuffer::FillPath* GrInOrderDrawBuffer::recordFillPath() { + fCmds.push_back(kFillPath_Cmd); + return &fFillPaths.push_back(); +} + GrInOrderDrawBuffer::Clear* GrInOrderDrawBuffer::recordClear() { fCmds.push_back(kClear_Cmd); return &fClears.push_back(); diff --git a/src/gpu/GrInOrderDrawBuffer.h b/src/gpu/GrInOrderDrawBuffer.h index ca6af3fe8b..b9e5ca8718 100644 --- a/src/gpu/GrInOrderDrawBuffer.h +++ b/src/gpu/GrInOrderDrawBuffer.h @@ -87,6 +87,7 @@ private: kSetClip_Cmd = 4, kClear_Cmd = 5, kCopySurface_Cmd = 6, + kFillPath_Cmd = 7, }; class DrawRecord : public DrawInfo { @@ -104,6 +105,15 @@ private: SkPath::FillType fFill; }; + struct FillPath : public ::SkNoncopyable { + FillPath(); + + SkAutoTUnref fPath; + SkStrokeRec fStroke; + SkPath::FillType fFill; + GrDeviceCoordTexture fDstCopy; + }; + struct Clear : public ::SkNoncopyable { Clear() : fRenderTarget(NULL) {} ~Clear() { SkSafeUnref(fRenderTarget); } @@ -127,6 +137,8 @@ private: const SkRect* localRect, const SkMatrix* localMatrix) SK_OVERRIDE; virtual void onStencilPath(const GrPath*, const SkStrokeRec& stroke, SkPath::FillType) SK_OVERRIDE; + virtual void onFillPath(const GrPath*, const SkStrokeRec& stroke, SkPath::FillType, + const GrDeviceCoordTexture* dstCopy) SK_OVERRIDE; virtual bool onReserveVertexSpace(size_t vertexSize, int vertexCount, void** vertices) SK_OVERRIDE; @@ -169,6 +181,7 @@ private: void recordClip(); DrawRecord* recordDraw(const DrawInfo&); StencilPath* recordStencilPath(); + FillPath* recordFillPath(); Clear* recordClear(); CopySurface* recordCopySurface(); @@ -177,6 +190,7 @@ private: kCmdPreallocCnt = 32, kDrawPreallocCnt = 8, kStencilPathPreallocCnt = 8, + kFillPathPreallocCnt = 8, kStatePreallocCnt = 8, kClipPreallocCnt = 8, kClearPreallocCnt = 4, @@ -187,6 +201,7 @@ private: SkSTArray fCmds; GrSTAllocator fDraws; GrSTAllocator fStencilPaths; + GrSTAllocator fFillPaths; GrSTAllocator fStates; GrSTAllocator fClears; GrSTAllocator fCopySurfaces; diff --git a/src/gpu/GrStencilAndCoverPathRenderer.cpp b/src/gpu/GrStencilAndCoverPathRenderer.cpp index f7330a81f6..ef3b953861 100644 --- a/src/gpu/GrStencilAndCoverPathRenderer.cpp +++ b/src/gpu/GrStencilAndCoverPathRenderer.cpp @@ -17,7 +17,7 @@ GrPathRenderer* GrStencilAndCoverPathRenderer::Create(GrContext* context) { SkASSERT(NULL != context); SkASSERT(NULL != context->getGpu()); - if (context->getGpu()->caps()->pathStencilingSupport()) { + if (context->getGpu()->caps()->pathRenderingSupport()) { return SkNEW_ARGS(GrStencilAndCoverPathRenderer, (context->getGpu())); } else { return NULL; @@ -25,7 +25,7 @@ GrPathRenderer* GrStencilAndCoverPathRenderer::Create(GrContext* context) { } GrStencilAndCoverPathRenderer::GrStencilAndCoverPathRenderer(GrGpu* gpu) { - SkASSERT(gpu->caps()->pathStencilingSupport()); + SkASSERT(gpu->caps()->pathRenderingSupport()); fGpu = gpu; gpu->ref(); } @@ -40,6 +40,7 @@ bool GrStencilAndCoverPathRenderer::canDrawPath(const SkPath& path, bool antiAlias) const { return stroke.isFillStyle() && !antiAlias && // doesn't do per-path AA, relies on the target having MSAA + NULL != target->getDrawState().getRenderTarget()->getStencilBuffer() && target->getDrawState().getStencil().isDisabled(); } @@ -70,27 +71,7 @@ bool GrStencilAndCoverPathRenderer::onDrawPath(const SkPath& path, SkAutoTUnref p(fGpu->createPath(path)); - SkPath::FillType nonInvertedFill = SkPath::ConvertToNonInverseFillType(path.getFillType()); - target->stencilPath(p, stroke, nonInvertedFill); - - // TODO: Use built in cover operation rather than a rect draw. This will require making our - // fragment shaders be able to eat varyings generated by a matrix. - - // fill the path, zero out the stencil - SkRect bounds = p->getBounds(); - SkScalar bloat = drawState->getViewMatrix().getMaxStretch() * SK_ScalarHalf; - GrDrawState::AutoViewMatrixRestore avmr; - - if (nonInvertedFill == path.getFillType()) { - GR_STATIC_CONST_SAME_STENCIL(kStencilPass, - kZero_StencilOp, - kZero_StencilOp, - kNotEqual_StencilFunc, - 0xffff, - 0x0000, - 0xffff); - *drawState->stencil() = kStencilPass; - } else { + if (path.isInverseFillType()) { GR_STATIC_CONST_SAME_STENCIL(kInvertedStencilPass, kZero_StencilOp, kZero_StencilOp, @@ -101,23 +82,22 @@ bool GrStencilAndCoverPathRenderer::onDrawPath(const SkPath& path, 0xffff, 0x0000, 0xffff); - SkMatrix vmi; - bounds.setLTRB(0, 0, - SkIntToScalar(drawState->getRenderTarget()->width()), - SkIntToScalar(drawState->getRenderTarget()->height())); - // mapRect through persp matrix may not be correct - if (!drawState->getViewMatrix().hasPerspective() && drawState->getViewInverse(&vmi)) { - vmi.mapRect(&bounds); - // theoretically could set bloat = 0, instead leave it because of matrix inversion - // precision. - } else { - avmr.setIdentity(drawState); - bloat = 0; - } + *drawState->stencil() = kInvertedStencilPass; + } else { + GR_STATIC_CONST_SAME_STENCIL(kStencilPass, + kZero_StencilOp, + kZero_StencilOp, + kNotEqual_StencilFunc, + 0xffff, + 0x0000, + 0xffff); + + *drawState->stencil() = kStencilPass; } - bounds.outset(bloat, bloat); - target->drawSimpleRect(bounds, NULL); + + target->fillPath(p, stroke, path.getFillType()); + target->drawState()->stencil()->setDisabled(); return true; } diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp index aac97361f8..17a4743b67 100644 --- a/src/gpu/gl/GrGLCaps.cpp +++ b/src/gpu/gl/GrGLCaps.cpp @@ -302,8 +302,8 @@ void GrGLCaps::init(const GrGLContextInfo& ctxInfo, const GrGLInterface* gli) { // attachment, hence this min: fMaxRenderTargetSize = GrMin(fMaxTextureSize, fMaxRenderTargetSize); - fPathStencilingSupport = GR_GL_USE_NV_PATH_RENDERING && - ctxInfo.hasExtension("GL_NV_path_rendering"); + fPathRenderingSupport = GR_GL_USE_NV_PATH_RENDERING && + ctxInfo.hasExtension("GL_NV_path_rendering"); fDstReadInShaderSupport = kNone_FBFetchType != fFBFetchType; diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp index 1ca61e3748..a69333f437 100644 --- a/src/gpu/gl/GrGLProgram.cpp +++ b/src/gpu/gl/GrGLProgram.cpp @@ -52,7 +52,7 @@ GrGLProgram::GrGLProgram(GrGpuGL* gpu, if (fDesc.getHeader().fHasVertexCode || !fGpu->glCaps().fixedFunctionSupport() || - !fGpu->glCaps().pathStencilingSupport()) { + !fGpu->glCaps().pathRenderingSupport()) { GrGLFullShaderBuilder fullBuilder(fGpu, fUniformManager, fDesc); if (this->genProgram(&fullBuilder, colorStages, coverageStages)) { diff --git a/src/gpu/gl/GrGLProgramDesc.cpp b/src/gpu/gl/GrGLProgramDesc.cpp index 8b731fbc2c..f9728ff556 100644 --- a/src/gpu/gl/GrGLProgramDesc.cpp +++ b/src/gpu/gl/GrGLProgramDesc.cpp @@ -122,11 +122,13 @@ void GrGLProgramDesc::Build(const GrDrawState& drawState, header->fExperimentalGS = false; #endif #endif + bool defaultToUniformInputs = GR_GL_NO_CONSTANT_ATTRIBUTES || gpu->caps()->pathRenderingSupport(); + if (colorIsTransBlack) { header->fColorInput = kTransBlack_ColorInput; } else if (colorIsSolidWhite) { header->fColorInput = kSolidWhite_ColorInput; - } else if (GR_GL_NO_CONSTANT_ATTRIBUTES && !requiresColorAttrib) { + } else if (defaultToUniformInputs && !requiresColorAttrib) { header->fColorInput = kUniform_ColorInput; } else { header->fColorInput = kAttribute_ColorInput; @@ -139,7 +141,7 @@ void GrGLProgramDesc::Build(const GrDrawState& drawState, header->fCoverageInput = kTransBlack_ColorInput; } else if (covIsSolidWhite) { header->fCoverageInput = kSolidWhite_ColorInput; - } else if (GR_GL_NO_CONSTANT_ATTRIBUTES && !requiresCoverageAttrib) { + } else if (defaultToUniformInputs && !requiresCoverageAttrib) { header->fCoverageInput = kUniform_ColorInput; } else { header->fCoverageInput = kAttribute_ColorInput; diff --git a/src/gpu/gl/GrGLShaderBuilder.cpp b/src/gpu/gl/GrGLShaderBuilder.cpp index 00fefe1305..eb7cfa72f4 100644 --- a/src/gpu/gl/GrGLShaderBuilder.cpp +++ b/src/gpu/gl/GrGLShaderBuilder.cpp @@ -930,7 +930,7 @@ GrGLFragmentOnlyShaderBuilder::GrGLFragmentOnlyShaderBuilder(GrGpuGL* gpu, SkASSERT(!desc.getHeader().fHasVertexCode); SkASSERT(gpu->glCaps().fixedFunctionSupport()); - SkASSERT(gpu->glCaps().pathStencilingSupport()); + SkASSERT(gpu->glCaps().pathRenderingSupport()); SkASSERT(GrGLProgramDesc::kAttribute_ColorInput != desc.getHeader().fColorInput); SkASSERT(GrGLProgramDesc::kAttribute_ColorInput != desc.getHeader().fCoverageInput); } diff --git a/src/gpu/gl/GrGpuGL.cpp b/src/gpu/gl/GrGpuGL.cpp index 18165d9ca3..113866759e 100644 --- a/src/gpu/gl/GrGpuGL.cpp +++ b/src/gpu/gl/GrGpuGL.cpp @@ -365,26 +365,30 @@ void GrGpuGL::onResetContext(uint32_t resetBits) { fHWBoundRenderTarget = NULL; } - if (resetBits & kFixedFunction_GrGLBackendState && this->glCaps().fixedFunctionSupport()) { + if (resetBits & (kFixedFunction_GrGLBackendState | kPathRendering_GrGLBackendState)) { + if (this->glCaps().fixedFunctionSupport()) { + fHWProjectionMatrixState.invalidate(); + // we don't use the model view matrix. + GL_CALL(MatrixMode(GR_GL_MODELVIEW)); + GL_CALL(LoadIdentity()); - fHWProjectionMatrixState.invalidate(); - // we don't use the model view matrix. - GL_CALL(MatrixMode(GR_GL_MODELVIEW)); - GL_CALL(LoadIdentity()); - - for (int i = 0; i < this->glCaps().maxFixedFunctionTextureCoords(); ++i) { - GL_CALL(ActiveTexture(GR_GL_TEXTURE0 + i)); - GL_CALL(Disable(GR_GL_TEXTURE_GEN_S)); - GL_CALL(Disable(GR_GL_TEXTURE_GEN_T)); - GL_CALL(Disable(GR_GL_TEXTURE_GEN_Q)); - GL_CALL(Disable(GR_GL_TEXTURE_GEN_R)); - if (this->caps()->pathStencilingSupport()) { - GL_CALL(PathTexGen(GR_GL_TEXTURE0 + i, GR_GL_NONE, 0, NULL)); + for (int i = 0; i < this->glCaps().maxFixedFunctionTextureCoords(); ++i) { + GL_CALL(ActiveTexture(GR_GL_TEXTURE0 + i)); + GL_CALL(Disable(GR_GL_TEXTURE_GEN_S)); + GL_CALL(Disable(GR_GL_TEXTURE_GEN_T)); + GL_CALL(Disable(GR_GL_TEXTURE_GEN_Q)); + GL_CALL(Disable(GR_GL_TEXTURE_GEN_R)); + if (this->caps()->pathRenderingSupport()) { + GL_CALL(PathTexGen(GR_GL_TEXTURE0 + i, GR_GL_NONE, 0, NULL)); + } + fHWTexGenSettings[i].fMode = GR_GL_NONE; + fHWTexGenSettings[i].fNumComponents = 0; } - fHWTexGenSettings[i].fMode = GR_GL_NONE; - fHWTexGenSettings[i].fNumComponents = 0; + fHWActiveTexGenSets = 0; + } + if (this->caps()->pathRenderingSupport()) { + fHWPathStencilSettings.invalidate(); } - fHWActiveTexGenSets = 0; } // we assume these values @@ -1273,7 +1277,7 @@ GrIndexBuffer* GrGpuGL::onCreateIndexBuffer(uint32_t size, bool dynamic) { } GrPath* GrGpuGL::onCreatePath(const SkPath& inPath) { - SkASSERT(this->caps()->pathStencilingSupport()); + SkASSERT(this->caps()->pathRenderingSupport()); return SkNEW_ARGS(GrGLPath, (this, inPath)); } @@ -1666,81 +1670,77 @@ void GrGpuGL::onGpuDraw(const DrawInfo& info) { #endif } -namespace { - -static const uint16_t kOnes16 = static_cast(~0); -const GrStencilSettings& winding_nv_path_stencil_settings() { - GR_STATIC_CONST_SAME_STENCIL_STRUCT(gSettings, - kIncClamp_StencilOp, - kIncClamp_StencilOp, - kAlwaysIfInClip_StencilFunc, - kOnes16, kOnes16, kOnes16); - return *GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(&gSettings); -} -const GrStencilSettings& even_odd_nv_path_stencil_settings() { - GR_STATIC_CONST_SAME_STENCIL_STRUCT(gSettings, - kInvert_StencilOp, - kInvert_StencilOp, - kAlwaysIfInClip_StencilFunc, - kOnes16, kOnes16, kOnes16); - return *GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(&gSettings); -} -} - -void GrGpuGL::setStencilPathSettings(const GrPath&, - SkPath::FillType fill, - GrStencilSettings* settings) { - switch (fill) { - case SkPath::kEvenOdd_FillType: - *settings = even_odd_nv_path_stencil_settings(); - return; - case SkPath::kWinding_FillType: - *settings = winding_nv_path_stencil_settings(); - return; +static GrGLenum gr_stencil_op_to_gl_path_rendering_fill_mode(GrStencilOp op) { + switch (op) { default: GrCrash("Unexpected path fill."); + /* fallthrough */; + case kIncClamp_StencilOp: + return GR_GL_COUNT_UP; + case kInvert_StencilOp: + return GR_GL_INVERT; } } void GrGpuGL::onGpuStencilPath(const GrPath* path, SkPath::FillType fill) { - SkASSERT(this->caps()->pathStencilingSupport()); + SkASSERT(this->caps()->pathRenderingSupport()); GrGLuint id = static_cast(path)->pathID(); - GrDrawState* drawState = this->drawState(); - SkASSERT(NULL != drawState->getRenderTarget()); - if (NULL == drawState->getRenderTarget()->getStencilBuffer()) { - return; - } + SkASSERT(NULL != this->drawState()->getRenderTarget()); + SkASSERT(NULL != this->drawState()->getRenderTarget()->getStencilBuffer()); + + flushPathStencilSettings(fill); // Decide how to manipulate the stencil buffer based on the fill rule. - // Also, assert that the stencil settings we set in setStencilPathSettings - // are present. - SkASSERT(!fStencilSettings.isTwoSided()); - GrGLenum fillMode; - switch (fill) { - case SkPath::kWinding_FillType: - fillMode = GR_GL_COUNT_UP; - SkASSERT(kIncClamp_StencilOp == - fStencilSettings.passOp(GrStencilSettings::kFront_Face)); - SkASSERT(kIncClamp_StencilOp == - fStencilSettings.failOp(GrStencilSettings::kFront_Face)); - break; - case SkPath::kEvenOdd_FillType: - fillMode = GR_GL_INVERT; - SkASSERT(kInvert_StencilOp == - fStencilSettings.passOp(GrStencilSettings::kFront_Face)); - SkASSERT(kInvert_StencilOp == - fStencilSettings.failOp(GrStencilSettings::kFront_Face)); - break; - default: - // Only the above two fill rules are allowed. - GrCrash("Unexpected path fill."); - return; // suppress unused var warning. - } - GrGLint writeMask = fStencilSettings.writeMask(GrStencilSettings::kFront_Face); + SkASSERT(!fHWPathStencilSettings.isTwoSided()); + + GrGLenum fillMode = + gr_stencil_op_to_gl_path_rendering_fill_mode(fHWPathStencilSettings.passOp(GrStencilSettings::kFront_Face)); + GrGLint writeMask = fHWPathStencilSettings.writeMask(GrStencilSettings::kFront_Face); GL_CALL(StencilFillPath(id, fillMode, writeMask)); } +void GrGpuGL::onGpuFillPath(const GrPath* path, SkPath::FillType fill) { + SkASSERT(this->caps()->pathRenderingSupport()); + + GrGLuint id = static_cast(path)->pathID(); + SkASSERT(NULL != this->drawState()->getRenderTarget()); + SkASSERT(NULL != this->drawState()->getRenderTarget()->getStencilBuffer()); + SkASSERT(!fCurrentProgram->hasVertexShader()); + + flushPathStencilSettings(fill); + + SkPath::FillType nonInvertedFill = SkPath::ConvertToNonInverseFillType(fill); + SkASSERT(!fHWPathStencilSettings.isTwoSided()); + GrGLenum fillMode = + gr_stencil_op_to_gl_path_rendering_fill_mode(fHWPathStencilSettings.passOp(GrStencilSettings::kFront_Face)); + GrGLint writeMask = fHWPathStencilSettings.writeMask(GrStencilSettings::kFront_Face); + GL_CALL(StencilFillPath(id, fillMode, writeMask)); + + if (nonInvertedFill == fill) { + GL_CALL(CoverFillPath(id, GR_GL_BOUNDING_BOX)); + } else { + GrDrawState* drawState = this->drawState(); + GrDrawState::AutoViewMatrixRestore avmr; + SkRect bounds = SkRect::MakeLTRB(0, 0, + SkIntToScalar(drawState->getRenderTarget()->width()), + SkIntToScalar(drawState->getRenderTarget()->height())); + SkMatrix vmi; + // mapRect through persp matrix may not be correct + if (!drawState->getViewMatrix().hasPerspective() && drawState->getViewInverse(&vmi)) { + vmi.mapRect(&bounds); + // theoretically could set bloat = 0, instead leave it because of matrix inversion + // precision. + SkScalar bloat = drawState->getViewMatrix().getMaxStretch() * SK_ScalarHalf; + bounds.outset(bloat, bloat); + } else { + avmr.setIdentity(drawState); + } + + this->drawSimpleRect(bounds, NULL); + } +} + void GrGpuGL::onResolveRenderTarget(GrRenderTarget* target) { GrGLRenderTarget* rt = static_cast(target); if (rt->needsResolve()) { @@ -1862,16 +1862,7 @@ void set_gl_stencil(const GrGLInterface* gl, } void GrGpuGL::flushStencil(DrawType type) { - if (kStencilPath_DrawType == type) { - SkASSERT(!fStencilSettings.isTwoSided()); - // Just the func, ref, and mask is set here. The op and write mask are params to the call - // that draws the path to the SB (glStencilFillPath) - GrGLenum func = - gr_to_gl_stencil_func(fStencilSettings.func(GrStencilSettings::kFront_Face)); - GL_CALL(PathStencilFunc(func, - fStencilSettings.funcRef(GrStencilSettings::kFront_Face), - fStencilSettings.funcMask(GrStencilSettings::kFront_Face))); - } else if (fHWStencilSettings != fStencilSettings) { + if (kStencilPath_DrawType != type && fHWStencilSettings != fStencilSettings) { if (fStencilSettings.isDisabled()) { if (kNo_TriState != fHWStencilTestEnabled) { GL_CALL(Disable(GR_GL_STENCIL_TEST)); @@ -1961,6 +1952,22 @@ void GrGpuGL::flushAAState(DrawType type) { } } +void GrGpuGL::flushPathStencilSettings(SkPath::FillType fill) { + GrStencilSettings pathStencilSettings; + this->getPathStencilSettingsForFillType(fill, &pathStencilSettings); + if (fHWPathStencilSettings != pathStencilSettings) { + // Just the func, ref, and mask is set here. The op and write mask are params to the call + // that draws the path to the SB (glStencilFillPath) + GrGLenum func = + gr_to_gl_stencil_func(pathStencilSettings.func(GrStencilSettings::kFront_Face)); + GL_CALL(PathStencilFunc(func, + pathStencilSettings.funcRef(GrStencilSettings::kFront_Face), + pathStencilSettings.funcMask(GrStencilSettings::kFront_Face))); + + fHWPathStencilSettings = pathStencilSettings; + } +} + void GrGpuGL::flushBlend(bool isLines, GrBlendCoeff srcCoeff, GrBlendCoeff dstCoeff) { @@ -2148,7 +2155,7 @@ void GrGpuGL::enableTexGen(int unitIdx, const GrGLfloat* coefficients) { SkASSERT(this->glCaps().fixedFunctionSupport()); - SkASSERT(this->caps()->pathStencilingSupport()); + SkASSERT(this->caps()->pathRenderingSupport()); SkASSERT(components >= kS_TexGenComponents && components <= kSTR_TexGenComponents); if (GR_GL_OBJECT_LINEAR == fHWTexGenSettings[unitIdx].fMode && diff --git a/src/gpu/gl/GrGpuGL.h b/src/gpu/gl/GrGpuGL.h index a59654a0f8..a945e54c99 100644 --- a/src/gpu/gl/GrGpuGL.h +++ b/src/gpu/gl/GrGpuGL.h @@ -151,11 +151,8 @@ private: virtual void onGpuDraw(const DrawInfo&) SK_OVERRIDE; - virtual void setStencilPathSettings(const GrPath&, - SkPath::FillType, - GrStencilSettings* settings) - SK_OVERRIDE; virtual void onGpuStencilPath(const GrPath*, SkPath::FillType) SK_OVERRIDE; + virtual void onGpuFillPath(const GrPath*, SkPath::FillType) SK_OVERRIDE; virtual void clearStencil() SK_OVERRIDE; virtual void clearStencilClip(const SkIRect& rect, @@ -245,6 +242,7 @@ private: void flushRenderTarget(const SkIRect* bound); void flushStencil(DrawType); void flushAAState(DrawType); + void flushPathStencilSettings(SkPath::FillType fill); bool configToGLFormats(GrPixelConfig config, bool getSizedInternal, @@ -434,6 +432,7 @@ private: GrStencilSettings fHWStencilSettings; TriState fHWStencilTestEnabled; + GrStencilSettings fHWPathStencilSettings; GrDrawState::DrawFace fHWDrawFace; TriState fHWWriteToColor; diff --git a/src/gpu/gl/GrGpuGL_program.cpp b/src/gpu/gl/GrGpuGL_program.cpp index 92eed8d9bb..f3b566c209 100644 --- a/src/gpu/gl/GrGpuGL_program.cpp +++ b/src/gpu/gl/GrGpuGL_program.cpp @@ -252,6 +252,9 @@ bool GrGpuGL::flushGraphicsState(DrawType type, const GrDeviceCoordTexture* dstC SkDEBUGFAIL("Failed to create program!"); return false; } + + SkASSERT(kFillPath_DrawType != type || !fCurrentProgram->hasVertexShader()); + fCurrentProgram.get()->ref(); GrGLuint programID = fCurrentProgram->programID();