From 1c7bbaea89f2b3771d7cce1bcca044d465b64da5 Mon Sep 17 00:00:00 2001 From: Eduardo Bart Date: Mon, 18 Jun 2012 05:13:52 -0300 Subject: [PATCH] Fix memory leaks * Fix recursive reference memory leak in UIWidget * Make Event/ScheduledEvent memory-leak safe * Fix exit crashs by freeing graphics resources before destroying GL context * Add many asserts to avoid any leak regression --- src/framework/CMakeLists.txt | 3 ++ src/framework/application.cpp | 32 +++++++------ src/framework/application.h | 3 ++ src/framework/core/configmanager.cpp | 5 ++ src/framework/core/configmanager.h | 1 + src/framework/core/eventdispatcher.cpp | 15 +++++- src/framework/core/eventdispatcher.h | 23 ++++++++-- src/framework/core/modulemanager.cpp | 6 +++ src/framework/core/modulemanager.h | 2 + src/framework/graphics/declarations.h | 2 + src/framework/graphics/fontmanager.cpp | 4 +- src/framework/graphics/fontmanager.h | 4 +- src/framework/graphics/framebuffer.cpp | 14 ++---- src/framework/graphics/framebuffer.h | 7 ++- src/framework/graphics/framebuffermanager.cpp | 44 ++++++++++++++++++ src/framework/graphics/framebuffermanager.h | 46 +++++++++++++++++++ src/framework/graphics/graphics.cpp | 14 ++++-- src/framework/graphics/graphics.h | 4 +- src/framework/graphics/hardwarebuffer.cpp | 43 +++++++++++++++++ src/framework/graphics/hardwarebuffer.h | 11 +---- src/framework/graphics/painterogl2.cpp | 6 --- src/framework/graphics/painterogl2.h | 1 - src/framework/graphics/shader.cpp | 7 ++- src/framework/graphics/shaderprogram.cpp | 7 ++- src/framework/graphics/texture.cpp | 5 +- src/framework/graphics/texturemanager.cpp | 22 ++++++++- src/framework/graphics/texturemanager.h | 9 +++- src/framework/luascript/luainterface.cpp | 8 ++++ src/framework/luascript/luainterface.h | 2 + src/framework/luascript/luaobject.cpp | 3 ++ src/framework/net/connection.cpp | 2 + src/framework/ui/uimanager.cpp | 20 ++++---- src/framework/ui/uimanager.h | 2 + src/framework/ui/uiwidget.cpp | 1 + src/main.cpp | 1 + src/otclient/core/creature.cpp | 8 ++-- src/otclient/core/declarations.h | 1 + src/otclient/core/mapview.cpp | 13 ++++-- src/otclient/core/mapview.h | 1 + src/otclient/core/shadermanager.cpp | 1 + src/otclient/core/thingtype.h | 2 - src/otclient/otclient.cpp | 2 + 42 files changed, 326 insertions(+), 81 deletions(-) create mode 100644 src/framework/graphics/framebuffermanager.cpp create mode 100644 src/framework/graphics/framebuffermanager.h create mode 100644 src/framework/graphics/hardwarebuffer.cpp diff --git a/src/framework/CMakeLists.txt b/src/framework/CMakeLists.txt index c65aeed3..9eccf9c0 100644 --- a/src/framework/CMakeLists.txt +++ b/src/framework/CMakeLists.txt @@ -213,9 +213,12 @@ SET(framework_SOURCES ${framework_SOURCES} ${CMAKE_CURRENT_LIST_DIR}/graphics/fontmanager.h ${CMAKE_CURRENT_LIST_DIR}/graphics/framebuffer.cpp ${CMAKE_CURRENT_LIST_DIR}/graphics/framebuffer.h + ${CMAKE_CURRENT_LIST_DIR}/graphics/framebuffermanager.cpp + ${CMAKE_CURRENT_LIST_DIR}/graphics/framebuffermanager.h ${CMAKE_CURRENT_LIST_DIR}/graphics/glutil.h ${CMAKE_CURRENT_LIST_DIR}/graphics/graphics.cpp ${CMAKE_CURRENT_LIST_DIR}/graphics/graphics.h + ${CMAKE_CURRENT_LIST_DIR}/graphics/hardwarebuffer.cpp ${CMAKE_CURRENT_LIST_DIR}/graphics/hardwarebuffer.h ${CMAKE_CURRENT_LIST_DIR}/graphics/image.cpp ${CMAKE_CURRENT_LIST_DIR}/graphics/image.h diff --git a/src/framework/application.cpp b/src/framework/application.cpp index fa24d336..1e086a55 100644 --- a/src/framework/application.cpp +++ b/src/framework/application.cpp @@ -116,11 +116,8 @@ void Application::init(const std::vector& args) m_initialized = true; } -void Application::terminate() +void Application::deinit() { - if(!m_initialized) - return; - g_lua.callGlobalField("g_app", "onTerminate"); // hide the window because there is no render anymore @@ -128,6 +125,7 @@ void Application::terminate() // run modules unload events g_modules.unloadModules(); + g_modules.clear(); // release remaining lua object references g_lua.collectGarbage(); @@ -135,19 +133,20 @@ void Application::terminate() // poll remaining events poll(); + // destroy any remaining widget + g_ui.terminate(); +} + +void Application::terminate() +{ + assert(m_initialized); + // terminate network Connection::terminate(); - // terminate graphics - g_ui.terminate(); - g_window.terminate(); - // terminate sound g_sounds.terminate(); - // flush remaining dispatcher events - g_eventDispatcher.flush(); - // save configurations g_configs.save(); @@ -157,15 +156,22 @@ void Application::terminate() // terminate script environment g_lua.terminate(); + // flush remaining dispatcher events + g_eventDispatcher.flush(); + + // terminate graphics m_foreground = nullptr; + g_graphics.terminate(); + g_window.terminate(); g_logger.info("Application ended successfully."); + + m_terminated = true; } void Application::run() { - if(!m_initialized) - return; + assert(m_initialized); m_stopping = false; m_running = true; diff --git a/src/framework/application.h b/src/framework/application.h index d04a0746..ebae01b3 100644 --- a/src/framework/application.h +++ b/src/framework/application.h @@ -39,6 +39,7 @@ public: virtual void init(const std::vector& args); virtual void registerLuaFunctions(); + virtual void deinit(); virtual void terminate(); virtual void run(); virtual void exit(); @@ -53,6 +54,7 @@ public: bool isRunning() { return m_running; } bool isStopping() { return m_stopping; } + bool isTermianted() { return m_terminated; } bool isOnInputEvent() { return m_onInputEvent; } const std::string& getName() { return m_appName; } const std::string& getVersion() { return m_appVersion; } @@ -76,6 +78,7 @@ protected: Boolean m_initialized; Boolean m_running; Boolean m_stopping; + Boolean m_terminated; Boolean m_onInputEvent; Boolean m_mustRepaint; AdaptativeFrameCounter m_backgroundFrameCounter; diff --git a/src/framework/core/configmanager.cpp b/src/framework/core/configmanager.cpp index dbeba9de..c62f9936 100644 --- a/src/framework/core/configmanager.cpp +++ b/src/framework/core/configmanager.cpp @@ -57,6 +57,11 @@ bool ConfigManager::save() return m_confsDoc->save(m_fileName); } +void ConfigManager::clear() +{ + m_confsDoc->clear(); +} + void ConfigManager::set(const std::string& key, const std::string& value) { if(key == "") { diff --git a/src/framework/core/configmanager.h b/src/framework/core/configmanager.h index 0e4ee758..12667ce0 100644 --- a/src/framework/core/configmanager.h +++ b/src/framework/core/configmanager.h @@ -32,6 +32,7 @@ public: ConfigManager(); bool load(const std::string& file); bool save(); + void clear(); void set(const std::string& key, const std::string& value); void setList(const std::string& key, const std::vector& list); diff --git a/src/framework/core/eventdispatcher.cpp b/src/framework/core/eventdispatcher.cpp index c6a8689b..99c37616 100644 --- a/src/framework/core/eventdispatcher.cpp +++ b/src/framework/core/eventdispatcher.cpp @@ -32,8 +32,12 @@ void EventDispatcher::flush() while(!m_eventList.empty()) poll(); - while(!m_scheduledEventList.empty()) + while(!m_scheduledEventList.empty()) { + ScheduledEventPtr scheduledEvent = m_scheduledEventList.top(); + scheduledEvent->cancel(); m_scheduledEventList.pop(); + } + m_disabled = true; } void EventDispatcher::poll() @@ -77,6 +81,9 @@ void EventDispatcher::poll() ScheduledEventPtr EventDispatcher::scheduleEvent(const std::function& callback, int delay) { + if(m_disabled) + return ScheduledEventPtr(new ScheduledEvent(nullptr, delay, 1)); + assert(delay >= 0); ScheduledEventPtr scheduledEvent(new ScheduledEvent(callback, delay, 1)); m_scheduledEventList.push(scheduledEvent); @@ -85,6 +92,9 @@ ScheduledEventPtr EventDispatcher::scheduleEvent(const std::function& ca ScheduledEventPtr EventDispatcher::cycleEvent(const std::function& callback, int delay) { + if(m_disabled) + return ScheduledEventPtr(new ScheduledEvent(nullptr, delay, 0)); + assert(delay > 0); ScheduledEventPtr scheduledEvent(new ScheduledEvent(callback, delay, 0)); m_scheduledEventList.push(scheduledEvent); @@ -93,6 +103,9 @@ ScheduledEventPtr EventDispatcher::cycleEvent(const std::function& callb EventPtr EventDispatcher::addEvent(const std::function& callback, bool pushFront) { + if(m_disabled) + return EventPtr(new Event(nullptr)); + EventPtr event(new Event(callback)); // front pushing is a way to execute an event before others if(pushFront) { diff --git a/src/framework/core/eventdispatcher.h b/src/framework/core/eventdispatcher.h index a553239d..5a4ec2d2 100644 --- a/src/framework/core/eventdispatcher.h +++ b/src/framework/core/eventdispatcher.h @@ -31,14 +31,24 @@ class Event : public LuaObject { public: Event(const std::function& callback) : m_callback(callback), m_canceled(false), m_executed(false) { } + virtual ~Event() { + // assure that we lost callback refs + assert(m_callback == nullptr); + } virtual void execute() { if(!m_canceled && !m_executed && m_callback) { m_callback(); m_executed = true; } + + // reset callback to free object refs + m_callback = nullptr; + } + void cancel() { + m_canceled = true; + m_callback = nullptr; } - void cancel() { m_canceled = true; } bool isCanceled() { return m_canceled; } bool isExecuted() { return m_executed; } @@ -63,17 +73,21 @@ public: if(!m_canceled && m_callback && (m_maxCycles == 0 || m_cyclesExecuted < m_maxCycles)) { m_callback(); m_executed = true; + // callback may be used in the next cycle + } else { + // reset callback to free object refs + m_callback = nullptr; } m_cyclesExecuted++; } bool nextCycle() { - if(m_canceled) - return false; - if(m_maxCycles == 0 || m_cyclesExecuted < m_maxCycles) { + if(m_callback && !m_canceled && (m_maxCycles == 0 || m_cyclesExecuted < m_maxCycles)) { m_ticks += m_delay; return true; } + // reset callback to free object refs + m_callback = nullptr; return false; } @@ -109,6 +123,7 @@ public: private: std::list m_eventList; int m_pollEventsSize; + Boolean m_disabled; std::priority_queue, lessScheduledEvent> m_scheduledEventList; }; diff --git a/src/framework/core/modulemanager.cpp b/src/framework/core/modulemanager.cpp index 1be2c47b..54671d2d 100644 --- a/src/framework/core/modulemanager.cpp +++ b/src/framework/core/modulemanager.cpp @@ -28,6 +28,12 @@ ModuleManager g_modules; +void ModuleManager::clear() +{ + m_modules.clear(); + m_autoLoadModules.clear(); +} + void ModuleManager::discoverModules() { // remove modules that are not loaded diff --git a/src/framework/core/modulemanager.h b/src/framework/core/modulemanager.h index bada770a..4d908948 100644 --- a/src/framework/core/modulemanager.h +++ b/src/framework/core/modulemanager.h @@ -28,6 +28,8 @@ class ModuleManager { public: + void clear(); + void discoverModulesPath(); void discoverModules(); void autoLoadModules(int maxPriority); diff --git a/src/framework/graphics/declarations.h b/src/framework/graphics/declarations.h index b21be575..dda811d3 100644 --- a/src/framework/graphics/declarations.h +++ b/src/framework/graphics/declarations.h @@ -27,11 +27,13 @@ #include "glutil.h" class Texture; +class TextureManager; class Image; class AnimatedTexture; class BitmapFont; class CachedText; class FrameBuffer; +class FrameBufferManager; class Shader; class ShaderProgram; class PainterShaderProgram; diff --git a/src/framework/graphics/fontmanager.cpp b/src/framework/graphics/fontmanager.cpp index f711f19a..01dbb01d 100644 --- a/src/framework/graphics/fontmanager.cpp +++ b/src/framework/graphics/fontmanager.cpp @@ -32,10 +32,10 @@ FontManager::FontManager() m_defaultFont = BitmapFontPtr(new BitmapFont("emptyfont")); } -void FontManager::releaseFonts() +void FontManager::terminate() { - m_defaultFont.reset(); m_fonts.clear(); + m_defaultFont = nullptr; } bool FontManager::importFont(std::string fontFile) diff --git a/src/framework/graphics/fontmanager.h b/src/framework/graphics/fontmanager.h index 3dacc501..7a47725f 100644 --- a/src/framework/graphics/fontmanager.h +++ b/src/framework/graphics/fontmanager.h @@ -30,10 +30,8 @@ class FontManager public: FontManager(); - /// Release fonts references, thus making possible to destruct them - void releaseFonts(); + void terminate(); - /// Import a font from .otfont file bool importFont(std::string fontFile); bool fontExists(const std::string& fontName); diff --git a/src/framework/graphics/framebuffer.cpp b/src/framework/graphics/framebuffer.cpp index 88517f87..23f93f70 100644 --- a/src/framework/graphics/framebuffer.cpp +++ b/src/framework/graphics/framebuffer.cpp @@ -23,7 +23,9 @@ #include "framebuffer.h" #include "graphics.h" #include "texture.h" + #include +#include uint FrameBuffer::boundFbo = 0; @@ -32,12 +34,6 @@ FrameBuffer::FrameBuffer() internalCreate(); } -FrameBuffer::FrameBuffer(const Size& size) -{ - internalCreate(); - resize(size); -} - void FrameBuffer::internalCreate() { m_prevBoundFbo = 0; @@ -51,14 +47,14 @@ void FrameBuffer::internalCreate() FrameBuffer::~FrameBuffer() { - if(m_fbo != 0) + assert(!g_app->isTermianted()); + if(g_graphics.ok() && m_fbo != 0) glDeleteFramebuffers(1, &m_fbo); } void FrameBuffer::resize(const Size& size) { - if(!size.isValid()) - return; + assert(size.isValid()); if(m_texture && m_texture->getSize() == size) return; diff --git a/src/framework/graphics/framebuffer.h b/src/framework/graphics/framebuffer.h index 6e79a5a6..35b8f002 100644 --- a/src/framework/graphics/framebuffer.h +++ b/src/framework/graphics/framebuffer.h @@ -28,9 +28,12 @@ class FrameBuffer { -public: +protected: FrameBuffer(); - FrameBuffer(const Size& size); + + friend class FrameBufferManager; + +public: virtual ~FrameBuffer(); void resize(const Size& size); diff --git a/src/framework/graphics/framebuffermanager.cpp b/src/framework/graphics/framebuffermanager.cpp new file mode 100644 index 00000000..a82136fa --- /dev/null +++ b/src/framework/graphics/framebuffermanager.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2010-2012 OTClient + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#include "framebuffermanager.h" + +FrameBufferManager g_framebuffers; + +void FrameBufferManager::init() +{ + m_temporaryFramebuffer = FrameBufferPtr(new FrameBuffer()); +} + +void FrameBufferManager::terminate() +{ + m_framebuffers.clear(); + m_temporaryFramebuffer = nullptr; +} + +FrameBufferPtr FrameBufferManager::createFrameBuffer() +{ + FrameBufferPtr fbo = FrameBufferPtr(new FrameBuffer()); + m_framebuffers.push_back(fbo); + return fbo; +} diff --git a/src/framework/graphics/framebuffermanager.h b/src/framework/graphics/framebuffermanager.h new file mode 100644 index 00000000..2d2c7365 --- /dev/null +++ b/src/framework/graphics/framebuffermanager.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2010-2012 OTClient + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#ifndef FRAMEBUFFERMANAGER_H +#define FRAMEBUFFERMANAGER_H + +#include "framebuffer.h" + +class FrameBufferManager +{ +public: + void init(); + void terminate(); + void clear(); + + FrameBufferPtr createFrameBuffer(); + const FrameBufferPtr& getTemporaryFrameBuffer() { return m_temporaryFramebuffer; } + +protected: + FrameBufferPtr m_temporaryFramebuffer; + std::vector m_framebuffers; +}; + +extern FrameBufferManager g_framebuffers; + +#endif diff --git a/src/framework/graphics/graphics.cpp b/src/framework/graphics/graphics.cpp index eb3ce17c..cc80d2f0 100644 --- a/src/framework/graphics/graphics.cpp +++ b/src/framework/graphics/graphics.cpp @@ -33,6 +33,8 @@ #include #include +#include "texturemanager.h" +#include "framebuffermanager.h" #include Graphics g_graphics; @@ -88,13 +90,19 @@ void Graphics::init() m_alphaBits = 0; glGetIntegerv(GL_ALPHA_BITS, &m_alphaBits); + m_ok = true; + selectPainterEngine(m_prefferedPainterEngine); - m_emptyTexture = TexturePtr(new Texture); + + g_textures.init(); + g_framebuffers.init(); } void Graphics::terminate() { - g_fonts.releaseFonts(); + g_fonts.terminate(); + g_framebuffers.terminate(); + g_textures.terminate(); #ifdef PAINTER_OGL2 if(g_painterOGL2) { @@ -112,7 +120,7 @@ void Graphics::terminate() g_painter = nullptr; - m_emptyTexture.reset(); + m_ok = false; } bool Graphics::parseOption(const std::string& option) diff --git a/src/framework/graphics/graphics.h b/src/framework/graphics/graphics.h index 25a1ea76..d1dbe11b 100644 --- a/src/framework/graphics/graphics.h +++ b/src/framework/graphics/graphics.h @@ -51,13 +51,13 @@ public: int getMaxTextureSize() { return m_maxTextureSize; } const Size& getViewportSize() { return m_viewportSize; } - TexturePtr& getEmptyTexture() { return m_emptyTexture; } std::string getVendor() { return (const char*)glGetString(GL_VENDOR); } std::string getRenderer() { return (const char*)glGetString(GL_RENDERER); } std::string getVersion() { return (const char*)glGetString(GL_VERSION); } std::string getExtensions() { return (const char*)glGetString(GL_EXTENSIONS); } + bool ok() { return m_ok; } bool canUseDrawArrays(); bool canUseShaders(); bool canUseFBO(); @@ -72,10 +72,10 @@ public: private: Size m_viewportSize; - TexturePtr m_emptyTexture; int m_maxTextureSize; int m_alphaBits; + Boolean m_ok; Boolean m_useDrawArrays; Boolean m_useFBO; Boolean m_useHardwareBuffers; diff --git a/src/framework/graphics/hardwarebuffer.cpp b/src/framework/graphics/hardwarebuffer.cpp new file mode 100644 index 00000000..db902dd2 --- /dev/null +++ b/src/framework/graphics/hardwarebuffer.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2010-2012 OTClient + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "hardwarebuffer.h" +#include "graphics.h" + +#include +#include + +HardwareBuffer::HardwareBuffer(Type type) +{ + m_type = type; + m_id = 0; + glGenBuffers(1, &m_id); + if(!m_id) + g_logger.fatal("Unable to create hardware buffer."); +} + +HardwareBuffer::~HardwareBuffer() +{ + assert(!g_app->isTermianted()); + if(g_graphics.ok()) + glDeleteBuffers(1, &m_id); +} diff --git a/src/framework/graphics/hardwarebuffer.h b/src/framework/graphics/hardwarebuffer.h index 475abeb6..6565d969 100644 --- a/src/framework/graphics/hardwarebuffer.h +++ b/src/framework/graphics/hardwarebuffer.h @@ -40,15 +40,8 @@ public: DynamicDraw = GL_DYNAMIC_DRAW }; - HardwareBuffer(Type type) { - m_type = type; - m_id = 0; - glGenBuffers(1, &m_id); - assert(m_id != 0); - } - ~HardwareBuffer() { - glDeleteBuffers(1, &m_id); - } + HardwareBuffer(Type type); + ~HardwareBuffer(); void bind() { glBindBuffer(m_type, m_id); } static void unbind(Type type) { glBindBuffer(type, 0); } diff --git a/src/framework/graphics/painterogl2.cpp b/src/framework/graphics/painterogl2.cpp index 48ab55ed..9f6eb574 100644 --- a/src/framework/graphics/painterogl2.cpp +++ b/src/framework/graphics/painterogl2.cpp @@ -48,12 +48,6 @@ PainterOGL2::PainterOGL2() PainterShaderProgram::release(); } -PainterOGL2::~PainterOGL2() -{ - m_drawTexturedProgram = nullptr; - m_drawSolidColorProgram = nullptr; -} - void PainterOGL2::bind() { Painter::bind(); diff --git a/src/framework/graphics/painterogl2.h b/src/framework/graphics/painterogl2.h index 8358d2bd..281e9e95 100644 --- a/src/framework/graphics/painterogl2.h +++ b/src/framework/graphics/painterogl2.h @@ -36,7 +36,6 @@ class PainterOGL2 : public Painter { public: PainterOGL2(); - ~PainterOGL2(); void bind(); void unbind(); diff --git a/src/framework/graphics/shader.cpp b/src/framework/graphics/shader.cpp index 20cf5b65..d860856c 100644 --- a/src/framework/graphics/shader.cpp +++ b/src/framework/graphics/shader.cpp @@ -21,6 +21,9 @@ */ #include "shader.h" +#include "graphics.h" + +#include #include Shader::Shader(Shader::ShaderType shaderType) @@ -41,7 +44,9 @@ Shader::Shader(Shader::ShaderType shaderType) Shader::~Shader() { - glDeleteShader(m_shaderId); + assert(!g_app->isTermianted()); + if(g_graphics.ok()) + glDeleteShader(m_shaderId); } bool Shader::compileSourceCode(const std::string& sourceCode) diff --git a/src/framework/graphics/shaderprogram.cpp b/src/framework/graphics/shaderprogram.cpp index 24618ea7..2e6430b0 100644 --- a/src/framework/graphics/shaderprogram.cpp +++ b/src/framework/graphics/shaderprogram.cpp @@ -21,6 +21,9 @@ */ #include "shaderprogram.h" +#include "graphics.h" + +#include GLuint ShaderProgram::m_currentProgram = 0; @@ -35,7 +38,9 @@ ShaderProgram::ShaderProgram() ShaderProgram::~ShaderProgram() { - glDeleteProgram(m_programId); + assert(!g_app->isTermianted()); + if(g_graphics.ok()) + glDeleteProgram(m_programId); } bool ShaderProgram::addShader(const ShaderPtr& shader) { diff --git a/src/framework/graphics/texture.cpp b/src/framework/graphics/texture.cpp index 9e8a460b..cdd3e515 100644 --- a/src/framework/graphics/texture.cpp +++ b/src/framework/graphics/texture.cpp @@ -25,6 +25,8 @@ #include "framebuffer.h" #include "image.h" +#include + Texture::Texture() { m_id = 0; @@ -77,8 +79,9 @@ Texture::Texture(const ImagePtr& image, bool buildMipmaps) Texture::~Texture() { + assert(!g_app->isTermianted()); // free texture from gl memory - if(m_id > 0) + if(g_graphics.ok() && m_id != 0) glDeleteTextures(1, &m_id); } diff --git a/src/framework/graphics/texturemanager.cpp b/src/framework/graphics/texturemanager.cpp index 631461c5..d48b6c75 100644 --- a/src/framework/graphics/texturemanager.cpp +++ b/src/framework/graphics/texturemanager.cpp @@ -30,6 +30,26 @@ TextureManager g_textures; +void TextureManager::init() +{ + m_emptyTexture = TexturePtr(new Texture); +} + +void TextureManager::terminate() +{ +#ifndef NDEBUG + // check for leaks + int refs = 0; + for(const auto& it : m_textures) + if(it.second.use_count() > 1) + refs++; + if(refs > 0) + g_logger.debug(stdext::format("%d textures references left", refs)); +#endif + m_textures.clear(); + m_emptyTexture = nullptr; +} + TexturePtr TextureManager::getTexture(const std::string& fileName) { TexturePtr texture; @@ -59,7 +79,7 @@ TexturePtr TextureManager::getTexture(const std::string& fileName) texture = loadPNG(fin); } catch(stdext::exception& e) { g_logger.error(stdext::format("unable to load texture '%s': %s", fileName, e.what())); - texture = g_graphics.getEmptyTexture(); + texture = g_textures.getEmptyTexture(); } if(texture) diff --git a/src/framework/graphics/texturemanager.h b/src/framework/graphics/texturemanager.h index 71898d13..268d7e34 100644 --- a/src/framework/graphics/texturemanager.h +++ b/src/framework/graphics/texturemanager.h @@ -28,12 +28,17 @@ class TextureManager { public: - TexturePtr getTexture(const std::string& fileName); + void init(); + void terminate(); - static TexturePtr loadPNG(std::stringstream& file); + TexturePtr getTexture(const std::string& fileName); + const TexturePtr& getEmptyTexture() { return m_emptyTexture; } private: + TexturePtr loadPNG(std::stringstream& file); + std::unordered_map m_textures; + TexturePtr m_emptyTexture; }; extern TextureManager g_textures; diff --git a/src/framework/luascript/luainterface.cpp b/src/framework/luascript/luainterface.cpp index 0fc1c31b..0a6281be 100644 --- a/src/framework/luascript/luainterface.cpp +++ b/src/framework/luascript/luainterface.cpp @@ -35,6 +35,8 @@ LuaInterface::LuaInterface() L = nullptr; m_cppCallbackDepth = 0; m_weakTableRef = 0; + m_totalObjRefs = 0; + m_totalFuncRefs = 0; } LuaInterface::~LuaInterface() @@ -64,6 +66,8 @@ void LuaInterface::terminate() { // close lua state, it will release all objects closeLuaState(); + assert(m_totalFuncRefs == 0); + assert(m_totalObjRefs == 0); } void LuaInterface::registerSingletonClass(const std::string& className) @@ -280,6 +284,7 @@ int LuaInterface::luaObjectCollectEvent(LuaInterface* lua) // resets pointer to decrease object use count objPtr->reset(); + g_lua.m_totalObjRefs--; return 0; } @@ -603,6 +608,7 @@ int LuaInterface::luaCollectCppFunction(lua_State* L) auto funcPtr = static_cast(g_lua.popUserdata()); assert(funcPtr); funcPtr->reset(); + g_lua.m_totalFuncRefs--; return 0; } @@ -1033,6 +1039,7 @@ void LuaInterface::pushObject(const LuaObjectPtr& obj) { // fills a new userdata with a new LuaObjectPtr pointer new(newUserdata(sizeof(LuaObjectPtr))) LuaObjectPtr(obj); + m_totalObjRefs++; // set the userdata metatable getGlobal(stdext::format("%s_mt", obj->getClassName())); @@ -1050,6 +1057,7 @@ void LuaInterface::pushCppFunction(const LuaCppFunction& func) { // create a pointer to func (this pointer will hold the function existence) new(newUserdata(sizeof(LuaCppFunctionPtr))) LuaCppFunctionPtr(new LuaCppFunction(func)); + m_totalFuncRefs++; // sets the userdata __gc metamethod, needed to free the function pointer when it gets collected newTable(); diff --git a/src/framework/luascript/luainterface.h b/src/framework/luascript/luainterface.h index d1fc4100..d0178276 100644 --- a/src/framework/luascript/luainterface.h +++ b/src/framework/luascript/luainterface.h @@ -329,6 +329,8 @@ private: lua_State* L; int m_weakTableRef; int m_cppCallbackDepth; + int m_totalObjRefs; + int m_totalFuncRefs; }; extern LuaInterface g_lua; diff --git a/src/framework/luascript/luaobject.cpp b/src/framework/luascript/luaobject.cpp index da442833..dcf208b3 100644 --- a/src/framework/luascript/luaobject.cpp +++ b/src/framework/luascript/luaobject.cpp @@ -23,12 +23,15 @@ #include "luaobject.h" #include "luainterface.h" +#include + LuaObject::LuaObject() : m_fieldsTableRef(-1) { } LuaObject::~LuaObject() { + assert(!g_app->isTermianted()); releaseLuaFieldsTable(); } diff --git a/src/framework/net/connection.cpp b/src/framework/net/connection.cpp index 4144b7d9..79977046 100644 --- a/src/framework/net/connection.cpp +++ b/src/framework/net/connection.cpp @@ -22,6 +22,7 @@ #include "connection.h" +#include #include #include @@ -40,6 +41,7 @@ Connection::Connection() : Connection::~Connection() { + assert(!g_app->isTermianted()); close(); } diff --git a/src/framework/ui/uimanager.cpp b/src/framework/ui/uimanager.cpp index 92d1fff9..9d8b21cb 100644 --- a/src/framework/ui/uimanager.cpp +++ b/src/framework/ui/uimanager.cpp @@ -50,6 +50,9 @@ void UIManager::terminate() m_draggingWidget = nullptr; m_hoveredWidget = nullptr; m_pressedWidget = nullptr; + m_styles.clear(); + m_destroyedWidgets.clear(); + m_checkEvent = nullptr; } void UIManager::render(Fw::DrawPane drawPane) @@ -266,22 +269,19 @@ void UIManager::onWidgetDestroy(const UIWidgetPtr& widget) if(m_draggingWidget == widget) updateDraggingWidget(nullptr); -#ifdef DEBUG - static UIWidgetList destroyedWidgets; - static ScheduledEventPtr checkEvent; - - if(widget == m_rootWidget) +#ifndef DEBUG + if(widget == m_rootWidget || !m_rootWidget) return; - destroyedWidgets.push_back(widget); + m_destroyedWidgets.push_back(widget); - if(checkEvent && !checkEvent->isExecuted()) + if(m_checkEvent && !m_checkEvent->isExecuted()) return; - checkEvent = g_eventDispatcher.scheduleEvent([] { + m_checkEvent = g_eventDispatcher.scheduleEvent([] { g_lua.collectGarbage(); - UIWidgetList backupList = destroyedWidgets; - destroyedWidgets.clear(); + UIWidgetList backupList = m_destroyedWidgets; + m_destroyedWidgets.clear(); g_eventDispatcher.scheduleEvent([backupList] { g_lua.collectGarbage(); for(const UIWidgetPtr& widget : backupList) { diff --git a/src/framework/ui/uimanager.h b/src/framework/ui/uimanager.h index f03902d5..255b43f2 100644 --- a/src/framework/ui/uimanager.h +++ b/src/framework/ui/uimanager.h @@ -82,6 +82,8 @@ private: Boolean m_hoverUpdateScheduled; Boolean m_drawDebugBoxes; std::unordered_map m_styles; + UIWidgetList m_destroyedWidgets; + ScheduledEventPtr m_checkEvent; }; extern UIManager g_ui; diff --git a/src/framework/ui/uiwidget.cpp b/src/framework/ui/uiwidget.cpp index 538e5f49..ac9a66d3 100644 --- a/src/framework/ui/uiwidget.cpp +++ b/src/framework/ui/uiwidget.cpp @@ -46,6 +46,7 @@ UIWidget::UIWidget() UIWidget::~UIWidget() { + assert(!g_app->isTermianted()); #ifdef DEBUG if(!m_destroyed) g_logger.warning(stdext::format("widget '%s' was not explicitly destroyed", m_id)); diff --git a/src/main.cpp b/src/main.cpp index 3dd67b25..0cd538ed 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -28,6 +28,7 @@ int main(int argc, const char* argv[]) OTClient app; app.init(args); app.run(); + app.deinit(); app.terminate(); return 0; } diff --git a/src/otclient/core/creature.cpp b/src/otclient/core/creature.cpp index 05f24e86..1022ba4f 100644 --- a/src/otclient/core/creature.cpp +++ b/src/otclient/core/creature.cpp @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include "spritemanager.h" Creature::Creature() : Thing() @@ -161,10 +161,8 @@ void Creature::internalDrawOutfit(const Point& dest, float scaleFactor, bool ani void Creature::drawOutfit(const Rect& destRect, bool resize) { if(g_graphics.canUseFBO()) { - static FrameBufferPtr outfitBuffer; - if(!outfitBuffer) - outfitBuffer = FrameBufferPtr(new FrameBuffer(Size(2*Otc::TILE_PIXELS, 2*Otc::TILE_PIXELS))); - + const FrameBufferPtr& outfitBuffer = g_framebuffers.getTemporaryFrameBuffer(); + outfitBuffer->resize(Size(2*Otc::TILE_PIXELS, 2*Otc::TILE_PIXELS)); outfitBuffer->bind(); g_painter->setAlphaWriting(true); g_painter->clear(Color::alpha); diff --git a/src/otclient/core/declarations.h b/src/otclient/core/declarations.h index db5482da..3c03eb3e 100644 --- a/src/otclient/core/declarations.h +++ b/src/otclient/core/declarations.h @@ -62,5 +62,6 @@ typedef std::shared_ptr StaticTextPtr; typedef std::shared_ptr ItemShaderPtr; typedef std::vector ThingList; +typedef std::vector ThingTypeList; #endif diff --git a/src/otclient/core/mapview.cpp b/src/otclient/core/mapview.cpp index 73975a8f..d3121b9e 100644 --- a/src/otclient/core/mapview.cpp +++ b/src/otclient/core/mapview.cpp @@ -22,8 +22,6 @@ #include "mapview.h" -#include -#include #include "creature.h" #include "map.h" #include "tile.h" @@ -31,7 +29,11 @@ #include "animatedtext.h" #include "missile.h" #include "shadermanager.h" + +#include +#include #include +#include MapView::MapView() { @@ -41,12 +43,17 @@ MapView::MapView() m_cachedLastVisibleFloor = 7; m_optimizedSize = Size(Otc::AWARE_X_TILES, Otc::AWARE_Y_TILES) * Otc::TILE_PIXELS; - m_framebuffer = FrameBufferPtr(new FrameBuffer()); + m_framebuffer = g_framebuffers.createFrameBuffer(); setVisibleDimension(Size(15, 11)); m_shader = g_shaders.getDefaultMapShader(); } +MapView::~MapView() +{ + assert(!g_app->isTermianted()); +} + void MapView::draw(const Rect& rect) { // update visible tiles cache when needed diff --git a/src/otclient/core/mapview.h b/src/otclient/core/mapview.h index a6e9be1a..50936b05 100644 --- a/src/otclient/core/mapview.h +++ b/src/otclient/core/mapview.h @@ -50,6 +50,7 @@ public: }; MapView(); + ~MapView(); void draw(const Rect& rect); private: diff --git a/src/otclient/core/shadermanager.cpp b/src/otclient/core/shadermanager.cpp index 8aacdb57..5f06cee0 100644 --- a/src/otclient/core/shadermanager.cpp +++ b/src/otclient/core/shadermanager.cpp @@ -44,6 +44,7 @@ void ShaderManager::terminate() { m_defaultItemShader = nullptr; m_defaultMapShader = nullptr; + m_shaders.clear(); } PainterShaderProgramPtr ShaderManager::createShader(const std::string& name) diff --git a/src/otclient/core/thingtype.h b/src/otclient/core/thingtype.h index 3864904e..acb6f2a4 100644 --- a/src/otclient/core/thingtype.h +++ b/src/otclient/core/thingtype.h @@ -156,6 +156,4 @@ private: friend class ThingsType; }; -typedef std::vector ThingTypeList; - #endif diff --git a/src/otclient/otclient.cpp b/src/otclient/otclient.cpp index f2153c0f..e1833ec9 100644 --- a/src/otclient/otclient.cpp +++ b/src/otclient/otclient.cpp @@ -117,5 +117,7 @@ void OTClient::init(const std::vector& args) void OTClient::terminate() { g_shaders.terminate(); + g_map.clean(); + g_thingsType.unload(); Application::terminate(); }