Я работаю над игрой на С++, которая запускается в видеопотоке GLSurfaceView (цикл игры - это цикл GLSurfaceView, потому что он работает в непрерывном режиме). У меня проблемы с корректной обработкой Activity.onPause/onResume вместе с nativePause моей игры. В nativePause я выпускаю ресурсы opengl и разные большие данные. У меня нет nativeResume, потому что это обрабатывается Android, когда я вызываю GLSurfaceView.onResume(), который снова вызывает методы onSurfaceCreated/onSurfaceChanged, в которых я снова выделяю свои ресурсы.
Вот как я это делаю сейчас:
при паузе
Активность в java обрабатывает onPause и запускает пользовательский метод nativePause glSurfaceView:
@Override
protected void onPause() {
super.onPause();
glSurfaceView.nativePause();
}
nativePause отправляет асинхронный запрос видеоциклу игры. В цикле видео обрабатывается и высвобождаются различные ресурсы. Затем я отправляю еще одно сообщение в основной поток с информацией о том, что nativePause завершен, и я делаю GLSurfaceView.onPause(), это останавливает видеопоток.
onResume
этот метод имеет простую реализацию, он только запускает видеопоток SurfaceView с помощью onResume()
@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();
}
Нативная функция onDrawFrame() в RendererWrapper является основным игровым циклом.
Сторона С++
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);
}
};
nativePause()
блок, пока он не завершится. Я считаю, что вызовGLSurfaceView.onPause()
после того, какActivity.onPause()
уже вернулся, незаконен, и вы, вероятно, понимаете, почему. Должно быть довольно легко сделатьnativePause()
синхронным; вы по-прежнему можете рассчитывать на наличие вашего потока OpenGL; Я предполагаю, что вы не полагаетесь на какие-либо промежуточные вызовыonDrawFrame()
для очистки; вы должны просто иметь возможность блокироваться до тех пор, пока ваш поток OpenGL не уведомит переменную условия или что-то еще о том, что действияnativePause()
были выполнены. - person greeble31   schedule 14.11.2019nativePause()
), либо 2.) Попросить поток A захватить блокировку, сделать очистку, снимите блокировку и вернитесь изnativePause()
.pthread_mutex_lock
илиpthread_cond_wait/pthread_cond_signal
, вероятно, то, что вы хотите использовать здесь. Если вам нужна помощь, лучше всего добавить весь соответствующий код дляnativePause()
к вашему вопросу. - person greeble31   schedule 15.11.2019inputQueue
. У вас есть документация? Есть ли в нем блокирующая версия метода getReadEvent()? Кроме того, я предполагаю, что весь смысл в том, чтобы поток OpenGL вызывалrelease()
(что бы это ни было), иначе все сломается? - person greeble31   schedule 15.11.2019