diff --git a/libgag/include/EventListener.h b/libgag/include/EventListener.h index f7909e74..84fe39d8 100644 --- a/libgag/include/EventListener.h +++ b/libgag/include/EventListener.h @@ -41,10 +41,11 @@ class EventListener { static std::condition_variable startedCond; static std::mutex doneMutex; static std::condition_variable doneCond; - static std::mutex renderMutex; + static std::recursive_mutex renderMutex; void setPainter(std::function f); void addPainter(const std::string& name, std::function f); void removePainter(const std::string& name); + static void ensureContext(); void paint(); private: std::function painter; diff --git a/libgag/src/EventListener.cpp b/libgag/src/EventListener.cpp index 505a5cdb..63c07592 100644 --- a/libgag/src/EventListener.cpp +++ b/libgag/src/EventListener.cpp @@ -26,21 +26,24 @@ std::mutex EventListener::startMutex; std::condition_variable EventListener::startedCond; std::mutex EventListener::doneMutex; std::condition_variable EventListener::doneCond; -std::mutex EventListener::renderMutex; +std::recursive_mutex EventListener::renderMutex; #define SIZE_MOVE_TIMER_ID 1 #if defined(_WIN32) || defined(__MINGW32__) || defined(__MINGW64__) #define WINDOWS_OR_MINGW 1 #endif +// The depth variable is used to return early when indirect recursion happens EventListener::EventListener(GraphicContext* gfx) : painter(nullptr), depth(0) { + assert(gfx); this->gfx = gfx; el = this; done = false; quit = true; } + void EventListener::stop() { quit = true; @@ -49,25 +52,38 @@ void EventListener::stop() doneCond.wait(lock); } } + EventListener::~EventListener() { } + +// Create an OpenGL context or set existing context as current on this thread. +void EventListener::ensureContext() +{ + instance()->gfx->createGLContext(); +} + +// deprecated; use addPainter/removePainter instead. void EventListener::setPainter(std::function f) { - std::unique_lock lock(renderMutex); + std::unique_lock lock(renderMutex); painter = f; } +/* name should be the class name that the function is called on + * f is the function to call to draw the screen. + */ void EventListener::addPainter(const std::string& name, std::function f) { - std::unique_lock lock(renderMutex); + std::unique_lock lock(renderMutex); painters.insert(std::pair >(name, f)); } +// Erase the latest painter added with the name `name` from the multimap void EventListener::removePainter(const std::string& name) { if (painters.empty()) assert("Tried to remove a painter when painters map is empty."); - std::unique_lock lock(renderMutex); + std::unique_lock lock(renderMutex); for (std::multimap >::reverse_iterator it = painters.rbegin(); it != painters.rend(); ++it) { if (it->first == name) @@ -79,6 +95,7 @@ void EventListener::removePainter(const std::string& name) } } } +// Draw all the registered painters in order void EventListener::paint() { depth++; @@ -86,7 +103,7 @@ void EventListener::paint() return; if (painters.size()) { - std::unique_lock lock(renderMutex); + std::unique_lock lock(renderMutex); gfx->createGLContext(); for (std::multimap >::iterator it = painters.begin(); it != painters.end(); ++it) { diff --git a/libgag/src/GUIBase.cpp b/libgag/src/GUIBase.cpp index 44057fc6..6443d64f 100644 --- a/libgag/src/GUIBase.cpp +++ b/libgag/src/GUIBase.cpp @@ -530,10 +530,7 @@ namespace GAGGUI dispatchEvents(&windowEvent); // draw - { - std::unique_lock lock(EventListener::instance()->renderMutex); - dispatchPaint(); - } + dispatchPaint(); // wait timer frameWaitTime=SDL_GetTicks()-frameStartTime; @@ -680,6 +677,8 @@ namespace GAGGUI void Screen::dispatchPaint(void) { assert(gfx); + std::unique_lock lock(EventListener::renderMutex); + EventListener::ensureContext(); gfx->setClipRect(); paint(); for (std::set::iterator it=widgets.begin(); it!=widgets.end(); ++it) diff --git a/libgag/src/GraphicContext.cpp b/libgag/src/GraphicContext.cpp index 379fe826..efb23c1e 100644 --- a/libgag/src/GraphicContext.cpp +++ b/libgag/src/GraphicContext.cpp @@ -349,6 +349,8 @@ namespace GAGCore #ifdef HAVE_OPENGL if (_gc->optionFlags & GraphicContext::USEGPU) { + std::unique_lock lock(EventListener::renderMutex); + EventListener::ensureContext(); glState.setTexture(texture); void *pixelsPtr; @@ -2061,7 +2063,7 @@ namespace GAGCore } SDL_Surface* GraphicContext::getOrCreateSurface(int w, int h, Uint32 flags) { - std::unique_lock lock(EventListener::instance()->renderMutex); + std::unique_lock lock(EventListener::renderMutex); if (flags & USEGPU) { if (sdlsurface) diff --git a/src/Engine.cpp b/src/Engine.cpp index 79c377a1..f146ea4f 100644 --- a/src/Engine.cpp +++ b/src/Engine.cpp @@ -507,13 +507,13 @@ int Engine::run(void) if (nextGuiStep == 0) { // we draw - static bool isPainterSet = false; - if (!isPainterSet) + if (!gui.isRegistered) { + std::unique_lock lock(EventListener::renderMutex); EventListener::instance()->addPainter("GameGUI", std::bind(&GameGUI::drawAll, &gui, gui.localTeamNo)); - isPainterSet = true; + gui.isRegistered = true; } - std::unique_lock lock(EventListener::instance()->renderMutex); + std::unique_lock lock(EventListener::renderMutex); globalContainer->gfx->createGLContext(); gui.drawAll(gui.localTeamNo); globalContainer->gfx->nextFrame(); diff --git a/src/GameGUI.cpp b/src/GameGUI.cpp index caac66b3..0048855d 100644 --- a/src/GameGUI.cpp +++ b/src/GameGUI.cpp @@ -184,7 +184,7 @@ GameGUI::GameGUI() 128, // width 128, //height Minimap::ShowFOW), // minimap mode - + isRegistered(false), ghostManager(game) { } @@ -4385,6 +4385,8 @@ void GameGUI::drawInGameScrollableText(void) void GameGUI::drawAll(int team) { + std::unique_lock lock(EventListener::renderMutex); + EventListener::ensureContext(); // draw the map Uint32 drawOptions = (drawHealthFoodBar ? Game::DRAW_HEALTH_FOOD_BAR : 0) | (drawPathLines ? Game::DRAW_PATH_LINE : 0) | diff --git a/src/GameGUI.h b/src/GameGUI.h index b43db896..167d83e9 100644 --- a/src/GameGUI.h +++ b/src/GameGUI.h @@ -201,6 +201,10 @@ class GameGUI bool hardPause; bool isRunning; bool notmenu; + + // isRegistered tracks whether this instance of GameGUI has been registered with EventListener. + bool isRegistered; + //! true if user close the glob2 window. bool exitGlobCompletely; //! true if the game needs to flush all outgoing orders and exit diff --git a/src/MapEdit.cpp b/src/MapEdit.cpp index d5ceab67..3566c237 100644 --- a/src/MapEdit.cpp +++ b/src/MapEdit.cpp @@ -1210,6 +1210,8 @@ bool MapEdit::save(const std::string filename, const std::string name) void MapEdit::draw(void) { + std::unique_lock lock(EventListener::renderMutex); + EventListener::ensureContext(); drawMap(0, 0, globalContainer->gfx->getW() - 0, globalContainer->gfx->getH(), true, true); drawMenu();