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 #ifndef tessellate_PatchWriter_DEFINED
9 #define tessellate_PatchWriter_DEFINED
10
11 #include "src/gpu/GrVertexChunkArray.h"
12 #include "src/gpu/tessellate/MiddleOutPolygonTriangulator.h"
13 #include "src/gpu/tessellate/Tessellation.h"
14 #include "src/gpu/tessellate/shaders/GrTessellationShader.h"
15
16 namespace skgpu {
17
18 #if SK_GPU_V1
19 class PathTessellator;
20 #endif
21
22 // Writes out tessellation patches, formatted with their specific attribs, to a GPU buffer.
23 class PatchWriter {
24 public:
PatchWriter(GrMeshDrawTarget* target, GrVertexChunkArray* vertexChunkArray, PatchAttribs attribs, size_t patchStride, int initialAllocCount)25 PatchWriter(GrMeshDrawTarget* target,
26 GrVertexChunkArray* vertexChunkArray,
27 PatchAttribs attribs,
28 size_t patchStride,
29 int initialAllocCount)
30 : fPatchAttribs(attribs)
31 , fChunker(target, vertexChunkArray, patchStride, initialAllocCount) {
32 }
33
34 #if SK_GPU_V1
35 // Creates a PatchWriter that writes directly to the GrVertexChunkArray stored on the provided
36 // PathTessellator.
37 PatchWriter(GrMeshDrawTarget*, PathTessellator* tessellator, int initialPatchAllocCount);
38 #endif
39
40 // Updates the fan point that will be written out with each patch (i.e., the point that wedges
41 // fan around).
42 // PathPatchAttrib::kFanPoint must be enabled.
updateFanPointAttrib(SkPoint fanPoint)43 void updateFanPointAttrib(SkPoint fanPoint) {
44 SkASSERT(fPatchAttribs & PatchAttribs::kFanPoint);
45 fFanPointAttrib = fanPoint;
46 }
47
48 // Updates the color that will be written out with each patch.
49 // PathPatchAttrib::kColor must be enabled.
updateColorAttrib(const SkPMColor4f& color)50 void updateColorAttrib(const SkPMColor4f& color) {
51 SkASSERT(fPatchAttribs & PatchAttribs::kColor);
52 fColorAttrib.set(color, fPatchAttribs & PatchAttribs::kWideColorIfEnabled);
53 }
54
55 // RAII. Appends a patch during construction and writes the remaining data for a cubic during
56 // destruction. The caller outputs p0,p1,p2,p3 (8 floats):
57 //
58 // CubicPatch(patchWriter) << p0 << p1 << p2 << p3;
59 //
60 struct CubicPatch {
CubicPatchskgpu::PatchWriter::CubicPatch61 CubicPatch(PatchWriter& w) : fPatchWriter(w), fVertexWriter(w.appendPatch()) {}
~CubicPatchskgpu::PatchWriter::CubicPatch62 ~CubicPatch() {
63 fPatchWriter.outputPatchAttribs(std::move(fVertexWriter),
64 GrTessellationShader::kCubicCurveType);
65 }
operator VertexWriter&skgpu::PatchWriter::CubicPatch66 operator VertexWriter&() { return fVertexWriter; }
67 PatchWriter& fPatchWriter;
68 VertexWriter fVertexWriter;
69 };
70
71 // RAII. Appends a patch during construction and writes the remaining data for a conic during
72 // destruction. The caller outputs p0,p1,p2,w (7 floats):
73 //
74 // ConicPatch(patchWriter) << p0 << p1 << p2 << w;
75 //
76 struct ConicPatch {
ConicPatchskgpu::PatchWriter::ConicPatch77 ConicPatch(PatchWriter& w) : fPatchWriter(w), fVertexWriter(w.appendPatch()) {}
~ConicPatchskgpu::PatchWriter::ConicPatch78 ~ConicPatch() {
79 fVertexWriter << VertexWriter::kIEEE_32_infinity; // p3.y=Inf indicates a conic.
80 fPatchWriter.outputPatchAttribs(std::move(fVertexWriter),
81 GrTessellationShader::kConicCurveType);
82 }
operator VertexWriter&skgpu::PatchWriter::ConicPatch83 operator VertexWriter&() { return fVertexWriter; }
84 PatchWriter& fPatchWriter;
85 VertexWriter fVertexWriter;
86 };
87
88 // RAII. Appends a patch during construction and writes the remaining data for a triangle during
89 // destruction. The caller outputs p0,p1,p2 (6 floats):
90 //
91 // TrianglePatch(patchWriter) << p0 << p1 << p2;
92 //
93 struct TrianglePatch {
TrianglePatchskgpu::PatchWriter::TrianglePatch94 TrianglePatch(PatchWriter& w) : fPatchWriter(w), fVertexWriter(w.appendPatch()) {}
~TrianglePatchskgpu::PatchWriter::TrianglePatch95 ~TrianglePatch() {
96 // Mark this patch as a triangle by setting it to a conic with w=Inf.
97 fVertexWriter.fill(VertexWriter::kIEEE_32_infinity, 2);
98 fPatchWriter.outputPatchAttribs(std::move(fVertexWriter),
99 GrTessellationShader::kTriangularConicCurveType);
100 }
operator VertexWriter&skgpu::PatchWriter::TrianglePatch101 operator VertexWriter&() { return fVertexWriter; }
102 PatchWriter& fPatchWriter;
103 VertexWriter fVertexWriter;
104 };
105
106 // Chops the given quadratic into 'numPatches' equal segments (in the parametric sense) and
107 // writes them to the GPU buffer.
108 //
109 // Fills space between chops with triangles if PathPatchAttrib::kFanPoint is not enabled.
110 void chopAndWriteQuads(float2 p0, float2 p1, float2 p2, int numPatches);
111
112 // Chops the given conic into 'numPatches' equal segments (in the parametric sense) and
113 // writes them to the GPU buffer.
114 //
115 // Fills space between chops with triangles if PathPatchAttrib::kFanPoint is not enabled.
116 void chopAndWriteConics(float2 p0, float2 p1, float2 p2, float w, int numPatches);
117
118 // Chops the given cubic into 'numPatches' equal segments (in the parametric sense) and
119 // writes them to the GPU buffer.
120 //
121 // Fills space between chops with triangles if PathPatchAttrib::kFanPoint is not enabled.
122 void chopAndWriteCubics(float2 p0, float2 p1, float2 p2, float2 p3, int numPatches);
123
124 private:
appendPatch()125 VertexWriter appendPatch() {
126 VertexWriter vertexWriter = fChunker.appendVertex();
127 if (!vertexWriter) {
128 // Failed to allocate GPU storage for the patch. Write to a throwaway location so the
129 // callsites don't have to do null checks.
130 if (!fFallbackPatchStorage) {
131 fFallbackPatchStorage.reset(fChunker.stride());
132 }
133 vertexWriter = fFallbackPatchStorage.data();
134 }
135 return vertexWriter;
136 }
137
138 template <typename T>
If(bool c, const T& v)139 static VertexWriter::Conditional<T> If(bool c, const T& v) { return VertexWriter::If(c,v); }
140
outputPatchAttribs(VertexWriter vertexWriter, float explicitCurveType)141 void outputPatchAttribs(VertexWriter vertexWriter, float explicitCurveType) {
142 vertexWriter << If((fPatchAttribs & PatchAttribs::kFanPoint), fFanPointAttrib)
143 << If((fPatchAttribs & PatchAttribs::kColor), fColorAttrib)
144 << If((fPatchAttribs & PatchAttribs::kExplicitCurveType), explicitCurveType);
145 }
146
147 const PatchAttribs fPatchAttribs;
148 SkPoint fFanPointAttrib;
149 GrVertexColor fColorAttrib;
150
151 GrVertexChunkBuilder fChunker;
152
153 // For when fChunker fails to allocate a patch in GPU memory.
154 SkAutoTMalloc<char> fFallbackPatchStorage;
155 };
156
157 // Converts a line to a cubic when being output via '<<' to a VertexWriter.
158 struct LineToCubic {
159 float4 fP0P1;
160 };
161
operator <<(VertexWriter& vertexWriter, const LineToCubic& line)162 SK_MAYBE_UNUSED SK_ALWAYS_INLINE VertexWriter& operator<<(VertexWriter& vertexWriter,
163 const LineToCubic& line) {
164 float4 p0p1 = line.fP0P1;
165 float4 v = p0p1.zwxy() - p0p1;
166 return vertexWriter << p0p1.lo << (v * (1/3.f) + p0p1) << p0p1.hi;
167 }
168
169 // Converts a quadratic to a cubic when being output via '<<' to a VertexWriter.
170 struct QuadToCubic {
171 float2 fP0, fP1, fP2;
172 };
173
operator <<(VertexWriter& vertexWriter, const QuadToCubic& quadratic)174 SK_MAYBE_UNUSED SK_ALWAYS_INLINE VertexWriter& operator<<(VertexWriter& vertexWriter,
175 const QuadToCubic& quadratic) {
176 auto [p0, p1, p2] = quadratic;
177 return vertexWriter << p0 << mix(float4(p0,p2), p1.xyxy(), 2/3.f) << p2;
178 }
179
operator <<( PatchWriter& w, MiddleOutPolygonTriangulator::PoppedTriangleStack&& stack)180 SK_MAYBE_UNUSED SK_ALWAYS_INLINE void operator<<(
181 PatchWriter& w, MiddleOutPolygonTriangulator::PoppedTriangleStack&& stack) {
182 for (auto [p0, p1, p2] : stack) {
183 PatchWriter::TrianglePatch(w) << p0 << p1 << p2;
184 }
185 }
186
187 } // namespace skgpu
188
189 #endif // tessellate_PatchWriter_DEFINED
190