ffi 3.2 C3: migrate uikit.sx RunLoop + display-timing cluster
Third cluster: NSRunLoop and CADisplayLink move to declarative
`#objc_class` blocks.
Classes declared:
- NSRunLoop → currentRunLoop (class)
- CADisplayLink → displayLinkWithTarget_selector (class),
addToRunLoop_forMode (instance),
targetTimestamp (instance), duration (instance)
The display-link instance is created with the new typed call shape:
link := CADisplayLink.displayLinkWithTarget_selector(plat.gl_view, sel_tick);
plat.display_link = xx link; // keep the *void slot in the
// platform struct for ABI parity
runloop := NSRunLoop.currentRunLoop();
link.addToRunLoop_forMode(runloop, mode_ns);
The `sxTick:` callback's `link: *void` param is cast to
`*CADisplayLink` at function entry so the body's `link.duration()` /
`link.targetTimestamp()` calls type-check.
Two now-redundant `objc_getClass(...)` lookups for CADisplayLink /
NSRunLoop are gone — the class slots come from the declarative
declarations via `__sx_objc_class_init`.
166/166 tests; chess builds clean on macOS / iOS / Android.
This commit is contained in:
@@ -560,9 +560,17 @@ declared as `#foreign #objc_class` blocks. The 4-keyword
|
||||
`addObserver:selector:name:object:` selector derives cleanly from
|
||||
the underscore-separated sx name (`addObserver_selector_name_object`).
|
||||
|
||||
Phase 3.2 C3 landed: RunLoop + display-timing cluster. NSRunLoop
|
||||
(`currentRunLoop`) and CADisplayLink
|
||||
(`displayLinkWithTarget_selector`, `addToRunLoop_forMode`,
|
||||
`targetTimestamp`, `duration`) declared as `#foreign #objc_class`
|
||||
blocks. The `link` parameter on the `sxTick:` callback is now cast
|
||||
to `*CADisplayLink` at function entry so subsequent method calls
|
||||
type-check.
|
||||
|
||||
Open work:
|
||||
- **Phase 3 step 3.2 — C3..C5** — uikit.sx migration, one cluster
|
||||
per commit, chess regression after each.
|
||||
- **Phase 3 step 3.2 — C4..C5** — uikit.sx migration, two clusters
|
||||
remaining (UIKit chrome, view tree + GL).
|
||||
test for the default-mangling table. Escape hatch for selectors
|
||||
that don't fit the underscore-split rule (e.g. `tableView_
|
||||
numberOfRowsInSection_` with an asymmetric keyword count).
|
||||
|
||||
@@ -100,6 +100,19 @@ NSNotificationCenter :: #foreign #objc_class("NSNotificationCenter") {
|
||||
addObserver_selector_name_object :: (self: *Self, observer: *void, sel: *void, name: *void, obj: *void);
|
||||
}
|
||||
|
||||
// ── RunLoop + display timing (Phase 3.2 C3) ────────────────────────────
|
||||
|
||||
NSRunLoop :: #foreign #objc_class("NSRunLoop") {
|
||||
currentRunLoop :: () -> *NSRunLoop;
|
||||
}
|
||||
|
||||
CADisplayLink :: #foreign #objc_class("CADisplayLink") {
|
||||
displayLinkWithTarget_selector :: (target: *void, sel: *void) -> *CADisplayLink;
|
||||
addToRunLoop_forMode :: (self: *Self, runloop: *NSRunLoop, mode: *void);
|
||||
targetTimestamp :: (self: *Self) -> f64;
|
||||
duration :: (self: *Self) -> f64;
|
||||
}
|
||||
|
||||
// GLenum constants for renderbuffer/framebuffer setup that aren't in opengl.sx's
|
||||
// loader path (they live on the framework's symbol table directly).
|
||||
GL_RENDERBUFFER :u32: 0x8D41;
|
||||
@@ -534,8 +547,9 @@ uikit_scene_will_connect_ios :: (delegate: *void, scene: *void) {
|
||||
UIViewController := objc_getClass("UIViewController".ptr);
|
||||
SxGLView := objc_getClass("SxGLView".ptr);
|
||||
SxMetalView := objc_getClass("SxMetalView".ptr);
|
||||
CADisplayLink := objc_getClass("CADisplayLink".ptr);
|
||||
NSRunLoop := objc_getClass("NSRunLoop".ptr);
|
||||
// CADisplayLink and NSRunLoop class objects come from the
|
||||
// declarative `#objc_class` slots populated by emit_llvm's
|
||||
// __sx_objc_class_init constructor — no local objc_getClass needed.
|
||||
|
||||
win_raw := #objc_call(*void)(UIWindow, "alloc");
|
||||
plat.window = #objc_call(*void)(win_raw, "initWithWindowScene:", scene);
|
||||
@@ -619,10 +633,11 @@ uikit_scene_will_connect_ios :: (delegate: *void, scene: *void) {
|
||||
// a SEL value (not a dispatch selector), so it still goes through
|
||||
// sel_registerName.
|
||||
sel_tick := sel_registerName("sxTick:".ptr);
|
||||
plat.display_link = #objc_call(*void)(CADisplayLink, "displayLinkWithTarget:selector:", plat.gl_view, sel_tick);
|
||||
runloop := #objc_call(*void)(NSRunLoop, "currentRunLoop");
|
||||
mode := ns_string("kCFRunLoopDefaultMode".ptr);
|
||||
#objc_call(void)(plat.display_link, "addToRunLoop:forMode:", runloop, mode);
|
||||
link := CADisplayLink.displayLinkWithTarget_selector(plat.gl_view, sel_tick);
|
||||
plat.display_link = xx link;
|
||||
runloop := NSRunLoop.currentRunLoop();
|
||||
mode_ns := ns_string("kCFRunLoopDefaultMode".ptr);
|
||||
link.addToRunLoop_forMode(runloop, mode_ns);
|
||||
|
||||
NSLog(ns_string("[sx] UIKitPlatform booted\n".ptr));
|
||||
}
|
||||
@@ -682,7 +697,8 @@ uikit_gl_view_layer_class :: (cls: *void, _cmd: *void) -> *void callconv(.c) {
|
||||
objc_getClass("CAEAGLLayer".ptr);
|
||||
}
|
||||
|
||||
uikit_gl_view_tick :: (self: *void, _cmd: *void, link: *void) callconv(.c) {
|
||||
uikit_gl_view_tick :: (self: *void, _cmd: *void, link_raw: *void) callconv(.c) {
|
||||
link : *CADisplayLink = xx link_raw;
|
||||
if g_uikit_plat == null { return; }
|
||||
plat := g_uikit_plat;
|
||||
|
||||
@@ -696,7 +712,7 @@ uikit_gl_view_tick :: (self: *void, _cmd: *void, link: *void) callconv(.c) {
|
||||
// and keyboardLayoutGuide — none eliminated the lag without
|
||||
// cascade-breaking the GL view's frame.
|
||||
if plat.kb_animating {
|
||||
target_ts := #objc_call(f64)(link, "targetTimestamp");
|
||||
target_ts := link.targetTimestamp();
|
||||
elapsed := target_ts - plat.kb_anim_start;
|
||||
// Negative elapsed can happen if the just-fired willChangeFrame
|
||||
// set kb_anim_start to a wall time AFTER the tick already
|
||||
@@ -723,11 +739,11 @@ uikit_gl_view_tick :: (self: *void, _cmd: *void, link: *void) callconv(.c) {
|
||||
if !plat.has_frame_closure { return; }
|
||||
if !plat.gl_initialized { return; }
|
||||
|
||||
dur_d := #objc_call(f64)(link, "duration");
|
||||
dur_d := link.duration();
|
||||
plat.delta_time = xx dur_d;
|
||||
// Stash the targetTimestamp so begin_frame can hand it down to the
|
||||
// game in FrameContext for Metal presentDrawable:atTime:.
|
||||
plat.last_target_ts = #objc_call(f64)(link, "targetTimestamp");
|
||||
plat.last_target_ts = link.targetTimestamp();
|
||||
|
||||
fn := plat.frame_closure;
|
||||
fn();
|
||||
|
||||
Reference in New Issue
Block a user