• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "src/gpu/ops/GrFillRectOp.h"
9 
10 #include "include/core/SkMatrix.h"
11 #include "include/core/SkRect.h"
12 #include "src/gpu/GrCaps.h"
13 #include "src/gpu/GrGeometryProcessor.h"
14 #include "src/gpu/GrPaint.h"
15 #include "src/gpu/GrProgramInfo.h"
16 #include "src/gpu/SkGr.h"
17 #include "src/gpu/geometry/GrQuad.h"
18 #include "src/gpu/geometry/GrQuadBuffer.h"
19 #include "src/gpu/geometry/GrQuadUtils.h"
20 #include "src/gpu/glsl/GrGLSLColorSpaceXformHelper.h"
21 #include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
22 #include "src/gpu/glsl/GrGLSLVarying.h"
23 #include "src/gpu/ops/GrMeshDrawOp.h"
24 #include "src/gpu/ops/GrQuadPerEdgeAA.h"
25 #include "src/gpu/ops/GrSimpleMeshDrawOpHelperWithStencil.h"
26 
27 namespace {
28 
29 using VertexSpec = GrQuadPerEdgeAA::VertexSpec;
30 using ColorType = GrQuadPerEdgeAA::ColorType;
31 
32 #if GR_TEST_UTILS
dump_quad_info(int index,const GrQuad * deviceQuad,const GrQuad * localQuad,const SkPMColor4f & color,GrQuadAAFlags aaFlags)33 static SkString dump_quad_info(int index, const GrQuad* deviceQuad,
34                                const GrQuad* localQuad, const SkPMColor4f& color,
35                                GrQuadAAFlags aaFlags) {
36     GrQuad safeLocal = localQuad ? *localQuad : GrQuad();
37     SkString str;
38     str.appendf("%d: Color: [%.2f, %.2f, %.2f, %.2f], Edge AA: l%u_t%u_r%u_b%u, \n"
39                 "  device quad: [(%.2f, %2.f, %.2f), (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f), "
40                 "(%.2f, %.2f, %.2f)],\n"
41                 "  local quad: [(%.2f, %2.f, %.2f), (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f), "
42                 "(%.2f, %.2f, %.2f)]\n",
43                 index, color.fR, color.fG, color.fB, color.fA,
44                 (uint32_t) (aaFlags & GrQuadAAFlags::kLeft),
45                 (uint32_t) (aaFlags & GrQuadAAFlags::kTop),
46                 (uint32_t) (aaFlags & GrQuadAAFlags::kRight),
47                 (uint32_t) (aaFlags & GrQuadAAFlags::kBottom),
48                 deviceQuad->x(0), deviceQuad->y(0), deviceQuad->w(0),
49                 deviceQuad->x(1), deviceQuad->y(1), deviceQuad->w(1),
50                 deviceQuad->x(2), deviceQuad->y(2), deviceQuad->w(2),
51                 deviceQuad->x(3), deviceQuad->y(3), deviceQuad->w(3),
52                 safeLocal.x(0), safeLocal.y(0), safeLocal.w(0),
53                 safeLocal.x(1), safeLocal.y(1), safeLocal.w(1),
54                 safeLocal.x(2), safeLocal.y(2), safeLocal.w(2),
55                 safeLocal.x(3), safeLocal.y(3), safeLocal.w(3));
56     return str;
57 }
58 #endif
59 
60 class FillRectOp final : public GrMeshDrawOp {
61 private:
62     using Helper = GrSimpleMeshDrawOpHelperWithStencil;
63 
64 public:
Make(GrRecordingContext * context,GrPaint && paint,GrAAType aaType,DrawQuad * quad,const GrUserStencilSettings * stencilSettings,Helper::InputFlags inputFlags)65     static GrOp::Owner Make(GrRecordingContext* context,
66                             GrPaint&& paint,
67                             GrAAType aaType,
68                             DrawQuad* quad,
69                             const GrUserStencilSettings* stencilSettings,
70                             Helper::InputFlags inputFlags) {
71         // Clean up deviations between aaType and edgeAA
72         GrQuadUtils::ResolveAAType(aaType, quad->fEdgeFlags, quad->fDevice,
73                                    &aaType, &quad->fEdgeFlags);
74         return Helper::FactoryHelper<FillRectOp>(context, std::move(paint), aaType, quad,
75                                                  stencilSettings, inputFlags);
76     }
77 
78     // aaType is passed to Helper in the initializer list, so incongruities between aaType and
79     // edgeFlags must be resolved prior to calling this constructor.
FillRectOp(GrProcessorSet * processorSet,SkPMColor4f paintColor,GrAAType aaType,DrawQuad * quad,const GrUserStencilSettings * stencil,Helper::InputFlags inputFlags)80     FillRectOp(GrProcessorSet* processorSet, SkPMColor4f paintColor, GrAAType aaType,
81                DrawQuad* quad, const GrUserStencilSettings* stencil, Helper::InputFlags inputFlags)
82             : INHERITED(ClassID())
83             , fHelper(processorSet, aaType, stencil, inputFlags)
84             , fQuads(1, !fHelper.isTrivial()) {
85         // Set bounds before clipping so we don't have to worry about unioning the bounds of
86         // the two potential quads (GrQuad::bounds() is perspective-safe).
87         this->setBounds(quad->fDevice.bounds(), HasAABloat(aaType == GrAAType::kCoverage),
88                         IsHairline::kNo);
89 
90         DrawQuad extra;
91         // Only clip when there's anti-aliasing. When non-aa, the GPU clips just fine and there's
92         // no inset/outset math that requires w > 0.
93         int count = quad->fEdgeFlags != GrQuadAAFlags::kNone ? GrQuadUtils::ClipToW0(quad, &extra)
94                                                              : 1;
95         if (count == 0) {
96             // We can't discard the op at this point, but disable AA flags so it won't go through
97             // inset/outset processing
98             quad->fEdgeFlags = GrQuadAAFlags::kNone;
99             count = 1;
100         }
101 
102         // Conservatively keep track of the local coordinates; it may be that the paint doesn't
103         // need them after analysis is finished. If the paint is known to be solid up front they
104         // can be skipped entirely.
105         fQuads.append(quad->fDevice, {paintColor, quad->fEdgeFlags},
106                       fHelper.isTrivial() ? nullptr : &quad->fLocal);
107         if (count > 1) {
108             fQuads.append(extra.fDevice, { paintColor, extra.fEdgeFlags },
109                           fHelper.isTrivial() ? nullptr : &extra.fLocal);
110         }
111     }
112 
name() const113     const char* name() const override { return "FillRectOp"; }
114 
visitProxies(const VisitProxyFunc & func) const115     void visitProxies(const VisitProxyFunc& func) const override {
116         if (fProgramInfo) {
117             fProgramInfo->visitFPProxies(func);
118         } else {
119             return fHelper.visitProxies(func);
120         }
121     }
122 
finalize(const GrCaps & caps,const GrAppliedClip * clip,GrClampType clampType)123     GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
124                                       GrClampType clampType) override {
125         // Initialize aggregate color analysis with the first quad's color (which always exists)
126         auto iter = fQuads.metadata();
127         SkAssertResult(iter.next());
128         GrProcessorAnalysisColor quadColors(iter->fColor);
129         // Then combine the colors of any additional quads (e.g. from MakeSet)
130         while(iter.next()) {
131             quadColors = GrProcessorAnalysisColor::Combine(quadColors, iter->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         auto coverage = fHelper.aaType() == GrAAType::kCoverage
142                                                     ? GrProcessorAnalysisCoverage::kSingleChannel
143                                                     : GrProcessorAnalysisCoverage::kNone;
144         auto result = fHelper.finalizeProcessors(caps, clip, clampType, 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         iter = fQuads.metadata();
148         SkPMColor4f colorOverride;
149         if (quadColors.isConstant(&colorOverride)) {
150             fColorType = GrQuadPerEdgeAA::MinColorType(colorOverride);
151             while(iter.next()) {
152                 iter->fColor = colorOverride;
153             }
154         } else {
155             // Otherwise compute the color type needed as the max over all quads.
156             fColorType = ColorType::kNone;
157             while(iter.next()) {
158                 fColorType = std::max(fColorType, GrQuadPerEdgeAA::MinColorType(iter->fColor));
159             }
160         }
161         // Most SkShaders' FPs multiply their calculated color by the paint color or alpha. We want
162         // to use ColorType::kNone to optimize out that multiply. However, if there are no color
163         // FPs then were really writing a special shader for white rectangles and not saving any
164         // multiples. So in that case use bytes to avoid the extra shader (and possibly work around
165         // an ANGLE issue: crbug.com/942565).
166         if (fColorType == ColorType::kNone && !result.hasColorFragmentProcessor()) {
167             fColorType = ColorType::kByte;
168         }
169 
170         return result;
171     }
172 
fixedFunctionFlags() const173     FixedFunctionFlags fixedFunctionFlags() const override {
174         // Since the AA type of the whole primitive is kept consistent with the per edge AA flags
175         // the helper's fixed function flags are appropriate.
176         return fHelper.fixedFunctionFlags();
177     }
178 
179     DEFINE_OP_CLASS_ID
180 
181 private:
182     friend class ::GrFillRectOp; // for access to addQuad
183 
184 #if GR_TEST_UTILS
numQuads() const185     int numQuads() const final { return fQuads.count(); }
186 #endif
187 
vertexSpec() const188     VertexSpec vertexSpec() const {
189         auto indexBufferOption = GrQuadPerEdgeAA::CalcIndexBufferOption(fHelper.aaType(),
190                                                                         fQuads.count());
191 
192         return VertexSpec(fQuads.deviceQuadType(), fColorType, fQuads.localQuadType(),
193                           fHelper.usesLocalCoords(), GrQuadPerEdgeAA::Subset::kNo,
194                           fHelper.aaType(),
195                           fHelper.compatibleWithCoverageAsAlpha(), indexBufferOption);
196     }
197 
programInfo()198     GrProgramInfo* programInfo() override {
199         return fProgramInfo;
200     }
201 
onCreateProgramInfo(const GrCaps * caps,SkArenaAlloc * arena,const GrSurfaceProxyView & writeView,GrAppliedClip && appliedClip,const GrXferProcessor::DstProxyView & dstProxyView,GrXferBarrierFlags renderPassXferBarriers,GrLoadOp colorLoadOp)202     void onCreateProgramInfo(const GrCaps* caps,
203                              SkArenaAlloc* arena,
204                              const GrSurfaceProxyView& writeView,
205                              GrAppliedClip&& appliedClip,
206                              const GrXferProcessor::DstProxyView& dstProxyView,
207                              GrXferBarrierFlags renderPassXferBarriers,
208                              GrLoadOp colorLoadOp) override {
209         const VertexSpec vertexSpec = this->vertexSpec();
210 
211         GrGeometryProcessor* gp = GrQuadPerEdgeAA::MakeProcessor(arena, vertexSpec);
212         SkASSERT(gp->vertexStride() == vertexSpec.vertexSize());
213 
214         fProgramInfo = fHelper.createProgramInfoWithStencil(caps, arena, writeView,
215                                                             std::move(appliedClip),
216                                                             dstProxyView, gp,
217                                                             vertexSpec.primitiveType(),
218                                                             renderPassXferBarriers, colorLoadOp);
219     }
220 
onPrePrepareDraws(GrRecordingContext * rContext,const GrSurfaceProxyView & writeView,GrAppliedClip * clip,const GrXferProcessor::DstProxyView & dstProxyView,GrXferBarrierFlags renderPassXferBarriers,GrLoadOp colorLoadOp)221     void onPrePrepareDraws(GrRecordingContext* rContext,
222                            const GrSurfaceProxyView& writeView,
223                            GrAppliedClip* clip,
224                            const GrXferProcessor::DstProxyView& dstProxyView,
225                            GrXferBarrierFlags renderPassXferBarriers,
226                            GrLoadOp colorLoadOp) override {
227         TRACE_EVENT0("skia.gpu", TRACE_FUNC);
228 
229         SkASSERT(!fPrePreparedVertices);
230 
231         INHERITED::onPrePrepareDraws(rContext, writeView, clip, dstProxyView,
232                                      renderPassXferBarriers, colorLoadOp);
233 
234         SkArenaAlloc* arena = rContext->priv().recordTimeAllocator();
235 
236         const VertexSpec vertexSpec = this->vertexSpec();
237 
238         const int totalNumVertices = fQuads.count() * vertexSpec.verticesPerQuad();
239         const size_t totalVertexSizeInBytes = vertexSpec.vertexSize() * totalNumVertices;
240 
241         fPrePreparedVertices = arena->makeArrayDefault<char>(totalVertexSizeInBytes);
242 
243         this->tessellate(vertexSpec, fPrePreparedVertices);
244     }
245 
tessellate(const VertexSpec & vertexSpec,char * dst) const246     void tessellate(const VertexSpec& vertexSpec, char* dst) const {
247         static constexpr SkRect kEmptyDomain = SkRect::MakeEmpty();
248 
249         GrQuadPerEdgeAA::Tessellator tessellator(vertexSpec, dst);
250         auto iter = fQuads.iterator();
251         while (iter.next()) {
252             // All entries should have local coords, or no entries should have local coords,
253             // matching !helper.isTrivial() (which is more conservative than helper.usesLocalCoords)
254             SkASSERT(iter.isLocalValid() != fHelper.isTrivial());
255             auto info = iter.metadata();
256             tessellator.append(iter.deviceQuad(), iter.localQuad(),
257                                info.fColor, kEmptyDomain, info.fAAFlags);
258         }
259     }
260 
onPrepareDraws(Target * target)261     void onPrepareDraws(Target* target) override {
262         TRACE_EVENT0("skia.gpu", TRACE_FUNC);
263 
264         const VertexSpec vertexSpec = this->vertexSpec();
265 
266         // Make sure that if the op thought it was a solid color, the vertex spec does not use
267         // local coords.
268         SkASSERT(!fHelper.isTrivial() || !fHelper.usesLocalCoords());
269 
270         const int totalNumVertices = fQuads.count() * vertexSpec.verticesPerQuad();
271 
272         // Fill the allocated vertex data
273         void* vdata = target->makeVertexSpace(vertexSpec.vertexSize(), totalNumVertices,
274                                               &fVertexBuffer, &fBaseVertex);
275         if (!vdata) {
276             SkDebugf("Could not allocate vertices\n");
277             return;
278         }
279 
280         if (fPrePreparedVertices) {
281             const size_t totalVertexSizeInBytes = vertexSpec.vertexSize() * totalNumVertices;
282 
283             memcpy(vdata, fPrePreparedVertices, totalVertexSizeInBytes);
284         } else {
285             this->tessellate(vertexSpec, (char*) vdata);
286         }
287 
288         if (vertexSpec.needsIndexBuffer()) {
289             fIndexBuffer = GrQuadPerEdgeAA::GetIndexBuffer(target, vertexSpec.indexBufferOption());
290             if (!fIndexBuffer) {
291                 SkDebugf("Could not allocate indices\n");
292                 return;
293             }
294         }
295     }
296 
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)297     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
298         if (!fVertexBuffer) {
299             return;
300         }
301 
302         const VertexSpec vertexSpec = this->vertexSpec();
303 
304         if (vertexSpec.needsIndexBuffer() && !fIndexBuffer) {
305             return;
306         }
307 
308         if (!fProgramInfo) {
309             this->createProgramInfo(flushState);
310         }
311 
312         const int totalNumVertices = fQuads.count() * vertexSpec.verticesPerQuad();
313 
314         flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
315         flushState->bindBuffers(std::move(fIndexBuffer), nullptr, std::move(fVertexBuffer));
316         flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
317         GrQuadPerEdgeAA::IssueDraw(flushState->caps(), flushState->opsRenderPass(), vertexSpec, 0,
318                                    fQuads.count(), totalNumVertices, fBaseVertex);
319     }
320 
onCombineIfPossible(GrOp * t,SkArenaAlloc *,const GrCaps & caps)321     CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
322         TRACE_EVENT0("skia.gpu", TRACE_FUNC);
323         const auto* that = t->cast<FillRectOp>();
324 
325         bool upgradeToCoverageAAOnMerge = false;
326         if (fHelper.aaType() != that->fHelper.aaType()) {
327             if (!CanUpgradeAAOnMerge(fHelper.aaType(), that->fHelper.aaType())) {
328                 return CombineResult::kCannotCombine;
329             }
330             upgradeToCoverageAAOnMerge = true;
331         }
332 
333         if (CombinedQuadCountWillOverflow(fHelper.aaType(), upgradeToCoverageAAOnMerge,
334                                           fQuads.count() + that->fQuads.count())) {
335             return CombineResult::kCannotCombine;
336         }
337 
338         // Unlike most users of the draw op helper, this op can merge none-aa and coverage-aa draw
339         // ops together, so pass true as the last argument.
340         if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds(), true)) {
341             return CombineResult::kCannotCombine;
342         }
343 
344         // If the paints were compatible, the trivial/solid-color state should be the same
345         SkASSERT(fHelper.isTrivial() == that->fHelper.isTrivial());
346 
347         // If the processor sets are compatible, the two ops are always compatible; it just needs to
348         // adjust the state of the op to be the more general quad and aa types of the two ops and
349         // then concatenate the per-quad data.
350         fColorType = std::max(fColorType, that->fColorType);
351 
352         // The helper stores the aa type, but isCompatible(with true arg) allows the two ops' aa
353         // types to be none and coverage, in which case this op's aa type must be lifted to coverage
354         // so that quads with no aa edges can be batched with quads that have some/all edges aa'ed.
355         if (upgradeToCoverageAAOnMerge) {
356             fHelper.setAAType(GrAAType::kCoverage);
357         }
358 
359         fQuads.concat(that->fQuads);
360         return CombineResult::kMerged;
361     }
362 
363 #if GR_TEST_UTILS
onDumpInfo() const364     SkString onDumpInfo() const override {
365         SkString str = SkStringPrintf("# draws: %u\n", fQuads.count());
366         str.appendf("Device quad type: %u, local quad type: %u\n",
367                     (uint32_t) fQuads.deviceQuadType(), (uint32_t) fQuads.localQuadType());
368         str += fHelper.dumpInfo();
369         int i = 0;
370         auto iter = fQuads.iterator();
371         while(iter.next()) {
372             const ColorAndAA& info = iter.metadata();
373             str += dump_quad_info(i, iter.deviceQuad(), iter.localQuad(),
374                                   info.fColor, info.fAAFlags);
375             i++;
376         }
377         return str;
378     }
379 #endif
380 
canAddQuads(int numQuads,GrAAType aaType)381     bool canAddQuads(int numQuads, GrAAType aaType) {
382         // The new quad's aa type should be the same as the first quad's or none, except when the
383         // first quad's aa type was already downgraded to none, in which case the stored type must
384         // be lifted to back to the requested type.
385         int quadCount = fQuads.count() + numQuads;
386         if (aaType != fHelper.aaType() && aaType != GrAAType::kNone) {
387             auto indexBufferOption = GrQuadPerEdgeAA::CalcIndexBufferOption(aaType, quadCount);
388             if (quadCount > GrQuadPerEdgeAA::QuadLimit(indexBufferOption)) {
389                 // Promoting to the new aaType would've caused an overflow of the indexBuffer
390                 // limit
391                 return false;
392             }
393 
394             // Original quad was downgraded to non-aa, lift back up to this quad's required type
395             SkASSERT(fHelper.aaType() == GrAAType::kNone);
396             fHelper.setAAType(aaType);
397         } else {
398             auto indexBufferOption = GrQuadPerEdgeAA::CalcIndexBufferOption(fHelper.aaType(),
399                                                                             quadCount);
400             if (quadCount > GrQuadPerEdgeAA::QuadLimit(indexBufferOption)) {
401                 return false; // This op can't grow any more
402             }
403         }
404 
405         return true;
406     }
407 
408     // Similar to onCombineIfPossible, but adds a quad assuming its op would have been compatible.
409     // But since it's avoiding the op list management, it must update the op's bounds.
addQuad(DrawQuad * quad,const SkPMColor4f & color,GrAAType aaType)410     bool addQuad(DrawQuad* quad, const SkPMColor4f& color, GrAAType aaType) {
411         SkRect newBounds = this->bounds();
412         newBounds.joinPossiblyEmptyRect(quad->fDevice.bounds());
413 
414         DrawQuad extra;
415         int count = quad->fEdgeFlags != GrQuadAAFlags::kNone ? GrQuadUtils::ClipToW0(quad, &extra)
416                                                              : 1;
417         if (count == 0 ) {
418             // Just skip the append (trivial success)
419             return true;
420         } else if (!this->canAddQuads(count, aaType)) {
421             // Not enough room in the index buffer for the AA type
422             return false;
423         } else {
424             // Can actually add the 1 or 2 quads representing the draw
425             fQuads.append(quad->fDevice, { color, quad->fEdgeFlags },
426                           fHelper.isTrivial() ? nullptr : &quad->fLocal);
427             if (count > 1) {
428                 fQuads.append(extra.fDevice, { color, extra.fEdgeFlags },
429                               fHelper.isTrivial() ? nullptr : &extra.fLocal);
430             }
431             // Update the bounds
432             this->setBounds(newBounds, HasAABloat(fHelper.aaType() == GrAAType::kCoverage),
433                             IsHairline::kNo);
434             return true;
435         }
436     }
437 
438     struct ColorAndAA {
439         SkPMColor4f fColor;
440         GrQuadAAFlags fAAFlags;
441     };
442 
443     Helper fHelper;
444     GrQuadBuffer<ColorAndAA> fQuads;
445     char* fPrePreparedVertices = nullptr;
446 
447     GrProgramInfo* fProgramInfo = nullptr;
448     ColorType      fColorType;
449 
450     sk_sp<const GrBuffer> fVertexBuffer;
451     sk_sp<const GrBuffer> fIndexBuffer;
452     int fBaseVertex;
453 
454     using INHERITED = GrMeshDrawOp;
455 };
456 
457 } // anonymous namespace
458 
Make(GrRecordingContext * context,GrPaint && paint,GrAAType aaType,DrawQuad * quad,const GrUserStencilSettings * stencil,InputFlags inputFlags)459 GrOp::Owner GrFillRectOp::Make(GrRecordingContext* context,
460                                GrPaint&& paint,
461                                GrAAType aaType,
462                                DrawQuad* quad,
463                                const GrUserStencilSettings* stencil,
464                                InputFlags inputFlags) {
465     return FillRectOp::Make(context, std::move(paint), aaType, std::move(quad), stencil,
466                             inputFlags);
467 }
468 
MakeNonAARect(GrRecordingContext * context,GrPaint && paint,const SkMatrix & view,const SkRect & rect,const GrUserStencilSettings * stencil)469 GrOp::Owner GrFillRectOp::MakeNonAARect(GrRecordingContext* context,
470                                         GrPaint&& paint,
471                                         const SkMatrix& view,
472                                         const SkRect& rect,
473                                         const GrUserStencilSettings* stencil) {
474     DrawQuad quad{GrQuad::MakeFromRect(rect, view), GrQuad(rect), GrQuadAAFlags::kNone};
475     return FillRectOp::Make(context, std::move(paint), GrAAType::kNone, &quad, stencil,
476                             InputFlags::kNone);
477 }
478 
MakeOp(GrRecordingContext * context,GrPaint && paint,GrAAType aaType,const SkMatrix & viewMatrix,const GrSurfaceDrawContext::QuadSetEntry quads[],int cnt,const GrUserStencilSettings * stencilSettings,int * numConsumed)479 GrOp::Owner GrFillRectOp::MakeOp(GrRecordingContext* context,
480                                  GrPaint&& paint,
481                                  GrAAType aaType,
482                                  const SkMatrix& viewMatrix,
483                                  const GrSurfaceDrawContext::QuadSetEntry quads[],
484                                  int cnt,
485                                  const GrUserStencilSettings* stencilSettings,
486                                  int* numConsumed) {
487     // First make a draw op for the first quad in the set
488     SkASSERT(cnt > 0);
489 
490     DrawQuad quad{GrQuad::MakeFromRect(quads[0].fRect, viewMatrix),
491                   GrQuad::MakeFromRect(quads[0].fRect, quads[0].fLocalMatrix),
492                   quads[0].fAAFlags};
493     paint.setColor4f(quads[0].fColor);
494     GrOp::Owner op = FillRectOp::Make(context, std::move(paint), aaType,
495                                       &quad, stencilSettings, InputFlags::kNone);
496     FillRectOp* fillRects = op->cast<FillRectOp>();
497 
498     *numConsumed = 1;
499     // Accumulate remaining quads similar to onCombineIfPossible() without creating an op
500     for (int i = 1; i < cnt; ++i) {
501         quad = {GrQuad::MakeFromRect(quads[i].fRect, viewMatrix),
502                 GrQuad::MakeFromRect(quads[i].fRect, quads[i].fLocalMatrix),
503                 quads[i].fAAFlags};
504 
505         GrAAType resolvedAA;
506         GrQuadUtils::ResolveAAType(aaType, quads[i].fAAFlags, quad.fDevice,
507                                    &resolvedAA, &quad.fEdgeFlags);
508 
509         if (!fillRects->addQuad(&quad, quads[i].fColor, resolvedAA)) {
510             break;
511         }
512 
513         (*numConsumed)++;
514     }
515 
516     return op;
517 }
518 
AddFillRectOps(GrSurfaceDrawContext * rtc,const GrClip * clip,GrRecordingContext * context,GrPaint && paint,GrAAType aaType,const SkMatrix & viewMatrix,const GrSurfaceDrawContext::QuadSetEntry quads[],int cnt,const GrUserStencilSettings * stencilSettings)519 void GrFillRectOp::AddFillRectOps(GrSurfaceDrawContext* rtc,
520                                   const GrClip* clip,
521                                   GrRecordingContext* context,
522                                   GrPaint&& paint,
523                                   GrAAType aaType,
524                                   const SkMatrix& viewMatrix,
525                                   const GrSurfaceDrawContext::QuadSetEntry quads[],
526                                   int cnt,
527                                   const GrUserStencilSettings* stencilSettings) {
528 
529     int offset = 0;
530     int numLeft = cnt;
531     while (numLeft) {
532         int numConsumed = 0;
533 
534         GrOp::Owner op = MakeOp(context, GrPaint::Clone(paint), aaType, viewMatrix,
535                                 &quads[offset], numLeft, stencilSettings,
536                                 &numConsumed);
537 
538         offset += numConsumed;
539         numLeft -= numConsumed;
540 
541         rtc->addDrawOp(clip, std::move(op));
542     }
543 
544     SkASSERT(offset == cnt);
545 }
546 
547 #if GR_TEST_UTILS
548 
ClassID()549 uint32_t GrFillRectOp::ClassID() {
550     return FillRectOp::ClassID();
551 }
552 
553 #include "src/gpu/GrDrawOpTest.h"
554 #include "src/gpu/SkGr.h"
555 
GR_DRAW_OP_TEST_DEFINE(FillRectOp)556 GR_DRAW_OP_TEST_DEFINE(FillRectOp) {
557     SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
558     SkRect rect = GrTest::TestRect(random);
559 
560     GrAAType aaType = GrAAType::kNone;
561     if (random->nextBool()) {
562         aaType = (numSamples > 1) ? GrAAType::kMSAA : GrAAType::kCoverage;
563     }
564     const GrUserStencilSettings* stencil = random->nextBool() ? nullptr
565                                                               : GrGetRandomStencil(random, context);
566 
567     GrQuadAAFlags aaFlags = GrQuadAAFlags::kNone;
568     aaFlags |= random->nextBool() ? GrQuadAAFlags::kLeft : GrQuadAAFlags::kNone;
569     aaFlags |= random->nextBool() ? GrQuadAAFlags::kTop : GrQuadAAFlags::kNone;
570     aaFlags |= random->nextBool() ? GrQuadAAFlags::kRight : GrQuadAAFlags::kNone;
571     aaFlags |= random->nextBool() ? GrQuadAAFlags::kBottom : GrQuadAAFlags::kNone;
572 
573     if (random->nextBool()) {
574         if (random->nextBool()) {
575             // Single local matrix
576             SkMatrix localMatrix = GrTest::TestMatrixInvertible(random);
577             DrawQuad quad = {GrQuad::MakeFromRect(rect, viewMatrix),
578                              GrQuad::MakeFromRect(rect, localMatrix), aaFlags};
579             return GrFillRectOp::Make(context, std::move(paint), aaType, &quad, stencil);
580         } else {
581             // Pass local rect directly
582             SkRect localRect = GrTest::TestRect(random);
583             DrawQuad quad = {GrQuad::MakeFromRect(rect, viewMatrix),
584                              GrQuad(localRect), aaFlags};
585             return GrFillRectOp::Make(context, std::move(paint), aaType, &quad, stencil);
586         }
587     } else {
588         // The simplest constructor
589         DrawQuad quad = {GrQuad::MakeFromRect(rect, viewMatrix), GrQuad(rect), aaFlags};
590         return GrFillRectOp::Make(context, std::move(paint), aaType, &quad, stencil);
591     }
592 }
593 
594 #endif
595