runtime (macOS): MoltenVK opened first, so a process holds one - the SDK's loader in /usr/local/lib loaded its own MoltenVK beside the linked one and the program drew through it; the loader only when a layer is asked for, pinned to ours (VK_DRIVER_FILES, lib/macos-arm64/MoltenVK_icd.json) unless a driver is named. steady's stream round: a 300-cell warm-up, then the least of three 150-cell windows

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-30 00:41:27 +03:00
parent 2e0a7f0031
commit 1ab5ca14fb
4 changed files with 134 additions and 17 deletions

View file

@ -97,21 +97,36 @@ program Steady {
return grew
}
# bytes gained while the camera crosses new ground for 300 cells: a streamed layer's cache holds
# at most 256 chunks, so it fills, evicts and compacts on the way. Each chunk once brought its
# own record and copy, made as the ground was found.
# the camera over `cells` new cells from cell `from` on, two frames a cell; the heap's gain across them
function stream_window(render3d_st: mut Render3dState, from: int, cells: int) -> long {
let before = settled(render3d_st)
for step in from .. from + cells {
cam_set(render3d_st, float(step) * 32.0, 3.0, float(step % 7) * 32.0, 0.0, -10.0)
for f in 0 .. 2 {
r3d_frame(render3d_st, float(step * 2 + f) / 60.0)
r3d_present(render3d_st)
}
}
return settled(render3d_st) - before
}
# bytes gained while the camera crosses new ground: a streamed layer's cache holds at most 256
# chunks, so over a warm-up of 300 cells it fills, evicts and compacts; then the least of three
# windows of 150 more. A leak grows in every window; a pool finding a new high-water mark, or the
# GPU's lag on a busy machine (one sample read +75 KB where its twin read -5 KB), does not repeat
# three times. Each chunk once brought its own record and copy, made as the ground was found.
function stream_rounds(render3d_st: mut Render3dState, m: Model) -> long {
render3d_st.STREAM_MAX_CHUNKS = 256
let l = layer_new(render3d_st, m, 20000, false, 0.0, 0.0, 400.0)
let st = stream_new(render3d_st, l, 32.0, 96.0, 32.0, 64.0, 80.0, 96.0)
cam_set(render3d_st, 0.0, 3.0, 0.0, 0.0, -10.0)
frame_rounds(render3d_st, 10)
let before = settled(render3d_st)
for step in 1 .. 301 {
cam_set(render3d_st, float(step) * 32.0, 3.0, float(step % 7) * 32.0, 0.0, -10.0)
frame_rounds(render3d_st, 2)
stream_window(render3d_st, 1, 300)
var grew = stream_window(render3d_st, 301, 150)
for w in 1 .. 3 {
let g = stream_window(render3d_st, 301 + w * 150, 150)
if g < grew { grew = g }
}
let grew = settled(render3d_st) - before
# every chunk kept, after all that evicting and compacting, still holds its own cell's instances
var wrong = 0
for i in 0 .. st.n {
@ -180,7 +195,7 @@ program Steady {
let text = Fs.read_text("packages/ludic.lab/plate/plate.gltf")
parse_rounds(render3d_st, text, 20)
let grew_p = parse_rounds(render3d_st, text, 200)
print(`steady: the buffer path gained {grew_b} bytes over 5000 rounds, the frame {grew_f} over 600, a glTF parsed and freed {grew_p} over 200, a model loaded and let go {grew_m} over 200, an actor placed and released {grew_a} over 2000, a frame drawing 20 to 200 actors {grew_r}, a stream over 300 new cells {grew_s}`)
print(`steady: the buffer path gained {grew_b} bytes over 5000 rounds, the frame {grew_f} over 600, a glTF parsed and freed {grew_p} over 200, a model loaded and let go {grew_m} over 200, an actor placed and released {grew_a} over 2000, a frame drawing 20 to 200 actors {grew_r}, a stream over 150 new cells (the least of three windows) {grew_s}`)
# a few KB of slack for what the system's own libraries keep (Metal's caches, the allocator)
var ok = grew_b < 16384
if not ok { print("steady: FAILED - releasing and making a buffer again leaves memory behind") }
@ -211,6 +226,8 @@ program Steady {
ok = false
print("steady: FAILED - Vulkan's counted host allocations grow with the frame")
}
# 4 KB over 150 new cells, in the least of three windows: a chunk's own record or copy kept would be
# 20 to 51 instances' worth (over 150 bytes) a cell, some 25 KB a window
if grew_s >= 4096 {
ok = false
print("steady: FAILED - streaming new ground leaves memory behind")