aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu/batches
diff options
context:
space:
mode:
authorGravatar ethannicholas <ethannicholas@google.com>2016-04-04 07:57:39 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2016-04-04 07:57:39 -0700
commit47a2dc8e229e93e1bcf7405747320920da1ab742 (patch)
treedc189b62d9a71ef21fb5cae9a1a2516d7b3300bc /src/gpu/batches
parent3bc969264d4720d67f0b137552b3777a03b431a8 (diff)
added GrMSAAPathRenderer
Diffstat (limited to 'src/gpu/batches')
-rw-r--r--src/gpu/batches/GrDefaultPathRenderer.cpp124
-rw-r--r--src/gpu/batches/GrDefaultPathRenderer.h1
-rw-r--r--src/gpu/batches/GrMSAAPathRenderer.cpp745
-rw-r--r--src/gpu/batches/GrMSAAPathRenderer.h35
-rw-r--r--src/gpu/batches/GrPathStencilSettings.h131
5 files changed, 912 insertions, 124 deletions
diff --git a/src/gpu/batches/GrDefaultPathRenderer.cpp b/src/gpu/batches/GrDefaultPathRenderer.cpp
index 94508b130b..708bcd7234 100644
--- a/src/gpu/batches/GrDefaultPathRenderer.cpp
+++ b/src/gpu/batches/GrDefaultPathRenderer.cpp
@@ -29,130 +29,6 @@ GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport,
, fStencilWrapOps(stencilWrapOpsSupport) {
}
-
-////////////////////////////////////////////////////////////////////////////////
-// Stencil rules for paths
-
-////// Even/Odd
-
-GR_STATIC_CONST_SAME_STENCIL(gEOStencilPass,
- kInvert_StencilOp,
- kKeep_StencilOp,
- kAlwaysIfInClip_StencilFunc,
- 0xffff,
- 0xffff,
- 0xffff);
-
-// ok not to check clip b/c stencil pass only wrote inside clip
-GR_STATIC_CONST_SAME_STENCIL(gEOColorPass,
- kZero_StencilOp,
- kZero_StencilOp,
- kNotEqual_StencilFunc,
- 0xffff,
- 0x0000,
- 0xffff);
-
-// have to check clip b/c outside clip will always be zero.
-GR_STATIC_CONST_SAME_STENCIL(gInvEOColorPass,
- kZero_StencilOp,
- kZero_StencilOp,
- kEqualIfInClip_StencilFunc,
- 0xffff,
- 0x0000,
- 0xffff);
-
-////// Winding
-
-// when we have separate stencil we increment front faces / decrement back faces
-// when we don't have wrap incr and decr we use the stencil test to simulate
-// them.
-
-GR_STATIC_CONST_STENCIL(gWindStencilSeparateWithWrap,
- kIncWrap_StencilOp, kDecWrap_StencilOp,
- kKeep_StencilOp, kKeep_StencilOp,
- kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
- 0xffff, 0xffff,
- 0xffff, 0xffff,
- 0xffff, 0xffff);
-
-// if inc'ing the max value, invert to make 0
-// if dec'ing zero invert to make all ones.
-// we can't avoid touching the stencil on both passing and
-// failing, so we can't resctrict ourselves to the clip.
-GR_STATIC_CONST_STENCIL(gWindStencilSeparateNoWrap,
- kInvert_StencilOp, kInvert_StencilOp,
- kIncClamp_StencilOp, kDecClamp_StencilOp,
- kEqual_StencilFunc, kEqual_StencilFunc,
- 0xffff, 0xffff,
- 0xffff, 0x0000,
- 0xffff, 0xffff);
-
-// When there are no separate faces we do two passes to setup the winding rule
-// stencil. First we draw the front faces and inc, then we draw the back faces
-// and dec. These are same as the above two split into the incrementing and
-// decrementing passes.
-GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapInc,
- kIncWrap_StencilOp,
- kKeep_StencilOp,
- kAlwaysIfInClip_StencilFunc,
- 0xffff,
- 0xffff,
- 0xffff);
-
-GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapDec,
- kDecWrap_StencilOp,
- kKeep_StencilOp,
- kAlwaysIfInClip_StencilFunc,
- 0xffff,
- 0xffff,
- 0xffff);
-
-GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilNoWrapInc,
- kInvert_StencilOp,
- kIncClamp_StencilOp,
- kEqual_StencilFunc,
- 0xffff,
- 0xffff,
- 0xffff);
-
-GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilNoWrapDec,
- kInvert_StencilOp,
- kDecClamp_StencilOp,
- kEqual_StencilFunc,
- 0xffff,
- 0x0000,
- 0xffff);
-
-// Color passes are the same whether we use the two-sided stencil or two passes
-
-GR_STATIC_CONST_SAME_STENCIL(gWindColorPass,
- kZero_StencilOp,
- kZero_StencilOp,
- kNonZeroIfInClip_StencilFunc,
- 0xffff,
- 0x0000,
- 0xffff);
-
-GR_STATIC_CONST_SAME_STENCIL(gInvWindColorPass,
- kZero_StencilOp,
- kZero_StencilOp,
- kEqualIfInClip_StencilFunc,
- 0xffff,
- 0x0000,
- 0xffff);
-
-////// Normal render to stencil
-
-// Sometimes the default path renderer can draw a path directly to the stencil
-// buffer without having to first resolve the interior / exterior.
-GR_STATIC_CONST_SAME_STENCIL(gDirectToStencil,
- kZero_StencilOp,
- kIncClamp_StencilOp,
- kAlwaysIfInClip_StencilFunc,
- 0xffff,
- 0x0000,
- 0xffff);
-
////////////////////////////////////////////////////////////////////////////////
// Helpers for drawPath
diff --git a/src/gpu/batches/GrDefaultPathRenderer.h b/src/gpu/batches/GrDefaultPathRenderer.h
index 8a2ce7b9c4..9973c2be5e 100644
--- a/src/gpu/batches/GrDefaultPathRenderer.h
+++ b/src/gpu/batches/GrDefaultPathRenderer.h
@@ -9,6 +9,7 @@
#define GrDefaultPathRenderer_DEFINED
#include "GrPathRenderer.h"
+#include "GrPathStencilSettings.h"
#include "SkTypes.h"
/**
diff --git a/src/gpu/batches/GrMSAAPathRenderer.cpp b/src/gpu/batches/GrMSAAPathRenderer.cpp
new file mode 100644
index 0000000000..86868e1d8c
--- /dev/null
+++ b/src/gpu/batches/GrMSAAPathRenderer.cpp
@@ -0,0 +1,745 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrMSAAPathRenderer.h"
+
+#include "GrBatchFlushState.h"
+#include "GrDefaultGeoProcFactory.h"
+#include "GrPathStencilSettings.h"
+#include "GrPathUtils.h"
+#include "GrPipelineBuilder.h"
+#include "GrMesh.h"
+#include "SkGeometry.h"
+#include "SkTraceEvent.h"
+#include "glsl/GrGLSLGeometryProcessor.h"
+#include "glsl/GrGLSLFragmentShaderBuilder.h"
+#include "glsl/GrGLSLVertexShaderBuilder.h"
+#include "glsl/GrGLSLProgramDataManager.h"
+#include "glsl/GrGLSLUtil.h"
+#include "gl/GrGLVaryingHandler.h"
+#include "batches/GrRectBatchFactory.h"
+#include "batches/GrVertexBatch.h"
+
+static const float kTolerance = 0.5f;
+
+////////////////////////////////////////////////////////////////////////////////
+// Helpers for drawPath
+
+static inline bool single_pass_path(const SkPath& path, const SkStrokeRec& stroke) {
+ if (!path.isInverseFillType()) {
+ return path.isConvex();
+ }
+ return false;
+}
+
+GrPathRenderer::StencilSupport
+GrMSAAPathRenderer::onGetStencilSupport(const SkPath& path, const GrStrokeInfo& stroke) const {
+ if (single_pass_path(path, stroke)) {
+ return GrPathRenderer::kNoRestriction_StencilSupport;
+ } else {
+ return GrPathRenderer::kStencilOnly_StencilSupport;
+ }
+}
+
+struct MSAALineVertices {
+ struct Vertex {
+ SkPoint fPosition;
+ SkColor fColor;
+ };
+ Vertex* vertices;
+ Vertex* nextVertex;
+#ifdef SK_DEBUG
+ Vertex* verticesEnd;
+#endif
+ uint16_t* indices;
+ uint16_t* nextIndex;
+};
+
+struct MSAAQuadVertices {
+ struct Vertex {
+ SkPoint fPosition;
+ SkPoint fUV;
+ SkColor fColor;
+ };
+ Vertex* vertices;
+ Vertex* nextVertex;
+#ifdef SK_DEBUG
+ Vertex* verticesEnd;
+#endif
+ uint16_t* indices;
+ uint16_t* nextIndex;
+};
+
+static inline void append_contour_edge_indices(uint16_t fanCenterIdx,
+ uint16_t edgeV0Idx,
+ MSAALineVertices& lines) {
+ *(lines.nextIndex++) = fanCenterIdx;
+ *(lines.nextIndex++) = edgeV0Idx;
+ *(lines.nextIndex++) = edgeV0Idx + 1;
+}
+
+static inline void add_quad(MSAALineVertices& lines, MSAAQuadVertices& quads, const SkPoint pts[],
+ SkColor color, bool indexed, uint16_t subpathLineIdxStart) {
+ SkASSERT(lines.nextVertex < lines.verticesEnd);
+ *lines.nextVertex = { pts[2], color };
+ if (indexed) {
+ int prevIdx = (uint16_t) (lines.nextVertex - lines.vertices - 1);
+ if (prevIdx > subpathLineIdxStart) {
+ append_contour_edge_indices(subpathLineIdxStart, prevIdx, lines);
+ }
+ }
+ lines.nextVertex++;
+
+ SkASSERT(quads.nextVertex + 2 < quads.verticesEnd);
+ // the texture coordinates are drawn from the Loop-Blinn rendering algorithm
+ *(quads.nextVertex++) = { pts[0], SkPoint::Make(0.0, 0.0), color };
+ *(quads.nextVertex++) = { pts[1], SkPoint::Make(0.5, 0.0), color };
+ *(quads.nextVertex++) = { pts[2], SkPoint::Make(1.0, 1.0), color };
+ if (indexed) {
+ uint16_t offset = (uint16_t) (quads.nextVertex - quads.vertices) - 3;
+ *(quads.nextIndex++) = offset++;
+ *(quads.nextIndex++) = offset++;
+ *(quads.nextIndex++) = offset++;
+ }
+}
+
+class MSAAQuadProcessor : public GrGeometryProcessor {
+public:
+ static GrGeometryProcessor* Create(const SkMatrix& viewMatrix) {
+ return new MSAAQuadProcessor(viewMatrix);
+ }
+
+ virtual ~MSAAQuadProcessor() {}
+
+ const char* name() const override { return "MSAAQuadProcessor"; }
+
+ const Attribute* inPosition() const { return fInPosition; }
+ const Attribute* inUV() const { return fInUV; }
+ const Attribute* inColor() const { return fInColor; }
+ const SkMatrix& viewMatrix() const { return fViewMatrix; }
+ const SkMatrix& localMatrix() const { return SkMatrix::I(); }
+
+ class GLSLProcessor : public GrGLSLGeometryProcessor {
+ public:
+ GLSLProcessor(const GrGeometryProcessor& qpr) {}
+
+ void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
+ const MSAAQuadProcessor& qp = args.fGP.cast<MSAAQuadProcessor>();
+ GrGLSLVertexBuilder* vsBuilder = args.fVertBuilder;
+ GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
+ GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
+
+ // emit attributes
+ varyingHandler->emitAttributes(qp);
+ varyingHandler->addPassThroughAttribute(qp.inColor(), args.fOutputColor);
+
+ GrGLSLVertToFrag uv(kVec2f_GrSLType);
+ varyingHandler->addVarying("uv", &uv, kHigh_GrSLPrecision);
+ vsBuilder->codeAppendf("%s = %s;", uv.vsOut(), qp.inUV()->fName);
+
+ // Setup position
+ this->setupPosition(vsBuilder, uniformHandler, gpArgs, qp.inPosition()->fName,
+ qp.viewMatrix(), &fViewMatrixUniform);
+
+ // emit transforms
+ this->emitTransforms(vsBuilder, varyingHandler, uniformHandler, gpArgs->fPositionVar,
+ qp.inPosition()->fName, SkMatrix::I(), args.fTransformsIn,
+ args.fTransformsOut);
+
+ GrGLSLPPFragmentBuilder* fsBuilder = args.fFragBuilder;
+ fsBuilder->codeAppendf("if (%s.x * %s.x >= %s.y) discard;", uv.fsIn(), uv.fsIn(),
+ uv.fsIn());
+ fsBuilder->codeAppendf("%s = vec4(1.0);", args.fOutputCoverage);
+ }
+
+ static inline void GenKey(const GrGeometryProcessor& gp,
+ const GrGLSLCaps&,
+ GrProcessorKeyBuilder* b) {
+ const MSAAQuadProcessor& qp = gp.cast<MSAAQuadProcessor>();
+ uint32_t key = 0;
+ key |= qp.viewMatrix().hasPerspective() ? 0x1 : 0x0;
+ key |= qp.viewMatrix().isIdentity() ? 0x2: 0x0;
+ b->add32(key);
+ }
+
+ virtual void setData(const GrGLSLProgramDataManager& pdman,
+ const GrPrimitiveProcessor& gp) override {
+ const MSAAQuadProcessor& qp = gp.cast<MSAAQuadProcessor>();
+ if (!qp.viewMatrix().isIdentity()) {
+ float viewMatrix[3 * 3];
+ GrGLSLGetMatrix<3>(viewMatrix, qp.viewMatrix());
+ pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
+ }
+ }
+
+ void setTransformData(const GrPrimitiveProcessor& primProc,
+ const GrGLSLProgramDataManager& pdman,
+ int index,
+ const SkTArray<const GrCoordTransform*, true>& transforms) override {
+ this->setTransformDataHelper<MSAAQuadProcessor>(primProc, pdman, index, transforms);
+ }
+
+ private:
+ typedef GrGLSLGeometryProcessor INHERITED;
+
+ UniformHandle fViewMatrixUniform;
+ };
+
+ virtual void getGLSLProcessorKey(const GrGLSLCaps& caps,
+ GrProcessorKeyBuilder* b) const override {
+ GLSLProcessor::GenKey(*this, caps, b);
+ }
+
+ virtual GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
+ return new GLSLProcessor(*this);
+ }
+
+private:
+ MSAAQuadProcessor(const SkMatrix& viewMatrix)
+ : fViewMatrix(viewMatrix) {
+ this->initClassID<MSAAQuadProcessor>();
+ fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
+ kHigh_GrSLPrecision));
+ fInUV = &this->addVertexAttrib(Attribute("inUV", kVec2f_GrVertexAttribType,
+ kHigh_GrSLPrecision));
+ fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
+ this->setSampleShading(1.0f);
+ }
+
+ const Attribute* fInPosition;
+ const Attribute* fInUV;
+ const Attribute* fInColor;
+ SkMatrix fViewMatrix;
+
+ GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
+
+ typedef GrGeometryProcessor INHERITED;
+};
+
+class MSAAPathBatch : public GrVertexBatch {
+public:
+ DEFINE_BATCH_CLASS_ID
+
+ struct Geometry {
+ GrColor fColor;
+ SkPath fPath;
+ SkScalar fTolerance;
+ };
+
+ static MSAAPathBatch* Create(const Geometry& geometry, const SkMatrix& viewMatrix,
+ const SkRect& devBounds) {
+ return new MSAAPathBatch(geometry, viewMatrix, devBounds);
+ }
+
+ const char* name() const override { return "MSAAPathBatch"; }
+
+ void computePipelineOptimizations(GrInitInvariantOutput* color,
+ GrInitInvariantOutput* coverage,
+ GrBatchToXPOverrides* overrides) const override {
+ // When this is called on a batch, there is only one geometry bundle
+ color->setKnownFourComponents(fGeoData[0].fColor);
+ coverage->setKnownSingleComponent(0xff);
+ }
+
+ bool isValid() const {
+ return !fIsIndexed || fMaxLineIndices <= SK_MaxU16;
+ }
+
+private:
+ void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
+ // Handle any color overrides
+ if (!overrides.readsColor()) {
+ fGeoData[0].fColor = GrColor_ILLEGAL;
+ }
+ overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
+ }
+
+ void computeWorstCasePointCount(const SkPath& path, int* subpaths, SkScalar tol,
+ int* outLinePointCount, int* outQuadPointCount) const {
+ int linePointCount = 0;
+ int quadPointCount = 0;
+ *subpaths = 1;
+
+ bool first = true;
+
+ SkPath::Iter iter(path, false);
+ SkPath::Verb verb;
+
+ SkPoint pts[4];
+ while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+ switch (verb) {
+ case SkPath::kLine_Verb:
+ linePointCount += 1;
+ break;
+ case SkPath::kConic_Verb: {
+ SkScalar weight = iter.conicWeight();
+ SkAutoConicToQuads converter;
+ converter.computeQuads(pts, weight, kTolerance);
+ int quadPts = converter.countQuads();
+ linePointCount += quadPts;
+ quadPointCount += 3 * quadPts;
+ }
+ case SkPath::kQuad_Verb:
+ linePointCount += 1;
+ quadPointCount += 3;
+ break;
+ case SkPath::kCubic_Verb: {
+ SkSTArray<15, SkPoint, true> quadPts;
+ GrPathUtils::convertCubicToQuads(pts, kTolerance, &quadPts);
+ int count = quadPts.count();
+ linePointCount += count / 3;
+ quadPointCount += count;
+ break;
+ }
+ case SkPath::kMove_Verb:
+ linePointCount += 1;
+ if (!first) {
+ ++(*subpaths);
+ }
+ break;
+ default:
+ break;
+ }
+ first = false;
+ }
+ *outLinePointCount = linePointCount;
+ *outQuadPointCount = quadPointCount;
+ }
+
+ void onPrepareDraws(Target* target) const override {
+ SkASSERT(this->isValid());
+ if (fMaxLineVertices == 0) {
+ SkASSERT(fMaxQuadVertices == 0);
+ return;
+ }
+
+ GrPrimitiveType primitiveType = fIsIndexed ? kTriangles_GrPrimitiveType
+ : kTriangleFan_GrPrimitiveType;
+
+ // allocate vertex / index buffers
+ const GrBuffer* lineVertexBuffer;
+ int firstLineVertex;
+ MSAALineVertices lines;
+ size_t lineVertexStride = sizeof(MSAALineVertices::Vertex);
+ lines.vertices = (MSAALineVertices::Vertex*) target->makeVertexSpace(lineVertexStride,
+ fMaxLineVertices,
+ &lineVertexBuffer,
+ &firstLineVertex);
+ if (!lines.vertices) {
+ SkDebugf("Could not allocate vertices\n");
+ return;
+ }
+ lines.nextVertex = lines.vertices;
+ SkDEBUGCODE(lines.verticesEnd = lines.vertices + fMaxLineVertices;)
+
+ MSAAQuadVertices quads;
+ size_t quadVertexStride = sizeof(MSAAQuadVertices::Vertex);
+ SkAutoFree quadVertexPtr(sk_malloc_throw(fMaxQuadVertices * quadVertexStride));
+ quads.vertices = (MSAAQuadVertices::Vertex*) quadVertexPtr.get();
+ quads.nextVertex = quads.vertices;
+ SkDEBUGCODE(quads.verticesEnd = quads.vertices + fMaxQuadVertices;)
+
+ const GrBuffer* lineIndexBuffer = nullptr;
+ int firstLineIndex;
+ if (fIsIndexed) {
+ lines.indices = target->makeIndexSpace(fMaxLineIndices, &lineIndexBuffer,
+ &firstLineIndex);
+ if (!lines.indices) {
+ SkDebugf("Could not allocate indices\n");
+ return;
+ }
+ lines.nextIndex = lines.indices;
+ } else {
+ lines.indices = nullptr;
+ lines.nextIndex = nullptr;
+ }
+
+ SkAutoFree quadIndexPtr;
+ if (fIsIndexed) {
+ quads.indices = (uint16_t*) sk_malloc_throw(fMaxQuadIndices * sizeof(uint16_t));
+ quadIndexPtr.set(quads.indices);
+ quads.nextIndex = quads.indices;
+ } else {
+ quads.indices = nullptr;
+ quads.nextIndex = nullptr;
+ }
+
+ // fill buffers
+ for (int i = 0; i < fGeoData.count(); i++) {
+ const Geometry& args = fGeoData[i];
+
+ if (!this->createGeom(lines,
+ quads,
+ args.fPath,
+ args.fTolerance,
+ fViewMatrix,
+ args.fColor,
+ fIsIndexed)) {
+ return;
+ }
+ }
+ int lineVertexOffset = (int) (lines.nextVertex - lines.vertices);
+ int lineIndexOffset = (int) (lines.nextIndex - lines.indices);
+ SkASSERT(lineVertexOffset <= fMaxLineVertices && lineIndexOffset <= fMaxLineIndices);
+ int quadVertexOffset = (int) (quads.nextVertex - quads.vertices);
+ int quadIndexOffset = (int) (quads.nextIndex - quads.indices);
+ SkASSERT(quadVertexOffset <= fMaxQuadVertices && quadIndexOffset <= fMaxQuadIndices);
+
+ if (lineVertexOffset) {
+ SkAutoTUnref<const GrGeometryProcessor> lineGP;
+ {
+ using namespace GrDefaultGeoProcFactory;
+ lineGP.reset(GrDefaultGeoProcFactory::Create(Color(Color::kAttribute_Type),
+ Coverage(255),
+ LocalCoords(LocalCoords::kUnused_Type),
+ fViewMatrix));
+ }
+ SkASSERT(lineVertexStride == lineGP->getVertexStride());
+
+ GrMesh lineMeshes;
+ if (fIsIndexed) {
+ lineMeshes.initIndexed(primitiveType, lineVertexBuffer, lineIndexBuffer,
+ firstLineVertex, firstLineIndex, lineVertexOffset,
+ lineIndexOffset);
+ } else {
+ lineMeshes.init(primitiveType, lineVertexBuffer, firstLineVertex,
+ lineVertexOffset);
+ }
+ target->draw(lineGP, lineMeshes);
+ }
+
+ if (quadVertexOffset) {
+ SkAutoTUnref<const GrGeometryProcessor> quadGP(MSAAQuadProcessor::Create(fViewMatrix));
+ SkASSERT(quadVertexStride == quadGP->getVertexStride());
+
+ const GrBuffer* quadVertexBuffer;
+ int firstQuadVertex;
+ MSAAQuadVertices::Vertex* quadVertices = (MSAAQuadVertices::Vertex*)
+ target->makeVertexSpace(quadVertexStride, quadVertexOffset, &quadVertexBuffer,
+ &firstQuadVertex);
+ memcpy(quadVertices, quads.vertices, quadVertexStride * quadVertexOffset);
+ GrMesh quadMeshes;
+ if (fIsIndexed) {
+ const GrBuffer* quadIndexBuffer;
+ int firstQuadIndex;
+ uint16_t* quadIndices = (uint16_t*) target->makeIndexSpace(quadIndexOffset,
+ &quadIndexBuffer,
+ &firstQuadIndex);
+ memcpy(quadIndices, quads.indices, sizeof(uint16_t) * quadIndexOffset);
+ quadMeshes.initIndexed(kTriangles_GrPrimitiveType, quadVertexBuffer,
+ quadIndexBuffer, firstQuadVertex, firstQuadIndex,
+ quadVertexOffset, quadIndexOffset);
+ } else {
+ quadMeshes.init(kTriangles_GrPrimitiveType, quadVertexBuffer, firstQuadVertex,
+ quadVertexOffset);
+ }
+ target->draw(quadGP, quadMeshes);
+ }
+ }
+
+ SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
+
+ MSAAPathBatch(const Geometry& geometry, const SkMatrix& viewMatrix, const SkRect& devBounds)
+ : INHERITED(ClassID())
+ , fViewMatrix(viewMatrix) {
+ fGeoData.push_back(geometry);
+ this->setBounds(devBounds);
+ int contourCount;
+ this->computeWorstCasePointCount(geometry.fPath, &contourCount, kTolerance,
+ &fMaxLineVertices, &fMaxQuadVertices);
+ fMaxLineIndices = fMaxLineVertices * 3;
+ fMaxQuadIndices = fMaxQuadVertices * 3;
+ fIsIndexed = contourCount > 1;
+ }
+
+ bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
+ MSAAPathBatch* that = t->cast<MSAAPathBatch>();
+ if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
+ that->bounds(), caps)) {
+ return false;
+ }
+
+ if (!fViewMatrix.cheapEqualTo(that->fViewMatrix)) {
+ return false;
+ }
+
+ if (fMaxLineIndices + that->fMaxLineIndices > SK_MaxU16) {
+ return false;
+ }
+ SkASSERT(fMaxQuadIndices + that->fMaxQuadIndices <= SK_MaxU16);
+
+ fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
+ this->joinBounds(that->bounds());
+ fIsIndexed = true;
+ fMaxLineVertices += that->fMaxLineVertices;
+ fMaxQuadVertices += that->fMaxQuadVertices;
+ fMaxLineIndices += that->fMaxLineIndices;
+ fMaxQuadIndices += that->fMaxQuadIndices;
+ return true;
+ }
+
+ bool createGeom(MSAALineVertices& lines,
+ MSAAQuadVertices& quads,
+ const SkPath& path,
+ SkScalar srcSpaceTol,
+ const SkMatrix& m,
+ SkColor color,
+ bool isIndexed) const {
+ {
+ uint16_t subpathIdxStart = (uint16_t) (lines.nextVertex - lines.vertices);
+
+ SkPoint pts[4];
+
+ bool first = true;
+ SkPath::Iter iter(path, false);
+
+ bool done = false;
+ while (!done) {
+ SkPath::Verb verb = iter.next(pts);
+ switch (verb) {
+ case SkPath::kMove_Verb:
+ if (!first) {
+ uint16_t currIdx = (uint16_t) (lines.nextVertex - lines.vertices);
+ subpathIdxStart = currIdx;
+ }
+ SkASSERT(lines.nextVertex < lines.verticesEnd);
+ *(lines.nextVertex++) = { pts[0], color };
+ break;
+ case SkPath::kLine_Verb:
+ if (isIndexed) {
+ uint16_t prevIdx = (uint16_t) (lines.nextVertex - lines.vertices - 1);
+ if (prevIdx > subpathIdxStart) {
+ append_contour_edge_indices(subpathIdxStart, prevIdx, lines);
+ }
+ }
+ SkASSERT(lines.nextVertex < lines.verticesEnd);
+ *(lines.nextVertex++) = { pts[1], color };
+ break;
+ case SkPath::kConic_Verb: {
+ SkScalar weight = iter.conicWeight();
+ SkAutoConicToQuads converter;
+ const SkPoint* quadPts = converter.computeQuads(pts, weight,
+ kTolerance);
+ for (int i = 0; i < converter.countQuads(); ++i) {
+ add_quad(lines, quads, quadPts + i * 2, color, isIndexed,
+ subpathIdxStart);
+ }
+ break;
+ }
+ case SkPath::kQuad_Verb: {
+ add_quad(lines, quads, pts, color, isIndexed, subpathIdxStart);
+ break;
+ }
+ case SkPath::kCubic_Verb: {
+ SkSTArray<15, SkPoint, true> quadPts;
+ GrPathUtils::convertCubicToQuads(pts, kTolerance, &quadPts);
+ int count = quadPts.count();
+ for (int i = 0; i < count; i += 3) {
+ add_quad(lines, quads, &quadPts[i], color, isIndexed, subpathIdxStart);
+ }
+ break;
+ }
+ case SkPath::kClose_Verb:
+ break;
+ case SkPath::kDone_Verb:
+ done = true;
+ }
+ first = false;
+ }
+ }
+ return true;
+ }
+
+ SkSTArray<1, Geometry, true> fGeoData;
+
+ SkMatrix fViewMatrix;
+ int fMaxLineVertices;
+ int fMaxQuadVertices;
+ int fMaxLineIndices;
+ int fMaxQuadIndices;
+ bool fIsIndexed;
+
+ typedef GrVertexBatch INHERITED;
+};
+
+bool GrMSAAPathRenderer::internalDrawPath(GrDrawTarget* target,
+ GrPipelineBuilder* pipelineBuilder,
+ GrColor color,
+ const SkMatrix& viewMatrix,
+ const SkPath& path,
+ const GrStrokeInfo& origStroke,
+ bool stencilOnly) {
+ SkTCopyOnFirstWrite<GrStrokeInfo> stroke(origStroke);
+
+ const GrXPFactory* xpFactory = pipelineBuilder->getXPFactory();
+ SkAutoTUnref<const GrXPFactory> backupXPFactory(SkSafeRef(xpFactory));
+ // face culling doesn't make sense here
+ SkASSERT(GrPipelineBuilder::kBoth_DrawFace == pipelineBuilder->getDrawFace());
+
+ int passCount = 0;
+ const GrStencilSettings* passes[3];
+ GrPipelineBuilder::DrawFace drawFace[3];
+ bool reverse = false;
+ bool lastPassIsBounds;
+
+ if (single_pass_path(path, *stroke)) {
+ passCount = 1;
+ if (stencilOnly) {
+ passes[0] = &gDirectToStencil;
+ } else {
+ passes[0] = nullptr;
+ }
+ drawFace[0] = GrPipelineBuilder::kBoth_DrawFace;
+ lastPassIsBounds = false;
+ } else {
+ switch (path.getFillType()) {
+ case SkPath::kInverseEvenOdd_FillType:
+ reverse = true;
+ // fallthrough
+ case SkPath::kEvenOdd_FillType:
+ passes[0] = &gEOStencilPass;
+ if (stencilOnly) {
+ passCount = 1;
+ lastPassIsBounds = false;
+ } else {
+ passCount = 2;
+ lastPassIsBounds = true;
+ if (reverse) {
+ passes[1] = &gInvEOColorPass;
+ } else {
+ passes[1] = &gEOColorPass;
+ }
+ }
+ drawFace[0] = drawFace[1] = GrPipelineBuilder::kBoth_DrawFace;
+ break;
+
+ case SkPath::kInverseWinding_FillType:
+ reverse = true;
+ // fallthrough
+ case SkPath::kWinding_FillType:
+ passes[0] = &gWindStencilSeparateWithWrap;
+ passCount = 2;
+ drawFace[0] = GrPipelineBuilder::kBoth_DrawFace;
+ if (stencilOnly) {
+ lastPassIsBounds = false;
+ --passCount;
+ } else {
+ lastPassIsBounds = true;
+ drawFace[passCount-1] = GrPipelineBuilder::kBoth_DrawFace;
+ if (reverse) {
+ passes[passCount-1] = &gInvWindColorPass;
+ } else {
+ passes[passCount-1] = &gWindColorPass;
+ }
+ }
+ break;
+ default:
+ SkDEBUGFAIL("Unknown path fFill!");
+ return false;
+ }
+ }
+
+ SkRect devBounds;
+ GetPathDevBounds(path, pipelineBuilder->getRenderTarget(), viewMatrix, &devBounds);
+
+ for (int p = 0; p < passCount; ++p) {
+ pipelineBuilder->setDrawFace(drawFace[p]);
+ if (passes[p]) {
+ *pipelineBuilder->stencil() = *passes[p];
+ }
+
+ if (lastPassIsBounds && (p == passCount-1)) {
+ // Reset the XP Factory on pipelineBuilder
+ pipelineBuilder->setXPFactory(backupXPFactory);
+ SkRect bounds;
+ SkMatrix localMatrix = SkMatrix::I();
+ if (reverse) {
+ SkASSERT(pipelineBuilder->getRenderTarget());
+ // draw over the dev bounds (which will be the whole dst surface for inv fill).
+ bounds = devBounds;
+ SkMatrix vmi;
+ // mapRect through persp matrix may not be correct
+ if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) {
+ vmi.mapRect(&bounds);
+ } else {
+ if (!viewMatrix.invert(&localMatrix)) {
+ return false;
+ }
+ }
+ } else {
+ bounds = path.getBounds();
+ }
+ const SkMatrix& viewM = (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() :
+ viewMatrix;
+ SkAutoTUnref<GrDrawBatch> batch(
+ GrRectBatchFactory::CreateNonAAFill(color, viewM, bounds, nullptr,
+ &localMatrix));
+ target->drawBatch(*pipelineBuilder, batch);
+ } else {
+ if (passCount > 1) {
+ pipelineBuilder->setDisableColorXPFactory();
+ }
+
+ MSAAPathBatch::Geometry geometry;
+ geometry.fColor = color;
+ geometry.fPath = path;
+ geometry.fTolerance = kTolerance;
+
+ SkAutoTUnref<MSAAPathBatch> batch(MSAAPathBatch::Create(geometry, viewMatrix,
+ devBounds));
+ if (batch->isValid()) {
+ target->drawBatch(*pipelineBuilder, batch);
+ }
+ else {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool GrMSAAPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
+ return !IsStrokeHairlineOrEquivalent(*args.fStroke, *args.fViewMatrix, nullptr) &&
+ !args.fAntiAlias;
+}
+
+bool GrMSAAPathRenderer::onDrawPath(const DrawPathArgs& args) {
+ GR_AUDIT_TRAIL_AUTO_FRAME(args.fTarget->getAuditTrail(), "GrMSAAPathRenderer::onDrawPath");
+ SkPath path;
+ GrStrokeInfo stroke(*args.fStroke);
+ if (stroke.isDashed()) {
+ if (!stroke.applyDashToPath(&path, &stroke, *args.fPath)) {
+ return false;
+ }
+ } else {
+ path = *args.fPath;
+ }
+ if (!stroke.isFillStyle()) {
+ stroke.setResScale(SkScalarAbs(args.fViewMatrix->getMaxScale()));
+ if (!stroke.applyToPath(&path, path)) {
+ return false;
+ }
+ stroke.setFillStyle();
+ }
+ return this->internalDrawPath(args.fTarget,
+ args.fPipelineBuilder,
+ args.fColor,
+ *args.fViewMatrix,
+ path,
+ stroke,
+ false);
+}
+
+void GrMSAAPathRenderer::onStencilPath(const StencilPathArgs& args) {
+ GR_AUDIT_TRAIL_AUTO_FRAME(args.fTarget->getAuditTrail(),"GrMSAAPathRenderer::onStencilPath");
+ SkASSERT(SkPath::kInverseEvenOdd_FillType != args.fPath->getFillType());
+ SkASSERT(SkPath::kInverseWinding_FillType != args.fPath->getFillType());
+ this->internalDrawPath(args.fTarget, args.fPipelineBuilder, GrColor_WHITE, *args.fViewMatrix,
+ *args.fPath, *args.fStroke, true);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/batches/GrMSAAPathRenderer.h b/src/gpu/batches/GrMSAAPathRenderer.h
new file mode 100644
index 0000000000..0ffd280291
--- /dev/null
+++ b/src/gpu/batches/GrMSAAPathRenderer.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrMSAAPathRenderer_DEFINED
+#define GrMSAAPathRenderer_DEFINED
+
+#include "GrPathRenderer.h"
+#include "SkTypes.h"
+
+class SK_API GrMSAAPathRenderer : public GrPathRenderer {
+private:
+ StencilSupport onGetStencilSupport(const SkPath&, const GrStrokeInfo&) const override;
+
+ bool onCanDrawPath(const CanDrawPathArgs&) const override;
+
+ bool onDrawPath(const DrawPathArgs&) override;
+
+ void onStencilPath(const StencilPathArgs&) override;
+
+ bool internalDrawPath(GrDrawTarget*,
+ GrPipelineBuilder*,
+ GrColor,
+ const SkMatrix& viewMatrix,
+ const SkPath&,
+ const GrStrokeInfo&,
+ bool stencilOnly);
+
+ typedef GrPathRenderer INHERITED;
+};
+
+#endif
diff --git a/src/gpu/batches/GrPathStencilSettings.h b/src/gpu/batches/GrPathStencilSettings.h
new file mode 100644
index 0000000000..a04f77a200
--- /dev/null
+++ b/src/gpu/batches/GrPathStencilSettings.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrPathStencilSettings_DEFINED
+#define GrPathStencilSettings_DEFINED
+
+////// Even/Odd
+
+GR_STATIC_CONST_SAME_STENCIL(gEOStencilPass,
+ kInvert_StencilOp,
+ kKeep_StencilOp,
+ kAlwaysIfInClip_StencilFunc,
+ 0xffff,
+ 0xffff,
+ 0xffff);
+
+// ok not to check clip b/c stencil pass only wrote inside clip
+GR_STATIC_CONST_SAME_STENCIL(gEOColorPass,
+ kZero_StencilOp,
+ kZero_StencilOp,
+ kNotEqual_StencilFunc,
+ 0xffff,
+ 0x0000,
+ 0xffff);
+
+// have to check clip b/c outside clip will always be zero.
+GR_STATIC_CONST_SAME_STENCIL(gInvEOColorPass,
+ kZero_StencilOp,
+ kZero_StencilOp,
+ kEqualIfInClip_StencilFunc,
+ 0xffff,
+ 0x0000,
+ 0xffff);
+
+////// Winding
+
+// when we have separate stencil we increment front faces / decrement back faces
+// when we don't have wrap incr and decr we use the stencil test to simulate
+// them.
+
+GR_STATIC_CONST_STENCIL(gWindStencilSeparateWithWrap,
+ kIncWrap_StencilOp, kDecWrap_StencilOp,
+ kKeep_StencilOp, kKeep_StencilOp,
+ kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
+ 0xffff, 0xffff,
+ 0xffff, 0xffff,
+ 0xffff, 0xffff);
+
+// if inc'ing the max value, invert to make 0
+// if dec'ing zero invert to make all ones.
+// we can't avoid touching the stencil on both passing and
+// failing, so we can't resctrict ourselves to the clip.
+GR_STATIC_CONST_STENCIL(gWindStencilSeparateNoWrap,
+ kInvert_StencilOp, kInvert_StencilOp,
+ kIncClamp_StencilOp, kDecClamp_StencilOp,
+ kEqual_StencilFunc, kEqual_StencilFunc,
+ 0xffff, 0xffff,
+ 0xffff, 0x0000,
+ 0xffff, 0xffff);
+
+// When there are no separate faces we do two passes to setup the winding rule
+// stencil. First we draw the front faces and inc, then we draw the back faces
+// and dec. These are same as the above two split into the incrementing and
+// decrementing passes.
+GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapInc,
+ kIncWrap_StencilOp,
+ kKeep_StencilOp,
+ kAlwaysIfInClip_StencilFunc,
+ 0xffff,
+ 0xffff,
+ 0xffff);
+
+GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapDec,
+ kDecWrap_StencilOp,
+ kKeep_StencilOp,
+ kAlwaysIfInClip_StencilFunc,
+ 0xffff,
+ 0xffff,
+ 0xffff);
+
+GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilNoWrapInc,
+ kInvert_StencilOp,
+ kIncClamp_StencilOp,
+ kEqual_StencilFunc,
+ 0xffff,
+ 0xffff,
+ 0xffff);
+
+GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilNoWrapDec,
+ kInvert_StencilOp,
+ kDecClamp_StencilOp,
+ kEqual_StencilFunc,
+ 0xffff,
+ 0x0000,
+ 0xffff);
+
+// Color passes are the same whether we use the two-sided stencil or two passes
+
+GR_STATIC_CONST_SAME_STENCIL(gWindColorPass,
+ kZero_StencilOp,
+ kZero_StencilOp,
+ kNonZeroIfInClip_StencilFunc,
+ 0xffff,
+ 0x0000,
+ 0xffff);
+
+GR_STATIC_CONST_SAME_STENCIL(gInvWindColorPass,
+ kZero_StencilOp,
+ kZero_StencilOp,
+ kEqualIfInClip_StencilFunc,
+ 0xffff,
+ 0x0000,
+ 0xffff);
+
+////// Normal render to stencil
+
+// Sometimes the default path renderer can draw a path directly to the stencil
+// buffer without having to first resolve the interior / exterior.
+GR_STATIC_CONST_SAME_STENCIL(gDirectToStencil,
+ kZero_StencilOp,
+ kIncClamp_StencilOp,
+ kAlwaysIfInClip_StencilFunc,
+ 0xffff,
+ 0x0000,
+ 0xffff);
+
+#endif