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