Friday 29 June 2012

Mixing UIKit with OpenGL ES on iPhone

UIKit is a rich library with classes that have been highly polished by Apple. Lot's of development time can be saved by using it in the right way in your game's UI, however, there are problems that can arise.
Standard buttons and the like tend to sit on an OpenGL view without too many issues but as soon as you start using something slightly more complex such as a UISlider, the problems start.
I have a screen 90% drawn in OpenGL with an animated 3D model of a ship rotating round in it. This is kind of important as I need the ship to update in real time whilst a sliding control underneath shows different items the player may select. I wanted to make use of the UISliders functionality for this task.
I started by having a horizontal UISlider with a content width set based on the number of items and their size. I set it to not clip subviews. This allows me to display parts of items on screen that aren't currently centred by the page snapping of the UISlider.

The first problem I encountered was when I pressed down on the UISlider it would block the OpenGL ES layer animating, so the ship and any other OpenGL animation would pause. This is really ugly to watch.

The first step to fixing this is to change the display link run loop mode from

[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]

[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]

Stackoverflow: OpenGL Animation freezes with UIScrollView

This fixed the OpenGL blocking behaviour but meant that some touch events were being missed on the UISlider. So I investigated further and found a fix to this where someone put the OpenGL draw loop in a grand central dispatch thread. Like so:

if (dispatch_semaphore_wait(frameRenderingSemaphore, DISPATCH_TIME_NOW) != 0) { return; }

dispatch_async(openGLESContextQueue, ^{ [EAGLContext setCurrentContext:context]; // Render here dispatch_semaphore_signal(frameRenderingSemaphore); });

I found this at:

CADisplayLink OpenGL rendering UIScrollView behaviour

courtesy of the 'Molecules' app creator Brad Larson

This fixed the issue entirely... however, for some reason my UIView slider items would sometimes just not appear now! Sometimes they would appear after a large delay (10 seconds or so) and sometimes a variable number of them would appear. I then investigated a work around for this.

It turns out that there are several hacky ways to force the main run loop to update after calling a UIView's setNeedsDisplay function and whilst these seemed to help somewhat they were far from a concrete 100% fix for me.

Stackoverflow: more ways of controlling UIView rendering and some hacks

In the end I decided to stop using custom UIViews for rendering and just kept the UISlider for it's polished physics whilst rendering my own OpenGL buttons in the same positions as the slider's items. This gives me no further rendering issues but let's me keep using the best part of the UISlider - it's physics and touch functionality, whilst leaving the graphics control entirely with me using OpenGL.

No comments:

Post a Comment