diff --git a/packages/ludic.render3d/actor.ludic b/packages/ludic.render3d/actor.ludic index 40af73a1..65eef2b5 100644 --- a/packages/ludic.render3d/actor.ludic +++ b/packages/ludic.render3d/actor.ludic @@ -448,3 +448,31 @@ function actor_release(render3d_st: mut Render3dState, a: Actor) -> void { if render3d_st.ac_spare == null { render3d_st.ac_spare = new []Actor } push(render3d_st.ac_spare, a) } + +# One command buffer's worth of draws at start-up, through the path every draw takes (pipeline, set, +# buffers, draw), so Metal's pooled command storage is already the size a frame can need: a bigger +# frame than any before once grew it in play (IOGPU, under vkQueueSubmit). Drawn into the screen +# image before the first frame clears it. +function r3d_warm_commands(render3d_st: mut Render3dState) -> void { + let n = gvk_cb_cap_of(render3d_st) + if Os.platform() != "macos" or n <= 0 or render3d_st.ac_lit == null { return } + let m = gpu_mesh_new(render3d_st) + let v = floats(24) + for k in 0 .. 3 { v[k * 8 + k] = 0.01; v[k * 8 + 4] = 1.0 } + gpu_mesh_vertices(render3d_st, m, data_of(v), 96, GPU_STATIC) + gpu_mesh_attr(render3d_st, m, 0, 3, GPU_F32, 32, 0, false) + gpu_mesh_attr(render3d_st, m, 1, 3, GPU_F32, 32, 12, false) + gpu_mesh_attr(render3d_st, m, 2, 2, GPU_F32, 32, 24, false) + let ix = words(3) + ix[0] = 0; ix[1] = 1; ix[2] = 2 + gpu_mesh_indices(render3d_st, m, data_of(ix), 12, 4) + m.count = 3 + gpu_mesh_done(render3d_st, m) + gpu_fb_bind(render3d_st, 0) + gpu_use_program(render3d_st, render3d_st.ac_lit.prog) + for i in 0 .. n { gpu_draw_mesh(render3d_st, m) } + gvk_flush(render3d_st) + mesh_release(render3d_st, m) + free(v) + free(ix) +} diff --git a/packages/ludic.render3d/env.ludic b/packages/ludic.render3d/env.ludic index 446217d9..125ab03c 100644 --- a/packages/ludic.render3d/env.ludic +++ b/packages/ludic.render3d/env.ludic @@ -339,6 +339,8 @@ export state Render3dState { gvk_us_set: long = 0 gvk_us_draw: long = 0 gvk_n_draws: int = 0 + gvk_cb_draws: int = 0 # draws recorded into the frame's command buffer so far + gvk_cb_cap: int = -1 # a command buffer is submitted after this many (0 = never; -1 unread) gvk_n_asked: int = 0 # draws that reached gvk_draw_now: every one the renderer asked for gvk_n_flush: int = 0 gvk_prof_frames: int = 0 diff --git a/packages/ludic.render3d/gpu_vk_draw.ludic b/packages/ludic.render3d/gpu_vk_draw.ludic index fdb3bd3e..7d608d1e 100644 --- a/packages/ludic.render3d/gpu_vk_draw.ludic +++ b/packages/ludic.render3d/gpu_vk_draw.ludic @@ -1248,6 +1248,10 @@ function gvk_set_view(render3d_st: mut Render3dState, cb: pointer) -> void { # first / count select vertices or indices; count 0 means the mesh's own count. instances >= 1. function gvk_draw(render3d_st: mut Render3dState, p: int, m: Mesh, st: GvkState, first: int, count: int, instances: int, tx: words, tx_w: int, tx_cap: int, rec: words, rec_o: int) -> void { if render3d_st.gvk_prog_var == null or p <= 0 or p >= len(render3d_st.gvk_prog_var) or render3d_st.gvk_prog_var[p] == null { return } + let cap = gvk_cb_cap_of(render3d_st) + if cap > 0 and render3d_st.gvk_cb_draws >= cap { gvk_flush(render3d_st) } + if render3d_st.gvk_cb == null { render3d_st.gvk_cb_draws = 0 } + render3d_st.gvk_cb_draws += 1 let prof = gvk_prof(render3d_st) var t0: long = 0 if prof { t0 = gl_now_us() } @@ -1974,3 +1978,15 @@ function gvk_pipeline_fast(render3d_st: mut Render3dState, p: int, m: Mesh, st: if p < 4096 { render3d_st.gvk_pc_last[p] = len(render3d_st.gvk_pc_prog) - 1 } return pipe } + +# Draws a command buffer holds before it is submitted and the frame goes on in the next. Metal pools +# the storage a command buffer grew to and grows it only when one encodes more than any before, so +# with a bound and that many drawn at start-up (r3d_warm_commands) play never grows it. 0: no bound. +const GVK_CB_DRAWS: int = 512 +function gvk_cb_cap_of(render3d_st: mut Render3dState) -> int { + if render3d_st.gvk_cb_cap < 0 { + render3d_st.gvk_cb_cap = GVK_CB_DRAWS + if r3d_env_has(render3d_st, "R3D_CB_DRAWS") { render3d_st.gvk_cb_cap = Text.to_int(r3d_env(render3d_st, "R3D_CB_DRAWS")) } + } + return render3d_st.gvk_cb_cap +} diff --git a/packages/ludic.render3d/render.ludic b/packages/ludic.render3d/render.ludic index 611ffc73..66a2650a 100644 --- a/packages/ludic.render3d/render.ludic +++ b/packages/ludic.render3d/render.ludic @@ -127,6 +127,7 @@ function r3d_load_step(render3d_st: mut Render3dState, i: int) -> bool { render3d_st.r3d_fog_density = 0.00024 render3d_st.r3d_fog_falloff = 0.002 render3d_st.r3d_ready = true + r3d_warm_commands(render3d_st) gpu_check(render3d_st, "r3d init") } return true