aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu/ccpr/GrCCPRTriangleProcessor.cpp
blob: bb2ad1b4ba6f1c50ec6f9d8d5f7c01fe717b89ab (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
/*
 * 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 "GrCCPRTriangleProcessor.h"

#include "glsl/GrGLSLFragmentShaderBuilder.h"
#include "glsl/GrGLSLGeometryShaderBuilder.h"
#include "glsl/GrGLSLVertexShaderBuilder.h"

void GrCCPRTriangleProcessor::onEmitVertexShader(const GrCCPRCoverageProcessor& proc,
                                                 GrGLSLVertexBuilder* v,
                                                 const TexelBufferHandle& pointsBuffer,
                                                 const char* atlasOffset, const char* rtAdjust,
                                                 GrGPArgs* gpArgs) const {
    v->codeAppend ("highp float2 self = ");
    v->appendTexelFetch(pointsBuffer,
                        SkStringPrintf("%s[sk_VertexID]", proc.instanceAttrib()).c_str());
    v->codeAppendf(".xy + %s;", atlasOffset);
    gpArgs->fPositionVar.set(kVec2f_GrSLType, "self");
}

void GrCCPRTriangleProcessor::defineInputVertices(GrGLSLGeometryBuilder* g) const {
    // Prepend in_vertices at the start of the shader.
    g->codePrependf("highp float3x2 in_vertices = float3x2(sk_in[0].gl_Position.xy, "
                                                          "sk_in[1].gl_Position.xy, "
                                                          "sk_in[2].gl_Position.xy);");
}

void GrCCPRTriangleProcessor::emitWind(GrGLSLGeometryBuilder* g, const char* /*rtAdjust*/,
                                       const char* outputWind) const {
    // We will define in_vertices in defineInputVertices.
    g->codeAppendf("%s = sign(determinant(float2x2(in_vertices[1] - in_vertices[0], "
                                                  "in_vertices[2] - in_vertices[0])));",
                   outputWind);
}

void GrCCPRTriangleHullAndEdgeProcessor::onEmitGeometryShader(GrGLSLGeometryBuilder* g,
                                                              const char* emitVertexFn,
                                                              const char* wind,
                                                              const char* rtAdjust) const {
    this->defineInputVertices(g);
    int maxOutputVertices = 0;

    if (GeometryType::kEdges != fGeometryType) {
        maxOutputVertices += this->emitHullGeometry(g, emitVertexFn, "in_vertices", 3,
                                                    "sk_InvocationID");
    }

    if (GeometryType::kHulls != fGeometryType) {
        g->codeAppend ("int edgeidx0 = sk_InvocationID, "
                           "edgeidx1 = (edgeidx0 + 1) % 3;");
        g->codeAppendf("highp float2 edgept0 = in_vertices[%s > 0 ? edgeidx0 : edgeidx1];", wind);
        g->codeAppendf("highp float2 edgept1 = in_vertices[%s > 0 ? edgeidx1 : edgeidx0];", wind);

        maxOutputVertices += this->emitEdgeGeometry(g, emitVertexFn, "edgept0", "edgept1");
    }

    g->configure(GrGLSLGeometryBuilder::InputType::kTriangles,
                 GrGLSLGeometryBuilder::OutputType::kTriangleStrip,
                 maxOutputVertices, 3);
}

void GrCCPRTriangleCornerProcessor::onEmitVertexShader(const GrCCPRCoverageProcessor& proc,
                                                       GrGLSLVertexBuilder* v,
                                                       const TexelBufferHandle& pointsBuffer,
                                                       const char* atlasOffset,
                                                       const char* rtAdjust,
                                                       GrGPArgs* gpArgs) const {
    this->INHERITED::onEmitVertexShader(proc, v, pointsBuffer, atlasOffset, rtAdjust, gpArgs);

    // Fetch and transform the next point in the triangle.
    v->codeAppend ("highp float2 next = ");
    v->appendTexelFetch(pointsBuffer,
                        SkStringPrintf("%s[(sk_VertexID+1) %% 3]", proc.instanceAttrib()).c_str());
    v->codeAppendf(".xy + %s;", atlasOffset);

    // Find the plane that gives distance from the [self -> next] edge, normalized to its AA
    // bloat width.
    v->codeAppend ("highp float2 n = float2(next.y - self.y, self.x - next.x);");
    v->codeAppendf("highp float2 d = n * float2x2(self + %f * sign(n), "
                                                 "self - %f * sign(n));",
                   kAABloatRadius, kAABloatRadius);

    // Clamp for when n=0. (wind=0 when n=0, so as long as we don't get Inf or NaN we are fine.)
    v->codeAppendf("%s.xy = n / max(d[0] - d[1], 1e-30);", fEdgeDistance.vsOut());
    v->codeAppendf("%s.z = -dot(%s.xy, self);", fEdgeDistance.vsOut(), fEdgeDistance.vsOut());

    // Emit device coords to geo shader.
    v->codeAppendf("%s = self;", fDevCoord.vsOut());
}

void GrCCPRTriangleCornerProcessor::onEmitGeometryShader(GrGLSLGeometryBuilder* g,
                                                         const char* emitVertexFn, const char* wind,
                                                         const char* rtAdjust) const {
    this->defineInputVertices(g);

    g->codeAppend ("highp float2 self = in_vertices[sk_InvocationID];");
    int numVertices = this->emitCornerGeometry(g, emitVertexFn, "self");

    g->configure(GrGLSLGeometryBuilder::InputType::kTriangles,
                 GrGLSLGeometryBuilder::OutputType::kTriangleStrip,
                 numVertices, 3);
}

void GrCCPRTriangleCornerProcessor::emitPerVertexGeometryCode(SkString* fnBody,
                                                              const char* position,
                                                              const char* /*coverage*/,
                                                              const char* wind) const {
    fnBody->appendf("%s.xy = %s[(sk_InvocationID + 1) %% 3];",
                    fNeighbors.gsOut(), fDevCoord.gsIn());
    fnBody->appendf("%s.zw = %s[(sk_InvocationID + 2) %% 3];",
                    fNeighbors.gsOut(), fDevCoord.gsIn());
    fnBody->appendf("%s = float3x3(%s[(sk_InvocationID + 2) %% 3], "
                                  "%s[sk_InvocationID], "
                                  "%s[(sk_InvocationID + 1) %% 3]) * %s;",
                    fEdgeDistances.gsOut(), fEdgeDistance.gsIn(), fEdgeDistance.gsIn(),
                    fEdgeDistance.gsIn(), wind);

    // Otherwise, fEdgeDistances = float3x3(...) * sign(wind * rtAdjust.x * rdAdjust.z).
    GR_STATIC_ASSERT(kTopLeft_GrSurfaceOrigin == GrCCPRCoverageProcessor::kAtlasOrigin);

    fnBody->appendf("%s = sk_InvocationID;", fCornerIdx.gsOut());
}

void GrCCPRTriangleCornerProcessor::emitShaderCoverage(GrGLSLFragmentBuilder* f,
                                                       const char* outputCoverage) const {
    // FIXME: Adreno breaks if we don't put the frag coord in an intermediate highp variable.
    f->codeAppendf("highp float2 fragcoord = sk_FragCoord.xy;");

    // Approximate coverage by tracking where 4 horizontal lines enter and leave the triangle.
    GrShaderVar samples("samples", kVec4f_GrSLType, GrShaderVar::kNonArray,
                        kHigh_GrSLPrecision);
    f->declareGlobal(samples);
    f->codeAppendf("%s = fragcoord.y + float4(-0.375, -0.125, 0.125, 0.375);", samples.c_str());

    GrShaderVar leftedge("leftedge", kVec4f_GrSLType, GrShaderVar::kNonArray,
                         kHigh_GrSLPrecision);
    f->declareGlobal(leftedge);
    f->codeAppendf("%s = float4(fragcoord.x - 0.5);", leftedge.c_str());

    GrShaderVar rightedge("rightedge", kVec4f_GrSLType, GrShaderVar::kNonArray,
                          kHigh_GrSLPrecision);
    f->declareGlobal(rightedge);
    f->codeAppendf("%s = float4(fragcoord.x + 0.5);", rightedge.c_str());

    SkString sampleEdgeFn;
    GrShaderVar edgeArg("edge_distance", kVec3f_GrSLType, GrShaderVar::kNonArray,
                        kHigh_GrSLPrecision);
    f->emitFunction(kVoid_GrSLType, "sampleEdge", 1, &edgeArg, [&]() {
        SkString b;
        b.appendf("highp float m = abs(%s.x) < 1e-3 ? 1e18 : -1 / %s.x;",
                  edgeArg.c_str(), edgeArg.c_str());
        b.appendf("highp float4 edge = m * (%s.y * samples + %s.z);",
                  edgeArg.c_str(), edgeArg.c_str());
        b.appendf("if (%s.x <= 1e-3 || (abs(%s.x) < 1e-3 && %s.y > 0)) {",
                  edgeArg.c_str(), edgeArg.c_str(), edgeArg.c_str());
        b.appendf(    "%s = max(%s, edge);", leftedge.c_str(), leftedge.c_str());
        b.append ("} else {");
        b.appendf(    "%s = min(%s, edge);", rightedge.c_str(), rightedge.c_str());
        b.append ("}");
        return b;
    }().c_str(), &sampleEdgeFn);

    // See if the previous neighbor already handled this pixel.
    f->codeAppendf("if (all(lessThan(abs(fragcoord - %s.zw), float2(%f)))) {",
                   fNeighbors.fsIn(), kAABloatRadius);
    // Handle the case where all 3 corners defer to the previous neighbor.
    f->codeAppendf(    "if (%s != 0 || !all(lessThan(abs(fragcoord - %s.xy), float2(%f)))) {",
                       fCornerIdx.fsIn(), fNeighbors.fsIn(), kAABloatRadius);
    f->codeAppend (        "discard;");
    f->codeAppend (    "}");
    f->codeAppend ("}");

    // Erase what the hull and two edges wrote at this corner in previous shaders (the two .5's
    // for the edges and the -1 for the hull cancel each other out).
    f->codeAppendf("%s = dot(float3(fragcoord, 1) * float2x3(%s), float2(1));",
                   outputCoverage, fEdgeDistances.fsIn());

    // Sample the two edges at this corner.
    f->codeAppendf("%s(%s[0]);", sampleEdgeFn.c_str(), fEdgeDistances.fsIn());
    f->codeAppendf("%s(%s[1]);", sampleEdgeFn.c_str(), fEdgeDistances.fsIn());

    // Handle the opposite edge if the next neighbor will defer to us.
    f->codeAppendf("if (all(lessThan(abs(fragcoord - %s.xy), float2(%f)))) {",
                   fNeighbors.fsIn(), kAABloatRadius);
    // Erase the coverage the opposite edge wrote to this corner.
    f->codeAppendf(    "%s += dot(%s[2], float3(fragcoord, 1)) + 0.5;",
                       outputCoverage, fEdgeDistances.fsIn());
    // Sample the opposite edge.
    f->codeAppendf(    "%s(%s[2]);", sampleEdgeFn.c_str(), fEdgeDistances.fsIn());
    f->codeAppend ("}");

    f->codeAppendf("highp float4 widths = max(%s - %s, 0);", rightedge.c_str(), leftedge.c_str());
    f->codeAppendf("%s += dot(widths, float4(0.25));", outputCoverage);
}