| Index: src/gpu/batches/GrAnalyticRectBatch.cpp
 | 
| diff --git a/src/gpu/batches/GrAnalyticRectBatch.cpp b/src/gpu/batches/GrAnalyticRectBatch.cpp
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..b41aa7f71940e841a3a4c6aa891de41bb901d2bb
 | 
| --- /dev/null
 | 
| +++ b/src/gpu/batches/GrAnalyticRectBatch.cpp
 | 
| @@ -0,0 +1,414 @@
 | 
| +/*
 | 
| + * 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 "GrAnalyticRectBatch.h"
 | 
| +
 | 
| +#include "GrBatchFlushState.h"
 | 
| +#include "GrBatchTest.h"
 | 
| +#include "GrGeometryProcessor.h"
 | 
| +#include "GrInvariantOutput.h"
 | 
| +#include "GrProcessor.h"
 | 
| +#include "GrResourceProvider.h"
 | 
| +#include "SkRRect.h"
 | 
| +#include "SkStrokeRec.h"
 | 
| +#include "batches/GrVertexBatch.h"
 | 
| +#include "glsl/GrGLSLFragmentShaderBuilder.h"
 | 
| +#include "glsl/GrGLSLGeometryProcessor.h"
 | 
| +#include "glsl/GrGLSLProgramDataManager.h"
 | 
| +#include "glsl/GrGLSLVarying.h"
 | 
| +#include "glsl/GrGLSLVertexShaderBuilder.h"
 | 
| +#include "glsl/GrGLSLUniformHandler.h"
 | 
| +#include "glsl/GrGLSLUtil.h"
 | 
| +
 | 
| +namespace {
 | 
| +
 | 
| +struct RectVertex {
 | 
| +    SkPoint  fPos;
 | 
| +    GrColor  fColor;
 | 
| +    SkPoint  fCenter;
 | 
| +    SkVector fDownDir;
 | 
| +    SkScalar fHalfWidth;
 | 
| +    SkScalar fHalfHeight;
 | 
| +};
 | 
| +
 | 
| +}
 | 
| +
 | 
| +///////////////////////////////////////////////////////////////////////////////
 | 
| +
 | 
| +/**
 | 
| + * The output of this effect is the input color and coverage for an arbitrarily oriented rect. The
 | 
| + * rect is specified as:
 | 
| + *      Center of the rect
 | 
| + *      Unit vector point down the height of the rect
 | 
| + *      Half width + 0.5
 | 
| + *      Half height + 0.5
 | 
| + * The center and vector are stored in a vec4 varying ("RectEdge") with the
 | 
| + * center in the xy components and the vector in the zw components.
 | 
| + * The munged width and height are stored in a vec2 varying ("WidthHeight")
 | 
| + * with the width in x and the height in y.
 | 
| + */
 | 
| +class RectGeometryProcessor : public GrGeometryProcessor {
 | 
| +public:
 | 
| +    RectGeometryProcessor(const SkMatrix& localMatrix) : fLocalMatrix(localMatrix) {
 | 
| +        this->initClassID<RectGeometryProcessor>();
 | 
| +        fInPosition    = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
 | 
| +                                                          kHigh_GrSLPrecision));
 | 
| +        fInColor       = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
 | 
| +        fInRectEdge    = &this->addVertexAttrib(Attribute("inRectEdge", kVec4f_GrVertexAttribType));
 | 
| +        fInWidthHeight = &this->addVertexAttrib(Attribute("inWidthHeight",
 | 
| +                                                          kVec2f_GrVertexAttribType));
 | 
| +    }
 | 
| +
 | 
| +    bool implementsDistanceVector() const override { return true; };
 | 
| +
 | 
| +    const Attribute* inPosition()    const { return fInPosition; }
 | 
| +    const Attribute* inColor()       const { return fInColor; }
 | 
| +    const Attribute* inRectEdge()    const { return fInRectEdge;    }
 | 
| +    const Attribute* inWidthHeight() const { return fInWidthHeight; }
 | 
| +
 | 
| +    const SkMatrix& localMatrix() const { return fLocalMatrix; }
 | 
| +
 | 
| +    virtual ~RectGeometryProcessor() {}
 | 
| +
 | 
| +    const char* name() const override { return "RectEdge"; }
 | 
| +
 | 
| +    class GLSLProcessor : public GrGLSLGeometryProcessor {
 | 
| +    public:
 | 
| +        GLSLProcessor() {}
 | 
| +
 | 
| +        void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
 | 
| +            const RectGeometryProcessor& rgp = args.fGP.cast<RectGeometryProcessor>();
 | 
| +            GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
 | 
| +            GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
 | 
| +            GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
 | 
| +
 | 
| +            // emit attributes
 | 
| +            varyingHandler->emitAttributes(rgp);
 | 
| +
 | 
| +            // setup the varying for the position
 | 
| +            GrGLSLVertToFrag positionVary(kVec2f_GrSLType);
 | 
| +            varyingHandler->addVarying("Position", &positionVary);
 | 
| +            vertBuilder->codeAppendf("%s = %s;", positionVary.vsOut(), rgp.inPosition()->fName);
 | 
| +
 | 
| +            // setup the varying for the center point and the unit vector that points down the
 | 
| +            // height of the rect
 | 
| +            GrGLSLVertToFrag rectEdgeVary(kVec4f_GrSLType);
 | 
| +            varyingHandler->addVarying("RectEdge", &rectEdgeVary);
 | 
| +            vertBuilder->codeAppendf("%s = %s;", rectEdgeVary.vsOut(), rgp.inRectEdge()->fName);
 | 
| +
 | 
| +            // setup the varying for the width/2+.5 and height/2+.5
 | 
| +            GrGLSLVertToFrag widthHeightVary(kVec2f_GrSLType);
 | 
| +            varyingHandler->addVarying("WidthHeight", &widthHeightVary);
 | 
| +            vertBuilder->codeAppendf("%s = %s;",
 | 
| +                                     widthHeightVary.vsOut(), rgp.inWidthHeight()->fName);
 | 
| +
 | 
| +            GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
 | 
| +
 | 
| +            // setup pass through color
 | 
| +            varyingHandler->addPassThroughAttribute(rgp.inColor(), args.fOutputColor);
 | 
| +
 | 
| +            // Setup position
 | 
| +            this->setupPosition(vertBuilder, gpArgs, rgp.inPosition()->fName);
 | 
| +
 | 
| +            // emit transforms
 | 
| +            this->emitTransforms(vertBuilder,
 | 
| +                                 varyingHandler,
 | 
| +                                 uniformHandler,
 | 
| +                                 gpArgs->fPositionVar,
 | 
| +                                 rgp.inPosition()->fName,
 | 
| +                                 rgp.localMatrix(),
 | 
| +                                 args.fTransformsIn,
 | 
| +                                 args.fTransformsOut);
 | 
| +
 | 
| +            // TODO: compute all these offsets, spans, and scales in the VS
 | 
| +            fragBuilder->codeAppendf("float insetW = min(1.0, %s.x) - 0.5;",
 | 
| +                                     widthHeightVary.fsIn());
 | 
| +            fragBuilder->codeAppendf("float insetH = min(1.0, %s.y) - 0.5;",
 | 
| +                                     widthHeightVary.fsIn());
 | 
| +            fragBuilder->codeAppend("float outset = 0.5;");
 | 
| +            // For rects > 1 pixel wide and tall the span's are noops (i.e., 1.0). For rects
 | 
| +            // < 1 pixel wide or tall they serve to normalize the < 1 ramp to a 0 .. 1 range.
 | 
| +            fragBuilder->codeAppend("float spanW = insetW + outset;");
 | 
| +            fragBuilder->codeAppend("float spanH = insetH + outset;");
 | 
| +            // For rects < 1 pixel wide or tall, these scale factors are used to cap the maximum
 | 
| +            // value of coverage that is used. In other words it is the coverage that is
 | 
| +            // used in the interior of the rect after the ramp.
 | 
| +            fragBuilder->codeAppend("float scaleW = min(1.0, 2.0*insetW/spanW);");
 | 
| +            fragBuilder->codeAppend("float scaleH = min(1.0, 2.0*insetH/spanH);");
 | 
| +            // Compute the coverage for the rect's width
 | 
| +            fragBuilder->codeAppendf("vec2 offset = %s.xy - %s.xy;",
 | 
| +                                     positionVary.fsIn(), rectEdgeVary.fsIn());
 | 
| +            fragBuilder->codeAppendf("float perpDot = abs(offset.x * %s.w - offset.y * %s.z);",
 | 
| +                                     rectEdgeVary.fsIn(), rectEdgeVary.fsIn());
 | 
| +
 | 
| +            if (args.fDistanceVectorName) {
 | 
| +                fragBuilder->codeAppendf("float widthDistance = %s.x - perpDot;",
 | 
| +                                         widthHeightVary.fsIn());
 | 
| +            }
 | 
| +
 | 
| +            fragBuilder->codeAppendf(
 | 
| +                    "float coverage = scaleW*clamp((%s.x-perpDot)/spanW, 0.0, 1.0);",
 | 
| +                    widthHeightVary.fsIn());
 | 
| +            // Compute the coverage for the rect's height and merge with the width
 | 
| +            fragBuilder->codeAppendf("perpDot = abs(dot(offset, %s.zw));",
 | 
| +                                   rectEdgeVary.fsIn());
 | 
| +
 | 
| +            if (args.fDistanceVectorName) {
 | 
| +                fragBuilder->codeAppendf("float heightDistance = %s.y - perpDot;",
 | 
| +                                         widthHeightVary.fsIn());
 | 
| +            }
 | 
| +
 | 
| +            fragBuilder->codeAppendf(
 | 
| +                    "coverage = coverage*scaleH*clamp((%s.y-perpDot)/spanH, 0.0, 1.0);",
 | 
| +                    widthHeightVary.fsIn());
 | 
| +
 | 
| +            fragBuilder->codeAppendf("%s = vec4(coverage);", args.fOutputCoverage);
 | 
| +
 | 
| +            if (args.fDistanceVectorName) {
 | 
| +                fragBuilder->codeAppend( "// Calculating distance vector\n");
 | 
| +                fragBuilder->codeAppend( "vec2 dvAxis;");
 | 
| +                fragBuilder->codeAppend( "float dvLength;");
 | 
| +
 | 
| +                fragBuilder->codeAppend( "if (heightDistance < widthDistance) {");
 | 
| +                fragBuilder->codeAppendf("    dvAxis = %s.zw;", rectEdgeVary.fsIn());
 | 
| +                fragBuilder->codeAppend( "    dvLength = heightDistance;");
 | 
| +                fragBuilder->codeAppend( "} else {");
 | 
| +                fragBuilder->codeAppendf("    dvAxis = vec2(-%s.w, %s.z);",
 | 
| +                                         rectEdgeVary.fsIn(), rectEdgeVary.fsIn());
 | 
| +                fragBuilder->codeAppend( "    dvLength = widthDistance;");
 | 
| +                fragBuilder->codeAppend( "}");
 | 
| +
 | 
| +                fragBuilder->codeAppend( "float dvSign = sign(dot(offset, dvAxis));");
 | 
| +                fragBuilder->codeAppendf("%s = vec3(dvSign * dvAxis, dvLength);",
 | 
| +                                         args.fDistanceVectorName);
 | 
| +
 | 
| +            }
 | 
| +        }
 | 
| +
 | 
| +        static void GenKey(const GrGeometryProcessor& gp,
 | 
| +                           const GrGLSLCaps&,
 | 
| +                           GrProcessorKeyBuilder* b) {
 | 
| +            b->add32(0x0);
 | 
| +        }
 | 
| +
 | 
| +        void setData(const GrGLSLProgramDataManager& pdman,
 | 
| +                     const GrPrimitiveProcessor& gp) override {}
 | 
| +
 | 
| +        void setTransformData(const GrPrimitiveProcessor& primProc,
 | 
| +                              const GrGLSLProgramDataManager& pdman,
 | 
| +                              int index,
 | 
| +                              const SkTArray<const GrCoordTransform*, true>& transforms) override {
 | 
| +            this->setTransformDataHelper<RectGeometryProcessor>(primProc, pdman, index, transforms);
 | 
| +        }
 | 
| +
 | 
| +    private:
 | 
| +        typedef GrGLSLGeometryProcessor INHERITED;
 | 
| +    };
 | 
| +
 | 
| +    void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
 | 
| +        GLSLProcessor::GenKey(*this, caps, b);
 | 
| +    }
 | 
| +
 | 
| +    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
 | 
| +        return new GLSLProcessor();
 | 
| +    }
 | 
| +
 | 
| +private:
 | 
| +    SkMatrix         fLocalMatrix;
 | 
| +
 | 
| +    const Attribute* fInPosition;
 | 
| +    const Attribute* fInColor;
 | 
| +    const Attribute* fInRectEdge;
 | 
| +    const Attribute* fInWidthHeight;
 | 
| +
 | 
| +    GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
 | 
| +
 | 
| +    typedef GrGeometryProcessor INHERITED;
 | 
| +};
 | 
| +
 | 
| +GR_DEFINE_GEOMETRY_PROCESSOR_TEST(RectGeometryProcessor);
 | 
| +
 | 
| +sk_sp<GrGeometryProcessor> RectGeometryProcessor::TestCreate(GrProcessorTestData* d) {
 | 
| +    return sk_sp<GrGeometryProcessor>(
 | 
| +        new RectGeometryProcessor(GrTest::TestMatrix(d->fRandom)));
 | 
| +}
 | 
| +
 | 
| +///////////////////////////////////////////////////////////////////////////////
 | 
| +
 | 
| +class AnalyticRectBatch : public GrVertexBatch {
 | 
| +public:
 | 
| +    DEFINE_BATCH_CLASS_ID
 | 
| +
 | 
| +    AnalyticRectBatch(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect,
 | 
| +                      const SkRect& croppedRect, const SkRect& bounds)
 | 
| +        : INHERITED(ClassID())
 | 
| +        , fViewMatrixIfUsingLocalCoords(viewMatrix) {
 | 
| +        SkPoint center = SkPoint::Make(rect.centerX(), rect.centerY());
 | 
| +        viewMatrix.mapPoints(¢er, 1);
 | 
| +        SkScalar halfWidth = viewMatrix.mapRadius(SkScalarHalf(rect.width()));
 | 
| +        SkScalar halfHeight = viewMatrix.mapRadius(SkScalarHalf(rect.height()));
 | 
| +        SkVector downDir = viewMatrix.mapVector(0.0f, 1.0f);
 | 
| +        downDir.normalize();
 | 
| +
 | 
| +        SkRect deviceSpaceCroppedRect = croppedRect;
 | 
| +        viewMatrix.mapRect(&deviceSpaceCroppedRect);
 | 
| +
 | 
| +        fGeoData.emplace_back(Geometry {color, center, downDir, halfWidth, halfHeight,
 | 
| +                                        deviceSpaceCroppedRect});
 | 
| +
 | 
| +        this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
 | 
| +    }
 | 
| +
 | 
| +    const char* name() const override { return "AnalyticRectBatch"; }
 | 
| +
 | 
| +    SkString dumpInfo() const override {
 | 
| +        SkString string;
 | 
| +        for (int i = 0; i < fGeoData.count(); ++i) {
 | 
| +            string.appendf("Color: 0x%08x Rect [C:(%.2f, %.2f) D:<%.2f,%.3f> W/2:%.2f H/2:%.2f]\n",
 | 
| +                           fGeoData[i].fColor,
 | 
| +                           fGeoData[i].fCenter.x(), fGeoData[i].fCenter.y(),
 | 
| +                           fGeoData[i].fDownDir.x(), fGeoData[i].fDownDir.y(),
 | 
| +                           fGeoData[i].fHalfWidth,
 | 
| +                           fGeoData[i].fHalfHeight);
 | 
| +        }
 | 
| +        string.append(INHERITED::dumpInfo());
 | 
| +        return string;
 | 
| +    }
 | 
| +
 | 
| +    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->setUnknownSingleComponent();
 | 
| +    }
 | 
| +
 | 
| +private:
 | 
| +    void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
 | 
| +        // Handle any overrides that affect our GP.
 | 
| +        overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
 | 
| +        if (!overrides.readsLocalCoords()) {
 | 
| +            fViewMatrixIfUsingLocalCoords.reset();
 | 
| +        }
 | 
| +    }
 | 
| +
 | 
| +    void onPrepareDraws(Target* target) const override {
 | 
| +        SkMatrix localMatrix;
 | 
| +        if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
 | 
| +            return;
 | 
| +        }
 | 
| +
 | 
| +        // Setup geometry processor
 | 
| +        SkAutoTUnref<GrGeometryProcessor> gp(new RectGeometryProcessor(localMatrix));
 | 
| +
 | 
| +        int instanceCount = fGeoData.count();
 | 
| +        size_t vertexStride = gp->getVertexStride();
 | 
| +        SkASSERT(vertexStride == sizeof(RectVertex));
 | 
| +        QuadHelper helper;
 | 
| +        RectVertex* verts = reinterpret_cast<RectVertex*>(helper.init(target, vertexStride,
 | 
| +                                                                      instanceCount));
 | 
| +        if (!verts) {
 | 
| +            return;
 | 
| +        }
 | 
| +
 | 
| +        for (int i = 0; i < instanceCount; i++) {
 | 
| +            const Geometry& geom = fGeoData[i];
 | 
| +
 | 
| +            GrColor  color       = geom.fColor;
 | 
| +            SkPoint  center      = geom.fCenter;
 | 
| +            SkVector downDir     = geom.fDownDir;
 | 
| +            SkScalar halfWidth   = geom.fHalfWidth;
 | 
| +            SkScalar halfHeight  = geom.fHalfHeight;
 | 
| +            SkRect   croppedRect = geom.fCroppedRect;
 | 
| +
 | 
| +            SkVector rightDir;
 | 
| +            downDir.rotateCCW(&rightDir);
 | 
| +
 | 
| +            verts[0].fPos = {croppedRect.fLeft, croppedRect.fTop};
 | 
| +            verts[0].fColor = color;
 | 
| +            verts[0].fCenter = center;
 | 
| +            verts[0].fDownDir = downDir;
 | 
| +            verts[0].fHalfWidth = halfWidth;
 | 
| +            verts[0].fHalfHeight = halfHeight;
 | 
| +
 | 
| +            verts[1].fPos = {croppedRect.fRight, croppedRect.fTop};
 | 
| +            verts[1].fColor = color;
 | 
| +            verts[1].fCenter = center;
 | 
| +            verts[1].fDownDir = downDir;
 | 
| +            verts[1].fHalfWidth = halfWidth;
 | 
| +            verts[1].fHalfHeight = halfHeight;
 | 
| +
 | 
| +            verts[2].fPos = {croppedRect.fRight, croppedRect.fBottom};
 | 
| +            verts[2].fColor = color;
 | 
| +            verts[2].fCenter = center;
 | 
| +            verts[2].fDownDir = downDir;
 | 
| +            verts[2].fHalfWidth = halfWidth;
 | 
| +            verts[2].fHalfHeight = halfHeight;
 | 
| +
 | 
| +            verts[3].fPos = {croppedRect.fLeft, croppedRect.fBottom};
 | 
| +            verts[3].fColor = color;
 | 
| +            verts[3].fCenter = center;
 | 
| +            verts[3].fDownDir = downDir;
 | 
| +            verts[3].fHalfWidth = halfWidth;
 | 
| +            verts[3].fHalfHeight = halfHeight;
 | 
| +
 | 
| +            verts += kVerticesPerQuad;
 | 
| +        }
 | 
| +        helper.recordDraw(target, gp);
 | 
| +    }
 | 
| +
 | 
| +    bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
 | 
| +        AnalyticRectBatch* that = t->cast<AnalyticRectBatch>();
 | 
| +        if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
 | 
| +                                    that->bounds(), caps)) {
 | 
| +            return false;
 | 
| +        }
 | 
| +
 | 
| +        if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
 | 
| +            return false;
 | 
| +        }
 | 
| +
 | 
| +        fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
 | 
| +        this->joinBounds(*that);
 | 
| +        return true;
 | 
| +    }
 | 
| +
 | 
| +    struct Geometry {
 | 
| +        GrColor  fColor;
 | 
| +        SkPoint  fCenter;
 | 
| +        SkVector fDownDir;
 | 
| +        SkScalar fHalfWidth;
 | 
| +        SkScalar fHalfHeight;
 | 
| +        SkRect   fCroppedRect;
 | 
| +    };
 | 
| +
 | 
| +    SkMatrix                     fViewMatrixIfUsingLocalCoords;
 | 
| +    SkSTArray<1, Geometry, true> fGeoData;
 | 
| +
 | 
| +    typedef GrVertexBatch INHERITED;
 | 
| +};
 | 
| +
 | 
| +GrDrawBatch* GrAnalyticRectBatch::CreateAnalyticRectBatch(GrColor color,
 | 
| +                                                          const SkMatrix& viewMatrix,
 | 
| +                                                          const SkRect& rect,
 | 
| +                                                          const SkRect& croppedRect,
 | 
| +                                                          const SkRect& bounds) {
 | 
| +    return new AnalyticRectBatch(color, viewMatrix, rect, croppedRect, bounds);
 | 
| +}
 | 
| +
 | 
| +#ifdef GR_TEST_UTILS
 | 
| +
 | 
| +DRAW_BATCH_TEST_DEFINE(AnalyticRectBatch) {
 | 
| +    SkMatrix viewMatrix = GrTest::TestMatrix(random);
 | 
| +    GrColor color = GrRandomColor(random);
 | 
| +    SkRect rect = GrTest::TestSquare(random);
 | 
| +    SkRect croppedRect = GrTest::TestSquare(random);
 | 
| +    SkRect bounds = GrTest::TestSquare(random);
 | 
| +    return new AnalyticRectBatch(color, viewMatrix, rect, croppedRect, bounds);
 | 
| +}
 | 
| +
 | 
| +#endif
 | 
| 
 |