Spruce up iOS viewer and cut CPU drain
Main outcome here is that we only draw once per frame instead
of pinning the CPU. We also cut out a little extra traffic – an
autorelease pool per draw, but trivial. We also fix an overrelease
on NSProcessInfo.arguments, and a missing -init for the UIViewController
etc.
Change-Id: Ibb5ea5832bc65d2ff60351d4e3b76fd3b4ff88ee
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/337116
Commit-Queue: Adlai Holler <adlai@google.com>
Reviewed-by: Jim Van Verth <jvanverth@google.com>
diff --git a/tools/sk_app/ios/Window_ios.mm b/tools/sk_app/ios/Window_ios.mm
index 36f50fc..d3e34b3 100644
--- a/tools/sk_app/ios/Window_ios.mm
+++ b/tools/sk_app/ios/Window_ios.mm
@@ -8,6 +8,10 @@
#include "tools/sk_app/ios/WindowContextFactory_ios.h"
#include "tools/sk_app/ios/Window_ios.h"
+#if __has_feature(objc_arc)
+#error "File should not be compiled with ARC."
+#endif
+
@interface WindowViewController : UIViewController
- (WindowViewController*)initWithWindow:(sk_app::Window_ios*)initWindow;
@@ -119,8 +123,10 @@
}
- (WindowViewController*)initWithWindow:(sk_app::Window_ios *)initWindow {
- fWindow = initWindow;
-
+ self = [super initWithNibName:nil bundle:nil];
+ if (self) {
+ fWindow = initWindow;
+ }
return self;
}
@@ -144,7 +150,7 @@
sk_app::Window_ios* fWindow;
}
-- (IBAction)panGestureAction:(UIGestureRecognizer*)sender {
+- (void)panGestureAction:(UIGestureRecognizer*)sender {
CGPoint location = [sender locationInView:self];
switch (sender.state) {
case UIGestureRecognizerStateBegan:
@@ -168,7 +174,7 @@
}
}
-- (IBAction)tapGestureAction:(UIGestureRecognizer*)sender {
+- (void)tapGestureAction:(UIGestureRecognizer*)sender {
CGPoint location = [sender locationInView:self];
switch (sender.state) {
case UIGestureRecognizerStateEnded:
@@ -182,7 +188,7 @@
}
}
-- (IBAction)pinchGestureAction:(UIGestureRecognizer*)sender {
+- (void)pinchGestureAction:(UIGestureRecognizer*)sender {
CGPoint location = [sender locationInView:self];
UIPinchGestureRecognizer* pinchGestureRecognizer = (UIPinchGestureRecognizer*) sender;
float scale = pinchGestureRecognizer.scale;
@@ -204,13 +210,13 @@
}
}
-- (IBAction)swipeRightGestureAction:(UIGestureRecognizer*)sender {
+- (void)swipeRightGestureAction:(UIGestureRecognizer*)sender {
if (UIGestureRecognizerStateEnded == sender.state) {
fWindow->onFling(skui::InputState::kRight);
}
}
-- (IBAction)swipeLeftGestureAction:(UIGestureRecognizer*)sender {
+- (void)swipeLeftGestureAction:(UIGestureRecognizer*)sender {
if (UIGestureRecognizerStateEnded == sender.state) {
fWindow->onFling(skui::InputState::kLeft);
}
diff --git a/tools/sk_app/ios/main_ios.mm b/tools/sk_app/ios/main_ios.mm
index b4b670c..c626619 100644
--- a/tools/sk_app/ios/main_ios.mm
+++ b/tools/sk_app/ios/main_ios.mm
@@ -6,33 +6,36 @@
*/
#include "include/core/SkTypes.h"
-#include "include/private/SkTHash.h"
#include "tools/sk_app/Application.h"
#include "tools/sk_app/ios/Window_ios.h"
-#include "tools/timer/Timer.h"
+#import <QuartzCore/QuartzCore.h>
#import <UIKit/UIKit.h>
-using sk_app::Application;
+#if __has_feature(objc_arc)
+#error "File should not be compiled with ARC."
+#endif
+
+namespace {
+
+static int gArgc;
+static char** gArgv;
+
+}
////////////////////////////////////////////////////////////////////
@interface AppDelegate : UIResponder<UIApplicationDelegate>
-@property (nonatomic, assign) BOOL done;
-@property (strong, nonatomic) UIWindow *window;
-
@end
-@implementation AppDelegate
-
-@synthesize done = _done;
-@synthesize window = _window;
-
-- (void)applicationWillTerminate:(UIApplication *)sender {
- _done = TRUE;
+@implementation AppDelegate {
+ CADisplayLink* fDisplayLink; // Owned by the run loop.
+ std::unique_ptr<sk_app::Application> fApp;
}
+#pragma mark - UIApplicationDelegate
+
- (void)applicationWillResignActive:(UIApplication *)sender {
sk_app::Window_ios* mainWindow = sk_app::Window_ios::MainWindow();
if (mainWindow) {
@@ -47,82 +50,43 @@
}
}
-- (void)launchApp {
- // Extract argc and argv from NSProcessInfo
- NSArray *arguments = [[NSProcessInfo processInfo] arguments];
- int argc = arguments.count;
- char** argv = (char **)malloc((argc+1) * sizeof(char *));
- int i = 0;
- for (NSString* string in arguments) {
- size_t bufferSize = (string.length+1) * sizeof(char);
- argv[i] = (char*)malloc(bufferSize);
- [string getCString:argv[i]
- maxLength:bufferSize
- encoding:NSUTF8StringEncoding];
- ++i;
- }
- argv[i] = NULL;
- [arguments release];
-
- NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
-
- Application* app = Application::Create(argc, argv, nullptr);
-
- // Free the memory we used for argc and argv
- for (i = 0; i < argc; i++) {
- free(argv[i]);
- }
- free(argv);
-
- sk_app::Window_ios* mainWindow = sk_app::Window_ios::MainWindow();
- if (!mainWindow) {
- return;
- }
- self.window = mainWindow->uiWindow();
- mainWindow->onActivate(
- UIApplication.sharedApplication.applicationState == UIApplicationStateActive);
-
- // take over the main event loop
- bool done = false;
- while (!done) {
- // TODO: consider using a dispatch queue or CADisplayLink instead of this
- const CFTimeInterval kSeconds = 0.000002;
- CFRunLoopRunResult result;
- do {
- result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, kSeconds, TRUE);
- } while (result == kCFRunLoopRunHandledSource);
-
- [pool drain];
- pool = [[NSAutoreleasePool alloc] init];
-
- // TODO: is this the right approach for iOS?
- // Rather than depending on an iOS event to drive this, we treat our window
- // invalidation flag as a separate event stream. Window::onPaint() will clear
- // the invalidation flag, effectively removing it from the stream.
- sk_app::Window_ios::PaintWindow();
-
- app->onIdle();
- }
- delete app;
+- (void)applicationWillTerminate:(UIApplication *)sender {
+ // Display link retains us, so we break the cycle now.
+ // Note: dealloc is never called.
+ [fDisplayLink invalidate];
+ fDisplayLink = nil;
+ fApp.reset();
}
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
- // let the system event loop run once, then launch into our main loop
- [self performSelector:@selector(launchApp) withObject:nil afterDelay:0.0];
+ fApp = std::unique_ptr<sk_app::Application>(sk_app::Application::Create(gArgc, gArgv, nullptr));
+
+ auto mainWindow = sk_app::Window_ios::MainWindow();
+ mainWindow->onActivate(application.applicationState == UIApplicationStateActive);
+
+ fDisplayLink = [CADisplayLink displayLinkWithTarget:self
+ selector:@selector(displayLinkFired)];
+ [fDisplayLink addToRunLoop:NSRunLoop.mainRunLoop forMode:NSRunLoopCommonModes];
return YES;
}
+- (void)displayLinkFired {
+ // TODO: Hook into CAMetalLayer's drawing event loop or our own run loop observer.
+ // Need to handle animated slides/redraw mode, so we need something that will wake up the
+ // run loop.
+ sk_app::Window_ios::PaintWindow();
+
+ fApp->onIdle();
+}
+
@end
///////////////////////////////////////////////////////////////////
int main(int argc, char **argv) {
- /* Give over control to run loop, AppDelegate will handle most things from here */
- @autoreleasepool {
- UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
- }
-
- return EXIT_SUCCESS;
+ gArgc = argc;
+ gArgv = argv;
+ return UIApplicationMain(argc, argv, nil, @"AppDelegate");
}