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