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 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-28 16:36:44 +03:00
parent 629579123a
commit 958a262ba1
12 changed files with 120 additions and 26 deletions

View file

@ -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) }
}

View file

@ -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

View file

@ -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) }
}

View file

@ -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})`) }

View file

@ -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) }

View file

@ -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])

View file

@ -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}`) }

View file

@ -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}`) }

View file

@ -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`) }

View file

@ -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`) }

View file

@ -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})`) }

View file

@ -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`) }