feat(render3d): Vulkan in a macOS window through MoltenVK; window built-ins take a slice's elements; the NEAR_FADE SPIR-V

- a CAMetalLayer on the view (cocoa.ll win_metal_layer), VK_EXT_metal_surface, QuartzCore linked
  with Vk.*; the drawable measured after the layer sets the backing scale
- vk_mac.ll opens MoltenVK directly after any loader: a bundle ships only libMoltenVK.dylib
- a covered window is not presented to (win_visible); one frame in flight on macOS
  (R3D_VK_INFLIGHT), with images, buffers and descriptor pools held until it is done
- win_held / win_mouse / win_pad / win_touch / win_text / win_present pass slice elements (arg_buf):
  every windowed program died on its first input poll
- variants.list and SPIR-V for the three NEAR_FADE foliage programs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-27 00:52:45 +03:00
parent 9862ec82e3
commit 38d4ea72b1
25 changed files with 627 additions and 164 deletions

View file

@ -0,0 +1,4 @@
bump: patch
type: fix
**The foliage's near-fade programs have SPIR-V.** The three `NEAR_FADE` variants were missing
from `variants.list`, so on Vulkan the trees' and cards' programs failed to build.

View file

@ -0,0 +1,10 @@
bump: minor
type: feature
**render3d's Vulkan renderer runs in a macOS window, through MoltenVK.** `R3D_GFX=vk` (or
`gpu_request("vulkan")`) now presents to a `CAMetalLayer` on the window (`VK_EXT_metal_surface`,
the runtime's `win_metal_layer()`), where it used to fall back to OpenGL. The loader also opens
MoltenVK on its own - `Contents/Frameworks/libMoltenVK.dylib` in a bundle, beside the executable,
or `$VULKAN_SDK/lib` - so a game needs no Vulkan SDK to ship it. A covered window is not drawn to
(its layer would hold every frame for a second), and on macOS one frame stays in flight so the CPU
runs the next tick while the GPU draws. OpenGL stays the default: on an M4 Pro the Vulkan frame is
still about a third slower, most of it in the alpha-tested foliage.

View file

@ -0,0 +1,7 @@
bump: patch
type: fix
**A windowed program's input no longer stops it on the first frame.** Since slices carry their
length, the window's built-ins (`win_held`, `win_mouse`, `win_pad`, `win_touch`, `win_text`,
`win_present`) were handed the slice's header instead of its elements, so the platform wrote the
mouse position over the length and the next read failed its bounds check. They take the elements,
as an `extern` call already did.

View file

@ -199,6 +199,10 @@ export state Render3dState {
gvk_al_spare: []int = null # ids to reuse
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_frame_pending: bool = false
gvk_frame_pending_cb: pointer = null
gvk_inflight: int = -1 # -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
gvk_qpool: long = 0

View file

@ -49,9 +49,10 @@ function gpu_select(render3d_st: mut Render3dState) -> int {
if render3d_st.gpu_wanted == 0 { render3d_st.gpu_wanted = GPU_GL }
render3d_st.gpu_kind = GPU_GL
if render3d_st.gpu_wanted == GPU_VK {
# a window needs a surface, and only the Win32 one is built
# a window needs a surface: Win32 on Windows, a CAMetalLayer through MoltenVK on macOS
render3d_st.gvk_want_surface = is_windowed()
if is_windowed() and Os.platform() != "windows" { render3d_st.gpu_fallback_reason = "the Vulkan window is built for Windows only" }
let os = Os.platform()
if is_windowed() and os != "windows" and os != "macos" { render3d_st.gpu_fallback_reason = `no Vulkan window is built for {os}` }
else if not gvk_init(render3d_st) { render3d_st.gpu_fallback_reason = render3d_st.gvk_why }
else if not gvk_manifest(render3d_st) { render3d_st.gpu_fallback_reason = "the renderer's SPIR-V manifest is missing" }
else { render3d_st.gpu_kind = GPU_VK }

View file

@ -43,6 +43,11 @@ function gvk_ext_in(props: bytes, n: int, want: string) -> bool {
# device with the Tier 1 floor switched on. False, with gvk_why set, if any of it is missing:
# the caller stays on OpenGL.
function gvk_surface_ext() -> string {
if Os.platform() == "macos" { return VK_EXT_METAL_SURFACE_EXTENSION_NAME }
return VK_KHR_WIN32_SURFACE_EXTENSION_NAME
}
function gvk_init(render3d_st: mut Render3dState) -> bool {
if render3d_st.gvk_ready { return true }
gsl_boot(render3d_st)
@ -72,12 +77,13 @@ function gvk_init(render3d_st: mut Render3dState) -> bool {
Vk.put_ptr(iext_names, n_iext * 8, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); n_iext += 1
Vk.put_i32(ici, VkInstanceCreateInfo_flags, VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR)
}
# a window's surface: only the Win32 one is built, so a window elsewhere stays on OpenGL
render3d_st.gvk_has_surface = render3d_st.gvk_want_surface and gvk_ext_in(iexts, nie, VK_KHR_SURFACE_EXTENSION_NAME) and gvk_ext_in(iexts, nie, VK_KHR_WIN32_SURFACE_EXTENSION_NAME)
if render3d_st.gvk_want_surface and not render3d_st.gvk_has_surface { render3d_st.gvk_why = "no Win32 surface in this Vulkan instance"; return false }
# a window's surface: Win32 on Windows, Metal (MoltenVK) on macOS
let surf_ext = gvk_surface_ext()
render3d_st.gvk_has_surface = render3d_st.gvk_want_surface and gvk_ext_in(iexts, nie, VK_KHR_SURFACE_EXTENSION_NAME) and gvk_ext_in(iexts, nie, surf_ext)
if render3d_st.gvk_want_surface and not render3d_st.gvk_has_surface { render3d_st.gvk_why = `no {surf_ext} in this Vulkan instance`; return false }
if render3d_st.gvk_has_surface {
Vk.put_ptr(iext_names, n_iext * 8, VK_KHR_SURFACE_EXTENSION_NAME); n_iext += 1
Vk.put_ptr(iext_names, n_iext * 8, VK_KHR_WIN32_SURFACE_EXTENSION_NAME); n_iext += 1
Vk.put_ptr(iext_names, n_iext * 8, surf_ext); n_iext += 1
}
# HDR output: an instance only lists the HDR colour spaces when it asks for them
render3d_st.gvk_has_colorspace = render3d_st.gvk_has_surface and gvk_ext_in(iexts, nie, VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME)
@ -488,6 +494,9 @@ function gvk_cmd_init(render3d_st: mut Render3dState) -> bool {
render3d_st.gvk_fence = bytes(8)
r = Vk.create_fence(render3d_st.gvk_dev, fci, null, render3d_st.gvk_fence)
if r != VK_SUCCESS { return gvk_fail(render3d_st, "vkCreateFence", r) }
render3d_st.gvk_frame_fence = bytes(8)
r = Vk.create_fence(render3d_st.gvk_dev, fci, null, render3d_st.gvk_frame_fence)
if r != VK_SUCCESS { return gvk_fail(render3d_st, "vkCreateFence", r) }
return true
}
# a command buffer, begun
@ -510,6 +519,7 @@ function gvk_once_begin(render3d_st: Render3dState) -> pointer {
}
# end it, submit it, wait for it, free it
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)
@ -529,12 +539,61 @@ function gvk_once_end(render3d_st: mut Render3dState, cb: pointer) -> bool {
return true
}
# ---- one frame in flight -------------------------------------------------------------------
# A window's frame is submitted and presented without waiting, so the game's next tick runs on the
# CPU while the GPU draws: waiting at the present serialised the two, which cost MoltenVK a third
# of its frame rate against OpenGL. Anything that would reuse what that frame reads - the next
# frame's command buffer and ring, a one-off submit, a descriptor pool reset, a swapchain rebuild -
# waits for it first (gvk_frame_wait), and a buffer it reads is busy (gvk_buf_busy). On by default
# on macOS; R3D_VK_INFLIGHT=0/1 says otherwise.
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 }
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
}
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)
Vk.put_ptr(cbs, 0, cb)
Vk.reset_fences(render3d_st.gvk_dev, 1, render3d_st.gvk_frame_fence)
let si = bytes(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)
Vk.put_ptr(si, VkSubmitInfo_pCommandBuffers, cbs)
r = Vk.queue_submit(render3d_st.gvk_queue, 1, si, Vk.get_i64(render3d_st.gvk_frame_fence, 0))
if r != VK_SUCCESS { return gvk_fail(render3d_st, "vkQueueSubmit", r) }
render3d_st.gvk_frame_pending = true
render3d_st.gvk_frame_pending_cb = cb
return true
}
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)
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
render3d_st.gvk_frame_pending_cb = null
gvk_retire_flush(render3d_st)
}
# ---- teardown -----------------------------------------------------------------------------
function gvk_shutdown(render3d_st: mut Render3dState) -> void {
if not render3d_st.gvk_ready { return }
gvk_frame_wait(render3d_st)
Vk.device_wait_idle(render3d_st.gvk_dev)
gsl_shutdown(render3d_st)
Vk.destroy_fence(render3d_st.gvk_dev, Vk.get_i64(render3d_st.gvk_fence, 0), null)
Vk.destroy_fence(render3d_st.gvk_dev, Vk.get_i64(render3d_st.gvk_frame_fence, 0), null)
Vk.destroy_command_pool(render3d_st.gvk_dev, render3d_st.gvk_pool, null)
Vk.destroy_device(render3d_st.gvk_dev, null)
Vk.destroy_instance(render3d_st.gvk_inst, null)

View file

@ -729,6 +729,7 @@ function gvk_sc_clear(render3d_st: mut Render3dState) -> void {
# the pool is full: finish the frame recorded so far (it may use sets from it), then start again
function gvk_kpool_reset(render3d_st: mut Render3dState) -> void {
gvk_flush(render3d_st)
gvk_frame_wait(render3d_st)
Vk.reset_descriptor_pool(render3d_st.gvk_dev, render3d_st.gvk_kpool, 0)
gvk_sc_clear(render3d_st)
}
@ -953,6 +954,7 @@ function gvk_screen_make(render3d_st: mut Render3dState, w: int, h: int) -> bool
function gvk_frame_cb(render3d_st: mut Render3dState) -> pointer {
if render3d_st.gvk_cb == null {
gvk_frame_wait(render3d_st)
gvk_frame_reset(render3d_st)
render3d_st.gvk_cb = gvk_once_begin(render3d_st)
}
@ -1279,7 +1281,13 @@ function gvk_flush(render3d_st: mut Render3dState) -> void {
# - and that image is presented.
function gvk_present(render3d_st: mut Render3dState) -> void {
gvk_prof_frame(render3d_st)
if render3d_st.gvk_swap != 0 { gvk_present_window(render3d_st); render3d_st.gvk_frame_no += 1; gvk_retire_flush(render3d_st); return }
if render3d_st.gvk_swap != 0 {
gvk_present_window(render3d_st)
render3d_st.gvk_frame_no += 1
# what the frame in flight retired goes when it is done (gvk_frame_wait)
if not render3d_st.gvk_frame_pending { gvk_retire_flush(render3d_st) }
return
}
if render3d_st.gvk_cb == null { return }
gvk_pass_end(render3d_st)
gvk_once_end(render3d_st, render3d_st.gvk_cb)
@ -1332,6 +1340,8 @@ function gvk_open(render3d_st: mut Render3dState, w: int, h: int, title: string)
if is_windowed() {
win_open(w, h, 1, title) # the window exists before main: this retitles it
win_gl_resize(w, h)
# macOS: the Metal layer sets the backing scale, so it exists before the drawable is measured
if Os.platform() == "macos" and win_metal_layer() == null { render3d_st.gvk_why = "no Metal layer on the window"; return false }
if render3d_st.gvk_size_buf == null { render3d_st.gvk_size_buf = words(4) }
win_gl_drawable(render3d_st.gvk_size_buf)
if render3d_st.gvk_size_buf[0] > 0 and render3d_st.gvk_size_buf[1] > 0 { gl_set_drawable(render3d_st.gvk_size_buf[0], render3d_st.gvk_size_buf[1]) }
@ -1519,13 +1529,24 @@ function gvk_read_screen(render3d_st: mut Render3dState, w: int, h: int, out: po
}
# ---- the window: surface and swapchain ----------------------------------------------------------
# Windows only for now (the Win32 surface); gpu_select keeps a window elsewhere on OpenGL.
# The Win32 surface on Windows; on macOS a CAMetalLayer the runtime hangs off the view (MoltenVK).
extern function win_native_window() -> pointer = "win_native_window"
extern function win_native_instance() -> pointer = "win_native_instance"
extern function win_metal_layer() -> pointer = "win_metal_layer"
extern function win_visible() -> int = "win_visible"
function gvk_surface_make(render3d_st: mut Render3dState) -> bool {
if render3d_st.gvk_surface != 0 { return true }
function gvk_surface_os(render3d_st: mut Render3dState, out: pointer) -> bool {
if Os.platform() == "macos" {
let layer = win_metal_layer()
if layer == null { render3d_st.gvk_why = "no Metal layer on the window"; return false }
let mci = bytes(VkMetalSurfaceCreateInfoEXT_sizeof)
Vk.zero(mci, VkMetalSurfaceCreateInfoEXT_sizeof)
Vk.put_i32(mci, VkMetalSurfaceCreateInfoEXT_sType, VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT)
Vk.put_ptr(mci, VkMetalSurfaceCreateInfoEXT_pLayer, layer)
let rm = Vk.create_metal_surface_ext(render3d_st.gvk_inst, mci, null, out)
if rm != VK_SUCCESS { return gvk_fail(render3d_st, "vkCreateMetalSurfaceEXT", rm) }
return true
}
let hwnd = win_native_window()
if hwnd == null { render3d_st.gvk_why = "no window to present to"; return false }
let sci = bytes(VkWin32SurfaceCreateInfoKHR_sizeof)
@ -1533,9 +1554,15 @@ function gvk_surface_make(render3d_st: mut Render3dState) -> bool {
Vk.put_i32(sci, VkWin32SurfaceCreateInfoKHR_sType, VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR)
Vk.put_ptr(sci, VkWin32SurfaceCreateInfoKHR_hinstance, win_native_instance())
Vk.put_ptr(sci, VkWin32SurfaceCreateInfoKHR_hwnd, hwnd)
let out = bytes(8)
let r = Vk.create_win32_surface_khr(render3d_st.gvk_inst, sci, null, out)
if r != VK_SUCCESS { return gvk_fail(render3d_st, "vkCreateWin32SurfaceKHR", r) }
return true
}
function gvk_surface_make(render3d_st: mut Render3dState) -> bool {
if render3d_st.gvk_surface != 0 { return true }
let out = bytes(8)
if not gvk_surface_os(render3d_st, out) { return false }
render3d_st.gvk_surface = gvk_handle(out)
let ok = bytes(4)
Vk.put_i32(ok, 0, 0)
@ -1551,6 +1578,7 @@ function gvk_surface_make(render3d_st: mut Render3dState) -> bool {
# (Re)make the swapchain for a w x h client area. The old one is handed over and then destroyed.
function gvk_swap_make(render3d_st: mut Render3dState, w: int, h: int) -> bool {
if not gvk_surface_make(render3d_st) { return false }
gvk_frame_wait(render3d_st)
Vk.device_wait_idle(render3d_st.gvk_dev)
let caps = bytes(VkSurfaceCapabilitiesKHR_sizeof)
Vk.get_physical_device_surface_capabilities_khr(render3d_st.gvk_pd, render3d_st.gvk_surface, caps)
@ -1641,6 +1669,14 @@ function gvk_swap_make(render3d_st: mut Render3dState, w: int, h: int) -> bool {
function gvk_present_window(render3d_st: mut Render3dState) -> void {
let cb = gvk_frame_cb(render3d_st)
gvk_pass_end(render3d_st)
# a covered window is not drawn to: its layer would hold the frame for a drawable the compositor
# hands back once a second. The frame still runs, paced at about 60 Hz, so the game keeps time.
if win_visible() == 0 {
if gvk_inflight_on(render3d_st) { gvk_frame_submit(render3d_st, cb) } else { gvk_once_end(render3d_st, cb) }
render3d_st.gvk_cb = null
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)
Vk.reset_fences(render3d_st.gvk_dev, 1, render3d_st.gvk_acq_fence)
@ -1670,7 +1706,7 @@ function gvk_present_window(render3d_st: mut Render3dState) -> void {
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_once_end(render3d_st, cb)
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)
Vk.zero(pi, VkPresentInfoKHR_sizeof)

View file

@ -134,6 +134,7 @@ function gvk_staging(render3d_st: mut Render3dState, n: int, usage: int) -> poin
return Vk.get_ptr(pp, 0)
}
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)
Vk.destroy_buffer(render3d_st.gvk_dev, render3d_st.gvk_st_buf, null)
Vk.free_memory(render3d_st.gvk_dev, render3d_st.gvk_st_mem, null)
@ -142,6 +143,7 @@ function gvk_staging_free(render3d_st: mut Render3dState) -> void {
# (Re)make the image behind a handle: glTexImage2D on a texture that already has one replaces it.
function gvk_tex_storage(render3d_st: mut Render3dState, tex: int, array: bool, ifmt: int, w: int, h: int, layers: int, with_mips: bool) -> bool {
gvk_frame_wait(render3d_st) # the frame in flight may still read it
if tex <= 0 or tex >= len(render3d_st.gvk_tex_image) { return false }
let vkfmt = gvk_format(ifmt)
if vkfmt == VK_FORMAT_UNDEFINED { print(`r3d: vulkan: no image format for GL format {ifmt}`); return false }
@ -304,6 +306,7 @@ function gvk_tex_upload(render3d_st: mut Render3dState, tex: int, ifmt: int, w:
# A target made without pixels has one level; the renderer asking it for mipmaps (the exposure
# measure reads the HDR scene's smallest level every frame) grows it a full chain, level 0 kept.
function gvk_tex_grow_mips(render3d_st: mut Render3dState, tex: int, w: int, h: int) -> bool {
gvk_frame_wait(render3d_st) # the frame in flight may still read it
let old_image = render3d_st.gvk_tex_image[tex]
let old_view = render3d_st.gvk_tex_view[tex]
let old_mem = render3d_st.gvk_tex_mem[tex]
@ -420,6 +423,7 @@ function gvk_tex_read(render3d_st: mut Render3dState, tex: int, ifmt: int, w: in
}
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
Vk.destroy_image_view(render3d_st.gvk_dev, render3d_st.gvk_tex_view[tex], null)
@ -553,8 +557,8 @@ function gvk_buf_new(render3d_st: mut Render3dState) -> int {
function gvk_buf_release(render3d_st: mut Render3dState, b: int) -> void {
if render3d_st.gvk_buf[b] == 0 { return }
let zero: long = 0
if render3d_st.gvk_buf_used[b] == render3d_st.gvk_frame_no {
# a draw recorded this frame still reads it: destroy it once the frame has been submitted
if gvk_buf_busy(render3d_st, b) {
# a draw recorded this frame (or the frame in flight) still reads it: destroy it once the frame has been submitted
push(render3d_st.gvk_retired_buf, render3d_st.gvk_buf[b]); push(render3d_st.gvk_retired_mem, render3d_st.gvk_buf_mem[b])
} else {
Vk.destroy_buffer(render3d_st.gvk_dev, render3d_st.gvk_buf[b], null)
@ -562,6 +566,12 @@ function gvk_buf_release(render3d_st: mut Render3dState, b: int) -> void {
}
render3d_st.gvk_buf[b] = zero; render3d_st.gvk_buf_mem[b] = zero; render3d_st.gvk_buf_size[b] = 0; render3d_st.gvk_buf_map[b] = null; render3d_st.gvk_buf_used[b] = 0
}
# read by a draw recorded this frame, or by the presented frame still on the GPU
function gvk_buf_busy(render3d_st: Render3dState, b: int) -> bool {
let u = render3d_st.gvk_buf_used[b]
return u == render3d_st.gvk_frame_no or (render3d_st.gvk_frame_pending and u == render3d_st.gvk_frame_no - 1)
}
# the frame's submitted work is done: storage retired during it can go
function gvk_retire_flush(render3d_st: mut Render3dState) -> void {
if render3d_st.gvk_retired_buf == null { return }
@ -587,7 +597,7 @@ function gvk_buf_is_gpu(render3d_st: Render3dState, b: int) -> bool { return ren
function gvk_buf_reserve(render3d_st: mut Render3dState, b: int, n: int) -> bool {
if b <= 0 or b >= len(render3d_st.gvk_buf) { return false }
# already big enough, and no draw this frame reads what is there: fill it in place
if render3d_st.gvk_buf[b] != 0 and render3d_st.gvk_buf_size[b] >= n and (render3d_st.gvk_buf_used[b] != render3d_st.gvk_frame_no or gvk_buf_is_gpu(render3d_st, b)) { return true }
if render3d_st.gvk_buf[b] != 0 and render3d_st.gvk_buf_size[b] >= n and (not gvk_buf_busy(render3d_st, b) or gvk_buf_is_gpu(render3d_st, b)) { return true }
gvk_buf_release(render3d_st, b)
var size = n
if size < 64 { size = 64 }

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -141,6 +141,56 @@ T c57b4f14 u_shadow 5
T c57b4f14 u_src 6
T c57b4f14 u_tershadow 7
T c57b4f14 u_ts_height 8
P 5161e6ba fullscreen.vert dof.frag
B 5161e6ba frag 1 916
U 5161e6ba frag u_cascade_vp 0 mat4 5 64
U 5161e6ba frag u_cascade_split 320 float 5 16
U 5161e6ba frag u_cascade_range 400 float 5 16
U 5161e6ba frag u_cascade_texel 480 float 5 16
U 5161e6ba frag u_sun_dir 560 vec3 1 0
U 5161e6ba frag u_sun_color 576 vec3 1 0
U 5161e6ba frag u_cam_pos 592 vec3 1 0
U 5161e6ba frag u_prefilter_levels 604 float 1 0
U 5161e6ba frag u_fog_density 608 float 1 0
U 5161e6ba frag u_fog_height_falloff 612 float 1 0
U 5161e6ba frag u_fog_base 616 float 1 0
U 5161e6ba frag u_clip_y 620 float 1 0
U 5161e6ba frag u_spec_scale 624 float 1 0
U 5161e6ba frag u_sky_rot 632 vec2 1 0
U 5161e6ba frag u_ground_alb 640 vec3 1 0
U 5161e6ba frag u_ground_alb_hi 656 vec3 1 0
U 5161e6ba frag u_ground_hi_y 668 float 1 0
U 5161e6ba frag u_ground_hi_w 672 float 1 0
U 5161e6ba frag u_ibl_scale 688 vec3 1 0
U 5161e6ba frag u_daylight 700 float 1 0
U 5161e6ba frag u_fire_pos 704 vec3 1 0
U 5161e6ba frag u_fire_color 720 vec3 1 0
U 5161e6ba frag u_hand_pos 736 vec3 1 0
U 5161e6ba frag u_hand_color 752 vec3 1 0
U 5161e6ba frag u_hand_dir 768 vec3 1 0
U 5161e6ba frag u_hand_cone 780 float 1 0
U 5161e6ba frag u_hand_reach 784 float 1 0
U 5161e6ba frag u_ts_origin 792 vec2 1 0
U 5161e6ba frag u_ts_half 800 float 1 0
U 5161e6ba frag u_ts_on 804 float 1 0
U 5161e6ba frag u_force_cascade 808 int 1 0
U 5161e6ba frag u_cloud_shadow 812 float 1 0
U 5161e6ba frag u_time 816 float 1 0
U 5161e6ba frag u_fog_inscatter 820 float 1 0
U 5161e6ba frag u_fog_desat 824 float 1 0
U 5161e6ba frag u_inv_proj 832 mat4 1 0
U 5161e6ba frag u_texel 896 vec2 1 0
U 5161e6ba frag u_focus 904 float 1 0
U 5161e6ba frag u_aperture 908 float 1 0
U 5161e6ba frag u_max_coc 912 float 1 0
T 5161e6ba u_brdf 2
T 5161e6ba u_depth 3
T 5161e6ba u_irradiance 4
T 5161e6ba u_prefilter 5
T 5161e6ba u_shadow 6
T 5161e6ba u_src 7
T 5161e6ba u_tershadow 8
T 5161e6ba u_ts_height 9
P 4be7acd9 fullscreen.vert heightgen.frag #define DEM;
B 4be7acd9 frag 1 920
U 4be7acd9 frag u_cascade_vp 0 mat4 5 64
@ -740,6 +790,58 @@ T a4683767 u_prefilter 8
T a4683767 u_shadow 9
T a4683767 u_tershadow 10
T a4683767 u_ts_height 11
P 74258189 fullscreen.vert volumetric.frag
B 74258189 frag 1 924
U 74258189 frag u_cascade_vp 0 mat4 5 64
U 74258189 frag u_cascade_split 320 float 5 16
U 74258189 frag u_cascade_range 400 float 5 16
U 74258189 frag u_cascade_texel 480 float 5 16
U 74258189 frag u_sun_dir 560 vec3 1 0
U 74258189 frag u_sun_color 576 vec3 1 0
U 74258189 frag u_cam_pos 592 vec3 1 0
U 74258189 frag u_prefilter_levels 604 float 1 0
U 74258189 frag u_fog_density 608 float 1 0
U 74258189 frag u_fog_height_falloff 612 float 1 0
U 74258189 frag u_fog_base 616 float 1 0
U 74258189 frag u_clip_y 620 float 1 0
U 74258189 frag u_spec_scale 624 float 1 0
U 74258189 frag u_sky_rot 632 vec2 1 0
U 74258189 frag u_ground_alb 640 vec3 1 0
U 74258189 frag u_ground_alb_hi 656 vec3 1 0
U 74258189 frag u_ground_hi_y 668 float 1 0
U 74258189 frag u_ground_hi_w 672 float 1 0
U 74258189 frag u_ibl_scale 688 vec3 1 0
U 74258189 frag u_daylight 700 float 1 0
U 74258189 frag u_fire_pos 704 vec3 1 0
U 74258189 frag u_fire_color 720 vec3 1 0
U 74258189 frag u_hand_pos 736 vec3 1 0
U 74258189 frag u_hand_color 752 vec3 1 0
U 74258189 frag u_hand_dir 768 vec3 1 0
U 74258189 frag u_hand_cone 780 float 1 0
U 74258189 frag u_hand_reach 784 float 1 0
U 74258189 frag u_ts_origin 792 vec2 1 0
U 74258189 frag u_ts_half 800 float 1 0
U 74258189 frag u_ts_on 804 float 1 0
U 74258189 frag u_force_cascade 808 int 1 0
U 74258189 frag u_cloud_shadow 812 float 1 0
U 74258189 frag u_time 816 float 1 0
U 74258189 frag u_fog_inscatter 820 float 1 0
U 74258189 frag u_fog_desat 824 float 1 0
U 74258189 frag u_inv_vp 832 mat4 1 0
U 74258189 frag u_vol_steps 896 float 1 0
U 74258189 frag u_vol_density 900 float 1 0
U 74258189 frag u_vol_falloff 904 float 1 0
U 74258189 frag u_vol_far 908 float 1 0
U 74258189 frag u_vol_g 912 float 1 0
U 74258189 frag u_vol_mist 916 float 1 0
U 74258189 frag u_vol_mist_h 920 float 1 0
T 74258189 u_brdf 2
T 74258189 u_depth 3
T 74258189 u_irradiance 4
T 74258189 u_prefilter 5
T 74258189 u_shadow 6
T 74258189 u_tershadow 7
T 74258189 u_ts_height 8
P d48d6a48 grass.mesh model.frag #define FOLIAGE;#define BLADE;#define MESH;
B d48d6a48 vert 0 4384
U d48d6a48 vert u_view 0 mat4 1 0
@ -1355,6 +1457,68 @@ T bfa15301 u_prefilter 5
T bfa15301 u_shadow 6
T bfa15301 u_tershadow 7
T bfa15301 u_ts_height 8
P 266d2401 model.vert depth.frag #define FOLIAGE;#define WIND;#define ALPHA_TEST;#define NEAR_FADE;
B 266d2401 vert 0 236
U 266d2401 vert u_view 0 mat4 1 0
U 266d2401 vert u_proj 64 mat4 1 0
U 266d2401 vert u_light_vp 128 mat4 1 0
U 266d2401 vert u_time 192 float 1 0
U 266d2401 vert u_wind 196 float 1 0
U 266d2401 vert u_card_w 200 float 1 0
U 266d2401 vert u_card_h 204 float 1 0
U 266d2401 vert u_ground 208 float 1 0
U 266d2401 vert u_ts_origin 216 vec2 1 0
U 266d2401 vert u_ts_half 224 float 1 0
U 266d2401 vert u_model_h 228 float 1 0
U 266d2401 vert u_flutter 232 float 1 0
I 266d2401 i_rot 4 vec4
I 266d2401 a_pos 0 vec3
I 266d2401 i_pos 3 vec4
I 266d2401 a_nrm 1 vec3
I 266d2401 a_uv 2 vec2
B 266d2401 frag 1 828
U 266d2401 frag u_cascade_vp 0 mat4 5 64
U 266d2401 frag u_cascade_split 320 float 5 16
U 266d2401 frag u_cascade_range 400 float 5 16
U 266d2401 frag u_cascade_texel 480 float 5 16
U 266d2401 frag u_sun_dir 560 vec3 1 0
U 266d2401 frag u_sun_color 576 vec3 1 0
U 266d2401 frag u_cam_pos 592 vec3 1 0
U 266d2401 frag u_prefilter_levels 604 float 1 0
U 266d2401 frag u_fog_density 608 float 1 0
U 266d2401 frag u_fog_height_falloff 612 float 1 0
U 266d2401 frag u_fog_base 616 float 1 0
U 266d2401 frag u_clip_y 620 float 1 0
U 266d2401 frag u_spec_scale 624 float 1 0
U 266d2401 frag u_sky_rot 632 vec2 1 0
U 266d2401 frag u_ground_alb 640 vec3 1 0
U 266d2401 frag u_ground_alb_hi 656 vec3 1 0
U 266d2401 frag u_ground_hi_y 668 float 1 0
U 266d2401 frag u_ground_hi_w 672 float 1 0
U 266d2401 frag u_ibl_scale 688 vec3 1 0
U 266d2401 frag u_daylight 700 float 1 0
U 266d2401 frag u_fire_pos 704 vec3 1 0
U 266d2401 frag u_fire_color 720 vec3 1 0
U 266d2401 frag u_hand_pos 736 vec3 1 0
U 266d2401 frag u_hand_color 752 vec3 1 0
U 266d2401 frag u_hand_dir 768 vec3 1 0
U 266d2401 frag u_hand_cone 780 float 1 0
U 266d2401 frag u_hand_reach 784 float 1 0
U 266d2401 frag u_ts_origin 792 vec2 1 0
U 266d2401 frag u_ts_half 800 float 1 0
U 266d2401 frag u_ts_on 804 float 1 0
U 266d2401 frag u_force_cascade 808 int 1 0
U 266d2401 frag u_cloud_shadow 812 float 1 0
U 266d2401 frag u_time 816 float 1 0
U 266d2401 frag u_fog_inscatter 820 float 1 0
U 266d2401 frag u_fog_desat 824 float 1 0
T 266d2401 u_brdf 2
T 266d2401 u_diff 3
T 266d2401 u_irradiance 4
T 266d2401 u_prefilter 5
T 266d2401 u_shadow 6
T 266d2401 u_tershadow 7
T 266d2401 u_ts_height 8
P 01089da8 model.vert model.frag
B 01089da8 vert 0 236
U 01089da8 vert u_view 0 mat4 1 0
@ -1566,6 +1730,76 @@ T 1ce5c5d1 u_prefilter 8
T 1ce5c5d1 u_shadow 9
T 1ce5c5d1 u_tershadow 10
T 1ce5c5d1 u_ts_height 11
P 5e70d731 model.vert model.frag #define FOLIAGE;#define WIND;#define ALPHA_TEST;#define NEAR_FADE;
B 5e70d731 vert 0 236
U 5e70d731 vert u_view 0 mat4 1 0
U 5e70d731 vert u_proj 64 mat4 1 0
U 5e70d731 vert u_light_vp 128 mat4 1 0
U 5e70d731 vert u_time 192 float 1 0
U 5e70d731 vert u_wind 196 float 1 0
U 5e70d731 vert u_card_w 200 float 1 0
U 5e70d731 vert u_card_h 204 float 1 0
U 5e70d731 vert u_ground 208 float 1 0
U 5e70d731 vert u_ts_origin 216 vec2 1 0
U 5e70d731 vert u_ts_half 224 float 1 0
U 5e70d731 vert u_model_h 228 float 1 0
U 5e70d731 vert u_flutter 232 float 1 0
I 5e70d731 i_rot 4 vec4
I 5e70d731 a_pos 0 vec3
I 5e70d731 i_pos 3 vec4
I 5e70d731 a_nrm 1 vec3
I 5e70d731 a_uv 2 vec2
B 5e70d731 frag 1 916
U 5e70d731 frag u_cascade_vp 0 mat4 5 64
U 5e70d731 frag u_cascade_split 320 float 5 16
U 5e70d731 frag u_cascade_range 400 float 5 16
U 5e70d731 frag u_cascade_texel 480 float 5 16
U 5e70d731 frag u_sun_dir 560 vec3 1 0
U 5e70d731 frag u_sun_color 576 vec3 1 0
U 5e70d731 frag u_cam_pos 592 vec3 1 0
U 5e70d731 frag u_prefilter_levels 604 float 1 0
U 5e70d731 frag u_fog_density 608 float 1 0
U 5e70d731 frag u_fog_height_falloff 612 float 1 0
U 5e70d731 frag u_fog_base 616 float 1 0
U 5e70d731 frag u_clip_y 620 float 1 0
U 5e70d731 frag u_spec_scale 624 float 1 0
U 5e70d731 frag u_sky_rot 632 vec2 1 0
U 5e70d731 frag u_ground_alb 640 vec3 1 0
U 5e70d731 frag u_ground_alb_hi 656 vec3 1 0
U 5e70d731 frag u_ground_hi_y 668 float 1 0
U 5e70d731 frag u_ground_hi_w 672 float 1 0
U 5e70d731 frag u_ibl_scale 688 vec3 1 0
U 5e70d731 frag u_daylight 700 float 1 0
U 5e70d731 frag u_fire_pos 704 vec3 1 0
U 5e70d731 frag u_fire_color 720 vec3 1 0
U 5e70d731 frag u_hand_pos 736 vec3 1 0
U 5e70d731 frag u_hand_color 752 vec3 1 0
U 5e70d731 frag u_hand_dir 768 vec3 1 0
U 5e70d731 frag u_hand_cone 780 float 1 0
U 5e70d731 frag u_hand_reach 784 float 1 0
U 5e70d731 frag u_ts_origin 792 vec2 1 0
U 5e70d731 frag u_ts_half 800 float 1 0
U 5e70d731 frag u_ts_on 804 float 1 0
U 5e70d731 frag u_force_cascade 808 int 1 0
U 5e70d731 frag u_cloud_shadow 812 float 1 0
U 5e70d731 frag u_time 816 float 1 0
U 5e70d731 frag u_fog_inscatter 820 float 1 0
U 5e70d731 frag u_fog_desat 824 float 1 0
U 5e70d731 frag u_model_h 828 float 1 0
U 5e70d731 frag u_view 832 mat4 1 0
U 5e70d731 frag u_tint 896 vec3 1 0
U 5e70d731 frag u_rough_scale 908 float 1 0
U 5e70d731 frag u_emissive 912 float 1 0
T 5e70d731 u_arm 2
T 5e70d731 u_blade_tex 3
T 5e70d731 u_brdf 4
T 5e70d731 u_diff 5
T 5e70d731 u_irradiance 6
T 5e70d731 u_nrm 7
T 5e70d731 u_prefilter 8
T 5e70d731 u_shadow 9
T 5e70d731 u_tershadow 10
T 5e70d731 u_ts_height 11
P f4a2b586 model.vert model.frag #define FOLIAGE;#define WIND;#define BLADE;
B f4a2b586 vert 0 252
U f4a2b586 vert u_view 0 mat4 1 0
@ -1783,6 +2017,76 @@ T 4dda406a u_prefilter 8
T 4dda406a u_shadow 9
T 4dda406a u_tershadow 10
T 4dda406a u_ts_height 11
P b603a056 model.vert model.frag #define FOLIAGE;#define WIND;#define EQ_PASS;#define NEAR_FADE;
B b603a056 vert 0 236
U b603a056 vert u_view 0 mat4 1 0
U b603a056 vert u_proj 64 mat4 1 0
U b603a056 vert u_light_vp 128 mat4 1 0
U b603a056 vert u_time 192 float 1 0
U b603a056 vert u_wind 196 float 1 0
U b603a056 vert u_card_w 200 float 1 0
U b603a056 vert u_card_h 204 float 1 0
U b603a056 vert u_ground 208 float 1 0
U b603a056 vert u_ts_origin 216 vec2 1 0
U b603a056 vert u_ts_half 224 float 1 0
U b603a056 vert u_model_h 228 float 1 0
U b603a056 vert u_flutter 232 float 1 0
I b603a056 i_rot 4 vec4
I b603a056 a_pos 0 vec3
I b603a056 i_pos 3 vec4
I b603a056 a_nrm 1 vec3
I b603a056 a_uv 2 vec2
B b603a056 frag 1 916
U b603a056 frag u_cascade_vp 0 mat4 5 64
U b603a056 frag u_cascade_split 320 float 5 16
U b603a056 frag u_cascade_range 400 float 5 16
U b603a056 frag u_cascade_texel 480 float 5 16
U b603a056 frag u_sun_dir 560 vec3 1 0
U b603a056 frag u_sun_color 576 vec3 1 0
U b603a056 frag u_cam_pos 592 vec3 1 0
U b603a056 frag u_prefilter_levels 604 float 1 0
U b603a056 frag u_fog_density 608 float 1 0
U b603a056 frag u_fog_height_falloff 612 float 1 0
U b603a056 frag u_fog_base 616 float 1 0
U b603a056 frag u_clip_y 620 float 1 0
U b603a056 frag u_spec_scale 624 float 1 0
U b603a056 frag u_sky_rot 632 vec2 1 0
U b603a056 frag u_ground_alb 640 vec3 1 0
U b603a056 frag u_ground_alb_hi 656 vec3 1 0
U b603a056 frag u_ground_hi_y 668 float 1 0
U b603a056 frag u_ground_hi_w 672 float 1 0
U b603a056 frag u_ibl_scale 688 vec3 1 0
U b603a056 frag u_daylight 700 float 1 0
U b603a056 frag u_fire_pos 704 vec3 1 0
U b603a056 frag u_fire_color 720 vec3 1 0
U b603a056 frag u_hand_pos 736 vec3 1 0
U b603a056 frag u_hand_color 752 vec3 1 0
U b603a056 frag u_hand_dir 768 vec3 1 0
U b603a056 frag u_hand_cone 780 float 1 0
U b603a056 frag u_hand_reach 784 float 1 0
U b603a056 frag u_ts_origin 792 vec2 1 0
U b603a056 frag u_ts_half 800 float 1 0
U b603a056 frag u_ts_on 804 float 1 0
U b603a056 frag u_force_cascade 808 int 1 0
U b603a056 frag u_cloud_shadow 812 float 1 0
U b603a056 frag u_time 816 float 1 0
U b603a056 frag u_fog_inscatter 820 float 1 0
U b603a056 frag u_fog_desat 824 float 1 0
U b603a056 frag u_model_h 828 float 1 0
U b603a056 frag u_view 832 mat4 1 0
U b603a056 frag u_tint 896 vec3 1 0
U b603a056 frag u_rough_scale 908 float 1 0
U b603a056 frag u_emissive 912 float 1 0
T b603a056 u_arm 2
T b603a056 u_blade_tex 3
T b603a056 u_brdf 4
T b603a056 u_diff 5
T b603a056 u_irradiance 6
T b603a056 u_nrm 7
T b603a056 u_prefilter 8
T b603a056 u_shadow 9
T b603a056 u_tershadow 10
T b603a056 u_ts_height 11
P 3b183d8f model.vert model.frag #define FOLIAGE;#define WIND;#define FLOWER;
B 3b183d8f vert 0 236
U 3b183d8f vert u_view 0 mat4 1 0
@ -3218,105 +3522,3 @@ T 46e2325c u_scene 7
T 46e2325c u_shadow 8
T 46e2325c u_tershadow 9
T 46e2325c u_ts_height 10
P 74258189 fullscreen.vert volumetric.frag
B 74258189 frag 1 924
U 74258189 frag u_cascade_vp 0 mat4 5 64
U 74258189 frag u_cascade_split 320 float 5 16
U 74258189 frag u_cascade_range 400 float 5 16
U 74258189 frag u_cascade_texel 480 float 5 16
U 74258189 frag u_sun_dir 560 vec3 1 0
U 74258189 frag u_sun_color 576 vec3 1 0
U 74258189 frag u_cam_pos 592 vec3 1 0
U 74258189 frag u_prefilter_levels 604 float 1 0
U 74258189 frag u_fog_density 608 float 1 0
U 74258189 frag u_fog_height_falloff 612 float 1 0
U 74258189 frag u_fog_base 616 float 1 0
U 74258189 frag u_clip_y 620 float 1 0
U 74258189 frag u_spec_scale 624 float 1 0
U 74258189 frag u_sky_rot 632 vec2 1 0
U 74258189 frag u_ground_alb 640 vec3 1 0
U 74258189 frag u_ground_alb_hi 656 vec3 1 0
U 74258189 frag u_ground_hi_y 668 float 1 0
U 74258189 frag u_ground_hi_w 672 float 1 0
U 74258189 frag u_ibl_scale 688 vec3 1 0
U 74258189 frag u_daylight 700 float 1 0
U 74258189 frag u_fire_pos 704 vec3 1 0
U 74258189 frag u_fire_color 720 vec3 1 0
U 74258189 frag u_hand_pos 736 vec3 1 0
U 74258189 frag u_hand_color 752 vec3 1 0
U 74258189 frag u_hand_dir 768 vec3 1 0
U 74258189 frag u_hand_cone 780 float 1 0
U 74258189 frag u_hand_reach 784 float 1 0
U 74258189 frag u_ts_origin 792 vec2 1 0
U 74258189 frag u_ts_half 800 float 1 0
U 74258189 frag u_ts_on 804 float 1 0
U 74258189 frag u_force_cascade 808 int 1 0
U 74258189 frag u_cloud_shadow 812 float 1 0
U 74258189 frag u_time 816 float 1 0
U 74258189 frag u_fog_inscatter 820 float 1 0
U 74258189 frag u_fog_desat 824 float 1 0
U 74258189 frag u_inv_vp 832 mat4 1 0
U 74258189 frag u_vol_steps 896 float 1 0
U 74258189 frag u_vol_density 900 float 1 0
U 74258189 frag u_vol_falloff 904 float 1 0
U 74258189 frag u_vol_far 908 float 1 0
U 74258189 frag u_vol_g 912 float 1 0
U 74258189 frag u_vol_mist 916 float 1 0
U 74258189 frag u_vol_mist_h 920 float 1 0
T 74258189 u_brdf 2
T 74258189 u_depth 3
T 74258189 u_irradiance 4
T 74258189 u_prefilter 5
T 74258189 u_shadow 6
T 74258189 u_tershadow 7
T 74258189 u_ts_height 8
P 5161e6ba fullscreen.vert dof.frag
B 5161e6ba frag 1 916
U 5161e6ba frag u_cascade_vp 0 mat4 5 64
U 5161e6ba frag u_cascade_split 320 float 5 16
U 5161e6ba frag u_cascade_range 400 float 5 16
U 5161e6ba frag u_cascade_texel 480 float 5 16
U 5161e6ba frag u_sun_dir 560 vec3 1 0
U 5161e6ba frag u_sun_color 576 vec3 1 0
U 5161e6ba frag u_cam_pos 592 vec3 1 0
U 5161e6ba frag u_prefilter_levels 604 float 1 0
U 5161e6ba frag u_fog_density 608 float 1 0
U 5161e6ba frag u_fog_height_falloff 612 float 1 0
U 5161e6ba frag u_fog_base 616 float 1 0
U 5161e6ba frag u_clip_y 620 float 1 0
U 5161e6ba frag u_spec_scale 624 float 1 0
U 5161e6ba frag u_sky_rot 632 vec2 1 0
U 5161e6ba frag u_ground_alb 640 vec3 1 0
U 5161e6ba frag u_ground_alb_hi 656 vec3 1 0
U 5161e6ba frag u_ground_hi_y 668 float 1 0
U 5161e6ba frag u_ground_hi_w 672 float 1 0
U 5161e6ba frag u_ibl_scale 688 vec3 1 0
U 5161e6ba frag u_daylight 700 float 1 0
U 5161e6ba frag u_fire_pos 704 vec3 1 0
U 5161e6ba frag u_fire_color 720 vec3 1 0
U 5161e6ba frag u_hand_pos 736 vec3 1 0
U 5161e6ba frag u_hand_color 752 vec3 1 0
U 5161e6ba frag u_hand_dir 768 vec3 1 0
U 5161e6ba frag u_hand_cone 780 float 1 0
U 5161e6ba frag u_hand_reach 784 float 1 0
U 5161e6ba frag u_ts_origin 792 vec2 1 0
U 5161e6ba frag u_ts_half 800 float 1 0
U 5161e6ba frag u_ts_on 804 float 1 0
U 5161e6ba frag u_force_cascade 808 int 1 0
U 5161e6ba frag u_cloud_shadow 812 float 1 0
U 5161e6ba frag u_time 816 float 1 0
U 5161e6ba frag u_fog_inscatter 820 float 1 0
U 5161e6ba frag u_fog_desat 824 float 1 0
U 5161e6ba frag u_inv_proj 832 mat4 1 0
U 5161e6ba frag u_texel 896 vec2 1 0
U 5161e6ba frag u_focus 904 float 1 0
U 5161e6ba frag u_aperture 908 float 1 0
U 5161e6ba frag u_max_coc 912 float 1 0
T 5161e6ba u_brdf 2
T 5161e6ba u_depth 3
T 5161e6ba u_irradiance 4
T 5161e6ba u_prefilter 5
T 5161e6ba u_shadow 6
T 5161e6ba u_src 7
T 5161e6ba u_tershadow 8
T 5161e6ba u_ts_height 9

View file

@ -5,6 +5,7 @@
fullscreen.vert|adapt.frag|
fullscreen.vert|bloom_down.frag|
fullscreen.vert|bloom_up.frag|
fullscreen.vert|dof.frag|
fullscreen.vert|heightgen.frag|#define DEM;
fullscreen.vert|ibl_brdf.frag|
fullscreen.vert|ibl_irradiance.frag|
@ -17,6 +18,7 @@ fullscreen.vert|ternormal.frag|
fullscreen.vert|tershadow.frag|#define NOISE_ONLY;
fullscreen.vert|tonemap.frag|
fullscreen.vert|tonemap.frag|#define HDR10;
fullscreen.vert|volumetric.frag|
grass.mesh|model.frag|#define FOLIAGE;#define BLADE;#define MESH;
grass.vert|model.frag|#define FOLIAGE;#define BLADE;
grass.vert|model.frag|#define FOLIAGE;#define BLADE;#define TILES;
@ -26,12 +28,15 @@ model.vert|bake.frag|
model.vert|bake.frag|#define CARD;
model.vert|bake.frag|#define FLOWER;
model.vert|depth.frag|#define FOLIAGE;#define WIND;#define ALPHA_TEST;
model.vert|depth.frag|#define FOLIAGE;#define WIND;#define ALPHA_TEST;#define NEAR_FADE;
model.vert|model.frag|
model.vert|model.frag|#define CARD;#define CHEAP;
model.vert|model.frag|#define FOLIAGE;#define WIND;#define ALPHA_TEST;
model.vert|model.frag|#define FOLIAGE;#define WIND;#define ALPHA_TEST;#define NEAR_FADE;
model.vert|model.frag|#define FOLIAGE;#define WIND;#define BLADE;
model.vert|model.frag|#define FOLIAGE;#define WIND;#define CARD;
model.vert|model.frag|#define FOLIAGE;#define WIND;#define EQ_PASS;
model.vert|model.frag|#define FOLIAGE;#define WIND;#define EQ_PASS;#define NEAR_FADE;
model.vert|model.frag|#define FOLIAGE;#define WIND;#define FLOWER;
model.vert|model.frag|#define SHADOW_PASS;#define WIND;#define CARD;
model.vert|model.frag|#define WIND;
@ -54,5 +59,3 @@ terrain.vert|terrain.frag|#define SUN_INLINE;#define FAR_ONLY;
terrain.vert|terrain.frag|#define SUN_INLINE;#define NEAR_ONLY;
terrain.vert|tersun.frag|
water.vert|water.frag|
fullscreen.vert|volumetric.frag|
fullscreen.vert|dof.frag|

View file

@ -1607,6 +1607,14 @@ go:
%scale = select i1 %ison, i32 %bsv, i32 1
store i32 %scale, ptr @W_glscale
call void @win_gl_update()
; a Metal layer (Vulkan) draws at the same scale
%ml = load ptr, ptr @W_mtl
%noml = icmp eq ptr %ml, null
br i1 %noml, label %out, label %mtl
mtl:
%scd = sitofp i32 %scale to double
%sel_csc = call ptr @sel_registerName(ptr @.s_setcsc)
%rm = call ptr (ptr, ptr, double) @objc_msgSend(ptr %ml, ptr %sel_csc, double %scd)
br label %out
out:
ret void
@ -2035,3 +2043,81 @@ define ptr @win_native_instance() {
entry:
ret ptr null
}
; ---- a Metal layer on the view, for Vulkan through MoltenVK -----------------------------
; VK_EXT_metal_surface presents to a CAMetalLayer, so the view is made layer-hosting with one
; (the layer set before wantsLayer, which is what makes the view host it rather than back it).
; Its contentsScale is the window's backing scale, or the swapchain's pixels are stretched over
; a Retina window's points; @W_glscale follows it, as win_gl_attach sets it for OpenGL. The class
; is looked up by name: the program links QuartzCore when it uses Vk.* (null without it).
; win_metal_layer() -> CAMetalLayer* | null (no window, or no QuartzCore)
@.c_mtllayer = private unnamed_addr constant [13 x i8] c"CAMetalLayer\00"
@.s_layer = private unnamed_addr constant [6 x i8] c"layer\00"
@.s_retain = private unnamed_addr constant [7 x i8] c"retain\00"
@.s_setlayer = private unnamed_addr constant [10 x i8] c"setLayer:\00"
@.s_wantsl = private unnamed_addr constant [15 x i8] c"setWantsLayer:\00"
@.s_setcsc = private unnamed_addr constant [18 x i8] c"setContentsScale:\00"
@W_mtl = internal global ptr null
define ptr @win_metal_layer() {
entry:
%have = load ptr, ptr @W_mtl
%got = icmp ne ptr %have, null
br i1 %got, label %done, label %make
make:
%view = load ptr, ptr @W_view
%win = load ptr, ptr @W_win
%noview = icmp eq ptr %view, null
%nowin = icmp eq ptr %win, null
%nothing = or i1 %noview, %nowin
br i1 %nothing, label %none, label %cls
cls:
%c = call ptr @objc_getClass(ptr @.c_mtllayer)
%noc = icmp eq ptr %c, null
br i1 %noc, label %none, label %go
go:
%sel_l = call ptr @sel_registerName(ptr @.s_layer)
%l0 = call ptr @objc_msgSend(ptr %c, ptr %sel_l)
%sel_r = call ptr @sel_registerName(ptr @.s_retain)
%l = call ptr @objc_msgSend(ptr %l0, ptr %sel_r)
%sel_bs = call ptr @sel_registerName(ptr @.s_bscale)
%bs = call double (ptr, ptr) @objc_msgSend(ptr %win, ptr %sel_bs)
%bsok = fcmp ogt double %bs, 0.0
%scale = select i1 %bsok, double %bs, double 1.0
%sel_cs = call ptr @sel_registerName(ptr @.s_setcsc)
%r0 = call ptr (ptr, ptr, double) @objc_msgSend(ptr %l, ptr %sel_cs, double %scale)
%sel_sl = call ptr @sel_registerName(ptr @.s_setlayer)
%r1 = call ptr (ptr, ptr, ptr) @objc_msgSend(ptr %view, ptr %sel_sl, ptr %l)
%sel_wl = call ptr @sel_registerName(ptr @.s_wantsl)
%r2 = call ptr (ptr, ptr, i8) @objc_msgSend(ptr %view, ptr %sel_wl, i8 1)
%si = fptosi double %scale to i32
store i32 %si, ptr @W_glscale
store ptr %l, ptr @W_mtl
ret ptr %l
done:
ret ptr %have
none:
ret ptr null
}
; ---- is the window on screen ----------------------------------------------------------------------
; A covered or minimised window's CAMetalLayer hands out drawables at the compositor's pace (a
; second's timeout each), so a Vulkan frame waiting for one stalled the whole game at 3 fps behind
; another window. NSWindowOcclusionStateVisible is bit 1 of -occlusionState.
; win_visible() -> 1 | 0 (1 with no window)
@.s_occl = private unnamed_addr constant [15 x i8] c"occlusionState\00"
define i32 @win_visible() {
entry:
%win = load ptr, ptr @W_win
%nowin = icmp eq ptr %win, null
br i1 %nowin, label %yes, label %ask
ask:
%sel = call ptr @sel_registerName(ptr @.s_occl)
%st = call i64 (ptr, ptr) @objc_msgSend(ptr %win, ptr %sel)
%bit = and i64 %st, 2
%vis = icmp ne i64 %bit, 0
%r = zext i1 %vis to i32
ret i32 %r
yes:
ret i32 1
}

View file

@ -398,6 +398,15 @@ define weak ptr @win_native_instance() {
entry:
ret ptr null
}
define weak ptr @win_metal_layer() {
entry:
ret ptr null
}
; the window is always drawn to here (cocoa.ll asks macOS whether it is covered)
define weak i32 @win_visible() {
entry:
ret i32 1
}
; the drawable's size, for a graphics API that follows the window without a GL context (render3d's
; Vulkan resize check); headless there is no window, so 0 x 0. cocoa.ll's real one wins when linked.
; win_set_title: cocoa.ll's in a windowed build; nothing to retitle headless

View file

@ -52,6 +52,16 @@ define ptr @win_native_instance() {
entry:
ret ptr null
}
; macOS only (cocoa.ll): the CAMetalLayer MoltenVK presents to
define ptr @win_metal_layer() {
entry:
ret ptr null
}
; the window is always drawn to here (cocoa.ll asks macOS whether it is covered)
define i32 @win_visible() {
entry:
ret i32 1
}
define void @win_set_title(ptr %title) {
entry:

View file

@ -4,8 +4,8 @@
; macOS has no Vulkan of its own: the LunarG loader and MoltenVK (Vulkan over
; Metal) come with the Vulkan SDK or with a game that bundles them. So the loader
; is looked for at run time - beside the executable, in the usual library
; directories, then under $VULKAN_SDK - and a Mac without it gets 0 from vk_open
; and stays on OpenGL. The contract is vk_win.ll's:
; directories, then MoltenVK on its own (a bundle's Contents/Frameworks), then under
; $VULKAN_SDK - and a Mac without any gets 0 from vk_open and stays on OpenGL. The contract is vk_win.ll's:
;
; lvk_open() -> 1 | 0 lvk_sym(name) -> ptr lvk_has(name) -> 1 | 0
;
@ -25,6 +25,10 @@ declare i32 @lvk_bind()
@.lvk_c3 = private unnamed_addr constant [36 x i8] c"/opt/homebrew/lib/libvulkan.1.dylib\00"
@.lvk_env = private unnamed_addr constant [11 x i8] c"VULKAN_SDK\00"
@.lvk_fmt = private unnamed_addr constant [25 x i8] c"%s/lib/libvulkan.1.dylib\00"
@.lvk_fmtm = private unnamed_addr constant [25 x i8] c"%s/lib/libMoltenVK.dylib\00"
@.lvk_m0 = private unnamed_addr constant [49 x i8] c"@executable_path/../Frameworks/libMoltenVK.dylib\00"
@.lvk_m1 = private unnamed_addr constant [35 x i8] c"@executable_path/libMoltenVK.dylib\00"
@.lvk_m2 = private unnamed_addr constant [18 x i8] c"libMoltenVK.dylib\00"
@lvk_lib = internal global ptr null
; RTLD_NOW (2) | RTLD_LOCAL (4)
@ -34,52 +38,51 @@ entry:
ret ptr %h
}
; the candidates in order: a Vulkan loader first (the SDK's, so the validation layer can be
; stacked in development), then MoltenVK itself, which exports every vk* entry point and is what
; a bundle ships in Contents/Frameworks - no loader, no ICD json.
@lvk_paths = internal constant [7 x ptr] [ptr @.lvk_c0, ptr @.lvk_c1, ptr @.lvk_c2, ptr @.lvk_c3, ptr @.lvk_m0, ptr @.lvk_m1, ptr @.lvk_m2]
define i32 @lvk_open() {
entry:
%have = load ptr, ptr @lvk_lib
%open = icmp ne ptr %have, null
br i1 %open, label %ok, label %t0
t0:
%h0 = call ptr @lvk_try(ptr @.lvk_c0)
%n0 = icmp eq ptr %h0, null
br i1 %n0, label %t1, label %got0
got0:
store ptr %h0, ptr @lvk_lib
br i1 %open, label %ok, label %loop
loop:
%i = phi i32 [ 0, %entry ], [ %i1, %next ]
%slot = getelementptr [7 x ptr], ptr @lvk_paths, i32 0, i32 %i
%path = load ptr, ptr %slot
%h = call ptr @lvk_try(ptr %path)
%miss = icmp eq ptr %h, null
br i1 %miss, label %next, label %got
next:
%i1 = add i32 %i, 1
%more = icmp slt i32 %i1, 7
br i1 %more, label %loop, label %sdk
got:
store ptr %h, ptr @lvk_lib
br label %bind
t1:
%h1 = call ptr @lvk_try(ptr @.lvk_c1)
%n1 = icmp eq ptr %h1, null
br i1 %n1, label %t2, label %got1
got1:
store ptr %h1, ptr @lvk_lib
br label %bind
t2:
%h2 = call ptr @lvk_try(ptr @.lvk_c2)
%n2 = icmp eq ptr %h2, null
br i1 %n2, label %t3, label %got2
got2:
store ptr %h2, ptr @lvk_lib
br label %bind
t3:
%h3 = call ptr @lvk_try(ptr @.lvk_c3)
%n3 = icmp eq ptr %h3, null
br i1 %n3, label %t4, label %got3
got3:
store ptr %h3, ptr @lvk_lib
br label %bind
t4:
%sdk = call ptr @getenv(ptr @.lvk_env)
%nosdk = icmp eq ptr %sdk, null
br i1 %nosdk, label %fail, label %t4b
t4b:
sdk:
%sdkp = call ptr @getenv(ptr @.lvk_env)
%nosdk = icmp eq ptr %sdkp, null
br i1 %nosdk, label %fail, label %s1
s1:
%buf = alloca [1024 x i8]
%w = call i32 (ptr, i64, ptr, ...) @snprintf(ptr %buf, i64 1024, ptr @.lvk_fmt, ptr %sdk)
%w = call i32 (ptr, i64, ptr, ...) @snprintf(ptr %buf, i64 1024, ptr @.lvk_fmt, ptr %sdkp)
%h4 = call ptr @lvk_try(ptr %buf)
%n4 = icmp eq ptr %h4, null
br i1 %n4, label %fail, label %got4
br i1 %n4, label %s2, label %got4
s2:
%w2 = call i32 (ptr, i64, ptr, ...) @snprintf(ptr %buf, i64 1024, ptr @.lvk_fmtm, ptr %sdkp)
%h5 = call ptr @lvk_try(ptr %buf)
%n5 = icmp eq ptr %h5, null
br i1 %n5, label %fail, label %got5
got4:
store ptr %h4, ptr @lvk_lib
br label %bind
got5:
store ptr %h5, ptr @lvk_lib
br label %bind
bind:
%n = call i32 @lvk_bind()
br label %ok

View file

@ -547,6 +547,16 @@ entry:
%inst = call ptr @GetModuleHandleA(ptr null)
ret ptr %inst
}
; macOS only (cocoa.ll): the CAMetalLayer MoltenVK presents to
define ptr @win_metal_layer() {
entry:
ret ptr null
}
; the window is always drawn to here (cocoa.ll asks macOS whether it is covered)
define i32 @win_visible() {
entry:
ret i32 1
}
; set (%on != 0) or clear a key's bit in the 256-bit held set
define void @win_held_bit(i32 %k, i32 %on) {

View file

@ -20,6 +20,12 @@ function is_intrinsic(name: pointer) -> bool {
function emit_bind(rest: pointer) -> pointer { let r = nreg(); emit(" "); emit(r); emit(" = "); emit(rest); emit("\n"); return r }
function arg_code(e: Node, i: int) -> pointer { let v = emit_expr(e.kids[i]); return v.code }
# a buffer handed to the platform layer: a slice's elements, never its header (as an extern's are)
function arg_buf(e: Node, i: int) -> pointer {
let v = emit_expr(e.kids[i])
if is_slice_ty(v.ty) { return emit_bind(`load ptr, ptr {slice_field(v.code, 0)}`) }
return v.code
}
function emit_intrinsic(name: pointer, e: Node) -> Val {
g_intrin_ok = true

View file

@ -70,7 +70,7 @@ function emit_intrinsic2(name: pointer, e: Node) -> Val {
return val("0", "void")
}
if (name == "win_present") {
let a = arg_code(e, 0); let b = arg_code(e, 1); let c = arg_code(e, 2)
let a = arg_buf(e, 0); let b = arg_code(e, 1); let c = arg_code(e, 2)
emit(" call void @win_present(ptr "); emit(a); emit(", i32 "); emit(b); emit(", i32 "); emit(c); emit(")\n")
return val("0", "void")
}
@ -78,19 +78,19 @@ function emit_intrinsic2(name: pointer, e: Node) -> Val {
# #50 — fill a caller buffer with the platform device state (windowed only,
# DCE'd headless). win_held: an 8-word held-key bitset; win_mouse: [x, y,
# button-mask, wheel-delta].
if (name == "win_held") { let a = arg_code(e, 0); emit(" call void @win_held(ptr "); emit(a); emit(")\n"); return val("0", "void") }
if (name == "win_mouse") { let a = arg_code(e, 0); emit(" call void @win_mouse(ptr "); emit(a); emit(")\n"); return val("0", "void") }
if (name == "win_held") { let a = arg_buf(e, 0); emit(" call void @win_held(ptr "); emit(a); emit(")\n"); return val("0", "void") }
if (name == "win_mouse") { let a = arg_buf(e, 0); emit(" call void @win_mouse(ptr "); emit(a); emit(")\n"); return val("0", "void") }
# what the active keyboard layout types on a held-set key position (windowed only,
# DCE'd headless): a code point, 0 for none.
if (name == "win_key_char") { let a = arg_code(e, 0); return val(emit_bind(`call i32 @win_key_char(i32 {a})`), "int") }
# what the player TYPED since the last call - UTF-16 code units into the caller's buffer, one per
# word, returning how many. The layout, the modifiers and any dead key are already applied, which
# is why a name field must read this rather than build text out of key codes.
if (name == "win_text") { let a = arg_code(e, 0); let b = arg_code(e, 1); return val(emit_bind(`call i32 @win_text(ptr {a}, i32 {b})`), "int") }
if (name == "win_text") { let a = arg_buf(e, 0); let b = arg_code(e, 1); return val(emit_bind(`call i32 @win_text(ptr {a}, i32 {b})`), "int") }
# #51 — win_pad: 6 words/pad [connected, mask, lx, ly, rx, ry]; win_touch: 3
# words/point [active, x, y]. Both windowed-only, DCE'd headless.
if (name == "win_pad") { let a = arg_code(e, 0); emit(" call void @win_pad(ptr "); emit(a); emit(")\n"); return val("0", "void") }
if (name == "win_touch") { let a = arg_code(e, 0); emit(" call void @win_touch(ptr "); emit(a); emit(")\n"); return val("0", "void") }
if (name == "win_pad") { let a = arg_buf(e, 0); emit(" call void @win_pad(ptr "); emit(a); emit(")\n"); return val("0", "void") }
if (name == "win_touch") { let a = arg_buf(e, 0); emit(" call void @win_touch(ptr "); emit(a); emit(")\n"); return val("0", "void") }
# #89 — set the OS cursor capture mode (windowed only; DCE'd headless).
if (name == "win_cursor_mode") { let a = arg_code(e, 0); emit(" call void @win_cursor_mode(i32 "); emit(a); emit(")\n"); return val("0", "void") }
# #22 audio backend — AVAudioPlayer via audio.ll; windowed-only, DCE'd headless.

View file

@ -358,6 +358,8 @@ entry {
var vkl = join_path(home, "runtime/native/vk_mac.ll")
if g_target_win { vkl = join_path(home, "runtime/native/vk_win.ll") }
cmd = `{cmd} {vkt} {vkl}`
# the CAMetalLayer a macOS window presents through, found by name in cocoa.ll
if not g_target_win { cmd = `{cmd} -Wl,-needed_framework,QuartzCore` }
}
# A shipped Windows game is a GUI-subsystem program: started from Explorer, a
# console-subsystem one opens a console window behind the game. mainCRTStartup keeps

View file

@ -102,7 +102,8 @@ function gl_link_flags(ll: pointer) -> pointer {
function vk_link_flags(ll: pointer) -> pointer {
if not shq(`grep -q "@lvk_" {ll}`) { return "" }
let home = ludic_home()
return ` {home}runtime/native/vk_thunks.ll {home}runtime/native/vk_mac.ll`
# QuartzCore: the CAMetalLayer a window presents through (cocoa.ll finds the class by name)
return ` {home}runtime/native/vk_thunks.ll {home}runtime/native/vk_mac.ll -Wl,-needed_framework,QuartzCore`
}
# A program that uses Http.* calls the @hs_* transport; link http.ll and Foundation only