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