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>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-29 17:13:11 +03:00
parent 329439cf61
commit 617ac10c5a
15 changed files with 571 additions and 12 deletions

View file

@ -848,6 +848,35 @@ export state Render3dState {
tt_reads: int = 0 # tiles read from the file, over the run and this frame
tt_frame_reads: int = 0
tt_warned: bool = false
tp_on: bool = false # the GPU pages the fine tiles round the camera (terrain_pages.ludic)
tp_page_tex: int = 0 # R32F tt_n a side: slot + 1 of each resident tile, 0 = the coarse level
tp_h_tex: int = 0 # the pool: heights, normals and photograph, a layer a slot
tp_n_tex: int = 0
tp_o_tex: int = 0
tp_dummy_page: int = 0 # bound while paging is off: an array sampler needs an array
tp_dummy_h: int = 0
tp_dummy_n: int = 0
tp_dummy_o: int = 0
tp_slots: int = 0
tp_reach: float = 0.0 # metres the pool was sized for
tp_page: floats = null # the page table's CPU mirror, uploaded whole when it changes
tp_tile_of: words = null # per slot: its tile, or -1
tp_free: words = null # the free slots, a stack
tp_nfree: int = 0
tp_dirty: bool = false
tp_cam_t: int = -1 # the camera's tile when residency last ran
tp_short: bool = false # a wanted tile was left for the next frame
tp_cand: words = null # this frame's loads, nearest first: their tiles, distances, slots
tp_cand_d: floats = null
tp_cand_s: words = null
tp_zero: words = null # the one layer a 2-D texture has, for gpu_layers_copy
tp_st_buf: long = 0 # the staging buffer the loads are written into, two halves
tp_st_mem: long = 0
tp_st_ptr: pointer = null
tp_st_bytes: int = 0
tp_loads: int = 0 # tiles loaded over the run, and this frame
tp_frame_loads: int = 0
tp_bytes: int = 0 # the pool's and the page table's GPU bytes
ter_shadow_prog_aux: int = 0 # the bake of the aux target (TS_AUX)
ter_shadow_aux: int = 0 # beside it, RG16F: the occluder distance and the cloud mask
ter_shadow_yaw: float = 1000000000.0 # the sky yaw it was baked for