glReadPixels медленно - PullRequest
       10

glReadPixels медленно

3 голосов
/ 30 мая 2019

Я создаю пользовательский предварительный просмотр камеры, используя GLSurfaceView, используя OpenGl для рендеринга кадров, предоставленных мне камерой. У меня полностью реализована камера, и я работаю так, как я ожидаю, что она будет работать без потери кадров в секунду и правильных соотношений сторон и т. Д. Но потом возникла проблема, когда мне нужно было захватывать кадры, поступающие с канала камеры, моей первой мыслью было использование glReadPixles. ()

Использование GLES20.glReadPixels () Я обнаружил, что некоторые устройства испытывают потерю fps, это имеет смысл в основном для устройств с более высоким разрешением экрана, потому что glReadPixels должен считывать больше пикселей с более высоким разрешением.

Я немного покопался и обнаружил, что у других были похожие проблемы с glReadPixels, и многие предлагали использовать PBO, хорошо используя два из них в качестве двойного буфера, что позволило бы мне читать данные пикселей, не блокируя / не останавливая текущий процесс рендеринга. Я полностью понимаю концепцию двойной буферизации, я довольно новичок в OpenGL и мне нужно несколько советов о том, как заставить работать PBO с двойной буферизацией.

Я нашел несколько решений для двойной буферизации PBO, но так и не смог найти полного решения, чтобы полностью понять, как оно взаимодействует с GLES.

Моя реализация GLSurfaceView.Renderer.onDrawFrame ()

    // mBuffer and mBitmap are declared and allocated outside of the onDrawFrame Method

    // Buffer is used to store pixel data from glReadPixels
    mBuffer.rewind();


    GLES20.glUseProgram(hProgram);
    if (tex_matrix != null)
    {
        GLES20.glUniformMatrix4fv(muTexMatrixLoc, 1, false, tex_matrix, 0);
    }
    GLES20.glUniformMatrix4fv(muMVPMatrixLoc, 1, false, mMvpMatrix, 0);

    GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, tex_id);
    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, GLConstants.VERTEX_NUM);
    GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);

    // Read pixels from the current GLES context
    GLES10.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, mBuffer);

    // Copy the Pixels from the buffer
    mBitmap.copyPixelsFromBuffer(mBuffer);

    GLES20.glUseProgram(0);

1 Ответ

1 голос
/ 19 июня 2019

После большого количества исследований и копаний я нашел решение для glReadPixels и как использовать PBO для буферизации изображений / кадров для последующей обработки.

Итак, первое, что нам нужно сделать, это предоставить дополнительную функцию в GLES2. В своем модуле приложения добавьте новый каталог с именем cpp, затем создайте новый файл c с именем GlesHelper (или как хотите, чтобы он назывался так)

И вставьте следующий код:

#include <jni.h>
#include <GLES2/gl2.h>
JNIEXPORT void JNICALL


// Change
Java_com_your_full_package_name_helper_GlesHelper_glReadPixels(JNIEnv *env, jobject instance, jint x,
                                                        jint y, jint width, jint height,
                                                        jint format, jint type) {
    // TODO
    glReadPixels(x, y, width, height, format, type, 0);
}

Тогда нам нужно добавить CMakeFile в корневой каталог вашего проекта. Щелкните правой кнопкой мыши, новый файл, введите CMakeLists.txt

И вставьте следующий код

cmake_minimum_required(VERSION 3.4.1)

add_library( # Specifies the name of the library.
             native-lib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main//cpp//GlesHelper.c )

target_link_libraries( # Specifies the target library.
        native-lib

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib}
        GLESv2)

Теперь откройте файл app / modules build.gradle

Вставьте это в раздел android.defaultConfig файла Gradle

externalNativeBuild {
    // Encapsulates your CMake build configurations.
    cmake {
        // Provides a relative path to your CMake build script.
        cppFlags "-std=c++11 -fexceptions"
        arguments "-DANDROID_STL=c++_shared"
    }
}

Затем вставьте это в раздел android файла Gradle

externalNativeBuild {
// Encapsulates your CMake build configurations.
    cmake {

        // Provides a relative path to your CMake build script.
        path "CMakeLists.txt"
    }
}

Итак, все, что нужно для MakeFile и c, - все настройки позволяют перейти к некоторому java

Создайте новый файл в вашем проекте, который соответствует пакету в файле c, т.е. com_your_full_package_name_helper = com.your.full.package.name.helper

Убедитесь, что они совпадают правильно, то же самое с именем класса и именем функции.

Итак, ваш класс должен выглядеть так

package com.your.full.package.name.helper;

public class GlesHelper
{
    public static native void glReadPixels(int x, int y, int width, int height, int format, int type);
}

Поскольку мы добавили нативный код в проект, нам нужно использовать System.loadLibrary ("native-lib") для загрузки в нашем новом методе.

Прежде чем мы начнем следующий бит, добавьте эти переменные-члены в ваш Renderer

/**
 * The PBO Ids, increase the allocate amount for more PBO's
 * The more PBO's the smoother the frame rate (to an extent)
 * Side affect of having more PBO's the frames you get from the PBO's will lag behind by the amount of pbo's
 */
private IntBuffer mPboIds = IntBuffer.allocate(2);;

/**
 * The current PBO Index
 */
private int mCurrentPboIndex = 0;

/**
 * The next PBO Index
 */
private int mNextPboIndex = 1;

Так что теперь нам нужно инициализировать наши PBO, это довольно просто

    // Generate the buffers for the pbo's
    GLES30.glGenBuffers(mPboIds.capacity(), mPboIds);

    // Loop for how many pbo's we have
    for (int i = 0; i < mPboIds.capacity(); i++)
    {
        // Bind the Pixel_Pack_Buffer to the current pbo id
        GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, mPboIds.get(i));

        // Buffer empty data, capacity is the width * height * 4
        GLES30.glBufferData(GLES30.GL_PIXEL_PACK_BUFFER, capacity, null, GLES30.GL_STATIC_READ);
    }

    // Reset the current buffer so we can draw properly
    GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, 0);

Затем, прежде чем мы начнем рисовать, вызовите этот метод, он будет считывать данные пикселей в pbo, менять буфер и предоставлять вам доступ к данным пикселей.

/**
 * Reads the pixels from the PBO and swaps the buffers
 */
private void readPixelsFromPBO()
{
    // Bind the current buffer
    GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, mPboIds.get(mCurrentPboIndex));

    // Read pixels into the bound buffer
    GlesHelper.glReadPixels(0, 0, mViewWidth, mViewHeight, GLES20.GL_RGBA, GLES30.GL_UNSIGNED_BYTE);

    // Bind the next buffer
    GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, mPboIds.get(mNextPboIndex));

    // Map to buffer to a byte buffer, this is our pixel data
    ByteBuffer pixelsBuffer = (ByteBuffer) GLES30.glMapBufferRange(GLES30.GL_PIXEL_PACK_BUFFER, 0, mViewWidth * mViewHeight * 4, GLES30.GL_MAP_READ_BIT);

    if(mSkipFirstFrame)
    {
        // Skip the first frame as the PBO's have nothing in them until the second render cycle
    }
    // Set skip first frame to true so we can begin frame processing
    mSkipFirstFrame = true;

    // Swap the buffer index
    mCurrentPboIndex = (mCurrentPboIndex + 1) % mPboIds.capacity();
    mNextPboIndex = (mNextPboIndex + 1) % mPboIds.capacity();

    // Unmap the buffers
    GLES30.glUnmapBuffer(GLES30.GL_PIXEL_PACK_BUFFER);
    GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, GLES20.GL_NONE);
    GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, GLES20.GL_NONE);
}

Итак, возвращаясь к моему первоначальному вопросу, наш Redner / onDrawMethod будет выглядеть примерно так:

    // Use the OpenGL Program for rendering
    GLES20.glUseProgram(mProgram);

    // If the Texture Matrix is not null
    if (textureMatrix != null)
    {
        // Apply the Matrix
        GLES20.glUniformMatrix4fv(mTexMatrixLoc, 1, false, textureMatrix, 0);
    }


    // Apply the Matrix
    GLES20.glUniformMatrix4fv(mMVPMatrixLoc, 1, false, mMvpMatrix, 0);

    // Bind the Texture
    GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureID);

    // Draw the texture
    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, GLConstants.VERTEX_NUM);

    // Unbind the Texture
    GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);

    // Read from PBO
    readPixelsFromPBO()

Надеюсь, это поможет кому-то, у кого похожая проблема с производительностью с glReadPixels или, по крайней мере, изо всех сил для реализации PBO

...