water_reflection_pass made view(water_saved, 16, 16) and view(water_saved, 32, 16)
inside the block that makes the reflection target, so every time the target was
made again (a resize, a render scale, a settings change) two more 16-byte views
were made and the last two dropped: the windowed walk's fence caught one such
frame, +32 B. water_saved is the state's for good, so its views are made once.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AVFAudio keeps a 64-byte AudioQueueOwner for good on every AVAudioPlayer play
after the clip has finished - measured one a play, whatever is called around
it (prepareToPlay, pause, no rewind) and still there after the player is
released; a stop before the play made one every time. The windowed walk showed
it as AudioQueueOwner 73 -> 87 in a minute.
A sound is now decoded once into a PCM buffer and played by a voice of its own
on one shared engine: a player node (the buffer scheduled again on each play,
looping for -1), a varispeed (the rate) and a small mixer (volume and pan).
snd_playing compares the uptime clock with the end worked out when the clip was
played (asking the node where it is made two AVAudioTime objects a call), and
snd_play drains an autorelease pool of its own. The C interface is unchanged.
A harness driving snd_* directly (400 plays past the end, the playing flag
checked during and after each, a loop and a stop, the setters) holds the heap
flat to a block and gets the flag right 400 of 400; the windowed valley
compiles and links against it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- the call graph is by name, and a local or a parameter named as a function (a float bd, part, bx)
linked to that function: a name the function binds itself is never an edge now.
- drain_actions, generated, calls every reducer; a reducer is reached from its action's dispatch,
so the drain's calls are not edges.
- a fresh value flowing into a local that also holds kept memory is still the frame's (storing it
anywhere kept would have made it ESC): HEAP now keeps only a push's growth off the arena.
- the arena starts at its first use rather than at the first frame mark, so boot's temporaries are
scratch too - dead once the Start handlers return - and a scratch site is never a birth.
Plus ludic.hints' rail and three of ludic.update's one-off lines declared. The arena goldens pass
poisoned. Maroon Lake (d79d189f): 39/11/66 -> 30/9/0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ludic.update: whether this copy can update itself is worked out once, when the updater is configured
(the panel asked every frame, building the path to the executable each time); the notes are a list
the state keeps, filled by update_notes_for when the version or the language changes, which the
game calls from its update tick. The feed's address is declared (once, when a check starts).
ludic.steps, ludic.effects, ludic.telemetry: what is left is made on an event and declared with its
bound - an arc's tables, a chapter's columns, a step's fact, an effect's start, end and clear, the
fact pool's growth, the player id, a props record's nesting stack - and two pushes into a caller's
kept list, at most a chapter's steps and the ring's size.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
render3d: shadow_fit, water_reflection_pass, layer_partition_lods and the
GPU cull's scratch are made with the state; v3_dist is scalar; the pushes
into lists sized at start-up, the caps probe, the table growth, the loads
and the constructors declared with their bounds (one statement a line);
the renderer's name made once with the device; the two error messages
given back; the dead lupine models removed.
runtime: a component's text is held interned in its value cell (one copy
per distinct text), so the getter's own text goes with its frame instead
of being kept by ludic.ui's model - 80 of the 83 keeps.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The analysis made every component function a frame root, event handlers (cmp_x_on_delete) too, and
every reducer, whether its action is dispatched every frame or once a trip: a component's 'on'
handlers are no longer roots, and a dispatch is an edge to its action's reducers, so a reducer
counts only when frame code dispatches it. A push into a field declared @max(n) is bounded by the
fence's own check and no longer counted. Maroon Lake: frame_allocs 395 -> 337, frame_keeps 188 -> 169.
ludic.photo: the roll's order and a page of it are kept lists refilled in place (the pack's page
asked for both every frame), its kept lists say @max(256), and a shot's tags, a photograph's fact
and a new roll are declared (once per shot, sale or trip). 18 allocs and 9 keeps -> 0 and 0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gvk_screenshot kept a buffer the size of the screen per shot (the valley scan's largest unreachable
site), and its PPM header and row buffer; each goes on every way out now. Compiled (steady).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The last kept bytes of the leak gate's pack, shop and journal screens (memory_final, count mode).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The valley's reachability scan (R3D_ALLOC_SCAN) listed render3d start-up objects nothing held; all were
dropped after being handed to the driver or copied into a key: gvk_module's SPIR-V bytes, create info
and handle slot; gvk_program's key parts, .spv paths, bindings and layout create infos; the compute
program's the same; gvk_sampler's and gvk_view_of's create infos; gvk_zero_vbuf_get's 64 KB of zeros;
gvk_layout_key's result (always a copy now, freed by gvk_pipeline once its key holds it) and
r3d_program's defines. Each goes once the handle it made has been read. Compiled (steady).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Target's framebuffer and its colour and depth textures, a Mesh's index buffer (gvk_buf_new /
gvk_buf_delete now @creates / @releases GpuBuffer too), a Prim's mesh and an Actor's own skin clone.
ludic deps --resources over main: resource_drops 0, owned_leaks 0; a probe freeing a Target's fbo
alone was reported for its colour and depth (2), then removed. Compiled (steady).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A pipeline has no release: it is kept in the cache for the program's life (gvk_pipe_build stores it).
With every render3d handle annotated, ludic deps --resources over main e447acdd reads 0 drops; a
GpuBuffer made and bound to a local in a game function was reported (1), so the 0 is a real one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>