• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/SkGr.h"
16 #include "src/gpu/geometry/GrQuad.h"
17 #include "src/gpu/geometry/GrQuadBuffer.h"
18 #include "src/gpu/geometry/GrQuadUtils.h"
19 #include "src/gpu/glsl/GrGLSLColorSpaceXformHelper.h"
20 #include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
21 #include "src/gpu/glsl/GrGLSLVarying.h"
22 #include "src/gpu/ops/GrMeshDrawOp.h"
23 #include "src/gpu/ops/GrQuadPerEdgeAA.h"
24 #include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
25 
26 namespace {
27 
28 using VertexSpec = GrQuadPerEdgeAA::VertexSpec;
29 using ColorType = GrQuadPerEdgeAA::ColorType;
30 
31 #ifdef SK_DEBUG
dump_quad_info(int index,const GrQuad & deviceQuad,const GrQuad & localQuad,const SkPMColor4f & color,GrQuadAAFlags aaFlags)32 static SkString dump_quad_info(int index, const GrQuad& deviceQuad,
33                                const GrQuad& localQuad, const SkPMColor4f& color,
34                                GrQuadAAFlags aaFlags) {
35     SkString str;
36     str.appendf("%d: Color: [%.2f, %.2f, %.2f, %.2f], Edge AA: l%u_t%u_r%u_b%u, \n"
37                 "  device quad: [(%.2f, %2.f, %.2f), (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f), "
38                 "(%.2f, %.2f, %.2f)],\n"
39                 "  local quad: [(%.2f, %2.f, %.2f), (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f), "
40                 "(%.2f, %.2f, %.2f)]\n",
41                 index, color.fR, color.fG, color.fB, color.fA,
42                 (uint32_t) (aaFlags & GrQuadAAFlags::kLeft),
43                 (uint32_t) (aaFlags & GrQuadAAFlags::kTop),
44                 (uint32_t) (aaFlags & GrQuadAAFlags::kRight),
45                 (uint32_t) (aaFlags & GrQuadAAFlags::kBottom),
46                 deviceQuad.x(0), deviceQuad.y(0), deviceQuad.w(0),
47                 deviceQuad.x(1), deviceQuad.y(1), deviceQuad.w(1),
48                 deviceQuad.x(2), deviceQuad.y(2), deviceQuad.w(2),
49                 deviceQuad.x(3), deviceQuad.y(3), deviceQuad.w(3),
50                 localQuad.x(0), localQuad.y(0), localQuad.w(0),
51                 localQuad.x(1), localQuad.y(1), localQuad.w(1),
52                 localQuad.x(2), localQuad.y(2), localQuad.w(2),
53                 localQuad.x(3), localQuad.y(3), localQuad.w(3));
54     return str;
55 }
56 #endif
57 
58 class FillRectOp final : public GrMeshDrawOp {
59 private:
60     using Helper = GrSimpleMeshDrawOpHelperWithStencil;
61 
62 public:
Make(GrRecordingContext * context,GrPaint && paint,GrAAType aaType,GrQuadAAFlags edgeAA,const GrUserStencilSettings * stencilSettings,const GrQuad & deviceQuad,const GrQuad & localQuad)63     static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
64                                           GrPaint&& paint,
65                                           GrAAType aaType,
66                                           GrQuadAAFlags edgeAA,
67                                           const GrUserStencilSettings* stencilSettings,
68                                           const GrQuad& deviceQuad,
69                                           const GrQuad& localQuad) {
70         // Clean up deviations between aaType and edgeAA
71         GrQuadUtils::ResolveAAType(aaType, edgeAA, deviceQuad, &aaType, &edgeAA);
72         return Helper::FactoryHelper<FillRectOp>(context, std::move(paint), aaType, edgeAA,
73                 stencilSettings, deviceQuad, localQuad);
74     }
75 
76     // aaType is passed to Helper in the initializer list, so incongruities between aaType and
77     // edgeFlags must be resolved prior to calling this constructor.
FillRectOp(Helper::MakeArgs args,SkPMColor4f paintColor,GrAAType aaType,GrQuadAAFlags edgeFlags,const GrUserStencilSettings * stencil,const GrQuad & deviceQuad,const GrQuad & localQuad)78     FillRectOp(Helper::MakeArgs args, SkPMColor4f paintColor, GrAAType aaType,
79                GrQuadAAFlags edgeFlags, const GrUserStencilSettings* stencil,
80                const GrQuad& deviceQuad, const GrQuad& localQuad)
81             : INHERITED(ClassID())
82             , fHelper(args, aaType, stencil)
83             , fQuads(1, !fHelper.isTrivial()) {
84         // Conservatively keep track of the local coordinates; it may be that the paint doesn't
85         // need them after analysis is finished. If the paint is known to be solid up front they
86         // can be skipped entirely.
87         fQuads.append(deviceQuad, { paintColor, edgeFlags },
88                       fHelper.isTrivial() ? nullptr : &localQuad);
89         this->setBounds(deviceQuad.bounds(), HasAABloat(aaType == GrAAType::kCoverage),
90                         IsZeroArea::kNo);
91     }
92 
name() const93     const char* name() const override { return "FillRectOp"; }
94 
visitProxies(const VisitProxyFunc & func) const95     void visitProxies(const VisitProxyFunc& func) const override {
96         return fHelper.visitProxies(func);
97     }
98 
99 #ifdef SK_DEBUG
dumpInfo() const100     SkString dumpInfo() const override {
101         SkString str;
102         str.appendf("# draws: %u\n", fQuads.count());
103         str.appendf("Device quad type: %u, local quad type: %u\n",
104                     (uint32_t) fQuads.deviceQuadType(), (uint32_t) fQuads.localQuadType());
105         str += fHelper.dumpInfo();
106         int i = 0;
107         auto iter = fQuads.iterator();
108         while(iter.next()) {
109             const ColorAndAA& info = iter.metadata();
110             str += dump_quad_info(i, iter.deviceQuad(), iter.localQuad(),
111                                   info.fColor, info.fAAFlags);
112             i++;
113         }
114         str += INHERITED::dumpInfo();
115         return str;
116     }
117 #endif
118 
finalize(const GrCaps & caps,const GrAppliedClip * clip,bool hasMixedSampledCoverage,GrClampType clampType)119     GrProcessorSet::Analysis finalize(
120             const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
121             GrClampType clampType) override {
122         // Initialize aggregate color analysis with the first quad's color (which always exists)
123         auto iter = fQuads.metadata();
124         SkAssertResult(iter.next());
125         GrProcessorAnalysisColor quadColors(iter->fColor);
126         // Then combine the colors of any additional quads (e.g. from MakeSet)
127         while(iter.next()) {
128             quadColors = GrProcessorAnalysisColor::Combine(quadColors, iter->fColor);
129             if (quadColors.isUnknown()) {
130                 // No point in accumulating additional starting colors, combining cannot make it
131                 // less unknown.
132                 break;
133             }
134         }
135 
136         // If the AA type is coverage, it will be a single value per pixel; if it's not coverage AA
137         // then the coverage is always 1.0, so specify kNone for more optimal blending.
138         GrProcessorAnalysisCoverage coverage = fHelper.aaType() == GrAAType::kCoverage ?
139                 GrProcessorAnalysisCoverage::kSingleChannel :
140                 GrProcessorAnalysisCoverage::kNone;
141         auto result = fHelper.finalizeProcessors(
142                 caps, clip, hasMixedSampledCoverage, clampType, coverage, &quadColors);
143         // If there is a constant color after analysis, that means all of the quads should be set
144         // to the same color (even if they started out with different colors).
145         iter = fQuads.metadata();
146         SkPMColor4f colorOverride;
147         if (quadColors.isConstant(&colorOverride)) {
148             fColorType = GrQuadPerEdgeAA::MinColorType(colorOverride, clampType, caps);
149             while(iter.next()) {
150                 iter->fColor = colorOverride;
151             }
152         } else {
153             // Otherwise compute the color type needed as the max over all quads.
154             fColorType = ColorType::kNone;
155             while(iter.next()) {
156                 fColorType = SkTMax(fColorType,
157                                     GrQuadPerEdgeAA::MinColorType(iter->fColor, clampType, caps));
158             }
159         }
160         // Most SkShaders' FPs multiply their calculated color by the paint color or alpha. We want
161         // to use ColorType::kNone to optimize out that multiply. However, if there are no color
162         // FPs then were really writing a special shader for white rectangles and not saving any
163         // multiples. So in that case use bytes to avoid the extra shader (and possibly work around
164         // an ANGLE issue: crbug.com/942565).
165         if (fColorType == ColorType::kNone && !result.hasColorFragmentProcessor()) {
166             fColorType = ColorType::kByte;
167         }
168 
169         return result;
170     }
171 
fixedFunctionFlags() const172     FixedFunctionFlags fixedFunctionFlags() const override {
173         // Since the AA type of the whole primitive is kept consistent with the per edge AA flags
174         // the helper's fixed function flags are appropriate.
175         return fHelper.fixedFunctionFlags();
176     }
177 
178     DEFINE_OP_CLASS_ID
179 
180 private:
181     // For GrFillRectOp::MakeSet's use of addQuad
182     friend std::unique_ptr<GrDrawOp> GrFillRectOp::MakeSet(
183             GrRecordingContext*,
184             GrPaint&&,
185             GrAAType, const SkMatrix& viewMatrix,
186             const GrRenderTargetContext::QuadSetEntry quads[], int quadCount,
187             const GrUserStencilSettings*);
188 
onPrepareDraws(Target * target)189     void onPrepareDraws(Target* target) override {
190         TRACE_EVENT0("skia.gpu", TRACE_FUNC);
191 
192         using Domain = GrQuadPerEdgeAA::Domain;
193         static constexpr SkRect kEmptyDomain = SkRect::MakeEmpty();
194 
195         VertexSpec vertexSpec(fQuads.deviceQuadType(), fColorType, fQuads.localQuadType(),
196                               fHelper.usesLocalCoords(), Domain::kNo, fHelper.aaType(),
197                               fHelper.compatibleWithCoverageAsAlpha());
198         // Make sure that if the op thought it was a solid color, the vertex spec does not use
199         // local coords.
200         SkASSERT(!fHelper.isTrivial() || !fHelper.usesLocalCoords());
201 
202         sk_sp<GrGeometryProcessor> gp = GrQuadPerEdgeAA::MakeProcessor(vertexSpec);
203         size_t vertexSize = gp->vertexStride();
204 
205         sk_sp<const GrBuffer> vbuffer;
206         int vertexOffsetInBuffer = 0;
207 
208         // Fill the allocated vertex data
209         void* vdata = target->makeVertexSpace(
210                 vertexSize, fQuads.count() * vertexSpec.verticesPerQuad(),
211                 &vbuffer, &vertexOffsetInBuffer);
212         if (!vdata) {
213             SkDebugf("Could not allocate vertices\n");
214             return;
215         }
216 
217         // vertices pointer advances through vdata based on Tessellate's return value
218         void* vertices = vdata;
219         auto iter = fQuads.iterator();
220         while(iter.next()) {
221             // All entries should have local coords, or no entries should have local coords,
222             // matching !helper.isTrivial() (which is more conservative than helper.usesLocalCoords)
223             SkASSERT(iter.isLocalValid() != fHelper.isTrivial());
224             auto info = iter.metadata();
225             vertices = GrQuadPerEdgeAA::Tessellate(vertices, vertexSpec, iter.deviceQuad(),
226                     info.fColor, iter.localQuad(), kEmptyDomain, info.fAAFlags);
227         }
228 
229         // Configure the mesh for the vertex data
230         GrMesh* mesh = target->allocMeshes(1);
231         if (!GrQuadPerEdgeAA::ConfigureMeshIndices(target, mesh, vertexSpec, fQuads.count())) {
232             SkDebugf("Could not allocate indices\n");
233             return;
234         }
235         mesh->setVertexData(std::move(vbuffer), vertexOffsetInBuffer);
236         target->recordDraw(std::move(gp), mesh);
237     }
238 
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)239     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
240         fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
241     }
242 
onCombineIfPossible(GrOp * t,const GrCaps & caps)243     CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
244         TRACE_EVENT0("skia.gpu", TRACE_FUNC);
245         const auto* that = t->cast<FillRectOp>();
246 
247         if ((fHelper.aaType() == GrAAType::kCoverage ||
248              that->fHelper.aaType() == GrAAType::kCoverage) &&
249             fQuads.count() + that->fQuads.count() > GrQuadPerEdgeAA::kNumAAQuadsInIndexBuffer) {
250             // This limit on batch size seems to help on Adreno devices
251             return CombineResult::kCannotCombine;
252         }
253 
254         // Unlike most users of the draw op helper, this op can merge none-aa and coverage-aa draw
255         // ops together, so pass true as the last argument.
256         if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds(), true)) {
257             return CombineResult::kCannotCombine;
258         }
259 
260         // If the paints were compatible, the trivial/solid-color state should be the same
261         SkASSERT(fHelper.isTrivial() == that->fHelper.isTrivial());
262 
263         // If the processor sets are compatible, the two ops are always compatible; it just needs to
264         // adjust the state of the op to be the more general quad and aa types of the two ops and
265         // then concatenate the per-quad data.
266         fColorType = SkTMax(fColorType, that->fColorType);
267 
268         // The helper stores the aa type, but isCompatible(with true arg) allows the two ops' aa
269         // types to be none and coverage, in which case this op's aa type must be lifted to coverage
270         // so that quads with no aa edges can be batched with quads that have some/all edges aa'ed.
271         if (fHelper.aaType() == GrAAType::kNone && that->fHelper.aaType() == GrAAType::kCoverage) {
272             fHelper.setAAType(GrAAType::kCoverage);
273         }
274 
275         fQuads.concat(that->fQuads);
276         return CombineResult::kMerged;
277     }
278 
279     // Similar to onCombineIfPossible, but adds a quad assuming its op would have been compatible.
280     // But since it's avoiding the op list management, it must update the op's bounds. This is only
281     // used with quad sets, which uses the same view matrix for each quad so this assumes that the
282     // device quad type of the new quad is the same as the op's.
addQuad(const GrQuad & deviceQuad,const GrQuad & localQuad,const SkPMColor4f & color,GrQuadAAFlags edgeAA,GrAAType aaType)283     void addQuad(const GrQuad& deviceQuad, const GrQuad& localQuad,
284                  const SkPMColor4f& color, GrQuadAAFlags edgeAA, GrAAType aaType) {
285         // The new quad's aa type should be the same as the first quad's or none, except when the
286         // first quad's aa type was already downgraded to none, in which case the stored type must
287         // be lifted to back to the requested type.
288         if (aaType != fHelper.aaType()) {
289             if (aaType != GrAAType::kNone) {
290                 // Original quad was downgraded to non-aa, lift back up to this quad's required type
291                 SkASSERT(fHelper.aaType() == GrAAType::kNone);
292                 fHelper.setAAType(aaType);
293             }
294             // else the new quad could have been downgraded but the other quads can't be, so don't
295             // reset the op's accumulated aa type.
296         }
297 
298         // Update the bounds and add the quad to this op's storage
299         SkRect newBounds = this->bounds();
300         newBounds.joinPossiblyEmptyRect(deviceQuad.bounds());
301         this->setBounds(newBounds, HasAABloat(fHelper.aaType() == GrAAType::kCoverage),
302                         IsZeroArea::kNo);
303         fQuads.append(deviceQuad, { color, edgeAA }, fHelper.isTrivial() ? nullptr : &localQuad);
304     }
305 
306     struct ColorAndAA {
307         SkPMColor4f fColor;
308         GrQuadAAFlags fAAFlags;
309     };
310 
311     Helper fHelper;
312     GrQuadBuffer<ColorAndAA> fQuads;
313 
314     ColorType fColorType;
315 
316     typedef GrMeshDrawOp INHERITED;
317 };
318 
319 } // anonymous namespace
320 
321 namespace GrFillRectOp {
322 
Make(GrRecordingContext * context,GrPaint && paint,GrAAType aaType,GrQuadAAFlags aaFlags,const GrQuad & deviceQuad,const GrQuad & localQuad,const GrUserStencilSettings * stencil)323 std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
324                                GrPaint&& paint,
325                                GrAAType aaType,
326                                GrQuadAAFlags aaFlags,
327                                const GrQuad& deviceQuad,
328                                const GrQuad& localQuad,
329                                const GrUserStencilSettings* stencil) {
330     return FillRectOp::Make(context, std::move(paint), aaType, aaFlags, stencil,
331                             deviceQuad, localQuad);
332 }
333 
MakeNonAARect(GrRecordingContext * context,GrPaint && paint,const SkMatrix & view,const SkRect & rect,const GrUserStencilSettings * stencil)334 std::unique_ptr<GrDrawOp> MakeNonAARect(GrRecordingContext* context,
335                                         GrPaint&& paint,
336                                         const SkMatrix& view,
337                                         const SkRect& rect,
338                                         const GrUserStencilSettings* stencil) {
339     return FillRectOp::Make(context, std::move(paint), GrAAType::kNone, GrQuadAAFlags::kNone,
340                             stencil, GrQuad::MakeFromRect(rect, view), GrQuad(rect));
341 }
342 
MakeSet(GrRecordingContext * context,GrPaint && paint,GrAAType aaType,const SkMatrix & viewMatrix,const GrRenderTargetContext::QuadSetEntry quads[],int cnt,const GrUserStencilSettings * stencilSettings)343 std::unique_ptr<GrDrawOp> MakeSet(GrRecordingContext* context,
344                                   GrPaint&& paint,
345                                   GrAAType aaType,
346                                   const SkMatrix& viewMatrix,
347                                   const GrRenderTargetContext::QuadSetEntry quads[],
348                                   int cnt,
349                                   const GrUserStencilSettings* stencilSettings) {
350     // First make a draw op for the first quad in the set
351     SkASSERT(cnt > 0);
352 
353     paint.setColor4f(quads[0].fColor);
354     std::unique_ptr<GrDrawOp> op = FillRectOp::Make(context, std::move(paint), aaType,
355             quads[0].fAAFlags, stencilSettings,
356             GrQuad::MakeFromRect(quads[0].fRect, viewMatrix),
357             GrQuad::MakeFromRect(quads[0].fRect, quads[0].fLocalMatrix));
358     auto* fillRects = op->cast<FillRectOp>();
359 
360     // Accumulate remaining quads similar to onCombineIfPossible() without creating an op
361     for (int i = 1; i < cnt; ++i) {
362         GrQuad deviceQuad = GrQuad::MakeFromRect(quads[i].fRect, viewMatrix);
363 
364         GrAAType resolvedAA;
365         GrQuadAAFlags resolvedEdgeFlags;
366         GrQuadUtils::ResolveAAType(aaType, quads[i].fAAFlags, deviceQuad,
367                                    &resolvedAA, &resolvedEdgeFlags);
368 
369         fillRects->addQuad(deviceQuad,
370                            GrQuad::MakeFromRect(quads[i].fRect, quads[i].fLocalMatrix),
371                            quads[i].fColor, resolvedEdgeFlags,resolvedAA);
372     }
373 
374     return op;
375 }
376 
377 } // namespace GrFillRectOp
378 
379 #if GR_TEST_UTILS
380 
381 #include "src/gpu/GrDrawOpTest.h"
382 #include "src/gpu/SkGr.h"
383 
GR_DRAW_OP_TEST_DEFINE(FillRectOp)384 GR_DRAW_OP_TEST_DEFINE(FillRectOp) {
385     SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
386     SkRect rect = GrTest::TestRect(random);
387 
388     GrAAType aaType = GrAAType::kNone;
389     if (random->nextBool()) {
390         aaType = (numSamples > 1) ? GrAAType::kMSAA : GrAAType::kCoverage;
391     }
392     const GrUserStencilSettings* stencil = random->nextBool() ? nullptr
393                                                               : GrGetRandomStencil(random, context);
394 
395     GrQuadAAFlags aaFlags = GrQuadAAFlags::kNone;
396     aaFlags |= random->nextBool() ? GrQuadAAFlags::kLeft : GrQuadAAFlags::kNone;
397     aaFlags |= random->nextBool() ? GrQuadAAFlags::kTop : GrQuadAAFlags::kNone;
398     aaFlags |= random->nextBool() ? GrQuadAAFlags::kRight : GrQuadAAFlags::kNone;
399     aaFlags |= random->nextBool() ? GrQuadAAFlags::kBottom : GrQuadAAFlags::kNone;
400 
401     if (random->nextBool()) {
402         if (random->nextBool()) {
403             if (random->nextBool()) {
404                 // Local matrix with a set op
405                 uint32_t extraQuadCt = random->nextRangeU(1, 4);
406                 SkTArray<GrRenderTargetContext::QuadSetEntry> quads(extraQuadCt + 1);
407                 quads.push_back(
408                         {rect, SkPMColor4f::FromBytes_RGBA(SkColorToPremulGrColor(random->nextU())),
409                          GrTest::TestMatrixInvertible(random), aaFlags});
410                 for (uint32_t i = 0; i < extraQuadCt; ++i) {
411                     GrQuadAAFlags aaFlags = GrQuadAAFlags::kNone;
412                     aaFlags |= random->nextBool() ? GrQuadAAFlags::kLeft : GrQuadAAFlags::kNone;
413                     aaFlags |= random->nextBool() ? GrQuadAAFlags::kTop : GrQuadAAFlags::kNone;
414                     aaFlags |= random->nextBool() ? GrQuadAAFlags::kRight : GrQuadAAFlags::kNone;
415                     aaFlags |= random->nextBool() ? GrQuadAAFlags::kBottom : GrQuadAAFlags::kNone;
416 
417                     quads.push_back(
418                         {GrTest::TestRect(random),
419                          SkPMColor4f::FromBytes_RGBA(SkColorToPremulGrColor(random->nextU())),
420                          GrTest::TestMatrixInvertible(random), aaFlags});
421                 }
422 
423                 return GrFillRectOp::MakeSet(context, std::move(paint), aaType, viewMatrix,
424                                              quads.begin(), quads.count(), stencil);
425             } else {
426                 // Single local matrix
427                 SkMatrix localMatrix = GrTest::TestMatrixInvertible(random);
428                 return GrFillRectOp::Make(context, std::move(paint), aaType, aaFlags,
429                                           GrQuad::MakeFromRect(rect, viewMatrix),
430                                           GrQuad::MakeFromRect(rect, localMatrix), stencil);
431             }
432         } else {
433             // Pass local rect directly
434             SkRect localRect = GrTest::TestRect(random);
435             return GrFillRectOp::Make(context, std::move(paint), aaType, aaFlags,
436                                       GrQuad::MakeFromRect(rect, viewMatrix),
437                                       GrQuad(localRect), stencil);
438         }
439     } else {
440         // The simplest constructor
441         return GrFillRectOp::Make(context, std::move(paint), aaType, aaFlags,
442                                   GrQuad::MakeFromRect(rect, viewMatrix),
443                                   GrQuad(rect), stencil);
444     }
445 }
446 
447 #endif
448