4
\$\begingroup\$

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;
}
\$\endgroup\$
  • 1
    \$\begingroup\$ Do you use SDL2 or SDL1.X? (Also, there's no use in setting SDL_HINT_RENDER_SCALE_QUALITY if you use OpenGL for rendering. The hint only applies to rendering done with SDL_Renderer). \$\endgroup\$ – Tyyppi_77 Jul 11 '16 at 10:11
  • \$\begingroup\$ It's a strange problem. I've never faced that issue with same libraries (SDL2 + OpenGL). Do you have same issue with a simple hello world triangle? \$\endgroup\$ – HolyBlackCat Jul 11 '16 at 10:32
  • \$\begingroup\$ @Tyyppi_77 SDL2, specifically version 2.0.4. Thanks for the tip, I'll remove that hint as all of my rendering is done with OpenGL \$\endgroup\$ – nathanburns Jul 11 '16 at 11:00
  • \$\begingroup\$ @HolyBlackCat It does seem strange, I've not been able to find anyone else with the same issue. In terms of a simple hello world triangle, if I include the 'window restore' code above I can draw primitives no problem; it seems to be a texture specific problem. \$\endgroup\$ – nathanburns Jul 11 '16 at 11:04
  • 1
    \$\begingroup\$ I mean does the simple triangle breaks without restoring code? \$\endgroup\$ – HolyBlackCat Jul 11 '16 at 11:45
0
\$\begingroup\$

OK so I have a partial answer (more of a kludge) that might eventually lead to the real, proper solution for this.

I've added some code to check for a SDL_WINDOWEVENT_RESTORED event; when this occurs, openGL is reinitialised, and all of the textures that were loaded on program start are cleared and re-loaded.

This feels very hacky, and carries about a 40MB memory leak for each time the window is minimised and re-opened, but it'll do for now. The memory leak is possibly the result of me not freeing all of the memory I should be freeing, but I'm unclear as to what parts of OpenGL initalisation actually takes up memory.

I'm still pretty unsure what's actually causing this issue; I don't know if it's SDL or OpenGL, or just something simple I'm missing in initalisation that would otherwise prevent this.

Comments / insight welcome.

For posterity, I use the following code to free memory on window restore:

SDL_GL_DeleteContext glDeleteProgram glDeleteShader glDeleteBuffers glDeleteTextures

\$\endgroup\$
  • \$\begingroup\$ Marking this as the answer given that it works and there haven't been any other responses thus far. \$\endgroup\$ – nathanburns Oct 25 '17 at 15:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.