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

@ -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
}