Each pair runs the work as the renderer always has and reads the result back, or makes the same
textures and fills them from bytes instead: impostor_bytes / impostor_from_bytes (and impostor_fill,
for a fog that opens past a layer's cards), terrain_shadow_bytes / terrain_shadow_from_bytes,
sky_ibl_bytes / sky_ibl_from_bytes (sky_precompute split into sky_ibl_make and the convolutions).
A bundle is a word count, a word length per part and the parts (bake_pack / bake_unpack), checked
against the textures' shapes before any byte is used. gpu_vk_readback.ludic reads a texture's level 0,
every layer, as stored; r3d_baked_read reads a bake's file (ludic.base's LBAK header: key and version
must match) since render3d cannot import ludic.base. Nothing calls them yet: behaviour unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
runtime/native/inflate.ludic: ZInflate holds the bit reader, the RFC tables and every table and scratch
list a block builds, made once (z_inflate_new) and rebuilt in place - an inflate allocates nothing, and a
thread that inflates holds a context of its own. RtInflateState keeps one, so z_inflate / z_uncompress /
z_gunzip and their callers are unchanged; z_*_in take the context.
render3d png_jobs.ludic: png_parse (reads, walks the chunks, makes every buffer), png_work (inflates,
unfilters, packs; makes nothing, touches no state), png_take (frees, sets tex_*); tex_prefetch runs
png_work over a list on every core through Job.parallel_for and png_decode takes a waiting result, so
what uploads and in what order is unchanged. Nothing calls tex_prefetch yet. Compiles with the game.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The build's placements ask exact heights over the whole map before any ring exists, and through the
tiles that read the file ~25 000 times in one frame. The copies now stay after the cut and the game
calls terrain_tiles_build_done() when the world is built (the end of world_things, and after a swap's
terrain_reload); every answer is the same before and after, from the copy or the tile. The GPU half
merged here (r3d/tp-rt, r3d/tp-shd: the page table, the pool and the shaders) is unchanged by it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
At the cut the whole 4096 height, normal and photograph textures go, replaced by a coarse
2048 level (the CPU's tt_coarse uploaded; the normal and photograph blitted down on the GPU,
the photograph mipmapped) and a pool of fine tiles in three array textures - heights, normals,
photograph, each layer a tile with a one-texel border - addressed through a tt_n^2 page table
(u_tp_page: slot + 1, 0 = the coarse level). The pool holds the tiles within the reach (900 m,
or the fog wall's when nearer) and a ring, and is made again when the fog wall changes its size
(terrain_pages_fog). Once a frame (tp_frame, beside tt_frame) tiles past the reach and two tiles
go and the wanted ones come in nearest first, 8 a frame, written into a staging buffer kept for
the process (two halves, one per frame in flight) and copied into their layers inside the frame's
own command buffer - no submit of their own - with the page table re-uploaded only when it
changed. tp_bind binds the pool (or 1-layer stand-in arrays while paging is off, u_tp_on = 0) for
every program that reads the ground: terrain_bind_height (models, scatter, shadow_bind, grass),
terrain_bind_prog, the sun pass and the shadow bake. grass_cull.comp reads through the same page
table. R3D_VKMEM prints the pool's line. Tiles off, nothing changes. Compile-only: not run.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Within a pass a row writes only its own still-masked texels and reads only neighbours already let go,
which no row writes that pass, so the result is the single-threaded one. The worker takes a DilateJob
of plain buffers and allocates nothing. tex_dilate_bytes (safe_api) and examples/rendering/dilate.ludic,
which checks it against the old loop on RGB and RGBA atlases of sizes that do not divide (DILATE OK).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
terpage.glsl is the reference block (tpSlot, tpUV, terHeight, terHeightSmooth, terNormalXZ,
terOrtho, tpOrthoRes, tpOrthoLod), pasted by section into terrain.vert, terrain.frag,
tersun.frag, model.vert, grass.vert and grass.mesh. u_tp_on = 0 reads the old samplers with the
old coordinates and filtering; on, a resident tile is read at level 0 from u_tp_h / u_tp_nrm /
u_tp_ortho, anything else from the coarse map now bound under the old names. The B-splines use
the FULL map's texel and take every tap through the page, so a tile edge stays one surface;
blurred photograph reads (lod 1-2.5) stay on u_ortho with the level moved down by the coarse
map's ratio. The fragment stages drop their unused u_height. SPIR-V rebuilt: terrain programs
carry 23 samplers (21 in the fragment stage), up from 19.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The volumetric pass marched 800 m in a fixed number of steps whatever the wall: it now stops at the
wall with steps in proportion (at least 8) - 0.57 -> 0.33 ms at 30 m by the pass timers. SSGI skipped
only past 900 m; it now also skips past 0.85 of a wall, where the fog is solid (u_ao_far). applyFog
samples the fog's colour only where its weight is above zero. A card layer whose casters inside the
wall are 80% or more of it (a kilometre's wall) casts from its static list and is not refiltered and
re-uploaded every 4 m of camera motion. Fog off: the frame unchanged (0 pixels over 8).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
terrain_tiles_to(dir, key) before a map is made: once made, its heights, photograph and baked normals
are written tile by tile (64 m, TT_TEX) to <dir>/<key>.tiles - fresh every time, header last, so no
other generator's or a torn file is ever read - and the whole copies go. Every read goes through the
tile: resident, else read from the file there and then into a 2048-tile clock, so terrain_height,
terrain_height_smooth, terrain_ortho* and terrain_chunk_heights answer exactly what the whole copy
did (R3D_TT_CHECK: worst 0.0 m over 4000 points) whatever is resident - two machines and the boot's
placements agree to the bit. terrain_chunk_heights takes render3d_st mut for it.
terrain_height_near(read-only): the tile if it is in, else a coarse 2048^2 level (worst 0.91 m), never
the file - for line checks that must not take render3d_st mut. terrain_texel() is the texel size,
terrain_tiles_prefetch(x, z, r, budget) reads ahead, and a frame that reads more than 8 tiles says so
once. R3D_TERRAIN_TILES=<dir> turns it on for a run.
At the overlook with MallocLargeCache=0: 1731 MB off, 1658 MB on; 192 MB written in ~200 ms; the
frame unchanged (0 pixels over 8).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The DEM (R16 with mips, 44 MB) was read only by terrain_generate and kept for the map's life; a
world swap loads its own again. The height-field sun shadow was one RGBA32F for three values and an
unused fourth: the lowest lit height stays 32-bit (a receiver 3000 m up compares against it to a
quarter metre), the occluder distance and the cloud mask go beside it in RG16F (64 -> 32 MB), baked
in a pass of their own (TS_AUX): a pipeline takes one colour format for all its targets, and drawn
together into R32F + RG16F the writes went nowhere and nothing was lit.
-75 MB at every fog level, off included (2008 -> 1933 MB at the overlook); the fog-off frame is
unchanged (0 pixels over 8).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The ring held 2 x 64 MB of host memory for a frame that uses 3 MB at most (2761 draws at the
overlook, 1.7 MB in town): it starts at 16 MB a half, and a frame that ever runs out grows it for the
frames after (gvk_ring_grow, making the kept sets again; R3D_RING_KB=<n> starts small to watch it).
-95 MB at every fog level, off included.
A streamed layer's arrays and its stream's arena are made for the reach a fog wall leaves (twice its
area's share, at least 4096 instances) and emptied to refill within the frame budget
(fog_streams.ludic). The near streams' reach is already inside most walls, so it is -9 MB at 30 m.
R3D_VKMEM adds the ring's high-water mark, the largest buffers and the streamed layers' share.
Footprint at the overlook: 2008 MB off (2108 before this phase), 1748 MB at 175 m, 1682 MB at 30 m.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A cascade that begins past the wall was fitted and cleared every frame for nothing: the array is now
made with the ones that begin inside it (2 at 30 m, 3 at 175 m, 5 without a fog) and made again when
the wall changes that count; shadow maps 80 -> 32 MB at 30 m, 48 MB at 175 m. A layer the array lacks
clamps to its last, read only past the wall where everything is fogged.
The sky: at walls of 110 m and nearer the whole sky is the fog's colour, overhead too, with a soft
glow at the sun or moon and no stars or clouds through it, easing out by 175 m; from 175 m on the
horizon band alone, and off as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Inside the wall every instance is a mesh whose LOD2 casts its shadow, so a card is neither drawn nor
cast there: fog_impostors.ludic frees a layer's atlases while wall + margin < its near (trees 420-480
m, rocks 320 m) and paints them again from the model when it opens past it (impostor_paint, split out
of the bake). Impostor atlases get memory of their own, so a release goes back to the driver instead
of leaving a hole in a shared block. R3D_FOG_AT=<frame> with R3D_FOG_TO=<m> changes the wall mid-run.
At the overlook: 2102 MB off, 1824 MB at 30 m (249 -> 67 MB of atlases; the rest is the ground cover's
card atlases, drawn inside the wall); opening re-bakes 16 layers in 42-46 ms, back to 2102 MB.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every allocation carries the owner its maker was wrapped in (models, terrain, impostor atlases,
scatter, grass, shadow maps, frame targets, sky, water, game textures, made in a frame), and the
report prints each owner's images and buffers, the 25 largest images and the scatter layers',
streams' and terrain's CPU copies. Nothing drawn changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Whatever is cut at the wall is now cut inside full fog: at 0.3-1.0 the last metres before the cut
were 80-95% fogged against fully fogged ground behind, which drew a line of tufts at 70 m and a
dark band of blade tips at 30 m. The blades thin out while the fog is still coming in.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cam_sphere_visible refuses a sphere wholly past the wall (terrain patches, scatter cells, stream
chunks, grass tiles, water), actors past it are skipped for draws, casters and outlines, the grass
reach and the streams' generate-and-gather reach end at it, and the card shadows cast only from
the wall's share of a layer (fog_casters.ludic, rebuilt every 4 m). r3d_beyond_fog is exported for
the game to skip animating what will not be drawn. Render-only: no query of the ground or the world
changes, and off is the old frame (0 pixels over 8 against 160ce96, twice per side).
Draws per frame at the overlook: 2757 off, 1104 at 175 m, 484 at 30 m.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every lit program blends toward the horizon colour (the prefiltered sky with the sun's inscatter,
so it carries the day's grade); the sky's horizon band is pulled to the same colour, wider the
closer the wall. The far plane, scatter and stream cull reach and the shadow cascades are clamped
to the wall; the reflection pass shares the shaders and the cull. 0 is off: every branch is gated
on u_fog_wall > 0 and r3d_reach hands back the far it was given. R3D_FOG_WALL=<m> for a shot.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The valley self-test died after the Lamar swap, loading Maroon's pine LODs:
gvk_fr_blk (the device suballocator's free ranges) grew past its @max(4096).
Nothing failed to coalesce: a range was carved at the buffer's bare size, so
what was left after it started unaligned and the next buffer, rounding its
start up, left a sliver before it that no later buffer could use. One host
block held 3,968 small buffers and 3,718 free ranges between them - 496 KB
free in all, 133 bytes a hole. Those merge away when a neighbour is freed, so
nothing leaked; the count simply rose with live buffers.
gvk_mem_new now carves `span`, the size rounded up to the alignment, and
records it as the allocation's length, so a free gives back exactly what was
carved and the rest of a range always starts aligned.
The same repro (the lab's `tests` scene, headless, a fresh test root): the list
never reached 256 slots (it passed 4096 before), the run completes (exit 0),
LAMAR OK and THINGS OK. r3d_small was 0 throughout, as ECS said.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A small window (r3d_small: the launcher's UI process) draws its interface flat
and tests no depth, but gvk_screen_make gave it a 32-bit depth image the size
of the screen - 8 MB at 1920 x 1080. In small mode the screen has none: the
pass and a clear with no depth attachment already leave depth alone (clearing
DEPTH_BUFFER_BIT with no attachment is skipped), and pipelines follow the
pass's formats.
With the launcher's own 1920 x 1080 backdrop (maroon-lake game/launcher-small),
the launcher window measured 378 -> 295 MB of footprint at 25 s; its headless
shot (R3D_SMALL=1, so no depth) differs from the unchanged build by 0.279% of
pixels, the resized backdrop, and two runs of each are identical.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The atlas is white glyphs on alpha and the overlay reads only the coverage,
but it was uploaded RGBA8: Maroon Lake's 3072 x 2688 atlas was 43 MB of Metal
memory with its mips, the largest single thing in the launcher window.
overlay_font now keeps the alpha alone as R8 (ov_font_tex; any other PNG
shape is uploaded as before) and overlay.frag reads .r - the white fallback
texture reads 1 there as it did from .a.
Launcher window, graphics regions: the atlas 43.0 -> 11.5 MB; process
footprint at 25 s 402 -> 378 MB. The headless launcher shot is identical to
the unchanged build's (0 pixels differ; two unchanged runs differ from each
other by 1.69%, and the new one by the same against them).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With one frame in flight, a buffer filled every frame (the interface's vertex
buffer, ov_vbo) was still being read by the frame on the GPU when the next
frame filled it, so gvk_buf_reserve released it and made a new VkBuffer and
its memory every frame - and every new buffer took a prime submit of its own.
Each handle now keeps a second buffer (gvk_bsp_*): a busy one swaps with it
when it is free, else the busy one becomes the second and one new buffer is
made; releasing a handle lets both go.
The launcher window (--launcher-ui, R3D_VK_PROF, per 120 frames): buffers
made/destroyed 120/120 -> 0/0, command buffers 240 -> 120 (one a frame). Its
footprint does not move with it (358 MB at 40 s, both): the churn was work,
not memory.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Set before r3d_open: the frame's uniform ring is 2 x 4 MB (was 2 x 64), memory blocks are 8 MB with
anything over 4 MB on its own (was 64 and 16), and the descriptor pools are an eighth of a world's.
Maroon Lake's launcher window draws a picture and text and never becomes the game; its launcher home
and Settings page draw pixel-identical on it. The full-size path is unchanged (steady: STEADY OK).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gvk_swap_make freed none of what it made - the surface caps, the count, the
format and present-mode lists, the create info, the handle out - and replaced
gvk_swap_images without freeing it: a few hundred bytes every time the window
changed size, went to or from Retina, or the chain came back suboptimal. Each is
now freed on every way out (the minimised return and the failed create included),
and the old image list before the new one is made.
A windowed memory walk built against it (main 5ee600d2, 300 s, the autopilot
opening screens every 6 s): footprint 2816 MB at 120 s, 2818 MB at 300 s; the
fence judged 0 frames kept.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gvk_sc_make's cache (program + bound textures -> a set in the kept pool) evicted
nothing until the pool's 8192 sets filled. A texture freed - a photograph's
thumbnail, a wall frame, any picture a screen loads - left its entries and their
sets behind for good, since its handle's generation moved on and the key could
never match again: 262 KB over 12 minutes of the user's play, and bound only by
the pool, hours away.
gvk_tex_release counts the releases (gvk_sc_dead); at GVK_SC_DEAD_MAX (256) the
next frame's start resets the kept pool and empties the cache (gvk_kpool_reset,
which waits for the frame in flight), and each draw still in use makes its set
again the first time it is drawn - the budget is the live combinations plus at
most 256 textures' dead ones. The declared reason on gvk_sc_make was the
swapchain's, copied; it now says what the cache is and what bounds it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every tree on the map was drawn as an impostor card into all the cascades,
the near two included, whose receivers (within 60 m) are shaded by the near
trees' own LOD2 meshes cast beside them. The cards now cast from cascade 2 on.
Aspen view, same main and foundations, 240 frames, with the figure capsule:
shadow instances 323,587 -> 202,743; shadow GPU 4808-4942 -> 4579/4578 us;
median frame 17200-17693 -> 17178/17205 us. The look held: shots of the view
taken twice a side are identical within a side (0.00% of pixels past 8) and
differ by 0.04% across (every pairing).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In cascades 2 and out (receivers from 60 m) a skinned figure of more than
eight pieces - a dressed person, up to 86 of them - casts one capsule its own
height (radius 13% of it) instead of every visible piece: past 60 m nobody
can tell a garment's shadow from the body's. Animals (a piece or two), rigid
props and the near two cascades are unchanged. The capsule is made once.
Aspen view, same main (30ec1722) and foundations (15f1020), 240 frames, twice
a side: shadow draws 1889 -> 1799; shadow GPU 4808/4819 -> 4710/4714 us;
median frame 17200/17221 -> 16910/17128 us. Most of the far cascades' actor
draws turned out to be rigid props (the census's "skinned" counts draws, not
actors), so this is the smaller of the two.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The user saw kit grass, ferns and flowers lit up at night. Nothing was
emissive and every layer draw binds the scene's lighting; what differed was
occlusion. At night the sky's light is nearly all the light, and it is
scaled by ambient occlusion: the meadow's blades are heavily occluded near
the ground, while a kit plant's own AO map knows nothing of the grass around
it, so it took the full sky. By day the sun hides the difference.
model.vert hands on each vertex's height above the model's base (v_lh), and
a non-blade FOLIAGE plant under 2 m scales its AO from 0.35 at the ground to
1 at 0.9 m. Crowns and the blades are untouched.
Measured in the lab (R3D_AT=tree, same binary, old and new SPIR-V): the fern
against the meadow beside it was 2.05x at 23:00 and 1.66x at 13:00; now 1.58x
and 1.60x - the same relation by night as by day. The flowers' brightest
tenth at night 78 -> 52. The noon frame moves 8.4% of pixels past 8 (the fern
and flower bases a little darker; run-to-run noise is part of that).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The user's play found the aspens' branches swinging like rope. Three terms:
- the whole-tree bend reached 22% of the tree's height at a gust's top (three
metres on a 14 m aspen); 4.5% now, and the side-to-side part at a quarter of
that and slower;
- a branch term: nothing within 0.35 m of the trunk, growing with the distance
out, phased by the direction the branch leaves the trunk (constant along a
branch, so it moves as one piece and its neighbours are out of step);
- the aspen's leaf flutter took its phase from each vertex at eleven radians a
metre, which bent every twig along its length; about three now, and half the
amplitude.
Measured on the lab's `aspen` view (10-frame bursts, crowns only, same binary
with the old and new SPIR-V): pixels moving more than 8 over ten frames 16.8%
-> 9.7%, frame to frame 4.4% -> 2.1%. SPIR-V rebuilt with `ludic-dev shaders`
(vertex stages only; the manifest is unchanged).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every read of the screen made bytes(screen_w * screen_h * 4) and dropped it:
the user's manual play left 22,970,368 B unreachable (exactly 3024x1898x4)
at gpu_vk_draw.ludic:1673 - one per photograph (shots' ph_grab, through
gpu_read_screen_bytes). The read-back is now gvk_read_px, made once and made
again (the old one freed) only when the screen's size changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
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>
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 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>
The retire, free-range and spare-id lists (4096), the per-buffer flags and the prime handles (16384),
the stage and the actor spares (2048), the per-program uniform offsets (4096) and a draw's set key
(130) are each @max'd at the room gvk_startup_state / actor_init made, so outgrowing one ends a dev
run with its name instead of quietly copying. Compiled (steady).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The texture and framebuffer tables grow to 4096/1024 and gvk_prime's queue is made in
gvk_startup_state; gpu_unit_2d's and the actor lists' lazy starts go. @alloc_ok on layer_room (a layer
outgrowing its cap), overlay_init, gvk_read_screen, gvk_hdr_metadata (a settings change), DLSS's
gsl_struct/gsl_fn, an actor's part tables (given back by actor_release) and the pool's fallback.
Birth leaks freed: gpu_caps_probe's create structs, gpu_caps_fake's text, gvk_note's line,
gvk_layout_key's table and each key it grew from, ov_text_wrap's slices, the default sky path.
(ludic deps --births still lists freed sites: its walk does not see free().) Compiled.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Seventeen inline messages (allocation failures, missing conversions, the no-pipeline and compressed-
target warnings, resize and swapchain lines, the test-frame camera line, DLSS evaluate, shadow memory,
stream and terrain debug, the water test) are each a function of their own under @alloc_ok, taking
numbers, so the paths that say them hold no site. post_measure's two 1x1 exposure targets are made in
post_init and DLSS's evaluate structs in gsl_init; DLSS's camera up is a state field. The lists play
pushes into (retired, free ranges, spare ids, the per-buffer gpu flags, the stage and the actor spares)
get their room at start-up (gvk_room_*, actor_room), so a push fits. Compiled (steady).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Render3dState's cam_planes, gpu_u_tmp, q_scratch, ov_nine_buf, the caster test's four points,
ter_bw, ter_scr and water_saved default to their buffers (as ludic.anim's clip state does), so the
lazy starts in cam_planes_update, gpu_tmp, terrain_height_smooth, ter_scratch, the water pass and
gvk_startup_state go. q_euler makes its views of q_scratch once (guarded on the view now). Compiled.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
shadow_pass's fbo status and probe block, shadow_bind's two location prints, layer_partition_lods'
LOD line and layer_update's dump are each a function of their own under @alloc_ok (debug switches
only), so the passes themselves hold no allocation site. Compiled (steady).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The renderer starts itself from the first frame (gpu_select), so the device, its tables, the
manifest and programs, grass, shadows, sky, terrain textures and the actor pool read as frame
allocations: each is @alloc_ok as start-up, with gvk_fail (a failure) and the actor census and
texture dump (debug switches). ov_nine's four corner/uv arrays are one floats(16) made in
gvk_startup_state; ac_in_light's four points are made there too. Compiled (steady, and ludic deps
over main 4316ff97).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
actor_own_skin(a) gives an actor its own clone of its model's skeleton and actor_release frees it
(skin_clone_free: the pose, matrices, views and scratch the clone made; the rest data stays its
source's). gvk_startup_state, at the end of gvk_init, makes the scratch and tables a draw used to
make on first need (gvk_tmp_buf, the pipeline fast cache, the set key and per-program uniform
offsets pre-sized to 4096 programs, skip/seen/size words, spare and retired lists, the grass cull's
words), and those frame paths no longer start them. Compiled (steady).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>