Add a GrPathRange class
Adds a GrPathRange object that represents a range of paths on the gpu.
Updates GrDrawTarget::drawPaths and supporting code to use GrPathRange
instead of an array of GrPath objects.
Change-Id: I67845f3893cd4d955db947d699aa3733cbb081e0
BUG=skia:
R=bsalomon@google.com, jvanverth@google.com, kkinnunen@nvidia.com
Author: cdalton@nvidia.com
Review URL: https://codereview.chromium.org/400713003
diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi
index ca2dc1d..603a2fd 100644
--- a/gyp/gpu.gypi
+++ b/gyp/gpu.gypi
@@ -94,6 +94,7 @@
'<(skia_src_path)/gpu/GrPaint.cpp',
'<(skia_src_path)/gpu/GrPath.cpp',
'<(skia_src_path)/gpu/GrPath.h',
+ '<(skia_src_path)/gpu/GrPathRange.h',
'<(skia_src_path)/gpu/GrPathRendererChain.cpp',
'<(skia_src_path)/gpu/GrPathRenderer.cpp',
'<(skia_src_path)/gpu/GrPathRenderer.h',
@@ -200,6 +201,8 @@
'<(skia_src_path)/gpu/gl/GrGLNoOpInterface.h',
'<(skia_src_path)/gpu/gl/GrGLPath.cpp',
'<(skia_src_path)/gpu/gl/GrGLPath.h',
+ '<(skia_src_path)/gpu/gl/GrGLPathRange.cpp',
+ '<(skia_src_path)/gpu/gl/GrGLPathRange.h',
'<(skia_src_path)/gpu/gl/GrGLProgram.cpp',
'<(skia_src_path)/gpu/gl/GrGLProgram.h',
'<(skia_src_path)/gpu/gl/GrGLProgramDesc.cpp',
diff --git a/include/core/SkStrokeRec.h b/include/core/SkStrokeRec.h
index 42bed8c..0c5892f 100644
--- a/include/core/SkStrokeRec.h
+++ b/include/core/SkStrokeRec.h
@@ -30,6 +30,9 @@
kStroke_Style,
kStrokeAndFill_Style
};
+ enum {
+ kStyleCount = kStrokeAndFill_Style + 1
+ };
Style getStyle() const;
SkScalar getWidth() const { return fWidth; }
diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp
index 483a9a5..72204fa 100644
--- a/src/gpu/GrDrawTarget.cpp
+++ b/src/gpu/GrDrawTarget.cpp
@@ -548,34 +548,25 @@
this->onDrawPath(path, fill, dstCopy.texture() ? &dstCopy : NULL);
}
-void GrDrawTarget::drawPaths(int pathCount, const GrPath** paths,
- const SkMatrix* transforms,
- SkPath::FillType fill, SkStrokeRec::Style stroke) {
- SkASSERT(pathCount > 0);
- SkASSERT(NULL != paths);
- SkASSERT(NULL != paths[0]);
+void GrDrawTarget::drawPaths(const GrPathRange* pathRange,
+ const uint32_t indices[], int count,
+ const float transforms[], PathTransformType transformsType,
+ SkPath::FillType fill) {
SkASSERT(this->caps()->pathRenderingSupport());
- SkASSERT(!SkPath::IsInverseFillType(fill));
+ SkASSERT(NULL != pathRange);
+ SkASSERT(NULL != indices);
+ SkASSERT(NULL != transforms);
- const GrDrawState* drawState = &getDrawState();
-
- SkRect devBounds;
- transforms[0].mapRect(&devBounds, paths[0]->getBounds());
- for (int i = 1; i < pathCount; ++i) {
- SkRect mappedPathBounds;
- transforms[i].mapRect(&mappedPathBounds, paths[i]->getBounds());
- devBounds.join(mappedPathBounds);
- }
-
- SkMatrix viewM = drawState->getViewMatrix();
- viewM.mapRect(&devBounds);
-
+ // Don't compute a bounding box for setupDstReadIfNecessary(), we'll opt
+ // instead for it to just copy the entire dst. Realistically this is a moot
+ // point, because any context that supports NV_path_rendering will also
+ // support NV_blend_equation_advanced.
GrDeviceCoordTexture dstCopy;
- if (!this->setupDstReadIfNecessary(&dstCopy, &devBounds)) {
+ if (!this->setupDstReadIfNecessary(&dstCopy, NULL)) {
return;
}
- this->onDrawPaths(pathCount, paths, transforms, fill, stroke,
+ this->onDrawPaths(pathRange, indices, count, transforms, transformsType, fill,
dstCopy.texture() ? &dstCopy : NULL);
}
diff --git a/src/gpu/GrDrawTarget.h b/src/gpu/GrDrawTarget.h
index 7898dfd..c868e56 100644
--- a/src/gpu/GrDrawTarget.h
+++ b/src/gpu/GrDrawTarget.h
@@ -26,6 +26,7 @@
class GrClipData;
class GrDrawTargetCaps;
class GrPath;
+class GrPathRange;
class GrVertexBuffer;
class GrDrawTarget : public SkRefCnt {
@@ -349,14 +350,45 @@
* Draws many paths. It will respect the HW
* antialias flag on the draw state (if possible in the 3D API).
*
- * @param transforms array of 2d affine transformations, one for each path.
- * @param fill the fill type for drawing all the paths. Fill must not be a
- * hairline.
- * @param stroke the stroke for drawing all the paths.
+ * @param pathRange Source of paths to draw from
+ * @param indices Array of indices into the the pathRange
+ * @param count Number of paths to draw (length of indices array)
+ * @param transforms Array of individual transforms, one for each path
+ * @param transformsType Type of transformations in the array. Array contains
+ PathTransformSize(transformsType) × count elements
+ * @param fill Fill type for drawing all the paths
*/
- void drawPaths(int pathCount, const GrPath** paths,
- const SkMatrix* transforms, SkPath::FillType fill,
- SkStrokeRec::Style stroke);
+ enum PathTransformType {
+ kNone_PathTransformType, //!< []
+ kTranslateX_PathTransformType, //!< [kMTransX]
+ kTranslateY_PathTransformType, //!< [kMTransY]
+ kTranslate_PathTransformType, //!< [kMTransX, kMTransY]
+ kAffine_PathTransformType, //!< [kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY]
+
+ kLast_PathTransformType = kAffine_PathTransformType
+ };
+ void drawPaths(const GrPathRange* pathRange,
+ const uint32_t indices[], int count,
+ const float transforms[], PathTransformType transformsType,
+ SkPath::FillType fill);
+
+ static inline int PathTransformSize(PathTransformType type) {
+ switch (type) {
+ case kNone_PathTransformType:
+ return 0;
+ case kTranslateX_PathTransformType:
+ case kTranslateY_PathTransformType:
+ return 1;
+ case kTranslate_PathTransformType:
+ return 2;
+ case kAffine_PathTransformType:
+ return 6;
+
+ default:
+ SkFAIL("Unknown path transform type");
+ return 0;
+ }
+ }
/**
* Helper function for drawing rects. It performs a geometry src push and pop
@@ -516,11 +548,12 @@
/**
* For subclass internal use to invoke a call to onDrawPaths().
*/
- void executeDrawPaths(int pathCount, const GrPath** paths,
- const SkMatrix* transforms, SkPath::FillType fill,
- SkStrokeRec::Style stroke,
+ void executeDrawPaths(const GrPathRange* pathRange,
+ const uint32_t indices[], int count,
+ const float transforms[], PathTransformType transformsType,
+ SkPath::FillType fill,
const GrDeviceCoordTexture* dstCopy) {
- this->onDrawPaths(pathCount, paths, transforms, fill, stroke, dstCopy);
+ this->onDrawPaths(pathRange, indices, count, transforms, transformsType, fill, dstCopy);
}
inline bool isGpuTracingEnabled() const {
@@ -909,9 +942,10 @@
virtual void onStencilPath(const GrPath*, SkPath::FillType) = 0;
virtual void onDrawPath(const GrPath*, SkPath::FillType,
const GrDeviceCoordTexture* dstCopy) = 0;
- virtual void onDrawPaths(int, const GrPath**, const SkMatrix*,
- SkPath::FillType, SkStrokeRec::Style,
- const GrDeviceCoordTexture* dstCopy) = 0;
+ virtual void onDrawPaths(const GrPathRange*,
+ const uint32_t indices[], int count,
+ const float transforms[], PathTransformType,
+ SkPath::FillType, const GrDeviceCoordTexture*) = 0;
virtual void didAddGpuTraceMarker() = 0;
virtual void didRemoveGpuTraceMarker() = 0;
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index fd249de..840f8ee 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -218,6 +218,12 @@
return this->onCreatePath(path, stroke);
}
+GrPathRange* GrGpu::createPathRange(size_t size, const SkStrokeRec& stroke) {
+ SkASSERT(this->caps()->pathRenderingSupport());
+ this->handleDirtyContext();
+ return this->onCreatePathRange(size, stroke);
+}
+
void GrGpu::clear(const SkIRect* rect,
GrColor color,
bool canIgnoreRect,
@@ -419,10 +425,10 @@
this->onGpuDrawPath(path, fill);
}
-void GrGpu::onDrawPaths(int pathCount, const GrPath** paths,
- const SkMatrix* transforms, SkPath::FillType fill,
- SkStrokeRec::Style style,
- const GrDeviceCoordTexture* dstCopy) {
+void GrGpu::onDrawPaths(const GrPathRange* pathRange,
+ const uint32_t indices[], int count,
+ const float transforms[], PathTransformType transformsType,
+ SkPath::FillType fill, const GrDeviceCoordTexture* dstCopy) {
this->handleDirtyContext();
drawState()->setDefaultVertexAttribs();
@@ -432,7 +438,7 @@
return;
}
- this->onGpuDrawPaths(pathCount, paths, transforms, fill, style);
+ this->onGpuDrawPaths(pathRange, indices, count, transforms, transformsType, fill);
}
void GrGpu::finalizeReservedVertices() {
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index cd7502e..17b7b69 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -16,6 +16,7 @@
class GrGpuObject;
class GrIndexBufferAllocPool;
class GrPath;
+class GrPathRange;
class GrPathRenderer;
class GrPathRendererChain;
class GrStencilBuffer;
@@ -138,6 +139,13 @@
GrPath* createPath(const SkPath& path, const SkStrokeRec& stroke);
/**
+ * Creates a path range object that can be used to draw multiple paths via
+ * drawPaths(). It is only legal to call this if the caps report support for
+ * path rendering.
+ */
+ GrPathRange* createPathRange(size_t size, const SkStrokeRec&);
+
+ /**
* Returns an index buffer that can be used to render quads.
* Six indices per quad: 0, 1, 2, 0, 2, 3, etc.
* The max number of quads can be queried using GrIndexBuffer::maxQuads().
@@ -432,6 +440,7 @@
virtual GrVertexBuffer* onCreateVertexBuffer(size_t size, bool dynamic) = 0;
virtual GrIndexBuffer* onCreateIndexBuffer(size_t size, bool dynamic) = 0;
virtual GrPath* onCreatePath(const SkPath& path, const SkStrokeRec&) = 0;
+ virtual GrPathRange* onCreatePathRange(size_t size, const SkStrokeRec&) = 0;
// overridden by backend-specific derived class to perform the clear and
// clearRect. NULL rect means clear whole target. If canIgnoreRect is
@@ -444,8 +453,10 @@
// overridden by backend-specific derived class to perform the path stenciling.
virtual void onGpuStencilPath(const GrPath*, SkPath::FillType) = 0;
virtual void onGpuDrawPath(const GrPath*, SkPath::FillType) = 0;
- virtual void onGpuDrawPaths(int, const GrPath**, const SkMatrix*,
- SkPath::FillType, SkStrokeRec::Style) = 0;
+ virtual void onGpuDrawPaths(const GrPathRange*,
+ const uint32_t indices[], int count,
+ const float transforms[], PathTransformType,
+ SkPath::FillType) = 0;
// overridden by backend-specific derived class to perform the read pixels.
virtual bool onReadPixels(GrRenderTarget* target,
@@ -488,9 +499,10 @@
virtual void onStencilPath(const GrPath*, SkPath::FillType) SK_OVERRIDE;
virtual void onDrawPath(const GrPath*, SkPath::FillType,
const GrDeviceCoordTexture* dstCopy) SK_OVERRIDE;
- virtual void onDrawPaths(int, const GrPath**, const SkMatrix*,
- SkPath::FillType, SkStrokeRec::Style,
- const GrDeviceCoordTexture* dstCopy) SK_OVERRIDE;
+ virtual void onDrawPaths(const GrPathRange*,
+ const uint32_t indices[], int count,
+ const float transforms[], PathTransformType,
+ SkPath::FillType, const GrDeviceCoordTexture*) 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 dfff247..26b5452 100644
--- a/src/gpu/GrInOrderDrawBuffer.cpp
+++ b/src/gpu/GrInOrderDrawBuffer.cpp
@@ -13,6 +13,7 @@
#include "GrGpu.h"
#include "GrIndexBuffer.h"
#include "GrPath.h"
+#include "GrPathRange.h"
#include "GrRenderTarget.h"
#include "GrTemplates.h"
#include "GrTexture.h"
@@ -419,10 +420,9 @@
if (fTransforms) {
SkDELETE_ARRAY(fTransforms);
}
- for (int i = 0; i < fPathCount; ++i) {
- fPaths[i]->unref();
+ if (fIndices) {
+ SkDELETE_ARRAY(fIndices);
}
- SkDELETE_ARRAY(fPaths);
}
void GrInOrderDrawBuffer::onStencilPath(const GrPath* path, SkPath::FillType fill) {
@@ -457,12 +457,13 @@
}
}
-void GrInOrderDrawBuffer::onDrawPaths(int pathCount, const GrPath** paths,
- const SkMatrix* transforms,
- SkPath::FillType fill,
- SkStrokeRec::Style stroke,
- const GrDeviceCoordTexture* dstCopy) {
- SkASSERT(pathCount);
+void GrInOrderDrawBuffer::onDrawPaths(const GrPathRange* pathRange,
+ const uint32_t indices[], int count,
+ const float transforms[], PathTransformType transformsType,
+ SkPath::FillType fill, const GrDeviceCoordTexture* dstCopy) {
+ SkASSERT(NULL != pathRange);
+ SkASSERT(NULL != indices);
+ SkASSERT(NULL != transforms);
if (this->needsNewClip()) {
this->recordClip();
@@ -471,18 +472,17 @@
this->recordState();
}
DrawPaths* dp = this->recordDrawPaths();
- dp->fPathCount = pathCount;
- dp->fPaths = SkNEW_ARRAY(const GrPath*, pathCount);
- memcpy(dp->fPaths, paths, sizeof(GrPath*) * pathCount);
- for (int i = 0; i < pathCount; ++i) {
- dp->fPaths[i]->ref();
- }
+ dp->fPathRange.reset(SkRef(pathRange));
+ dp->fIndices = SkNEW_ARRAY(uint32_t, count); // TODO: Accomplish this without a malloc
+ memcpy(dp->fIndices, indices, sizeof(uint32_t) * count);
+ dp->fCount = count;
- dp->fTransforms = SkNEW_ARRAY(SkMatrix, pathCount);
- memcpy(dp->fTransforms, transforms, sizeof(SkMatrix) * pathCount);
+ const int transformsLength = PathTransformSize(transformsType) * count;
+ dp->fTransforms = SkNEW_ARRAY(float, transformsLength);
+ memcpy(dp->fTransforms, transforms, sizeof(float) * transformsLength);
+ dp->fTransformsType = transformsType;
dp->fFill = fill;
- dp->fStroke = stroke;
if (NULL != dstCopy) {
dp->fDstCopy = *dstCopy;
@@ -633,9 +633,13 @@
SkAssertResult(drawPathsIter.next());
const GrDeviceCoordTexture* dstCopy =
NULL !=drawPathsIter->fDstCopy.texture() ? &drawPathsIter->fDstCopy : NULL;
- fDstGpu->executeDrawPaths(drawPathsIter->fPathCount, drawPathsIter->fPaths,
- drawPathsIter->fTransforms, drawPathsIter->fFill,
- drawPathsIter->fStroke, dstCopy);
+ fDstGpu->executeDrawPaths(drawPathsIter->fPathRange.get(),
+ drawPathsIter->fIndices,
+ drawPathsIter->fCount,
+ drawPathsIter->fTransforms,
+ drawPathsIter->fTransformsType,
+ drawPathsIter->fFill,
+ dstCopy);
break;
}
case kSetState_Cmd:
diff --git a/src/gpu/GrInOrderDrawBuffer.h b/src/gpu/GrInOrderDrawBuffer.h
index 3025c06..b18112e 100644
--- a/src/gpu/GrInOrderDrawBuffer.h
+++ b/src/gpu/GrInOrderDrawBuffer.h
@@ -19,6 +19,7 @@
class GrGpu;
class GrIndexBufferAllocPool;
+class GrPathRange;
class GrVertexBufferAllocPool;
/**
@@ -119,11 +120,12 @@
DrawPaths();
~DrawPaths();
- int fPathCount;
- const GrPath** fPaths;
- SkMatrix* fTransforms;
+ SkAutoTUnref<const GrPathRange> fPathRange;
+ uint32_t* fIndices;
+ size_t fCount;
+ float* fTransforms;
+ PathTransformType fTransformsType;
SkPath::FillType fFill;
- SkStrokeRec::Style fStroke;
GrDeviceCoordTexture fDstCopy;
};
@@ -160,9 +162,10 @@
virtual void onStencilPath(const GrPath*, SkPath::FillType) SK_OVERRIDE;
virtual void onDrawPath(const GrPath*, SkPath::FillType,
const GrDeviceCoordTexture* dstCopy) SK_OVERRIDE;
- virtual void onDrawPaths(int, const GrPath**, const SkMatrix*,
- SkPath::FillType, SkStrokeRec::Style,
- const GrDeviceCoordTexture* dstCopy) SK_OVERRIDE;
+ virtual void onDrawPaths(const GrPathRange*,
+ const uint32_t indices[], int count,
+ const float transforms[], PathTransformType,
+ SkPath::FillType, const GrDeviceCoordTexture*) SK_OVERRIDE;
virtual bool onReserveVertexSpace(size_t vertexSize,
int vertexCount,
diff --git a/src/gpu/GrPath.cpp b/src/gpu/GrPath.cpp
index adb3fe6..5426e49 100644
--- a/src/gpu/GrPath.cpp
+++ b/src/gpu/GrPath.cpp
@@ -7,25 +7,54 @@
#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);
+}
+
GrResourceKey GrPath::ComputeKey(const SkPath& path, const SkStrokeRec& stroke) {
static const GrResourceKey::ResourceType gPathResourceType = GrResourceKey::GenerateResourceType();
static const GrCacheID::Domain gPathDomain = GrCacheID::GenerateDomain();
GrCacheID::Key key;
- uint32_t* keyData = key.fData32;
+ uint64_t* keyData = key.fData64;
keyData[0] = path.getGenerationID();
-
- SK_COMPILE_ASSERT(SkPaint::kJoinCount <= 3, cap_shift_will_be_wrong);
- keyData[1] = stroke.needToApply();
- if (0 != keyData[1]) {
- keyData[1] |= stroke.getJoin() << 1;
- keyData[1] |= stroke.getCap() << 3;
- keyData[2] = static_cast<uint32_t>(stroke.getMiter());
- keyData[3] = static_cast<uint32_t>(stroke.getWidth());
- } else {
- keyData[2] = 0;
- keyData[3] = 0;
- }
+ keyData[1] = ComputeStrokeKey(stroke);
return GrResourceKey(GrCacheID(gPathDomain, key), gPathResourceType, 0);
}
+
+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;
+ }
+
+ 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;
+}
diff --git a/src/gpu/GrPath.h b/src/gpu/GrPath.h
index d324e6a..2c0040c 100644
--- a/src/gpu/GrPath.h
+++ b/src/gpu/GrPath.h
@@ -18,6 +18,9 @@
public:
SK_DECLARE_INST_COUNT(GrPath);
+ /**
+ * Initialize to a path with a fixed stroke. Stroke must not be hairline.
+ */
GrPath(GrGpu* gpu, bool isWrapped, const SkPath& skPath, const SkStrokeRec& stroke)
: INHERITED(gpu, isWrapped),
fSkPath(skPath),
@@ -26,6 +29,7 @@
}
static GrResourceKey ComputeKey(const SkPath& path, const SkStrokeRec& stroke);
+ static uint64_t ComputeStrokeKey(const SkStrokeRec&);
bool isEqualTo(const SkPath& path, const SkStrokeRec& stroke) {
return fSkPath == path && fStroke == stroke;
diff --git a/src/gpu/GrPathRange.h b/src/gpu/GrPathRange.h
new file mode 100644
index 0000000..02342c4
--- /dev/null
+++ b/src/gpu/GrPathRange.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrPathRange_DEFINED
+#define GrPathRange_DEFINED
+
+#include "GrGpuObject.h"
+#include "GrResourceCache.h"
+#include "SkStrokeRec.h"
+
+class SkPath;
+
+/**
+ * Represents a contiguous range of GPU path objects with a common stroke. The
+ * path range is immutable with the exception that individual paths can be
+ * initialized lazily. Unititialized paths are silently ignored by drawing
+ * functions.
+ */
+class GrPathRange : public GrGpuObject {
+public:
+ SK_DECLARE_INST_COUNT(GrPathRange);
+
+ static const bool kIsWrapped = false;
+
+ static GrResourceKey::ResourceType resourceType() {
+ static const GrResourceKey::ResourceType type = GrResourceKey::GenerateResourceType();
+ return type;
+ }
+
+ /**
+ * Initialize to a range with a fixed size and stroke. Stroke must not be hairline.
+ */
+ GrPathRange(GrGpu* gpu, size_t size, const SkStrokeRec& stroke)
+ : INHERITED(gpu, kIsWrapped),
+ fSize(size),
+ fStroke(stroke) {
+ }
+
+ size_t getSize() const { return fSize; }
+ const SkStrokeRec& getStroke() const { return fStroke; }
+
+ /**
+ * Initialize a path in the range. It is invalid to call this method for a
+ * path that has already been initialized.
+ */
+ virtual void initAt(size_t index, const SkPath&) = 0;
+
+protected:
+ size_t fSize;
+ SkStrokeRec fStroke;
+
+private:
+ typedef GrGpuObject INHERITED;
+};
+
+#endif
diff --git a/src/gpu/GrStencilAndCoverTextContext.cpp b/src/gpu/GrStencilAndCoverTextContext.cpp
index 637c85a..65c2642 100644
--- a/src/gpu/GrStencilAndCoverTextContext.cpp
+++ b/src/gpu/GrStencilAndCoverTextContext.cpp
@@ -7,11 +7,9 @@
#include "GrStencilAndCoverTextContext.h"
#include "GrDrawTarget.h"
-#include "GrFontScaler.h"
#include "GrGpu.h"
#include "GrPath.h"
-#include "GrTextStrike.h"
-#include "GrTextStrike_impl.h"
+#include "GrPathRange.h"
#include "SkAutoKern.h"
#include "SkDraw.h"
#include "SkDrawProcs.h"
@@ -20,12 +18,95 @@
#include "SkPath.h"
#include "SkTextMapStateProc.h"
-static const int kMaxReservedGlyphs = 64;
+class GrStencilAndCoverTextContext::GlyphPathRange : public GrCacheable {
+ static const int kMaxGlyphCount = 1 << 16; // Glyph IDs are uint16_t's
+ static const int kGlyphGroupSize = 16; // Glyphs get tracked in groups of 16
+
+public:
+ static GlyphPathRange* Create(GrContext* context,
+ SkGlyphCache* cache,
+ const SkStrokeRec& stroke) {
+ static const GrCacheID::Domain gGlyphPathRangeDomain = GrCacheID::GenerateDomain();
+
+ GrCacheID::Key key;
+ key.fData32[0] = cache->getDescriptor().getChecksum();
+ key.fData32[1] = cache->getScalerContext()->getTypeface()->uniqueID();
+ key.fData64[1] = GrPath::ComputeStrokeKey(stroke);
+
+ GrResourceKey resourceKey(GrCacheID(gGlyphPathRangeDomain, key),
+ GrPathRange::resourceType(), 0);
+ SkAutoTUnref<GlyphPathRange> glyphs(
+ static_cast<GlyphPathRange*>(context->findAndRefCachedResource(resourceKey)));
+
+ if (NULL == glyphs ||
+ !glyphs->fDesc->equals(cache->getDescriptor() /*checksum collision*/)) {
+ glyphs.reset(SkNEW_ARGS(GlyphPathRange, (context, cache->getDescriptor(), stroke)));
+ context->addResourceToCache(resourceKey, glyphs);
+ }
+
+ return glyphs.detach();
+ }
+
+ const GrPathRange* pathRange() const { return fPathRange.get(); }
+
+ void preloadGlyph(uint16_t glyphID, SkGlyphCache* cache) {
+ const uint16_t groupIndex = glyphID / kGlyphGroupSize;
+ const uint16_t groupByte = groupIndex >> 3;
+ const uint8_t groupBit = 1 << (groupIndex & 7);
+
+ const bool hasGlyph = 0 != (fLoadedGlyphs[groupByte] & groupBit);
+ if (hasGlyph) {
+ return;
+ }
+
+ // We track which glyphs are loaded in groups of kGlyphGroupSize. To
+ // mark a glyph loaded we need to load the entire group.
+ const uint16_t groupFirstID = groupIndex * kGlyphGroupSize;
+ const uint16_t groupLastID = groupFirstID + kGlyphGroupSize - 1;
+ SkPath skPath;
+ for (int id = groupFirstID; id <= groupLastID; ++id) {
+ const SkGlyph& skGlyph = cache->getGlyphIDMetrics(id);
+ if (const SkPath* skPath = cache->findPath(skGlyph)) {
+ fPathRange->initAt(id, *skPath);
+ } // GrGpu::drawPaths will silently ignore undefined paths.
+ }
+
+ fLoadedGlyphs[groupByte] |= groupBit;
+ this->didChangeGpuMemorySize();
+ }
+
+ // GrCacheable overrides
+ virtual size_t gpuMemorySize() const SK_OVERRIDE { return fPathRange->gpuMemorySize(); }
+ virtual bool isValidOnGpu() const SK_OVERRIDE { return fPathRange->isValidOnGpu(); }
+
+private:
+ GlyphPathRange(GrContext* context, const SkDescriptor& desc, const SkStrokeRec& stroke)
+ : fDesc(desc.copy())
+ // We reserve a range of kMaxGlyphCount paths because of fallbacks fonts. We
+ // can't know exactly how many glyphs we might need without preloading every
+ // fallback, which we don't want to do at this point.
+ , fPathRange(context->getGpu()->createPathRange(kMaxGlyphCount, stroke)) {
+ memset(fLoadedGlyphs, 0, sizeof(fLoadedGlyphs));
+ }
+
+ ~GlyphPathRange() {
+ SkDescriptor::Free(fDesc);
+ }
+
+ static const int kMaxGroupCount = (kMaxGlyphCount + (kGlyphGroupSize - 1)) / kGlyphGroupSize;
+ SkDescriptor* const fDesc;
+ uint8_t fLoadedGlyphs[(kMaxGroupCount + 7) >> 3]; // One bit per glyph group
+ SkAutoTUnref<GrPathRange> fPathRange;
+
+ typedef GrCacheable INHERITED;
+};
+
GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(
GrContext* context, const SkDeviceProperties& properties)
: GrTextContext(context, properties)
- , fStroke(SkStrokeRec::kFill_InitStyle) {
+ , fStroke(SkStrokeRec::kFill_InitStyle)
+ , fPendingGlyphCount(0) {
}
GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
@@ -73,10 +154,8 @@
SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, glyphCacheTransform);
- SkGlyphCache* cache = autoCache.getCache();
- GrFontScaler* scaler = GetGrFontScaler(cache);
- GrTextStrike* strike =
- fContext->getFontCache()->getStrike(scaler, true);
+ fGlyphCache = autoCache.getCache();
+ fGlyphs = GlyphPathRange::Create(fContext, fGlyphCache, fStroke);
const char* stop = text + byteLength;
@@ -89,7 +168,7 @@
while (textPtr < stop) {
// We don't need x, y here, since all subpixel variants will have the
// same advance.
- const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0);
+ const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &textPtr, 0, 0);
stopX += glyph.fAdvanceX;
stopY += glyph.fAdvanceY;
@@ -115,17 +194,10 @@
SkFixed fx = SkScalarToFixed(x);
SkFixed fy = SkScalarToFixed(y);
while (text < stop) {
- const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
+ const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
fx += SkFixedMul_portable(autokern.adjust(glyph), fixedSizeRatio);
if (glyph.fWidth) {
- this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
- glyph.getSubXFixed(),
- glyph.getSubYFixed()),
- SkPoint::Make(
- SkFixedToScalar(fx),
- SkFixedToScalar(fy)),
- strike,
- scaler);
+ this->appendGlyph(glyph.getGlyphID(), SkFixedToScalar(fx), SkFixedToScalar(fy));
}
fx += SkFixedMul_portable(glyph.fAdvanceX, fixedSizeRatio);
@@ -164,10 +236,8 @@
SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, NULL);
- SkGlyphCache* cache = autoCache.getCache();
- GrFontScaler* scaler = GetGrFontScaler(cache);
- GrTextStrike* strike =
- fContext->getFontCache()->getStrike(scaler, true);
+ fGlyphCache = autoCache.getCache();
+ fGlyphs = GlyphPathRange::Create(fContext, fGlyphCache, fStroke);
const char* stop = text + byteLength;
SkTextAlignProcScalar alignProc(fSkPaint.getTextAlign());
@@ -177,33 +247,22 @@
while (text < stop) {
SkPoint loc;
tmsProc(pos, &loc);
- const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
+ const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
if (glyph.fWidth) {
- this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
- glyph.getSubXFixed(),
- glyph.getSubYFixed()),
- loc,
- strike,
- scaler);
+ this->appendGlyph(glyph.getGlyphID(), loc.x(), loc.y());
}
pos += scalarsPerPosition;
}
} else {
while (text < stop) {
- const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
+ const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
if (glyph.fWidth) {
SkPoint tmsLoc;
tmsProc(pos, &tmsLoc);
SkPoint loc;
alignProc(tmsLoc, glyph, &loc);
- this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
- glyph.getSubXFixed(),
- glyph.getSubYFixed()),
- loc,
- strike,
- scaler);
-
+ this->appendGlyph(glyph.getGlyphID(), loc.x(), loc.y());
}
pos += scalarsPerPosition;
}
@@ -305,62 +364,45 @@
*fDrawTarget->drawState()->stencil() = kStencilPass;
- size_t reserveAmount;
- switch (skPaint.getTextEncoding()) {
- default:
- SkASSERT(false);
- case SkPaint::kUTF8_TextEncoding:
- reserveAmount = textByteLength;
- break;
- case SkPaint::kUTF16_TextEncoding:
- reserveAmount = textByteLength / 2;
- break;
- case SkPaint::kUTF32_TextEncoding:
- case SkPaint::kGlyphID_TextEncoding:
- reserveAmount = textByteLength / 4;
- break;
- }
- fPaths.setReserve(reserveAmount);
- fTransforms.setReserve(reserveAmount);
+ SkASSERT(0 == fPendingGlyphCount);
}
-inline void GrStencilAndCoverTextContext::appendGlyph(GrGlyph::PackedID glyphID,
- const SkPoint& pos,
- GrTextStrike* strike,
- GrFontScaler* scaler) {
- GrGlyph* glyph = strike->getGlyph(glyphID, scaler);
- if (NULL == glyph || glyph->fBounds.isEmpty()) {
+inline void GrStencilAndCoverTextContext::appendGlyph(uint16_t glyphID, float x, float y) {
+ if (fPendingGlyphCount >= kGlyphBufferSize) {
+ this->flush();
+ }
+
+ fGlyphs->preloadGlyph(glyphID, fGlyphCache);
+
+ fIndexBuffer[fPendingGlyphCount] = glyphID;
+ fTransformBuffer[6 * fPendingGlyphCount + 0] = fTextRatio;
+ fTransformBuffer[6 * fPendingGlyphCount + 1] = 0;
+ fTransformBuffer[6 * fPendingGlyphCount + 2] = x;
+ fTransformBuffer[6 * fPendingGlyphCount + 3] = 0;
+ fTransformBuffer[6 * fPendingGlyphCount + 4] = fTextRatio;
+ fTransformBuffer[6 * fPendingGlyphCount + 5] = y;
+
+ ++fPendingGlyphCount;
+}
+
+void GrStencilAndCoverTextContext::flush() {
+ if (0 == fPendingGlyphCount) {
return;
}
- if (scaler->getGlyphPath(glyph->glyphID(), &fTmpPath)) {
- if (!fTmpPath.isEmpty()) {
- *fPaths.append() = fContext->createPath(fTmpPath, fStroke);
- SkMatrix* t = fTransforms.append();
- t->setTranslate(pos.fX, pos.fY);
- t->preScale(fTextRatio, fTextRatio);
- }
- }
+ fDrawTarget->drawPaths(fGlyphs->pathRange(), fIndexBuffer, fPendingGlyphCount,
+ fTransformBuffer, GrDrawTarget::kAffine_PathTransformType,
+ SkPath::kWinding_FillType);
+
+ fPendingGlyphCount = 0;
}
void GrStencilAndCoverTextContext::finish() {
- if (fPaths.count() > 0) {
- fDrawTarget->drawPaths(static_cast<size_t>(fPaths.count()),
- fPaths.begin(), fTransforms.begin(),
- SkPath::kWinding_FillType, fStroke.getStyle());
+ this->flush();
- for (int i = 0; i < fPaths.count(); ++i) {
- fPaths[i]->unref();
- }
- if (fPaths.count() > kMaxReservedGlyphs) {
- fPaths.reset();
- fTransforms.reset();
- } else {
- fPaths.rewind();
- fTransforms.rewind();
- }
- }
- fTmpPath.reset();
+ SkSafeUnref(fGlyphs);
+ fGlyphs = NULL;
+ fGlyphCache = NULL;
fDrawTarget->drawState()->stencil()->setDisabled();
fStateRestore.set(NULL);
diff --git a/src/gpu/GrStencilAndCoverTextContext.h b/src/gpu/GrStencilAndCoverTextContext.h
index 2e79f5c..021a84d 100644
--- a/src/gpu/GrStencilAndCoverTextContext.h
+++ b/src/gpu/GrStencilAndCoverTextContext.h
@@ -36,17 +36,23 @@
virtual bool canDraw(const SkPaint& paint) SK_OVERRIDE;
private:
+ class GlyphPathRange;
+ static const int kGlyphBufferSize = 1024;
+
void init(const GrPaint&, const SkPaint&, size_t textByteLength);
- void appendGlyph(GrGlyph::PackedID, const SkPoint&,
- GrTextStrike*, GrFontScaler*);
+ void initGlyphs(SkGlyphCache* cache);
+ void appendGlyph(uint16_t glyphID, float x, float y);
+ void flush();
void finish();
GrDrawState::AutoRestoreEffects fStateRestore;
SkScalar fTextRatio;
SkStrokeRec fStroke;
- SkTDArray<const GrPath*> fPaths;
- SkTDArray<SkMatrix> fTransforms;
- SkPath fTmpPath;
+ SkGlyphCache* fGlyphCache;
+ GlyphPathRange* fGlyphs;
+ uint32_t fIndexBuffer[kGlyphBufferSize];
+ float fTransformBuffer[6 * kGlyphBufferSize];
+ int fPendingGlyphCount;
SkMatrix fGlyphTransform;
bool fNeedsDeviceSpaceGlyphs;
};
diff --git a/src/gpu/gl/GrGLPath.cpp b/src/gpu/gl/GrGLPath.cpp
index bb26b12..1c74580 100644
--- a/src/gpu/gl/GrGLPath.cpp
+++ b/src/gpu/gl/GrGLPath.cpp
@@ -85,23 +85,21 @@
static const bool kIsWrapped = false; // The constructor creates the GL path object.
-GrGLPath::GrGLPath(GrGpuGL* gpu, const SkPath& path, const SkStrokeRec& stroke)
- : INHERITED(gpu, kIsWrapped, path, stroke) {
- SkASSERT(!path.isEmpty());
-
- fPathID = gpu->createGLPathObject();
-
+void GrGLPath::InitPathObject(const GrGLInterface* gl,
+ GrGLuint pathID,
+ const SkPath& skPath,
+ const SkStrokeRec& stroke) {
SkSTArray<16, GrGLubyte, true> pathCommands;
SkSTArray<16, SkPoint, true> pathPoints;
- int verbCnt = fSkPath.countVerbs();
- int pointCnt = fSkPath.countPoints();
+ int verbCnt = skPath.countVerbs();
+ int pointCnt = skPath.countPoints();
pathCommands.resize_back(verbCnt);
pathPoints.resize_back(pointCnt);
// TODO: Direct access to path points since we could pass them on directly.
- fSkPath.getPoints(&pathPoints[0], pointCnt);
- fSkPath.getVerbs(&pathCommands[0], verbCnt);
+ skPath.getPoints(&pathPoints[0], pointCnt);
+ skPath.getVerbs(&pathCommands[0], verbCnt);
SkDEBUGCODE(int numPts = 0);
for (int i = 0; i < verbCnt; ++i) {
@@ -111,21 +109,34 @@
}
SkASSERT(pathPoints.count() == numPts);
- GL_CALL(PathCommands(fPathID,
- verbCnt, &pathCommands[0],
- 2 * pointCnt, GR_GL_FLOAT, &pathPoints[0]));
+ GR_GL_CALL(gl, PathCommands(pathID,
+ verbCnt, &pathCommands[0],
+ 2 * pointCnt, GR_GL_FLOAT, &pathPoints[0]));
if (stroke.needToApply()) {
- GL_CALL(PathParameterf(fPathID, GR_GL_PATH_STROKE_WIDTH, SkScalarToFloat(stroke.getWidth())));
- GL_CALL(PathParameterf(fPathID, GR_GL_PATH_MITER_LIMIT, SkScalarToFloat(stroke.getMiter())));
+ SkASSERT(!stroke.isHairlineStyle());
+ GR_GL_CALL(gl, PathParameterf(pathID, GR_GL_PATH_STROKE_WIDTH, SkScalarToFloat(stroke.getWidth())));
+ GR_GL_CALL(gl, PathParameterf(pathID, GR_GL_PATH_MITER_LIMIT, SkScalarToFloat(stroke.getMiter())));
GrGLenum join = join_to_gl_join(stroke.getJoin());
- GL_CALL(PathParameteri(fPathID, GR_GL_PATH_JOIN_STYLE, join));
+ GR_GL_CALL(gl, PathParameteri(pathID, GR_GL_PATH_JOIN_STYLE, join));
GrGLenum cap = cap_to_gl_cap(stroke.getCap());
- GL_CALL(PathParameteri(fPathID, GR_GL_PATH_INITIAL_END_CAP, cap));
- GL_CALL(PathParameteri(fPathID, GR_GL_PATH_TERMINAL_END_CAP, cap));
+ GR_GL_CALL(gl, PathParameteri(pathID, GR_GL_PATH_INITIAL_END_CAP, cap));
+ GR_GL_CALL(gl, PathParameteri(pathID, GR_GL_PATH_TERMINAL_END_CAP, cap));
+ }
+}
+GrGLPath::GrGLPath(GrGpuGL* gpu, const SkPath& path, const SkStrokeRec& stroke)
+ : INHERITED(gpu, kIsWrapped, path, stroke) {
+ SkASSERT(!path.isEmpty());
+
+ fPathID = gpu->createGLPathObject();
+
+ InitPathObject(static_cast<GrGpuGL*>(this->getGpu())->glInterface(),
+ fPathID, fSkPath, stroke);
+
+ if (stroke.needToApply()) {
// FIXME: try to account for stroking, without rasterizing the stroke.
- fBounds.outset(SkScalarToFloat(stroke.getWidth()), SkScalarToFloat(stroke.getWidth()));
+ fBounds.outset(stroke.getWidth(), stroke.getWidth());
}
}
diff --git a/src/gpu/gl/GrGLPath.h b/src/gpu/gl/GrGLPath.h
index 3409547..4831b72 100644
--- a/src/gpu/gl/GrGLPath.h
+++ b/src/gpu/gl/GrGLPath.h
@@ -13,6 +13,7 @@
#include "gl/GrGLFunctions.h"
class GrGpuGL;
+struct GrGLInterface;
/**
* Currently this represents a path built using GL_NV_path_rendering. If we
@@ -22,6 +23,11 @@
class GrGLPath : public GrPath {
public:
+ static void InitPathObject(const GrGLInterface*,
+ GrGLuint pathID,
+ const SkPath&,
+ const SkStrokeRec&);
+
GrGLPath(GrGpuGL* gpu, const SkPath& path, const SkStrokeRec& stroke);
virtual ~GrGLPath();
GrGLuint pathID() const { return fPathID; }
diff --git a/src/gpu/gl/GrGLPathRange.cpp b/src/gpu/gl/GrGLPathRange.cpp
new file mode 100644
index 0000000..2df04e3
--- /dev/null
+++ b/src/gpu/gl/GrGLPathRange.cpp
@@ -0,0 +1,62 @@
+
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrGLPathRange.h"
+#include "GrGLPath.h"
+#include "GrGpuGL.h"
+
+#define GPUGL static_cast<GrGpuGL*>(this->getGpu())
+
+#define GL_CALL(X) GR_GL_CALL(GPUGL->glInterface(), X)
+#define GL_CALL_RET(R, X) GR_GL_CALL_RET(GPUGL->glInterface(), R, X)
+
+GrGLPathRange::GrGLPathRange(GrGpu* gpu, size_t size, const SkStrokeRec& stroke)
+ : INHERITED(gpu, size, stroke),
+ fNumDefinedPaths(0) {
+ GL_CALL_RET(fBasePathID, GenPaths(fSize));
+}
+
+GrGLPathRange::~GrGLPathRange() {
+ this->release();
+}
+
+void GrGLPathRange::initAt(size_t index, const SkPath& skPath) {
+ GrGpuGL* gpu = static_cast<GrGpuGL*>(this->getGpu());
+ if (NULL == gpu) {
+ return;
+ }
+
+#ifdef SK_DEBUG
+ // Make sure the path at this index hasn't been initted already.
+ GrGLboolean hasPathAtIndex;
+ GL_CALL_RET(hasPathAtIndex, IsPath(fBasePathID + index));
+ SkASSERT(GR_GL_FALSE == hasPathAtIndex);
+#endif
+
+ GrGLPath::InitPathObject(gpu->glInterface(), fBasePathID + index, skPath, fStroke);
+
+ ++fNumDefinedPaths;
+ this->didChangeGpuMemorySize();
+}
+
+void GrGLPathRange::onRelease() {
+ SkASSERT(NULL != this->getGpu());
+
+ if (0 != fBasePathID && !this->isWrapped()) {
+ GL_CALL(DeletePaths(fBasePathID, fSize));
+ fBasePathID = 0;
+ }
+
+ INHERITED::onRelease();
+}
+
+void GrGLPathRange::onAbandon() {
+ fBasePathID = 0;
+
+ INHERITED::onAbandon();
+}
diff --git a/src/gpu/gl/GrGLPathRange.h b/src/gpu/gl/GrGLPathRange.h
new file mode 100644
index 0000000..6c6b78e
--- /dev/null
+++ b/src/gpu/gl/GrGLPathRange.h
@@ -0,0 +1,48 @@
+
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrGLPathRange_DEFINED
+#define GrGLPathRange_DEFINED
+
+#include "../GrPathRange.h"
+#include "gl/GrGLFunctions.h"
+
+class GrGpuGL;
+
+/**
+ * Currently this represents a range of GL_NV_path_rendering Path IDs. If we
+ * support other GL path extensions then this would have to have a type enum
+ * and/or be subclassed.
+ */
+
+class GrGLPathRange : public GrPathRange {
+public:
+ GrGLPathRange(GrGpu*, size_t size, const SkStrokeRec&);
+ virtual ~GrGLPathRange();
+
+ GrGLuint basePathID() const { return fBasePathID; }
+
+ virtual void initAt(size_t index, const SkPath&);
+
+ // TODO: Use a better approximation for the individual path sizes.
+ virtual size_t gpuMemorySize() const SK_OVERRIDE {
+ return 100 * fNumDefinedPaths;
+ }
+
+protected:
+ virtual void onRelease() SK_OVERRIDE;
+ virtual void onAbandon() SK_OVERRIDE;
+
+private:
+ GrGLuint fBasePathID;
+ size_t fNumDefinedPaths;
+
+ typedef GrPathRange INHERITED;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGpuGL.cpp b/src/gpu/gl/GrGpuGL.cpp
index 6d47627..d29c141 100644
--- a/src/gpu/gl/GrGpuGL.cpp
+++ b/src/gpu/gl/GrGpuGL.cpp
@@ -10,6 +10,7 @@
#include "GrGLNameAllocator.h"
#include "GrGLStencilBuffer.h"
#include "GrGLPath.h"
+#include "GrGLPathRange.h"
#include "GrGLShaderBuilder.h"
#include "GrTemplates.h"
#include "GrTypes.h"
@@ -34,6 +35,21 @@
///////////////////////////////////////////////////////////////////////////////
+static const GrGLenum gXformType2GLType[] = {
+ GR_GL_NONE,
+ GR_GL_TRANSLATE_X,
+ GR_GL_TRANSLATE_Y,
+ GR_GL_TRANSLATE_2D,
+ GR_GL_TRANSPOSE_AFFINE_2D
+};
+
+GR_STATIC_ASSERT(0 == GrDrawTarget::kNone_PathTransformType);
+GR_STATIC_ASSERT(1 == GrDrawTarget::kTranslateX_PathTransformType);
+GR_STATIC_ASSERT(2 == GrDrawTarget::kTranslateY_PathTransformType);
+GR_STATIC_ASSERT(3 == GrDrawTarget::kTranslate_PathTransformType);
+GR_STATIC_ASSERT(4 == GrDrawTarget::kAffine_PathTransformType);
+GR_STATIC_ASSERT(GrDrawTarget::kAffine_PathTransformType == GrDrawTarget::kLast_PathTransformType);
+
static const GrGLenum gXfermodeCoeff2Blend[] = {
GR_GL_ZERO,
GR_GL_ONE,
@@ -1369,6 +1385,11 @@
return SkNEW_ARGS(GrGLPath, (this, inPath, stroke));
}
+GrPathRange* GrGpuGL::onCreatePathRange(size_t size, const SkStrokeRec& stroke) {
+ SkASSERT(this->caps()->pathRenderingSupport());
+ return SkNEW_ARGS(GrGLPathRange, (this, size, stroke));
+}
+
void GrGpuGL::flushScissor() {
if (fScissorState.fEnabled) {
// Only access the RT if scissoring is being enabled. We can call this before performing
@@ -1907,35 +1928,19 @@
}
}
-void GrGpuGL::onGpuDrawPaths(int pathCount, const GrPath** paths,
- const SkMatrix* transforms,
- SkPath::FillType fill,
- SkStrokeRec::Style stroke) {
+void GrGpuGL::onGpuDrawPaths(const GrPathRange* pathRange,
+ const uint32_t indices[], int count,
+ const float transforms[], PathTransformType transformsType,
+ SkPath::FillType fill) {
SkASSERT(this->caps()->pathRenderingSupport());
SkASSERT(NULL != this->drawState()->getRenderTarget());
SkASSERT(NULL != this->drawState()->getRenderTarget()->getStencilBuffer());
SkASSERT(!fCurrentProgram->hasVertexShader());
- SkASSERT(stroke != SkStrokeRec::kHairline_Style);
- SkAutoMalloc pathData(pathCount * sizeof(GrGLuint));
- SkAutoMalloc transformData(pathCount * sizeof(GrGLfloat) * 6);
- GrGLfloat* transformValues =
- reinterpret_cast<GrGLfloat*>(transformData.get());
- GrGLuint* pathIDs = reinterpret_cast<GrGLuint*>(pathData.get());
-
- for (int i = 0; i < pathCount; ++i) {
- SkASSERT(transforms[i].asAffine(NULL));
- const SkMatrix& m = transforms[i];
- transformValues[i * 6] = m.getScaleX();
- transformValues[i * 6 + 1] = m.getSkewY();
- transformValues[i * 6 + 2] = m.getSkewX();
- transformValues[i * 6 + 3] = m.getScaleY();
- transformValues[i * 6 + 4] = m.getTranslateX();
- transformValues[i * 6 + 5] = m.getTranslateY();
- pathIDs[i] = static_cast<const GrGLPath*>(paths[i])->pathID();
- }
+ GrGLuint baseID = static_cast<const GrGLPathRange*>(pathRange)->basePathID();
flushPathStencilSettings(fill);
+ const SkStrokeRec& stroke = pathRange->getStroke();
SkPath::FillType nonInvertedFill =
SkPath::ConvertToNonInverseFillType(fill);
@@ -1947,36 +1952,28 @@
GrGLint writeMask =
fHWPathStencilSettings.writeMask(GrStencilSettings::kFront_Face);
- bool doFill = stroke == SkStrokeRec::kFill_Style
- || stroke == SkStrokeRec::kStrokeAndFill_Style;
- bool doStroke = stroke == SkStrokeRec::kStroke_Style
- || stroke == SkStrokeRec::kStrokeAndFill_Style;
-
- if (doFill) {
- GL_CALL(StencilFillPathInstanced(pathCount, GR_GL_UNSIGNED_INT,
- pathIDs, 0,
- fillMode, writeMask,
- GR_GL_AFFINE_2D, transformValues));
+ if (stroke.isFillStyle() || SkStrokeRec::kStrokeAndFill_Style == stroke.getStyle()) {
+ GL_CALL(StencilFillPathInstanced(count, GR_GL_UNSIGNED_INT, indices, baseID, fillMode,
+ writeMask, gXformType2GLType[transformsType],
+ transforms));
}
- if (doStroke) {
- GL_CALL(StencilStrokePathInstanced(pathCount, GR_GL_UNSIGNED_INT,
- pathIDs, 0,
- 0xffff, writeMask,
- GR_GL_AFFINE_2D, transformValues));
+ if (stroke.needToApply()) {
+ GL_CALL(StencilStrokePathInstanced(count, GR_GL_UNSIGNED_INT, indices, baseID, 0xffff,
+ writeMask, gXformType2GLType[transformsType],
+ transforms));
}
if (nonInvertedFill == fill) {
- if (doStroke) {
+ if (stroke.needToApply()) {
GL_CALL(CoverStrokePathInstanced(
- pathCount, GR_GL_UNSIGNED_INT, pathIDs, 0,
+ count, GR_GL_UNSIGNED_INT, indices, baseID,
GR_GL_BOUNDING_BOX_OF_BOUNDING_BOXES,
- GR_GL_AFFINE_2D, transformValues));
+ gXformType2GLType[transformsType], transforms));
} else {
GL_CALL(CoverFillPathInstanced(
- pathCount, GR_GL_UNSIGNED_INT, pathIDs, 0,
+ count, GR_GL_UNSIGNED_INT, indices, baseID,
GR_GL_BOUNDING_BOX_OF_BOUNDING_BOXES,
- GR_GL_AFFINE_2D, transformValues));
-
+ gXformType2GLType[transformsType], transforms));
}
} else {
GrDrawState* drawState = this->drawState();
diff --git a/src/gpu/gl/GrGpuGL.h b/src/gpu/gl/GrGpuGL.h
index 52e7b86..526e1ca 100644
--- a/src/gpu/gl/GrGpuGL.h
+++ b/src/gpu/gl/GrGpuGL.h
@@ -135,6 +135,7 @@
virtual GrVertexBuffer* onCreateVertexBuffer(size_t size, bool dynamic) SK_OVERRIDE;
virtual GrIndexBuffer* onCreateIndexBuffer(size_t size, bool dynamic) SK_OVERRIDE;
virtual GrPath* onCreatePath(const SkPath&, const SkStrokeRec&) SK_OVERRIDE;
+ virtual GrPathRange* onCreatePathRange(size_t size, const SkStrokeRec&) SK_OVERRIDE;
virtual GrTexture* onWrapBackendTexture(const GrBackendTextureDesc&) SK_OVERRIDE;
virtual GrRenderTarget* onWrapBackendRenderTarget(const GrBackendRenderTargetDesc&) SK_OVERRIDE;
virtual bool createStencilBufferForRenderTarget(GrRenderTarget* rt,
@@ -164,9 +165,10 @@
virtual void onGpuStencilPath(const GrPath*, SkPath::FillType) SK_OVERRIDE;
virtual void onGpuDrawPath(const GrPath*, SkPath::FillType) SK_OVERRIDE;
- virtual void onGpuDrawPaths(int, const GrPath**, const SkMatrix*,
- SkPath::FillType,
- SkStrokeRec::Style) SK_OVERRIDE;
+ virtual void onGpuDrawPaths(const GrPathRange*,
+ const uint32_t indices[], int count,
+ const float transforms[], PathTransformType,
+ SkPath::FillType) SK_OVERRIDE;
virtual void clearStencil() SK_OVERRIDE;
virtual void clearStencilClip(const SkIRect& rect,