Browse Source

Fixed initialization issues due to caret
Reorganized GUI initialization order and made sure GUIManager manually destroys all GUIWidget components so it can be deinitialized before CmApplication

Marko Pintera 12 years ago
parent
commit
a8ba9930af

+ 3 - 3
BansheeEngine/Source/BsApplication.cpp

@@ -64,11 +64,11 @@ namespace BansheeEngine
 
 		BuiltinMaterialManager::shutDown();
 
-		CM::gApplication().shutDown();
-
 		OverlayManager::shutDown();
-		GUIMaterialManager::shutDown();
 		GUIManager::shutDown();
+		GUIMaterialManager::shutDown();
+		
+		CM::gApplication().shutDown();
 	}
 
 	void Application::update()

+ 9 - 3
BansheeEngine/Source/BsGUIManager.cpp

@@ -19,7 +19,6 @@
 #include "CmDebug.h"
 
 using namespace CamelotFramework;
-
 namespace BansheeEngine
 {
 	struct GUIGroupElement
@@ -59,12 +58,19 @@ namespace BansheeEngine
 		mWindowLostFocusConn = RenderWindowManager::instance().onFocusLost.connect(boost::bind(&GUIManager::onWindowFocusLost, this, _1));
 		mWindowMovedOrResizedConn = RenderWindowManager::instance().onMovedOrResized.connect(boost::bind(&GUIManager::onWindowMovedOrResized, this, _1));
 
-		updateCaretTexture();
-		updateCaretSprite();
+		// Need to defer these calls because I want to make sure all managers are initialized first
+		deferredCall(std::bind(&GUIManager::updateCaretTexture, this));
+		deferredCall(std::bind(&GUIManager::updateCaretSprite, this));
 	}
 
 	GUIManager::~GUIManager()
 	{
+		// Make a copy of widgets, since destroying them will remove them from mWidgets and
+		// we can't iterate over an array thats getting modified
+		Vector<GUIWidget*>::type widgetCopy = mWidgets;
+		for(auto& widget : widgetCopy)
+			widget->destroy();
+
 		mOnButtonDownConn.disconnect();
 		mOnButtonUpConn.disconnect();
 		mOnMouseMovedConn.disconnect();

+ 7 - 0
CamelotCore/Include/CmComponent.h

@@ -23,6 +23,13 @@ namespace CamelotFramework
 		 */
 		virtual void update() = 0;
 
+		/**
+		 * @brief	Removes the component from parent SceneObject and deletes it. All
+		 * 			the references to this component will be marked as destroyed and you
+		 * 			will get an exception if you try to use them.
+		 */
+		void destroy();
+
 	protected:
 		friend class SceneObject;
 

+ 4 - 132
CamelotCore/Include/CmDeferredCallManager.h

@@ -2,55 +2,9 @@
 
 #include "CmPrerequisites.h"
 #include "CmModule.h"
-#include "boost/function.hpp"
 
 namespace CamelotFramework
 {
-	class DeferredCall
-	{
-	public:
-		~DeferredCall()
-		{
-			disconnect();
-		}
-
-		DeferredCall(DeferredCall& copy)
-		{
-			copy.mOwner = false;
-
-			mId = copy.mId;
-			mOwner = true;
-		}
-
-		void disconnect()
-		{
-			if(mOwner)
-			{
-				DeferredCallManager::instance().removeCall(mId);
-				mOwner = false;
-			}
-		}
-
-	private:
-		friend class DeferredCallManager;
-
-		DeferredCall(UINT32 id)
-			:mId(id), mOwner(true)
-		{ }
-
-		UINT32 mId;
-		bool mOwner;
-	};
-
-	template<int Priority>
-	struct DeferredCallPriority 
-	{ 
-		static_assert(false, 
-			"Provided type isn't plain-old-data. You need to specialize RTTIPlainType template in order to serialize this type. "\
-			" (Or call CM_ALLOW_MEMCPY_SERIALIZATION(type) macro if you are sure the type can be properly serialized using just memcpy.)");
-
-	};
-
 	/**
 	 * @brief	Allows you to queue calls that can get executed later.
 	 * 			
@@ -58,16 +12,6 @@ namespace CamelotFramework
 	 */
 	class CM_EXPORT DeferredCallManager : public Module<DeferredCallManager>
 	{
-		struct DeferredCallData
-		{
-			DeferredCallData(boost::function<void()> _func, UINT32 _id)
-				:func(_func), id(_id)
-			{ }
-
-			boost::function<void()> func;
-			UINT32 id;
-		};
-
 	public:
 		DeferredCallManager();
 
@@ -75,89 +19,17 @@ namespace CamelotFramework
 		 * @brief	Register a deferred call that will be executed once at the start of next frame.
 		 *
 		 * @param	func		The function to execute.
-		 * @tparam	priority	Priority 0-255 determines in what order to execute the calls. Calls with lowest
-		 * 						priority are executed first (i.e. calls with 0 are executed first).
-		 *
-		 * @note	You cannot use just any random priority number. You must use priorities defined during compile time using
-		 * 			DEFERRED_CALL_PRIORITY macro. Otherwise you will receive a compiler error.
 		 */
-		template <UINT8 priority = 0>
-		void queueDeferredCall(boost::function<void()> func)
-		{
-			DeferredCallPriority<priority> ensureValidPriority;
-
-			mOneShotCallbacks[priority].push_back(DeferredCallData(func, (mMaxID++) | ((UINT32)priority << 24)));
-			assert(mMaxID <= (0x00FFFFFF)); // ID is 24bit max, 8 bits are reserved for priority
-		}
+		void queueDeferredCall(std::function<void()> func);
 
 		/**
-		 * @brief	Register a deferred call that will be executed once every frame.
-		 *
-		 * @param	func		The function to execute.
-		 * @tparam	priority	Priority 0-255 determines in what order to execute the calls. Calls with lowest
-		 * 						priority are executed first (i.e. calls with 0 are executed first).
-		 * 						
-		 * @return  DeferredCall structure you must keep safe. Deferred call will continue to be executed as long as that structure
-		 * 			exists or until DeferredCall.disconnect is called. Do not copy the returned structure in any way, only one copy must always
-		 * 			exists, although I do not provide any mechanic for guaranteeing that.
-		 *
-		 * @note	You cannot use just any random priority number. You must use priorities defined during compile time using
-		 * 			DEFERRED_CALL_PRIORITY macro. Otherwise you will receive a compiler error.
+		 * @brief	Executes all the scheduled calls.
 		 */
-		template <UINT8 priority = 0>
-		DeferredCall queueOngoingDeferredCall(boost::function<void()> func)
-		{
-			DeferredCallPriority<priority> ensureValidPriority;
-
-			mOngoingCallbacks[priority].push_back(DeferredCallData(func, (mMaxID++) | ((UINT32)priority << 24)));
-			assert(mMaxID <= (0x00FFFFFF)); // ID is 24bit max, 8 bits are reserved for priority
-		}
+		void update();
 
 	private:
 		friend class DeferredCall;
 
-		UnorderedMap<UINT8, Vector<DeferredCallData>::type>::type mOneShotCallbacks;
-		UnorderedMap<UINT8, Vector<DeferredCallData>::type>::type mOngoingCallbacks;
-
-		UINT16 mMaxID;
-
-		void removeCall(UINT32 combinedId)
-		{
-			UINT8 priority = (combinedId & 0xFF000000) >> 24;
-			UINT32 callId = combinedId & 0x00FFFFFF;
-
-			bool foundCall = false;
-			auto iterFind1 = mOneShotCallbacks.find(priority);
-			if(iterFind1 != mOneShotCallbacks.end())
-			{
-				auto callbacksForPriority = iterFind1->second;
-				for(auto iter = callbacksForPriority.begin(); iter != callbacksForPriority.end(); ++iter)
-				{
-					if(iter->id == callId)
-					{
-						callbacksForPriority.erase(iter);
-						foundCall = true;
-						break;
-					}
-				}
-			}
-
-			if(foundCall)
-				return;
-
-			auto iterFind2 = mOngoingCallbacks.find(priority);
-			if(iterFind2 != mOngoingCallbacks.end())
-			{
-				auto callbacksForPriority = iterFind2->second;
-				for(auto iter = callbacksForPriority.begin(); iter != callbacksForPriority.end(); ++iter)
-				{
-					if(iter->id == callId)
-					{
-						callbacksForPriority.erase(iter);
-						break;
-					}
-				}
-			}
-		}
+		Vector<std::function<void()>>::type mCallbacks;
 	};
 }

+ 21 - 0
CamelotCore/Include/CmPrerequisites.h

@@ -319,6 +319,27 @@ namespace CamelotFramework
 	typedef ResourceHandle<Font> HFont;
 }
 
+namespace CamelotFramework
+{
+	/**
+	 * @brief	Defers function execution until the next frame. If this function is called
+	 * 			within another deferred call, then it will be executed the same frame,
+	 * 			but only after all existing deferred calls are done.
+	 * 			
+	 * @note	This method can be used for breaking dependencies among other things. If a class
+	 * 			A depends on class B having something done, but class B also depends in some way on class A,
+	 * 			you can break up the initialization into two separate steps, queuing the second step
+	 * 			using this method.
+	 * 			
+	 *			Similar situation can happen if you have multiple classes being initialized in an undefined order
+	 *			but some of them depend on others. Using this method you can defer the dependent step until next frame,
+	 *			which will ensure everything was initialized.
+	 *
+	 * @param	callback	The callback.
+	 */
+	void CM_EXPORT deferredCall(std::function<void()> callback);
+}
+
 #endif
 
 

+ 1 - 1
CamelotCore/Include/CmRenderWindowManager.h

@@ -20,7 +20,7 @@ namespace CamelotFramework
 		/**
 		 * @brief	Called once per frame. Dispatches events. Internal method.
 		 */
-		void _update();
+		void update();
 
 		Vector<RenderWindow*>::type getRenderWindows() const;
 

+ 7 - 0
CamelotCore/Include/CmSceneObject.h

@@ -283,6 +283,13 @@ namespace CamelotFramework
 		 */
 		void destroyComponent(const HComponent& component);
 
+		/**
+		 * @brief	Removes the component from this SceneObject, and deallocates it.
+		 *
+		 * @param [in]	component	The component to destroy.
+		 */
+		void destroyComponent(Component* component);
+
 		/**
 		 * @brief	Returns all components on this SceneObject.
 		 */

+ 5 - 1
CamelotCore/Source/CmApplication.cpp

@@ -26,6 +26,7 @@
 #include "CmFontManager.h"
 #include "CmRenderWindowManager.h"
 #include "CmRenderer.h"
+#include "CmDeferredCallManager.h"
 #include "CmCoreThread.h"
 
 #include "CmMaterial.h"
@@ -48,6 +49,7 @@ namespace CamelotFramework
 	{
 		MemStack::setupHeap(HID_Main);
 
+		DeferredCallManager::startUp(cm_new<DeferredCallManager>());
 		Time::startUp(cm_new<Time>());
 		DynLibManager::startUp(cm_new<DynLibManager>());
 		CoreGpuObjectManager::startUp(cm_new<CoreGpuObjectManager>());
@@ -90,7 +92,8 @@ namespace CamelotFramework
 
 		while(mRunMainLoop)
 		{
-			RenderWindowManager::instance()._update();
+			DeferredCallManager::instance().update();
+			RenderWindowManager::instance().update();
 			gInput().update();
 			gSceneManager().update();
 
@@ -161,6 +164,7 @@ namespace CamelotFramework
 		CoreGpuObjectManager::shutDown(); // Must shut down before DynLibManager to ensure all objects are destroyed before unloading their libraries
 		DynLibManager::shutDown();
 		Time::shutDown();
+		DeferredCallManager::shutDown();
 	}
 
 	void* Application::loadPlugin(const String& pluginName)

+ 6 - 0
CamelotCore/Source/CmComponent.cpp

@@ -1,4 +1,5 @@
 #include "CmComponent.h"
+#include "CmSceneObject.h"
 #include "CmComponentRTTI.h"
 
 namespace CamelotFramework
@@ -14,6 +15,11 @@ namespace CamelotFramework
 
 	}
 
+	void Component::destroy()
+	{
+		SO()->destroyComponent(this);
+	}
+
 	RTTITypeBase* Component::getRTTIStatic()
 	{
 		return ComponentRTTI::instance();

+ 26 - 0
CamelotCore/Source/CmDeferredCallManager.cpp

@@ -6,4 +6,30 @@ namespace CamelotFramework
 	{
 
 	}
+
+	void DeferredCallManager::queueDeferredCall(std::function<void()> func)
+	{
+		mCallbacks.push_back(func);
+	}
+
+	void DeferredCallManager::update()
+	{
+		while(!mCallbacks.empty())
+		{
+			// Copy because callbacks can be queued within callbacks
+			Vector<std::function<void()>>::type callbackCopy = mCallbacks;
+			mCallbacks.clear();
+
+			for(auto& call : callbackCopy)
+			{
+				call();
+			}
+		}
+	}
+
+	// Declared in CmPrerequisites.h
+	void deferredCall(std::function<void()> callback)
+	{
+		DeferredCallManager::instance().queueDeferredCall(callback);
+	}
 }

+ 1 - 1
CamelotCore/Source/CmRenderWindowManager.cpp

@@ -53,7 +53,7 @@ namespace CamelotFramework
 			mMovedOrResizedWindows.push_back(window);
 	}
 
-	void RenderWindowManager::_update()
+	void RenderWindowManager::update()
 	{
 		RenderWindow* newWinInFocus = nullptr;
 		Vector<RenderWindow*>::type movedOrResizedWindows;

+ 17 - 2
CamelotCore/Source/CmSceneObject.cpp

@@ -363,15 +363,30 @@ namespace CamelotFramework
 
 		if(iter != mComponents.end())
 		{
+			gSceneManager().notifyComponentRemoved((*iter));
+
 			(*iter).destroy();
 			mComponents.erase(iter);
-
-			gSceneManager().notifyComponentRemoved((*iter));
 		}
 		else
 			LOGDBG("Trying to remove a component that doesn't exist on this SceneObject.");
 	}
 
+	void SceneObject::destroyComponent(Component* component)
+	{
+		auto iterFind = std::find_if(mComponents.begin(), mComponents.end(), 
+			[component](const HComponent& x) { return x.getHandleData()->mPtr == component; });
+
+		if(iterFind == mComponents.end())
+		{
+			LOGDBG("Trying to remove a component that doesn't exist on this SceneObject.");
+		}
+		else
+		{
+			destroyComponent(*iterFind);
+		}
+	}
+
 	RTTITypeBase* SceneObject::getRTTIStatic()
 	{
 		return SceneObjectRTTI::instance();