18
votes

I am looking into converting my OpenGL rendering code to take advantage of a few features of GLKit (namely the asynchronous texture loading and the automation provided by GLKView/Controller). However, it appears that the classes are designed mainly to accommodate people rendering using an animation loop, whereas I'm working with on-demand rendering. Additionally, some of the rendering is to a texture rather than the GLKView's framebuffer, so should I be looking to just subclass the GLKView and add additional FBOs?

Is there a recommended approach for this type of setup? I would expect something along the lines of:

  • Set the view controller's preferredFramesPerSecond to 0, or just pause the frame updates?
  • Ignore the glkViewControllerUpdate or glkView:drawInRect: methods and just draw what I need, when I need it.
  • Use the view's setNeedsDisplay as with a normal UIView in order to display the frame (do I need to call bindDrawable given that I will be rendering to a texture as well?).

Perhaps it's not worth the effort if this is not what the new API is designed for? I wish the documentation was a little more thorough than it is. Perhaps more samples will be provided when the API has 'matured' a little...

Thanks

3
you can still render to secondary FBO in conjunction with GLKit. mickyd.wordpress.com/2012/05/20/… - Micky

3 Answers

24
votes

The approach I ended up using was to not bother with the GLKViewController, but just use GLKView directly under a UIViewController subclass.

Clearly, the GLKViewController is intended for use by people who need a consistent rendering loop for apps such as games. Without it, drawing to the GLKView is as simple as calling [glkView setNeedsDisplay]. Be sure to set enableSetNeedsDisplay to YES in order to enable this behaviour.

If you did still want to make use of a GLKViewController, you can disable the animation rendering loop in viewWillAppear like so:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];    // setPaused automatically set to NO in super's implementation

    [self setPaused:YES];
}

Also, set resumeOnDidBecomeActive to NO to prevent the view controller from resuming again automatically.

Using a plain UIViewController with a GLKView is perfectly acceptable however, and I have seen it recommended by an Apple engineer as an appropriate way to perform on-demand drawing.

3
votes

I've just converted my code from using an EAGLContext manager I rolled myself to using the GLKit classes.

You suggest you might "..ignore the.. glkView:drawInRect: methods and just draw what [you] need, when I need it". This seems like a sensible option performance-wise; I assume (though haven't tried) if you simply don't specify a GLKViewDelegate or provide a subclassed GLKView with its drawInRect: defined then no animation loop rendering will occur. Have you attempted this?

The alternative would be to simply create some @property (assign, nonatomic) BOOL shouldUpdate; in your MyController : GLKViewController <GLKViewDelegate> class which will only update if there is something to do:

[self setDelegate:self]; // in init or awakeFromNib or other..

-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
    if ([self shouldUpdate]) { ...

I'm sure you get the idea, it's hardly complicated.

One thing worth mentioning: the official API docs state that viewDidLoad should be used in your GLKViewController for initial GL setup. I had issues with this; for some reason my glCreateShader calls always returned zero. This may have been due to my setting the EAGLContext post-initialisation; I couldn't pass it as an init parameter since I created the controller in Storyboard. However, there was nothing logically wrong with the code, so I offer this friendly warning in case you encounter similar issues. My solution is simply to have the following in my drawInRect:

-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
    if ([self initialGLSetupDone] == NO) {
        [self beforeFirstRender];
        [self setInitialGLSetupDone:YES];
    }
    // .. rest of render code goes here.
}

Obviously it's not ideal to have an IF in there unnecessarily, but it was an easy solution.

Let me know how it goes if you try updating to use GLKit.

1
votes

After you have created GLKView, add this line:

glkView.enableSetNeedsDisplay = TRUE;

(Because of this, no one will redraw the view automatically)

When you want redraw, insert this line:

[glkView setNeedsDisplay];

... then drawInRect routine will be called only once.

Hope it helps.