Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use OpenGL ES on a separate thread on iphone?

The OpenGL ES rendering loop is placed on a separate thread in my iphone application. Everything goes fine except that the EAGLContext's presentRenderbuffer method fails. The result is a blank white screen. When the same code is run on the main thread, presentRenderbuffer succeeds and the graphics is properly shown. What is the correct way of doing OpenGL on a separate thread?

like image 298
Klaus Kilhe Avatar asked Aug 10 '09 04:08

Klaus Kilhe


2 Answers

You need to create an EAGLSharegroup.

Check out this thread on sharing OpenGL contexts between threads.

UPDATE
Previous to iOS5 I shared OpenGL contexts between threads to allow asynchronous loading of textures from disk. But iOS5's CVOpenGLESTextureCaches essentially make texture uploads free so I don't need shareGroups anymore and my code is simpler and faster.

like image 126
Rhythmic Fistman Avatar answered Oct 22 '22 07:10

Rhythmic Fistman


Thanks, Fistman. I made it work and got the performance gain that I expected from using a separate thread. EAGLSharegroup solved the problem.

I created the context for the second thread as described here.

Here is the boilerplate code:


#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");
    }   
}


like image 24
2 revs Avatar answered Oct 22 '22 05:10

2 revs