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