• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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