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/GrQuadPerEdgeAA.h"
9 
10 #include "include/private/SkVx.h"
11 #include "src/gpu/SkGr.h"
12 #include "src/gpu/geometry/GrQuadUtils.h"
13 #include "src/gpu/glsl/GrGLSLColorSpaceXformHelper.h"
14 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
15 #include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
16 #include "src/gpu/glsl/GrGLSLPrimitiveProcessor.h"
17 #include "src/gpu/glsl/GrGLSLVarying.h"
18 #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
19 
20 static_assert((int)GrQuadAAFlags::kLeft   == SkCanvas::kLeft_QuadAAFlag, "");
21 static_assert((int)GrQuadAAFlags::kTop    == SkCanvas::kTop_QuadAAFlag, "");
22 static_assert((int)GrQuadAAFlags::kRight  == SkCanvas::kRight_QuadAAFlag, "");
23 static_assert((int)GrQuadAAFlags::kBottom == SkCanvas::kBottom_QuadAAFlag, "");
24 static_assert((int)GrQuadAAFlags::kNone   == SkCanvas::kNone_QuadAAFlags, "");
25 static_assert((int)GrQuadAAFlags::kAll    == SkCanvas::kAll_QuadAAFlags, "");
26 
27 namespace {
28 
29 // Generic WriteQuadProc that can handle any VertexSpec. It writes the 4 vertices in triangle strip
30 // order, although the data per-vertex is dependent on the VertexSpec.
write_quad_generic(GrVertexWriter * vb,const GrQuadPerEdgeAA::VertexSpec & spec,const GrQuad * deviceQuad,const GrQuad * localQuad,const float coverage[4],const SkPMColor4f & color,const SkRect & geomDomain,const SkRect & texDomain)31 static void write_quad_generic(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
32                                const GrQuad* deviceQuad, const GrQuad* localQuad,
33                                const float coverage[4], const SkPMColor4f& color,
34                                const SkRect& geomDomain, const SkRect& texDomain) {
35     static constexpr auto If = GrVertexWriter::If<float>;
36 
37     SkASSERT(!spec.hasLocalCoords() || localQuad);
38 
39     GrQuadPerEdgeAA::CoverageMode mode = spec.coverageMode();
40     for (int i = 0; i < 4; ++i) {
41         // save position, this is a float2 or float3 or float4 depending on the combination of
42         // perspective and coverage mode.
43         vb->write(deviceQuad->x(i), deviceQuad->y(i),
44                   If(spec.deviceQuadType() == GrQuad::Type::kPerspective, deviceQuad->w(i)),
45                   If(mode == GrQuadPerEdgeAA::CoverageMode::kWithPosition, coverage[i]));
46 
47         // save color
48         if (spec.hasVertexColors()) {
49             bool wide = spec.colorType() == GrQuadPerEdgeAA::ColorType::kFloat;
50             vb->write(GrVertexColor(
51                 color * (mode == GrQuadPerEdgeAA::CoverageMode::kWithColor ? coverage[i] : 1.f),
52                 wide));
53         }
54 
55         // save local position
56         if (spec.hasLocalCoords()) {
57             vb->write(localQuad->x(i), localQuad->y(i),
58                       If(spec.localQuadType() == GrQuad::Type::kPerspective, localQuad->w(i)));
59         }
60 
61         // save the geometry domain
62         if (spec.requiresGeometryDomain()) {
63             vb->write(geomDomain);
64         }
65 
66         // save the texture domain
67         if (spec.hasDomain()) {
68             vb->write(texDomain);
69         }
70     }
71 }
72 
73 // Specialized WriteQuadProcs for particular VertexSpecs that show up frequently (determined
74 // experimentally through recorded GMs, SKPs, and SVGs, as well as SkiaRenderer's usage patterns):
75 
76 // 2D (XY), no explicit coverage, vertex color, no locals, no geometry domain, no texture domain
77 // This represents simple, solid color or shader, non-AA (or AA with cov. as alpha) rects.
write_2d_color(GrVertexWriter * vb,const GrQuadPerEdgeAA::VertexSpec & spec,const GrQuad * deviceQuad,const GrQuad * localQuad,const float coverage[4],const SkPMColor4f & color,const SkRect & geomDomain,const SkRect & texDomain)78 static void write_2d_color(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
79                            const GrQuad* deviceQuad, const GrQuad* localQuad,
80                            const float coverage[4], const SkPMColor4f& color,
81                            const SkRect& geomDomain, const SkRect& texDomain) {
82     // Assert assumptions about VertexSpec
83     SkASSERT(spec.deviceQuadType() != GrQuad::Type::kPerspective);
84     SkASSERT(!spec.hasLocalCoords());
85     SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kNone ||
86              spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kWithColor);
87     SkASSERT(spec.hasVertexColors());
88     SkASSERT(!spec.requiresGeometryDomain());
89     SkASSERT(!spec.hasDomain());
90     // We don't assert that localQuad == nullptr, since it is possible for GrFillRectOp to
91     // accumulate local coords conservatively (paint not trivial), and then after analysis realize
92     // the processors don't need local coordinates.
93 
94     bool wide = spec.colorType() == GrQuadPerEdgeAA::ColorType::kFloat;
95     for (int i = 0; i < 4; ++i) {
96         // If this is not coverage-with-alpha, make sure coverage == 1 so it doesn't do anything
97         SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kWithColor ||
98                  coverage[i] == 1.f);
99         vb->write(deviceQuad->x(i), deviceQuad->y(i), GrVertexColor(color * coverage[i], wide));
100     }
101 }
102 
103 // 2D (XY), no explicit coverage, UV locals, no color, no geometry domain, no texture domain
104 // This represents opaque, non AA, textured rects
write_2d_uv(GrVertexWriter * vb,const GrQuadPerEdgeAA::VertexSpec & spec,const GrQuad * deviceQuad,const GrQuad * localQuad,const float coverage[4],const SkPMColor4f & color,const SkRect & geomDomain,const SkRect & texDomain)105 static void write_2d_uv(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
106                         const GrQuad* deviceQuad, const GrQuad* localQuad,
107                         const float coverage[4], const SkPMColor4f& color,
108                         const SkRect& geomDomain, const SkRect& texDomain) {
109     // Assert assumptions about VertexSpec
110     SkASSERT(spec.deviceQuadType() != GrQuad::Type::kPerspective);
111     SkASSERT(spec.hasLocalCoords() && spec.localQuadType() != GrQuad::Type::kPerspective);
112     SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kNone);
113     SkASSERT(!spec.hasVertexColors());
114     SkASSERT(!spec.requiresGeometryDomain());
115     SkASSERT(!spec.hasDomain());
116     SkASSERT(localQuad);
117 
118     for (int i = 0; i < 4; ++i) {
119         vb->write(deviceQuad->x(i), deviceQuad->y(i), localQuad->x(i), localQuad->y(i));
120     }
121 }
122 
123 // 2D (XY), no explicit coverage, UV locals, vertex color, no geometry or texture domains
124 // This represents transparent, non AA (or AA with cov. as alpha), textured rects
write_2d_color_uv(GrVertexWriter * vb,const GrQuadPerEdgeAA::VertexSpec & spec,const GrQuad * deviceQuad,const GrQuad * localQuad,const float coverage[4],const SkPMColor4f & color,const SkRect & geomDomain,const SkRect & texDomain)125 static void write_2d_color_uv(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
126                               const GrQuad* deviceQuad, const GrQuad* localQuad,
127                               const float coverage[4], const SkPMColor4f& color,
128                               const SkRect& geomDomain, const SkRect& texDomain) {
129     // Assert assumptions about VertexSpec
130     SkASSERT(spec.deviceQuadType() != GrQuad::Type::kPerspective);
131     SkASSERT(spec.hasLocalCoords() && spec.localQuadType() != GrQuad::Type::kPerspective);
132     SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kNone ||
133              spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kWithColor);
134     SkASSERT(spec.hasVertexColors());
135     SkASSERT(!spec.requiresGeometryDomain());
136     SkASSERT(!spec.hasDomain());
137     SkASSERT(localQuad);
138 
139     bool wide = spec.colorType() == GrQuadPerEdgeAA::ColorType::kFloat;
140     for (int i = 0; i < 4; ++i) {
141         // If this is not coverage-with-alpha, make sure coverage == 1 so it doesn't do anything
142         SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kWithColor ||
143                  coverage[i] == 1.f);
144         vb->write(deviceQuad->x(i), deviceQuad->y(i), GrVertexColor(color * coverage[i], wide),
145                   localQuad->x(i), localQuad->y(i));
146     }
147 }
148 
149 // 2D (XY), explicit coverage, UV locals, no color, no geometry domain, no texture domain
150 // This represents opaque, AA, textured rects
write_2d_cov_uv(GrVertexWriter * vb,const GrQuadPerEdgeAA::VertexSpec & spec,const GrQuad * deviceQuad,const GrQuad * localQuad,const float coverage[4],const SkPMColor4f & color,const SkRect & geomDomain,const SkRect & texDomain)151 static void write_2d_cov_uv(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
152                             const GrQuad* deviceQuad, const GrQuad* localQuad,
153                             const float coverage[4], const SkPMColor4f& color,
154                             const SkRect& geomDomain, const SkRect& texDomain) {
155     // Assert assumptions about VertexSpec
156     SkASSERT(spec.deviceQuadType() != GrQuad::Type::kPerspective);
157     SkASSERT(spec.hasLocalCoords() && spec.localQuadType() != GrQuad::Type::kPerspective);
158     SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kWithPosition);
159     SkASSERT(!spec.hasVertexColors());
160     SkASSERT(!spec.requiresGeometryDomain());
161     SkASSERT(!spec.hasDomain());
162     SkASSERT(localQuad);
163 
164     for (int i = 0; i < 4; ++i) {
165         vb->write(deviceQuad->x(i), deviceQuad->y(i), coverage[i],
166                   localQuad->x(i), localQuad->y(i));
167     }
168 }
169 
170 // NOTE: The three _strict specializations below match the non-strict uv functions above, except
171 // that they also write the UV domain. These are included to benefit SkiaRenderer, which must make
172 // use of both fast and strict constrained domains. When testing _strict was not that common across
173 // GMS, SKPs, and SVGs but we have little visibility into actual SkiaRenderer statistics. If
174 // SkiaRenderer can avoid domains more, these 3 functions should probably be removed for simplicity.
175 
176 // 2D (XY), no explicit coverage, UV locals, no color, tex domain but no geometry domain
177 // This represents opaque, non AA, textured rects with strict uv sampling
write_2d_uv_strict(GrVertexWriter * vb,const GrQuadPerEdgeAA::VertexSpec & spec,const GrQuad * deviceQuad,const GrQuad * localQuad,const float coverage[4],const SkPMColor4f & color,const SkRect & geomDomain,const SkRect & texDomain)178 static void write_2d_uv_strict(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
179                                const GrQuad* deviceQuad, const GrQuad* localQuad,
180                                const float coverage[4], const SkPMColor4f& color,
181                                const SkRect& geomDomain, const SkRect& texDomain) {
182     // Assert assumptions about VertexSpec
183     SkASSERT(spec.deviceQuadType() != GrQuad::Type::kPerspective);
184     SkASSERT(spec.hasLocalCoords() && spec.localQuadType() != GrQuad::Type::kPerspective);
185     SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kNone);
186     SkASSERT(!spec.hasVertexColors());
187     SkASSERT(!spec.requiresGeometryDomain());
188     SkASSERT(spec.hasDomain());
189     SkASSERT(localQuad);
190 
191     for (int i = 0; i < 4; ++i) {
192         vb->write(deviceQuad->x(i), deviceQuad->y(i), localQuad->x(i), localQuad->y(i), texDomain);
193     }
194 }
195 
196 // 2D (XY), no explicit coverage, UV locals, vertex color, tex domain but no geometry domain
197 // This represents transparent, non AA (or AA with cov. as alpha), textured rects with strict sample
write_2d_color_uv_strict(GrVertexWriter * vb,const GrQuadPerEdgeAA::VertexSpec & spec,const GrQuad * deviceQuad,const GrQuad * localQuad,const float coverage[4],const SkPMColor4f & color,const SkRect & geomDomain,const SkRect & texDomain)198 static void write_2d_color_uv_strict(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
199                                      const GrQuad* deviceQuad, const GrQuad* localQuad,
200                                      const float coverage[4], const SkPMColor4f& color,
201                                      const SkRect& geomDomain, const SkRect& texDomain) {
202     // Assert assumptions about VertexSpec
203     SkASSERT(spec.deviceQuadType() != GrQuad::Type::kPerspective);
204     SkASSERT(spec.hasLocalCoords() && spec.localQuadType() != GrQuad::Type::kPerspective);
205     SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kNone ||
206              spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kWithColor);
207     SkASSERT(spec.hasVertexColors());
208     SkASSERT(!spec.requiresGeometryDomain());
209     SkASSERT(spec.hasDomain());
210     SkASSERT(localQuad);
211 
212     bool wide = spec.colorType() == GrQuadPerEdgeAA::ColorType::kFloat;
213     for (int i = 0; i < 4; ++i) {
214         // If this is not coverage-with-alpha, make sure coverage == 1 so it doesn't do anything
215         SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kWithColor ||
216                  coverage[i] == 1.f);
217         vb->write(deviceQuad->x(i), deviceQuad->y(i), GrVertexColor(color * coverage[i], wide),
218                   localQuad->x(i), localQuad->y(i), texDomain);
219     }
220 }
221 
222 // 2D (XY), explicit coverage, UV locals, no color, tex domain but no geometry domain
223 // This represents opaque, AA, textured rects with strict uv sampling
write_2d_cov_uv_strict(GrVertexWriter * vb,const GrQuadPerEdgeAA::VertexSpec & spec,const GrQuad * deviceQuad,const GrQuad * localQuad,const float coverage[4],const SkPMColor4f & color,const SkRect & geomDomain,const SkRect & texDomain)224 static void write_2d_cov_uv_strict(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
225                                    const GrQuad* deviceQuad, const GrQuad* localQuad,
226                                    const float coverage[4], const SkPMColor4f& color,
227                                    const SkRect& geomDomain, const SkRect& texDomain) {
228     // Assert assumptions about VertexSpec
229     SkASSERT(spec.deviceQuadType() != GrQuad::Type::kPerspective);
230     SkASSERT(spec.hasLocalCoords() && spec.localQuadType() != GrQuad::Type::kPerspective);
231     SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kWithPosition);
232     SkASSERT(!spec.hasVertexColors());
233     SkASSERT(!spec.requiresGeometryDomain());
234     SkASSERT(spec.hasDomain());
235     SkASSERT(localQuad);
236 
237     for (int i = 0; i < 4; ++i) {
238         vb->write(deviceQuad->x(i), deviceQuad->y(i), coverage[i],
239                   localQuad->x(i), localQuad->y(i), texDomain);
240     }
241 }
242 
243 } // anonymous namespace
244 
245 namespace GrQuadPerEdgeAA {
246 
CalcIndexBufferOption(GrAAType aa,int numQuads)247 IndexBufferOption CalcIndexBufferOption(GrAAType aa, int numQuads) {
248     if (aa == GrAAType::kCoverage) {
249         return IndexBufferOption::kPictureFramed;
250     } else if (numQuads > 1) {
251         return IndexBufferOption::kIndexedRects;
252     } else {
253         return IndexBufferOption::kTriStrips;
254     }
255 }
256 
257 // This is a more elaborate version of fitsInBytes() that allows "no color" for white
MinColorType(SkPMColor4f color)258 ColorType MinColorType(SkPMColor4f color) {
259     if (color == SK_PMColor4fWHITE) {
260         return ColorType::kNone;
261     } else {
262         return color.fitsInBytes() ? ColorType::kByte : ColorType::kFloat;
263     }
264 }
265 
266 ////////////////// Tessellator Implementation
267 
GetWriteQuadProc(const VertexSpec & spec)268 Tessellator::WriteQuadProc Tessellator::GetWriteQuadProc(const VertexSpec& spec) {
269     // All specialized writing functions requires 2D geometry and no geometry domain. This is not
270     // the same as just checking device type vs. kRectilinear since non-AA general 2D quads do not
271     // require a geometry domain and could then go through a fast path.
272     if (spec.deviceQuadType() != GrQuad::Type::kPerspective && !spec.requiresGeometryDomain()) {
273         CoverageMode mode = spec.coverageMode();
274         if (spec.hasVertexColors()) {
275             if (mode != CoverageMode::kWithPosition) {
276                 // Vertex colors, but no explicit coverage
277                 if (!spec.hasLocalCoords()) {
278                     // Non-UV with vertex colors (possibly with coverage folded into alpha)
279                     return write_2d_color;
280                 } else if (spec.localQuadType() != GrQuad::Type::kPerspective) {
281                     // UV locals with vertex colors (possibly with coverage-as-alpha)
282                     return spec.hasDomain() ? write_2d_color_uv_strict : write_2d_color_uv;
283                 }
284             }
285             // Else fall through; this is a spec that requires vertex colors and explicit coverage,
286             // which means it's anti-aliased and the FPs don't support coverage as alpha, or
287             // it uses 3D local coordinates.
288         } else if (spec.hasLocalCoords() && spec.localQuadType() != GrQuad::Type::kPerspective) {
289             if (mode == CoverageMode::kWithPosition) {
290                 // UV locals with explicit coverage
291                 return spec.hasDomain() ? write_2d_cov_uv_strict : write_2d_cov_uv;
292             } else {
293                 SkASSERT(mode == CoverageMode::kNone);
294                 return spec.hasDomain() ? write_2d_uv_strict : write_2d_uv;
295             }
296         }
297         // Else fall through to generic vertex function; this is a spec that has no vertex colors
298         // and [no|uvr] local coords, which doesn't happen often enough to warrant specialization.
299     }
300 
301     // Arbitrary spec hits the slow path
302     return write_quad_generic;
303 }
304 
Tessellator(const VertexSpec & spec,char * vertices)305 Tessellator::Tessellator(const VertexSpec& spec, char* vertices)
306         : fVertexSpec(spec)
307         , fVertexWriter{vertices}
308         , fWriteProc(Tessellator::GetWriteQuadProc(spec)) {}
309 
append(GrQuad * deviceQuad,GrQuad * localQuad,const SkPMColor4f & color,const SkRect & uvDomain,GrQuadAAFlags aaFlags)310 void Tessellator::append(GrQuad* deviceQuad, GrQuad* localQuad,
311                          const SkPMColor4f& color, const SkRect& uvDomain, GrQuadAAFlags aaFlags) {
312     // We allow Tessellator to be created with a null vertices pointer for convenience, but it is
313     // assumed it will never actually be used in those cases.
314     SkASSERT(fVertexWriter.fPtr);
315     SkASSERT(deviceQuad->quadType() <= fVertexSpec.deviceQuadType());
316     SkASSERT(localQuad || !fVertexSpec.hasLocalCoords());
317     SkASSERT(!fVertexSpec.hasLocalCoords() || localQuad->quadType() <= fVertexSpec.localQuadType());
318 
319     static const float kFullCoverage[4] = {1.f, 1.f, 1.f, 1.f};
320     static const float kZeroCoverage[4] = {0.f, 0.f, 0.f, 0.f};
321     static const SkRect kIgnoredDomain = SkRect::MakeEmpty();
322 
323     if (fVertexSpec.usesCoverageAA()) {
324         SkASSERT(fVertexSpec.coverageMode() == CoverageMode::kWithColor ||
325                  fVertexSpec.coverageMode() == CoverageMode::kWithPosition);
326         // Must calculate inner and outer quadrilaterals for the vertex coverage ramps, and possibly
327         // a geometry domain if corners are not right angles
328         SkRect geomDomain;
329         if (fVertexSpec.requiresGeometryDomain()) {
330             geomDomain = deviceQuad->bounds();
331             geomDomain.outset(0.5f, 0.5f); // account for AA expansion
332         }
333 
334         if (aaFlags == GrQuadAAFlags::kNone) {
335             // Have to write the coverage AA vertex structure, but there's no math to be done for a
336             // non-aa quad batched into a coverage AA op.
337             fWriteProc(&fVertexWriter, fVertexSpec, deviceQuad, localQuad, kFullCoverage, color,
338                        geomDomain, uvDomain);
339             // Since we pass the same corners in, the outer vertex structure will have 0 area and
340             // the coverage interpolation from 1 to 0 will not be visible.
341             fWriteProc(&fVertexWriter, fVertexSpec, deviceQuad, localQuad, kZeroCoverage, color,
342                        geomDomain, uvDomain);
343         } else {
344             // Reset the tessellation helper to match the current geometry
345             fAAHelper.reset(*deviceQuad, localQuad);
346 
347             // Edge inset/outset distance ordered LBTR, set to 0.5 for a half pixel if the AA flag
348             // is turned on, or 0.0 if the edge is not anti-aliased.
349             skvx::Vec<4, float> edgeDistances;
350             if (aaFlags == GrQuadAAFlags::kAll) {
351                 edgeDistances = 0.5f;
352             } else {
353                 edgeDistances = { (aaFlags & GrQuadAAFlags::kLeft)   ? 0.5f : 0.f,
354                                   (aaFlags & GrQuadAAFlags::kBottom) ? 0.5f : 0.f,
355                                   (aaFlags & GrQuadAAFlags::kTop)    ? 0.5f : 0.f,
356                                   (aaFlags & GrQuadAAFlags::kRight)  ? 0.5f : 0.f };
357             }
358 
359             // Write inner vertices first
360             float coverage[4];
361             fAAHelper.inset(edgeDistances, deviceQuad, localQuad).store(coverage);
362             fWriteProc(&fVertexWriter, fVertexSpec, deviceQuad, localQuad, coverage, color,
363                        geomDomain, uvDomain);
364 
365             // Then outer vertices, which use 0.f for their coverage
366             fAAHelper.outset(edgeDistances, deviceQuad, localQuad);
367             fWriteProc(&fVertexWriter, fVertexSpec, deviceQuad, localQuad, kZeroCoverage, color,
368                        geomDomain, uvDomain);
369         }
370     } else {
371         // No outsetting needed, just write a single quad with full coverage
372         SkASSERT(fVertexSpec.coverageMode() == CoverageMode::kNone &&
373                  !fVertexSpec.requiresGeometryDomain());
374         fWriteProc(&fVertexWriter, fVertexSpec, deviceQuad, localQuad, kFullCoverage, color,
375                    kIgnoredDomain, uvDomain);
376     }
377 }
378 
GetIndexBuffer(GrMeshDrawOp::Target * target,IndexBufferOption indexBufferOption)379 sk_sp<const GrBuffer> GetIndexBuffer(GrMeshDrawOp::Target* target,
380                                      IndexBufferOption indexBufferOption) {
381     auto resourceProvider = target->resourceProvider();
382 
383     switch (indexBufferOption) {
384         case IndexBufferOption::kPictureFramed: return resourceProvider->refAAQuadIndexBuffer();
385         case IndexBufferOption::kIndexedRects:  return resourceProvider->refNonAAQuadIndexBuffer();
386         case IndexBufferOption::kTriStrips:     // fall through
387         default:                                return nullptr;
388     }
389 }
390 
QuadLimit(IndexBufferOption option)391 int QuadLimit(IndexBufferOption option) {
392     switch (option) {
393         case IndexBufferOption::kPictureFramed: return GrResourceProvider::MaxNumAAQuads();
394         case IndexBufferOption::kIndexedRects:  return GrResourceProvider::MaxNumNonAAQuads();
395         case IndexBufferOption::kTriStrips:     return SK_MaxS32; // not limited by an indexBuffer
396     }
397 
398     SkUNREACHABLE;
399 }
400 
IssueDraw(const GrCaps & caps,GrOpsRenderPass * renderPass,const VertexSpec & spec,int runningQuadCount,int quadsInDraw,int maxVerts,int absVertBufferOffset)401 void IssueDraw(const GrCaps& caps, GrOpsRenderPass* renderPass, const VertexSpec& spec,
402                int runningQuadCount, int quadsInDraw, int maxVerts, int absVertBufferOffset) {
403     if (spec.indexBufferOption() == IndexBufferOption::kTriStrips) {
404         int offset = absVertBufferOffset +
405                                     runningQuadCount * GrResourceProvider::NumVertsPerNonAAQuad();
406         renderPass->draw(4, offset);
407         return;
408     }
409 
410     SkASSERT(spec.indexBufferOption() == IndexBufferOption::kPictureFramed ||
411              spec.indexBufferOption() == IndexBufferOption::kIndexedRects);
412 
413     int maxNumQuads, numIndicesPerQuad, numVertsPerQuad;
414 
415     if (spec.indexBufferOption() == IndexBufferOption::kPictureFramed) {
416         // AA uses 8 vertices and 30 indices per quad, basically nested rectangles
417         maxNumQuads = GrResourceProvider::MaxNumAAQuads();
418         numIndicesPerQuad = GrResourceProvider::NumIndicesPerAAQuad();
419         numVertsPerQuad = GrResourceProvider::NumVertsPerAAQuad();
420     } else {
421         // Non-AA uses 4 vertices and 6 indices per quad
422         maxNumQuads = GrResourceProvider::MaxNumNonAAQuads();
423         numIndicesPerQuad = GrResourceProvider::NumIndicesPerNonAAQuad();
424         numVertsPerQuad = GrResourceProvider::NumVertsPerNonAAQuad();
425     }
426 
427     SkASSERT(runningQuadCount + quadsInDraw <= maxNumQuads);
428 
429     if (caps.avoidLargeIndexBufferDraws()) {
430         // When we need to avoid large index buffer draws we modify the base vertex of the draw
431         // which, in GL, requires rebinding all vertex attrib arrays, so a base index is generally
432         // preferred.
433         int offset = absVertBufferOffset + runningQuadCount * numVertsPerQuad;
434 
435         renderPass->drawIndexPattern(numIndicesPerQuad, quadsInDraw, maxNumQuads, numVertsPerQuad,
436                                      offset);
437     } else {
438         int baseIndex = runningQuadCount * numIndicesPerQuad;
439         int numIndicesToDraw = quadsInDraw * numIndicesPerQuad;
440 
441         int minVertex = runningQuadCount * numVertsPerQuad;
442         int maxVertex = (runningQuadCount + quadsInDraw) * numVertsPerQuad;
443 
444         renderPass->drawIndexed(numIndicesToDraw, baseIndex, minVertex, maxVertex,
445                                 absVertBufferOffset);
446     }
447 }
448 
449 ////////////////// VertexSpec Implementation
450 
deviceDimensionality() const451 int VertexSpec::deviceDimensionality() const {
452     return this->deviceQuadType() == GrQuad::Type::kPerspective ? 3 : 2;
453 }
454 
localDimensionality() const455 int VertexSpec::localDimensionality() const {
456     return fHasLocalCoords ? (this->localQuadType() == GrQuad::Type::kPerspective ? 3 : 2) : 0;
457 }
458 
coverageMode() const459 CoverageMode VertexSpec::coverageMode() const {
460     if (this->usesCoverageAA()) {
461         if (this->compatibleWithCoverageAsAlpha() && this->hasVertexColors() &&
462             !this->requiresGeometryDomain()) {
463             // Using a geometric domain acts as a second source of coverage and folding
464             // the original coverage into color makes it impossible to apply the color's
465             // alpha to the geometric domain's coverage when the original shape is clipped.
466             return CoverageMode::kWithColor;
467         } else {
468             return CoverageMode::kWithPosition;
469         }
470     } else {
471         return CoverageMode::kNone;
472     }
473 }
474 
475 // This needs to stay in sync w/ QuadPerEdgeAAGeometryProcessor::initializeAttrs
vertexSize() const476 size_t VertexSpec::vertexSize() const {
477     bool needsPerspective = (this->deviceDimensionality() == 3);
478     CoverageMode coverageMode = this->coverageMode();
479 
480     size_t count = 0;
481 
482     if (coverageMode == CoverageMode::kWithPosition) {
483         if (needsPerspective) {
484             count += GrVertexAttribTypeSize(kFloat4_GrVertexAttribType);
485         } else {
486             count += GrVertexAttribTypeSize(kFloat2_GrVertexAttribType) +
487                      GrVertexAttribTypeSize(kFloat_GrVertexAttribType);
488         }
489     } else {
490         if (needsPerspective) {
491             count += GrVertexAttribTypeSize(kFloat3_GrVertexAttribType);
492         } else {
493             count += GrVertexAttribTypeSize(kFloat2_GrVertexAttribType);
494         }
495     }
496 
497     if (this->requiresGeometryDomain()) {
498         count += GrVertexAttribTypeSize(kFloat4_GrVertexAttribType);
499     }
500 
501     count += this->localDimensionality() * GrVertexAttribTypeSize(kFloat_GrVertexAttribType);
502 
503     if (ColorType::kByte == this->colorType()) {
504         count += GrVertexAttribTypeSize(kUByte4_norm_GrVertexAttribType);
505     } else if (ColorType::kFloat == this->colorType()) {
506         count += GrVertexAttribTypeSize(kFloat4_GrVertexAttribType);
507     }
508 
509     if (this->hasDomain()) {
510         count += GrVertexAttribTypeSize(kFloat4_GrVertexAttribType);
511     }
512 
513     return count;
514 }
515 
516 ////////////////// Geometry Processor Implementation
517 
518 class QuadPerEdgeAAGeometryProcessor : public GrGeometryProcessor {
519 public:
520     using Saturate = GrTextureOp::Saturate;
521 
Make(SkArenaAlloc * arena,const VertexSpec & spec)522     static GrGeometryProcessor* Make(SkArenaAlloc* arena, const VertexSpec& spec) {
523         return arena->make<QuadPerEdgeAAGeometryProcessor>(spec);
524     }
525 
Make(SkArenaAlloc * arena,const VertexSpec & vertexSpec,const GrShaderCaps & caps,const GrBackendFormat & backendFormat,GrSamplerState samplerState,const GrSwizzle & swizzle,sk_sp<GrColorSpaceXform> textureColorSpaceXform,Saturate saturate)526     static GrGeometryProcessor* Make(SkArenaAlloc* arena,
527                                      const VertexSpec& vertexSpec,
528                                      const GrShaderCaps& caps,
529                                      const GrBackendFormat& backendFormat,
530                                      GrSamplerState samplerState,
531                                      const GrSwizzle& swizzle,
532                                      sk_sp<GrColorSpaceXform> textureColorSpaceXform,
533                                      Saturate saturate) {
534         return arena->make<QuadPerEdgeAAGeometryProcessor>(
535                 vertexSpec, caps, backendFormat, samplerState, swizzle,
536                 std::move(textureColorSpaceXform), saturate);
537     }
538 
name() const539     const char* name() const override { return "QuadPerEdgeAAGeometryProcessor"; }
540 
getGLSLProcessorKey(const GrShaderCaps &,GrProcessorKeyBuilder * b) const541     void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
542         // texturing, device-dimensions are single bit flags
543         uint32_t x = (fTexDomain.isInitialized() ? 0 : 0x1)
544                    | (fSampler.isInitialized()   ? 0 : 0x2)
545                    | (fNeedsPerspective          ? 0 : 0x4)
546                    | (fSaturate == Saturate::kNo ? 0 : 0x8);
547         // local coords require 2 bits (3 choices), 00 for none, 01 for 2d, 10 for 3d
548         if (fLocalCoord.isInitialized()) {
549             x |= kFloat3_GrVertexAttribType == fLocalCoord.cpuType() ? 0x10 : 0x20;
550         }
551         // similar for colors, 00 for none, 01 for bytes, 10 for half-floats
552         if (fColor.isInitialized()) {
553             x |= kUByte4_norm_GrVertexAttribType == fColor.cpuType() ? 0x40 : 0x80;
554         }
555         // and coverage mode, 00 for none, 01 for withposition, 10 for withcolor, 11 for
556         // position+geomdomain
557         SkASSERT(!fGeomDomain.isInitialized() || fCoverageMode == CoverageMode::kWithPosition);
558         if (fCoverageMode != CoverageMode::kNone) {
559             x |= fGeomDomain.isInitialized()
560                          ? 0x300
561                          : (CoverageMode::kWithPosition == fCoverageMode ? 0x100 : 0x200);
562         }
563 
564         b->add32(GrColorSpaceXform::XformKey(fTextureColorSpaceXform.get()));
565         b->add32(x);
566     }
567 
createGLSLInstance(const GrShaderCaps & caps) const568     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override {
569         class GLSLProcessor : public GrGLSLGeometryProcessor {
570         public:
571             void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
572                          const CoordTransformRange& transformRange) override {
573                 const auto& gp = proc.cast<QuadPerEdgeAAGeometryProcessor>();
574                 this->setTransformDataHelper(SkMatrix::I(), pdman, transformRange);
575                 fTextureColorSpaceXformHelper.setData(pdman, gp.fTextureColorSpaceXform.get());
576             }
577 
578         private:
579             void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
580                 using Interpolation = GrGLSLVaryingHandler::Interpolation;
581 
582                 const auto& gp = args.fGP.cast<QuadPerEdgeAAGeometryProcessor>();
583                 fTextureColorSpaceXformHelper.emitCode(args.fUniformHandler,
584                                                        gp.fTextureColorSpaceXform.get());
585 
586                 args.fVaryingHandler->emitAttributes(gp);
587 
588                 if (gp.fCoverageMode == CoverageMode::kWithPosition) {
589                     // Strip last channel from the vertex attribute to remove coverage and get the
590                     // actual position
591                     if (gp.fNeedsPerspective) {
592                         args.fVertBuilder->codeAppendf("float3 position = %s.xyz;",
593                                                        gp.fPosition.name());
594                     } else {
595                         args.fVertBuilder->codeAppendf("float2 position = %s.xy;",
596                                                        gp.fPosition.name());
597                     }
598                     gpArgs->fPositionVar = {"position",
599                                             gp.fNeedsPerspective ? kFloat3_GrSLType
600                                                                  : kFloat2_GrSLType,
601                                             GrShaderVar::TypeModifier::None};
602                 } else {
603                     // No coverage to eliminate
604                     gpArgs->fPositionVar = gp.fPosition.asShaderVar();
605                 }
606 
607                 // Handle local coordinates if they exist. This is required even when the op
608                 // isn't providing local coords but there are FPs called with explicit coords.
609                 // It installs the uniforms that transform their coordinates in the fragment
610                 // shader.
611                 // NOTE: If the only usage of local coordinates is for the inline texture fetch
612                 // before FPs, then there are no registered FPCoordTransforms and this ends up
613                 // emitting nothing, so there isn't a duplication of local coordinates
614                 this->emitTransforms(args.fVertBuilder,
615                                      args.fVaryingHandler,
616                                      args.fUniformHandler,
617                                      gp.fLocalCoord.asShaderVar(),
618                                      args.fFPCoordTransformHandler);
619 
620                 // Solid color before any texturing gets modulated in
621                 if (gp.fColor.isInitialized()) {
622                     SkASSERT(gp.fCoverageMode != CoverageMode::kWithColor || !gp.fNeedsPerspective);
623                     // The color cannot be flat if the varying coverage has been modulated into it
624                     args.fVaryingHandler->addPassThroughAttribute(gp.fColor, args.fOutputColor,
625                             gp.fCoverageMode == CoverageMode::kWithColor ?
626                             Interpolation::kInterpolated : Interpolation::kCanBeFlat);
627                 } else {
628                     // Output color must be initialized to something
629                     args.fFragBuilder->codeAppendf("%s = half4(1);", args.fOutputColor);
630                 }
631 
632                 // If there is a texture, must also handle texture coordinates and reading from
633                 // the texture in the fragment shader before continuing to fragment processors.
634                 if (gp.fSampler.isInitialized()) {
635                     // Texture coordinates clamped by the domain on the fragment shader; if the GP
636                     // has a texture, it's guaranteed to have local coordinates
637                     args.fFragBuilder->codeAppend("float2 texCoord;");
638                     if (gp.fLocalCoord.cpuType() == kFloat3_GrVertexAttribType) {
639                         // Can't do a pass through since we need to perform perspective division
640                         GrGLSLVarying v(gp.fLocalCoord.gpuType());
641                         args.fVaryingHandler->addVarying(gp.fLocalCoord.name(), &v);
642                         args.fVertBuilder->codeAppendf("%s = %s;",
643                                                        v.vsOut(), gp.fLocalCoord.name());
644                         args.fFragBuilder->codeAppendf("texCoord = %s.xy / %s.z;",
645                                                        v.fsIn(), v.fsIn());
646                     } else {
647                         args.fVaryingHandler->addPassThroughAttribute(gp.fLocalCoord, "texCoord");
648                     }
649 
650                     // Clamp the now 2D localCoordName variable by the domain if it is provided
651                     if (gp.fTexDomain.isInitialized()) {
652                         args.fFragBuilder->codeAppend("float4 domain;");
653                         args.fVaryingHandler->addPassThroughAttribute(gp.fTexDomain, "domain",
654                                                                       Interpolation::kCanBeFlat);
655                         args.fFragBuilder->codeAppend(
656                                 "texCoord = clamp(texCoord, domain.xy, domain.zw);");
657                     }
658 
659                     // Now modulate the starting output color by the texture lookup
660                     args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor);
661                     args.fFragBuilder->appendTextureLookupAndBlend(
662                             args.fOutputColor, SkBlendMode::kModulate, args.fTexSamplers[0],
663                             "texCoord", &fTextureColorSpaceXformHelper);
664                     args.fFragBuilder->codeAppend(";");
665                     if (gp.fSaturate == Saturate::kYes) {
666                         args.fFragBuilder->codeAppendf("%s = saturate(%s);",
667                                                        args.fOutputColor, args.fOutputColor);
668                     }
669                 } else {
670                     // Saturate is only intended for use with a proxy to account for the fact
671                     // that GrTextureOp skips SkPaint conversion, which normally handles this.
672                     SkASSERT(gp.fSaturate == Saturate::kNo);
673                 }
674 
675                 // And lastly, output the coverage calculation code
676                 if (gp.fCoverageMode == CoverageMode::kWithPosition) {
677                     GrGLSLVarying coverage(kFloat_GrSLType);
678                     args.fVaryingHandler->addVarying("coverage", &coverage);
679                     if (gp.fNeedsPerspective) {
680                         // Multiply by "W" in the vertex shader, then by 1/w (sk_FragCoord.w) in
681                         // the fragment shader to get screen-space linear coverage.
682                         args.fVertBuilder->codeAppendf("%s = %s.w * %s.z;",
683                                                        coverage.vsOut(), gp.fPosition.name(),
684                                                        gp.fPosition.name());
685                         args.fFragBuilder->codeAppendf("float coverage = %s * sk_FragCoord.w;",
686                                                         coverage.fsIn());
687                     } else {
688                         args.fVertBuilder->codeAppendf("%s = %s;",
689                                                        coverage.vsOut(), gp.fCoverage.name());
690                         args.fFragBuilder->codeAppendf("float coverage = %s;", coverage.fsIn());
691                     }
692 
693                     if (gp.fGeomDomain.isInitialized()) {
694                         // Calculate distance from sk_FragCoord to the 4 edges of the domain
695                         // and clamp them to (0, 1). Use the minimum of these and the original
696                         // coverage. This only has to be done in the exterior triangles, the
697                         // interior of the quad geometry can never be clipped by the domain box.
698                         args.fFragBuilder->codeAppend("float4 geoDomain;");
699                         args.fVaryingHandler->addPassThroughAttribute(gp.fGeomDomain, "geoDomain",
700                                         Interpolation::kCanBeFlat);
701                         args.fFragBuilder->codeAppend(
702                                 "if (coverage < 0.5) {"
703                                 "   float4 dists4 = clamp(float4(1, 1, -1, -1) * "
704                                         "(sk_FragCoord.xyxy - geoDomain), 0, 1);"
705                                 "   float2 dists2 = dists4.xy * dists4.zw;"
706                                 "   coverage = min(coverage, dists2.x * dists2.y);"
707                                 "}");
708                     }
709 
710                     args.fFragBuilder->codeAppendf("%s = half4(half(coverage));",
711                                                    args.fOutputCoverage);
712                 } else {
713                     // Set coverage to 1, since it's either non-AA or the coverage was already
714                     // folded into the output color
715                     SkASSERT(!gp.fGeomDomain.isInitialized());
716                     args.fFragBuilder->codeAppendf("%s = half4(1);", args.fOutputCoverage);
717                 }
718             }
719             GrGLSLColorSpaceXformHelper fTextureColorSpaceXformHelper;
720         };
721         return new GLSLProcessor;
722     }
723 
724 private:
725     friend class ::SkArenaAlloc; // for access to ctor
726 
QuadPerEdgeAAGeometryProcessor(const VertexSpec & spec)727     QuadPerEdgeAAGeometryProcessor(const VertexSpec& spec)
728             : INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID)
729             , fTextureColorSpaceXform(nullptr) {
730         SkASSERT(!spec.hasDomain());
731         this->initializeAttrs(spec);
732         this->setTextureSamplerCnt(0);
733     }
734 
QuadPerEdgeAAGeometryProcessor(const VertexSpec & spec,const GrShaderCaps & caps,const GrBackendFormat & backendFormat,GrSamplerState samplerState,const GrSwizzle & swizzle,sk_sp<GrColorSpaceXform> textureColorSpaceXform,Saturate saturate)735     QuadPerEdgeAAGeometryProcessor(const VertexSpec& spec,
736                                    const GrShaderCaps& caps,
737                                    const GrBackendFormat& backendFormat,
738                                    GrSamplerState samplerState,
739                                    const GrSwizzle& swizzle,
740                                    sk_sp<GrColorSpaceXform> textureColorSpaceXform,
741                                    Saturate saturate)
742             : INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID)
743             , fSaturate(saturate)
744             , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
745             , fSampler(samplerState, backendFormat, swizzle) {
746         SkASSERT(spec.hasLocalCoords());
747         this->initializeAttrs(spec);
748         this->setTextureSamplerCnt(1);
749     }
750 
751     // This needs to stay in sync w/ VertexSpec::vertexSize
initializeAttrs(const VertexSpec & spec)752     void initializeAttrs(const VertexSpec& spec) {
753         fNeedsPerspective = spec.deviceDimensionality() == 3;
754         fCoverageMode = spec.coverageMode();
755 
756         if (fCoverageMode == CoverageMode::kWithPosition) {
757             if (fNeedsPerspective) {
758                 fPosition = {"positionWithCoverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
759             } else {
760                 fPosition = {"position", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
761                 fCoverage = {"coverage", kFloat_GrVertexAttribType, kFloat_GrSLType};
762             }
763         } else {
764             if (fNeedsPerspective) {
765                 fPosition = {"position", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
766             } else {
767                 fPosition = {"position", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
768             }
769         }
770 
771         // Need a geometry domain when the quads are AA and not rectilinear, since their AA
772         // outsetting can go beyond a half pixel.
773         if (spec.requiresGeometryDomain()) {
774             fGeomDomain = {"geomDomain", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
775         }
776 
777         int localDim = spec.localDimensionality();
778         if (localDim == 3) {
779             fLocalCoord = {"localCoord", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
780         } else if (localDim == 2) {
781             fLocalCoord = {"localCoord", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
782         } // else localDim == 0 and attribute remains uninitialized
783 
784         if (spec.hasVertexColors()) {
785             fColor = MakeColorAttribute("color", ColorType::kFloat == spec.colorType());
786         }
787 
788         if (spec.hasDomain()) {
789             fTexDomain = {"texDomain", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
790         }
791 
792         this->setVertexAttributes(&fPosition, 6);
793     }
794 
onTextureSampler(int) const795     const TextureSampler& onTextureSampler(int) const override { return fSampler; }
796 
797     Attribute fPosition; // May contain coverage as last channel
798     Attribute fCoverage; // Used for non-perspective position to avoid Intel Metal issues
799     Attribute fColor; // May have coverage modulated in if the FPs support it
800     Attribute fLocalCoord;
801     Attribute fGeomDomain; // Screen-space bounding box on geometry+aa outset
802     Attribute fTexDomain; // Texture-space bounding box on local coords
803 
804     // The positions attribute may have coverage built into it, so float3 is an ambiguous type
805     // and may mean 2d with coverage, or 3d with no coverage
806     bool fNeedsPerspective;
807     // Should saturate() be called on the color? Only relevant when created with a texture.
808     Saturate fSaturate = Saturate::kNo;
809     CoverageMode fCoverageMode;
810 
811     // Color space will be null and fSampler.isInitialized() returns false when the GP is configured
812     // to skip texturing.
813     sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
814     TextureSampler fSampler;
815 
816     typedef GrGeometryProcessor INHERITED;
817 };
818 
MakeProcessor(SkArenaAlloc * arena,const VertexSpec & spec)819 GrGeometryProcessor* MakeProcessor(SkArenaAlloc* arena, const VertexSpec& spec) {
820     return QuadPerEdgeAAGeometryProcessor::Make(arena, spec);
821 }
822 
MakeTexturedProcessor(SkArenaAlloc * arena,const VertexSpec & spec,const GrShaderCaps & caps,const GrBackendFormat & backendFormat,GrSamplerState samplerState,const GrSwizzle & swizzle,sk_sp<GrColorSpaceXform> textureColorSpaceXform,Saturate saturate)823 GrGeometryProcessor* MakeTexturedProcessor(SkArenaAlloc* arena,
824                                            const VertexSpec& spec,
825                                            const GrShaderCaps& caps,
826                                            const GrBackendFormat& backendFormat,
827                                            GrSamplerState samplerState,
828                                            const GrSwizzle& swizzle,
829                                            sk_sp<GrColorSpaceXform> textureColorSpaceXform,
830                                            Saturate saturate) {
831     return QuadPerEdgeAAGeometryProcessor::Make(arena, spec, caps, backendFormat, samplerState,
832                                                 swizzle, std::move(textureColorSpaceXform),
833                                                 saturate);
834 }
835 
836 } // namespace GrQuadPerEdgeAA
837