【问题标题】:How to share CVOpenGLESTextureCache between UIView and another thread?如何在 UIView 和另一个线程之间共享 CVOpenGLESTextureCache?
【发布时间】:2013-07-26 03:10:11
【问题描述】:

这是节奏拳手或任何知道答案的人的问题:

根据线程:How to use OpenGL ES on a separate thread on iphone?

Rhythmic Fistman 发现“iOS5 的 CVOpenGLESTextureCaches 本质上使纹理上传免费,因此我不再需要 shareGroups,而且我的代码更简单、更快。”

我目前正在开发一个应用程序,它可以绘制 3D 图形并将其保存在电影文件中。据我了解,UIView 的 OpenGL ES 帧缓冲区必须以 colorRenderBuffer 为后盾,而不是 CVOpenGLESTextureCache,这是电影文件保存 3D 图形的 opengl 纹理的方式。

我不希望 OpenGLES 两次渲染相同的 3D 图形,我想分享渲染的结果。

您能否分享有关如何使用 CVOpenGLESTextureCache 在保存 openGL 纹理的工作线程和显示帧缓冲区的主线程 UIView 之间共享的知识和/或源代码?

提前致谢。

问候,霍华德

===========更新========

谢谢,我按照 Brad 的回答和 RosyWriter 示例代码编写了一些简单的代码,从 avCaptureOutput Dispatch 队列线程渲染最终缓冲区和主 UIView。 (稍后会润色)。

有 2 个 OpenGL-ES 2.0 上下文,1 个为 UIWebView 创建的 mainContext,2 个 workingContext,为 avCaptureOutput 的调度队列创建的。他们共享同一个组。

到目前为止一切顺利。会试试看有没有出现画面撕裂的效果。

非常感谢!

下面是我的代码:

//Draw texture
-(void)Draw:(CVPixelBufferRef)updatedImageBuffer
{
    CVOpenGLESTextureRef cvTexture;

    /////////////////////////////////////////////
    //first draw graphics to the CVPixelBufferRef
    /////////////////////////////////////////////
    //creates a live binding between the image buffer and the underlying texture object.
    CVReturn err = CVOpenGLESTextureCacheCreateTextureFromImage(
                                                                kCFAllocatorDefault,
                                                                cvTextureCache,
                                                                updatedImageBuffer,
                                                                NULL,
                                                                GL_TEXTURE_2D,
                                                                GL_RGBA,//  opengl format
                                                                esContext.bufWidth,
                                                                esContext.bufHeight,
                                                                GL_BGRA,// native iOS format
                                                                GL_UNSIGNED_BYTE,
                                                                0,
                                                                &cvTexture);
    if (err == kCVReturnSuccess) {
        assert(CVOpenGLESTextureGetTarget(cvTexture) == GL_TEXTURE_2D);
        GLint texId = CVOpenGLESTextureGetName(cvTexture);
        if (!workingContext || [EAGLContext setCurrentContext:workingContext] == NO) {
            NSLog(@"SwapBuffers: [EAGLContext setCurrentContext:workingContext] failed");
            return;
        }
        glBindTexture(GL_TEXTURE_2D, texId);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

        glBindFramebuffer(GL_FRAMEBUFFER, workerFrameBuffer);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texId, 0);

        GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
        if ( status == GL_FRAMEBUFFER_COMPLETE ) {

            drawGraphics(&esContext);
            glBindTexture(GL_TEXTURE_2D, 0);
            //glFlush();

            /////////////////////////////////////////////
            //then draw the texture to the main UIView
            /////////////////////////////////////////////
            if (!mainContext || [EAGLContext setCurrentContext:mainContext] == NO) {
                NSLog(@"SwapBuffers: [EAGLContext setCurrentContext:mainContext] failed");
                return;
            }
            glBindTexture(GL_TEXTURE_2D, texId);

            // Set texture parameters
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

            glBindFramebuffer(GL_FRAMEBUFFER, mainFrameBuffer);

            status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
            if ( status == GL_FRAMEBUFFER_COMPLETE ) {

                // Draw the texture on the screen with OpenGL ES 2
                drawUIView(&esContext, textureVertices);

                // Present the UIView
                glBindRenderbuffer(GL_RENDERBUFFER, mainColorBuffer);
                [mainContext presentRenderbuffer:GL_RENDERBUFFER];
                glBindTexture(GL_TEXTURE_2D, 0);
            }
            //glFlush();
        }
        //Flush textureCache
        CVOpenGLESTextureCacheFlush(cvTextureCache, 0);

        //Release created texture
        CFRelease(cvTexture);
    }
}

void drawUIView( ESContext *esContext, const GLfloat* textureVertices)
{
    UserData *userData = esContext->userData;
    static const GLfloat squareVertices[] = {
        -1.0f, -1.0f,
        1.0f, -1.0f,
        -1.0f,  1.0f,
        1.0f,  1.0f,
    };

    // Set the view port to the entire view
    glViewport(0, 0, esContext->viewWidth, esContext->viewHeight);

    // Clear the color buffer
    glClear ( GL_COLOR_BUFFER_BIT );

    // Use shader program.
    glUseProgram(userData->passThroughProgram);

    // Update attribute values.
    glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, squareVertices);
    glEnableVertexAttribArray(ATTRIB_VERTEX);
    glVertexAttribPointer(ATTRIB_TEXTUREPOSITON, 2, GL_FLOAT, 0, 0, textureVertices);
    glEnableVertexAttribArray(ATTRIB_TEXTUREPOSITON);

    // Update uniform values if there are any
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
///
// Draw a triangle using the shader pair created in Init()
//
void drawGraphics ( ESContext *esContext )
{
    UserData *userData = esContext->userData;
    static const GLfloat vVertices[] = {  0.0f,  0.5f, 0.0f,
        -0.5f, -0.5f, 0.0f,
        0.5f, -0.5f, 0.0f };

    // Set the viewport
    glViewport ( 0, 0, esContext->bufWidth, esContext->bufHeight );

    // Clear the color buffer
    glClear ( GL_COLOR_BUFFER_BIT );

    // Use the program object
    glUseProgram ( userData->graphicsProgram );

    // Load the vertex data
    glVertexAttribPointer ( 0, 3, GL_FLOAT, GL_FALSE, 0, vVertices );
    glEnableVertexAttribArray ( 0 );

    glDrawArrays ( GL_TRIANGLES, 0, 3 );
}

【问题讨论】:

    标签: ios multithreading uiview opengl-es


    【解决方案1】:

    纹理缓存中的像素缓冲区必须与纹理相关联,因此您不能直接将其与 CAEAGLLayer 渲染目标一起使用。

    但是,您可以通过将场景渲染到与 CVOpenGLESTextureCache 相关的纹理,然后使用简单的四边形和直通着色器将该纹理渲染到 CAEAGLLayer 中的屏幕,来避免两次渲染同一场景。这对渲染性能的影响很小,但可以避免 glReadPixels() 提取用于电影录制的场景信息的能力。

    【讨论】:

    • 非常感谢。基本上,您所说的就是 Apple 的 RosyWriter 示例所做的。 developer.apple.com/library/ios/#samplecode/RosyWriter/… 有大问题。 OpenGL-ES EAGLContext 从主线程切换到工作线程将需要互斥锁来锁定它们以避免硬件驱动程序呈现错误的结果? B/c 只有 1 个硬件资源。每次主线程/工作线程需要绘制时,都必须调用 [EAGLContext setCurrentContext:currentContext] ,这将成为瓶颈。你有办法解决这个问题吗?
    • @Howy - 好吧,从技术上讲,您可以在任何线程中访问像素缓冲区中的字节,而不仅仅是您的 OpenGL ES 上下文的当前线程。但是,您会注意到,如果您这样做,由于在电影编写器从像素缓冲区读取字节时发生渲染,因此在记录此缓冲区时会出现屏幕撕裂。无论如何,您需要在阅读之前在渲染 OpenGL ES 上下文中使用阻塞 glFinish()。为此,我使用了一个串行调度队列,并将对一个 OpenGL ES 上下文的访问包装在调度到该队列的块中。
    • 这是非常轻量级的,并且避免了围绕共享上下文的昂贵锁。我在我的图像处理框架和分子建模器中使用它,用于以 60 FPS 平稳运行的操作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-20
    • 1970-01-01
    • 2017-03-12
    • 1970-01-01
    • 1970-01-01
    • 2011-04-06
    • 1970-01-01
    相关资源
    最近更新 更多