Recast compact later hotpaths - #6
Open
OursCodeur wants to merge 4 commits into
Open
Conversation
OursCodeur
marked this pull request as ready for review
August 18, 2026 21:08
OursCodeur
force-pushed
the
split/recast-compact-later-hotpaths
branch
from
August 19, 2026 12:07
28cc437 to
70c9b04
Compare
OursCodeur
force-pushed
the
split/recast-compact-later-hotpaths
branch
from
August 20, 2026 23:14
70c9b04 to
1b6e37e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Putting the decision first, because it's the only permanent cost in this stack and it isn't mine to make. This adds four absolute neighbour indices per compact span, which is 16 bytes per span, allocated for the lifetime of the
rcCompactHeightfield, plus one pointer in the struct. On my two measured examples that's about 500 KiB after compact construction, 513,792 B and 509,760 B, and the delta is exactly4 * sizeof(int) * spanCount. Adding the field changes the public struct layout, so anything linking against Recast has to be rebuilt. The packedconconnections stay where they are, so source compatibility is unaffected. If that trade isn't one you want in our fork, say so and I'll close this rather than argue it, the rest of the stack doesn't depend on it.What it buys : everything downstream of
rcBuildCompactHeightfieldwalks span connections constantly, and each hop currently rebuilds the neighbour's cell coordinates and decodes a packed offset to reach a span it will visit again from another direction a moment later. Erosion, the distance field, region building, contours and detail height traversal all do it. Paying once at construction and reading an index afterwards is the whole idea. Region work entries also stop carrying x and y that can be derived.Separately, detail sampling recomputes the same per-triangle values, origin, edges, dot products and the inverse denominator, for every point query against that triangle. Those are cached per triangle and reused.
Four commits : build the neighbour table, then consume it in erosion, distance, regions, contours and detail height, then the detail triangle cache, then the benchmarks.
Per stage it's a mixed picture : compact construction itself gets 6.7% slower on the single-layer terrain, which is where the table gets built, and comes out neutral on the multilayer one. Erosion gains 45.8% to 49.9%, the distance field 48.1% to 53.0%, regions 17.5% to 20.1%, contours 16.8% to 17.6%. Polygon mesh construction is unchanged source and stays inside 0.5% either way, which makes it a useful control inside the harness. Full synthetic pipelines land at 1.465× and 1.488×.
Per-stage tables, both workloads
192×192 undulating terrain, 32,112 spans :
112×112 terrain, three layers, 31,860 spans :
Same rig and protocol as #5. The detail workload uses 1.0-unit sampling with 0.1-unit error on purpose, which is what makes it that heavy; it proves the mechanism and the exactness for those shapes and nothing about a universal ratio.
Output is identical across 48 timing runs and 4 allocation-tracked runs : same contour, polygon and detail counts, and the same common-output hash in both arms,
6501ab981e7022d5for terrain and70ba6d7cfd4906cffor multilayer.On memory, the allocator-level numbers above are the ones I'd trust. Process peak RSS didn't move on the default allocator, 43,236 KiB in both arms on terrain, because the detail stage's allocations dominate that high water mark and hide 500 KiB completely.