Chromium Code Reviews| Index: src/gpu/batches/GrMSAAPathRenderer.cpp |
| diff --git a/src/gpu/batches/GrMSAAPathRenderer.cpp b/src/gpu/batches/GrMSAAPathRenderer.cpp |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..b6e359f41d08033a6a8a2ffbd295686deb09ad9e |
| --- /dev/null |
| +++ b/src/gpu/batches/GrMSAAPathRenderer.cpp |
| @@ -0,0 +1,880 @@ |
| +/* |
| + * Copyright 2011 Google Inc. |
|
egdaniel
2016/03/29 20:57:39
2016
|
| + * |
| + * 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 "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; |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| +// Stencil rules for paths |
|
egdaniel
2016/03/29 20:57:39
Can we share all these with default path renderer
|
| + |
| +////// 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 |
| + |
| +GR_STATIC_CONST_STENCIL(gWindStencil, |
| + kIncWrap_StencilOp, kDecWrap_StencilOp, |
| + kKeep_StencilOp, kKeep_StencilOp, |
| + kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc, |
| + 0xffff, 0xffff, |
| + 0xffff, 0xffff, |
| + 0xffff, 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 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 |
| + |
| +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 { |
| + SkPoint* vertices; |
| + SkPoint* nextVertex; |
| +#ifdef SK_DEBUG |
| + SkPoint* verticesEnd; |
| +#endif |
| + uint16_t* indices; |
| + uint16_t* nextIndex; |
| +}; |
| + |
| +struct MSAAQuadVertices { |
| + struct Vertex { |
| + SkPoint position; |
| + SkPoint uv; |
| + }; |
| + 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, |
|
egdaniel
2016/03/29 20:57:39
align these
|
| + MSAALineVertices& lines) { |
| + *(lines.nextIndex++) = fanCenterIdx; |
| + *(lines.nextIndex++) = edgeV0Idx; |
| + *(lines.nextIndex++) = edgeV0Idx + 1; |
| +} |
| + |
| +static inline void add_quad(MSAALineVertices& lines, MSAAQuadVertices& quads, const SkPoint pts[], |
| + bool indexed, uint16_t subpathLineIdxStart) { |
| + SkASSERT(lines.nextVertex < lines.verticesEnd); |
| + *lines.nextVertex = pts[2]; |
| + 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) }; |
| + *(quads.nextVertex++) = { pts[1], SkPoint::Make(0.5, 0.0) }; |
| + *(quads.nextVertex++) = { pts[2], SkPoint::Make(1.0, 1.0) }; |
| + 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(GrColor color, |
| + const SkMatrix& viewMatrix) { |
| + return new MSAAQuadProcessor(color, viewMatrix); |
| + } |
| + |
| + virtual ~MSAAQuadProcessor() {} |
| + |
| + const char* name() const override { return "MSAAQuadProcessor"; } |
| + |
| + const Attribute* inPosition() const { return fInPosition; } |
| + const Attribute* inUV() const { return fInUV; } |
| + GrColor color() const { return fColor; } |
| + bool colorIgnored() const { return GrColor_ILLEGAL == fColor; } |
| + const SkMatrix& viewMatrix() const { return fViewMatrix; } |
| + const SkMatrix& localMatrix() const { return SkMatrix::I(); } |
| + bool usesLocalCoords() const { return fUsesLocalCoords; } |
| + |
| + 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); |
| + |
| + 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); |
| + if (!qp.colorIgnored()) { |
| + this->setupUniformColor(fsBuilder, uniformHandler, args.fOutputColor, |
| + &fColorUniform); |
| + } |
| + } |
| + |
| + 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; |
|
egdaniel
2016/03/29 20:57:39
something feels wrong with having to check perspec
ethannicholas
2016/03/31 19:33:35
GrGLSLGeometryProcessor::setupPosition generates d
|
| + 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.colorIgnored()) { |
| + GrGLfloat c[4]; |
| + GrColorToRGBAFloat(qp.color(), c); |
| + pdman.set4fv(fColorUniform, 1, c); |
| + fColor = qp.color(); |
| + } |
| + 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; |
| + |
| + GrColor fColor; |
|
egdaniel
2016/03/29 20:57:39
why are you storing fColor on the GLSLProcessor?
ethannicholas
2016/03/31 19:33:35
Cared about it at one point, forgot to remove the
|
| + UniformHandle fColorUniform; |
| + 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(GrColor color, const SkMatrix& viewMatrix) |
| + : fColor(color) |
| + , fViewMatrix(viewMatrix) { |
| + this->initClassID<MSAAQuadProcessor>(); |
| + fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType, |
| + kHigh_GrSLPrecision)); |
| + fInUV = &this->addVertexAttrib(Attribute("inUV", kVec2f_GrVertexAttribType, |
| + kHigh_GrSLPrecision)); |
| + this->setSampleShading(1.0f); |
| + } |
| + |
| + const GrColor fColor; |
| + const Attribute* fInPosition; |
| + const Attribute* fInUV; |
| + SkMatrix fViewMatrix; |
| + bool fUsesLocalCoords; |
| + |
| + 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 GrDrawBatch* Create(const Geometry& geometry, uint8_t coverage, |
| + const SkMatrix& viewMatrix, const SkRect& devBounds) { |
| + return new MSAAPathBatch(geometry, coverage, 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(this->coverage()); |
| + } |
| + |
| +private: |
| + void initBatchTracker(const GrXPOverridesForBatch& overrides) override { |
| + // Handle any color overrides |
| + if (!overrides.readsColor()) { |
| + fGeoData[0].fColor = GrColor_ILLEGAL; |
| + } |
| + overrides.getOverrideColorIfSet(&fGeoData[0].fColor); |
| + |
| + // setup batch properties |
| + fBatch.fColorIgnored = !overrides.readsColor(); |
| + fBatch.fColor = fGeoData[0].fColor; |
| + fBatch.fUsesLocalCoords = overrides.readsLocalCoords(); |
| + fBatch.fCoverageIgnored = !overrides.readsCoverage(); |
| + } |
| + |
| + 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 { |
| + SkAutoTUnref<const GrGeometryProcessor> lineGP; |
| + { |
| + using namespace GrDefaultGeoProcFactory; |
| + Color color(this->color()); |
| + Coverage coverage(this->coverage()); |
| + if (this->coverageIgnored()) { |
| + coverage.fType = Coverage::kNone_Type; |
| + } |
| + LocalCoords localCoords(this->usesLocalCoords() ? LocalCoords::kUsePosition_Type : |
| + LocalCoords::kUnused_Type); |
| + lineGP.reset(GrDefaultGeoProcFactory::Create(color, coverage, localCoords, |
| + this->viewMatrix())); |
| + } |
| + |
| + size_t lineVertexStride = lineGP->getVertexStride(); |
| + SkASSERT(lineVertexStride == sizeof(SkPoint)); |
| + |
| + SkAutoTUnref<const GrGeometryProcessor> quadGP; |
| + { |
| + using namespace GrDefaultGeoProcFactory; |
| + Color color(this->color()); |
| + Coverage coverage(this->coverage()); |
| + if (this->coverageIgnored()) { |
| + coverage.fType = Coverage::kNone_Type; |
| + } |
| + quadGP.reset(MSAAQuadProcessor::Create(this->color(), this->viewMatrix())); |
| + } |
| + size_t quadVertexStride = quadGP->getVertexStride(); |
| + SkASSERT(quadVertexStride == sizeof(MSAAQuadVertices::Vertex)); |
| + |
| + int instanceCount = fGeoData.count(); |
| + |
| + // compute number of vertices |
| + int maxLineVertices = 0; |
| + int maxQuadVertices = 0; |
| + |
| + // We will use index buffers if we have multiple paths or one path with multiple contours |
| + bool isIndexed = instanceCount > 1; |
| + for (int i = 0; i < instanceCount; i++) { |
| + const Geometry& args = fGeoData[i]; |
| + |
| + int contourCount; |
| + int lineVertices; |
| + int quadVertices; |
| + this->computeWorstCasePointCount(args.fPath, &contourCount, kTolerance, |
| + &lineVertices, &quadVertices); |
| + maxLineVertices += lineVertices; |
| + maxQuadVertices += quadVertices; |
| + isIndexed = isIndexed || contourCount > 1; |
| + } |
| + |
| + if (maxLineVertices == 0) { |
| + SkASSERT(maxQuadVertices == 0); |
| + return; |
| + } |
| + |
| + // determine primitiveType |
| + int maxLineIndices; |
| + int maxQuadIndices; |
| + GrPrimitiveType primitiveType; |
| + if (isIndexed) { |
| + maxLineIndices = maxLineVertices * 3; |
| + maxQuadIndices = maxQuadVertices * 3; |
| + primitiveType = kTriangles_GrPrimitiveType; |
| + } else { |
| + maxLineIndices = 0; |
| + maxQuadIndices = 0; |
| + primitiveType = kTriangleFan_GrPrimitiveType; |
| + } |
| + |
| + if (isIndexed && (maxLineIndices > SK_MaxU16 || maxQuadIndices > SK_MaxU16)) { |
| + SkDebugf("path too complex to render\n"); |
| + return; |
| + } |
| + |
| + // allocate vertex / index buffers |
| + const GrBuffer* lineVertexBuffer; |
| + int firstLineVertex; |
| + MSAALineVertices lines; |
| + lines.vertices = (SkPoint*) target->makeVertexSpace(lineVertexStride, |
| + maxLineVertices, |
| + &lineVertexBuffer, &firstLineVertex); |
| + if (!lines.vertices) { |
| + SkDebugf("Could not allocate vertices\n"); |
| + return; |
| + } |
| + lines.nextVertex = lines.vertices; |
| + SkDEBUGCODE(lines.verticesEnd = lines.vertices + maxLineVertices;) |
| + |
| + MSAAQuadVertices quads; |
| + SkAutoFree quadVertexPtr(sk_malloc_throw(maxQuadVertices * quadVertexStride)); |
| + quads.vertices = (MSAAQuadVertices::Vertex*) quadVertexPtr.get(); |
| + quads.nextVertex = quads.vertices; |
| + SkDEBUGCODE(quads.verticesEnd = quads.vertices + maxQuadVertices;) |
| + |
| + const GrBuffer* lineIndexBuffer = nullptr; |
| + int firstLineIndex; |
| + if (isIndexed) { |
| + lines.indices = target->makeIndexSpace(maxLineIndices, &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 (isIndexed) { |
| + quads.indices = (uint16_t*) sk_malloc_throw(maxQuadIndices * 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 < instanceCount; i++) { |
| + const Geometry& args = fGeoData[i]; |
| + |
| + if (!this->createGeom(lines, |
| + quads, |
| + args.fPath, |
| + args.fTolerance, |
| + this->viewMatrix(), |
| + isIndexed)) { |
| + return; |
| + } |
| + } |
| + int lineVertexOffset = lines.nextVertex - lines.vertices; |
| + int lineIndexOffset = lines.nextIndex - lines.indices; |
| + SkASSERT(lineVertexOffset <= maxLineVertices && lineIndexOffset <= maxLineIndices); |
| + int quadVertexOffset = quads.nextVertex - quads.vertices; |
| + int quadIndexOffset = quads.nextIndex - quads.indices; |
| + SkASSERT(quadVertexOffset <= maxQuadVertices && quadIndexOffset <= maxQuadIndices); |
| + |
| + if (lineVertexOffset) { |
| + target->initDraw(lineGP); |
| + GrMesh lineMeshes; |
| + if (isIndexed) { |
| + lineMeshes.initIndexed(primitiveType, lineVertexBuffer, lineIndexBuffer, |
| + firstLineVertex, firstLineIndex, lineVertexOffset, |
| + lineIndexOffset); |
| + } else { |
| + lineMeshes.init(primitiveType, lineVertexBuffer, firstLineVertex, |
| + lineVertexOffset); |
| + } |
| + target->draw(lineMeshes); |
| + } |
| + |
| + if (quadVertexOffset) { |
| + const GrBuffer* quadVertexBuffer; |
| + int firstQuadVertex; |
| + MSAAQuadVertices::Vertex* quadVertices = (MSAAQuadVertices::Vertex*) |
| + target->makeVertexSpace(quadVertexStride, quadVertexOffset, &quadVertexBuffer, |
| + &firstQuadVertex); |
| + memcpy(quadVertices, quads.vertices, quadVertexStride * quadVertexOffset); |
| + target->initDraw(quadGP); |
| + GrMesh quadMeshes; |
| + if (isIndexed) { |
| + const GrBuffer* quadIndexBuffer; |
| + int firstQuadIndex; |
| + uint16_t* quadIndices = (uint16_t*) target->makeIndexSpace(maxQuadIndices, |
| + &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(quadMeshes); |
| + } |
| + } |
| + |
| + SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } |
| + |
| + MSAAPathBatch(const Geometry& geometry, uint8_t coverage, const SkMatrix& viewMatrix, |
| + const SkRect& devBounds) |
| + : INHERITED(ClassID()) { |
| + fBatch.fCoverage = coverage; |
| + fBatch.fViewMatrix = viewMatrix; |
| + fGeoData.push_back(geometry); |
| + |
| + this->setBounds(devBounds); |
| + } |
| + |
| + 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; |
| + } |
| + |
|
bsalomon
2016/03/29 21:43:22
why not use vertex colors?
|
| + if (this->color() != that->color()) { |
| + return false; |
| + } |
| + |
| + if (this->coverage() != that->coverage()) { |
| + return false; |
| + } |
| + |
| + if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) { |
| + return false; |
| + } |
| + |
| + fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin()); |
| + this->joinBounds(that->bounds()); |
| + return true; |
| + } |
| + |
| + bool createGeom(MSAALineVertices& lines, |
| + MSAAQuadVertices& quads, |
| + const SkPath& path, |
| + SkScalar srcSpaceTol, |
| + const SkMatrix& m, |
| + 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]; |
| + 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]; |
| + 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, isIndexed, subpathIdxStart); |
| + } |
| + break; |
| + } |
| + case SkPath::kQuad_Verb: { |
| + add_quad(lines, quads, pts, 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], isIndexed, subpathIdxStart); |
| + } |
| + break; |
| + } |
| + case SkPath::kClose_Verb: |
| + break; |
| + case SkPath::kDone_Verb: |
| + done = true; |
| + } |
| + first = false; |
| + } |
| + } |
| + return true; |
| + } |
| + |
| + GrColor color() const { return fBatch.fColor; } |
| + uint8_t coverage() const { return fBatch.fCoverage; } |
|
bsalomon
2016/03/29 21:43:22
Why do you have a coverage?
|
| + bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } |
| + const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; } |
| + bool coverageIgnored() const { return fBatch.fCoverageIgnored; } |
| + |
| + struct BatchTracker { |
|
bsalomon
2016/03/29 21:43:22
Older batches use this batch tracker struct, but t
|
| + GrColor fColor; |
| + uint8_t fCoverage; |
| + SkMatrix fViewMatrix; |
| + bool fUsesLocalCoords; |
| + bool fColorIgnored; |
|
bsalomon
2016/03/29 21:43:22
I think we should drop the color ignored stuff. It
|
| + bool fCoverageIgnored; |
| + }; |
| + |
| + BatchTracker fBatch; |
| + SkSTArray<1, Geometry, true> fGeoData; |
| + |
| + 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); |
| + |
| + uint8_t newCoverage = 0xff; |
|
bsalomon
2016/03/29 21:43:22
?
|
| + |
| + // Save the current xp on the draw state so we can reset it if needed |
|
bsalomon
2016/03/29 21:43:22
draw state?
|
| + 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] = &gWindStencil; |
| + 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<GrDrawBatch> batch(MSAAPathBatch::Create(geometry, newCoverage, |
| + viewMatrix, devBounds)); |
| + |
| + target->drawBatch(*pipelineBuilder, batch); |
| + } |
| + } |
| + 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(); |
| + } |
| + bool result = this->internalDrawPath(args.fTarget, |
| + args.fPipelineBuilder, |
| + args.fColor, |
| + *args.fViewMatrix, |
| + path, |
| + stroke, |
| + false); |
| + return result; |
| +} |
| + |
| +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); |
| +} |
| + |
| +/////////////////////////////////////////////////////////////////////////////////////////////////// |