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