From 70a92a88625f6ff1ec48d1d2636d894a0d407008 Mon Sep 17 00:00:00 2001 From: Orkuncakilkaya Date: Sun, 27 Sep 2026 23:07:28 +0300 Subject: [PATCH] fix(render3d): a refused pipeline is not retried every draw, and an evicted chunk is freed whole gvk_pipeline_fast cached only pipelines it made, so one the driver refused was attempted again on every draw, each attempt building its key string and create structs anew; the refusal is cached too (a Vulkan program's variants are fixed when it is made, so it would fail the same way). stream_evict made a new chunk list per eviction and never freed the evicted chunks' records; it compacts the list in place and frees each evicted chunk with its data. Co-Authored-By: Claude Opus 5.5 --- packages/ludic.render3d/gpu_vk_draw.ludic | 3 ++- packages/ludic.render3d/stream.ludic | 18 +++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/packages/ludic.render3d/gpu_vk_draw.ludic b/packages/ludic.render3d/gpu_vk_draw.ludic index 5efb7f12..c8a5b294 100644 --- a/packages/ludic.render3d/gpu_vk_draw.ludic +++ b/packages/ludic.render3d/gpu_vk_draw.ludic @@ -1903,8 +1903,9 @@ function gvk_pipeline_fast(render3d_st: mut Render3dState, p: int, m: Mesh, st: return render3d_st.gvk_pc_pipe[k] } } + # a pipeline the driver refused is remembered as 0 too: retried on every draw, each try built its + # key and its create structs again and gave none of them back let pipe = gvk_pipeline(render3d_st, p, m, st, n_color, color_fmt, depth_fmt, samples) - if pipe == 0 { return pipe } push(render3d_st.gvk_pc_prog, p); push(render3d_st.gvk_pc_layout, layout); push(render3d_st.gvk_pc_state, state) push(render3d_st.gvk_pc_bias, bias); push(render3d_st.gvk_pc_pass, pass); push(render3d_st.gvk_pc_pipe, pipe) if p < 4096 { render3d_st.gvk_pc_last[p] = len(render3d_st.gvk_pc_prog) - 1 } diff --git a/packages/ludic.render3d/stream.ludic b/packages/ludic.render3d/stream.ludic index d5bde33a..754c9c4c 100644 --- a/packages/ludic.render3d/stream.ludic +++ b/packages/ludic.render3d/stream.ludic @@ -162,16 +162,24 @@ function stream_evict(render3d_st: mut Render3dState, s: Stream) -> void { } t = lo # everything wanted by the walk in progress stays whatever the threshold says - let kept = new []Chunk + # compacted in place, and an evicted chunk goes whole: a new list per eviction and the chunks' + # own records were never given back + var w = 0 var i = 0 while i < s.n { let c = s.chunks[i] - if c.used >= t or c.used == render3d_st.stream_walk_no { push(kept, c) } - else { if c.data != null { free(c.data) } } + if c.used >= t or c.used == render3d_st.stream_walk_no { + s.chunks[w] = c + w += 1 + } else { + if c.data != null { free(c.data) } + free(c) + } i += 1 } - s.chunks = kept - s.n = len(kept) + let ch = s.chunks + while len(ch) > w { List.pop(ch) } + s.n = w for h in 0 .. STREAM_HASH { s.htab[h] = 0 } i = 0 while i < s.n { s.keys[i] = s.chunks[i].key; stream_remember(s, s.chunks[i].key, i); i += 1 }