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