Black texture if picture size > 1 * 1 in a second OpenGL thread

0

I am obliged to use multithreading in my project and my problem is that in a second thread I register a texture and if this one is larger than 1 px for width and height, the texture is rendered in black otherwise it works well...

I am using two OpenGL context and three threads.

Here is a standalone code which works well with two pictures: blue.jpg and red.jpg if red.jpg has 1 pixel for width and for height.

#include <SDL.h>
#include <SDL_image.h>
#include <gl.h>
#include <glu.h>
#include <cstring>
#include <thread>
#include <iostream>
#include <windows.h>
#include <wingdi.h>
#define WINDOW_WIDTH 1365
#define WINDOW_HEIGHT 704
using namespace std;

SDL_Window *screen;
SDL_GLContext ctx, ctxa;

SDL_Surface *flipSurface(SDL_Surface *surface)
{
    int current_line, pitch;
    SDL_Surface *fliped_surface = SDL_CreateRGBSurface(SDL_SWSURFACE, surface->w,surface->h, surface->format->BitsPerPixel, surface->format->Rmask, surface->format->Gmask, surface->format->Bmask, surface->format->Amask);
    SDL_LockSurface(surface);
    SDL_LockSurface(fliped_surface);
    pitch = surface->pitch;
    for(current_line = 0; current_line < surface->h; current_line++)
        memcpy(&((unsigned char*)fliped_surface->pixels)[current_line*pitch], &((unsigned char*)surface->pixels)[(surface->h - 1  - current_line)*pitch], pitch);
    SDL_UnlockSurface(fliped_surface);
    SDL_UnlockSurface(surface);
    return fliped_surface;
}

GLuint loadTexture(const char *filename, bool useMipMap)
{
    GLuint glID;
    SDL_Surface * picture_surface = NULL;
    SDL_Surface *gl_surface = NULL;
    SDL_Surface * gl_fliped_surface = NULL;
    Uint32 rmask, gmask, bmask, amask;
    picture_surface = IMG_Load(filename);
    if (picture_surface == NULL)
        return 0;
    #if SDL_BYTEORDER == SDL_BIG_ENDIAN
        rmask = 0xff000000;
        gmask = 0x00ff0000;
        bmask = 0x0000ff00;
        amask = 0x000000ff;
    #else

        rmask = 0x000000ff;
        gmask = 0x0000ff00;
        bmask = 0x00ff0000;
        amask = 0xff000000;
    #endif
    SDL_PixelFormat format = *(picture_surface->format);
    format.BitsPerPixel = 32;
    format.BytesPerPixel = 4;
    format.Rmask = rmask;
    format.Gmask = gmask;
    format.Bmask = bmask;
    format.Amask = amask;
    gl_surface = SDL_ConvertSurface(picture_surface,&format,SDL_SWSURFACE);
    gl_fliped_surface = flipSurface(gl_surface);
    glGenTextures(1, &glID);
    glBindTexture(GL_TEXTURE_2D, glID);
    if(useMipMap)
    {
        gluBuild2DMipmaps(GL_TEXTURE_2D, 4, gl_fliped_surface->w, gl_fliped_surface->h, GL_RGBA,GL_UNSIGNED_BYTE, gl_fliped_surface->pixels);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    }
    else
    {
        glTexImage2D(GL_TEXTURE_2D, 0, 4, gl_fliped_surface->w, gl_fliped_surface->h, 0, GL_RGBA,GL_UNSIGNED_BYTE, gl_fliped_surface->pixels);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    }
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    SDL_FreeSurface(gl_fliped_surface);
    SDL_FreeSurface(gl_surface);
    SDL_FreeSurface(picture_surface);
    return glID;
}

GLuint texture = 0;

void render(GLuint picture)
{
    glBindTexture(GL_TEXTURE_2D, picture);
    glBegin(GL_QUADS);
        glTexCoord2i(0, 0);glVertex3d(-1,-1,picture);
        glTexCoord2i(0, 1);glVertex3d(-1,1,picture);
        glTexCoord2i(1, 1);glVertex3d(1,1,picture);
        glTexCoord2i(1, 0);glVertex3d(1,-1,picture);
    glEnd();
    cout << picture << "^" << endl;
}

bool needRegister = false;

void textureManager()
{
    SDL_GL_MakeCurrent(screen, ctxa);
    while(1)
    {
        if(needRegister)
        {
            texture = loadTexture("red.jpg", false);
            needRegister = false;
            cout << texture << endl;
        }
        SDL_Delay(50);
    }
}

GLuint registerRed()
{
    needRegister = true;
    while(1)
    {
        SDL_Delay(50);
        if(texture)
            return texture;
    }
}

void b()
{
    SDL_Init(SDL_INIT_VIDEO);
    screen = SDL_CreateWindow("My App", 100, 100, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_OPENGL);
    SDL_Event event;

    ctx = SDL_GL_CreateContext(screen);
    ctxa = SDL_GL_CreateContext(screen);
    bool a = wglShareLists((HGLRC)ctx, (HGLRC)ctxa);
    SDL_GL_MakeCurrent(screen, ctx);
    thread registerRedThread = thread(&textureManager);

    bool running = true;

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(70, (double)WINDOW_WIDTH / WINDOW_HEIGHT, 1, 100);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_TEXTURE_2D);

    GLuint blue = loadTexture("blue.jpg", false);
    registerRed();
    cout << endl << "Registered: " << texture << endl << endl;

    while(running)
    {
        SDL_PollEvent(&event);
        switch(event.type)
        {
            case SDL_QUIT:
                {
                    running = false;
                    break;
                }
        }

        cout << SDL_GetTicks() << endl;
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();

        gluLookAt(3, 4, 0, 0, 0, 1, 0, 0, 1);

        render(blue);
        if(texture != 0)
        {
            cout << "@" << blue << " " << texture << endl;
            cout << "Rendering the red picture..." << endl;
            render(texture);
        }

        glFlush();
        SDL_GL_SwapWindow(screen);

        SDL_Delay(1000);
    }
    registerRedThread.detach();
    SDL_Quit();
}

thread a;

int main(int argc, char** argv)
{
    a = thread(&b);
    while(1);
    return 0;
}

Futhermore if we try the same test in the first picture (registered in the "first" thread) it works well... We can use multipixel texture, other colors and transparency. I don't understand why the problem happends only in the second thread.

EDIT: Moreover if I use a transparent for the picture registered in the other thread I just see a blank picture.

c++
multithreading
opengl
sdl-2
asked on Stack Overflow Aug 19, 2017 by Benjamin LOISON • edited Aug 19, 2017 by Benjamin LOISON

1 Answer

0

As PeterT said in comments:

Driver optimizations might have gotten you. On my Nvidia card it works if I add a glFlush() after texture = loadTexture("red.jpg", false);

I guess glFinish() would be the more correct method. Anyway, just make sure the all the commands regarding the texture uploading have been executed.

answered on Stack Overflow Aug 21, 2019 by Benjamin LOISON

User contributions licensed under CC BY-SA 3.0