2011-08-29 27 views
14

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

đồ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. –

Trả lời

0

Bạn không ràng buộc framerate mình. Vì vậy, nó bị ràng buộc bởi CPU.

Điều đó có nghĩa là nó sẽ chạy nhanh khi CPU không có gì để làm và chậm hơn khi thực hiện các công cụ khác.

Các nội dung khác đang chạy trên điện thoại của bạn, đôi khi mất thời gian CPU từ trò chơi của bạn. Garbage Collector cũng sẽ làm điều này, mặc dù không đóng băng trò chơi của bạn như trước đây (vì nó hiện đang chạy trong một chủ đề riêng biệt)

Bạn sẽ thấy điều này xảy ra trên bất kỳ hệ điều hành đa lập trình nào được sử dụng bình thường. Nó không chỉ là lỗi của Java.

Đề xuất của tôi: liên kết tốc độ khung hình với giá trị thấp hơn nếu yêu cầu tốc độ bit không đổi. Gợi ý: sử dụng kết hợp của Thread.sleep() và chờ đợi bận (trong khi vòng lặp), vì giấc ngủ có thể vượt quá thời gian chờ đợi yêu cầu.

3

Không chắc chắn đây có phải là câu trả lời hay không, nhưng lưu ý rằng lệnh gọi tới các khối eglSwapBuffers() trong ít nhất 16 ms, ngay cả khi không có gì để vẽ.

Chạy logic trò chơi trong một chuỗi riêng biệt có thể giành lại một thời gian.

Xem bài đăng trên blog tại trò chơi Nền tảng nguồn mở Đảo Relica. Logic trò chơi là nặng, nhưng framrate là mịn do các tác giả đường ống/giải pháp đệm đôi.

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

Các vấn đề liên quan