Home | History | Annotate | Download | only in ops
      1 /*
      2  * Copyright 2015 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 "GrNonAAFillRectOp.h"
      9 
     10 #include "GrColor.h"
     11 #include "GrDefaultGeoProcFactory.h"
     12 #include "GrMeshDrawOp.h"
     13 #include "GrOpFlushState.h"
     14 #include "GrPrimitiveProcessor.h"
     15 #include "GrQuad.h"
     16 #include "GrResourceProvider.h"
     17 
     18 static const int kVertsPerInstance = 4;
     19 static const int kIndicesPerInstance = 6;
     20 
     21 /** We always use per-vertex colors so that rects can be combined across color changes. Sometimes
     22     we  have explicit local coords and sometimes not. We *could* always provide explicit local
     23     coords and just duplicate the positions when the caller hasn't provided a local coord rect,
     24     but we haven't seen a use case which frequently switches between local rect and no local
     25     rect draws.
     26 
     27     The vertex attrib order is always pos, color, [local coords].
     28  */
     29 static sk_sp<GrGeometryProcessor> make_persp_gp(const SkMatrix& viewMatrix,
     30                                                 bool hasExplicitLocalCoords,
     31                                                 const SkMatrix* localMatrix) {
     32     SkASSERT(viewMatrix.hasPerspective() || (localMatrix && localMatrix->hasPerspective()));
     33 
     34     using namespace GrDefaultGeoProcFactory;
     35 
     36     // If we have perspective on the viewMatrix then we won't map on the CPU, nor will we map
     37     // the local rect on the cpu (in case the localMatrix also has perspective).
     38     // Otherwise, if we have a local rect, then we apply the localMatrix directly to the localRect
     39     // to generate vertex local coords
     40     if (viewMatrix.hasPerspective()) {
     41         LocalCoords localCoords(hasExplicitLocalCoords ? LocalCoords::kHasExplicit_Type
     42                                                        : LocalCoords::kUsePosition_Type,
     43                                 localMatrix);
     44         return GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type,
     45                                              Coverage::kSolid_Type, localCoords, viewMatrix);
     46     } else if (hasExplicitLocalCoords) {
     47         LocalCoords localCoords(LocalCoords::kHasExplicit_Type, localMatrix);
     48         return GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type,
     49                                              Coverage::kSolid_Type, localCoords, SkMatrix::I());
     50     } else {
     51         LocalCoords localCoords(LocalCoords::kUsePosition_Type, localMatrix);
     52         return GrDefaultGeoProcFactory::MakeForDeviceSpace(Color::kPremulGrColorAttribute_Type,
     53                                                            Coverage::kSolid_Type, localCoords,
     54                                                            viewMatrix);
     55     }
     56 }
     57 
     58 static void tesselate(intptr_t vertices,
     59                       size_t vertexStride,
     60                       GrColor color,
     61                       const SkMatrix* viewMatrix,
     62                       const SkRect& rect,
     63                       const GrQuad* localQuad) {
     64     SkPoint* positions = reinterpret_cast<SkPoint*>(vertices);
     65 
     66     positions->setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vertexStride);
     67 
     68     if (viewMatrix) {
     69         viewMatrix->mapPointsWithStride(positions, vertexStride, kVertsPerInstance);
     70     }
     71 
     72     // Setup local coords
     73     // TODO we should only do this if local coords are being read
     74     if (localQuad) {
     75         static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor);
     76         for (int i = 0; i < kVertsPerInstance; i++) {
     77             SkPoint* coords =
     78                     reinterpret_cast<SkPoint*>(vertices + kLocalOffset + i * vertexStride);
     79             *coords = localQuad->point(i);
     80         }
     81     }
     82 
     83     static const int kColorOffset = sizeof(SkPoint);
     84     GrColor* vertColor = reinterpret_cast<GrColor*>(vertices + kColorOffset);
     85     for (int j = 0; j < 4; ++j) {
     86         *vertColor = color;
     87         vertColor = (GrColor*)((intptr_t)vertColor + vertexStride);
     88     }
     89 }
     90 
     91 // We handle perspective in the local matrix or viewmatrix with special ops.
     92 class NonAAFillRectPerspectiveOp final : public GrMeshDrawOp {
     93 public:
     94     DEFINE_OP_CLASS_ID
     95 
     96     NonAAFillRectPerspectiveOp(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect,
     97                                const SkRect* localRect, const SkMatrix* localMatrix)
     98             : INHERITED(ClassID()), fViewMatrix(viewMatrix) {
     99         SkASSERT(viewMatrix.hasPerspective() || (localMatrix && localMatrix->hasPerspective()));
    100         RectInfo& info = fRects.push_back();
    101         info.fColor = color;
    102         info.fRect = rect;
    103         fHasLocalRect = SkToBool(localRect);
    104         fHasLocalMatrix = SkToBool(localMatrix);
    105         if (fHasLocalMatrix) {
    106             fLocalMatrix = *localMatrix;
    107         }
    108         if (fHasLocalRect) {
    109             info.fLocalRect = *localRect;
    110         }
    111         this->setTransformedBounds(rect, viewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
    112     }
    113 
    114     const char* name() const override { return "NonAAFillRectPerspectiveOp"; }
    115 
    116     SkString dumpInfo() const override {
    117         SkString str;
    118         str.appendf("# combined: %d\n", fRects.count());
    119         for (int i = 0; i < fRects.count(); ++i) {
    120             const RectInfo& geo = fRects[0];
    121             str.appendf("%d: Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", i,
    122                         geo.fColor, geo.fRect.fLeft, geo.fRect.fTop, geo.fRect.fRight,
    123                         geo.fRect.fBottom);
    124         }
    125         str.append(DumpPipelineInfo(*this->pipeline()));
    126         str.append(INHERITED::dumpInfo());
    127         return str;
    128     }
    129 
    130 private:
    131     NonAAFillRectPerspectiveOp() : INHERITED(ClassID()) {}
    132 
    133     void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
    134                                             GrPipelineAnalysisCoverage* coverage) const override {
    135         color->setToConstant(fRects[0].fColor);
    136         *coverage = GrPipelineAnalysisCoverage::kNone;
    137     }
    138 
    139     void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
    140         optimizations.getOverrideColorIfSet(&fRects[0].fColor);
    141     }
    142 
    143     void onPrepareDraws(Target* target) const override {
    144         sk_sp<GrGeometryProcessor> gp = make_persp_gp(fViewMatrix,
    145                                                       fHasLocalRect,
    146                                                       fHasLocalMatrix ? &fLocalMatrix : nullptr);
    147         if (!gp) {
    148             SkDebugf("Couldn't create GrGeometryProcessor\n");
    149             return;
    150         }
    151         SkASSERT(fHasLocalRect
    152                          ? gp->getVertexStride() ==
    153                                    sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr)
    154                          : gp->getVertexStride() ==
    155                                    sizeof(GrDefaultGeoProcFactory::PositionColorAttr));
    156 
    157         size_t vertexStride = gp->getVertexStride();
    158         int instanceCount = fRects.count();
    159 
    160         sk_sp<const GrBuffer> indexBuffer(target->resourceProvider()->refQuadIndexBuffer());
    161         InstancedHelper helper;
    162         void* vertices =
    163                 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(),
    164                             kVertsPerInstance, kIndicesPerInstance, instanceCount);
    165         if (!vertices || !indexBuffer) {
    166             SkDebugf("Could not allocate vertices\n");
    167             return;
    168         }
    169 
    170         for (int i = 0; i < instanceCount; i++) {
    171             const RectInfo& info = fRects[i];
    172             intptr_t verts =
    173                     reinterpret_cast<intptr_t>(vertices) + i * kVertsPerInstance * vertexStride;
    174             if (fHasLocalRect) {
    175                 GrQuad quad(info.fLocalRect);
    176                 tesselate(verts, vertexStride, info.fColor, nullptr, info.fRect, &quad);
    177             } else {
    178                 tesselate(verts, vertexStride, info.fColor, nullptr, info.fRect, nullptr);
    179             }
    180         }
    181         helper.recordDraw(target, gp.get());
    182     }
    183 
    184     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
    185         NonAAFillRectPerspectiveOp* that = t->cast<NonAAFillRectPerspectiveOp>();
    186         if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
    187                                     that->bounds(), caps)) {
    188             return false;
    189         }
    190 
    191         // We could combine across perspective vm changes if we really wanted to.
    192         if (!fViewMatrix.cheapEqualTo(that->fViewMatrix)) {
    193             return false;
    194         }
    195         if (fHasLocalRect != that->fHasLocalRect) {
    196             return false;
    197         }
    198         if (fHasLocalMatrix && !fLocalMatrix.cheapEqualTo(that->fLocalMatrix)) {
    199             return false;
    200         }
    201 
    202         fRects.push_back_n(that->fRects.count(), that->fRects.begin());
    203         this->joinBounds(*that);
    204         return true;
    205     }
    206 
    207     struct RectInfo {
    208         SkRect fRect;
    209         GrColor fColor;
    210         SkRect fLocalRect;
    211     };
    212 
    213     SkSTArray<1, RectInfo, true> fRects;
    214     bool fHasLocalMatrix;
    215     bool fHasLocalRect;
    216     SkMatrix fLocalMatrix;
    217     SkMatrix fViewMatrix;
    218 
    219     typedef GrMeshDrawOp INHERITED;
    220 };
    221 
    222 namespace GrNonAAFillRectOp {
    223 
    224 std::unique_ptr<GrMeshDrawOp> MakeWithPerspective(GrColor color,
    225                                                   const SkMatrix& viewMatrix,
    226                                                   const SkRect& rect,
    227                                                   const SkRect* localRect,
    228                                                   const SkMatrix* localMatrix) {
    229     return std::unique_ptr<GrMeshDrawOp>(
    230             new NonAAFillRectPerspectiveOp(color, viewMatrix, rect, localRect, localMatrix));
    231 }
    232 };
    233 
    234 ///////////////////////////////////////////////////////////////////////////////////////////////////
    235 
    236 #if GR_TEST_UTILS
    237 
    238 #include "GrDrawOpTest.h"
    239 
    240 DRAW_OP_TEST_DEFINE(NonAAFillRectPerspectiveOp) {
    241     GrColor color = GrRandomColor(random);
    242     SkRect rect = GrTest::TestRect(random);
    243     SkRect localRect = GrTest::TestRect(random);
    244     SkMatrix viewMatrix = GrTest::TestMatrix(random);
    245     bool hasLocalMatrix = random->nextBool();
    246     SkMatrix localMatrix;
    247     if (!viewMatrix.hasPerspective()) {
    248         localMatrix = GrTest::TestMatrixPerspective(random);
    249         hasLocalMatrix = true;
    250     }
    251 
    252     bool hasLocalRect = random->nextBool();
    253     return GrNonAAFillRectOp::MakeWithPerspective(color, viewMatrix, rect,
    254                                                   hasLocalRect ? &localRect : nullptr,
    255                                                   hasLocalMatrix ? &localMatrix : nullptr);
    256 }
    257 
    258 #endif
    259