Commit graph

8 commits

Author SHA1 Message Date
617ac10c5a render3d: the terrain's GPU maps paged round the camera while the tiles are on
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>
2026-09-29 17:13:11 +03:00
6289564382 render3d: R3D_VKMEM=<frame> says where the GPU memory is, by owner, and the renderer's big CPU arrays
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>
2026-09-29 15:58:59 +03:00
2ce2d997be render3d: the fog wall culls on the CPU - nothing past it costs a draw, a vertex or a caster
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>
2026-09-29 15:38:00 +03:00
9f233104a3 render3d: start-up declared, ov_nine and the caster test on buffers made once - 362 frame sites to 122
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>
2026-09-28 16:21:51 +03:00
f4e121ae94 render3d: actor_own_skin / skin_clone_free, and a frame's scratch and tables made with the device
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>
2026-09-28 16:02:21 +03:00
1675eb451f perf(render3d): the terrain's height and normal in R32F + RG16F, 128 MB where one RGBA32F was 256
The first generation pass's R32F height is kept as ter_height_tex (not copied into an RGBA32F),
so every reader of the height - placement, the read-back, physics, selftest16's 9 mm - sees the
same 32 bits. ternormal.frag writes the baked normal's x and z into ter_normal_tex (RG16F), and
the six places that read it (terrain.frag twice, tersun.frag, grass.vert, grass.mesh,
grass_cull.comp, which takes a third texture at binding 6) rebuild y. SPIR-V regenerated.

Maroon Lake's play, headless Vulkan: 2793 -> 2647 MB. The camp's frame: 0.066% of pixels differ by
more than 8 (mean 0.024/255), isolated grass blades at the slope gate. ludic-dev test 307/307.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 02:31:18 +03:00
80764f85c1 fix(render3d): the Vulkan renderer stops leaking as it loads and draws
- every texture upload malloc'd a CPU copy of its pixels and never freed it (236 MB over the
  valley's boot, and again for every model loaded later): the pixels are converted straight
  into the mapped staging buffer
- the per-level and per-layer views gvk_view_of made outlived their texture, and on MoltenVK a
  view keeps its Metal texture alive: a released texture takes its views with it, and the cache
  is keyed by numbers instead of a string built on every call
- the Vulkan structs filled for a draw, pass, barrier, descriptor set, buffer, allocation or
  upload (about sixty call sites) come from a reused 1 MB scratch ring (gvk_tmp)
- the descriptor-set cache and the retired buffers are emptied in place, not replaced; the grass
  cull's dispatch arguments are made once
- macOS drains an autorelease pool each frame (Vk.frame_pool, lvk_frame_pool in vk_mac.ll)
- R3D_VK_PROF reports Vulkan objects made and destroyed by kind, and every cache's length
- examples/rendering/smooth presents through render3d, so it runs on Vulkan too

The full valley on headless Vulkan loads to 2.18 GB and holds (it passed 8 GB while loading
before). ludic-dev test 305/305, selfhost-test 33/33.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 23:03:38 +03:00
cc384efee0 feat(render3d): the meadow's blades are culled on the GPU - under 1.5 ms of a MoltenVK frame for all the grass
- grass_cull.comp decides each candidate blade once a frame (place, ground, density, water,
  slope, frustum, colour field) and writes survivors into three bands by distance, one indirect
  draw each; grass_inst.vert only bends and places the vertices. OpenGL keeps grass.vert's
  per-vertex path; R3D_GRASS_GPU=0 compares
- compute programs take sampled textures after their buffers (gpu_compute_tex / gpu_dispatch_tex),
  and every dispatch now records a compute-to-draw memory barrier
- the blades as a sward: 4 m cells, bands with five, three and one-quad blades, spacing doubling
  every 18 m to 70 m, never narrower than a pixel; lit facing the sun and leaning to the sky,
  shadow looked up above the ground (it read the terrain as its caster), a colour ramp that
  leaves only the sheath dark, clumps, dry patches and a tussock shade worked out per blade
- scatter layers flagged grass are skipped while the blades draw (and under R3D_NOGRASS);
  R3D_BLADES, R3D_NOBLADES win over the game's setting; R3D_GRASS_S0/D0 for measuring

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 15:47:36 +03:00