Improve caching of dashed paths in GrStencilAndCoverPathRenderer
authorkkinnunen <kkinnunen@nvidia.com>
Tue, 19 May 2015 06:02:07 +0000 (23:02 -0700)
committerCommit bot <commit-bot@chromium.org>
Tue, 19 May 2015 06:02:07 +0000 (23:02 -0700)
Improve caching of dashed paths in GrStencilAndCoverPathRenderer.
Look up the (NVPR specific) GrGLPath based on GrStrokeInfo and
the original path.

Use unique keys for all GrPaths.

Dash the path with Skia dash stroker and use that path geometry for
NVPR path.

NVPR internal dashing stroke is not used, because the dashing
implementation of NVPR does not match Skia implementation.

Review URL: https://codereview.chromium.org/1116123003

17 files changed:
src/gpu/GrPath.cpp
src/gpu/GrPath.h
src/gpu/GrPathRange.cpp
src/gpu/GrPathRange.h
src/gpu/GrPathRendering.cpp
src/gpu/GrPathRendering.h
src/gpu/GrStencilAndCoverPathRenderer.cpp
src/gpu/GrStencilAndCoverTextContext.cpp
src/gpu/GrStencilAndCoverTextContext.h
src/gpu/GrStrokeInfo.cpp
src/gpu/GrStrokeInfo.h
src/gpu/gl/GrGLPath.cpp
src/gpu/gl/GrGLPath.h
src/gpu/gl/GrGLPathRange.cpp
src/gpu/gl/GrGLPathRange.h
src/gpu/gl/GrGLPathRendering.cpp
src/gpu/gl/GrGLPathRendering.h

index 3a66865..e76bdf2 100644 (file)
@@ -7,49 +7,14 @@
 
 #include "GrPath.h"
 
-template<int NumBits> static uint64_t get_top_n_float_bits(float f) {
-    char* floatData = reinterpret_cast<char*>(&f);
-    uint32_t floatBits = *reinterpret_cast<uint32_t*>(floatData);
-    return floatBits >> (32 - NumBits);
-}
-
-void GrPath::ComputeKey(const SkPath& path, const SkStrokeRec& stroke, GrUniqueKey* key) {
-    static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
-    GrUniqueKey::Builder builder(key, kDomain, 3);
-    *reinterpret_cast<uint64_t*>(&builder[0]) = ComputeStrokeKey(stroke);
-    builder[2] = path.getGenerationID();
-}
-
-uint64_t GrPath::ComputeStrokeKey(const SkStrokeRec& stroke) {
-    enum {
-        kStyleBits = 2,
-        kJoinBits = 2,
-        kCapBits = 2,
-        kWidthBits = 29,
-        kMiterBits = 29,
-
-        kJoinShift = kStyleBits,
-        kCapShift = kJoinShift + kJoinBits,
-        kWidthShift = kCapShift + kCapBits,
-        kMiterShift = kWidthShift + kWidthBits,
-
-        kBitCount = kMiterShift + kMiterBits
-    };
-
-    SK_COMPILE_ASSERT(SkStrokeRec::kStyleCount <= (1 << kStyleBits), style_shift_will_be_wrong);
-    SK_COMPILE_ASSERT(SkPaint::kJoinCount <= (1 << kJoinBits), cap_shift_will_be_wrong);
-    SK_COMPILE_ASSERT(SkPaint::kCapCount <= (1 << kCapBits), miter_shift_will_be_wrong);
-    SK_COMPILE_ASSERT(kBitCount == 64, wrong_stroke_key_size);
-
-    if (!stroke.needToApply()) {
-        return SkStrokeRec::kFill_Style;
+void GrPath::ComputeKey(const SkPath& path, const GrStrokeInfo& stroke, GrUniqueKey* key) {
+    static const GrUniqueKey::Domain kPathDomain = GrUniqueKey::GenerateDomain();
+    int strokeDataCnt = stroke.computeUniqueKeyFragmentData32Cnt();
+    GrUniqueKey::Builder builder(key, kPathDomain, 2 + strokeDataCnt);
+    builder[0] = path.getGenerationID();
+    builder[1] = path.getFillType();
+    if (strokeDataCnt > 0) {
+        stroke.asUniqueKeyFragment(&builder[2]);
     }
-
-    uint64_t key = stroke.getStyle();
-    key |= stroke.getJoin() << kJoinShift;
-    key |= stroke.getCap() << kCapShift;
-    key |= get_top_n_float_bits<kWidthBits>(stroke.getWidth()) << kWidthShift;
-    key |= get_top_n_float_bits<kMiterBits>(stroke.getMiter()) << kMiterShift;
-
-    return key;
 }
+
index ab8c51f..a535e69 100644 (file)
@@ -9,9 +9,9 @@
 #define GrPath_DEFINED
 
 #include "GrGpuResource.h"
+#include "GrStrokeInfo.h"
 #include "SkPath.h"
 #include "SkRect.h"
-#include "SkStrokeRec.h"
 
 class GrPath : public GrGpuResource {
 public:
@@ -20,28 +20,32 @@ public:
     /**
      * Initialize to a path with a fixed stroke. Stroke must not be hairline.
      */
-    GrPath(GrGpu* gpu, const SkPath& skPath, const SkStrokeRec& stroke)
-        : INHERITED(gpu, kCached_LifeCycle),
-          fSkPath(skPath),
-          fStroke(stroke),
-          fBounds(skPath.getBounds()) {
+    GrPath(GrGpu* gpu, const SkPath& skPath, const GrStrokeInfo& stroke)
+        : INHERITED(gpu, kCached_LifeCycle)
+        , fBounds(skPath.getBounds())
+#ifdef SK_DEBUG
+        , fSkPath(skPath)
+        , fStroke(stroke)
+#endif
+    {
     }
 
-    static void ComputeKey(const SkPath& path, const SkStrokeRec& stroke, GrUniqueKey* key);
-    static uint64_t ComputeStrokeKey(const SkStrokeRec&);
-
-    bool isEqualTo(const SkPath& path, const SkStrokeRec& stroke) {
-        return fSkPath == path && fStroke.hasEqualEffect(stroke);
-    }
+    static void ComputeKey(const SkPath& path, const GrStrokeInfo& stroke, GrUniqueKey* key);
 
     const SkRect& getBounds() const { return fBounds; }
 
-    const SkStrokeRec& getStroke() const { return fStroke; }
+#ifdef SK_DEBUG
+    bool isEqualTo(const SkPath& path, const GrStrokeInfo& stroke) {
+        return fSkPath == path && fStroke.hasEqualEffect(stroke);
+    }
+#endif
 
 protected:
-    SkPath fSkPath;
-    SkStrokeRec fStroke;
     SkRect fBounds;
+#ifdef SK_DEBUG
+    SkPath fSkPath;
+    GrStrokeInfo fStroke;
+#endif
 
 private:
     typedef GrGpuResource INHERITED;
index 8bac750..5e71014 100644 (file)
@@ -13,23 +13,19 @@ enum {
 };
 
 GrPathRange::GrPathRange(GrGpu* gpu,
-                         PathGenerator* pathGenerator,
-                         const SkStrokeRec& stroke)
+                         PathGenerator* pathGenerator)
     : INHERITED(gpu, kCached_LifeCycle),
       fPathGenerator(SkRef(pathGenerator)),
-      fNumPaths(fPathGenerator->getNumPaths()),
-      fStroke(stroke) {
+      fNumPaths(fPathGenerator->getNumPaths()) {
     const int numGroups = (fNumPaths + kPathsPerGroup - 1) / kPathsPerGroup;
     fGeneratedPaths.reset((numGroups + 7) / 8); // 1 bit per path group.
     memset(&fGeneratedPaths.front(), 0, fGeneratedPaths.count());
 }
 
 GrPathRange::GrPathRange(GrGpu* gpu,
-                         int numPaths,
-                         const SkStrokeRec& stroke)
+                         int numPaths)
     : INHERITED(gpu, kCached_LifeCycle),
-      fNumPaths(numPaths),
-      fStroke(stroke) {
+      fNumPaths(numPaths) {
 }
 
 void GrPathRange::willDrawPaths(const void* indices, PathIndexType indexType, int count) const {
index 86883e3..035837f 100644 (file)
 
 #include "GrGpuResource.h"
 #include "SkRefCnt.h"
-#include "SkStrokeRec.h"
 #include "SkTArray.h"
 
 class SkPath;
 class SkDescriptor;
 
 /**
- * Represents a contiguous range of GPU path objects, all with a common stroke.
+ * Represents a contiguous range of GPU path objects.
  * This object is immutable with the exception that individual paths may be
  * initialized lazily.
  */
@@ -50,7 +49,9 @@ public:
     public:
         virtual int getNumPaths() = 0;
         virtual void generatePath(int index, SkPath* out) = 0;
+#ifdef SK_DEBUG
         virtual bool isEqualTo(const SkDescriptor&) const { return false; }
+#endif
         virtual ~PathGenerator() {}
     };
 
@@ -58,22 +59,22 @@ public:
      * Initialize a lazy-loaded path range. This class will generate an SkPath and call
      * onInitPath() for each path within the range before it is drawn for the first time.
      */
-    GrPathRange(GrGpu*, PathGenerator*, const SkStrokeRec& stroke);
+    GrPathRange(GrGpu*, PathGenerator*);
 
     /**
      * Initialize an eager-loaded path range. The subclass is responsible for ensuring all
      * the paths are initialized up front.
      */
-    GrPathRange(GrGpu*, int numPaths, const SkStrokeRec& stroke);
-
-    virtual bool isEqualTo(const SkDescriptor& desc) const {
-        return NULL != fPathGenerator.get() && fPathGenerator->isEqualTo(desc);
-    }
+    GrPathRange(GrGpu*, int numPaths);
 
     int getNumPaths() const { return fNumPaths; }
-    const SkStrokeRec& getStroke() const { return fStroke; }
     const PathGenerator* getPathGenerator() const { return fPathGenerator.get(); }
 
+#ifdef SK_DEBUG
+    virtual bool isEqualTo(const SkDescriptor& desc) const {
+        return NULL != fPathGenerator.get() && fPathGenerator->isEqualTo(desc);
+    }
+#endif
 protected:
     // Initialize a path in the range before drawing. This is only called when
     // fPathGenerator is non-null. The child class need not call didChangeGpuMemorySize(),
@@ -89,7 +90,6 @@ private:
     mutable SkAutoTUnref<PathGenerator> fPathGenerator;
     mutable SkTArray<uint8_t, true /*MEM_COPY*/> fGeneratedPaths;
     const int fNumPaths;
-    const SkStrokeRec fStroke;
 
     typedef GrGpuResource INHERITED;
 };
index fba55d5..e97d754 100644 (file)
 class GlyphGenerator : public GrPathRange::PathGenerator {
 public:
     GlyphGenerator(const SkTypeface& typeface, const SkDescriptor& desc)
-        : fDesc(desc.copy()),
-          fScalerContext(typeface.createScalerContext(fDesc)) {
+        : fScalerContext(typeface.createScalerContext(&desc))
+#ifdef SK_DEBUG
+        , fDesc(desc.copy())
+#endif
+    {
         fFlipMatrix.setScale(1, -1);
     }
 
     virtual ~GlyphGenerator() {
+#ifdef SK_DEBUG
         SkDescriptor::Free(fDesc);
+#endif
     }
 
     int getNumPaths() override {
@@ -36,20 +41,22 @@ public:
         fScalerContext->getPath(skGlyph, out);
         out->transform(fFlipMatrix); // Load glyphs with the inverted y-direction.
     }
-
+#ifdef SK_DEBUG
     bool isEqualTo(const SkDescriptor& desc) const override {
         return fDesc->equals(desc);
     }
-
+#endif
 private:
-    SkDescriptor* const fDesc;
     const SkAutoTDelete<SkScalerContext> fScalerContext;
     SkMatrix fFlipMatrix;
+#ifdef SK_DEBUG
+    SkDescriptor* const fDesc;
+#endif
 };
 
 GrPathRange* GrPathRendering::createGlyphs(const SkTypeface* typeface,
                                            const SkDescriptor* desc,
-                                           const SkStrokeRec& stroke) {
+                                           const GrStrokeInfo& stroke) {
     if (NULL == typeface) {
         typeface = SkTypeface::GetDefaultTypeface();
         SkASSERT(NULL != typeface);
index 3e2cfc6..e198d69 100644 (file)
 #include "SkPath.h"
 #include "GrPathRange.h"
 
-class SkStrokeRec;
 class SkDescriptor;
 class SkTypeface;
 class GrPath;
 class GrGpu;
 class GrStencilSettings;
+class GrStrokeInfo;
 
 /**
  * Abstract class wrapping HW path rendering API.
@@ -84,17 +84,17 @@ public:
      * @param stroke the path stroke.
      * @return a new path.
      */
-    virtual GrPath* createPath(const SkPath&, const SkStrokeRec&) = 0;
+    virtual GrPath* createPath(const SkPath&, const GrStrokeInfo&) = 0;
 
     /**
      * Creates a range of gpu paths with a common stroke. The caller owns a ref on the
      * returned path range which must be balanced by a call to unref.
      *
      * @param PathGenerator class that generates SkPath objects for each path in the range.
-     * @param SkStrokeRec   the common stroke applied to each path in the range.
+     * @param GrStrokeInfo   the common stroke applied to each path in the range.
      * @return a new path range.
      */
-    virtual GrPathRange* createPathRange(GrPathRange::PathGenerator*, const SkStrokeRec&) = 0;
+    virtual GrPathRange* createPathRange(GrPathRange::PathGenerator*, const GrStrokeInfo&) = 0;
 
     /**
      * Creates a range of glyph paths, indexed by glyph id. The glyphs will have an
@@ -117,14 +117,15 @@ public:
      *                     including with the stroke information baked directly into
      *                     the outlines.
      *
-     * @param SkStrokeRec  Common stroke that the GPU will apply to every path. Note that
+     * @param GrStrokeInfo Common stroke that the GPU will apply to every path. Note that
      *                     if the glyph outlines contain baked-in strokes from the font
      *                     descriptor, the GPU stroke will be applied on top of those
      *                     outlines.
      *
      * @return a new path range populated with glyphs.
      */
-    virtual GrPathRange* createGlyphs(const SkTypeface*, const SkDescriptor*, const SkStrokeRec&) = 0;
+    virtual GrPathRange* createGlyphs(const SkTypeface*, const SkDescriptor*,
+                                      const GrStrokeInfo&) = 0;
 
     virtual void stencilPath(const GrPath*, const GrStencilSettings&) = 0;
     virtual void drawPath(const GrPath*, const GrStencilSettings&) = 0;
index bfb0f04..e60bea2 100644 (file)
@@ -60,7 +60,6 @@ bool GrStencilAndCoverPathRenderer::canDrawPath(const GrDrawTarget* target,
                                                 const GrStrokeInfo& stroke,
                                                 bool antiAlias) const {
     return !stroke.isHairlineStyle() &&
-        !stroke.isDashed() &&
         !antiAlias && // doesn't do per-path AA, relies on the target having MSAA
         pipelineBuilder->getStencil().isDisabled();
 }
@@ -73,15 +72,17 @@ GrStencilAndCoverPathRenderer::onGetStencilSupport(const GrDrawTarget*,
     return GrPathRenderer::kStencilOnly_StencilSupport;
 }
 
-static GrPath* get_gr_path(GrGpu* gpu, const SkPath& skPath, const SkStrokeRec& stroke) {
+static GrPath* get_gr_path(GrGpu* gpu, const SkPath& skPath, const GrStrokeInfo& stroke) {
     GrContext* ctx = gpu->getContext();
     GrUniqueKey key;
     GrPath::ComputeKey(skPath, stroke, &key);
     SkAutoTUnref<GrPath> path(
         static_cast<GrPath*>(ctx->resourceProvider()->findAndRefResourceByUniqueKey(key)));
-    if (NULL == path || !path->isEqualTo(skPath, stroke)) {
+    if (NULL == path) {
         path.reset(gpu->pathRendering()->createPath(skPath, stroke));
         ctx->resourceProvider()->assignUniqueKeyToResource(key, path);
+    } else {
+        SkASSERT(path->isEqualTo(skPath, stroke));
     }
     return path.detach();
 }
@@ -106,7 +107,6 @@ bool GrStencilAndCoverPathRenderer::onDrawPath(GrDrawTarget* target,
                                                bool antiAlias) {
     SkASSERT(!antiAlias);
     SkASSERT(!stroke.isHairlineStyle());
-    SkASSERT(!stroke.isDashed());
     SkASSERT(pipelineBuilder->getStencil().isDisabled());
 
     SkAutoTUnref<GrPath> p(get_gr_path(fGpu, path, stroke));
index a2abfa7..3431951 100644 (file)
@@ -55,8 +55,10 @@ bool GrStencilAndCoverTextContext::canDraw(const GrRenderTarget* rt,
     if (skPaint.getMaskFilter()) {
         return false;
     }
-    if (skPaint.getPathEffect()) {
-        return false;
+    if (SkPathEffect* pe = skPaint.getPathEffect()) {
+        if (pe->asADash(NULL) != SkPathEffect::kDash_DashType) {
+            return false;
+        }
     }
 
     // No hairlines unless we can map the 1 px width to the object space.
@@ -220,26 +222,27 @@ void GrStencilAndCoverTextContext::onDrawPosText(GrRenderTarget* rt,
 static GrPathRange* get_gr_glyphs(GrContext* ctx,
                                   const SkTypeface* typeface,
                                   const SkDescriptor* desc,
-                                  const SkStrokeRec& stroke) {
-    static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
-    GrUniqueKey key;
-    GrUniqueKey::Builder builder(&key, kDomain, 4);
-    struct GlyphKey {
-        uint32_t fChecksum;
-        uint32_t fTypeface;
-        uint64_t fStroke;
-    };
-    GlyphKey* glyphKey = reinterpret_cast<GlyphKey*>(&builder[0]);
-    glyphKey->fChecksum = desc ? desc->getChecksum() : 0;
-    glyphKey->fTypeface = typeface ? typeface->uniqueID() : 0;
-    glyphKey->fStroke = GrPath::ComputeStrokeKey(stroke);
+                                  const GrStrokeInfo& stroke) {
+
+    static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::GenerateDomain();
+    int strokeDataCount = stroke.computeUniqueKeyFragmentData32Cnt();
+    GrUniqueKey glyphKey;
+    GrUniqueKey::Builder builder(&glyphKey, kPathGlyphDomain, 2 + strokeDataCount);
+    reinterpret_cast<uint32_t&>(builder[0]) = desc ? desc->getChecksum() : 0;
+    reinterpret_cast<uint32_t&>(builder[1]) = typeface ? typeface->uniqueID() : 0;
+    if (strokeDataCount > 0) {
+        stroke.asUniqueKeyFragment(&builder[2]);
+    }
     builder.finish();
 
     SkAutoTUnref<GrPathRange> glyphs(
-        static_cast<GrPathRange*>(ctx->resourceProvider()->findAndRefResourceByUniqueKey(key)));
-    if (NULL == glyphs || (NULL != desc && !glyphs->isEqualTo(*desc))) {
+        static_cast<GrPathRange*>(
+            ctx->resourceProvider()->findAndRefResourceByUniqueKey(glyphKey)));
+    if (NULL == glyphs) {
         glyphs.reset(ctx->getGpu()->pathRendering()->createGlyphs(typeface, desc, stroke));
-        ctx->resourceProvider()->assignUniqueKeyToResource(key, glyphs);
+        ctx->resourceProvider()->assignUniqueKeyToResource(glyphKey, glyphs);
+    } else {
+        SkASSERT(NULL == desc || glyphs->isEqualTo(*desc));
     }
 
     return glyphs.detach();
@@ -273,7 +276,7 @@ void GrStencilAndCoverTextContext::init(GrRenderTarget* rt,
 
         // The whole shape (including stroke) will be baked into the glyph outlines. Make
         // NVPR just fill the baked shapes.
-        fStroke = SkStrokeRec(SkStrokeRec::kFill_InitStyle);
+        fStroke = GrStrokeInfo(SkStrokeRec::kFill_InitStyle);
 
         fTextRatio = fTextInverseRatio = 1.0f;
 
@@ -298,7 +301,7 @@ void GrStencilAndCoverTextContext::init(GrRenderTarget* rt,
     } else {
         // Don't bake strokes into the glyph outlines. We will stroke the glyphs
         // using the GPU instead. This is the fast path.
-        fStroke = SkStrokeRec(fSkPaint);
+        fStroke = GrStrokeInfo(fSkPaint);
         fSkPaint.setStyle(SkPaint::kFill_Style);
 
         if (fStroke.isHairlineStyle()) {
@@ -327,8 +330,8 @@ void GrStencilAndCoverTextContext::init(GrRenderTarget* rt,
         }
 
         bool canUseRawPaths;
-
-        if (otherBackendsWillDrawAsPaths || kMaxPerformance_RenderMode == renderMode) {
+        if (!fStroke.isDashed() && (otherBackendsWillDrawAsPaths ||
+                                    kMaxPerformance_RenderMode == renderMode)) {
             // We can draw the glyphs from canonically sized paths.
             fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
             fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.getTextSize();
index d3d2470..0cb5553 100644 (file)
@@ -10,7 +10,7 @@
 
 #include "GrTextContext.h"
 #include "GrDrawTarget.h"
-#include "SkStrokeRec.h"
+#include "GrStrokeInfo.h"
 
 class GrTextStrike;
 class GrPath;
@@ -58,7 +58,7 @@ private:
     float                                               fTextInverseRatio;
     SkGlyphCache*                                       fGlyphCache;
     GrPathRange*                                        fGlyphs;
-    SkStrokeRec                                         fStroke;
+    GrStrokeInfo                                        fStroke;
     uint16_t                                            fGlyphIndices[kGlyphBufferSize];
     SkPoint                                             fGlyphPositions[kGlyphBufferSize];
     int                                                 fQueuedGlyphCount;
index eb3008b..0ad4179 100644 (file)
@@ -6,7 +6,7 @@
  */
 
 #include "GrStrokeInfo.h"
-
+#include "GrResourceKey.h"
 #include "SkDashPathPriv.h"
 
 bool GrStrokeInfo::applyDashToPath(SkPath* dst, GrStrokeInfo* dstStrokeInfo,
@@ -24,3 +24,55 @@ bool GrStrokeInfo::applyDashToPath(SkPath* dst, GrStrokeInfo* dstStrokeInfo,
     }
     return false;
 }
+
+void GrStrokeInfo::asUniqueKeyFragment(uint32_t* data) const {
+    const int kSkScalarData32Cnt = sizeof(SkScalar) / sizeof(uint32_t);
+    enum {
+        kStyleBits = 2,
+        kJoinBits = 2,
+        kCapBits = 32 - kStyleBits - kJoinBits,
+
+        kJoinShift = kStyleBits,
+        kCapShift = kJoinShift + kJoinBits,
+    };
+
+    SK_COMPILE_ASSERT(SkStrokeRec::kStyleCount <= (1 << kStyleBits), style_shift_will_be_wrong);
+    SK_COMPILE_ASSERT(SkPaint::kJoinCount <= (1 << kJoinBits), cap_shift_will_be_wrong);
+    SK_COMPILE_ASSERT(SkPaint::kCapCount <= (1 << kCapBits), cap_does_not_fit);
+    uint32_t styleKey = this->getStyle();
+    if (this->needToApply()) {
+        styleKey |= this->getJoin() << kJoinShift;
+        styleKey |= this->getCap() << kCapShift;
+    }
+    int i = 0;
+    data[i++] = styleKey;
+
+    // Memcpy the scalar fields. Does not "reinterpret_cast<SkScalar&>(data[i]) = ..." due to
+    // scalars having more strict alignment requirements than what data can guarantee. The
+    // compiler should optimize memcpys to assignments.
+    SkScalar scalar;
+    scalar = this->getMiter();
+    memcpy(&data[i], &scalar, sizeof(scalar));
+    i += kSkScalarData32Cnt;
+
+    scalar = this->getWidth();
+    memcpy(&data[i], &scalar, sizeof(scalar));
+    i += kSkScalarData32Cnt;
+
+    if (this->isDashed()) {
+        SkScalar phase = this->getDashPhase();
+        memcpy(&data[i], &phase, sizeof(phase));
+        i += kSkScalarData32Cnt;
+
+        int32_t count = this->getDashCount() & static_cast<int32_t>(~1);
+        SkASSERT(count == this->getDashCount());
+        const SkScalar* intervals = this->getDashIntervals();
+        int intervalByteCnt = count * sizeof(SkScalar);
+        memcpy(&data[i], intervals, intervalByteCnt);
+        // Enable the line below if fields are added after dashing.
+        SkDEBUGCODE(i += kSkScalarData32Cnt * count);
+    }
+
+    SkASSERT(this->computeUniqueKeyFragmentData32Cnt() == i);
+}
+
index 5ba3cfe..1cc2dfe 100644 (file)
@@ -11,6 +11,8 @@
 #include "SkStrokeRec.h"
 #include "SkPathEffect.h"
 
+class GrUniqueKey;
+
 /*
  * GrStrokeInfo encapsulates all the pertinent infomation regarding the stroke. The SkStrokeRec
  * which holds information on fill style, width, miter, cap, and join. It also holds information
@@ -60,6 +62,21 @@ public:
         return *this;
     }
 
+    bool hasEqualEffect(const GrStrokeInfo& other) const {
+        if (this->isDashed() != other.isDashed()) {
+            return false;
+        }
+        if (this->isDashed()) {
+            if (fDashPhase != other.fDashPhase ||
+                fIntervals.count() != other.fIntervals.count() ||
+                memcmp(fIntervals.get(), other.fIntervals.get(),
+                       fIntervals.count() * sizeof(SkScalar)) != 0) {
+                return false;
+            }
+        }
+        return this->INHERITED::hasEqualEffect(other);
+    }
+
     /*
      * This functions takes in a patheffect and updates the dashing information if the path effect
      * is a Dash type. Returns true if the path effect is a dashed effect and we are stroking,
@@ -127,8 +144,31 @@ public:
      */
     bool applyDashToPath(SkPath* dst, GrStrokeInfo* dstStrokeInfo, const SkPath& src) const;
 
+    /**
+     * Computes the length of the data that will be written by asUniqueKeyFragment() function.
+     */
+    int computeUniqueKeyFragmentData32Cnt() const {
+        const int kSkScalarData32Cnt = sizeof(SkScalar) / sizeof(uint32_t);
+        // SkStrokeRec data: 32 bits for style+join+cap and 2 scalars for miter and width.
+        int strokeKeyData32Cnt = 1 + 2 * kSkScalarData32Cnt;
+
+        if (this->isDashed()) {
+            // One scalar for dash phase and one for each dash value.
+            strokeKeyData32Cnt += (1 + this->getDashCount()) * kSkScalarData32Cnt;
+        }
+        return strokeKeyData32Cnt;
+    }
+
+    /**
+     * Writes the object contents as uint32_t data, to be used with GrUniqueKey.
+     * Note: the data written does not encode the length, so care must be taken to ensure
+     * that the full unique key data is encoded properly. For example, GrStrokeInfo
+     * fragment can be placed last in the sequence, at fixed index.
+     */
+    void asUniqueKeyFragment(uint32_t*) const;
+
 private:
-    // Prevent accidental usage, not implemented for GrStrokeInfos.
+    // Prevent accidental usage, should use GrStrokeInfo::hasEqualEffect.
     bool hasEqualEffect(const SkStrokeRec& other) const;
 
     void init(const SkPaint& paint) {
index 037134a..80c6356 100644 (file)
@@ -91,7 +91,8 @@ inline void points_to_coords(const SkPoint points[], size_t first_point, size_t
 void GrGLPath::InitPathObject(GrGLGpu* gpu,
                               GrGLuint pathID,
                               const SkPath& skPath,
-                              const SkStrokeRec& stroke) {
+                              const GrStrokeInfo& stroke) {
+    SkASSERT(!stroke.isDashed());
     if (!skPath.isEmpty()) {
         int verbCnt = skPath.countVerbs();
         int pointCnt = skPath.countPoints();
@@ -182,16 +183,33 @@ void GrGLPath::InitPathObject(GrGLGpu* gpu,
     }
 }
 
-GrGLPath::GrGLPath(GrGLGpu* gpu, const SkPath& path, const SkStrokeRec& stroke)
-    : INHERITED(gpu, path, stroke),
+GrGLPath::GrGLPath(GrGLGpu* gpu, const SkPath& origSkPath, const GrStrokeInfo& origStroke)
+    : INHERITED(gpu, origSkPath, origStroke),
       fPathID(gpu->glPathRendering()->genPaths(1)) {
+    // Convert a dashing to either a stroke or a fill.
+    const SkPath* skPath = &origSkPath;
+    SkTLazy<SkPath> tmpPath;
+    const GrStrokeInfo* stroke = &origStroke;
+    GrStrokeInfo tmpStroke(SkStrokeRec::kFill_InitStyle);
+
+    if (stroke->isDashed()) {
+        if (stroke->applyDashToPath(tmpPath.init(), &tmpStroke, *skPath)) {
+            skPath = tmpPath.get();
+            stroke = &tmpStroke;
+        }
+    }
 
-    InitPathObject(gpu, fPathID, fSkPath, stroke);
+    InitPathObject(gpu, fPathID, *skPath, *stroke);
 
-    if (stroke.needToApply()) {
+    fShouldStroke = stroke->needToApply();
+    fShouldFill = stroke->isFillStyle() ||
+            stroke->getStyle() == SkStrokeRec::kStrokeAndFill_Style;
+
+    if (fShouldStroke) {
         // FIXME: try to account for stroking, without rasterizing the stroke.
-        fBounds.outset(stroke.getWidth(), stroke.getWidth());
+        fBounds.outset(stroke->getWidth(), stroke->getWidth());
     }
+
     this->registerWithCache();
 }
 
index f02f705..b394050 100644 (file)
@@ -25,11 +25,13 @@ public:
     static void InitPathObject(GrGLGpu*,
                                GrGLuint pathID,
                                const SkPath&,
-                               const SkStrokeRec&);
+                               const GrStrokeInfo&);
 
-    GrGLPath(GrGLGpu* gpu, const SkPath& path, const SkStrokeRec& stroke);
+    GrGLPath(GrGLGpu* gpu, const SkPath& path, const GrStrokeInfo& stroke);
     GrGLuint pathID() const { return fPathID; }
 
+    bool shouldStroke() const { return fShouldStroke; }
+    bool shouldFill() const { return fShouldFill; }
 protected:
     void onRelease() override;
     void onAbandon() override;
@@ -39,6 +41,8 @@ private:
     size_t onGpuMemorySize() const override { return 100; }
 
     GrGLuint fPathID;
+    bool fShouldStroke;
+    bool fShouldFill;
 
     typedef GrPath INHERITED;
 };
index d7ccf1b..b577d89 100644 (file)
 #include "GrGLPathRendering.h"
 #include "GrGLGpu.h"
 
-GrGLPathRange::GrGLPathRange(GrGLGpu* gpu, PathGenerator* pathGenerator, const SkStrokeRec& stroke)
-    : INHERITED(gpu, pathGenerator, stroke),
+GrGLPathRange::GrGLPathRange(GrGLGpu* gpu, PathGenerator* pathGenerator, const GrStrokeInfo& stroke)
+    : INHERITED(gpu, pathGenerator),
+      fStroke(stroke),
       fBasePathID(gpu->glPathRendering()->genPaths(this->getNumPaths())),
       fGpuMemorySize(0) {
+    this->init();
     this->registerWithCache();
 }
 
@@ -22,14 +24,27 @@ GrGLPathRange::GrGLPathRange(GrGLGpu* gpu,
                              GrGLuint basePathID,
                              int numPaths,
                              size_t gpuMemorySize,
-                             const SkStrokeRec& stroke)
-    : INHERITED(gpu, numPaths, stroke),
+                             const GrStrokeInfo& stroke)
+    : INHERITED(gpu, numPaths),
+      fStroke(stroke),
       fBasePathID(basePathID),
       fGpuMemorySize(gpuMemorySize) {
+    this->init();
     this->registerWithCache();
 }
 
-void GrGLPathRange::onInitPath(int index, const SkPath& skPath) const {
+void GrGLPathRange::init() {
+    if (fStroke.isDashed()) {
+        fShouldStroke = false;
+        fShouldFill = true;
+    } else {
+        fShouldStroke = fStroke.needToApply();
+        fShouldFill = fStroke.isFillStyle() ||
+                fStroke.getStyle() == SkStrokeRec::kStrokeAndFill_Style;
+    }
+}
+
+void GrGLPathRange::onInitPath(int index, const SkPath& origSkPath) const {
     GrGLGpu* gpu = static_cast<GrGLGpu*>(this->getGpu());
     if (NULL == gpu) {
         return;
@@ -41,7 +56,31 @@ void GrGLPathRange::onInitPath(int index, const SkPath& skPath) const {
         GR_GL_CALL_RET(gpu->glInterface(), isPath, IsPath(fBasePathID + index)));
     SkASSERT(GR_GL_FALSE == isPath);
 
-    GrGLPath::InitPathObject(gpu, fBasePathID + index, skPath, this->getStroke());
+    const SkPath* skPath = &origSkPath;
+    SkTLazy<SkPath> tmpPath;
+    const GrStrokeInfo* stroke = &fStroke;
+    GrStrokeInfo tmpStroke(SkStrokeRec::kFill_InitStyle);
+
+    // Dashing must be applied to the path. However, if dashing is present,
+    // we must convert all the paths to fills. The GrStrokeInfo::applyDash leaves
+    // simple paths as strokes but converts other paths to fills.
+    // Thus we must stroke the strokes here, so that all paths in the
+    // path range are using the same style.
+    if (fStroke.isDashed()) {
+        if (!stroke->applyDashToPath(tmpPath.init(), &tmpStroke, *skPath)) {
+            return;
+        }
+        skPath = tmpPath.get();
+        stroke = &tmpStroke;
+        if (tmpStroke.needToApply()) {
+            if (!tmpStroke.applyToPath(tmpPath.get(), *tmpPath.get())) {
+                return;
+            }
+            tmpStroke.setFillStyle();
+        }
+    }
+
+    GrGLPath::InitPathObject(gpu, fBasePathID + index, *skPath, *stroke);
 
     // TODO: Use a better approximation for the individual path sizes.
     fGpuMemorySize += 100;
index 5cba9c7..22dd1c0 100644 (file)
@@ -10,6 +10,7 @@
 #define GrGLPathRange_DEFINED
 
 #include "../GrPathRange.h"
+#include "GrStrokeInfo.h"
 #include "gl/GrGLFunctions.h"
 
 class GrGLGpu;
@@ -26,7 +27,7 @@ public:
      * Initialize a GL path range from a PathGenerator. This class will allocate
      * the GPU path objects and initialize them lazily.
      */
-    GrGLPathRange(GrGLGpu*, PathGenerator*, const SkStrokeRec&);
+    GrGLPathRange(GrGLGpu*, PathGenerator*, const GrStrokeInfo&);
 
     /**
      * Initialize a GL path range from an existing range of pre-initialized GPU
@@ -37,10 +38,13 @@ public:
                   GrGLuint basePathID,
                   int numPaths,
                   size_t gpuMemorySize,
-                  const SkStrokeRec&);
+                  const GrStrokeInfo&);
 
     GrGLuint basePathID() const { return fBasePathID; }
 
+    bool shouldStroke() const { return fShouldStroke; }
+    bool shouldFill() const { return fShouldFill; }
+
 protected:
     void onInitPath(int index, const SkPath&) const override;
 
@@ -48,10 +52,14 @@ protected:
     void onAbandon() override;
 
 private:
+    void init();
     size_t onGpuMemorySize() const override { return fGpuMemorySize; }
 
+    const GrStrokeInfo fStroke;
     GrGLuint fBasePathID;
     mutable size_t fGpuMemorySize;
+    bool fShouldStroke;
+    bool fShouldFill;
 
     typedef GrPathRange INHERITED;
 };
index 8a0b35c..3ceb876 100644 (file)
@@ -91,19 +91,19 @@ void GrGLPathRendering::resetContext() {
     fHWPathStencilSettings.invalidate();
 }
 
-GrPath* GrGLPathRendering::createPath(const SkPath& inPath, const SkStrokeRec& stroke) {
+GrPath* GrGLPathRendering::createPath(const SkPath& inPath, const GrStrokeInfo& stroke) {
     return SkNEW_ARGS(GrGLPath, (fGpu, inPath, stroke));
 }
 
 GrPathRange* GrGLPathRendering::createPathRange(GrPathRange::PathGenerator* pathGenerator,
-                                                const SkStrokeRec& stroke) {
+                                                const GrStrokeInfo& stroke) {
     return SkNEW_ARGS(GrGLPathRange, (fGpu, pathGenerator, stroke));
 }
 
 GrPathRange* GrGLPathRendering::createGlyphs(const SkTypeface* typeface,
                                              const SkDescriptor* desc,
-                                             const SkStrokeRec& stroke) {
-    if (NULL != desc || !caps().glyphLoadingSupport) {
+                                             const GrStrokeInfo& stroke) {
+    if (NULL != desc || !caps().glyphLoadingSupport || stroke.isDashed()) {
         return GrPathRendering::createGlyphs(typeface, desc, stroke);
     }
 
@@ -152,44 +152,40 @@ GrPathRange* GrGLPathRendering::createGlyphs(const SkTypeface* typeface,
 }
 
 void GrGLPathRendering::stencilPath(const GrPath* path, const GrStencilSettings& stencilSettings) {
-    GrGLuint id = static_cast<const GrGLPath*>(path)->pathID();
+    const GrGLPath* glPath = static_cast<const GrGLPath*>(path);
 
     this->flushPathStencilSettings(stencilSettings);
     SkASSERT(!fHWPathStencilSettings.isTwoSided());
 
-    const SkStrokeRec& stroke = path->getStroke();
-
-    GrGLenum fillMode =
-        gr_stencil_op_to_gl_path_rendering_fill_mode(fHWPathStencilSettings.passOp(GrStencilSettings::kFront_Face));
+    GrGLenum fillMode = gr_stencil_op_to_gl_path_rendering_fill_mode(
+        fHWPathStencilSettings.passOp(GrStencilSettings::kFront_Face));
     GrGLint writeMask = fHWPathStencilSettings.writeMask(GrStencilSettings::kFront_Face);
 
-    if (stroke.isFillStyle() || SkStrokeRec::kStrokeAndFill_Style == stroke.getStyle()) {
-        GL_CALL(StencilFillPath(id, fillMode, writeMask));
+    if (glPath->shouldFill()) {
+        GL_CALL(StencilFillPath(glPath->pathID(), fillMode, writeMask));
     }
-    if (stroke.needToApply()) {
-        GL_CALL(StencilStrokePath(id, 0xffff, writeMask));
+    if (glPath->shouldStroke()) {
+        GL_CALL(StencilStrokePath(glPath->pathID(), 0xffff, writeMask));
     }
 }
 
 void GrGLPathRendering::drawPath(const GrPath* path, const GrStencilSettings& stencilSettings) {
-    GrGLuint id = static_cast<const GrGLPath*>(path)->pathID();
+    const GrGLPath* glPath = static_cast<const GrGLPath*>(path);
 
     this->flushPathStencilSettings(stencilSettings);
     SkASSERT(!fHWPathStencilSettings.isTwoSided());
 
-    const SkStrokeRec& stroke = path->getStroke();
-
-    GrGLenum fillMode =
-        gr_stencil_op_to_gl_path_rendering_fill_mode(fHWPathStencilSettings.passOp(GrStencilSettings::kFront_Face));
+    GrGLenum fillMode = gr_stencil_op_to_gl_path_rendering_fill_mode(
+        fHWPathStencilSettings.passOp(GrStencilSettings::kFront_Face));
     GrGLint writeMask = fHWPathStencilSettings.writeMask(GrStencilSettings::kFront_Face);
 
-    if (stroke.needToApply()) {
-        if (SkStrokeRec::kStrokeAndFill_Style == stroke.getStyle()) {
-            GL_CALL(StencilFillPath(id, fillMode, writeMask));
+    if (glPath->shouldStroke()) {
+        if (glPath->shouldFill()) {
+            GL_CALL(StencilFillPath(glPath->pathID(), fillMode, writeMask));
         }
-        this->stencilThenCoverStrokePath(id, 0xffff, writeMask, GR_GL_BOUNDING_BOX);
+        this->stencilThenCoverStrokePath(glPath->pathID(), 0xffff, writeMask, GR_GL_BOUNDING_BOX);
     } else {
-        this->stencilThenCoverFillPath(id, fillMode, writeMask, GR_GL_BOUNDING_BOX);
+        this->stencilThenCoverFillPath(glPath->pathID(), fillMode, writeMask, GR_GL_BOUNDING_BOX);
     }
 }
 
@@ -199,32 +195,31 @@ void GrGLPathRendering::drawPaths(const GrPathRange* pathRange,
                                   int count, const GrStencilSettings& stencilSettings) {
     SkASSERT(fGpu->caps()->shaderCaps()->pathRenderingSupport());
 
-    GrGLuint baseID = static_cast<const GrGLPathRange*>(pathRange)->basePathID();
+    const GrGLPathRange* glPathRange = static_cast<const GrGLPathRange*>(pathRange);
 
     this->flushPathStencilSettings(stencilSettings);
     SkASSERT(!fHWPathStencilSettings.isTwoSided());
 
-    const SkStrokeRec& stroke = pathRange->getStroke();
-
     GrGLenum fillMode =
         gr_stencil_op_to_gl_path_rendering_fill_mode(
             fHWPathStencilSettings.passOp(GrStencilSettings::kFront_Face));
     GrGLint writeMask =
         fHWPathStencilSettings.writeMask(GrStencilSettings::kFront_Face);
 
-    if (stroke.needToApply()) {
-        if (SkStrokeRec::kStrokeAndFill_Style == stroke.getStyle()) {
+    if (glPathRange->shouldStroke()) {
+        if (glPathRange->shouldFill()) {
             GL_CALL(StencilFillPathInstanced(
-                            count, gIndexType2GLType[indexType], indices, baseID, fillMode,
-                            writeMask, gXformType2GLType[transformType], transformValues));
+                            count, gIndexType2GLType[indexType], indices, glPathRange->basePathID(),
+                            fillMode, writeMask, gXformType2GLType[transformType],
+                            transformValues));
         }
         this->stencilThenCoverStrokePathInstanced(
-                            count, gIndexType2GLType[indexType], indices, baseID,
+                            count, gIndexType2GLType[indexType], indices, glPathRange->basePathID(),
                             0xffff, writeMask, GR_GL_BOUNDING_BOX_OF_BOUNDING_BOXES,
                             gXformType2GLType[transformType], transformValues);
     } else {
         this->stencilThenCoverFillPathInstanced(
-                            count, gIndexType2GLType[indexType], indices, baseID,
+                            count, gIndexType2GLType[indexType], indices, glPathRange->basePathID(),
                             fillMode, writeMask, GR_GL_BOUNDING_BOX_OF_BOUNDING_BOXES,
                             gXformType2GLType[transformType], transformValues);
     }
index 25e8ef0..84bac6c 100644 (file)
@@ -33,12 +33,12 @@ public:
     virtual ~GrGLPathRendering();
 
     // GrPathRendering implementations.
-    GrPath* createPath(const SkPath&, const SkStrokeRec&) override;
+    GrPath* createPath(const SkPath&, const GrStrokeInfo&) override;
     virtual GrPathRange* createPathRange(GrPathRange::PathGenerator*,
-                                         const SkStrokeRec&) override;
+                                         const GrStrokeInfo&) override;
     virtual GrPathRange* createGlyphs(const SkTypeface*,
                                       const SkDescriptor*,
-                                      const SkStrokeRec&) override;
+                                      const GrStrokeInfo&) override;
     void stencilPath(const GrPath*, const GrStencilSettings&) override;
     void drawPath(const GrPath*, const GrStencilSettings&) override;
     virtual void drawPaths(const GrPathRange*, const void* indices, PathIndexType,