Просмотр исходного кода

Refactored core object sync so it is faster by not iterating over all core objects every frame (WIP)
Fixed an issue with core object sync when the owner object was destroyed before it could properly sync its data (WIP)

BearishSun 10 лет назад
Родитель
Сommit
2fd89957eb

+ 9 - 2
BansheeCore/Include/BsCoreObject.h

@@ -192,7 +192,7 @@ namespace BansheeEngine
 		 *					internal data is dirty. syncToCore() will be called regardless
 		 *					internal data is dirty. syncToCore() will be called regardless
 		 *					and it's up to the implementation to read the flags value if needed.
 		 *					and it's up to the implementation to read the flags value if needed.
 		 */
 		 */
-		void markCoreDirty(UINT32 flags = 0xFFFFFFFF) { mCoreDirtyFlags |= flags; }
+		void markCoreDirty(UINT32 flags = 0xFFFFFFFF);
 
 
 		/**
 		/**
 		 * @brief	Marks the core data as clean. Normally called right after syncToCore()
 		 * @brief	Marks the core data as clean. Normally called right after syncToCore()
@@ -200,6 +200,13 @@ namespace BansheeEngine
 		 */
 		 */
 		void markCoreClean() { mCoreDirtyFlags = 0; }
 		void markCoreClean() { mCoreDirtyFlags = 0; }
 
 
+		/**
+		 * @brief	Notifies the core object manager that this object is dependant on some other CoreObject(s), and the
+		 * 			dependencies changes since the last call to this method. This will trigger a call to 
+		 * 			::getCoreDependencies to collect the new dependencies.
+		 */
+		void markDependenciesDirty();
+
 		/**
 		/**
 		 * @brief	Checks is the core dirty flag set. This is used by external systems 
 		 * @brief	Checks is the core dirty flag set. This is used by external systems 
 		 *			to know when internal data has changed and core thread potentially needs to be notified.
 		 *			to know when internal data has changed and core thread potentially needs to be notified.
@@ -224,7 +231,7 @@ namespace BansheeEngine
 		/**
 		/**
 		 * @brief	Populates the provided array with all core objects that this core object depends upon.
 		 * @brief	Populates the provided array with all core objects that this core object depends upon.
 		 */
 		 */
-		virtual void getCoreDependencies(FrameVector<SPtr<CoreObject>>& dependencies) { }
+		virtual void getCoreDependencies(Vector<CoreObject*>& dependencies) { }
 
 
 	protected:
 	protected:
 		SPtr<CoreObjectCore> mCoreSpecific;
 		SPtr<CoreObjectCore> mCoreSpecific;

+ 46 - 4
BansheeCore/Include/BsCoreObjectManager.h

@@ -23,14 +23,14 @@ namespace BansheeEngine
 		struct CoreStoredSyncObjData
 		struct CoreStoredSyncObjData
 		{
 		{
 			CoreStoredSyncObjData()
 			CoreStoredSyncObjData()
-				:destinationObj(nullptr)
+				:destinationObj(nullptr), internalId(0)
 			{ }
 			{ }
 
 
-			CoreStoredSyncObjData(CoreObjectCore* destObj, UINT64 internalId, const CoreSyncData& syncData)
+			CoreStoredSyncObjData(const SPtr<CoreObjectCore> destObj, UINT64 internalId, const CoreSyncData& syncData)
 				:destinationObj(destObj), syncData(syncData), internalId(internalId)
 				:destinationObj(destObj), syncData(syncData), internalId(internalId)
 			{ }
 			{ }
 
 
-			CoreObjectCore* destinationObj;
+			SPtr<CoreObjectCore> destinationObj;
 			CoreSyncData syncData;
 			CoreSyncData syncData;
 			UINT64 internalId;
 			UINT64 internalId;
 		};
 		};
@@ -46,6 +46,16 @@ namespace BansheeEngine
 			Vector<CoreStoredSyncObjData> entries;
 			Vector<CoreStoredSyncObjData> entries;
 		};
 		};
 
 
+		/**
+		 * @brief	Contains information about a dirty CoreObject that requires syncing to
+		 * 			the core thread.
+		 */	
+		struct DirtyObjectData
+		{
+			CoreObject* object;
+			INT32 syncDataId;
+		};
+
 	public:
 	public:
 		CoreObjectManager();
 		CoreObjectManager();
 		~CoreObjectManager();
 		~CoreObjectManager();
@@ -62,15 +72,35 @@ namespace BansheeEngine
 		 */
 		 */
 		void unregisterObject(CoreObject* object);
 		void unregisterObject(CoreObject* object);
 
 
+		/**
+		 * @brief	Notifies the system that a CoreObject is dirty and needs to be synced with the
+		 * 			core thread.
+		 */
+		void notifyCoreDirty(CoreObject* object);
+
+		/**
+		 * @brief	Notifies the system that CoreObject dependencies are dirty and should be updated.
+		 */
+		void notifyDependenciesDirty(CoreObject* object);
+
 		/**
 		/**
 		 * @brief	Synchronizes all dirty CoreObjects with the core thread. Their dirty data will be
 		 * @brief	Synchronizes all dirty CoreObjects with the core thread. Their dirty data will be
-		 *			allocated using the provided allocator and then queued for update using the provided
+		 *			allocated using the global frame allocator and then queued for update using the provided
 		 *			core thread accessor.
 		 *			core thread accessor.
 		 *
 		 *
 		 * @note	Sim thread only.
 		 * @note	Sim thread only.
 		 */
 		 */
 		void syncToCore(CoreAccessor& accessor);
 		void syncToCore(CoreAccessor& accessor);
 
 
+		/**
+		 * @brief	Synchronizes an individual dirty CoreObject with the core thread. Its dirty data will be
+		 *			allocated using the global frame allocator and then queued for update using the provided
+		 *			core thread accessor.
+		 *
+		 * @note	Sim thread only.
+		 */
+		void syncToCore(CoreObject* object, CoreAccessor& accessor);
+
 	private:
 	private:
 		/**
 		/**
 		 * @brief	Stores all syncable data from dirty core objects into memory allocated
 		 * @brief	Stores all syncable data from dirty core objects into memory allocated
@@ -93,9 +123,21 @@ namespace BansheeEngine
 		 */
 		 */
 		void syncUpload();
 		void syncUpload();
 
 
+		/**
+		 * @brief	Updates the cached list of dependencies and dependants for the specified object.
+		 * 			
+		 * @param	object			Update to update dependencies for.
+		 * @param	dependencies	New set of dependencies, or null to clear all dependencies.
+		 */
+		void updateDependencies(CoreObject* object, Vector<CoreObject*>* dependencies);
+
 		UINT64 mNextAvailableID;
 		UINT64 mNextAvailableID;
 		Map<UINT64, CoreObject*> mObjects;
 		Map<UINT64, CoreObject*> mObjects;
+		Map<UINT64, DirtyObjectData> mDirtyObjects;
+		Map<UINT64, Vector<CoreObject*>> mDependencies;
+		Map<UINT64, Vector<CoreObject*>> mDependants;
 
 
+		Vector<CoreStoredSyncObjData> mDestroyedSyncData;
 		List<CoreStoredSyncData> mCoreSyncData;
 		List<CoreStoredSyncData> mCoreSyncData;
 
 
 		BS_MUTEX(mObjectsMutex);
 		BS_MUTEX(mObjectsMutex);

+ 1 - 1
BansheeCore/Include/BsFont.h

@@ -92,7 +92,7 @@ namespace BansheeEngine
 		/**
 		/**
 		 * @copydoc	CoreObject::getCoreDependencies
 		 * @copydoc	CoreObject::getCoreDependencies
 		 */
 		 */
-		void getCoreDependencies(FrameVector<SPtr<CoreObject>>& dependencies) override;
+		void getCoreDependencies(Vector<CoreObject*>& dependencies) override;
 
 
 	private:
 	private:
 		Map<UINT32, SPtr<FontBitmap>> mFontDataPerSize;
 		Map<UINT32, SPtr<FontBitmap>> mFontDataPerSize;

+ 1 - 1
BansheeCore/Include/BsMaterial.h

@@ -690,7 +690,7 @@ namespace BansheeEngine
 		/**
 		/**
 		 * @copydoc	CoreObject::getCoreDependencies
 		 * @copydoc	CoreObject::getCoreDependencies
 		 */
 		 */
-		void getCoreDependencies(FrameVector<SPtr<CoreObject>>& dependencies) override;
+		void getCoreDependencies(Vector<CoreObject*>& dependencies) override;
 
 
 		/**
 		/**
 		 * @copydoc	CoreObject::markCoreDirty
 		 * @copydoc	CoreObject::markCoreDirty

+ 1 - 1
BansheeCore/Include/BsPass.h

@@ -197,7 +197,7 @@ namespace BansheeEngine
 		/**
 		/**
 		 * @copydoc	CoreObject::syncToCore
 		 * @copydoc	CoreObject::syncToCore
 		 */
 		 */
-		void getCoreDependencies(FrameVector<SPtr<CoreObject>>& dependencies) override;
+		void getCoreDependencies(Vector<CoreObject*>& dependencies) override;
 
 
 		/**
 		/**
 		 * @brief	Creates a new empty pass but doesn't initialize it.
 		 * @brief	Creates a new empty pass but doesn't initialize it.

+ 1 - 1
BansheeCore/Include/BsShader.h

@@ -466,7 +466,7 @@ namespace BansheeEngine
 		/**
 		/**
 		 * @copydoc	CoreObject::getCoreDependencies
 		 * @copydoc	CoreObject::getCoreDependencies
 		 */
 		 */
-		void getCoreDependencies(FrameVector<SPtr<CoreObject>>& dependencies) override;
+		void getCoreDependencies(Vector<CoreObject*>& dependencies) override;
 
 
 		/**
 		/**
 		 * @copydoc	CoreObject::createCore
 		 * @copydoc	CoreObject::createCore

+ 1 - 1
BansheeCore/Include/BsTechnique.h

@@ -112,7 +112,7 @@ namespace BansheeEngine
 		/**
 		/**
 		 * @copydoc	CoreObject::getCoreDependencies
 		 * @copydoc	CoreObject::getCoreDependencies
 		 */
 		 */
-		void getCoreDependencies(FrameVector<SPtr<CoreObject>>& dependencies);
+		void getCoreDependencies(Vector<CoreObject*>& dependencies) override;
 
 
 		/**
 		/**
 		 * @brief	Creates a new technique but doesn't initialize it.
 		 * @brief	Creates a new technique but doesn't initialize it.

+ 2 - 2
BansheeCore/Include/BsViewport.h

@@ -217,7 +217,7 @@ namespace BansheeEngine
 		/**
 		/**
 		 * @brief	Sets the render target the viewport will be associated with.
 		 * @brief	Sets the render target the viewport will be associated with.
 		 */
 		 */
-		void setTarget(const RenderTargetPtr& target) { mTarget = target; _markCoreDirty(); }
+		void setTarget(const RenderTargetPtr& target);
 
 
 		/**
 		/**
 		 * @brief	Retrieves a core implementation of a viewport usable only from the
 		 * @brief	Retrieves a core implementation of a viewport usable only from the
@@ -260,7 +260,7 @@ namespace BansheeEngine
 		/**
 		/**
 		 * @copydoc	CoreObject::getCoreDependencies
 		 * @copydoc	CoreObject::getCoreDependencies
 		 */
 		 */
-		void getCoreDependencies(FrameVector<SPtr<CoreObject>>& dependencies) override;
+		void getCoreDependencies(Vector<CoreObject*>& dependencies) override;
 
 
 		/**
 		/**
 		 * @copydoc	CoreObject::createCore
 		 * @copydoc	CoreObject::createCore

+ 22 - 60
BansheeCore/Source/BsCoreObject.cpp

@@ -33,18 +33,18 @@ namespace BansheeEngine
 				"the object is being deleted? You shouldn't delete CoreObjects manually.");
 				"the object is being deleted? You shouldn't delete CoreObjects manually.");
 		}
 		}
 #endif
 #endif
-
-		CoreObjectManager::instance().unregisterObject(this);
 	}
 	}
 
 
 	void CoreObject::destroy()
 	void CoreObject::destroy()
 	{
 	{
+		CoreObjectManager::instance().unregisterObject(this);
 		setIsDestroyed(true);
 		setIsDestroyed(true);
 
 
 		if(requiresInitOnCoreThread())
 		if(requiresInitOnCoreThread())
 		{
 		{
 			assert(BS_THREAD_CURRENT_ID != CoreThread::instance().getCoreThreadId() && "Cannot destroy sim thead object from core thread.");
 			assert(BS_THREAD_CURRENT_ID != CoreThread::instance().getCoreThreadId() && "Cannot destroy sim thead object from core thread.");
 
 
+			// This will only destroy the CoreObjectCore if this was the last reference
 			queueDestroyGpuCommand(mCoreSpecific);
 			queueDestroyGpuCommand(mCoreSpecific);
 		}
 		}
 
 
@@ -68,8 +68,16 @@ namespace BansheeEngine
 			else
 			else
 			{
 			{
 				mCoreSpecific->initialize();
 				mCoreSpecific->initialize();
+
+				// Even though this object might not require initialization on the core thread, it will be used on it, therefore
+				// do a memory barrier to ensure any stores are finished before continuing (When it requires init on core thread
+				// we use the core accessor which uses a mutex, and therefore executes all stores as well, so we dont need to 
+				// do this explicitly)
+				std::atomic_thread_fence(std::memory_order_release);
 			}
 			}
 		}
 		}
+
+		markDependenciesDirty();
 	}
 	}
 
 
 	void CoreObject::blockUntilCoreInitialized()
 	void CoreObject::blockUntilCoreInitialized()
@@ -80,68 +88,22 @@ namespace BansheeEngine
 
 
 	void CoreObject::syncToCore(CoreAccessor& accessor)
 	void CoreObject::syncToCore(CoreAccessor& accessor)
 	{
 	{
-		struct IndividualCoreSyncData
-		{
-			SPtr<CoreObjectCore> destination;
-			CoreSyncData syncData;
-			FrameAlloc* allocator;
-		};
-
-		FrameAlloc* allocator = gCoreThread().getFrameAlloc();
-		Vector<IndividualCoreSyncData> syncData;
-
-		bs_frame_mark();
-		{
-			FrameVector<SPtr<CoreObject>> dependencies;
-			UINT32 stackPos = 0;
-
-			dependencies.push_back(getThisPtr());
-			while (stackPos < dependencies.size())
-			{
-				SPtr<CoreObject> curObj = dependencies[stackPos];
-				stackPos++;
-
-				if (curObj->isCoreDirty())
-				{
-					SPtr<CoreObjectCore> destObj = curObj->getCore();
-					if (destObj == nullptr)
-						return;
-
-					IndividualCoreSyncData data;
-					data.allocator = allocator;
-					data.destination = destObj;
-					data.syncData = syncToCore(data.allocator);
-
-					syncData.push_back(data);
-
-					curObj->markCoreClean();
-				}
-
-				// Note: I don't check for recursion. Possible infinite loop if two objects
-				// are dependent on one another.
-				curObj->getCoreDependencies(dependencies);
-			}
-		}
-		bs_frame_clear();
+		CoreObjectManager::instance().syncToCore(this, accessor);
+	}
 
 
-		std::function<void(const Vector<IndividualCoreSyncData>&)> callback =
-			[](const Vector<IndividualCoreSyncData>& data)
-		{
-			// Traverse in reverse to sync dependencies before dependants
-			for (auto& riter = data.rbegin(); riter != data.rend(); ++riter)
-			{
-				const IndividualCoreSyncData& entry = *riter;
-				entry.destination->syncToCore(entry.syncData);
+	void CoreObject::markCoreDirty(UINT32 flags)
+	{
+		bool wasDirty = isCoreDirty();
 
 
-				UINT8* dataPtr = entry.syncData.getBuffer();
+		mCoreDirtyFlags |= flags;
 
 
-				if (dataPtr != nullptr)
-					entry.allocator->dealloc(dataPtr);
-			}
-		};
+		if (!wasDirty && isCoreDirty())
+			CoreObjectManager::instance().notifyCoreDirty(this);
+	}
 
 
-		if (syncData.size() > 0)
-			accessor.queueCommand(std::bind(callback, syncData));
+	void CoreObject::markDependenciesDirty()
+	{
+		CoreObjectManager::instance().notifyDependenciesDirty(this);
 	}
 	}
 
 
 	void CoreObject::_setThisPtr(std::shared_ptr<CoreObject> ptrThis)
 	void CoreObject::_setThisPtr(std::shared_ptr<CoreObject> ptrThis)

+ 240 - 35
BansheeCore/Source/BsCoreObjectManager.cpp

@@ -46,25 +46,121 @@ namespace BansheeEngine
 	{
 	{
 		assert(object != nullptr);
 		assert(object != nullptr);
 
 
+		UINT64 internalId = object->getInternalID();
+
+		// If dirty, we generate sync data before it is destroyed
+		if (object->isCoreDirty())
+		{
+			BS_LOCK_MUTEX(mObjectsMutex);
+
+			SPtr<CoreObjectCore> coreObject = object->getCore();
+			if (coreObject != nullptr)
+			{
+				CoreSyncData objSyncData = object->syncToCore(gCoreThread().getFrameAlloc());
+				
+				mDestroyedSyncData.push_back(CoreStoredSyncObjData(coreObject, internalId, objSyncData));
+
+				DirtyObjectData& dirtyObjData = mDirtyObjects[internalId];
+				dirtyObjData.syncDataId = (INT32)mDestroyedSyncData.size() - 1;
+				dirtyObjData.object = nullptr;
+			}
+		}
+
+		{
+			BS_LOCK_MUTEX(mObjectsMutex);
+
+			mObjects.erase(internalId);
+		}
+
+		updateDependencies(object, nullptr);
+	}
+
+	void CoreObjectManager::notifyCoreDirty(CoreObject* object)
+	{
+		UINT64 id = object->getInternalID();
+
 		BS_LOCK_MUTEX(mObjectsMutex);
 		BS_LOCK_MUTEX(mObjectsMutex);
 
 
-		UINT64 internalId = object->getInternalID();
-		mObjects.erase(internalId);
+		mDirtyObjects[id] = { object, -1 };
+	}
+
+	void CoreObjectManager::notifyDependenciesDirty(CoreObject* object)
+	{
+		Vector<CoreObject*> dependencies;
+		object->getCoreDependencies(dependencies);
+
+		updateDependencies(object, &dependencies);
+	}
+
+	void CoreObjectManager::updateDependencies(CoreObject* object, Vector<CoreObject*>* dependencies)
+	{
+		UINT64 id = object->getInternalID();
 
 
-		for (auto& syncData : mCoreSyncData)
+		bs_frame_mark();
 		{
 		{
-			auto iterFind = std::find_if(syncData.entries.begin(), syncData.entries.end(), 
-				[&](const CoreStoredSyncObjData& data) { return data.internalId == internalId; });
-			if (iterFind != syncData.entries.end())
+			FrameVector<CoreObject*> toRemove;
+			FrameVector<CoreObject*> toAdd;
+
+			BS_LOCK_MUTEX(mObjectsMutex);
+
+			// Add dependencies and clear old dependencies from dependants
 			{
 			{
-				UINT8* data = iterFind->syncData.getBuffer();
+				auto iterFind = mDependencies.find(id);
+				if (iterFind != mDependencies.end())
+				{
+					const Vector<CoreObject*>& oldDependencies = iterFind->second;
+
+					if (dependencies != nullptr)
+					{
+						std::set_difference(dependencies->begin(), dependencies->end(),
+							dependencies->begin(), dependencies->end(), toRemove.begin());
+
+						std::set_difference(oldDependencies.begin(), oldDependencies.end(),
+							oldDependencies.begin(), oldDependencies.end(), toAdd.begin());
+					}
+					else
+					{
+						for (auto& dependency : oldDependencies)
+							toRemove.push_back(dependency);
+					}
+
+					for (auto& dependency : toRemove)
+					{
+						UINT64 dependencyId = dependency->getInternalID();
+						auto iterFind2 = mDependants.find(dependencyId);
+
+						if (iterFind2 != mDependants.end())
+						{
+							Vector<CoreObject*>& dependants = iterFind2->second;
+							auto findIter3 = std::find(dependants.begin(), dependants.end(), object);
+							dependants.erase(findIter3);
+
+							if (dependants.size() == 0)
+								mDependants.erase(iterFind2);
+						}
+					}
+				}
+				else
+				{
+					for (auto& dependency : *dependencies)
+						toAdd.push_back(dependency);
+				}
 
 
-				if (data != nullptr && syncData.alloc != nullptr)
-					syncData.alloc->dealloc(data);
+				if (dependencies != nullptr)
+					mDependencies[id] = *dependencies;
+			}
 
 
-				syncData.entries.erase(iterFind);
+			// Register dependants
+			{
+				for (auto& dependency : toAdd)
+				{
+					UINT64 dependencyId = dependency->getInternalID();
+					Vector<CoreObject*>& dependants = mDependants[dependencyId];
+					dependants.push_back(object);
+				}
 			}
 			}
 		}
 		}
+		bs_frame_clear();
 	}
 	}
 
 
 	void CoreObjectManager::syncToCore(CoreAccessor& accessor)
 	void CoreObjectManager::syncToCore(CoreAccessor& accessor)
@@ -73,6 +169,79 @@ namespace BansheeEngine
 		accessor.queueCommand(std::bind(&CoreObjectManager::syncUpload, this));
 		accessor.queueCommand(std::bind(&CoreObjectManager::syncUpload, this));
 	}
 	}
 
 
+	void CoreObjectManager::syncToCore(CoreObject* object, CoreAccessor& accessor)
+	{
+		struct IndividualCoreSyncData
+		{
+			SPtr<CoreObjectCore> destination;
+			CoreSyncData syncData;
+			FrameAlloc* allocator;
+		};
+
+		BS_LOCK_MUTEX(mObjectsMutex);
+
+		FrameAlloc* allocator = gCoreThread().getFrameAlloc();
+		Vector<IndividualCoreSyncData> syncData;
+
+		std::function<void(CoreObject*)> syncObject = [&](CoreObject* curObj)
+		{
+			if (!curObj->isCoreDirty())
+				return; // We already processed it as some other object's dependency
+
+			// Sync dependencies before dependants
+			// Note: I don't check for recursion. Possible infinite loop if two objects
+			// are dependent on one another.
+
+			UINT64 id = curObj->getInternalID();
+			auto iterFind = mDependencies.find(id);
+
+			if (iterFind != mDependencies.end())
+			{
+				const Vector<CoreObject*>& dependencies = iterFind->second;
+				for (auto& dependency : dependencies)
+					syncObject(dependency);
+			}
+
+			SPtr<CoreObjectCore> objectCore = curObj->getCore();
+			if (objectCore == nullptr)
+			{
+				curObj->markCoreClean();
+				mDirtyObjects.erase(id);
+				return;
+			}
+
+			syncData.push_back(IndividualCoreSyncData());
+			IndividualCoreSyncData& data = syncData.back();
+			data.allocator = allocator;
+			data.destination = objectCore;
+			data.syncData = curObj->syncToCore(allocator);
+
+			curObj->markCoreClean();
+			mDirtyObjects.erase(id);
+		};
+
+		syncObject(object);
+
+		std::function<void(const Vector<IndividualCoreSyncData>&)> callback =
+			[](const Vector<IndividualCoreSyncData>& data)
+		{
+			// Traverse in reverse to sync dependencies before dependants
+			for (auto& riter = data.rbegin(); riter != data.rend(); ++riter)
+			{
+				const IndividualCoreSyncData& entry = *riter;
+				entry.destination->syncToCore(entry.syncData);
+
+				UINT8* dataPtr = entry.syncData.getBuffer();
+
+				if (dataPtr != nullptr)
+					entry.allocator->dealloc(dataPtr);
+			}
+		};
+
+		if (syncData.size() > 0)
+			accessor.queueCommand(std::bind(callback, syncData));
+	}
+
 	void CoreObjectManager::syncDownload(FrameAlloc* allocator)
 	void CoreObjectManager::syncDownload(FrameAlloc* allocator)
 	{
 	{
 		BS_LOCK_MUTEX(mObjectsMutex);
 		BS_LOCK_MUTEX(mObjectsMutex);
@@ -81,49 +250,85 @@ namespace BansheeEngine
 		CoreStoredSyncData& syncData = mCoreSyncData.back();
 		CoreStoredSyncData& syncData = mCoreSyncData.back();
 
 
 		syncData.alloc = allocator;
 		syncData.alloc = allocator;
-		bs_frame_mark();
 		
 		
-		// Note: Optimization possibilities
-		//  - Iterating over every single object takes too long. Instead dirty objects should be added to a separate array 
-		//    and then just directly iterated over.
-		//  - Retrieving dependencies takes too long. A better system should be implemented so I can immediately tell
-		//    what depends on a dependency at the moment it is marked dirty. Then we can queue it into the same array above.
-		//     - e.g. Whenever dependencies change (e.g. object is created or modified) register the dependencies so we
-		//       can look them up later.
+		// Add all objects dependant on the dirty objects
+		bs_frame_mark();
+		{
+			FrameSet<CoreObject*> dirtyDependants;
+			for (auto& objectData : mDirtyObjects)
+			{
+				auto iterFind = mDependants.find(objectData.first);
+				if (iterFind != mDependants.end())
+				{
+					const Vector<CoreObject*>& dependants = iterFind->second;
+					for (auto& dependant : dependants)
+					{
+						if (!dependant->isCoreDirty())
+							dirtyDependants.insert(dependant);
+					}
+				}
+			}
+
+			for (auto& dirtyDependant : dirtyDependants)
+			{
+				UINT64 id = dirtyDependant->getInternalID();
+
+				mDirtyObjects[id] = { dirtyDependant, -1 };
+			}
+		}
 
 
+		bs_frame_clear();
+		
 		// Order in which objects are recursed in matters, ones with lower ID will have been created before
 		// Order in which objects are recursed in matters, ones with lower ID will have been created before
 		// ones with higher ones and should be updated first.
 		// ones with higher ones and should be updated first.
-		for (auto& objectData : mObjects)
+		for (auto& objectData : mDirtyObjects)
 		{
 		{
 			std::function<void(CoreObject*)> syncObject = [&](CoreObject* curObj)
 			std::function<void(CoreObject*)> syncObject = [&](CoreObject* curObj)
 			{
 			{
+				if (!curObj->isCoreDirty())
+					return; // We already processed it as some other object's dependency
+
 				// Sync dependencies before dependants
 				// Sync dependencies before dependants
 				// Note: I don't check for recursion. Possible infinite loop if two objects
 				// Note: I don't check for recursion. Possible infinite loop if two objects
 				// are dependent on one another.
 				// are dependent on one another.
-				FrameVector<SPtr<CoreObject>> dependencies;
-				curObj->getCoreDependencies(dependencies);
-
-				for (auto& dependency : dependencies)
-					syncObject(dependency.get());
+				
+				UINT64 id = curObj->getInternalID();
+				auto iterFind = mDependencies.find(id);
 
 
-				if (curObj->isCoreDirty())
+				if (iterFind != mDependencies.end())
 				{
 				{
-					SPtr<CoreObjectCore> objectCore = curObj->getCore();
-					if (objectCore == nullptr)
-						return;
+					const Vector<CoreObject*>& dependencies = iterFind->second;
+					for (auto& dependency : dependencies)
+						syncObject(dependency);
+				}
 
 
-					CoreSyncData objSyncData = curObj->syncToCore(allocator);
+				SPtr<CoreObjectCore> objectCore = curObj->getCore();
+				if (objectCore == nullptr)
+				{
 					curObj->markCoreClean();
 					curObj->markCoreClean();
-
-					syncData.entries.push_back(CoreStoredSyncObjData(objectCore.get(),
-						curObj->getInternalID(), objSyncData));
+					return;
 				}
 				}
+
+				CoreSyncData objSyncData = curObj->syncToCore(allocator);
+				curObj->markCoreClean();
+
+				syncData.entries.push_back(CoreStoredSyncObjData(objectCore,
+					curObj->getInternalID(), objSyncData));
 			};
 			};
 
 
-			CoreObject* object = objectData.second;
-			syncObject(object);
+			CoreObject* object = objectData.second.object;
+			if (object != nullptr)
+				syncObject(object);
+			else
+			{
+				// Object was destroyed but we still need to sync its modifications before it was destroyed
+				if (objectData.second.syncDataId != -1)
+					syncData.entries.push_back(mDestroyedSyncData[objectData.second.syncDataId]);
+			}
 		}
 		}
-		bs_frame_clear();
+
+		mDirtyObjects.clear();
+		mDestroyedSyncData.clear();
 	}
 	}
 
 
 	void CoreObjectManager::syncUpload()
 	void CoreObjectManager::syncUpload()

+ 2 - 2
BansheeCore/Source/BsFont.cpp

@@ -95,14 +95,14 @@ namespace BansheeEngine
 		}
 		}
 	}
 	}
 
 
-	void Font::getCoreDependencies(FrameVector<SPtr<CoreObject>>& dependencies)
+	void Font::getCoreDependencies(Vector<CoreObject*>& dependencies)
 	{
 	{
 		for (auto& fontDataEntry : mFontDataPerSize)
 		for (auto& fontDataEntry : mFontDataPerSize)
 		{
 		{
 			for (auto& texture : fontDataEntry.second->texturePages)
 			for (auto& texture : fontDataEntry.second->texturePages)
 			{
 			{
 				if (texture.isLoaded())
 				if (texture.isLoaded())
-					dependencies.push_back(texture.getInternalPtr());
+					dependencies.push_back(texture.get());
 			}
 			}
 		}
 		}
 	}
 	}

+ 16 - 6
BansheeCore/Source/BsGpuParams.cpp

@@ -141,17 +141,27 @@ UINT32 GpuParamsBase::getDataParamSize(const String& name) const
 		:GpuParamsBase(paramDesc, transposeMatrices), mParamBlockBuffers(nullptr), mTextures(nullptr),
 		:GpuParamsBase(paramDesc, transposeMatrices), mParamBlockBuffers(nullptr), mTextures(nullptr),
 		mSamplerStates(nullptr)
 		mSamplerStates(nullptr)
 	{
 	{
-		mParamBlockBuffers = bs_newN<ParamsBufferType>(mNumParamBlocks);
-		mTextures = bs_newN<TextureType>(mNumTextures);
-		mSamplerStates = bs_newN<SamplerType>(mNumSamplerStates);
+		if (mNumParamBlocks > 0)
+			mParamBlockBuffers = bs_newN<ParamsBufferType>(mNumParamBlocks);
+
+		if (mNumTextures > 0)
+			mTextures = bs_newN<TextureType>(mNumTextures);
+
+		if (mNumSamplerStates > 0)
+			mSamplerStates = bs_newN<SamplerType>(mNumSamplerStates);
 	}
 	}
 
 
 	template<bool Core>
 	template<bool Core>
 	TGpuParams<Core>::~TGpuParams()
 	TGpuParams<Core>::~TGpuParams()
 	{
 	{
-		bs_deleteN(mParamBlockBuffers, mNumParamBlocks);
-		bs_deleteN(mTextures, mNumTextures);
-		bs_deleteN(mSamplerStates, mNumSamplerStates);
+		if (mParamBlockBuffers != nullptr)
+			bs_deleteN(mParamBlockBuffers, mNumParamBlocks);
+
+		if (mTextures != nullptr)
+			bs_deleteN(mTextures, mNumTextures);
+
+		if (mSamplerStates != nullptr)
+			bs_deleteN(mSamplerStates, mNumSamplerStates);
 	}
 	}
 
 
 	template<bool Core>
 	template<bool Core>

+ 4 - 3
BansheeCore/Source/BsMaterial.cpp

@@ -1167,10 +1167,10 @@ namespace BansheeEngine
 		return CoreSyncData(buffer, size);
 		return CoreSyncData(buffer, size);
 	}
 	}
 
 
-	void Material::getCoreDependencies(FrameVector<SPtr<CoreObject>>& dependencies)
+	void Material::getCoreDependencies(Vector<CoreObject*>& dependencies)
 	{
 	{
 		if (mShader.isLoaded())
 		if (mShader.isLoaded())
-			dependencies.push_back(mShader.getInternalPtr());
+			dependencies.push_back(mShader.get());
 
 
 		for (auto& params : mParametersPerPass)
 		for (auto& params : mParametersPerPass)
 		{
 		{
@@ -1178,7 +1178,7 @@ namespace BansheeEngine
 			{
 			{
 				GpuParamsPtr gpuParams = params->getParamByIdx(i);
 				GpuParamsPtr gpuParams = params->getParamByIdx(i);
 				if (gpuParams != nullptr)
 				if (gpuParams != nullptr)
-					dependencies.push_back(gpuParams);
+					dependencies.push_back(gpuParams.get());
 			}
 			}
 		}
 		}
 	}
 	}
@@ -1228,6 +1228,7 @@ namespace BansheeEngine
 				mLoadFlags = Load_All;
 				mLoadFlags = Load_All;
 
 
 				initBestTechnique();
 				initBestTechnique();
+				markDependenciesDirty();
 				markCoreDirty();
 				markCoreDirty();
 			}
 			}
 		}
 		}

+ 10 - 10
BansheeCore/Source/BsPass.cpp

@@ -124,34 +124,34 @@ namespace BansheeEngine
 		return CoreSyncData(data, size);
 		return CoreSyncData(data, size);
 	}
 	}
 
 
-	void Pass::getCoreDependencies(FrameVector<SPtr<CoreObject>>& dependencies)
+	void Pass::getCoreDependencies(Vector<CoreObject*>& dependencies)
 	{
 	{
 		if (mData.blendState != nullptr)
 		if (mData.blendState != nullptr)
-			dependencies.push_back(mData.blendState);
+			dependencies.push_back(mData.blendState.get());
 
 
 		if (mData.rasterizerState != nullptr)
 		if (mData.rasterizerState != nullptr)
-			dependencies.push_back(mData.rasterizerState);
+			dependencies.push_back(mData.rasterizerState.get());
 
 
 		if (mData.depthStencilState != nullptr)
 		if (mData.depthStencilState != nullptr)
-			dependencies.push_back(mData.depthStencilState);
+			dependencies.push_back(mData.depthStencilState.get());
 
 
 		if (mData.vertexProgram != nullptr)
 		if (mData.vertexProgram != nullptr)
-			dependencies.push_back(mData.vertexProgram);
+			dependencies.push_back(mData.vertexProgram.get());
 
 
 		if (mData.fragmentProgram != nullptr)
 		if (mData.fragmentProgram != nullptr)
-			dependencies.push_back(mData.fragmentProgram);
+			dependencies.push_back(mData.fragmentProgram.get());
 
 
 		if (mData.geometryProgram != nullptr)
 		if (mData.geometryProgram != nullptr)
-			dependencies.push_back(mData.geometryProgram);
+			dependencies.push_back(mData.geometryProgram.get());
 
 
 		if (mData.hullProgram != nullptr)
 		if (mData.hullProgram != nullptr)
-			dependencies.push_back(mData.hullProgram);
+			dependencies.push_back(mData.hullProgram.get());
 
 
 		if (mData.domainProgram != nullptr)
 		if (mData.domainProgram != nullptr)
-			dependencies.push_back(mData.domainProgram);
+			dependencies.push_back(mData.domainProgram.get());
 
 
 		if (mData.computeProgram != nullptr)
 		if (mData.computeProgram != nullptr)
-			dependencies.push_back(mData.computeProgram);
+			dependencies.push_back(mData.computeProgram.get());
 	}
 	}
 
 
 	PassPtr Pass::create(const PASS_DESC& desc)
 	PassPtr Pass::create(const PASS_DESC& desc)

+ 2 - 2
BansheeCore/Source/BsShader.cpp

@@ -369,10 +369,10 @@ namespace BansheeEngine
 		return output;
 		return output;
 	}
 	}
 
 
-	void Shader::getCoreDependencies(FrameVector<SPtr<CoreObject>>& dependencies)
+	void Shader::getCoreDependencies(Vector<CoreObject*>& dependencies)
 	{
 	{
 		for (auto& technique : mTechniques)
 		for (auto& technique : mTechniques)
-			dependencies.push_back(technique);
+			dependencies.push_back(technique.get());
 	}
 	}
 
 
 	bool Shader::isSampler(GpuParamObjectType type)
 	bool Shader::isSampler(GpuParamObjectType type)

+ 2 - 2
BansheeCore/Source/BsTechnique.cpp

@@ -90,10 +90,10 @@ namespace BansheeEngine
 		return techniquePtr;
 		return techniquePtr;
 	}
 	}
 
 
-	void Technique::getCoreDependencies(FrameVector<SPtr<CoreObject>>& dependencies)
+	void Technique::getCoreDependencies(Vector<CoreObject*>& dependencies)
 	{
 	{
 		for (auto& pass : mPasses)
 		for (auto& pass : mPasses)
-			dependencies.push_back(pass);
+			dependencies.push_back(pass.get());
 	}
 	}
 
 
 	TechniquePtr Technique::create(const StringID& renderAPI, const StringID& renderer, const Vector<SPtr<Pass>>& passes)
 	TechniquePtr Technique::create(const StringID& renderAPI, const StringID& renderer, const Vector<SPtr<Pass>>& passes)

+ 10 - 2
BansheeCore/Source/BsViewport.cpp

@@ -133,6 +133,14 @@ namespace BansheeEngine
 
 
 	}
 	}
 
 
+	void Viewport::setTarget(const RenderTargetPtr& target)
+	{
+		mTarget = target; 
+		
+		markDependenciesDirty();
+		_markCoreDirty();
+	}
+
 	SPtr<ViewportCore> Viewport::getCore() const
 	SPtr<ViewportCore> Viewport::getCore() const
 	{
 	{
 		return std::static_pointer_cast<ViewportCore>(mCoreSpecific);
 		return std::static_pointer_cast<ViewportCore>(mCoreSpecific);
@@ -200,10 +208,10 @@ namespace BansheeEngine
 		return CoreSyncData(buffer, size);
 		return CoreSyncData(buffer, size);
 	}
 	}
 
 
-	void Viewport::getCoreDependencies(FrameVector<SPtr<CoreObject>>& dependencies)
+	void Viewport::getCoreDependencies(Vector<CoreObject*>& dependencies)
 	{
 	{
 		if (mTarget != nullptr)
 		if (mTarget != nullptr)
-			dependencies.push_back(mTarget);
+			dependencies.push_back(mTarget.get());
 	}
 	}
 
 
 	ViewportPtr Viewport::create(const RenderTargetPtr& target, float x, float y, float width, float height)
 	ViewportPtr Viewport::create(const RenderTargetPtr& target, float x, float y, float width, float height)

+ 1 - 1
BansheeEngine/Include/BsCamera.h

@@ -607,7 +607,7 @@ namespace BansheeEngine
 		/**
 		/**
 		 * @copydoc	CoreObject::getCoreDependencies
 		 * @copydoc	CoreObject::getCoreDependencies
 		 */
 		 */
-		void getCoreDependencies(FrameVector<SPtr<CoreObject>>& dependencies) override;
+		void getCoreDependencies(Vector<CoreObject*>& dependencies) override;
 
 
 		/**
 		/**
 		 * @brief	Creates a new camera without initializing it.
 		 * @brief	Creates a new camera without initializing it.

+ 16 - 6
BansheeEngine/Include/BsRenderable.h

@@ -145,6 +145,11 @@ namespace BansheeEngine
 		 */
 		 */
 		virtual void _markCoreDirty(RenderableDirtyFlag flag = RenderableDirtyFlag::Everything) { }
 		virtual void _markCoreDirty(RenderableDirtyFlag flag = RenderableDirtyFlag::Everything) { }
 
 
+		/**
+		 * @copydoc	CoreObject::markDependenciesDirty
+		 */
+		virtual void _markDependenciesDirty() { }
+
 		/**
 		/**
 		 * @copydoc	IResourceListener::markResourcesDirty
 		 * @copydoc	IResourceListener::markResourcesDirty
 		 */
 		 */
@@ -251,10 +256,15 @@ namespace BansheeEngine
 		void _markCoreDirty(RenderableDirtyFlag flag = RenderableDirtyFlag::Everything) override;
 		void _markCoreDirty(RenderableDirtyFlag flag = RenderableDirtyFlag::Everything) override;
 
 
 		/**
 		/**
-		 * @copydoc	IResourceListener::markResourcesDirty
-		 */
+		* @copydoc	IResourceListener::markResourcesDirty
+		*/
 		void _markResourcesDirty() override;
 		void _markResourcesDirty() override;
 
 
+		/**
+		 * @copydoc	CoreObject::markDependenciesDirty
+		 */
+		void _markDependenciesDirty() override;
+
 		/**
 		/**
 		 * @copydoc	CoreObject::syncToCore
 		 * @copydoc	CoreObject::syncToCore
 		 */
 		 */
@@ -263,7 +273,7 @@ namespace BansheeEngine
 		/**
 		/**
 		 * @copydoc	CoreObject::getCoreDependencies
 		 * @copydoc	CoreObject::getCoreDependencies
 		 */
 		 */
-		void getCoreDependencies(FrameVector<SPtr<CoreObject>>& dependencies) override;
+		void getCoreDependencies(Vector<CoreObject*>& dependencies) override;
 
 
 		/**
 		/**
 		 * @copydoc	IResourceListener::getListenerResources
 		 * @copydoc	IResourceListener::getListenerResources
@@ -273,17 +283,17 @@ namespace BansheeEngine
 		/**
 		/**
 		 * @copydoc IResourceListener::notifyResourceLoaded
 		 * @copydoc IResourceListener::notifyResourceLoaded
 		 */
 		 */
-		void notifyResourceLoaded(const HResource& resource) override { markCoreDirty(); }
+		void notifyResourceLoaded(const HResource& resource) override;
 
 
 		/**
 		/**
 		 * @copydoc IResourceListener::notifyResourceDestroyed
 		 * @copydoc IResourceListener::notifyResourceDestroyed
 		 */
 		 */
-		void notifyResourceDestroyed(const HResource& resource) override { markCoreDirty(); }
+		void notifyResourceDestroyed(const HResource& resource) override;
 
 
 		/**
 		/**
 		 * @copydoc IResourceListener::notifyResourceChanged
 		 * @copydoc IResourceListener::notifyResourceChanged
 		 */
 		 */
-		void notifyResourceChanged(const HResource& resource) override { markCoreDirty(); }
+		void notifyResourceChanged(const HResource& resource) override;
 
 
 		/**
 		/**
 		 * @brief	Creates a new renderable handler instance without initializing it.
 		 * @brief	Creates a new renderable handler instance without initializing it.

+ 1 - 1
BansheeEngine/Include/BsSpriteTexture.h

@@ -105,7 +105,7 @@ namespace BansheeEngine
 		/**
 		/**
 		 * @copydoc	CoreObject::getCoreDependencies
 		 * @copydoc	CoreObject::getCoreDependencies
 		 */
 		 */
-		void getCoreDependencies(FrameVector<SPtr<CoreObject>>& dependencies) override;
+		void getCoreDependencies(Vector<CoreObject*>& dependencies) override;
 
 
 		HTexture mAtlasTexture;
 		HTexture mAtlasTexture;
 		Vector2 mUVOffset;
 		Vector2 mUVOffset;

+ 2 - 2
BansheeEngine/Source/BsCamera.cpp

@@ -818,9 +818,9 @@ namespace BansheeEngine
 		return CoreSyncData(buffer, size);
 		return CoreSyncData(buffer, size);
 	}
 	}
 
 
-	void Camera::getCoreDependencies(FrameVector<SPtr<CoreObject>>& dependencies)
+	void Camera::getCoreDependencies(Vector<CoreObject*>& dependencies)
 	{
 	{
-		dependencies.push_back(mViewport);
+		dependencies.push_back(mViewport.get());
 	}
 	}
 
 
 	void Camera::_markCoreDirty()
 	void Camera::_markCoreDirty()

+ 29 - 3
BansheeEngine/Source/BsRenderable.cpp

@@ -36,6 +36,7 @@ namespace BansheeEngine
 
 
 		mMaterials.resize(numSubMeshes);
 		mMaterials.resize(numSubMeshes);
 
 
+		_markDependenciesDirty();
 		_markResourcesDirty();
 		_markResourcesDirty();
 		_markCoreDirty();
 		_markCoreDirty();
 	}
 	}
@@ -48,6 +49,7 @@ namespace BansheeEngine
 
 
 		mMaterials[idx] = material;
 		mMaterials[idx] = material;
 
 
+		_markDependenciesDirty();
 		_markResourcesDirty();
 		_markResourcesDirty();
 		_markCoreDirty();
 		_markCoreDirty();
 	}
 	}
@@ -64,6 +66,7 @@ namespace BansheeEngine
 		for (UINT32 i = min; i < numMaterials; i++)
 		for (UINT32 i = min; i < numMaterials; i++)
 			mMaterials[i] = nullptr;
 			mMaterials[i] = nullptr;
 
 
+		_markDependenciesDirty();
 		_markResourcesDirty();
 		_markResourcesDirty();
 		_markCoreDirty();
 		_markCoreDirty();
 	}
 	}
@@ -250,6 +253,11 @@ namespace BansheeEngine
 		markCoreDirty((UINT32)flag);
 		markCoreDirty((UINT32)flag);
 	}
 	}
 
 
+	void Renderable::_markDependenciesDirty()
+	{
+		markDependenciesDirty();
+	}
+
 	void Renderable::_markResourcesDirty()
 	void Renderable::_markResourcesDirty()
 	{
 	{
 		markListenerResourcesDirty();
 		markListenerResourcesDirty();
@@ -297,15 +305,15 @@ namespace BansheeEngine
 		return CoreSyncData(data, size);
 		return CoreSyncData(data, size);
 	}
 	}
 
 
-	void Renderable::getCoreDependencies(FrameVector<SPtr<CoreObject>>& dependencies)
+	void Renderable::getCoreDependencies(Vector<CoreObject*>& dependencies)
 	{
 	{
 		if (mMesh.isLoaded())
 		if (mMesh.isLoaded())
-			dependencies.push_back(mMesh.getInternalPtr());
+			dependencies.push_back(mMesh.get());
 
 
 		for (auto& material : mMaterials)
 		for (auto& material : mMaterials)
 		{
 		{
 			if (material.isLoaded())
 			if (material.isLoaded())
-				dependencies.push_back(material.getInternalPtr());
+				dependencies.push_back(material.get());
 		}
 		}
 	}
 	}
 
 
@@ -321,6 +329,24 @@ namespace BansheeEngine
 		}
 		}
 	}
 	}
 
 
+	void Renderable::notifyResourceLoaded(const HResource& resource)
+	{
+		markDependenciesDirty();
+		markCoreDirty();
+	}
+
+	void Renderable::notifyResourceDestroyed(const HResource& resource)
+	{
+		markDependenciesDirty();
+		markCoreDirty();
+	}
+
+	void Renderable::notifyResourceChanged(const HResource& resource)
+	{
+		markDependenciesDirty();
+		markCoreDirty();
+	}
+
 	RenderablePtr Renderable::create()
 	RenderablePtr Renderable::create()
 	{
 	{
 		RenderablePtr handlerPtr = createEmpty();
 		RenderablePtr handlerPtr = createEmpty();

+ 4 - 2
BansheeEngine/Source/BsSpriteTexture.cpp

@@ -19,6 +19,8 @@ namespace BansheeEngine
 	void SpriteTexture::setTexture(const HTexture& texture)
 	void SpriteTexture::setTexture(const HTexture& texture)
 	{
 	{
 		mAtlasTexture = texture;
 		mAtlasTexture = texture;
+
+		markDependenciesDirty();
 	}
 	}
 
 
 	Vector2 SpriteTexture::transformUV(const Vector2& uv) const
 	Vector2 SpriteTexture::transformUV(const Vector2& uv) const
@@ -54,10 +56,10 @@ namespace BansheeEngine
 			dependencies.push_back(mAtlasTexture);
 			dependencies.push_back(mAtlasTexture);
 	}
 	}
 
 
-	void SpriteTexture::getCoreDependencies(FrameVector<SPtr<CoreObject>>& dependencies)
+	void SpriteTexture::getCoreDependencies(Vector<CoreObject*>& dependencies)
 	{
 	{
 		if (mAtlasTexture.isLoaded())
 		if (mAtlasTexture.isLoaded())
-			dependencies.push_back(mAtlasTexture.getInternalPtr());
+			dependencies.push_back(mAtlasTexture.get());
 	}
 	}
 
 
 	HSpriteTexture SpriteTexture::create(const HTexture& texture)
 	HSpriteTexture SpriteTexture::create(const HTexture& texture)