feat: add iOS threading and vsync handling
This commit is contained in:
parent
eba14aadbc
commit
202a35833e
|
|
@ -1,6 +1,8 @@
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import <OpenGLES/EAGL.h>
|
#import <OpenGLES/EAGL.h>
|
||||||
#import <OpenGLES/ES3/gl.h>
|
#import <OpenGLES/ES3/gl.h>
|
||||||
|
#import <QuartzCore/CADisplayLink.h>
|
||||||
|
#include <QuartzCore/CAFrameRateRange.h>
|
||||||
#import <QuartzCore/QuartzCore.h>
|
#import <QuartzCore/QuartzCore.h>
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
|
@ -41,6 +43,11 @@ typedef struct IOSBridgeState {
|
||||||
|
|
||||||
static IOSBridgeState ios = {0};
|
static IOSBridgeState ios = {0};
|
||||||
|
|
||||||
|
static dispatch_queue_t raylibQueue = nil;
|
||||||
|
static CADisplayLink *displayLink = nil;
|
||||||
|
static dispatch_semaphore_t frameSem = nil;
|
||||||
|
static int64_t vsyncCount = 0;
|
||||||
|
|
||||||
@interface RaylibGLView : UIView
|
@interface RaylibGLView : UIView
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
@ -75,6 +82,7 @@ static IOSBridgeState ios = {0};
|
||||||
[super viewWillDisappear:animated];
|
[super viewWillDisappear:animated];
|
||||||
ios_set_window_focused(false);
|
ios_set_window_focused(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)forwardTouches:(NSSet<UITouch *> *)touches action:(int)action {
|
- (void)forwardTouches:(NSSet<UITouch *> *)touches action:(int)action {
|
||||||
for (UITouch *touch in touches) {
|
for (UITouch *touch in touches) {
|
||||||
CGPoint point = [touch locationInView:self.view];
|
CGPoint point = [touch locationInView:self.view];
|
||||||
|
|
@ -122,57 +130,113 @@ static IOSBridgeState ios = {0};
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
void ios_shutdown_window(void) {
|
@interface RaylibDisplayLinkTarget : NSObject
|
||||||
|
+ (void)startDisplayLink;
|
||||||
|
+ (void)onDisplayFrame:(CADisplayLink *)link;
|
||||||
|
+ (void)stopDisplayLink;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation RaylibDisplayLinkTarget
|
||||||
|
|
||||||
|
+ (void)startDisplayLink {
|
||||||
|
if (displayLink != nil)
|
||||||
|
return;
|
||||||
|
|
||||||
|
frameSem = dispatch_semaphore_create(0);
|
||||||
|
vsyncCount = 0;
|
||||||
|
|
||||||
|
displayLink =
|
||||||
|
[CADisplayLink displayLinkWithTarget:[self class]
|
||||||
|
selector:@selector(onDisplayFrame:)];
|
||||||
|
|
||||||
|
NSInteger maxFPS = [UIScreen mainScreen].maximumFramesPerSecond;
|
||||||
|
|
||||||
|
// Use the display native refresh rate
|
||||||
|
displayLink.preferredFrameRateRange =
|
||||||
|
CAFrameRateRangeMake(60.0f, maxFPS, maxFPS);
|
||||||
|
|
||||||
|
[displayLink addToRunLoop:[NSRunLoop mainRunLoop]
|
||||||
|
forMode:NSRunLoopCommonModes];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)onDisplayFrame:(CADisplayLink *)link {
|
||||||
|
(void)link;
|
||||||
|
__atomic_fetch_add(&vsyncCount, 1, __ATOMIC_RELEASE);
|
||||||
|
dispatch_semaphore_signal(frameSem);
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)stopDisplayLink {
|
||||||
|
if (displayLink == nil)
|
||||||
|
return;
|
||||||
|
|
||||||
|
[displayLink invalidate];
|
||||||
|
displayLink = nil;
|
||||||
|
|
||||||
|
dispatch_semaphore_signal(frameSem);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
static void shutdown_internal(void) {
|
||||||
|
[RaylibDisplayLinkTarget stopDisplayLink];
|
||||||
|
|
||||||
|
if (ios.context != nil) {
|
||||||
|
[EAGLContext setCurrentContext:ios.context];
|
||||||
|
|
||||||
|
if (ios.framebuffer != 0)
|
||||||
|
glDeleteFramebuffers(1, &ios.framebuffer);
|
||||||
|
if (ios.colorBuffer != 0)
|
||||||
|
glDeleteRenderbuffers(1, &ios.colorBuffer);
|
||||||
|
if (ios.depthBuffer != 0)
|
||||||
|
glDeleteRenderbuffers(1, &ios.depthBuffer);
|
||||||
|
|
||||||
|
ios.framebuffer = 0;
|
||||||
|
ios.colorBuffer = 0;
|
||||||
|
ios.depthBuffer = 0;
|
||||||
|
ios.context = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
ios.glView = nil;
|
||||||
|
ios.viewController = nil;
|
||||||
|
ios.window = nil;
|
||||||
|
ios.initialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ios_close_platform(void) {
|
||||||
if (!ios.initialized || ios.shuttingDown)
|
if (!ios.initialized || ios.shuttingDown)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ios.shuttingDown = true;
|
ios.shuttingDown = true;
|
||||||
|
|
||||||
void (^shutdownBlock)(void) = ^{
|
|
||||||
if (ios.context != nil) {
|
|
||||||
[EAGLContext setCurrentContext:ios.context];
|
|
||||||
|
|
||||||
if (ios.framebuffer != 0)
|
|
||||||
glDeleteFramebuffers(1, &ios.framebuffer);
|
|
||||||
if (ios.colorBuffer != 0)
|
|
||||||
glDeleteRenderbuffers(1, &ios.colorBuffer);
|
|
||||||
if (ios.depthBuffer != 0)
|
|
||||||
glDeleteRenderbuffers(1, &ios.depthBuffer);
|
|
||||||
|
|
||||||
ios.framebuffer = 0;
|
|
||||||
ios.colorBuffer = 0;
|
|
||||||
ios.depthBuffer = 0;
|
|
||||||
ios.context = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
ios.glView = nil;
|
|
||||||
ios.viewController = nil;
|
|
||||||
ios.window = nil;
|
|
||||||
ios.initialized = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
if ([NSThread isMainThread])
|
if ([NSThread isMainThread])
|
||||||
shutdownBlock();
|
shutdown_internal();
|
||||||
else
|
else
|
||||||
dispatch_sync(dispatch_get_main_queue(), shutdownBlock);
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
shutdown_internal();
|
||||||
ios.shuttingDown = false;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@interface RaylibAppDelegate : UIResponder <UIApplicationDelegate>
|
@interface RaylibAppDelegate : UIResponder <UIApplicationDelegate>
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface RaylibSceneDelegate : UIResponder <UIWindowSceneDelegate>
|
@interface RaylibSceneDelegate : UIResponder <UIWindowSceneDelegate>
|
||||||
|
|
||||||
@property(strong, nonatomic) UIWindow *window;
|
@property(strong, nonatomic) UIWindow *window;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
static void ios_start_raylib_main(void) {
|
static void ios_start_raylib_main(void) {
|
||||||
if (ios.raylibStarted)
|
if (ios.raylibStarted)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ios.raylibStarted = true;
|
ios.raylibStarted = true;
|
||||||
|
|
||||||
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
|
raylibQueue =
|
||||||
|
dispatch_queue_create("com.raylib.engine", DISPATCH_QUEUE_SERIAL);
|
||||||
|
|
||||||
|
dispatch_async(raylibQueue, ^{
|
||||||
|
[[NSThread currentThread] setName:@"com.raylib.engine"];
|
||||||
|
|
||||||
|
// NSLog(@"[raylib][IOS] raylib thread: %@", [NSThread currentThread].name);
|
||||||
|
|
||||||
raylib_main(ios.argc, ios.argv);
|
raylib_main(ios.argc, ios.argv);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -240,6 +304,8 @@ static void ios_start_raylib_main(void) {
|
||||||
self.window.rootViewController = ios.viewController;
|
self.window.rootViewController = ios.viewController;
|
||||||
[self.window makeKeyAndVisible];
|
[self.window makeKeyAndVisible];
|
||||||
|
|
||||||
|
[RaylibDisplayLinkTarget startDisplayLink];
|
||||||
|
|
||||||
ios_start_raylib_main();
|
ios_start_raylib_main();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -256,7 +322,7 @@ static void ios_start_raylib_main(void) {
|
||||||
- (void)sceneDidDisconnect:(UIScene *)scene {
|
- (void)sceneDidDisconnect:(UIScene *)scene {
|
||||||
(void)scene;
|
(void)scene;
|
||||||
ios_request_close();
|
ios_request_close();
|
||||||
ios_shutdown_window();
|
ios_close_platform();
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
@ -314,9 +380,6 @@ static BOOL ios_setup_gl(void) {
|
||||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
|
||||||
GL_RENDERBUFFER, ios.depthBuffer);
|
GL_RENDERBUFFER, ios.depthBuffer);
|
||||||
|
|
||||||
BOOL fbComplete =
|
|
||||||
(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
|
|
||||||
|
|
||||||
GLenum fbStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
GLenum fbStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||||
if (fbStatus != GL_FRAMEBUFFER_COMPLETE) {
|
if (fbStatus != GL_FRAMEBUFFER_COMPLETE) {
|
||||||
NSLog(@"[raylib][IOS] Framebuffer incomplete: 0x%04X", fbStatus);
|
NSLog(@"[raylib][IOS] Framebuffer incomplete: 0x%04X", fbStatus);
|
||||||
|
|
@ -327,7 +390,7 @@ static BOOL ios_setup_gl(void) {
|
||||||
|
|
||||||
[EAGLContext setCurrentContext:nil];
|
[EAGLContext setCurrentContext:nil];
|
||||||
|
|
||||||
return fbComplete;
|
return fbStatus == GL_FRAMEBUFFER_COMPLETE;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ios_initialize_window(int requestedWidth, int requestedHeight,
|
bool ios_initialize_window(int requestedWidth, int requestedHeight,
|
||||||
|
|
@ -419,6 +482,17 @@ void ios_present_frame(void) {
|
||||||
err = glGetError();
|
err = glGetError();
|
||||||
if (err != GL_NO_ERROR)
|
if (err != GL_NO_ERROR)
|
||||||
NSLog(@"[raylib][IOS] glGetError after present: 0x%04X", err);
|
NSLog(@"[raylib][IOS] glGetError after present: 0x%04X", err);
|
||||||
|
if (frameSem != nil && !ios.shuttingDown) {
|
||||||
|
static int64_t lastVsync = 0;
|
||||||
|
int64_t current = __atomic_load_n(&vsyncCount, __ATOMIC_ACQUIRE);
|
||||||
|
|
||||||
|
while ((current - lastVsync) < 1 && !ios.shuttingDown) {
|
||||||
|
dispatch_semaphore_wait(frameSem, DISPATCH_TIME_FOREVER);
|
||||||
|
current = __atomic_load_n(&vsyncCount, __ATOMIC_ACQUIRE);
|
||||||
|
}
|
||||||
|
|
||||||
|
lastVsync = current;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void *ios_get_window_handle(void) { return (__bridge void *)ios.window; }
|
void *ios_get_window_handle(void) { return (__bridge void *)ios.window; }
|
||||||
|
|
@ -442,7 +516,7 @@ void ios_get_window_metrics(int *screenWidth, int *screenHeight,
|
||||||
*scaleX = ios.scaleX;
|
*scaleX = ios.scaleX;
|
||||||
|
|
||||||
if (scaleY)
|
if (scaleY)
|
||||||
*scaleY = ios.scaleY;
|
*scaleY = ios.scaleY;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *ios_get_proc_address(const char *name) {
|
void *ios_get_proc_address(const char *name) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user