TL; DRAndroid EGL/OpenGL ES Frame Rate nói lắp
Ngay cả khi thực hiện không vẽ gì cả, có vẻ như không thể duy trì tốc độ cập nhật 60Hz trên một chủ đề vẽ OpenGL ES trên một thiết bị Android. Các đột biến bí ẩn thường xuyên bị cắt (được thể hiện trong mã ở dưới), và mọi nỗ lực mà tôi đã thực hiện để tìm ra lý do tại sao hoặc làm thế nào dẫn đến kết thúc chết. Thời gian trong các ví dụ phức tạp hơn với một chuỗi hiển thị tùy chỉnh đã liên tục hiển thị eglSwapBuffers() là thủ phạm, thường xuyên xuất hiện trong hơn 17ms-32ms. Cứu giúp?
Xem chi tiết
Điều này đặc biệt chỉ trích vì các yêu cầu vẽ cho dự án của chúng tôi là các yếu tố màn hình canh trơn tru di chuyển theo chiều ngang ở cố định, tốc độ cao tốc độ từ một phía của màn hình để người kia. Nói cách khác, một trò chơi platforming. Các giọt thường xuyên từ 60Hz kết quả đáng chú ý popping và lurching, cả hai có và không có chuyển động dựa trên thời gian. Hiển thị ở 30Hz không phải là một tùy chọn vì tốc độ cuộn cao, đó là một phần không thể thương lượng của thiết kế.
Dự án của chúng tôi dựa trên Java để tối đa hóa khả năng tương thích và sử dụng OpenGL ES 2.0. Chúng tôi chỉ nhúng vào NDK để hiển thị OpenGL ES 2.0 trên các thiết bị API 7-8 và hỗ trợ ETC1 trên các thiết bị API 7. Trong cả mã và mã thử nghiệm được đưa ra dưới đây, tôi đã xác minh không có sự phân bổ/sự kiện GC ngoại trừ việc in nhật ký và các chuỗi tự động ngoài tầm kiểm soát của tôi.
Tôi đã tạo lại sự cố trong một tệp duy nhất sử dụng các lớp Android chứng khoán và không có NDK. Đoạn mã dưới đây có thể được dán vào một dự án Android mới được tạo trong Eclipse và nên làm việc khá nhiều, miễn là bạn chọn API cấp 8 trở lên.
Xét nghiệm này đã được tái hiện trên một loạt các thiết bị với một loạt các GPU và hệ điều hành phiên bản:
- 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) (khi dr opping yêu cầu API xuống mức 7)
Mẫu đầu ra (thu thập từ dưới 1 giây thời gian chạy):
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
Tôi đã đuổi theo cái này trong một thời gian và có khoảng trúng một viên gạch Tường. Nếu một sửa chữa không có sẵn, sau đó ít nhất một lời giải thích về lý do tại sao điều này xảy ra và lời khuyên về cách thức này đã được khắc phục trong các dự án với các yêu cầu tương tự sẽ được đánh giá rất nhiều.
Ví dụ Mã
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();
}
đồng hồ đầu ra logcat cho các tin nhắn GC, đó là một cuộc sống thực tế-of-nếu bạn đang xây dựng nó trong Java. các phiên bản mới hơn của Android có GC đồng thời nhưng rất khó để tránh một GC đầy đủ không thường xuyên và tạm dừng đi kèm. bạn nên hài lòng với 40 khung hình/giây và phấn đấu cho GC đồng thời. –