Home | History | Annotate | Download | only in ccpr
      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