nav, physics: Recast's and Detour's memory counted (nav_heap_bytes / _peak / _allocs, a counted allocator registered at load, tile bytes staged from it); package tests that crossing the map and back holds the native bytes
The user's walk showed the footprint rising outside the Ludic heap. Jolt's bytes were already counted; the navmesh's were not, so nothing could see them. Now: - ludic.nav nav_heap_test: fifty crossings of a four-tile map by the resident index hold the tiles in and the native bytes to the first crossing's; a thousand crowd walkers in and out grow nothing; a reset gives back every byte the mesh took. - ludic.physics cross_heap_test: a 1 km map of 64 m chunks kept to a ring round a player crossing corner to corner and back, each chunk a heightfield, twelve owned posts and six owned scaled hulls as the game makes them: six more crossings hold the bodies, the shapes and Jolt's bytes and peak exactly. (A post made as an offset of a cylinder, with only the offset owned, leaked the cylinder every time: the game's solid_pillar owns its cylinder directly.) lib/macos-arm64 rebuilt; lib/windows-x64 needs native/build.sh run on the PC for the new exports. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
7b91300962
commit
b368912c78
9 changed files with 226 additions and 7 deletions
42
packages/ludic.nav/native/shim/nav_alloc.inl
Normal file
42
packages/ludic.nav/native/shim/nav_alloc.inl
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
// nav_alloc.inl - Recast's and Detour's allocations counted apart from the game's (plan 25), as
|
||||
// ludic.physics counts Jolt's: libc's malloc underneath, each block carrying its size in a 16-byte
|
||||
// header, so the tiles, queries and crowds are measured exactly. Registered when the library loads.
|
||||
#include <atomic>
|
||||
|
||||
namespace {
|
||||
constexpr size_t kNavHead = 16;
|
||||
std::atomic<long long> g_nav_live{0}, g_nav_peak{0}, g_nav_allocs{0};
|
||||
|
||||
void *nav_heap_alloc(size_t n) {
|
||||
char *b = static_cast<char *>(std::malloc(n + kNavHead));
|
||||
if (!b) return nullptr;
|
||||
std::memcpy(b, &n, sizeof n);
|
||||
g_nav_allocs.fetch_add(1, std::memory_order_relaxed);
|
||||
long long now = g_nav_live.fetch_add((long long)n, std::memory_order_relaxed) + (long long)n;
|
||||
long long p = g_nav_peak.load(std::memory_order_relaxed);
|
||||
while (now > p && !g_nav_peak.compare_exchange_weak(p, now, std::memory_order_relaxed)) {}
|
||||
return b + kNavHead;
|
||||
}
|
||||
void nav_heap_free(void *p) {
|
||||
if (!p) return;
|
||||
char *b = static_cast<char *>(p) - kNavHead;
|
||||
size_t n;
|
||||
std::memcpy(&n, b, sizeof n);
|
||||
g_nav_live.fetch_sub((long long)n, std::memory_order_relaxed);
|
||||
std::free(b);
|
||||
}
|
||||
void *nav_dt_alloc(size_t n, dtAllocHint) { return nav_heap_alloc(n); }
|
||||
void *nav_rc_alloc(size_t n, rcAllocHint) { return nav_heap_alloc(n); }
|
||||
|
||||
struct NavHeapRegister {
|
||||
NavHeapRegister() {
|
||||
dtAllocSetCustom(nav_dt_alloc, nav_heap_free);
|
||||
rcAllocSetCustom(nav_rc_alloc, nav_heap_free);
|
||||
}
|
||||
} g_nav_heap_register;
|
||||
} // namespace
|
||||
|
||||
// bytes Recast and Detour hold now, the most they ever held, and how many blocks they asked for
|
||||
NAV_SHIM long long nav_heap_bytes(void) { return g_nav_live.load(std::memory_order_relaxed); }
|
||||
NAV_SHIM long long nav_heap_peak(void) { return g_nav_peak.load(std::memory_order_relaxed); }
|
||||
NAV_SHIM long long nav_heap_allocs(void) { return g_nav_allocs.load(std::memory_order_relaxed); }
|
||||
Loading…
Add table
Add a link
Reference in a new issue