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