aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu/ccpr/GrCCCoverageProcessor.cpp
blob: 75d0667b0922e76e4894a035da3b24c55d1eec8b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
/*
 * Copyright 2017 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "GrCCCoverageProcessor.h"

#include "GrGpuCommandBuffer.h"
#include "GrOpFlushState.h"
#include "SkMakeUnique.h"
#include "ccpr/GrCCConicShader.h"
#include "ccpr/GrCCCubicShader.h"
#include "ccpr/GrCCQuadraticShader.h"
#include "glsl/GrGLSLVertexGeoBuilder.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h"
#include "glsl/GrGLSLVertexGeoBuilder.h"

class GrCCCoverageProcessor::TriangleShader : public GrCCCoverageProcessor::Shader {
    void onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, GrGLSLVarying::Scope scope,
                        SkString* code, const char* position, const char* coverage,
                        const char* cornerCoverage) override {
        if (!cornerCoverage) {
            fCoverages.reset(kHalf_GrSLType, scope);
            varyingHandler->addVarying("coverage", &fCoverages);
            code->appendf("%s = %s;", OutName(fCoverages), coverage);
        } else {
            fCoverages.reset(kHalf3_GrSLType, scope);
            varyingHandler->addVarying("coverages", &fCoverages);
            code->appendf("%s = half3(%s, %s);", OutName(fCoverages), coverage, cornerCoverage);
        }
    }

    void onEmitFragmentCode(GrGLSLFPFragmentBuilder* f, const char* outputCoverage) const override {
        if (kHalf_GrSLType == fCoverages.type()) {
            f->codeAppendf("%s = %s;", outputCoverage, fCoverages.fsIn());
        } else {
            f->codeAppendf("%s = %s.z * %s.y + %s.x;",
                           outputCoverage, fCoverages.fsIn(), fCoverages.fsIn(), fCoverages.fsIn());
        }
    }

    GrGLSLVarying fCoverages;
};

void GrCCCoverageProcessor::Shader::CalcWind(const GrCCCoverageProcessor& proc,
                                             GrGLSLVertexGeoBuilder* s, const char* pts,
                                             const char* outputWind) {
    if (3 == proc.numInputPoints()) {
        s->codeAppendf("float2 a = %s[0] - %s[1], "
                              "b = %s[0] - %s[2];", pts, pts, pts, pts);
    } else {
        // All inputs are convex, so it's sufficient to just average the middle two input points.
        SkASSERT(4 == proc.numInputPoints());
        s->codeAppendf("float2 p12 = (%s[1] + %s[2]) * .5;", pts, pts);
        s->codeAppendf("float2 a = %s[0] - p12, "
                              "b = %s[0] - %s[3];", pts, pts, pts);
    }

    s->codeAppend ("float area_x2 = determinant(float2x2(a, b));");
    if (proc.isTriangles()) {
        // We cull extremely thin triangles by zeroing wind. When a triangle gets too thin it's
        // possible for FP round-off error to actually give us the wrong winding direction, causing
        // rendering artifacts. The criteria we choose is "height <~ 1/1024". So we drop a triangle
        // if the max effect it can have on any single pixel is <~ 1/1024, or 1/4 of a bit in 8888.
        s->codeAppend ("float2 bbox_size = max(abs(a), abs(b));");
        s->codeAppend ("float basewidth = max(bbox_size.x + bbox_size.y, 1);");
        s->codeAppendf("%s = (abs(area_x2 * 1024) > basewidth) ? sign(area_x2) : 0;", outputWind);
    } else {
        // We already converted nearly-flat curves to lines on the CPU, so no need to worry about
        // thin curve hulls at this point.
        s->codeAppendf("%s = sign(area_x2);", outputWind);
    }
}

void GrCCCoverageProcessor::Shader::EmitEdgeDistanceEquation(GrGLSLVertexGeoBuilder* s,
                                                             const char* leftPt,
                                                             const char* rightPt,
                                                             const char* outputDistanceEquation) {
    s->codeAppendf("float2 n = float2(%s.y - %s.y, %s.x - %s.x);",
                   rightPt, leftPt, leftPt, rightPt);
    s->codeAppend ("float nwidth = (abs(n.x) + abs(n.y)) * (bloat * 2);");
    // When nwidth=0, wind must also be 0 (and coverage * wind = 0). So it doesn't matter what we
    // come up with here as long as it isn't NaN or Inf.
    s->codeAppend ("n /= (0 != nwidth) ? nwidth : 1;");
    s->codeAppendf("%s = float3(-n, dot(n, %s) - .5);", outputDistanceEquation, leftPt);
}

void GrCCCoverageProcessor::Shader::CalcEdgeCoverageAtBloatVertex(GrGLSLVertexGeoBuilder* s,
                                                                  const char* leftPt,
                                                                  const char* rightPt,
                                                                  const char* rasterVertexDir,
                                                                  const char* outputCoverage) {
    // Here we find an edge's coverage at one corner of a conservative raster bloat box whose center
    // falls on the edge in question. (A bloat box is axis-aligned and the size of one pixel.) We
    // always set up coverage so it is -1 at the outermost corner, 0 at the innermost, and -.5 at
    // the center. Interpolated, these coverage values convert jagged conservative raster edges into
    // smooth antialiased edges.
    //
    // d1 == (P + sign(n) * bloat) dot n                   (Distance at the bloat box vertex whose
    //    == P dot n + (abs(n.x) + abs(n.y)) * bloatSize    coverage=-1, where the bloat box is
    //                                                      centered on P.)
    //
    // d0 == (P - sign(n) * bloat) dot n                   (Distance at the bloat box vertex whose
    //    == P dot n - (abs(n.x) + abs(n.y)) * bloatSize    coverage=0, where the bloat box is
    //                                                      centered on P.)
    //
    // d == (P + rasterVertexDir * bloatSize) dot n        (Distance at the bloat box vertex whose
    //   == P dot n + (rasterVertexDir dot n) * bloatSize   coverage we wish to calculate.)
    //
    // coverage == -(d - d0) / (d1 - d0)                   (coverage=-1 at d=d1; coverage=0 at d=d0)
    //
    //          == (rasterVertexDir dot n) / (abs(n.x) + abs(n.y)) * -.5 - .5
    //
    s->codeAppendf("float2 n = float2(%s.y - %s.y, %s.x - %s.x);",
                   rightPt, leftPt, leftPt, rightPt);
    s->codeAppend ("float nwidth = abs(n.x) + abs(n.y);");
    s->codeAppendf("float t = dot(%s, n);", rasterVertexDir);
    // The below conditional guarantees we get exactly 1 on the divide when nwidth=t (in case the
    // GPU divides by multiplying by the reciprocal?) It also guards against NaN when nwidth=0.
    s->codeAppendf("%s = (abs(t) != nwidth ? t / nwidth : sign(t)) * -.5 - .5;", outputCoverage);
}

void GrCCCoverageProcessor::Shader::CalcEdgeCoveragesAtBloatVertices(GrGLSLVertexGeoBuilder* s,
                                                                     const char* leftPt,
                                                                     const char* rightPt,
                                                                     const char* bloatDir1,
                                                                     const char* bloatDir2,
                                                                     const char* outputCoverages) {
    // See comments in CalcEdgeCoverageAtBloatVertex.
    s->codeAppendf("float2 n = float2(%s.y - %s.y, %s.x - %s.x);",
                   rightPt, leftPt, leftPt, rightPt);
    s->codeAppend ("float nwidth = abs(n.x) + abs(n.y);");
    s->codeAppendf("float2 t = n * float2x2(%s, %s);", bloatDir1, bloatDir2);
    s->codeAppendf("for (int i = 0; i < 2; ++i) {");
    s->codeAppendf(    "%s[i] = (abs(t[i]) != nwidth ? t[i] / nwidth : sign(t[i])) * -.5 - .5;",
                       outputCoverages);
    s->codeAppendf("}");
}

void GrCCCoverageProcessor::Shader::CalcCornerAttenuation(GrGLSLVertexGeoBuilder* s,
                                                          const char* leftDir, const char* rightDir,
                                                          const char* outputAttenuation) {
    // obtuseness = cos(corner_angle)  if corner_angle > 90 degrees
    //                              0  if corner_angle <= 90 degrees
    s->codeAppendf("half obtuseness = max(dot(%s, %s), 0);", leftDir, rightDir);

    // axis_alignedness = 1  when the leftDir/rightDir bisector is aligned with the x- or y-axis
    //                    0  when the bisector falls on a 45 degree angle
    //                    (i.e. 1 - tan(angle_to_nearest_axis))
    s->codeAppendf("half2 abs_bisect = abs(%s - %s);", leftDir, rightDir);
    s->codeAppend ("half axis_alignedness = 1 - min(abs_bisect.y, abs_bisect.x) / "
                                               "max(abs_bisect.x, abs_bisect.y);");

    // ninety_degreesness = sin^2(corner_angle)
    // sin^2 just because... it's always positive and the results looked better than plain sine... ?
    s->codeAppendf("half ninety_degreesness = determinant(half2x2(%s, %s));", leftDir, rightDir);
    s->codeAppend ("ninety_degreesness = ninety_degreesness * ninety_degreesness;");

    // The below formula is not smart. It was just arrived at by considering the following
    // observations:
    //
    // 1. 90-degree, axis-aligned corners have full attenuation along the bisector.
    //    (i.e. coverage = 1 - distance_to_corner^2)
    //    (i.e. outputAttenuation = 0)
    //
    // 2. 180-degree corners always have zero attenuation.
    //    (i.e. coverage = 1 - distance_to_corner)
    //    (i.e. outputAttenuation = 1)
    //
    // 3. 90-degree corners whose bisector falls on a 45 degree angle also do not attenuate.
    //    (i.e. outputAttenuation = 1)
    s->codeAppendf("%s = max(obtuseness, axis_alignedness * ninety_degreesness);",
                   outputAttenuation);
}

void GrCCCoverageProcessor::getGLSLProcessorKey(const GrShaderCaps&,
                                                GrProcessorKeyBuilder* b) const {
    int key = (int)fPrimitiveType << 2;
    if (GSSubpass::kCorners == fGSSubpass) {
        key |= 2;
    }
    if (Impl::kVertexShader == fImpl) {
        key |= 1;
    }
#ifdef SK_DEBUG
    uint32_t bloatBits;
    memcpy(&bloatBits, &fDebugBloat, 4);
    b->add32(bloatBits);
#endif
    b->add32(key);
}

GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGLSLInstance(const GrShaderCaps&) const {
    std::unique_ptr<Shader> shader;
    switch (fPrimitiveType) {
        case PrimitiveType::kTriangles:
        case PrimitiveType::kWeightedTriangles:
            shader = skstd::make_unique<TriangleShader>();
            break;
        case PrimitiveType::kQuadratics:
            shader = skstd::make_unique<GrCCQuadraticShader>();
            break;
        case PrimitiveType::kCubics:
            shader = skstd::make_unique<GrCCCubicShader>();
            break;
        case PrimitiveType::kConics:
            shader = skstd::make_unique<GrCCConicShader>();
            break;
    }
    return Impl::kGeometryShader == fImpl ? this->createGSImpl(std::move(shader))
                                          : this->createVSImpl(std::move(shader));
}

void GrCCCoverageProcessor::Shader::emitFragmentCode(const GrCCCoverageProcessor& proc,
                                                     GrGLSLFPFragmentBuilder* f,
                                                     const char* skOutputColor,
                                                     const char* skOutputCoverage) const {
    f->codeAppendf("half coverage = 0;");
    this->onEmitFragmentCode(f, "coverage");
    f->codeAppendf("%s.a = coverage;", skOutputColor);
    f->codeAppendf("%s = half4(1);", skOutputCoverage);
}

void GrCCCoverageProcessor::draw(GrOpFlushState* flushState, const GrPipeline& pipeline,
                                 const GrMesh meshes[],
                                 const GrPipeline::DynamicState dynamicStates[], int meshCount,
                                 const SkRect& drawBounds) const {
    GrGpuRTCommandBuffer* cmdBuff = flushState->rtCommandBuffer();
    cmdBuff->draw(pipeline, *this, meshes, dynamicStates, meshCount, drawBounds);

    // Geometry shader backend draws primitives in two subpasses.
    if (Impl::kGeometryShader == fImpl) {
        SkASSERT(GSSubpass::kHulls == fGSSubpass);
        GrCCCoverageProcessor cornerProc(*this, GSSubpass::kCorners);
        cmdBuff->draw(pipeline, cornerProc, meshes, dynamicStates, meshCount, drawBounds);
    }
}