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