Chris Dalton | 1a325d2 | 2017-07-14 15:17:41 -0600 | [diff] [blame] | 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 "GrCCPRTriangleProcessor.h" |
| 9 | |
| 10 | #include "glsl/GrGLSLFragmentShaderBuilder.h" |
| 11 | #include "glsl/GrGLSLGeometryShaderBuilder.h" |
| 12 | #include "glsl/GrGLSLVertexShaderBuilder.h" |
| 13 | |
| 14 | void GrCCPRTriangleProcessor::onEmitVertexShader(const GrCCPRCoverageProcessor& proc, |
| 15 | GrGLSLVertexBuilder* v, |
| 16 | const TexelBufferHandle& pointsBuffer, |
| 17 | const char* atlasOffset, const char* rtAdjust, |
| 18 | GrGPArgs* gpArgs) const { |
Chris Dalton | 29f642a | 2017-10-02 13:17:33 -0600 | [diff] [blame] | 19 | // Copy the input attrib to an intermediate array. The Intel GLSL compiler hits an internal |
| 20 | // assertion if we index the input attrib itself with sk_VertexID. |
| 21 | v->codeAppendf("int indices[3] = int[3](%s.x, %s.y, %s.z);", |
| 22 | proc.instanceAttrib(), proc.instanceAttrib(), proc.instanceAttrib()); |
Ethan Nicholas | 8aa4569 | 2017-09-20 11:24:15 -0400 | [diff] [blame] | 23 | v->codeAppend ("float2 self = "); |
Chris Dalton | 29f642a | 2017-10-02 13:17:33 -0600 | [diff] [blame] | 24 | v->appendTexelFetch(pointsBuffer, "indices[sk_VertexID]"); |
Chris Dalton | 1a325d2 | 2017-07-14 15:17:41 -0600 | [diff] [blame] | 25 | v->codeAppendf(".xy + %s;", atlasOffset); |
Ethan Nicholas | 8aa4569 | 2017-09-20 11:24:15 -0400 | [diff] [blame] | 26 | gpArgs->fPositionVar.set(kFloat2_GrSLType, "self"); |
Chris Dalton | 1a325d2 | 2017-07-14 15:17:41 -0600 | [diff] [blame] | 27 | } |
| 28 | |
| 29 | void GrCCPRTriangleProcessor::defineInputVertices(GrGLSLGeometryBuilder* g) const { |
| 30 | // Prepend in_vertices at the start of the shader. |
Ethan Nicholas | bed683a | 2017-09-26 14:23:59 -0400 | [diff] [blame] | 31 | g->codePrependf("float3x2 in_vertices = float3x2(sk_in[0].sk_Position.xy, " |
| 32 | "sk_in[1].sk_Position.xy, " |
| 33 | "sk_in[2].sk_Position.xy);"); |
Chris Dalton | 1a325d2 | 2017-07-14 15:17:41 -0600 | [diff] [blame] | 34 | } |
| 35 | |
| 36 | void GrCCPRTriangleProcessor::emitWind(GrGLSLGeometryBuilder* g, const char* /*rtAdjust*/, |
| 37 | const char* outputWind) const { |
| 38 | // We will define in_vertices in defineInputVertices. |
Ethan Nicholas | 8aa4569 | 2017-09-20 11:24:15 -0400 | [diff] [blame] | 39 | g->codeAppendf("%s = sign(determinant(float2x2(in_vertices[1] - in_vertices[0], " |
| 40 | "in_vertices[2] - in_vertices[0])));", |
Ethan Nicholas | 5af9ea3 | 2017-07-28 15:19:46 -0400 | [diff] [blame] | 41 | outputWind); |
Chris Dalton | 1a325d2 | 2017-07-14 15:17:41 -0600 | [diff] [blame] | 42 | } |
| 43 | |
| 44 | void GrCCPRTriangleHullAndEdgeProcessor::onEmitGeometryShader(GrGLSLGeometryBuilder* g, |
| 45 | const char* emitVertexFn, |
| 46 | const char* wind, |
| 47 | const char* rtAdjust) const { |
| 48 | this->defineInputVertices(g); |
| 49 | int maxOutputVertices = 0; |
| 50 | |
| 51 | if (GeometryType::kEdges != fGeometryType) { |
| 52 | maxOutputVertices += this->emitHullGeometry(g, emitVertexFn, "in_vertices", 3, |
| 53 | "sk_InvocationID"); |
| 54 | } |
| 55 | |
| 56 | if (GeometryType::kHulls != fGeometryType) { |
| 57 | g->codeAppend ("int edgeidx0 = sk_InvocationID, " |
| 58 | "edgeidx1 = (edgeidx0 + 1) % 3;"); |
Ethan Nicholas | 8aa4569 | 2017-09-20 11:24:15 -0400 | [diff] [blame] | 59 | g->codeAppendf("float2 edgept0 = in_vertices[%s > 0 ? edgeidx0 : edgeidx1];", wind); |
| 60 | g->codeAppendf("float2 edgept1 = in_vertices[%s > 0 ? edgeidx1 : edgeidx0];", wind); |
Chris Dalton | 1a325d2 | 2017-07-14 15:17:41 -0600 | [diff] [blame] | 61 | |
| 62 | maxOutputVertices += this->emitEdgeGeometry(g, emitVertexFn, "edgept0", "edgept1"); |
| 63 | } |
| 64 | |
| 65 | g->configure(GrGLSLGeometryBuilder::InputType::kTriangles, |
| 66 | GrGLSLGeometryBuilder::OutputType::kTriangleStrip, |
| 67 | maxOutputVertices, 3); |
| 68 | } |
| 69 | |
Chris Dalton | 1a325d2 | 2017-07-14 15:17:41 -0600 | [diff] [blame] | 70 | void GrCCPRTriangleCornerProcessor::onEmitGeometryShader(GrGLSLGeometryBuilder* g, |
| 71 | const char* emitVertexFn, const char* wind, |
| 72 | const char* rtAdjust) const { |
| 73 | this->defineInputVertices(g); |
| 74 | |
Ethan Nicholas | 8aa4569 | 2017-09-20 11:24:15 -0400 | [diff] [blame] | 75 | g->codeAppend ("float2 corner = in_vertices[sk_InvocationID];"); |
| 76 | g->codeAppend ("float2x2 vectors = float2x2(corner - in_vertices[(sk_InvocationID + 2) % 3], " |
| 77 | "corner - in_vertices[(sk_InvocationID + 1) % 3]);"); |
Chris Dalton | 71e3797 | 2017-09-18 22:23:53 -0600 | [diff] [blame] | 78 | |
| 79 | // Make sure neither vector is 0 in order to avoid a divide-by-zero. Wind will be zero anyway if |
| 80 | // this is the case, so whatever we output won't have any effect as long it isn't NaN or Inf. |
| 81 | g->codeAppendf("for (int i = 0; i < 2; ++i) {"); |
Ethan Nicholas | 8aa4569 | 2017-09-20 11:24:15 -0400 | [diff] [blame] | 82 | g->codeAppendf( "vectors[i] = any(notEqual(vectors[i], float2(0))) ? " |
| 83 | "vectors[i] : float2(1);"); |
Chris Dalton | 71e3797 | 2017-09-18 22:23:53 -0600 | [diff] [blame] | 84 | g->codeAppendf("}"); |
| 85 | |
| 86 | // Find the vector that bisects the region outside the incoming edges. Each edge is responsible |
| 87 | // to subtract the outside region on its own the side of the bisector. |
Ethan Nicholas | 8aa4569 | 2017-09-20 11:24:15 -0400 | [diff] [blame] | 88 | g->codeAppendf("float2 leftdir = normalize(vectors[%s > 0 ? 0 : 1]);", wind); |
| 89 | g->codeAppendf("float2 rightdir = normalize(vectors[%s > 0 ? 1 : 0]);", wind); |
| 90 | g->codeAppendf("float2 bisect = dot(leftdir, rightdir) >= 0 ? leftdir + rightdir : " |
| 91 | "float2(leftdir.y - rightdir.y, rightdir.x - leftdir.x);"); |
Chris Dalton | 71e3797 | 2017-09-18 22:23:53 -0600 | [diff] [blame] | 92 | |
| 93 | // In ccpr we don't calculate exact geometric pixel coverage. What the distance-to-edge method |
| 94 | // actually finds is coverage inside a logical "AA box", one that is rotated inline with the |
| 95 | // edge, and in our case, up-scaled to circumscribe the actual pixel. Below we set up |
| 96 | // transformations into normalized logical AA box space for both incoming edges. These will tell |
| 97 | // the fragment shader where the corner is located within each edge's AA box. |
| 98 | g->declareGlobal(fAABoxMatrices); |
| 99 | g->declareGlobal(fAABoxTranslates); |
| 100 | g->declareGlobal(fGeoShaderBisects); |
| 101 | g->codeAppendf("for (int i = 0; i < 2; ++i) {"); |
| 102 | // The X component runs parallel to the edge (i.e. distance to the corner). |
Ethan Nicholas | 8aa4569 | 2017-09-20 11:24:15 -0400 | [diff] [blame] | 103 | g->codeAppendf( "float2 n = -vectors[%s > 0 ? i : 1 - i];", wind); |
| 104 | g->codeAppendf( "float nwidth = dot(abs(n), bloat) * 2;"); |
Chris Dalton | 71e3797 | 2017-09-18 22:23:53 -0600 | [diff] [blame] | 105 | g->codeAppendf( "n /= nwidth;"); // nwidth != 0 because both vectors != 0. |
| 106 | g->codeAppendf( "%s[i][0] = n;", fAABoxMatrices.c_str()); |
| 107 | g->codeAppendf( "%s[i][0] = -dot(n, corner) + .5;", fAABoxTranslates.c_str()); |
| 108 | |
| 109 | // The Y component runs perpendicular to the edge (i.e. distance-to-edge). |
| 110 | // NOTE: once we are back in device space and bloat.x == bloat.y, we will not need to find and |
| 111 | // divide by nwidth a second time. |
Ethan Nicholas | 8aa4569 | 2017-09-20 11:24:15 -0400 | [diff] [blame] | 112 | g->codeAppendf( "n = (i == 0) ? float2(-n.y, n.x) : float2(n.y, -n.x);"); |
Chris Dalton | 71e3797 | 2017-09-18 22:23:53 -0600 | [diff] [blame] | 113 | g->codeAppendf( "nwidth = dot(abs(n), bloat) * 2;"); |
| 114 | g->codeAppendf( "n /= nwidth;"); |
| 115 | g->codeAppendf( "%s[i][1] = n;", fAABoxMatrices.c_str()); |
| 116 | g->codeAppendf( "%s[i][1] = -dot(n, corner) + .5;", fAABoxTranslates.c_str()); |
| 117 | |
| 118 | // Translate the bisector into logical AA box space. |
| 119 | // NOTE: Since the region outside two edges of a convex shape is in [180 deg, 360 deg], the |
| 120 | // bisector will therefore be in [90 deg, 180 deg]. Or, x >= 0 and y <= 0 in AA box space. |
| 121 | g->codeAppendf( "%s[i] = -bisect * %s[i];", |
| 122 | fGeoShaderBisects.c_str(), fAABoxMatrices.c_str()); |
| 123 | g->codeAppendf("}"); |
| 124 | |
| 125 | int numVertices = this->emitCornerGeometry(g, emitVertexFn, "corner"); |
Chris Dalton | 1a325d2 | 2017-07-14 15:17:41 -0600 | [diff] [blame] | 126 | |
| 127 | g->configure(GrGLSLGeometryBuilder::InputType::kTriangles, |
| 128 | GrGLSLGeometryBuilder::OutputType::kTriangleStrip, |
Chris Dalton | b072bb6 | 2017-08-07 09:00:46 -0600 | [diff] [blame] | 129 | numVertices, 3); |
Chris Dalton | 1a325d2 | 2017-07-14 15:17:41 -0600 | [diff] [blame] | 130 | } |
| 131 | |
| 132 | void GrCCPRTriangleCornerProcessor::emitPerVertexGeometryCode(SkString* fnBody, |
| 133 | const char* position, |
| 134 | const char* /*coverage*/, |
| 135 | const char* wind) const { |
Chris Dalton | 71e3797 | 2017-09-18 22:23:53 -0600 | [diff] [blame] | 136 | fnBody->appendf("for (int i = 0; i < 2; ++i) {"); |
| 137 | fnBody->appendf( "%s[i] = %s * %s[i] + %s[i];", |
| 138 | fCornerLocationInAABoxes.gsOut(), position, fAABoxMatrices.c_str(), |
| 139 | fAABoxTranslates.c_str()); |
| 140 | fnBody->appendf( "%s[i] = %s[i];", fBisectInAABoxes.gsOut(), fGeoShaderBisects.c_str()); |
| 141 | fnBody->appendf("}"); |
Chris Dalton | 1a325d2 | 2017-07-14 15:17:41 -0600 | [diff] [blame] | 142 | } |
| 143 | |
| 144 | void GrCCPRTriangleCornerProcessor::emitShaderCoverage(GrGLSLFragmentBuilder* f, |
| 145 | const char* outputCoverage) const { |
Chris Dalton | 71e3797 | 2017-09-18 22:23:53 -0600 | [diff] [blame] | 146 | // By the time we reach this shader, the pixel is in the following state: |
| 147 | // |
| 148 | // 1. The hull shader has emitted a coverage of 1. |
| 149 | // 2. Both edges have subtracted the area on their outside. |
| 150 | // |
| 151 | // This generally works, but it is a problem for corner pixels. There is a region within corner |
| 152 | // pixels that is outside both edges at the same time. This means the region has been double |
| 153 | // subtracted (once by each edge). The purpose of this shader is to fix these corner pixels. |
| 154 | // |
| 155 | // More specifically, each edge redoes its coverage analysis so that it only subtracts the |
| 156 | // outside area that falls on its own side of the bisector line. |
| 157 | // |
| 158 | // NOTE: unless the edges fall on multiples of 90 deg from one another, they will have different |
| 159 | // AA boxes. (For an explanation of AA boxes, see comments in onEmitGeometryShader.) This means |
| 160 | // the coverage analysis will only be approximate. It seems acceptable, but if we want exact |
| 161 | // coverage we will need to switch to a more expensive model. |
| 162 | f->codeAppendf("%s = 0;", outputCoverage); |
Chris Dalton | 1a325d2 | 2017-07-14 15:17:41 -0600 | [diff] [blame] | 163 | |
Chris Dalton | 71e3797 | 2017-09-18 22:23:53 -0600 | [diff] [blame] | 164 | // Loop through both edges. |
| 165 | f->codeAppendf("for (int i = 0; i < 2; ++i) {"); |
| 166 | f->codeAppendf( "half2 corner = %s[i];", fCornerLocationInAABoxes.fsIn()); |
| 167 | f->codeAppendf( "half2 bisect = %s[i];", fBisectInAABoxes.fsIn()); |
Chris Dalton | 1a325d2 | 2017-07-14 15:17:41 -0600 | [diff] [blame] | 168 | |
Chris Dalton | 71e3797 | 2017-09-18 22:23:53 -0600 | [diff] [blame] | 169 | // Find the point at which the bisector exits the logical AA box. |
| 170 | // (The inequality works because bisect.x is known >= 0 and bisect.y is known <= 0.) |
| 171 | f->codeAppendf( "half2 d = half2(1 - corner.x, -corner.y);"); |
| 172 | f->codeAppendf( "half T = d.y * bisect.x >= d.x * bisect.y ? d.y / bisect.y " |
| 173 | ": d.x / bisect.x;"); |
| 174 | f->codeAppendf( "half2 exit = corner + bisect * T;"); |
Chris Dalton | 1a325d2 | 2017-07-14 15:17:41 -0600 | [diff] [blame] | 175 | |
Chris Dalton | 71e3797 | 2017-09-18 22:23:53 -0600 | [diff] [blame] | 176 | // These lines combined (and the final multiply by .5) accomplish the following: |
| 177 | // 1. Add back the area beyond the corner that was subtracted out previously. |
| 178 | // 2. Subtract out the area beyond the corner, but under the bisector. |
| 179 | // The other edge will take care of the area on its own side of the bisector. |
| 180 | f->codeAppendf( "%s += (2 - corner.x - exit.x) * corner.y;", outputCoverage); |
| 181 | f->codeAppendf( "%s += (corner.x - 1) * exit.y;", outputCoverage); |
| 182 | f->codeAppendf("}"); |
Chris Dalton | 1a325d2 | 2017-07-14 15:17:41 -0600 | [diff] [blame] | 183 | |
Chris Dalton | 71e3797 | 2017-09-18 22:23:53 -0600 | [diff] [blame] | 184 | f->codeAppendf("%s *= .5;", outputCoverage); |
Chris Dalton | 1a325d2 | 2017-07-14 15:17:41 -0600 | [diff] [blame] | 185 | } |