Flutter Engine
The Flutter Engine
Tessellation.h
Go to the documentation of this file.
1/*
2 * Copyright 2021 Google Inc.
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 skgpu_tessellate_Tessellation_DEFINED
9#define skgpu_tessellate_Tessellation_DEFINED
10
14#include "include/gpu/GrTypes.h"
16
17#include <math.h>
18#include <algorithm>
19#include <cstddef>
20#include <cstdint>
21
22class SkMatrix;
23class SkPath;
24struct SkRect;
25
26namespace skgpu::tess {
27
28// Don't allow linearized segments to be off by more than 1/4th of a pixel from the true curve.
29constexpr static float kPrecision = 4;
30
31// This is the maximum number of subdivisions of a Bezier curve that can be represented in the fixed
32// count vertex and index buffers. If rendering a curve that requires more subdivisions, it must be
33// chopped.
34constexpr static int kMaxResolveLevel = 5;
35
36// This is the maximum number of parametric segments (linear sections) that a curve can be split
37// into. This is the same for path filling and stroking, although fixed-count stroking also uses
38// additional vertices to handle radial segments, joins, and caps. Additionally the fixed-count
39// path filling algorithms snap their dynamic vertex counts to powers-of-two, whereas the stroking
40// algorithm does not.
41constexpr static int kMaxParametricSegments = 1 << kMaxResolveLevel;
45
46// Don't tessellate paths that might have an individual curve that requires more than 1024 segments.
47// (See wangs_formula::worst_case_cubic). If this is the case, call "PreChopPathCurves" first.
48// Standard chopping, when Wang's formula is between kMaxParametricSegments and
49// kMaxTessellationSegmentsPerCurve is handled automatically by PatchWriter. It differs from
50// PreChopPathCurves in that it does no culling of offscreen chopped paths.
51constexpr static float kMaxSegmentsPerCurve = 1024;
54
55// Returns a new path, equivalent to 'path' within the given viewport, whose verbs can all be drawn
56// with 'maxSegments' tessellation segments or fewer, while staying within '1/tessellationPrecision'
57// pixels of the true curve. Curves and chops that fall completely outside the viewport are
58// flattened into lines.
59SkPath PreChopPathCurves(float tessellationPrecision,
60 const SkPath&,
61 const SkMatrix&,
62 const SkRect& viewport);
63
64// How many triangles are in a curve with 2^resolveLevel line segments?
65// Resolve level defines the tessellation factor for filled paths drawn using curves or wedges.
66constexpr static int NumCurveTrianglesAtResolveLevel(int resolveLevel) {
67 // resolveLevel=0 -> 0 line segments -> 0 triangles
68 // resolveLevel=1 -> 2 line segments -> 1 triangle
69 // resolveLevel=2 -> 4 line segments -> 3 triangles
70 // resolveLevel=3 -> 8 line segments -> 7 triangles
71 // ...
72 return (1 << resolveLevel) - 1;
73}
74
75// Optional attribs that are included in tessellation patches, following the control points and in
76// the same order as they appear here.
77enum class PatchAttribs {
78 // Attribs.
79 kNone = 0,
80 kJoinControlPoint = 1 << 0, // [float2] Used by strokes. This defines tangent direction.
81 kFanPoint = 1 << 1, // [float2] Used by wedges. This is the center point the wedges fan around.
82 kStrokeParams = 1 << 2, // [float2] Used when strokes have different widths or join types.
83 kColor = 1 << 3, // [ubyte4 or float4] Used when patches have different colors.
84 kPaintDepth = 1 << 4, // [float] Used in Graphite to specify depth attachment value for draw.
85 kExplicitCurveType = 1 << 5, // [float] Used when GPU can't infer curve type based on infinity.
86 kSsboIndex = 1 << 7, // [int] Used to index into a shared storage buffer for this patch's
87 // uniform values.
88
89 // Extra flags.
90 kWideColorIfEnabled = 1 << 6, // If kColor is set, specifies it to be float4 wide color.
91};
92
94
95// When PatchAttribs::kExplicitCurveType is set, these are the values that tell the GPU what type of
96// curve is being drawn.
97constexpr static float kCubicCurveType [[maybe_unused]] = 0;
98constexpr static float kConicCurveType [[maybe_unused]] = 1;
99constexpr static float kTriangularConicCurveType [[maybe_unused]] = 2; // Conic curve with w=Inf.
100
101// Returns the packed size in bytes of the attribs portion of tessellation patches (or instances) in
102// GPU buffers.
103constexpr size_t PatchAttribsStride(PatchAttribs attribs) {
104 return (attribs & PatchAttribs::kJoinControlPoint ? sizeof(float) * 2 : 0) +
105 (attribs & PatchAttribs::kFanPoint ? sizeof(float) * 2 : 0) +
106 (attribs & PatchAttribs::kStrokeParams ? sizeof(float) * 2 : 0) +
107 (attribs & PatchAttribs::kColor
108 ? (attribs & PatchAttribs::kWideColorIfEnabled ? sizeof(float)
109 : sizeof(uint8_t)) * 4 : 0) +
110 (attribs & PatchAttribs::kPaintDepth ? sizeof(float) : 0) +
111 (attribs & PatchAttribs::kExplicitCurveType ? sizeof(float) : 0) +
112 (attribs & PatchAttribs::kSsboIndex ? (sizeof(int)) : 0);
113}
114constexpr size_t PatchStride(PatchAttribs attribs) {
115 return 4*sizeof(SkPoint) + PatchAttribsStride(attribs);
116}
117
118// Finds 0, 1, or 2 T values at which to chop the given curve in order to guarantee the resulting
119// cubics are convex and rotate no more than 180 degrees.
120//
121// - If the cubic is "serpentine", then the T values are any inflection points in [0 < T < 1].
122// - If the cubic is linear, then the T values are any 180-degree cusp points in [0 < T < 1].
123// - Otherwise the T value is the point at which rotation reaches 180 degrees, iff in [0 < T < 1].
124//
125// 'areCusps' is set to true if the chop point occurred at a cusp (within tolerance), or if the chop
126// point(s) occurred at 180-degree turnaround points on a degenerate flat line.
127int FindCubicConvex180Chops(const SkPoint[], float T[2], bool* areCusps);
128
129// Returns true if the given conic (or quadratic) has a cusp point. The w value is not necessary in
130// determining this. If there is a cusp, it can be found at the midtangent.
131inline bool ConicHasCusp(const SkPoint p[3]) {
132 SkVector a = p[1] - p[0];
133 SkVector b = p[2] - p[1];
134 // A conic of any class can only have a cusp if it is a degenerate flat line with a 180 degree
135 // turnarund. To detect this, the beginning and ending tangents must be parallel
136 // (a.cross(b) == 0) and pointing in opposite directions (a.dot(b) < 0).
137 return a.cross(b) == 0 && a.dot(b) < 0;
138}
139
140// We encode all of a join's information in a single float value:
141//
142// Negative => Round Join
143// Zero => Bevel Join
144// Positive => Miter join, and the value is also the miter limit
145//
146inline float GetJoinType(const SkStrokeRec& stroke) {
147 switch (stroke.getJoin()) {
148 case SkPaint::kRound_Join: return -1;
149 case SkPaint::kBevel_Join: return 0;
150 case SkPaint::kMiter_Join: SkASSERT(stroke.getMiter() >= 0); return stroke.getMiter();
151 }
153}
154
155// This float2 gets written out with each patch/instance if PatchAttribs::kStrokeParams is enabled.
157 StrokeParams() = default;
158 StrokeParams(float radius, float joinType) : fRadius(radius), fJoinType(joinType) {}
160 this->set(stroke);
161 }
162 void set(const SkStrokeRec& stroke) {
163 fRadius = stroke.getWidth() * .5f;
165 }
166
167 float fRadius;
168 float fJoinType; // See GetJoinType().
169};
170
171inline bool StrokesHaveEqualParams(const SkStrokeRec& a, const SkStrokeRec& b) {
172 return a.getWidth() == b.getWidth() && a.getJoin() == b.getJoin() &&
173 (a.getJoin() != SkPaint::kMiter_Join || a.getMiter() == b.getMiter());
174}
175
176// Returns the fixed number of edges that are always emitted with the given join type. If the
177// join is round, the caller needs to account for the additional radial edges on their own.
178// Specifically, each join always emits:
179//
180// * Two colocated edges at the beginning (a full-width edge to seam with the preceding stroke
181// and a half-width edge to begin the join).
182//
183// * An extra edge in the middle for miter joins, or else a variable number of radial edges
184// for round joins (the caller is responsible for counting radial edges from round joins).
185//
186// * A half-width edge at the end of the join that will be colocated with the first
187// (full-width) edge of the stroke.
188//
189constexpr int NumFixedEdgesInJoin(SkPaint::Join joinType) {
190 switch (joinType) {
192 return 4;
194 // The caller is responsible for counting the variable number of middle, radial
195 // segments on round joins.
196 [[fallthrough]];
198 return 3;
199 }
201}
202constexpr int NumFixedEdgesInJoin(const StrokeParams& strokeParams) {
203 // The caller is responsible for counting the variable number of segments for round joins.
204 return strokeParams.fJoinType > 0.f ? /* miter */ 4 : /* round or bevel */ 3;
205}
206
207// Decides the number of radial segments the tessellator adds for each curve. (Uniform steps
208// in tangent angle.) The tessellator will add this number of radial segments for each
209// radian of rotation in local path space.
210inline float CalcNumRadialSegmentsPerRadian(float approxDevStrokeRadius) {
211 float cosTheta = 1.f - (1.f / kPrecision) / approxDevStrokeRadius;
212 return .5f / acosf(std::max(cosTheta, -1.f));
213}
214
215} // namespace skgpu::tess
216
217#endif // skgpu_tessellate_Tessellation_DEFINED
#define GR_MAKE_BITFIELD_CLASS_OPS(X)
Definition: GrTypes.h:42
#define SkUNREACHABLE
Definition: SkAssert.h:135
#define SkASSERT(cond)
Definition: SkAssert.h:116
@ kRound_Join
adds circle
Definition: SkPaint.h:360
@ kMiter_Join
extends to miter limit
Definition: SkPaint.h:359
@ kBevel_Join
connects outside edges
Definition: SkPaint.h:361
Definition: SkPath.h:59
static bool b
struct MyStruct a[10]
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static constexpr float kMaxSegmentsPerCurve_p4
Definition: Tessellation.h:53
static constexpr int kMaxParametricSegments_p4
Definition: Tessellation.h:43
SkPath PreChopPathCurves(float tessellationPrecision, const SkPath &path, const SkMatrix &matrix, const SkRect &viewport)
static constexpr float kMaxSegmentsPerCurve_p2
Definition: Tessellation.h:52
float GetJoinType(const SkStrokeRec &stroke)
Definition: Tessellation.h:146
static constexpr float kTriangularConicCurveType
Definition: Tessellation.h:99
static constexpr float kMaxSegmentsPerCurve
Definition: Tessellation.h:51
bool ConicHasCusp(const SkPoint p[3])
Definition: Tessellation.h:131
float CalcNumRadialSegmentsPerRadian(float approxDevStrokeRadius)
Definition: Tessellation.h:210
static constexpr int NumCurveTrianglesAtResolveLevel(int resolveLevel)
Definition: Tessellation.h:66
int FindCubicConvex180Chops(const SkPoint pts[], float T[2], bool *areCusps)
static constexpr int kMaxResolveLevel
Definition: Tessellation.h:34
static constexpr int kMaxParametricSegments_p2
Definition: Tessellation.h:42
constexpr size_t PatchAttribsStride(PatchAttribs attribs)
Definition: Tessellation.h:103
static constexpr float kPrecision
Definition: Tessellation.h:29
constexpr size_t PatchStride(PatchAttribs attribs)
Definition: Tessellation.h:114
bool StrokesHaveEqualParams(const SkStrokeRec &a, const SkStrokeRec &b)
Definition: Tessellation.h:171
static constexpr float kCubicCurveType
Definition: Tessellation.h:97
static constexpr int kMaxParametricSegments
Definition: Tessellation.h:41
static constexpr float kConicCurveType
Definition: Tessellation.h:98
constexpr int NumFixedEdgesInJoin(SkPaint::Join joinType)
Definition: Tessellation.h:189
#define T
Definition: precompiler.cc:65
void set(const SkStrokeRec &stroke)
Definition: Tessellation.h:162
StrokeParams(const SkStrokeRec &stroke)
Definition: Tessellation.h:159
StrokeParams(float radius, float joinType)
Definition: Tessellation.h:158