Initial support for GL_NV_path_renering. Experimental, there are still some issues...
authorbsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 28 Jun 2012 18:48:06 +0000 (18:48 +0000)
committerbsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 28 Jun 2012 18:48:06 +0000 (18:48 +0000)
http://codereview.appspot.com/6349049/

git-svn-id: http://skia.googlecode.com/svn/trunk@4390 2bbb7eff-a529-9590-31e7-b0007b416f81

19 files changed:
gyp/common_variables.gypi
gyp/gpu.gyp
include/gpu/gl/GrGLConfig.h
src/gpu/GrAddPathRenderers_default.cpp
src/gpu/GrClipMaskManager.cpp
src/gpu/GrDrawState.h
src/gpu/GrDrawTarget.cpp
src/gpu/GrDrawTarget.h
src/gpu/GrGpu.cpp
src/gpu/GrGpu.h
src/gpu/GrInOrderDrawBuffer.cpp
src/gpu/GrInOrderDrawBuffer.h
src/gpu/GrPath.h
src/gpu/GrStencilAndCoverPathRenderer.cpp [new file with mode: 0644]
src/gpu/GrStencilAndCoverPathRenderer.h [new file with mode: 0644]
src/gpu/gl/GrGLPath.cpp
src/gpu/gl/GrGpuGL.cpp
src/gpu/gl/GrGpuGL.h
src/gpu/gl/GrGpuGL_program.cpp

index 69cd50962b972f9ff52e5d700cef3a13c35bfba4..ee00a342c5b70c363ba984beee31326276ccafab 100644 (file)
@@ -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)',
index 5aa3cb78f6f93cbb1784b43199d04ba12ac28bd0..381d6bcc5e4c8a371ce2a1d5f929399d4f5ca175 100644 (file)
         '../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',
         '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',
index 5932bb932ec5eedf3608cc7ae719298fcfeabf72..dd38283e259eed9c0da235ce203e496444ba8daa 100644 (file)
  * 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)
     #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
index 2c175e204295a1c9852cefa866ee21afddfcb495..32b2a07e2c1910ef9235e3cd04f31ebbb571ef10 100644 (file)
@@ -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)) {
index cb543e1979f28840d89d26fa957a3f81a877c102..30944d95bbb568ac7af948fa867bac22ee11dc50 100644 (file)
@@ -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
index 81960feb22945d9845cda2370af6fe69f9325893..91327704cfcec8849eb691e2c162d2a811ab6f02 100644 (file)
@@ -489,6 +489,7 @@ public:
             }
             fDrawState = ds;
         }
+        bool isSet() const { return NULL != fDrawState; }
     private:
         GrDrawState* fDrawState;
         GrMatrix fSavedMatrix;
index 64dc81527753a12278228cec53e3aec2d6eae4a6..745e4c947a73d26daa9b51f0aa9c11b89cd0390e 100644 (file)
@@ -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));
index d42a1f9bf8d62627af66c6264865ef4e8f311559..9b9be648328018994b5e9154c36f7cdec15fa7c5 100644 (file)
@@ -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
index 7807324e0f19eee84e27f57ceba9746a9cd8c9e8..71e341b31801cb4c99c9778b7a32de048483a9c5 100644 (file)
@@ -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<GrStencilSettings> asr(this->drawState()->stencil());
+
+    this->setStencilPathSettings(*path, fill, this->drawState()->stencil());
     if (!this->setupClipAndFlushState(kStencilPath_DrawType)) {
         return;
     }
index 83d20c6491d0fbf74541a86a651d912f56e8eeae..5c21b55edf01a721a2e5ff6ef33b4658447867d3 100644 (file)
@@ -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();
index 26be3610856613afcf762d0b567f632e2f02b995..18a25e6f85f8f9108808b97010395a1f958b09e3 100644 (file)
@@ -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();
index 61fa9e2a86df89d8029382ce3bd49528658861ab..72ebe4d084fe835fc7e0afd4e069ad71cc893913 100644 (file)
 #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<const GrPath>  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<kCmdPreallocCnt, uint8_t, true>           fCmds;
     GrSTAllocator<kDrawPreallocCnt, Draw>               fDraws;
+    GrSTAllocator<kStatePreallocCnt, StencilPath>       fStencilPaths;
     GrSTAllocator<kStatePreallocCnt, GrDrawState>       fStates;
     GrSTAllocator<kClearPreallocCnt, Clear>             fClears;
     GrSTAllocator<kClipPreallocCnt, GrClip>             fClips;
index 872859d982b00074e2de83b973dca1c41500ccbc..1404546b7369369f112628c4038b8204749afd3c 100644 (file)
@@ -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 (file)
index 0000000..40d998b
--- /dev/null
@@ -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<GrPath> 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<GrPath> 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 (file)
index 0000000..0204506
--- /dev/null
@@ -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
index e09d073ee2fad4cb332e1ca547c675139ff1dfec..1626bb08227f30c52356298353f541aa595c05b0 100644 (file)
@@ -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;
     }
 }
index 2662f8e0b52b6173adfcb59fbbff699d8994cf26..48ccd5daf78c04fd55f9d30a530a68a63a1989f3 100644 (file)
@@ -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<const GrGLPath*>(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;
index 6220efafe4cda74e8601056edf9c6553e9aeb605..2d6aa681202238d5a64e6ece949f8a75e06a1f57 100644 (file)
@@ -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;
 
index 3ad352070a82985bf92035cb9037205927d7bb96..e33f469f9350232f0f42d366c46bef58b6ce9c93 100644 (file)
@@ -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<const GrGLRenderTarget*>(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<const GrGLTexture*>(
-                        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<const GrGLTexture*>(
+                            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;