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 "GrCCCoverageProcessor.h" 9 10 #include "GrMesh.h" 11 #include "glsl/GrGLSLVertexGeoBuilder.h" 12 13 using InputType = GrGLSLGeometryBuilder::InputType; 14 using OutputType = GrGLSLGeometryBuilder::OutputType; 15 using Shader = GrCCCoverageProcessor::Shader; 16 17 /** 18 * This class and its subclasses implement the coverage processor with geometry shaders. 19 */ 20 class GrCCCoverageProcessor::GSImpl : public GrGLSLGeometryProcessor { 21 protected: 22 GSImpl(std::unique_ptr<Shader> shader) : fShader(std::move(shader)) {} 23 24 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&, 25 FPCoordTransformIter&& transformIter) final { 26 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter); 27 } 28 29 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final { 30 const GrCCCoverageProcessor& proc = args.fGP.cast<GrCCCoverageProcessor>(); 31 32 // The vertex shader simply forwards transposed x or y values to the geometry shader. 33 SkASSERT(1 == proc.numAttribs()); 34 gpArgs->fPositionVar.set(4 == proc.numInputPoints() ? kFloat4_GrSLType : kFloat3_GrSLType, 35 proc.getAttrib(0).fName); 36 37 // Geometry shader. 38 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; 39 this->emitGeometryShader(proc, varyingHandler, args.fGeomBuilder, args.fRTAdjustName); 40 varyingHandler->emitAttributes(proc); 41 varyingHandler->setNoPerspective(); 42 SkASSERT(!args.fFPCoordTransformHandler->nextCoordTransform()); 43 44 // Fragment shader. 45 fShader->emitFragmentCode(proc, args.fFragBuilder, args.fOutputColor, args.fOutputCoverage); 46 } 47 48 void emitGeometryShader(const GrCCCoverageProcessor& proc, 49 GrGLSLVaryingHandler* varyingHandler, GrGLSLGeometryBuilder* g, 50 const char* rtAdjust) const { 51 int numInputPoints = proc.numInputPoints(); 52 SkASSERT(3 == numInputPoints || 4 == numInputPoints); 53 54 const char* posValues = (4 == numInputPoints) ? "sk_Position" : "sk_Position.xyz"; 55 g->codeAppendf("float%ix2 pts = transpose(float2x%i(sk_in[0].%s, sk_in[1].%s));", 56 numInputPoints, numInputPoints, posValues, posValues); 57 58 GrShaderVar wind("wind", kHalf_GrSLType); 59 g->declareGlobal(wind); 60 g->codeAppend ("float area_x2 = determinant(float2x2(pts[0] - pts[1], pts[0] - pts[2]));"); 61 if (4 == numInputPoints) { 62 g->codeAppend ("area_x2 += determinant(float2x2(pts[0] - pts[2], pts[0] - pts[3]));"); 63 } 64 g->codeAppendf("%s = sign(area_x2);", wind.c_str()); 65 66 SkString emitVertexFn; 67 SkSTArray<2, GrShaderVar> emitArgs; 68 const char* position = emitArgs.emplace_back("position", kFloat2_GrSLType).c_str(); 69 const char* coverage = nullptr; 70 if (RenderPass::kTriangleEdges == proc.fRenderPass) { 71 coverage = emitArgs.emplace_back("coverage", kHalf_GrSLType).c_str(); 72 } 73 g->emitFunction(kVoid_GrSLType, "emitVertex", emitArgs.count(), emitArgs.begin(), [&]() { 74 SkString fnBody; 75 fShader->emitVaryings(varyingHandler, GrGLSLVarying::Scope::kGeoToFrag, &fnBody, 76 position, coverage, wind.c_str()); 77 g->emitVertex(&fnBody, position, rtAdjust); 78 return fnBody; 79 }().c_str(), &emitVertexFn); 80 81 float bloat = kAABloatRadius; 82 #ifdef SK_DEBUG 83 if (proc.debugVisualizationsEnabled()) { 84 bloat *= proc.debugBloat(); 85 } 86 #endif 87 g->defineConstant("bloat", bloat); 88 89 this->onEmitGeometryShader(g, wind, emitVertexFn.c_str()); 90 } 91 92 virtual void onEmitGeometryShader(GrGLSLGeometryBuilder*, const GrShaderVar& wind, 93 const char* emitVertexFn) const = 0; 94 95 virtual ~GSImpl() {} 96 97 const std::unique_ptr<Shader> fShader; 98 99 typedef GrGLSLGeometryProcessor INHERITED; 100 }; 101 102 /** 103 * Generates a conservative raster hull around a triangle. (See comments for RenderPass) 104 */ 105 class GSHull3Impl : public GrCCCoverageProcessor::GSImpl { 106 public: 107 GSHull3Impl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {} 108 109 void onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind, 110 const char* emitVertexFn) const override { 111 Shader::GeometryVars vars; 112 fShader->emitSetupCode(g, "pts", nullptr, wind.c_str(), &vars); 113 114 const char* hullPts = vars.fHullVars.fAlternatePoints; 115 if (!hullPts) { 116 hullPts = "pts"; 117 } 118 119 // Visualize the input triangle as upright and equilateral, with a flat base. Paying special 120 // attention to wind, we can identify the points as top, bottom-left, and bottom-right. 121 // 122 // NOTE: We generate the hull in 2 independent invocations, so each invocation designates 123 // the corner it will begin with as the top. 124 g->codeAppendf("int i = %s > 0 ? sk_InvocationID : 1 - sk_InvocationID;", wind.c_str()); 125 g->codeAppendf("float2 top = %s[i];", hullPts); 126 g->codeAppendf("float2 left = %s[%s > 0 ? (1 - i) * 2 : i + 1];", hullPts, wind.c_str()); 127 g->codeAppendf("float2 right = %s[%s > 0 ? i + 1 : (1 - i) * 2];", hullPts, wind.c_str()); 128 129 // Determine how much to outset the conservative raster hull from each of the three edges. 130 g->codeAppend ("float2 leftbloat = float2(top.y > left.y ? +bloat : -bloat, " 131 "top.x > left.x ? -bloat : +bloat);"); 132 g->codeAppend ("float2 rightbloat = float2(right.y > top.y ? +bloat : -bloat, " 133 "right.x > top.x ? -bloat : +bloat);"); 134 g->codeAppend ("float2 downbloat = float2(left.y > right.y ? +bloat : -bloat, " 135 "left.x > right.x ? -bloat : +bloat);"); 136 137 // Here we generate the conservative raster geometry. It is the convex hull of 3 pixel-size 138 // boxes centered on the input points, split between two invocations. This translates to a 139 // polygon with either one, two, or three vertices at each input point, depending on how 140 // sharp the corner is. For more details on conservative raster, see: 141 // https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html 142 g->codeAppendf("bool2 left_right_notequal = notEqual(leftbloat, rightbloat);"); 143 g->codeAppend ("if (all(left_right_notequal)) {"); 144 // The top corner will have three conservative raster vertices. Emit the 145 // middle one first to the triangle strip. 146 g->codeAppendf( "%s(top + float2(-leftbloat.y, leftbloat.x));", emitVertexFn); 147 g->codeAppend ("}"); 148 g->codeAppend ("if (any(left_right_notequal)) {"); 149 // Second conservative raster vertex for the top corner. 150 g->codeAppendf( "%s(top + rightbloat);", emitVertexFn); 151 g->codeAppend ("}"); 152 153 // Main interior body of the triangle. 154 g->codeAppendf("%s(top + leftbloat);", emitVertexFn); 155 g->codeAppendf("%s(right + rightbloat);", emitVertexFn); 156 157 // Here the two invocations diverge. We can't symmetrically divide three triangle points 158 // between two invocations, so each does the following: 159 // 160 // sk_InvocationID=0: Finishes the main interior body of the triangle. 161 // sk_InvocationID=1: Remaining two conservative raster vertices for the third corner. 162 g->codeAppendf("bool2 right_down_notequal = notEqual(rightbloat, downbloat);"); 163 g->codeAppend ("if (any(right_down_notequal) || 0 == sk_InvocationID) {"); 164 g->codeAppendf( "%s(sk_InvocationID == 0 ? left + leftbloat : right + downbloat);", 165 emitVertexFn); 166 g->codeAppend ("}"); 167 g->codeAppend ("if (all(right_down_notequal) && 0 != sk_InvocationID) {"); 168 g->codeAppendf( "%s(right + float2(-rightbloat.y, rightbloat.x));", emitVertexFn); 169 g->codeAppend ("}"); 170 171 g->configure(InputType::kLines, OutputType::kTriangleStrip, 6, 2); 172 } 173 }; 174 175 /** 176 * Generates a conservative raster hull around a convex quadrilateral. (See comments for RenderPass) 177 */ 178 class GSHull4Impl : public GrCCCoverageProcessor::GSImpl { 179 public: 180 GSHull4Impl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {} 181 182 void onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind, 183 const char* emitVertexFn) const override { 184 Shader::GeometryVars vars; 185 fShader->emitSetupCode(g, "pts", nullptr, wind.c_str(), &vars); 186 187 const char* hullPts = vars.fHullVars.fAlternatePoints; 188 if (!hullPts) { 189 hullPts = "pts"; 190 } 191 192 // Visualize the input (convex) quadrilateral as a square. Paying special attention to wind, 193 // we can identify the points by their corresponding corner. 194 // 195 // NOTE: We split the square down the diagonal from top-right to bottom-left, and generate 196 // the hull in two independent invocations. Each invocation designates the corner it will 197 // begin with as top-left. 198 g->codeAppend ("int i = sk_InvocationID * 2;"); 199 g->codeAppendf("float2 topleft = %s[i];", hullPts); 200 g->codeAppendf("float2 topright = %s[%s > 0 ? i + 1 : 3 - i];", hullPts, wind.c_str()); 201 g->codeAppendf("float2 bottomleft = %s[%s > 0 ? 3 - i : i + 1];", hullPts, wind.c_str()); 202 g->codeAppendf("float2 bottomright = %s[2 - i];", hullPts); 203 204 // Determine how much to outset the conservative raster hull from the relevant edges. 205 g->codeAppend ("float2 leftbloat = float2(topleft.y > bottomleft.y ? +bloat : -bloat, " 206 "topleft.x > bottomleft.x ? -bloat : bloat);"); 207 g->codeAppend ("float2 upbloat = float2(topright.y > topleft.y ? +bloat : -bloat, " 208 "topright.x > topleft.x ? -bloat : +bloat);"); 209 g->codeAppend ("float2 rightbloat = float2(bottomright.y > topright.y ? +bloat : -bloat, " 210 "bottomright.x > topright.x ? -bloat : +bloat);"); 211 212 // Here we generate the conservative raster geometry. It is the convex hull of 4 pixel-size 213 // boxes centered on the input points, split evenly between two invocations. This translates 214 // to a polygon with either one, two, or three vertices at each input point, depending on 215 // how sharp the corner is. For more details on conservative raster, see: 216 // https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html 217 g->codeAppendf("bool2 left_up_notequal = notEqual(leftbloat, upbloat);"); 218 g->codeAppend ("if (all(left_up_notequal)) {"); 219 // The top-left corner will have three conservative raster vertices. 220 // Emit the middle one first to the triangle strip. 221 g->codeAppendf( "%s(topleft + float2(-leftbloat.y, leftbloat.x));", emitVertexFn); 222 g->codeAppend ("}"); 223 g->codeAppend ("if (any(left_up_notequal)) {"); 224 // Second conservative raster vertex for the top-left corner. 225 g->codeAppendf( "%s(topleft + leftbloat);", emitVertexFn); 226 g->codeAppend ("}"); 227 228 // Main interior body of this invocation's half of the hull. 229 g->codeAppendf("%s(topleft + upbloat);", emitVertexFn); 230 g->codeAppendf("%s(bottomleft + leftbloat);", emitVertexFn); 231 g->codeAppendf("%s(topright + upbloat);", emitVertexFn); 232 233 // Remaining two conservative raster vertices for the top-right corner. 234 g->codeAppendf("bool2 up_right_notequal = notEqual(upbloat, rightbloat);"); 235 g->codeAppend ("if (any(up_right_notequal)) {"); 236 g->codeAppendf( "%s(topright + rightbloat);", emitVertexFn); 237 g->codeAppend ("}"); 238 g->codeAppend ("if (all(up_right_notequal)) {"); 239 g->codeAppendf( "%s(topright + float2(-upbloat.y, upbloat.x));", emitVertexFn); 240 g->codeAppend ("}"); 241 242 g->configure(InputType::kLines, OutputType::kTriangleStrip, 7, 2); 243 } 244 }; 245 246 /** 247 * Generates conservatives around each edge of a triangle. (See comments for RenderPass) 248 */ 249 class GSEdgeImpl : public GrCCCoverageProcessor::GSImpl { 250 public: 251 GSEdgeImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {} 252 253 void onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind, 254 const char* emitVertexFn) const override { 255 fShader->emitSetupCode(g, "pts", "sk_InvocationID", wind.c_str(), nullptr); 256 257 g->codeAppend ("int nextidx = 2 != sk_InvocationID ? sk_InvocationID + 1 : 0;"); 258 g->codeAppendf("float2 left = pts[%s > 0 ? sk_InvocationID : nextidx];", wind.c_str()); 259 g->codeAppendf("float2 right = pts[%s > 0 ? nextidx : sk_InvocationID];", wind.c_str()); 260 261 Shader::EmitEdgeDistanceEquation(g, "left", "right", "float3 edge_distance_equation"); 262 263 // Which quadrant does the vector from left -> right fall into? 264 g->codeAppend ("float2 qlr = sign(right - left);"); 265 g->codeAppend ("float2x2 outer_pts = float2x2(left - bloat * qlr, right + bloat * qlr);"); 266 g->codeAppend ("half2 outer_coverage = edge_distance_equation.xy * outer_pts + " 267 "edge_distance_equation.z;"); 268 269 g->codeAppend ("float2 d1 = float2(qlr.y, -qlr.x);"); 270 g->codeAppend ("float2 d2 = d1;"); 271 g->codeAppend ("bool aligned = qlr.x == 0 || qlr.y == 0;"); 272 g->codeAppend ("if (aligned) {"); 273 g->codeAppend ( "d1 -= qlr;"); 274 g->codeAppend ( "d2 += qlr;"); 275 g->codeAppend ("}"); 276 277 // Emit the convex hull of 2 pixel-size boxes centered on the endpoints of the edge. Each 278 // invocation emits a different edge. Emit negative coverage that subtracts the appropiate 279 // amount back out from the hull we drew above. 280 g->codeAppend ("if (!aligned) {"); 281 g->codeAppendf( "%s(outer_pts[0], outer_coverage[0]);", emitVertexFn); 282 g->codeAppend ("}"); 283 g->codeAppendf("%s(left + bloat * d1, -1);", emitVertexFn); 284 g->codeAppendf("%s(left - bloat * d2, 0);", emitVertexFn); 285 g->codeAppendf("%s(right + bloat * d2, -1);", emitVertexFn); 286 g->codeAppendf("%s(right - bloat * d1, 0);", emitVertexFn); 287 g->codeAppend ("if (!aligned) {"); 288 g->codeAppendf( "%s(outer_pts[1], outer_coverage[1]);", emitVertexFn); 289 g->codeAppend ("}"); 290 291 g->configure(InputType::kLines, OutputType::kTriangleStrip, 6, 3); 292 } 293 }; 294 295 /** 296 * Generates conservative rasters around corners. (See comments for RenderPass) 297 */ 298 class GSCornerImpl : public GrCCCoverageProcessor::GSImpl { 299 public: 300 GSCornerImpl(std::unique_ptr<Shader> shader, int numCorners) 301 : GSImpl(std::move(shader)), fNumCorners(numCorners) {} 302 303 void onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind, 304 const char* emitVertexFn) const override { 305 Shader::GeometryVars vars; 306 fShader->emitSetupCode(g, "pts", "sk_InvocationID", wind.c_str(), &vars); 307 308 const char* corner = vars.fCornerVars.fPoint; 309 SkASSERT(corner); 310 311 g->codeAppendf("%s(%s + float2(-bloat, -bloat));", emitVertexFn, corner); 312 g->codeAppendf("%s(%s + float2(-bloat, +bloat));", emitVertexFn, corner); 313 g->codeAppendf("%s(%s + float2(+bloat, -bloat));", emitVertexFn, corner); 314 g->codeAppendf("%s(%s + float2(+bloat, +bloat));", emitVertexFn, corner); 315 316 g->configure(InputType::kLines, OutputType::kTriangleStrip, 4, fNumCorners); 317 } 318 319 private: 320 const int fNumCorners; 321 }; 322 323 void GrCCCoverageProcessor::initGS() { 324 SkASSERT(Impl::kGeometryShader == fImpl); 325 if (RenderPassIsCubic(fRenderPass)) { 326 this->addVertexAttrib("x_or_y_values", kFloat4_GrVertexAttribType); // (See appendMesh.) 327 SkASSERT(sizeof(CubicInstance) == this->getVertexStride() * 2); 328 } else { 329 this->addVertexAttrib("x_or_y_values", kFloat3_GrVertexAttribType); // (See appendMesh.) 330 SkASSERT(sizeof(TriangleInstance) == this->getVertexStride() * 2); 331 } 332 this->setWillUseGeoShader(); 333 } 334 335 void GrCCCoverageProcessor::appendGSMesh(GrBuffer* instanceBuffer, int instanceCount, 336 int baseInstance, SkTArray<GrMesh>* out) const { 337 // GSImpl doesn't actually make instanced draw calls. Instead, we feed transposed x,y point 338 // values to the GPU in a regular vertex array and draw kLines (see initGS). Then, each vertex 339 // invocation receives either the shape's x or y values as inputs, which it forwards to the 340 // geometry shader. 341 SkASSERT(Impl::kGeometryShader == fImpl); 342 GrMesh& mesh = out->emplace_back(GrPrimitiveType::kLines); 343 mesh.setNonIndexedNonInstanced(instanceCount * 2); 344 mesh.setVertexData(instanceBuffer, baseInstance * 2); 345 } 346 347 GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGSImpl(std::unique_ptr<Shader> shadr) const { 348 switch (fRenderPass) { 349 case RenderPass::kTriangleHulls: 350 return new GSHull3Impl(std::move(shadr)); 351 case RenderPass::kQuadraticHulls: 352 case RenderPass::kCubicHulls: 353 return new GSHull4Impl(std::move(shadr)); 354 case RenderPass::kTriangleEdges: 355 return new GSEdgeImpl(std::move(shadr)); 356 case RenderPass::kTriangleCorners: 357 return new GSCornerImpl(std::move(shadr), 3); 358 case RenderPass::kQuadraticCorners: 359 case RenderPass::kCubicCorners: 360 return new GSCornerImpl(std::move(shadr), 2); 361 } 362 SK_ABORT("Invalid RenderPass"); 363 return nullptr; 364 } 365