r3d/runtime: allocs, keeps and births at 0 on this side
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>
This commit is contained in:
parent
5be2422c84
commit
f993c4a36a
18 changed files with 105 additions and 177 deletions
|
|
@ -144,145 +144,8 @@ function layer_cards(render3d_st: mut Render3dState, scan: Model, cap: int, wind
|
|||
return l
|
||||
}
|
||||
|
||||
# A lupine spike (1 m tall): a stem of two crossed quads (uv.x in [0,1]) and
|
||||
# seven tiers of crossed floret quads (uv.x in [1,2]), coloured in the shader.
|
||||
function model_lupine(render3d_st: mut Render3dState) -> Model {
|
||||
let model = new Model
|
||||
model.prims = new []Prim
|
||||
let pr = new Prim
|
||||
let m = gpu_mesh_new(render3d_st)
|
||||
# quads: stem x2 + tiers 12 x 2 + 3 leaves = 29 quads
|
||||
let nq = 29
|
||||
let v = gl_floats(nq * 4 * 8)
|
||||
let idx = words(nq * 6)
|
||||
var k = 0
|
||||
var qi = 0
|
||||
for q in 0 .. nq {
|
||||
var w = 0.012; var y0 = 0.0; var y1 = 0.62; var ukind = 0.0
|
||||
var ang = 0.0
|
||||
if q >= 2 and q < 26 {
|
||||
let tier = (q - 2) / 2
|
||||
let t = float(tier) / 12.0
|
||||
w = 0.05 * (1.1 - t)
|
||||
y0 = 0.27 + t * 0.36
|
||||
y1 = y0 + 0.045
|
||||
ukind = 1.0
|
||||
ang = float(tier) / 12.0 * 2.1
|
||||
if (q & 1) == 1 { ang = ang + PI * 0.5 }
|
||||
} else if q >= 26 {
|
||||
# a rosette of three leaves near the ground
|
||||
w = 0.09; y0 = 0.02; y1 = 0.2; ukind = 2.0
|
||||
ang = float(q - 26) / 3.0 * (2.0 * PI)
|
||||
} else {
|
||||
if (q & 1) == 1 { ang = ang + PI * 0.5 }
|
||||
}
|
||||
let cx = Math.cos(ang) * w; let cz = Math.sin(ang) * w
|
||||
for c in 0 .. 4 {
|
||||
var sx = -1.0; var sy = y0; var u = 0.0
|
||||
if c == 1 or c == 2 { sx = 1.0; u = 1.0 }
|
||||
if c == 2 or c == 3 { sy = y1 }
|
||||
gl_put_bits(v, k, float_bits(cx * sx)); gl_put_bits(v, k + 1, float_bits(sy)); gl_put_bits(v, k + 2, float_bits(cz * sx))
|
||||
gl_put_bits(v, k + 3, float_bits(-cz)); gl_put_bits(v, k + 4, float_bits(0.2)); gl_put_bits(v, k + 5, float_bits(cx))
|
||||
gl_put_bits(v, k + 6, float_bits(ukind + u))
|
||||
var vv = sy
|
||||
if ukind == 1.0 { vv = (sy - 0.27) / 0.4 }
|
||||
if ukind == 2.0 { vv = (sy - 0.02) / 0.18 }
|
||||
gl_put_bits(v, k + 7, float_bits(vv))
|
||||
k += 8
|
||||
}
|
||||
let b = q * 4
|
||||
idx[qi] = b; idx[qi + 1] = b + 1; idx[qi + 2] = b + 2; idx[qi + 3] = b; idx[qi + 4] = b + 2; idx[qi + 5] = b + 3
|
||||
qi += 6
|
||||
}
|
||||
gpu_mesh_vertices(render3d_st, m, v, gl_bytes_of(nq * 4 * 8), GPU_STATIC)
|
||||
sc_model_layout(render3d_st, m)
|
||||
free(v)
|
||||
gpu_mesh_indices(render3d_st, m, data_of(idx), nq * 6 * 4, 4)
|
||||
free(idx)
|
||||
m.count = nq * 6
|
||||
gpu_mesh_done(render3d_st, m)
|
||||
pr.mesh = m
|
||||
if render3d_st.gltf_white == 0 { render3d_st.gltf_white = tex_solid(render3d_st, 200, 200, 200, 255); render3d_st.gltf_flat = tex_solid(render3d_st, 128, 128, 255, 255) }
|
||||
pr.diff = render3d_st.gltf_white; pr.nrm = render3d_st.gltf_flat; pr.arm = render3d_st.gltf_white
|
||||
push(model.prims, pr)
|
||||
model.radius = 0.08; model.height = 0.65; model.tris = nq * 2
|
||||
return model
|
||||
}
|
||||
|
||||
# A dense lupine for baking into a card: a stem, ~220 small floret quads in a
|
||||
# tapering spiral (uv.x in [1,2]) and five leaves (uv.x in [2,3]).
|
||||
function model_lupine_dense(render3d_st: mut Render3dState) -> Model {
|
||||
let model = new Model
|
||||
model.prims = new []Prim
|
||||
let pr = new Prim
|
||||
let m = gpu_mesh_new(render3d_st)
|
||||
let nfl = 220
|
||||
let nq = 2 + nfl + 5
|
||||
let v = gl_floats(nq * 4 * 8)
|
||||
let idx = words(nq * 6)
|
||||
var k = 0
|
||||
var qi = 0
|
||||
seed(5)
|
||||
for q in 0 .. nq {
|
||||
var w = 0.008; var y0 = 0.0; var y1 = 0.66; var ukind = 0.0
|
||||
var ang = 0.0; var ox = 0.0; var oz = 0.0; var tilt = 0.0
|
||||
if q >= 2 and q < 2 + nfl {
|
||||
let t = float(q - 2) / float(nfl)
|
||||
let yy = 0.28 + t * 0.4
|
||||
ang = float(q) * 2.39996 # golden angle spiral
|
||||
let rad = 0.055 * (1.05 - t)
|
||||
ox = Math.cos(ang) * rad; oz = Math.sin(ang) * rad
|
||||
w = 0.028 * (1.1 - t * 0.5)
|
||||
y0 = yy - 0.016; y1 = yy + 0.016
|
||||
ukind = 1.0
|
||||
tilt = 0.6
|
||||
} else if q >= 2 + nfl {
|
||||
w = 0.05; y0 = 0.03; y1 = 0.16; ukind = 2.0
|
||||
ang = float(q - 2 - nfl) / 5.0 * (2.0 * PI)
|
||||
ox = Math.cos(ang) * 0.05; oz = Math.sin(ang) * 0.05
|
||||
} else {
|
||||
if (q & 1) == 1 { ang = PI * 0.5 }
|
||||
}
|
||||
# the quad faces outward (its normal along the spiral radius), leaning out by `tilt`
|
||||
let nx = Math.cos(ang); let nz = Math.sin(ang)
|
||||
let tx = -nz; let tz = nx # tangent (quad width direction)
|
||||
for c in 0 .. 4 {
|
||||
var sx = -1.0; var sy = y0; var u = 0.0
|
||||
if c == 1 or c == 2 { sx = 1.0; u = 1.0 }
|
||||
if c == 2 or c == 3 { sy = y1 }
|
||||
var lean = 0.0
|
||||
if c == 2 or c == 3 { lean = tilt * w }
|
||||
gl_put_bits(v, k, float_bits(ox + tx * (sx * w) + nx * lean))
|
||||
gl_put_bits(v, k + 1, float_bits(sy))
|
||||
gl_put_bits(v, k + 2, float_bits(oz + tz * (sx * w) + nz * lean))
|
||||
gl_put_bits(v, k + 3, float_bits(nx)); gl_put_bits(v, k + 4, float_bits(0.35)); gl_put_bits(v, k + 5, float_bits(nz))
|
||||
gl_put_bits(v, k + 6, float_bits(ukind + u))
|
||||
var vv = sy
|
||||
if ukind == 1.0 { vv = (sy - 0.27) / 0.42 }
|
||||
if ukind == 2.0 { vv = (sy - 0.03) / 0.19 }
|
||||
gl_put_bits(v, k + 7, float_bits(vv))
|
||||
k += 8
|
||||
}
|
||||
let b = q * 4
|
||||
idx[qi] = b; idx[qi + 1] = b + 1; idx[qi + 2] = b + 2; idx[qi + 3] = b; idx[qi + 4] = b + 2; idx[qi + 5] = b + 3
|
||||
qi += 6
|
||||
}
|
||||
gpu_mesh_vertices(render3d_st, m, v, gl_bytes_of(nq * 4 * 8), GPU_STATIC)
|
||||
sc_model_layout(render3d_st, m)
|
||||
free(v)
|
||||
gpu_mesh_indices(render3d_st, m, data_of(idx), nq * 6 * 4, 4)
|
||||
free(idx)
|
||||
m.count = nq * 6
|
||||
gpu_mesh_done(render3d_st, m)
|
||||
pr.mesh = m
|
||||
if render3d_st.gltf_white == 0 { render3d_st.gltf_white = tex_solid(render3d_st, 200, 200, 200, 255); render3d_st.gltf_flat = tex_solid(render3d_st, 128, 128, 255, 255) }
|
||||
pr.diff = render3d_st.gltf_white; pr.nrm = render3d_st.gltf_flat; pr.arm = render3d_st.gltf_white
|
||||
push(model.prims, pr)
|
||||
model.radius = 0.11; model.height = 0.68; model.tris = nq * 2
|
||||
return model
|
||||
}
|
||||
|
||||
# A procedural grass blade (1 m tall, 5 cm wide, curved): 5 rows of 2 vertices.
|
||||
@alloc_ok("a model: made once by the program that asks for it, and kept with its layer")
|
||||
function model_blade(render3d_st: mut Render3dState) -> Model {
|
||||
let model = new Model
|
||||
model.prims = new []Prim
|
||||
|
|
@ -553,6 +416,7 @@ function scatter_begin_frame(render3d_st: mut Render3dState) -> void {
|
|||
# wrote - one draw per material covering every level. Nothing is partitioned or uploaded on the CPU
|
||||
# when the view moves. PC camp benchmark: 2791 -> 2657 draws, 4.3 -> 4.1 s for 400 frames.
|
||||
const SC_REC_W: int = 20 # a VkDrawIndexedIndirectCommand
|
||||
const SC_LOD_ROOM: int = 64 # a layer's LODs plus near and far, most (sc_lod_* are made this size)
|
||||
const SC_RECS: int = 29 # 16 level x prim, 1 impostor, 12 shadow LOD (scatter_cull.comp)
|
||||
|
||||
function layer_gpu_eligible(render3d_st: Render3dState, l: Layer) -> bool {
|
||||
|
|
@ -661,7 +525,7 @@ function layer_gpu_prepare(render3d_st: mut Render3dState, l: Layer) -> bool {
|
|||
gpu_buffer_upload(render3d_st, l.g_dst, (n + 1) * cap * INST_FLOATS * 4, null, GPU_DYNAMIC)
|
||||
if l.g_arena == null { layer_arena_build(render3d_st, l) }
|
||||
let n_mat = len(l.g_arena)
|
||||
let rec = words(SC_RECS * 5)
|
||||
let rec = render3d_st.sc_gpu_rec
|
||||
for i in 0 .. SC_RECS * 5 { rec[i] = 0 }
|
||||
# material j, level k: that level's range of the merged mesh, its instances from bucket k
|
||||
for j in 0 .. n_mat {
|
||||
|
|
@ -681,10 +545,9 @@ function layer_gpu_prepare(render3d_st: mut Render3dState, l: Layer) -> bool {
|
|||
}
|
||||
}
|
||||
gpu_buffer_upload(render3d_st, l.g_cmds, SC_RECS * SC_REC_W, data_of(rec), GPU_DYNAMIC)
|
||||
let zeros = words(5)
|
||||
let zeros = render3d_st.sc_gpu_zeros
|
||||
for i in 0 .. 5 { zeros[i] = 0 }
|
||||
gpu_buffer_upload(render3d_st, l.g_counts, 20, data_of(zeros), GPU_DYNAMIC)
|
||||
free(rec); free(zeros)
|
||||
# the card casts every instance, as on the CPU path (layer_grid_build uploads this there)
|
||||
l.n_sh = l.count
|
||||
gpu_buffer_upload(render3d_st, l.sh_buf, l.count * INST_FLOATS * 4, data_of(l.inst), GPU_STATIC)
|
||||
|
|
@ -695,7 +558,7 @@ function layer_gpu_prepare(render3d_st: mut Render3dState, l: Layer) -> bool {
|
|||
|
||||
# the dispatch for the view as it stands: frustum, camera, distances, the layer's shape
|
||||
function layer_gpu_cull(render3d_st: mut Render3dState, l: Layer) -> void {
|
||||
let pr = words(36)
|
||||
let pr = render3d_st.sc_gpu_pr
|
||||
for i in 0 .. 36 { pr[i] = 0 }
|
||||
if render3d_st.cam_planes != null { for i in 0 .. 16 { pr[i] = float_bits(render3d_st.cam_planes[i]) } }
|
||||
pr[16] = float_bits(render3d_st.cam_pos[0]); pr[17] = float_bits(render3d_st.cam_pos[1]); pr[18] = float_bits(render3d_st.cam_pos[2]); pr[19] = float_bits(l.cull)
|
||||
|
|
@ -703,10 +566,9 @@ function layer_gpu_cull(render3d_st: mut Render3dState, l: Layer) -> void {
|
|||
pr[28] = l.count; pr[29] = l.count; pr[30] = l.n_lods; pr[31] = 1
|
||||
# as layer_grid_gather pads a cell: the tallest instance, plus a margin
|
||||
pr[32] = float_bits(l.lods[0].height * 2.0); pr[33] = float_bits(4.0)
|
||||
let bufs = words(4)
|
||||
let bufs = render3d_st.sc_gpu_bufs
|
||||
bufs[0] = l.g_src; bufs[1] = l.g_dst; bufs[2] = l.g_cmds; bufs[3] = l.g_counts
|
||||
gpu_dispatch(render3d_st, render3d_st.sc_cull_prog, data_of(pr), 144, bufs, 1)
|
||||
free(pr); free(bufs)
|
||||
}
|
||||
|
||||
# Sort a static layer's instances into square cells (call once, after placement; a
|
||||
|
|
@ -789,7 +651,8 @@ function layer_grid_gather(render3d_st: Render3dState, l: Layer) -> void {
|
|||
# the impostor bucket last, and upload one buffer per level.
|
||||
function layer_partition_lods(render3d_st: mut Render3dState, l: Layer, src: floats, total: int) -> void {
|
||||
let n = l.n_lods
|
||||
let counts = words(n + 2)
|
||||
if n + 2 > SC_LOD_ROOM { return } # past the room made with the state
|
||||
let counts = render3d_st.sc_lod_counts
|
||||
for k in 0 .. n + 2 { counts[k] = 0 }
|
||||
let cull2 = l.cull * l.cull
|
||||
let open = l.lod_dist[n - 1] == 0.0 # the last level runs out to the cull distance
|
||||
|
|
@ -810,10 +673,10 @@ function layer_partition_lods(render3d_st: mut Render3dState, l: Layer, src: flo
|
|||
counts[lv] += 1
|
||||
}
|
||||
# prefix offsets (in instances) per bucket
|
||||
let start = words(n + 2)
|
||||
let start = render3d_st.sc_lod_start
|
||||
var acc = 0
|
||||
for k in 0 .. n + 2 { start[k] = acc; acc += counts[k] }
|
||||
let fill = words(n + 2)
|
||||
let fill = render3d_st.sc_lod_fill
|
||||
for k in 0 .. n + 2 { fill[k] = start[k] }
|
||||
let tmp = l.scratch
|
||||
for i in 0 .. total {
|
||||
|
|
@ -841,7 +704,6 @@ function layer_partition_lods(render3d_st: mut Render3dState, l: Layer, src: flo
|
|||
l.n_sh = total
|
||||
if total > 0 { gpu_buffer_upload(render3d_st, l.sh_buf, total * INST_FLOATS * 4, data_of(src), GPU_DYNAMIC) }
|
||||
}
|
||||
free(counts); free(start); free(fill)
|
||||
}
|
||||
|
||||
# split the instances by distance to the camera (only when the view changed)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue