Skip to content

PlanarTriangulation: reusable ISweepLineCache for the sweep-line triangulation - #6542

Draft
Grantim wants to merge 6 commits into
masterfrom
sweep-line-cache
Draft

PlanarTriangulation: reusable ISweepLineCache for the sweep-line triangulation#6542
Grantim wants to merge 6 commits into
masterfrom
sweep-line-cache

Conversation

@Grantim

@Grantim Grantim commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

SweepLineQueue becomes reusable behind a new ISweepLineCache interface with a makeSweepLineCache() factory: init() resets a run while every internal buffer, including the topology (via new MeshTopology::clear()) and the projected points, keeps its capacity. The queue itself is now a cheap per-run object working on a SweepLineQueue::Cache (the only implementation of the interface) that holds all the reusable buffers.

  • triangulateDisjointContours and fillContours2DPlan accept an optional cache as a defaulted last parameter (ISweepLineCache* cache = nullptr), so existing callers and bound signatures gain the parameter without new overloads. ISweepLineCache is abstract (pure virtual dtor): instances come only from the factory.
  • New C++-only triangulateDisjointContoursTopology builds the patch connectivity for a HoleFillPlan inside the cache without materializing a Mesh at all.
  • HoleFillPlanner owns a cache member created on the first swept run (like its optimalStepsCache_), so each worker of getPlanarHoleFillPlans reuses the buffers across all the holes it plans within a call; the per-worker planners are then released in a parallel pass, since freeing them serially in the ETS destructor dominates small batches.

Plans stay bit-identical (FNV hash over plan items, verified against master). Measured with cMinSweptHoleSize lowered to 3 so that every hole goes through the swept path: on MeshInspector's Boolean Benchmark workload (3366-vert spheres, DifferenceAB, ~9.7k per-face holes per cut, ~500 per worker) the whole boolean improves by 7-8% (median 19.5 -> 17.8 ms, interleaved rounds); on MRTest MeshBoolean (~30 holes per cut, 1-3 per worker, so nothing to amortize the cache against) it costs about 5% (38 vs 36 ms). At the shipping threshold both are at parity, and a serial 30-hole plan batch with a persistent cache goes 0.229 -> 0.184 ms.

🤖 Generated with Claude Code

Grantim and others added 6 commits August 6, 2026 22:12
…ngulation

SweepLineQueue becomes reusable behind a new ISweepLineCache interface with a makeSweepLineCache()
factory: init() resets a run while every internal buffer, including the topology (via new
MeshTopology::clear()) and the projected points, keeps its capacity. triangulateDisjointContours and
fillContours2DPlan accept an optional cache, and new triangulateDisjointContoursTopology builds the
patch connectivity for a HoleFillPlan inside the cache without materializing a Mesh at all.
HoleFillPlanner passes an optional cache through, and getPlanarHoleFillPlans injects per-worker
caches persisting across calls. Plans stay bit-identical; plan-batch microbenchmarks improve by
10-30%, and MeshBoolean with cMinSweptHoleSize lowered to 4 (routing every hole through the
swept-line path) improves by about 5%.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the function-static per-worker caches in getPlanarHoleFillPlans with a cache member
created on the first swept run, like optimalStepsCache_: ownership is local to the planner,
which is already thread-confined, instead of thread-keyed static state.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… being the cache

All reusable buffers move into the nested SweepLineQueue::Cache (the only ISweepLineCache
implementation), and the queue references one given to its constructor: the queue is the
algorithm, the cache is the storage.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Per-call ETS planners reuse a member cache only 1-3 times while destroying every per-thread cache
serially at the join, which measured slower than master's per-hole temporaries (MeshBoolean with
cMinSweptHoleSize=3: 41 vs 36 ms median; without the member: parity). The cache remains an opt-in
fillContours2DPlan parameter for callers that keep one across many calls.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This reverts commit 84e30ef: the removal was measured on cuts with ~30 holes, where per-call
cache provisioning had nothing to amortize against. On cuts that flood holes (MeshInspector's
Boolean Benchmark spheres produce ~9.7k holes per getPlanarHoleFillPlans call, ~500 per worker)
the per-worker reuse is deep and the cache wins 7-8% of the whole boolean with cMinSweptHoleSize
lowered to 3, while staying neutral at the shipping threshold.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The ETS destructor frees every worker's planner (grown sweep-line cache and triangulation maps)
serially on the calling thread, which dominates small batches: MRTest MeshBoolean with
cMinSweptHoleSize=3 improves 41 -> 38 ms (master 36), while deep-batch cuts keep their win
(Boolean Benchmark spheres: 17.8 vs master 19.5 ms median).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant