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
12 namespace skgpu {
13
to_skpoint(float2 p)14 SK_ALWAYS_INLINE SkPoint to_skpoint(float2 p) { return skvx::bit_pun<SkPoint>(p); }
15
16 #if SK_GPU_V1
PatchWriter(GrMeshDrawTarget * target,PathTessellator * tessellator,int initialPatchAllocCount)17 PatchWriter::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
chopAndWriteQuads(float2 p0,float2 p1,float2 p2,int numPatches)28 void 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
chopAndWriteConics(float2 p0,float2 p1,float2 p2,float w,int numPatches)72 void 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
chopAndWriteCubics(float2 p0,float2 p1,float2 p2,float2 p3,int numPatches)110 void 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