From 737d6e18593db07de28e0c0940e3353a34ead735 Mon Sep 17 00:00:00 2001 From: Orkuncakilkaya Date: Wed, 30 Sep 2026 17:18:48 +0300 Subject: [PATCH] wip: uncommitted work gathered before the branch is merged into main Co-Authored-By: Claude Opus 5.5 --- examples/rendering/smooth.ludic | 4 +- packages/ludic.render3d/env.ludic | 25 +++- packages/ludic.render3d/gpu.ludic | 2 +- packages/ludic.render3d/gpu_vk.ludic | 47 +++++-- packages/ludic.render3d/gpu_vk_draw.ludic | 118 +++++++++++------- packages/ludic.render3d/gpu_vk_res.ludic | 93 ++++++++++---- packages/ludic.render3d/grass_gpu.ludic | 19 +-- packages/ludic.render3d/shaders/grass.vert | 2 +- .../ludic.render3d/shaders/grass_cull.comp | 11 +- packages/ludic.render3d/shaders/model.frag | 4 +- .../shaders/spv/1f95f31b.frag.spv | Bin 90372 -> 92292 bytes .../shaders/spv/2e2908c8.frag.spv | Bin 92272 -> 94192 bytes .../shaders/spv/5e4589a1.frag.spv | Bin 105036 -> 106956 bytes .../shaders/spv/a0f2dbac.frag.spv | Bin 105036 -> 106956 bytes .../shaders/spv/b20fdcb5.frag.spv | Bin 48492 -> 48524 bytes .../shaders/spv/b8cff226.frag.spv | Bin 48492 -> 48524 bytes .../shaders/spv/b8cff226.vert.spv | Bin 33844 -> 33812 bytes .../shaders/spv/cb13f3c6.frag.spv | Bin 48492 -> 48524 bytes .../shaders/spv/cb13f3c6.vert.spv | Bin 33512 -> 33480 bytes .../shaders/spv/d48d6a48.frag.spv | Bin 53292 -> 53324 bytes .../shaders/spv/f2952361.frag.spv | Bin 106904 -> 108824 bytes .../shaders/spv/f4382918.frag.spv | Bin 90372 -> 92292 bytes .../shaders/spv/f4a2b586.frag.spv | Bin 53292 -> 53324 bytes .../shaders/spv/grass_cull.comp.spv | Bin 24092 -> 25236 bytes .../ludic.render3d/shaders/spv/manifest.txt | 54 ++++---- packages/ludic.render3d/shaders/terrain.frag | 13 ++ packages/ludic.render3d/terrain.ludic | 3 + runtime/native/vk.ludic | 3 + runtime/native/vk_mac.ll | 23 ++++ runtime/native/vk_win.ll | 6 + 30 files changed, 307 insertions(+), 120 deletions(-) diff --git a/examples/rendering/smooth.ludic b/examples/rendering/smooth.ludic index 97449be4..a6204913 100644 --- a/examples/rendering/smooth.ludic +++ b/examples/rendering/smooth.ludic @@ -192,7 +192,7 @@ program Smooth { handler Draw(render3d_st: mut Render3dState, smooth_st: mut SmoothState) phase Render { r3d_frame(render3d_st, float(Time.elapsed())) smooth_st.frame += 1 - if smooth_st.frame == smooth_st.shot_at { Gl.screenshot(path: "build/smooth.ppm") } - Gl.swap() + if smooth_st.frame == smooth_st.shot_at { r3d_screenshot(render3d_st, "build/smooth.ppm") } + r3d_present(render3d_st) } } diff --git a/packages/ludic.render3d/env.ludic b/packages/ludic.render3d/env.ludic index fc07c7f6..915b3520 100644 --- a/packages/ludic.render3d/env.ludic +++ b/packages/ludic.render3d/env.ludic @@ -189,6 +189,9 @@ export state Render3dState { gvk_pool: long = 0 gvk_fence: bytes = null gvk_frame_fence: bytes = null # the presented frame still on the GPU (one frame in flight) + gvk_tmp_buf: pointer = null # the per-call scratch ring (gvk_tmp) + gvk_tmp_off: int = 0 + gvk_seen: words = null # gvk_draw's vertex buffers already bound, reused gvk_frame_pending: bool = false gvk_frame_pending_cb: pointer = null gvk_frame_pending_no: int = -1 # which frame it is (gvk_frame_no when it was submitted) @@ -197,6 +200,20 @@ export state Render3dState { 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_inflight: int = -1 + gvk_nopool: bool = false + # R3D_VK_PROF: Vulkan objects made and destroyed, reported every 120 frames + gvk_mk_img: int = 0 + gvk_mk_view: int = 0 + gvk_mk_buf: int = 0 + gvk_mk_mem: int = 0 + gvk_mk_smp: int = 0 + gvk_mk_set: int = 0 + gvk_mk_cmd: int = 0 + gvk_mk_dpool: int = 0 + gvk_mk_x_img: int = 0 + gvk_mk_x_view: int = 0 + gvk_mk_x_buf: int = 0 + gvk_mk_x_mem: int = 0 gvk_label_depth: int = 0 # -1 until asked: 1 leaves the presented frame in flight gvk_has_hqr: bool = false gvk_ts_period: float = 0.0 # float bits: nanoseconds per timestamp tick @@ -269,8 +286,9 @@ export state Render3dState { gvk_screen_depth: int = 0 gvk_screen_w: int = 0 gvk_screen_h: int = 0 - gvk_layer_views: []string = null # "tex:layer" -> index into gvk_layer_view + gvk_layer_views: []int = null # generation * 4096 + layer, beside gvk_layer_view_tex gvk_layer_view: []long = null + gvk_layer_view_tex: []int = null # the texture each cached view is of, so a release drops them gvk_fb_ncolor: words = null # per framebuffer: colour slots drawn (draw buffers; 0 = none) gvk_hdr_want: bool = false # r3d_hdr: the setting gvk_hdr_on: bool = false # the swapchain is HDR10 now @@ -377,6 +395,11 @@ export state Render3dState { gg_mesh: []Mesh = null gg_n: int = 0 gg_max: int = 0 + gg_rb: words = null # the per-frame dispatch arguments, made once (grass_gpu.ludic) + gg_cb: words = null + gg_bufs: words = null + gg_texs: words = null + gg_pr: words = null grass_band_mesh: []Mesh = null # per band: the blade it draws (rows fall with distance) grass_mesh3: Mesh = null # three rows: the middle distance grass_mesh2: Mesh = null # two rows, one quad: far enough that the arch is under a pixel diff --git a/packages/ludic.render3d/gpu.ludic b/packages/ludic.render3d/gpu.ludic index 49fea526..947b930a 100644 --- a/packages/ludic.render3d/gpu.ludic +++ b/packages/ludic.render3d/gpu.ludic @@ -201,7 +201,7 @@ function gpu_query_new(render3d_st: mut Render3dState, n: int, ids: words) -> vo function gpu_query_begin(render3d_st: mut Render3dState, id: int) -> void { if render3d_st.gpu_kind == GPU_VK { gvk_query_begin(render3d_st, id); return }; gl_begin_query(GL_TIME_ELAPSED, id) } function gpu_query_end(render3d_st: mut Render3dState) -> void { if render3d_st.gpu_kind == GPU_VK { gvk_query_end(render3d_st); return }; gl_end_query(GL_TIME_ELAPSED) } # true once the query has its result; the nanoseconds (low 32 bits) are then in out[0] -function gpu_query_result(render3d_st: Render3dState, id: int, out: words) -> bool { +function gpu_query_result(render3d_st: mut Render3dState, id: int, out: words) -> bool { if render3d_st.gpu_kind == GPU_VK { return gvk_query_result(render3d_st, id, out) } gl_get_query_objectiv(id, GL_QUERY_RESULT_AVAILABLE, out) if out[0] == 0 { return false } diff --git a/packages/ludic.render3d/gpu_vk.ludic b/packages/ludic.render3d/gpu_vk.ludic index 87f2b6d5..741869fe 100644 --- a/packages/ludic.render3d/gpu_vk.ludic +++ b/packages/ludic.render3d/gpu_vk.ludic @@ -298,6 +298,7 @@ function gvk_alloc(render3d_st: mut Render3dState, req: bytes, want: int) -> lon Vk.put_i64(mai, VkMemoryAllocateInfo_allocationSize, Vk.get_i64(req, VkMemoryRequirements_size)) Vk.put_i32(mai, VkMemoryAllocateInfo_memoryTypeIndex, t) let out = bytes(8) + render3d_st.gvk_mk_mem += 1 let r = Vk.allocate_memory(render3d_st.gvk_dev, mai, null, out) if r != VK_SUCCESS { print(`r3d: vulkan: vkAllocateMemory failed (VkResult {r})`); return zero } render3d_st.gvk_n_allocs += 1 @@ -338,6 +339,7 @@ function gvk_mem_raw(render3d_st: mut Render3dState, t: int, size: int, host: bo Vk.put_i64(mai, VkMemoryAllocateInfo_allocationSize, size_l) Vk.put_i32(mai, VkMemoryAllocateInfo_memoryTypeIndex, t) let out = bytes(8) + render3d_st.gvk_mk_mem += 1 let r = Vk.allocate_memory(render3d_st.gvk_dev, mai, null, out) if r != VK_SUCCESS { gvk_note(render3d_st, `r3d: vulkan: vkAllocateMemory of {size} bytes failed (VkResult {r}, {render3d_st.gvk_n_allocs} allocations live)`); return zero } render3d_st.gvk_n_allocs += 1 @@ -447,6 +449,7 @@ function gvk_mem_free(render3d_st: mut Render3dState, a: int) -> void { let zero: long = 0 if b < 0 { if render3d_st.gvk_al_map[a] != null { Vk.unmap_memory(render3d_st.gvk_dev, render3d_st.gvk_al_mem[a]) } + render3d_st.gvk_mk_x_mem += 1 Vk.free_memory(render3d_st.gvk_dev, render3d_st.gvk_al_mem[a], null) render3d_st.gvk_n_allocs -= 1 } else { @@ -466,6 +469,7 @@ function gvk_mem_free(render3d_st: mut Render3dState, a: int) -> void { if off == 0 and n == render3d_st.gvk_blk_size[b] { # the block is empty again: give it back, or a world swapped out keeps its memory for good if render3d_st.gvk_blk_map[b] != null { Vk.unmap_memory(render3d_st.gvk_dev, render3d_st.gvk_blk_mem[b]) } + render3d_st.gvk_mk_x_mem += 1 Vk.free_memory(render3d_st.gvk_dev, render3d_st.gvk_blk_mem[b], null) render3d_st.gvk_n_allocs -= 1 render3d_st.gvk_blk_mem[b] = zero; render3d_st.gvk_blk_map[b] = null; render3d_st.gvk_blk_kind[b] = -1; render3d_st.gvk_blk_size[b] = 0 @@ -503,17 +507,35 @@ function gvk_cmd_init(render3d_st: mut Render3dState) -> bool { return true } # a command buffer, begun -function gvk_once_begin(render3d_st: Render3dState) -> pointer { - let cbai = bytes(VkCommandBufferAllocateInfo_sizeof) +# ---- scratch ------------------------------------------------------------------------------- +# The structs a Vulkan call reads are copied before it returns, so the ones built for a draw, a +# pass, a barrier or a submit need not outlive it. They were each a bytes() - a malloc nothing +# freed, several per draw: about 0.5 MB a frame, and one windowed run grew past 150 GB. They +# come from this ring instead: one block, handed out in order and wrapped when it is used up. +const GVK_TMP_BYTES: int = 1048576 + +function gvk_tmp(render3d_st: mut Render3dState, n: int) -> pointer { + if render3d_st.gvk_tmp_buf == null { render3d_st.gvk_tmp_buf = bytes(GVK_TMP_BYTES) } + let sz = (n + 15) / 16 * 16 + if sz > GVK_TMP_BYTES { print(`r3d: vulkan: {n} bytes of scratch asked for at once`); return null } + if render3d_st.gvk_tmp_off + sz > GVK_TMP_BYTES { render3d_st.gvk_tmp_off = 0 } + let p = mem_off(render3d_st.gvk_tmp_buf, render3d_st.gvk_tmp_off) + render3d_st.gvk_tmp_off += sz + return p +} + +function gvk_once_begin(render3d_st: mut Render3dState) -> pointer { + let cbai = gvk_tmp(render3d_st, VkCommandBufferAllocateInfo_sizeof) Vk.zero(cbai, VkCommandBufferAllocateInfo_sizeof) Vk.put_i32(cbai, VkCommandBufferAllocateInfo_sType, VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO) Vk.put_i64(cbai, VkCommandBufferAllocateInfo_commandPool, render3d_st.gvk_pool) Vk.put_i32(cbai, VkCommandBufferAllocateInfo_level, VK_COMMAND_BUFFER_LEVEL_PRIMARY) Vk.put_i32(cbai, VkCommandBufferAllocateInfo_commandBufferCount, 1) - let cbs = bytes(8) + let cbs = gvk_tmp(render3d_st, 8) + render3d_st.gvk_mk_cmd += 1 if Vk.allocate_command_buffers(render3d_st.gvk_dev, cbai, cbs) != VK_SUCCESS { return null } let cb = Vk.get_ptr(cbs, 0) - let cbbi = bytes(VkCommandBufferBeginInfo_sizeof) + let cbbi = gvk_tmp(render3d_st, VkCommandBufferBeginInfo_sizeof) Vk.zero(cbbi, VkCommandBufferBeginInfo_sizeof) Vk.put_i32(cbbi, VkCommandBufferBeginInfo_sType, VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO) Vk.put_i32(cbbi, VkCommandBufferBeginInfo_flags, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT) @@ -525,10 +547,10 @@ function gvk_once_end(render3d_st: mut Render3dState, cb: pointer) -> bool { gvk_frame_wait(render3d_st) var r = Vk.end_command_buffer(cb) if r != VK_SUCCESS { return gvk_fail(render3d_st, "vkEndCommandBuffer", r) } - let cbs = bytes(8) + let cbs = gvk_tmp(render3d_st, 8) Vk.put_ptr(cbs, 0, cb) Vk.reset_fences(render3d_st.gvk_dev, 1, render3d_st.gvk_fence) - let si = bytes(VkSubmitInfo_sizeof) + let si = gvk_tmp(render3d_st, VkSubmitInfo_sizeof) Vk.zero(si, VkSubmitInfo_sizeof) Vk.put_i32(si, VkSubmitInfo_sType, VK_STRUCTURE_TYPE_SUBMIT_INFO) Vk.put_i32(si, VkSubmitInfo_commandBufferCount, 1) @@ -556,6 +578,7 @@ function gvk_inflight_on(render3d_st: mut Render3dState) -> bool { if render3d_st.gvk_inflight < 0 { render3d_st.gvk_inflight = 0 if Os.platform() == "macos" { render3d_st.gvk_inflight = 1 } + render3d_st.gvk_nopool = r3d_env_has(render3d_st, "R3D_VK_NOPOOL") if r3d_env_has(render3d_st, "R3D_VK_INFLIGHT") { render3d_st.gvk_inflight = Text.to_int(r3d_env(render3d_st, "R3D_VK_INFLIGHT")) } } return render3d_st.gvk_inflight == 1 @@ -565,10 +588,10 @@ function gvk_frame_submit(render3d_st: mut Render3dState, cb: pointer) -> bool { gvk_frame_wait(render3d_st) var r = Vk.end_command_buffer(cb) if r != VK_SUCCESS { return gvk_fail(render3d_st, "vkEndCommandBuffer", r) } - let cbs = bytes(8) + let cbs = gvk_tmp(render3d_st, 8) Vk.put_ptr(cbs, 0, cb) Vk.reset_fences(render3d_st.gvk_dev, 1, render3d_st.gvk_frame_fence) - let si = bytes(VkSubmitInfo_sizeof) + let si = gvk_tmp(render3d_st, VkSubmitInfo_sizeof) Vk.zero(si, VkSubmitInfo_sizeof) Vk.put_i32(si, VkSubmitInfo_sType, VK_STRUCTURE_TYPE_SUBMIT_INFO) Vk.put_i32(si, VkSubmitInfo_commandBufferCount, 1) @@ -585,7 +608,7 @@ function gvk_frame_wait(render3d_st: mut Render3dState) -> void { if not render3d_st.gvk_frame_pending { return } let forever: long = -1 Vk.wait_for_fences(render3d_st.gvk_dev, 1, render3d_st.gvk_frame_fence, 1, forever) - let cbs = bytes(8) + let cbs = gvk_tmp(render3d_st, 8) Vk.put_ptr(cbs, 0, render3d_st.gvk_frame_pending_cb) Vk.free_command_buffers(render3d_st.gvk_dev, render3d_st.gvk_pool, 1, cbs) render3d_st.gvk_frame_pending = false @@ -636,9 +659,9 @@ function gvk_query_end(render3d_st: mut Render3dState) -> void { Vk.cmd_write_timestamp(gvk_frame_cb(render3d_st), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, render3d_st.gvk_qpool, render3d_st.gvk_q_active * 2 + 1) render3d_st.gvk_q_active = -1 } -function gvk_query_result(render3d_st: Render3dState, id: int, out: words) -> bool { +function gvk_query_result(render3d_st: mut Render3dState, id: int, out: words) -> bool { if render3d_st.gvk_qpool == 0 { return false } - let data = bytes(16) + let data = gvk_tmp(render3d_st, 16) let size: long = 16 let stride: long = 8 if Vk.get_query_pool_results(render3d_st.gvk_dev, render3d_st.gvk_qpool, id * 2, 2, size, data, stride, VK_QUERY_RESULT_64_BIT) != VK_SUCCESS { return false } @@ -653,7 +676,7 @@ function gvk_label_begin(render3d_st: mut Render3dState, name: pointer) -> void # only inside a frame: a pass the load profiles comes before the frame's pools exist if render3d_st.gpu_kind != GPU_VK or render3d_st.gvk_cb == null { return } let cb = render3d_st.gvk_cb - let li = bytes(VkDebugUtilsLabelEXT_sizeof) + let li = gvk_tmp(render3d_st, VkDebugUtilsLabelEXT_sizeof) Vk.zero(li, VkDebugUtilsLabelEXT_sizeof) Vk.put_i32(li, VkDebugUtilsLabelEXT_sType, VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT) Vk.put_ptr(li, VkDebugUtilsLabelEXT_pLabelName, name) diff --git a/packages/ludic.render3d/gpu_vk_draw.ludic b/packages/ludic.render3d/gpu_vk_draw.ludic index b5bb3acb..e4049ffc 100644 --- a/packages/ludic.render3d/gpu_vk_draw.ludic +++ b/packages/ludic.render3d/gpu_vk_draw.ludic @@ -195,15 +195,16 @@ function gvk_dispatch_tex(render3d_st: mut Render3dState, c: int, params: pointe let nb = render3d_st.gvk_cp_nbuf[c] var nt = 0 if render3d_st.gvk_cp_ntex != null and c < len(render3d_st.gvk_cp_ntex) { nt = render3d_st.gvk_cp_ntex[c] } - let dsai = bytes(VkDescriptorSetAllocateInfo_sizeof) + let dsai = gvk_tmp(render3d_st, VkDescriptorSetAllocateInfo_sizeof) Vk.zero(dsai, VkDescriptorSetAllocateInfo_sizeof) Vk.put_i32(dsai, VkDescriptorSetAllocateInfo_sType, VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO) Vk.put_i64(dsai, VkDescriptorSetAllocateInfo_descriptorPool, render3d_st.gvk_dpool) Vk.put_i32(dsai, VkDescriptorSetAllocateInfo_descriptorSetCount, 1) - let layouts = bytes(8) + let layouts = gvk_tmp(render3d_st, 8) Vk.put_i64(layouts, 0, render3d_st.gvk_cp_dsl[c]) Vk.put_ptr(dsai, VkDescriptorSetAllocateInfo_pSetLayouts, layouts) - let sets = bytes(8) + let sets = gvk_tmp(render3d_st, 8) + render3d_st.gvk_mk_set += 1 let r = Vk.allocate_descriptor_sets(render3d_st.gvk_dev, dsai, sets) if r != VK_SUCCESS { gvk_fail(render3d_st, "vkAllocateDescriptorSets (compute)", r); return } let set = Vk.get_i64(sets, 0) @@ -212,11 +213,11 @@ function gvk_dispatch_tex(render3d_st: mut Render3dState, c: int, params: pointe let ww = VkWriteDescriptorSet_sizeof let bw = VkDescriptorBufferInfo_sizeof let iw = VkDescriptorImageInfo_sizeof - let writes = bytes(ww * (nb + 1 + nt)) + let writes = gvk_tmp(render3d_st, ww * (nb + 1 + nt)) Vk.zero(writes, ww * (nb + 1 + nt)) - let infos = bytes(bw * (nb + 1)) + let infos = gvk_tmp(render3d_st, bw * (nb + 1)) Vk.zero(infos, bw * (nb + 1)) - let iis = bytes(iw * (nt + 1)) + let iis = gvk_tmp(render3d_st, iw * (nt + 1)) Vk.zero(iis, iw * (nt + 1)) for k in 0 .. nb + 1 { var buf = render3d_st.gvk_ring_buf @@ -254,7 +255,7 @@ function gvk_dispatch_tex(render3d_st: mut Render3dState, c: int, params: pointe Vk.cmd_bind_pipeline(cb, VK_PIPELINE_BIND_POINT_COMPUTE, render3d_st.gvk_cp_pipe[c]) Vk.cmd_bind_descriptor_sets(cb, VK_PIPELINE_BIND_POINT_COMPUTE, render3d_st.gvk_cp_layout[c], 0, 1, sets, 0, null) Vk.cmd_dispatch(cb, gx, gy, gz) - let mb = bytes(VkMemoryBarrier_sizeof) + let mb = gvk_tmp(render3d_st, VkMemoryBarrier_sizeof) Vk.zero(mb, VkMemoryBarrier_sizeof) Vk.put_i32(mb, VkMemoryBarrier_sType, VK_STRUCTURE_TYPE_MEMORY_BARRIER) Vk.put_i32(mb, VkMemoryBarrier_srcAccessMask, VK_ACCESS_SHADER_WRITE_BIT) @@ -704,6 +705,7 @@ function gvk_frame_init(render3d_st: mut Render3dState) -> bool { let out = bytes(8) render3d_st.gvk_dpools = new []long for k in 0 .. 2 { + render3d_st.gvk_mk_dpool += 1 let r = Vk.create_descriptor_pool(render3d_st.gvk_dev, dpci, null, out) if r != VK_SUCCESS { return gvk_fail(render3d_st, "vkCreateDescriptorPool", r) } push(render3d_st.gvk_dpools, gvk_handle(out)) @@ -717,6 +719,8 @@ function gvk_frame_init(render3d_st: mut Render3dState) -> bool { } function gvk_frame_reset(render3d_st: mut Render3dState) -> void { + # what MoltenVK autoreleased last frame goes back here (a no-op off macOS); R3D_VK_NOPOOL=1 for an A/B + if not render3d_st.gvk_nopool { Vk.frame_pool() } let slot = render3d_st.gvk_frame_no & 1 render3d_st.gvk_ring_off = slot * GVK_RING_BYTES render3d_st.gvk_ring_end = (slot + 1) * GVK_RING_BYTES @@ -756,6 +760,7 @@ function gvk_kpool_make(render3d_st: mut Render3dState) -> bool { Vk.put_i32(dpci, VkDescriptorPoolCreateInfo_poolSizeCount, 2) Vk.put_ptr(dpci, VkDescriptorPoolCreateInfo_pPoolSizes, sizes) let out = bytes(8) + render3d_st.gvk_mk_dpool += 1 let r = Vk.create_descriptor_pool(render3d_st.gvk_dev, dpci, null, out) if r != VK_SUCCESS { return gvk_fail(render3d_st, "vkCreateDescriptorPool (kept sets)", r) } render3d_st.gvk_kpool = gvk_handle(out) @@ -764,8 +769,13 @@ function gvk_kpool_make(render3d_st: mut Render3dState) -> bool { } function gvk_sc_clear(render3d_st: mut Render3dState) -> void { - render3d_st.gvk_sc_prog = new []int; render3d_st.gvk_sc_koff = new []int; render3d_st.gvk_sc_set = new []long; render3d_st.gvk_sc_next = new []int - render3d_st.gvk_sc_keys = new []long + # emptied, not replaced: five fresh lists each time the pool filled were never given back + if render3d_st.gvk_sc_prog == null { + render3d_st.gvk_sc_prog = new []int; render3d_st.gvk_sc_koff = new []int; render3d_st.gvk_sc_set = new []long; render3d_st.gvk_sc_next = new []int + render3d_st.gvk_sc_keys = new []long + } + List.clear(render3d_st.gvk_sc_prog); List.clear(render3d_st.gvk_sc_koff); List.clear(render3d_st.gvk_sc_set); List.clear(render3d_st.gvk_sc_next) + List.clear(render3d_st.gvk_sc_keys) if render3d_st.gvk_sc_head == null { render3d_st.gvk_sc_head = words(4096) } for i in 0 .. 4096 { render3d_st.gvk_sc_head[i] = -1 } } @@ -863,6 +873,7 @@ function gvk_sc_make(render3d_st: mut Render3dState, p: int, nt: int) -> long { Vk.put_i32(dsai, VkDescriptorSetAllocateInfo_descriptorSetCount, 1) Vk.put_i64(layouts, 0, render3d_st.gvk_prog_dsl[p]) Vk.put_ptr(dsai, VkDescriptorSetAllocateInfo_pSetLayouts, layouts) + render3d_st.gvk_mk_set += 1 r = Vk.allocate_descriptor_sets(render3d_st.gvk_dev, dsai, sets) if r == VK_SUCCESS { tries = 2 } else { gvk_kpool_reset(render3d_st); tries += 1 } } @@ -1011,9 +1022,10 @@ function gvk_frame_cb(render3d_st: mut Render3dState) -> pointer { # on its own. Keyed by the image's generation, so a replaced image never reuses a stale view. function gvk_view_of(render3d_st: mut Render3dState, tex: int, layer1: int) -> long { if layer1 == 0 and render3d_st.gvk_tex_levels[tex] <= 1 and render3d_st.gvk_tex_layers[tex] <= 1 { return render3d_st.gvk_tex_view[tex] } - let key = `{tex}:{render3d_st.gvk_tex_gen[tex]}:{layer1}` - if render3d_st.gvk_layer_views == null { render3d_st.gvk_layer_views = new []string; render3d_st.gvk_layer_view = new []long } - for i in 0 .. len(render3d_st.gvk_layer_views) { if render3d_st.gvk_layer_views[i] == key { return render3d_st.gvk_layer_view[i] } } + # keyed by numbers, not a string built on every call: the texture, its generation and the layer + let key = render3d_st.gvk_tex_gen[tex] * 4096 + layer1 + if render3d_st.gvk_layer_views == null { render3d_st.gvk_layer_views = new []int; render3d_st.gvk_layer_view = new []long; render3d_st.gvk_layer_view_tex = new []int } + for i in 0 .. len(render3d_st.gvk_layer_views) { if render3d_st.gvk_layer_view_tex[i] == tex and render3d_st.gvk_layer_views[i] == key { return render3d_st.gvk_layer_view[i] } } let depth = render3d_st.gvk_tex_vkfmt[tex] == VK_FORMAT_D32_SFLOAT let vci = bytes(VkImageViewCreateInfo_sizeof) Vk.zero(vci, VkImageViewCreateInfo_sizeof) @@ -1028,16 +1040,18 @@ function gvk_view_of(render3d_st: mut Render3dState, tex: int, layer1: int) -> l Vk.put_i32(vci, sr + VkImageSubresourceRange_layerCount, 1) let out = bytes(8) let zero: long = 0 + render3d_st.gvk_mk_view += 1 if Vk.create_image_view(render3d_st.gvk_dev, vci, null, out) != VK_SUCCESS { return zero } push(render3d_st.gvk_layer_views, key) push(render3d_st.gvk_layer_view, gvk_handle(out)) + push(render3d_st.gvk_layer_view_tex, tex) return gvk_handle(out) } # the layer range a barrier for an attachment covers: the whole image unless one layer is drawn -function gvk_att_barrier(render3d_st: Render3dState, cb: pointer, tex: int, layer1: int, depth: bool, old_layout: int, new_layout: int) -> void { +function gvk_att_barrier(render3d_st: mut Render3dState, cb: pointer, tex: int, layer1: int, depth: bool, old_layout: int, new_layout: int) -> void { # an attachment whose image was never made (no memory for it) has nothing to transition if tex <= 0 or tex >= len(render3d_st.gvk_tex_image) or render3d_st.gvk_tex_image[tex] == 0 { return } - let b = bytes(VkImageMemoryBarrier_sizeof) + let b = gvk_tmp(render3d_st, VkImageMemoryBarrier_sizeof) Vk.zero(b, VkImageMemoryBarrier_sizeof) Vk.put_i32(b, VkImageMemoryBarrier_sType, VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER) Vk.put_i32(b, VkImageMemoryBarrier_srcAccessMask, gvk_layout_access(old_layout)) @@ -1085,7 +1099,7 @@ function gvk_pass_begin(render3d_st: mut Render3dState, rec: words, rec_o: int) let cb = gvk_frame_cb(render3d_st) gvk_pass_collect(render3d_st, render3d_st.gvk_fb_cur, rec, rec_o) let aw = VkRenderingAttachmentInfo_sizeof - let catt = bytes(aw * 3) + let catt = gvk_tmp(render3d_st, aw * 3) Vk.zero(catt, aw * 3) render3d_st.gvk_pass_w = 0 render3d_st.gvk_pass_h = 0 @@ -1108,7 +1122,7 @@ function gvk_pass_begin(render3d_st: mut Render3dState, rec: words, rec_o: int) render3d_st.gvk_pass_samples = gvk_tex_samples_of(render3d_st, tex) if render3d_st.gvk_pass_w == 0 { render3d_st.gvk_pass_w = gvk_tex_w(render3d_st, tex); render3d_st.gvk_pass_h = gvk_tex_h(render3d_st, tex) } } - let datt = bytes(aw) + let datt = gvk_tmp(render3d_st, aw) Vk.zero(datt, aw) let dtex = render3d_st.gvk_pass_dep[0] if dtex > 0 { @@ -1126,7 +1140,7 @@ function gvk_pass_begin(render3d_st: mut Render3dState, rec: words, rec_o: int) if render3d_st.gvk_pass_w == 0 { render3d_st.gvk_pass_w = gvk_tex_w(render3d_st, dtex); render3d_st.gvk_pass_h = gvk_tex_h(render3d_st, dtex) } } render3d_st.gvk_clear_bits = 0 - let ri = bytes(VkRenderingInfo_sizeof) + let ri = gvk_tmp(render3d_st, VkRenderingInfo_sizeof) Vk.zero(ri, VkRenderingInfo_sizeof) Vk.put_i32(ri, VkRenderingInfo_sType, VK_STRUCTURE_TYPE_RENDERING_INFO) Vk.put_i32(ri, VkRenderingInfo_renderArea + VkRect2D_extent + VkExtent2D_width, render3d_st.gvk_pass_w) @@ -1157,7 +1171,7 @@ function gvk_clear(render3d_st: mut Render3dState, mask: int, rec: words, rec_o: if not render3d_st.gvk_in_pass { render3d_st.gvk_clear_bits = render3d_st.gvk_clear_bits | mask; return } let cb = render3d_st.gvk_cb let caw = VkClearAttachment_sizeof - let atts = bytes(caw * 4) + let atts = gvk_tmp(render3d_st, caw * 4) Vk.zero(atts, caw * 4) var n = 0 if (mask & GL_COLOR_BUFFER_BIT) != 0 { @@ -1174,7 +1188,7 @@ function gvk_clear(render3d_st: mut Render3dState, mask: int, rec: words, rec_o: n += 1 } if n == 0 { return } - let rect = bytes(VkClearRect_sizeof) + let rect = gvk_tmp(render3d_st, VkClearRect_sizeof) Vk.zero(rect, VkClearRect_sizeof) Vk.put_i32(rect, VkClearRect_rect + VkRect2D_extent + VkExtent2D_width, render3d_st.gvk_pass_w) Vk.put_i32(rect, VkClearRect_rect + VkRect2D_extent + VkExtent2D_height, render3d_st.gvk_pass_h) @@ -1187,8 +1201,8 @@ function gvk_tex_h(render3d_st: Render3dState, tex: int) -> int { return render3 # viewport and scissor for the draw; OpenGL's rows count from the bottom and so do a Vulkan # target's here (no y flip), so both pass straight through -function gvk_set_view(render3d_st: Render3dState, cb: pointer) -> void { - let vp = bytes(VkViewport_sizeof) +function gvk_set_view(render3d_st: mut Render3dState, cb: pointer) -> void { + let vp = gvk_tmp(render3d_st, VkViewport_sizeof) Vk.zero(vp, VkViewport_sizeof) Vk.put_i32(vp, VkViewport_x, float_bits(float(render3d_st.gvk_vp[0]))) Vk.put_i32(vp, VkViewport_y, float_bits(float(render3d_st.gvk_vp[1]))) @@ -1196,7 +1210,7 @@ function gvk_set_view(render3d_st: Render3dState, cb: pointer) -> void { Vk.put_i32(vp, VkViewport_height, float_bits(float(render3d_st.gvk_vp[3]))) Vk.put_i32(vp, VkViewport_maxDepth, 0x3F800000) Vk.cmd_set_viewport(cb, 0, 1, vp) - let sc = bytes(VkRect2D_sizeof) + let sc = gvk_tmp(render3d_st, VkRect2D_sizeof) Vk.zero(sc, VkRect2D_sizeof) if render3d_st.gvk_sc[0] == 1 { Vk.put_i32(sc, VkRect2D_offset + VkOffset2D_x, render3d_st.gvk_sc[1]) @@ -1238,16 +1252,17 @@ function gvk_draw(render3d_st: mut Render3dState, p: int, m: Mesh, st: GvkState, let set = gvk_draw_set(render3d_st, p, tx, tx_w, tx_cap) if set == 0 { return } if prof { render3d_st.gvk_us_set = render3d_st.gvk_us_set + (gl_now_us() - t1) } - let sets = bytes(8) + let sets = gvk_tmp(render3d_st, 8) Vk.put_i64(sets, 0, set) Vk.cmd_bind_descriptor_sets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, render3d_st.gvk_prog_layout[p], 0, 1, sets, render3d_st.gvk_set_ndyn, render3d_st.gvk_set_offs) let v = render3d_st.gvk_prog_var[p] if m != null and m.attrs != null { # the same bindings, in the same order, as gvk_pipeline gave the layout - let bufs = bytes(8 * (GPU_MAX_VBUFS + 1)) - let offs = bytes(8 * (GPU_MAX_VBUFS + 1)) + let bufs = gvk_tmp(render3d_st, 8 * (GPU_MAX_VBUFS + 1)) + let offs = gvk_tmp(render3d_st, 8 * (GPU_MAX_VBUFS + 1)) Vk.zero(offs, 8 * (GPU_MAX_VBUFS + 1)) - let seen = words(GPU_MAX_VBUFS) + if render3d_st.gvk_seen == null { render3d_st.gvk_seen = words(GPU_MAX_VBUFS) } + let seen = render3d_st.gvk_seen var nbd = 0 for i in 0 .. m.n_attrs { let o = i * GPU_ATTR_W @@ -1510,7 +1525,7 @@ function gvk_resolve(render3d_st: mut Render3dState, cb: pointer, src: int, dst: gvk_att_barrier(render3d_st, cb, src, 0, depth, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, layout) gvk_att_barrier(render3d_st, cb, dst, 0, depth, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, layout) let aw = VkRenderingAttachmentInfo_sizeof - let att = bytes(aw) + let att = gvk_tmp(render3d_st, aw) Vk.zero(att, aw) Vk.put_i32(att, VkRenderingAttachmentInfo_sType, VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO) Vk.put_i64(att, VkRenderingAttachmentInfo_imageView, gvk_view_of(render3d_st, src, 0)) @@ -1520,7 +1535,7 @@ function gvk_resolve(render3d_st: mut Render3dState, cb: pointer, src: int, dst: Vk.put_i32(att, VkRenderingAttachmentInfo_resolveImageLayout, layout) Vk.put_i32(att, VkRenderingAttachmentInfo_loadOp, VK_ATTACHMENT_LOAD_OP_LOAD) Vk.put_i32(att, VkRenderingAttachmentInfo_storeOp, VK_ATTACHMENT_STORE_OP_STORE) - let ri = bytes(VkRenderingInfo_sizeof) + let ri = gvk_tmp(render3d_st, VkRenderingInfo_sizeof) Vk.zero(ri, VkRenderingInfo_sizeof) Vk.put_i32(ri, VkRenderingInfo_sType, VK_STRUCTURE_TYPE_RENDERING_INFO) Vk.put_i32(ri, VkRenderingInfo_renderArea + VkRect2D_extent + VkExtent2D_width, w) @@ -1536,9 +1551,9 @@ function gvk_resolve(render3d_st: mut Render3dState, cb: pointer, src: int, dst: function gvk_copy(render3d_st: mut Render3dState, cb: pointer, src: int, dst: int, depth: bool, w: int, h: int) -> void { if src <= 0 or dst <= 0 or render3d_st.gvk_tex_image[src] == 0 or render3d_st.gvk_tex_image[dst] == 0 { return } if gvk_tex_samples_of(render3d_st, src) > 1 and gvk_tex_samples_of(render3d_st, dst) == 1 { gvk_resolve(render3d_st, cb, src, dst, depth, w, h); return } - gvk_barrier(cb, render3d_st.gvk_tex_image[src], depth, 0, 1, render3d_st.gvk_tex_layers[src], VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) - gvk_barrier(cb, render3d_st.gvk_tex_image[dst], depth, 0, 1, render3d_st.gvk_tex_layers[dst], VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) - let ic = bytes(VkImageCopy_sizeof) + gvk_barrier(render3d_st, cb, render3d_st.gvk_tex_image[src], depth, 0, 1, render3d_st.gvk_tex_layers[src], VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) + gvk_barrier(render3d_st, cb, render3d_st.gvk_tex_image[dst], depth, 0, 1, render3d_st.gvk_tex_layers[dst], VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) + let ic = gvk_tmp(render3d_st, VkImageCopy_sizeof) Vk.zero(ic, VkImageCopy_sizeof) var aspect = VK_IMAGE_ASPECT_COLOR_BIT if depth { aspect = VK_IMAGE_ASPECT_DEPTH_BIT } @@ -1550,8 +1565,8 @@ function gvk_copy(render3d_st: mut Render3dState, cb: pointer, src: int, dst: in Vk.put_i32(ic, VkImageCopy_extent + VkExtent3D_height, h) Vk.put_i32(ic, VkImageCopy_extent + VkExtent3D_depth, 1) Vk.cmd_copy_image(cb, render3d_st.gvk_tex_image[src], VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, render3d_st.gvk_tex_image[dst], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ic) - gvk_barrier(cb, render3d_st.gvk_tex_image[src], depth, 0, 1, render3d_st.gvk_tex_layers[src], VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) - gvk_barrier(cb, render3d_st.gvk_tex_image[dst], depth, 0, 1, render3d_st.gvk_tex_layers[dst], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + gvk_barrier(render3d_st, cb, render3d_st.gvk_tex_image[src], depth, 0, 1, render3d_st.gvk_tex_layers[src], VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + gvk_barrier(render3d_st, cb, render3d_st.gvk_tex_image[dst], depth, 0, 1, render3d_st.gvk_tex_layers[dst], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) } function gvk_blit(render3d_st: mut Render3dState, w: int, h: int, mask: int) -> void { gvk_pass_end(render3d_st) @@ -1725,20 +1740,20 @@ function gvk_present_window(render3d_st: mut Render3dState) -> void { Time.sleep_us(16000) return } - if render3d_st.gvk_swap_stale { gvk_once_end(render3d_st, cb); render3d_st.gvk_cb = null; gvk_swap_make(render3d_st, render3d_st.gvk_swap_w, render3d_st.gvk_swap_h); return } - let idx = bytes(4) + if render3d_st.gvk_swap_stale { gvk_once_end(render3d_st, cb); render3d_st.gvk_cb = null; print("r3d: vulkan: swapchain remade at acquire"); gvk_swap_make(render3d_st, render3d_st.gvk_swap_w, render3d_st.gvk_swap_h); return } + let idx = gvk_tmp(render3d_st, 4) Vk.reset_fences(render3d_st.gvk_dev, 1, render3d_st.gvk_acq_fence) let forever: long = -1 let zero: long = 0 var r = Vk.acquire_next_image_khr(render3d_st.gvk_dev, render3d_st.gvk_swap, forever, zero, Vk.get_i64(render3d_st.gvk_acq_fence, 0), idx) - if r == VK_ERROR_OUT_OF_DATE_KHR { gvk_once_end(render3d_st, cb); render3d_st.gvk_cb = null; gvk_swap_make(render3d_st, render3d_st.gvk_swap_w, render3d_st.gvk_swap_h); return } + if r == VK_ERROR_OUT_OF_DATE_KHR { gvk_once_end(render3d_st, cb); render3d_st.gvk_cb = null; print("r3d: vulkan: swapchain remade at acquire"); gvk_swap_make(render3d_st, render3d_st.gvk_swap_w, render3d_st.gvk_swap_h); return } Vk.wait_for_fences(render3d_st.gvk_dev, 1, render3d_st.gvk_acq_fence, 1, forever) let i = Vk.get_i32(idx, 0) let dst = Vk.get_i64(render3d_st.gvk_swap_images, i * 8) let src = render3d_st.gvk_tex_image[render3d_st.gvk_screen_color] - gvk_barrier(cb, src, false, 0, 1, 1, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) - gvk_barrier(cb, dst, false, 0, 1, 1, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) - let blit = bytes(VkImageBlit_sizeof) + gvk_barrier(render3d_st, cb, src, false, 0, 1, 1, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) + gvk_barrier(render3d_st, cb, dst, false, 0, 1, 1, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) + let blit = gvk_tmp(render3d_st, VkImageBlit_sizeof) Vk.zero(blit, VkImageBlit_sizeof) Vk.put_i32(blit, VkImageBlit_srcSubresource + VkImageSubresourceLayers_aspectMask, VK_IMAGE_ASPECT_COLOR_BIT) Vk.put_i32(blit, VkImageBlit_srcSubresource + VkImageSubresourceLayers_layerCount, 1) @@ -1752,14 +1767,14 @@ function gvk_present_window(render3d_st: mut Render3dState) -> void { Vk.put_i32(blit, VkImageBlit_dstOffsets + VkOffset3D_sizeof + VkOffset3D_y, render3d_st.gvk_swap_h) Vk.put_i32(blit, VkImageBlit_dstOffsets + VkOffset3D_sizeof + VkOffset3D_z, 1) Vk.cmd_blit_image(cb, src, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, blit, VK_FILTER_LINEAR) - gvk_barrier(cb, src, false, 0, 1, 1, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) - gvk_barrier(cb, dst, false, 0, 1, 1, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) + gvk_barrier(render3d_st, cb, src, false, 0, 1, 1, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + gvk_barrier(render3d_st, cb, dst, false, 0, 1, 1, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) if gvk_inflight_on(render3d_st) { gvk_frame_submit(render3d_st, cb) } else { gvk_once_end(render3d_st, cb) } render3d_st.gvk_cb = null - let pi = bytes(VkPresentInfoKHR_sizeof) + let pi = gvk_tmp(render3d_st, VkPresentInfoKHR_sizeof) Vk.zero(pi, VkPresentInfoKHR_sizeof) Vk.put_i32(pi, VkPresentInfoKHR_sType, VK_STRUCTURE_TYPE_PRESENT_INFO_KHR) - let chains = bytes(8) + let chains = gvk_tmp(render3d_st, 8) Vk.put_i64(chains, 0, render3d_st.gvk_swap) Vk.put_i32(pi, VkPresentInfoKHR_swapchainCount, 1) Vk.put_ptr(pi, VkPresentInfoKHR_pSwapchains, chains) @@ -1779,6 +1794,8 @@ function gvk_resize_check(render3d_st: mut Render3dState) -> bool { let h = render3d_st.gvk_size_buf[1] if w <= 0 or h <= 0 { return false } if w == gl_width() and h == gl_height() and not render3d_st.gvk_swap_stale { return false } + # a remake is rare (a resize); said, so one on every frame shows in the log + print(`r3d: vulkan: swapchain remade {w}x{h} (was {gl_width()}x{gl_height()}, stale {render3d_st.gvk_swap_stale})`) gvk_flush(render3d_st) gl_set_drawable(w, h) gvk_screen_make(render3d_st, w, h) @@ -1816,11 +1833,26 @@ function gvk_prof_frame(render3d_st: mut Render3dState) -> void { if render3d_st.gvk_n_asked != render3d_st.gvk_n_draws { print(`r3d: vulkan: {(render3d_st.gvk_n_asked - render3d_st.gvk_n_draws) / f} draws a frame were asked for and not made ({render3d_st.gvk_n_asked / f} asked, {render3d_st.gvk_n_draws / f} made)`) } + gvk_prof_made(render3d_st) let zero: long = 0 render3d_st.gvk_us_pipe = zero; render3d_st.gvk_us_set = zero; render3d_st.gvk_us_draw = zero render3d_st.gvk_n_draws = 0; render3d_st.gvk_n_asked = 0; render3d_st.gvk_n_flush = 0; render3d_st.gvk_prof_frames = 0; render3d_st.gvk_n_pipe_new = 0 } +# what was made and destroyed over those frames, and how long the caches are: a kind made every +# frame and never destroyed, or a cache that only grows, is a leak +function gvk_prof_made(render3d_st: mut Render3dState) -> void { + let r = render3d_st + print(`r3d: vulkan made/destroyed: images {r.gvk_mk_img}/{r.gvk_mk_x_img}, views {r.gvk_mk_view}/{r.gvk_mk_x_view}, buffers {r.gvk_mk_buf}/{r.gvk_mk_x_buf}, memory {r.gvk_mk_mem}/{r.gvk_mk_x_mem}, samplers {r.gvk_mk_smp}, sets {r.gvk_mk_set}, pools {r.gvk_mk_dpool}, cmds {r.gvk_mk_cmd}`) + print(`r3d: vulkan caches: layer views {gvk_len_i(r.gvk_layer_views)}, pipelines {gvk_len_s(r.gvk_pipe_keys)}, layouts {gvk_len_s(r.gvk_layout_keys)}, pipe cache {gvk_len_i(r.gvk_pc_prog)}, retired {gvk_len_l(r.gvk_retired_buf)}, buffers {gvk_len_l(r.gvk_buf)}, allocs {r.gvk_n_allocs}`) + render3d_st.gvk_mk_img = 0; render3d_st.gvk_mk_x_img = 0; render3d_st.gvk_mk_view = 0; render3d_st.gvk_mk_x_view = 0 + render3d_st.gvk_mk_buf = 0; render3d_st.gvk_mk_x_buf = 0; render3d_st.gvk_mk_mem = 0; render3d_st.gvk_mk_x_mem = 0 + render3d_st.gvk_mk_smp = 0; render3d_st.gvk_mk_set = 0; render3d_st.gvk_mk_dpool = 0; render3d_st.gvk_mk_cmd = 0 +} +function gvk_len_s(xs: []string) -> int { if xs == null { return 0 }; return len(xs) } +function gvk_len_i(xs: []int) -> int { if xs == null { return 0 }; return len(xs) } +function gvk_len_l(xs: []long) -> int { if xs == null { return 0 }; return len(xs) } + # ---- the pipeline cache, by integers ----------------------------------------------------------- # gvk_pipeline builds and keys pipelines by a string; a draw only needs to find one it already has. # A mesh carries an interned layout id (its recorded layout, buffers named by order), the render diff --git a/packages/ludic.render3d/gpu_vk_res.ludic b/packages/ludic.render3d/gpu_vk_res.ludic index 4d25a78a..c9919531 100644 --- a/packages/ludic.render3d/gpu_vk_res.ludic +++ b/packages/ludic.render3d/gpu_vk_res.ludic @@ -88,10 +88,10 @@ function gvk_layout_access(layout: int) -> int { return 0 } # levels [base, base + n) of every layer of an image, from one layout to another -function gvk_barrier(cb: pointer, image: long, depth: bool, base: int, n: int, layers: int, old_layout: int, new_layout: int) -> void { +function gvk_barrier(render3d_st: mut Render3dState, cb: pointer, image: long, depth: bool, base: int, n: int, layers: int, old_layout: int, new_layout: int) -> void { # an image that was never made (its memory could not be had) has nothing to transition if image == 0 { return } - let b = bytes(VkImageMemoryBarrier_sizeof) + let b = gvk_tmp(render3d_st, VkImageMemoryBarrier_sizeof) Vk.zero(b, VkImageMemoryBarrier_sizeof) Vk.put_i32(b, VkImageMemoryBarrier_sType, VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER) Vk.put_i32(b, VkImageMemoryBarrier_srcAccessMask, gvk_layout_access(old_layout)) @@ -122,6 +122,7 @@ function gvk_staging(render3d_st: mut Render3dState, n: int, usage: int) -> poin Vk.put_i32(bci, VkBufferCreateInfo_usage, usage) Vk.put_i32(bci, VkBufferCreateInfo_sharingMode, VK_SHARING_MODE_EXCLUSIVE) let out = bytes(8) + render3d_st.gvk_mk_buf += 1 if Vk.create_buffer(render3d_st.gvk_dev, bci, null, out) != VK_SUCCESS { return null } render3d_st.gvk_st_buf = gvk_handle(out) let req = bytes(VkMemoryRequirements_sizeof) @@ -136,7 +137,9 @@ function gvk_staging(render3d_st: mut Render3dState, n: int, usage: int) -> poin function gvk_staging_free(render3d_st: mut Render3dState) -> void { gvk_frame_wait(render3d_st) # the frame in flight may still read it Vk.unmap_memory(render3d_st.gvk_dev, render3d_st.gvk_st_mem) + render3d_st.gvk_mk_x_buf += 1 Vk.destroy_buffer(render3d_st.gvk_dev, render3d_st.gvk_st_buf, null) + render3d_st.gvk_mk_x_mem += 1 Vk.free_memory(render3d_st.gvk_dev, render3d_st.gvk_st_mem, null) render3d_st.gvk_n_allocs -= 1 } @@ -175,6 +178,7 @@ function gvk_tex_storage(render3d_st: mut Render3dState, tex: int, array: bool, Vk.put_i32(ici, VkImageCreateInfo_sharingMode, VK_SHARING_MODE_EXCLUSIVE) Vk.put_i32(ici, VkImageCreateInfo_initialLayout, VK_IMAGE_LAYOUT_UNDEFINED) let out = bytes(8) + render3d_st.gvk_mk_img += 1 var r = Vk.create_image(render3d_st.gvk_dev, ici, null, out) if r != VK_SUCCESS { return gvk_fail(render3d_st, `vkCreateImage {w}x{h}x{layers} format {vkfmt}`, r) } let image = gvk_handle(out) @@ -186,6 +190,7 @@ function gvk_tex_storage(render3d_st: mut Render3dState, tex: int, array: bool, # no memory for it (one allocation per resource meets the driver's allocation limit long before # the card is full): say so instead of binding a null allocation, which the driver may accept if mem == 0 { + render3d_st.gvk_mk_x_img += 1 Vk.destroy_image(render3d_st.gvk_dev, image, null) return gvk_fail(render3d_st, `no device memory for a {w}x{h}x{layers} image ({render3d_st.gvk_n_allocs} allocations live)`, VK_ERROR_OUT_OF_DEVICE_MEMORY) } @@ -201,6 +206,7 @@ function gvk_tex_storage(render3d_st: mut Render3dState, tex: int, array: bool, if depth { Vk.put_i32(vci, sr + VkImageSubresourceRange_aspectMask, VK_IMAGE_ASPECT_DEPTH_BIT) } else { Vk.put_i32(vci, sr + VkImageSubresourceRange_aspectMask, VK_IMAGE_ASPECT_COLOR_BIT) } Vk.put_i32(vci, sr + VkImageSubresourceRange_levelCount, levels) Vk.put_i32(vci, sr + VkImageSubresourceRange_layerCount, layers) + render3d_st.gvk_mk_view += 1 r = Vk.create_image_view(render3d_st.gvk_dev, vci, null, out) if r != VK_SUCCESS { return gvk_fail(render3d_st, "vkCreateImageView", r) } render3d_st.gvk_tex_image[tex] = image; render3d_st.gvk_tex_view[tex] = gvk_handle(out); render3d_st.gvk_tex_mem[tex] = mem @@ -211,7 +217,7 @@ function gvk_tex_storage(render3d_st: mut Render3dState, tex: int, array: bool, if array { render3d_st.gvk_tex_array[tex] = 1 } else { render3d_st.gvk_tex_array[tex] = 0 } # a target starts cleared-to-nothing but readable: every level in the layout samplers expect let cb = gvk_once_begin(render3d_st) - gvk_barrier(cb, image, depth, 0, levels, layers, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + gvk_barrier(render3d_st, cb, image, depth, 0, levels, layers, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) return gvk_once_end(render3d_st, cb) } @@ -248,7 +254,8 @@ function gvk_tex_upload(render3d_st: mut Render3dState, tex: int, ifmt: int, w: let n = texels * cout * bout let dst = gvk_staging(render3d_st, n, VK_BUFFER_USAGE_TRANSFER_SRC_BIT) if dst == null { print("r3d: vulkan: no staging buffer for an upload"); return false } - let buf = bytes(n) + # written straight into the mapped staging memory: a copy of its own was never freed + let buf: pointer = dst if cin == cout and bin == bout and not (bin == 2 and render3d_st.gvk_unpack_swap) { mem_copy(buf, data, n) } else { @@ -281,12 +288,11 @@ function gvk_tex_upload(render3d_st: mut Render3dState, tex: int, ifmt: int, w: } } } - mem_copy(dst, buf, n) let image = render3d_st.gvk_tex_image[tex] let depth = gvk_is_depth(ifmt) let levels = render3d_st.gvk_tex_levels[tex] let cb = gvk_once_begin(render3d_st) - gvk_barrier(cb, image, depth, 0, levels, layers, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) + gvk_barrier(render3d_st, cb, image, depth, 0, levels, layers, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) let bic = bytes(VkBufferImageCopy_sizeof) Vk.zero(bic, VkBufferImageCopy_sizeof) if depth { Vk.put_i32(bic, VkBufferImageCopy_imageSubresource + VkImageSubresourceLayers_aspectMask, VK_IMAGE_ASPECT_DEPTH_BIT) } @@ -296,7 +302,7 @@ function gvk_tex_upload(render3d_st: mut Render3dState, tex: int, ifmt: int, w: Vk.put_i32(bic, VkBufferImageCopy_imageExtent + VkExtent3D_height, h) Vk.put_i32(bic, VkBufferImageCopy_imageExtent + VkExtent3D_depth, 1) Vk.cmd_copy_buffer_to_image(cb, render3d_st.gvk_st_buf, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, bic) - gvk_barrier(cb, image, depth, 0, levels, layers, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + gvk_barrier(render3d_st, cb, image, depth, 0, levels, layers, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) let ok = gvk_once_end(render3d_st, cb) gvk_staging_free(render3d_st) return ok @@ -315,8 +321,8 @@ function gvk_tex_grow_mips(render3d_st: mut Render3dState, tex: int, w: int, h: render3d_st.gvk_tex_image[tex] = zero if not gvk_tex_storage(render3d_st, tex, render3d_st.gvk_tex_array[tex] == 1, render3d_st.gvk_tex_glfmt[tex], w, h, layers, true) { return false } let cb = gvk_once_begin(render3d_st) - gvk_barrier(cb, old_image, false, 0, 1, layers, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) - gvk_barrier(cb, render3d_st.gvk_tex_image[tex], false, 0, 1, layers, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) + gvk_barrier(render3d_st, cb, old_image, false, 0, 1, layers, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) + gvk_barrier(render3d_st, cb, render3d_st.gvk_tex_image[tex], false, 0, 1, layers, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) let ic = bytes(VkImageCopy_sizeof) Vk.zero(ic, VkImageCopy_sizeof) Vk.put_i32(ic, VkImageCopy_srcSubresource + VkImageSubresourceLayers_aspectMask, VK_IMAGE_ASPECT_COLOR_BIT) @@ -327,9 +333,11 @@ function gvk_tex_grow_mips(render3d_st: mut Render3dState, tex: int, w: int, h: Vk.put_i32(ic, VkImageCopy_extent + VkExtent3D_height, h) Vk.put_i32(ic, VkImageCopy_extent + VkExtent3D_depth, 1) Vk.cmd_copy_image(cb, old_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, render3d_st.gvk_tex_image[tex], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ic) - gvk_barrier(cb, render3d_st.gvk_tex_image[tex], false, 0, 1, layers, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + gvk_barrier(render3d_st, cb, render3d_st.gvk_tex_image[tex], false, 0, 1, layers, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) let ok = gvk_once_end(render3d_st, cb) + render3d_st.gvk_mk_x_view += 1 Vk.destroy_image_view(render3d_st.gvk_dev, old_view, null) + render3d_st.gvk_mk_x_img += 1 Vk.destroy_image(render3d_st.gvk_dev, old_image, null) gvk_mem_free(render3d_st, gvk_mem_id(old_mem)) return ok @@ -346,7 +354,7 @@ function gvk_tex_mips(render3d_st: mut Render3dState, tex: int, w: int, h: int) return gvk_once_end(render3d_st, cb) } # the chain's blits and barriers recorded into cb (the frame's own, when one is open) -function gvk_tex_mips_into(render3d_st: Render3dState, cb: pointer, tex: int, w: int, h: int) -> void { +function gvk_tex_mips_into(render3d_st: mut Render3dState, cb: pointer, tex: int, w: int, h: int) -> void { let levels = render3d_st.gvk_tex_levels[tex] if levels <= 1 { return } let image = render3d_st.gvk_tex_image[tex] @@ -358,8 +366,8 @@ function gvk_tex_mips_into(render3d_st: Render3dState, cb: pointer, tex: int, w: if dw < 1 { dw = 1 } var dh = sh / 2 if dh < 1 { dh = 1 } - gvk_barrier(cb, image, false, lv - 1, 1, layers, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) - gvk_barrier(cb, image, false, lv, 1, layers, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) + gvk_barrier(render3d_st, cb, image, false, lv - 1, 1, layers, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) + gvk_barrier(render3d_st, cb, image, false, lv, 1, layers, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) let blit = bytes(VkImageBlit_sizeof) Vk.zero(blit, VkImageBlit_sizeof) Vk.put_i32(blit, VkImageBlit_srcSubresource + VkImageSubresourceLayers_aspectMask, VK_IMAGE_ASPECT_COLOR_BIT) @@ -375,8 +383,8 @@ function gvk_tex_mips_into(render3d_st: Render3dState, cb: pointer, tex: int, w: Vk.put_i32(blit, VkImageBlit_dstOffsets + VkOffset3D_sizeof + VkOffset3D_y, dh) Vk.put_i32(blit, VkImageBlit_dstOffsets + VkOffset3D_sizeof + VkOffset3D_z, 1) Vk.cmd_blit_image(cb, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, blit, VK_FILTER_LINEAR) - gvk_barrier(cb, image, false, lv - 1, 1, layers, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) - gvk_barrier(cb, image, false, lv, 1, layers, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + gvk_barrier(render3d_st, cb, image, false, lv - 1, 1, layers, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + gvk_barrier(render3d_st, cb, image, false, lv, 1, layers, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) sw = dw sh = dh } @@ -400,7 +408,7 @@ function gvk_tex_read(render3d_st: mut Render3dState, tex: int, ifmt: int, w: in let image = render3d_st.gvk_tex_image[tex] let depth = gvk_is_depth(ifmt) let cb = gvk_once_begin(render3d_st) - gvk_barrier(cb, image, depth, 0, 1, 1, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) + gvk_barrier(render3d_st, cb, image, depth, 0, 1, 1, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) let bic = bytes(VkBufferImageCopy_sizeof) Vk.zero(bic, VkBufferImageCopy_sizeof) if depth { Vk.put_i32(bic, VkBufferImageCopy_imageSubresource + VkImageSubresourceLayers_aspectMask, VK_IMAGE_ASPECT_DEPTH_BIT) } @@ -410,7 +418,7 @@ function gvk_tex_read(render3d_st: mut Render3dState, tex: int, ifmt: int, w: in Vk.put_i32(bic, VkBufferImageCopy_imageExtent + VkExtent3D_height, h) Vk.put_i32(bic, VkBufferImageCopy_imageExtent + VkExtent3D_depth, 1) Vk.cmd_copy_image_to_buffer(cb, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, render3d_st.gvk_st_buf, 1, bic) - gvk_barrier(cb, image, depth, 0, 1, 1, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + gvk_barrier(render3d_st, cb, image, depth, 0, 1, 1, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) let ok = gvk_once_end(render3d_st, cb) if ok and cwant == cout { mem_copy(out, src, n) } if ok and cwant < cout { @@ -426,14 +434,39 @@ function gvk_tex_release(render3d_st: mut Render3dState, tex: int) -> void { gvk_frame_wait(render3d_st) # the frame in flight may still read it if render3d_st.gvk_tex_image[tex] == 0 { return } let zero: long = 0 + render3d_st.gvk_mk_x_view += 1 Vk.destroy_image_view(render3d_st.gvk_dev, render3d_st.gvk_tex_view[tex], null) + render3d_st.gvk_mk_x_img += 1 Vk.destroy_image(render3d_st.gvk_dev, render3d_st.gvk_tex_image[tex], null) gvk_mem_free(render3d_st, gvk_mem_id(render3d_st.gvk_tex_mem[tex])) + gvk_layer_views_drop(render3d_st, tex) render3d_st.gvk_tex_image[tex] = zero; render3d_st.gvk_tex_view[tex] = zero; render3d_st.gvk_tex_mem[tex] = zero render3d_st.gvk_tex_levels[tex] = 0; render3d_st.gvk_tex_layers[tex] = 0; render3d_st.gvk_tex_vkfmt[tex] = 0 render3d_st.gvk_tex_gen[tex] = render3d_st.gvk_tex_gen[tex] + 1 } +# the per-level and per-layer views gvk_view_of made of a texture go with it: kept, each held its +# old Metal texture alive, so every rebuild of the screen targets left the last set's memory behind +function gvk_layer_views_drop(render3d_st: mut Render3dState, tex: int) -> void { + if render3d_st.gvk_layer_views == null { return } + let keys = render3d_st.gvk_layer_views + let views = render3d_st.gvk_layer_view + let texs = render3d_st.gvk_layer_view_tex + var w = 0 + for i in 0 .. len(keys) { + if texs[i] == tex { + render3d_st.gvk_mk_x_view += 1 + Vk.destroy_image_view(render3d_st.gvk_dev, views[i], null) + } else { + keys[w] = keys[i]; views[w] = views[i]; texs[w] = texs[i] + w += 1 + } + } + while len(keys) > w { List.pop(keys) } + while len(views) > w { List.pop(views) } + while len(texs) > w { List.pop(texs) } +} + # ---- samplers ----------------------------------------------------------------------------- # One VkSampler per distinct way of reading a texture, made the first time it is asked for. # The arguments are what the renderer set through gpu_tex_param (0 where it set nothing, which @@ -526,6 +559,7 @@ function gvk_sampler(render3d_st: mut Render3dState, min_f: int, mag_f: int, wra Vk.put_i32(sci, VkSamplerCreateInfo_borderColor, VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE) let out = bytes(8) let zero: long = 0 + render3d_st.gvk_mk_smp += 1 let r = Vk.create_sampler(render3d_st.gvk_dev, sci, null, out) if r != VK_SUCCESS { gvk_fail(render3d_st, "vkCreateSampler", r); return zero } let s = gvk_handle(out) @@ -564,6 +598,7 @@ function gvk_buf_release(render3d_st: mut Render3dState, b: int) -> void { while len(render3d_st.gvk_retired_frame) < len(render3d_st.gvk_retired_buf) - 1 { push(render3d_st.gvk_retired_frame, 0) } push(render3d_st.gvk_retired_frame, render3d_st.gvk_buf_used[b]) } else { + render3d_st.gvk_mk_x_buf += 1 Vk.destroy_buffer(render3d_st.gvk_dev, render3d_st.gvk_buf[b], null) gvk_mem_free(render3d_st, gvk_mem_id(render3d_st.gvk_buf_mem[b])) } @@ -582,22 +617,27 @@ function gvk_retire_flush(render3d_st: mut Render3dState) -> void { gvk_retire_u # the frame being recorded read stays function gvk_retire_upto(render3d_st: mut Render3dState, done: int) -> void { if render3d_st.gvk_retired_buf == null { return } - let keep_buf = new []long - let keep_mem = new []long - let keep_frame = new []int + if render3d_st.gvk_retired_frame == null { render3d_st.gvk_retired_frame = new []int } + while len(render3d_st.gvk_retired_frame) < len(render3d_st.gvk_retired_buf) { push(render3d_st.gvk_retired_frame, 0) } + # compacted in place: three fresh lists a frame were never given back + var w = 0 for i in 0 .. len(render3d_st.gvk_retired_buf) { - var f = 0 - if render3d_st.gvk_retired_frame != null and i < len(render3d_st.gvk_retired_frame) { f = render3d_st.gvk_retired_frame[i] } + let f = render3d_st.gvk_retired_frame[i] if f > done { - push(keep_buf, render3d_st.gvk_retired_buf[i]); push(keep_mem, render3d_st.gvk_retired_mem[i]); push(keep_frame, f) + render3d_st.gvk_retired_buf[w] = render3d_st.gvk_retired_buf[i]; render3d_st.gvk_retired_mem[w] = render3d_st.gvk_retired_mem[i]; render3d_st.gvk_retired_frame[w] = f + w += 1 } else { + render3d_st.gvk_mk_x_buf += 1 Vk.destroy_buffer(render3d_st.gvk_dev, render3d_st.gvk_retired_buf[i], null) gvk_mem_free(render3d_st, gvk_mem_id(render3d_st.gvk_retired_mem[i])) } } - render3d_st.gvk_retired_buf = keep_buf - render3d_st.gvk_retired_mem = keep_mem - render3d_st.gvk_retired_frame = keep_frame + let rb = render3d_st.gvk_retired_buf + let rm = render3d_st.gvk_retired_mem + let rf = render3d_st.gvk_retired_frame + while len(rb) > w { List.pop(rb) } + while len(rm) > w { List.pop(rm) } + while len(rf) > w { List.pop(rf) } } # A buffer a compute pass writes: a draw later in the same frame reads what the GPU put there, @@ -626,6 +666,7 @@ function gvk_buf_reserve(render3d_st: mut Render3dState, b: int, n: int) -> bool Vk.put_i32(bci, VkBufferCreateInfo_usage, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) Vk.put_i32(bci, VkBufferCreateInfo_sharingMode, VK_SHARING_MODE_EXCLUSIVE) let out = bytes(8) + render3d_st.gvk_mk_buf += 1 var r = Vk.create_buffer(render3d_st.gvk_dev, bci, null, out) if r != VK_SUCCESS { return gvk_fail(render3d_st, `vkCreateBuffer ({size} bytes)`, r) } let buf = gvk_handle(out) diff --git a/packages/ludic.render3d/grass_gpu.ludic b/packages/ludic.render3d/grass_gpu.ludic index 845ea613..1c200b1f 100644 --- a/packages/ludic.render3d/grass_gpu.ludic +++ b/packages/ludic.render3d/grass_gpu.ludic @@ -102,9 +102,14 @@ function gg_cull_frame(render3d_st: mut Render3dState) -> void { gg_tiles(render3d_st, 4, 0.0, 16.0) gg_tiles(render3d_st, 8, 16.0, 40.0) gg_tiles(render3d_st, 16, 40.0, render3d_st.grass_radius) - let rb = words(4) + # made once: a words() a frame is a calloc nothing frees + if render3d_st.gg_rb == null { + render3d_st.gg_rb = words(4); render3d_st.gg_cb = words(1); render3d_st.gg_bufs = words(3) + render3d_st.gg_texs = words(2); render3d_st.gg_pr = words(48) + } + let rb = render3d_st.gg_rb rb[0] = GG_BANDS; rb[1] = 0; rb[2] = 0; rb[3] = 0 - let cb = words(1) + let cb = render3d_st.gg_cb cb[0] = render3d_st.gg_cmds gpu_dispatch(render3d_st, render3d_st.gg_reset, data_of(rb), 16, cb, 1) if render3d_st.gg_n == 0 { return } @@ -112,16 +117,16 @@ function gg_cull_frame(render3d_st: mut Render3dState) -> void { let tb = render3d_st.gg_tiles_buf[render3d_st.r3d_test_frame % 2] gpu_buffer_upload(render3d_st, tb, render3d_st.gg_n * 16, data_of(render3d_st.gg_tv), GPU_DYNAMIC) let pr = gg_params(render3d_st) - let bufs = words(3) + let bufs = render3d_st.gg_bufs bufs[0] = tb; bufs[1] = render3d_st.gg_out; bufs[2] = render3d_st.gg_cmds - let texs = words(2) + let texs = render3d_st.gg_texs texs[0] = render3d_st.ter_height_tex; texs[1] = render3d_st.ter_ortho_tex gpu_dispatch_tex(render3d_st, render3d_st.gg_cull, data_of(pr), 192, bufs, texs, (render3d_st.gg_max + 63) / 64, render3d_st.gg_n) } -# grass_cull.comp's Params, std140: 12 vec4s -function gg_params(render3d_st: Render3dState) -> words { - let pr = words(48) +# grass_cull.comp's Params, std140: 12 vec4s, into the block made once in gg_cull_frame +function gg_params(render3d_st: mut Render3dState) -> words { + let pr = render3d_st.gg_pr for i in 0 .. 48 { pr[i] = 0 } if render3d_st.cam_planes != null { for i in 0 .. 16 { pr[i] = float_bits(render3d_st.cam_planes[i]) } } pr[16] = float_bits(render3d_st.cam_pos[0]); pr[17] = float_bits(render3d_st.cam_pos[1]); pr[18] = float_bits(render3d_st.cam_pos[2]); pr[19] = float_bits(render3d_st.grass_radius) diff --git a/packages/ludic.render3d/shaders/grass.vert b/packages/ludic.render3d/shaders/grass.vert index 32f75c16..456989cb 100644 --- a/packages/ludic.render3d/shaders/grass.vert +++ b/packages/ludic.render3d/shaders/grass.vert @@ -84,7 +84,7 @@ vec3 bladeField(vec2 xz, float y) { // dry and green patches a few metres across, and the tussocks' own shade of the same green float dryp = smoothstep(0.2, 0.8, gnoise(xz * 0.33 + 17.0) * 0.5 + 0.5); t *= mix(vec3(0.92, 1.0, 0.95), vec3(1.30, 1.10, 0.62), dryp); - return t * (0.72 + 0.56 * (gnoise(xz * 1.9 + 41.0) * 0.5 + 0.5)); + return t * (0.55 + 0.80 * (gnoise(xz * 1.9 + 41.0) * 0.5 + 0.5)); } void main() { diff --git a/packages/ludic.render3d/shaders/grass_cull.comp b/packages/ludic.render3d/shaders/grass_cull.comp index 4f4567ad..822abc24 100644 --- a/packages/ludic.render3d/shaders/grass_cull.comp +++ b/packages/ludic.render3d/shaders/grass_cull.comp @@ -61,7 +61,7 @@ vec3 bladeField(vec2 xz, float y) { t *= mix(vec3(0.7, 0.8, 0.55), vec3(1.1, 1.05, 0.85), patchy); float dryp = smoothstep(0.2, 0.8, gnoise(xz * 0.33 + 17.0) * 0.5 + 0.5); t *= mix(vec3(0.92, 1.0, 0.95), vec3(1.30, 1.10, 0.62), dryp); - return t * (0.72 + 0.56 * (gnoise(xz * 1.9 + 41.0) * 0.5 + 0.5)); + return t * (0.55 + 0.80 * (gnoise(xz * 1.9 + 41.0) * 0.5 + 0.5)); } void main() { @@ -118,6 +118,13 @@ void main() { uint o = (pr.base[b] + slot) * 4u; outv[o] = vec4(xz.x, h - 0.02, xz.y, seed); outv[o + 1u] = vec4(sin(ang), cos(ang), tall, bw); - outv[o + 2u] = vec4(bladeField(xz, h), 0.6 + 0.8 * h4); + // each blade its own: a hue a little toward olive or toward blue-green, and in a dry patch more + // of them gone to straw - a meadow is mixed blade by blade, not tinted as one + vec3 field = bladeField(xz, h); + float hj = bladeHash(ci, j, 7) - 0.5; + field *= vec3(1.0 + 0.30 * hj, 1.0, 1.0 - 0.30 * hj); + float dryp = smoothstep(0.2, 0.8, gnoise(xz * 0.33 + 17.0) * 0.5 + 0.5); + if (bladeHash(ci, j, 8) < 0.10 + 0.45 * dryp) field *= vec3(1.45, 1.18, 0.52); + outv[o + 2u] = vec4(field, 0.6 + 0.8 * h4); outv[o + 3u] = vec4(gn, dist); } diff --git a/packages/ludic.render3d/shaders/model.frag b/packages/ludic.render3d/shaders/model.frag index d6356ab3..e40149eb 100644 --- a/packages/ludic.render3d/shaders/model.frag +++ b/packages/ludic.render3d/shaders/model.frag @@ -142,7 +142,9 @@ void main() { // an occlusion term; grass albedo is capped near 0.5 and this was dividing it by five. // A blade is a thin thing standing in open air: shaded at the root by its neighbours, // not buried. - arm = vec3(mix(0.55, 1.0, t), 0.85, 0.0); + // the inside of a sward is its own shade: a blade's base sits among its neighbours (the old clumps + // got this from the shadow maps, at 4.5 ms; seen from above it is most of a meadow's texture) + arm = vec3(mix(0.30, 1.0, smoothstep(0.0, 0.8, t)), 0.85, 0.0); #elif defined(CARD) // thin grass is lit from either side: face the card toward the sun before shading if (dot(N, u_sun_dir) < 0.0) N = -N; diff --git a/packages/ludic.render3d/shaders/spv/1f95f31b.frag.spv b/packages/ludic.render3d/shaders/spv/1f95f31b.frag.spv index d8fef303459a7c1383d1f66d60de7c9eef5473f8..d8adce45abc27e85d3bf81aa183c048c4d9a344a 100644 GIT binary patch delta 21833 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 delta 20093 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 Xb?oH%-Fk6^Mp(h@p3ikVR>%JVGEt3n delta 21789 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 b-`M&=rJ$oB8ltD;!1n%oW`KBWrqKTb3Gv6v diff --git a/packages/ludic.render3d/shaders/spv/5e4589a1.frag.spv b/packages/ludic.render3d/shaders/spv/5e4589a1.frag.spv index af676aef88118cd549356cdba887034e90bb6bf8..4eb4d9081eba81914abcda6e1af63c1dc41e2615 100644 GIT binary patch delta 21668 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 diff --git a/packages/ludic.render3d/shaders/spv/a0f2dbac.frag.spv b/packages/ludic.render3d/shaders/spv/a0f2dbac.frag.spv index 3a316009563fccc7881315a1e0347bde5b47c6bc..dc30dcd6ec647c125e2c6969495231d2db83a59a 100644 GIT binary patch delta 21668 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 TtAF? delta 19790 zcmZ9T2b@*K`G(JBcPUC00ZSqZb_7L4MMR}2Rs_`8T^Cr2!Y;6jXf$5C#BSEOme_lV z2C?^&*rNW8(V!+8dx^bw{XfsS_qz;t`JLZ+-}n3Go0)Is%$z&-^3)?8pSrbUx0((E zJ7rlRTQTdLt$KF1z3ZOqnPuG;&pL7CP93x64q3KdTdzL-3hTFR&}a3*8)R9wpnhRv zEBB84)4pM@wKO)=&u%=daW-mQt`BOP*QfX3jdE-A{8=+*H32Ku!MT3etj2}QA`NL< zOiddvd7{tn`W0)k*Slre+|e1~YO;646H53!@nmsb_Az)t{ru*sht^LsyxJ$rw7g2S zPnT&8CBNG2GvhSOZWazDfo7H&y(*D*i(i|FMeyRK zo6_8xY=f?l`J+o@E%5|#ZMHo)ZT0xJNkjU#oi(Jk*wQ+`p_Sh0wNI7TepP(`DtZu6UB&mQ;&b2)Q<@KJ zET?Sla`u`-vIPh}pU$Daxq)E*sjrc;#|lj5>T_L(9zZ4Xw5?ujxLt*$s2|4F1e=Wool$tN2p*^l6Q)7I08r zud=_nj2Cll!H5FbgIn-8! z67LM3K7Urz402$AHQ_TB%$|KjxknpT`EOLk2QSBKvJvox`E!U>lkEg9b*v_vRD_Jl zgVtnwfm@qO6;1;;O`P4()G$9U)G9CgoeXYhF8Q4Tp5MG+#>{fVu7b~LY;Blb=2yd8 zW;RS~W|6id*TBvkZFgN zn7eX~r$zDsVE^Rq)7rUJcxrdg*1lrdE>G?5LE3p-^YZeh>gBu7AR@2K{dr!a2DLRz=wDcW z$*~jmDD*NlVSJ5FY~1$t?zEFM)0wBAdTQ&9cq8(y>Ob kc9uvZc*} z(Ckg{uwgCRhTmIowNtt3xI%v)ycK1isbiQ6SvHdLk5k^H`xo4#^H;10X4Su_a5!0f z`l+tl1s@4N^Wm?y4n74wefqlF25*ID*`;HGFUa}hW20e5!f)!3WkacFQ?8O9PtH*o zmR|AcHc{|*@X2?sQQ=3!`=9v1s0dVqTSh-^QSLbK)fbK%89j6w_!suObAG_s;P=Bn zEKa(!Qqcnlk9>9QHc`=oaGeIyYHd;QE%2~mZIz*T8@>h8zAxjXE&P52^RQGZT#X4j zYK4B4jI9nY-ZcL8O3!|UV2X6q7=Z?YKb*8>rQq7|EIX|dc%7VYHZJ@p=e+0W;Pvof z!O`Q$nyFR-Xm;Bxq#Yt&UTaRvM?CX|iR7J+X8-!#6VlEK^HEE$zJ zkHDGSm4?5Z+h5x*%CFXQy(~N6%rTWzuFfN7Z&%~)6+^LeTVbDm#Re4TwAZF2Ji;7< zPWmhPI5y`Ej4i zf}>^wP58gyu3o`Egb&Wk)MP(#{}yiLU0G=8SM~sxjgz}rsSV0TmR5p32nU?mft=N4 zEHznQ_?A3S>M{3S!TbxpO53D;2Nm74ETQU(Gu*u@xa*&LRm$C~Qtn|OL z?+L2$WiGXEvkp1_kr^UZGpG&zFL)^v#fCZ<@)1;ECtd?z$6Z~!gQpf9c?0~4Zz5?= z-bB=JJVET)Ti|+ns%{WRSS>R34%loMw1pnO3%2LGQ8;|wLsK`V4SOGKIrXltCsy=7 zh)=+nvV)a5ebf#9HBjPi^Rh_H zY$D0^k@^D702a`0Ce9|XwlCpUu201H3aoAn&jIY%*Kl<^LadDuFGqobX`^1M;hpRE9i7Iy*5M=%6VmKDL5 zaL=Dyu=c22TU5{k9F_Yx%i~Gt33l~VkD=-XR-c4k5mzdUPK?zvKM8|2Ya$~|5RZU;PKDsj$K3^2YT)MkC)J0^Spe7A%f zz|*G)Xn9Ycf?M9xr{MZ~`V?G$PoHw`(`NyXpTfZ&c+wQy26)<(a!;Cq>+eZZ$~|dH zxhGA*ZICBT@}gU&E%K--9Bhy$O~Gw|Crv5$q$%Z|GzGT;Pnv=o-;<``#`mNN-d-GA z7+$o12TS2#0S}gfTfl>*;QD*86kLA~mV)c=!BTKL;K5RG{XJMJ^U@^rU@06d;Neor zJzxrM13X{~Za@#1g4+NOn1UP61E%2l5Lfv4(p!sXP2pex&ze&1Ia6>O;5k!p8}MxV zOXGM^@Hi=z-%Pme@-!*zR^w?>aKn0<6x^_$CIz>=r%Ayr?`cwS$`|wFq!0{H!&)2M z0G=iVH$dkqzGlL0kf%xEZ#++vf*a4%q~IG;e}0^_OkgT4=fIL##rLY>o-U>3TN7@B zJz)yF+*76sccZu}72sDV+@bfFsZs#$DO1XCNVxuu{O-xeY3mW}SJ-zLWI!P}ODy2Hv1;^w+D zZ>1x$p7{&kFYw{`*(eWBJTUQ$#D-X*aV>8-H)1#6pSf&A(Nt$_4H^u-F?V^iumHAz zw#~T9!`1<8AZ?p-mxrwuY({oxUOThv;A&p}b>KQ9t7~s=D%ADqh^WI9g2bnUPVEoP zEA%dQM()HF^RWw9&EcEKkQ#dhxVqP|Wo<>UVYS&l-zd~<-(!^N1~yr8V|0h>6YcK- zX4F=ybf71KVvqKQCfd~tsFXH&CAcGZ}ch%iv3D$S{102wrMrEWzsgSj;0=M`W09$ZPTye>R!>NHNe)PZ6BWb2gqB0 zu(>3EYmiDlSjKcu16E^xd5T4GPwc%=B&KahZ^Cs{9 zI%wMKlh1tX>RZZD?z0|T&5QDEW=Vkc5p_rdY@l-zU_-c?S9Jphp=q~#BxEDFow9M} z!#eaU<%5sRgCRO?)c*9`LchL4fJz6_uvlO>?%ww5-})9eLDsx6mveY6?rM>%q1uw% zZCb^L!OeFpshffIQ8$M^`A?Gdm)G zX4lyYX#`iyudTsqBe}wLRC_~9q4zr5AdTV**KNUS{ktr4-L8G;d}hvQ=a zouPg_9Sb*vwpbm;fo*{{Gv`{qgPfk2)$PG@@p$g~i+AMyQNGM}GWPUrCxjXbSwKe} zqTM@#ZzogwL}PXVd%&$N(!DF#ENZh7hfgiiGNF{g-H|46MfizewIc}^u9NZ%Za5iv zQsTM?SnXC^qa}O7Cw6adno}54jJa+a_C=30>;snDs{Od{UB-U$d3RYFvIF2|L4W7uuH(}E!>OKf;{o1-_9ZSc!^$`5>FH&@f00)Dw#GpL_G?X=MHf}fWYL2C2J{7Fy zB$ZDC8+$01lXnVSpHdyX%{4--M8~LPI=D#2v_;?<;O+2>z%$_y*u(G6WJRB7{4B6> z^@}oxl*^CaPI_18~(jP&op+8ynfL8rjci@6v{Jrz+yTqexEpN3{X zhj1MUc2?*ceLNlPtgu4;&w#60E@p+AZ`1Lv>U~tk`G*g|nHcv(h_~Le;A(F1p85U( z?4K{a+Ro-u^QBj8Lwrp-2i#WXHsp`sH2LSk)x7-c&_1iFu;So$sHU!bYKT6a2TynA z^Wkde<>NN^0-#b_KCsgmw@%TINyDX zmjad6r6hnyEzXMl$JMnoJY-*#-d*C#U@59x&+NU-ZdKLc% zQM{6AZy}%058<&NegKx+z>m1=FaDT&x*vW5cR$o$Kkcy}ehR)4n?A80eg@`^s+ehv zBgS+;~%Gcn_a?XC}Z2JbH zQ&jpb*u^IJcW{@|`10;BHjQ{8gr&GKt{Ze)q zf`hZp4+s{DO8jk*73milDWK)Q87ki+BOTz85qoSW^)(~*UVHw&X9j8@>5F46{2`pu z7som@_0i>*%Z_EA^zfh)ntC*@GuU!wOIy6kbpaz4^LII&B4sOp)6}mBcl;v*UE%tu zht>_O?lv72cZaLT6G8@4XAmrU(+95R*hg>r!qwx7-t!E(F1 z6LE~@31IuGE&91TIPK>|xZ3We zeog`^J;8G~n=K)~*s?ACUb61N#9|%sDmV@A@GLYJR!ViY~P@k9;OOJd5#2?XV z2&XAvoubmIU^ORKSH|8z)8KR_Tc1n2KU!N#?>ccOb2_oqAlOtUFT*%Y@x=e zd?wgl#T&pZuzu>{a|qa{IM%{L!D?~ES~wfcMh@XJGse=_%&ldbba0iv73?bQeRO#* zYib|8h$p-mmnJUjcJZEPd4k4*FdwewalP&%UP*5HSuxW z{csqaIUZi>409%0bpoP>_nxRRa)*H@dNT}l4{ zjuqx?u-rDZao1mb4)=70`6JwwRDb-6xkId)=OXIh$XKy+vK_9j76~{H4$Twb>G43g zevz>A!Hc*e?ge1WsKukhF%Je zhW@HNQu-L{V(yp5+`1Anoc%d|WOWsMBt2BOF+SB-gRkJy7IX9(u=-ofO@m$rR*TWU z7VNwYeqEVI`E{;GP&B>abc0S@v2@+o{`6^ue#Ji{*_k0+7SruUuIT(tU^^AXZU(Ex zWm4?NU(h0s?K7^vdHZRr_K3LzTux}|!M+tCGI|Txxvx_cxE-8+cDW<*Z(*w32G=J> z_)f4f^|JwsxvNFIyTEF9yBRs@?}jMGA<4a9|9ptG-NU8kLoAL!_k-<8{F3njuv#?a zL9p6nu9%|_fn8O4@*s@bhYEZk9!As<1^)_GdxR?%?Z1HysLckL_D8|#Lhu+|?a|Ug z@HkM(Pa{$P6Yxs?;GYaiir0+k$_FtQyj?|t9J8O8ta(k420o|GZ ze2=lq0{p8iYP2!;aaW5(zNk*2SW#bstG!rC!&>$^EFtzjfup316Nlw-g{uR zNX&QO%7g#C%Hw)NF*0Wf$;bPcuST z0m1CW{<$KY`tpt5UJk^jPgL3!>>Iy+5u+RUz9dF>c*OAY{hKTT`b3N#MMxJ6I!2J5 zU|Smv=>?Aepa8RpdXnl#Fhsz8S|3z6K6D#T7ev z|5CyHO~6W4=hD|kSfO@bk?y5a?t|X7`I|}Wh@P(n@6&DnWpBs>5ENr`T^sD5o2#~g zTxtdtM^D!Qr%Up>aJ6;Xhn~&nqxFDF4<;X@*N6MHzL!aM1=#>iUu}kVj~WCnKPdTc zJmvPq-*@US_P3qsb20?($7p{OieE8zh^%jnh%KMfo=f==#R8F(O~8H7W0Z%&Eu$Vu zR5QVm3-6JOO~Enh&Vph7uN05&;+=0Z9ENc{f)E>Xy^fYe^xO!YM8@5EV zL%tVUx$*V2u{LB&AFycMR^Y)&#UtVAca2-a^-+&9qrm5bKF_z+mrLED zwAb5#jis$}Ts#`$Z~7w87Jexh*kIbCLEDRyI6J`8uNue8xzvfXdETNO zAqLZEYDREZi=exJwA?VcuxtCV=P`G8L*A7uMsEUGS9KS7JG(o0XD)4VAUqN5 zsCXc(-70tDt{zoR23uwDJ>dHI@!Pn2I(H2+j!S193^a*5#!A88fc@R7fm~_!hNq*t z4_qJhsBB;GJ4u=S;OVID58v0ht06|?0Ivy47hp><4myS)FaL;u$hfGhk%O) zO24W&6s{ox%?4XgJ$f<+Yz5jPcoR6CUd?c0@6EQVkHa<>t{$=HIsO*Z5W(kztpH;s zO)cQal6JE+jk|g@U;$Vy_+enbp@_;C%GvdT9}ZvKo$-$~=?DZx_ue=l`YqgwJ>oNf zKCycp3DzE0tXD^&S;qCsM%fU3W6Xb7w)^B9<@np1R_+?2^+$u%g6kKLlBqlno_-EJ z9&V+6IdBa4ST22{u_u7_i7OggJP|QkYo%7CbF}uPvfb9|8;woy`~&zn0>)8W8(b}p+WrXk&ld@8=Wwa{A|Z}A=YrFQ zwwrDPN2|_*$8hNEHxTXIqfy!o=JI(yIOd2MyZ~;jn9vu()zVG&PjL0PR*Q*n5n@zq z;Njic??10FplAgbb48_>fZcV2Us~qzOkW09^JOx6eL38Q#bmq!t`=9c;YzeKN9Edk3%#c|H<5GRht z+qlc4)pvr;fVMlh%fogz*vC)XUEJkiyBBQZwcW#A9=7|zW}>L^K8V5yrm5+D0DL}o zduK0ft&OsBD>0a5EPE4o{S9o{hryA7hrsf%{S_P;cmxbzwmv+D5UqU_ERWVc0Z!NU rC*i&yg#GVux9pgJPr)stpEeu%ICu4E%+uhTdNLkAGvOp#GB^Jpqf7Zs diff --git a/packages/ludic.render3d/shaders/spv/b20fdcb5.frag.spv b/packages/ludic.render3d/shaders/spv/b20fdcb5.frag.spv index 35eaa795ed43282d51643f7b7420258cde01da7f..b48636f10cf15e549427261a7b66a457e4b8e4f3 100644 GIT binary patch delta 6047 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 WBBBADpo77!@(|A#+uLRjT>gJWaF>w) diff --git a/packages/ludic.render3d/shaders/spv/b8cff226.frag.spv b/packages/ludic.render3d/shaders/spv/b8cff226.frag.spv index a7862f95f0c31c2c3b60650ed2683a3c2e3f43aa..f7b38d76c8478a4010065a2c08ef5f90f2483745 100644 GIT binary patch delta 6047 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 diff --git a/packages/ludic.render3d/shaders/spv/b8cff226.vert.spv b/packages/ludic.render3d/shaders/spv/b8cff226.vert.spv index 12eafb74446c276e5ecdc7d157a8988cd9cdc90c..386b6ddde2d7ed401b558b411a9cf52f112ab35b 100644 GIT binary patch literal 33812 zcmZ|12fSQW^}avkrqO$ENob)+3%y=Km(Zmt7%s_8av{kLxhZs#(0fPeML|$NiqfP+ zKtK!#SO5hqD1JpmRJsbk=Q(rUVKe{Fe`LGg_g!nRy>>bKoSC_kj()2RYBc6+EZ7*_ zm^+|Ry%uluLuoXuH>O^&F|gvhj@p0J`hC;JuD{8q8|t`7qryDG&(2)C<7^jD1#~UQ%6HIZ#{0Kt&#BUAkRkQnB$swA@H^e4WXjZkf-C{+% z#ar$AS0kD-p{uXEiqpTbBz51ko^j(UKA^E2HRcH$RY9BmE{is$wy#v%H!kce7WP%a z<0tp_^mR8K%w?6rz9x9w5tAxo55v^HdduF?SOeTUW?D5*>#2_!KYV)aw=R4JWdQ5C z3H1>ZyT*3!MzX_a^lZE}(F|DG`!_bC9@9N>Vv~1NYkp*9>u8LiKB~fl8{1G%=Mq9fEweMEc)4GqT@>H8n0_y!*W@>L=U(>cf ze0*(VTQsIk?VfbR#O^AmGqkaXs49LY^^~ruU6X2l_7`|Z<6d}AW$S1>K;7H}9gT;; z(<{Ay;~DBQ#*1zI^)~+I7kEeGJ$M(zaeY9|Fe>~ZwHK87C)87GJRjFcGpGKIg~6Zq z-O*SLp7v{lXVz`D0Z*uK|Hc7rZ4QG^?e44kIRfnD*yjXr#(NYv^Dr4alaoN76UA&K z@%iErO?ra9{0zqqz3J8QfdzcZ(~1VB{&Kr^vo%}Osw34YR2fvQ?qA0<7SL@*Ej8+cQZ!!b$7Et_4n+XF?womb6)#b=irRd z6X>#O_e`8I`snFhM>Y4T=i`jg(-?_%&Np~-A8L+cyb(B*`|$|z=h}-cKL#D|HiIu{3~$!@d&K^4{ftg{Q~c391owy zhVpFpUcegcKDoIzyH6h5Tyw`h8(a4J=~Wvpzz7j zSGK7)B5_s$$Wv1hXTy*VEXRl0XYeVdy1R_0@R z9|uIVMJjDtkF%ZJ7Hw&xy={2RU~A6RVwG)5_teVtd7Q;tZTecVePSRtuKdsamaMd~ zwbqQeRHaSr8rw6y?yH&a(v?1?J{OwWGR^i!G|&Bx#<}&{4`7U!fIrWyc}-n~HikCt zvFpHW1#thyP3pxx@m_h)*vjwW3OC!#1&^bV?H>nE;Oe*i6X5dte6o!{-Nv73kH5nErgV+znLNJY@4?6PPWMQucn6#NsP67+ zzdMHc;m!L?|HgvgatsT%@kQJCa`1^g6 z*awYUcYQW?G-koab3NGhRNIbd=6f2r%;}6ae%2RwN8=pZZqjUf?iXzR8|Sz23%|fS z8W+Q7R%dfZ<7)8H)tq%S?gIC6wwT)s;xWzmFM{i9xw5?lAJ;Xt!ta1{-~9kwj$wWd zlIC5a8FO)XSG`_KfJ@({+W0bG;2n(>;9ex=wla9k=w@z1z~iU(`WR5z)__kSA@}Oq z;AvfX{^NNNUiP^ye9Xk@liVcw@Aw6KM`J(u5k$7nN!n^ZpXR0#w z4R9aN=FQytH@>N@cow^|jbGEouloY;Xxs#!FuwU*{2I8c6}zMHhQ4E)<9JhCopw$B zugccr_OHhB8T^PU$BxDp9PHB>nB&+I+Y!}HeJPY z!4=rb@m=)=KCpTp#C@>&&{N+9cO|CJJzTy#^A2b|11)H>ymn(<@?7M(8Xxa*`dCLZ zh8o+jrHR!Ta${Hi-)9})i};e;vD`RvpLOKMlKZS9H;&wA9eLvUo=?tDE4$A+a_@`! z`>Z4Pekk`@zb2TR>Is2@j*1ynvR#024(0o=<8&YUKE2s@GG}lCJy+ZT(KyAZ9b01e4 z#^mDjf!Zd8wp6WcQD{CJ=(kOw`D~!piRK>j*yLM zIQ0;UFLPX*+BWL;;n>CMj&c1;|9p%a!1ZtH0~;HH?L*x@EXy;3&8XGQt$c;A$XsrY zK|k}@qIOa<_bsVyukLuwrIT9C_0VSr>Q0Jw=WJbkw11iM!SF-358nfR=mq_@UynBC zKC1RnkIz0$pGV`fFI>%7@i_o|{8D#p8-6f++_*J6!w+rpR=;0|AH3xcwu!w9e&rR1 zwCrOm{z{AYz@MErZ@c&(1z+H$)mr^|uYPvkLM?xv8S#HO{(kQ9?7Z7r_7mY(Ua@?O z&w`H_v1BXXIq*hfR?B`q?8+;yX!U<3e8h-PM<)JN+F!Y9$IQ{ya*BF ze+S-}d-{&yw?LmAwRtPXt#EzR<8vpNS1ZrCP1@p5Rh;vxUeBlDYL;lvgB_omafegC zMDZ0L&uH!XMe{tCThi_wu=_Uoy$|NK{c^>fc$Xg z#p{Ky1s|}-fwzTwFIcJYUmwo#*=j!m{>s13Uo8Hc!9B0!L#eliH)ehAKN5b}d*k}Y ze>eDboome(z9;;?&pvM1_k-Vc?B>^M_nJNgPQVSqkE{6H&hV3(_D;`ooCX}zo9ClJK0G~shlC@YE{gc1{Z7cau?2E%~7yEMX`&Svzx*nE?j~Fqc z#aDu}lXr^$$`wDZb%qS9{O@U(!_=URLXc<&OYY@PONRqYqs!Fkyb?3{H{V&Ay3 zU*GC~1bp1Mo4G!l904COqOaA@7`Q%jXr(Rf#)9{|f0dSBH{3ZgCvEYYq(0)`t@yq0Mq~AnuEz;r zwpO)I+?R6SYpB_NDD}ye|9pJE)4y>B*lVmji_X-J=C$to676cM@Fe1U3frV~;_5Zw z9-*4;Twyfx8`qy5WZZH~|R*aGacy07@}TgT9UzZ$zn+MSQX&}@?a z50^A%JsN&g#pky6)J*t@5%*Nw{4as$nNWT?+~@D~e>;3|!5@M9?4S5QhBq45x1N1| zQt{oYGst*9gFD8I&-WU>(*7kl&t}^=zQ5MKxgR@EZ&&`8x6ZWx!7q61{pwlK_%v(X zPxW68{@X==+L`$4z~A2Y{?-}tB{<{PJvz5x$mviv`?wAdFKp01??%g&zN&4S%=fm`L8u^XG4Y; z6y`bVlX?FwM_k)S|65RK~nWy*Q+zYCEk@@Vzre5~G7q*D~1o#3k zeXClhv%roa>nis>qMGCMZ1O##d^4^NXq7uvwO2nv!OVLX?yuDML3W|ufh(gw!u@cc zCwxZpdfyg5b9w+>Tl^j@{Jak0_ak&|v+2cl4}oo~ep;wrcwR zkI`&FaXiL!?tFQzY>uDTh}VVK>&dxxoQqI9Cu+Mk!IV?#yuJ#y zt+s`j9p~q36xZK*(w=ySfy+Mmn75(kd%*Y{4lesV0)h1Jt?-a0YwI#Qs!O2Z~ z`j`stp=eKTel~7?+FVQbidu4*0ajaxo#_0}q!@c6itm4?Q}p?iJ*lnS%ijNdElSB= zUaZFM)5QzCM1hwCyT?86vd51DmwWt-f)56tS#V>YUGt3Nc({FJj!pn)jgrb>El$i^r1cNPXlXnEzL=YX9{ZH`;MWqrMz3-(&EjW&IpJN2|b4{U$Q?*g#e`IPuv2)12(E&{8${u#r? zVB2a-yGy{XXWCr~?xLt?4_^kJL(vw$%fZIe=H8N*duutkuVpCNTg!soH_KDozw>ql zMLlQTHMMp%b=K)xaMnqCa=0F>?K(<)ZvgAN0Ofj$xv3>L`&^0QYXwSjTM=w-D^n-8 zZ-dp7+f86KbBoW-;L_*2aJ5@0`*2R$-}flC)#m!@ua+3Mfz=#?{saqg&X z8Lz(IuYDb__W0ga+wP=J9(RLXM~iD~`+F$*YICmSnX7@|RVlv8bzBARTn(Z2`uF<2 zmtwr|2io{UZTy)w{$d+{y^X(F^UT?OaP!EV-4Axo%tf2?Y)m#>c^Iq~{z%Q;gO2x6iazS``7ziwS&PTOYUNry4xd|>ti=;>b^R0nCt&-~W`542 zTKakt>^Qx~&F7~SwWlfiJI`uc*7l!)-T&%|F%N9M$>UkDnlZBXp99-gTiX2`T=x9} zTV=>H8O8`&LhkUxLfNe+5@FM*99W*tXhsU>x&Pzf5s1UZNOlAoVL0_1p`8 z19pAartNn>$-)~Z@rSHFh?OQ!D{tsOC{a3h}G1B*2VB2cT-2Dyw5T%^Ex8drUyT5}o zcjj*#eG=mzU}L;PF(2puU5a{Q{1coQu7xr6@to0Co-=F0eXT~xnY?<9hf%LV@yuC^ zI?o*Mfd>`*gEszQ!9NCnQgCC=M{;RD81COB%AJ2>zh7hHK2>951{==->Tj%ml#j`= zgW6oJKVWVKP!FW|(mtrRTRV3{(FcQ#5k5cIG2g|0_kHn#6n)e`p^dyh#n|>G&N?gv zHh;O}Sp@D}nx|uwyRTfcMZv35tLMJzccjM9mY9o!od?eu+bscC)7QA#)Z)J+I63IQ z6x_K>`=#M(`q^HaTG}rIwtaG17Os{x(Wa*VN8shau7}()FAsOj7m=@g1-L%y_NPtF z^>A&RLu=a)#kL~Y^_WF_`O085zbCN0HnnoSU5n(u3fMUbUlr~c&#dQf2wWd^uLpUq zhvE3GNAa}|#p_{h>UAmR`6X(v0kzd==lH_?+gZ8&8fSI5?M|g%`I>OGH7Jfv&F{L^ z{f@)28B;AW)&Z-9uUqrS>h@oPtNEQsVr~Fd_j{4}`CYlOv?bO?U}Ha7$J!XIpL%lM z1g!3ND(2@H)e>VonSS;D>?xm*J=l_dgf+F zu-ZuLI`&RrW9Muh3f>t_J+XHItCg{Lg{vp_?qIduD4Dk}gY8pW^4;rZ#6K7wzTH@>vR@;w~I9~x9OPhPcc~@P zy`j$$ZG1u-KdOySZsRAm@$=jG>DuI68C z`mA+0SgjwW7azyk1y*+r9KYOn+QxvLr|_|Geew+44c13J?`6h;y%$*W+&UgjUv2i| zys9N`4_Gbl8IA<=U-kU2%{I=xdfH3`mu)7&`LBAPq0Kg~k9yklf{l}0r-0|s#*$o* zMpM@}?WTguc75>ncGJ+*_09ZD2OCdYV$A@Tv1Y>CV;zI0u5V%;3pSp%#NNc&up+h>3a@XKlSu|3izbDWbRIdtLvXwr-94< zPKT?_CD-_zQTwF7GvWHF$LFjzpR?imsr&5W*v|o*gSO;(F1US;&O_5zn{(uKs+PVk z0Go68g*8up7s2&WkI%(zK9|7tQ%^3Jg4=Vs3{78c=HeNlmbl*lyM~GJO>p~uxdKgH z-?Y0D?6|TvSAq3YkI&U$`-{&tVExq1#q&um{?~!cCH#7@ePsS`0PCY}d(Shq_yv{oMi1*%rU=qv@+H@$Up5MoIj;!1}2>uA8aV;(rgg9M=!v8CUxGA)0>L((Ycc z^A~;}*f~qT_k;COPn-w9#?qE?JqT_e*N@Qj)t2}Vfy;3{4A)QHaotU=roZ>2N5I~X zcHsJ)pZZa_W6!gd_w~7O_4xd__Q^AnK98ZPC*Q}xwo^ZtEB^`VpHS>iTR+-7McJ2r zZU1C#2e$fo3e7!G=JGUL-RsKW57!M0VOU61ouV8^O0v3?D9e#)F* zf~zOi%V688XMSD*m-F)*c>9|F7EL{Uy$W^?%f4QNt7m>*2is0P^Yc5f{b_5TpWmZt zFX!hE@b)$TBbs`~^aj|r>KW6Yz~z|!3~yiaH__BHroYra<(U2tO+91!E7*4G8Pi)} z`_tAwroW+SFURyY{6=Vf{j(?{7<-gV*LwjTlK{H zH`u+P&1Z3Wp2Z!*cFmMIA)3$J+f&=;fAIS+U_?%AaQ)|ko$hY-%^GTgD+WdWB6Oj*yTeDZu@l#J`BEY!TtL$e@|JqA6anQ?^5t# z@LdaTfBvR2?e#yjjr*HQ?e2T)h2SHp^Q^cCTy0@W_VA+MlUO5*`@wdLq3Ns5b#Y$Q z#LkI-SC{YEmIALy8-288pDhj6=6xyqY#F$^nz5XJHS_hnUIDC@&pB5FlUDVf+rQoO zZ`t&T|0-bDHU6u@wHeoapeA+?l=HP7-1*W+TXOpnSX((?>%-O6GR6(y8RIape}kx>eYqdCXMQ#X zJI3^}8CcDB`!M63#N8aMo?<`hTcFv``V_A>{}xeS`*9y@w;z4C1goXrZNcV}ez(&` zvEOLhgVj%@eRAo9tEVKF9ng}CeQiV0H@Rrf8tw=lNzAO_&TzGzDEfPisfoS5%C*@I z?%L?1ExGOv)>f{~m*MK!%Ok;RnS;H-Y8l%;V6~KtZC|vEZ4`JfioTiK{lLzxxpb1- z{$MrRW&COx|AF9<#rO|~s~tqi9{wuWzs0m<4}T4;uQua)wy5Rv?8Cr*o~@pF`8rs= zoR`sX^>WW14$q!F0K5l9Kl|}a)1G-72Ug3xjR&ixWZovAW!}bsyD9o+-g?00yd4Qw zvt8y*E&fM=_0K#`1dlA{aWY(O5+(CE1>9Sg%;V8;eYM%oRBE-%V;|UgRL?w41FM(w zI32F87QY#A=T2MB)S2MP_-J#_cY==rJ05M`&*kO)d>4Ft<$k^^*n8kG>f`bC9uPje z;NEjiFS!1fwDGIj_;m%h{Y`EB-h%sC@WTc7o;SDPwtu|f`ae zo9~c<`}eSWv~ho59sdo~P29 z@36zo-``=E+}~k`+rPiVF1f$OF8Po)?(eZnyT8c}H(!5~9d3MolO3*o$AWA3H`%c} zU;Zw;n$sxWCDc-T3|{J6wN%lU;IulU;IulU;IulO1k6f0rF@yf@mozs-(a ze}9`@@(&7bdw;7PyW{ou+9mfl+u{2AyX}&97Tos!emi#iKfR6n8}8VR=Wn>>-baQ~ zpG1rkDQ9pt%V&YrPNsN&@Vr%v{~WOX;irK0@w*z^p9)s9{hV4mO-#{dOwVk!#5f;p z4F4|o0_qDX=Tgq2Xw&ZrZl)K3we8Ia9K*$6HH$A?s`%amv?czf;AZ?m)o%#C0Z)8w zm({*L<9`#ZJ@>CGz-r#-Cf9RzC0yOHcz=#?KAGQZ!TPA%{wiv<_+Jk$ z+us1!C-xh`#>;$v3#^ZN=KI@V<7l%F?{R9zaNcePJ4cE69k6}uzda=>NUL$Zaa0q>v8UG2dk&e9pEzV_u=hv??hA2{M-e$oqFQl4OY){%{^fIQqObr z55Vf>`@|o@9ji9y!F#(}=HWiDTH@Xh&U?*##`yr6e%j*qAlUto=OO3xM`-$L%lAwV zf!&i{m)gz2=Lz-X@CaDVz2$qEN5N|PK1?moeEt~hT4r7z1FPvfms+l`aee}}y|yQ) z<%#nY*f`prq?U_)7V&eEE!icm=TG5&A8vd7v?u=0z=`iOi#+k41tSKRCo2jM0*THJ;7w7wTVE4;N ziZ%KTzWHMy)@B&jJMe6Wn&{9}@FTYG3|6k+wfm)cpKO zoZo5u1?*>QewL+8pY;1Tu>HP8NxyG{)x-Y|E_3|{+&1qNKJS9nz1|$(Kf$(BpIvMJ z0?(;!d9U$rH2c%$Jo#L!mcBm#tA&49^R)j6u8(^9{uul|MO()C3D}&=`TY-EJ+b}^ zR$HEucK-vnxBC=LJvn^_c7A-%=-3%Xwf_DMhcWfn$Ni*zKJ0U9zx3S?&Ahca5AT81 z(no)=ntUj22f#0ZFT_S zgVi&>wZV7Nr+Uuib-?PD+#}Zo+lRLJtp{$uM}7%SUu}+U2(_9y`E0sASgri7VFS4O z{q?$T2v&Ov|67<7&$o@>>aOz$YG1Ci`o z_gM}5-W+V-?i2m(BYABB)^1)~QTsA4^(`rC<|R&ETZ7H(9elGc+rZT`2it`QIzGr(V|^{mqYaIeF1uO5h|9-o82#x8vhhO5Wt5U_E|_5LcF z`abM2-}ihCY;4aNXLYG3XV^)VDR*G8Pay218Uj&~efJZecW5_$JyZOdaA#tnFCj|IChqfo|<#gNqtJK z?+n)0Jv@tA-IzXCoDR-&#c5!)8?CSZQ1`Errdho+wQKi7lREWTXhJa^oHW=wq?voUW3x5xYzntEb>8?0ub2_yLYS>q;% zarJSIuBKMY7;Xlug@32!`CZH{aDCKs{@n^zpF=;+*Z07_oG)$PrKmYyVq?2jw}D-w ztmhqI$98++^L?&Q(IODKybh~ zpR3)c&x3urPqqD=qUJso8)tLuzW~2jbFcGXf`?G@T=XljKI)`U-B0$e&+lKOX}9=t zjQTi+7tpk4o?Zs4m3#6PxO#kkQ~Q+X*Kg6(vj(q%ZKt03eGQ!Xb)UQh*Dv$?I@rCV z-)ycCueINSd#JT#PJa(}?$hoMU^RWSE^1zPnM>=m|0DREg1-SaUe@SOV13kGqc^F2 zxklRlOi^=<#JQ*b1zg@!^+}(91N%8a`uICs?QM!VzeTO4zwb%@0ammBe^IA@^?y>{ zr`W$Z>+)~#9+dJLe-Ey1AMaAjofl);-Z=K@p860xgre^U)bjid&PQN>FENT*TjHsg z@r;`>e+;%yec#cqwmH5}z!~3v!M>bd_5V=R%uk$npMs5(z3~}X?SGW?p=Lg5Z|(ft z%G}S#1FP33-BdA$0{em0pTbAp0ao)fl<%RLYkzp9gI$k-a9`%DZ2(2he8tIk5V*{D zFkH|AH8k~nuC_YZcIw6)LammVYl2T@9>UkEdB#2*u8+Fy{f<^G{_BF( za+akrat<6Ew2y0mz?ui`i5||4JgLr|ua2URy15vK`oG^tE*)^yNL_Fd(9K`%W!=vopmF_6BFYcLmR6ZuLuk`+%L(#Ml?CX1mO{TKvBPb}z*L0I+e@<8vUm z+%E^g%l+baKl&ucgKN9{-aTSW+oX>}!2475GpGHi)$Gr+{Hx&HcMb*n@*G$H8b!@> zT$~&a0~;s&>tN4^^f?-?=047Qgu~&E!}kcb*C+XCORO%ia~ggGxQsmpu690g5_>G% z*lSR1uTSP*oA;B9!#c6M!R0u{!F?vlIX40BIXAv&(*st|SdRqTPCeRDVD&wV@lS+j z{PwF)#;?sjr%)eF>7|tSxT&?Cy!+tho&KhQ^-)ip>0sl$!kAdr> zo;b&XjZ;3`9tT%Xoa4b|oD<;ssK@6-u+Nyu`6Re!sQPTi?zMX|*mGN3{APiTr!9Y< zI2-Jk%~!kgIEh+aJcs%eievD8bvoE}JdHZ}oB>u(KWBo~#uoE+7JPn6e9i%@olQy3 z=Ynmk&HjAeI1lW(TCU;wXzGb|Az1AKN@86EHlDV`x)^M%avd*0Q%|hRz-pIL66t^W16R*^aSJ%t zqBe6h)=gk->HAi&@x#9h)+f1q53G-Rd~O4mKDWd5Q%}1)!0Mil$@}|o^Dg(yopANp ztc%yjU0^lar`_G)v~vt{{gTf;;BpKB> z_d$Ff1grTho@dk_!JYd>DB9!u5ZIX7Gq#7pj;%a<9)YW8P96oT**@*&g4519k?WT{ ze+*uk6931*nZx)zj;5aco&eiUJw87HuRuwhC&B4AK2M>kC(hGg+o@*^KLs0KTVg!} zHdeWhe+E}i?0I1OKe^F-_IVbpmKe{0+sE~DH1*{3JlJ;XiTeV$oSzrr#wpj~7jX5& z`6bwP>KWIsz{b~>v-{Uz$Lm`FJi~%+4nlwv(I_*Z(@E2Pk;J!;`4hn^?ZK&2e6vOmwVXfN#p$y&6xT) zp4X_=(%+xJYPkmf47UA9iZca=-T4<*Z&{Cx6rj^ef|bkJAmoekLORNsC z@yq)}e>C-seE`_C$=VNu>!Ti@L11IYXE0np^=R{h)wgEPr2hrrUaw{U3!lgbXV8>OScZ;H_C+Ee$<+v7y>!Ti@CBWslmW1o4o^dS&Rxii3 zG~E2kYi$`c^^9v-u$t|Y_j2I0b6j%$lF#yBa}HktUgozVTp#u1w-Q+0&w10&%5eKB z=Vui(^}MfL6|83av>O6WJM)w4=RVH$Xf4lKX`OtB;=3|D@rJ?Yz{~Yt4NX1yuMW1I zdg81BUZIGyCfqpXe6NM3o;bt7wo{*7@2$1L#@Cjd)&U!)dg5#Xww-#$wJF&6+H$Yl4D5Ivi+1lH zo$QIt!JZX6Q}l6N?NeRs-`~$ypY!@}1y?g?`PSfBychHAk#D0v^-dIhwHwF3+t-$M z+kw?`AKM;myO9)aw()QK)ibXUk2-w;yppXJ<#;ko;gs<9E<{cJ*A&L;f}%2&}_dK zSReJY*&A%%;roD%k@?>jp83~Td*bgWrX>FUVExtIJNEe%u>ERF9|wTTJ`Zf;2f@uD z-yt6i*GD~l9RfCvw&e3wu;a~{^EL1sa!^mZL&3JymK+ZQo2NGC)xOn|@7KZZ;q)^a zUe4>`aDCL%rVCt-@d&svGOuIcnOA+aC;nJ5B{_G4^;b{Mc9X%j)s`H4!RDzg=ff1R$zw`^?(DbM2el)puw#KVLZ(-2VB>acJtv_js`1HD#Zk2v<9Sk~k-U+do@58BIOo zm<2Yr?X_jTW`o_sS%*1rebvqR7;3fnp8{6X_9+u5kL^^jbFYo%X~?zt`Rr-nd_H?7 z*q3LY`WcjqDV}{|$2EmG=YY?ql=tX!;p*`@ul6a=<@3?hbDz2Z?E3|E$LX`(h2UN3 zLtCD$F9I81n`1bgTFt#?&gN&Hj>-2+mw?SLzVg!7Hu1d_Y&?BC$VINrTrLC8U(eeB zZ1UJH2YbH{{|4OqbaMVCTpx8~52U@Ccre)K@8obLdOj<=0xVD5tHJIw+g$~g`yFv7 z?XLmvL~R>w`pi#k^|ZehTz(IJ9bC<8N+0*Y^>FvV&8&g^2DpCe`TM>b!Rp>4zfJAS zeWLy?ikj;nb`RW29yft~kBM8=*HGZiXzKa?_d8&hZa~_A!t6+<~Tk zF>!43eX#A+_35O)JHcLy`L~Yl0;~Coc6Y52x~1JyYlLMV4XXZj><4JJ*I)Y_Z23FZ ZAHwZlA9MEKZn+m+{@X2b{qk?C{6F6Z9LxX! literal 33844 zcmZ{s2fSQW^|mLuY4qL^NNAx*3%y=Kmrke(#+&3Oxsc?B+!R153B84m^s02JN|O@l zA|QwgD1sIB4IE8uD!$7eqxM*@Z~D0PHr`|d9T#qNSXH(~8jDwW0RKO^t7nSj zV}g8Yp#F_U2W22-5M?lB2xUIX@suNJbQXR8h|rFWo{h#)$29SR;OUhH-!@#8B#u(2#P=7}3tL7V+9gEqCcuUOkRD(ov1_LadC zru6pobvGT%Wu?Nt26+6K$(6B(VQOEkW$$RL4(=U0y_%Cc2Anr(TA70ry8Y8HWsPK@+*3=WbdnQbr zK5BAr@AQeo`!?EeE7zd*-I98G_fb`zYBNZ{HQW|FcE+Sh!z;hRjUB10e0J{Xo;0o+ zUTZBzV#^xt06w~EGpMmM^`sf&dd7~PFln?Kj9|{kuIS?`p8<`%sJq5a>+S1n+D5@A z)Hb$7WBRo2$zvvUS2>-kjXgwF@zbcMc1`P=T=O%&z&jfE!h0%PN8rQs4dRmS9ag8){8qin>{CVFU zjaA`kzZQ5_-DYd>#0n2+9MIP0F!;3YzN()wU?;~uCxSEHBfy!5Dd1V01o|8+W+RE` zibpi@#bOQ-^{c>rwf}YE+W!V{Z>`@guJl2Te}h@=eY&R|*gegS#cL4%Q}?BDXSot5?W}p5A&c1<9XG3|m zdoN%OcAL^%o86|2Yp%ItKM`B@`e{`gF663@A&s+|?RM-Q-!)^>^aG~!jPIQ`dE}(t zu^e23h^*b$jL|cDy15XdxwJDzPo37wVHZtT{HpxjYqoW7%oshkYx3x7>07?+0QBiS zlgT8$1Djgk=-z2P6SxgV9~6CJ*Cel{=-hmX(K~JW#NN?tWAwpQpF=8r5?f*Pr0$vB z)!Cdj^HsLK?ylOD+~;q#`8>YKUeru5DlJ*j7k`@K0I3s$;!MSYu^_g3a(Mjr=6 zw1q2edXKZ6+!kqRqrGi-%wTKI)uNScYWK9t^m&}cT5bATv3+77H?I88{g$Y-akbWr zxn!kH>l)WHqwcGj?^2aMwLTY`+S1MTW18naXHmWO0~zB*;LkH_UQ<`1jirry>{>8e z0X(2_qk3^qykFikuJU`d!p%01fydLx_D_N*a`oH(DR6mxKGVjZZR5|i@fX_oi(lX! zjhErMPreEsSDmo~8gGg@+!#ic$J_9}sa<1xrc9{#hw!nzGdxl%K9J3QM0aWkQ z-n_qXPXU)>Sfq_F*2b5IPwE+8UzZ(?Vep9@hsNDgOsB5HUSO`9zR}Hxi7Jo%(71Ki zXJbcW4txUFgKbZ-?TBW+r-I9zPH*F9et~y1&bIBw&9-yDU>ndlw~e3w1>VuP5I(Cq zo4I#^kF4gbqj3+om$Sv(UKWpS#{W6EzLqQ7JMi&c(<=NPIQQL;z~vYg;2>$-Nvu|0`F+t2%k8i`CR-axT_Vrqwyzw$2G_C7jbpkHTnCMt;g+O zjin!l`Isulj>eYY84S#EYz1yU6L&PW1NYT?VIs*vj!; z`2{|xdLP7nu=&ta-vxIerq4ZGzC7~|Y&`=lV6wb+WnJ=IB>R+pwjH z)tG|DF8sgGI=&b2CAVX_apXSh$c-iUSx0Uhxz9TC#PdC$oS{~BpLOKk7xnj9NACSl z?z4dTtW)zd;rcFG@s8>qiByd{_jhAE#&BclUuvtueK}wHIi9keyuF>8?K&xGXWwNz zd3!rG+nqp34$hC-yh3x1)SfFeW2?PbXpUd))k3pRwbu)6NUi<0(0pdl?~jG%^McxY zh31^AIWfuEX9cwZh32z@+My4>z9emx4RiRVe+cdO$T+?dtZoDs|dB54IUjy$Z#b zIj%)*8+H3|>|%AtxL&1yKF0Oo`Zx7KjSax|p>7|ROH^Lk9 zPT3*+Cg}5fY}Sf#Gh83__}mHR)yi{TleYNN73aLF*Yg>;nkCwcV8^Fs+~L%(P<+M5 zGg`ZT(L9gkmb7~p?7mHYAAosnw=8i#0t}|~r?`(luDN>r{{vp;wHI3R{9pJ>!!B>_ z$N$0gQBRvsn?9}m_!(SHKXWy{N!eDL?S@kKH&I?olF|I&=qrLX$(@VUAwQUP{<`67 z!Uyht;BDdF3sx-r*MoC>w%U(?zxL0$i^hLbxaXC8DD`&m#tEPMkAxrg{`dj$-xYp! z=bHV(_k`c~*~cyWe(<}F-s~#vUekxb3AldvF%_TJ8Gc;T-sw5<1lTs_yCVPpVa;8~ zm9Y3+79VXE*X|=~&lc@l)!fgU)YfH{ZSx7mJ!5g7x=(ETpW06!`TuI(iQQOopW&LZ z1~t?_gC~}C{M<~XUq85Z`?5BE|JrA8-LB%brhPz-od@H}eI_)nTz{Vl!?pWNSaP2U z!_D7kLixJPVJG$c@Hx~eS&N0xKl$6+Tgr!GUkq-$*q4RhztX_g^{^a##E20sz9O8R zykq>oRPke4XUMS1|DM)4wJMx*uEke}_bz_oR%yRx)qc_KotF*3&RHiV_Khn0HLdxLu8+E7On*Cq-`RKb*15hr{Q2kh zXtmp;_E-1JPrE%Uzq49?d%@L=U*>f{<+n#`J`RFE{=rQxeh9qLIBomH?Sij-@)50Z zjDe3B(bwu{EL@*Cw9=M#BQA*#yw(P7Tbk));>e2EqOl_yS2JwIGgzVSM}u>TBSzd)ar3_jo@Yk+C2*h5)BhdtAqAfY_q{>l zKMrp+u4z3RJzeo#t24@Y&%+&K#^-wxUupjuoM*SK9pBrvZ|>L5)4P@b#jP{%Gx#}= zeNa718oxgqJoj7umxup)kso&^{<`pY_PxJ##;gzL*{(Go8^Y(Fy5K9qLX!+pP)c(1{Iubg@M5YD}#x>uRc&TQ&s z?t5wT*pGwH|H}2%I-Lo23|UvX?;+J3r)QJzA?2HLbwI1!sj9vD5ejDB+i?G-egLvF z_4ZsD0}vjB`@G>ZoY(s{_?goW(Y3|zp~BDWAbt;{Ynx3kwtEC@TlJG`?NPA0&#;L% z4_@}WE!nE+`#(mrImPi9)4B8IwXzw0UL#%?Vy`FX+Ho#S?VPCXO39qb_iE##YVJ7n z+l^w(3+q1jtTo@anVa#{;=ebz^xqGz=6g8(?N2T9v_IGwnUe#-Y6no_a}e0E#ODyO z+QF0)>%6`Swym}WnH}fnYZTYtdD5PEhk?sJ`53sN=6l5Wd;?teIR>uQMM!@FK9r(8?I(iGTU&BD9PGTM-6XKu5tR5$2ABEv!quiwlJ8Wo zZM7x0Bf-f{d-|9L?xAQ;Zhm%de%f41_ljC_mhm*8qIQtaC`RZHJ5gY8>AF@6p%`~C%7%^2zXmtfm!+n#aENBt_rxp;+Q ztU=VTQPguU{1w>sU5{e^#?dEz{~BE8{~Ng4>lE{UgIX>5zX>*f^~CrsxXk}|a5ZBj z|KEdct1a#R051Ff6I|_&6#IUQS}lG58EoI`iSZY3+4o=JYQ{+4{|C0Mw#?nz;D;#X z+`R)=&)oeDoVhc9N0^8~cMA8~5oN8#CB= z4p4t%^{0GHjvdtIYW*Q|Gmv@^#h3QMwcXme8;U*zY>e>vz>fJY_Pg(k7og~){t0d5 z11QF}FLBmkL9qGD9nZpW=h8eKquhPvnk@oenOZ&fRlh?uhPK394D393&e(2oxSGDk z)utBzCBVr+|0UthUD_`NSJTh-+SJm1X|U~+(=u?itcf-?{XYUP3wAx^j(It_V?K|3 z<;%nMQMW&BYOaTC;~ZMsekisTz^=y$w3mMgtmgL!w%4XsuD5HE{8s`yC*do@9ph>B z{H+4lN8Rf|p6g*ae(O?vtxfTISc`fcig|vS+G{{J4k`*|AZ9M}S?IO{lXD{#|pqE(g@y^V9a*z>W8cn6(;8y)Bx0_FN}e&F_-F zg^z2sJy<<+vjbRdq;(y8N3gMTHV*~wgr=U@JA>8A*t@{h6MHwX+OCw$+gHH$sV#Z$ z4t740;~sFe#Muk1wkIWV_68eETjJ~kb}kcVU$|Q0i~_6eM@gLh!N$_&-f*7PJl~wJ z1HoQP>e^iwwd{+7z-m*OU)TL$uv*TYuY%PMp}0@vYVq|aVGp3DCY~x3?@hNTm*fu`5jbGfxuWIAhwecI;_}w*k&Nd_8ufrYV zIQXX2qv2})MW@eN-vF!ir}X0ESi8XLu7Ts18&BIyc>c`lj79aM`X8-rjCHn!3K3pBZ4|X-lk`;4;=Mczdj)(A4!!tfRrk z)0X_d2|lJS-oIS4Z^8A|<{Y>u)zbg*VEfN|pcBAq#);2tus-Q~4p=|+^nD`uxVmKS zPJ*lJpI9e@%l=M*tIZ?V_?%k%q`%YP`l-j~^fsR};QFci?BdwZ1e=4lgMA4q!$0H!R8Wv4cI<1|JQ=` zQMbM4nOgj>2OBf|J7D`y`|pDFQMdhd)N1j+5$rXcxwr}3J{LEm>8CC2ZULKT_^n`b z%QbTwSReJ|@;$Jzw57k>!8zOFcL$oj+7kax@L`n1zYDCNy5qWmS}p$ffXi`xAD(fg zpC6#-hp37DcAgOaP^GoU9fG{v*v#XJ63JU`8}|)%AEfJ zS5K^ef^DmwSpNdM7qs~-F3+>LW7xKtGABgynR`2G`}_}n|D}9P=}++tk$+O-PpLnv zv6In{qE83)GgaJ7<`?A`}bk~-ZJrx zH?rWi-?`w!;JXxDe}8kC_WJvq%aZ$>OS${rdO`R|>O3nh3|Cu-l0Cc#_&C4g;@B z(a*lzkJ>Xon}8i-`q&h#X1jfu@lN7y23Ak8AN9@A>}NfS*W1PveeK76tlfU}-2$wZ zezyUeOZweb8^wO3Z3k9AmiEb|6Rw_;T((C`F81Z$KkA!Yv}X->0FNYQ)^I1d+Kv?c zy~fnUUSH+f>8H z=$m=#0hjZ3I9$zknK!lg9|6`s^Ed@OvY5xIaJ61a=5ZSM$hu@6`{4R&v!ChIYMI9w zVCPXi^EeZ%Ue4nzxVl>Wj)G67Xv>*;GVOxT>p#O_?2z^+Jf8u#x{O`!Tl`w(Sm!=d#vELf3o2EKUMHy z@Mqfivjx}xxq|EeLK}av;P&@&8-KOn=J#eBf4hx;*v9=GcIMai3l!Y^7Ap8KxWC1Y zzuezqhuhxYVuzdWu!8&duzR*~e}5hS4ajXy!Hwr{uwyqLe}^4z|NaiU+2H{0RPm%rOC`AG%W-`{J;F84Rv;l}qj+u_>%&34KC&34KC&34IeEV%Lf z-F9jBciScRx7*?R``hi3e^hYu={N9myW{ou-0_$Do9=M^{ats-{cU%+{{Fr@-2P8% z20YVkh_tbh2)V14|q#`dRx)oeef z)=t$<(Pm7~Y_-HV7i^}7cNzN?*ZBp z|6*`6{^0621ed}SU)#59U!UvFJ~_qi$coLvD|cP!ptbAd%J7D8wzP}6BM?Ld>1K2p)?8AGUnlYTWo50RdV%`k4 zkL_8nTd8lOI6t>g;`6;)KeJxb+u^oT_q!hF?hdee+S~~)}pHTRb9Wqt%!)AvzodFJzRuxpulc>=7a z?_<<*eU0-p*!J3!&^Op9d$t&n)u9 z|0y`}UjWOE|04B@Mf{iG#<#tG+B3&L1KYnowtbRX%|4v(pMzby@Lz!S$$t1HSgqU- zufT1m&6qD!t0l&3U^TCUUs3;>;(lF>qE9FNz7AH;8T1>ln#Gq@zVCPgVoZJP&u25W z^!Hn^n)}81{vFu;GLoXrHg8g^+eV+?gVoIO66!xv%<&JD`24BXAHipS0=@;eo%+9t z`4?(m{ymYlKU38F{7IbOY5W!JXKQ|zrA?po`!}%tzC%gB?}F9C{|+v5{RiAO?-f4( z1gm?!Ilg~^ZKpoF*4_utscm_$@d29sX>*=@u2oClAA!}vKdyP&e*)J>J$?TN{BMf3 zjPt)>b1vuie{l80`V_3T93|~O1Gl#$&4zk%>IZjze9!3E`@>y-|Axbu`s?F<(%u1{ zL#9so9PZO+4oV72ry2&^U_O54Hki{J}#70HLd?bE+E(&qJ<^JPA`_RP=xVB@LV zm+uwSjP04TF!)hoYx8}AJpC>T&Y9qQ2DyH|U+_0|Idc{VdnVXkKkfSYK15r_wFFo# zd`YnRJXr7frNH{AyXTjo_T`?}wlqb}eJ#$OUlv^Md41By3Sj%l+utnpCr9BleIZr?wt8P9WXRdCLz)xfJ$+-J^VeAcM-oI`8E zZKt07xfa-V{ys6et_@bt_|^g6NuTODm)8ZWTXK*5GT1(}#cw@u`#o}fG<~%>Hs1@X znUl|^8-Ufy?;19QtKVO*>qcO;r}4juIq`fO0atgOH=*|BI;(F?QFEQe>1$JPdtaNO zsb>#u4pwtL{CmH|vHpP7u#{9eJ#(-fSj{;wuf(xVUY%h3Hdp=aBYABP)^1)qQ2R13^^p`c^Aabo9l`B+?S!VD z`Pvz*mibB?>*Tcy*uKqGfBP_x-Kdj?KD$!X%tM^(^DE%;`qamH+Kk3~gZHYr*UCO% zbIx~P`-1gRXY04Vv)L1_&9UxJeE`L4V-&TqPX`}Z>zR*(;MMa3ak5Saqp8Q|5U{b! z{rpw9dVIbHHcq+ThoY(P!yfZ}&tYI=+g95g`t7ljiw~e zF0gv`(^#;Odhp*@wMg`_bn0kzB2_52u11pZiCD$C0rg3D)k| z`>1_6cJ*l#HODSa{?ozcU#{Z}xO&Du6P&SYOWRrC_OTy@rk=4M16DhllCggitX_`& zTX6f)=Gc>~b;f=y*zq|Z`a2HCdOUT;s?Tv0HODH>HFyHJyax4gZ@C{&1iR<=uD_=_ z39M#u>?|EUHRq(0`s7;Q39PSscn-C?F@3H$4V>qSQ^9g=L$RF!&U3}-V7W2Qq#i}d zbH!O;pDS#ypLYFDq1KkZ&H<;dxnOzvIuD$_&IQYjaXz*05z^NMaQm{oe%kdrn_8Re z?z8*FV9%M0sO`i3{B5wh{uffq{ai%w7JldfxwB2UfHAa*gxcaXp$b^>NI`{0_K1=6BK5 z6Y~bJnuR8e;PYpV8zIKk$2q!+S}kL^39J@=bItR+m|NibsOS8<6|6poew?rGfqgk& z+HRw$IbUL9yH>Y@U8Ah$onXgyN8xi9Sl#Ds+uRLyj)vFmwP&rhdCjITYkAhn+P-{N z`aam}q&#na09Vhm|7+yLtuT>qdg2(UzIsZK99hYkNef{TlLGi_bAw$^vgW|2<$x1qfY;ifz{L36JWK+ zDQWj4xV_y|XzFSA3|Q@HO2+?VaQpb5MN`i>ege)o?Ati{WE{_d9gBXh_dV2V$?XNO zxjj#vG5r**p6mKWu$s@+?$e)veYsDyy+l!SpNfs+bI~urKd-sh`7go4D0wb=1+0%c zDOC59oZqjaX}9=tjQTi+m(jFmo_+;ZEBEBD;p*{uz4j^3uiv1lXARx}+fF_6`zAQ^ z>ppo6u3zT&w_x{$js8OI%Qe#WXNsC@B+fncui)~Ys!#g-8`#eY(#PN7YVT6a`5kIC{e4gJ z53rj3zfYb1)&HeVv43&a7857<8SeUDn6@%5#T#mV02=L0C^ zGknsAn)#%?wexc;bKil@>(hUK#+XBG4px5}ANfGAnxCQEiso7jf~)rjyBT$117aN}t+Kfk9`b3E(ew-h+fRenb)&pa*z&U2ODSIUjyca_7LgFII) z2lu(k_WEg0PAh=5`|knS*6&5tGM`@ptL0gIB``};{TmG0Y~y#K>S?nIxNI{NZkvTE z+HB+ZrRr(3D%j5*T?e1pR)f25%FjtxM^n$|YHNUPr*2HYb5%>s;owu4hw!y(p0TeD z*GJvkrat<6EpGt6pPchq`bKcI4JpR=J6^T?&B(^! zWof6LJ~jcTkKKvozr&5o6jWeS8gk07XA@+Mim@{yfVM1?Rr= zb+9kbarMI}YM$fbpup z-3?cpOPs_W4>$Je6x-{Q`Pb(CB;&A7>0^3eK z+GMc$p2hg5z%zdP)hFZEW}nlj`zS|J%6r`OT2J0H;O3qFW`gxmPn=m`t0C!R_;P6`H#KS5V6{m)C%eukC7T zx%gV@r|D~p`n$mE;7h{o&lvjZlYYMgw%_Zi6Z^Yh^^ElfuyNGw<5Ft1%-2m|_d>bv zZ-%SqytoCNYf+my8tX=|w)A}~*!bbMf%Qo)-vjHT9-rI6rOzF3{nXR$PO!S?WAeTW zZrYrxHtXW`aSvF{_G$NhaN0Qrxqiv#2jFrH_rlBk?t|;2o;LTl`8)vEPd#IJ z5UlR!9%=VOxceYJ4}sNu7SA*4!*J(*VT$(nJ_0tT_KfXOuwyIFo_TQf%*l_yYPL_i z$G~aloXGV{o{xi9ro{gVaON;RPok+Ozo)>qQ;*Nn;1wx}^9(rs#^=Xq>WT9#*mmj} z!%x7**Opk%fsIw}ML1hyhdI2`D?hgd}i=E zSk3F#_n~iqefj*N?Kc!PpI^j|&$I8hV9!41$-jyD9X$Q%(}~aT(bV(#?H|Bu7GLgR zpC^s?M>J#V<9ObrR!e_xfz@&i{26TfkrZvV`4hEz=HRd3mVujJd95vrrk-&v2UfFv@?IXCc8*J~U-DT2Y|h~;!pr==1lLDB`K<(2 z_jBI#vohR%%K2FZO+D{xhl15?pLWB*X=i?N{oKd79?GaPQ5a=zC>Q%{_=!M0PMUGJ@Rz{b~>oYn;!tDKuJqp7F= z^}zO@d*%9YwZzx}+&-=i(bSXAMqt~iC+-MvIX@f2+sCyDntI}F3bvhk# z+#Kw99gBAFAD!%pEx?`?J5ls;UF}m{?BCz_tIv7;w}z{kvwR!y9Nvq0_QC63 z&J^zlyHWe{zM^ecikfG&IB~uLHcoQi9c=EI+daVgqUXS9kB&=Ye4R)s{XE z0+)Rr+{O=qn?t@s{wiD__4M^MuyM2{pF_cpH)qaa;5p==o_1dc+g4k091S*4ZO*HG zt0muWfZfCCrwd-r>lnB`>S;3;T#j)Z+!&eHZg}QZU+sxM9$e1r1i1d{$$28!ezm2~ z9&p*`;cff~xH)8AC&Bem&-_dV8%JC6nF227wHNNZs;Avlux+&^$0Nb!sV(QjG_d2| ziK5;4HYat@`CKp78CM@T^WRCk>2R;xeqa-EKXwecF${$^GcszEka++|{>dzx(;hH^J?nuY3zlJ^3CB_PeI+)8pZ4$59gJ z1aSLjE3?tmGmbf6W7}R^=IcbTdpPTG5?o((b3TSzE&eBi)wF%e#K~hj1?=2wV|f~K zZGJv`Dmb6do&omd*{6OwH})XftBHqzef~}kSD@#!vdh8p#Jvja zKC|7GV7cEBchdf9@Q&2B(WcLQ#8yxHYry6A;Mc;{yr%SV4_pU#58S{S$ghX%r=Gv> z`wm#$d*lt&zT7A3-=(Oz4r2Gft>ked*!P(I>v7pYMNffvfi? z7uPMZt@HPIw}Q>bwbb8!Z)RRxx7)zl#}IQ4+<6&H9_r?{Kl=B;=H~a#&VzridOMnW zeD0`y%p*Q`qN$%x9NXLlww<~@o%DA%*lRKW*3ms+HDA%bUu%SJX+Nkn!m^K=f5&<+ jn(g)1J_lR=j`cpc{p(}S{@X40gUf%rMXq1|ZI%BAk|rX+ diff --git a/packages/ludic.render3d/shaders/spv/cb13f3c6.frag.spv b/packages/ludic.render3d/shaders/spv/cb13f3c6.frag.spv index a7862f95f0c31c2c3b60650ed2683a3c2e3f43aa..f7b38d76c8478a4010065a2c08ef5f90f2483745 100644 GIT binary patch delta 6047 zcmZ9Q3w&2)8OP5y#&x7fT(UqBQ51AQMF9;MhF}>&DWzd!!eGQ@Y;0gj9TBZagzS$| z26VAZN)4NqQ7LKJ#V$k_wJW7WC3d4wa9ZE**?I7=|Hqf}`~9BF`@HXa&hzf$p=0G6 zpD*uQQ9is!l9VNVlAg&6H}`Go+ZXyY<2lCBdp1-L7^kwjuDPv#X+u((R3xXso0BBN z1HsEO+%2gFuWGDc*tDi}Y`|G%6HCJeR$n;{abD4+v3IxI}^-+FKAju zvTn)DF1~N-!Y}T^XO-?6cv?EAi+X7nKKBG(kz5X6omW!_zN&t~QfOAeV)&Yt`jx?( z;H`~IRs_EZUR=$V9!XjdDw6FutR}nrbQ`#_u@mnE&uW^P>33&%ZZ=^%cvWLVgEP1V zkHU)+JlTaG=)w>7NKXv#=$h)-(WaIx;h_@(DwAj7ZFQ}y>KYrCEN*Pgd_3yabuCS; zc@al5zM`SNrL(AGSWeaz9Nif+kgY}TPww2q zz*Pl)hDLtctwaBAIkygZ&zxI_@s$N$h%a3-wAVSfWVx)dU`smk*z(-o(Oy4v(n)j5 zkVhbmVp7YlgdZ9C)YM7vNeC{ftYAM4zG%_yQ^NmT_&M`7PaaM5Dfo>qJ#*fmbR{as zGA+ECGk)C#@?Kcl;KO-_eI0!BLv53TUk@j1P4N4&_`S8kcW3=p@1pk2_89DFxvi=L7x4C@EX!aVI9N`~*)UzCDo~A!} zD_iC1xmA;rKAn5#@s*W^R1fbJAsy`#sz;TjHV}iA`|z}h6uFJ0$j23UfBawHg-pQKW^(plmJAAC&hySvwjI#R_x!otYv&`51>V89kyqsfMirkmkK}*+<7;z1<7B`do-d-!&c7&g z>ocCNVEklW+zz;UEwAT3U^#d%Prv3GUMj!D3b48y_cZAS*$BFUAmh8k)%@hEP_mq0 zi|L))!?!28#r5F1E|2d8wkU0z(44e4*on18A$`DWyVIskrc)JyqRy^nEiMi$#^a)&v4}Fk7k~15e)>q9HRIjO|XjnbHxMT zuGl81eLJE>`HgCeRSW`K=$3r-gW+l;a(}1&xJ|N%^N6*Ml$HYry_o zk;YduY8b94!+Bw^MVm+>zYYt19b9cZ!@}&@YGV8Kete^dN$^E*ZT9X|VM6|bz5(1u zLK7IMgN|Mb+FY4MT9AdsPE7_E3z`B~i-M+tGmTue;6g!ZasfhAGz~0|g5C)B4@46f zs67hORxHTEVwKau7Ok&0oLuZ*#CymWgYxp+2DUtT+@&HJtJj+7+3@``}T+YmJvFo&l+5?=;(HQFxaDG%G* z!Lf^T!SV@o9^a35fY?EzE+3If=;(GVu(a21LKVqSQRSfBH_8D;Tl30n`i^bJ}wZ%M*;OQ)~)-JIR=OqZw?7PiR`4F4`kAnar<&XR1o~>Q3v1&UbF2ZYdo`xsjaNeyALb}*SMLS;vt4O>4@1rF zAdW=WfSoXQV=Y*1E0sktYJSWS?=z0!eclp&Z1GaOAEVXwt9;4H9a4{r|5`ACiFy=r z9oRFDLe_)VG1SB72Jm7=_oZSb(25XGl;X`02)fUM%fL(d`d>E{sdidN3 z_M;A;jbQ!M_3=yE~-2~pmn2?|CX7JTGWjdMxA3-ybwn%a_*pJ&q=<`vqn!X-` zHns3CfuqHxTOj*60w*>27WkPw)nm&)1~y1rtY#}%{Z8IakI%mMad-#0c7bCTK7pnl zakqkvRmW$1Ht8oJ#T9IWtLYpoP>c6uN7lMNg|dSY>-sdf*t$Lg*GD~C*Jr_gPqr@Y zX5Y?JJ@R}GtQNC<9^AD#-0rKZAvXOBU;{B6pX?5BOs3t*ZsVyQlYJ5FT@?IFaDAfd z+zHl4J-W$V;4xK7qV2ERBlq24ZRV!l z11)lD-_3}e={F%d z45Q0?3?}^++{v`L-R(TpqUs01P8$7n57-Ipug&Cn0IpB?e;aI$nDimA8gc3I2^1sX zVQ?|vJ8-qZ%n}8u#mn_waMzai2%382{2thx@eO?+{D`efLpc2aTwQSbAy`Lsef+q8 z1deX=C|Dld=Eq>Wje1P<6R>%-h4xdhddWXi+_s4>0Z^SnglvZtq{g8+e+} zIPFo4wk$vIU-AqjCO-t0$4&Bg@IZXjW72Kp}wAOX>y5ib9D# zj54sYGAu20tdue*%@*5;7P1u$tw39>5P_Ee|NXwh?`6K9m-{@=IrrRim-k#`^YQZM zkCj(fln?EmBxOlu(jz&swYs^w3VMk467A^yn`-(^RasNFs;z!$L((;=NczB6B}s<+ zftO{tOELz$x~YCq^IC0PlFQ&L>s#uVCxt`(E-0H^=+nRE`e}$Q&25XDrWXTDhc9ei zmJOJ3azHV_m7Vw-I`OMI@mYnP{RgMlbW-P@!Yh(_@HM5B-wwW^e&JGRmO&$YZA<;i z;49#*O-oh;-wZDgJi5eqV;K%_e*Ryt=8O z!5Q4MC*k=Cp6SF7cH+-<;)lAYNzz=$jyAVs0S}+z&^0*%Z>wuvUDwpGWN}k#=HpSX zscUI&EhTX@<0~5KTZ&1&oQ+wuq_MHck7ayC%kq-{@r>7Jd`A0~0~dEqy)%O_ccaZ< zUEV0UKe=fP0axYt`5O6YHzobMl(;F$dz82-IlgO-=f?L+%u$E#g);{Y?pa=PJKkPD zXu@f85{$!dnZ)$k0%=pb$$a`XGg%70<`+E46$J!mgorv!flKC##S zY&`MPWIw`%OmH3({smkC-bQm-at}c*0{jzxWW@ZF%la35{+9B|qu5qp@^AjoN8K2Q z&^$PQ@>aIWgL11TBxe`*&XXz%#Q~mFQmC#O+CHUbSXpZEP;q=Ey#*xCEh2e7Hplzo ze_1E~njAOY_#D@N@~OOcG9@Qe7M{MiZ?z?f|FV?&7RtsB9c$q^sbI121a}e(PoB>z z%pF@SJbBf^6Wmnh<+xESK4F(zeDd7l6Ws9@pFFqt&*p#N|I)_smG8nf ziQ9Psu3pO>+=o_1yuI|soyeW>b1Mg{%W+LAz?%WLFh<@LuI6W5m2^upu^YsD*{kFb z&fU>Z$Dn;fDL@afNol(c%}INLomg8W(hIzGS2<^p13`}~4jSR_&dUO>E9(8chYls~%g26)+C1@`BPG+sh`EzK2) zBe7S5FDH;+hub_3u68NS#4O2bV$1OXd?Sg=;f-)@mgZDpd{Vrj6Toc*G=Kwj(9xQ& z&6Sy?37J?VG!dLnXp%NsBxKE3Gu9OPTtaCw6(KUJ1v~oC}tZr}TI~ z-U8l8?*d(j37eP+4RI2KImp1%=^YUD{<^}}X?@eTqFhId^}h}5zHKaB!TE4~)T!{< zwAX|E`lr#f8+{(Vdc>&%tEF6dQ(XX7i`nbJyf$gFhe8^IZa~x!^>HECf$C9g7lEC8 z1WmgeAGU^)?JP9?ZsKJzIp2S6G0$S~G!|KFmDq=K6M}DFKA9zG>XFPlzz$T8*I+4F zeGg;(Xg%m= z39PoA%pw^zKjs*3AIGrHSHh1iF2%bsnr$Ec3KLH6K~r}UFOoaPz(hR~*#P#8BaxfI z>uKuY^ImWxEqrbPZ?sBih_ibi*ufg(b$CD6Nwr0=4}e{H_-q2}ryf2Z1p85k&xgSJ zsq5onZZ&X}smiCSy zCcOiaU%@BfYC6XX)Z(7(%u3gtC_8Dfu1|vVrR!60ebl3LeH!fdWa-jw^d0o-5$7{t zwV3U*;LgS2b6#ByvFUe%9f;xNWS>jXhQSdLo^@*zUMX)~VQBCdz zmoa}<19gaUvJ0#+f_({`5B_DiD~sT}!TP92@cY3gZGF`qvF`(GGdB4q?Ot6cjDcSP zJJ90W4t|gpAKO>K+Wmi%2=FzyL8FvC1nyw0dc@oVj+ojXrbW#3>ku7=P~|-av-$?y z$+WrM574W{q~8QPY1G$8!A@X(T}7Nn;QEAr2iO=f>9@dYj7v{Wpcn%l1Lp@k4p$q< zERm2}T&{0}JD0pC(9|R5cfiJsZ|J+=CoEkW!s&b9F*&F2gLPEb$B+95;HWlFg5^z47yJv3tMJSJ2}0*De=nMPY~4@6p1*n=*FLbXg|?XBXJ9AM z7K#5HJe}q^7xD{ms;INus!=fp{1O~5i|^pC;BNE&l8<-gDX@A>{A;k|)FaMs!1;-P z3s=Lpc>Wp{W5DmgYCG8SJDKG7V6{8ozO{b-QGWgH_;oNL-a4Ux6K}~{Qp{Hmy!Sg diff --git a/packages/ludic.render3d/shaders/spv/cb13f3c6.vert.spv b/packages/ludic.render3d/shaders/spv/cb13f3c6.vert.spv index 4858c09915a992f7ff658b2f685c8ca3119a4818..6c4bf083b772a501b8172cd2030b0920be885d74 100644 GIT binary patch literal 33480 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 HuDSYu*C&St diff --git a/packages/ludic.render3d/shaders/spv/f2952361.frag.spv b/packages/ludic.render3d/shaders/spv/f2952361.frag.spv index 1baabb14cc532f6fe85d3ccb028a083e1ae09e55..e31ae9e532935e700be886a4009c94e08a535916 100644 GIT binary patch delta 23793 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 delta 21899 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 D7n87s diff --git a/packages/ludic.render3d/shaders/spv/f4382918.frag.spv b/packages/ludic.render3d/shaders/spv/f4382918.frag.spv index 9f268a77983b3378560e2adce02e95030c051d64..1aef3e6596008aaad557136fc98abd5b86b8dffa 100644 GIT binary patch delta 21833 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 delta 20093 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 IrkYFs2T5Lu$^ZZW delta 5946 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 GS3M8H!-oX` diff --git a/packages/ludic.render3d/shaders/spv/grass_cull.comp.spv b/packages/ludic.render3d/shaders/spv/grass_cull.comp.spv index 4b9c926000f26850a2e1d63b9e66b526f51582a5..c9656fde0cf980d946e17d5fbf43e0726246aa8d 100644 GIT binary patch literal 25236 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 diff --git a/packages/ludic.render3d/shaders/spv/manifest.txt b/packages/ludic.render3d/shaders/spv/manifest.txt index 78000865..b494cca9 100644 --- a/packages/ludic.render3d/shaders/spv/manifest.txt +++ b/packages/ludic.render3d/shaders/spv/manifest.txt @@ -2996,10 +2996,11 @@ U 2e2908c8 frag u_half 828 float 1 0 U 2e2908c8 frag u_texel 832 float 1 0 U 2e2908c8 frag u_ortho_on 836 float 1 0 U 2e2908c8 frag u_carpet_on 840 float 1 0 -U 2e2908c8 frag u_snow_line 844 float 1 0 -U 2e2908c8 frag u_lake_level 848 float 1 0 -U 2e2908c8 frag u_wet 852 float 1 0 -U 2e2908c8 frag u_sea_level 856 float 1 0 +U 2e2908c8 frag u_grass_reach 844 float 1 0 +U 2e2908c8 frag u_snow_line 848 float 1 0 +U 2e2908c8 frag u_lake_level 852 float 1 0 +U 2e2908c8 frag u_wet 856 float 1 0 +U 2e2908c8 frag u_sea_level 860 float 1 0 U 2e2908c8 frag u_lake 864 vec4 1 0 U 2e2908c8 frag u_origin 880 vec2 1 0 U 2e2908c8 frag u_view 896 mat4 1 0 @@ -3073,10 +3074,11 @@ U 1f95f31b frag u_half 828 float 1 0 U 1f95f31b frag u_texel 832 float 1 0 U 1f95f31b frag u_ortho_on 836 float 1 0 U 1f95f31b frag u_carpet_on 840 float 1 0 -U 1f95f31b frag u_snow_line 844 float 1 0 -U 1f95f31b frag u_lake_level 848 float 1 0 -U 1f95f31b frag u_wet 852 float 1 0 -U 1f95f31b frag u_sea_level 856 float 1 0 +U 1f95f31b frag u_grass_reach 844 float 1 0 +U 1f95f31b frag u_snow_line 848 float 1 0 +U 1f95f31b frag u_lake_level 852 float 1 0 +U 1f95f31b frag u_wet 856 float 1 0 +U 1f95f31b frag u_sea_level 860 float 1 0 U 1f95f31b frag u_lake 864 vec4 1 0 U 1f95f31b frag u_origin 880 vec2 1 0 U 1f95f31b frag u_view 896 mat4 1 0 @@ -3150,10 +3152,11 @@ U f4382918 frag u_half 828 float 1 0 U f4382918 frag u_texel 832 float 1 0 U f4382918 frag u_ortho_on 836 float 1 0 U f4382918 frag u_carpet_on 840 float 1 0 -U f4382918 frag u_snow_line 844 float 1 0 -U f4382918 frag u_lake_level 848 float 1 0 -U f4382918 frag u_wet 852 float 1 0 -U f4382918 frag u_sea_level 856 float 1 0 +U f4382918 frag u_grass_reach 844 float 1 0 +U f4382918 frag u_snow_line 848 float 1 0 +U f4382918 frag u_lake_level 852 float 1 0 +U f4382918 frag u_wet 856 float 1 0 +U f4382918 frag u_sea_level 860 float 1 0 U f4382918 frag u_lake 864 vec4 1 0 U f4382918 frag u_origin 880 vec2 1 0 U f4382918 frag u_view 896 mat4 1 0 @@ -3227,10 +3230,11 @@ U f2952361 frag u_half 828 float 1 0 U f2952361 frag u_texel 832 float 1 0 U f2952361 frag u_ortho_on 836 float 1 0 U f2952361 frag u_carpet_on 840 float 1 0 -U f2952361 frag u_snow_line 844 float 1 0 -U f2952361 frag u_lake_level 848 float 1 0 -U f2952361 frag u_wet 852 float 1 0 -U f2952361 frag u_sea_level 856 float 1 0 +U f2952361 frag u_grass_reach 844 float 1 0 +U f2952361 frag u_snow_line 848 float 1 0 +U f2952361 frag u_lake_level 852 float 1 0 +U f2952361 frag u_wet 856 float 1 0 +U f2952361 frag u_sea_level 860 float 1 0 U f2952361 frag u_lake 864 vec4 1 0 U f2952361 frag u_origin 880 vec2 1 0 U f2952361 frag u_view 896 mat4 1 0 @@ -3304,10 +3308,11 @@ U a0f2dbac frag u_half 828 float 1 0 U a0f2dbac frag u_texel 832 float 1 0 U a0f2dbac frag u_ortho_on 836 float 1 0 U a0f2dbac frag u_carpet_on 840 float 1 0 -U a0f2dbac frag u_snow_line 844 float 1 0 -U a0f2dbac frag u_lake_level 848 float 1 0 -U a0f2dbac frag u_wet 852 float 1 0 -U a0f2dbac frag u_sea_level 856 float 1 0 +U a0f2dbac frag u_grass_reach 844 float 1 0 +U a0f2dbac frag u_snow_line 848 float 1 0 +U a0f2dbac frag u_lake_level 852 float 1 0 +U a0f2dbac frag u_wet 856 float 1 0 +U a0f2dbac frag u_sea_level 860 float 1 0 U a0f2dbac frag u_lake 864 vec4 1 0 U a0f2dbac frag u_origin 880 vec2 1 0 U a0f2dbac frag u_view 896 mat4 1 0 @@ -3381,10 +3386,11 @@ U 5e4589a1 frag u_half 828 float 1 0 U 5e4589a1 frag u_texel 832 float 1 0 U 5e4589a1 frag u_ortho_on 836 float 1 0 U 5e4589a1 frag u_carpet_on 840 float 1 0 -U 5e4589a1 frag u_snow_line 844 float 1 0 -U 5e4589a1 frag u_lake_level 848 float 1 0 -U 5e4589a1 frag u_wet 852 float 1 0 -U 5e4589a1 frag u_sea_level 856 float 1 0 +U 5e4589a1 frag u_grass_reach 844 float 1 0 +U 5e4589a1 frag u_snow_line 848 float 1 0 +U 5e4589a1 frag u_lake_level 852 float 1 0 +U 5e4589a1 frag u_wet 856 float 1 0 +U 5e4589a1 frag u_sea_level 860 float 1 0 U 5e4589a1 frag u_lake 864 vec4 1 0 U 5e4589a1 frag u_origin 880 vec2 1 0 U 5e4589a1 frag u_view 896 mat4 1 0 diff --git a/packages/ludic.render3d/shaders/terrain.frag b/packages/ludic.render3d/shaders/terrain.frag index a2d2cb2a..12877b55 100644 --- a/packages/ludic.render3d/shaders/terrain.frag +++ b/packages/ludic.render3d/shaders/terrain.frag @@ -16,6 +16,7 @@ uniform float u_ortho_on; uniform sampler2D u_rock_d; uniform sampler2D u_rock_n; uniform sampler2D u_rock_a; uniform sampler2D u_snow_d; uniform sampler2D u_carpet; // the clump cards baked straight down (alpha = coverage) uniform float u_carpet_on; +uniform float u_grass_reach; // where the GPU blades stop (0: none): the ground under them is in their shade uniform float u_snow_line; uniform float u_lake_level; // the ground just above the water is wet and dark // ---- RAIN THAT LEAVES SOMETHING BEHIND ----------------------------------------------------- @@ -339,6 +340,18 @@ vec3 groundShade(vec3 p, vec3 N, float slope, float dist, float viewDepth, bool vec3 cc = cp.rgb * vec3(0.5, 0.57, 0.45) * mix(vec3(0.85, 0.92, 0.9), vec3(1.1, 1.05, 0.85), smoothstep(-0.35, 0.35, N.z)) * (0.75 + 0.35 * lush); gA = mix(gA, cc, max(cp.a, 0.35) * carpetW * 0.97); } + // The blades' own patches (grass_cull.comp's bladeField): patchiness and dry patches, so where + // the blades thin out the ground they stand on carries the same meadow, not a lawn + { + float patchy = fbmC(cheap, p.xz * 0.045, 3) * 0.5 + 0.5; + float dryp = smoothstep(0.2, 0.8, gnoise(p.xz * 0.33 + 17.0) * 0.5 + 0.5); + vec3 pf = mix(vec3(0.7, 0.8, 0.55), vec3(1.1, 1.05, 0.85), patchy) * mix(vec3(0.92, 1.0, 0.95), vec3(1.30, 1.10, 0.62), dryp); + // and the tussocks' own shade, half a metre across: where blades stand a pixel wide the ground + // is most of what shows, and it has to carry the same light and dark as the blades on it + float tus = 0.55 + 0.80 * (gnoise(p.xz * 1.9 + 41.0) * 0.5 + 0.5); + gA *= mix(vec3(1.0), pf / vec3(0.95, 0.96, 0.72) * tus, grassW); + if (u_grass_reach > 0.0) gA *= mix(1.0, mix(0.50, 1.0, smoothstep(u_grass_reach * 0.25, u_grass_reach, dist)), grassW); + } // Subalpine forest floor: dark duff where the stands are dense. The photograph-driven // term marks duff only where the survey actually shows dense conifer, and is the same // at any distance — ground shading must not depend on where the viewer is. diff --git a/packages/ludic.render3d/terrain.ludic b/packages/ludic.render3d/terrain.ludic index ecf7866c..5cf87f24 100644 --- a/packages/ludic.render3d/terrain.ludic +++ b/packages/ludic.render3d/terrain.ludic @@ -418,6 +418,9 @@ function terrain_bind_prog(render3d_st: mut Render3dState, p: int) -> void { r3d_bind_2d(render3d_st, p, "u_snow_d", 10, render3d_st.ter_tex[9]) if render3d_st.ter_carpet != 0 { r3d_bind_2d(render3d_st, p, "u_carpet", 11, render3d_st.ter_carpet); u_f(render3d_st, gpu_uniform(render3d_st, p, "u_carpet_on"), 1.0) } else { r3d_bind_2d(render3d_st, p, "u_carpet", 11, render3d_st.ter_tex[0]); u_f(render3d_st, gpu_uniform(render3d_st, p, "u_carpet_on"), 0.0) } + var reach = 0.0 + if grass_live(render3d_st) { reach = render3d_st.grass_radius } + u_f(render3d_st, gpu_uniform(render3d_st, p, "u_grass_reach"), reach) u_f(render3d_st, gpu_uniform(render3d_st, p, "u_half"), float(render3d_st.TERRAIN_HALF)) u_f(render3d_st, gpu_uniform(render3d_st, p, "u_texel"), 1.0 / float(TERRAIN_RES)) u_f(render3d_st, gpu_uniform(render3d_st, p, "u_snow_line"), render3d_st.ter_snow_line) diff --git a/runtime/native/vk.ludic b/runtime/native/vk.ludic index 2a6985ac..24bca19b 100644 --- a/runtime/native/vk.ludic +++ b/runtime/native/vk.ludic @@ -25,6 +25,9 @@ import "vk_api.ludic" # Vk.sl_active() says whether it was. Each Vk.sl_* is the sl* export of the same name and returns # its sl::Result (0 is eOk); -1 means there is no Streamline (macOS, or the DLL missing). The # structs are plain memory, laid out as the SDK headers (include/sl_*.h, SDK 2.14.1) declare them. +# Once a frame: on macOS drains what MoltenVK and the layer autoreleased during the last one (a +# program pumping its own events has no other pool); nothing elsewhere. +extern function vk_frame_pool() = "lvk_frame_pool" extern function vk_sl_prefer(on: int) = "lvk_sl_prefer" extern function vk_sl_active() -> int = "lvk_sl_active" extern function vk_sl_init(pref: pointer, sdk_version: long) -> int = "lsl_slInit" diff --git a/runtime/native/vk_mac.ll b/runtime/native/vk_mac.ll index e34cd65b..9cd51b9c 100644 --- a/runtime/native/vk_mac.ll +++ b/runtime/native/vk_mac.ll @@ -225,3 +225,26 @@ entry: %r = call i64 %fn(ptr %a0, ptr %a1) ret i64 %r } + +; ---- the frame's autorelease pool -------------------------------------------------------------- +; MoltenVK and CAMetalLayer autorelease objects on every frame (the drawable, each render pass's +; descriptor), and a program that pumps its own events never drains a pool: without this they are +; kept for the life of the process - 160 MB/s of small allocations in a windowed valley. +; lvk_frame_pool() pops the pool the last frame pushed and pushes the next one. +declare ptr @objc_autoreleasePoolPush() +declare void @objc_autoreleasePoolPop(ptr) +@lvk_pool = internal global ptr null + +define void @lvk_frame_pool() { +entry: + %old = load ptr, ptr @lvk_pool + %have = icmp ne ptr %old, null + br i1 %have, label %pop, label %push +pop: + call void @objc_autoreleasePoolPop(ptr %old) + br label %push +push: + %p = call ptr @objc_autoreleasePoolPush() + store ptr %p, ptr @lvk_pool + ret void +} diff --git a/runtime/native/vk_win.ll b/runtime/native/vk_win.ll index 5bca3814..2263e3e5 100644 --- a/runtime/native/vk_win.ll +++ b/runtime/native/vk_win.ll @@ -363,3 +363,9 @@ entry: %r = call i64 %fn(ptr %a0, ptr %a1) ret i64 %r } + +; the frame's autorelease pool is a macOS matter (vk_mac.ll); Windows has nothing to drain +define void @lvk_frame_pool() { +entry: + ret void +}