Android EGL / OpenGL ES Частота кадров заикания

TL; DR

Даже при отсутствии рисунка вообще невозможно поддерживать частоту обновления 60 Гц в потоке рендеринга OpenGL ES на устройстве Android. Таинственные всплески часто появляются (демонстрируются в коде внизу) и все усилия, которые я сделал, чтобы выяснить, почему и как это привело к тупику. Сроки в более сложных примерах с пользовательским потоком рендеринга последовательно показывали eglSwapBuffers () как виновника, часто возникающие в течение 17 мс-32 мс. Помогите?

Подробнее

Это особенно опасно, потому что требования к рендерингу для нашего проекта состоят из выровненных по экрану элементов, плавно прокручивающихся горизонтально с фиксированной высокой скоростью с одной стороны экрана на другую. Другими словами, платформа игры. Частые падения с 60 Гц приводят к заметному появлению и удару, как с движением, так и без него. Рендеринг на частоте 30 Гц не является вариантом из-за высокой скорости прокрутки, которая является необоротной частью дизайна.

Наш проект основан на Java, чтобы максимизировать совместимость и использует OpenGL ES 2.0. Мы просто погружаемся в NDK для рендеринга OpenGL ES 2.0 на устройствах API 7-8 и поддержку ETC1 на устройствах API 7. Как в этом, так и в тестовом коде, приведенном ниже, я не проверял никаких событий распределения / GC, кроме журнала и автоматических потоков, находящихся вне моего контроля.

Я воссоздал проблему в одном файле, в котором используются классы акций Android и нет NDK. Код ниже может быть вставлен в новый проект Android, созданный в Eclipse, и должен в значительной степени работать из коробки, пока вы выбираете уровень API 8 или выше.

Тест был воспроизведен на различных устройствах с множеством графических процессоров и версий ОС:

  • Galaxy Tab 10.1 (Android 3.1)
  • Nexus S (Android 2.3.4)
  • Galaxy S II (Android 2.3.3)
  • XPERIA Play (Android 2.3.2)
  • Droid Incredible (Android 2.2)
  • Galaxy S (Android 2.1-update1) (при снижении требований API до уровня 7)

Пример вывода (собранный менее 1 секунды времени выполнения):

Spike: 0.017554 Spike: 0.017767 Spike: 0.018017 Spike: 0.016855 Spike: 0.016759 Spike: 0.016669 Spike: 0.024925 Spike: 0.017083999 Spike: 0.032984 Spike: 0.026052998 Spike: 0.017372 

Я гонялся за этим на какое-то время и пробрался в кирпичную стену. Если исправление не доступно, то, по крайней мере, объясните, почему это происходит, и рекомендации по тому, как это было преодолено в проектах с аналогичными требованиями.

Пример кода

 package com.test.spikeglsurfview; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.app.Activity; import android.opengl.GLSurfaceView; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.Window; import android.view.WindowManager; import android.widget.LinearLayout; /** * A simple Activity that demonstrates frequent frame rate dips from 60Hz, * even when doing no rendering at all. * * This class targets API level 8 and is meant to be drop-in compatible with a * fresh auto-generated Android project in Eclipse. * * This example uses stock Android classes whenever possible. * * @author Bill Roeske */ public class SpikeActivity extends Activity { @Override public void onCreate( Bundle savedInstanceState ) { super.onCreate( savedInstanceState ); // Make the activity fill the screen. requestWindowFeature( Window.FEATURE_NO_TITLE ); getWindow().setFlags( WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN ); // Get a reference to the default layout. final LayoutInflater factory = getLayoutInflater(); final LinearLayout layout = (LinearLayout)factory.inflate( R.layout.main, null ); // Clear the layout to remove the default "Hello World" TextView. layout.removeAllViews(); // Create a GLSurfaceView and add it to the layout. GLSurfaceView glView = new GLSurfaceView( getApplicationContext() ); layout.addView( glView ); // Configure the GLSurfaceView for OpenGL ES 2.0 rendering with the test renderer. glView.setEGLContextClientVersion( 2 ); glView.setRenderer( new SpikeRenderer() ); // Apply the modified layout to this activity's UI. setContentView( layout ); } } class SpikeRenderer implements GLSurfaceView.Renderer { @Override public void onDrawFrame( GL10 gl ) { // Update base time values. final long timeCurrentNS = System.nanoTime(); final long timeDeltaNS = timeCurrentNS - timePreviousNS; timePreviousNS = timeCurrentNS; // Determine time since last frame in seconds. final float timeDeltaS = timeDeltaNS * 1.0e-9f; // Print a notice if rendering falls behind 60Hz. if( timeDeltaS > (1.0f / 60.0f) ) { Log.d( "SpikeTest", "Spike: " + timeDeltaS ); } /*// Clear the screen. gl.glClear( GLES20.GL_COLOR_BUFFER_BIT );*/ } @Override public void onSurfaceChanged( GL10 gl, int width, int height ) { } @Override public void onSurfaceCreated( GL10 gl, EGLConfig config ) { // Set clear color to purple. gl.glClearColor( 0.5f, 0.0f, 0.5f, 1.0f ); } private long timePreviousNS = System.nanoTime(); } 

Solutions Collecting From Web of "Android EGL / OpenGL ES Частота кадров заикания"

Не уверен, что это ответ, но обратите внимание, что вызов блоков eglSwapBuffers () не менее 16 мс, даже если рисовать нечего.

Запуск логики игры в отдельном потоке может отыграться некоторое время.

Посмотрите сообщение в блоге на платформе Open Source Platforming Relica Island. Логика игры тяжелая, но скорость кадра является гладкой из-за решения автора / двойного буфера.

http://replicaisland.blogspot.com/2009/10/rendering-with-two-threads.html

Вы не привязываете себя к частоте кадров. Так что это связано с процессором.

Это означает, что он будет работать быстро, когда процессор не имеет ничего общего и медленнее, когда он делает другие вещи.

На вашем телефоне работает другой материал, изредка занимающий процессорное время из вашей игры. Сборщик мусора также будет заниматься этим, хотя и не замораживая вашу игру, как раньше (поскольку теперь она работает в отдельном потоке)

Вы увидите, что это происходит в любой операционной системе мультипрограммирования, которая была в обычном режиме. Это не просто ошибка Java.

Мое предложение: привяжите частоту кадров к более низкому значению, если требуется постоянный бит. Подсказка: используйте сочетание Thread.sleep () и ожидание ожидания (while loop), так как сон может переместиться через необходимое время ожидания.