platform: UIKit backend renders GLES3 via CAEAGLLayer + CADisplayLink

End-to-end on iOS sim: UIKitPlatform boots an SxAppDelegate, installs
an SxGLView (UIView subclass overriding +layerClass to return
CAEAGLLayer) as the root view controller's view, sets the drawable
properties (EAGLColorFormatRGBA8, non-retained backing — looked up by
dlsym so pointer-identity-checked constants match), creates an
EAGLContext (GLES3), and registers a CADisplayLink that invokes the
user's frame closure on every vsync. end_frame presents the
renderbuffer via [EAGLContext presentRenderbuffer:].

The renderbuffer is allocated lazily in -[SxGLView layoutSubviews] once
the layer has its real on-screen bounds — allocating earlier (e.g. in
didFinishLaunching) failed with INCOMPLETE_ATTACHMENT because the
SxGLView's frame was still zero at that point. Setting the SxGLView
as the VC's `view` (via setView:) lets the standard VC layout pipeline
size it to the window without us having to read CGRect struct returns
from objc_msgSend.

EAGL drawableProperties dict keys/values are dlsym'd from OpenGLES —
the framework checks them by pointer identity, so synthesized NSString
literals with the same contents don't work.

examples/66-uikit-platform.sx — runnable smoke test that cycles the
screen color (red → green → blue every 30 frames) so you can confirm
the display-link tick and present pipeline.

modules/opengl.sx gains glGenFramebuffers, glGenRenderbuffers,
glBindFramebuffer, glBindRenderbuffer, glFramebufferRenderbuffer,
glGetRenderbufferParameteriv, glCheckFramebufferStatus — needed for
the iOS GLES FBO-to-renderbuffer setup. They're wired into load_gl
so SDL and the iOS dlsym loader both pick them up.

Compiles cleanly on macOS / WASM / iOS-sim. Non-iOS targets never
reference the unresolved UIKit/QuartzCore/OpenGLES symbols because
every Obj-C touch lives inside `inline if OS == .ios`.

Game's iOS path still goes through SDL3 for now. Touch events + game
wire-up + keyboard observer = next steps.
This commit is contained in:
agra
2026-05-17 15:51:57 +03:00
parent 32da32ca66
commit 858d691181
3 changed files with 510 additions and 0 deletions

View File

@@ -90,6 +90,14 @@ glPixelStorei : (u32, s32) -> void = ---;
glTexSubImage2D : (u32, s32, s32, s32, s32, s32, u32, u32, *void) -> void = ---;
glDeleteTextures : (s32, *u32) -> void = ---;
glGenFramebuffers : (s32, *u32) -> void = ---;
glGenRenderbuffers : (s32, *u32) -> void = ---;
glBindFramebuffer : (u32, u32) -> void = ---;
glBindRenderbuffer : (u32, u32) -> void = ---;
glFramebufferRenderbuffer : (u32, u32, u32, u32) -> void = ---;
glGetRenderbufferParameteriv : (u32, u32, *s32) -> void = ---;
glCheckFramebufferStatus : (u32) -> u32 = ---;
GL_TEXTURE_WRAP_S :u32: 0x2802;
GL_TEXTURE_WRAP_T :u32: 0x2803;
GL_CLAMP_TO_EDGE :u32: 0x812F;
@@ -143,6 +151,14 @@ load_gl :: (get_proc: ([*]u8) -> *void) {
glPixelStorei = xx get_proc("glPixelStorei");
glTexSubImage2D = xx get_proc("glTexSubImage2D");
glDeleteTextures = xx get_proc("glDeleteTextures");
glGenFramebuffers = xx get_proc("glGenFramebuffers");
glGenRenderbuffers = xx get_proc("glGenRenderbuffers");
glBindFramebuffer = xx get_proc("glBindFramebuffer");
glBindRenderbuffer = xx get_proc("glBindRenderbuffer");
glFramebufferRenderbuffer = xx get_proc("glFramebufferRenderbuffer");
glGetRenderbufferParameteriv = xx get_proc("glGetRenderbufferParameteriv");
glCheckFramebufferStatus = xx get_proc("glCheckFramebufferStatus");
}