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