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>
48 lines
2.1 KiB
Text
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]) }
|
|
}
|