Shared context for background thread loading of resources

Hi,

I've been trying to get shared context to loading opengl resources in a background thread however I'm can't seems to get it to work.

Here is what I am doing:

mEglContextMain = eglCreateContext( mEglDisplay, mEglConfig, EGL_NO_CONTEXT, contextAttribs);

mEglContextThread = eglCreateContext( mEglDisplay, mEglConfig, mEglContextMain, contextAttribs);

mEglWindow = eglCreateWindowSurface( mEglDisplay, mEglConfig, nwt, attrib_list );

mAuxSurface = eglCreatePbufferSurface( mEglDisplay, auxConfig, pbufferAttribs);

on the main thread, I call:
eglMakeCurrent( mEglDisplay, mEglWindow, mEglWindow, mEglContextMain );

on the background loading thread (using std::thread), I call:
eglMakeCurrent( mEglDisplay, mAuxSurface, mAuxSurface, mEglContextThread);

The error I'm getting is when I call the make current call on the background thread, the main thread opengl es functions will start to give me an error of INVALID_OPERATION.

I've managed to get this shared context working on iPhone (xcode) and regular Opengl, but I can't seems to get it to work with the PowerVR Imagination SDK.

Does anyone have any ideas?

Thanks.

Hi Digi,

There’s no obvious reason for this code to be generating an error. Have you tried calling eglGetError after each egl function to see if anything is going wrong at this stage?

Are you using the PVRShell when you do this test? If so, how are you integrating this code?

All else being correct, have you checked that the values of mEglContextThread and mEglContextMain are in fact different values when being passed to eglMakeCurrent?

Regards,
Tobias

Thanks for the reply Tobias.  I’ll provide a few more details, hopefully that might help.


My computer is running Windows 8 64bit.
GPU is AMD Radeon HD 6750M
Imagination PowerVR SDK 3.0
Binaries: Windows x86-32

I don’t think I am using PVRShell, I am just calling the EGL functions.

Here is my code and findings:

1) I start of by making two contexts, and setting eglMakeCurrent with the maincontext on the main thread. When I get to the point where I spawn my second thread, I would set eglMakeCurrent with the threadcontext on the second thread. However, when the main thread continues to render, it will give me an INVALID_OPERATION.

2) My fix for the (1) was to make sure I set the eglMakeCurrent with the maincontext on the main thread again before doing any opengl es operations. example, call eglMakeCurrent(maincontext) at the beginning of the frame.  
- is this call necessary? Why would setting eglMakeCurrent on the second thread require me to re-set the eglMakeCurrent on the main thread?

3) Step (2) will fix the INVALID_OPERATION, and my app will continue, however it will continue with a hard crash of my system display driver on the next eglMakeCurrent(maincontext) call, and I would see a Windows notification telling me my “Display Driver has not responding but has recovered”.

Here is my OpenGL ES context code.

OGLESContext::OGLESContext()
{
    Reset();
}

//===========================================================================

OGLESContext::~OGLESContext()
{
    Terminate();
}

//===========================================================================

EGLConfig OGLESContext::SelectEGLConfiguration( bool needPbuffer )
{
    EGLint num_config;
    EGLint conflist[32];
    EGLConfig conf;
    int  i = 0;

    // Select default configuration
    conflist[i++] = EGL_LEVEL;
    conflist[i++] = 0;

    conflist[i++] = EGL_NATIVE_RENDERABLE;
    conflist[i++] = EGL_FALSE;

    conflist[i++] = EGL_BUFFER_SIZE;
    conflist[i++] = 0;

    conflist[i++] = EGL_DEPTH_SIZE;
    conflist[i++] = 16;

    conflist[i++] = EGL_STENCIL_SIZE;
    conflist[i++] = 8;

    conflist[i++] = EGL_SURFACE_TYPE;
    conflist = EGL_WINDOW_BIT;

    if ( needPbuffer )
    {
        conflist |= EGL_PBUFFER_BIT;
    }

    i++;

    conflist[i++] = EGL_RENDERABLE_TYPE;
    conflist[i++] = EGL_OPENGL_ES2_BIT;

    conflist[i++] = EGL_SAMPLE_BUFFERS;
    conflist[i++] = 0;

    // Terminate the list with EGL_NONE
    conflist[i++] = EGL_NONE;

    // Return null config if config is not found
    if(!eglChooseConfig( mEglDisplay, conflist, &conf, 1, &num_config) || num_config != 1)
    {
        Assert( false, “” );
        return 0;
    }

    // Return config index
    return conf;
}

//===========================================================================

void OGLESContext::Initialize( HWND hWnd )
{
    //
    // Remember the window handle (HWND)
    //
    mHWnd = hWnd;

    //
    // Get the device context (DC)
    //
    mHDC = GetDC( mHWnd );

    EGLNativeDisplayType ndt;
    ndt = (EGLNativeDisplayType)mHDC;
    mEglDisplay = eglGetDisplay(ndt);    

    if ( !eglInitialize( mEglDisplay, &mMajorVersion, &mMinorVersion ) )
    {
        Assert( false, “” );
    }

    if ( !eglBindAPI(EGL_OPENGL_ES_API ) )
    {
        Assert( false, “” );
    }

    mEglConfig = SelectEGLConfiguration( false );

    EGLint contextAttribs[] = 
    { 
        EGL_CONTEXT_CLIENT_VERSION, 
        2, 
        EGL_NONE 
    };

    mEglContextMain = eglCreateContext( mEglDisplay, mEglConfig, EGL_NO_CONTEXT, contextAttribs);
    Assert( eglGetError() == EGL_SUCCESS, “” );
    mEglContextThread = eglCreateContext( mEglDisplay, mEglConfig, mEglContextMain, contextAttribs);
    Assert( eglGetError() == EGL_SUCCESS, “” );

    EGLint attrib_list[16];
    int i = 0;
    attrib_list = EGL_NONE;

    EGLNativeWindowType nwt = (EGLNativeWindowType)hWnd;
    mEglWindow = eglCreateWindowSurface( mEglDisplay, mEglConfig, nwt, attrib_list );
    Assert( eglGetError() == EGL_SUCCESS, “” );

    EGLBoolean contextResults = eglMakeCurrent( mEglDisplay, mEglWindow, mEglWindow, mEglContextMain );
    Assert( contextResults != 0, “” );
}

//===========================================================================

void OGLESContext::Terminate()
{
}

//===========================================================================

void OGLESContext::SwapFrameBuffers()
{
    eglSwapBuffers( mEglDisplay, mEglWindow );
    Assert( eglGetError() == EGL_SUCCESS, “” );
}

//===========================================================================

void OGLESContext::Reset()
{
    mHWnd = NULL;
    mHDC = NULL;
    mHRC = NULL;
}

//===========================================================================

void OGLESContext::SetMainContext()
{       
    EGLBoolean contextResults = eglMakeCurrent( mEglDisplay, mEglWindow, mEglWindow, mEglContextMain );    
    Assert( contextResults != 0, “” );
}

//===========================================================================

void OGLESContext::SetThreadContext()
{       
    EGLBoolean contextResults = eglMakeCurrent( mEglDisplay, mEglWindow, mEglWindow, mEglContextThread );
    Assert( contextResults != 0, “” );    
}

//===========================================================================

void OGLESContext::SetEmptyContext()
{       
    eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);    
}

I also want to note that my code works with the lib/dlls in the OpenGL ES SDK from AMD (http://developer.amd.com/tools/graphics-development/amd-opengl-es-sdk/).

Hi Digi,



Ah sorry, I didn’t realise you were using our VFrame emulation libraries. Unfortunately these libraries don’t currently support multi-threaded OpenGL ES.



We have an engineer looking at it, but it’s a fundamental problem with how the libraries were originally coded. As a result we’re having to update a lot of code, but we do understand that it’s a fairly important issue, particularly moving forward. I’ll note your feedback which will help justify making it a priority.



In the meantime, please keep an eye on our release notes for any updates. I’m sorry that I can’t be of more help with this issue :frowning:



Regards,

Tobias

Tobias, thank you for your replies. Much appreciated.

did any one got this working in emulator