Android:Adreno 530 GPU上使用glReadPixels的图像不完整

时间:2017-04-28 08:40:04

标签: android multithreading unity3d opengl-es glreadpixels

有没有人遇到类似的问题,或者可以指出问题可能出现在下面的代码中?出于某种原因,屏幕截图中出现透明的“点”。并非所有帧都可能每秒都有。

Issue

我看到Adreno 530 GPU(LG G6,Google Pixel)的一些奇怪行为,其他GPU工作正常。我认为有一些线程问题,因为它在主线程上工作正常。这是交易。

我有一个原生的Android插件,它截取Unity项目的截图(OpenGLES3)。 OpenGL上下文在后台线程中从Unity复制。构造函数如下所示:

    this.handlerThread = new HandlerThread("UnityScreenCapture");
    this.handlerThread.start();
    this.handler = new Handler(handlerThread.getLooper());

    final EGLContext currentContext = EGL14.eglGetCurrentContext();
    final EGLDisplay currentDisplay = EGL14.eglGetCurrentDisplay();

    final int[] eglContextClientVersion = new int[1];
    eglQueryContext(currentDisplay,
            currentContext,
            EGL14.EGL_CONTEXT_CLIENT_VERSION,
            eglContextClientVersion,
            0
    );

    this.handler.post(new Runnable() {
        @Override
        public void run() {
            final EGLDisplay eglDisplay = eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);

            final int[] version = new int[2];
            eglInitialize(eglDisplay, version, 0, version, 1);

            int[] configAttributes = {EGL14.EGL_COLOR_BUFFER_TYPE,
                    EGL14.EGL_RGB_BUFFER,
                    EGL14.EGL_LEVEL,
                    0,
                    EGL14.EGL_RENDERABLE_TYPE,
                    EGL14.EGL_OPENGL_ES2_BIT,
                    EGL14.EGL_SURFACE_TYPE,
                    EGL14.EGL_PBUFFER_BIT,
                    EGL14.EGL_NONE};
            final EGLConfig[] eglConfigs = new EGLConfig[1];
            final int[] numConfig = new int[1];
            eglChooseConfig(eglDisplay, configAttributes, 0, eglConfigs, 0, 1, numConfig, 0);

            final EGLConfig eglConfig = eglConfigs[0];

            int[] surfaceAttributes = {EGL14.EGL_WIDTH, 1, EGL14.EGL_HEIGHT, 1, EGL14.EGL_NONE};
            EGLSurface eglSurface = eglCreatePbufferSurface(eglDisplay,
                    eglConfig,
                    surfaceAttributes,
                    0
            );

            int[] contextAttributes = {EGL14.EGL_CONTEXT_CLIENT_VERSION,
                    eglContextClientVersion[0],
                    EGL14.EGL_NONE};

            final EGLContext eglContext = eglCreateContext(eglDisplay,
                    eglConfig,
                    currentContext,
                    contextAttributes,
                    0
            );

            eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
        }
    });
}

然后当我拍摄截图时,我会调用相同的后台线程以避免阻塞main:

    handler.post(new Runnable() {
        @Override
        public void run() {
            frame.setBytes(native_readFrame(frame.getBytes()));
            callback.capturedFrame(frame);
        }
    });

native_readFrame看起来像这样(剥离):

mcluint texture = capture_context->texture;

if (capture_context->framebuffer == 0) {
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    mcluint framebuffer;

    // Generate framebuffer
    glGenFramebuffers(1, &framebuffer);

    // Bind texture to framebuffer
    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
    capture_context->framebuffer = framebuffer;
}

int width = capture_context->width;
int height = capture_context->height;
rect_t crop = capture_context->crop;

glBindTexture(GL_TEXTURE_2D, texture);

glBindFramebuffer(GL_FRAMEBUFFER, capture_context->framebuffer);

mclbyte *pixels = NULL;
if (dst != NULL) {
    pixels = *dst;
} else {
    pixels = (mclbyte *) malloc(crop.width * crop.height * sizeof(mclbyte) * 4);
}

glReadPixels(crop.x, crop.y, width, height, GL_RGBA, GL_UNSIGNED_BYTE,
             pixels);

if (length != NULL) {
    *length = crop.width * crop.height * sizeof(mclbyte) * 4;
}
return pixels;

更新 如果我在readFrame之前调用主右边的glFinish(),它就可以了。但glFinish是一个重大的操作阻止主线程10ms,这是不理想的。

1 个答案:

答案 0 :(得分:0)

您必须确保在读取框架之前已经渲染了框架。

Opengl有一个客户端 - 服务器架构。 cpu给出了gpu draw调用,它不会阻塞cpu线程。 gpu将安排这些调用稍后执行,但如果调用glFlush(),则可以强制它们立即开始执行。另一方面,glFinish()使客户端等待除了调用glFlush()。

由于对gpu的请求可能来自不同的线程,我建议你看看如何同步它们:

https://www.khronos.org/opengl/wiki/Sync_Object