I'm partway through developing a 2D game in C++ using SDL and OpenGL; my IDE of choice is CodeBlocks. I've hit an issue that I can't seem to get around.
Basically, if I minimise and then restore the window that my game is in, the graphics more or less break. Initially I was presented with just a black screen, though with some trial and error of SDL and OpenGL initialising I've managed to get OpenGL primitives back. However textures are showing up as white boxes.
I'm not sure where the issue is, though I think it has something to do with generating / binding OpenGL textures as the actual data of the textures seems to be present. Additionally, reloading all textures on window restore seems to solve this problem (though this feels like a very ugly hack).
I'd appreciate any help or guidance on this.
For context, here is the code I use to actually initialise OpenGL on program start:
bool init()
{
//Initialize SDL and all subsystems
if( SDL_Init( SDL_INIT_EVERYTHING ) == -1 )
{
return false;
}
Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 4096);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);
//Enable VSync
if( !SDL_SetHint( SDL_HINT_RENDER_VSYNC, "1" ) )
printf( "WARNING: VSync failed" );
//Set texture filtering to linear
if( !SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, "1" ) )
printf( "WARNING: Linear texture filtering failed" );
//Set up the primariy window
mainwindow = SDL_CreateWindow("...", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_OPENGL);// && SDL_WINDOW_FULLSCREEN);
//Tie mainwindow to main screen
screen = SDL_GetWindowSurface( mainwindow );
SCREEN_CENTRE_X = SCREEN_WIDTH / 2;
SCREEN_CENTRE_Y = SCREEN_HEIGHT / 2;
//Initialize SDL_ttf
if( TTF_Init() == -1 )
{
return false;
}
//Initialization flag
bool success = true;
//Use OpenGL 3.1 core
//SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 3 );
//SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 1 );
//SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE );
gContext = SDL_GL_CreateContext( mainwindow );
if( gContext == NULL )
{
printf( "OpenGL context could not be created! SDL Error: %s\n", SDL_GetError() );
success = false;
}
else
{
//Initialize GLEW
glewExperimental = GL_TRUE;
GLenum glewError = glewInit();
if( glewError != GLEW_OK )
{
printf( "Error initializing GLEW! %s\n", glewGetErrorString( glewError ) );
}
//Use Vsync
if( SDL_GL_SetSwapInterval( 1 ) < 0 )
{
printf( "Warning: Unable to set VSync! SDL Error: %s\n", SDL_GetError() );
}
//Initialize OpenGL
if( !initGL() )
{
printf( "Unable to initialize OpenGL!\n" );
success = false;
}
}
//If everything initialized fine
return true;
}
bool initGL()
{
//Success flag to return
bool success = true;
//Generate program and ID
gProgramID = glCreateProgram();
//Create vertex shader
GLuint vertexShader = glCreateShader( GL_VERTEX_SHADER );
char rvc;
std::string rvertshaderinput = "";
std::ifstream rvs("resources/shader_regularvert.glsl", std::fstream::in | std::fstream::app);
while(rvs.get(rvc))
{
rvertshaderinput.push_back(rvc);
}
rvs.close();
const char *glrvertshaderinput = rvertshaderinput.c_str();
//Set fragment source
glShaderSource( vertexShader, 1, &glrvertshaderinput, NULL );
//Compile vertex source
glCompileShader( vertexShader );
//Attach vertex shader to program
glAttachShader( gProgramID, vertexShader );
//Create fragment shader
GLuint fragmentShader = glCreateShader( GL_FRAGMENT_SHADER );
char rfc;
std::string rfragshaderinput = "";
std::ifstream rfs("resources/shader_regularfrag.glsl", std::fstream::in | std::fstream::app);
while(rfs.get(rfc))
{
rfragshaderinput.push_back(rfc);
}
rfs.close();
const char *glrfragshaderinput = rfragshaderinput.c_str();
//Set fragment source
glShaderSource( fragmentShader, 1, &glrfragshaderinput, NULL );
//Compile fragment source
glCompileShader( fragmentShader );
//Attach fragment shader to program
glAttachShader( gProgramID, fragmentShader );
//Link program
glLinkProgram( gProgramID );
//Initialize clear color
glClearColor( 0.f, 0.f, 0.f, 1.f );
//VBO data
GLfloat vertexData[] =
{
-0.5f, -0.5f,
0.5f, -0.5f,
0.5f, 0.5f,
-0.5f, 0.5f
};
//IBO data
GLuint indexData[] = { 0, 1, 2, 3 };
//Create VBO
glGenBuffers( 1, &gVBO );
glBindBuffer( GL_ARRAY_BUFFER, gVBO );
glBufferData( GL_ARRAY_BUFFER, 2 * 4 * sizeof(GLfloat), vertexData, GL_STATIC_DRAW );
//Create IBO
glGenBuffers( 1, &gIBO );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, gIBO );
glBufferData( GL_ELEMENT_ARRAY_BUFFER, 4 * sizeof(GLuint), indexData, GL_STATIC_DRAW );
init_GL_blur_shaders();
//Set the viewport
glViewport( 0.f, 0.f, SCREEN_WIDTH, SCREEN_HEIGHT );
//Initialize Projection Matrix
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glOrtho( 0.0, SCREEN_WIDTH, SCREEN_HEIGHT, 0.0, 1.0, -1.0 );
//Initialize Modelview Matrix
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
//Initialize clear color
glClearColor( 0.f, 0.f, 0.f, 1.f );
//Enable texturing
glEnable( GL_TEXTURE_2D );
glEnable(GL_DEBUG_OUTPUT);
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
return success;
}
And here is the code I'm currently using to try and get the graphics back after window restore (which partially works but is very much a shotgun approach):
if(event.window.event == SDL_WINDOWEVENT_RESTORED)
{
printf("SDL window restored\n");
//SDL_GL_DeleteContext(gContext);
//gContext = SDL_GL_CreateContext( mainwindow );
screen = SDL_GetWindowSurface( mainwindow );
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);
//Enable VSync
if( !SDL_SetHint( SDL_HINT_RENDER_VSYNC, "1" ) )
{
printf( "Warning: VSync not enabled!" );
}
//Set texture filtering to linear
if( !SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, "1" ) )
{
printf( "Warning: Linear texture filtering not enabled!" );
}
SDL_GL_DeleteContext(gContext);
gContext = SDL_GL_CreateContext( mainwindow );
if( gContext == NULL )
{
printf( "OpenGL context could not be created! SDL Error: %s\n", SDL_GetError() );
}
else
{
//Initialize GLEW
glewExperimental = GL_TRUE;
GLenum glewError = glewInit();
if( glewError != GLEW_OK )
{
printf( "Error initializing GLEW! %s\n", glewGetErrorString( glewError ) );
}
//Use Vsync
if( SDL_GL_SetSwapInterval( 1 ) < 0 )
{
printf( "Warning: Unable to set VSync! SDL Error: %s\n", SDL_GetError() );
}
glDeleteProgram(gProgramID);
gProgramID = glCreateProgram();
//Initialize OpenGL
if( !initGL() )
{
printf( "Unable to initialize OpenGL!\n" );
}
}
}
Simple prototype using the following code also falls prey to this issue:
void cgl_drawcirclefilled(float cx, float cy, float xr, float yr, SDL_Color color)
{
//Bind program
glUseProgram( gProgramID );
float vx = ((cx * 2) - (SCREEN_WIDTH)) / (float)SCREEN_WIDTH;
float vy = -((cy - SCREEN_HEIGHT) / SCREEN_CENTRE_Y + 1);
float vxr = xr / (float)SCREEN_WIDTH;
float vyr = yr / (float)SCREEN_HEIGHT;
fragmentcolourlocation = glGetUniformLocation( gProgramID, "fragcolour" );
glUniform4f(fragmentcolourlocation, (float)color.r/ 255.0f, (float)color.g/ 255.0f, (float)color.b/ 255.0f, (float)color.a/ 255.0f);
int num_segments = 100;
glEnable(GL_MULTISAMPLE);
glBegin(GL_TRIANGLE_FAN);
for (int ii = 0; ii < num_segments; ii++)
{
float theta = 2.0f * 3.1415926f * float(ii) / float(num_segments);//get the current angle
float x = vxr * cosf(theta);//calculate the x component
float y = vyr * sinf(theta);//calculate the y component
glVertex2f(x + vx, y + vy);//output vertex
}
glEnd();
glDisable(GL_MULTISAMPLE);
//Disable vertex position
glDisableVertexAttribArray( gVertexPos2DLocation );
//Unbind program
glUseProgram( NULL );
}
int main( int argc, char* args[] )
{
if( init() == false )
{
return 2;
}
while(quit == false)
{
while( SDL_PollEvent( &event ) )
{
//If the user has Xed out the window
if( event.type == SDL_QUIT )
{
//Quit the program
quit = true;
}
if( event.type == SDL_WINDOWEVENT)
{
if(event.window.event == SDL_WINDOWEVENT_RESTORED)
{
printf("SDL window restored\n");
}
}
}
cgl_drawcirclefilled(SCREEN_CENTRE_X, SCREEN_CENTRE_Y, 50, 50, colour_white);
SDL_GL_SwapWindow( mainwindow );
}
return 0;
}
SDL_HINT_RENDER_SCALE_QUALITY
if you use OpenGL for rendering. The hint only applies to rendering done withSDL_Renderer
). \$\endgroup\$