首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >glReadPixels慢

glReadPixels慢
EN

Stack Overflow用户
提问于 2019-05-30 14:02:51
回答 1查看 1.5K关注 0票数 4

我正在创建一个使用GLSurfaceView的自定义摄像机预览,使用OpenGl来呈现相机给我的帧。我已经完全实现了这台相机,我希望它能够在没有fps损失和正确的纵横比等情况下工作。但是当我需要捕捉来自摄像机馈送的帧时,我的第一个想法是使用glReadPixles()。

使用GLES20.glReadPixels(),我发现一些设备会丢失fps,主要是屏幕分辨率较高的设备--这是有意义的,因为glReadPixels需要读取更高分辨率的像素。

我做了一些调查,发现其他人与glReadPixels有类似的问题,许多人建议使用PBO,很好地使用其中的两个作为双缓冲区,这样我就可以读取像素数据,而不会阻塞/延迟当前的呈现过程。我完全理解双缓冲的概念,我对OpenGL相当陌生,需要一些关于如何获得双缓冲PBO工作的指导。

我已经为PBO双缓冲找到了一些解决方案,但我永远找不到一个完整的解决方案来完全理解它如何与GLES交互。

GLSurfaceView.Renderer.onDrawFrame()的实现

代码语言:javascript
运行
复制
    // 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);
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-06-19 08:33:27

经过大量的研究和挖掘,我找到了glReadPixels的解决方案,以及如何使用PBO来缓冲图像/帧以供以后处理。

因此,我们需要做的第一件事是在GLES2中公开一个额外的函数。在您的应用程序模块中添加一个名为cpp的新目录,然后创建一个名为GlesHelper的新c文件(或者您想要调用它的任何东西)

并粘贴以下代码:

代码语言:javascript
运行
复制
#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

并粘贴以下代码

代码语言:javascript
运行
复制
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)

现在打开您的应用程序/模块build.gradle文件

将其粘贴到Gradle文件的android.defaultConfig部分

代码语言:javascript
运行
复制
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"
    }
}

然后将其粘贴到Gradle文件的android部分

代码语言:javascript
运行
复制
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 )中的包匹配的新文件

确保这些匹配正确,类名和函数名相同。

所以你的课应该是这样

代码语言:javascript
运行
复制
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(“本机库”)来加载我们的新方法。

在启动下一位之前,将这些成员变量添加到您的渲染器中

代码语言:javascript
运行
复制
/**
 * 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--这很简单

代码语言:javascript
运行
复制
    // 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,交换缓冲区,并允许您访问像素数据。

代码语言:javascript
运行
复制
/**
 * 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会是这样的。

代码语言:javascript
运行
复制
    // 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的人。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/56379809

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档