【问题标题】:How to use OpenGL ES on a separate thread on iphone?如何在 iPhone 上的单独线程上使用 OpenGL ES?
【发布时间】:2009-08-10 04:49:16
【问题描述】:

OpenGL ES 渲染循环放置在我的 iphone 应用程序的单独线程中。一切都很好,除了 EAGLContext 的 presentRenderbuffer 方法失败。结果是一个空白的白色屏幕。 当在主线程上运行相同的代码时,presentRenderbuffer 成功并正确显示图形。 在单独的线程上做 OpenGL 的正确方法是什么?

【问题讨论】:

    标签: iphone multithreading opengl-es


    【解决方案1】:

    您需要创建一个EAGLSharegroup

    查看this thread 在线程之间共享 OpenGL 上下文。

    更新
    在 iOS5 之前,我在线程之间共享 OpenGL 上下文以允许从磁盘异步加载纹理。但是 iOS5 的 CVOpenGLESTextureCaches 基本上可以免费上传纹理,所以我不再需要 shareGroups,而且我的代码更简单、更快。

    【讨论】:

    • “免费上传纹理”是什么意思?我尝试使用来自 CVPixelBufferCreateWithBytes() 的结果调用 CVOpenGLESTextureCacheCreateTextureFromImage(),它所花费的时间与使用 glTexImage2D() 上传纹理的时间一样长。
    • glTexImage2D 相当于将 memcpy 放入纹理中,而 TextureFromImage 将纹理指向您的图像 - 无需复制。可以节省大量内存带宽。仅时序不会揭示多少 GL 是异步的。您需要查看 GPU/CPU 使用率和帧速率。
    • stackoverflow.com/questions/12813442/…,当用户进入缓冲区时,它似乎不像你描述的那样工作。事实上,我看到在这个执行路径中 glTexImage2D 花费了大量的 CPU 时间。
    • 也许你正在做一些让你走慢的事情。开始一个新问题并发布一些代码。
    • iOS 的 GPU 和 CPU 内存与 glTexImage2D 无法利用的相同,因为它不知道像素的范围。因此纹理缓存。由于没有太多文档,因此很难确定正确使用方法。
    【解决方案2】:

    谢谢,拳头。我让它工作并获得了使用单独线程所期望的性能提升。 EAGLSharegroup 解决了这个问题。

    我按照here 的描述为第二个线程创建了上下文。

    这是样板代码:

    
    #import <UIKit/UIKit.h>
    #import <OpenGLES/EAGL.h>
    #import <OpenGLES/ES1/gl.h>
    #import <OpenGLES/ES1/glext.h>
    #import <QuartzCore/QuartzCore.h>
    #import <OpenGLES/EAGLDrawable.h>
    
    
    struct OpenGLContext
    {
        GLint Width;
        GLint Height;
    
        GLuint RenderBuffer;
        GLuint FrameBuffer;
        GLuint DepthBuffer;
    
        UIView* View;
        EAGLContext* MainContext;
        EAGLContext* WorkingContext;
        EAGLSharegroup* Sharegroup; 
    
        // Trivial constructor.
        OpenGLContext();
    
        // Call on the main thread before use.
        // I call it in layoutSubviews.
        // view must not be nil.
        void MainInit(UIView* view);
    
        // Call on the rendering thread before use, but
        // after MainInit();
        void InitOnSecondaryThread();   
    
        // Call before any OpenGL ES calls, at the
        // beginning of each frame.
        void PrepareBuffers();
    
        // Present frame. Call at the end of each
        // frame.
        void SwapBuffers();
    };
    
    OpenGLContext::OpenGLContext()
    {
        Width = 0;
        Height = 0;
    
        RenderBuffer = 0;
        FrameBuffer = 0;
        DepthBuffer = 0;
    
        View = 0;
        MainContext = 0;
        WorkingContext = 0;
        Sharegroup = 0; 
    }
    
    void OpenGLContext::InitOnSecondaryThread()
    {
        EAGLSharegroup* group = MainContext.sharegroup;
        if (!group)
        {
            NSLog(@"Could not get sharegroup from the main context");
        }
        WorkingContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1 sharegroup:group];
        if (!WorkingContext || ![EAGLContext setCurrentContext:WorkingContext]) {
            NSLog(@"Could not create WorkingContext");
        }
    }
    
    void OpenGLContext::MainInit(UIView* view)
    {
        View = view;
        MainContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
    
        if (!MainContext || ![EAGLContext setCurrentContext:MainContext]) {
            NSLog(@"Could not create EAGLContext"); 
            return;
        }
        NSLog(@"Main EAGLContext created");     
    
        glGenFramebuffersOES(1, &FrameBuffer);
        glGenRenderbuffersOES(1, &RenderBuffer);
        glGenRenderbuffersOES(1, &DepthBuffer);
    
        glBindFramebufferOES(GL_FRAMEBUFFER_OES, FrameBuffer);
        glBindRenderbufferOES(GL_RENDERBUFFER_OES, RenderBuffer);
    
        if (![MainContext renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)View.layer])
        {
            NSLog(@"error calling MainContext renderbufferStorage");
            return;
        }
    
        glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, RenderBuffer);
    
        glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &Width);
        glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &Height);
    
        glBindRenderbufferOES(GL_RENDERBUFFER_OES, DepthBuffer);
        glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, Width, Height);
        glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, DepthBuffer);
    
        glFlush();
    
        if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) {
            NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
        }
    
        WorkingContext = MainContext;
    }
    
    void OpenGLContext::PrepareBuffers()
    {   
        if (!WorkingContext || [EAGLContext setCurrentContext:WorkingContext] == NO)
        {
            NSLog(@"PrepareBuffers: [EAGLContext setCurrentContext:WorkingContext] failed");
            return;
        }
        glBindFramebufferOES(GL_FRAMEBUFFER_OES, FrameBuffer);  
    }
    
    void OpenGLContext::SwapBuffers()
    {
        if (!WorkingContext || [EAGLContext setCurrentContext:WorkingContext] == NO)
        {
            NSLog(@"SwapBuffers: [EAGLContext setCurrentContext:WorkingContext] failed");
            return;
        }
    
        glBindRenderbufferOES(GL_RENDERBUFFER_OES, RenderBuffer);
    
        if([WorkingContext presentRenderbuffer:GL_RENDERBUFFER_OES] == NO)
        {
            NSLog(@"SwapBuffers: [WorkingContext presentRenderbuffer:GL_RENDERBUFFER_OES] failed");
        }   
    }
    
    
    

    【讨论】:

      【解决方案3】:

      您不应该在不同的线程上呈现上下文。相反,在不同的线程上进行所有计算,然后确保在主显示线程上进行渲染。

      【讨论】:

      • 您可以使用 GCD 轻松做到这一点。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多