Explorar o código

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 %!s(int64=12) %!d(string=hai) anos
pai
achega
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();