Merge branch 'r3d/keeps-max' into lang/foundations

This commit is contained in:
Orkun ÇAKILKAYA 2026-09-28 16:47:35 +03:00
commit 3205408290
10 changed files with 77 additions and 37 deletions

View file

@ -118,17 +118,18 @@ function actor_remove(render3d_st: mut Render3dState, a: Actor) -> void {
# back on the stage (an actor kept across a world swap) # back on the stage (an actor kept across a world swap)
function actor_keep(render3d_st: mut Render3dState, a: Actor) -> void { function actor_keep(render3d_st: mut Render3dState, a: Actor) -> void {
if a == null { return } if a == null { return }
if render3d_st.ac_actors == null { render3d_st.ac_actors = new []Actor }
a.row = len(render3d_st.ac_actors) a.row = len(render3d_st.ac_actors)
push(render3d_st.ac_actors, a) push(render3d_st.ac_actors, a)
} }
# colour one named part of the model (a material name from the file) # colour one named part of the model (a material name from the file)
@alloc_ok("an actor's part tint or hidden-part table, made once per actor and given back by actor_release")
function actor_tint_part(a: Actor, name: string, r: float, g: float, b: float) -> void { function actor_tint_part(a: Actor, name: string, r: float, g: float, b: float) -> void {
if a.model == null { return } if a.model == null { return }
let n = len(a.model.prims) let n = len(a.model.prims)
if a.ptint == null { a.ptint = words(n * 4); for i in 0 .. n * 4 { a.ptint[i] = 0 } } if a.ptint == null { a.ptint = words(n * 4); for i in 0 .. n * 4 { a.ptint[i] = 0 } }
for i in 0 .. n { if a.model.prims[i].name == name { a.ptint[i * 4] = 1; a.ptint[i * 4 + 1] = float_bits(r); a.ptint[i * 4 + 2] = float_bits(g); a.ptint[i * 4 + 3] = float_bits(b) } } for i in 0 .. n { if a.model.prims[i].name == name { a.ptint[i * 4] = 1; a.ptint[i * 4 + 1] = float_bits(r); a.ptint[i * 4 + 2] = float_bits(g); a.ptint[i * 4 + 3] = float_bits(b) } }
} }
@alloc_ok("an actor's part tint or hidden-part table, made once per actor and given back by actor_release")
function actor_hide_part(a: Actor, name: string, hidden: bool) -> void { function actor_hide_part(a: Actor, name: string, hidden: bool) -> void {
if a.model == null { return } if a.model == null { return }
let n = len(a.model.prims) let n = len(a.model.prims)
@ -428,13 +429,14 @@ function ac_caster_count(render3d_st: mut Render3dState, a: Actor) -> void {
# every actor off the stage at once, for a world being replaced. Actors own no GL objects; # every actor off the stage at once, for a world being replaced. Actors own no GL objects;
# their models belong to whoever loaded them. # their models belong to whoever loaded them.
function actor_clear_all(render3d_st: mut Render3dState) -> void { function actor_clear_all(render3d_st: mut Render3dState) -> void {
if render3d_st.ac_actors == null { render3d_st.ac_actors = new []Actor } if render3d_st.ac_actors == null { return }
for i in 0 .. len(render3d_st.ac_actors) { render3d_st.ac_actors[i].row = -1 } for i in 0 .. len(render3d_st.ac_actors) { render3d_st.ac_actors[i].row = -1 }
List.clear(render3d_st.ac_actors) List.clear(render3d_st.ac_actors)
outline_clear(render3d_st) outline_clear(render3d_st)
} }
# A record as `new Actor` makes it, with its matrix and tint: a released one when there is one. # A record as `new Actor` makes it, with its matrix and tint: a released one when there is one.
@alloc_ok("the actor pool ran dry: one more record, which actor_release keeps in the pool after")
function actor_fresh(render3d_st: mut Render3dState) -> Actor { function actor_fresh(render3d_st: mut Render3dState) -> Actor {
let sp = render3d_st.ac_spare let sp = render3d_st.ac_spare
if sp == null or len(sp) == 0 { if sp == null or len(sp) == 0 {
@ -465,7 +467,7 @@ function actor_release(render3d_st: mut Render3dState, a: Actor) -> void {
a.skin_owned = false a.skin_owned = false
a.model = null; a.skin = null; a.ocol = null a.model = null; a.skin = null; a.ocol = null
a.id = 0 a.id = 0
if render3d_st.ac_spare == null { render3d_st.ac_spare = new []Actor } if render3d_st.ac_spare == null { return } # made in actor_init, with its room
push(render3d_st.ac_spare, a) push(render3d_st.ac_spare, a)
} }

View file

@ -11,9 +11,9 @@ export state Render3dState {
ac_sh_cut: AcProg = null ac_sh_cut: AcProg = null
ac_out: AcProg = null ac_out: AcProg = null
ac_out_cut: AcProg = null ac_out_cut: AcProg = null
ac_actors: []Actor = null @max(2048) ac_actors: []Actor = null
ac_next_id: int = 0 ac_next_id: int = 0
ac_spare: []Actor = null # released actors, taken again by actor_new (actor_release) @max(2048) ac_spare: []Actor = null # released actors, taken again by actor_new (actor_release)
ac_frame: int = 0 ac_frame: int = 0
ac_oq: []OutlineReq = null ac_oq: []OutlineReq = null
ac_oq_n: int = 0 # live entries; the array is kept and reused ac_oq_n: int = 0 # live entries; the array is kept and reused
@ -181,15 +181,15 @@ export state Render3dState {
gvk_blk_kind: []int = null # memory type * 2, + 1 for images gvk_blk_kind: []int = null # memory type * 2, + 1 for images
gvk_blk_size: []int = null gvk_blk_size: []int = null
gvk_blk_map: []pointer = null gvk_blk_map: []pointer = null
gvk_fr_blk: []int = null # free ranges: block, offset, length (0 = an unused slot) @max(4096) gvk_fr_blk: []int = null # free ranges: block, offset, length (0 = an unused slot)
gvk_fr_off: []int = null @max(4096) gvk_fr_off: []int = null
gvk_fr_len: []int = null @max(4096) gvk_fr_len: []int = null
gvk_al_blk: []int = null # per allocation id: its block, or -1 for its own memory gvk_al_blk: []int = null # per allocation id: its block, or -1 for its own memory
gvk_al_mem: []long = null # its own memory when it has one gvk_al_mem: []long = null # its own memory when it has one
gvk_al_off: []int = null gvk_al_off: []int = null
gvk_al_len: []int = null gvk_al_len: []int = null
gvk_al_map: []pointer = null gvk_al_map: []pointer = null
gvk_al_spare: []int = null # ids to reuse @max(4096) gvk_al_spare: []int = null # ids to reuse
gvk_pool: long = 0 gvk_pool: long = 0
gvk_fence: bytes = null gvk_fence: bytes = null
gvk_frame_fence: bytes = null # the presented frame still on the GPU (one frame in flight) gvk_frame_fence: bytes = null # the presented frame still on the GPU (one frame in flight)
@ -201,7 +201,7 @@ export state Render3dState {
gvk_frame_pending_no: int = -1 # which frame it is (gvk_frame_no when it was submitted) gvk_frame_pending_no: int = -1 # which frame it is (gvk_frame_no when it was submitted)
gvk_dpools: []long = null # a descriptor pool per frame slot (frame_no & 1) gvk_dpools: []long = null # a descriptor pool per frame slot (frame_no & 1)
gvk_ring_end: int = 0 # the end of this frame's half of the ring gvk_ring_end: int = 0 # the end of this frame's half of the ring
gvk_retired_frame: []int = null # the last frame that read each retired buffer @max(4096) gvk_retired_frame: []int = null # the last frame that read each retired buffer
gvk_labels: bool = false # R3D_VK_LABELS: each profiled pass is a debug label (Metal System Trace) gvk_labels: bool = false # R3D_VK_LABELS: each profiled pass is a debug label (Metal System Trace)
gvk_inflight: int = -1 gvk_inflight: int = -1
gvk_nopool: bool = false gvk_nopool: bool = false
@ -212,8 +212,8 @@ export state Render3dState {
gvk_lay_flat: []int = null # every layout known, end to end; gvk_lay_off / _len find each gvk_lay_flat: []int = null # every layout known, end to end; gvk_lay_off / _len find each
gvk_lay_off: []int = null gvk_lay_off: []int = null
gvk_lay_len: []int = null gvk_lay_len: []int = null
gvk_tex_spare: []int = null # ids of freed textures, handed out again by gvk_tex_new @max(4096) gvk_tex_spare: []int = null # ids of freed textures, handed out again by gvk_tex_new
gvk_buf_spare: []int = null # the same for buffers (gvk_buf_delete) @max(4096) gvk_buf_spare: []int = null # the same for buffers (gvk_buf_delete)
gvk_has_bc: bool = false # textureCompressionBC: .dds textures are uploaded compressed gvk_has_bc: bool = false # textureCompressionBC: .dds textures are uploaded compressed
# R3D_VK_PROF: Vulkan objects made and destroyed, reported every 120 frames # R3D_VK_PROF: Vulkan objects made and destroyed, reported every 120 frames
gvk_mk_img: int = 0 gvk_mk_img: int = 0
@ -271,12 +271,12 @@ export state Render3dState {
gvk_sc_next: []int = null # the program's next cached set, -1 at the end gvk_sc_next: []int = null # the program's next cached set, -1 at the end
gvk_sc_keys: []long = null # per texture of a key: handle * 65536 + generation, sampler gvk_sc_keys: []long = null # per texture of a key: handle * 65536 + generation, sampler
gvk_sc_head: words = null # per program (< 4096): its most recently made set, -1 if none gvk_sc_head: words = null # per program (< 4096): its most recently made set, -1 if none
gvk_sc_tmp: []long = null # this draw's key while it is looked up @max(130) gvk_sc_tmp: []long = null # this draw's key while it is looked up
gvk_set_offs: bytes = null # this draw's dynamic offsets, in binding order gvk_set_offs: bytes = null # this draw's dynamic offsets, in binding order
gvk_set_ndyn: int = 0 gvk_set_ndyn: int = 0
gvk_ub_frame: []int = null # per program: the frame its blocks were last copied into the ring @max(4096) gvk_ub_frame: []int = null # per program: the frame its blocks were last copied into the ring
gvk_ub_offv: []int = null @max(4096) gvk_ub_offv: []int = null
gvk_ub_offf: []int = null @max(4096) gvk_ub_offf: []int = null
gvk_sc_made: int = 0 # sets made since start (R3D_VK_PROF) gvk_sc_made: int = 0 # sets made since start (R3D_VK_PROF)
gvk_default_smp: long = 0 # linear, clamped: for a texture slot nothing was bound to gvk_default_smp: long = 0 # linear, clamped: for a texture slot nothing was bound to
gvk_cb: pointer = null gvk_cb: pointer = null
@ -376,9 +376,9 @@ export state Render3dState {
gvk_buf_map: []pointer = null # host-visible buffers stay mapped for their whole life gvk_buf_map: []pointer = null # host-visible buffers stay mapped for their whole life
gvk_buf_used: []int = null # the frame (gvk_frame_no) a draw last bound the buffer in gvk_buf_used: []int = null # the frame (gvk_frame_no) a draw last bound the buffer in
gvk_frame_no: int = 1 gvk_frame_no: int = 1
gvk_retired_buf: []long = null @max(4096) gvk_retired_buf: []long = null
gvk_retired_mem: []long = null @max(4096) gvk_retired_mem: []long = null
gvk_buf_gpu: []int = null @max(16384) gvk_buf_gpu: []int = null
grass_prog: int = 0 grass_prog: int = 0
grass_mesh: Mesh = null grass_mesh: Mesh = null
grass_on: bool = true grass_on: bool = true
@ -696,7 +696,7 @@ export state Render3dState {
ov_nine_buf: floats = floats(16) # ov_nine's corners and uvs 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_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 gvk_prime_b: words = null # buffers made since the frame began, read once so MoltenVK makes their
gvk_prime_h: []long = null # Metal buffers now (gvk_prime), and their handles @max(16384) gvk_prime_h: []long = null # Metal buffers now (gvk_prime), and their handles
gvk_prime_n: int = 0 gvk_prime_n: int = 0
gvk_prime_dst: long = 0 # the 64 bytes those reads land in gvk_prime_dst: long = 0 # the 64 bytes those reads land in
gvk_prime_mem: long = 0 gvk_prime_mem: long = 0

View file

@ -231,7 +231,9 @@ function gpu_caps_fake(render3d_st: mut Render3dState, kind: string) -> void {
render3d_st.gpu_cap_vulkan = true; render3d_st.gpu_cap_floor = true; render3d_st.gpu_cap_hdr = true render3d_st.gpu_cap_vulkan = true; render3d_st.gpu_cap_floor = true; render3d_st.gpu_cap_hdr = true
if kind == "amd" or kind == "intel" { render3d_st.gpu_cap_rt = true; render3d_st.gpu_cap_mesh = true; render3d_st.gpu_cap_device = `test {kind} GPU`; return } if kind == "amd" or kind == "intel" { render3d_st.gpu_cap_rt = true; render3d_st.gpu_cap_mesh = true; render3d_st.gpu_cap_device = `test {kind} GPU`; return }
render3d_st.gpu_cap_nvidia = true; render3d_st.gpu_cap_rt = true; render3d_st.gpu_cap_mesh = true; render3d_st.gpu_cap_reflex = true render3d_st.gpu_cap_nvidia = true; render3d_st.gpu_cap_rt = true; render3d_st.gpu_cap_mesh = true; render3d_st.gpu_cap_reflex = true
render3d_st.gpu_cap_rtx = gpu_rtx_generation(`RTX {kind[3 .. 5]}`) let rtx = `RTX {kind[3 .. 5]}`
render3d_st.gpu_cap_rtx = gpu_rtx_generation(rtx)
free(rtx)
render3d_st.gpu_cap_device = `test NVIDIA GeForce RTX {kind[3 .. 5]}` render3d_st.gpu_cap_device = `test NVIDIA GeForce RTX {kind[3 .. 5]}`
} }
@ -265,8 +267,14 @@ function gpu_caps_probe(render3d_st: mut Render3dState) -> void {
inst = render3d_st.gvk_inst inst = render3d_st.gvk_inst
} else { } else {
let out = bytes(8) let out = bytes(8)
if Vk.create_instance(ici, null, out) != VK_SUCCESS { return } let made = Vk.create_instance(ici, null, out)
free(app); free(ici)
if made != VK_SUCCESS {
free(out)
return
}
inst = Vk.get_ptr(out, 0) inst = Vk.get_ptr(out, 0)
free(out)
own = true own = true
} }
Vk.put_i32(cnt, 0, 0) Vk.put_i32(cnt, 0, 0)
@ -534,7 +542,6 @@ function gpu_tex_bind(render3d_st: mut Render3dState, kind: int, tex: int) -> vo
ds_tex_bind(render3d_st) ds_tex_bind(render3d_st)
if kind == GPU_TEX2D_ARRAY { render3d_st.gpu_bound_array = tex } else { render3d_st.gpu_bound_2d = tex } if kind == GPU_TEX2D_ARRAY { render3d_st.gpu_bound_array = tex } else { render3d_st.gpu_bound_2d = tex }
if kind != GPU_TEX2D_ARRAY and render3d_st.gpu_unit_cur < 32 { if kind != GPU_TEX2D_ARRAY and render3d_st.gpu_unit_cur < 32 {
if render3d_st.gpu_unit_2d == null { render3d_st.gpu_unit_2d = words(32); for i in 0 .. 32 { render3d_st.gpu_unit_2d[i] = -1 } }
render3d_st.gpu_unit_2d[render3d_st.gpu_unit_cur] = tex render3d_st.gpu_unit_2d[render3d_st.gpu_unit_cur] = tex
} }
} }

View file

@ -30,6 +30,7 @@ function gvk_note(render3d_st: mut Render3dState, msg: string) -> void {
let line = msg + "\n" let line = msg + "\n"
file_write(f, line, len(line)) file_write(f, line, len(line))
file_close(f) file_close(f)
free(line)
} }
function gvk_handle(out: bytes) -> long { return Vk.get_i64(out, 0) } function gvk_handle(out: bytes) -> long { return Vk.get_i64(out, 0) }
@ -747,6 +748,10 @@ function gvk_startup_state(render3d_st: mut Render3dState) -> void {
if render3d_st.gvk_retired_frame == null { render3d_st.gvk_retired_frame = 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_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 } if render3d_st.gvk_retired_buf == null { render3d_st.gvk_retired_buf = new []long; render3d_st.gvk_retired_mem = new []long }
# the prime queue, and the texture and framebuffer tables grown to their working size, now
gvk_prime_tables(render3d_st)
gpu_tx_at(render3d_st, 4095)
gpu_fb_at(render3d_st, 1023)
# room made now in every list play pushes into, so a push never grows one (plan 25.2) # 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_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_buf, GVK_LIST_ROOM)

View file

@ -375,7 +375,7 @@ function gvk_depth_op(f: int) -> int {
# at another instance buffer keeps its layout, and so its pipeline. # at another instance buffer keeps its layout, and so its pipeline.
function gvk_layout_key(m: Mesh) -> string { function gvk_layout_key(m: Mesh) -> string {
if m == null or m.attrs == null { return "none" } if m == null or m.attrs == null { return "none" }
var k = "" var k: string = null # each step frees the key it grew from
let seen = words(GPU_MAX_ATTRS) let seen = words(GPU_MAX_ATTRS)
var ns = 0 var ns = 0
for i in 0 .. m.n_attrs { for i in 0 .. m.n_attrs {
@ -384,8 +384,16 @@ function gvk_layout_key(m: Mesh) -> string {
var bi = -1 var bi = -1
for q in 0 .. ns { if seen[q] == m.attrs[o] and bi < 0 { bi = q } } for q in 0 .. ns { if seen[q] == m.attrs[o] and bi < 0 { bi = q } }
if bi < 0 { bi = ns; seen[ns] = m.attrs[o]; ns += 1 } if bi < 0 { bi = ns; seen[ns] = m.attrs[o]; ns += 1 }
k = k + `{i}:{bi}:{m.attrs[o + 1]}:{m.attrs[o + 2]}:{m.attrs[o + 3]}:{m.attrs[o + 4]}:{m.attrs[o + 5]}:{m.attrs[o + 6]};` let part = `{i}:{bi}:{m.attrs[o + 1]}:{m.attrs[o + 2]}:{m.attrs[o + 3]}:{m.attrs[o + 4]}:{m.attrs[o + 5]}:{m.attrs[o + 6]};`
if k == null { k = part } else {
let nk = k + part
free(k)
free(part)
k = nk
}
} }
free(seen)
if k == null { return "" }
return k return k
} }
@ -982,6 +990,7 @@ function r3d_hdr_calibrate(render3d_st: mut Render3dState, peak: float, paper: f
} }
# what the picture is: graded in BT.709 around D65, highlights to the calibrated peak, paper white average # what the picture is: graded in BT.709 around D65, highlights to the calibrated peak, paper white average
@alloc_ok("a settings change (HDR output): made once per change")
function gvk_hdr_metadata(render3d_st: Render3dState) -> void { function gvk_hdr_metadata(render3d_st: Render3dState) -> void {
let md = bytes(VkHdrMetadataEXT_sizeof) let md = bytes(VkHdrMetadataEXT_sizeof)
Vk.zero(md, VkHdrMetadataEXT_sizeof) Vk.zero(md, VkHdrMetadataEXT_sizeof)
@ -1615,6 +1624,7 @@ function gvk_blit(render3d_st: mut Render3dState, w: int, h: int, mask: int) ->
} }
# the screen as RGB8, bottom row first, as glReadPixels hands it back (a photograph) # the screen as RGB8, bottom row first, as glReadPixels hands it back (a photograph)
@alloc_ok("asked for by the player or a tool, not by the frame")
function gvk_read_screen(render3d_st: mut Render3dState, w: int, h: int, out: pointer) -> void { function gvk_read_screen(render3d_st: mut Render3dState, w: int, h: int, out: pointer) -> void {
gvk_present(render3d_st) gvk_present(render3d_st)
let px = bytes(render3d_st.gvk_screen_w * render3d_st.gvk_screen_h * 4) let px = bytes(render3d_st.gvk_screen_w * render3d_st.gvk_screen_h * 4)

View file

@ -761,14 +761,7 @@ function gvk_buf_reserve(render3d_st: mut Render3dState, b: int, n: int) -> bool
# read once (4 bytes, copied out) at the start of the next frame's commands instead. # read once (4 bytes, copied out) at the start of the next frame's commands instead.
const GVK_PRIME_MAX: int = 16384 const GVK_PRIME_MAX: int = 16384
function gvk_prime(render3d_st: mut Render3dState, b: int, buf: long) -> void { function gvk_prime(render3d_st: mut Render3dState, b: int, buf: long) -> void {
if Os.platform() != "macos" { return } if Os.platform() != "macos" or render3d_st.gvk_prime_b == null { return } # before start-up: its first draw makes it
if render3d_st.gvk_prime_b == null {
render3d_st.gvk_prime_b = words(GVK_PRIME_MAX)
render3d_st.gvk_prime_h = new []long
let hs = render3d_st.gvk_prime_h
let none: long = 0
for i in 0 .. GVK_PRIME_MAX { push(hs, none) }
}
if render3d_st.gvk_prime_n >= GVK_PRIME_MAX { return } if render3d_st.gvk_prime_n >= GVK_PRIME_MAX { return }
render3d_st.gvk_prime_b[render3d_st.gvk_prime_n] = b render3d_st.gvk_prime_b[render3d_st.gvk_prime_n] = b
render3d_st.gvk_prime_h[render3d_st.gvk_prime_n] = buf render3d_st.gvk_prime_h[render3d_st.gvk_prime_n] = buf
@ -839,3 +832,12 @@ function gvk_say_no_conversion(bin: int, bout: int) -> void { print(`r3d: vulkan
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}`) } 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") @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) } function gvk_fail_create_buffer(render3d_st: mut Render3dState, size: int, r: int) -> bool { return gvk_fail(render3d_st, `vkCreateBuffer ({size} bytes)`, r) }
# gvk_prime's queue: GVK_PRIME_MAX buffers and their handles, made with the device
function gvk_prime_tables(render3d_st: mut Render3dState) -> void {
render3d_st.gvk_prime_b = words(GVK_PRIME_MAX)
render3d_st.gvk_prime_h = new []long
let hs = render3d_st.gvk_prime_h
let none: long = 0
for i in 0 .. GVK_PRIME_MAX { push(hs, none) }
}

View file

@ -45,6 +45,7 @@ function ov_pick_prog(render3d_st: mut Render3dState) -> void {
render3d_st.ov_prog = render3d_st.ov_prog_sdr render3d_st.ov_prog = render3d_st.ov_prog_sdr
} }
@alloc_ok("start-up: the overlay's tables are made once")
function overlay_init(render3d_st: mut Render3dState, font_dir: string) -> bool { function overlay_init(render3d_st: mut Render3dState, font_dir: string) -> bool {
r3d_program_log(render3d_st, "overlay.vert", "overlay.frag", "") r3d_program_log(render3d_st, "overlay.vert", "overlay.frag", "")
render3d_st.ov_prog = gpu_program(render3d_st, "overlay.vert", "overlay.frag", "") render3d_st.ov_prog = gpu_program(render3d_st, "overlay.vert", "overlay.frag", "")
@ -340,11 +341,14 @@ function ov_text_wrap(render3d_st: mut Render3dState, x: int, y: int, size: int,
if sp[i] == 10 { stop = i; break } if sp[i] == 10 { stop = i; break }
if sp[i] == 32 { last_space = i } if sp[i] == 32 { last_space = i }
let piece: string = s[first .. i + 1] let piece: string = s[first .. i + 1]
if ov_text_w(render3d_st, size, piece) > maxw and last_space > first { stop = last_space; break } let pw = ov_text_w(render3d_st, size, piece)
free(piece)
if pw > maxw and last_space > first { stop = last_space; break }
i += 1 i += 1
} }
let line: string = s[first .. stop] let line: string = s[first .. stop]
ov_text(render3d_st, x, ly, size, line, r, g, b, a) ov_text(render3d_st, x, ly, size, line, r, g, b, a)
free(line)
ly += size * 13 / 10 ly += size * 13 / 10
first = stop first = stop
while first < n and (sp[first] == 32 or sp[first] == 10) { first += 1 } while first < n and (sp[first] == 32 or sp[first] == 10) { first += 1 }

View file

@ -100,9 +100,16 @@ function r3d_load_count() -> int { return 4 + TERRAIN_INIT_STEPS }
function r3d_load_step(render3d_st: mut Render3dState, i: int) -> bool { function r3d_load_step(render3d_st: mut Render3dState, i: int) -> bool {
let t = i - 1 let t = i - 1
if i == 0 { if i == 0 {
var sky = render3d_st.r3d_assets + "/hdri/kloofendal_48d_partly_cloudy_puresky_4k.hdr" # the default sky's path is made only when no path was given, and goes once the sky is read
if render3d_st.r3d_sky_path != null { sky = render3d_st.r3d_sky_path } var sky = render3d_st.r3d_sky_path
if not sky_load(render3d_st, sky) { return false } var made: string = null
if sky == null {
made = render3d_st.r3d_assets + "/hdri/kloofendal_48d_partly_cloudy_puresky_4k.hdr"
sky = made
}
let got = sky_load(render3d_st, sky)
if made != null { free(made) }
if not got { return false }
daylight_init(render3d_st) daylight_init(render3d_st)
} else if t >= 0 and t < TERRAIN_INIT_STEPS { } else if t >= 0 and t < TERRAIN_INIT_STEPS {
if render3d_st.r3d_plate { return true } if render3d_st.r3d_plate { return true }

View file

@ -407,6 +407,7 @@ function layer_new(render3d_st: mut Render3dState, model: Model, cap: int, folia
# room for n instances (at most the layer's cap), doubling what there is so a fill costs a few copies; # room for n instances (at most the layer's cap), doubling what there is so a fill costs a few copies;
# a caller writing l.inst itself reserves first # a caller writing l.inst itself reserves first
function layer_reserve(l: Layer, n: int) -> void { layer_room(l, n) } function layer_reserve(l: Layer, n: int) -> void { layer_room(l, n) }
@alloc_ok("a layer outgrowing its capacity, grown once to what it now holds (rare, and bounded by the scene)")
function layer_room(l: Layer, n: int) -> void { function layer_room(l: Layer, n: int) -> void {
if n <= l.have { return } if n <= l.have { return }
var want = max(l.have * 2, n) var want = max(l.have * 2, n)

View file

@ -42,6 +42,7 @@ function gsl_header(p: pointer, a: int, b: int, c: int, d: int, e: int, f: int,
let v: long = version let v: long = version
Vk.put_i64(p, 24, v) Vk.put_i64(p, 24, v)
} }
@alloc_ok("DLSS set-up: its structs and entry points, made once")
function gsl_struct(size: int) -> bytes { function gsl_struct(size: int) -> bytes {
let p = bytes(size) let p = bytes(size)
Vk.zero(p, size) Vk.zero(p, size)
@ -49,6 +50,7 @@ function gsl_struct(size: int) -> bytes {
} }
# a feature's own function (slDLSSSetOptions, slReflexSleep, ...), or null # a feature's own function (slDLSSSetOptions, slReflexSleep, ...), or null
@alloc_ok("DLSS set-up: its structs and entry points, made once")
function gsl_fn(feature: int, name: string) -> pointer { function gsl_fn(feature: int, name: string) -> pointer {
let out = bytes(8) let out = bytes(8)
let zero: long = 0 let zero: long = 0