1/*
2 * Copyright 2021 Google LLC.
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 "src/gpu/tessellate/PatchWriter.h"
9
10#include "src/gpu/tessellate/PathTessellator.h"
11
12namespace skgpu {
13
14SK_ALWAYS_INLINE SkPoint to_skpoint(float2 p) { return skvx::bit_pun<SkPoint>(p); }
15
16#if SK_GPU_V1
17PatchWriter::PatchWriter(GrMeshDrawTarget* target,
18                         PathTessellator* tessellator,
19                         int initialPatchAllocCount)
20        : PatchWriter(target,
21                      &tessellator->fVertexChunkArray,
22                      tessellator->fAttribs,
23                      sizeof(SkPoint) * 4 + PatchAttribsStride(tessellator->fAttribs),
24                      initialPatchAllocCount) {
25}
26#endif
27
28void PatchWriter::chopAndWriteQuads(float2 p0, float2 p1, float2 p2, int numPatches) {
29    // If we aren't fanning, we need to fill the space between chops with triangles.
30    bool needsInnerTriangles = !(fPatchAttribs & PatchAttribs::kFanPoint);
31    MiddleOutPolygonTriangulator innerTriangulator(numPatches, to_skpoint(p0));
32    for (; numPatches >= 3; numPatches -= 2) {
33        // Chop into 3 quads.
34        float4 T = float4(1,1,2,2) / numPatches;
35        float4 ab = mix(p0.xyxy(), p1.xyxy(), T);
36        float4 bc = mix(p1.xyxy(), p2.xyxy(), T);
37        float4 abc = mix(ab, bc, T);
38        // p1 & p2 of the cubic representation of the middle quad.
39        float4 middle = mix(ab, bc, mix(T, T.zwxy(), 2/3.f));
40
41        CubicPatch(*this) << QuadToCubic{p0, ab.lo, abc.lo};  // Write the 1st quad.
42        if (needsInnerTriangles) {
43            TrianglePatch(*this) << p0 << abc.lo << abc.hi;
44        }
45        CubicPatch(*this) << abc.lo << middle << abc.hi;  // Write the 2nd quad.
46        if (needsInnerTriangles) {
47            *this << innerTriangulator.pushVertex(to_skpoint(abc.hi));
48        }
49        std::tie(p0, p1) = {abc.hi, bc.hi};  // Save the 3rd quad.
50    }
51    if (numPatches == 2) {
52        // Chop into 2 quads.
53        float2 ab = (p0 + p1) * .5f;
54        float2 bc = (p1 + p2) * .5f;
55        float2 abc = (ab + bc) * .5f;
56
57        CubicPatch(*this) << QuadToCubic{p0, ab, abc};  // Write the 1st quad.
58        if (needsInnerTriangles) {
59            TrianglePatch(*this) << p0 << abc << p2;
60        }
61        CubicPatch(*this) << QuadToCubic{abc, bc, p2};  // Write the 2nd quad.
62    } else {
63        SkASSERT(numPatches == 1);
64        CubicPatch(*this) << QuadToCubic{p0, p1, p2};  // Write the single quad.
65    }
66    if (needsInnerTriangles) {
67        *this << innerTriangulator.pushVertex(to_skpoint(p2));
68        *this << innerTriangulator.close();
69    }
70}
71
72void PatchWriter::chopAndWriteConics(float2 p0, float2 p1, float2 p2, float w, int numPatches) {
73    // If we aren't fanning, we need to fill the space between chops with triangles.
74    bool needsInnerTriangles = !(fPatchAttribs & PatchAttribs::kFanPoint);
75    MiddleOutPolygonTriangulator innerTriangulator(numPatches, to_skpoint(p0));
76    // Load the conic in 3d homogeneous (unprojected) space.
77    float4 h0 = float4(p0,1,1);
78    float4 h1 = float4(p1,1,1) * w;
79    float4 h2 = float4(p2,1,1);
80    for (; numPatches >= 2; --numPatches) {
81        // Chop in homogeneous space.
82        float T = 1.f/numPatches;
83        float4 ab = mix(h0, h1, T);
84        float4 bc = mix(h1, h2, T);
85        float4 abc = mix(ab, bc, T);
86
87        // Project and write the 1st conic.
88        float2 midpoint = abc.xy() / abc.w();
89        ConicPatch(*this) << (h0.xy() / h0.w())
90                          << (ab.xy() / ab.w())
91                          << midpoint
92                          << (ab.w() / sqrtf(h0.w() * abc.w()));
93        if (needsInnerTriangles) {
94            *this << innerTriangulator.pushVertex(to_skpoint(midpoint));
95        }
96        std::tie(h0, h1) = {abc, bc};  // Save the 2nd conic (in homogeneous space).
97    }
98    // Project and write the remaining conic.
99    SkASSERT(numPatches == 1);
100    ConicPatch(*this) << (h0.xy() / h0.w())
101                      << (h1.xy() / h1.w())
102                      << h2.xy()  // h2.w == 1
103                      << (h1.w() / sqrtf(h0.w()));
104    if (needsInnerTriangles) {
105        *this << innerTriangulator.pushVertex(to_skpoint(h2.xy()));
106        *this << innerTriangulator.close();
107    }
108}
109
110void PatchWriter::chopAndWriteCubics(float2 p0, float2 p1, float2 p2, float2 p3, int numPatches) {
111    // If we aren't fanning, we need to fill the space between chops with triangles.
112    bool needsInnerTriangles = !(fPatchAttribs & PatchAttribs::kFanPoint);
113    MiddleOutPolygonTriangulator innerTriangulator(numPatches, to_skpoint(p0));
114    for (; numPatches >= 3; numPatches -= 2) {
115        // Chop into 3 cubics.
116        float4 T = float4(1,1,2,2) / numPatches;
117        float4 ab = mix(p0.xyxy(), p1.xyxy(), T);
118        float4 bc = mix(p1.xyxy(), p2.xyxy(), T);
119        float4 cd = mix(p2.xyxy(), p3.xyxy(), T);
120        float4 abc = mix(ab, bc, T);
121        float4 bcd = mix(bc, cd, T);
122        float4 abcd = mix(abc, bcd, T);
123        float4 middle = mix(abc, bcd, T.zwxy());  // p1 & p2 of the middle cubic.
124
125        CubicPatch(*this) << p0 << ab.lo << abc.lo << abcd.lo;  // Write the 1st cubic.
126        if (needsInnerTriangles) {
127            TrianglePatch(*this) << p0 << abcd.lo << abcd.hi;
128        }
129        CubicPatch(*this) << abcd.lo << middle << abcd.hi;  // Write the 2nd cubic.
130        if (needsInnerTriangles) {
131            *this << innerTriangulator.pushVertex(to_skpoint(abcd.hi));
132        }
133        std::tie(p0, p1, p2) = {abcd.hi, bcd.hi, cd.hi};  // Save the 3rd cubic.
134    }
135    if (numPatches == 2) {
136        // Chop into 2 cubics.
137        float2 ab = (p0 + p1) * .5f;
138        float2 bc = (p1 + p2) * .5f;
139        float2 cd = (p2 + p3) * .5f;
140        float2 abc = (ab + bc) * .5f;
141        float2 bcd = (bc + cd) * .5f;
142        float2 abcd = (abc + bcd) * .5f;
143
144        CubicPatch(*this) << p0 << ab << abc << abcd;  // Write the 1st cubic.
145        if (needsInnerTriangles) {
146            TrianglePatch(*this) << p0 << abcd << p3;
147        }
148        CubicPatch(*this) << abcd << bcd << cd << p3;  // Write the 2nd cubic.
149    } else {
150        SkASSERT(numPatches == 1);
151        CubicPatch(*this) << p0 << p1 << p2 << p3;  // Write the single cubic.
152    }
153    if (needsInnerTriangles) {
154        *this << innerTriangulator.pushVertex(to_skpoint(p3));
155        *this << innerTriangulator.close();
156    }
157}
158
159}  // namespace skgpu
160