feat(render3d): the meadow's blades are culled on the GPU - under 1.5 ms of a MoltenVK frame for all the grass

- grass_cull.comp decides each candidate blade once a frame (place, ground, density, water,
  slope, frustum, colour field) and writes survivors into three bands by distance, one indirect
  draw each; grass_inst.vert only bends and places the vertices. OpenGL keeps grass.vert's
  per-vertex path; R3D_GRASS_GPU=0 compares
- compute programs take sampled textures after their buffers (gpu_compute_tex / gpu_dispatch_tex),
  and every dispatch now records a compute-to-draw memory barrier
- the blades as a sward: 4 m cells, bands with five, three and one-quad blades, spacing doubling
  every 18 m to 70 m, never narrower than a pixel; lit facing the sun and leaning to the sky,
  shadow looked up above the ground (it read the terrain as its caster), a colour ramp that
  leaves only the sheath dark, clumps, dry patches and a tussock shade worked out per blade
- scatter layers flagged grass are skipped while the blades draw (and under R3D_NOGRASS);
  R3D_BLADES, R3D_NOBLADES win over the game's setting; R3D_GRASS_S0/D0 for measuring

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-27 15:38:43 +03:00
parent f815f3e3cf
commit cc384efee0
34 changed files with 798 additions and 212 deletions

View file

@ -214,6 +214,7 @@ export state Render3dState {
gvk_cp_layout: []long = null
gvk_cp_dsl: []long = null
gvk_cp_nbuf: []int = null
gvk_cp_ntex: []int = null
gvk_pipe_keys: []string = null
gvk_pipe: []long = null
gvk_zero_vbuf: int = 0
@ -363,8 +364,22 @@ export state Render3dState {
grass_n: int = 0
grass_cmds: int = 0
grass_band_start: words = null # per band: its first record
grass_band_cells: words = null # ... and its 16 m cells per tile side
grass_band_cells: words = null # ... and its cells per tile side
grass_band_n: int = 0
gg_on: bool = false # the blades are culled on the GPU (grass_gpu.ludic)
gg_reset: int = 0
gg_cull: int = 0
gg_prog: int = 0
gg_tiles_buf: []int = null
gg_tv: floats = null
gg_out: int = 0
gg_cmds: int = 0
gg_mesh: []Mesh = null
gg_n: int = 0
gg_max: int = 0
grass_band_mesh: []Mesh = null # per band: the blade it draws (rows fall with distance)
grass_mesh3: Mesh = null # three rows: the middle distance
grass_mesh2: Mesh = null # two rows, one quad: far enough that the arch is under a pixel
grass_mesh_on: bool = false
grass_mesh_prog: int = 0
r3d_draw_cb: fn() = null
@ -590,6 +605,7 @@ export state Render3dState {
sc_skip_blade: bool = false
sc_skip_grass: bool = false
grass_force: bool = false # R3D_BLADES: the blades on whatever the game set
grass_env_off: bool = false # R3D_NOBLADES: the blades off whatever the game set
sc_skip_card: bool = false
cb_state: int = 12345
shadow_res: int = 2048