memononen / nanovg

Antialiased 2D vector drawing library on top of OpenGL for UI and visualizations.
zlib License
5.19k stars 778 forks source link

Multi-context and multithread problems with FreeType #610

Open mulle-nat opened 3 years ago

mulle-nat commented 3 years ago

Nanovg uses a static FT_Library variable, which it initializes with FT_Init_FreeType during fons__tt_init which runs for each nanovg context being created. As soon as you are creating two nanovg contexts (and then destroying them), you will run into problems, as FT_Init_FreeType and FT_Done_FreeType don't reference count:

static FT_Library ftLibrary;

int fons__tt_init(FONScontext *context)
{
    FT_Error ftError;
    FONS_NOTUSED(context);
    ftError = FT_Init_FreeType(&ftLibrary);
    return ftError == 0;
}

int fons__tt_done(FONScontext *context)
{
    FT_Error ftError;
    FONS_NOTUSED(context);
    ftError = FT_Done_FreeType(ftLibrary);
    return ftError == 0;
}

The FreeType dox suggest replacing with FT_New_Library and FT_Done_Library. These don't use atomics for reference counting though and are therefore inherently not thread safe. So it's not a perfect solution, but solves the two context problem for a single thread app.

nanovg should either reference count (and skip) superflous FT_Init_FreeType and FT_Done_FreeType attempts or should embed the FT_Library static in the FONScontext. That would create duplicate fonts though.

Locking during nanovg context creation/deletion and font loading would still be needed for multi-threaded apps. I don't think locking would be a good addition to nanovg itself though.

I will have to fix this for my project, but would like to get some feedback.

mulle-nat commented 3 years ago

I have implemented FT_Library as a member of FONScontext. Not sure if this solves the multithread problems completely, but it should solve the multi-context problem. I will probably make a pull request, when this has been used a bit more.