Welcome!

Welcome to the official BlackBerry Support Community Forums.

This is your resource to discuss support topics with your peers, and learn from each other.

inside custom component

Native Development

Reply
New Developer
Posts: 50
Registered: ‎01-22-2009
My Device: Z10
My Carrier: Rogers

Issue making shared OpenGL context current

Good day,

 

I am encountering an issue when trying to make a shared opengl context current from a background thread.   I am doing this to enable background texturing in order to speed up page loads.  The overall environment is a cross platform app engine I have written for BB10, iOS, Android, Windows and OSX-- the code works fine on all other platforms.

 

Here's what happens:

 

Main Thread:

 

- initializes opengl, creates a 'bank' of shared opengl contexts (none of which has been made current yet).

- renders just fine

- each shared context uses the same config as the original context

 

Background Thread

 

- Assigns a shared context to itself

- Calls eglMakeCurrent, using the same surface as the first context

 

eglMakeCurrent fails from the Background thread.  Attempting to retrieve the error code results in a SIGSEGV-- making it really hard to debug.

 

I have also tried a variant where the background thread calls eglMakeCurrent with a pixelbuffer surface-- same result.

 

Any thoughts on why what I am doing would not work on BB10, but works fine everywhere else?

 

Dave

New Developer
Posts: 50
Registered: ‎01-22-2009
My Device: Z10
My Carrier: Rogers

Re: Issue making shared OpenGL context current

I forgot to add:  just before things go haywire, I see the following:

 

PVR:(Warning): InitContext: ignoring buffer type CBUF_TYPE_PDS_VERT_SECONDARY_PREGEN_BUFFER [778, /eglglue.c]
PVR:(Warning): InitContext: ignoring buffer type CBUF_TYPE_PDS_VERT_SECONDARY_PREGEN_BUFFER [778, /eglglue.c]
PVR:(Warning): InitContext: ignoring buffer type CBUF_TYPE_PDS_VERT_SECONDARY_PREGEN_BUFFER [778, /eglglue.c]
PVR:(Warning): InitContext: ignoring buffer type CBUF_TYPE_PDS_VERT_SECONDARY_PREGEN_BUFFER [778, /eglglue.c]
PVR:(Warning): InitContext: ignoring buffer type CBUF_TYPE_PDS_VERT_SECONDARY_PREGEN_BUFFER [778, /eglglue.c]
PVR:(Warning): InitContext: ignoring buffer type CBUF_TYPE_PDS_VERT_SECONDARY_PREGEN_BUFFER [778, /eglglue.c]
PVR:(Warning): InitContext: ignoring buffer type CBUF_TYPE_PDS_VERT_SECONDARY_PREGEN_BUFFER [778, /eglglue.c]
PVR:(Warning): InitContext: ignoring buffer type CBUF_TYPE_PDS_VERT_SECONDARY_PREGEN_BUFFER [778, /eglglue.c]
PVR:(Warning): InitContext: ignoring buffer type CBUF_TYPE_PDS_VERT_SECONDARY_PREGEN_BUFFER [778, /eglglue.c]
PVR:(Warning): InitContext: ignoring buffer type CBUF_TYPE_PDS_VERT_SECONDARY_PREGEN_BUFFER [778, /eglglue.c]
PVR:(Warning): InitContext: ignoring buffer type CBUF_TYPE_PDS_VERT_SECONDARY_PREGEN_BUFFER [778, /eglglue.c]
326 bytes retrieved
PVR:(Warning): InitContext: ignoring buffer type CBUF_TYPE_PDS_VERT_SECONDARY_PREGEN_BUFFER [778, /eglglue.c]
PVR:(Warning): InitContext: ignoring buffer type CBUF_TYPE_PDS_VERT_SECONDARY_PREGEN_BUFFER [778, /eglglue.c]
PVR:(Warning): PBGrowShrinkComplete: New PB size is: 1572864 bytes [1228, /sgxpb.c]
PVR:(Warning): InitContext: ignoring buffer type CBUF_TYPE_PDS_VERT_SECONDARY_PREGEN_BUFFER [778, /eglglue.c]
PVR:(Warning): InitContext: ignoring buffer type CBUF_TYPE_PDS_VERT_SECONDARY_PREGEN_BUFFER [778, /eglglue.c]
PVR:(Warning): PrepareToDraw: Creating ZS buffer for SPM [779, /sgxif.c]
PVR:(Warning): InitContext: ignoring buffer type CBUF_TYPE_PDS_VERT_SECONDARY_PREGEN_BUFFER [778, /eglglue.c]

 

 

Here is the code I use to generate the shared contexts and make them current:

 

void QSPlatform::makeGLContextCurrent( const QSGLContext& qsglcontext )
{

	if (!eglMakeCurrent(qsglcontext.display, qsglcontext.surface, qsglcontext.surface, qsglcontext.context)) {
		//glCheck();  // crashes!
		QSLog::e("Unable to make OpenGL context current");
	}

}

const QSGLContext QSPlatform::createDerivedGLContext( const QSGLContext& mainGLContext )
{
	QSGLContext ctx = mainGLContext;
	EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE, EGL_NONE };

	ctx.context = eglCreateContext(mainGLContext.display, mainGLContext.config, mainGLContext.context, contextAttribs);


	// Tested using a separate surface-- to no avail:
	/*const EGLint auxConfigAttribs[] =
	{
		EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
		EGL_BLUE_SIZE, 8,
		EGL_GREEN_SIZE, 8,
		EGL_RED_SIZE, 8,
		EGL_ALPHA_SIZE, 0,
		EGL_DEPTH_SIZE, 0,
		EGL_STENCIL_SIZE, 0,
		EGL_NONE
	};

	EGLint pbufferAttribs[] =
	{
		EGL_WIDTH, 1,
		EGL_HEIGHT, 1,
		EGL_TEXTURE_TARGET, EGL_NO_TEXTURE,
		EGL_TEXTURE_FORMAT, EGL_NO_TEXTURE,
		EGL_NONE
	};

	EGLint auxNumConfigs;
	EGLConfig auxConfig;

	eglChooseConfig(mainGLContext.display, auxConfigAttribs, &auxConfig, 1, &auxNumConfigs);
	glCheck();

	ctx.surface = eglCreatePbufferSurface(mainGLContext.display, auxConfig, pbufferAttribs);
	glCheck();

	ctx.context = eglCreateContext(mainGLContext.display, mainGLContext.config, mainGLContext.context, contextAttribs);
	glCheck();

	*/

	if (ctx.context == EGL_NO_CONTEXT ){
		glCheck();
		QSLog::e("Unable to create derived OpenGL Context");
	}

	return ctx;
}

 

New Developer
Posts: 50
Registered: ‎01-22-2009
My Device: Z10
My Carrier: Rogers

Re: Issue making shared OpenGL context current

[ Edited ]

The call to glCheck (a macro which essentially calls glGetError()) crashes with the SIGSEGV.  Not clear to me if it should return GL_INVALID_OPERATION, because no context is set.  But then how can I determine why GL was not able to make this thread's context current?

 

In any case, I will report the crash as a bug since it is readily duplicatable.

New Developer
Posts: 50
Registered: ‎01-22-2009
My Device: Z10
My Carrier: Rogers

Re: Issue making shared OpenGL context current

I whittled this down to a simple multi-threaded context loading program in order to demonstrate the bug in the BB10 platform.  The only problem was the simplified example ran just fine Smiley Happy  The good news is that I found the bug elsewhere in my own code.

 

So, in case other developers are interested, here is a simple example showing the creation of OpenGL textures in background threads while drawing in the main thread.  To keep things simple, the textures are just populated with random colors and drawn at random location.  The five textures are added one at a time, at 5 second intervals.

 

I hope someone out there finds this useful one day...

 

/*
 * Copyright (c) 2011-2012 Research In Motion Limited.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <assert.h>
#include <screen/screen.h>
#include <bps/navigator.h>
#include <bps/screen.h>
#include <bps/bps.h>
#include <bps/event.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

#include <EGL/egl.h>
#include <GLES2/gl2.h>

#include <vector>
#include <string>

#include <math.h>

#include "bbutil.h"

#include "pthread.h"
#include <bps/dialog.h>
#include <sys/time.h>

#define THREADED_TEXTURES 1
//#undef THREADED_TEXTURES

dialog_instance_t alert_dialog;

static screen_context_t screen_cxt;

std::vector<EGLContext> derivedContexts;
pthread_mutex_t derivedContextsMutex;

std::vector<GLuint> someTextures;
pthread_mutex_t someTexturesMutex;

GLuint bitmapProgram;
GLuint bitmapMatrixHandle;
GLuint bitmapProjectionHandle;
GLuint bitmapProgramInputPosition;
GLuint bitmapProgramTextureCoordinate;
GLuint bitmapProgramTextureSlot;
GLuint bitmapRecolourHandle;
GLuint bitmapColourHandle;
GLuint bitmapWindowSizeHandle;
GLuint bitmapGlobalAlphaHandle;

void handleScreenEvent(bps_event_t *event) {
    screen_event_t screen_event = screen_event_get_event(event);

    int screen_val;
    screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_TYPE, &screen_val);

    switch (screen_val) {
    case SCREEN_EVENT_MTOUCH_TOUCH:
    case SCREEN_EVENT_MTOUCH_MOVE:
    case SCREEN_EVENT_MTOUCH_RELEASE:
        break;
    }
}

#define glCheck() (checkForGLErrors(__FILE__, __LINE__))
void checkForGLErrors(const char *file, const int line);
const std::string getGLErrorDescription(const GLenum err);

void checkForGLErrors(const char *file, const int line) {
    int err = glGetError();
    if (err != 0) {
    	//QSLog::i("GL error 0x%X", err);

        std::string errString = getGLErrorDescription(err);
		fprintf(stderr, "%s(%i):OpenGL ERROR:%s\n", file, line, errString.c_str());
    }
}

const std::string getGLErrorDescription(const GLenum err){

	switch (err) {
		case 0:                          return "Success!";
		case 0x0500:                     return "INVALID ENUM";
		case 0x0501:                     return "INVALID VALUE";
		case 0x0502:                     return "INVALID OPERATION";
		case 0x0504:                     return "STACK UNDERFLOW";
		case 0x0503:                     return "OUT OF MEMORY OR STACK OVERFLOW";
		case 0x8031:                     return "TABLE TOO LARGE";

	}

	char msg[100];
	sprintf(msg, "Unknown Error %i", err);
	return msg;

}

void createBitmapProgram();

int initialize() {

	createBitmapProgram();

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

    // Create some shared contexts:
    for (int i=0;i<10;i++){
    	derivedContexts.push_back(eglCreateContext(egl_disp, egl_conf, egl_ctx, contextAttribs));
    }



    return EXIT_SUCCESS;
}

int thread_delay= 0;

void* loadTextureThread(void *not_used) {


	GLuint textureID;

#ifdef THREADED_TEXTURES
	EGLint numConfigs;
	EGLConfig auxConfig;
	EGLSurface auxSurface;

	int mydelay = ++thread_delay;

	// introduce new textures one at a time at one-second intervals
	usleep(mydelay*5000000);

	fprintf(stderr, "Loading texture now\n");


	const EGLint auxConfigAttribs[] =
	    {
	        EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
	        EGL_BLUE_SIZE, 8,
	        EGL_GREEN_SIZE, 8,
	        EGL_RED_SIZE, 8,
	        EGL_ALPHA_SIZE, 0,
	        EGL_DEPTH_SIZE, 0,
	        EGL_STENCIL_SIZE, 0,
	        EGL_NONE
	    };

	EGLint pbufferAttribs[] =
	    {
	        EGL_WIDTH, 1,
	        EGL_HEIGHT, 1,
	        EGL_TEXTURE_TARGET, EGL_NO_TEXTURE,
	        EGL_TEXTURE_FORMAT, EGL_NO_TEXTURE,
	        EGL_NONE
	    };

	eglChooseConfig(egl_disp, auxConfigAttribs, &auxConfig, 1, &numConfigs);
	auxSurface = eglCreatePbufferSurface(egl_disp, auxConfig, pbufferAttribs);

	pthread_mutex_lock(&derivedContextsMutex);
	if (derivedContexts.empty()){
		fprintf(stderr, "ERROR: ran out of derivedContexts");
		pthread_mutex_unlock(&derivedContextsMutex);
		return NULL;
	}
	EGLContext derivedContext = derivedContexts.back();
	derivedContexts.pop_back();
	pthread_mutex_unlock(&derivedContextsMutex);

	EGLBoolean  result = eglMakeCurrent(egl_disp, auxSurface, auxSurface, derivedContext);
	if (result == EGL_FALSE){
		fprintf(stderr, "UNABLE TO MAKE CONTEXT CURRENT\n");
		//int err = glGetError();
		bbutil_terminate();
	} else {
		fprintf(stderr, "made shared context current!\n");
	}
#endif

	// create a texture and put some dummy data in it:
	glGenTextures(1, &textureID);

	glBindTexture(GL_TEXTURE_2D, textureID);
	glCheck();
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glCheck();
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	// This is necessary for non-power-of-two textures
	glCheck();
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glCheck();
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 200, 200, 0, GL_ALPHA, GL_UNSIGNED_BYTE, 0);

	int size = 200;

	// create a dummy picture:
	unsigned char *image =  new unsigned char[size*size*4];

	unsigned char r = rand() * 255 / RAND_MAX;
	unsigned char g = rand() * 255 / RAND_MAX;
	unsigned char b = rand() * 255 / RAND_MAX;

	for (int i=0;i<size*size*4;i+=4){
		image[i] = r;
		image[i+1] = g;
		image[i+2] = b;
		image[i+3] = 255;
	}

	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);

	glBindTexture(GL_TEXTURE_2D, 0);


	pthread_mutex_lock(&someTexturesMutex);

	someTextures.push_back(textureID);

	pthread_mutex_unlock(&someTexturesMutex);

	delete [] image;

	return NULL;
}

int loadShader(const GLenum type, const std::string shaderCode) {

    int shader = glCreateShader(type);
    glCheck();

    const char *shaderCodeString = shaderCode.c_str();
    const int length = shaderCode.length();

    glShaderSource(shader, 1, &shaderCodeString, &length);
    glCheck();
    glCompileShader(shader);
    glCheck();
    GLint status;
	glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
	if (GL_FALSE == status) {
		GLchar log[256];
		glGetShaderInfoLog(shader, 256, NULL, log);

		fprintf(stderr, "Failed to compile vertex shader: %s\n", log);

		glDeleteShader(shader);
	}
    return shader;
}
bool validateProgram(GLuint prog) {

    GLint logLength, status;

    glValidateProgram(prog);
    glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength);
    if (logLength > 1) {
        GLchar *log = (GLchar *)malloc(logLength);
        glGetProgramInfoLog(prog, logLength, &logLength, log);
        fprintf(stderr, "Program validate log:\n%s", log);
        free(log);
    }

    glGetProgramiv(prog, GL_VALIDATE_STATUS, &status);
    if (status == 0) {
        return false;
    }

    return true;
}

void createBitmapProgram()
{
	GLint vertexShader = 0;
	GLint fragmentShader = 0;

	std::string vertexShaderProgram  =
		"attribute vec4 pos;\n"
		"varying vec2 texcoord;\n"
		"attribute vec2 texcoord_in;\n";

	vertexShaderProgram +=
		"void main()\n"
		"{\n"
		"   gl_Position = pos;\n"
		"	texcoord = texcoord_in;"
		"}\n";

	std::string fragmentShaderProgram =
		"precision mediump float;  \n"
		"varying vec2 texcoord;\n"
		"uniform sampler2D texture;\n";

	fragmentShaderProgram +=
		"void main()\n"
		"{\n"
		"		gl_FragColor = texture2D(texture, texcoord);"
		"}\n";

	fprintf(stderr, "load bitmap vertex shader...\n");
	vertexShader = loadShader(GL_VERTEX_SHADER, vertexShaderProgram.c_str());
	fprintf(stderr, "load bitamp fragment shader...\n");
	fragmentShader = loadShader(GL_FRAGMENT_SHADER, fragmentShaderProgram.c_str());

	fprintf(stderr, "create bitmap program...\n");
	bitmapProgram = glCreateProgram();
	glCheck();
	fprintf(stderr, "bitmap vertex shader program...\n");
	glAttachShader(bitmapProgram, vertexShader);
	glCheck();
	fprintf(stderr, "bitmap fragment shader program...\n");
	glAttachShader(bitmapProgram, fragmentShader);
	glCheck();

	fprintf(stderr, "Linking bitmap program\n");

	glLinkProgram(bitmapProgram);
	glCheck();
	validateProgram(bitmapProgram);
	glCheck();

	glUseProgram(bitmapProgram);
	glCheck();

	bitmapProgramInputPosition = glGetAttribLocation(bitmapProgram, "pos");
	glCheck();
	glEnableVertexAttribArray(bitmapProgramInputPosition);
	glCheck();
	bitmapProgramTextureCoordinate = glGetAttribLocation(bitmapProgram, "texcoord_in");
	glCheck();
	glEnableVertexAttribArray(bitmapProgramTextureCoordinate);
	glCheck();
	bitmapProgramTextureSlot =  glGetUniformLocation(bitmapProgram, "texture");
	glCheck();

}
void drawBitmapFromVertexBuffersAndDestRect( GLuint textureID, const GLfloat textureCoords[], GLfloat screenCoords[], int numPoints)
{

	glUseProgram(bitmapProgram);
	glCheck();

	glActiveTexture(GL_TEXTURE0);
	glCheck();
	glBindTexture(GL_TEXTURE_2D, textureID);
	glCheck();
	glUniform1i(bitmapProgramTextureSlot, 0);
	glCheck();
	glVertexAttribPointer(bitmapProgramInputPosition, 2, GL_FLOAT, 0, 0, screenCoords);
	glCheck();
	glVertexAttribPointer(bitmapProgramTextureCoordinate, 2, GL_FLOAT, 0, 0, textureCoords);
	glCheck();

	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	glDrawArrays(GL_TRIANGLE_STRIP, 0, numPoints);

}

void render() {

	glClear(GL_COLOR_BUFFER_BIT);

	eglMakeCurrent(egl_disp, egl_surf, egl_surf, egl_ctx);

    pthread_mutex_lock(&someTexturesMutex);
	float x = ((float)rand()*2)/RAND_MAX - 1.0f;
	float y = ((float)rand()*2)/RAND_MAX - 1.0f;

    for (unsigned i=0;i<someTextures.size();i++) {
    	GLfloat screenCoords[] = { x, y+0.5f,
    	    						x+0.5f, y+0.5f,
    	    						x, y,
    	    						x+0.5f, y};

    	    GLfloat texCoords[] = { -1.0f, 1.0f,
    	    						1.0f, 1.0f,
    	    						-1.0f, -1.0f,
    	    						1.0f, -1.0f};

    	drawBitmapFromVertexBuffersAndDestRect(someTextures[i], texCoords, screenCoords, 4);

    	x = ((float)rand())/RAND_MAX - 0.5f;
    	y = ((float)rand())/RAND_MAX - 0.5f;
    }

    pthread_mutex_unlock(&someTexturesMutex);
    bbutil_swap();
    usleep(10000);
}


void loadTextures() {
    // start a thread:
#ifdef THREADED_TEXTURES
    std::vector<pthread_t>  threads;
#endif

    pthread_mutex_init(&someTexturesMutex, NULL);
    pthread_mutex_init(&derivedContextsMutex, NULL);


    for (int i=0;i<5;i++){
#ifdef THREADED_TEXTURES
    	pthread_t _t;
    	pthread_create(&_t, NULL, loadTextureThread, NULL);
    	threads.push_back(_t);
#else
    loadTextureThread(NULL);
#endif
    }
}

int main(int argc, char *argv[]) {

    int exit_application = 0;

    timeval tim;
   	gettimeofday(&tim, NULL);
    srand((unsigned)tim.tv_usec);

    //glGetError();
    //Initialize BPS library
    bps_initialize();

    //Create a screen context that will be used to create an EGL surface to to receive libscreen events
    screen_create_context(&screen_cxt, 0);

    //Signal BPS library that navigator and screen events will be requested
    if (BPS_SUCCESS != screen_request_events(screen_cxt)) {
        fprintf(stderr, "screen_request_events failed\n");
        bbutil_terminate();
        screen_destroy_context(screen_cxt);
        bps_shutdown();
        return 0;
    }

    if (BPS_SUCCESS != navigator_request_events(0)) {
        fprintf(stderr, "navigator_request_events failed\n");
        bbutil_terminate();
        screen_destroy_context(screen_cxt);
        bps_shutdown();
        return 0;
    }

    //Signal BPS library that navigator orientation is not to be locked
    if (BPS_SUCCESS != navigator_rotation_lock(false)) {
        fprintf(stderr, "navigator_rotation_lock failed\n");
        bbutil_terminate();
        screen_destroy_context(screen_cxt);
        bps_shutdown();
        return 0;
    }


    //Use utility code to initialize EGL for rendering with GL ES 2.0
    if (EXIT_SUCCESS != bbutil_init_egl(screen_cxt)) {
        fprintf(stderr, "bbutil_init_egl failed\n");
        bbutil_terminate();
        screen_destroy_context(screen_cxt);
        return 0;
    }

    //Initialize application logic
    if (EXIT_SUCCESS != initialize()) {
        fprintf(stderr, "initialize failed\n");
        bbutil_terminate();
        screen_destroy_context(screen_cxt);
        bps_shutdown();
        return 0;
    }


    loadTextures();


    while (!exit_application) {
        //Request and process all available BPS events
        bps_event_t *event = NULL;


        for(;;) {

        	bps_get_event(&event, 0);

            if (event) {
                int domain = bps_event_get_domain(event);

                if (domain == screen_get_domain()) {
                    handleScreenEvent(event);
                } else if ((domain == navigator_get_domain())
                        && (NAVIGATOR_EXIT == bps_event_get_code(event))) {
                    exit_application = 1;
                }
            } else {
                break;
            }
        }
        render();
    }

    //Stop requesting events from libscreen
    screen_stop_events(screen_cxt);

    //Shut down BPS library for this process
    bps_shutdown();

    //Use utility code to terminate EGL setup
    bbutil_terminate();

    //Destroy libscreen context
    screen_destroy_context(screen_cxt);
    return 0;
}