From c736fd3b09f5c440a71238b9fc853e90a7eae0a1 Mon Sep 17 00:00:00 2001 From: Orkuncakilkaya Date: Mon, 28 Sep 2026 14:03:36 +0300 Subject: [PATCH] render3d: buffer primes in a command buffer of their own, submitted at once A co-op guest loading its models segfaulted in copyBufferToBuffer under vkQueueSubmit (0x68, AGX LegacyBlitContext): a buffer primed into the frame's command buffer was released later in that frame, and gvk_buf_release destroys a buffer no draw has marked, so the copy read a freed one. The copies now run in their own command buffer before the frame's begins, checked against the slot's handle as they are recorded. Co-Authored-By: Claude Opus 5.5 --- packages/ludic.render3d/gpu_vk_draw.ludic | 2 +- packages/ludic.render3d/gpu_vk_res.ludic | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/ludic.render3d/gpu_vk_draw.ludic b/packages/ludic.render3d/gpu_vk_draw.ludic index fdb3bd3e..b56bd38d 100644 --- a/packages/ludic.render3d/gpu_vk_draw.ludic +++ b/packages/ludic.render3d/gpu_vk_draw.ludic @@ -1012,8 +1012,8 @@ function gvk_frame_cb(render3d_st: mut Render3dState) -> pointer { # the frame in flight uses the other half of the ring and the other pool, unless it has this slot if render3d_st.gvk_frame_pending and (render3d_st.gvk_frame_pending_no & 1) == (render3d_st.gvk_frame_no & 1) { gvk_frame_wait(render3d_st) } gvk_frame_reset(render3d_st) + gvk_prime_submit(render3d_st) render3d_st.gvk_cb = gvk_once_begin(render3d_st) - gvk_prime_flush(render3d_st, render3d_st.gvk_cb) } return render3d_st.gvk_cb } diff --git a/packages/ludic.render3d/gpu_vk_res.ludic b/packages/ludic.render3d/gpu_vk_res.ludic index bdcc6b3f..7eab6624 100644 --- a/packages/ludic.render3d/gpu_vk_res.ludic +++ b/packages/ludic.render3d/gpu_vk_res.ludic @@ -775,6 +775,14 @@ function gvk_prime(render3d_st: mut Render3dState, b: int, buf: long) -> void { render3d_st.gvk_prime_h[render3d_st.gvk_prime_n] = buf render3d_st.gvk_prime_n += 1 } +# In a command buffer of their own, submitted and waited for at once: recorded into the frame's, a +# buffer released later in that frame (a guest loading its models) was destroyed before the copy ran. +function gvk_prime_submit(render3d_st: mut Render3dState) -> void { + if render3d_st.gvk_prime_n == 0 { return } + let cb = gvk_once_begin(render3d_st) + gvk_prime_flush(render3d_st, cb) + gvk_once_end(render3d_st, cb) +} function gvk_prime_flush(render3d_st: mut Render3dState, cb: pointer) -> void { if render3d_st.gvk_prime_n == 0 { return } if render3d_st.gvk_prime_dst == 0 and not gvk_prime_target(render3d_st) {