blob: 332a64c6643b4ceeffdc10e4019f70f802586e7f [file] [log] [blame]
Chris Dalton1a325d22017-07-14 15:17:41 -06001/*
2 * Copyright 2017 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 "GrCCPRCoverageProcessor.h"
9
Robert Phillips2890fbf2017-07-26 15:48:41 -040010#include "GrRenderTargetProxy.h"
Chris Dalton1a325d22017-07-14 15:17:41 -060011#include "ccpr/GrCCPRTriangleProcessor.h"
12#include "ccpr/GrCCPRQuadraticProcessor.h"
13#include "ccpr/GrCCPRCubicProcessor.h"
14#include "glsl/GrGLSLFragmentShaderBuilder.h"
15#include "glsl/GrGLSLGeometryShaderBuilder.h"
16#include "glsl/GrGLSLProgramBuilder.h"
17#include "glsl/GrGLSLVertexShaderBuilder.h"
18
19const char* GrCCPRCoverageProcessor::GetProcessorName(Mode mode) {
20 switch (mode) {
21 case Mode::kTriangleHulls:
22 return "GrCCPRTriangleHullAndEdgeProcessor (hulls)";
23 case Mode::kTriangleEdges:
24 return "GrCCPRTriangleHullAndEdgeProcessor (edges)";
25 case Mode::kCombinedTriangleHullsAndEdges:
26 return "GrCCPRTriangleHullAndEdgeProcessor (combined hulls & edges)";
27 case Mode::kTriangleCorners:
28 return "GrCCPRTriangleCornerProcessor";
29 case Mode::kQuadraticHulls:
30 return "GrCCPRQuadraticHullProcessor";
31 case Mode::kQuadraticFlatEdges:
32 return "GrCCPRQuadraticSharedEdgeProcessor";
33 case Mode::kSerpentineInsets:
34 return "GrCCPRCubicInsetProcessor (serpentine)";
35 case Mode::kSerpentineBorders:
36 return "GrCCPRCubicBorderProcessor (serpentine)";
37 case Mode::kLoopInsets:
38 return "GrCCPRCubicInsetProcessor (loop)";
39 case Mode::kLoopBorders:
40 return "GrCCPRCubicBorderProcessor (loop)";
41 }
42 SkFAIL("Unexpected ccpr coverage processor mode.");
43 return nullptr;
44}
45
46GrCCPRCoverageProcessor::GrCCPRCoverageProcessor(Mode mode, GrBuffer* pointsBuffer)
47 : fMode(mode)
48 , fInstanceAttrib(this->addInstanceAttrib("instance", kVec4i_GrVertexAttribType,
49 kHigh_GrSLPrecision)) {
50 fPointsBufferAccess.reset(kRG_float_GrPixelConfig, pointsBuffer, kVertex_GrShaderFlag);
51 this->addBufferAccess(&fPointsBufferAccess);
52
53 this->setWillUseGeoShader();
54
55 this->initClassID<GrCCPRCoverageProcessor>();
56}
57
58void GrCCPRCoverageProcessor::getGLSLProcessorKey(const GrShaderCaps&,
59 GrProcessorKeyBuilder* b) const {
60 b->add32(int(fMode));
61}
62
63GrGLSLPrimitiveProcessor* GrCCPRCoverageProcessor::createGLSLInstance(const GrShaderCaps&) const {
64 switch (fMode) {
65 using GeometryType = GrCCPRTriangleHullAndEdgeProcessor::GeometryType;
66
67 case Mode::kTriangleHulls:
68 return new GrCCPRTriangleHullAndEdgeProcessor(GeometryType::kHulls);
69 case Mode::kTriangleEdges:
70 return new GrCCPRTriangleHullAndEdgeProcessor(GeometryType::kEdges);
71 case Mode::kCombinedTriangleHullsAndEdges:
72 return new GrCCPRTriangleHullAndEdgeProcessor(GeometryType::kHullsAndEdges);
73 case Mode::kTriangleCorners:
74 return new GrCCPRTriangleCornerProcessor();
75 case Mode::kQuadraticHulls:
76 return new GrCCPRQuadraticHullProcessor();
77 case Mode::kQuadraticFlatEdges:
78 return new GrCCPRQuadraticSharedEdgeProcessor();
79 case Mode::kSerpentineInsets:
80 return new GrCCPRCubicInsetProcessor(GrCCPRCubicProcessor::Type::kSerpentine);
81 case Mode::kSerpentineBorders:
82 return new GrCCPRCubicBorderProcessor(GrCCPRCubicProcessor::Type::kSerpentine);
83 case Mode::kLoopInsets:
84 return new GrCCPRCubicInsetProcessor(GrCCPRCubicProcessor::Type::kLoop);
85 case Mode::kLoopBorders:
86 return new GrCCPRCubicBorderProcessor(GrCCPRCubicProcessor::Type::kLoop);
87 }
88 SkFAIL("Unexpected ccpr coverage processor mode.");
89 return nullptr;
90}
91
92using PrimitiveProcessor = GrCCPRCoverageProcessor::PrimitiveProcessor;
93
94void PrimitiveProcessor::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
95 const GrCCPRCoverageProcessor& proc = args.fGP.cast<GrCCPRCoverageProcessor>();
96
97 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
98 switch (fCoverageType) {
99 case CoverageType::kOne:
100 case CoverageType::kShader:
101 varyingHandler->addFlatVarying("wind", &fFragWind, kLow_GrSLPrecision);
102 break;
103 case CoverageType::kInterpolated:
104 varyingHandler->addVarying("coverage_times_wind", &fFragCoverageTimesWind,
105 kMedium_GrSLPrecision);
106 break;
107 }
108 this->resetVaryings(varyingHandler);
109
110 varyingHandler->emitAttributes(proc);
111
112 this->emitVertexShader(proc, args.fVertBuilder, args.fTexelBuffers[0], args.fRTAdjustName,
113 gpArgs);
114 this->emitGeometryShader(proc, args.fGeomBuilder, args.fRTAdjustName);
115 this->emitCoverage(proc, args.fFragBuilder, args.fOutputColor, args.fOutputCoverage);
116
117 SkASSERT(!args.fFPCoordTransformHandler->nextCoordTransform());
118}
119
120void PrimitiveProcessor::emitVertexShader(const GrCCPRCoverageProcessor& proc,
121 GrGLSLVertexBuilder* v,
122 const TexelBufferHandle& pointsBuffer,
123 const char* rtAdjust, GrGPArgs* gpArgs) const {
124 v->codeAppendf("int packedoffset = %s.w;", proc.instanceAttrib());
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400125 v->codeAppend ("highp float2 atlasoffset = float2((packedoffset<<16) >> 16, "
126 "packedoffset >> 16);");
Chris Dalton1a325d22017-07-14 15:17:41 -0600127
128 this->onEmitVertexShader(proc, v, pointsBuffer, "atlasoffset", rtAdjust, gpArgs);
129}
130
131void PrimitiveProcessor::emitGeometryShader(const GrCCPRCoverageProcessor& proc,
132 GrGLSLGeometryBuilder* g, const char* rtAdjust) const {
133 g->declareGlobal(fGeomWind);
134 this->emitWind(g, rtAdjust, fGeomWind.c_str());
135
136 SkString emitVertexFn;
137 SkSTArray<2, GrShaderVar> emitArgs;
138 const char* position = emitArgs.emplace_back("position", kVec2f_GrSLType,
139 GrShaderVar::kNonArray,
140 kHigh_GrSLPrecision).c_str();
141 const char* coverage = emitArgs.emplace_back("coverage", kFloat_GrSLType,
142 GrShaderVar::kNonArray,
143 kHigh_GrSLPrecision).c_str();
144 g->emitFunction(kVoid_GrSLType, "emitVertex", emitArgs.count(), emitArgs.begin(), [&]() {
145 SkString fnBody;
146 this->emitPerVertexGeometryCode(&fnBody, position, coverage, fGeomWind.c_str());
147 if (fFragWind.gsOut()) {
148 fnBody.appendf("%s = %s;", fFragWind.gsOut(), fGeomWind.c_str());
149 }
150 if (fFragCoverageTimesWind.gsOut()) {
151 fnBody.appendf("%s = %s * %s;",
152 fFragCoverageTimesWind.gsOut(), coverage, fGeomWind.c_str());
153 }
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400154 fnBody.append ("gl_Position = float4(position, 0, 1);");
Chris Dalton1a325d22017-07-14 15:17:41 -0600155 fnBody.append ("EmitVertex();");
156 return fnBody;
157 }().c_str(), &emitVertexFn);
158
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400159 g->codeAppendf("highp float2 bloat = %f * abs(%s.xz);", kAABloatRadius, rtAdjust);
Chris Dalton1a325d22017-07-14 15:17:41 -0600160
161#ifdef SK_DEBUG
162 if (proc.debugVisualizations()) {
163 g->codeAppendf("bloat *= %f;", GrCCPRCoverageProcessor::kDebugBloat);
164 }
165#endif
166
167 return this->onEmitGeometryShader(g, emitVertexFn.c_str(), fGeomWind.c_str(), rtAdjust);
168}
169
170int PrimitiveProcessor::emitHullGeometry(GrGLSLGeometryBuilder* g, const char* emitVertexFn,
171 const char* polygonPts, int numSides,
172 const char* wedgeIdx, const char* insetPts) const {
173 SkASSERT(numSides >= 3);
174
175 if (!insetPts) {
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400176 g->codeAppendf("highp float2 centroidpt = %s * float%i(%f);",
Chris Dalton1a325d22017-07-14 15:17:41 -0600177 polygonPts, numSides, 1.0 / numSides);
178 }
179
180 g->codeAppendf("int previdx = (%s + %i) %% %i, "
181 "nextidx = (%s + 1) %% %i;",
182 wedgeIdx, numSides - 1, numSides, wedgeIdx, numSides);
183
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400184 g->codeAppendf("highp float2 self = %s[%s];"
Chris Dalton1a325d22017-07-14 15:17:41 -0600185 "highp int leftidx = %s > 0 ? previdx : nextidx;"
186 "highp int rightidx = %s > 0 ? nextidx : previdx;",
187 polygonPts, wedgeIdx, fGeomWind.c_str(), fGeomWind.c_str());
188
189 // Which quadrant does the vector from self -> right fall into?
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400190 g->codeAppendf("highp float2 right = %s[rightidx];", polygonPts);
Chris Dalton1a325d22017-07-14 15:17:41 -0600191 if (3 == numSides) {
192 // TODO: evaluate perf gains.
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400193 g->codeAppend ("highp float2 qsr = sign(right - self);");
Chris Dalton1a325d22017-07-14 15:17:41 -0600194 } else {
195 SkASSERT(4 == numSides);
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400196 g->codeAppendf("highp float2 diag = %s[(%s + 2) %% 4];", polygonPts, wedgeIdx);
197 g->codeAppend ("highp float2 qsr = sign((right != self ? right : diag) - self);");
Chris Dalton1a325d22017-07-14 15:17:41 -0600198 }
199
200 // Which quadrant does the vector from left -> self fall into?
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400201 g->codeAppendf("highp float2 qls = sign(self - %s[leftidx]);", polygonPts);
Chris Dalton1a325d22017-07-14 15:17:41 -0600202
203 // d2 just helps us reduce triangle counts with orthogonal, axis-aligned lines.
204 // TODO: evaluate perf gains.
205 const char* dr2 = "dr";
206 if (3 == numSides) {
207 // TODO: evaluate perf gains.
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400208 g->codeAppend ("highp float2 dr = float2(qsr.y != 0 ? +qsr.y : +qsr.x, "
Chris Dalton1a325d22017-07-14 15:17:41 -0600209 "qsr.x != 0 ? -qsr.x : +qsr.y);");
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400210 g->codeAppend ("highp float2 dr2 = float2(qsr.y != 0 ? +qsr.y : -qsr.x, "
Chris Dalton1a325d22017-07-14 15:17:41 -0600211 "qsr.x != 0 ? -qsr.x : -qsr.y);");
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400212 g->codeAppend ("highp float2 dl = float2(qls.y != 0 ? +qls.y : +qls.x, "
Chris Dalton1a325d22017-07-14 15:17:41 -0600213 "qls.x != 0 ? -qls.x : +qls.y);");
214 dr2 = "dr2";
215 } else {
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400216 g->codeAppend ("highp float2 dr = float2(qsr.y != 0 ? +qsr.y : 1, "
Chris Dalton1a325d22017-07-14 15:17:41 -0600217 "qsr.x != 0 ? -qsr.x : 1);");
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400218 g->codeAppend ("highp float2 dl = (qls == float2(0)) ? dr : "
219 "float2(qls.y != 0 ? +qls.y : 1, qls.x != 0 ? -qls.x : 1);");
Chris Dalton1a325d22017-07-14 15:17:41 -0600220 }
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400221 g->codeAppendf("bool2 dnotequal = notEqual(%s, dl);", dr2);
Chris Dalton1a325d22017-07-14 15:17:41 -0600222
223 // Emit one third of what is the convex hull of pixel-size boxes centered on the vertices.
224 // Each invocation emits a different third.
225 if (insetPts) {
226 g->codeAppendf("%s(%s[rightidx], 1);", emitVertexFn, insetPts);
227 }
228 g->codeAppendf("%s(right + bloat * dr, 1);", emitVertexFn);
229 if (insetPts) {
230 g->codeAppendf("%s(%s[%s], 1);", emitVertexFn, insetPts, wedgeIdx);
231 } else {
232 g->codeAppendf("%s(centroidpt, 1);", emitVertexFn);
233 }
234 g->codeAppendf("%s(self + bloat * %s, 1);", emitVertexFn, dr2);
235 g->codeAppend ("if (any(dnotequal)) {");
236 g->codeAppendf( "%s(self + bloat * dl, 1);", emitVertexFn);
237 g->codeAppend ("}");
238 g->codeAppend ("if (all(dnotequal)) {");
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400239 g->codeAppendf( "%s(self + bloat * float2(-dl.y, dl.x), 1);", emitVertexFn);
Chris Dalton1a325d22017-07-14 15:17:41 -0600240 g->codeAppend ("}");
241 g->codeAppend ("EndPrimitive();");
242
243 return insetPts ? 6 : 5;
244}
245
246int PrimitiveProcessor::emitEdgeGeometry(GrGLSLGeometryBuilder* g, const char* emitVertexFn,
247 const char* leftPt, const char* rightPt,
248 const char* distanceEquation) const {
249 if (!distanceEquation) {
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400250 this->emitEdgeDistanceEquation(g, leftPt, rightPt, "highp float3 edge_distance_equation");
Chris Dalton1a325d22017-07-14 15:17:41 -0600251 distanceEquation = "edge_distance_equation";
252 }
253
254 // qlr is defined in emitEdgeDistanceEquation.
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400255 g->codeAppendf("highp float2x2 endpts = float2x2(%s - bloat * qlr, %s + bloat * qlr);",
Chris Dalton1a325d22017-07-14 15:17:41 -0600256 leftPt, rightPt);
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400257 g->codeAppendf("mediump float2 endpts_coverage = %s.xy * endpts + %s.z;",
Chris Dalton1a325d22017-07-14 15:17:41 -0600258 distanceEquation, distanceEquation);
259
260 // d1 is defined in emitEdgeDistanceEquation.
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400261 g->codeAppend ("highp float2 d2 = d1;");
Chris Dalton1a325d22017-07-14 15:17:41 -0600262 g->codeAppend ("bool aligned = qlr.x == 0 || qlr.y == 0;");
263 g->codeAppend ("if (aligned) {");
264 g->codeAppend ( "d1 -= qlr;");
265 g->codeAppend ( "d2 += qlr;");
266 g->codeAppend ("}");
267
268 // Emit the convex hull of 2 pixel-size boxes centered on the endpoints of the edge. Each
269 // invocation emits a different edge. Emit negative coverage that subtracts the appropiate
270 // amount back out from the hull we drew above.
271 g->codeAppend ("if (!aligned) {");
272 g->codeAppendf( "%s(endpts[0], endpts_coverage[0]);", emitVertexFn);
273 g->codeAppend ("}");
274 g->codeAppendf("%s(%s + bloat * d1, -1);", emitVertexFn, leftPt);
275 g->codeAppendf("%s(%s - bloat * d2, 0);", emitVertexFn, leftPt);
276 g->codeAppendf("%s(%s + bloat * d2, -1);", emitVertexFn, rightPt);
277 g->codeAppendf("%s(%s - bloat * d1, 0);", emitVertexFn, rightPt);
278 g->codeAppend ("if (!aligned) {");
279 g->codeAppendf( "%s(endpts[1], endpts_coverage[1]);", emitVertexFn);
280 g->codeAppend ("}");
281 g->codeAppend ("EndPrimitive();");
282
283 return 6;
284}
285
286void PrimitiveProcessor::emitEdgeDistanceEquation(GrGLSLGeometryBuilder* g,
287 const char* leftPt, const char* rightPt,
288 const char* outputDistanceEquation) const {
289 // Which quadrant does the vector from left -> right fall into?
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400290 g->codeAppendf("highp float2 qlr = sign(%s - %s);", rightPt, leftPt);
291 g->codeAppend ("highp float2 d1 = float2(qlr.y, -qlr.x);");
Chris Dalton1a325d22017-07-14 15:17:41 -0600292
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400293 g->codeAppendf("highp float2 n = float2(%s.y - %s.y, %s.x - %s.x);",
Chris Dalton1a325d22017-07-14 15:17:41 -0600294 rightPt, leftPt, leftPt, rightPt);
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400295 g->codeAppendf("highp float2 kk = n * float2x2(%s + bloat * d1, %s - bloat * d1);",
296 leftPt, leftPt);
Chris Dalton1a325d22017-07-14 15:17:41 -0600297 // Clamp for when n=0. wind=0 when n=0 so as long as we don't get Inf or NaN we are fine.
298 g->codeAppendf("highp float scale = 1 / max(kk[0] - kk[1], 1e-30);");
299
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400300 g->codeAppendf("%s = float3(-n, kk[1]) * scale;", outputDistanceEquation);
Chris Dalton1a325d22017-07-14 15:17:41 -0600301}
302
303void PrimitiveProcessor::emitCoverage(const GrCCPRCoverageProcessor& proc, GrGLSLFragmentBuilder* f,
304 const char* outputColor, const char* outputCoverage) const {
305 switch (fCoverageType) {
306 case CoverageType::kOne:
307 f->codeAppendf("%s.a = %s;", outputColor, fFragWind.fsIn());
308 break;
309 case CoverageType::kInterpolated:
310 f->codeAppendf("%s.a = %s;", outputColor, fFragCoverageTimesWind.fsIn());
311 break;
312 case CoverageType::kShader:
313 f->codeAppendf("mediump float coverage = 0;");
314 this->emitShaderCoverage(f, "coverage");
315 f->codeAppendf("%s.a = coverage * %s;", outputColor, fFragWind.fsIn());
316 break;
317 }
318
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400319 f->codeAppendf("%s = float4(1);", outputCoverage);
Chris Dalton1a325d22017-07-14 15:17:41 -0600320
321#ifdef SK_DEBUG
322 if (proc.debugVisualizations()) {
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400323 f->codeAppendf("%s = float4(-%s.a, %s.a, 0, 1);", outputColor, outputColor, outputColor);
Chris Dalton1a325d22017-07-14 15:17:41 -0600324 }
325#endif
326}
327
328int PrimitiveProcessor::defineSoftSampleLocations(GrGLSLFragmentBuilder* f,
329 const char* samplesName) const {
330 // Standard DX11 sample locations.
331#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_IOS)
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400332 f->defineConstant("highp float2[8]", samplesName, "float2[8]("
333 "float2(+1, -3)/16, float2(-1, +3)/16, float2(+5, +1)/16, float2(-3, -5)/16, "
334 "float2(-5, +5)/16, float2(-7, -1)/16, float2(+3, +7)/16, float2(+7, -7)/16."
Chris Dalton1a325d22017-07-14 15:17:41 -0600335 ")");
336 return 8;
337#else
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400338 f->defineConstant("highp float2[16]", samplesName, "float2[16]("
339 "float2(+1, +1)/16, float2(-1, -3)/16, float2(-3, +2)/16, float2(+4, -1)/16, "
340 "float2(-5, -2)/16, float2(+2, +5)/16, float2(+5, +3)/16, float2(+3, -5)/16, "
341 "float2(-2, +6)/16, float2( 0, -7)/16, float2(-4, -6)/16, float2(-6, +4)/16, "
342 "float2(-8, 0)/16, float2(+7, -4)/16, float2(+6, +7)/16, float2(-7, -8)/16."
Chris Dalton1a325d22017-07-14 15:17:41 -0600343 ")");
344 return 16;
345#endif
346}
347
348#ifdef SK_DEBUG
349
350#include "GrRenderTarget.h"
351
Robert Phillips2890fbf2017-07-26 15:48:41 -0400352void GrCCPRCoverageProcessor::Validate(GrRenderTargetProxy* atlasProxy) {
353 SkASSERT(kAtlasOrigin == atlasProxy->origin());
354 SkASSERT(GrPixelConfigIsAlphaOnly(atlasProxy->config()));
355 SkASSERT(GrPixelConfigIsFloatingPoint(atlasProxy->config()));
Chris Dalton1a325d22017-07-14 15:17:41 -0600356}
357
358#endif