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.
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.