From eb1cd3e01a628d93b67c0a778da24c37fe8e674d Mon Sep 17 00:00:00 2001 From: Orkuncakilkaya Date: Tue, 15 Sep 2026 17:14:53 +0300 Subject: [PATCH] render3d: HUD text, panels and images share a draw - the font stays bound and each vertex says what it reads A panel and its label alternated the white and font textures, and every switch closed the overlay's draw range: 77-91 ranges a frame in town. The font atlas is now bound beside the image texture for the whole flush, and v carries 4 x the vertex's mode (0 image, 1 font, 2 flat colour) on top of its real coordinate, so only a different image texture or a clip rectangle closes a range. Town: 24 ranges. OpenGL frames byte-identical at all five viewpoints; MoltenVK with validation adds no message. Co-Authored-By: Claude Opus 5 --- packages/ludic.render3d/overlay.ludic | 22 +++++++++++------- packages/ludic.render3d/shaders/overlay.frag | 16 +++++++++---- .../shaders/spv/1b654972.frag.spv | Bin 3880 -> 4440 bytes .../ludic.render3d/shaders/spv/manifest.txt | 16 ++++++------- 4 files changed, 33 insertions(+), 21 deletions(-) diff --git a/packages/ludic.render3d/overlay.ludic b/packages/ludic.render3d/overlay.ludic index 61ce30c7..a76ccf71 100644 --- a/packages/ludic.render3d/overlay.ludic +++ b/packages/ludic.render3d/overlay.ludic @@ -17,6 +17,8 @@ var ov_vbo: int = 0 var ov_buf: pointer = null var ov_n: int = 0 var ov_tex: int = 0 +var ov_mode: int = 2 # what the next quads read: 0 the image texture, 1 the font, 2 a flat colour +var ov_voff: int = 0 # float bits: 4 x ov_mode, added to v (overlay.frag) var ov_white: int = 0 var ov_font: int = 0 var ov_font_adv: words = null # float bits, em units, one per glyph in the atlas @@ -165,6 +167,7 @@ function ov_begin() -> void { u_f2(gpu_uniform(ov_prog, "u_screen"), fi(gl_w), fi(gl_h)) ov_n = 0; ov_nr = 0; ov_range_start = 0 ov_tex = ov_white + ov_mode = 2; ov_voff = fi(8) ov_clip_w = 0 ov_open = true } @@ -198,18 +201,15 @@ function ov_flush() -> void { gpu_use_program(ov_prog) gpu_mesh_bind(ov_mesh) gpu_buffer_upload(ov_vbo, gl_bytes_of(ov_n * 6 * OV_FLOATS), ov_buf, GPU_STREAM) + var font = ov_font + if font == 0 { font = ov_white } + r3d_bind_2d(ov_prog, "u_font", 1, font) var last = -1 var clipped = false for i in 0 .. ov_nr { let o = i * OV_RANGE_W let t = ov_ranges[o] - if t != last { - r3d_bind_2d(ov_prog, "u_tex", 0, t) - var is_font = F_ZERO - if t == ov_font { is_font = F_ONE } - u_f(gpu_uniform(ov_prog, "u_is_font"), is_font) - last = t - } + if t != last { r3d_bind_2d(ov_prog, "u_tex", 0, t); last = t } if ov_ranges[o + 5] > 0 { clipped = true gpu_scissor(ov_ranges[o + 3], ov_ranges[o + 4], ov_ranges[o + 5], ov_ranges[o + 6]) @@ -227,7 +227,13 @@ function ov_end() -> void { gpu_depth_test(true) ov_open = false } +# Text and flat panels never end a draw: the font atlas stays bound beside the image texture and each +# vertex says which it reads (ov_voff). Only a different image texture closes the range. A panel and +# its label used to alternate the white and font textures, and the HUD was 77-91 draws a frame. function ov_use_tex(t: int) -> void { + if t != 0 and t == ov_font { ov_mode = 1; ov_voff = fi(4); return } + if t == ov_white { ov_mode = 2; ov_voff = fi(8); return } + ov_mode = 0; ov_voff = F_ZERO if t != ov_tex { ov_close_range(); ov_tex = t } } @@ -235,7 +241,7 @@ function ov_use_tex(t: int) -> void { function ov_vert(k: int, x: int, y: int, u: int, v: int, r: int, g: int, b: int, a: int) -> void { let o = k * OV_FLOATS gl_put_bits(ov_buf, o, x); gl_put_bits(ov_buf, o + 1, y) - gl_put_bits(ov_buf, o + 2, u); gl_put_bits(ov_buf, o + 3, v) + gl_put_bits(ov_buf, o + 2, u); gl_put_bits(ov_buf, o + 3, f_add(v, ov_voff)) gl_put_bits(ov_buf, o + 4, r); gl_put_bits(ov_buf, o + 5, g); gl_put_bits(ov_buf, o + 6, b); gl_put_bits(ov_buf, o + 7, a) } # a textured quad, float-bit pixel corners and uvs diff --git a/packages/ludic.render3d/shaders/overlay.frag b/packages/ludic.render3d/shaders/overlay.frag index 6ea8067c..e77a927b 100644 --- a/packages/ludic.render3d/shaders/overlay.frag +++ b/packages/ludic.render3d/shaders/overlay.frag @@ -1,14 +1,20 @@ -// 2D overlay: a texture times a colour; the font atlas is white glyphs on alpha +// 2D overlay: an image texture, the font atlas (white glyphs on alpha) or a flat colour, times a +// colour. Which one is the vertex's own: v carries 4 x the mode (0 image, 1 font, 2 flat) on top of +// its real coordinate, so text, panels and an image's quads share a draw (overlay.ludic, ov_use_tex). in vec2 v_uv; in vec4 v_col; uniform sampler2D u_tex; -uniform float u_is_font; +uniform sampler2D u_font; out vec4 o_color; void main() { - vec4 t = texture(u_tex, v_uv); - if (u_is_font > 0.5) { - o_color = vec4(v_col.rgb, v_col.a * t.a); + float mode = floor((v_uv.y + 0.5) * 0.25); + vec2 uv = vec2(v_uv.x, v_uv.y - 4.0 * mode); + if (mode > 1.5) { + o_color = v_col; + } else if (mode > 0.5) { + o_color = vec4(v_col.rgb, v_col.a * texture(u_font, uv).a); } else { + vec4 t = texture(u_tex, uv); o_color = vec4(v_col.rgb * t.rgb, v_col.a * t.a); } } diff --git a/packages/ludic.render3d/shaders/spv/1b654972.frag.spv b/packages/ludic.render3d/shaders/spv/1b654972.frag.spv index 2008c3384d78935e3884a9e57f6d5391e5eb3951..31bb45b55ee36003c350d90888b9e93235ac4f71 100644 GIT binary patch literal 4440 zcmZveO>-4h5Qc|@gdj-3@F|GlLj{S53Wx$BAeuy@1QAg|CzCsK?;V-kx!#Wmuqs#= zj0DR;F*XGQVghl)Zl0dIGAG(y_Uh6v7f>> ztmcglcrcg|mg;P?&eln8I6NF&bvSLcqBxBjm2&@~Aas1Ql~mKLlN9?bIBu~OSBW(g z#16OXQJjA+Rm@1h8ERR0KB-3CtaG`MR`b@+4tVHcf6erZsi{jXm4r&Q{6tZXDVT zw^%!iSm}~7PJg+^7e`N!>u#uyDHp+Y3r=Tpr4OdnqttY_gaM|feLPWO`T;vVy+^`5 zH@sIm1596I=MEb7?esZzdI3Awx6|*~=?Uy$-@YJ>eL#X8?Av(@u+ux(!M?p#+NMf- zy~Nxv{L>Zq(+BXNt08;O$d`IDIO~jl8>T;r=X3jn8OE)W4mTymIO4+H_PD{4yXSG_ z4!U~4&pi|29V0B+vnI2+tGkE2#_3m(Ti0T+85?*d%ilIp2yMG zaKCvRJq`D}$I;Vpe|Q|d4ELwU(Zg`}#c}`W;XM+D;k^ZPn7+nN9kGLbJ3Wq_`e6r~ zeJmKz4|e_wD0*nCH15f`lhrORvEjC9J#o-kH!jQ_SS5i!Sz_)0+!kSKKQ3V$DlxUk z&U$K&PE72~)KcS&VQLlxwob- zmQHY-2h3Z`z&@>+JlJ`#&q^5Pf3<)C`(KmJ`E0+HGy8umJATCfL^A`9cwpXMhQ(iS zC~)y3=@y^Z4~xGj9zWt&G&A6c2j*>NY!}v_F!ycL$MRW~&bzit_L^pV;J|gw>~A<- zf$iPK&b_>@d0fIdck73@nwZ34E&kNhgxC+0wWSX>^Yv$Vd~7ZInob^;gy|*rCl6qH z9?tG>F8!C$*(t+K;cxHUnKb;F@E4oE?_vIzJa}z%azZa<86i?ldP8)e!R`pjWb}M)CtkyZ9m>y_Tz1^{djNT&1a|dm1o)|4&K)AzYnqP zJWopJeS!a~Jma`@)?u3$N4@t**l%3I`N$8P#oa5NxIfB&zvibTaQh^G$^RM6jDM6X zY)?xLOQ-`naSmvHPQp9H9=uoJzvaVvd|6|AJ}=$h^N27U`DV`-H8a=~+Y6F6B*a0t zJ&#If&kKsi{W>OrLm1SC`(t^2Svvl_hjx||(&53pai=AxN=*JHCFJai^22wYk>CS= zvCR9Fba?Kk)#t2qVoyoT@0@hb$T03T>2RFYxbr2)+2?he+4pq`>+#1wlz3F8A#ycx}KmgBdj`#F9`7!F}rj?MpqIAT~$=A8~MhWV2x zYIadVO#G})?@C`;r%U4C@u9!pldz9p%gZHiHF#f`b?}zkD^AxhHy8c>fzuzX!G|U1 e*We>@tj8Za@vR2f{2EXT|Nhel|0-K2B>w>yl!v|m literal 3880 zcmZvd%X1V}6o;FHgkY3_2`@p+!xs-@R6rC71EL8UO%M?uRJEO%o=g{=>8a_NNXm^H zcP`lC%D=#3?aDvEg?nAFbm78paXU=ojX0Hq zoqfdF>)BdZi<^1U*>rn%x-*c&ZiuZBCu@yPSdY>)tJj5l-+id(vl``bpN~3TO_Nqg z$lmuJc06yz)ljKSwWGhMc&Gg4X4uYnJG=8PwVNwBsTf zc30kA_kkD5lWwsdb+$&-to?V-c%T1o&qUwm9?4?u?el8ZoIy3_PxjGRoIUnqfcQ!dyF21-SZgr1N+5e)DY}f zk5Nak-#kVw!G8A`y$HMSF?td9w-_}^A5KYB0zG|NYX)|D)30%Q6W#pjP4*8;@JILe z)1%m_E9~gLoqolx<2XCIZ>N8;Q-kbB_wDpDcIpv3x^Jhiu~UcG(ak=tuMRjn|K*D_ z?9+-^lal@Nr>4<4|D^sB2ZkKfe}C#0`!bSr+yi*v!zb`GWZ@fN<7Ym zKlMp0IR5C=C+vW9>W#B-zPGm6Cy8T9>b3ST{ z^N}yUM?FnSIL{GP6Tee@gSf9 zwDCRkmo&2%-D-k9Vo+mmYsN-A#@3k{TN1~Qc<*KezmpI3^K{9mt3MRuSq4Bm+Qau(|81quGtnVsdulH)^-os@6~-{(}xzmyN>KP_P&{7T7Q z)|&4#jGfUM#yO0=QZml9qFZo}S0(JnA3O2MCH;f#Pu+r?aDVb(_kT@m|NiHs!w`nu z-~8vrxSQqcyw)&;VgB5cd{!jH#LsegLF=tKyy)zO4>^2YvNea7N^UuPLpuB5mYX-V ou1FZh7PPKN{2VTpY(TT+@UqtI#~(ZKEr;0r9Fog_6!DDYU)DrH-2eap diff --git a/packages/ludic.render3d/shaders/spv/manifest.txt b/packages/ludic.render3d/shaders/spv/manifest.txt index cc2c4f9f..3d564e5d 100644 --- a/packages/ludic.render3d/shaders/spv/manifest.txt +++ b/packages/ludic.render3d/shaders/spv/manifest.txt @@ -1771,7 +1771,7 @@ U 1b654972 vert u_screen 0 vec2 1 0 I 1b654972 a_pos 0 vec2 I 1b654972 a_uv 1 vec2 I 1b654972 a_col 2 vec4 -B 1b654972 frag 1 768 +B 1b654972 frag 0 764 U 1b654972 frag u_cascade_vp 0 mat4 5 64 U 1b654972 frag u_cascade_split 320 float 5 16 U 1b654972 frag u_cascade_range 400 float 5 16 @@ -1800,14 +1800,14 @@ U 1b654972 frag u_ts_on 748 float 1 0 U 1b654972 frag u_force_cascade 752 int 1 0 U 1b654972 frag u_cloud_shadow 756 float 1 0 U 1b654972 frag u_time 760 float 1 0 -U 1b654972 frag u_is_font 764 float 1 0 T 1b654972 u_brdf 2 -T 1b654972 u_irradiance 3 -T 1b654972 u_prefilter 4 -T 1b654972 u_shadow 5 -T 1b654972 u_tershadow 6 -T 1b654972 u_tex 7 -T 1b654972 u_ts_height 8 +T 1b654972 u_font 3 +T 1b654972 u_irradiance 4 +T 1b654972 u_prefilter 5 +T 1b654972 u_shadow 6 +T 1b654972 u_tershadow 7 +T 1b654972 u_tex 8 +T 1b654972 u_ts_height 9 P 1fde9cd2 skin.vert model.frag B 1fde9cd2 vert 0 3344 U 1fde9cd2 vert u_model 0 mat4 1 0