R3D_VK_PROF at the camp view (about 2950 draws a frame) showed the frame spent in bookkeeping: 19.5 ms a frame finding pipelines by string key and 16 ms building descriptor sets. - Pipelines: a mesh carries an interned vertex-layout id (dropped only when an attribute's shape changes, not when an instance buffer is swapped), render state packs into an int and the pass formats into another; the program's last hit is tried first. The string path only builds. - Samplers: each texture keeps the sampler for its parameters until they change. - Descriptor sets: a program's last set is reused within the frame while its blocks and resolved textures are unchanged; a uniform write that repeats the value it already holds changes nothing. Headless on the RTX 3070 Ti at 1920x1080: 21.7 -> 53.3 fps (pipelines 0.2 ms, sets 8.5 ms, inside draws 10 ms a frame; OpenGL 114 fps). The camp frame is unchanged and validation-clean. OpenGL frames byte-identical at the five viewpoints; 59 self-tests pass; VKRES OK. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
92 lines
3.5 KiB
Text
92 lines
3.5 KiB
Text
# ============================================================================
|
|
# mesh.ludic — vertex data on the GPU: a Mesh record (buffers + a draw call), and
|
|
# the procedural meshes the renderer needs (a grid for the terrain, a full-screen
|
|
# triangle, a unit quad for instanced cards). Built and drawn through gpu.ludic.
|
|
# ============================================================================
|
|
|
|
property Mesh {
|
|
vao: int = 0, # OpenGL's vertex array object (gpu.ludic's, on that backend)
|
|
vbo: int = 0, # the first vertex buffer
|
|
ebo: int = 0,
|
|
count: int = 0, # indices (ebo != 0) or vertices
|
|
mode: int = 4, # GL_TRIANGLES
|
|
itype: int = 0x1405, # GL_UNSIGNED_INT
|
|
attrs: words, # the layout, GPU_ATTR_W words per attribute index (gpu.ludic)
|
|
n_attrs: int = 0,
|
|
vbufs: words, # every vertex buffer the mesh owns, freed with it
|
|
n_vbufs: int = 0,
|
|
cur_buf: int = 0, # the buffer the next attribute reads (while building)
|
|
ibuf: int = 0, # the instance buffer attached last
|
|
vk_layout: int = 0 # the Vulkan backend's interned id for the recorded layout (0: not yet)
|
|
}
|
|
|
|
function mesh_draw(m: Mesh) -> void { gpu_draw_mesh(m) }
|
|
function mesh_draw_instanced(m: Mesh, n: int) -> void { gpu_draw_mesh_instanced(m, n) }
|
|
|
|
# A flat n x n vertex grid over [-half, half]^2 in x/z, y = 0. Attribute 0 = (x, z).
|
|
# The terrain vertex shader lifts it with the height map.
|
|
function mesh_grid(n: int, half: int) -> Mesh {
|
|
let m = gpu_mesh_new()
|
|
let nv = n * n
|
|
let v = gl_floats(nv * 2)
|
|
var k = 0
|
|
for j in 0 .. n {
|
|
for i in 0 .. n {
|
|
let x = f_sub(f_mul(f_mul(fr(i, n - 1), F_TWO), half), half)
|
|
let z = f_sub(f_mul(f_mul(fr(j, n - 1), F_TWO), half), half)
|
|
gl_put_bits(v, k, x); gl_put_bits(v, k + 1, z)
|
|
k += 2
|
|
}
|
|
}
|
|
gpu_mesh_vertices(m, v, gl_bytes_of(nv * 2), GPU_STATIC)
|
|
gpu_mesh_attr(m, 0, 2, GPU_F32, 8, 0, false)
|
|
free(v)
|
|
let ni = (n - 1) * (n - 1) * 6
|
|
let idx = words(ni)
|
|
k = 0
|
|
for j in 0 .. n - 1 {
|
|
for i in 0 .. n - 1 {
|
|
let a = j * n + i
|
|
idx[k] = a; idx[k + 1] = a + n; idx[k + 2] = a + 1
|
|
idx[k + 3] = a + 1; idx[k + 4] = a + n; idx[k + 5] = a + n + 1
|
|
k += 6
|
|
}
|
|
}
|
|
gpu_mesh_indices(m, idx, ni * 4, 4)
|
|
free(idx)
|
|
m.count = ni
|
|
gpu_mesh_done(m)
|
|
return m
|
|
}
|
|
|
|
# A full-screen triangle with no attributes (the vertex shader uses gl_VertexID).
|
|
function mesh_fullscreen() -> Mesh {
|
|
let m = gpu_mesh_new()
|
|
gpu_mesh_done(m)
|
|
m.count = 3
|
|
return m
|
|
}
|
|
|
|
# A unit quad in x/y ([-0.5, 0.5] x [0, 1]) with uv, attribute 0 = xy, 1 = uv.
|
|
function mesh_card() -> Mesh {
|
|
let m = gpu_mesh_new()
|
|
let v = gl_floats(16)
|
|
gl_put(v, 0, -0.5); gl_put(v, 1, 0.0); gl_put(v, 2, 0.0); gl_put(v, 3, 0.0)
|
|
gl_put(v, 4, 0.5); gl_put(v, 5, 0.0); gl_put(v, 6, 1.0); gl_put(v, 7, 0.0)
|
|
gl_put(v, 8, 0.5); gl_put(v, 9, 1.0); gl_put(v, 10, 1.0); gl_put(v, 11, 1.0)
|
|
gl_put(v, 12, -0.5); gl_put(v, 13, 1.0); gl_put(v, 14, 0.0); gl_put(v, 15, 1.0)
|
|
gpu_mesh_vertices(m, v, 64, GPU_STATIC)
|
|
gpu_mesh_attr(m, 0, 2, GPU_F32, 16, 0, false)
|
|
gpu_mesh_attr(m, 1, 2, GPU_F32, 16, 8, false)
|
|
free(v)
|
|
let idx = words(6)
|
|
idx[0] = 0; idx[1] = 1; idx[2] = 2; idx[3] = 0; idx[4] = 2; idx[5] = 3
|
|
gpu_mesh_indices(m, idx, 24, 4)
|
|
free(idx)
|
|
m.count = 6
|
|
gpu_mesh_done(m)
|
|
return m
|
|
}
|
|
|
|
# release a mesh's GPU objects (a mesh this package built for something being thrown away)
|
|
function mesh_free(m: Mesh) -> void { gpu_mesh_free(m) }
|