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/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

View File

@ -116,11 +116,8 @@ void Application::init(const std::vector<std::string>& 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;

View File

@ -39,6 +39,7 @@ public:
virtual void init(const std::vector<std::string>& 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<false> m_initialized;
Boolean<false> m_running;
Boolean<false> m_stopping;
Boolean<false> m_terminated;
Boolean<false> m_onInputEvent;
Boolean<false> m_mustRepaint;
AdaptativeFrameCounter m_backgroundFrameCounter;

View File

@ -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 == "") {

View File

@ -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<std::string>& list);

View File

@ -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<void()>& 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<void()>& ca
ScheduledEventPtr EventDispatcher::cycleEvent(const std::function<void()>& 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<void()>& callb
EventPtr EventDispatcher::addEvent(const std::function<void()>& 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) {

View File

@ -31,14 +31,24 @@ class Event : public LuaObject
{
public:
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() {
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<EventPtr> m_eventList;
int m_pollEventsSize;
Boolean<false> m_disabled;
std::priority_queue<ScheduledEventPtr, std::vector<ScheduledEventPtr>, lessScheduledEvent> m_scheduledEventList;
};

View File

@ -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

View File

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

View File

@ -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;

View File

@ -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)

View File

@ -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);

View File

@ -23,7 +23,9 @@
#include "framebuffer.h"
#include "graphics.h"
#include "texture.h"
#include <framework/platform/platformwindow.h>
#include <framework/application.h>
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;

View File

@ -28,9 +28,12 @@
class FrameBuffer
{
public:
protected:
FrameBuffer();
FrameBuffer(const Size& size);
friend class FrameBufferManager;
public:
virtual ~FrameBuffer();
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/texture.h>
#include "texturemanager.h"
#include "framebuffermanager.h"
#include <framework/platform/platformwindow.h>
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)

View File

@ -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<false> m_ok;
Boolean<true> m_useDrawArrays;
Boolean<true> m_useFBO;
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
};
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); }

View File

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

View File

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

View File

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

View File

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

View File

@ -25,6 +25,8 @@
#include "framebuffer.h"
#include "image.h"
#include <framework/application.h>
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);
}

View File

@ -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)

View File

@ -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<std::string, TextureWeakPtr> m_textures;
TexturePtr m_emptyTexture;
};
extern TextureManager g_textures;

View File

@ -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<LuaCppFunctionPtr*>(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();

View File

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

View File

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

View File

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

View File

@ -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) {

View File

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

View File

@ -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));

View File

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

View File

@ -36,7 +36,7 @@
#include <framework/graphics/paintershaderprogram.h>
#include <framework/graphics/painterogl2_shadersources.h>
#include <framework/graphics/texturemanager.h>
#include <framework/graphics/framebuffer.h>
#include <framework/graphics/framebuffermanager.h>
#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);

View File

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

View File

@ -22,8 +22,6 @@
#include "mapview.h"
#include <framework/graphics/graphics.h>
#include <framework/graphics/framebuffer.h>
#include "creature.h"
#include "map.h"
#include "tile.h"
@ -31,7 +29,11 @@
#include "animatedtext.h"
#include "missile.h"
#include "shadermanager.h"
#include <framework/graphics/graphics.h>
#include <framework/graphics/framebuffermanager.h>
#include <framework/core/eventdispatcher.h>
#include <framework/application.h>
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

View File

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

View File

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

View File

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

View File

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