SDL_TTF Access Violation on functions that involve text

Started by
11 comments, last by Kercyn 7 years, 3 months ago

Hello forum people.

I'm trying to implement text rendering for my game engine and I've been running into a particular problem.

I have a resource manager whose job is to load and store the resources needed for the game. When a new map loads, the manager parses the map and loads any fonts needed. The part of the function that does the font loading is this (using PhysicsFS):

[spoiler]


// Consider this the "loading" function.
 
BPhysFSFilePtr File(Path);

PHYSFS_sint64 FontFileSize = MPhysFSManager::Instance().fileLength(File);

std::vector<PHYSFS_sint64> FontData(FontFileSize);
MPhysFSManager::Instance().read<PHYSFS_sint64>(File, FontData, 1, FontFileSize);

RWopsPtr RWops(SDL_RWFromMem(FontData.data(), FontFileSize), SDL_FreeRW);
            
Fonts.emplace(ID, BTTFFont(std::unique_ptr<TTF_Font, decltype(&TTF_CloseFont)>(TTF_OpenFontRW(RWops.get(), 0, Size), TTF_CloseFont), ID, Size));

[/spoiler]

BTTFFont is a wrapper class that overloads operator TTF_Font* and RWopsPtr is an alias for std::unique_ptr<SDL_RWops, decltype(&SDL_FreeRW)>.

The loaded fonts are then used to create various OpenGL textures using different texts. The function that does that is this one:

[spoiler]


// Consider this the "texturing" function.
 
using SurfacePtr = std::unique_ptr<SDL_Surface, decltype(&SDL_FreeSurface)>;

auto& Font = MResourceManager::Instance().RequestFont(FontID);
const auto& FontData = MResourceManager::Instance().RequestFontData(ID);

SurfacePtr Surface(nullptr, nullptr);

if (FontData.Rendering == EFontRendering::Solid)
{
    Surface = SurfacePtr(TTF_RenderUTF8_Solid(Font, Text.c_str(), FontData.Color), SDL_FreeSurface);
}
else if (FontData.Rendering == EFontRendering::Blended)
{
    Surface = SurfacePtr(TTF_RenderUTF8_Blended_Wrapped(Font, Text.c_str(), FontData.Color, WrapLength), SDL_FreeSurface);
}
    
MResourceManager::Instance().CreateTextureFromSurface(ID, Surface.get());

[/spoiler]

The problem is that I'm getting an access violation exception as soon as either TTF_Render function is called. TTF_Init has been successfully called.

What I've checked for:

  • Text.c_str() is null, it's not
  • Font is null, it's not
  • Call TTF_GetStyle and TTF_SetStyle using Font, they both work just fine
  • Call TTF_SizeUTF8 using Font and Text.c_str(), access violation
  • Call TTF_SizeUTF8 using Font and a hardcoded const char* passed directly as argument, access violation
  • Call TTF_SizeUTF8 using Font and a const char* variable with some text, access violation

I then added these lines to the font loading function:


using SurfacePtr = std::unique_ptr<SDL_Surface, decltype(&SDL_FreeSurface)>;
auto Surface = SurfacePtr(TTF_RenderUTF8_Blended(Fonts.at(ID), "Use the Arrow keys to move", SDL_Color()), SDL_FreeSurface);

This worked for the first call of the texturing function but throws an access violation on the second call (I currently only have 2 text boxes that call this). If I add this line


Surface = SurfacePtr(TTF_RenderUTF8_Blended(Fonts.at(ID), "Press and hold the Space bar to jump", SDL_Color()), SDL_FreeSurface);

then the game plays as it should and texts are rendered fine.

If, however, I change the text in the loading function, then it may throw an access violation. By "may" I mean that if I completely change it, it throws. If I write "Use the Arrow keyssssssss to move" instead of "Use the Arrow keys to move", it works fine. I don't know how much I need to change the text in order to cause an exception. I've also noticed that if I use Solid instead of Blended rendering in the loading function, it causes an access violation as well (the particular text boxes I have use Blended rendering).

Any ideas on what the problem might be?

Thank you.

Advertisement

Sounds like you're overrunning a buffer somewhere. The code you've posted is not useful for diagnosing the issue, since it's just a bunch of calls involving functions and objects that we know nothing about. Are you using an IDE with a debugger? Walking through the loading process and watching pointers get set and handed out may reveal something amiss.

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.
http://www.gamedev.net/blog/355/entry-2254834-oh-noes-my-code-is-teh-crash/

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]



Sounds like you're overrunning a buffer somewhere. The code you've posted is not useful for diagnosing the issue, since it's just a bunch of calls involving functions and objects that we know nothing about. Are you using an IDE with a debugger? Walking through the loading process and watching pointers get set and handed out may reveal something amiss.

You're right about the code, it's just that there isn't much more to post. PhysFS loads the font file to memory, then I use SDL_RWFromMem and TTF_OpenFontRW to load the font. TTF_Font is opaque, so I'm not really sure what I should be seeing other than if it's null or not. That's why I tried using other TTF functions with the font pointer, to see that it works correctly even when it's not null. What else do you feel is missing?


Thanks, I had completely forgotten about that.

Crash message

Exception thrown at 0x741D87B8 (msvcrt.dll) in PipantasGameEngine.exe: 0xC0000005: Access violation reading location 0x09815338.

This happens after I step over line 37

[attachment=34293:chinko1.png]

Call stack before the crash

[attachment=34300:chinko3.png]

Call stack after the crash

[attachment=34294:chinko2.png]

Is there anything more I should add?

I can't know for sure, but you could be trying to use or access a deleted object. After the exception, I would inspect the memory around 0x09815338 (or whatever location the exception indicates) and see if you can't determine what used to live there. Even if it's deleted there could be remnants of the last object that could provide clues.

If you put a breakpoint on the crashing line and inspect the values you're passing to it, are they all sane values?

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

I can't know for sure, but you could be trying to use or access a deleted object. After the exception, I would inspect the memory around 0x09815338 (or whatever location the exception indicates) and see if you can't determine what used to live there. Even if it's deleted there could be remnants of the last object that could provide clues.

I had added a data breakpoint at the address of the memory that caused the crash to see what's going on. At some point, ucrtbased.dll changed the data at that memory location to 0xcb. After a few step overs, it changed a larger chunk of that memory to 0xcb, then vcruntime140d.dll changed the first chunk to 0x00, then the second chunk to 0x00. By the time it got in the offending code, that memory segment's contents were ??. Not null, not random trash but ??. I guess that's memory my program no longer has access to? I couldn't find info on what those ?? are. I tried reproducing this today but the crash address kept changing, so I can't really catch it. This is the first time the address changes without me restarting the IDE; all the previous times the address stayed the same for as long as VS was running.

If you put a breakpoint on the crashing line and inspect the values you're passing to it, are they all sane values?

As far as I can tell, yes. I had tried assigning the wrapped values to raw pointers and use those instead, but the same thing happened. It crashes even if I use a const char* literal directly.

EDIT: The address doesn't change this time so I managed to attach a breakpoint. After vcrun140d.dll changes the chunk to 0x00, ucrtbased.dll comes again and changes a part of it to 0xdd, then on the next step over it changes everything to 0xdd. At some point it changes everything back to 0x00, then to 0xcd, then to 0x00, then to 0xdd and after some time to ??.

What were the call stacks when those addresses changed? At least one of those values, 0xDD, is a "magic value" set by your compiler to indicate deleted memory. If you enable Microsoft Symbol Servers, I believe symbols for vcruntime140d.dll should be available, which will help isolate what's being called.

[attachment=34342:bliblikia4.png]

This is the stack with symbols loaded. I've been trying time and again to isolate the memory of the crash, but it keeps changing every time I run the application, so I can't place a breakpoint because I don't know where to place it.

Symbols for libfreetype-6.dll and SDL2.dll would also certainly help, but if I had to venture a guess, I'd say that your font is being released (TTF_CloseFont) by your resource manager before it should be. Possibly a reference counting bug?

I would place breakpoints, logging, etc., everywhere in your codebase where fonts are initialized and release to make sure there's nothing happening unexpectedly.

This topic is closed to new replies.

Advertisement