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
|
`addObserver:selector:name:object:` selector derives cleanly from
|
||||||
the underscore-separated sx name (`addObserver_selector_name_object`).
|
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:
|
Open work:
|
||||||
- **Phase 3 step 3.2 — C3..C5** — uikit.sx migration, one cluster
|
- **Phase 3 step 3.2 — C4..C5** — uikit.sx migration, two clusters
|
||||||
per commit, chess regression after each.
|
remaining (UIKit chrome, view tree + GL).
|
||||||
test for the default-mangling table. Escape hatch for selectors
|
test for the default-mangling table. Escape hatch for selectors
|
||||||
that don't fit the underscore-split rule (e.g. `tableView_
|
that don't fit the underscore-split rule (e.g. `tableView_
|
||||||
numberOfRowsInSection_` with an asymmetric keyword count).
|
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);
|
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
|
// GLenum constants for renderbuffer/framebuffer setup that aren't in opengl.sx's
|
||||||
// loader path (they live on the framework's symbol table directly).
|
// loader path (they live on the framework's symbol table directly).
|
||||||
GL_RENDERBUFFER :u32: 0x8D41;
|
GL_RENDERBUFFER :u32: 0x8D41;
|
||||||
@@ -534,8 +547,9 @@ uikit_scene_will_connect_ios :: (delegate: *void, scene: *void) {
|
|||||||
UIViewController := objc_getClass("UIViewController".ptr);
|
UIViewController := objc_getClass("UIViewController".ptr);
|
||||||
SxGLView := objc_getClass("SxGLView".ptr);
|
SxGLView := objc_getClass("SxGLView".ptr);
|
||||||
SxMetalView := objc_getClass("SxMetalView".ptr);
|
SxMetalView := objc_getClass("SxMetalView".ptr);
|
||||||
CADisplayLink := objc_getClass("CADisplayLink".ptr);
|
// CADisplayLink and NSRunLoop class objects come from the
|
||||||
NSRunLoop := objc_getClass("NSRunLoop".ptr);
|
// 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");
|
win_raw := #objc_call(*void)(UIWindow, "alloc");
|
||||||
plat.window = #objc_call(*void)(win_raw, "initWithWindowScene:", scene);
|
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
|
// a SEL value (not a dispatch selector), so it still goes through
|
||||||
// sel_registerName.
|
// sel_registerName.
|
||||||
sel_tick := sel_registerName("sxTick:".ptr);
|
sel_tick := sel_registerName("sxTick:".ptr);
|
||||||
plat.display_link = #objc_call(*void)(CADisplayLink, "displayLinkWithTarget:selector:", plat.gl_view, sel_tick);
|
link := CADisplayLink.displayLinkWithTarget_selector(plat.gl_view, sel_tick);
|
||||||
runloop := #objc_call(*void)(NSRunLoop, "currentRunLoop");
|
plat.display_link = xx link;
|
||||||
mode := ns_string("kCFRunLoopDefaultMode".ptr);
|
runloop := NSRunLoop.currentRunLoop();
|
||||||
#objc_call(void)(plat.display_link, "addToRunLoop:forMode:", runloop, mode);
|
mode_ns := ns_string("kCFRunLoopDefaultMode".ptr);
|
||||||
|
link.addToRunLoop_forMode(runloop, mode_ns);
|
||||||
|
|
||||||
NSLog(ns_string("[sx] UIKitPlatform booted\n".ptr));
|
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);
|
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; }
|
if g_uikit_plat == null { return; }
|
||||||
plat := g_uikit_plat;
|
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
|
// and keyboardLayoutGuide — none eliminated the lag without
|
||||||
// cascade-breaking the GL view's frame.
|
// cascade-breaking the GL view's frame.
|
||||||
if plat.kb_animating {
|
if plat.kb_animating {
|
||||||
target_ts := #objc_call(f64)(link, "targetTimestamp");
|
target_ts := link.targetTimestamp();
|
||||||
elapsed := target_ts - plat.kb_anim_start;
|
elapsed := target_ts - plat.kb_anim_start;
|
||||||
// Negative elapsed can happen if the just-fired willChangeFrame
|
// Negative elapsed can happen if the just-fired willChangeFrame
|
||||||
// set kb_anim_start to a wall time AFTER the tick already
|
// 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.has_frame_closure { return; }
|
||||||
if !plat.gl_initialized { return; }
|
if !plat.gl_initialized { return; }
|
||||||
|
|
||||||
dur_d := #objc_call(f64)(link, "duration");
|
dur_d := link.duration();
|
||||||
plat.delta_time = xx dur_d;
|
plat.delta_time = xx dur_d;
|
||||||
// Stash the targetTimestamp so begin_frame can hand it down to the
|
// Stash the targetTimestamp so begin_frame can hand it down to the
|
||||||
// game in FrameContext for Metal presentDrawable:atTime:.
|
// 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 := plat.frame_closure;
|
||||||
fn();
|
fn();
|
||||||
|
|||||||
Reference in New Issue
Block a user