render3d(vulkan): GPU-driven trees - one merged mesh per material, one draw per material; on by default
Every level of a kit tree or rock carries the same materials, so a GPU-culled layer merges each material's levels into one mesh (layer_arena_build) and scatter_cull.comp writes a record per (material, level) naming that level's index and vertex range; one indirect draw covers them all. A conifer's lit pass and prepass go from 8 draws to 2, its shadow LOD from 6 to 2. Layers that do not fit (card levels, other materials or attributes, 32-bit indices) keep the CPU path. GPU culling is now the Vulkan default (R3D_GPU_CULL=0 turns it off). Camp benchmark, 400 frames: PC 2791 -> 2657 draws, 4.3 -> 4.1 s (both runs); Mac 2791 -> 2657, 8.0/7.4 -> 7.7/7.2 s. Self-tests 59/59 on both machines, PC validation 0 errors; frames within run-to-run noise; OpenGL frames unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
bda53f759e
commit
d8300a7bf9
4 changed files with 130 additions and 26 deletions
|
|
@ -27,10 +27,12 @@ layout(set = 0, binding = 0) uniform Params {
|
|||
layout(set = 0, binding = 1) readonly buffer Src { float src[]; };
|
||||
layout(set = 0, binding = 2) buffer Dst { float dst[]; };
|
||||
// VkDrawIndexedIndirectCommand records, 5 uints each; the CPU fills indexCount / firstIndex /
|
||||
// vertexOffset / firstInstance once, this writes instanceCount:
|
||||
// 0 .. 15 level * 4 + prim the lit pass and the depth prepass
|
||||
// 16 the impostor card bucket n_lods
|
||||
// 17 .. 28 17 + bucket * 4 + prim level 2's mesh cast from buckets 0 .. 2 (the shadow LOD)
|
||||
// vertexOffset / firstInstance once, this writes instanceCount. A prim is a material of the layer's
|
||||
// merged meshes (scatter.ludic, layer_arena_build), and one material's records sit together so a
|
||||
// single draw covers every level of it:
|
||||
// 0 .. 15 prim * 4 + level the lit pass and the depth prepass
|
||||
// 16 the impostor card bucket n_lods
|
||||
// 17 .. 28 17 + prim * 3 + bucket level 2's range cast from buckets 0 .. 2 (the shadow LOD)
|
||||
layout(set = 0, binding = 3) buffer Cmds { uint cmds[]; };
|
||||
// how many instances landed in each bucket (levels, then the impostor), for the CPU to read
|
||||
layout(set = 0, binding = 4) buffer Counts { uint counts[5]; };
|
||||
|
|
@ -67,14 +69,14 @@ void main() {
|
|||
}
|
||||
for (uint k = 0u; k < 4u; k++) {
|
||||
for (uint j = 0u; j < 4u; j++) {
|
||||
uint c = (k * 4u + j) * 5u;
|
||||
uint c = (j * 4u + k) * 5u;
|
||||
cmds[c + 1u] = (k < n && j < pr.prims[k]) ? cnt[k] : 0u;
|
||||
}
|
||||
}
|
||||
cmds[16u * 5u + 1u] = (pr.has_imp != 0u) ? cnt[n] : 0u;
|
||||
for (uint b = 0u; b < 3u; b++) {
|
||||
for (uint j = 0u; j < 4u; j++) {
|
||||
uint c = (17u + b * 4u + j) * 5u;
|
||||
uint c = (17u + j * 3u + b) * 5u;
|
||||
cmds[c + 1u] = (n > 2u && b < n && j < pr.prims[2]) ? cnt[b] : 0u;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue