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