Home | History | Annotate | Download | only in ops
      1 /*
      2  * Copyright 2018 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 "GrFillRectOp.h"
      9 
     10 #include "GrGeometryProcessor.h"
     11 #include "GrMeshDrawOp.h"
     12 #include "GrPaint.h"
     13 #include "GrQuad.h"
     14 #include "GrQuadPerEdgeAA.h"
     15 #include "GrSimpleMeshDrawOpHelper.h"
     16 #include "SkMatrix.h"
     17 #include "SkRect.h"
     18 #include "glsl/GrGLSLColorSpaceXformHelper.h"
     19 #include "glsl/GrGLSLGeometryProcessor.h"
     20 #include "glsl/GrGLSLVarying.h"
     21 
     22 namespace {
     23 
     24 using VertexSpec = GrQuadPerEdgeAA::VertexSpec;
     25 using ColorType = GrQuadPerEdgeAA::ColorType;
     26 
     27 #ifdef SK_DEBUG
     28 static SkString dump_quad_info(int index, const GrPerspQuad& deviceQuad,
     29                                const GrPerspQuad& localQuad, const SkPMColor4f& color,
     30                                GrQuadAAFlags aaFlags) {
     31     SkString str;
     32     str.appendf("%d: Color: [%.2f, %.2f, %.2f, %.2f], Edge AA: l%u_t%u_r%u_b%u, \n"
     33                 "  device quad: [(%.2f, %2.f, %.2f), (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f), "
     34                 "(%.2f, %.2f, %.2f)],\n"
     35                 "  local quad: [(%.2f, %2.f, %.2f), (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f), "
     36                 "(%.2f, %.2f, %.2f)]\n",
     37                 index, color.fR, color.fG, color.fB, color.fA,
     38                 (uint32_t) (aaFlags & GrQuadAAFlags::kLeft),
     39                 (uint32_t) (aaFlags & GrQuadAAFlags::kTop),
     40                 (uint32_t) (aaFlags & GrQuadAAFlags::kRight),
     41                 (uint32_t) (aaFlags & GrQuadAAFlags::kBottom),
     42                 deviceQuad.x(0), deviceQuad.y(0), deviceQuad.w(0),
     43                 deviceQuad.x(1), deviceQuad.y(1), deviceQuad.w(1),
     44                 deviceQuad.x(2), deviceQuad.y(2), deviceQuad.w(2),
     45                 deviceQuad.x(3), deviceQuad.y(3), deviceQuad.w(3),
     46                 localQuad.x(0), localQuad.y(0), localQuad.w(0),
     47                 localQuad.x(1), localQuad.y(1), localQuad.w(1),
     48                 localQuad.x(2), localQuad.y(2), localQuad.w(2),
     49                 localQuad.x(3), localQuad.y(3), localQuad.w(3));
     50     return str;
     51 }
     52 #endif
     53 
     54 class FillRectOp final : public GrMeshDrawOp {
     55 private:
     56     using Helper = GrSimpleMeshDrawOpHelperWithStencil;
     57 
     58 public:
     59     static std::unique_ptr<GrDrawOp> Make(GrContext* context,
     60                                           GrPaint&& paint,
     61                                           GrAAType aaType,
     62                                           GrQuadAAFlags edgeAA,
     63                                           const GrUserStencilSettings* stencilSettings,
     64                                           const GrPerspQuad& deviceQuad,
     65                                           GrQuadType deviceQuadType,
     66                                           const GrPerspQuad& localQuad,
     67                                           GrQuadType localQuadType) {
     68         // Clean up deviations between aaType and edgeAA
     69         GrResolveAATypeForQuad(aaType, edgeAA, deviceQuad, deviceQuadType, &aaType, &edgeAA);
     70         return Helper::FactoryHelper<FillRectOp>(context, std::move(paint), aaType, edgeAA,
     71                 stencilSettings, deviceQuad, deviceQuadType, localQuad, localQuadType);
     72     }
     73 
     74     // aaType is passed to Helper in the initializer list, so incongruities between aaType and
     75     // edgeFlags must be resolved prior to calling this constructor.
     76     FillRectOp(Helper::MakeArgs args, SkPMColor4f paintColor, GrAAType aaType,
     77                GrQuadAAFlags edgeFlags, const GrUserStencilSettings* stencil,
     78                const GrPerspQuad& deviceQuad, GrQuadType deviceQuadType,
     79                const GrPerspQuad& localQuad, GrQuadType localQuadType)
     80             : INHERITED(ClassID())
     81             , fHelper(args, aaType, stencil)
     82             , fWideColor(!SkPMColor4fFitsInBytes(paintColor)) {
     83         // The color stored with the quad is the clear color if a scissor-clear is decided upon
     84         // when executing the op.
     85         fDeviceQuads.push_back(deviceQuad, deviceQuadType, { paintColor, edgeFlags });
     86 
     87         if (!fHelper.isTrivial()) {
     88             // Conservatively keep track of the local coordinates; it may be that the paint doesn't
     89             // need them after analysis is finished. If the paint is known to be solid up front they
     90             // can be skipped entirely.
     91             fLocalQuads.push_back(localQuad, localQuadType);
     92         }
     93         this->setBounds(deviceQuad.bounds(deviceQuadType),
     94                         HasAABloat(aaType == GrAAType::kCoverage), IsZeroArea::kNo);
     95     }
     96 
     97     const char* name() const override { return "FillRectOp"; }
     98 
     99     void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
    100         return fHelper.visitProxies(func);
    101     }
    102 
    103 #ifdef SK_DEBUG
    104     SkString dumpInfo() const override {
    105         SkString str;
    106         str.appendf("# draws: %u\n", this->quadCount());
    107         str.appendf("Device quad type: %u, local quad type: %u\n",
    108                     (uint32_t) fDeviceQuads.quadType(), (uint32_t) fLocalQuads.quadType());
    109         str += fHelper.dumpInfo();
    110         GrPerspQuad device, local;
    111         for (int i = 0; i < this->quadCount(); i++) {
    112             device = fDeviceQuads[i];
    113             const ColorAndAA& info = fDeviceQuads.metadata(i);
    114             if (!fHelper.isTrivial()) {
    115                 local = fLocalQuads[i];
    116             }
    117             str += dump_quad_info(i, device, local, info.fColor, info.fAAFlags);
    118         }
    119         str += INHERITED::dumpInfo();
    120         return str;
    121     }
    122 #endif
    123 
    124     GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
    125         // Initialize aggregate color analysis with the first quad's color (which always exists)
    126         SkASSERT(this->quadCount() > 0);
    127         GrProcessorAnalysisColor quadColors(fDeviceQuads.metadata(0).fColor);
    128         // Then combine the colors of any additional quads (e.g. from MakeSet)
    129         for (int i = 1; i < this->quadCount(); ++i) {
    130             quadColors = GrProcessorAnalysisColor::Combine(quadColors,
    131                                                            fDeviceQuads.metadata(i).fColor);
    132             if (quadColors.isUnknown()) {
    133                 // No point in accumulating additional starting colors, combining cannot make it
    134                 // less unknown.
    135                 break;
    136             }
    137         }
    138 
    139         // If the AA type is coverage, it will be a single value per pixel; if it's not coverage AA
    140         // then the coverage is always 1.0, so specify kNone for more optimal blending.
    141         GrProcessorAnalysisCoverage coverage = fHelper.aaType() == GrAAType::kCoverage ?
    142                 GrProcessorAnalysisCoverage::kSingleChannel :
    143                 GrProcessorAnalysisCoverage::kNone;
    144         auto result = fHelper.finalizeProcessors(caps, clip, coverage, &quadColors);
    145         // If there is a constant color after analysis, that means all of the quads should be set
    146         // to the same color (even if they started out with different colors).
    147         SkPMColor4f colorOverride;
    148         if (quadColors.isConstant(&colorOverride)) {
    149             for (int i = 0; i < this->quadCount(); ++i) {
    150                 fDeviceQuads.metadata(i).fColor = colorOverride;
    151             }
    152         }
    153 
    154         return result;
    155     }
    156 
    157     FixedFunctionFlags fixedFunctionFlags() const override {
    158         // Since the AA type of the whole primitive is kept consistent with the per edge AA flags
    159         // the helper's fixed function flags are appropriate.
    160         return fHelper.fixedFunctionFlags();
    161     }
    162 
    163     DEFINE_OP_CLASS_ID
    164 
    165 private:
    166     // For GrFillRectOp::MakeSet's use of addQuad
    167     friend std::unique_ptr<GrDrawOp> GrFillRectOp::MakeSet(GrContext* context, GrPaint&& paint,
    168             GrAAType aaType, const SkMatrix& viewMatrix,
    169             const GrRenderTargetContext::QuadSetEntry quads[], int quadCount,
    170             const GrUserStencilSettings* stencilSettings);
    171 
    172     void onPrepareDraws(Target* target) override {
    173         TRACE_EVENT0("skia", TRACE_FUNC);
    174 
    175         using Domain = GrQuadPerEdgeAA::Domain;
    176         static constexpr SkRect kEmptyDomain = SkRect::MakeEmpty();
    177 
    178         VertexSpec vertexSpec(fDeviceQuads.quadType(),
    179                               fWideColor ? ColorType::kHalf : ColorType::kByte,
    180                               fLocalQuads.quadType(), fHelper.usesLocalCoords(), Domain::kNo,
    181                               fHelper.aaType(), fHelper.compatibleWithAlphaAsCoverage());
    182         // Make sure that if the op thought it was a solid color, the vertex spec does not use
    183         // local coords.
    184         SkASSERT(!fHelper.isTrivial() || !fHelper.usesLocalCoords());
    185 
    186         sk_sp<GrGeometryProcessor> gp = GrQuadPerEdgeAA::MakeProcessor(vertexSpec);
    187         size_t vertexSize = gp->vertexStride();
    188 
    189         sk_sp<const GrBuffer> vbuffer;
    190         int vertexOffsetInBuffer = 0;
    191 
    192         // Fill the allocated vertex data
    193         void* vdata = target->makeVertexSpace(
    194                 vertexSize, this->quadCount() * vertexSpec.verticesPerQuad(),
    195                 &vbuffer, &vertexOffsetInBuffer);
    196         if (!vdata) {
    197             SkDebugf("Could not allocate vertices\n");
    198             return;
    199         }
    200 
    201         // vertices pointer advances through vdata based on Tessellate's return value
    202         void* vertices = vdata;
    203         if (fHelper.isTrivial()) {
    204             SkASSERT(fLocalQuads.count() == 0); // No local coords, so send an ignored dummy quad
    205             static const GrPerspQuad kIgnoredLocal(SkRect::MakeEmpty(), SkMatrix::I());
    206             for (int i = 0; i < this->quadCount(); ++i) {
    207                 const ColorAndAA& info = fDeviceQuads.metadata(i);
    208                 vertices = GrQuadPerEdgeAA::Tessellate(vertices, vertexSpec, fDeviceQuads[i],
    209                         info.fColor, kIgnoredLocal, kEmptyDomain, info.fAAFlags);
    210             }
    211         } else {
    212             SkASSERT(fLocalQuads.count() == fDeviceQuads.count());
    213             for (int i = 0; i < this->quadCount(); ++i) {
    214                 const ColorAndAA& info = fDeviceQuads.metadata(i);
    215                 vertices = GrQuadPerEdgeAA::Tessellate(vertices, vertexSpec, fDeviceQuads[i],
    216                         info.fColor, fLocalQuads[i], kEmptyDomain, info.fAAFlags);
    217             }
    218         }
    219 
    220         // Configure the mesh for the vertex data
    221         GrMesh* mesh = target->allocMeshes(1);
    222         if (!GrQuadPerEdgeAA::ConfigureMeshIndices(target, mesh, vertexSpec, this->quadCount())) {
    223             SkDebugf("Could not allocate indices\n");
    224             return;
    225         }
    226         mesh->setVertexData(std::move(vbuffer), vertexOffsetInBuffer);
    227 
    228         auto pipe = fHelper.makePipeline(target);
    229         target->draw(std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
    230    }
    231 
    232     CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
    233         TRACE_EVENT0("skia", TRACE_FUNC);
    234         const auto* that = t->cast<FillRectOp>();
    235 
    236         if ((fHelper.aaType() == GrAAType::kCoverage ||
    237              that->fHelper.aaType() == GrAAType::kCoverage) &&
    238             this->quadCount() + that->quadCount() > GrQuadPerEdgeAA::kNumAAQuadsInIndexBuffer) {
    239             // This limit on batch size seems to help on Adreno devices
    240             return CombineResult::kCannotCombine;
    241         }
    242 
    243         // Unlike most users of the draw op helper, this op can merge none-aa and coverage-aa draw
    244         // ops together, so pass true as the last argument.
    245         if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds(), true)) {
    246             return CombineResult::kCannotCombine;
    247         }
    248 
    249         // If the paints were compatible, the trivial/solid-color state should be the same
    250         SkASSERT(fHelper.isTrivial() == that->fHelper.isTrivial());
    251 
    252         // If the processor sets are compatible, the two ops are always compatible; it just needs to
    253         // adjust the state of the op to be the more general quad and aa types of the two ops and
    254         // then concatenate the per-quad data.
    255         fWideColor |= that->fWideColor;
    256 
    257         // The helper stores the aa type, but isCompatible(with true arg) allows the two ops' aa
    258         // types to be none and coverage, in which case this op's aa type must be lifted to coverage
    259         // so that quads with no aa edges can be batched with quads that have some/all edges aa'ed.
    260         if (fHelper.aaType() == GrAAType::kNone && that->fHelper.aaType() == GrAAType::kCoverage) {
    261             fHelper.setAAType(GrAAType::kCoverage);
    262         }
    263 
    264         fDeviceQuads.concat(that->fDeviceQuads);
    265         if (!fHelper.isTrivial()) {
    266             fLocalQuads.concat(that->fLocalQuads);
    267         }
    268         return CombineResult::kMerged;
    269     }
    270 
    271     // Similar to onCombineIfPossible, but adds a quad assuming its op would have been compatible.
    272     // But since it's avoiding the op list management, it must update the op's bounds. This is only
    273     // used with quad sets, which uses the same view matrix for each quad so this assumes that the
    274     // device quad type of the new quad is the same as the op's.
    275     void addQuad(const GrPerspQuad& deviceQuad, const GrPerspQuad& localQuad,
    276                  GrQuadType localQuadType, const SkPMColor4f& color, GrQuadAAFlags edgeAA,
    277                  GrAAType aaType) {
    278         SkASSERT(deviceQuad.quadType() <= fDeviceQuads.quadType());
    279 
    280         // The new quad's aa type should be the same as the first quad's or none, except when the
    281         // first quad's aa type was already downgraded to none, in which case the stored type must
    282         // be lifted to back to the requested type.
    283         if (aaType != fHelper.aaType()) {
    284             if (aaType != GrAAType::kNone) {
    285                 // Original quad was downgraded to non-aa, lift back up to this quad's required type
    286                 SkASSERT(fHelper.aaType() == GrAAType::kNone);
    287                 fHelper.setAAType(aaType);
    288             }
    289             // else the new quad could have been downgraded but the other quads can't be, so don't
    290             // reset the op's accumulated aa type.
    291         }
    292 
    293         // clear compatible won't need to be updated, since device quad type and paint is the same,
    294         // but this quad has a new color, so maybe update wide color
    295         fWideColor |= !SkPMColor4fFitsInBytes(color);
    296 
    297         // Update the bounds and add the quad to this op's storage
    298         SkRect newBounds = this->bounds();
    299         newBounds.joinPossiblyEmptyRect(deviceQuad.bounds(fDeviceQuads.quadType()));
    300         this->setBounds(newBounds, HasAABloat(fHelper.aaType() == GrAAType::kCoverage),
    301                         IsZeroArea::kNo);
    302         fDeviceQuads.push_back(deviceQuad, fDeviceQuads.quadType(), { color, edgeAA });
    303         if (!fHelper.isTrivial()) {
    304             fLocalQuads.push_back(localQuad, localQuadType);
    305         }
    306     }
    307 
    308     int quadCount() const {
    309         // Sanity check that the parallel arrays for quad properties all have the same size
    310         SkASSERT(fDeviceQuads.count() == fLocalQuads.count() ||
    311                  (fLocalQuads.count() == 0 && fHelper.isTrivial()));
    312         return fDeviceQuads.count();
    313     }
    314 
    315     struct ColorAndAA {
    316         SkPMColor4f fColor;
    317         GrQuadAAFlags fAAFlags;
    318     };
    319 
    320     Helper fHelper;
    321     GrTQuadList<ColorAndAA> fDeviceQuads;
    322     // No metadata attached to the local quads; this list is empty when local coords are not needed.
    323     GrQuadList fLocalQuads;
    324 
    325     unsigned fWideColor: 1;
    326 
    327     typedef GrMeshDrawOp INHERITED;
    328 };
    329 
    330 } // anonymous namespace
    331 
    332 namespace GrFillRectOp {
    333 
    334 std::unique_ptr<GrDrawOp> MakePerEdge(GrContext* context,
    335                                       GrPaint&& paint,
    336                                       GrAAType aaType,
    337                                       GrQuadAAFlags edgeAA,
    338                                       const SkMatrix& viewMatrix,
    339                                       const SkRect& rect,
    340                                       const GrUserStencilSettings* stencilSettings) {
    341     return FillRectOp::Make(context, std::move(paint), aaType, edgeAA, stencilSettings,
    342                             GrPerspQuad(rect, viewMatrix), GrQuadTypeForTransformedRect(viewMatrix),
    343                             GrPerspQuad(rect, SkMatrix::I()), GrQuadType::kRect);
    344 }
    345 
    346 std::unique_ptr<GrDrawOp> MakePerEdgeWithLocalMatrix(GrContext* context,
    347                                                      GrPaint&& paint,
    348                                                      GrAAType aaType,
    349                                                      GrQuadAAFlags edgeAA,
    350                                                      const SkMatrix& viewMatrix,
    351                                                      const SkMatrix& localMatrix,
    352                                                      const SkRect& rect,
    353                                                      const GrUserStencilSettings* stencilSettings) {
    354     GrQuadType localQuadType = GrQuadTypeForTransformedRect(localMatrix);
    355     return FillRectOp::Make(context, std::move(paint), aaType, edgeAA, stencilSettings,
    356                             GrPerspQuad(rect, viewMatrix), GrQuadTypeForTransformedRect(viewMatrix),
    357                             GrPerspQuad(rect, localMatrix), localQuadType);
    358 }
    359 
    360 std::unique_ptr<GrDrawOp> MakePerEdgeWithLocalRect(GrContext* context,
    361                                                    GrPaint&& paint,
    362                                                    GrAAType aaType,
    363                                                    GrQuadAAFlags edgeAA,
    364                                                    const SkMatrix& viewMatrix,
    365                                                    const SkRect& rect,
    366                                                    const SkRect& localRect,
    367                                                    const GrUserStencilSettings* stencilSettings) {
    368     return FillRectOp::Make(context, std::move(paint), aaType, edgeAA, stencilSettings,
    369                             GrPerspQuad(rect, viewMatrix), GrQuadTypeForTransformedRect(viewMatrix),
    370                             GrPerspQuad(localRect, SkMatrix::I()), GrQuadType::kRect);
    371 }
    372 
    373 std::unique_ptr<GrDrawOp> MakeSet(GrContext* context,
    374                                   GrPaint&& paint,
    375                                   GrAAType aaType,
    376                                   const SkMatrix& viewMatrix,
    377                                   const GrRenderTargetContext::QuadSetEntry quads[],
    378                                   int cnt,
    379                                   const GrUserStencilSettings* stencilSettings) {
    380     // First make a draw op for the first quad in the set
    381     SkASSERT(cnt > 0);
    382     GrQuadType deviceQuadType = GrQuadTypeForTransformedRect(viewMatrix);
    383 
    384     paint.setColor4f(quads[0].fColor);
    385     std::unique_ptr<GrDrawOp> op = FillRectOp::Make(context, std::move(paint), aaType,
    386             quads[0].fAAFlags, stencilSettings, GrPerspQuad(quads[0].fRect, viewMatrix),
    387             deviceQuadType, GrPerspQuad(quads[0].fRect, quads[0].fLocalMatrix),
    388             GrQuadTypeForTransformedRect(quads[0].fLocalMatrix));
    389     auto* fillRects = op->cast<FillRectOp>();
    390 
    391     // Accumulate remaining quads similar to onCombineIfPossible() without creating an op
    392     for (int i = 1; i < cnt; ++i) {
    393         GrPerspQuad deviceQuad(quads[i].fRect, viewMatrix);
    394 
    395         GrAAType resolvedAA;
    396         GrQuadAAFlags resolvedEdgeFlags;
    397         GrResolveAATypeForQuad(aaType, quads[i].fAAFlags, deviceQuad, deviceQuadType,
    398                                &resolvedAA, &resolvedEdgeFlags);
    399 
    400         fillRects->addQuad(deviceQuad, GrPerspQuad(quads[i].fRect, quads[i].fLocalMatrix),
    401                            GrQuadTypeForTransformedRect(quads[i].fLocalMatrix), quads[i].fColor,
    402                            resolvedEdgeFlags,resolvedAA);
    403     }
    404 
    405     return op;
    406 }
    407 
    408 std::unique_ptr<GrDrawOp> Make(GrContext* context,
    409                                GrPaint&& paint,
    410                                GrAAType aaType,
    411                                const SkMatrix& viewMatrix,
    412                                const SkRect& rect,
    413                                const GrUserStencilSettings* stencil) {
    414     return MakePerEdge(context, std::move(paint), aaType,
    415             aaType == GrAAType::kCoverage ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone,
    416             viewMatrix, rect, stencil);
    417 }
    418 
    419 std::unique_ptr<GrDrawOp> MakeWithLocalMatrix(GrContext* context,
    420                                               GrPaint&& paint,
    421                                               GrAAType aaType,
    422                                               const SkMatrix& viewMatrix,
    423                                               const SkMatrix& localMatrix,
    424                                               const SkRect& rect,
    425                                               const GrUserStencilSettings* stencil) {
    426     return MakePerEdgeWithLocalMatrix(context, std::move(paint), aaType,
    427             aaType == GrAAType::kCoverage ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone,
    428             viewMatrix, localMatrix, rect, stencil);
    429 }
    430 
    431 std::unique_ptr<GrDrawOp> MakeWithLocalRect(GrContext* context,
    432                                             GrPaint&& paint,
    433                                             GrAAType aaType,
    434                                             const SkMatrix& viewMatrix,
    435                                             const SkRect& rect,
    436                                             const SkRect& localRect,
    437                                             const GrUserStencilSettings* stencil) {
    438     return MakePerEdgeWithLocalRect(context, std::move(paint), aaType,
    439             aaType == GrAAType::kCoverage ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone,
    440             viewMatrix, rect, localRect, stencil);
    441 }
    442 
    443 } // namespace GrFillRectOp
    444 
    445 #if GR_TEST_UTILS
    446 
    447 #include "GrDrawOpTest.h"
    448 #include "SkGr.h"
    449 
    450 GR_DRAW_OP_TEST_DEFINE(FillRectOp) {
    451     SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
    452     SkRect rect = GrTest::TestRect(random);
    453 
    454     GrAAType aaType = GrAAType::kNone;
    455     if (random->nextBool()) {
    456         aaType = (fsaaType == GrFSAAType::kUnifiedMSAA) ? GrAAType::kMSAA : GrAAType::kCoverage;
    457     }
    458     const GrUserStencilSettings* stencil = random->nextBool() ? nullptr
    459                                                               : GrGetRandomStencil(random, context);
    460 
    461     GrQuadAAFlags aaFlags = GrQuadAAFlags::kNone;
    462     aaFlags |= random->nextBool() ? GrQuadAAFlags::kLeft : GrQuadAAFlags::kNone;
    463     aaFlags |= random->nextBool() ? GrQuadAAFlags::kTop : GrQuadAAFlags::kNone;
    464     aaFlags |= random->nextBool() ? GrQuadAAFlags::kRight : GrQuadAAFlags::kNone;
    465     aaFlags |= random->nextBool() ? GrQuadAAFlags::kBottom : GrQuadAAFlags::kNone;
    466 
    467     if (random->nextBool()) {
    468         if (random->nextBool()) {
    469             if (random->nextBool()) {
    470                 // Local matrix with a set op
    471                 uint32_t extraQuadCt = random->nextRangeU(1, 4);
    472                 SkTArray<GrRenderTargetContext::QuadSetEntry> quads(extraQuadCt + 1);
    473                 quads.push_back(
    474                         {rect, SkPMColor4f::FromBytes_RGBA(SkColorToPremulGrColor(random->nextU())),
    475                          GrTest::TestMatrixInvertible(random), aaFlags});
    476                 for (uint32_t i = 0; i < extraQuadCt; ++i) {
    477                     GrQuadAAFlags aaFlags = GrQuadAAFlags::kNone;
    478                     aaFlags |= random->nextBool() ? GrQuadAAFlags::kLeft : GrQuadAAFlags::kNone;
    479                     aaFlags |= random->nextBool() ? GrQuadAAFlags::kTop : GrQuadAAFlags::kNone;
    480                     aaFlags |= random->nextBool() ? GrQuadAAFlags::kRight : GrQuadAAFlags::kNone;
    481                     aaFlags |= random->nextBool() ? GrQuadAAFlags::kBottom : GrQuadAAFlags::kNone;
    482 
    483                     quads.push_back(
    484                         {GrTest::TestRect(random),
    485                          SkPMColor4f::FromBytes_RGBA(SkColorToPremulGrColor(random->nextU())),
    486                          GrTest::TestMatrixInvertible(random), aaFlags});
    487                 }
    488 
    489                 return GrFillRectOp::MakeSet(context, std::move(paint), aaType, viewMatrix,
    490                                              quads.begin(), quads.count(), stencil);
    491             } else {
    492                 // Single local matrix
    493                 SkMatrix localMatrix = GrTest::TestMatrixInvertible(random);
    494                 return GrFillRectOp::MakePerEdgeWithLocalMatrix(context, std::move(paint), aaType,
    495                                                                 aaFlags, viewMatrix, localMatrix,
    496                                                                 rect, stencil);
    497             }
    498         } else {
    499             // Pass local rect directly
    500             SkRect localRect = GrTest::TestRect(random);
    501             return GrFillRectOp::MakePerEdgeWithLocalRect(context, std::move(paint), aaType,
    502                                                           aaFlags, viewMatrix, rect, localRect,
    503                                                           stencil);
    504         }
    505     } else {
    506         // The simplest constructor
    507         return GrFillRectOp::MakePerEdge(context, std::move(paint), aaType, aaFlags, viewMatrix,
    508                                          rect, stencil);
    509     }
    510 }
    511 
    512 #endif
    513