Sign in to follow this  

SDL_TTF Access Violation on functions that involve text

Recommended Posts

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 [url="https://icculus.org/physfs/"]PhysicsFS[/url]):

[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.

Edited by Kercyn

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites


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?

Edited by Kercyn

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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 ??.

Edited by Kercyn

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

[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.

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

As of now, the engine never unloads resources, so TTF_CloseFont shouldn't be called until it exits. The only place where a font is loaded is the LoadFont function, the important part of which is in the first post. The font wrapper object when the font is to be used is the same as when it was loaded (same text, same pointer address, same size). As for the debug symbols, I'm new so bear with me. I build the project using the /DEBUG option, do I need to build SDL and FreeType separately myself with /DEBUG in order to generate their symbols?

 

EDIT: I built SDL with symbols and I got this stack. Apparently, the RWops pointer is bad. I tried building FreeType too but then I remembered that libfreetype-6.dll came with SDL so I can't really change it. Thank you all very very much for your help, I'll see if I can finally figure it out and hopefully won't come back with more questions.

Share this post


Link to post
Share on other sites

I took a look at the loading code from your original post, and noticed that you're loading the font data into a stack-based vector, and then wrapping it in an SDL stream object using SDL_RWFromMem. However the documentation for that method states that the buffer you pass to it "is not copied by the RWops; the pointer you provide must remain valid until you close the stream." So your code is probably crashing because it's trying to access a stack variable that has long since gone out of scope. You'll need to find a way to store the font data more permanently, perhaps inside your BTTFFont wrapper.

Share this post


Link to post
Share on other sites

You're absolutely right. I built SDL_TTF with debug symbols as well and I saw that the exception is happening when at some point it tries to access the data of a RWops pointer (which contains invalid data). I had no idea why this was happening, since, as far as I understood it, the RWops data had been loaded and stored and there was no reason why it would still try to access them.

 

[attachment=34365:bliblikia6.png]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this