blob: 6ed875822e43bb3c35923896e09620f81494c6cb [file] [log] [blame]
Chris Dalton6a3dbee2017-10-16 10:44:41 -06001/*
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
Chris Dalton383a2ef2018-01-08 17:21:41 -05008#include "GrCCTriangleShader.h"
Chris Dalton6a3dbee2017-10-16 10:44:41 -06009
10#include "glsl/GrGLSLFragmentShaderBuilder.h"
Chris Daltonc17bf322017-10-24 10:59:03 -060011#include "glsl/GrGLSLVertexGeoBuilder.h"
Chris Dalton6a3dbee2017-10-16 10:44:41 -060012
Chris Dalton383a2ef2018-01-08 17:21:41 -050013using Shader = GrCCCoverageProcessor::Shader;
Chris Dalton6a3dbee2017-10-16 10:44:41 -060014
Chris Daltonf510e262018-01-30 16:42:37 -070015void GrCCTriangleShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
16 GrGLSLVarying::Scope scope, SkString* code,
17 const char* /*position*/, const char* inputCoverage,
18 const char* wind) {
Chris Dalton90e8fb12017-12-22 02:24:53 -070019 fCoverageTimesWind.reset(kHalf_GrSLType, scope);
Chris Daltonf510e262018-01-30 16:42:37 -070020 if (!inputCoverage) {
Chris Dalton7b046312018-02-02 11:06:30 -070021 varyingHandler->addVarying("wind", &fCoverageTimesWind,
22 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
Chris Dalton90e8fb12017-12-22 02:24:53 -070023 code->appendf("%s = %s;", OutName(fCoverageTimesWind), wind);
Chris Daltonde5a8142017-12-18 10:05:15 -070024 } else {
25 varyingHandler->addVarying("coverage_times_wind", &fCoverageTimesWind);
Chris Daltonf510e262018-01-30 16:42:37 -070026 code->appendf("%s = %s * %s;", OutName(fCoverageTimesWind), inputCoverage, wind);
Chris Daltonde5a8142017-12-18 10:05:15 -070027 }
Chris Dalton6a3dbee2017-10-16 10:44:41 -060028}
29
Robert Phillips7f861922018-01-30 13:13:42 +000030void GrCCTriangleShader::onEmitFragmentCode(GrGLSLPPFragmentBuilder* f,
Chris Dalton383a2ef2018-01-08 17:21:41 -050031 const char* outputCoverage) const {
Chris Dalton6a3dbee2017-10-16 10:44:41 -060032 f->codeAppendf("%s = %s;", outputCoverage, fCoverageTimesWind.fsIn());
33}
34
Chris Dalton383a2ef2018-01-08 17:21:41 -050035void GrCCTriangleCornerShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts,
36 const char* repetitionID, const char* wind,
37 GeometryVars* vars) const {
Chris Dalton1fbdb612017-12-12 12:48:47 -070038 s->codeAppendf("float2 corner = %s[%s];", pts, repetitionID);
Chris Dalton6a3dbee2017-10-16 10:44:41 -060039 vars->fCornerVars.fPoint = "corner";
40
Chris Dalton1fbdb612017-12-12 12:48:47 -070041 s->codeAppendf("float2x2 vectors = float2x2(corner - %s[0 != %s ? %s - 1 : 2], "
42 "corner - %s[2 != %s ? %s + 1 : 0]);",
43 pts, repetitionID, repetitionID, pts, repetitionID,
44 repetitionID);
Chris Dalton6a3dbee2017-10-16 10:44:41 -060045
46 // Make sure neither vector is 0 to avoid a divide-by-zero. Wind will be zero anyway if this
47 // is the case, so whatever we output won't have any effect as long it isn't NaN or Inf.
48 s->codeAppend ("for (int i = 0; i < 2; ++i) {");
49 s->codeAppend ( "vectors[i] = (vectors[i] != float2(0)) ? vectors[i] : float2(1);");
50 s->codeAppend ("}");
51
52 // Find the vector that bisects the region outside the incoming edges. Each edge is
53 // responsible to subtract the outside region on its own the side of the bisector.
54 s->codeAppendf("float2 leftdir = normalize(vectors[%s > 0 ? 0 : 1]);", wind);
55 s->codeAppendf("float2 rightdir = normalize(vectors[%s > 0 ? 1 : 0]);", wind);
56 s->codeAppend ("float2 bisect = dot(leftdir, rightdir) >= 0 ? "
57 "leftdir + rightdir : "
58 "float2(leftdir.y - rightdir.y, rightdir.x - leftdir.x);");
59
60 // In ccpr we don't calculate exact geometric pixel coverage. What the distance-to-edge
61 // method actually finds is coverage inside a logical "AA box", one that is rotated inline
62 // with the edge, and in our case, up-scaled to circumscribe the actual pixel. Below we set
63 // up transformations into normalized logical AA box space for both incoming edges. These
64 // will tell the fragment shader where the corner is located within each edge's AA box.
65 s->declareGlobal(fAABoxMatrices);
66 s->declareGlobal(fAABoxTranslates);
67 s->declareGlobal(fGeoShaderBisects);
68 s->codeAppendf("for (int i = 0; i < 2; ++i) {");
69 // The X component runs parallel to the edge (i.e. distance to the corner).
70 s->codeAppendf( "float2 n = -vectors[%s > 0 ? i : 1 - i];", wind);
Chris Daltonc17bf322017-10-24 10:59:03 -060071 s->codeAppend ( "float nwidth = (abs(n.x) + abs(n.y)) * (bloat * 2);");
Chris Dalton6a3dbee2017-10-16 10:44:41 -060072 s->codeAppend ( "n /= nwidth;"); // nwidth != 0 because both vectors != 0.
73 s->codeAppendf( "%s[i][0] = n;", fAABoxMatrices.c_str());
74 s->codeAppendf( "%s[i][0] = -dot(n, corner) + .5;", fAABoxTranslates.c_str());
75
76 // The Y component runs perpendicular to the edge (i.e. distance-to-edge).
Chris Dalton6a3dbee2017-10-16 10:44:41 -060077 s->codeAppend ( "n = (i == 0) ? float2(-n.y, n.x) : float2(n.y, -n.x);");
Chris Dalton6a3dbee2017-10-16 10:44:41 -060078 s->codeAppendf( "%s[i][1] = n;", fAABoxMatrices.c_str());
79 s->codeAppendf( "%s[i][1] = -dot(n, corner) + .5;", fAABoxTranslates.c_str());
80
81 // Translate the bisector into logical AA box space.
82 // NOTE: Since the region outside two edges of a convex shape is in [180 deg, 360 deg], the
83 // bisector will therefore be in [90 deg, 180 deg]. Or, x >= 0 and y <= 0 in AA box space.
84 s->codeAppendf( "%s[i] = -bisect * %s[i];",
85 fGeoShaderBisects.c_str(), fAABoxMatrices.c_str());
86 s->codeAppend ("}");
87}
88
Chris Daltonf510e262018-01-30 16:42:37 -070089void GrCCTriangleCornerShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
90 GrGLSLVarying::Scope scope, SkString* code,
91 const char* position, const char* inputCoverage,
92 const char* wind) {
Chris Dalton7b046312018-02-02 11:06:30 -070093 using Interpolation = GrGLSLVaryingHandler::Interpolation;
Chris Daltonf510e262018-01-30 16:42:37 -070094 SkASSERT(!inputCoverage);
Chris Daltonde5a8142017-12-18 10:05:15 -070095
Chris Dalton90e8fb12017-12-22 02:24:53 -070096 fCornerLocationInAABoxes.reset(kFloat2x2_GrSLType, scope);
Chris Dalton6a3dbee2017-10-16 10:44:41 -060097 varyingHandler->addVarying("corner_location_in_aa_boxes", &fCornerLocationInAABoxes);
Chris Dalton90e8fb12017-12-22 02:24:53 -070098
99 fBisectInAABoxes.reset(kFloat2x2_GrSLType, scope);
Chris Dalton7b046312018-02-02 11:06:30 -0700100 varyingHandler->addVarying("bisect_in_aa_boxes", &fBisectInAABoxes, Interpolation::kCanBeFlat);
Chris Dalton90e8fb12017-12-22 02:24:53 -0700101
Chris Dalton6a3dbee2017-10-16 10:44:41 -0600102 code->appendf("for (int i = 0; i < 2; ++i) {");
103 code->appendf( "%s[i] = %s * %s[i] + %s[i];",
Chris Dalton90e8fb12017-12-22 02:24:53 -0700104 OutName(fCornerLocationInAABoxes), position, fAABoxMatrices.c_str(),
Chris Dalton6a3dbee2017-10-16 10:44:41 -0600105 fAABoxTranslates.c_str());
Chris Dalton90e8fb12017-12-22 02:24:53 -0700106 code->appendf( "%s[i] = %s[i];", OutName(fBisectInAABoxes), fGeoShaderBisects.c_str());
Chris Dalton6a3dbee2017-10-16 10:44:41 -0600107 code->appendf("}");
108
Chris Daltonf510e262018-01-30 16:42:37 -0700109 fWindTimesHalf.reset(kHalf_GrSLType, scope);
Chris Dalton7b046312018-02-02 11:06:30 -0700110 varyingHandler->addVarying("wind_times_half", &fWindTimesHalf, Interpolation::kCanBeFlat);
Chris Daltonf510e262018-01-30 16:42:37 -0700111 code->appendf("%s = %s * .5;", OutName(fWindTimesHalf), wind);
Chris Dalton6a3dbee2017-10-16 10:44:41 -0600112}
113
Robert Phillips7f861922018-01-30 13:13:42 +0000114void GrCCTriangleCornerShader::onEmitFragmentCode(GrGLSLPPFragmentBuilder* f,
Chris Dalton383a2ef2018-01-08 17:21:41 -0500115 const char* outputCoverage) const {
Chris Dalton6a3dbee2017-10-16 10:44:41 -0600116 // By the time we reach this shader, the pixel is in the following state:
117 //
118 // 1. The hull shader has emitted a coverage of 1.
119 // 2. Both edges have subtracted the area on their outside.
120 //
121 // This generally works, but it is a problem for corner pixels. There is a region within
122 // corner pixels that is outside both edges at the same time. This means the region has been
123 // double subtracted (once by each edge). The purpose of this shader is to fix these corner
124 // pixels.
125 //
126 // More specifically, each edge redoes its coverage analysis so that it only subtracts the
127 // outside area that falls on its own side of the bisector line.
128 //
129 // NOTE: unless the edges fall on multiples of 90 deg from one another, they will have
130 // different AA boxes. (For an explanation of AA boxes, see comments in
131 // onEmitGeometryShader.) This means the coverage analysis will only be approximate. It
132 // seems acceptable, but if we want exact coverage we will need to switch to a more
133 // expensive model.
134 f->codeAppendf("for (int i = 0; i < 2; ++i) {"); // Loop through both edges.
135 f->codeAppendf( "half2 corner = %s[i];", fCornerLocationInAABoxes.fsIn());
136 f->codeAppendf( "half2 bisect = %s[i];", fBisectInAABoxes.fsIn());
137
138 // Find the point at which the bisector exits the logical AA box.
139 // (The inequality works because bisect.x is known >= 0 and bisect.y is known <= 0.)
140 f->codeAppendf( "half2 d = half2(1 - corner.x, -corner.y);");
141 f->codeAppendf( "half T = d.y * bisect.x >= d.x * bisect.y ? d.y / bisect.y "
142 ": d.x / bisect.x;");
143 f->codeAppendf( "half2 exit = corner + bisect * T;");
144
145 // These lines combined (and the final multiply by .5) accomplish the following:
146 // 1. Add back the area beyond the corner that was subtracted out previously.
147 // 2. Subtract out the area beyond the corner, but under the bisector.
148 // The other edge will take care of the area on its own side of the bisector.
149 f->codeAppendf( "%s += (2 - corner.x - exit.x) * corner.y;", outputCoverage);
150 f->codeAppendf( "%s += (corner.x - 1) * exit.y;", outputCoverage);
151 f->codeAppendf("}");
152
Chris Daltonf510e262018-01-30 16:42:37 -0700153 f->codeAppendf("%s *= %s;", outputCoverage, fWindTimesHalf.fsIn());
Chris Dalton6a3dbee2017-10-16 10:44:41 -0600154}