Вызов SDL "IMG_Load" второй раз приводит к "EXC_BAD_ACCESS (code = EXC_I386_GPFLT)"

Im работает на MacOS 10.12, используя Xcode 8.3.3 с SDL2, установленным через Homebrew в качестве Dylib.

Ниже приведен некоторый слегка измененный пример кода от ленивого foo.

Я просто добавил вторую текстуру gTexture2 и функцию loadMedia2, чтобы иметь возможность воспроизвести проблему. Во второй раз IMG_Load выполняется сбой со следующим сообщением:

EXC_BAD_ACCESS (code=EXC_I386_GPFLT)

Поиск того, как решить проблему с "Общей защитой", также не помог мне дальше, авария, похоже, происходит внутри SDL. Вероятно, я действительно неправильно понимаю здесь что-то, что приводит к этой проблеме и действительно приветствует любую помощь.

введите описание изображения здесь

Неудивительно, что это не всегда сбой, только около 2 из 3 раз.

Сбой, кажется, происходит внутри SDL_AllocFormat_REAL ():

введите описание изображения здесь

Вот пример кода.

/*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;
}   

Маленькое обновление:

  • Я пробовал это на окнах, там все работает отлично. Поэтому я думаю, что проблема связана с MacOs.

  • Я уже пытался переустановить все библиотеки.

  • Я использую С++ 14.

Решение

Ну, это всего лишь половина решения, более обходное решение .

Благодаря @Sahib Yar он указал, чтобы попытаться поместить изображения в один каталог. Что решает проблему.

Но я думаю, что это действительно странно, вы должны иметь возможность загружать ресурсы из разных каталогов или, по крайней мере, в подкаталог.

Последний вопрос

Теперь мне действительно понравилось бы объяснение, почему мы не можем загружать изображения из нескольких каталогов с помощью SDL на MacOS. Это просто ошибка, известная вещь или я совершил большую ошибку?

Ответ 1

Кажется, что вы не уничтожаете текстуру2, которая не нужна.

SDL_DestroyTexture( gTexture );
SDL_DestroyTexture( gTexture2 );

gTexture = NULL;
gTexture2 = NULL;

В этот учебник lazyfoo упоминается, что

В нашей функции очистки мы должны помнить о том, чтобы освободить нашу текстуры с использованием SDL_DestroyTexture.

Изменить 1:

Попробуйте поместить все ваши изображения в один и тот же каталог.

Изменить 2:

Он не связан с каталогом в MacOS. этот учебник, похоже, что компилятор делает некоторую оптимизацию с std::string path как std::string - mutable. Попробуйте очистить объект std::string path в конце функции, чтобы очистить всю память, зарезервированную его объектами.

добавьте эту строку.

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

Ваша проблема - висячий указатель. EXC_BAD_ACCESS - это стонали CPU, что вы обращаетесь к несуществующей памяти или памяти, которая находится за пределами вашей области прав доступа. Причиной является отсутствие восстановления объекта, который вызывает раннее освобождение, а затем перезаписывается. В это время (которое может быть отложено) указатель укажет на мусор, чье разумирование вызывает бросок EXC_BAD_ACCESS.

Изменить 3:

Это не связано с SDL2. После Googling я обнаружил, что в Xcode все в конечном итоге упаковано в один отдельный каталог. Я нашел несколько вопросов относительно этого. Это может быть связано с ссылкой на папку и группами. На мой взгляд, это может быть что-то связанное с синими папками. Если это так, вы можете запросить этот ответ и использовать соответственно для SDL.