1 | /* |
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 "src/gpu/ccpr/GrGSCoverageProcessor.h" |
9 | |
10 | #include "src/gpu/GrOpsRenderPass.h" |
11 | #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h" |
12 | #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h" |
13 | |
14 | using InputType = GrGLSLGeometryBuilder::InputType; |
15 | using OutputType = GrGLSLGeometryBuilder::OutputType; |
16 | |
17 | /** |
18 | * This class and its subclasses implement the coverage processor with geometry shaders. |
19 | */ |
20 | class GrGSCoverageProcessor::Impl : public GrGLSLGeometryProcessor { |
21 | protected: |
22 | Impl(std::unique_ptr<Shader> shader) : fShader(std::move(shader)) {} |
23 | |
24 | virtual bool hasCoverage(const GrGSCoverageProcessor& proc) const { return false; } |
25 | |
26 | void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&, |
27 | const CoordTransformRange& transformRange) final { |
28 | this->setTransformDataHelper(SkMatrix::I(), pdman, transformRange); |
29 | } |
30 | |
31 | void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final { |
32 | const GrGSCoverageProcessor& proc = args.fGP.cast<GrGSCoverageProcessor>(); |
33 | |
34 | // The vertex shader simply forwards transposed x or y values to the geometry shader. |
35 | SkASSERT(1 == proc.numVertexAttributes()); |
36 | gpArgs->fPositionVar = proc.fInputXOrYValues.asShaderVar(); |
37 | |
38 | // Geometry shader. |
39 | GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; |
40 | this->emitGeometryShader(proc, varyingHandler, args.fGeomBuilder, args.fRTAdjustName); |
41 | varyingHandler->emitAttributes(proc); |
42 | varyingHandler->setNoPerspective(); |
43 | SkASSERT(!*args.fFPCoordTransformHandler); |
44 | |
45 | // Fragment shader. |
46 | GrGLSLFPFragmentBuilder* f = args.fFragBuilder; |
47 | f->codeAppendf("half coverage;" ); |
48 | fShader->emitFragmentCoverageCode(f, "coverage" ); |
49 | f->codeAppendf("%s = half4(coverage);" , args.fOutputColor); |
50 | f->codeAppendf("%s = half4(1);" , args.fOutputCoverage); |
51 | } |
52 | |
53 | void emitGeometryShader( |
54 | const GrGSCoverageProcessor& proc, GrGLSLVaryingHandler* varyingHandler, |
55 | GrGLSLGeometryBuilder* g, const char* rtAdjust) const { |
56 | int numInputPoints = proc.numInputPoints(); |
57 | SkASSERT(3 == numInputPoints || 4 == numInputPoints); |
58 | |
59 | int inputWidth = (4 == numInputPoints || proc.hasInputWeight()) ? 4 : 3; |
60 | const char* posValues = (4 == inputWidth) ? "sk_Position" : "sk_Position.xyz" ; |
61 | g->codeAppendf("float%ix2 pts = transpose(float2x%i(sk_in[0].%s, sk_in[1].%s));" , |
62 | inputWidth, inputWidth, posValues, posValues); |
63 | |
64 | GrShaderVar wind("wind" , kHalf_GrSLType); |
65 | g->declareGlobal(wind); |
66 | Shader::CalcWind(proc, g, "pts" , wind.c_str()); |
67 | if (PrimitiveType::kWeightedTriangles == proc.primitiveType()) { |
68 | SkASSERT(3 == numInputPoints); |
69 | SkASSERT(kFloat4_GrVertexAttribType == proc.fInputXOrYValues.cpuType()); |
70 | g->codeAppendf("%s *= half(sk_in[0].sk_Position.w);" , wind.c_str()); |
71 | } |
72 | |
73 | SkString emitVertexFn; |
74 | SkSTArray<3, GrShaderVar> emitArgs; |
75 | const char* corner = emitArgs.emplace_back("corner" , kFloat2_GrSLType).c_str(); |
76 | const char* bloatdir = emitArgs.emplace_back("bloatdir" , kFloat2_GrSLType).c_str(); |
77 | const char* inputCoverage = nullptr; |
78 | if (this->hasCoverage(proc)) { |
79 | inputCoverage = emitArgs.emplace_back("coverage" , kHalf_GrSLType).c_str(); |
80 | } |
81 | const char* cornerCoverage = nullptr; |
82 | if (Subpass::kCorners == proc.fSubpass) { |
83 | cornerCoverage = emitArgs.emplace_back("corner_coverage" , kHalf2_GrSLType).c_str(); |
84 | } |
85 | g->emitFunction(kVoid_GrSLType, "emitVertex" , emitArgs.count(), emitArgs.begin(), [&]() { |
86 | SkString fnBody; |
87 | fnBody.appendf("float2 vertexpos = fma(%s, float2(bloat), %s);" , bloatdir, corner); |
88 | const char* coverage = inputCoverage; |
89 | if (!coverage) { |
90 | if (!fShader->calculatesOwnEdgeCoverage()) { |
91 | // Flat edge opposite the curve. Coverages need full precision since distance |
92 | // to the opposite edge can be large. |
93 | fnBody.appendf("float coverage = dot(float3(vertexpos, 1), %s);" , |
94 | fEdgeDistanceEquation.c_str()); |
95 | } else { |
96 | // The "coverage" param should hold only the signed winding value. |
97 | fnBody.appendf("float coverage = 1;" ); |
98 | } |
99 | coverage = "coverage" ; |
100 | } |
101 | fnBody.appendf("%s *= %s;" , coverage, wind.c_str()); |
102 | if (cornerCoverage) { |
103 | fnBody.appendf("%s.x *= %s;" , cornerCoverage, wind.c_str()); |
104 | } |
105 | fShader->emitVaryings(varyingHandler, GrGLSLVarying::Scope::kGeoToFrag, &fnBody, |
106 | "vertexpos" , coverage, cornerCoverage, wind.c_str()); |
107 | g->emitVertex(&fnBody, "vertexpos" , rtAdjust); |
108 | return fnBody; |
109 | }().c_str(), &emitVertexFn); |
110 | |
111 | float bloat = kAABloatRadius; |
112 | #ifdef SK_DEBUG |
113 | if (proc.debugBloatEnabled()) { |
114 | bloat *= proc.debugBloat(); |
115 | } |
116 | #endif |
117 | g->defineConstant("bloat" , bloat); |
118 | |
119 | if (!this->hasCoverage(proc) && !fShader->calculatesOwnEdgeCoverage()) { |
120 | // Determine the amount of coverage to subtract out for the flat edge of the curve. |
121 | g->declareGlobal(fEdgeDistanceEquation); |
122 | g->codeAppendf("float2 p0 = pts[0], p1 = pts[%i];" , numInputPoints - 1); |
123 | g->codeAppendf("float2 n = float2(p0.y - p1.y, p1.x - p0.x);" ); |
124 | g->codeAppend ("float nwidth = bloat*2 * (abs(n.x) + abs(n.y));" ); |
125 | // When nwidth=0, wind must also be 0 (and coverage * wind = 0). So it doesn't matter |
126 | // what we come up with here as long as it isn't NaN or Inf. |
127 | g->codeAppend ("n /= (0 != nwidth) ? nwidth : 1;" ); |
128 | g->codeAppendf("%s = float3(-n, dot(n, p0) - .5*sign(%s));" , |
129 | fEdgeDistanceEquation.c_str(), wind.c_str()); |
130 | } |
131 | |
132 | this->onEmitGeometryShader(proc, g, wind, emitVertexFn.c_str()); |
133 | } |
134 | |
135 | virtual void onEmitGeometryShader(const GrGSCoverageProcessor&, GrGLSLGeometryBuilder*, |
136 | const GrShaderVar& wind, const char* emitVertexFn) const = 0; |
137 | |
138 | const std::unique_ptr<Shader> fShader; |
139 | const GrShaderVar fEdgeDistanceEquation{"edge_distance_equation" , kFloat3_GrSLType}; |
140 | |
141 | typedef GrGLSLGeometryProcessor INHERITED; |
142 | }; |
143 | |
144 | /** |
145 | * Generates conservative rasters around a triangle and its edges, and calculates coverage ramps. |
146 | * |
147 | * Triangle rough outlines are drawn in two steps: (1) draw a conservative raster of the entire |
148 | * triangle, with a coverage of +1, and (2) draw conservative rasters around each edge, with a |
149 | * coverage ramp from -1 to 0. These edge coverage values convert jagged conservative raster edges |
150 | * into smooth, antialiased ones. |
151 | * |
152 | * The final corners get touched up in a later step by TriangleCornerImpl. |
153 | */ |
154 | class GrGSCoverageProcessor::TriangleHullImpl : public GrGSCoverageProcessor::Impl { |
155 | public: |
156 | TriangleHullImpl(std::unique_ptr<Shader> shader) : Impl(std::move(shader)) {} |
157 | |
158 | bool hasCoverage(const GrGSCoverageProcessor& proc) const override { return true; } |
159 | |
160 | void onEmitGeometryShader(const GrGSCoverageProcessor&, GrGLSLGeometryBuilder* g, |
161 | const GrShaderVar& wind, const char* emitVertexFn) const override { |
162 | fShader->emitSetupCode(g, "pts" ); |
163 | |
164 | // Visualize the input triangle as upright and equilateral, with a flat base. Paying special |
165 | // attention to wind, we can identify the points as top, bottom-left, and bottom-right. |
166 | // |
167 | // NOTE: We generate the rasters in 5 independent invocations, so each invocation designates |
168 | // the corner it will begin with as the top. |
169 | g->codeAppendf("int i = (%s > 0 ? sk_InvocationID : 4 - sk_InvocationID) %% 3;" , |
170 | wind.c_str()); |
171 | g->codeAppend ("float2 top = pts[i];" ); |
172 | g->codeAppendf("float2 right = pts[(i + (%s > 0 ? 1 : 2)) %% 3];" , wind.c_str()); |
173 | g->codeAppendf("float2 left = pts[(i + (%s > 0 ? 2 : 1)) %% 3];" , wind.c_str()); |
174 | |
175 | // Determine which direction to outset the conservative raster from each of the three edges. |
176 | g->codeAppend ("float2 leftbloat = sign(top - left);" ); |
177 | g->codeAppend ("leftbloat = float2(0 != leftbloat.y ? leftbloat.y : leftbloat.x, " |
178 | "0 != leftbloat.x ? -leftbloat.x : -leftbloat.y);" ); |
179 | |
180 | g->codeAppend ("float2 rightbloat = sign(right - top);" ); |
181 | g->codeAppend ("rightbloat = float2(0 != rightbloat.y ? rightbloat.y : rightbloat.x, " |
182 | "0 != rightbloat.x ? -rightbloat.x : -rightbloat.y);" ); |
183 | |
184 | g->codeAppend ("float2 downbloat = sign(left - right);" ); |
185 | g->codeAppend ("downbloat = float2(0 != downbloat.y ? downbloat.y : downbloat.x, " |
186 | "0 != downbloat.x ? -downbloat.x : -downbloat.y);" ); |
187 | |
188 | // The triangle's conservative raster has a coverage of +1 all around. |
189 | g->codeAppend ("half4 coverages = half4(+1);" ); |
190 | |
191 | // Edges have coverage ramps. |
192 | g->codeAppend ("if (sk_InvocationID >= 2) {" ); // Are we an edge? |
193 | Shader::CalcEdgeCoverageAtBloatVertex(g, "top" , "right" , |
194 | "float2(+rightbloat.y, -rightbloat.x)" , |
195 | "coverages[0]" ); |
196 | g->codeAppend ( "coverages.yzw = half3(-1, 0, -1 - coverages[0]);" ); |
197 | // Reassign bloats to characterize a conservative raster around a single edge, rather than |
198 | // the entire triangle. |
199 | g->codeAppend ( "leftbloat = downbloat = -rightbloat;" ); |
200 | g->codeAppend ("}" ); |
201 | |
202 | // Here we generate the conservative raster geometry. The triangle's conservative raster is |
203 | // the convex hull of 3 pixel-size boxes centered on the input points. This translates to a |
204 | // convex polygon with either one, two, or three vertices at each input point (depending on |
205 | // how sharp the corner is) that we split between two invocations. Edge conservative rasters |
206 | // are convex hulls of 2 pixel-size boxes, one at each endpoint. For more details on |
207 | // conservative raster, see: |
208 | // https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html |
209 | g->codeAppendf("bool2 left_right_notequal = notEqual(leftbloat, rightbloat);" ); |
210 | g->codeAppend ("if (all(left_right_notequal)) {" ); |
211 | // The top corner will have three conservative raster vertices. Emit the |
212 | // middle one first to the triangle strip. |
213 | g->codeAppendf( "%s(top, float2(-leftbloat.y, +leftbloat.x), coverages[0]);" , |
214 | emitVertexFn); |
215 | g->codeAppend ("}" ); |
216 | g->codeAppend ("if (any(left_right_notequal)) {" ); |
217 | // Second conservative raster vertex for the top corner. |
218 | g->codeAppendf( "%s(top, rightbloat, coverages[1]);" , emitVertexFn); |
219 | g->codeAppend ("}" ); |
220 | |
221 | // Main interior body. |
222 | g->codeAppendf("%s(top, leftbloat, coverages[2]);" , emitVertexFn); |
223 | g->codeAppendf("%s(right, rightbloat, coverages[1]);" , emitVertexFn); |
224 | |
225 | // Here the invocations diverge slightly. We can't symmetrically divide three triangle |
226 | // points between two invocations, so each does the following: |
227 | // |
228 | // sk_InvocationID=0: Finishes the main interior body of the triangle hull. |
229 | // sk_InvocationID=1: Remaining two conservative raster vertices for the third hull corner. |
230 | // sk_InvocationID=2..4: Finish the opposite endpoint of their corresponding edge. |
231 | g->codeAppendf("bool2 right_down_notequal = notEqual(rightbloat, downbloat);" ); |
232 | g->codeAppend ("if (any(right_down_notequal) || 0 == sk_InvocationID) {" ); |
233 | g->codeAppendf( "%s((0 == sk_InvocationID) ? left : right, " |
234 | "(0 == sk_InvocationID) ? leftbloat : downbloat, " |
235 | "coverages[2]);" , emitVertexFn); |
236 | g->codeAppend ("}" ); |
237 | g->codeAppend ("if (all(right_down_notequal) && 0 != sk_InvocationID) {" ); |
238 | g->codeAppendf( "%s(right, float2(-rightbloat.y, +rightbloat.x), coverages[3]);" , |
239 | emitVertexFn); |
240 | g->codeAppend ("}" ); |
241 | |
242 | // 5 invocations: 2 triangle hull invocations and 3 edges. |
243 | g->configure(InputType::kLines, OutputType::kTriangleStrip, 6, 5); |
244 | } |
245 | }; |
246 | |
247 | /** |
248 | * Generates a conservative raster around a convex quadrilateral that encloses a cubic or quadratic. |
249 | */ |
250 | class GrGSCoverageProcessor::CurveHullImpl : public GrGSCoverageProcessor::Impl { |
251 | public: |
252 | CurveHullImpl(std::unique_ptr<Shader> shader) : Impl(std::move(shader)) {} |
253 | |
254 | void onEmitGeometryShader(const GrGSCoverageProcessor&, GrGLSLGeometryBuilder* g, |
255 | const GrShaderVar& wind, const char* emitVertexFn) const override { |
256 | const char* hullPts = "pts" ; |
257 | fShader->emitSetupCode(g, "pts" , &hullPts); |
258 | |
259 | // Visualize the input (convex) quadrilateral as a square. Paying special attention to wind, |
260 | // we can identify the points by their corresponding corner. |
261 | // |
262 | // NOTE: We split the square down the diagonal from top-right to bottom-left, and generate |
263 | // the hull in two independent invocations. Each invocation designates the corner it will |
264 | // begin with as top-left. |
265 | g->codeAppend ("int i = sk_InvocationID * 2;" ); |
266 | g->codeAppendf("float2 topleft = %s[i];" , hullPts); |
267 | g->codeAppendf("float2 topright = %s[%s > 0 ? i + 1 : 3 - i];" , hullPts, wind.c_str()); |
268 | g->codeAppendf("float2 bottomleft = %s[%s > 0 ? 3 - i : i + 1];" , hullPts, wind.c_str()); |
269 | g->codeAppendf("float2 bottomright = %s[2 - i];" , hullPts); |
270 | |
271 | // Determine how much to outset the conservative raster hull from the relevant edges. |
272 | g->codeAppend ("float2 leftbloat = float2(topleft.y > bottomleft.y ? +1 : -1, " |
273 | "topleft.x > bottomleft.x ? -1 : +1);" ); |
274 | g->codeAppend ("float2 upbloat = float2(topright.y > topleft.y ? +1 : -1, " |
275 | "topright.x > topleft.x ? -1 : +1);" ); |
276 | g->codeAppend ("float2 rightbloat = float2(bottomright.y > topright.y ? +1 : -1, " |
277 | "bottomright.x > topright.x ? -1 : +1);" ); |
278 | |
279 | // Here we generate the conservative raster geometry. It is the convex hull of 4 pixel-size |
280 | // boxes centered on the input points, split evenly between two invocations. This translates |
281 | // to a polygon with either one, two, or three vertices at each input point, depending on |
282 | // how sharp the corner is. For more details on conservative raster, see: |
283 | // https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html |
284 | g->codeAppendf("bool2 left_up_notequal = notEqual(leftbloat, upbloat);" ); |
285 | g->codeAppend ("if (all(left_up_notequal)) {" ); |
286 | // The top-left corner will have three conservative raster vertices. |
287 | // Emit the middle one first to the triangle strip. |
288 | g->codeAppendf( "%s(topleft, float2(-leftbloat.y, leftbloat.x));" , emitVertexFn); |
289 | g->codeAppend ("}" ); |
290 | g->codeAppend ("if (any(left_up_notequal)) {" ); |
291 | // Second conservative raster vertex for the top-left corner. |
292 | g->codeAppendf( "%s(topleft, leftbloat);" , emitVertexFn); |
293 | g->codeAppend ("}" ); |
294 | |
295 | // Main interior body of this invocation's half of the hull. |
296 | g->codeAppendf("%s(topleft, upbloat);" , emitVertexFn); |
297 | g->codeAppendf("%s(bottomleft, leftbloat);" , emitVertexFn); |
298 | g->codeAppendf("%s(topright, upbloat);" , emitVertexFn); |
299 | |
300 | // Remaining two conservative raster vertices for the top-right corner. |
301 | g->codeAppendf("bool2 up_right_notequal = notEqual(upbloat, rightbloat);" ); |
302 | g->codeAppend ("if (any(up_right_notequal)) {" ); |
303 | g->codeAppendf( "%s(topright, rightbloat);" , emitVertexFn); |
304 | g->codeAppend ("}" ); |
305 | g->codeAppend ("if (all(up_right_notequal)) {" ); |
306 | g->codeAppendf( "%s(topright, float2(-upbloat.y, upbloat.x));" , emitVertexFn); |
307 | g->codeAppend ("}" ); |
308 | |
309 | g->configure(InputType::kLines, OutputType::kTriangleStrip, 7, 2); |
310 | } |
311 | }; |
312 | |
313 | /** |
314 | * Generates conservative rasters around corners (aka pixel-size boxes) and calculates |
315 | * coverage and attenuation ramps to fix up the coverage values written by the hulls. |
316 | */ |
317 | class GrGSCoverageProcessor::CornerImpl : public GrGSCoverageProcessor::Impl { |
318 | public: |
319 | CornerImpl(std::unique_ptr<Shader> shader) : Impl(std::move(shader)) {} |
320 | |
321 | bool hasCoverage(const GrGSCoverageProcessor& proc) const override { |
322 | return proc.isTriangles(); |
323 | } |
324 | |
325 | void onEmitGeometryShader(const GrGSCoverageProcessor& proc, GrGLSLGeometryBuilder* g, |
326 | const GrShaderVar& wind, const char* emitVertexFn) const override { |
327 | fShader->emitSetupCode(g, "pts" ); |
328 | |
329 | g->codeAppendf("int corneridx = sk_InvocationID;" ); |
330 | if (!proc.isTriangles()) { |
331 | g->codeAppendf("corneridx *= %i;" , proc.numInputPoints() - 1); |
332 | } |
333 | |
334 | g->codeAppendf("float2 corner = pts[corneridx];" ); |
335 | g->codeAppendf("float2 left = pts[(corneridx + (%s > 0 ? %i : 1)) %% %i];" , |
336 | wind.c_str(), proc.numInputPoints() - 1, proc.numInputPoints()); |
337 | g->codeAppendf("float2 right = pts[(corneridx + (%s > 0 ? 1 : %i)) %% %i];" , |
338 | wind.c_str(), proc.numInputPoints() - 1, proc.numInputPoints()); |
339 | |
340 | g->codeAppend ("float2 leftdir = corner - left;" ); |
341 | g->codeAppend ("leftdir = (float2(0) != leftdir) ? normalize(leftdir) : float2(1, 0);" ); |
342 | |
343 | g->codeAppend ("float2 rightdir = right - corner;" ); |
344 | g->codeAppend ("rightdir = (float2(0) != rightdir) ? normalize(rightdir) : float2(1, 0);" ); |
345 | |
346 | // Find "outbloat" and "crossbloat" at our corner. The outbloat points diagonally out of the |
347 | // triangle, in the direction that should ramp to zero coverage with attenuation. The |
348 | // crossbloat runs perpindicular to outbloat. |
349 | g->codeAppend ("float2 outbloat = float2(leftdir.x > rightdir.x ? +1 : -1, " |
350 | "leftdir.y > rightdir.y ? +1 : -1);" ); |
351 | g->codeAppend ("float2 crossbloat = float2(-outbloat.y, +outbloat.x);" ); |
352 | |
353 | g->codeAppend ("half attenuation; {" ); |
354 | Shader::CalcCornerAttenuation(g, "leftdir" , "rightdir" , "attenuation" ); |
355 | g->codeAppend ("}" ); |
356 | |
357 | if (proc.isTriangles()) { |
358 | g->codeAppend ("half2 left_coverages; {" ); |
359 | Shader::CalcEdgeCoveragesAtBloatVertices(g, "left" , "corner" , "-outbloat" , |
360 | "-crossbloat" , "left_coverages" ); |
361 | g->codeAppend ("}" ); |
362 | |
363 | g->codeAppend ("half2 right_coverages; {" ); |
364 | Shader::CalcEdgeCoveragesAtBloatVertices(g, "corner" , "right" , "-outbloat" , |
365 | "crossbloat" , "right_coverages" ); |
366 | g->codeAppend ("}" ); |
367 | |
368 | // Emit a corner box. The first coverage argument erases the values that were written |
369 | // previously by the hull and edge geometry. The second pair are multiplied together by |
370 | // the fragment shader. They ramp to 0 with attenuation in the direction of outbloat, |
371 | // and linearly from left-edge coverage to right-edge coverage in the direction of |
372 | // crossbloat. |
373 | // |
374 | // NOTE: Since this is not a linear mapping, it is important that the box's diagonal |
375 | // shared edge points in the direction of outbloat. |
376 | g->codeAppendf("%s(corner, -crossbloat, right_coverages[1] - left_coverages[1]," |
377 | "half2(1 + left_coverages[1], 1));" , |
378 | emitVertexFn); |
379 | |
380 | g->codeAppendf("%s(corner, outbloat, 1 + left_coverages[0] + right_coverages[0], " |
381 | "half2(0, attenuation));" , |
382 | emitVertexFn); |
383 | |
384 | g->codeAppendf("%s(corner, -outbloat, -1 - left_coverages[0] - right_coverages[0], " |
385 | "half2(1 + left_coverages[0] + right_coverages[0], 1));" , |
386 | emitVertexFn); |
387 | |
388 | g->codeAppendf("%s(corner, crossbloat, left_coverages[1] - right_coverages[1]," |
389 | "half2(1 + right_coverages[1], 1));" , |
390 | emitVertexFn); |
391 | } else { |
392 | // Curves are simpler. Setting "wind = -wind" causes the Shader to erase what it had |
393 | // written in the previous pass hull. Then, at each vertex of the corner box, the Shader |
394 | // will calculate the curve's local coverage value, interpolate it alongside our |
395 | // attenuation parameter, and multiply the two together for a final coverage value. |
396 | g->codeAppendf("%s = -%s;" , wind.c_str(), wind.c_str()); |
397 | if (!fShader->calculatesOwnEdgeCoverage()) { |
398 | g->codeAppendf("%s = -%s;" , |
399 | fEdgeDistanceEquation.c_str(), fEdgeDistanceEquation.c_str()); |
400 | } |
401 | g->codeAppendf("%s(corner, -crossbloat, half2(-1, 1));" , emitVertexFn); |
402 | g->codeAppendf("%s(corner, outbloat, half2(0, attenuation));" , |
403 | emitVertexFn); |
404 | g->codeAppendf("%s(corner, -outbloat, half2(-1, 1));" , emitVertexFn); |
405 | g->codeAppendf("%s(corner, crossbloat, half2(-1, 1));" , emitVertexFn); |
406 | } |
407 | |
408 | g->configure(InputType::kLines, OutputType::kTriangleStrip, 4, proc.isTriangles() ? 3 : 2); |
409 | } |
410 | }; |
411 | |
412 | void GrGSCoverageProcessor::reset(PrimitiveType primitiveType, int subpassIdx, |
413 | GrResourceProvider*) { |
414 | fPrimitiveType = primitiveType; // This will affect the return values for numInputPoints, etc. |
415 | |
416 | if (4 == this->numInputPoints() || this->hasInputWeight()) { |
417 | fInputXOrYValues = |
418 | {"x_or_y_values" , kFloat4_GrVertexAttribType, kFloat4_GrSLType}; |
419 | static_assert(sizeof(QuadPointInstance) == |
420 | 2 * GrVertexAttribTypeSize(kFloat4_GrVertexAttribType)); |
421 | static_assert(offsetof(QuadPointInstance, fY) == |
422 | GrVertexAttribTypeSize(kFloat4_GrVertexAttribType)); |
423 | } else { |
424 | fInputXOrYValues = |
425 | {"x_or_y_values" , kFloat3_GrVertexAttribType, kFloat3_GrSLType}; |
426 | static_assert(sizeof(TriPointInstance) == |
427 | 2 * GrVertexAttribTypeSize(kFloat3_GrVertexAttribType)); |
428 | } |
429 | |
430 | this->setVertexAttributes(&fInputXOrYValues, 1); |
431 | |
432 | SkASSERT(subpassIdx == 0 || subpassIdx == 1); |
433 | fSubpass = (Subpass)subpassIdx; |
434 | } |
435 | |
436 | void GrGSCoverageProcessor::bindBuffers(GrOpsRenderPass* renderPass, |
437 | const GrBuffer* instanceBuffer) const { |
438 | renderPass->bindBuffers(nullptr, nullptr, instanceBuffer); |
439 | } |
440 | |
441 | void GrGSCoverageProcessor::drawInstances(GrOpsRenderPass* renderPass, int instanceCount, |
442 | int baseInstance) const { |
443 | // We don't actually make instanced draw calls. Instead, we feed transposed x,y point values to |
444 | // the GPU in a regular vertex array and draw kLines (see initGS). Then, each vertex invocation |
445 | // receives either the shape's x or y values as inputs, which it forwards to the geometry |
446 | // shader. |
447 | renderPass->draw(instanceCount * 2, baseInstance * 2); |
448 | } |
449 | |
450 | GrGLSLPrimitiveProcessor* GrGSCoverageProcessor::onCreateGLSLInstance( |
451 | std::unique_ptr<Shader> shader) const { |
452 | if (Subpass::kHulls == fSubpass) { |
453 | return this->isTriangles() |
454 | ? (Impl*) new TriangleHullImpl(std::move(shader)) |
455 | : (Impl*) new CurveHullImpl(std::move(shader)); |
456 | } |
457 | SkASSERT(Subpass::kCorners == fSubpass); |
458 | return new CornerImpl(std::move(shader)); |
459 | } |
460 | |