iOS OpenGL ES屏幕旋转,而后台应用栏可见 [英] iOS OpenGL ES screen rotation while background apps bar visible
问题描述
我的应用使用 GLKit
以 OpenGL ES
呈现3D场景。
一切正常,除了一件事。当我在iPad中启动我的应用程序并显示后台应用程序栏(单击双击主页按钮)然后更改设备的方向时,场景会被错误地更新(最后渲染的图像被拉伸以填充新的矩形)。
我找到了原因。当出现后台应用程序栏时, GLKViewController的
已暂停
设置为 YES
自动(应用程序委托接收 -applicationWillResignActive:
)并且在此栏关闭之前不会进行渲染。
我'在Apple指南中找到(适用于iOS的OpenGL ES编程指南/实现支持多任务的OpenGL ES应用程序)在收到 -applicationWillResignActive:
应用程序后,应停止GL渲染或终止。所以看起来一切都还好,除了旋转后的糟糕渲染:)
我检查了一些OpenGL游戏。当显示此栏时,它们也会暂停,但不知何故正确更新设备旋转时暂停的场景。他们是如何实现这一目标的?
tl; dr:可能你只需要设置 GLKView
's contentMode
to UIViewContentModeRedraw
<首先,我不认为您的应用程序实际进入后台,我认为它只会变为非活动状态。
applicationWillResignActive
和 applicationDidEnterBackground
委托方法之间的区别。假设应用程序仅处于非活动状态,请使用以下内容,以防它实际放入后台,然后参见下文。 Apple文档说你应该减少OpenGL ES框架当 applicationWillResignActive
被调用时,不会允许OpenGL ES调用,只有在应用程序进入后台后才会发生。
这意味着 GLKit
的 GLKView
/ GLKViewController
在这方面可能有点过分热心。要修复它,你需要确保:
-
GLKView
'scontentMode
设置为UIViewContentModeRedraw
-
GLKView
'sdrawRect
方法确实绘制框架,即使应用程序处于非活动状态但框架已更改,但当应用程序在后台时,不绘制框架(即使用OpenGL ES调用)。
但是,我的推测是,当应用程序在后台时,甚至不会调用 drawRect
方法,因此您可能不必担心OpenGL ES调用 glkView:drawInRect
委托方法。然而,在您的情况下不调用此函数的原因是视图不会失效。不被无效的原因有两个:
-
GLKViewController中的主框架循环
暂停
属性会暂时使视图无效。 -
GLKView
contentMode
可能是默认的'UIViewContetModeScaleToFill'
作为 GLKView
drawRect
方法可能甚至没有查看暂停的
属性,只是更改 contentMode
可能已足够。
我建议如果应用程序实际进入后台,则以下解决方案。由于您不允许在后台运行时使用OpenGL ES调用,因此解决方案非常简单:
您需要执行所有OpenGL ES调用以支持您的操作在你输入背景之前想要。
也就是说,在 applicationWillResignActive
中执行以下操作:
- 暂停游戏循环(通过设置
GLKViewController
的暂停
) - 暂停渲染循环(通过设置
GLKViewController
的暂停
) - 获取当前方向状态的当前帧缓冲区
- 使用帧缓冲区和视口再次渲染当前游戏状态对应于旋转的方向状态并抓住该帧缓冲区
此外,您需要 GLKView
将 contentMode
设置为 UIViewContentModeRedraw
以便 drawRect
方法实际上是通过方向更改更改了视图框架后调用。
最后在 GLKView
的 drawRect
您需要检查暂停
的方法是是
还是 NO
在 NO
的情况下,正常情况下,在 YES
的情况下进行渲染取一个保存在 applicationWillResignActive
中的帧缓冲区,并使用常规 UIKit
调用将其绘制到视图中。
我不确定这个hackish解决方案与 GLKit
的集成程度如何,你可能需要一些子类化。
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
's contentMode
to UIViewContentModeRedraw
Firstly I don't think that your application actually enters into the background, I think it only becomes inactive. The distinction between the applicationWillResignActive
and applicationDidEnterBackground
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
's GLKView
/GLKViewController
may be a bit overzealous in this regard. To fix it you need to make sure that:
- The
GLKView
'scontentMode
is set toUIViewContentModeRedraw
- The
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 the glkView: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:
- The main frame loop in
GLKViewController
that invalidates the view periodically is paused by thepaused
property. - The
GLKView
contentMode
is probably the default 'UIViewContetModeScaleToFill'
As the GLKView
drawRect
method is probably not even looking at the paused
property, just changing the contentMode
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:
- Pause your game loop (done by setting
GLKViewController
'spaused
) - Pause your render loop (done by setting
GLKViewController
'spaused
) - Grab the current framebuffer for the current orientation state
- Render the current game state one more time with a framebuffer and viewport that correspond to the rotated orientation state and grab that framebuffer
Furthermore you need GLKView
's contentMode
to be set to UIViewContentModeRedraw
so that the drawRect
method is actually called after the view's frame has been changed by the orientation change.
Finally in GLKView
's drawRect
method you need to check whether paused
is YES
or NO
in the NO
case do the rendering as normal, in the YES
case take one of the framebuffers saved in applicationWillResignActive
and draw it to the view with regular UIKit
calls.
I'm not sure how well this hackish solution would integrate with GLKit
, you might need some subclassing.
这篇关于iOS OpenGL ES屏幕旋转,而后台应用栏可见的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!