From: bsalomon@google.com Date: Thu, 28 Jun 2012 18:48:06 +0000 (+0000) Subject: Initial support for GL_NV_path_renering. Experimental, there are still some issues... X-Git-Tag: accepted/tizen/5.0/unified/20181102.025319~15760 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=ded4f4b163f5aa19c22c871178c55ecb34623846;p=platform%2Fupstream%2FlibSkiaSharp.git Initial support for GL_NV_path_renering. Experimental, there are still some issues to resolve, set gyp variable skia_nv_path_rendering=1 or build flag GR_GL_USE_NV_PATH_RENDERING to enable. http://codereview.appspot.com/6349049/ git-svn-id: http://skia.googlecode.com/svn/trunk@4390 2bbb7eff-a529-9590-31e7-b0007b416f81 --- diff --git a/gyp/common_variables.gypi b/gyp/common_variables.gypi index 69cd509..ee00a34 100644 --- a/gyp/common_variables.gypi +++ b/gyp/common_variables.gypi @@ -61,6 +61,7 @@ 'skia_scalar%': 'float', 'skia_mesa%': 0, + 'skia_nv_path_rendering%': 0, # Do not turn on 'skia_angle' - it is currently experimental 'skia_angle%': 0, 'skia_arch_type%': 'x86', @@ -73,6 +74,7 @@ 'os_posix%': '<(os_posix)', 'skia_scalar%': '<(skia_scalar)', 'skia_mesa%': '<(skia_mesa)', + 'skia_nv_path_rendering%': '<(skia_nv_path_rendering)', 'skia_angle%': '<(skia_angle)', 'skia_arch_type%': '<(skia_arch_type)', 'skia_arch_width%': '<(skia_arch_width)', diff --git a/gyp/gpu.gyp b/gyp/gpu.gyp index 5aa3cb7..381d6bc 100644 --- a/gyp/gpu.gyp +++ b/gyp/gpu.gyp @@ -264,6 +264,8 @@ '../src/gpu/GrResourceCache.h', '../src/gpu/GrStencil.cpp', '../src/gpu/GrStencil.h', + '../src/gpu/GrStencilAndCoverPathRenderer.cpp', + '../src/gpu/GrStencilAndCoverPathRenderer.h', '../src/gpu/GrStencilBuffer.cpp', '../src/gpu/GrStencilBuffer.h', '../src/gpu/GrStringBuilder.h', @@ -365,6 +367,11 @@ 'GR_IMPLEMENTATION=1', ], 'conditions': [ + [ 'skia_nv_path_rendering', { + 'defines': [ + 'GR_GL_USE_NV_PATH_RENDERING=1', + ], + }], [ 'skia_os == "linux"', { 'sources!': [ '../src/gpu/gl/GrGLDefaultInterface_none.cpp', diff --git a/include/gpu/gl/GrGLConfig.h b/include/gpu/gl/GrGLConfig.h index 5932bb9..dd38283 100644 --- a/include/gpu/gl/GrGLConfig.h +++ b/include/gpu/gl/GrGLConfig.h @@ -92,6 +92,10 @@ * check the first time we use a color format or a combination of color / * stencil formats as attachments. If the FBO is complete we will assume * subsequent attachments with the same formats are complete as well. + * + * GR_GL_USE_NV_PATH_RENDERING: Enable experimental support for + * GL_NV_path_rendering. There are known issues with clipping, non-AA paths, and + * perspective. */ #if !defined(GR_GL_LOG_CALLS) @@ -138,6 +142,10 @@ #define GR_GL_CHECK_FBO_STATUS_ONCE_PER_FORMAT 0 #endif +#if !defined(GR_GL_USE_NV_PATH_RENDERING) + #define GR_GL_USE_NV_PATH_RENDERING 0 +#endif + /** * There is a strange bug that occurs on Macs with NVIDIA GPUs. We don't * fully understand it. When (element) array buffers are continually diff --git a/src/gpu/GrAddPathRenderers_default.cpp b/src/gpu/GrAddPathRenderers_default.cpp index 2c175e2..32b2a07 100644 --- a/src/gpu/GrAddPathRenderers_default.cpp +++ b/src/gpu/GrAddPathRenderers_default.cpp @@ -6,7 +6,8 @@ * found in the LICENSE file. */ - + +#include "GrStencilAndCoverPathRenderer.h" #include "GrAAHairLinePathRenderer.h" #include "GrAAConvexPathRenderer.h" #include "GrSoftwarePathRenderer.h" @@ -14,6 +15,9 @@ void GrPathRenderer::AddPathRenderers(GrContext* ctx, GrPathRendererChain::UsageFlags flags, GrPathRendererChain* chain) { + if (GrPathRenderer* pr = GrStencilAndCoverPathRenderer::Create(ctx)) { + chain->addPathRenderer(pr)->unref(); + } if (!(GrPathRendererChain::kNonAAOnly_UsageFlag & flags)) { if (GrPathRenderer* pr = GrAAHairLinePathRenderer::Create(ctx)) { diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp index cb543e1..30944d9 100644 --- a/src/gpu/GrClipMaskManager.cpp +++ b/src/gpu/GrClipMaskManager.cpp @@ -687,9 +687,9 @@ bool GrClipMaskManager::createStencilClipMask(const GrClip& clipIn, bool clearToInside; SkRegion::Op startOp = SkRegion::kReplace_Op; // suppress warning int start = process_initial_clip_elements(clipCopy, - rtRect, - &clearToInside, - &startOp); + rtRect, + &clearToInside, + &startOp); fGpu->clearStencilClip(bounds, clearToInside); @@ -700,6 +700,14 @@ bool GrClipMaskManager::createStencilClipMask(const GrClip& clipIn, bool fillInverted; // enabled at bottom of loop drawState->disableState(GrGpu::kModifyStencilClip_StateBit); + // if the target is MSAA then we want MSAA enabled when the clip is soft + if (rt->isMultisampled()) { + if (clipCopy.getDoAA(c)) { + drawState->enableState(GrDrawState::kHWAntialias_StateBit); + } else { + drawState->disableState(GrDrawState::kHWAntialias_StateBit); + } + } bool canRenderDirectToStencil; // can the clip element be drawn // directly to the stencil buffer diff --git a/src/gpu/GrDrawState.h b/src/gpu/GrDrawState.h index 81960fe..9132770 100644 --- a/src/gpu/GrDrawState.h +++ b/src/gpu/GrDrawState.h @@ -489,6 +489,7 @@ public: } fDrawState = ds; } + bool isSet() const { return NULL != fDrawState; } private: GrDrawState* fDrawState; GrMatrix fSavedMatrix; diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp index 64dc815..745e4c9 100644 --- a/src/gpu/GrDrawTarget.cpp +++ b/src/gpu/GrDrawTarget.cpp @@ -818,8 +818,9 @@ void GrDrawTarget::drawNonIndexed(GrPrimitiveType type, } } -void GrDrawTarget::stencilPath(const GrPath& path, GrPathFill fill) { +void GrDrawTarget::stencilPath(const GrPath* path, GrPathFill fill) { // TODO: extract portions of checkDraw that are relevant to path stenciling. + GrAssert(NULL != path); GrAssert(fCaps.fPathStencilingSupport); GrAssert(kHairLine_GrPathFill != fill); GrAssert(!GrIsFillInverted(fill)); diff --git a/src/gpu/GrDrawTarget.h b/src/gpu/GrDrawTarget.h index d42a1f9..9b9be64 100644 --- a/src/gpu/GrDrawTarget.h +++ b/src/gpu/GrDrawTarget.h @@ -445,7 +445,7 @@ public: * winding (not inverse or hairline). It will respect the HW antialias flag * on the draw state (if possible in the 3D API). */ - void stencilPath(const GrPath& path, GrPathFill fill); + void stencilPath(const GrPath*, GrPathFill); /** * Helper function for drawing rects. This does not use the current index @@ -1003,7 +1003,7 @@ protected: virtual void onDrawNonIndexed(GrPrimitiveType type, int startVertex, int vertexCount) = 0; - virtual void onStencilPath(const GrPath& path, GrPathFill fill) = 0; + virtual void onStencilPath(const GrPath*, GrPathFill) = 0; // subclass overrides to be notified when clip is set. Must call // INHERITED::clipwillBeSet diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp index 7807324..71e341b 100644 --- a/src/gpu/GrGpu.cpp +++ b/src/gpu/GrGpu.cpp @@ -439,9 +439,13 @@ void GrGpu::onDrawNonIndexed(GrPrimitiveType type, this->onGpuDrawNonIndexed(type, sVertex, vertexCount); } -void GrGpu::onStencilPath(const GrPath& path, GrPathFill fill) { +void GrGpu::onStencilPath(const GrPath* path, GrPathFill fill) { this->handleDirtyContext(); + // TODO: make this more effecient (don't copy and copy back) + GrAutoTRestore asr(this->drawState()->stencil()); + + this->setStencilPathSettings(*path, fill, this->drawState()->stencil()); if (!this->setupClipAndFlushState(kStencilPath_DrawType)) { return; } diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h index 83d20c6..5c21b55 100644 --- a/src/gpu/GrGpu.h +++ b/src/gpu/GrGpu.h @@ -487,8 +487,15 @@ protected: virtual void onGpuDrawNonIndexed(GrPrimitiveType type, uint32_t vertexCount, uint32_t numVertices) = 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&, + GrPathFill, + GrStencilSettings* settings) = 0; // overridden by API-specific derived class to perform the path stenciling. - virtual void onGpuStencilPath(const GrPath& path, GrPathFill fill) = 0; + virtual void onGpuStencilPath(const GrPath*, GrPathFill) = 0; // overridden by API-specific derived class to perform flush virtual void onForceRenderTargetFlush() = 0; @@ -579,7 +586,7 @@ private: virtual void onDrawNonIndexed(GrPrimitiveType type, int startVertex, int vertexCount) SK_OVERRIDE; - virtual void onStencilPath(const GrPath& path, GrPathFill fill) SK_OVERRIDE; + virtual void onStencilPath(const GrPath* path, GrPathFill fill) SK_OVERRIDE; // readies the pools to provide vertex/index data. void prepareVertexPool(); diff --git a/src/gpu/GrInOrderDrawBuffer.cpp b/src/gpu/GrInOrderDrawBuffer.cpp index 26be361..18a25e6 100644 --- a/src/gpu/GrInOrderDrawBuffer.cpp +++ b/src/gpu/GrInOrderDrawBuffer.cpp @@ -8,12 +8,13 @@ #include "GrInOrderDrawBuffer.h" -#include "GrRenderTarget.h" -#include "GrTexture.h" #include "GrBufferAllocPool.h" +#include "GrGpu.h" #include "GrIndexBuffer.h" +#include "GrPath.h" +#include "GrRenderTarget.h" +#include "GrTexture.h" #include "GrVertexBuffer.h" -#include "GrGpu.h" GrInOrderDrawBuffer::GrInOrderDrawBuffer(const GrGpu* gpu, GrVertexBufferAllocPool* vertexPool, @@ -426,8 +427,18 @@ void GrInOrderDrawBuffer::onDrawNonIndexed(GrPrimitiveType primitiveType, draw->fIndexBuffer = NULL; } -void GrInOrderDrawBuffer::onStencilPath(const GrPath&, GrPathFill) { - GrCrash("Not implemented yet. Should not get here."); +void GrInOrderDrawBuffer::onStencilPath(const GrPath* path, GrPathFill fill) { + if (this->needsNewClip()) { + this->recordClip(); + } + // Only compare the subset of GrDrawState relevant to path stenciling? + if (this->needsNewState()) { + this->recordState(); + } + StencilPath* sp = this->recordStencilPath(); + sp->fPath.reset(path); + path->ref(); + sp->fFill = fill; } void GrInOrderDrawBuffer::clear(const GrIRect* rect, @@ -463,6 +474,7 @@ void GrInOrderDrawBuffer::reset() { } fCmds.reset(); fDraws.reset(); + fStencilPaths.reset(); fStates.reset(); fClears.reset(); fVertexPool.reset(); @@ -502,10 +514,11 @@ bool GrInOrderDrawBuffer::playback(GrDrawTarget* target) { GrDrawState* prevDrawState = target->drawState(); prevDrawState->ref(); - int currState = 0; - int currClip = 0; - int currClear = 0; - int currDraw = 0; + int currState = 0; + int currClip = 0; + int currClear = 0; + int currDraw = 0; + int currStencilPath = 0; for (int c = 0; c < numCmds; ++c) { switch (fCmds[c]) { @@ -530,6 +543,12 @@ bool GrInOrderDrawBuffer::playback(GrDrawTarget* target) { ++currDraw; break; } + case kStencilPath_Cmd: { + const StencilPath& sp = fStencilPaths[currStencilPath]; + target->stencilPath(sp.fPath.get(), sp.fFill); + ++currStencilPath; + break; + } case kSetState_Cmd: target->setDrawState(&fStates[currState]); ++currState; @@ -810,6 +829,11 @@ GrInOrderDrawBuffer::Draw* GrInOrderDrawBuffer::recordDraw() { return &fDraws.push_back(); } +GrInOrderDrawBuffer::StencilPath* GrInOrderDrawBuffer::recordStencilPath() { + fCmds.push_back(kStencilPath_Cmd); + return &fStencilPaths.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 61fa9e2..72ebe4d 100644 --- a/src/gpu/GrInOrderDrawBuffer.h +++ b/src/gpu/GrInOrderDrawBuffer.h @@ -14,8 +14,11 @@ #include "GrDrawTarget.h" #include "GrAllocPool.h" #include "GrAllocator.h" +#include "GrPath.h" #include "GrClip.h" +#include "SkTemplates.h" + class GrGpu; class GrIndexBufferAllocPool; class GrVertexBufferAllocPool; @@ -136,10 +139,11 @@ protected: int indexCount) SK_OVERRIDE; private: enum Cmd { - kDraw_Cmd = 1, - kSetState_Cmd = 2, - kSetClip_Cmd = 3, - kClear_Cmd = 4, + kDraw_Cmd = 1, + kStencilPath_Cmd = 2, + kSetState_Cmd = 3, + kSetClip_Cmd = 4, + kClear_Cmd = 5, }; struct Draw { @@ -153,6 +157,11 @@ private: const GrIndexBuffer* fIndexBuffer; }; + struct StencilPath { + SkAutoTUnref fPath; + GrPathFill fFill; + }; + struct Clear { Clear() : fRenderTarget(NULL) {} ~Clear() { GrSafeUnref(fRenderTarget); } @@ -171,7 +180,7 @@ private: virtual void onDrawNonIndexed(GrPrimitiveType primitiveType, int startVertex, int vertexCount) SK_OVERRIDE; - virtual void onStencilPath(const GrPath&, GrPathFill) SK_OVERRIDE; + virtual void onStencilPath(const GrPath*, GrPathFill) SK_OVERRIDE; virtual bool onReserveVertexSpace(GrVertexLayout layout, int vertexCount, void** vertices) SK_OVERRIDE; @@ -196,12 +205,13 @@ private: bool needsNewClip() const; // these functions record a command - void recordState(); - void recordDefaultState(); - void recordClip(); - void recordDefaultClip(); - Draw* recordDraw(); - Clear* recordClear(); + void recordState(); + void recordDefaultState(); + void recordClip(); + void recordDefaultClip(); + Draw* recordDraw(); + StencilPath* recordStencilPath(); + Clear* recordClear(); // call this to invalidate the tracking data that is used to concatenate // multiple draws into a single draw. @@ -210,6 +220,7 @@ private: enum { kCmdPreallocCnt = 32, kDrawPreallocCnt = 8, + kStencilPathPreallocCnt = 8, kStatePreallocCnt = 8, kClipPreallocCnt = 8, kClearPreallocCnt = 4, @@ -218,6 +229,7 @@ private: SkSTArray fCmds; GrSTAllocator fDraws; + GrSTAllocator fStencilPaths; GrSTAllocator fStates; GrSTAllocator fClears; GrSTAllocator fClips; diff --git a/src/gpu/GrPath.h b/src/gpu/GrPath.h index 872859d..1404546 100644 --- a/src/gpu/GrPath.h +++ b/src/gpu/GrPath.h @@ -16,10 +16,10 @@ class GrPath : public GrResource { public: GrPath(GrGpu* gpu) : INHERITED(gpu) {} - const GrIRect& getBounds() const { return fBounds; } + const GrRect& getBounds() const { return fBounds; } protected: - GrIRect fBounds; + GrRect fBounds; private: typedef GrResource INHERITED; diff --git a/src/gpu/GrStencilAndCoverPathRenderer.cpp b/src/gpu/GrStencilAndCoverPathRenderer.cpp new file mode 100644 index 0000000..40d998b --- /dev/null +++ b/src/gpu/GrStencilAndCoverPathRenderer.cpp @@ -0,0 +1,130 @@ + +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + +#include "GrStencilAndCoverPathRenderer.h" +#include "GrContext.h" +#include "GrGpu.h" +#include "GrPath.h" + +GrPathRenderer* GrStencilAndCoverPathRenderer::Create(GrContext* context) { + GrAssert(NULL != context); + GrAssert(NULL != context->getGpu()); + if (context->getGpu()->getCaps().fPathStencilingSupport) { + return new GrStencilAndCoverPathRenderer(context->getGpu()); + } else { + return NULL; + } +} + +GrStencilAndCoverPathRenderer::GrStencilAndCoverPathRenderer(GrGpu* gpu) { + GrAssert(gpu->getCaps().fPathStencilingSupport); + fGpu = gpu; + gpu->ref(); +} + +GrStencilAndCoverPathRenderer::~GrStencilAndCoverPathRenderer() { + fGpu->unref(); +} + +bool GrStencilAndCoverPathRenderer::canDrawPath(const SkPath& path, + GrPathFill fill, + const GrDrawTarget* target, + bool antiAlias) const { + return kHairLine_GrPathFill != fill && + !antiAlias && // doesn't do per-path AA, relies on the target having MSAA + target->getDrawState().getStencil().isDisabled(); +} + +bool GrStencilAndCoverPathRenderer::requiresStencilPass(const SkPath& path, + GrPathFill fill, + const GrDrawTarget* target) const { + return true; +} + +void GrStencilAndCoverPathRenderer::drawPathToStencil(const SkPath& path, + GrPathFill fill, + GrDrawTarget* target) { + GrAssert(kEvenOdd_GrPathFill == fill || kWinding_GrPathFill == fill); + SkAutoTUnref p(fGpu->createPath(path)); + target->stencilPath(p, fill); +} + +bool GrStencilAndCoverPathRenderer::onDrawPath(const SkPath& path, + GrPathFill fill, + const GrVec* translate, + GrDrawTarget* target, + GrDrawState::StageMask stageMask, + bool antiAlias){ + GrAssert(!antiAlias); + GrAssert(kHairLine_GrPathFill != fill); + + GrDrawState* drawState = target->drawState(); + GrAssert(drawState->getStencil().isDisabled()); + + SkAutoTUnref p(fGpu->createPath(path)); + GrDrawState::AutoViewMatrixRestore avmr; + if (translate) { + avmr.set(drawState); + drawState->viewMatrix()->postTranslate(translate->fX, translate->fY); + } + GrPathFill nonInvertedFill = GrNonInvertedFill(fill); + target->stencilPath(p, 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 + GrRect bounds = p->getBounds(); + GrScalar bloat = drawState->getViewMatrix().getMaxStretch() * GR_ScalarHalf; + if (nonInvertedFill == fill) { + GR_STATIC_CONST_SAME_STENCIL(kStencilPass, + kZero_StencilOp, + kZero_StencilOp, + kNotEqual_StencilFunc, + 0xffff, + 0x0000, + 0xffff); + *drawState->stencil() = kStencilPass; + } else { + GR_STATIC_CONST_SAME_STENCIL(kInvertedStencilPass, + kZero_StencilOp, + kZero_StencilOp, + kEqual_StencilFunc, + 0xffff, + 0x0000, + 0xffff); + GrMatrix vmi; + bounds.setLTRB(0, 0, + GrIntToScalar(drawState->getRenderTarget()->width()), + GrIntToScalar(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 { + if (stageMask) { + if (!drawState->getViewInverse(&vmi)) { + GrPrintf("Could not invert matrix."); + return false; + } + drawState->preConcatSamplerMatrices(stageMask, vmi); + } + if (avmr.isSet()) { + avmr.set(drawState); + } + drawState->viewMatrix()->reset(); + bloat = 0; + } + *drawState->stencil() = kInvertedStencilPass; + } + bounds.outset(bloat, bloat); + target->drawSimpleRect(bounds, NULL, stageMask); + target->drawState()->stencil()->setDisabled(); + return true; +} diff --git a/src/gpu/GrStencilAndCoverPathRenderer.h b/src/gpu/GrStencilAndCoverPathRenderer.h new file mode 100644 index 0000000..0204506 --- /dev/null +++ b/src/gpu/GrStencilAndCoverPathRenderer.h @@ -0,0 +1,57 @@ + +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrBuiltInPathRenderer_DEFINED +#define GrBuiltInPathRenderer_DEFINED + +#include "GrPathRenderer.h" + +class GrContext; +class GrGpu; + +/** + * Uses GrGpu::stencilPath followed by a cover rectangle. This subclass doesn't apply AA; it relies + * on the target having MSAA if AA is desired. + */ +class GrStencilAndCoverPathRenderer : public GrPathRenderer { +public: + + static GrPathRenderer* Create(GrContext* context); + + virtual ~GrStencilAndCoverPathRenderer(); + + virtual bool canDrawPath(const SkPath& path, + GrPathFill fill, + const GrDrawTarget* target, + bool antiAlias) const SK_OVERRIDE; + + virtual bool requiresStencilPass(const SkPath& path, + GrPathFill fill, + const GrDrawTarget* target) const SK_OVERRIDE; + + virtual void drawPathToStencil(const SkPath& path, + GrPathFill fill, + GrDrawTarget* target) SK_OVERRIDE; + +protected: + virtual bool onDrawPath(const SkPath& path, + GrPathFill fill, + const GrVec* translate, + GrDrawTarget* target, + GrDrawState::StageMask stageMask, + bool antiAlias) SK_OVERRIDE; + +private: + GrStencilAndCoverPathRenderer(GrGpu* gpu); + + GrGpu* fGpu; + + typedef GrPathRenderer INHERITED; +}; + +#endif diff --git a/src/gpu/gl/GrGLPath.cpp b/src/gpu/gl/GrGLPath.cpp index e09d073..1626bb0 100644 --- a/src/gpu/gl/GrGLPath.cpp +++ b/src/gpu/gl/GrGLPath.cpp @@ -54,11 +54,12 @@ inline int num_pts(const SkPath::Verb verb) { GrGLPath::GrGLPath(GrGpuGL* gpu, const SkPath& path) : INHERITED(gpu) { GL_CALL_RET(fPathID, GenPaths(1)); + //GrPrintf("\tGenPaths ID: %d\n", fPathID); SkPath::Iter iter(path, true); SkSTArray<16, GrGLubyte, true> pathCommands; #ifndef SK_SCALAR_IS_FLOAT - GrCrash("Expected scalar is float."); + GR_STATIC_ASSERT(false); #endif SkSTArray<16, SkPoint, true> pathPoints; @@ -82,8 +83,8 @@ GrGLPath::GrGLPath(GrGpuGL* gpu, const SkPath& path) : INHERITED(gpu) { GL_CALL(PathCommands(fPathID, verbCnt, &pathCommands[0], 2 * pointCnt, GR_GL_FLOAT, &pathPoints[0])); - GrRect bounds = path.getBounds(); - bounds.roundOut(&fBounds); + //GrPrintf("\tPathCommands ID: %d\n", fPathID); + fBounds = path.getBounds(); } GrGLPath::~GrGLPath() { @@ -92,7 +93,27 @@ GrGLPath::~GrGLPath() { void GrGLPath::onRelease() { if (0 != fPathID) { + // FIXME: When we draw a clipped path we may get a call sequence that looks + // like this: + // GenPaths(1, &fPathID); // fPathID = 1, the path to draw + // PathCommands(1, ...); + // GenPaths(1, &fPathID); // fPathID = 2, the clip path + // PathCommands(2, ...); + // PathStencilFunc(...); + // StencilFillPath(2, ...); // draw the clip + // DeletePath(1, &fPathID); // fPathID == 2 + // PathStencilFunc(...); + // StencilFillPath(2, ...); // draw the path + // DeletePath(1, &fPathID); // fPathID == 1 + // + // Deleting the clip path causes the second StencilFillPath to fail with + // INVALID_OPERATION. +#if 0 GL_CALL(DeletePaths(1, fPathID)); + //GrPrintf("\tDeletePaths ID: %d\n", fPathID); +#else + //GrPrintf("\tLeak Path ID: %d\n", fPathID); +#endif fPathID = 0; } } diff --git a/src/gpu/gl/GrGpuGL.cpp b/src/gpu/gl/GrGpuGL.cpp index 2662f8e..48ccd5d 100644 --- a/src/gpu/gl/GrGpuGL.cpp +++ b/src/gpu/gl/GrGpuGL.cpp @@ -268,6 +268,8 @@ void GrGpuGL::initCaps() { fCaps.fMaxRenderTargetSize = GrMin(fCaps.fMaxTextureSize, fCaps.fMaxRenderTargetSize); fCaps.fFSAASupport = GrGLCaps::kNone_MSFBOType != this->glCaps().msFBOType(); + fCaps.fPathStencilingSupport = GR_GL_USE_NV_PATH_RENDERING && + this->hasExtension("GL_NV_path_rendering"); // Enable supported shader-related caps if (kDesktop_GrGLBinding == this->glBinding()) { @@ -511,6 +513,8 @@ void GrGpuGL::onResetContext() { fHWBoundRenderTarget = NULL; + fHWPathMatrixState.invalidate(); + // we assume these values if (this->glCaps().unpackRowLengthSupport()) { GL_CALL(PixelStorei(GR_GL_UNPACK_ROW_LENGTH, 0)); @@ -1322,11 +1326,8 @@ GrIndexBuffer* GrGpuGL::onCreateIndexBuffer(uint32_t size, bool dynamic) { } GrPath* GrGpuGL::onCreatePath(const SkPath& inPath) { - GrPath* path = NULL; - if (fCaps.fPathStencilingSupport) { - path = new GrGLPath(this, inPath); - } - return path; + GrAssert(fCaps.fPathStencilingSupport); + return new GrGLPath(this, inPath); } void GrGpuGL::flushScissor() { @@ -1735,8 +1736,78 @@ void GrGpuGL::onGpuDrawNonIndexed(GrPrimitiveType type, #endif } -void GrGpuGL::onGpuStencilPath(const GrPath&, GrPathFill) { - GrCrash("Not implemented yet. Should not get here."); +namespace { +const GrStencilSettings& winding_nv_path_stencil_settings() { + GR_STATIC_CONST_SAME_STENCIL_STRUCT(gSettings, + kIncClamp_StencilOp, + kIncClamp_StencilOp, + kAlwaysIfInClip_StencilFunc, + ~0, ~0, ~0); + 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, + ~0, ~0, ~0); + return *GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(&gSettings); +} +} + + +void GrGpuGL::setStencilPathSettings(const GrPath&, + GrPathFill fill, + GrStencilSettings* settings) { + switch (fill) { + case kEvenOdd_GrPathFill: + *settings = even_odd_nv_path_stencil_settings(); + return; + case kWinding_GrPathFill: + *settings = winding_nv_path_stencil_settings(); + return; + default: + GrCrash("Unexpected path fill."); + } +} + +void GrGpuGL::onGpuStencilPath(const GrPath* path, GrPathFill fill) { + GrAssert(fCaps.fPathStencilingSupport); + + GrGLuint id = static_cast(path)->pathID(); + GrDrawState* drawState = this->drawState(); + GrAssert(NULL != drawState->getRenderTarget()); + if (NULL == drawState->getRenderTarget()->getStencilBuffer()) { + return; + } + + // Decide how to manipulate the stencil buffer based on the fill rule. + // Also, assert that the stencil settings we set in setStencilPathSettings + // are present. + GrAssert(!fStencilSettings.isTwoSided()); + GrGLenum fillMode; + switch (fill) { + case kWinding_GrPathFill: + fillMode = GR_GL_COUNT_UP; + GrAssert(kIncClamp_StencilOp == + fStencilSettings.passOp(GrStencilSettings::kFront_Face)); + GrAssert(kIncClamp_StencilOp == + fStencilSettings.failOp(GrStencilSettings::kFront_Face)); + break; + case kEvenOdd_GrPathFill: + fillMode = GR_GL_INVERT; + GrAssert(kInvert_StencilOp == + fStencilSettings.passOp(GrStencilSettings::kFront_Face)); + GrAssert(kInvert_StencilOp == + fStencilSettings.failOp(GrStencilSettings::kFront_Face)); + break; + default: + // Only the above two fill rules are allowed. + GrCrash("Unexpected path fill."); + } + GrGLint writeMask = fStencilSettings.writeMask(GrStencilSettings::kFront_Face); + GL_CALL(StencilFillPath(id, fillMode, writeMask)); + //GrPrintf("\tStencilFillPath ID: %d\n", id); } void GrGpuGL::onResolveRenderTarget(GrRenderTarget* target) { @@ -1863,19 +1934,28 @@ void set_gl_stencil(const GrGLInterface* gl, } } -void GrGpuGL::flushStencil() { - if (fStencilSettings.isDisabled()) { - if (kNo_TriState != fHWStencilTestEnabled) { - GL_CALL(Disable(GR_GL_STENCIL_TEST)); - fHWStencilTestEnabled = kNo_TriState; - } - } else { - if (kYes_TriState != fHWStencilTestEnabled) { - GL_CALL(Enable(GR_GL_STENCIL_TEST)); - fHWStencilTestEnabled = kYes_TriState; +void GrGpuGL::flushStencil(DrawType type) { + if (kStencilPath_DrawType == type) { + GrAssert(!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 (fStencilSettings.isDisabled()) { + if (kNo_TriState != fHWStencilTestEnabled) { + GL_CALL(Disable(GR_GL_STENCIL_TEST)); + fHWStencilTestEnabled = kNo_TriState; + } + } else { + if (kYes_TriState != fHWStencilTestEnabled) { + GL_CALL(Enable(GR_GL_STENCIL_TEST)); + fHWStencilTestEnabled = kYes_TriState; + } } - } - if (fHWStencilSettings != fStencilSettings) { if (!fStencilSettings.isDisabled()) { if (this->getCaps().fTwoSidedStencilSupport) { set_gl_stencil(this->glInterface(), @@ -1897,7 +1977,7 @@ void GrGpuGL::flushStencil() { } } -void GrGpuGL::flushAAState(bool isLines) { +void GrGpuGL::flushAAState(DrawType type) { const GrRenderTarget* rt = this->getDrawState().getRenderTarget(); if (kDesktop_GrGLBinding == this->glBinding()) { // ES doesn't support toggling GL_MULTISAMPLE and doesn't have @@ -1905,7 +1985,7 @@ void GrGpuGL::flushAAState(bool isLines) { // we prefer smooth lines over multisampled lines bool smoothLines = false; - if (isLines) { + if (kDrawLines_DrawType == type) { smoothLines = this->willUseHWAALines(); if (smoothLines) { if (kYes_TriState != fHWAAState.fSmoothLineEnabled) { @@ -1925,8 +2005,12 @@ void GrGpuGL::flushAAState(bool isLines) { } } } - if (!smoothLines && rt->isMultisampled()) { - if (this->getDrawState().isHWAntialiasState()) { + if (!smoothLines && rt->isMultisampled()) { + // FIXME: GL_NV_pr doesn't seem to like MSAA disabled. The paths + // convex hulls of each segment appear to get filled. + bool enableMSAA = kStencilPath_DrawType == type || + this->getDrawState().isHWAntialiasState(); + if (enableMSAA) { if (kYes_TriState != fHWAAState.fMSAAEnabled) { GL_CALL(Enable(GR_GL_MULTISAMPLE)); fHWAAState.fMSAAEnabled = kYes_TriState; diff --git a/src/gpu/gl/GrGpuGL.h b/src/gpu/gl/GrGpuGL.h index 6220efa..2d6aa68 100644 --- a/src/gpu/gl/GrGpuGL.h +++ b/src/gpu/gl/GrGpuGL.h @@ -98,7 +98,6 @@ protected: virtual void onResolveRenderTarget(GrRenderTarget* target) SK_OVERRIDE; - virtual void onGpuDrawIndexed(GrPrimitiveType type, uint32_t startVertex, uint32_t startIndex, @@ -107,7 +106,12 @@ protected: virtual void onGpuDrawNonIndexed(GrPrimitiveType type, uint32_t vertexCount, uint32_t numVertices) SK_OVERRIDE; - virtual void onGpuStencilPath(const GrPath&, GrPathFill) SK_OVERRIDE; + + virtual void setStencilPathSettings(const GrPath&, + GrPathFill, + GrStencilSettings* settings) + SK_OVERRIDE; + virtual void onGpuStencilPath(const GrPath*, GrPathFill) SK_OVERRIDE; virtual void clearStencil() SK_OVERRIDE; virtual void clearStencilClip(const GrIRect& rect, @@ -220,7 +224,7 @@ private: void flushCoverage(GrColor color); // sets the MVP matrix uniform for currently bound program - void flushViewMatrix(); + void flushViewMatrix(DrawType type); // flushes the parameters to two point radial gradient void flushRadial2(int stage); @@ -268,8 +272,8 @@ private: // bound is region that may be modified and therefore has to be resolved. // NULL means whole target. Can be an empty rect. void flushRenderTarget(const GrIRect* bound); - void flushStencil(); - void flushAAState(bool isLines); + void flushStencil(DrawType); + void flushAAState(DrawType); bool configToGLFormats(GrPixelConfig config, bool getSizedInternal, @@ -360,6 +364,15 @@ private: } } fHWAAState; + struct { + GrMatrix fViewMatrix; + SkISize fRTSize; + void invalidate() { + fViewMatrix = GrMatrix::InvalidMatrix(); + fRTSize.fWidth = -1; // just make the first value compared illegal. + } + } fHWPathMatrixState; + GrStencilSettings fHWStencilSettings; TriState fHWStencilTestEnabled; diff --git a/src/gpu/gl/GrGpuGL_program.cpp b/src/gpu/gl/GrGpuGL_program.cpp index 3ad3520..e33f469 100644 --- a/src/gpu/gl/GrGpuGL_program.cpp +++ b/src/gpu/gl/GrGpuGL_program.cpp @@ -104,7 +104,7 @@ void GrGpuGL::abandonResources(){ #define GL_CALL(X) GR_GL_CALL(this->glInterface(), X) -void GrGpuGL::flushViewMatrix() { +void GrGpuGL::flushViewMatrix(DrawType type) { const GrGLRenderTarget* rt = static_cast(this->getDrawState().getRenderTarget()); SkISize viewportSize; const GrGLIRect& viewport = rt->getViewport(); @@ -112,7 +112,56 @@ void GrGpuGL::flushViewMatrix() { const GrMatrix& vm = this->getDrawState().getViewMatrix(); - if (!fProgramData->fViewMatrix.cheapEqualTo(vm) || + if (kStencilPath_DrawType == type) { + if (fHWPathMatrixState.fViewMatrix != vm) { + // We use the GL model view matrix to hold the draw state's view + // matrix and the GL projection matrix to convert to normalized y-up + // coords. + GrGLfloat mv[] = { + // col 0 + GrScalarToFloat(vm[GrMatrix::kMScaleX]), + GrScalarToFloat(vm[GrMatrix::kMSkewY]), + 0, + GrScalarToFloat(vm[GrMatrix::kMPersp0]), + + // col 1 + GrScalarToFloat(vm[GrMatrix::kMSkewX]), + GrScalarToFloat(vm[GrMatrix::kMScaleY]), + 0, + GrScalarToFloat(vm[GrMatrix::kMPersp1]), + + // col 2 + 0, 0, 0, 0, + + // col3 + GrScalarToFloat(vm[GrMatrix::kMTransX]), + GrScalarToFloat(vm[GrMatrix::kMTransY]), + 0.5f, + GrScalarToFloat(vm[GrMatrix::kMPersp2]) + }; + GL_CALL(MatrixMode(GR_GL_MODELVIEW)); + GL_CALL(LoadMatrixf(mv)); + fHWPathMatrixState.fViewMatrix = vm; + } + if (fHWPathMatrixState.fRTSize != viewportSize) { + GrGLfloat p[] = { + // col 0 + 2.f / rt->width(), 0, 0, 0, + + // col 1 + 0, -2.f / rt->height(), 0, 0, + + // col 2 + 0, 0, 1.f, 0, + + // col 3 + -1.f, 1.f, 0, 1.f, + }; + GL_CALL(MatrixMode(GR_GL_PROJECTION)); + GL_CALL(LoadMatrixf(p)); + fHWPathMatrixState.fRTSize = viewportSize; + } + } else if (!fProgramData->fViewMatrix.cheapEqualTo(vm) || fProgramData->fViewportSize != viewportSize) { GrMatrix m; @@ -380,81 +429,79 @@ bool GrGpuGL::flushGraphicsState(DrawType type) { // and bailed if not true. GrAssert(NULL != drawState.getRenderTarget()); - this->flushMiscFixedFunctionState(); - this->flushStencil(); - this->flushAAState(kDrawLines_DrawType == type); - - GrBlendCoeff srcCoeff; - GrBlendCoeff dstCoeff; - BlendOptFlags blendOpts = this->getBlendOpts(false, &srcCoeff, &dstCoeff); - if (kSkipDraw_BlendOptFlag & blendOpts) { - return false; - } - - GrCustomStage* customStages [GrDrawState::kNumStages]; - this->buildProgram(kDrawPoints_DrawType == type, - blendOpts, dstCoeff, customStages); - fProgramData = fProgramCache->getProgramData(fCurrentProgram, - customStages); - if (NULL == fProgramData) { - GrAssert(!"Failed to create program!"); - return false; - } - - if (fHWProgramID != fProgramData->fProgramID) { - GL_CALL(UseProgram(fProgramData->fProgramID)); - fHWProgramID = fProgramData->fProgramID; - } - fCurrentProgram.overrideBlend(&srcCoeff, &dstCoeff); - this->flushBlend(kDrawLines_DrawType == type, srcCoeff, dstCoeff); - - GrColor color; - GrColor coverage; - if (blendOpts & kEmitTransBlack_BlendOptFlag) { - color = 0; - coverage = 0; - } else if (blendOpts & kEmitCoverage_BlendOptFlag) { - color = 0xffffffff; - coverage = drawState.getCoverage(); - } else { - color = drawState.getColor(); - coverage = drawState.getCoverage(); - } - this->flushColor(color); - this->flushCoverage(coverage); + if (kStencilPath_DrawType != type) { + this->flushMiscFixedFunctionState(); - this->flushViewMatrix(); + GrBlendCoeff srcCoeff; + GrBlendCoeff dstCoeff; + BlendOptFlags blendOpts = this->getBlendOpts(false, &srcCoeff, &dstCoeff); + if (kSkipDraw_BlendOptFlag & blendOpts) { + return false; + } - for (int s = 0; s < GrDrawState::kNumStages; ++s) { - if (this->isStageEnabled(s)) { + GrCustomStage* customStages [GrDrawState::kNumStages]; + this->buildProgram(kDrawPoints_DrawType == type, + blendOpts, dstCoeff, customStages); + fProgramData = fProgramCache->getProgramData(fCurrentProgram, + customStages); + if (NULL == fProgramData) { + GrAssert(!"Failed to create program!"); + return false; + } + if (fHWProgramID != fProgramData->fProgramID) { + GL_CALL(UseProgram(fProgramData->fProgramID)); + fHWProgramID = fProgramData->fProgramID; + } + fCurrentProgram.overrideBlend(&srcCoeff, &dstCoeff); + this->flushBlend(kDrawLines_DrawType == type, srcCoeff, dstCoeff); + + GrColor color; + GrColor coverage; + if (blendOpts & kEmitTransBlack_BlendOptFlag) { + color = 0; + coverage = 0; + } else if (blendOpts & kEmitCoverage_BlendOptFlag) { + color = 0xffffffff; + coverage = drawState.getCoverage(); + } else { + color = drawState.getColor(); + coverage = drawState.getCoverage(); + } + this->flushColor(color); + this->flushCoverage(coverage); + for (int s = 0; s < GrDrawState::kNumStages; ++s) { + if (this->isStageEnabled(s)) { #if GR_DEBUG - // check for circular rendering - GrAssert(NULL == drawState.getRenderTarget() || - NULL == drawState.getTexture(s) || - drawState.getTexture(s)->asRenderTarget() != - drawState.getRenderTarget()); + // check for circular rendering + GrAssert(NULL == drawState.getRenderTarget() || + NULL == drawState.getTexture(s) || + drawState.getTexture(s)->asRenderTarget() != + drawState.getRenderTarget()); #endif - - this->flushBoundTextureAndParams(s); - - this->flushTextureMatrixAndDomain(s); - - if (NULL != fProgramData->fCustomStage[s]) { - const GrSamplerState& sampler = - this->getDrawState().getSampler(s); - const GrGLTexture* texture = - static_cast( - this->getDrawState().getTexture(s)); - fProgramData->fCustomStage[s]->setData( - this->glInterface(), *texture, - *sampler.getCustomStage(), s); + this->flushBoundTextureAndParams(s); + + this->flushTextureMatrixAndDomain(s); + + if (NULL != fProgramData->fCustomStage[s]) { + const GrSamplerState& sampler = + this->getDrawState().getSampler(s); + const GrGLTexture* texture = + static_cast( + this->getDrawState().getTexture(s)); + fProgramData->fCustomStage[s]->setData( + this->glInterface(), *texture, + *sampler.getCustomStage(), s); + } } } + this->flushColorMatrix(); } - this->flushColorMatrix(); + this->flushStencil(type); + this->flushViewMatrix(type); this->flushScissor(); + this->flushAAState(type); GrIRect* rect = NULL; GrIRect clipBounds;