From 9f07fc8a624de9a69758237baa3f6f737f2e10d0 Mon Sep 17 00:00:00 2001 From: Orkuncakilkaya Date: Tue, 29 Sep 2026 22:30:35 +0300 Subject: [PATCH] render3d: heightgen's plain and SMOOTH variants compiled (61 programs) - a smoothed or noise-only terrain had no height field on Vulkan; run-time defines as pure functions (program_defs.ludic) and manifest_test holds every program the source can ask for, literal or computed, to the manifest Co-Authored-By: Claude Opus 5.5 --- changes/r3d-variants-all.md | 9 ++ packages/ludic.render3d/grass.ludic | 3 +- packages/ludic.render3d/program_defs.ludic | 25 ++++ packages/ludic.render3d/r3d.ludic | 1 + .../shaders/spv/5b8c3ae8.frag.spv | Bin 0 -> 15808 bytes .../shaders/spv/5b8c3ae8.vert.spv | Bin 0 -> 1664 bytes .../shaders/spv/c60a9a25.frag.spv | Bin 0 -> 8056 bytes .../shaders/spv/c60a9a25.vert.spv | Bin 0 -> 1664 bytes .../ludic.render3d/shaders/spv/manifest.txt | 96 +++++++++++++- packages/ludic.render3d/shaders/variants.list | 2 + packages/ludic.render3d/terrain.ludic | 12 +- .../ludic.render3d/tests/manifest_test.ludic | 117 ++++++++++++++++++ 12 files changed, 254 insertions(+), 11 deletions(-) create mode 100644 changes/r3d-variants-all.md create mode 100644 packages/ludic.render3d/program_defs.ludic create mode 100644 packages/ludic.render3d/shaders/spv/5b8c3ae8.frag.spv create mode 100644 packages/ludic.render3d/shaders/spv/5b8c3ae8.vert.spv create mode 100644 packages/ludic.render3d/shaders/spv/c60a9a25.frag.spv create mode 100644 packages/ludic.render3d/shaders/spv/c60a9a25.vert.spv diff --git a/changes/r3d-variants-all.md b/changes/r3d-variants-all.md new file mode 100644 index 00000000..b7bacfa0 --- /dev/null +++ b/changes/r3d-variants-all.md @@ -0,0 +1,9 @@ +bump: patch +type: fix +**Every program render3d can ask for is compiled.** heightgen with no survey and with SMOOTH were never in +variants.list, so a smoothed or noise-only terrain (examples/rendering/chunks, smooth, reload) had no +height field on Vulkan - silently until render3d's faults were made loud. Both are compiled now (61 +programs). A program's run-time defines are pure functions in program_defs.ludic (the height field, the +ground's tiers, the blades), and tests/manifest_test reads every r3d_program / gpu_program / ac_prog_new call +in render3d's source, holds each literal one and every value of each computed one to the shipped manifest, +and fails a new computed call site until it is given its function. diff --git a/packages/ludic.render3d/grass.ludic b/packages/ludic.render3d/grass.ludic index de093b42..ed734b2e 100644 --- a/packages/ludic.render3d/grass.ludic +++ b/packages/ludic.render3d/grass.ludic @@ -96,9 +96,8 @@ function grass_init(render3d_st: mut Render3dState) -> void { @alloc_ok("start-up: the device, its tables, the programs, the passes and the world's first textures are made once, before play") function grass_init__t(render3d_st: mut Render3dState) -> void { render3d_st.grass_merge = gpu_has_mdi(render3d_st) and not r3d_env_has(render3d_st, "R3D_GRASS_TILES") - var defs = "#define FOLIAGE\n#define BLADE\n#define GBLADE\n" + let defs = grass_defs(render3d_st.grass_merge) if render3d_st.grass_merge { - defs = defs + "#define TILES\n" render3d_st.grass_rec = words(GRASS_RECS * 5); render3d_st.grass_tv = floats(GRASS_RECS * 4); render3d_st.grass_chunk_tv = floats(GRASS_CHUNK * 4) render3d_st.grass_band_start = words(4); render3d_st.grass_band_cells = words(4) render3d_st.grass_band_mesh = new []Mesh diff --git a/packages/ludic.render3d/program_defs.ludic b/packages/ludic.render3d/program_defs.ludic new file mode 100644 index 00000000..e974cd8b --- /dev/null +++ b/packages/ludic.render3d/program_defs.ludic @@ -0,0 +1,25 @@ +# program_defs.ludic - the defines of every program whose defines are decided at run time, each a pure +# function of what decides it, so tests/manifest_test can ask for every one a run could and hold the +# SPIR-V manifest to them. A new program made with computed defines gets its function here, and a line there. + +# the height field's generator: from a survey (DEM), smoothed (SMOOTH wins), or noise alone +function ter_heightgen_defs(dem: bool, smooth: bool) -> string { + if smooth { return "#define SMOOTH\n" } + if dem { return "#define DEM\n" } + return "" +} +# the ground's program: `debug` the dev switches' defines ("" in every shipped run), the cascades read +# inside it on Vulkan (SUN_INLINE; R3D_SUN_PASS keeps a pass), tier 0 both, 1 far only, 2 near only +@alloc_ok("start-up: the device, its tables, the programs, the passes and the world's first textures are made once, before play") +function ter_prog_defs(debug: string, sun_inline: bool, tier: int) -> string { + var d = debug + if sun_inline { d = d + "#define SUN_INLINE\n" } + if tier == 1 { d = d + "#define FAR_ONLY\n" } + if tier == 2 { d = d + "#define NEAR_ONLY\n" } + return d +} +# the blades: merged into tiles where the device draws indirect (TILES), else a draw a chunk +function grass_defs(merge: bool) -> string { + if merge { return "#define FOLIAGE\n#define BLADE\n#define GBLADE\n#define TILES\n" } + return "#define FOLIAGE\n#define BLADE\n#define GBLADE\n" +} diff --git a/packages/ludic.render3d/r3d.ludic b/packages/ludic.render3d/r3d.ludic index b0aac9aa..db733d8f 100644 --- a/packages/ludic.render3d/r3d.ludic +++ b/packages/ludic.render3d/r3d.ludic @@ -21,6 +21,7 @@ import "prof.ludic" import "drawstats.ludic" import "r3d_root.ludic" import "programs.ludic" +import "program_defs.ludic" import "texture.ludic" import "baked_tex.ludic" import "mesh.ludic" diff --git a/packages/ludic.render3d/shaders/spv/5b8c3ae8.frag.spv b/packages/ludic.render3d/shaders/spv/5b8c3ae8.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..31bd877319e180d39fe23e2f07aa7e3d8f8b0111 GIT binary patch literal 15808 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 literal 0 HcmV?d00001 diff --git a/packages/ludic.render3d/shaders/spv/5b8c3ae8.vert.spv b/packages/ludic.render3d/shaders/spv/5b8c3ae8.vert.spv new file mode 100644 index 0000000000000000000000000000000000000000..45e56abeaeb7499d9674273404bb3e7106c3bf14 GIT binary patch literal 1664 zcmYk7+fEZv6o$7gQ&1E{P96}7c&HZZ2@phRi6(`Fiy9KINE$*XW|DCrbV`UB++nd`v+qG`m zsVyu%g)!uc84=E~yO^E(^!;%!N>GL*Q<93LE-C6$(Edvff$P)foEs=dNqXGtMEmUz zaho7=4CFH?{7?din6kbPkJ9jDE9rzMc`WiAw|mFhGT!~1*9wnvDmWRyS&Gp*=tgOD zkO=M1aQevbC`ossPa!g)J^x2{qW4jEnw@#@tKXf>6;ZOKK+ZcmE^v@ za_5@51$v^#M-p^;nU=7cibKuh-&7>4A3keSe8a9~dd|t;jTb%lBL;rdYq2WIh5F)DGc(SV0Omg%QI&2Lh=)p1K_YNkH&pmVQa=I`&gP9JxK6O((O?m6vc z2@5{@T?utgNx!EZKG?LMD~o{*q2CAE$;lg02YSV0R`)~cEUSyVckZ-~{}Y|?C$`n` zTxaX^g|}0S*_S+K@m@>+UD>Uhz9xP5TcJL$dE$Q7C2+v)O_&3ET@r^}+zX3Z{s;-Q zuL#BVOj43;=$m>LB=ksM)Vd}f^({)U8Skae#-nEAE$fW!v$Ei~BEc8@i^pDhoHw+1 zt2$de@~*2|Ft~r!&9cP$p%}c;ohCS99j2UU^wwTYF1TMC!mkq^WhWUqKZ$nOW I`1UUU0J&&u-v9sr literal 0 HcmV?d00001 diff --git a/packages/ludic.render3d/shaders/spv/c60a9a25.frag.spv b/packages/ludic.render3d/shaders/spv/c60a9a25.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..e2b3c7f7154cf3f148d611a787bb1ab58e95a304 GIT binary patch literal 8056 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 T>Fm+|&fFy(A9&sanD_8Mjd4%P literal 0 HcmV?d00001 diff --git a/packages/ludic.render3d/shaders/spv/c60a9a25.vert.spv b/packages/ludic.render3d/shaders/spv/c60a9a25.vert.spv new file mode 100644 index 0000000000000000000000000000000000000000..45e56abeaeb7499d9674273404bb3e7106c3bf14 GIT binary patch literal 1664 zcmYk7+fEZv6o$7gQ&1E{P96}7c&HZZ2@phRi6(`Fiy9KINE$*XW|DCrbV`UB++nd`v+qG`m zsVyu%g)!uc84=E~yO^E(^!;%!N>GL*Q<93LE-C6$(Edvff$P)foEs=dNqXGtMEmUz zaho7=4CFH?{7?din6kbPkJ9jDE9rzMc`WiAw|mFhGT!~1*9wnvDmWRyS&Gp*=tgOD zkO=M1aQevbC`ossPa!g)J^x2{qW4jEnw@#@tKXf>6;ZOKK+ZcmE^v@ za_5@51$v^#M-p^;nU=7cibKuh-&7>4A3keSe8a9~dd|t;jTb%lBL;rdYq2WIh5F)DGc(SV0Omg%QI&2Lh=)p1K_YNkH&pmVQa=I`&gP9JxK6O((O?m6vc z2@5{@T?utgNx!EZKG?LMD~o{*q2CAE$;lg02YSV0R`)~cEUSyVckZ-~{}Y|?C$`n` zTxaX^g|}0S*_S+K@m@>+UD>Uhz9xP5TcJL$dE$Q7C2+v)O_&3ET@r^}+zX3Z{s;-Q zuL#BVOj43;=$m>LB=ksM)Vd}f^({)U8Skae#-nEAE$fW!v$Ei~BEc8@i^pDhoHw+1 zt2$de@~*2|Ft~r!&9cP$p%}c;ohCS99j2UU^wwTYF1TMC!mkq^WhWUqKZ$nOW I`1UUU0J&&u-v9sr literal 0 HcmV?d00001 diff --git a/packages/ludic.render3d/shaders/spv/manifest.txt b/packages/ludic.render3d/shaders/spv/manifest.txt index ce1f66b0..99f2bbe8 100644 --- a/packages/ludic.render3d/shaders/spv/manifest.txt +++ b/packages/ludic.render3d/shaders/spv/manifest.txt @@ -199,6 +199,53 @@ T 5161e6ba u_src 7 T 5161e6ba u_tershadow 8 T 5161e6ba u_tershadow_aux 9 T 5161e6ba u_ts_height 10 +P 5b8c3ae8 fullscreen.vert heightgen.frag +B 5b8c3ae8 frag 1 836 +U 5b8c3ae8 frag u_cascade_vp 0 mat4 5 64 +U 5b8c3ae8 frag u_cascade_split 320 float 5 16 +U 5b8c3ae8 frag u_cascade_range 400 float 5 16 +U 5b8c3ae8 frag u_cascade_texel 480 float 5 16 +U 5b8c3ae8 frag u_sun_dir 560 vec3 1 0 +U 5b8c3ae8 frag u_sun_color 576 vec3 1 0 +U 5b8c3ae8 frag u_cam_pos 592 vec3 1 0 +U 5b8c3ae8 frag u_prefilter_levels 604 float 1 0 +U 5b8c3ae8 frag u_fog_density 608 float 1 0 +U 5b8c3ae8 frag u_fog_height_falloff 612 float 1 0 +U 5b8c3ae8 frag u_fog_base 616 float 1 0 +U 5b8c3ae8 frag u_clip_y 620 float 1 0 +U 5b8c3ae8 frag u_spec_scale 624 float 1 0 +U 5b8c3ae8 frag u_sky_rot 632 vec2 1 0 +U 5b8c3ae8 frag u_ground_alb 640 vec3 1 0 +U 5b8c3ae8 frag u_ground_alb_hi 656 vec3 1 0 +U 5b8c3ae8 frag u_ground_hi_y 668 float 1 0 +U 5b8c3ae8 frag u_ground_hi_w 672 float 1 0 +U 5b8c3ae8 frag u_ibl_scale 688 vec3 1 0 +U 5b8c3ae8 frag u_daylight 700 float 1 0 +U 5b8c3ae8 frag u_fire_pos 704 vec3 1 0 +U 5b8c3ae8 frag u_fire_color 720 vec3 1 0 +U 5b8c3ae8 frag u_hand_pos 736 vec3 1 0 +U 5b8c3ae8 frag u_hand_color 752 vec3 1 0 +U 5b8c3ae8 frag u_hand_dir 768 vec3 1 0 +U 5b8c3ae8 frag u_hand_cone 780 float 1 0 +U 5b8c3ae8 frag u_hand_reach 784 float 1 0 +U 5b8c3ae8 frag u_ts_origin 792 vec2 1 0 +U 5b8c3ae8 frag u_ts_half 800 float 1 0 +U 5b8c3ae8 frag u_ts_on 804 float 1 0 +U 5b8c3ae8 frag u_force_cascade 808 int 1 0 +U 5b8c3ae8 frag u_cloud_shadow 812 float 1 0 +U 5b8c3ae8 frag u_time 816 float 1 0 +U 5b8c3ae8 frag u_fog_inscatter 820 float 1 0 +U 5b8c3ae8 frag u_fog_desat 824 float 1 0 +U 5b8c3ae8 frag u_fog_wall 828 float 1 0 +U 5b8c3ae8 frag u_half 832 float 1 0 +T 5b8c3ae8 u_brdf 2 +T 5b8c3ae8 u_dem 3 +T 5b8c3ae8 u_irradiance 4 +T 5b8c3ae8 u_prefilter 5 +T 5b8c3ae8 u_shadow 6 +T 5b8c3ae8 u_tershadow 7 +T 5b8c3ae8 u_tershadow_aux 8 +T 5b8c3ae8 u_ts_height 9 P 4be7acd9 fullscreen.vert heightgen.frag #define DEM; B 4be7acd9 frag 1 920 U 4be7acd9 frag u_cascade_vp 0 mat4 5 64 @@ -257,6 +304,53 @@ T 4be7acd9 u_shadow 6 T 4be7acd9 u_tershadow 7 T 4be7acd9 u_tershadow_aux 8 T 4be7acd9 u_ts_height 9 +P c60a9a25 fullscreen.vert heightgen.frag #define SMOOTH; +B c60a9a25 frag 1 836 +U c60a9a25 frag u_cascade_vp 0 mat4 5 64 +U c60a9a25 frag u_cascade_split 320 float 5 16 +U c60a9a25 frag u_cascade_range 400 float 5 16 +U c60a9a25 frag u_cascade_texel 480 float 5 16 +U c60a9a25 frag u_sun_dir 560 vec3 1 0 +U c60a9a25 frag u_sun_color 576 vec3 1 0 +U c60a9a25 frag u_cam_pos 592 vec3 1 0 +U c60a9a25 frag u_prefilter_levels 604 float 1 0 +U c60a9a25 frag u_fog_density 608 float 1 0 +U c60a9a25 frag u_fog_height_falloff 612 float 1 0 +U c60a9a25 frag u_fog_base 616 float 1 0 +U c60a9a25 frag u_clip_y 620 float 1 0 +U c60a9a25 frag u_spec_scale 624 float 1 0 +U c60a9a25 frag u_sky_rot 632 vec2 1 0 +U c60a9a25 frag u_ground_alb 640 vec3 1 0 +U c60a9a25 frag u_ground_alb_hi 656 vec3 1 0 +U c60a9a25 frag u_ground_hi_y 668 float 1 0 +U c60a9a25 frag u_ground_hi_w 672 float 1 0 +U c60a9a25 frag u_ibl_scale 688 vec3 1 0 +U c60a9a25 frag u_daylight 700 float 1 0 +U c60a9a25 frag u_fire_pos 704 vec3 1 0 +U c60a9a25 frag u_fire_color 720 vec3 1 0 +U c60a9a25 frag u_hand_pos 736 vec3 1 0 +U c60a9a25 frag u_hand_color 752 vec3 1 0 +U c60a9a25 frag u_hand_dir 768 vec3 1 0 +U c60a9a25 frag u_hand_cone 780 float 1 0 +U c60a9a25 frag u_hand_reach 784 float 1 0 +U c60a9a25 frag u_ts_origin 792 vec2 1 0 +U c60a9a25 frag u_ts_half 800 float 1 0 +U c60a9a25 frag u_ts_on 804 float 1 0 +U c60a9a25 frag u_force_cascade 808 int 1 0 +U c60a9a25 frag u_cloud_shadow 812 float 1 0 +U c60a9a25 frag u_time 816 float 1 0 +U c60a9a25 frag u_fog_inscatter 820 float 1 0 +U c60a9a25 frag u_fog_desat 824 float 1 0 +U c60a9a25 frag u_fog_wall 828 float 1 0 +U c60a9a25 frag u_half 832 float 1 0 +T c60a9a25 u_brdf 2 +T c60a9a25 u_dem 3 +T c60a9a25 u_irradiance 4 +T c60a9a25 u_prefilter 5 +T c60a9a25 u_shadow 6 +T c60a9a25 u_tershadow 7 +T c60a9a25 u_tershadow_aux 8 +T c60a9a25 u_ts_height 9 P 858f5089 fullscreen.vert ibl_brdf.frag B 858f5089 frag 0 832 U 858f5089 frag u_cascade_vp 0 mat4 5 64 @@ -3995,4 +4089,4 @@ T b20fdcb5 u_shadow 9 T b20fdcb5 u_tershadow 10 T b20fdcb5 u_tershadow_aux 11 T b20fdcb5 u_ts_height 12 -E 59 +E 61 diff --git a/packages/ludic.render3d/shaders/variants.list b/packages/ludic.render3d/shaders/variants.list index 6a9a73e4..e7909b64 100644 --- a/packages/ludic.render3d/shaders/variants.list +++ b/packages/ludic.render3d/shaders/variants.list @@ -6,7 +6,9 @@ fullscreen.vert|adapt.frag| fullscreen.vert|bloom_down.frag| fullscreen.vert|bloom_up.frag| fullscreen.vert|dof.frag| +fullscreen.vert|heightgen.frag| fullscreen.vert|heightgen.frag|#define DEM; +fullscreen.vert|heightgen.frag|#define SMOOTH; fullscreen.vert|ibl_brdf.frag| fullscreen.vert|ibl_irradiance.frag| fullscreen.vert|ibl_prefilter.frag| diff --git a/packages/ludic.render3d/terrain.ludic b/packages/ludic.render3d/terrain.ludic index 2759406e..9483c73a 100644 --- a/packages/ludic.render3d/terrain.ludic +++ b/packages/ludic.render3d/terrain.ludic @@ -191,10 +191,7 @@ function terrain_generate(render3d_st: mut Render3dState) -> void { render3d_st.gvk_tag = was } function terrain_generate__t(render3d_st: mut Render3dState) -> void { - var defs = "" - if render3d_st.ter_dem_tex != 0 { defs = "#define DEM\n" } - if render3d_st.ter_smooth { defs = "#define SMOOTH\n" } - let p = r3d_program(render3d_st, "fullscreen.vert", "heightgen.frag", defs) + let p = r3d_program(render3d_st, "fullscreen.vert", "heightgen.frag", ter_heightgen_defs(render3d_st.ter_dem_tex != 0, render3d_st.ter_smooth)) # a map generated over another: its 256 MB height texture goes first if render3d_st.ter_height_tex != 0 { gpu_tex_free(render3d_st, render3d_st.ter_height_tex); render3d_st.ter_height_tex = 0 } render3d_st.ter_height_tex = tex_target(render3d_st, TERRAIN_RES, TERRAIN_RES, GL_R32F, GL_RED, GL_FLOAT, GL_LINEAR) @@ -453,10 +450,9 @@ function terrain_init_finish__t(render3d_st: mut Render3dState) -> void { # (terrain.frag, SUN_INLINE): one rasterisation of the patches instead of two, in the frame and in # the reflection. R3D_SUN_PASS=1 keeps the pass there too, for comparing. render3d_st.ter_sun_inline = render3d_st.gpu_kind == GPU_VK and not r3d_env_has(render3d_st, "R3D_SUN_PASS") - if render3d_st.ter_sun_inline { defs = defs + "#define SUN_INLINE\n" } - render3d_st.ter_prog = r3d_program(render3d_st, "terrain.vert", "terrain.frag", defs) - render3d_st.ter_prog_far = r3d_program(render3d_st, "terrain.vert", "terrain.frag", defs + "#define FAR_ONLY\n") - render3d_st.ter_prog_near = r3d_program(render3d_st, "terrain.vert", "terrain.frag", defs + "#define NEAR_ONLY\n") + render3d_st.ter_prog = r3d_program(render3d_st, "terrain.vert", "terrain.frag", ter_prog_defs(defs, render3d_st.ter_sun_inline, 0)) + render3d_st.ter_prog_far = r3d_program(render3d_st, "terrain.vert", "terrain.frag", ter_prog_defs(defs, render3d_st.ter_sun_inline, 1)) + render3d_st.ter_prog_near = r3d_program(render3d_st, "terrain.vert", "terrain.frag", ter_prog_defs(defs, render3d_st.ter_sun_inline, 2)) render3d_st.ter_sun_prog = r3d_program(render3d_st, "terrain.vert", "tersun.frag", "") render3d_st.ter_far_split = 200.0 if r3d_env_has(render3d_st, "R3D_TFAR") { render3d_st.ter_far_split = float(Text.to_int(r3d_env(render3d_st, "R3D_TFAR"))) } diff --git a/packages/ludic.render3d/tests/manifest_test.ludic b/packages/ludic.render3d/tests/manifest_test.ludic index 3b602d0e..12a92ce7 100644 --- a/packages/ludic.render3d/tests/manifest_test.ludic +++ b/packages/ludic.render3d/tests/manifest_test.ludic @@ -64,4 +64,121 @@ program ManifestTest { expect_eq(r3d_tilde("~other/x", "/Users/me"), "~other/x") expect_eq(r3d_tilde("~/x", null), "~/x") } + + # ---- every program render3d can ask for, not only what variants.list names ---- + # A request is r3d_program / gpu_program / ac_prog_new(state, vertex, fragment, defines). With all three + # literal it is read straight out of the source; with computed defines its site is one of KNOWN, whose + # defines are program_defs.ludic's functions, asked below for every value. A new computed site fails + # here by name until it is given a function there and a line in `computed_all`. + function shipped() -> []GpuVariant { return gpu_manifest_parse(Fs.read_text("packages/ludic.render3d/shaders/spv/manifest.txt")) } + function known_computed(file: string) -> int { + if file == "programs.ludic" { return 1 } # r3d_program itself, handing on to gpu_program + if file == "actor.ludic" { return 1 } # ac_prog_new's own call: its callers are literal + if file == "terrain.ludic" { return 4 } # heightgen and the ground's three tiers + if file == "grass.ludic" { return 1 } # the blades + return 0 + } + # the defines as the manifest keys them: the source's "\n" escapes as ';' + function key_defs(lit: string) -> string { + var out = "" + var i = 0 + while i < len(lit) { + if lit[i] == 92 and i + 1 < len(lit) and lit[i + 1] == 110 { + out = out + ";" + i += 2 + } else { + out = out + lit[i .. i + 1] + i += 1 + } + } + return out + } + function is_lit(a: string) -> bool { return len(a) >= 2 and a[0] == 34 and a[len(a) - 1] == 34 } + function unq(a: string) -> string { return a[1 .. len(a) - 1] } + # the top-level arguments of the call whose '(' is at `open`, trimmed + function args_at(src: string, open: int) -> []string { + let out = new []string + var depth = 0 + var inq = false + var start = open + 1 + var i = open + 1 + while i < len(src) { + let c = src[i] + if c == 34 { inq = not inq } + if not inq { + if c == 40 { depth += 1 } + if c == 41 and depth == 0 { + push(out, Text.trim(src[start .. i])) + return out + } + if c == 41 { depth -= 1 } + if c == 44 and depth == 0 { + push(out, Text.trim(src[start .. i])) + start = i + 1 + } + } + i += 1 + } + return out + } + # checks every request in one file against the manifest; returns how many had computed arguments + function file_requests(vs: []GpuVariant, file: string, missing: []string) -> int { + let src = Fs.read_text(`packages/ludic.render3d/{file}`) + var computed = 0 + let names = ["r3d_program(", "gpu_program(", "ac_prog_new("] + for n in 0 .. len(names) { + let parts = Text.split(src, names[n]) + var at = 0 + for k in 0 .. len(parts) - 1 { + at += len(parts[k]) + let before = parts[k] + let fn_decl = Text.ends_with(before, "function ") + let ident = len(before) > 0 and (char_word(before[len(before) - 1])) + if not fn_decl and not ident { + let a = args_at(src, at + len(names[n]) - 1) + if len(a) == 4 and is_lit(a[1]) and is_lit(a[2]) and is_lit(a[3]) { + if gpu_variant_in(vs, unq(a[1]), unq(a[2]), key_defs(unq(a[3]))) == null { push(missing, `{file}: {a[1]} {a[2]} {a[3]}`) } + } else { + computed += 1 + } + } + at += len(names[n]) + } + } + return computed + } + function char_word(c: int) -> bool { return (c >= 97 and c <= 122) or (c >= 65 and c <= 90) or (c >= 48 and c <= 57) or c == 95 or c == 46 } + # every value a computed site can take (dev switches' debug views left out: they are not shipped) + function computed_all(vs: []GpuVariant, missing: []string) -> void { + let flags = [false, true] + for a in 0 .. 2 { + for b in 0 .. 2 { need(vs, "fullscreen.vert", "heightgen.frag", ter_heightgen_defs(flags[a], flags[b]), missing) } + for t in 0 .. 3 { need(vs, "terrain.vert", "terrain.frag", ter_prog_defs("", flags[a], t), missing) } + need(vs, "grass.vert", "model.frag", grass_defs(flags[a]), missing) + } + } + function need(vs: []GpuVariant, v: string, f: string, defs: string, missing: []string) -> void { + let k = Text.replace(defs, "\n", ";") + if gpu_variant_in(vs, v, f, k) == null { push(missing, `{v}|{f}|{k}`) } + } + + test "every program render3d can ask for has a variant in the shipped manifest" { + let vs = shipped() + expect(vs != null) + let missing = new []string + let files = Fs.list("packages/ludic.render3d") + var asked_files = 0 + for i in 0 .. len(files) { + if not Text.ends_with(files[i], ".ludic") { continue } + let computed = file_requests(vs, files[i], missing) + if computed != known_computed(files[i]) { print(`{files[i]}: {computed} requests with computed arguments, {known_computed(files[i])} known - give the new one a function in program_defs.ludic`) } + expect_eq(computed, known_computed(files[i])) + asked_files += 1 + } + expect(asked_files > 20) + computed_all(vs, missing) + for i in 0 .. len(missing) { print(`no SPIR-V variant for {missing[i]}`) } + expect_eq(len(missing), 0) + } + }