Родной Android Opengl ES 3.0 PBO рендеринг пустого экрана

Я разрабатываю приложение для предварительного просмотра камеры для Android.
Схема моего приложения показана ниже.

1) JAVA: получение буфера предварительного просмотра камеры и передача его в JNI
2) CPP: создание текстуры из буфера предварительного просмотра камеры с помощью OpenGL ES 3.0
3) CPP: визуализация текстуры с помощью OpenGL ES 3.0

Итак, мое приложение отлично работает с обычным обходным путем обновления/рендеринга текстуры. Все в порядке.

Проблема в производительности. Мне приходится обновлять каждый кадр в памяти графического процессора с помощью glTexSubImage2D(...), и это несколько медленно.
Поэтому я пытаюсь использовать PBO, но отображаемый экран всегда пуст.

Я не знаю, что мне не хватает, кто-нибудь может помочь?

Вот мой код:

#include "native-lib.h"
#include "textureloader.h"
#include "vecmath.h"
#include "glutil.h"
#include "logger.h"

GLuint program;
GLuint p_mvp, p_vertices, p_uvs, p_tex;
GLuint tex;
GLuint pboIds[2];
GLfloat vertices[] = {0,0,0,0,0,0,0,0};
GLfloat uvs[] = {0,1,1,1,1,0,0,0};
GLushort indices[] = {0,1,2,0,2,3};
int dataSize;
float zoom;
mat4 mvp;

bool usePBO = true;
bool doublePBO = false;

cv::Mat mat;

std::string vs =
        "#version 300 es\n"
                "in vec4 a_vertices;\n"
                "in vec2 a_uvs;\n"
                "uniform mat4 u_mvp;\n"
                "out vec2 v_texCoord;\n"
                "void main(){\n"
                "  gl_Position = u_mvp * a_vertices;\n"
                "  v_texCoord = a_uvs;\n"
                "}\n";
std::string fs =
        "#version 300 es\n"
                "uniform sampler2D u_tex;\n"
                "in vec2 v_texCoord;\n"
                "out vec4 fragColor;\n"
                "void main(){\n"
                "  fragColor = texture(u_tex, v_texCoord);\n"
                "  fragColor.a = 1.0f;\n"
                "}\n";


JNIEXPORT jint JNICALL
Java_com_quram_vmtest3_Native_glInit(JNIEnv *env, jclass)
{
    // create program
    program = loadProgram(vs, fs, false);

    // create texture basic data
    p_vertices = glGetAttribLocation(program, "a_vertices");
    p_uvs = glGetAttribLocation(program, "a_uvs");
    p_mvp = glGetUniformLocation(program, "u_mvp");
    p_tex = glGetUniformLocation(program, "u_tex");

    // create PBO
    if(usePBO) {
        glGenBuffers(2, pboIds);
    }

    return 1;
}

JNIEXPORT jint JNICALL
Java_com_quram_vmtest3_Native_surfaceChanged(JNIEnv *env, jclass,
                                                   jint w, jint h)
{
    glViewport(0, 0, w, h);

    zoom = (w < h ? w : h) / 2;
    vec4 target = vec4(w / 2, h / 2, 0, 0);
    vec4 eye = target + vec4(0, 0, zoom, 1);
    mat4 view(mat4::lookAt(eye, target, vec4(0, 1, 0, 0)));
    mat4 proj(mat4::perspective(90, (float)w / (float)h, 1, 1000));
    mvp = proj * view;

    vertices[5] = vertices[7] = h;
    vertices[0] = vertices[6] = w;

    glGenTextures(1, texId);
    glBindTexture(GL_TEXTURE_2D, *texId);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);

    if(usePBO) {
        dataSize = w * h * 3;

        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pboIds[0]);
        glBufferData(GL_PIXEL_UNPACK_BUFFER, dataSize, NULL, GL_STREAM_DRAW);
        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pboIds[1]);
        glBufferData(GL_PIXEL_UNPACK_BUFFER, dataSize, NULL, GL_STREAM_DRAW);
        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
    }

    return 1;
}


JNIEXPORT jint JNICALL
Java_com_quram_vmtest3_Native_process(JNIEnv *env, jclass, jbyteArray bytearray,
                                      jint w, jint h)
{
    jbyte *buf = env->GetByteArrayElements(bytearray, 0);

    if(mat.empty())
        mat = cv::Mat(h+h/2, w, CV_8UC1, buf);
    else
        mat.data = reinterpret_cast<uchar*>(buf);
    cv::cvtColor(mat, mat, CV_YUV2RGB_NV21);
    rotateMat(mat, mat, -90);

    if(usePBO) {
        static int index = 0;
        if(doublePBO) {
            int nextIndex = 0;

            index = (index + 1) % 2;
            nextIndex = (index + 1) % 2;

            // bind the texture and PBO
            glBindTexture(GL_TEXTURE_2D, tex);
            glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pboIds[index]);
            glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

            // copy pixels from PBO to texture object
            // Use offset instead of ponter.
            glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, 0);
            glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pboIds[nextIndex]);
            glBufferData(GL_PIXEL_UNPACK_BUFFER, dataSize, 0, GL_STREAM_DRAW);
            GLubyte *ptr = (GLubyte *) glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, dataSize, GL_MAP_WRITE_BIT);
            if (ptr) {
                memcpy(ptr, mat.ptr(), dataSize);
                glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); // release pointer to mapping buffer
            }
            glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
            glBindTexture(GL_TEXTURE_2D, 0);
        }
        else {
            glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pboIds[index]);
            glBindTexture(GL_TEXTURE_2D, tex);
            GLubyte *ptr = (GLubyte *) glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, dataSize, GL_MAP_WRITE_BIT);
            if (ptr) {
                //  memcpy(ptr, mat.ptr(), dataSize);
                memset(ptr, 255, dataSize);
                glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, 0);
                glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); // release pointer to mapping buffer
            }
            glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
            glBindTexture(GL_TEXTURE_2D, 0);
        }
    }
    else {
        loadTextureWithMat(mat, &tex, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE);
    }

    // enable depth test
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);

    // enable alpha blending option
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glClearColor(1.f, 0.f, 0.f, 0.f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glUseProgram(program);

    glUniformMatrix4fv(p_mvp, 1, GL_FALSE, &mvp.x.x);
    glEnableVertexAttribArray(p_vertices);
    glVertexAttribPointer(p_vertices, 2, GL_FLOAT, GL_FALSE, 0, vertices);
    glEnableVertexAttribArray(p_uvs);
    glVertexAttribPointer(p_uvs, 2, GL_FLOAT, GL_FALSE, 0, uvs);
    glUniform1i(p_tex, 0);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, tex);

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);

    glBindTexture(GL_TEXTURE_2D, 0);
    glUseProgram(0);

    // disable alpha blending option
    glDisable(GL_BLEND);

    // disable depth test
    glDisable(GL_DEPTH_TEST);

    env->ReleaseByteArrayElements(bytearray, buf, JNI_ABORT);
    mat.release();

    return 1;
}

void rotateMat(cv::Mat const &src, cv::Mat &dst, int angle)
{
    if(angle == 270 || angle == -90){
        // Rotate clockwise 270 degrees
        cv::transpose(src, dst);
        cv::flip(dst, dst, 0);
    }else if(angle == 180 || angle == -180){
        // Rotate clockwise 180 degrees
        cv::flip(src, dst, -1);
    }else if(angle == 90 || angle == -270){
        // Rotate clockwise 90 degrees
        cv::transpose(src, dst);
        cv::flip(dst, dst, 1);
    }else if(angle == 360 || angle == 0 || angle == -360){
        if(src.data != dst.data){
            src.copyTo(dst);
        }
    }
}


Если я не использую PBO, все работает нормально (кроме fps), поэтому я предполагаю, что проверки кодов внутри флагов usePBO и doublePBO будет достаточно.


person Suhyeon Lee    schedule 26.04.2017    source источник


Ответы (1)


После некоторой борьбы я наконец нашел свою крошечную (но критическую) ошибку.

Я повернул свой cv::Mat, поэтому ширина и высота поменялись местами.

Java_com_quram_vmtest3_Native_process(JNIEnv *env, jclass, jbyteArray bytearray,
                                      jint w, jint h)
{
    mat = cv::Mat(h+h/2, w, CV_8UC1, buf);  // created w*(h*1.5) size mat
    cv::cvtColor(mat, mat, CV_YUV2RGB_NV21); // This makes mat height from h*1.5 to h
    rotateMat(mat, mat, -90);  // Now our mat is h*w
}

И я использовал ширину, высоту со значением параметра функции (которое не меняется местами).

glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, 0);

Я изменил приведенный выше код следующим образом:

glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, mat.cols, mat.rows, GL_RGB, GL_UNSIGNED_BYTE, 0);

Да, все работает отлично :)

ПРИМЕЧАНИЕ.
Кстати, производительность выглядит почти так же, как и раньше.
Возможно, использование PBO НЕ всегда гарантирует повышение производительности...

person Suhyeon Lee    schedule 26.04.2017