My app uses GLKit
to render 3D scene with OpenGL ES
.
All works fine, except one thing. When I launch my app in iPad and display background apps bar (with double "Home" button click) and then change device's orientation, scene is updated wrongly (last rendered image is simply stretched to fill new rectangle).
I found the reason. When background apps bar appears, GLKViewController's
paused
is set to YES
automatically (application delegate receives -applicationWillResignActive:
) and no rendering happens until this bar is closed.
I've found in Apple guides (OpenGL ES Programming Guide for iOS / Implementing a Multitasking-aware OpenGL ES Application) that after receiving -applicationWillResignActive:
application should stop GL rendering or will be terminated. So it seems that all is ok, except bad rendering after rotation :)
I checked some OpenGL games. They also became "paused" when this bar displayed, but somehow correctly update paused scene on device rotation. How do they achieve this?
tl;dr: possibly you only need to set the
GLKView
'scontentMode
toUIViewContentModeRedraw
Firstly I don't think that your application actually enters into the background, I think it only becomes inactive. The distinction between the
applicationWillResignActive
andapplicationDidEnterBackground
delegate methods. Assuming that the application is only inactive use the following, in case it actually gets put in the background then see below.The apple documentation says that you should "throttle down OpenGL ES framerates" when
applicationWillResignActive
gets called, not that OpenGL ES calls are not allowed, that only happens after the application goes into the background.This means that
GLKit
'sGLKView
/GLKViewController
may be a bit overzealous in this regard. To fix it you need to make sure that:GLKView
'scontentMode
is set toUIViewContentModeRedraw
GLKView
'sdrawRect
method does draw the frame even when the application is inactive but the frame is changed, but does not draw the frame (that is use OpenGL ES calls) when the application is in the background.However, my presumption is that the
drawRect
method does not even get called when the application is in the background, so you probably don't really have to worry about the OpenGL ES calls in theglkView:drawInRect
delegate method. The reason however that this function does not get called in your situation is that the view does not get invalidated. The reason for not being invalidated is twofold:GLKViewController
that invalidates the view periodically is paused by thepaused
property.GLKView
contentMode
is probably the default 'UIViewContetModeScaleToFill'As the
GLKView
drawRect
method is probably not even looking at thepaused
property, just changing thecontentMode
may already be enough.I would propose the following solution if the application actually does go into the background. As you're not allowed to use OpenGL ES calls while running in the background the solution is pretty straightforward:
Do all OpenGL ES calls you need to do to support what you want before you enter the background.
That is, in
applicationWillResignActive
do the following:GLKViewController
'spaused
)GLKViewController
'spaused
)Furthermore you need
GLKView
'scontentMode
to be set toUIViewContentModeRedraw
so that thedrawRect
method is actually called after the view's frame has been changed by the orientation change.Finally in
GLKView
'sdrawRect
method you need to check whetherpaused
isYES
orNO
in theNO
case do the rendering as normal, in theYES
case take one of the framebuffers saved inapplicationWillResignActive
and draw it to the view with regularUIKit
calls.I'm not sure how well this hackish solution would integrate with
GLKit
, you might need some subclassing.Implement the delegate method (if you haven't done it already)
-(void)applicationWillEnterForeground
and unpause theGLKViewController
from there.From what I understood from your question you can keep the game paused but resize the glView in this method as well, but without seeing any code is also difficult to really see what's going on.