Почему мой фрагмент OpenGL перестал перерисовываться при обновлении? - PullRequest
3 голосов
/ 31 октября 2019

У меня есть приложение для начинающих OpenGL, которое показывает вращающийся телец, загруженный из файла obj. Это работало очень хорошо, и я показал своим друзьям. Вчера я открыл приложение, и представление больше не обновляется. Если я нажму кнопку «Домой», а затем снова нажму на приложение, оно обновит представление, поэтому я знаю, что основной цикл активен.

Я пошел домой и подключил его к Android Studio, чтобы убедиться, что поток рендеринга запускается. просто отлично и view.requestRender(); тоже вызывается.

Я понятия не имею, почему это перестало работать.

Вот мой фрагмент Android, который загружает представление и средство визуализации

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
    /**
     * Inflate the layout for this fragment
     */
    View root = inflater.inflate(R.layout.glfragment, container, false);

    GLSurfaceView glview = (GLSurfaceView)root.findViewById(R.id.surface_view);

    Log.i("Method", "OpenGLFragment::onCreateView()");

    Context context = this.getActivity().getApplicationContext();

    MyRenderer renderer = new MyRenderer(context);
    glview.setEGLContextClientVersion(2);
    glview.setRenderer(renderer);
    glview.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);

    loadMeshes(context); // loads obj file into variable tourus

    renderer.addToScene(tourus);

    update = new GraphicsThread(glview);
    update.start();

    return root;
}

@Override
public void onDestroyView() {
    if(update != null) {
        update.quit();
    }

    super.onDestroyView();
}

Вот графический поток:

public class GraphicsThread extends Thread {
    private GLSurfaceView.Renderer renderer;
    private GLSurfaceView view;

    private boolean isRunning;

    public GraphicsThread(GLSurfaceView view) {
        this.view = view;
        this.isRunning = true;
    }

    @Override
    public void run() {
        while (this.isRunning) {
            view.requestRender(); // I verified this loop is executed just fine
        }
    }

    public void quit() {
        this.isRunning = false;
    }
}

Вот MyRenderer код

public class MyRenderer implements GLSurfaceView.Renderer {
    private int program; // default shader program
    private List<Mesh> drawables;
    private Context context;
    private long lastFrameTime;
    private RenderInfo info; // MVP and Light matrices
    private Bitmap bg;

    public MyRenderer(Context context) {
        this.drawables = new ArrayList<>();
        this.context = context;
        this.lastFrameTime = 0;
        this.info = null;
    }

    @Override
    public void onSurfaceCreated(GL10 unused, EGLConfig config) {
        info = new RenderInfo(context);

        GLES20.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
        GLES20.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

        GLES20.glEnable(GLES20.GL_CULL_FACE);
    }

    public void onDrawFrame(GL10 unused){
        // Redraw background color
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

        float elapsed = getElapsedTime();

        float rot = 10.0f*elapsed;

        for(Mesh m : drawables) {
            m.rotateX(rot);
            m.draw(info, elapsed);
        }
    }

    public void onSurfaceChanged(GL10 unused, int width, int height) {
        GLES20.glViewport(0, 0, width, height);

        if(width > 0 && height > 0) {
            final float ratio = (float) width / height;
            final float left = -ratio;
            final float right = ratio;
            info.resizePerspective(left, right, 1, -1);
        }
    }

    public void addToScene(Mesh mesh) {
        drawables.add(mesh);
    }

    private float getElapsedTime() {
        long currentTime = SystemClock.elapsedRealtime();
        float elapsed = (float)(currentTime - lastFrameTime) / 1000.0f; //convert ms to seconds
        lastFrameTime = currentTime;

        return elapsed;
    }
}

Наконец, вот как я рисую свои сетки. RenderInfo содержит информацию о мире, такую ​​как матрица MVP камеры, источники света и их матрицы. Ничего не связано с этой проблемой.

public void draw(RenderInfo info, float elapsed) {
    if(!loaded) {
        Log.d("Mesh", "failed to draw");
        return;
    };

    final int program = info.getProgram();

    int position = GLES20.glGetAttribLocation(program, "a_Position");
    int normal = GLES20.glGetAttribLocation(program, "a_Normal");
    int aColor = GLES20.glGetAttribLocation(program, "a_Color");

    //int textcoord = GLES20.glGetAttribLocation(program, "a_TexCoordinate");

    GLES20.glEnableVertexAttribArray(position);
    GLES20.glVertexAttribPointer(position, 3, GLES20.GL_FLOAT, false, 3 * 4, verticesBuffer);

    GLES20.glEnableVertexAttribArray(aColor);
    GLES20.glVertexAttribPointer(aColor, 4, GLES20.GL_FLOAT, true, 4*4, colorBuffer);

    //GLES20.glEnableVertexAttribArray(normal);
    //GLES20.glVertexAttribPointer(normal, 3, GLES20.GL_FLOAT, false, 3 * 4, normalBuffer);

    float[] modelMatrix = new float[16];

    Matrix.setIdentityM(modelMatrix, 0);
    Matrix.setRotateM(modelMatrix, 0, rotX , 1.0f, 0.0f, 0.0f);
    //Matrix.setRotateM(modelMatrix, 0, rotY , 0.0f, 1.0f, 0.0f);
    //Matrix.setRotateM(modelMatrix, 0, rotZ , 0.0f, 0.0f, 1.0f);

    float[] mvpMatrix = info.getMVP(modelMatrix);
    int MVP = GLES20.glGetUniformLocation(program, "u_MVP");
    GLES20.glUniformMatrix4fv(MVP, 1, false, mvpMatrix, 0);

    float[] mvMatrix = info.getMV();
    int MV = GLES20.glGetUniformLocation(program, "u_MV");
    GLES20.glUniformMatrix4fv(MV, 1, false, mvMatrix, 0);

    int lightM = GLES20.glGetAttribLocation(program, "u_LightPos");
    GLES20.glUniformMatrix4fv(lightM, 1, false, info.getLightMatrix(), 0);

    int lightCol = GLES20.glGetAttribLocation(program, "u_LightCol");
    GLES20.glUniform4fv(lightCol, 1, info.getLightColor(), 0);
    Log.d("boogio", "u_LightCol is: " + Integer.toString(lightCol));


    GLES20.glDrawElements(GLES20.GL_TRIANGLES, facesList.size() * 3, GLES20.GL_UNSIGNED_SHORT, facesBuffer);
    GLES20.glDisableVertexAttribArray(position);
    GLES20.glDisableVertexAttribArray(aColor);
    //GLES20.glDisableVertexAttribArray(normal);
}

TL; DR: Приложение отображается хорошо и используется для обновления. Внезапно приложение не обновляется визуально (без перерисовки). Он перерисовывается только тогда, когда приложение теряет и восстанавливает фокус на 1 кадр. Понятия не имею, почему.

1 Ответ

1 голос
/ 04 ноября 2019

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

1 - Сохранение контекста и добавление ошибки проверки OpenGL

Прежде всего, лучше сообщить GLView о сохранении заданного контекста OpenGL, используя setPreservceEGLContext . Если это не разрешит ситуацию, лучше включить трассировку DEBUG в контексте OpenGL ES, используя метод setDebugFlas .


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
    /**
     * Inflate the layout for this fragment
     */
    View root = inflater.inflate(R.layout.glfragment, container, false);

    GLSurfaceView glview = (GLSurfaceView)root.findViewById(R.id.surface_view);

    Log.i("Method", "OpenGLFragment::onCreateView()");

    Context context = this.getActivity().getApplicationContext();

    MyRenderer renderer = new MyRenderer(context);
    glview.setEGLContextClientVersion(2);
    // added code
    glview.setDebugFlags(GLSurfaceView.DEBUG_CHECK_GL_ERROR); // enable log
    glview.setPreserveEGLContextOnPause(true); // default is false

    glview.setRenderer(renderer);
    glview.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);

    loadMeshes(context); // loads obj file into variable tourus

    renderer.addToScene(tourus);

    update = new GraphicsThread(glview);
    update.start();

    return root;
}

Это добавит больше информации отладки в ваше приложение вlogcat (надеюсь, вы уже знаете, что это). Я полагаю, вы найдете некоторые ошибки (строки с ошибкой OPEN GL .. что-то).

Я подозреваю, что вы снова открываете действие, контекст OpenGL пытается использовать ресурсы, которые больше не действительны (потому что контекст OpenGLразрушен). Типичным примером ресурсов, которыми вы должны управлять при воссоздании контекста, являются программы для текстур и шейдеров.

2 - Проверка графического потока

Просто добавьте некоторую информацию об отладке журнала в Графический поток:

public class GraphicsThread extends Thread {
      private GLSurfaceView.Renderer renderer;
      private GLSurfaceView view;

      private boolean isRunning;

      public GraphicsThread(GLSurfaceView view) {
        this.view = view;
        this.isRunning = true;
        Log.i("GraphicsThread", "GraphicsThread::constructor()");
      }

    @Override
    public void run() {
        while (this.isRunning) {
            Log.i("GraphicsThread", "requestRender");
            view.requestRender(); // I verified this loop is executed just fine
        }


    }

    public void quit() {
        this.isRunning = false;
        Log.i("GraphicsThread", "GraphicsThread::quit()");
    }
}

Информация журнала в GraphicThread поможет вам проверить, работает ли поток в соответствии с указаниями.

Некоторые пояснения

  • glview.setPreserveEGLContextOnPause(true); сохраняет ресурсы при уничтожении контекста(поворот экрана или действие в фоновом режиме): альтернативой является воссоздание и перезагрузка всех ресурсов. Я предполагаю, что это было сделано, потому что вначале у Android-устройств не было памяти для сохранения GLContext, когда GLView был разрушен.
  • RENDERMODE_CONTINUOUSLY говорит GLView рисовать сцену, когда это возможно (и это лучшийпуть к тому, что я знаю).

Надеюсь, мои предложения помогут вам.

...