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
This commit is contained in:
Eduardo Bart 2012-06-18 05:13:52 -03:00
parent f650b0e5bb
commit 1c7bbaea89
42 changed files with 326 additions and 81 deletions

View File

@ -213,9 +213,12 @@ SET(framework_SOURCES ${framework_SOURCES}
${CMAKE_CURRENT_LIST_DIR}/graphics/fontmanager.h ${CMAKE_CURRENT_LIST_DIR}/graphics/fontmanager.h
${CMAKE_CURRENT_LIST_DIR}/graphics/framebuffer.cpp ${CMAKE_CURRENT_LIST_DIR}/graphics/framebuffer.cpp
${CMAKE_CURRENT_LIST_DIR}/graphics/framebuffer.h ${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/glutil.h
${CMAKE_CURRENT_LIST_DIR}/graphics/graphics.cpp ${CMAKE_CURRENT_LIST_DIR}/graphics/graphics.cpp
${CMAKE_CURRENT_LIST_DIR}/graphics/graphics.h ${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/hardwarebuffer.h
${CMAKE_CURRENT_LIST_DIR}/graphics/image.cpp ${CMAKE_CURRENT_LIST_DIR}/graphics/image.cpp
${CMAKE_CURRENT_LIST_DIR}/graphics/image.h ${CMAKE_CURRENT_LIST_DIR}/graphics/image.h

View File

@ -116,11 +116,8 @@ void Application::init(const std::vector<std::string>& args)
m_initialized = true; m_initialized = true;
} }
void Application::terminate() void Application::deinit()
{ {
if(!m_initialized)
return;
g_lua.callGlobalField("g_app", "onTerminate"); g_lua.callGlobalField("g_app", "onTerminate");
// hide the window because there is no render anymore // hide the window because there is no render anymore
@ -128,6 +125,7 @@ void Application::terminate()
// run modules unload events // run modules unload events
g_modules.unloadModules(); g_modules.unloadModules();
g_modules.clear();
// release remaining lua object references // release remaining lua object references
g_lua.collectGarbage(); g_lua.collectGarbage();
@ -135,19 +133,20 @@ void Application::terminate()
// poll remaining events // poll remaining events
poll(); poll();
// destroy any remaining widget
g_ui.terminate();
}
void Application::terminate()
{
assert(m_initialized);
// terminate network // terminate network
Connection::terminate(); Connection::terminate();
// terminate graphics
g_ui.terminate();
g_window.terminate();
// terminate sound // terminate sound
g_sounds.terminate(); g_sounds.terminate();
// flush remaining dispatcher events
g_eventDispatcher.flush();
// save configurations // save configurations
g_configs.save(); g_configs.save();
@ -157,15 +156,22 @@ void Application::terminate()
// terminate script environment // terminate script environment
g_lua.terminate(); g_lua.terminate();
// flush remaining dispatcher events
g_eventDispatcher.flush();
// terminate graphics
m_foreground = nullptr; m_foreground = nullptr;
g_graphics.terminate();
g_window.terminate();
g_logger.info("Application ended successfully."); g_logger.info("Application ended successfully.");
m_terminated = true;
} }
void Application::run() void Application::run()
{ {
if(!m_initialized) assert(m_initialized);
return;
m_stopping = false; m_stopping = false;
m_running = true; m_running = true;

View File

@ -39,6 +39,7 @@ public:
virtual void init(const std::vector<std::string>& args); virtual void init(const std::vector<std::string>& args);
virtual void registerLuaFunctions(); virtual void registerLuaFunctions();
virtual void deinit();
virtual void terminate(); virtual void terminate();
virtual void run(); virtual void run();
virtual void exit(); virtual void exit();
@ -53,6 +54,7 @@ public:
bool isRunning() { return m_running; } bool isRunning() { return m_running; }
bool isStopping() { return m_stopping; } bool isStopping() { return m_stopping; }
bool isTermianted() { return m_terminated; }
bool isOnInputEvent() { return m_onInputEvent; } bool isOnInputEvent() { return m_onInputEvent; }
const std::string& getName() { return m_appName; } const std::string& getName() { return m_appName; }
const std::string& getVersion() { return m_appVersion; } const std::string& getVersion() { return m_appVersion; }
@ -76,6 +78,7 @@ protected:
Boolean<false> m_initialized; Boolean<false> m_initialized;
Boolean<false> m_running; Boolean<false> m_running;
Boolean<false> m_stopping; Boolean<false> m_stopping;
Boolean<false> m_terminated;
Boolean<false> m_onInputEvent; Boolean<false> m_onInputEvent;
Boolean<false> m_mustRepaint; Boolean<false> m_mustRepaint;
AdaptativeFrameCounter m_backgroundFrameCounter; AdaptativeFrameCounter m_backgroundFrameCounter;

View File

@ -57,6 +57,11 @@ bool ConfigManager::save()
return m_confsDoc->save(m_fileName); return m_confsDoc->save(m_fileName);
} }
void ConfigManager::clear()
{
m_confsDoc->clear();
}
void ConfigManager::set(const std::string& key, const std::string& value) void ConfigManager::set(const std::string& key, const std::string& value)
{ {
if(key == "") { if(key == "") {

View File

@ -32,6 +32,7 @@ public:
ConfigManager(); ConfigManager();
bool load(const std::string& file); bool load(const std::string& file);
bool save(); bool save();
void clear();
void set(const std::string& key, const std::string& value); void set(const std::string& key, const std::string& value);
void setList(const std::string& key, const std::vector<std::string>& list); void setList(const std::string& key, const std::vector<std::string>& list);

View File

@ -32,8 +32,12 @@ void EventDispatcher::flush()
while(!m_eventList.empty()) while(!m_eventList.empty())
poll(); poll();
while(!m_scheduledEventList.empty()) while(!m_scheduledEventList.empty()) {
ScheduledEventPtr scheduledEvent = m_scheduledEventList.top();
scheduledEvent->cancel();
m_scheduledEventList.pop(); m_scheduledEventList.pop();
}
m_disabled = true;
} }
void EventDispatcher::poll() void EventDispatcher::poll()
@ -77,6 +81,9 @@ void EventDispatcher::poll()
ScheduledEventPtr EventDispatcher::scheduleEvent(const std::function<void()>& callback, int delay) ScheduledEventPtr EventDispatcher::scheduleEvent(const std::function<void()>& callback, int delay)
{ {
if(m_disabled)
return ScheduledEventPtr(new ScheduledEvent(nullptr, delay, 1));
assert(delay >= 0); assert(delay >= 0);
ScheduledEventPtr scheduledEvent(new ScheduledEvent(callback, delay, 1)); ScheduledEventPtr scheduledEvent(new ScheduledEvent(callback, delay, 1));
m_scheduledEventList.push(scheduledEvent); m_scheduledEventList.push(scheduledEvent);
@ -85,6 +92,9 @@ ScheduledEventPtr EventDispatcher::scheduleEvent(const std::function<void()>& ca
ScheduledEventPtr EventDispatcher::cycleEvent(const std::function<void()>& callback, int delay) ScheduledEventPtr EventDispatcher::cycleEvent(const std::function<void()>& callback, int delay)
{ {
if(m_disabled)
return ScheduledEventPtr(new ScheduledEvent(nullptr, delay, 0));
assert(delay > 0); assert(delay > 0);
ScheduledEventPtr scheduledEvent(new ScheduledEvent(callback, delay, 0)); ScheduledEventPtr scheduledEvent(new ScheduledEvent(callback, delay, 0));
m_scheduledEventList.push(scheduledEvent); m_scheduledEventList.push(scheduledEvent);
@ -93,6 +103,9 @@ ScheduledEventPtr EventDispatcher::cycleEvent(const std::function<void()>& callb
EventPtr EventDispatcher::addEvent(const std::function<void()>& callback, bool pushFront) EventPtr EventDispatcher::addEvent(const std::function<void()>& callback, bool pushFront)
{ {
if(m_disabled)
return EventPtr(new Event(nullptr));
EventPtr event(new Event(callback)); EventPtr event(new Event(callback));
// front pushing is a way to execute an event before others // front pushing is a way to execute an event before others
if(pushFront) { if(pushFront) {

View File

@ -31,14 +31,24 @@ class Event : public LuaObject
{ {
public: public:
Event(const std::function<void()>& callback) : m_callback(callback), m_canceled(false), m_executed(false) { } Event(const std::function<void()>& 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() { virtual void execute() {
if(!m_canceled && !m_executed && m_callback) { if(!m_canceled && !m_executed && m_callback) {
m_callback(); m_callback();
m_executed = true; 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 isCanceled() { return m_canceled; }
bool isExecuted() { return m_executed; } bool isExecuted() { return m_executed; }
@ -63,17 +73,21 @@ public:
if(!m_canceled && m_callback && (m_maxCycles == 0 || m_cyclesExecuted < m_maxCycles)) { if(!m_canceled && m_callback && (m_maxCycles == 0 || m_cyclesExecuted < m_maxCycles)) {
m_callback(); m_callback();
m_executed = true; m_executed = true;
// callback may be used in the next cycle
} else {
// reset callback to free object refs
m_callback = nullptr;
} }
m_cyclesExecuted++; m_cyclesExecuted++;
} }
bool nextCycle() { bool nextCycle() {
if(m_canceled) if(m_callback && !m_canceled && (m_maxCycles == 0 || m_cyclesExecuted < m_maxCycles)) {
return false;
if(m_maxCycles == 0 || m_cyclesExecuted < m_maxCycles) {
m_ticks += m_delay; m_ticks += m_delay;
return true; return true;
} }
// reset callback to free object refs
m_callback = nullptr;
return false; return false;
} }
@ -109,6 +123,7 @@ public:
private: private:
std::list<EventPtr> m_eventList; std::list<EventPtr> m_eventList;
int m_pollEventsSize; int m_pollEventsSize;
Boolean<false> m_disabled;
std::priority_queue<ScheduledEventPtr, std::vector<ScheduledEventPtr>, lessScheduledEvent> m_scheduledEventList; std::priority_queue<ScheduledEventPtr, std::vector<ScheduledEventPtr>, lessScheduledEvent> m_scheduledEventList;
}; };

View File

@ -28,6 +28,12 @@
ModuleManager g_modules; ModuleManager g_modules;
void ModuleManager::clear()
{
m_modules.clear();
m_autoLoadModules.clear();
}
void ModuleManager::discoverModules() void ModuleManager::discoverModules()
{ {
// remove modules that are not loaded // remove modules that are not loaded

View File

@ -28,6 +28,8 @@
class ModuleManager class ModuleManager
{ {
public: public:
void clear();
void discoverModulesPath(); void discoverModulesPath();
void discoverModules(); void discoverModules();
void autoLoadModules(int maxPriority); void autoLoadModules(int maxPriority);

View File

@ -27,11 +27,13 @@
#include "glutil.h" #include "glutil.h"
class Texture; class Texture;
class TextureManager;
class Image; class Image;
class AnimatedTexture; class AnimatedTexture;
class BitmapFont; class BitmapFont;
class CachedText; class CachedText;
class FrameBuffer; class FrameBuffer;
class FrameBufferManager;
class Shader; class Shader;
class ShaderProgram; class ShaderProgram;
class PainterShaderProgram; class PainterShaderProgram;

View File

@ -32,10 +32,10 @@ FontManager::FontManager()
m_defaultFont = BitmapFontPtr(new BitmapFont("emptyfont")); m_defaultFont = BitmapFontPtr(new BitmapFont("emptyfont"));
} }
void FontManager::releaseFonts() void FontManager::terminate()
{ {
m_defaultFont.reset();
m_fonts.clear(); m_fonts.clear();
m_defaultFont = nullptr;
} }
bool FontManager::importFont(std::string fontFile) bool FontManager::importFont(std::string fontFile)

View File

@ -30,10 +30,8 @@ class FontManager
public: public:
FontManager(); FontManager();
/// Release fonts references, thus making possible to destruct them void terminate();
void releaseFonts();
/// Import a font from .otfont file
bool importFont(std::string fontFile); bool importFont(std::string fontFile);
bool fontExists(const std::string& fontName); bool fontExists(const std::string& fontName);

View File

@ -23,7 +23,9 @@
#include "framebuffer.h" #include "framebuffer.h"
#include "graphics.h" #include "graphics.h"
#include "texture.h" #include "texture.h"
#include <framework/platform/platformwindow.h> #include <framework/platform/platformwindow.h>
#include <framework/application.h>
uint FrameBuffer::boundFbo = 0; uint FrameBuffer::boundFbo = 0;
@ -32,12 +34,6 @@ FrameBuffer::FrameBuffer()
internalCreate(); internalCreate();
} }
FrameBuffer::FrameBuffer(const Size& size)
{
internalCreate();
resize(size);
}
void FrameBuffer::internalCreate() void FrameBuffer::internalCreate()
{ {
m_prevBoundFbo = 0; m_prevBoundFbo = 0;
@ -51,14 +47,14 @@ void FrameBuffer::internalCreate()
FrameBuffer::~FrameBuffer() FrameBuffer::~FrameBuffer()
{ {
if(m_fbo != 0) assert(!g_app->isTermianted());
if(g_graphics.ok() && m_fbo != 0)
glDeleteFramebuffers(1, &m_fbo); glDeleteFramebuffers(1, &m_fbo);
} }
void FrameBuffer::resize(const Size& size) void FrameBuffer::resize(const Size& size)
{ {
if(!size.isValid()) assert(size.isValid());
return;
if(m_texture && m_texture->getSize() == size) if(m_texture && m_texture->getSize() == size)
return; return;

View File

@ -28,9 +28,12 @@
class FrameBuffer class FrameBuffer
{ {
public: protected:
FrameBuffer(); FrameBuffer();
FrameBuffer(const Size& size);
friend class FrameBufferManager;
public:
virtual ~FrameBuffer(); virtual ~FrameBuffer();
void resize(const Size& size); void resize(const Size& size);

View File

@ -0,0 +1,44 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/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;
}

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/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<FrameBufferPtr> m_framebuffers;
};
extern FrameBufferManager g_framebuffers;
#endif

View File

@ -33,6 +33,8 @@
#include <framework/graphics/graphics.h> #include <framework/graphics/graphics.h>
#include <framework/graphics/texture.h> #include <framework/graphics/texture.h>
#include "texturemanager.h"
#include "framebuffermanager.h"
#include <framework/platform/platformwindow.h> #include <framework/platform/platformwindow.h>
Graphics g_graphics; Graphics g_graphics;
@ -88,13 +90,19 @@ void Graphics::init()
m_alphaBits = 0; m_alphaBits = 0;
glGetIntegerv(GL_ALPHA_BITS, &m_alphaBits); glGetIntegerv(GL_ALPHA_BITS, &m_alphaBits);
m_ok = true;
selectPainterEngine(m_prefferedPainterEngine); selectPainterEngine(m_prefferedPainterEngine);
m_emptyTexture = TexturePtr(new Texture);
g_textures.init();
g_framebuffers.init();
} }
void Graphics::terminate() void Graphics::terminate()
{ {
g_fonts.releaseFonts(); g_fonts.terminate();
g_framebuffers.terminate();
g_textures.terminate();
#ifdef PAINTER_OGL2 #ifdef PAINTER_OGL2
if(g_painterOGL2) { if(g_painterOGL2) {
@ -112,7 +120,7 @@ void Graphics::terminate()
g_painter = nullptr; g_painter = nullptr;
m_emptyTexture.reset(); m_ok = false;
} }
bool Graphics::parseOption(const std::string& option) bool Graphics::parseOption(const std::string& option)

View File

@ -51,13 +51,13 @@ public:
int getMaxTextureSize() { return m_maxTextureSize; } int getMaxTextureSize() { return m_maxTextureSize; }
const Size& getViewportSize() { return m_viewportSize; } const Size& getViewportSize() { return m_viewportSize; }
TexturePtr& getEmptyTexture() { return m_emptyTexture; }
std::string getVendor() { return (const char*)glGetString(GL_VENDOR); } std::string getVendor() { return (const char*)glGetString(GL_VENDOR); }
std::string getRenderer() { return (const char*)glGetString(GL_RENDERER); } std::string getRenderer() { return (const char*)glGetString(GL_RENDERER); }
std::string getVersion() { return (const char*)glGetString(GL_VERSION); } std::string getVersion() { return (const char*)glGetString(GL_VERSION); }
std::string getExtensions() { return (const char*)glGetString(GL_EXTENSIONS); } std::string getExtensions() { return (const char*)glGetString(GL_EXTENSIONS); }
bool ok() { return m_ok; }
bool canUseDrawArrays(); bool canUseDrawArrays();
bool canUseShaders(); bool canUseShaders();
bool canUseFBO(); bool canUseFBO();
@ -72,10 +72,10 @@ public:
private: private:
Size m_viewportSize; Size m_viewportSize;
TexturePtr m_emptyTexture;
int m_maxTextureSize; int m_maxTextureSize;
int m_alphaBits; int m_alphaBits;
Boolean<false> m_ok;
Boolean<true> m_useDrawArrays; Boolean<true> m_useDrawArrays;
Boolean<true> m_useFBO; Boolean<true> m_useFBO;
Boolean<false> m_useHardwareBuffers; Boolean<false> m_useHardwareBuffers;

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/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 <framework/application.h>
#include <framework/core/logger.h>
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);
}

View File

@ -40,15 +40,8 @@ public:
DynamicDraw = GL_DYNAMIC_DRAW DynamicDraw = GL_DYNAMIC_DRAW
}; };
HardwareBuffer(Type type) { HardwareBuffer(Type type);
m_type = type; ~HardwareBuffer();
m_id = 0;
glGenBuffers(1, &m_id);
assert(m_id != 0);
}
~HardwareBuffer() {
glDeleteBuffers(1, &m_id);
}
void bind() { glBindBuffer(m_type, m_id); } void bind() { glBindBuffer(m_type, m_id); }
static void unbind(Type type) { glBindBuffer(type, 0); } static void unbind(Type type) { glBindBuffer(type, 0); }

View File

@ -48,12 +48,6 @@ PainterOGL2::PainterOGL2()
PainterShaderProgram::release(); PainterShaderProgram::release();
} }
PainterOGL2::~PainterOGL2()
{
m_drawTexturedProgram = nullptr;
m_drawSolidColorProgram = nullptr;
}
void PainterOGL2::bind() void PainterOGL2::bind()
{ {
Painter::bind(); Painter::bind();

View File

@ -36,7 +36,6 @@ class PainterOGL2 : public Painter
{ {
public: public:
PainterOGL2(); PainterOGL2();
~PainterOGL2();
void bind(); void bind();
void unbind(); void unbind();

View File

@ -21,6 +21,9 @@
*/ */
#include "shader.h" #include "shader.h"
#include "graphics.h"
#include <framework/application.h>
#include <framework/core/resourcemanager.h> #include <framework/core/resourcemanager.h>
Shader::Shader(Shader::ShaderType shaderType) Shader::Shader(Shader::ShaderType shaderType)
@ -41,7 +44,9 @@ Shader::Shader(Shader::ShaderType shaderType)
Shader::~Shader() Shader::~Shader()
{ {
glDeleteShader(m_shaderId); assert(!g_app->isTermianted());
if(g_graphics.ok())
glDeleteShader(m_shaderId);
} }
bool Shader::compileSourceCode(const std::string& sourceCode) bool Shader::compileSourceCode(const std::string& sourceCode)

View File

@ -21,6 +21,9 @@
*/ */
#include "shaderprogram.h" #include "shaderprogram.h"
#include "graphics.h"
#include <framework/application.h>
GLuint ShaderProgram::m_currentProgram = 0; GLuint ShaderProgram::m_currentProgram = 0;
@ -35,7 +38,9 @@ ShaderProgram::ShaderProgram()
ShaderProgram::~ShaderProgram() ShaderProgram::~ShaderProgram()
{ {
glDeleteProgram(m_programId); assert(!g_app->isTermianted());
if(g_graphics.ok())
glDeleteProgram(m_programId);
} }
bool ShaderProgram::addShader(const ShaderPtr& shader) { bool ShaderProgram::addShader(const ShaderPtr& shader) {

View File

@ -25,6 +25,8 @@
#include "framebuffer.h" #include "framebuffer.h"
#include "image.h" #include "image.h"
#include <framework/application.h>
Texture::Texture() Texture::Texture()
{ {
m_id = 0; m_id = 0;
@ -77,8 +79,9 @@ Texture::Texture(const ImagePtr& image, bool buildMipmaps)
Texture::~Texture() Texture::~Texture()
{ {
assert(!g_app->isTermianted());
// free texture from gl memory // free texture from gl memory
if(m_id > 0) if(g_graphics.ok() && m_id != 0)
glDeleteTextures(1, &m_id); glDeleteTextures(1, &m_id);
} }

View File

@ -30,6 +30,26 @@
TextureManager g_textures; 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 TextureManager::getTexture(const std::string& fileName)
{ {
TexturePtr texture; TexturePtr texture;
@ -59,7 +79,7 @@ TexturePtr TextureManager::getTexture(const std::string& fileName)
texture = loadPNG(fin); texture = loadPNG(fin);
} catch(stdext::exception& e) { } catch(stdext::exception& e) {
g_logger.error(stdext::format("unable to load texture '%s': %s", fileName, e.what())); g_logger.error(stdext::format("unable to load texture '%s': %s", fileName, e.what()));
texture = g_graphics.getEmptyTexture(); texture = g_textures.getEmptyTexture();
} }
if(texture) if(texture)

View File

@ -28,12 +28,17 @@
class TextureManager class TextureManager
{ {
public: 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: private:
TexturePtr loadPNG(std::stringstream& file);
std::unordered_map<std::string, TextureWeakPtr> m_textures; std::unordered_map<std::string, TextureWeakPtr> m_textures;
TexturePtr m_emptyTexture;
}; };
extern TextureManager g_textures; extern TextureManager g_textures;

View File

@ -35,6 +35,8 @@ LuaInterface::LuaInterface()
L = nullptr; L = nullptr;
m_cppCallbackDepth = 0; m_cppCallbackDepth = 0;
m_weakTableRef = 0; m_weakTableRef = 0;
m_totalObjRefs = 0;
m_totalFuncRefs = 0;
} }
LuaInterface::~LuaInterface() LuaInterface::~LuaInterface()
@ -64,6 +66,8 @@ void LuaInterface::terminate()
{ {
// close lua state, it will release all objects // close lua state, it will release all objects
closeLuaState(); closeLuaState();
assert(m_totalFuncRefs == 0);
assert(m_totalObjRefs == 0);
} }
void LuaInterface::registerSingletonClass(const std::string& className) void LuaInterface::registerSingletonClass(const std::string& className)
@ -280,6 +284,7 @@ int LuaInterface::luaObjectCollectEvent(LuaInterface* lua)
// resets pointer to decrease object use count // resets pointer to decrease object use count
objPtr->reset(); objPtr->reset();
g_lua.m_totalObjRefs--;
return 0; return 0;
} }
@ -603,6 +608,7 @@ int LuaInterface::luaCollectCppFunction(lua_State* L)
auto funcPtr = static_cast<LuaCppFunctionPtr*>(g_lua.popUserdata()); auto funcPtr = static_cast<LuaCppFunctionPtr*>(g_lua.popUserdata());
assert(funcPtr); assert(funcPtr);
funcPtr->reset(); funcPtr->reset();
g_lua.m_totalFuncRefs--;
return 0; return 0;
} }
@ -1033,6 +1039,7 @@ void LuaInterface::pushObject(const LuaObjectPtr& obj)
{ {
// fills a new userdata with a new LuaObjectPtr pointer // fills a new userdata with a new LuaObjectPtr pointer
new(newUserdata(sizeof(LuaObjectPtr))) LuaObjectPtr(obj); new(newUserdata(sizeof(LuaObjectPtr))) LuaObjectPtr(obj);
m_totalObjRefs++;
// set the userdata metatable // set the userdata metatable
getGlobal(stdext::format("%s_mt", obj->getClassName())); 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) // create a pointer to func (this pointer will hold the function existence)
new(newUserdata(sizeof(LuaCppFunctionPtr))) LuaCppFunctionPtr(new LuaCppFunction(func)); 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 // sets the userdata __gc metamethod, needed to free the function pointer when it gets collected
newTable(); newTable();

View File

@ -329,6 +329,8 @@ private:
lua_State* L; lua_State* L;
int m_weakTableRef; int m_weakTableRef;
int m_cppCallbackDepth; int m_cppCallbackDepth;
int m_totalObjRefs;
int m_totalFuncRefs;
}; };
extern LuaInterface g_lua; extern LuaInterface g_lua;

View File

@ -23,12 +23,15 @@
#include "luaobject.h" #include "luaobject.h"
#include "luainterface.h" #include "luainterface.h"
#include <framework/application.h>
LuaObject::LuaObject() : m_fieldsTableRef(-1) LuaObject::LuaObject() : m_fieldsTableRef(-1)
{ {
} }
LuaObject::~LuaObject() LuaObject::~LuaObject()
{ {
assert(!g_app->isTermianted());
releaseLuaFieldsTable(); releaseLuaFieldsTable();
} }

View File

@ -22,6 +22,7 @@
#include "connection.h" #include "connection.h"
#include <framework/application.h>
#include <framework/core/eventdispatcher.h> #include <framework/core/eventdispatcher.h>
#include <boost/asio.hpp> #include <boost/asio.hpp>
@ -40,6 +41,7 @@ Connection::Connection() :
Connection::~Connection() Connection::~Connection()
{ {
assert(!g_app->isTermianted());
close(); close();
} }

View File

@ -50,6 +50,9 @@ void UIManager::terminate()
m_draggingWidget = nullptr; m_draggingWidget = nullptr;
m_hoveredWidget = nullptr; m_hoveredWidget = nullptr;
m_pressedWidget = nullptr; m_pressedWidget = nullptr;
m_styles.clear();
m_destroyedWidgets.clear();
m_checkEvent = nullptr;
} }
void UIManager::render(Fw::DrawPane drawPane) void UIManager::render(Fw::DrawPane drawPane)
@ -266,22 +269,19 @@ void UIManager::onWidgetDestroy(const UIWidgetPtr& widget)
if(m_draggingWidget == widget) if(m_draggingWidget == widget)
updateDraggingWidget(nullptr); updateDraggingWidget(nullptr);
#ifdef DEBUG #ifndef DEBUG
static UIWidgetList destroyedWidgets; if(widget == m_rootWidget || !m_rootWidget)
static ScheduledEventPtr checkEvent;
if(widget == m_rootWidget)
return; return;
destroyedWidgets.push_back(widget); m_destroyedWidgets.push_back(widget);
if(checkEvent && !checkEvent->isExecuted()) if(m_checkEvent && !m_checkEvent->isExecuted())
return; return;
checkEvent = g_eventDispatcher.scheduleEvent([] { m_checkEvent = g_eventDispatcher.scheduleEvent([] {
g_lua.collectGarbage(); g_lua.collectGarbage();
UIWidgetList backupList = destroyedWidgets; UIWidgetList backupList = m_destroyedWidgets;
destroyedWidgets.clear(); m_destroyedWidgets.clear();
g_eventDispatcher.scheduleEvent([backupList] { g_eventDispatcher.scheduleEvent([backupList] {
g_lua.collectGarbage(); g_lua.collectGarbage();
for(const UIWidgetPtr& widget : backupList) { for(const UIWidgetPtr& widget : backupList) {

View File

@ -82,6 +82,8 @@ private:
Boolean<false> m_hoverUpdateScheduled; Boolean<false> m_hoverUpdateScheduled;
Boolean<false> m_drawDebugBoxes; Boolean<false> m_drawDebugBoxes;
std::unordered_map<std::string, OTMLNodePtr> m_styles; std::unordered_map<std::string, OTMLNodePtr> m_styles;
UIWidgetList m_destroyedWidgets;
ScheduledEventPtr m_checkEvent;
}; };
extern UIManager g_ui; extern UIManager g_ui;

View File

@ -46,6 +46,7 @@ UIWidget::UIWidget()
UIWidget::~UIWidget() UIWidget::~UIWidget()
{ {
assert(!g_app->isTermianted());
#ifdef DEBUG #ifdef DEBUG
if(!m_destroyed) if(!m_destroyed)
g_logger.warning(stdext::format("widget '%s' was not explicitly destroyed", m_id)); g_logger.warning(stdext::format("widget '%s' was not explicitly destroyed", m_id));

View File

@ -28,6 +28,7 @@ int main(int argc, const char* argv[])
OTClient app; OTClient app;
app.init(args); app.init(args);
app.run(); app.run();
app.deinit();
app.terminate(); app.terminate();
return 0; return 0;
} }

View File

@ -36,7 +36,7 @@
#include <framework/graphics/paintershaderprogram.h> #include <framework/graphics/paintershaderprogram.h>
#include <framework/graphics/painterogl2_shadersources.h> #include <framework/graphics/painterogl2_shadersources.h>
#include <framework/graphics/texturemanager.h> #include <framework/graphics/texturemanager.h>
#include <framework/graphics/framebuffer.h> #include <framework/graphics/framebuffermanager.h>
#include "spritemanager.h" #include "spritemanager.h"
Creature::Creature() : Thing() 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) void Creature::drawOutfit(const Rect& destRect, bool resize)
{ {
if(g_graphics.canUseFBO()) { if(g_graphics.canUseFBO()) {
static FrameBufferPtr outfitBuffer; const FrameBufferPtr& outfitBuffer = g_framebuffers.getTemporaryFrameBuffer();
if(!outfitBuffer) outfitBuffer->resize(Size(2*Otc::TILE_PIXELS, 2*Otc::TILE_PIXELS));
outfitBuffer = FrameBufferPtr(new FrameBuffer(Size(2*Otc::TILE_PIXELS, 2*Otc::TILE_PIXELS)));
outfitBuffer->bind(); outfitBuffer->bind();
g_painter->setAlphaWriting(true); g_painter->setAlphaWriting(true);
g_painter->clear(Color::alpha); g_painter->clear(Color::alpha);

View File

@ -62,5 +62,6 @@ typedef std::shared_ptr<StaticText> StaticTextPtr;
typedef std::shared_ptr<ItemShader> ItemShaderPtr; typedef std::shared_ptr<ItemShader> ItemShaderPtr;
typedef std::vector<ThingPtr> ThingList; typedef std::vector<ThingPtr> ThingList;
typedef std::vector<ThingType> ThingTypeList;
#endif #endif

View File

@ -22,8 +22,6 @@
#include "mapview.h" #include "mapview.h"
#include <framework/graphics/graphics.h>
#include <framework/graphics/framebuffer.h>
#include "creature.h" #include "creature.h"
#include "map.h" #include "map.h"
#include "tile.h" #include "tile.h"
@ -31,7 +29,11 @@
#include "animatedtext.h" #include "animatedtext.h"
#include "missile.h" #include "missile.h"
#include "shadermanager.h" #include "shadermanager.h"
#include <framework/graphics/graphics.h>
#include <framework/graphics/framebuffermanager.h>
#include <framework/core/eventdispatcher.h> #include <framework/core/eventdispatcher.h>
#include <framework/application.h>
MapView::MapView() MapView::MapView()
{ {
@ -41,12 +43,17 @@ MapView::MapView()
m_cachedLastVisibleFloor = 7; m_cachedLastVisibleFloor = 7;
m_optimizedSize = Size(Otc::AWARE_X_TILES, Otc::AWARE_Y_TILES) * Otc::TILE_PIXELS; 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)); setVisibleDimension(Size(15, 11));
m_shader = g_shaders.getDefaultMapShader(); m_shader = g_shaders.getDefaultMapShader();
} }
MapView::~MapView()
{
assert(!g_app->isTermianted());
}
void MapView::draw(const Rect& rect) void MapView::draw(const Rect& rect)
{ {
// update visible tiles cache when needed // update visible tiles cache when needed

View File

@ -50,6 +50,7 @@ public:
}; };
MapView(); MapView();
~MapView();
void draw(const Rect& rect); void draw(const Rect& rect);
private: private:

View File

@ -44,6 +44,7 @@ void ShaderManager::terminate()
{ {
m_defaultItemShader = nullptr; m_defaultItemShader = nullptr;
m_defaultMapShader = nullptr; m_defaultMapShader = nullptr;
m_shaders.clear();
} }
PainterShaderProgramPtr ShaderManager::createShader(const std::string& name) PainterShaderProgramPtr ShaderManager::createShader(const std::string& name)

View File

@ -156,6 +156,4 @@ private:
friend class ThingsType; friend class ThingsType;
}; };
typedef std::vector<ThingType> ThingTypeList;
#endif #endif

View File

@ -117,5 +117,7 @@ void OTClient::init(const std::vector<std::string>& args)
void OTClient::terminate() void OTClient::terminate()
{ {
g_shaders.terminate(); g_shaders.terminate();
g_map.clean();
g_thingsType.unload();
Application::terminate(); Application::terminate();
} }