From 5d8cbac06a73cb932089cfd0414d01872113be5a Mon Sep 17 00:00:00 2001 From: Orkuncakilkaya Date: Tue, 29 Sep 2026 13:56:02 +0300 Subject: [PATCH] render3d/vk: a swapchain rebuild gives back its scratch and the old image list gvk_swap_make freed none of what it made - the surface caps, the count, the format and present-mode lists, the create info, the handle out - and replaced gvk_swap_images without freeing it: a few hundred bytes every time the window changed size, went to or from Retina, or the chain came back suboptimal. Each is now freed on every way out (the minimised return and the failed create included), and the old image list before the new one is made. A windowed memory walk built against it (main 5ee600d2, 300 s, the autopilot opening screens every 6 s): footprint 2816 MB at 120 s, 2818 MB at 300 s; the fence judged 0 frames kept. Co-Authored-By: Claude Opus 5.5 --- packages/ludic.render3d/gpu_vk_draw.ludic | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/ludic.render3d/gpu_vk_draw.ludic b/packages/ludic.render3d/gpu_vk_draw.ludic index 6d51aa22..18fdf054 100644 --- a/packages/ludic.render3d/gpu_vk_draw.ludic +++ b/packages/ludic.render3d/gpu_vk_draw.ludic @@ -1755,7 +1755,10 @@ function gvk_swap_make(render3d_st: mut Render3dState, w: int, h: int) -> bool { var ew = Vk.get_i32(caps, VkSurfaceCapabilitiesKHR_currentExtent + VkExtent2D_width) var eh = Vk.get_i32(caps, VkSurfaceCapabilitiesKHR_currentExtent + VkExtent2D_height) if ew == -1 or ew <= 0 { ew = w; eh = h } - if ew <= 0 or eh <= 0 { return false } # minimised: keep the old chain until it has a size + if ew <= 0 or eh <= 0 { # minimised: keep the old chain until it has a size + free(caps) + return false + } let cnt = bytes(4) Vk.put_i32(cnt, 0, 0) Vk.get_physical_device_surface_formats_khr(render3d_st.gvk_pd, render3d_st.gvk_surface, cnt, null) @@ -1782,6 +1785,7 @@ function gvk_swap_make(render3d_st: mut Render3dState, w: int, h: int) -> bool { # the screen image carries the swapchain's depth of colour: remade when HDR comes or goes if hdr != render3d_st.gvk_hdr_on { render3d_st.gvk_hdr_on = hdr; gvk_screen_make(render3d_st, render3d_st.gvk_screen_w, render3d_st.gvk_screen_h) } Vk.put_i32(cnt, 0, 0) + free(fmts) Vk.get_physical_device_surface_present_modes_khr(render3d_st.gvk_pd, render3d_st.gvk_surface, cnt, null) let nm = Vk.get_i32(cnt, 0) let modes = bytes(nm * 4 + 8) @@ -1792,6 +1796,7 @@ function gvk_swap_make(render3d_st: mut Render3dState, w: int, h: int) -> bool { for i in 0 .. nm { if Vk.get_i32(modes, i * 4) == VK_PRESENT_MODE_IMMEDIATE_KHR { mode = VK_PRESENT_MODE_IMMEDIATE_KHR } } for i in 0 .. nm { if Vk.get_i32(modes, i * 4) == VK_PRESENT_MODE_MAILBOX_KHR { mode = VK_PRESENT_MODE_MAILBOX_KHR } } } + free(modes) var n = Vk.get_i32(caps, VkSurfaceCapabilitiesKHR_minImageCount) + 1 let mx = Vk.get_i32(caps, VkSurfaceCapabilitiesKHR_maxImageCount) if mx > 0 and n > mx { n = mx } @@ -1814,15 +1819,25 @@ function gvk_swap_make(render3d_st: mut Render3dState, w: int, h: int) -> bool { Vk.put_i64(sci, VkSwapchainCreateInfoKHR_oldSwapchain, render3d_st.gvk_swap) let out = bytes(8) let r = Vk.create_swapchain_khr(render3d_st.gvk_dev, sci, render3d_st.gvk_ac, out) - if r != VK_SUCCESS { return gvk_fail(render3d_st, "vkCreateSwapchainKHR", r) } + # the create's scratch goes on every way out: a resize once left six buffers and the image list behind + free(sci) + free(caps) + if r != VK_SUCCESS { + free(out) + free(cnt) + return gvk_fail(render3d_st, "vkCreateSwapchainKHR", r) + } if render3d_st.gvk_swap != 0 { Vk.destroy_swapchain_khr(render3d_st.gvk_dev, render3d_st.gvk_swap, render3d_st.gvk_ac) } render3d_st.gvk_swap = gvk_handle(out) + free(out) if render3d_st.gvk_hdr_on and render3d_st.gvk_has_hdr_meta { gvk_hdr_metadata(render3d_st) } Vk.put_i32(cnt, 0, 0) Vk.get_swapchain_images_khr(render3d_st.gvk_dev, render3d_st.gvk_swap, cnt, null) render3d_st.gvk_swap_n = Vk.get_i32(cnt, 0) + if render3d_st.gvk_swap_images != null { free(render3d_st.gvk_swap_images) } render3d_st.gvk_swap_images = bytes(render3d_st.gvk_swap_n * 8 + 8) Vk.get_swapchain_images_khr(render3d_st.gvk_dev, render3d_st.gvk_swap, cnt, render3d_st.gvk_swap_images) + free(cnt) render3d_st.gvk_swap_fmt = fmt render3d_st.gvk_swap_w = ew render3d_st.gvk_swap_h = eh