Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling SDL "IMG_Load" a second time results in a "EXC_BAD_ACCESS (code=EXC_I386_GPFLT)"

Tags:

c++

sdl-2

Im running this on MacOS 10.12 using Xcode 8.3.3 with SDL2 installed via Homebrew as Dylibs.

Below is some slightly modified sample code from lazy foo.

I just added a second texture gTexture2 and the function loadMedia2 to be able to reproduce the issue. The second time IMG_Load is executed it crashes with the following message:

EXC_BAD_ACCESS (code=EXC_I386_GPFLT)

Searching on how to solve a "General Protection Fault" problem did also not get me further, the crash seems to happen inside SDL. I probably really misunderstand here something that leads to this issue and would really welcome any help.

enter image description here

The really confusing thing is, it does not crash always, only about 2 of 3 times.

The crash seem to happen inside SDL_AllocFormat_REAL ():

enter image description here

Here is the code sample.

/*This source code copyrighted by Lazy Foo' Productions (2004-2015)
 and may not be redistributed without written permission.*/

//Using SDL, SDL_image, standard IO, and strings
#include <SDL.h>
#include <SDL_image.h>
#include <stdio.h>
#include <string>

//Screen dimension constants
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;

//Starts up SDL and creates window
bool init();

//Loads media
bool loadMedia();

//Frees media and shuts down SDL
void close();

//Loads individual image as texture
SDL_Texture* loadTexture( std::string path );

//The window we'll be rendering to
SDL_Window* gWindow = NULL;

//The window renderer
SDL_Renderer* gRenderer = NULL;

//Current displayed texture
SDL_Texture* gTexture = NULL;
SDL_Texture* gTexture2 = NULL;


bool init()
{
    //Initialization flag
    bool success = true;

    //Initialize SDL
    if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
    {
        printf( "SDL could not initialize! SDL Error: %s\n", SDL_GetError() );
        success = false;
    }
    else
    {
        //Set texture filtering to linear
        if( !SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, "1" ) )
        {
            printf( "Warning: Linear texture filtering not enabled!" );
        }

        //Create window
        gWindow = SDL_CreateWindow( "SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );
        if( gWindow == NULL )
        {
            printf( "Window could not be created! SDL Error: %s\n", SDL_GetError() );
            success = false;
        }
        else
        {
            //Create renderer for window
            gRenderer = SDL_CreateRenderer( gWindow, -1, SDL_RENDERER_ACCELERATED );
            if( gRenderer == NULL )
            {
                printf( "Renderer could not be created! SDL Error: %s\n", SDL_GetError() );
                success = false;
            }
            else
            {
                //Initialize renderer color
                SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );

                //Initialize PNG loading
                int imgFlags = IMG_INIT_PNG;
                if( !( IMG_Init( imgFlags ) & imgFlags ) )
                {
                    printf( "SDL_image could not initialize! SDL_image Error: %s\n", IMG_GetError() );
                    success = false;
                }
            }
        }
    }

    return success;
}

bool loadMedia()
{
    //Loading success flag
    bool success = true;

    //Load PNG texture
    gTexture = loadTexture( "../assets/player.png" );
    if( gTexture == NULL )
    {
        printf( "Failed to load texture image!\n" );
        success = false;
    }

    return success;
}

bool loadMedia2()
{
    //Loading success flag
    bool success = true;

    //Load PNG texture
    gTexture2 = loadTexture( "../assets/scene_main/background.png" );
    if( gTexture == NULL )
    {
        printf( "Failed to load texture image!\n" );
        success = false;
    }

    return success;
}

void close()
{
    //Free loaded image
    SDL_DestroyTexture( gTexture );
    SDL_DestroyTexture( gTexture2 );
    gTexture = NULL;
    gTexture2 = NULL;        

    //Destroy window
    SDL_DestroyRenderer( gRenderer );
    SDL_DestroyWindow( gWindow );
    gWindow = NULL;
    gRenderer = NULL;

    //Quit SDL subsystems
    IMG_Quit();
    SDL_Quit();
}

SDL_Texture* loadTexture( std::string path )
{
    //The final texture
    SDL_Texture* newTexture = NULL;

    //Load image at specified path
    SDL_Surface* loadedSurface = IMG_Load( path.c_str() );
    if( loadedSurface == NULL )
    {
        printf( "Unable to load image %s! SDL_image Error: %s\n", path.c_str(), IMG_GetError() );
    }
    else
    {
        //Create texture from surface pixels
        newTexture = SDL_CreateTextureFromSurface( gRenderer, loadedSurface );
        if( newTexture == NULL )
        {
            printf( "Unable to create texture from %s! SDL Error: %s\n", path.c_str(), SDL_GetError() );
        }

        //Get rid of old loaded surface
        SDL_FreeSurface( loadedSurface );
    }

    return newTexture;
}

int main( int argc, char* args[] )
{
    //Start up SDL and create window
    if( !init() )
    {
        printf( "Failed to initialize!\n" );
    }
    else
    {
        //Load media
        if( !loadMedia() || !loadMedia2() )
        {
            printf( "Failed to load media!\n" );
        }
        else
        {   
            //Main loop flag
            bool quit = false;

            //Event handler
            SDL_Event e;

            //While application is running
            while( !quit )
            {
                //Handle events on queue
                while( SDL_PollEvent( &e ) != 0 )
                {
                    //User requests quit
                    if( e.type == SDL_QUIT )
                    {
                        quit = true;
                    }
                }

                //Clear screen
                SDL_RenderClear( gRenderer );

                //Render texture to screen
                SDL_RenderCopy( gRenderer, gTexture, NULL, NULL );

                //Update screen
                SDL_RenderPresent( gRenderer );
            }
        }
    }

    //Free resources and close SDL
    close();

    return 0;
}   

Little Update:

  • I've tried it on windows, there it runs completely fine. So I guess the issue is related to MacOs.

  • I already tried to reinstall all libraries.

  • I'm using C++14.

The solution

Well its just half of a solution its more a workaround.

Thanks to @Sahib Yar he pointed out to try to put the images in the same directory. Which resolves the issue.

But I think this is really weird, you should be able to load resources from different directories or at least subdirectory.

The final question

Now I would really love an explanation why we can't load images from multiple directories using SDL on MacOS. Is that just a bug, known thing or did I make a big mistake?

like image 813
Mario Avatar asked Aug 17 '17 11:08

Mario


1 Answers

It seems that you are not destroying texture2 that is not needed.

SDL_DestroyTexture( gTexture );
SDL_DestroyTexture( gTexture2 );

gTexture = NULL;
gTexture2 = NULL;

In this lazyfoo tutorial, it is mentioned that

In our clean up function, we have to remember to deallocate our textures using SDL_DestroyTexture.

Edit 1:

Try to put all your images in the same directory.

Edit 2:

It is not related to directory in MacOS From this tutorial, it seems like compiler is doing some optimization with std::string path as the std::string is mutable. Try to clear the std::string path object at end of function to clear up all the memory reserved by its objects.

add this line.

std::string().swap(path);

Your issue is a dangling pointer. EXC_BAD_ACCESS is the CPU moaning that you are addressing non-existent memory or memory which is outside of your access rights area. The cause is a lack of reatainment of an object which causes early deallocation and then is overwritten. At which time (which may be delayed), the pointer will point to garbage whose dereference causes an EXC_BAD_ACCESS to be thrown.

Edit 3:

It is not something related to SDL2. After Googling, I have found that in Xcode, everything is eventually packed into 1 single directory. I have found multiple questions regarding this. It may be something related to folder reference and groups. To my guess it could be something related to blue folders. If this is the case you can consult this answer and use accordingly for SDL.

like image 128
Sahib Yar Avatar answered Oct 27 '22 21:10

Sahib Yar