2016-07-21 15 views
9

Od jakiegoś czasu doświadczyłem przerywanego "jąkania" duszków, które są w ruchu w mojej grze na Androida. Jest to prosta gra 2D OpenGL ES 2.0. (Jest to ciągły problem, który wielokrotnie odwiedzałem).Android: Zrozumienie OnDrawFrame, FPS i VSync (OpenGL ES 2.0)

W mojej pętli gry mam 2 "timery" - taki, który będzie rejestrował liczbę klatek w poprzedniej sekundzie, i drugi, który liczy czas (w milisekundach) od końca aktualnej iteracji OnDrawFrame do początku następnego.

To co znalazłem:

Kiedy nic nie czyniąc, mam 60 kl./s (dla większości), i za każdym razem onDrawFrame nazywa, zgłasza się jako że 16.667ms trwa dłużej. Teraz, jeśli wyrenderuję coś (nie ma znaczenia, czy jest to 1 quad lub 100 quadów, wynik jest taki sam), otrzymuję 60fps (w przeważającej części), ale teraz, tylko około 20% połączeń onDrawFrame raportuje się jako zajmujących więcej czasu niż 16,667ms od ostatniego połączenia.

Naprawdę nie rozumiem, dlaczego tak się dzieje, po pierwsze, dlaczego, kiedy onDrawFrame nic nie robi, nazywa się to "powoli" - i co ważniejsze, dlaczego jakakolwiek rozmowa GL (jeden prosty quad), wciąż czas między wywołaniami onDrawFrame przekracza 16.667 ms (choć znacznie rzadziej).

Powinienem powiedzieć, że kiedy onDrawFrame zgłasza trwa dłużej niż 16.667ms od ostatniej iteracji, prawie zawsze towarzyszy mu spadek FPS (do 58 lub 59), ale nie zawsze, czasami FPS pozostaje stały. I odwrotnie, czasami kiedy FPS spada, naDrawFrame jest wywoływana w ciągu 16.667ms od ostatniej iteracji kończącej.

Więc ......

Próbuję naprawić moją grę pętli i eliminowania tych 'zacina' - kilka innych rzeczy do uwaga:

  • Kiedy zrobić metodę profilowanie, pokazuje glSwapBuffers, czasami zajmuje dużo czasu
  • Kiedy robię GL Trace, większość scen mówi, że renderuje się w czasie krótszym niż 1 ms, ale czasami ramka nieparzysta zajmuje 3,5-4 ms - tę samą scenę. Nic się nie zmienia oprócz czasu, który zajmuje
  • Prawie za każdym razem, gdy klatka jest upuszczana lub na DrawFrame zgłasza duże opóźnienie (lub oba), pojawia się wizualny błąd, ale nie zawsze. Duże wizualne usterki zdają się zbiegać z wieloma "opóźnionymi" wywołaniami w usłudzeDrawFrame i/lub zrzuconymi ramkami.
  • Nie sądzę, że jest to problem złożoności sceny z dwóch powodów: 1) nawet jeśli renderuję moją scenę dwukrotnie, to nie sprawia, że ​​problem jest gorszy, nadal w przeważającej części, dostaję 60FPS z okazjonalnym upuść, tak jak poprzednio i 2), nawet jeśli odsłonię scenę, wciąż mam problem.

Oczywiście coś nie rozumiem, więc doceniłbym pchnięcie we właściwym kierunku.

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();   
} 

Pętla ta gra powyżej jest jednym biorące udział w tej doskonałej article.

+0

Czy usterki pokrywają się ze zdarzeniami zbierania śmieci? –

+0

@ReubenScratton, nie, wszystkie obiekty są tworzone z góry, nie ma przydziałów w pętli ani GC. Wydaje się, że to kwestia czasu z samą pętlą gry (mógłbym się mylić, ale wygląda na to w ten sposób). Dzięki – Zippy

+0

Zakładając, że używasz dedykowanego wątku renderowania, w jaki sposób synchronizujesz stan między nim a głównym wątkiem? (FWIW Nigdy nie używam dedykowanego wątku renderowania i wolę być jednowątkowym, ale nigdy nie napisałem skomplikowanej gry) –

Odpowiedz

7

Niektóre myśli:

  • Nie używają System.currentTimeMillis() o taktowaniu rzeczy. Jest oparty na zegar ścienny, który może być aktualizowany przez sieć. Użyj System.nanoTime(), który jest oparty na zegarze monotonicznym.
  • Aby uzyskać uwagi na temat pętli do gier, zobacz stronę this appendix. Nadziewanie kolejek jest dobre dla wielu rzeczy, ale zrozum, że nie pracujesz dokładnie nad VSYNC, więc czasy będą często niedokładne.
  • Niektóre urządzenia (zwłaszcza te oparte na SOC) qcom zmniejszają prędkość procesora, gdy myślą, że są bezczynne. Zawsze wykonuj pomiar czasu, jednocześnie aktywnie przesuwając palcem po ekranie dotykowym.
  • Jeśli chcesz debugować problemy z liczbą klatek, musisz użyć systrace. Profilowanie widoków śledzenia nie jest tutaj użyteczne.

Zobacz Grafika's "nagraj aplikację GL" Aktywność dla przykładu prostej aplikacji GLES, która usuwa klatki, ale dostosowuje animację w taki sposób, że jest rzadko zauważalna.

+0

Link do załącznika jest zepsuty (cóż, tylko część hashowa) - https : //source.android.com/devices/graphics/arch-gameloops wydaje się być teraz właściwy. – Karu

+0

@Karu: Podzielili dokument na kilka części i przestawili kilka rzeczy, więc istnieje kilka odpowiedzi z uszkodzonymi linkami. :-(Zaktualizowałem ten. Jeśli widzisz innych, możesz bezpośrednio edytować posty. – fadden