Android 游戏 - 如果游戏在 GLSurfaceView 的视频线程中运行,如何正确处理 onPause 和 onResume
Android Game - how correctly handle onPause and onResume if game runs in GLSurfaceView's video thread
我从事在 GLSurfaceView 的视频线程中运行的 C++ 游戏(游戏的循环是 GLSurfaceView 的循环,因为它以连续模式运行)。我在如何正确处理 Activity.onPause/onResume 以及游戏的 nativePause 时遇到问题。在 nativePause 我释放 opengl 资源和各种大数据。我没有 nativeResume,因为这是在我调用 GLSurfaceView.onResume() 时由 Android 处理的,它再次调用方法 onSurfaceCreated/onSurfaceChanged ,我在其中再次分配我的资源。
这是我现在的做法:
暂停
Activity in java 处理 onPause 并运行 glSurfaceView 的自定义 nativePause 方法:
@Override
protected void onPause() {
super.onPause();
glSurfaceView.nativePause();
}
nativePause 向游戏的视频循环发送异步请求。在视频循环中处理并释放各种资源。接下来,向主线程发送另一条消息,其中包含 nativePause 已完成的信息,我执行 GLSurfaceView.onPause(),这将停止视频线程。
恢复
该方法实现简单,只用onResume()启动surfaceview的视频线程
@Override
protected void onResume() {
super.onResume();
glSurfaceView.onResume();
}
但问题是,onPause 会异步调用视频线程并返回主线程。 Activity.onResume 通常在整个暂停机制完成之前更快地被调用,然后它崩溃或挂起。如果游戏在视频线程中运行,我应该如何正确处理onPause/onResume?
编辑:
Java 边 :
public class RendererWrapper implements Renderer {
public native void onSurfaceCreated();
public native void onSurfaceChanged(int width, int height);
public native void onDrawFrame();
....
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
onSurfaceCreated();
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
onSurfaceChanged(width, height);
}
@Override
public void onDrawFrame(GL10 gl) {
onDrawFrame();
}
....
}
public class VideoSurface extends GLSurfaceView {
public VideoSurface(Context context) {
super(context);
this.setEGLContextClientVersion(2);
this.renderer = new RendererWrapper();
this.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
this.setRenderer(renderer);
this.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
}
public native void nativePause();
}
RendererWrapper 中的原生 onDrawFrame() 是主游戏循环。
C++ 端
void nativePause() {
InputEvent *event = inputQueue.getWriteEvent();
event->type = InputEvent::PAUSE;
inputQueue.incWriteIndex();
}
void onDrawFrame() {
if (isPaused) {
return;
}
InputEvent *event = inputQueue.getReadEvent();
if (event) {
inputQueue.incReadIndex();
....
if (event->type == InputEvent::PAUSE) {
release();
return;
}
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
game->draw();
}
编辑2:
class EventQueue {
public:
static const int size = 256;
volatile int readIndex;
volatile int writeIndex;
InputEvent *events;
EventQueue() {
readIndex = 0;
writeIndex = 0;
events = new InputEvent[size];
}
InputEvent* getReadEvent() {
if (writeIndex == readIndex) {
return 0; // queue empty
}
return events + readIndex;
}
InputEvent* getWriteEvent() {
if (((writeIndex + 2) & (size - 1)) == readIndex) {
return 0; // queue full
}
return events + writeIndex;
}
void incReadIndex() {
readIndex = (readIndex + 1) & (size - 1);
}
void incWriteIndex() {
writeIndex = (writeIndex + 1) & (size - 1);
}
};
小心那个反复无常的把戏。在许多情况下,it doesn't do what you think it does。如果它到目前为止有效,那可能是因为运气。
由于InputQueue
class不太适合这个,我就教你如何用条件变量解决问题(代码未测试):
#include <pthread.h>
pthread_cond_t cond;
pthread_mutex_t mutex;
bool released = false;
...
pthread_cond_init(&cond, NULL); //TODO: check return value
pthread_mutex_init(&mutex, NULL); //TODO: check return value
...
void nativePause() {
InputEvent *event = inputQueue.getWriteEvent();
event->type = InputEvent::PAUSE;
inputQueue.incWriteIndex();
//Wait for the OpenGL thread to accomplish the release():
pthread_mutex_lock(&mutex);
while(!released) {
pthread_cond_wait(&cond, &mutex); //Expected to always return 0.
}
pthread_mutex_unlock(&mutex);
}
void onDrawFrame() {
...
if (event) {
inputQueue.incReadIndex();
....
if (event->type == InputEvent::PAUSE) {
release();
pthread_mutex_lock(&mutex);
released = true;
pthread_cond_broadcast(&cond); //Notifies the nativePause() thread, which is supposed to be blocking in the condition loop, at this point.
pthread_mutex_unlock(&mutex);
return;
}
}
...
}
...
void nativeCleanup()
{
pthread_cond_destroy(&cond); //Expected to return 0.
pthread_mutex_destroy(&mutex); //Expected to return 0.
}
至少,这应该有效。代码假定 OpenGL 线程保证存在直到 onPause()
returns 之后。我想那是真的;真的不记得了
我从事在 GLSurfaceView 的视频线程中运行的 C++ 游戏(游戏的循环是 GLSurfaceView 的循环,因为它以连续模式运行)。我在如何正确处理 Activity.onPause/onResume 以及游戏的 nativePause 时遇到问题。在 nativePause 我释放 opengl 资源和各种大数据。我没有 nativeResume,因为这是在我调用 GLSurfaceView.onResume() 时由 Android 处理的,它再次调用方法 onSurfaceCreated/onSurfaceChanged ,我在其中再次分配我的资源。
这是我现在的做法:
暂停
Activity in java 处理 onPause 并运行 glSurfaceView 的自定义 nativePause 方法:
@Override
protected void onPause() {
super.onPause();
glSurfaceView.nativePause();
}
nativePause 向游戏的视频循环发送异步请求。在视频循环中处理并释放各种资源。接下来,向主线程发送另一条消息,其中包含 nativePause 已完成的信息,我执行 GLSurfaceView.onPause(),这将停止视频线程。
恢复
该方法实现简单,只用onResume()启动surfaceview的视频线程
@Override
protected void onResume() {
super.onResume();
glSurfaceView.onResume();
}
但问题是,onPause 会异步调用视频线程并返回主线程。 Activity.onResume 通常在整个暂停机制完成之前更快地被调用,然后它崩溃或挂起。如果游戏在视频线程中运行,我应该如何正确处理onPause/onResume?
编辑:
Java 边 :
public class RendererWrapper implements Renderer {
public native void onSurfaceCreated();
public native void onSurfaceChanged(int width, int height);
public native void onDrawFrame();
....
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
onSurfaceCreated();
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
onSurfaceChanged(width, height);
}
@Override
public void onDrawFrame(GL10 gl) {
onDrawFrame();
}
....
}
public class VideoSurface extends GLSurfaceView {
public VideoSurface(Context context) {
super(context);
this.setEGLContextClientVersion(2);
this.renderer = new RendererWrapper();
this.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
this.setRenderer(renderer);
this.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
}
public native void nativePause();
}
RendererWrapper 中的原生 onDrawFrame() 是主游戏循环。
C++ 端
void nativePause() {
InputEvent *event = inputQueue.getWriteEvent();
event->type = InputEvent::PAUSE;
inputQueue.incWriteIndex();
}
void onDrawFrame() {
if (isPaused) {
return;
}
InputEvent *event = inputQueue.getReadEvent();
if (event) {
inputQueue.incReadIndex();
....
if (event->type == InputEvent::PAUSE) {
release();
return;
}
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
game->draw();
}
编辑2:
class EventQueue {
public:
static const int size = 256;
volatile int readIndex;
volatile int writeIndex;
InputEvent *events;
EventQueue() {
readIndex = 0;
writeIndex = 0;
events = new InputEvent[size];
}
InputEvent* getReadEvent() {
if (writeIndex == readIndex) {
return 0; // queue empty
}
return events + readIndex;
}
InputEvent* getWriteEvent() {
if (((writeIndex + 2) & (size - 1)) == readIndex) {
return 0; // queue full
}
return events + writeIndex;
}
void incReadIndex() {
readIndex = (readIndex + 1) & (size - 1);
}
void incWriteIndex() {
writeIndex = (writeIndex + 1) & (size - 1);
}
};
小心那个反复无常的把戏。在许多情况下,it doesn't do what you think it does。如果它到目前为止有效,那可能是因为运气。
由于InputQueue
class不太适合这个,我就教你如何用条件变量解决问题(代码未测试):
#include <pthread.h>
pthread_cond_t cond;
pthread_mutex_t mutex;
bool released = false;
...
pthread_cond_init(&cond, NULL); //TODO: check return value
pthread_mutex_init(&mutex, NULL); //TODO: check return value
...
void nativePause() {
InputEvent *event = inputQueue.getWriteEvent();
event->type = InputEvent::PAUSE;
inputQueue.incWriteIndex();
//Wait for the OpenGL thread to accomplish the release():
pthread_mutex_lock(&mutex);
while(!released) {
pthread_cond_wait(&cond, &mutex); //Expected to always return 0.
}
pthread_mutex_unlock(&mutex);
}
void onDrawFrame() {
...
if (event) {
inputQueue.incReadIndex();
....
if (event->type == InputEvent::PAUSE) {
release();
pthread_mutex_lock(&mutex);
released = true;
pthread_cond_broadcast(&cond); //Notifies the nativePause() thread, which is supposed to be blocking in the condition loop, at this point.
pthread_mutex_unlock(&mutex);
return;
}
}
...
}
...
void nativeCleanup()
{
pthread_cond_destroy(&cond); //Expected to return 0.
pthread_mutex_destroy(&mutex); //Expected to return 0.
}
至少,这应该有效。代码假定 OpenGL 线程保证存在直到 onPause()
returns 之后。我想那是真的;真的不记得了