2016-07-21 6 views
9

На какое-то время я испытал прерывистое «заикание» спрайтов, которые находятся в движении в моей Android-игре. Это абсолютно простая 2D игра OpenGL ES 2.0. (Это постоянная проблема, которую я неоднократно посещал).Android: Понимание OnDrawFrame, FPS и VSync (OpenGL ES 2.0)

В моем цикле игры у меня есть 2 'таймера' - тот, который будет регистрировать количество кадров в предыдущей секунде, а другой, который подсчитывает время (в миллисекундах) с конца текущей итерации onDrawFrame до начала из следующего.

Это то, что я нашел:

Когда ничего не делает, я получаю 60fps (по большей части), и каждый раз, когда onDrawFrame называется, он сообщает о себе как принимать дольше 16.667ms. Теперь, если я что-то делаю (неважно, если это 1 квад или 100 квадрациклов, результат такой же), я получаю 60 кадров в секунду (по большей части), но теперь только около 20% вызовов onDrawFrame сообщают о себе как о том, что они занимают больше времени чем 16,667 мс от последнего звонка.

Я не совсем понимаю, почему это происходит, во-первых, почему, когда onDrawFrame ничего не делает, называется ли это так «медленно» - и что более важно, почему любой вызов GL (один простой квад) сделать время между вызовами onDrawFrame дольше 16,667 мс (хотя и гораздо реже).

Я должен сказать, что когда onDrawFrame сообщает о длительности более 16,667 мс от последней итерации, он почти всегда сопровождается падением FPS (до 58 или 59), но не все время, иногда, FPS остается постоянная. И наоборот, иногда, когда FPS падает, onDrawFrame вызывается в течение 16.667 мс последнего завершения итерации.

Так ......

Я пытаюсь исправить свою игру петлю и искоренить эти «заикания» - некоторые другие вещи, чтобы отметить:

  • Когда я делаю метод профилирования, он показывает glSwapBuffers, иногда занимает много времени
  • Когда я делаю GL Trace, большинство сцен, которые он говорит, составляет менее 1 мс, но иногда нечетный кадр занимает 3,5-4 мс - ту же сцену. Ничего не меняется независимо от времени, которое требуется
  • Почти каждый раз, когда кадр падает, или onDrawFrame сообщает о большой задержке (или и том и другом), есть визуальный глюк, но не каждый раз. Большие визуальные сбои, похоже, совпадают с несколькими «задержанными» вызовами onDrawFrame и/или отброшенными кадрами.
  • Я не думаю, что это проблема сложности сцены по двум причинам: 1) даже если я снимаю свою сцену дважды, это не делает проблему хуже, я по-прежнему по большей части получаю 60FPS со случайными падение, как и раньше, и 2), даже если я снимаю сцену голыми, у меня все еще возникает проблема.

Я, очевидно, что-то недопонимаю, так что толчок в правильном направлении будет оценен.

OnDrawFrame

@Override 
public void onDrawFrame(GL10 gl) { 

    startTime = System.nanoTime();   
    fps++;       
    totalTime = System.nanoTime() - timeSinceLastCalled;  

    if (totalTime > 16667000) {  
     Log.v("Logging","Time between onDrawFrame calls: " + (totalTime /(double)1000000)); 
    } 

    //Grab time 
    newTime = System.currentTimeMillis() * 0.001; 
    frameTime = newTime - currentTime; //Time the last frame took 

    if (frameTime > 0.25) 
     frameTime = 0.25; 

    currentTime = newTime; 
    accumulator += frameTime; 

    while (accumulator >= dt){    
     saveGameState(); 
     updateLogic(); 
     accumulator -= dt; 
    } 

    interpolation = (float) (accumulator/dt); 

    Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0); 

    render(interpolation); 

    if (startTime > lastSecond + 1000000000) {   
     lastSecond = startTime; 
     Log.v("Logging","fps: "+fps); 
     fps=0;   
    } 

    endTime = startTime; 
    timeSinceLastCalled = System.nanoTime();   
} 

Эта петля игра выше, является одним признакам в этой превосходной article.

+0

Являются ли сбои совпадающими с событиями сбора мусора? –

+0

@ReubenScratton, нет, все объекты создаются спереди, в цикле нет выделений и нет GC. Кажется, это проблема синхронизации с самой игрой (я мог ошибаться, но, похоже, так). Спасибо – Zippy

+0

Предполагая, что вы используете выделенный поток рендеринга, как вы синхронизируете состояние между ним и основным потоком? (FWIW Я никогда не использую выделенный поток рендеринга и предпочитаю быть однопоточным, но тогда я никогда не писал сложную игру) –

ответ

7

Некоторые мысли:

  • Не используйте System.currentTimeMillis() для синхронизации вещи. Он основан на настенных часах, которые могут обновляться сетью. Используйте System.nanoTime(), который основан на монотонных часах.
  • См. this appendix для некоторых заметок в игровых циклах. Набивка очередей отлично подходит для многих вещей, но понимайте, что вы не совсем отработали VSYNC, поэтому тайминги будут иметь тенденцию быть неточными.
  • Некоторые устройства (особенно те, которые основаны на SOCOM), уменьшают скорость процессора, когда они думают, что они простаивают. Всегда выполняйте тайминги, активно двигая пальцем по сенсорному экрану.
  • Если вы хотите отлаживать проблемы с частотой кадров, вы должны использовать systrace. Профилирование трассировки здесь не так полезно.

См. Grafika's «запись GL app». Действия для примера простого приложения GLES, которое отбрасывает кадры, но настраивает анимацию так, что она редко заметна.

+0

Ссылка на приложение сломана (ну, только хэш-часть) - https // // источник /. – Karu

+0

@Karu: Они разделили документ на несколько частей и переделали некоторые вещи, поэтому есть куча ответов со сломанными ссылками. :-(Я обновил этот вариант. Если вы видите других, вы можете более подробно редактировать сообщения. – fadden