12

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?

Mario
  • 3,339
  • 2
  • 22
  • 41
  • SDL_image uses [ImageIO](https://developer.apple.com/library/content/documentation/GraphicsImaging/Conceptual/ImageIOGuide/imageio_intro/ikpg_intro.html) on MacOS for loading images, so this probably explains the different behavior to Windows. – ssbssa Aug 20 '17 at 16:50
  • loadImage2 has a wrong check. Other than that this code seems well enough. Perhaps a miscompiled SDL or incompatibility between the SDL compile and ImageIO / OSX libraries? – dascandy Aug 21 '17 at 13:00

1 Answers1

8

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.

Sahib Yar
  • 1,030
  • 11
  • 29
  • Its completely true that I forgot destroying the texture, but it has nothing to do with this issue. I've updated my sample code but this is not the solution. – Mario Aug 21 '17 at 10:47
  • have you tried placing both the images in the same directory ? – Sahib Yar Aug 21 '17 at 11:53
  • What the actual f**k. That seems to be the issue, would mind to please explain that behaviour? – Mario Aug 21 '17 at 11:58
  • @Mario have you tried the the code from the updated answer yet ? – Sahib Yar Aug 22 '17 at 14:51
  • Im sorry I was not able to yet, but I give you an updated tomorrow. – Mario Aug 22 '17 at 14:52
  • @Mario have checked the code, is there any I can assist ? – Sahib Yar Aug 25 '17 at 11:44
  • Clearing `std::string path` unfortunately does not resolve the issue either. When the images are in different directories the application still crashes sometimes. Again not always. – Mario Aug 25 '17 at 13:17
  • I really think this has something todo with imageIO and the directories because why would it otherwise work when the images are in the same directory? – Mario Aug 25 '17 at 13:20
  • @Mario You can take help from [this answer](https://stackoverflow.com/a/16589247/2949645). – Sahib Yar Aug 25 '17 at 15:21
  • Copying the resources on build into same directory where the executable is placed does also not resolve the issue. – Mario Aug 26 '17 at 11:14
  • @Mario Please check the updated answer. Sorry for not being able to help you accurately as I haven't work on mac. – Sahib Yar Aug 26 '17 at 11:56