app: remove iOS system draw loop

Much like the recent change for OS X, this puts the Go paint loop in
control of drawing onto the screen.

Change-Id: I37321e4bb58869d4c7cafc51951ea64e540d536b
Reviewed-on: https://go-review.googlesource.com/15611
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
This commit is contained in:
David Crawshaw 2015-10-08 13:49:30 -04:00
parent ab6091a309
commit b4e66eeef7
2 changed files with 91 additions and 25 deletions

View File

@ -14,11 +14,13 @@ package app
#include <stdint.h>
#include <pthread.h>
#include <UIKit/UIDevice.h>
#import <GLKit/GLKit.h>
extern struct utsname sysInfo;
void runApp(void);
void setContext(void* context);
void makeCurrentContext(GLintptr ctx);
void swapBuffers(GLintptr ctx);
uint64_t threadID();
*/
import "C"
@ -27,7 +29,6 @@ import (
"runtime"
"strings"
"sync"
"unsafe"
"golang.org/x/mobile/event/lifecycle"
"golang.org/x/mobile/event/paint"
@ -116,6 +117,7 @@ func updateConfig(width, height, orientation int32) {
PixelsPerPt: pixelsPerPt,
Orientation: o,
}
theApp.eventsIn <- paint.Event{External: true}
}
// touchIDs is the current active touches. The position in the array
@ -165,28 +167,50 @@ func sendTouch(cTouch, cTouchType uintptr, x, y float32) {
}
}
var workAvailable <-chan struct{}
//export lifecycleDead
func lifecycleDead() { theApp.sendLifecycle(lifecycle.StageDead) }
//export drawgl
func drawgl(ctx uintptr) {
if workAvailable == nil {
C.setContext(unsafe.Pointer(ctx))
workAvailable = theApp.worker.WorkAvailable()
// TODO(crawshaw): not just on process start.
theApp.sendLifecycle(lifecycle.StageFocused)
}
//export lifecycleAlive
func lifecycleAlive() { theApp.sendLifecycle(lifecycle.StageAlive) }
// TODO(crawshaw): don't send a paint.Event unconditionally. Only send one
// if the window actually needs redrawing.
theApp.eventsIn <- paint.Event{}
//export lifecycleVisible
func lifecycleVisible() { theApp.sendLifecycle(lifecycle.StageVisible) }
//export lifecycleFocused
func lifecycleFocused() { theApp.sendLifecycle(lifecycle.StageFocused) }
//export startloop
func startloop(ctx C.GLintptr) {
go theApp.loop(ctx)
}
// loop is the primary drawing loop.
//
// After UIKit has captured the initial OS thread for processing UIKit
// events in runApp, it starts loop on another goroutine. It is locked
// to an OS thread for its OpenGL context.
func (a *app) loop(ctx C.GLintptr) {
runtime.LockOSThread()
C.makeCurrentContext(ctx)
workAvailable := a.worker.WorkAvailable()
for {
select {
case <-workAvailable:
theApp.worker.DoWork()
a.worker.DoWork()
case <-theApp.publish:
loop1:
for {
select {
case <-workAvailable:
a.worker.DoWork()
default:
break loop1
}
}
C.swapBuffers(ctx)
theApp.publishResult <- PublishResult{}
return
}
}
}

View File

@ -15,7 +15,7 @@
struct utsname sysInfo;
@interface GoAppAppController : GLKViewController<UIContentContainer>
@interface GoAppAppController : GLKViewController<UIContentContainer, GLKViewDelegate>
@end
@interface GoAppAppDelegate : UIResponder<UIApplicationDelegate>
@ -25,27 +25,58 @@ struct utsname sysInfo;
@implementation GoAppAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
lifecycleAlive();
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.controller = [[GoAppAppController alloc] initWithNibName:nil bundle:nil];
self.window.rootViewController = self.controller;
[self.window makeKeyAndVisible];
return YES;
}
- (void)applicationDidBecomeActive:(UIApplication * )application {
lifecycleFocused();
}
- (void)applicationWillResignActive:(UIApplication *)application {
lifecycleVisible();
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
lifecycleAlive();
}
- (void)applicationWillTerminate:(UIApplication *)application {
lifecycleDead();
}
@end
@interface GoAppAppController ()
@property (strong, nonatomic) EAGLContext *context;
@property (strong, nonatomic) GLKView *glview;
@end
@implementation GoAppAppController
- (void)viewWillAppear:(BOOL)animated
{
// TODO: replace by swapping out GLKViewController for a UIVIewController.
[super viewWillAppear:animated];
self.paused = YES;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.preferredFramesPerSecond = 60;
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
GLKView *view = (GLKView *)self.view;
view.context = self.context;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
view.multipleTouchEnabled = true; // TODO expose setting to user.
self.glview = (GLKView*)self.view;
self.glview.drawableDepthFormat = GLKViewDrawableDepthFormat24;
self.glview.multipleTouchEnabled = true; // TODO expose setting to user.
self.glview.context = self.context;
self.glview.userInteractionEnabled = YES;
self.glview.enableSetNeedsDisplay = YES; // only invoked once
// Do not use the GLKViewController draw loop.
self.paused = YES;
self.resumeOnDidBecomeActive = NO;
self.preferredFramesPerSecond = 0;
int scale = 1;
if ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)]) {
@ -67,8 +98,11 @@ struct utsname sysInfo;
}];
}
- (void)update {
drawgl((GoUintptr)self.context);
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
// Now that we have been asked to do the first draw, disable any
// future draw and hand control over to the Go paint.Event cycle.
self.glview.enableSetNeedsDisplay = NO;
startloop((GLintptr)self.context);
}
#define TOUCH_TYPE_BEGIN 0 // touch.TypeBegin
@ -106,7 +140,7 @@ void runApp(void) {
}
}
void setContext(void* context) {
void makeCurrentContext(GLintptr context) {
EAGLContext* ctx = (EAGLContext*)context;
if (![EAGLContext setCurrentContext:ctx]) {
// TODO(crawshaw): determine how terrible this is. Exit?
@ -114,6 +148,14 @@ void setContext(void* context) {
}
}
void swapBuffers(GLintptr context) {
__block EAGLContext* ctx = (EAGLContext*)context;
dispatch_sync(dispatch_get_main_queue(), ^{
[EAGLContext setCurrentContext:ctx];
[ctx presentRenderbuffer:GL_RENDERBUFFER];
});
}
uint64_t threadID() {
uint64_t id;
if (pthread_threadid_np(pthread_self(), &id)) {