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 之后。我想那是真的;真的不记得了