From 6536ae590e3873694cb02cf25dab8001a71f269e Mon Sep 17 00:00:00 2001 From: ethannicholas Date: Mon, 2 May 2016 12:16:49 -0700 Subject: [PATCH] added GrMSAAPathRenderer GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1834133003 Committed: https://skia.googlesource.com/skia/+/47a2dc8e229e93e1bcf7405747320920da1ab742 Committed: https://skia.googlesource.com/skia/+/b0fabd4ed55abcb6f5ed2f331c96c783f42bd37c Review-Url: https://codereview.chromium.org/1834133003 --- gyp/gpu.gypi | 2 + src/gpu/GrPathRendererChain.cpp | 6 +- src/gpu/batches/GrDefaultPathRenderer.cpp | 124 ---- src/gpu/batches/GrDefaultPathRenderer.h | 1 + src/gpu/batches/GrMSAAPathRenderer.cpp | 745 ++++++++++++++++++++++ src/gpu/batches/GrMSAAPathRenderer.h | 35 + src/gpu/batches/GrPathStencilSettings.h | 134 ++++ src/gpu/gl/GrGLCaps.cpp | 3 +- 8 files changed, 924 insertions(+), 126 deletions(-) create mode 100644 src/gpu/batches/GrMSAAPathRenderer.cpp create mode 100644 src/gpu/batches/GrMSAAPathRenderer.h create mode 100644 src/gpu/batches/GrPathStencilSettings.h diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi index d0e78431ec..69f286d239 100644 --- a/gyp/gpu.gypi +++ b/gyp/gpu.gypi @@ -234,6 +234,8 @@ '<(skia_src_path)/gpu/batches/GrDrawPathBatch.h', '<(skia_src_path)/gpu/batches/GrDrawVerticesBatch.cpp', '<(skia_src_path)/gpu/batches/GrDrawVerticesBatch.h', + '<(skia_src_path)/gpu/batches/GrMSAAPathRenderer.cpp', + '<(skia_src_path)/gpu/batches/GrMSAAPathRenderer.h', '<(skia_src_path)/gpu/batches/GrNonAAFillRectBatch.h', '<(skia_src_path)/gpu/batches/GrNonAAFillRectBatch.cpp', '<(skia_src_path)/gpu/batches/GrNonAAStrokeRectBatch.cpp', diff --git a/src/gpu/GrPathRendererChain.cpp b/src/gpu/GrPathRendererChain.cpp index 098d8fb2d9..e19a134467 100644 --- a/src/gpu/GrPathRendererChain.cpp +++ b/src/gpu/GrPathRendererChain.cpp @@ -20,9 +20,10 @@ #include "batches/GrAALinearizingConvexPathRenderer.h" #include "batches/GrDashLinePathRenderer.h" #include "batches/GrDefaultPathRenderer.h" +#include "batches/GrMSAAPathRenderer.h" +#include "batches/GrPLSPathRenderer.h" #include "batches/GrStencilAndCoverPathRenderer.h" #include "batches/GrTessellatingPathRenderer.h" -#include "batches/GrPLSPathRenderer.h" GrPathRendererChain::GrPathRendererChain(GrContext* context) { const GrCaps& caps = *context->caps(); @@ -32,6 +33,9 @@ GrPathRendererChain::GrPathRendererChain(GrContext* context) { caps)) { this->addPathRenderer(pr)->unref(); } + if (caps.sampleShadingSupport()) { + this->addPathRenderer(new GrMSAAPathRenderer)->unref(); + } this->addPathRenderer(new GrTessellatingPathRenderer)->unref(); this->addPathRenderer(new GrAAHairLinePathRenderer)->unref(); this->addPathRenderer(new GrAAConvexPathRenderer)->unref(); diff --git a/src/gpu/batches/GrDefaultPathRenderer.cpp b/src/gpu/batches/GrDefaultPathRenderer.cpp index 4bf506ed77..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 - -static constexpr GrStencilSettings gEOStencilPass( - kInvert_StencilOp, - kKeep_StencilOp, - kAlwaysIfInClip_StencilFunc, - 0xffff, - 0xffff, - 0xffff); - -// ok not to check clip b/c stencil pass only wrote inside clip -static constexpr GrStencilSettings gEOColorPass( - kZero_StencilOp, - kZero_StencilOp, - kNotEqual_StencilFunc, - 0xffff, - 0x0000, - 0xffff); - -// have to check clip b/c outside clip will always be zero. -static constexpr GrStencilSettings 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. - -static constexpr GrStencilSettings 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. -static constexpr GrStencilSettings 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. -static constexpr GrStencilSettings gWindSingleStencilWithWrapInc( - kIncWrap_StencilOp, - kKeep_StencilOp, - kAlwaysIfInClip_StencilFunc, - 0xffff, - 0xffff, - 0xffff); - -static constexpr GrStencilSettings gWindSingleStencilWithWrapDec( - kDecWrap_StencilOp, - kKeep_StencilOp, - kAlwaysIfInClip_StencilFunc, - 0xffff, - 0xffff, - 0xffff); - -static constexpr GrStencilSettings gWindSingleStencilNoWrapInc( - kInvert_StencilOp, - kIncClamp_StencilOp, - kEqual_StencilFunc, - 0xffff, - 0xffff, - 0xffff); - -static constexpr GrStencilSettings 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 - -static constexpr GrStencilSettings gWindColorPass( - kZero_StencilOp, - kZero_StencilOp, - kNonZeroIfInClip_StencilFunc, - 0xffff, - 0x0000, - 0xffff); - -static constexpr GrStencilSettings 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. -static constexpr GrStencilSettings 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..988952822a --- /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(); + 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(); + 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(); + 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& transforms) override { + this->setTransformDataHelper(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(); + 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 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 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(); + 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) || + (fMaxQuadIndices + that->fMaxQuadIndices > SK_MaxU16)) { + return false; + } + + 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 stroke(origStroke); + + const GrXPFactory* xpFactory = pipelineBuilder->getXPFactory(); + SkAutoTUnref 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 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 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..dd930a0da7 --- /dev/null +++ b/src/gpu/batches/GrPathStencilSettings.h @@ -0,0 +1,134 @@ +/* + * 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 + +//////////////////////////////////////////////////////////////////////////////// +// Stencil rules for paths + +////// Even/Odd + +static constexpr GrStencilSettings gEOStencilPass( + kInvert_StencilOp, + kKeep_StencilOp, + kAlwaysIfInClip_StencilFunc, + 0xffff, + 0xffff, + 0xffff); + +// ok not to check clip b/c stencil pass only wrote inside clip +static constexpr GrStencilSettings gEOColorPass( + kZero_StencilOp, + kZero_StencilOp, + kNotEqual_StencilFunc, + 0xffff, + 0x0000, + 0xffff); + +// have to check clip b/c outside clip will always be zero. +static constexpr GrStencilSettings 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. + +static constexpr GrStencilSettings 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. +static constexpr GrStencilSettings 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. +static constexpr GrStencilSettings gWindSingleStencilWithWrapInc( + kIncWrap_StencilOp, + kKeep_StencilOp, + kAlwaysIfInClip_StencilFunc, + 0xffff, + 0xffff, + 0xffff); + +static constexpr GrStencilSettings gWindSingleStencilWithWrapDec( + kDecWrap_StencilOp, + kKeep_StencilOp, + kAlwaysIfInClip_StencilFunc, + 0xffff, + 0xffff, + 0xffff); + +static constexpr GrStencilSettings gWindSingleStencilNoWrapInc( + kInvert_StencilOp, + kIncClamp_StencilOp, + kEqual_StencilFunc, + 0xffff, + 0xffff, + 0xffff); + +static constexpr GrStencilSettings 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 + +static constexpr GrStencilSettings gWindColorPass( + kZero_StencilOp, + kZero_StencilOp, + kNonZeroIfInClip_StencilFunc, + 0xffff, + 0x0000, + 0xffff); + +static constexpr GrStencilSettings 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. +static constexpr GrStencilSettings gDirectToStencil( + kZero_StencilOp, + kIncClamp_StencilOp, + kAlwaysIfInClip_StencilFunc, + 0xffff, + 0x0000, + 0xffff); + +#endif diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp index f3e494acb9..9b12ce69c5 100644 --- a/src/gpu/gl/GrGLCaps.cpp +++ b/src/gpu/gl/GrGLCaps.cpp @@ -535,7 +535,8 @@ void GrGLCaps::init(const GrContextOptions& contextOptions, } if (kGL_GrGLStandard == standard) { - if (version >= GR_GL_VER(4, 0) || ctxInfo.hasExtension("GL_ARB_sample_shading")) { + if ((version >= GR_GL_VER(4, 0) || ctxInfo.hasExtension("GL_ARB_sample_shading")) && + ctxInfo.vendor() != kIntel_GrGLVendor) { fSampleShadingSupport = true; } } else if (ctxInfo.hasExtension("GL_OES_sample_shading")) {