ludic/packages/ludic.render3d/fog_streams.ludic
Orkuncakilkaya f8c81e4066 render3d: the uniform ring is 16 MB a half and grows when a frame outgrows it; streamed layers are sized for the fog's reach
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>
2026-09-29 16:22:45 +03:00

48 lines
2.1 KiB
Text

# fog_streams.ludic — a streamed layer's arrays and its stream's cache are sized for the reach the fog
# wall leaves it. Both were made for the stream's whole reach (800 m for the far cover) and kept
# whatever the fog; under a wall the ground cover inside it is a sliver of that.
const FOG_STREAM_SLACK: float = 2.0 # room over the area's share: the bands are not evenly dense
const FOG_STREAM_MIN: int = 4096 # instances: the least a layer is ever given
# the capacity a stream's layer needs at the wall: its area's share of the whole reach, with slack
function fog_stream_cap(render3d_st: Render3dState, s: Stream) -> int {
let reach = r3d_reach(render3d_st, s.reach)
if render3d_st.r3d_fog_wall <= 0.0 or reach <= 0.0 or reach >= s.reach { return s.cap0 }
let k = (reach + s.size) / (s.reach + s.size)
let share = Math.min(1.0, k * k * FOG_STREAM_SLACK)
return Math.max(FOG_STREAM_MIN, Math.min(s.cap0, int(float(s.cap0) * share)))
}
# the layer's arrays and the stream's arena made at that capacity, and the cache emptied to refill
@alloc_ok("a fog change from Settings: a streamed layer's arrays made again at the reach the wall leaves")
function fog_stream(render3d_st: mut Render3dState, s: Stream) -> void {
let cap = fog_stream_cap(render3d_st, s)
let l = s.layer
if cap == l.cap { return }
free(l.inst); free(l.scratch)
l.cap = cap; l.have = cap; l.count = 0
l.inst = floats(cap * INST_FLOATS)
l.scratch = floats(cap * INST_FLOATS)
if l.lvl != null { free(l.lvl); l.lvl = words(cap) }
free(s.arena)
s.arena = words(Math.max(cap, 4096) * 2 * INST_FLOATS)
s.top = 0
let chunks = s.chunks
while len(chunks) > 0 {
let c = List.pop(chunks)
c.off = -1
push(s.spare, c)
}
s.n = 0
for h in 0 .. STREAM_HASH { s.htab[h] = 0 }
# regrown within the frame budget from the next update, not all in one frame
s.last_cx = 999998
s.pending = true
l.view_gen = -1
}
function fog_streams(render3d_st: mut Render3dState) -> void {
if render3d_st.stream_all == null { return }
for i in 0 .. len(render3d_st.stream_all) { fog_stream(render3d_st, render3d_st.stream_all[i]) }
}