0
votes

I am developing SDL2 application which needs to have multiple windows on multiple monitors. And I am getting access violation when drawing string with SDL_ttf library. I should also mention that Application is opening windows in separate threads and is working ok if there is no SDL_ttf used. I get this for exception when using SDL_ttf:

Unhandled exception at 0x0F2BC191 (SDL2.dll) in SDLMultipleWindows.exe: 0xC0000005: Access violation writing location 0x0100000C.

And access violation is happening in this function:

bool loadFromRenderedText( std::string textureText, SDL_Color textColor )
{
    SDL_Surface* textSurface = TTF_RenderText_Solid( gFont, textureText.c_str(), textColor );
    SDL_Texture * mTexture = NULL;
    int w, h;
    if( textSurface != NULL )
    {
        mTexture = SDL_CreateTextureFromSurface( renderer, textSurface );
        w = textSurface->w;
        h = textSurface->h;
        SDL_FreeSurface( textSurface );
    }
    else
    {
        printf( "Unable to render text surface! SDL_ttf Error: %s\n", TTF_GetError() );
    }

    SDL_Rect renderQuad = { 250, 300, w, h };
    int result = SDL_RenderCopyEx( renderer, mTexture, NULL, &renderQuad, 0.0, NULL, SDL_FLIP_NONE );
    OutputDebugString(SDL_GetError());
    return true;
}

Exception happens on SDL_CreateTextureFromSurface(renderer, textSurface);

This is stack trace from Visual studio:

SDL2.dll!SDL_malloc_REAL(unsigned int bytes) Line 4206  C
SDL2.dll!SDL_calloc_REAL(unsigned int n_elements, unsigned int elem_size) Line 4406 C
SDL2.dll!SDL_CreateRGBSurface_REAL(unsigned int flags, int width, int height, int depth, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int Amask) Line 53    C
SDL2.dll!SDL_ConvertSurface_REAL(SDL_Surface * surface, const SDL_PixelFormat * format, unsigned int flags) Line 840    C
SDL2.dll!SDL_CreateTextureFromSurface_REAL(SDL_Renderer * renderer, SDL_Surface * surface) Line 536 C
SDL2.dll!SDL_CreateTextureFromSurface(SDL_Renderer * a, SDL_Surface * b) Line 342   C
SDLMultipleWindows.exe! loadFromRenderedText(std::basic_string<char,std::char_traits<char>,std::allocator<char> > textureText, SDL_Color textColor) Line 162    C++

Am I doing something wrong or SDL_ttf or SDL2 cannot work on multiple threads?

Is there another way to draw string in SDL2?

Thanks!

Edit:

Adding part of existing code:

ClientWindows::ClientWindows(void)
{
    SDL_Init(SDL_INIT_EVERYTHING);
    IMG_Init(IMG_INIT_PNG);
    TTF_Init();
}

Tread function: void ClientWindows::WindowThread(int i) { AppWindow* rWindow = new AppWindow(i * 1024, 0); Windows.push_back(rWindow); rWindow->InitScreen(); }

Start graphics function:

void ClientWindows::StartGraphics(int number)
{
    for(int i= 0; i<number; i++)
    {
        std::thread* wTread = new std::thread(&ClientWindows::WindowThread,this , i);
        Threads.push_back(wTread);
    }
.
.
.

Client window Constructor:

AppWindow::AppWindow(int x, int y)
{
    quit = false;
    SCREEN_WIDTH = 1024;
    SCREEN_HEIGHT = 768;
    imagePositionX = 50;
    imagePositionY = 50;
    speed_x = 10;
    speed_y = 10;
    moveX = 10;
    moveY = 10;
    std::ostringstream convert;
    convert << "Graphics";
    convert << x;
    string name = convert.str();

    window = SDL_CreateWindow(name.c_str(), x,
    y, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN | SDL_WINDOW_BORDERLESS | SDL_WINDOW_OPENGL);
    if (window == nullptr){
        std::cout << SDL_GetError() << std::endl;
    }
    opengl3_context = SDL_GL_CreateContext(window);
    SDL_assert(opengl3_context);
    mt.lock();
    renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
    if (renderer == nullptr){
        std::cout << SDL_GetError() << std::endl;
    }
    mt.unlock();
    background = nullptr,image = nullptr;
    background = SDLLoadImage("../res/Image1024x768.png");
    image = SDLLoadImage("../res/Image1024Classic.png");
    animeImage = SDLLoadImage("../res/32_80x80.png");
    gFont = TTF_OpenFont("../res/sample.ttf", 28);
}

Client window startGraphics function:

void AppWindow::InitScreen(void)
{
    Clear();
    Render();
    Present();

    //Init fps
    countedFrames = 0;
    fpsTimer.start();

    //For tracking if we want to quit
    Uint32 frameRate = 0;
    while (!quit)
    {
        if (fpsTimer.getTicks() > frameRate + 15)
        {
            frameRate = fpsTimer.getTicks();
            Clear();
            Render();
            Present();
        }
        SDL_Delay(5);
    }
}

Function in question:

bool AppWindow::loadFromRenderedText(std::string textureText, SDL_Color textColor)
{
    SDL_Surface* textSurface = TTF_RenderText_Solid( gFont, textureText.c_str(), textColor );
    SDL_Texture * mTexture = NULL;
    int w, h;
    if( textSurface != NULL )
    {
        mTexture = SDL_CreateTextureFromSurface( renderer, textSurface );
        w = textSurface->w;
        h = textSurface->h;
        SDL_FreeSurface( textSurface );
    }
    else
    {
        printf( "Unable to render text surface! SDL_ttf Error: %s\n", TTF_GetError() );
    }

    SDL_Rect renderQuad = { 250, 300, w, h };
    int result = SDL_RenderCopyEx( renderer, mTexture, NULL, &renderQuad, 0.0, NULL, SDL_FLIP_NONE );
    OutputDebugString(SDL_GetError());
    return true;
}
1

1 Answers

0
votes

You can't use SDL2 functions from other threads than the one in which the rendering context was created, SDL2 guarantees no thread safety for drawing functions.

If I recall correctly, the only thread safe part of SDL2 is pushing custom events to the event queue.

So I'm guessing the AccessViolation occurs because you're trying to use the renderering context from another thread than from on the one which it was created on.