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