Поведение очистки GLSurfaceView onDrawFrame

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

Однако кажется, что на некоторых устройствах буфер все равно очищается. Я запустил следующую модификацию программы «Hello Triangle» из руководства по программированию Addison Wesley OpenglES2.0 на некоторых тестовых устройствах с разными результатами:

  • Acer Iconia A500 (4.0.3): не очищено (ожидаемое поведение)
  • Sony XPERIA Go (4.0.4): снято
  • Galaxy S3 (4.1.1): очищен
  • LG Optimus 4x HD (4.0.3): не очищается
  • Samsung Galaxy Tab 20.1 (4.0.4): не очищается
  • Motorola Xoom (3.2): не очищено
  • Galaxy S2 (4.1.2 - root): очищен

Есть ли способ принудительно получить неизменный буфер при каждом обратном вызове отрисовки?

Результат для устройств с очищенным экраном выглядит следующим образом: очищенный экран .stack.imgur.com/rZjNP.png" alt="неочищенный экран - ожидаемое поведение">

Тестовая активность выглядит следующим образом:

package com.example.glcleartest;

import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.GLSurfaceView.Renderer;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;

public class MainActivity extends Activity {

protected static final int NUM_VERTICES = 3;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    GLSurfaceView glview = (GLSurfaceView) findViewById(R.id.glview);
    glview.setEGLConfigChooser(false);
    glview.setEGLContextClientVersion(2);
    glview.setRenderer(new Renderer() {

        private int programObject;
        private FloatBuffer vertexBuffer;

        @Override
        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        }

        @Override
        public void onSurfaceChanged(GL10 gl, int width, int height) {
            GLES20.glViewport(0, 0, width, height);
            init();
        }

        @Override
        public void onDrawFrame(GL10 gl) {
            float x = 0.1f*(float) Math.sin(System.currentTimeMillis()/1000.0);
            float[] vVertices = new float[]{x, 0.5f, 0.0f,
                    x-0.5f, -0.5f, 0.0f,
                    x+0.5f, -0.5f, 0.0f};
            vertexBuffer.rewind();
            vertexBuffer.put(vVertices);
            vertexBuffer.rewind();


            // Use the program object
            GLES20.glUseProgram(programObject);
            int handle = GLES20.glGetUniformLocation(programObject, "uColor");
            float r = (float) (0.5f+Math.sin(System.currentTimeMillis()/1000.0));
            float g = (float) (0.5f+Math.sin(System.currentTimeMillis()/300.0));
            GLES20.glUniform4f(handle, r, g,0,1);

            // Load the vertex data
            GLES20.glVertexAttribPointer(0, 3, GLES20.GL_FLOAT, false, 0, vertexBuffer);
            GLES20.glEnableVertexAttribArray(0);
            GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);             
        }

        private void error(String s) {
            Log.e("GLTEST", s);
        }

        private int loadShader(int shaderType, String source) {
            if (shaderType != GLES20.GL_FRAGMENT_SHADER && shaderType != GLES20.GL_VERTEX_SHADER) {
                throw new RuntimeException("Illegal shader type");
            }

            int shader = GLES20.glCreateShader(shaderType);
            if (shader != 0) {
                GLES20.glShaderSource(shader, source);
                GLES20.glCompileShader(shader);
                int[] compiled = new int[1];
                GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
                if (compiled[0] == 0) {
                    error("Could not compile shader :");
                    error(GLES20.glGetShaderInfoLog(shader));
                    GLES20.glDeleteShader(shader);
                    shader = 0;
                    throw new RuntimeException("Shader Syntax / compilation error");
                }
            }
            return shader;
        }

        private void init() {
            String vShaderStr = "attribute vec4 vPosition; \n" + 
                                    "void main() \n" + "{ \n" + 
                                    " gl_Position = vPosition; \n" + 
                                    "} \n";
            String fShaderStr = "precision mediump float; \n" +
                                        "uniform vec4 uColor;" +
                                        "void main() \n" + 
                                        "{ \n" + 
                                        " gl_FragColor = uColor; \n" + 
                                        "} \n";

            ByteBuffer vbb = ByteBuffer.allocateDirect(NUM_VERTICES*3*4);
            vbb.order(ByteOrder.nativeOrder());
            vertexBuffer = vbb.asFloatBuffer();

            int vertexShader;
            int fragmentShader;

            // Load the vertex/fragment shaders
            vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vShaderStr);
            fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fShaderStr);

            // Create the program object
            programObject = GLES20.glCreateProgram();
            if (programObject == 0)
                return;

            GLES20.glAttachShader(programObject, vertexShader);
            GLES20.glAttachShader(programObject, fragmentShader);
            // Bind vPosition to attribute 0
            GLES20.glBindAttribLocation(programObject, 0, "vPosition");
            // Link the program
            GLES20.glLinkProgram(programObject);
            int[] linkStatus = new int[1];
            GLES20.glGetProgramiv(programObject, GLES20.GL_LINK_STATUS, linkStatus, 0);

            if (linkStatus[0] != GLES20.GL_TRUE) {
                error("Could not link program: ");
                error(GLES20.glGetProgramInfoLog(programObject));
                GLES20.glDeleteProgram(programObject);
                programObject = 0;
            }
        }
    });
}
}

person P.Melch    schedule 29.01.2013    source источник


Ответы (1)


Если вы хотите сохранить содержимое вашего обратного буфера после подкачки, вы должны установить для атрибута EGL_SWAP_BEHAVIOR вашей поверхности подкачки значение EGL_BUFFER_PRESERVED, как описано в API EGL. Поймите, однако, что на большинстве платформ это будет довольно большим ударом по производительности. В большинстве случаев гораздо лучше просто перерисовать кадр.

Немного истории: см. http://www.khronos.org/registry/egl/specs/EGLTechNote0001.html

person sheu    schedule 29.01.2013
comment
Я бы сказал, что перерисовка кадра лучше во всех случаях. :-) Попытка предсказать предыдущее содержимое буфера, управляемого GLSurfaceView, неизбежно приведет к страданиям. - person fadden; 29.01.2013