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