Home | History | Annotate | Download | only in ops
      1 /*
      2  * Copyright 2016 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "GrAnalyticRectOp.h"
      9 
     10 #include "GrDrawOpTest.h"
     11 #include "GrGeometryProcessor.h"
     12 #include "GrOpFlushState.h"
     13 #include "GrProcessor.h"
     14 #include "GrResourceProvider.h"
     15 #include "SkRRect.h"
     16 #include "SkStrokeRec.h"
     17 #include "glsl/GrGLSLFragmentShaderBuilder.h"
     18 #include "glsl/GrGLSLGeometryProcessor.h"
     19 #include "glsl/GrGLSLProgramDataManager.h"
     20 #include "glsl/GrGLSLUniformHandler.h"
     21 #include "glsl/GrGLSLUtil.h"
     22 #include "glsl/GrGLSLVarying.h"
     23 #include "glsl/GrGLSLVertexShaderBuilder.h"
     24 #include "ops/GrMeshDrawOp.h"
     25 
     26 namespace {
     27 
     28 struct RectVertex {
     29     SkPoint fPos;
     30     GrColor fColor;
     31     SkPoint fCenter;
     32     SkVector fDownDir;
     33     SkScalar fHalfWidth;
     34     SkScalar fHalfHeight;
     35 };
     36 }
     37 
     38 ///////////////////////////////////////////////////////////////////////////////
     39 
     40 /**
     41  * The output of this effect is the input color and coverage for an arbitrarily oriented rect. The
     42  * rect is specified as:
     43  *      Center of the rect
     44  *      Unit vector point down the height of the rect
     45  *      Half width + 0.5
     46  *      Half height + 0.5
     47  * The center and vector are stored in a vec4 varying ("RectEdge") with the
     48  * center in the xy components and the vector in the zw components.
     49  * The munged width and height are stored in a vec2 varying ("WidthHeight")
     50  * with the width in x and the height in y.
     51  */
     52 class RectGeometryProcessor : public GrGeometryProcessor {
     53 public:
     54     RectGeometryProcessor(const SkMatrix& localMatrix) : fLocalMatrix(localMatrix) {
     55         this->initClassID<RectGeometryProcessor>();
     56         fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
     57                                              kHigh_GrSLPrecision);
     58         fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
     59         fInRectEdge = &this->addVertexAttrib("inRectEdge", kVec4f_GrVertexAttribType);
     60         fInWidthHeight = &this->addVertexAttrib("inWidthHeight", kVec2f_GrVertexAttribType);
     61     }
     62 
     63     bool implementsDistanceVector() const override { return true; }
     64 
     65     const Attribute* inPosition() const { return fInPosition; }
     66     const Attribute* inColor() const { return fInColor; }
     67     const Attribute* inRectEdge() const { return fInRectEdge; }
     68     const Attribute* inWidthHeight() const { return fInWidthHeight; }
     69 
     70     const SkMatrix& localMatrix() const { return fLocalMatrix; }
     71 
     72     ~RectGeometryProcessor() override {}
     73 
     74     const char* name() const override { return "RectEdge"; }
     75 
     76     class GLSLProcessor : public GrGLSLGeometryProcessor {
     77     public:
     78         GLSLProcessor() {}
     79 
     80         void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
     81             const RectGeometryProcessor& rgp = args.fGP.cast<RectGeometryProcessor>();
     82             GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
     83             GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
     84             GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
     85 
     86             // emit attributes
     87             varyingHandler->emitAttributes(rgp);
     88 
     89             // setup the varying for the position
     90             GrGLSLVertToFrag positionVary(kVec2f_GrSLType);
     91             varyingHandler->addVarying("Position", &positionVary);
     92             vertBuilder->codeAppendf("%s = %s;", positionVary.vsOut(), rgp.inPosition()->fName);
     93 
     94             // setup the varying for the center point and the unit vector that points down the
     95             // height of the rect
     96             GrGLSLVertToFrag rectEdgeVary(kVec4f_GrSLType);
     97             varyingHandler->addVarying("RectEdge", &rectEdgeVary);
     98             vertBuilder->codeAppendf("%s = %s;", rectEdgeVary.vsOut(), rgp.inRectEdge()->fName);
     99 
    100             // setup the varying for the width/2+.5 and height/2+.5
    101             GrGLSLVertToFrag widthHeightVary(kVec2f_GrSLType);
    102             varyingHandler->addVarying("WidthHeight", &widthHeightVary);
    103             vertBuilder->codeAppendf("%s = %s;", widthHeightVary.vsOut(),
    104                                      rgp.inWidthHeight()->fName);
    105 
    106             GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
    107 
    108             // setup pass through color
    109             varyingHandler->addPassThroughAttribute(rgp.inColor(), args.fOutputColor);
    110 
    111             // Setup position
    112             this->setupPosition(vertBuilder, gpArgs, rgp.inPosition()->fName);
    113 
    114             // emit transforms
    115             this->emitTransforms(vertBuilder,
    116                                  varyingHandler,
    117                                  uniformHandler,
    118                                  gpArgs->fPositionVar,
    119                                  rgp.inPosition()->fName,
    120                                  rgp.localMatrix(),
    121                                  args.fFPCoordTransformHandler);
    122 
    123             // TODO: compute all these offsets, spans, and scales in the VS
    124             fragBuilder->codeAppendf("float insetW = min(1.0, %s.x) - 0.5;",
    125                                      widthHeightVary.fsIn());
    126             fragBuilder->codeAppendf("float insetH = min(1.0, %s.y) - 0.5;",
    127                                      widthHeightVary.fsIn());
    128             fragBuilder->codeAppend("float outset = 0.5;");
    129             // For rects > 1 pixel wide and tall the span's are noops (i.e., 1.0). For rects
    130             // < 1 pixel wide or tall they serve to normalize the < 1 ramp to a 0 .. 1 range.
    131             fragBuilder->codeAppend("float spanW = insetW + outset;");
    132             fragBuilder->codeAppend("float spanH = insetH + outset;");
    133             // For rects < 1 pixel wide or tall, these scale factors are used to cap the maximum
    134             // value of coverage that is used. In other words it is the coverage that is
    135             // used in the interior of the rect after the ramp.
    136             fragBuilder->codeAppend("float scaleW = min(1.0, 2.0*insetW/spanW);");
    137             fragBuilder->codeAppend("float scaleH = min(1.0, 2.0*insetH/spanH);");
    138             // Compute the coverage for the rect's width
    139             fragBuilder->codeAppendf("vec2 offset = %s.xy - %s.xy;", positionVary.fsIn(),
    140                                      rectEdgeVary.fsIn());
    141             fragBuilder->codeAppendf("float perpDot = abs(offset.x * %s.w - offset.y * %s.z);",
    142                                      rectEdgeVary.fsIn(), rectEdgeVary.fsIn());
    143 
    144             if (args.fDistanceVectorName) {
    145                 fragBuilder->codeAppendf("float widthDistance = %s.x - perpDot;",
    146                                          widthHeightVary.fsIn());
    147             }
    148 
    149             fragBuilder->codeAppendf(
    150                     "float coverage = scaleW*clamp((%s.x-perpDot)/spanW, 0.0, 1.0);",
    151                     widthHeightVary.fsIn());
    152             // Compute the coverage for the rect's height and merge with the width
    153             fragBuilder->codeAppendf("perpDot = abs(dot(offset, %s.zw));", rectEdgeVary.fsIn());
    154 
    155             if (args.fDistanceVectorName) {
    156                 fragBuilder->codeAppendf("float heightDistance = %s.y - perpDot;",
    157                                          widthHeightVary.fsIn());
    158             }
    159 
    160             fragBuilder->codeAppendf(
    161                     "coverage = coverage*scaleH*clamp((%s.y-perpDot)/spanH, 0.0, 1.0);",
    162                     widthHeightVary.fsIn());
    163 
    164             fragBuilder->codeAppendf("%s = vec4(coverage);", args.fOutputCoverage);
    165 
    166             if (args.fDistanceVectorName) {
    167                 fragBuilder->codeAppend("// Calculating distance vector\n");
    168                 fragBuilder->codeAppend("vec2 dvAxis;");
    169                 fragBuilder->codeAppend("float dvLength;");
    170 
    171                 fragBuilder->codeAppend("if (heightDistance < widthDistance) {");
    172                 fragBuilder->codeAppendf("    dvAxis = %s.zw;", rectEdgeVary.fsIn());
    173                 fragBuilder->codeAppend("     dvLength = heightDistance;");
    174                 fragBuilder->codeAppend("} else {");
    175                 fragBuilder->codeAppendf("    dvAxis = vec2(-%s.w, %s.z);", rectEdgeVary.fsIn(),
    176                                          rectEdgeVary.fsIn());
    177                 fragBuilder->codeAppend("     dvLength = widthDistance;");
    178                 fragBuilder->codeAppend("}");
    179 
    180                 fragBuilder->codeAppend("float dvSign = sign(dot(offset, dvAxis));");
    181                 fragBuilder->codeAppendf("%s = vec4(dvSign * dvAxis, dvLength, 0.0);",
    182                                          args.fDistanceVectorName);
    183             }
    184         }
    185 
    186         static void GenKey(const GrGeometryProcessor& gp,
    187                            const GrShaderCaps&,
    188                            GrProcessorKeyBuilder* b) {
    189             b->add32(0x0);
    190         }
    191 
    192         void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
    193                      FPCoordTransformIter&& transformIter) override {
    194             const RectGeometryProcessor& rgp = primProc.cast<RectGeometryProcessor>();
    195             this->setTransformDataHelper(rgp.fLocalMatrix, pdman, &transformIter);
    196         }
    197 
    198     private:
    199         typedef GrGLSLGeometryProcessor INHERITED;
    200     };
    201 
    202     void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
    203         GLSLProcessor::GenKey(*this, caps, b);
    204     }
    205 
    206     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
    207         return new GLSLProcessor();
    208     }
    209 
    210 private:
    211     SkMatrix fLocalMatrix;
    212 
    213     const Attribute* fInPosition;
    214     const Attribute* fInColor;
    215     const Attribute* fInRectEdge;
    216     const Attribute* fInWidthHeight;
    217 
    218     GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
    219 
    220     typedef GrGeometryProcessor INHERITED;
    221 };
    222 
    223 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(RectGeometryProcessor);
    224 
    225 #if GR_TEST_UTILS
    226 sk_sp<GrGeometryProcessor> RectGeometryProcessor::TestCreate(GrProcessorTestData* d) {
    227     return sk_sp<GrGeometryProcessor>(new RectGeometryProcessor(GrTest::TestMatrix(d->fRandom)));
    228 }
    229 #endif
    230 
    231 ///////////////////////////////////////////////////////////////////////////////
    232 
    233 class AnalyticRectOp final : public GrMeshDrawOp {
    234 public:
    235     DEFINE_OP_CLASS_ID
    236 
    237     AnalyticRectOp(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect,
    238                    const SkRect& croppedRect, const SkRect& bounds)
    239             : INHERITED(ClassID()), fViewMatrixIfUsingLocalCoords(viewMatrix) {
    240         SkPoint center = SkPoint::Make(rect.centerX(), rect.centerY());
    241         viewMatrix.mapPoints(&center, 1);
    242         SkScalar halfWidth = viewMatrix.mapRadius(SkScalarHalf(rect.width()));
    243         SkScalar halfHeight = viewMatrix.mapRadius(SkScalarHalf(rect.height()));
    244         SkVector downDir = viewMatrix.mapVector(0.0f, 1.0f);
    245         downDir.normalize();
    246 
    247         SkRect deviceSpaceCroppedRect = croppedRect;
    248         viewMatrix.mapRect(&deviceSpaceCroppedRect);
    249 
    250         fGeoData.emplace_back(
    251                 Geometry{color, center, downDir, halfWidth, halfHeight, deviceSpaceCroppedRect});
    252 
    253         this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
    254     }
    255 
    256     const char* name() const override { return "AnalyticRectOp"; }
    257 
    258     SkString dumpInfo() const override {
    259         SkString string;
    260         for (int i = 0; i < fGeoData.count(); ++i) {
    261             string.appendf("Color: 0x%08x Rect [C:(%.2f, %.2f) D:<%.2f,%.3f> W/2:%.2f H/2:%.2f]\n",
    262                            fGeoData[i].fColor, fGeoData[i].fCenter.x(), fGeoData[i].fCenter.y(),
    263                            fGeoData[i].fDownDir.x(), fGeoData[i].fDownDir.y(),
    264                            fGeoData[i].fHalfWidth, fGeoData[i].fHalfHeight);
    265         }
    266         string.append(DumpPipelineInfo(*this->pipeline()));
    267         string.append(INHERITED::dumpInfo());
    268         return string;
    269     }
    270 
    271 private:
    272     void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
    273                                             GrPipelineAnalysisCoverage* coverage) const override {
    274         color->setToConstant(fGeoData[0].fColor);
    275         *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
    276     }
    277 
    278     void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
    279         optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
    280         if (!optimizations.readsLocalCoords()) {
    281             fViewMatrixIfUsingLocalCoords.reset();
    282         }
    283     }
    284 
    285     void onPrepareDraws(Target* target) const override {
    286         SkMatrix localMatrix;
    287         if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
    288             return;
    289         }
    290 
    291         // Setup geometry processor
    292         sk_sp<GrGeometryProcessor> gp(new RectGeometryProcessor(localMatrix));
    293 
    294         int instanceCount = fGeoData.count();
    295         size_t vertexStride = gp->getVertexStride();
    296         SkASSERT(vertexStride == sizeof(RectVertex));
    297         QuadHelper helper;
    298         RectVertex* verts =
    299                 reinterpret_cast<RectVertex*>(helper.init(target, vertexStride, instanceCount));
    300         if (!verts) {
    301             return;
    302         }
    303 
    304         for (int i = 0; i < instanceCount; i++) {
    305             const Geometry& geom = fGeoData[i];
    306 
    307             GrColor color = geom.fColor;
    308             SkPoint center = geom.fCenter;
    309             SkVector downDir = geom.fDownDir;
    310             SkScalar halfWidth = geom.fHalfWidth;
    311             SkScalar halfHeight = geom.fHalfHeight;
    312             SkRect croppedRect = geom.fCroppedRect;
    313 
    314             SkVector rightDir;
    315             downDir.rotateCCW(&rightDir);
    316 
    317             verts[0].fPos = {croppedRect.fLeft, croppedRect.fTop};
    318             verts[0].fColor = color;
    319             verts[0].fCenter = center;
    320             verts[0].fDownDir = downDir;
    321             verts[0].fHalfWidth = halfWidth;
    322             verts[0].fHalfHeight = halfHeight;
    323 
    324             verts[1].fPos = {croppedRect.fRight, croppedRect.fTop};
    325             verts[1].fColor = color;
    326             verts[1].fCenter = center;
    327             verts[1].fDownDir = downDir;
    328             verts[1].fHalfWidth = halfWidth;
    329             verts[1].fHalfHeight = halfHeight;
    330 
    331             verts[2].fPos = {croppedRect.fRight, croppedRect.fBottom};
    332             verts[2].fColor = color;
    333             verts[2].fCenter = center;
    334             verts[2].fDownDir = downDir;
    335             verts[2].fHalfWidth = halfWidth;
    336             verts[2].fHalfHeight = halfHeight;
    337 
    338             verts[3].fPos = {croppedRect.fLeft, croppedRect.fBottom};
    339             verts[3].fColor = color;
    340             verts[3].fCenter = center;
    341             verts[3].fDownDir = downDir;
    342             verts[3].fHalfWidth = halfWidth;
    343             verts[3].fHalfHeight = halfHeight;
    344 
    345             verts += kVerticesPerQuad;
    346         }
    347         helper.recordDraw(target, gp.get());
    348     }
    349 
    350     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
    351         AnalyticRectOp* that = t->cast<AnalyticRectOp>();
    352         if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
    353                                     that->bounds(), caps)) {
    354             return false;
    355         }
    356 
    357         if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
    358             return false;
    359         }
    360 
    361         fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
    362         this->joinBounds(*that);
    363         return true;
    364     }
    365 
    366     struct Geometry {
    367         GrColor fColor;
    368         SkPoint fCenter;
    369         SkVector fDownDir;
    370         SkScalar fHalfWidth;
    371         SkScalar fHalfHeight;
    372         SkRect fCroppedRect;
    373     };
    374 
    375     SkMatrix fViewMatrixIfUsingLocalCoords;
    376     SkSTArray<1, Geometry, true> fGeoData;
    377 
    378     typedef GrMeshDrawOp INHERITED;
    379 };
    380 
    381 std::unique_ptr<GrMeshDrawOp> GrAnalyticRectOp::Make(GrColor color,
    382                                                      const SkMatrix& viewMatrix,
    383                                                      const SkRect& rect,
    384                                                      const SkRect& croppedRect,
    385                                                      const SkRect& bounds) {
    386     return std::unique_ptr<GrMeshDrawOp>(
    387             new AnalyticRectOp(color, viewMatrix, rect, croppedRect, bounds));
    388 }
    389 
    390 #if GR_TEST_UTILS
    391 
    392 DRAW_OP_TEST_DEFINE(AnalyticRectOp) {
    393     SkMatrix viewMatrix = GrTest::TestMatrix(random);
    394     GrColor color = GrRandomColor(random);
    395     SkRect rect = GrTest::TestSquare(random);
    396     SkRect croppedRect = GrTest::TestSquare(random);
    397     SkRect bounds = GrTest::TestSquare(random);
    398     return std::unique_ptr<GrMeshDrawOp>(
    399             new AnalyticRectOp(color, viewMatrix, rect, croppedRect, bounds));
    400 }
    401 
    402 #endif
    403