SoFIE's Second Anniversary

Hard to believe that our Society has been running for two years.

A lot has changed. The founders (Paul and David) got new day jobs. They also launched another business, Paul & Dave's Mixed Drinks (visit us at pdmixeddrinks.com). David moved (within San Francisco - no cause for alarm). Swift 2.0.

We have been lax on calling meetings for the past couple of months, mostly because of all the turmoil. But things are settling down and we'll have more meetings soon.

Meanwhile, keep investigating and stay curious.

See you soon!

Relaxatron V2.0 Now Available in the App Store

The Splash

Version 2.0 of Relaxatron is available on the Apple App Store. We built a completely new drawing system, and now Relaxatron displays fluffy clouds. "Relax with your head in the clouds." Check it out in the Apple App Store

The Geekspeak

The rendering engine has been totally re-built to use OpenGL. The old version of Relaxatron used UIBezierPath animation, which was very awesome and super easy to put together. The OpenGL rendering engine was not so easy. Not even close.

But, the benefit is that the game simulation can compute new cell patterns, then hand those off to the rendering engine which draws the cells in different, cool ways. Right now we have the "classic" look - polka dots - and the new "cloud" look. The clouds are rendered using a particle system and animating textures. It was fun to put together.

Other lessons learned along the way: how to manage threaded drawing. While all actual drawing of the VBOs is done on the main thread, building the cell geometry is done on a background thread. We built it this way for performance reasons. Everything started out on the main thread, but building the new cell geometry takes around 20msec, which is too much time to maintain 60fps. Now the geometry building and the rendering are interleaved. The code is more complicated, but the work was worth it.

We wrote all the simulation, all the geometry building code and the OpenGL rendering code in C++. The big fat idea here is to make these portions cross-platform. The native parts of the code handle the UI (the views and buttons), and the threading. Using native code for threading was a little bit of a surprise, but good cross-platform threading libraries don't really exist. Unless you want to go through JNI and Boost, which seems to defeat the purpose of using threading to improve performance.

Musings on OpenGL and CADisplayLink

Musings on OpenGL rendering in iOS

by David Springer

Intro

To make Relaxatron 2.0, I wanted to move the simulation engine and the rendering into a common code base, and write it in C++. While the rest of the iOS world is progressing into Swift and Metal, I'm regressing to C++ and OpenGL.

Why? Because I want Relaxatron 2.0 to work on Android and iOS. This means as much common code as possible. I'll do another post on what I learned about where to draw the "native" versus "common" line when building Relaxatron.

The Challenge

The challenge in this exercise is to write an OpenGL animation loop that starts and stops. In particular, Relaxatron has two main loops: the outer loop that computes a new generation of the Game Of Life simulation; and an inner animation loop that draws the transition form one generation to the next. This second loop is the one I will talk about.

Why this is a challenge: Using OpenGL on iOS is really easy, if you use GLKViewController and GLKView. The problem I faced is that this set up is ideal if you run an "open loop" style of animation, where your animation updates continuously. Relaxatron's animation loop runs only between generation updates so it starts and stops. It doesn't run continuously.

My Answer to the Challenge

There are five parts to my solution.

  1. Use CADisplayLink to provide the animation "tick".
  2. Use a UIViewController (not a GLKViewController) as the main view controller.
  3. Use a subclass of GLKView as the simulation view.
  4. Use -[GLKView display] and set enableSetNeedsDisplay to NO.
  5. Send a message to the main view controller when the inner animation loop is finished.

Setting up CADisplayLink is really easy. The examples in the docs are great sources for this. The main trick in rendering the GLKView from within the updateDisplay: target method is to (1) set GLKView.enableSetNeedsDisplay to NO (I do this in -[GLKView initWithFrame:context:]) and (2) call -[GLKView display], not -[UIView setNeedsDisplay]. That's pretty much it!

Note: contrary to what people say, I found that CADisplayLink is quite reliable in the simulator. While I strongly agree that you need to always test on a device, I found that the simulator works just fine for getting things right. I use Xcode 6.1 and iOS 8.

Here is my -updateDisplay: CADisplayLink target implementation, note the call to -[self display]. Notice also where I send the message to the gameDelegate when the animation is done.

- (void)updateDisplay:(CADisplayLink *)displayLink {
  NSTimeInterval timestamp = displayLink.timestamp;
  if (_lastUpdateTime == 0.0) {
    _lastUpdateTime = timestamp;
  }
  _elapsedUpdateTime = timestamp - _lastUpdateTime;
  if (_elapsedUpdateTime < kCellDrawDuration) {
    [self display];
  } else {
    _elapsedUpdateTime = 0.0;
    [_displayLink invalidate];
    _displayLink = nil;
    if ([self.gameDelegate respondsToSelector:@selector(gameView:animationDidStop:)]) {
      [self.gameDelegate gameView:self animationDidStop:YES];
    }
  }
}

SoFIE's First Anniversary!

Our September meet-up marks the first anniversary of SoFIE! Hard to believe that we started as a couple of guys in a cafe a year ago. We're still a couple of guys in a cafe, but now it's a year later!

Relaxatron (our only shipping app) is updated to be "iOS 8 Optimized". You can get version 1.2 from the App Store now.

Here's looking forward to another great year of impractical engineering - join us! Bring your curiosity and your impractical ideas. Who knows? You might find something you thought you lost.