From 958a262ba1c2b7b91ec70a2740529b701f8ebb6a Mon Sep 17 00:00:00 2001 From: Orkuncakilkaya Date: Mon, 28 Sep 2026 16:36:44 +0300 Subject: [PATCH] render3d: to its floor - messages in declared helpers, post and DLSS set up at start, lists sized Seventeen inline messages (allocation failures, missing conversions, the no-pipeline and compressed- target warnings, resize and swapchain lines, the test-frame camera line, DLSS evaluate, shadow memory, stream and terrain debug, the water test) are each a function of their own under @alloc_ok, taking numbers, so the paths that say them hold no site. post_measure's two 1x1 exposure targets are made in post_init and DLSS's evaluate structs in gsl_init; DLSS's camera up is a state field. The lists play pushes into (retired, free ranges, spare ids, the per-buffer gpu flags, the stage and the actor spares) get their room at start-up (gvk_room_*, actor_room), so a push fits. Compiled (steady). Co-Authored-By: Claude Opus 5.5 --- packages/ludic.render3d/actor.ludic | 13 ++++++- packages/ludic.render3d/env.ludic | 1 + packages/ludic.render3d/gpu_vk.ludic | 42 +++++++++++++++++++++-- packages/ludic.render3d/gpu_vk_draw.ludic | 14 ++++++-- packages/ludic.render3d/gpu_vk_res.ludic | 14 ++++++-- packages/ludic.render3d/post.ludic | 11 +++--- packages/ludic.render3d/render.ludic | 10 ++++-- packages/ludic.render3d/shadow.ludic | 6 +++- packages/ludic.render3d/stream.ludic | 6 +++- packages/ludic.render3d/streamline.ludic | 13 ++++--- packages/ludic.render3d/terrain.ludic | 10 ++++-- packages/ludic.render3d/water.ludic | 6 +++- 12 files changed, 120 insertions(+), 26 deletions(-) diff --git a/packages/ludic.render3d/actor.ludic b/packages/ludic.render3d/actor.ludic index 18046019..59d63b10 100644 --- a/packages/ludic.render3d/actor.ludic +++ b/packages/ludic.render3d/actor.ludic @@ -82,6 +82,9 @@ function actor_init(render3d_st: mut Render3dState) -> void { # record, matrix and tint then (actor_release gives them back to this pool) if render3d_st.ac_spare == null { render3d_st.ac_spare = new []Actor } let sp = render3d_st.ac_spare + if render3d_st.ac_actors == null { render3d_st.ac_actors = new []Actor } + actor_room(render3d_st.ac_actors, 4 * ACTOR_POOL) + actor_room(sp, 4 * ACTOR_POOL) for i in 0 .. ACTOR_POOL { let a = new Actor a.tint = v3_new(1.0, 1.0, 1.0) @@ -94,7 +97,8 @@ function actor_init(render3d_st: mut Render3dState) -> void { render3d_st.ac_sh_cut = ac_prog_new(render3d_st, "skin.vert", "shadow.frag", "#define SHADOW_PASS\n#define ALPHA_TEST\n") render3d_st.ac_out = ac_prog_new(render3d_st, "skin.vert", "outline.frag", "#define OUTLINE\n") render3d_st.ac_out_cut = ac_prog_new(render3d_st, "skin.vert", "outline.frag", "#define OUTLINE\n#define ALPHA_TEST\n") - render3d_st.ac_actors = new []Actor + let stage = render3d_st.ac_actors # made above with its room; a new valley starts empty + List.clear(stage) } # off the stage: the last actor takes its row, so the list stays dense and nothing is rebuilt # (it was a new list of every other actor per removal, never freed) @@ -475,3 +479,10 @@ function actor_own_skin(a: Actor) -> Skin { a.skin_owned = true return a.skin } + +# room for n more actors in a list, made at start-up: filled and emptied, it keeps what it grew to +function actor_room(l: []Actor, n: int) -> void { + let have = len(l) + for i in 0 .. n { push(l, null) } + while len(l) > have { List.pop(l) } +} diff --git a/packages/ludic.render3d/env.ludic b/packages/ludic.render3d/env.ludic index c0514cc2..c340db2e 100644 --- a/packages/ludic.render3d/env.ludic +++ b/packages/ludic.render3d/env.ludic @@ -692,6 +692,7 @@ export state Render3dState { stream_debug_n: int = 0 stream_scratch: floats = null gvk_pipe_bufs: words = null # a pipeline's vertex buffers, while it is made + gsl_up: floats = floats(3) # the camera's up for DLSS's constants ov_nine_buf: floats = floats(16) # ov_nine's corners and uvs gvk_ac: pointer = null # the VkAllocationCallbacks every create and destroy is given (R3D_ALLOC_VK=1) gvk_prime_b: words = null # buffers made since the frame began, read once so MoltenVK makes their diff --git a/packages/ludic.render3d/gpu_vk.ludic b/packages/ludic.render3d/gpu_vk.ludic index 529a1ce8..d7b827e3 100644 --- a/packages/ludic.render3d/gpu_vk.ludic +++ b/packages/ludic.render3d/gpu_vk.ludic @@ -311,7 +311,7 @@ function gvk_alloc(render3d_st: mut Render3dState, req: bytes, want: int) -> lon # device-local is a preference; host-visible is a need if t < 0 and want == VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT { t = gvk_mem_type(render3d_st, allowed, 0) } let zero: long = 0 - if t < 0 { print(`r3d: vulkan: no memory type for properties {want}`); return zero } + if t < 0 { gvk_say_no_type(want); return zero } let mai = gvk_tmp(render3d_st, VkMemoryAllocateInfo_sizeof) Vk.zero(mai, VkMemoryAllocateInfo_sizeof) Vk.put_i32(mai, VkMemoryAllocateInfo_sType, VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO) @@ -320,7 +320,7 @@ function gvk_alloc(render3d_st: mut Render3dState, req: bytes, want: int) -> lon let out = gvk_tmp(render3d_st, 8) render3d_st.gvk_mk_mem += 1 let r = Vk.allocate_memory(render3d_st.gvk_dev, mai, render3d_st.gvk_ac, out) - if r != VK_SUCCESS { print(`r3d: vulkan: vkAllocateMemory failed (VkResult {r})`); return zero } + if r != VK_SUCCESS { gvk_say_alloc_failed(r); return zero } render3d_st.gvk_n_allocs += 1 return gvk_handle(out) } @@ -548,7 +548,7 @@ const GVK_TMP_BYTES: int = 1048576 function gvk_tmp(render3d_st: mut Render3dState, n: int) -> pointer { let sz = (n + 15) / 16 * 16 - if sz > GVK_TMP_BYTES { print(`r3d: vulkan: {n} bytes of scratch asked for at once`); return null } + if sz > GVK_TMP_BYTES { gvk_say_tmp_big(n); return null } if render3d_st.gvk_tmp_off + sz > GVK_TMP_BYTES { render3d_st.gvk_tmp_off = 0 } let p = mem_off(render3d_st.gvk_tmp_buf, render3d_st.gvk_tmp_off) render3d_st.gvk_tmp_off += sz @@ -746,6 +746,18 @@ function gvk_startup_state(render3d_st: mut Render3dState) -> void { if render3d_st.gvk_buf_spare == null { render3d_st.gvk_buf_spare = new []int } if render3d_st.gvk_retired_frame == null { render3d_st.gvk_retired_frame = new []int } if render3d_st.gvk_buf_gpu == null { render3d_st.gvk_buf_gpu = new []int } + if render3d_st.gvk_retired_buf == null { render3d_st.gvk_retired_buf = new []long; render3d_st.gvk_retired_mem = new []long } + # room made now in every list play pushes into, so a push never grows one (plan 25.2) + gvk_room_int(render3d_st.gvk_retired_frame, GVK_LIST_ROOM) + gvk_room_long(render3d_st.gvk_retired_buf, GVK_LIST_ROOM) + gvk_room_long(render3d_st.gvk_retired_mem, GVK_LIST_ROOM) + gvk_room_int(render3d_st.gvk_fr_blk, GVK_LIST_ROOM) + gvk_room_int(render3d_st.gvk_fr_off, GVK_LIST_ROOM) + gvk_room_int(render3d_st.gvk_fr_len, GVK_LIST_ROOM) + gvk_room_int(render3d_st.gvk_al_spare, GVK_LIST_ROOM) + gvk_room_int(render3d_st.gvk_tex_spare, GVK_LIST_ROOM) + gvk_room_int(render3d_st.gvk_buf_spare, GVK_LIST_ROOM) + gvk_room_int(render3d_st.gvk_buf_gpu, 4 * GVK_LIST_ROOM) render3d_st.gvk_sc_tmp = new []long let sct = render3d_st.gvk_sc_tmp for i in 0 .. 130 { push(sct, zero) } @@ -768,3 +780,27 @@ function gvk_startup_state(render3d_st: mut Render3dState) -> void { for i in 0 .. 32 { render3d_st.gpu_unit_2d[i] = -1 } } } + +# messages, each built in a function of its own so the path that says it holds no allocation +@alloc_ok("a message, built only when it is said: a failure, a warning or a debug switch") +function gvk_say_no_type(want: int) -> void { print(`r3d: vulkan: no memory type for properties {want}`) } +@alloc_ok("a message, built only when it is said: a failure, a warning or a debug switch") +function gvk_say_alloc_failed(r: int) -> void { print(`r3d: vulkan: vkAllocateMemory failed (VkResult {r})`) } +@alloc_ok("a message, built only when it is said: a failure, a warning or a debug switch") +function gvk_say_tmp_big(n: int) -> void { print(`r3d: vulkan: {n} bytes of scratch asked for at once`) } + +const GVK_LIST_ROOM: int = 4096 +# a list's room for n more, made now: filled and emptied again, it keeps what it grew to +function gvk_room_int(l: []int, n: int) -> void { + if l == null { return } + let have = len(l) + for i in 0 .. n { push(l, 0) } + while len(l) > have { List.pop(l) } +} +function gvk_room_long(l: []long, n: int) -> void { + if l == null { return } + let have = len(l) + let zero: long = 0 + for i in 0 .. n { push(l, zero) } + while len(l) > have { List.pop(l) } +} diff --git a/packages/ludic.render3d/gpu_vk_draw.ludic b/packages/ludic.render3d/gpu_vk_draw.ludic index 8bc1f850..9508c285 100644 --- a/packages/ludic.render3d/gpu_vk_draw.ludic +++ b/packages/ludic.render3d/gpu_vk_draw.ludic @@ -1113,7 +1113,7 @@ function gvk_pass_no_compressed(render3d_st: mut Render3dState) -> void { if tex > 0 and tex < len(render3d_st.gvk_tex_glfmt) and gvk_is_compressed(render3d_st.gvk_tex_glfmt[tex]) { if render3d_st.gvk_bc_said < 8 { render3d_st.gvk_bc_said += 1 - print(`r3d: vulkan: framebuffer {render3d_st.gvk_fb_cur} would draw into compressed texture {tex} ({gvk_tex_w(render3d_st, tex)}x{gvk_tex_h(render3d_st, tex)}); left out`) + gvk_say_compressed(render3d_st, tex) } } else { render3d_st.gvk_pass_col[w * 2] = tex; render3d_st.gvk_pass_col[w * 2 + 1] = render3d_st.gvk_pass_col[c * 2 + 1] @@ -1269,7 +1269,7 @@ function gvk_draw(render3d_st: mut Render3dState, p: int, m: Mesh, st: GvkState, # rejected every skinned one) otherwise leaves a whole layer missing from the frame in silence. if p < 4096 and render3d_st.gvk_skip_said[p] == 0 { render3d_st.gvk_skip_said[p] = 1 - print(`r3d: vulkan: no pipeline for {gpu_program_key(render3d_st, p)}; its draws are skipped`) + gvk_say_no_pipeline(render3d_st, p) } return } @@ -1835,7 +1835,7 @@ function gvk_resize_check(render3d_st: mut Render3dState) -> bool { if w <= 0 or h <= 0 { return false } if w == gl_width() and h == gl_height() and not render3d_st.gvk_swap_stale { return false } # a remake is rare (a resize); said, so one on every frame shows in the log - print(`r3d: vulkan: swapchain remade {w}x{h} (was {gl_width()}x{gl_height()}, stale {render3d_st.gvk_swap_stale})`) + gvk_say_swap_remade(render3d_st, w, h) gvk_flush(render3d_st) gl_set_drawable(w, h) gvk_screen_make(render3d_st, w, h) @@ -1986,3 +1986,11 @@ function gvk_pipe_build(render3d_st: mut Render3dState, p: int, m: Mesh, st: Gvk if p < 4096 { render3d_st.gvk_pc_last[p] = len(render3d_st.gvk_pc_prog) - 1 } return pipe } + +# messages, each built in a function of its own so the path that says it holds no allocation +@alloc_ok("a message, built only when it is said: a failure, a warning or a debug switch") +function gvk_say_compressed(render3d_st: Render3dState, tex: int) -> void { print(`r3d: vulkan: framebuffer {render3d_st.gvk_fb_cur} would draw into compressed texture {tex} ({gvk_tex_w(render3d_st, tex)}x{gvk_tex_h(render3d_st, tex)}); left out`) } +@alloc_ok("a message, built only when it is said: a failure, a warning or a debug switch") +function gvk_say_no_pipeline(render3d_st: Render3dState, p: int) -> void { print(`r3d: vulkan: no pipeline for {gpu_program_key(render3d_st, p)}; its draws are skipped`) } +@alloc_ok("a message, built only when it is said: a failure, a warning or a debug switch") +function gvk_say_swap_remade(render3d_st: Render3dState, w: int, h: int) -> void { print(`r3d: vulkan: swapchain remade {w}x{h} (was {gl_width()}x{gl_height()}, stale {render3d_st.gvk_swap_stale})`) } diff --git a/packages/ludic.render3d/gpu_vk_res.ludic b/packages/ludic.render3d/gpu_vk_res.ludic index 42a8a69b..92b0f9be 100644 --- a/packages/ludic.render3d/gpu_vk_res.ludic +++ b/packages/ludic.render3d/gpu_vk_res.ludic @@ -327,7 +327,7 @@ function gvk_tex_upload(render3d_st: mut Render3dState, tex: int, ifmt: int, w: let hv = gvk_half(Vk.get_i32(src, i)) buf[o] = hv & 255; buf[o + 1] = (hv >> 8) & 255 } else { - print(`r3d: vulkan: no conversion from {bin}-byte to {bout}-byte samples`) + gvk_say_no_conversion(bin, bout) gvk_staging_free(render3d_st) return false } @@ -448,7 +448,7 @@ function gvk_tex_read(render3d_st: mut Render3dState, tex: int, ifmt: int, w: in # a read may ask for fewer channels than the image has (the height field's R of an RGBA32F # bake), never more, and only in the sample size the image stores if cwant > cout or gvk_gl_type_bytes(ty) != bout { - print(`r3d: vulkan: no read-back conversion for GL format {ifmt} as {fmt}/{ty}`) + gvk_say_no_readback(ifmt, fmt, ty) return false } let n = w * h * cout * bout @@ -741,7 +741,7 @@ function gvk_buf_reserve(render3d_st: mut Render3dState, b: int, n: int) -> bool let out = gvk_tmp(render3d_st, 8) render3d_st.gvk_mk_buf += 1 var r = Vk.create_buffer(render3d_st.gvk_dev, bci, render3d_st.gvk_ac, out) - if r != VK_SUCCESS { return gvk_fail(render3d_st, `vkCreateBuffer ({size} bytes)`, r) } + if r != VK_SUCCESS { return gvk_fail_create_buffer(render3d_st, size, r) } let buf = gvk_handle(out) let req = gvk_tmp(render3d_st, VkMemoryRequirements_sizeof) Vk.get_buffer_memory_requirements(render3d_st.gvk_dev, buf, req) @@ -831,3 +831,11 @@ function gvk_buf_upload(render3d_st: mut Render3dState, b: int, n: int, data: po if data != null and n > 0 { mem_copy(render3d_st.gvk_buf_map[b], data, n) } return true } + +# messages, each built in a function of its own so the path that says it holds no allocation +@alloc_ok("a message, built only when it is said: a failure, a warning or a debug switch") +function gvk_say_no_conversion(bin: int, bout: int) -> void { print(`r3d: vulkan: no conversion from {bin}-byte to {bout}-byte samples`) } +@alloc_ok("a message, built only when it is said: a failure, a warning or a debug switch") +function gvk_say_no_readback(ifmt: int, fmt: int, ty: int) -> void { print(`r3d: vulkan: no read-back conversion for GL format {ifmt} as {fmt}/{ty}`) } +@alloc_ok("a message, built only when it is said: a failure, a warning or a debug switch") +function gvk_fail_create_buffer(render3d_st: mut Render3dState, size: int, r: int) -> bool { return gvk_fail(render3d_st, `vkCreateBuffer ({size} bytes)`, r) } diff --git a/packages/ludic.render3d/post.ludic b/packages/ludic.render3d/post.ludic index 059347fd..f00dc685 100644 --- a/packages/ludic.render3d/post.ludic +++ b/packages/ludic.render3d/post.ludic @@ -57,6 +57,12 @@ function post_set_msaa(render3d_st: mut Render3dState, n: int) -> void { @alloc_ok("a world being set up (a stream, a layer, water, post, a bake): the map loading or swapping, not a frame") function post_init(render3d_st: mut Render3dState, w: int, h: int) -> void { render3d_st.post_w = w; render3d_st.post_h = h + # the exposure's two 1x1 targets: made once, here, rather than by the first frame that measured + if render3d_st.post_adapt_t == null { + render3d_st.post_adapt_t = new []Target + for k in 0 .. 2 { push(render3d_st.post_adapt_t, target_new(render3d_st, 1, 1, GL_R32F, GL_RED, GL_FLOAT, false, GL_NEAREST)) } + render3d_st.post_adapt_reset = true + } render3d_st.post_hdr = target_new(render3d_st, w, h, GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, true, GL_LINEAR) if render3d_st.post_ms_samples > 1 { render3d_st.post_ms_fbo = gpu_fb_new(render3d_st) @@ -134,11 +140,6 @@ function post_measure(render3d_st: mut Render3dState) -> void { gpu_tex_bind(render3d_st, GPU_TEX2D, render3d_st.post_hdr.color) gpu_tex_param(render3d_st, GPU_TEX2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR) gpu_tex_mips(render3d_st, GPU_TEX2D) - if render3d_st.post_adapt_t == null { - render3d_st.post_adapt_t = new []Target - for k in 0 .. 2 { push(render3d_st.post_adapt_t, target_new(render3d_st, 1, 1, GL_R32F, GL_RED, GL_FLOAT, false, GL_NEAREST)) } - render3d_st.post_adapt_reset = true - } if render3d_st.post_p_adapt == 0 { render3d_st.post_p_adapt = r3d_program(render3d_st, "fullscreen.vert", "adapt.frag", "") } let next = 1 - render3d_st.post_adapt_i target_bind(render3d_st, render3d_st.post_adapt_t[next]) diff --git a/packages/ludic.render3d/render.ludic b/packages/ludic.render3d/render.ludic index 611ffc73..ef448996 100644 --- a/packages/ludic.render3d/render.ludic +++ b/packages/ludic.render3d/render.ludic @@ -163,7 +163,7 @@ function r3d_resize(render3d_st: mut Render3dState) -> void { post_free(render3d_st) post_init(render3d_st, gl_width(), gl_height()) if render3d_st.water_refl != null { target_free(render3d_st, render3d_st.water_refl); render3d_st.water_refl = null } - print(`r3d: resized to {gl_width()}x{gl_height()}`) + r3d_say_resized() } function r3d_frame(render3d_st: mut Render3dState, time: float) -> void { outline_frame(render3d_st) @@ -196,7 +196,7 @@ function r3d_frame(render3d_st: mut Render3dState, time: float) -> void { # camera that moves while the hiker stands still from a picture that shakes on its own if render3d_st.r3d_cam_log < 0 { render3d_st.r3d_cam_log = 0; if r3d_env_has(render3d_st, "R3D_CAM_LOG") { render3d_st.r3d_cam_log = 1 } } if render3d_st.r3d_cam_log == 1 { - print(`cam {render3d_st.r3d_test_frame} pos {int(render3d_st.cam_pos[0] * 1000.0)} {int(render3d_st.cam_pos[1] * 1000.0)} {int(render3d_st.cam_pos[2] * 1000.0)} yaw {int(render3d_st.cam_yaw * 1000.0)} pitch {int(render3d_st.cam_pitch * 1000.0)} jitter {int(render3d_st.gsl_jitter_x * 1000000.0)} {int(render3d_st.gsl_jitter_y * 1000000.0)} render {render3d_st.post_w}x{render3d_st.post_h} reset {render3d_st.gsl_reset} evalok {render3d_st.gsl_eval_ok} fresh {render3d_st.gsl_fresh}`) + r3d_say_test_frame(render3d_st) } # the height-field shadow rebakes as the light moves in steps (daylight), or with the sky yaw when there is no clock if not render3d_st.r3d_plate and ((not render3d_st.day_on and render3d_st.ter_shadow_yaw != render3d_st.sky_yaw) or render3d_st.ter_shadow_gen != render3d_st.day_gen) { @@ -285,3 +285,9 @@ function r3d_frame(render3d_st: mut Render3dState, time: float) -> void { prof_collect(render3d_st) gpu_check(render3d_st, "frame") } + +# messages, each built in a function of its own so the path that says it holds no allocation +@alloc_ok("a message, built only when it is said: a failure, a warning or a debug switch") +function r3d_say_resized() -> void { print(`r3d: resized to {gl_width()}x{gl_height()}`) } +@alloc_ok("a message, built only when it is said: a failure, a warning or a debug switch") +function r3d_say_test_frame(render3d_st: Render3dState) -> void { print(`cam {render3d_st.r3d_test_frame} pos {int(render3d_st.cam_pos[0] * 1000.0)} {int(render3d_st.cam_pos[1] * 1000.0)} {int(render3d_st.cam_pos[2] * 1000.0)} yaw {int(render3d_st.cam_yaw * 1000.0)} pitch {int(render3d_st.cam_pitch * 1000.0)} jitter {int(render3d_st.gsl_jitter_x * 1000000.0)} {int(render3d_st.gsl_jitter_y * 1000000.0)} render {render3d_st.post_w}x{render3d_st.post_h} reset {render3d_st.gsl_reset} evalok {render3d_st.gsl_eval_ok} fresh {render3d_st.gsl_fresh}`) } diff --git a/packages/ludic.render3d/shadow.ludic b/packages/ludic.render3d/shadow.ludic index 41faebe0..ad81e970 100644 --- a/packages/ludic.render3d/shadow.ludic +++ b/packages/ludic.render3d/shadow.ludic @@ -40,7 +40,7 @@ function shadow_set_res(render3d_st: mut Render3dState, r: int) -> void { # that is refused now, rather than ending with no shadow map at all if not gpu_tex_ok(render3d_st, render3d_st.sh_tex) { render3d_st.shadow_refused = r - print(`r3d: shadows: no memory for {r} x {r} cascades; keeping {was}`) + shadow_say_no_memory(r, was) var size = was render3d_st.shadow_res = size gpu_tex_free(render3d_st, render3d_st.sh_tex) @@ -283,3 +283,7 @@ function shadow_debug_range(render3d_st: mut Render3dState, prog: int) -> void { render3d_st.sh_printed3 = true print(`range {fixed(render3d_st.sh_range[0])} {fixed(render3d_st.sh_range[1])} {fixed(render3d_st.sh_range[2])} {fixed(render3d_st.sh_range[3])} texel*1000 {fixed(render3d_st.sh_texel[0] * 1000.0)} {fixed(render3d_st.sh_texel[1] * 1000.0)} {fixed(render3d_st.sh_texel[2] * 1000.0)} {fixed(render3d_st.sh_texel[3] * 1000.0)} locs {gpu_uniform(render3d_st, prog, "u_cascade_range")} {gpu_uniform(render3d_st, prog, "u_cascade_texel")}`) } + +# messages, each built in a function of its own so the path that says it holds no allocation +@alloc_ok("a message, built only when it is said: a failure, a warning or a debug switch") +function shadow_say_no_memory(r: int, was: int) -> void { print(`r3d: shadows: no memory for {r} x {r} cascades; keeping {was}`) } diff --git a/packages/ludic.render3d/stream.ludic b/packages/ludic.render3d/stream.ludic index 9dc573ce..9e3cba36 100644 --- a/packages/ludic.render3d/stream.ludic +++ b/packages/ludic.render3d/stream.ludic @@ -254,7 +254,7 @@ function stream_update(render3d_st: mut Render3dState, s: Stream, cam_x: float, let dt = gl_now_us() - t0 render3d_st.stream_us_gen = render3d_st.stream_us_gen + dt prof_chunk(render3d_st, s.kind, band, c.count, dt) - if render3d_st.r3d_debug and band == 0 and render3d_st.stream_debug_n < 40 { render3d_st.stream_debug_n += 1; print(`stream kind {s.kind} band {band} chunk {cx},{cz}: {c.count} instances`) } + if render3d_st.r3d_debug and band == 0 and render3d_st.stream_debug_n < 40 { render3d_st.stream_debug_n += 1; stream_say_chunk(s.kind, band, cx, cz, c.count) } let need = c.count * INST_FLOATS if s.top + need > len(s.arena) and not render3d_st.stream_no_evict { stream_evict(render3d_st, s) } # If the walk in progress wants more than the cache can hold, there is nothing to @@ -354,3 +354,7 @@ function stream_clear_all(render3d_st: mut Render3dState) -> void { let all = render3d_st.stream_all List.clear(all) } + +# messages, each built in a function of its own so the path that says it holds no allocation +@alloc_ok("a message, built only when it is said: a failure, a warning or a debug switch") +function stream_say_chunk(kind: int, band: int, cx: int, cz: int, n: int) -> void { print(`stream kind {kind} band {band} chunk {cx},{cz}: {n} instances`) } diff --git a/packages/ludic.render3d/streamline.ludic b/packages/ludic.render3d/streamline.ludic index 6fea92b6..dde0f1fe 100644 --- a/packages/ludic.render3d/streamline.ludic +++ b/packages/ludic.render3d/streamline.ludic @@ -68,6 +68,8 @@ function gsl_boot(render3d_st: mut Render3dState) -> void { # After Vk.open(), before vkCreateInstance. @alloc_ok("start-up: the device, its tables, the programs, the passes and the world's first textures are made once, before play") function gsl_init(render3d_st: mut Render3dState) -> void { + # the evaluate call's structs, made once with DLSS rather than by the first frame it drew + if render3d_st.gsl_res == null { render3d_st.gsl_res = gsl_struct(4 * 112); render3d_st.gsl_tags = gsl_struct(4 * 64); render3d_st.gsl_inputs = bytes(8) } if render3d_st.gsl_on or Vk.sl_active() == 0 { return } render3d_st.gsl_feats = gsl_struct(20) Vk.put_i32(render3d_st.gsl_feats, 0, GSL_DLSS); Vk.put_i32(render3d_st.gsl_feats, 4, GSL_REFLEX); Vk.put_i32(render3d_st.gsl_feats, 8, GSL_PCL) @@ -383,7 +385,7 @@ function gsl_constants(render3d_st: mut Render3dState) -> void { Vk.put_i32(k, 360, float_bits(1.0)) # mvecScale Vk.put_i32(k, 364, float_bits(1.0)) gsl_put_v3(k, 376, render3d_st.cam_pos) - let up = floats(3) + let up = render3d_st.gsl_up # made with the state v3_cross(up, render3d_st.cam_right, render3d_st.cam_fwd) gsl_put_v3(k, 388, up) gsl_put_v3(k, 400, render3d_st.cam_right) @@ -397,7 +399,7 @@ function gsl_constants(render3d_st: mut Render3dState) -> void { # not dilated, not jittered if render3d_st.gsl_reset { Vk.put_i32(k, 444, 256 * 256 * 256) } Vk.put_i32(k, 452, float_bits(40.0)) # minRelativeLinearDepthObjectSeparation - free(fix); free(proj); free(v2c); free(c2v); free(cur); free(prev); free(inv_cur); free(c2p); free(p2c); free(ident); free(up) + free(fix); free(proj); free(v2c); free(c2v); free(cur); free(prev); free(inv_cur); free(c2p); free(p2c); free(ident) } # make the DLSS targets at this frame's sizes @@ -418,7 +420,6 @@ function gsl_targets(render3d_st: mut Render3dState) -> void { render3d_st.post_ldr = target_new(render3d_st, gl_width(), gl_height(), post_ldr_fmt(render3d_st), GL_RGBA, GL_UNSIGNED_BYTE, false, GL_LINEAR) render3d_st.post_ldr_hdr = gpu_hdr_active(render3d_st) } - if render3d_st.gsl_res == null { render3d_st.gsl_res = gsl_struct(4 * 112); render3d_st.gsl_tags = gsl_struct(4 * 64); render3d_st.gsl_inputs = bytes(8) } } # Upscale post_hdr into gsl_out; the colour the rest of post reads (post_hdr's own if it failed). @@ -450,10 +451,14 @@ function gsl_dlss_eval(render3d_st: mut Render3dState) -> int { # forgetting, because every gvk_draw binds its pipeline, view and set afresh render3d_st.gsl_eval_ok = r == 0 if r != 0 { - if not render3d_st.gsl_said { print(`r3d: streamline: DLSS evaluate failed ({r}); drawing without it`); render3d_st.gsl_said = true } + if not render3d_st.gsl_said { gsl_say_eval_failed(r); render3d_st.gsl_said = true } render3d_st.post_color_w = render3d_st.post_w; render3d_st.post_color_h = render3d_st.post_h return render3d_st.post_hdr.color } render3d_st.post_color_w = gl_width(); render3d_st.post_color_h = gl_height() return render3d_st.gsl_out.color } + +# messages, each built in a function of its own so the path that says it holds no allocation +@alloc_ok("a message, built only when it is said: a failure, a warning or a debug switch") +function gsl_say_eval_failed(r: int) -> void { print(`r3d: streamline: DLSS evaluate failed ({r}); drawing without it`) } diff --git a/packages/ludic.render3d/terrain.ludic b/packages/ludic.render3d/terrain.ludic index 6285c1d8..01706e83 100644 --- a/packages/ludic.render3d/terrain.ludic +++ b/packages/ludic.render3d/terrain.ludic @@ -512,7 +512,7 @@ function terrain_sun_pass(render3d_st: mut Render3dState, w: int, h: int, depth: if not render3d_st.ter_sun_checked { render3d_st.ter_sun_checked = true let st = gpu_fb_status(render3d_st) - if st != GL_FRAMEBUFFER_COMPLETE { print(`r3d: sun-visibility framebuffer incomplete {st}`) } + if st != GL_FRAMEBUFFER_COMPLETE { terrain_say_sun_fbo(st) } } gpu_depth_test(render3d_st, true) gpu_depth_func(render3d_st, GL_LESS) @@ -585,7 +585,7 @@ function terrain_draw(render3d_st: mut Render3dState) -> void { cdlod_select(render3d_st, CD_LEVELS - 1, 0, 0) if render3d_st.ter_wire { gpu_wireframe(render3d_st, false) } gpu_depth_func(render3d_st, GL_LESS) - if render3d_st.r3d_debug and not render3d_st.ter_printed { render3d_st.ter_printed = true; print(`cdlod patches drawn: {render3d_st.cd_draws} (far {render3d_st.cd_far_draws}, near {render3d_st.cd_near_draws}, band {render3d_st.cd_draws - render3d_st.cd_far_draws - render3d_st.cd_near_draws})`) } + if render3d_st.r3d_debug and not render3d_st.ter_printed { render3d_st.ter_printed = true; terrain_say_patches(render3d_st) } } # ---- CDLOD -------------------------------------------------------------------------- @@ -792,3 +792,9 @@ function terrain_reload(render3d_st: mut Render3dState, dem: string, emin: float function ter_scratch(render3d_st: mut Render3dState) -> floats { return render3d_st.ter_scr } + +# messages, each built in a function of its own so the path that says it holds no allocation +@alloc_ok("a message, built only when it is said: a failure, a warning or a debug switch") +function terrain_say_sun_fbo(st: int) -> void { print(`r3d: sun-visibility framebuffer incomplete {st}`) } +@alloc_ok("a message, built only when it is said: a failure, a warning or a debug switch") +function terrain_say_patches(render3d_st: Render3dState) -> void { print(`cdlod patches drawn: {render3d_st.cd_draws} (far {render3d_st.cd_far_draws}, near {render3d_st.cd_near_draws}, band {render3d_st.cd_draws - render3d_st.cd_far_draws - render3d_st.cd_near_draws})`) } diff --git a/packages/ludic.render3d/water.ludic b/packages/ludic.render3d/water.ludic index 40823a4d..2cbda7f8 100644 --- a/packages/ludic.render3d/water.ludic +++ b/packages/ludic.render3d/water.ludic @@ -104,7 +104,7 @@ function water_reflect_visible(render3d_st: mut Render3dState) -> bool { var seen = 0 var hidden = 0 for i in 0 .. render3d_st.wb_ncells { if wb_cell_visible(render3d_st, i) { seen += 1; if wb_cell_occluded(render3d_st, i) { hidden += 1 } } } - print(`water reflection test: {render3d_st.wb_ncells} wet rectangles in {render3d_st.wb_nblocks} blocks (built in {render3d_st.wb_build_us} us), {seen} in view, {hidden} of them behind the ground`) + water_say_test(render3d_st, seen, hidden) } # A rectangle in the frustum can still be behind the ground or dry where it shows: standing on a shore # above the lake and looking down at your feet puts water inside the view and none of it on screen. @@ -366,3 +366,7 @@ function water_draw(render3d_st: mut Render3dState, depth_tex: int) -> void { } gpu_blend(render3d_st, false) } + +# messages, each built in a function of its own so the path that says it holds no allocation +@alloc_ok("a message, built only when it is said: a failure, a warning or a debug switch") +function water_say_test(render3d_st: Render3dState, seen: int, hidden: int) -> void { print(`water reflection test: {render3d_st.wb_ncells} wet rectangles in {render3d_st.wb_nblocks} blocks (built in {render3d_st.wb_build_us} us), {seen} in view, {hidden} of them behind the ground`) }