Simpify RawPath::Iter

Implement Iter as an STL-style iterator that can be traversed very
efficiently using range-for:

  for (auto [verb, pts] : rawPath) { ... }

Manually inject the implicit moveTos in RawPath (namely, the implicit
moveTo(0) at the beginning of a path, if the client didn't add one, and
a similar moveTo after close).

The implicit moveTos fix a bug in RawPath::bounds where we were not
accounting for the implicit moveTo(0), as well as a bug in
ContourMeasureIter::tryNext where we expected the first contour in a
path to begin with PathVerb::move.

With the implicit moveTos guaranteed to be in the raw path, the iterator
can be simplified to just peek back one point and give a contiguous array
beginning with p0 for each verb. In addition to generally simplifying
things, one more perk of having all the points contiguous is that it
also enables fast SIMD loads.

IterateRawPath bench result:

  MacOS NEON: 8.71ms -> 5.02 (58%)
  Windows SSE: 5.63ms -> 4.20 (75%)

BuildRawPath bench result:

  MacOS NEON: 2.09ms -> 2.19 (105%)
  Windows SSE: 2.18ms -> 2.27 (104%)

Diffs=
fdeff54d1 Simpify RawPath::Iter (#4157)
This commit is contained in:
csmartdalton
2022-09-06 20:08:05 +00:00
parent c4c90718a7
commit 2851c5a6c0
9 changed files with 227 additions and 302 deletions

View File

@@ -12,9 +12,6 @@ class RawPath;
/// Utilty for converting a RawPath into a contour segments.
class SegmentedContour {
private:
Vec2D m_pen;
Vec2D m_penDown;
bool m_isPenDown = false;
std::vector<Vec2D> m_contourPoints;
AABB m_bounds;
@@ -22,8 +19,6 @@ private:
float m_thresholdSquared;
void addVertex(Vec2D vertex);
void penDown();
void close();
void segmentCubic(const Vec2D& from,
const Vec2D& fromOut,
const Vec2D& toIn,
@@ -44,4 +39,4 @@ public:
void contour(const RawPath& rawPath, const Mat2D& transform);
};
} // namespace rive
#endif
#endif

View File

@@ -20,23 +20,6 @@ void SegmentedContour::addVertex(Vec2D vertex) {
AABB::expandTo(m_bounds, vertex);
}
void SegmentedContour::penDown() {
if (m_isPenDown) {
return;
}
m_isPenDown = true;
m_penDown = m_pen;
addVertex(m_penDown);
}
void SegmentedContour::close() {
if (!m_isPenDown) {
return;
}
m_pen = m_penDown;
m_isPenDown = false;
}
const std::size_t SegmentedContour::contourSize() const { return m_contourPoints.size(); }
const Span<const Vec2D> SegmentedContour::contourPoints(uint32_t endOffset) const {
@@ -70,36 +53,25 @@ void SegmentedContour::segmentCubic(const Vec2D& from,
void SegmentedContour::contour(const RawPath& rawPath, const Mat2D& transform) {
m_contourPoints.clear();
RawPath::Iter iter(rawPath);
// Possible perf consideration: could add second path that doesn't transform
// if transform is the identity.
while (auto rec = iter.next()) {
switch (rec.verb) {
case PathVerb::move:
m_isPenDown = false;
m_pen = transform * rec.pts[0];
break;
case PathVerb::line:
penDown();
m_pen = transform * rec.pts[0];
addVertex(m_pen);
break;
for (const auto [verb, pts] : rawPath) {
switch (verb) {
case PathVerb::move: addVertex(transform * pts[0]); break;
case PathVerb::line: addVertex(transform * pts[1]); break;
case PathVerb::cubic:
penDown();
segmentCubic(m_pen,
transform * rec.pts[0],
transform * rec.pts[1],
transform * rec.pts[2],
segmentCubic(transform * pts[0],
transform * pts[1],
transform * pts[2],
transform * pts[3],
0.0f,
1.0f);
m_pen = transform * rec.pts[2];
break;
case PathVerb::close: close(); break;
case PathVerb::close: break;
case PathVerb::quad:
// TODO: not currently used by render paths, however might be
// necessary for fonts.
break;
}
}
close();
}