Skip to content

port: face-bound trimmed surface cache and IntersectTSBound's cached branch - #697

Merged
bherbruck merged 7 commits into
tangency-labs:mainfrom
brett19:port/face-trimmed-cache
Oct 24, 2026
Merged

bherbruck merged 7 commits into
tangency-labs:mainfrom
brett19:port/face-trimmed-cache

Conversation

@brett19

@brett19 brett19 commented Oct 24, 2026 •

Copy link
Copy Markdown
Collaborator

Face surfaces now keep their trimmed surface cache (SmTrimSrfCache) the way upstream's smsurf_GetSurfaceCache does. IntersectTSBound takes its cached branch on that cache, with its context mark.

How the cache history is modelled

  • Where it lives. A surface's cache slot was already shared with its evaluation copies (Surface::cache_tree_state). It now also holds the trimmed cache (TrimCacheSlot: empty, stored, or checked out while in use). A stored cache's own surface copy gets a slot of its own, so the slot never owns itself.
  • Lookup. query::face::p2::face_surface_cache follows SmCacheMgrTSrf::CacheMakeOrValidate:
    • a stored cache is reused, whether it was built with the face or standalone, unless m_bFaceWasModified is set and editing is off (then it is made again);
    • a new cache is built with the face, or standalone while the owner is cleared, and kept;
    • a lookup while the cache is checked out gets an unkept build. That was the old behaviour; upstream would find the same object.
  • Other readers and writers.
    • PointClassify reads the kept cache (GetObjectCache).
    • Notify(PRE_EDIT) marks it modified.
    • A face-unaware smsurf_GetSurfaceCache made while the face's owner is cleared keeps the standalone cache it built (Surface::keep_standalone_trim_cache).
  • Owner-cleared scopes. IntersectVertices and IntersectEdges clear the surface's owner while they read gaps (SmCurveClass.cpp:14700, 15315), so the port's gap reads now run in an owner-cleared scope. A C++ probe confirmed it (not committed): heal.face_props' first cache is standalone (m_bHaveTSurfaceCache 0, point test 0, no UV trims).
  • Scratch copies. The classification snapshots, HasSeamProblem's seam objects and curve_class::p4's binormal and sector copies stand for the B-rep itself. They now share its cache slots (Brep::clone_sharing_surface_caches). These are the fix: B-rep copies keep geometry owners #681 copy sites this change needed; the other copy sites are unchanged.

The pattern for copy sites (how the face cache reaches curve_on_classify)

curve_on_classify still takes &Brep. Nothing new is threaded through its signature: the face cache travels with the surface object, and the mutable work happens on a copy that stands for the B-rep.

  1. Make the copy with Brep::clone_sharing_surface_caches(), not clone().
    • Each face surface of the copy shares the original's cache slot, so a trimmed cache made, reused or marked on the copy is the original surface's own.
    • The trimmed cache is stored on the slot as TrimSrfCache<EntityRef>, and the ids are the same in the copy.
    • curve_on_classify_cached's private snapshot is made this way. A caller that already holds a &mut Brep can pass it as the snapshot, through CurveOnClassifyCache, as fix: IsInnerShell classifies on the B-rep itself (region nesting) #694 does.
  2. Fetch the cache with query::face::p2::face_surface_cache(k, &mut brep, f) on whichever B-rep is mutable (the copy or the real one).
    • The call returns a FaceSurfaceCache lease, and lease.release() gives the cache back to the slot. Release on every path, errors included.
    • While the cache is out, nested lookups get an unkept cache and PointClassify finds none.
  3. Map the handles to the caller's. Code in another handle space maps EntityRef vertex uses and edge uses, as BoundaryFace::cache_handle does in IntersectTSBound, or uses TrimSrfCache::map_handles.
  4. Mark scopes where upstream clears the surface owner with Brep::begin_surface_owner_cleared(f) / end_surface_owner_cleared(f) (CreateUVTrimCurve(s), IntersectVertices/IntersectEdges gap reads). A cache made inside one is standalone and kept.

Consumers

  • IntersectTSBound fetches the face cache itself. When the cache is up to date it visits only the leaf nodes' vertex uses and edge uses, under SmNewMarkAndLock, and makes a missing UV trim curve (GetOrCreateUVTrimCurve) when it visits an edge use.
    • BoundaryFace maps the cache's handles to the caller's (cache_handle).
    • The CurveOnClassifyObjects::trim_cache hook is gone.
  • The ray path (face::p4::global_line_intersect*). A face-owned surface solves on the kept face cache. The caller-held cache now only stands for a surface without a face, which fixes fix: IsInnerShell classifies on the B-rep itself (region nesting) #694's 14 IsInnerShell deferrals.
  • ComputeUVPoint's global solve. It checks out the face cache around the solve, which stands for m_bHaveTSurfaceCache off. The fast drop tests against the face when the cache is trimmed and has point testing on.
  • EvaluateBinormal.

Marks

The context marks and locks are now atomics, and Context::claim_mark returns a guard that unlocks on drop. So IntersectTSBound claims Mark2 with &Kernel, without an &mut Kernel cascade through CurveOnClassify's ~15 callers and its traits. Please review this choice.

Harness

  • The shared graph import's gap refill would now build face caches with UV trims the C++ B-rep lacks (topology.brep_p4). It now runs as during a Boolean operation and drops the caches it kept.
  • topology.brep_p4 seeds the cache states the C++ file read leaves.
  • intersect.curve_class_p9's ComputeUVPoint fixture attaches a cache with point testing off first, as its shim does.

Results (CI vs main 9bc152f)

heal.face_props                 1431 -> 1439 pass, 97 -> 89 deferred (the 15 surface.surface_p2 cache-history deferrals are gone)
offset.offset_geometry_creation  832 ->  835 pass, 66 -> 63 deferred

No other unit changes. topology.face_p1, sweep.trans_sweep_geometry and blend.fillet_corner_p1 differ from the 9bc152f reports, but the same way as on the latest main.

Merged locally with #694 (not part of this PR):

PASS topology.topology_traverser cases=909 passed=909   (#694 alone: 895 passed, 14 deferred)
topology.brep_p6: the 7 region-nesting cases pass, Mark2 included (their known_mismatches entries can go)
primitive.primitive_creation_p2: gen0201, gen0214 pass

skin_concentric_loops still differs (record[7] cpp 1, rust 2), so #694 keeps that entry.

Local checks: fmt, clippy -D warnings (with difftest/cpp), cargo test -p solidcore (2083 passed) and wasm all pass. Details are in plan/findings.md ("Face surfaces keep their trimmed surface caches").

🤖 Generated with Claude Code

… branch

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brett19 and others added 6 commits October 23, 2026 18:03
…ntersectTSBound's cached branch

The surface's cache slot now holds its SmTrimSrfCache: smsurf_GetSurfaceCache of a
face surface reuses it (CacheMakeOrValidate), PointClassify reads it
(GetObjectCache), Notify(PRE_EDIT) marks it modified. IntersectTSBound fetches it and
takes the cached leaf-node branch with its context mark (SmNewMarkAndLock, claimed
through atomic context marks).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…und makes missing trims in its cached branch

ComputeUVPoint fetches the face surface's cache (smsurf_GetSurfaceCache) and keeps it
checked out around GlobalPointSolve, whose fast drop classifies on the B-rep when the
cache is trimmed with point testing on. IntersectTSBound's cached branch runs its
traversals itself, so GetOrCreateUVTrimCurve makes a missing trim on the
classification snapshot when the edge use is visited.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s cleared are kept; scratch copies share the cache slots

IntersectVertices and IntersectEdges clear the face surface's owner while they read
the vertex/face and edge/face gaps, so a trimmed cache that ComputeUVPoint makes then
is a standalone one, and upstream keeps it on the surface (SmCacheMgrTSrf). The
face-unaware smsurf_GetSurfaceCache keeps such a cache too when it is made in an
owner-cleared scope. The classification snapshots, HasSeamProblem's seam objects and
the curve classification's binormal and sector scratch copies stand for the B-rep
itself, so their face surfaces share the B-rep's cache slots
(Brep::clone_sharing_surface_caches).

difftest: intersect.curve_class_p9's ComputeUVPoint fixture attaches the shim's cache
first, as the shim does; topology.brep_p4 seeds the face surfaces' cache states a
file read leaves.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cs for the kept trimmed caches

The shared import recomputes the cached gaps the C++ export reported. Its
ComputeUVPoint calls now fetch the face surface's trimmed cache, which would build it
with the face (making UV trim curves the C++ B-rep does not have). The refill now runs
as during a Boolean operation and drops the trimmed caches it kept, leaving each
surface's slot as before (topology.brep_p4: 241 -> 302 passes).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…dings and unit notes for the kept trimmed caches

A face cache built while the B-rep is edited has no face data, and the base cache's
GetTree would ask to rebuild it with the face (an Unported dispatch gap): the face
source's own GetTree stands. Found with tangency-labs#694 merged (primitive_creation_p2 gen0201,
gen0214 then pass).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@brett19
brett19 marked this pull request as ready for review October 24, 2026 03:17
@bherbruck
bherbruck added this pull request to the merge queue Oct 24, 2026
brett19 added a commit to brett19/solidcore-fork that referenced this pull request Oct 24, 2026
…cy-labs#697's kept trimmed caches

Merged tangency-labs#697 (face surfaces keep their trimmed caches; IntersectTSBound's cached
branch and its mark). The 7 brep_p6 region-nesting known mismatches now pass and are
removed; the unit is green (595/595). The two skin annulus cases still differ (the cap
comes out as two faces) and stay listed with a findings entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merged via the queue into tangency-labs:main with commit d2944e9 Oct 24, 2026
7 checks passed
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.

2 participants