Chris Dalton | 6a3dbee | 2017-10-16 10:44: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 "GrCCPRTriangleShader.h" |
| 9 | |
| 10 | #include "glsl/GrGLSLFragmentShaderBuilder.h" |
Chris Dalton | c17bf32 | 2017-10-24 10:59:03 -0600 | [diff] [blame] | 11 | #include "glsl/GrGLSLVertexGeoBuilder.h" |
Chris Dalton | 6a3dbee | 2017-10-16 10:44:41 -0600 | [diff] [blame] | 12 | |
| 13 | void GrCCPRTriangleShader::appendInputPointFetch(const GrCCPRCoverageProcessor& proc, |
| 14 | GrGLSLShaderBuilder* s, |
| 15 | const TexelBufferHandle& pointsBuffer, |
| 16 | const char* pointId) const { |
| 17 | s->appendTexelFetch(pointsBuffer, |
| 18 | SkStringPrintf("%s[%s]", proc.instanceAttrib(), pointId).c_str()); |
| 19 | } |
| 20 | |
Chris Dalton | c17bf32 | 2017-10-24 10:59:03 -0600 | [diff] [blame] | 21 | void GrCCPRTriangleShader::emitWind(GrGLSLShaderBuilder* s, const char* pts, |
| 22 | const char* outputWind) const { |
Chris Dalton | 6a3dbee | 2017-10-16 10:44:41 -0600 | [diff] [blame] | 23 | s->codeAppendf("%s = sign(determinant(float2x2(%s[1] - %s[0], %s[2] - %s[0])));", |
| 24 | outputWind, pts, pts, pts, pts); |
| 25 | } |
| 26 | |
| 27 | GrCCPRTriangleHullShader::WindHandling |
| 28 | GrCCPRTriangleHullShader::onEmitVaryings(GrGLSLVaryingHandler*, SkString* code, |
| 29 | const char* /*position*/, const char* /*coverage*/, |
| 30 | const char* /*wind*/) { |
| 31 | return WindHandling::kNotHandled; // No varyings.Let the base class handle wind. |
| 32 | } |
| 33 | |
| 34 | void GrCCPRTriangleHullShader::onEmitFragmentCode(GrGLSLPPFragmentBuilder* f, |
| 35 | const char* outputCoverage) const { |
| 36 | f->codeAppendf("%s = 1;", outputCoverage); |
| 37 | } |
| 38 | |
| 39 | GrCCPRTriangleEdgeShader::WindHandling |
| 40 | GrCCPRTriangleEdgeShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, SkString* code, |
| 41 | const char* position, const char* coverage, |
| 42 | const char* wind) { |
Chris Dalton | fdde34e | 2017-10-16 14:15:26 -0600 | [diff] [blame] | 43 | varyingHandler->addVarying("coverage_times_wind", &fCoverageTimesWind); |
Chris Dalton | 6a3dbee | 2017-10-16 10:44:41 -0600 | [diff] [blame] | 44 | code->appendf("%s = %s * %s;", fCoverageTimesWind.gsOut(), coverage, wind); |
| 45 | return WindHandling::kHandled; |
| 46 | } |
| 47 | |
| 48 | void GrCCPRTriangleEdgeShader::onEmitFragmentCode(GrGLSLPPFragmentBuilder* f, |
| 49 | const char* outputCoverage) const { |
| 50 | f->codeAppendf("%s = %s;", outputCoverage, fCoverageTimesWind.fsIn()); |
| 51 | } |
| 52 | |
| 53 | void GrCCPRTriangleCornerShader::emitSetupCode(GrGLSLShaderBuilder* s, const char* pts, |
Chris Dalton | c17bf32 | 2017-10-24 10:59:03 -0600 | [diff] [blame] | 54 | const char* cornerId, const char* wind, |
Chris Dalton | 6a3dbee | 2017-10-16 10:44:41 -0600 | [diff] [blame] | 55 | GeometryVars* vars) const { |
| 56 | s->codeAppendf("float2 corner = %s[sk_InvocationID];", pts); |
| 57 | vars->fCornerVars.fPoint = "corner"; |
| 58 | |
| 59 | s->codeAppendf("float2x2 vectors = float2x2(corner - %s[(sk_InvocationID + 2) %% 3], " |
| 60 | "corner - %s[(sk_InvocationID + 1) %% 3]);", |
| 61 | pts, pts); |
| 62 | |
| 63 | // Make sure neither vector is 0 to avoid a divide-by-zero. Wind will be zero anyway if this |
| 64 | // is the case, so whatever we output won't have any effect as long it isn't NaN or Inf. |
| 65 | s->codeAppend ("for (int i = 0; i < 2; ++i) {"); |
| 66 | s->codeAppend ( "vectors[i] = (vectors[i] != float2(0)) ? vectors[i] : float2(1);"); |
| 67 | s->codeAppend ("}"); |
| 68 | |
| 69 | // Find the vector that bisects the region outside the incoming edges. Each edge is |
| 70 | // responsible to subtract the outside region on its own the side of the bisector. |
| 71 | s->codeAppendf("float2 leftdir = normalize(vectors[%s > 0 ? 0 : 1]);", wind); |
| 72 | s->codeAppendf("float2 rightdir = normalize(vectors[%s > 0 ? 1 : 0]);", wind); |
| 73 | s->codeAppend ("float2 bisect = dot(leftdir, rightdir) >= 0 ? " |
| 74 | "leftdir + rightdir : " |
| 75 | "float2(leftdir.y - rightdir.y, rightdir.x - leftdir.x);"); |
| 76 | |
| 77 | // In ccpr we don't calculate exact geometric pixel coverage. What the distance-to-edge |
| 78 | // method actually finds is coverage inside a logical "AA box", one that is rotated inline |
| 79 | // with the edge, and in our case, up-scaled to circumscribe the actual pixel. Below we set |
| 80 | // up transformations into normalized logical AA box space for both incoming edges. These |
| 81 | // will tell the fragment shader where the corner is located within each edge's AA box. |
| 82 | s->declareGlobal(fAABoxMatrices); |
| 83 | s->declareGlobal(fAABoxTranslates); |
| 84 | s->declareGlobal(fGeoShaderBisects); |
| 85 | s->codeAppendf("for (int i = 0; i < 2; ++i) {"); |
| 86 | // The X component runs parallel to the edge (i.e. distance to the corner). |
| 87 | s->codeAppendf( "float2 n = -vectors[%s > 0 ? i : 1 - i];", wind); |
Chris Dalton | c17bf32 | 2017-10-24 10:59:03 -0600 | [diff] [blame] | 88 | s->codeAppend ( "float nwidth = (abs(n.x) + abs(n.y)) * (bloat * 2);"); |
Chris Dalton | 6a3dbee | 2017-10-16 10:44:41 -0600 | [diff] [blame] | 89 | s->codeAppend ( "n /= nwidth;"); // nwidth != 0 because both vectors != 0. |
| 90 | s->codeAppendf( "%s[i][0] = n;", fAABoxMatrices.c_str()); |
| 91 | s->codeAppendf( "%s[i][0] = -dot(n, corner) + .5;", fAABoxTranslates.c_str()); |
| 92 | |
| 93 | // The Y component runs perpendicular to the edge (i.e. distance-to-edge). |
Chris Dalton | 6a3dbee | 2017-10-16 10:44:41 -0600 | [diff] [blame] | 94 | s->codeAppend ( "n = (i == 0) ? float2(-n.y, n.x) : float2(n.y, -n.x);"); |
Chris Dalton | 6a3dbee | 2017-10-16 10:44:41 -0600 | [diff] [blame] | 95 | s->codeAppendf( "%s[i][1] = n;", fAABoxMatrices.c_str()); |
| 96 | s->codeAppendf( "%s[i][1] = -dot(n, corner) + .5;", fAABoxTranslates.c_str()); |
| 97 | |
| 98 | // Translate the bisector into logical AA box space. |
| 99 | // NOTE: Since the region outside two edges of a convex shape is in [180 deg, 360 deg], the |
| 100 | // bisector will therefore be in [90 deg, 180 deg]. Or, x >= 0 and y <= 0 in AA box space. |
| 101 | s->codeAppendf( "%s[i] = -bisect * %s[i];", |
| 102 | fGeoShaderBisects.c_str(), fAABoxMatrices.c_str()); |
| 103 | s->codeAppend ("}"); |
| 104 | } |
| 105 | |
| 106 | GrCCPRTriangleCornerShader::WindHandling |
| 107 | GrCCPRTriangleCornerShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, SkString* code, |
| 108 | const char* position, const char* /*coverage*/, |
| 109 | const char* /*wind*/) { |
| 110 | varyingHandler->addVarying("corner_location_in_aa_boxes", &fCornerLocationInAABoxes); |
| 111 | varyingHandler->addFlatVarying("bisect_in_aa_boxes", &fBisectInAABoxes); |
| 112 | code->appendf("for (int i = 0; i < 2; ++i) {"); |
| 113 | code->appendf( "%s[i] = %s * %s[i] + %s[i];", |
| 114 | fCornerLocationInAABoxes.gsOut(), position, fAABoxMatrices.c_str(), |
| 115 | fAABoxTranslates.c_str()); |
| 116 | code->appendf( "%s[i] = %s[i];", fBisectInAABoxes.gsOut(), fGeoShaderBisects.c_str()); |
| 117 | code->appendf("}"); |
| 118 | |
| 119 | return WindHandling::kNotHandled; |
| 120 | } |
| 121 | |
| 122 | void GrCCPRTriangleCornerShader::onEmitFragmentCode(GrGLSLPPFragmentBuilder* f, |
| 123 | const char* outputCoverage) const { |
| 124 | // By the time we reach this shader, the pixel is in the following state: |
| 125 | // |
| 126 | // 1. The hull shader has emitted a coverage of 1. |
| 127 | // 2. Both edges have subtracted the area on their outside. |
| 128 | // |
| 129 | // This generally works, but it is a problem for corner pixels. There is a region within |
| 130 | // corner pixels that is outside both edges at the same time. This means the region has been |
| 131 | // double subtracted (once by each edge). The purpose of this shader is to fix these corner |
| 132 | // pixels. |
| 133 | // |
| 134 | // More specifically, each edge redoes its coverage analysis so that it only subtracts the |
| 135 | // outside area that falls on its own side of the bisector line. |
| 136 | // |
| 137 | // NOTE: unless the edges fall on multiples of 90 deg from one another, they will have |
| 138 | // different AA boxes. (For an explanation of AA boxes, see comments in |
| 139 | // onEmitGeometryShader.) This means the coverage analysis will only be approximate. It |
| 140 | // seems acceptable, but if we want exact coverage we will need to switch to a more |
| 141 | // expensive model. |
| 142 | f->codeAppendf("for (int i = 0; i < 2; ++i) {"); // Loop through both edges. |
| 143 | f->codeAppendf( "half2 corner = %s[i];", fCornerLocationInAABoxes.fsIn()); |
| 144 | f->codeAppendf( "half2 bisect = %s[i];", fBisectInAABoxes.fsIn()); |
| 145 | |
| 146 | // Find the point at which the bisector exits the logical AA box. |
| 147 | // (The inequality works because bisect.x is known >= 0 and bisect.y is known <= 0.) |
| 148 | f->codeAppendf( "half2 d = half2(1 - corner.x, -corner.y);"); |
| 149 | f->codeAppendf( "half T = d.y * bisect.x >= d.x * bisect.y ? d.y / bisect.y " |
| 150 | ": d.x / bisect.x;"); |
| 151 | f->codeAppendf( "half2 exit = corner + bisect * T;"); |
| 152 | |
| 153 | // These lines combined (and the final multiply by .5) accomplish the following: |
| 154 | // 1. Add back the area beyond the corner that was subtracted out previously. |
| 155 | // 2. Subtract out the area beyond the corner, but under the bisector. |
| 156 | // The other edge will take care of the area on its own side of the bisector. |
| 157 | f->codeAppendf( "%s += (2 - corner.x - exit.x) * corner.y;", outputCoverage); |
| 158 | f->codeAppendf( "%s += (corner.x - 1) * exit.y;", outputCoverage); |
| 159 | f->codeAppendf("}"); |
| 160 | |
| 161 | f->codeAppendf("%s *= .5;", outputCoverage); |
| 162 | } |