Browse Source

Added IResourceListener to properly handle async resource loading and notifying the core thread

Marko Pintera 11 years ago
parent
commit
1039be94b5

+ 4 - 0
BansheeCore/BansheeCore.vcxproj

@@ -280,6 +280,7 @@
   <ItemGroup>
   <ItemGroup>
     <ClInclude Include="Include\BsCoreObjectCore.h" />
     <ClInclude Include="Include\BsCoreObjectCore.h" />
     <ClInclude Include="Include\BsDrawList.h" />
     <ClInclude Include="Include\BsDrawList.h" />
+    <ClInclude Include="Include\BsIResourceListener.h" />
     <ClInclude Include="Include\BsMaterialParam.h" />
     <ClInclude Include="Include\BsMaterialParam.h" />
     <ClInclude Include="Include\BsRenderStats.h" />
     <ClInclude Include="Include\BsRenderStats.h" />
     <ClInclude Include="Include\BsCoreThread.h" />
     <ClInclude Include="Include\BsCoreThread.h" />
@@ -310,6 +311,7 @@
     <ClInclude Include="Include\BsPlatform.h" />
     <ClInclude Include="Include\BsPlatform.h" />
     <ClInclude Include="Include\BsProfilingManager.h" />
     <ClInclude Include="Include\BsProfilingManager.h" />
     <ClInclude Include="Include\BsQueryManager.h" />
     <ClInclude Include="Include\BsQueryManager.h" />
+    <ClInclude Include="Include\BsResourceListenerManager.h" />
     <ClInclude Include="Include\BsResourceManifest.h" />
     <ClInclude Include="Include\BsResourceManifest.h" />
     <ClInclude Include="Include\BsResourceManifestRTTI.h" />
     <ClInclude Include="Include\BsResourceManifestRTTI.h" />
     <ClInclude Include="Include\BsResourceMetaData.h" />
     <ClInclude Include="Include\BsResourceMetaData.h" />
@@ -425,6 +427,7 @@
     <ClCompile Include="Source\BsCoreObjectCore.cpp" />
     <ClCompile Include="Source\BsCoreObjectCore.cpp" />
     <ClCompile Include="Source\BsCoreThread.cpp" />
     <ClCompile Include="Source\BsCoreThread.cpp" />
     <ClCompile Include="Source\BsDrawList.cpp" />
     <ClCompile Include="Source\BsDrawList.cpp" />
+    <ClCompile Include="Source\BsIResourceListener.cpp" />
     <ClCompile Include="Source\BsMaterialParam.cpp" />
     <ClCompile Include="Source\BsMaterialParam.cpp" />
     <ClCompile Include="Source\BsProfilerCPU.cpp" />
     <ClCompile Include="Source\BsProfilerCPU.cpp" />
     <ClCompile Include="Source\BsDeferredCallManager.cpp" />
     <ClCompile Include="Source\BsDeferredCallManager.cpp" />
@@ -472,6 +475,7 @@
     <ClCompile Include="Source\BsProfilingManager.cpp" />
     <ClCompile Include="Source\BsProfilingManager.cpp" />
     <ClCompile Include="Source\BsQueryManager.cpp" />
     <ClCompile Include="Source\BsQueryManager.cpp" />
     <ClCompile Include="Source\BsCoreRenderer.cpp" />
     <ClCompile Include="Source\BsCoreRenderer.cpp" />
+    <ClCompile Include="Source\BsResourceListenerManager.cpp" />
     <ClCompile Include="Source\BsResourceManifest.cpp" />
     <ClCompile Include="Source\BsResourceManifest.cpp" />
     <ClCompile Include="Source\BsResourceMetaData.cpp" />
     <ClCompile Include="Source\BsResourceMetaData.cpp" />
     <ClCompile Include="Source\BsTextureImportOptions.cpp" />
     <ClCompile Include="Source\BsTextureImportOptions.cpp" />

+ 12 - 0
BansheeCore/BansheeCore.vcxproj.filters

@@ -516,6 +516,12 @@
     <ClInclude Include="Include\BsRenderAPICapabilities.h">
     <ClInclude Include="Include\BsRenderAPICapabilities.h">
       <Filter>Header Files\RenderAPI</Filter>
       <Filter>Header Files\RenderAPI</Filter>
     </ClInclude>
     </ClInclude>
+    <ClInclude Include="Include\BsIResourceListener.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsResourceListenerManager.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsCoreApplication.cpp">
     <ClCompile Include="Source\BsCoreApplication.cpp">
@@ -812,5 +818,11 @@
     <ClCompile Include="Source\BsRenderAPICapabilities.cpp">
     <ClCompile Include="Source\BsRenderAPICapabilities.cpp">
       <Filter>Source Files\RenderSystem</Filter>
       <Filter>Source Files\RenderSystem</Filter>
     </ClCompile>
     </ClCompile>
+    <ClCompile Include="Source\BsIResourceListener.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsResourceListenerManager.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   </ItemGroup>
 </Project>
 </Project>

+ 1 - 1
BansheeCore/Include/BsCoreObject.h

@@ -70,7 +70,7 @@ namespace BansheeEngine
 		 * @note	If you call this without calling initialize first a deadlock will occur.
 		 * @note	If you call this without calling initialize first a deadlock will occur.
 		 * 			You should not call this from core thread.
 		 * 			You should not call this from core thread.
 		 */
 		 */
-		void synchronize();
+		void blockUntilCoreInitialized();
 
 
 		/**
 		/**
 		 * @brief	Internal method. Sets a shared this pointer to this object. This MUST be called immediately after construction.
 		 * @brief	Internal method. Sets a shared this pointer to this object. This MUST be called immediately after construction.

+ 1 - 0
BansheeCore/Include/BsCorePrerequisites.h

@@ -157,6 +157,7 @@ namespace BansheeEngine
 	class TechniqueCore;
 	class TechniqueCore;
 	class MaterialCore;
 	class MaterialCore;
 	class GpuProgramCore;
 	class GpuProgramCore;
+	class IResourceListener;
 	// Asset import
 	// Asset import
 	class SpecificImporter;
 	class SpecificImporter;
 	class Importer;
 	class Importer;

+ 36 - 1
BansheeCore/Include/BsGpuParams.h

@@ -3,6 +3,7 @@
 #include "BsCorePrerequisites.h"
 #include "BsCorePrerequisites.h"
 #include "BsGpuParam.h"
 #include "BsGpuParam.h"
 #include "BsCoreObject.h"
 #include "BsCoreObject.h"
+#include "BsIResourceListener.h"
 
 
 #include "BsDebug.h"
 #include "BsDebug.h"
 
 
@@ -114,6 +115,13 @@ namespace BansheeEngine
 		 */
 		 */
 		virtual void _markCoreDirty() { }
 		virtual void _markCoreDirty() { }
 
 
+		/**
+		 * @copydoc	IResourceListener::markResourcesDirty
+		 *
+		 * @note	Internal method.
+		 */
+		virtual void _markResourcesDirty() { }
+
 	protected:
 	protected:
 		/**
 		/**
 		 * @brief	Gets a descriptor for a data parameter with the specified name.
 		 * @brief	Gets a descriptor for a data parameter with the specified name.
@@ -311,7 +319,7 @@ namespace BansheeEngine
 	 *
 	 *
 	 * @note	Sim thread only.
 	 * @note	Sim thread only.
 	 */
 	 */
-	class BS_CORE_EXPORT GpuParams : public CoreObject, public TGpuParams<false>
+	class BS_CORE_EXPORT GpuParams : public CoreObject, public TGpuParams<false>, public IResourceListener
 	{
 	{
 	public:
 	public:
 		~GpuParams() { }
 		~GpuParams() { }
@@ -323,6 +331,13 @@ namespace BansheeEngine
 		 */
 		 */
 		void _markCoreDirty();
 		void _markCoreDirty();
 
 
+		/**
+		 * @copydoc	IResourceListener::markResourcesDirty
+		 *
+		 * @note	Internal method.
+		 */
+		void _markResourcesDirty();
+
 		/**
 		/**
 		 * @brief	Retrieves a core implementation of a mesh usable only from the
 		 * @brief	Retrieves a core implementation of a mesh usable only from the
 		 *			core thread.
 		 *			core thread.
@@ -354,5 +369,25 @@ namespace BansheeEngine
 		 * @copydoc	CoreObject::syncToCore
 		 * @copydoc	CoreObject::syncToCore
 		 */
 		 */
 		CoreSyncData syncToCore(FrameAlloc* allocator);
 		CoreSyncData syncToCore(FrameAlloc* allocator);
+
+		/**
+		 * @copydoc	IResourceListener::getResourceDependencies
+		 */
+		void getResourceDependencies(Vector<HResource>& resources);
+
+		/**
+		 * @copydoc IResourceListener::notifyResourceLoaded
+		 */
+		void notifyResourceLoaded(const HResource& resource) { markCoreDirty(); }
+
+		/**
+		 * @copydoc IResourceListener::notifyResourceDestroyed
+		 */
+		void notifyResourceDestroyed(const HResource& resource) { markCoreDirty(); }
+
+		/**
+		 * @copydoc IResourceListener::notifyResourceChanged
+		 */
+		void notifyResourceChanged(const HResource& resource) { markCoreDirty(); }
 	};
 	};
 }
 }

+ 49 - 0
BansheeCore/Include/BsIResourceListener.h

@@ -0,0 +1,49 @@
+#pragma once
+
+#include "BsCorePrerequisites.h"
+
+namespace BansheeEngine
+{
+	/**
+	 * @brief	Interface that allows the implementing class to be notified
+	 *			when the resources it is referencing change.
+	 */
+	class BS_CORE_EXPORT IResourceListener
+	{
+	public:
+		IResourceListener();
+		virtual ~IResourceListener();
+
+	protected:
+		friend class ResourceListenerManager;
+
+		/**
+		 * @brief	Retrieves all the resources that the class depends on.
+		 *
+		 * @note	You must add the resources to the provided "resources" array.
+		 */
+		virtual void getResourceDependencies(Vector<HResource>& resources) = 0;
+
+		/**
+		 * @brief	Marks the resource dependencies list as dirty and schedules it for rebuild.
+		 */
+		virtual void markResourcesDirty();
+
+		/**
+		 * @brief	Called when a resource has been fully loaded.
+		 */
+		virtual void notifyResourceLoaded(const HResource& resource) { }
+
+		/**
+		 * @brief	Called when the internal resource the resource handle 
+		 *			is pointing to changes.
+		 */
+		virtual void notifyResourceChanged(const HResource& resource) { }
+
+		/**
+		 * @brief	Called just before the resource handle is destroyed and resource
+		 *			unloaded.
+		 */
+		virtual void notifyResourceDestroyed(const HResource& resource) { }
+	};
+}

+ 47 - 1
BansheeCore/Include/BsPass.h

@@ -4,6 +4,7 @@
 #include "BsColor.h"
 #include "BsColor.h"
 #include "BsIReflectable.h"
 #include "BsIReflectable.h"
 #include "BsCoreObject.h"
 #include "BsCoreObject.h"
+#include "BsIResourceListener.h"
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
@@ -126,6 +127,11 @@ namespace BansheeEngine
 		TPass();
 		TPass();
 		TPass(const PassDescType& desc);
 		TPass(const PassDescType& desc);
 
 
+		/**
+		 * @copydoc	IResourceListener::markResourcesDirty
+		 */
+		virtual void _markResourcesDirty() { }
+
 		PassDescType mData;
 		PassDescType mData;
     };
     };
 
 
@@ -150,6 +156,11 @@ namespace BansheeEngine
 
 
 		PassCore() { }
 		PassCore() { }
 		PassCore(const PASS_DESC_CORE& desc);
 		PassCore(const PASS_DESC_CORE& desc);
+
+		/**
+		 * @copydoc	CoreObjectCore::syncToCore
+		 */
+		void syncToCore(const CoreSyncData& data);
     };
     };
 
 
 	/**
 	/**
@@ -157,7 +168,7 @@ namespace BansheeEngine
 	 *
 	 *
 	 * @note	Sim thread.
 	 * @note	Sim thread.
 	 */
 	 */
-	class BS_CORE_EXPORT Pass : public IReflectable, public CoreObject, public TPass<false>
+	class BS_CORE_EXPORT Pass : public IReflectable, public CoreObject, public TPass<false>, public IResourceListener
     {
     {
     public:
     public:
 		virtual ~Pass() { }
 		virtual ~Pass() { }
@@ -179,11 +190,46 @@ namespace BansheeEngine
 		Pass() { }
 		Pass() { }
 		Pass(const PASS_DESC& desc);
 		Pass(const PASS_DESC& desc);
 
 
+		/**
+		 * @copydoc	CoreObject::syncToCore
+		 */
+		CoreSyncData syncToCore(FrameAlloc* allocator);
+
+		/**
+		 * @copydoc	IResourceListener::markResourcesDirty
+		 */
+		void _markResourcesDirty();
+
+		/**
+		* @copydoc	IResourceListener::getResourceDependencies
+		*/
+		void getResourceDependencies(Vector<HResource>& resources);
+
+		/**
+		* @copydoc IResourceListener::notifyResourceLoaded
+		*/
+		void notifyResourceLoaded(const HResource& resource) { markCoreDirty(); }
+
+		/**
+		* @copydoc IResourceListener::notifyResourceDestroyed
+		*/
+		void notifyResourceDestroyed(const HResource& resource) { markCoreDirty(); }
+
+		/**
+		* @copydoc IResourceListener::notifyResourceChanged
+		*/
+		void notifyResourceChanged(const HResource& resource) { markCoreDirty(); }
+
 		/**
 		/**
 		 * @copydoc	CoreObject::createCore
 		 * @copydoc	CoreObject::createCore
 		 */
 		 */
 		SPtr<CoreObjectCore> createCore() const;
 		SPtr<CoreObjectCore> createCore() const;
 
 
+		/**
+		 * @copydoc	CoreObject::initialize
+		 */
+		virtual void initialize();
+
 		/**
 		/**
 		 * @brief	Creates a new empty pass but doesn't initialize it.
 		 * @brief	Creates a new empty pass but doesn't initialize it.
 		 */
 		 */

+ 3 - 3
BansheeCore/Include/BsResourceHandle.h

@@ -36,11 +36,11 @@ namespace BansheeEngine
 		bool isLoaded() const;
 		bool isLoaded() const;
 
 
 		/**
 		/**
-		 * @brief	Blocks the current thread until the resource is fully loaded AND initialized.
+		 * @brief	Blocks the current thread until the resource is fully loaded.
 		 * 			
 		 * 			
-		 * @note	Careful not to call this on the thread that does the loading or initializing.
+		 * @note	Careful not to call this on the thread that does the loading.
 		 */
 		 */
-		void synchronize() const;
+		void blockUntilLoaded() const;
 
 
 		/**
 		/**
 		 * @brief	Returns the UUID of the resource the handle is referring to.
 		 * @brief	Returns the UUID of the resource the handle is referring to.

+ 87 - 0
BansheeCore/Include/BsResourceListenerManager.h

@@ -0,0 +1,87 @@
+#pragma once
+
+#include "BsCorePrerequisites.h"
+#include "BsModule.h"
+
+namespace BansheeEngine
+{
+	/**
+	 * @brief	Handles all active implementations of IResourceListener interface
+	 *			and notifies them when events they're listening to occur.
+	 *
+	 * @see		IResourceListener
+	 */
+	class BS_CORE_EXPORT ResourceListenerManager : public Module<ResourceListenerManager>
+	{
+	public:
+		ResourceListenerManager();
+		~ResourceListenerManager();
+
+		/**
+		 * @brief	Register a new listener to notify for events.
+		 */
+		void registerListener(IResourceListener* listener);
+
+		/**
+		 * @brief	Unregister a listener so it will no longer receive notifications.
+		 */
+		void unregisterListener(IResourceListener* listener);
+
+		/**
+		 * @brief	Marks the listener as dirty which forces the manager to updates
+		 *			its internal list of resources for the listener.
+		 */
+		void markListenerDirty(IResourceListener* listener);
+
+		/**
+		 * @brief	Refreshes the resource maps based on dirty listeners and sends out the necessary events.
+		 */
+		void update();
+
+	private:
+		/**
+		 * @brief	Triggered by the resources system when a resource has finished loading.
+		 */
+		void onResourceLoaded(const HResource& resource);
+
+		/**
+		 * @brief	Triggered by the resources system just before a resource is unloaded and destroyed.
+		 */
+		void onResourceDestroyed(const HResource& resource);
+
+		/**
+		 * @brief	Sends resource loaded event to all listeners referencing this resource.
+		 */
+		void sendResourceLoaded(const HResource& resource);
+
+		/**
+		 * @brief	Sends resource destroyed event to all listeners referencing this resource.
+		 */
+		void sendResourceDestroyed(const HResource& resource);
+
+		/**
+		 * @brief	Clears all the stored dependencies for the listener.
+		 */
+		void clearDependencies(IResourceListener* listener);
+
+		/**
+		 * @brief	Registers all the resource dependencies for the listener.
+		 */
+		void addDependencies(IResourceListener* listener);
+
+		HEvent mResourceLoadedConn;
+		HEvent mResourceDestroyedConn;
+
+		Set<IResourceListener*> mActiveListeners;
+		Set<IResourceListener*> mDirtyListeners;
+		Map<UINT64, Vector<IResourceListener*>> mResourceToListenerMap;
+		Map<IResourceListener*, Vector<UINT64>> mListenerToResourceMap;
+
+		Vector<HResource> mLoadedResources;
+		Vector<HResource> mDestroyedResources;
+
+		Vector<HResource> mTempResourceBuffer;
+
+		BS_MUTEX(mMutex)
+	};
+}

+ 22 - 0
BansheeCore/Include/BsResources.h

@@ -145,6 +145,28 @@ namespace BansheeEngine
 		 */
 		 */
 		bool getUUIDFromFilePath(const Path& path, String& uuid) const;
 		bool getUUIDFromFilePath(const Path& path, String& uuid) const;
 
 
+		/**
+		 * @brief	Called when the resource has been successfully loaded. 
+		 *
+		 * @note	It is undefined from which thread this will get called from.
+		 *			Most definitely not the sim thread if resource was being loaded
+		 *			asynchronously.
+		 */
+		Event<void(const HResource&)> onResourceLoaded;
+
+		/**
+		 * @brief	Called when the resource has been destroyed.
+		 *
+		 * @note	It is undefined from which thread this will get called from.
+		 */
+		Event<void(const HResource&)> onResourceDestroyed;
+
+		/**
+		 * @brief	Called when the internal resource the handle is pointing to has changed.
+		 *
+		 * @note	It is undefined from which thread this will get called from.
+		 */
+		Event<void(const HResource&)> onResourceModified; // TODO - Not used, implement when I add hot-swapping
 	private:
 	private:
 		/**
 		/**
 		 * @brief	Starts resource loading or returns an already loaded resource.
 		 * @brief	Starts resource loading or returns an already loaded resource.

+ 6 - 0
BansheeCore/Source/BsCoreApplication.cpp

@@ -39,6 +39,7 @@
 #include "BsUUID.h"
 #include "BsUUID.h"
 #include "BsRenderStats.h"
 #include "BsRenderStats.h"
 #include "BsMessageHandler.h"
 #include "BsMessageHandler.h"
+#include "BsResourceListenerManager.h"
 
 
 #include "BsMaterial.h"
 #include "BsMaterial.h"
 #include "BsShader.h"
 #include "BsShader.h"
@@ -83,6 +84,7 @@ namespace BansheeEngine
 		CoreObjectManager::startUp();
 		CoreObjectManager::startUp();
 		GameObjectManager::startUp();
 		GameObjectManager::startUp();
 		Resources::startUp();
 		Resources::startUp();
+		ResourceListenerManager::startUp();
 		GpuProgramManager::startUp();
 		GpuProgramManager::startUp();
 		GpuProgramCoreManager::startUp();
 		GpuProgramCoreManager::startUp();
 		RenderAPIManager::startUp();
 		RenderAPIManager::startUp();
@@ -129,6 +131,7 @@ namespace BansheeEngine
 		Input::shutDown();
 		Input::shutDown();
 
 
 		Resources::shutDown();
 		Resources::shutDown();
+		ResourceListenerManager::shutDown();
 		GameObjectManager::shutDown();
 		GameObjectManager::shutDown();
 
 
 		// All CoreObject related modules should be shut down now. They have likely queued CoreObjects for destruction, so
 		// All CoreObject related modules should be shut down now. They have likely queued CoreObjects for destruction, so
@@ -187,6 +190,9 @@ namespace BansheeEngine
 
 
 			update();
 			update();
 
 
+			// Send out resource events in case any were loaded/destroyed/modified
+			ResourceListenerManager::instance().update();
+
 			// Sync all dirty sim thread CoreObject data to core thread
 			// Sync all dirty sim thread CoreObject data to core thread
 			CoreObjectManager::instance().syncDownload(CoreObjectSync::Sim, gCoreThread().getFrameAlloc());
 			CoreObjectManager::instance().syncDownload(CoreObjectSync::Sim, gCoreThread().getFrameAlloc());
 			gCoreAccessor().queueCommand(std::bind(&CoreObjectManager::syncUpload, CoreObjectManager::instancePtr(), CoreObjectSync::Sim));
 			gCoreAccessor().queueCommand(std::bind(&CoreObjectManager::syncUpload, CoreObjectManager::instancePtr(), CoreObjectSync::Sim));

+ 1 - 1
BansheeCore/Source/BsCoreObject.cpp

@@ -72,7 +72,7 @@ namespace BansheeEngine
 		}
 		}
 	}
 	}
 
 
-	void CoreObject::synchronize()
+	void CoreObject::blockUntilCoreInitialized()
 	{
 	{
 		if (mCoreSpecific != nullptr)
 		if (mCoreSpecific != nullptr)
 			mCoreSpecific->synchronize();
 			mCoreSpecific->synchronize();

+ 3 - 0
BansheeCore/Source/BsGpuParam.cpp

@@ -194,6 +194,7 @@ namespace BansheeEngine
 		mParent->setTexture(mParamDesc->slot, texture);
 		mParent->setTexture(mParamDesc->slot, texture);
 		mParent->setIsLoadStoreTexture(mParamDesc->slot, false);
 		mParent->setIsLoadStoreTexture(mParamDesc->slot, false);
 
 
+		mParent->_markResourcesDirty();
 		mParent->_markCoreDirty();
 		mParent->_markCoreDirty();
 	}
 	}
 
 
@@ -226,6 +227,7 @@ namespace BansheeEngine
 		mParent->setIsLoadStoreTexture(mParamDesc->slot, true);
 		mParent->setIsLoadStoreTexture(mParamDesc->slot, true);
 		mParent->setLoadStoreSurface(mParamDesc->slot, surface);
 		mParent->setLoadStoreSurface(mParamDesc->slot, surface);
 
 
+		mParent->_markResourcesDirty();
 		mParent->_markCoreDirty();
 		mParent->_markCoreDirty();
 	}
 	}
 
 
@@ -256,6 +258,7 @@ namespace BansheeEngine
 
 
 		mParent->setSamplerState(mParamDesc->slot, samplerState);
 		mParent->setSamplerState(mParamDesc->slot, samplerState);
 
 
+		mParent->_markResourcesDirty();
 		mParent->_markCoreDirty();
 		mParent->_markCoreDirty();
 	}
 	}
 
 

+ 24 - 0
BansheeCore/Source/BsGpuParams.cpp

@@ -308,6 +308,8 @@ UINT32 GpuParamsBase::getDataParamSize(const String& name) const
 		}
 		}
 
 
 		mTextures[slot] = texture;
 		mTextures[slot] = texture;
+
+		_markResourcesDirty();
 		_markCoreDirty();
 		_markCoreDirty();
 	}
 	}
 
 
@@ -321,6 +323,8 @@ UINT32 GpuParamsBase::getDataParamSize(const String& name) const
 		}
 		}
 
 
 		mSamplerStates[slot] = sampler;
 		mSamplerStates[slot] = sampler;
+
+		_markResourcesDirty();
 		_markCoreDirty();
 		_markCoreDirty();
 	}
 	}
 
 
@@ -451,6 +455,11 @@ UINT32 GpuParamsBase::getDataParamSize(const String& name) const
 		markCoreDirty();
 		markCoreDirty();
 	}
 	}
 
 
+	void GpuParams::_markResourcesDirty()
+	{
+		markResourcesDirty();
+	}
+
 	SPtr<GpuParams> GpuParams::create(const GpuParamDescPtr& paramDesc, bool transposeMatrices)
 	SPtr<GpuParams> GpuParams::create(const GpuParamDescPtr& paramDesc, bool transposeMatrices)
 	{
 	{
 		GpuParams* params = new (bs_alloc<GpuParams>()) GpuParams(paramDesc, transposeMatrices);
 		GpuParams* params = new (bs_alloc<GpuParams>()) GpuParams(paramDesc, transposeMatrices);
@@ -516,4 +525,19 @@ UINT32 GpuParamsBase::getDataParamSize(const String& name) const
 
 
 		return CoreSyncData(data, totalSize);
 		return CoreSyncData(data, totalSize);
 	}
 	}
+
+	void GpuParams::getResourceDependencies(Vector<HResource>& resources)
+	{
+		for (UINT32 i = 0; i < mNumTextures; i++)
+		{
+			if (mTextures[i] != nullptr)
+				resources.push_back(mTextures[i]);
+		}
+
+		for (UINT32 i = 0; i < mNumSamplerStates; i++)
+		{
+			if (mSamplerStates[i] != nullptr)
+				resources.push_back(mSamplerStates[i]);
+		}
+	}
 }
 }

+ 1 - 1
BansheeCore/Source/BsGpuProgramImporter.cpp

@@ -39,7 +39,7 @@ namespace BansheeEngine
 		Vector<HGpuProgInclude> includes = gpuProgImportOptions->getIncludes();
 		Vector<HGpuProgInclude> includes = gpuProgImportOptions->getIncludes();
 
 
 		GpuProgramPtr gpuProgram = GpuProgram::_createPtr(shaderSource, entryPoint, language, gptype, profile, &includes);
 		GpuProgramPtr gpuProgram = GpuProgram::_createPtr(shaderSource, entryPoint, language, gptype, profile, &includes);
-		gpuProgram->synchronize();
+		gpuProgram->blockUntilCoreInitialized();
 
 
 		if (!gpuProgram->isCompiled())
 		if (!gpuProgram->isCompiled())
 		{
 		{

+ 20 - 0
BansheeCore/Source/BsIResourceListener.cpp

@@ -0,0 +1,20 @@
+#include "BsIResourceListener.h"
+#include "BsResourceListenerManager.h"
+
+namespace BansheeEngine
+{
+	IResourceListener::IResourceListener()
+	{
+		ResourceListenerManager::instance().registerListener(this);
+	}
+
+	IResourceListener::~IResourceListener()
+	{
+		ResourceListenerManager::instance().unregisterListener(this);
+	}
+
+	void IResourceListener::markResourcesDirty()
+	{
+		ResourceListenerManager::instance().markListenerDirty(this);
+	}
+}

+ 13 - 6
BansheeCore/Source/BsMaterial.cpp

@@ -355,42 +355,48 @@ namespace BansheeEngine
 			HGpuProgram vertProgram = curPass->getVertexProgram();
 			HGpuProgram vertProgram = curPass->getVertexProgram();
 			if (vertProgram)
 			if (vertProgram)
 			{
 			{
-				vertProgram.synchronize();
+				vertProgram.blockUntilLoaded();
+				vertProgram->blockUntilCoreInitialized();
 				allParamDescs.push_back(vertProgram->getParamDesc());
 				allParamDescs.push_back(vertProgram->getParamDesc());
 			}
 			}
 
 
 			HGpuProgram fragProgram = curPass->getFragmentProgram();
 			HGpuProgram fragProgram = curPass->getFragmentProgram();
 			if (fragProgram)
 			if (fragProgram)
 			{
 			{
-				fragProgram.synchronize();
+				fragProgram.blockUntilLoaded();
+				fragProgram->blockUntilCoreInitialized();
 				allParamDescs.push_back(fragProgram->getParamDesc());
 				allParamDescs.push_back(fragProgram->getParamDesc());
 			}
 			}
 
 
 			HGpuProgram geomProgram = curPass->getGeometryProgram();
 			HGpuProgram geomProgram = curPass->getGeometryProgram();
 			if (geomProgram)
 			if (geomProgram)
 			{
 			{
-				geomProgram.synchronize();
+				geomProgram.blockUntilLoaded();
+				geomProgram->blockUntilCoreInitialized();
 				allParamDescs.push_back(geomProgram->getParamDesc());
 				allParamDescs.push_back(geomProgram->getParamDesc());
 			}
 			}
 
 
 			HGpuProgram hullProgram = curPass->getHullProgram();
 			HGpuProgram hullProgram = curPass->getHullProgram();
 			if (hullProgram)
 			if (hullProgram)
 			{
 			{
-				hullProgram.synchronize();
+				hullProgram.blockUntilLoaded();
+				hullProgram->blockUntilCoreInitialized();
 				allParamDescs.push_back(hullProgram->getParamDesc());
 				allParamDescs.push_back(hullProgram->getParamDesc());
 			}
 			}
 
 
 			HGpuProgram domainProgram = curPass->getDomainProgram();
 			HGpuProgram domainProgram = curPass->getDomainProgram();
 			if (domainProgram)
 			if (domainProgram)
 			{
 			{
-				domainProgram.synchronize();
+				domainProgram.blockUntilLoaded();
+				domainProgram->blockUntilCoreInitialized();
 				allParamDescs.push_back(domainProgram->getParamDesc());
 				allParamDescs.push_back(domainProgram->getParamDesc());
 			}
 			}
 
 
 			HGpuProgram computeProgram = curPass->getComputeProgram();
 			HGpuProgram computeProgram = curPass->getComputeProgram();
 			if (computeProgram)
 			if (computeProgram)
 			{
 			{
-				computeProgram.synchronize();
+				computeProgram.blockUntilLoaded();
+				computeProgram->blockUntilCoreInitialized();
 				allParamDescs.push_back(computeProgram->getParamDesc());
 				allParamDescs.push_back(computeProgram->getParamDesc());
 			}
 			}
 		}
 		}
@@ -441,6 +447,7 @@ namespace BansheeEngine
 		mShader = shader;
 		mShader = shader;
 
 
 		initBestTechnique();
 		initBestTechnique();
+
 		_markCoreDirty();
 		_markCoreDirty();
 	}
 	}
 
 

+ 80 - 10
BansheeCore/Source/BsPass.cpp

@@ -11,6 +11,23 @@
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
+	/**
+	 * @brief	Converts a sim thread pass descriptor to a core thread one.
+	 */
+	void convertPassDesc(const PASS_DESC& input, PASS_DESC_CORE& output)
+	{
+		output.blendState = input.blendState.isLoaded() ? input.blendState->getCore() : nullptr;
+		output.rasterizerState = input.rasterizerState.isLoaded() ? input.rasterizerState->getCore() : nullptr;
+		output.depthStencilState = input.depthStencilState.isLoaded() ? input.depthStencilState->getCore() : nullptr;
+		output.stencilRefValue = input.stencilRefValue;
+		output.vertexProgram = input.vertexProgram.isLoaded() ? input.vertexProgram->getCore() : nullptr;
+		output.fragmentProgram = input.fragmentProgram.isLoaded() ? input.fragmentProgram->getCore() : nullptr;
+		output.geometryProgram = input.geometryProgram.isLoaded() ? input.geometryProgram->getCore() : nullptr;
+		output.hullProgram = input.hullProgram.isLoaded() ? input.hullProgram->getCore() : nullptr;
+		output.domainProgram = input.domainProgram.isLoaded() ? input.domainProgram->getCore() : nullptr;
+		output.hullProgram = input.hullProgram.isLoaded() ? input.hullProgram->getCore() : nullptr;
+	}
+
 	template<bool Core>
 	template<bool Core>
 	TPass<Core>::TPass()
 	TPass<Core>::TPass()
 	{
 	{
@@ -56,6 +73,15 @@ namespace BansheeEngine
 		:TPass(desc)
 		:TPass(desc)
 	{ }
 	{ }
 
 
+	void PassCore::syncToCore(const CoreSyncData& data)
+	{
+		UINT8* dataPtr = data.getBuffer();
+		PASS_DESC_CORE* desc = (PASS_DESC_CORE*)dataPtr;
+
+		mData = *desc;
+		desc->~PASS_DESC_CORE();
+	}
+
 	SPtr<PassCore> PassCore::create(const PASS_DESC_CORE& desc)
 	SPtr<PassCore> PassCore::create(const PASS_DESC_CORE& desc)
 	{
 	{
 		PassCore* newPass = new (bs_alloc<PassCore>()) PassCore(desc);
 		PassCore* newPass = new (bs_alloc<PassCore>()) PassCore(desc);
@@ -78,16 +104,7 @@ namespace BansheeEngine
 	SPtr<CoreObjectCore> Pass::createCore() const
 	SPtr<CoreObjectCore> Pass::createCore() const
 	{
 	{
 		PASS_DESC_CORE desc;
 		PASS_DESC_CORE desc;
-		desc.blendState = mData.blendState.isLoaded() ? mData.blendState->getCore() : nullptr;
-		desc.rasterizerState = mData.rasterizerState.isLoaded() ? mData.rasterizerState->getCore() : nullptr;
-		desc.depthStencilState = mData.depthStencilState.isLoaded() ? mData.depthStencilState->getCore() : nullptr;
-		desc.stencilRefValue = mData.stencilRefValue;
-		desc.vertexProgram = mData.vertexProgram != nullptr ? mData.vertexProgram->getCore() : nullptr;
-		desc.fragmentProgram = mData.fragmentProgram != nullptr ? mData.fragmentProgram->getCore() : nullptr;
-		desc.geometryProgram = mData.geometryProgram != nullptr ? mData.geometryProgram->getCore() : nullptr;
-		desc.hullProgram = mData.hullProgram != nullptr ? mData.hullProgram->getCore() : nullptr;
-		desc.domainProgram = mData.domainProgram != nullptr ? mData.domainProgram->getCore() : nullptr;
-		desc.hullProgram = mData.hullProgram != nullptr ? mData.hullProgram->getCore() : nullptr;
+		convertPassDesc(mData, desc);
 
 
 		PassCore* pass = new (bs_alloc<PassCore>()) PassCore(desc);
 		PassCore* pass = new (bs_alloc<PassCore>()) PassCore(desc);
 		SPtr<PassCore> passPtr = bs_shared_ptr<PassCore, GenAlloc>(pass);
 		SPtr<PassCore> passPtr = bs_shared_ptr<PassCore, GenAlloc>(pass);
@@ -96,6 +113,59 @@ namespace BansheeEngine
 		return passPtr;
 		return passPtr;
 	}
 	}
 
 
+	void Pass::initialize()
+	{
+		_markResourcesDirty();
+
+		CoreObject::initialize();
+	}
+
+	CoreSyncData Pass::syncToCore(FrameAlloc* allocator)
+	{
+		UINT32 size = sizeof(PASS_DESC_CORE);
+
+		UINT8* data = allocator->alloc(size);
+		PASS_DESC_CORE* passDesc = new (data) PASS_DESC_CORE();
+		convertPassDesc(mData, *passDesc);
+
+		return CoreSyncData(data, size);
+	}
+
+	void Pass::_markResourcesDirty()
+	{
+		markResourcesDirty();
+	}
+
+	void Pass::getResourceDependencies(Vector<HResource>& resources)
+	{
+		if (mData.blendState != nullptr)
+			resources.push_back(mData.blendState);
+
+		if (mData.rasterizerState != nullptr)
+			resources.push_back(mData.rasterizerState);
+
+		if (mData.depthStencilState != nullptr)
+			resources.push_back(mData.depthStencilState);
+
+		if (mData.vertexProgram != nullptr)
+			resources.push_back(mData.vertexProgram);
+
+		if (mData.fragmentProgram != nullptr)
+			resources.push_back(mData.fragmentProgram);
+
+		if (mData.geometryProgram != nullptr)
+			resources.push_back(mData.geometryProgram);
+
+		if (mData.hullProgram != nullptr)
+			resources.push_back(mData.hullProgram);
+
+		if (mData.domainProgram != nullptr)
+			resources.push_back(mData.domainProgram);
+
+		if (mData.computeProgram != nullptr)
+			resources.push_back(mData.computeProgram);
+	}
+
 	PassPtr Pass::create(const PASS_DESC& desc)
 	PassPtr Pass::create(const PASS_DESC& desc)
 	{
 	{
 		Pass* newPass = new (bs_alloc<Pass>()) Pass(desc);
 		Pass* newPass = new (bs_alloc<Pass>()) Pass(desc);

+ 1 - 3
BansheeCore/Source/BsResourceHandle.cpp

@@ -24,7 +24,7 @@ namespace BansheeEngine
 		return (mData != nullptr && mData->mIsCreated && mData->mPtr != nullptr); 
 		return (mData != nullptr && mData->mIsCreated && mData->mPtr != nullptr); 
 	}
 	}
 
 
-	void ResourceHandleBase::synchronize() const
+	void ResourceHandleBase::blockUntilLoaded() const
 	{
 	{
 		if(mData == nullptr)
 		if(mData == nullptr)
 			return;
 			return;
@@ -37,8 +37,6 @@ namespace BansheeEngine
 				BS_THREAD_WAIT(mResourceCreatedCondition, mResourceCreatedMutex, lock);
 				BS_THREAD_WAIT(mResourceCreatedCondition, mResourceCreatedMutex, lock);
 			}
 			}
 		}
 		}
-
-		mData->mPtr->synchronize();
 	}
 	}
 
 
 	void ResourceHandleBase::_setHandleData(std::shared_ptr<Resource> ptr, const String& uuid)
 	void ResourceHandleBase::_setHandleData(std::shared_ptr<Resource> ptr, const String& uuid)

+ 161 - 0
BansheeCore/Source/BsResourceListenerManager.cpp

@@ -0,0 +1,161 @@
+#include "BsResourceListenerManager.h"
+#include "BsResources.h"
+#include "BsIResourceListener.h"
+
+using namespace std::placeholders;
+
+namespace BansheeEngine
+{
+	ResourceListenerManager::ResourceListenerManager()
+	{
+		mResourceLoadedConn = gResources().onResourceLoaded.connect(std::bind(&ResourceListenerManager::onResourceLoaded, this, _1));
+		mResourceDestroyedConn = gResources().onResourceDestroyed.connect(std::bind(&ResourceListenerManager::onResourceDestroyed, this, _1));
+	}
+
+	ResourceListenerManager::~ResourceListenerManager()
+	{
+		assert(mResourceToListenerMap.size() == 0 && "Not all resource listeners had their resources unregistered properly.");
+
+		mResourceLoadedConn.disconnect();
+		mResourceDestroyedConn.disconnect();
+	}
+
+	void ResourceListenerManager::registerListener(IResourceListener* listener)
+	{
+		mActiveListeners.insert(listener);
+	}
+
+	void ResourceListenerManager::unregisterListener(IResourceListener* listener)
+	{
+		mActiveListeners.erase(listener);
+		mDirtyListeners.erase(listener);
+
+		clearDependencies(listener);
+	}
+
+	void ResourceListenerManager::markListenerDirty(IResourceListener* listener)
+	{
+		mDirtyListeners.insert(listener);
+	}
+
+	void ResourceListenerManager::update()
+	{
+		for (auto& listener : mDirtyListeners)
+		{
+			clearDependencies(listener);
+			addDependencies(listener);
+		}
+
+		mDirtyListeners.clear();
+
+		{
+			BS_LOCK_MUTEX(mMutex);
+
+			for (auto& resource : mLoadedResources)
+				sendResourceLoaded(resource);
+
+			for (auto& resource : mDestroyedResources)
+				sendResourceDestroyed(resource);
+
+			mLoadedResources.clear();
+			mDestroyedResources.clear();
+		}
+	}
+
+	void ResourceListenerManager::onResourceLoaded(const HResource& resource)
+	{
+		BS_LOCK_MUTEX(mMutex);
+
+		mLoadedResources.push_back(resource);
+	}
+
+	void ResourceListenerManager::onResourceDestroyed(const HResource& resource)
+	{
+		BS_LOCK_MUTEX(mMutex);
+
+		mDestroyedResources.push_back(resource);
+	}
+
+	void ResourceListenerManager::sendResourceLoaded(const HResource& resource)
+	{
+		UINT64 handleId = (UINT64)resource.getHandleData().get();
+
+		auto iterFind = mResourceToListenerMap.find(handleId);
+		if (iterFind == mResourceToListenerMap.end())
+			return;
+
+		const Vector<IResourceListener*> relevantListeners = iterFind->second;
+		for (auto& listener : relevantListeners)
+		{
+			assert(mActiveListeners.find(listener) != mActiveListeners.end() && "Attempting to notify a destroyed IResourceListener");
+
+			listener->notifyResourceLoaded(resource);
+		}
+	}
+
+	void ResourceListenerManager::sendResourceDestroyed(const HResource& resource)
+	{
+		UINT64 handleId = (UINT64)resource.getHandleData().get();
+
+		auto iterFind = mResourceToListenerMap.find(handleId);
+		if (iterFind == mResourceToListenerMap.end())
+			return;
+
+		const Vector<IResourceListener*> relevantListeners = iterFind->second;
+		for (auto& listener : relevantListeners)
+		{
+			assert(mActiveListeners.find(listener) != mActiveListeners.end() && "Attempting to notify a destroyed IResourceListener");
+
+			listener->notifyResourceDestroyed(resource);
+		}
+	}
+
+	void ResourceListenerManager::clearDependencies(IResourceListener* listener)
+	{
+		auto iterFind = mListenerToResourceMap.find(listener);
+		if (iterFind == mListenerToResourceMap.end())
+			return;
+
+		const Vector<UINT64>& dependantResources = iterFind->second;
+		for (auto& resourceHandleId : dependantResources)
+		{
+			auto iterFind2 = mResourceToListenerMap.find(resourceHandleId);
+			if (iterFind2 != mResourceToListenerMap.end())
+			{
+				Vector<IResourceListener*>& listeners = iterFind2->second;
+				auto iterFind3 = std::find(listeners.begin(), listeners.end(), listener);
+
+				if (iterFind3 != listeners.end())
+					listeners.erase(iterFind3);
+
+				if (listeners.size() == 0)
+					mResourceToListenerMap.erase(iterFind2);
+			}
+		}
+
+		mListenerToResourceMap.erase(iterFind);
+	}
+
+	void ResourceListenerManager::addDependencies(IResourceListener* listener)
+	{
+		listener->getResourceDependencies(mTempResourceBuffer);
+
+		if (mTempResourceBuffer.size() > 0)
+		{
+			Vector<UINT64> resourceHandleIds(mTempResourceBuffer.size());
+			UINT32 idx = 0;
+			for (auto& resource : mTempResourceBuffer)
+			{
+				UINT64 handleId = (UINT64)resource.getHandleData().get();
+				resourceHandleIds[idx] = handleId;
+				mResourceToListenerMap[handleId].push_back(listener);
+
+				idx++;
+			}
+
+			mListenerToResourceMap[listener] = resourceHandleIds;
+		}
+
+		mTempResourceBuffer.clear();
+	}
+}

+ 8 - 3
BansheeCore/Source/BsResources.cpp

@@ -134,7 +134,7 @@ namespace BansheeEngine
 			else
 			else
 			{
 			{
 				// Previously being loaded as async but now we want it synced, so we wait
 				// Previously being loaded as async but now we want it synced, so we wait
-				existingResource.synchronize();
+				existingResource.blockUntilLoaded();
 
 
 				return existingResource;
 				return existingResource;
 			}
 			}
@@ -187,7 +187,10 @@ namespace BansheeEngine
 	void Resources::unload(HResource resource)
 	void Resources::unload(HResource resource)
 	{
 	{
 		if(!resource.isLoaded()) // If it's still loading wait until that finishes
 		if(!resource.isLoaded()) // If it's still loading wait until that finishes
-			resource.synchronize();
+			resource.blockUntilLoaded();
+
+		// Call this before we actually destroy it
+		onResourceDestroyed(resource);
 
 
 		resource->destroy();
 		resource->destroy();
 
 
@@ -221,7 +224,7 @@ namespace BansheeEngine
 	void Resources::save(HResource resource, const Path& filePath, bool overwrite)
 	void Resources::save(HResource resource, const Path& filePath, bool overwrite)
 	{
 	{
 		if(!resource.isLoaded())
 		if(!resource.isLoaded())
-			resource.synchronize();
+			resource.blockUntilLoaded();
 
 
 		bool fileExists = FileSystem::isFile(filePath);
 		bool fileExists = FileSystem::isFile(filePath);
 		if(fileExists)
 		if(fileExists)
@@ -308,6 +311,8 @@ namespace BansheeEngine
 
 
 		resource._setHandleData(rawResource, resource.getUUID());
 		resource._setHandleData(rawResource, resource.getUUID());
 
 
+		onResourceLoaded(resource);
+
 		{
 		{
 			BS_LOCK_MUTEX(mLoadedResourceMutex);
 			BS_LOCK_MUTEX(mLoadedResourceMutex);
 			mLoadedResources[resource.getUUID()] = resource;
 			mLoadedResources[resource.getUUID()] = resource;

+ 5 - 2
BansheeEditor/Source/BsEditorApplication.cpp

@@ -41,6 +41,7 @@
 #include "BsEvent.h"
 #include "BsEvent.h"
 #include "BsCoreRenderer.h"
 #include "BsCoreRenderer.h"
 #include "BsProjectSettings.h"
 #include "BsProjectSettings.h"
+#include "BsMesh.h"
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
@@ -289,8 +290,10 @@ namespace BansheeEngine
 		mTestTexRef = static_resource_cast<Texture>(gResources().loadAsync(L"C:\\ExportTest.tex"));
 		mTestTexRef = static_resource_cast<Texture>(gResources().loadAsync(L"C:\\ExportTest.tex"));
 		mDbgMeshRef = static_resource_cast<Mesh>(gResources().loadAsync(L"C:\\ExportMesh.mesh"));
 		mDbgMeshRef = static_resource_cast<Mesh>(gResources().loadAsync(L"C:\\ExportMesh.mesh"));
 
 
-		mDbgMeshRef.synchronize();
-		mTestTexRef.synchronize();
+		mDbgMeshRef.blockUntilLoaded();
+		mDbgMeshRef->blockUntilCoreInitialized();
+		mTestTexRef.blockUntilLoaded();
+		mTestTexRef->blockUntilCoreInitialized();
 
 
 		mTestMaterial->setTexture("tex", mTestTexRef);
 		mTestMaterial->setTexture("tex", mTestTexRef);
 		gResources().save(mTestMaterial, L"C:\\ExportMaterial.mat", true);
 		gResources().save(mTestMaterial, L"C:\\ExportMaterial.mat", true);

+ 32 - 1
BansheeEngine/Include/BsRenderableHandler.h

@@ -3,6 +3,7 @@
 #include "BsPrerequisites.h"
 #include "BsPrerequisites.h"
 #include "BsIReflectable.h"
 #include "BsIReflectable.h"
 #include "BsCoreObject.h"
 #include "BsCoreObject.h"
+#include "BsIResourceListener.h"
 #include "BsBounds.h"
 #include "BsBounds.h"
 #include "BsAABox.h"
 #include "BsAABox.h"
 
 
@@ -122,6 +123,11 @@ namespace BansheeEngine
 		 */
 		 */
 		virtual void _markCoreDirty(RenderableDirtyFlag flag = RenderableDirtyFlag::Everything) { }
 		virtual void _markCoreDirty(RenderableDirtyFlag flag = RenderableDirtyFlag::Everything) { }
 
 
+		/**
+		 * @copydoc	IResourceListener::markResourcesDirty
+		 */
+		virtual void _markResourcesDirty() { }
+
 		MeshType mMesh;
 		MeshType mMesh;
 		Vector<MaterialType> mMaterials;
 		Vector<MaterialType> mMaterials;
 		UINT64 mLayer;
 		UINT64 mLayer;
@@ -180,7 +186,7 @@ namespace BansheeEngine
 	/**
 	/**
 	 * @copydoc	TRenderableHandler
 	 * @copydoc	TRenderableHandler
 	 */
 	 */
-	class BS_EXPORT RenderableHandler : public IReflectable, public CoreObject, public TRenderableHandler<false>
+	class BS_EXPORT RenderableHandler : public IReflectable, public CoreObject, public TRenderableHandler<false>, public IResourceListener
 	{
 	{
 	public:
 	public:
 		/**
 		/**
@@ -212,11 +218,36 @@ namespace BansheeEngine
 		 */
 		 */
 		void _markCoreDirty(RenderableDirtyFlag flag = RenderableDirtyFlag::Everything);
 		void _markCoreDirty(RenderableDirtyFlag flag = RenderableDirtyFlag::Everything);
 
 
+		/**
+		 * @copydoc	IResourceListener::markResourcesDirty
+		 */
+		void _markResourcesDirty();
+
 		/**
 		/**
 		 * @copydoc	CoreObject::syncToCore
 		 * @copydoc	CoreObject::syncToCore
 		 */
 		 */
 		CoreSyncData syncToCore(FrameAlloc* allocator);
 		CoreSyncData syncToCore(FrameAlloc* allocator);
 
 
+		/**
+		 * @copydoc	IResourceListener::getResourceDependencies
+		 */
+		void getResourceDependencies(Vector<HResource>& resources);
+
+		/**
+		 * @copydoc IResourceListener::notifyResourceLoaded
+		 */
+		void notifyResourceLoaded(const HResource& resource) { markCoreDirty(); }
+
+		/**
+		 * @copydoc IResourceListener::notifyResourceDestroyed
+		 */
+		void notifyResourceDestroyed(const HResource& resource) { markCoreDirty(); }
+
+		/**
+		 * @copydoc IResourceListener::notifyResourceChanged
+		 */
+		void notifyResourceChanged(const HResource& resource) { markCoreDirty(); }
+
 		/**
 		/**
 		 * @brief	Creates a new renderable handler instance without initializing it.
 		 * @brief	Creates a new renderable handler instance without initializing it.
 		 */
 		 */

+ 1 - 1
BansheeEngine/Source/BsCameraHandler.cpp

@@ -729,7 +729,7 @@ namespace BansheeEngine
 
 
 	CameraHandler::CameraHandler(RenderTargetPtr target, float left, float top, float width, float height)
 	CameraHandler::CameraHandler(RenderTargetPtr target, float left, float top, float width, float height)
 	{
 	{
-		target->synchronize();
+		target->blockUntilCoreInitialized();
 		mViewport = Viewport::create(target, left, top, width, height);
 		mViewport = Viewport::create(target, left, top, width, height);
 	}
 	}
 
 

+ 0 - 2
BansheeEngine/Source/BsGUIManager.cpp

@@ -598,7 +598,6 @@ namespace BansheeEngine
 		if(mCaretTexture == nullptr)
 		if(mCaretTexture == nullptr)
 		{
 		{
 			HTexture newTex = Texture::create(TEX_TYPE_2D, 1, 1, 0, PF_R8G8B8A8);
 			HTexture newTex = Texture::create(TEX_TYPE_2D, 1, 1, 0, PF_R8G8B8A8);
-			newTex->synchronize(); // TODO - Required due to a bug in allocateSubresourceBuffer
 			mCaretTexture = SpriteTexture::create(newTex);
 			mCaretTexture = SpriteTexture::create(newTex);
 		}
 		}
 
 
@@ -615,7 +614,6 @@ namespace BansheeEngine
 		if(mTextSelectionTexture == nullptr)
 		if(mTextSelectionTexture == nullptr)
 		{
 		{
 			HTexture newTex = Texture::create(TEX_TYPE_2D, 1, 1, 0, PF_R8G8B8A8);
 			HTexture newTex = Texture::create(TEX_TYPE_2D, 1, 1, 0, PF_R8G8B8A8);
-			newTex->synchronize(); // TODO - Required due to a bug in allocateSubresourceBuffer
 			mTextSelectionTexture = SpriteTexture::create(newTex);
 			mTextSelectionTexture = SpriteTexture::create(newTex);
 		}
 		}
 
 

+ 19 - 0
BansheeEngine/Source/BsRenderableHandler.cpp

@@ -30,6 +30,7 @@ namespace BansheeEngine
 		mMesh = mesh;
 		mMesh = mesh;
 		mMaterials.resize(mesh->getProperties().getNumSubMeshes());
 		mMaterials.resize(mesh->getProperties().getNumSubMeshes());
 
 
+		_markResourcesDirty();
 		_markCoreDirty();
 		_markCoreDirty();
 	}
 	}
 
 
@@ -38,6 +39,7 @@ namespace BansheeEngine
 	{
 	{
 		mMaterials[idx] = material;
 		mMaterials[idx] = material;
 
 
+		_markResourcesDirty();
 		_markCoreDirty();
 		_markCoreDirty();
 	}
 	}
 
 
@@ -214,6 +216,11 @@ namespace BansheeEngine
 		markCoreDirty((UINT32)flag);
 		markCoreDirty((UINT32)flag);
 	}
 	}
 
 
+	void RenderableHandler::_markResourcesDirty()
+	{
+		markResourcesDirty();
+	}
+
 	CoreSyncData RenderableHandler::syncToCore(FrameAlloc* allocator)
 	CoreSyncData RenderableHandler::syncToCore(FrameAlloc* allocator)
 	{
 	{
 		UINT32 numMaterials = (UINT32)mMaterials.size();
 		UINT32 numMaterials = (UINT32)mMaterials.size();
@@ -256,6 +263,18 @@ namespace BansheeEngine
 		return CoreSyncData(data, size);
 		return CoreSyncData(data, size);
 	}
 	}
 
 
+	void RenderableHandler::getResourceDependencies(Vector<HResource>& resources)
+	{
+		if (mMesh != nullptr)
+			resources.push_back(mMesh);
+
+		for (auto& material : mMaterials)
+		{
+			if (material != nullptr)
+				resources.push_back(material);
+		}
+	}
+
 	RenderableHandlerPtr RenderableHandler::create()
 	RenderableHandlerPtr RenderableHandler::create()
 	{
 	{
 		RenderableHandlerPtr handlerPtr = createEmpty();
 		RenderableHandlerPtr handlerPtr = createEmpty();

+ 0 - 2
BansheeFontImporter/Source/BsFontImporter.cpp

@@ -273,8 +273,6 @@ namespace BansheeEngine
 				}
 				}
 
 
 				HTexture newTex = Texture::create(TEX_TYPE_2D, pageIter->width, pageIter->height, 0, PF_R8G8);
 				HTexture newTex = Texture::create(TEX_TYPE_2D, pageIter->width, pageIter->height, 0, PF_R8G8);
-				newTex.synchronize(); // TODO - Required due to a bug in allocateSubresourceBuffer
-
 				UINT32 subresourceIdx = newTex->getProperties().mapToSubresourceIdx(0, 0);
 				UINT32 subresourceIdx = newTex->getProperties().mapToSubresourceIdx(0, 0);
 
 
 				// It's possible the formats no longer match
 				// It's possible the formats no longer match

+ 0 - 1
BansheeFreeImgImporter/Source/BsFreeImgImporter.cpp

@@ -158,7 +158,6 @@ namespace BansheeEngine
 		else
 		else
 			mipLevels.insert(mipLevels.begin(), imgData);
 			mipLevels.insert(mipLevels.begin(), imgData);
 
 
-		newTexture->synchronize(); // TODO - Required due to a bug in allocateSubresourceBuffer
 		for (UINT32 mip = 0; mip < (UINT32)mipLevels.size(); ++mip)
 		for (UINT32 mip = 0; mip < (UINT32)mipLevels.size(); ++mip)
 		{
 		{
 			UINT32 subresourceIdx = newTexture->getProperties().mapToSubresourceIdx(0, mip);
 			UINT32 subresourceIdx = newTexture->getProperties().mapToSubresourceIdx(0, mip);

+ 10 - 27
TODO.txt

@@ -1,35 +1,18 @@
 --------- ALL LONG TERM TASKS / FIXES BELONG TO GOOGLE DOCS: ImplementationTODO OR PossibleImprovements ----------
 --------- ALL LONG TERM TASKS / FIXES BELONG TO GOOGLE DOCS: ImplementationTODO OR PossibleImprovements ----------
 
 
-Renderable TODO:
-
-Disallow CoreObject creation from core thread
- - Add asserts in CoreObject::destroy and CoreObject::initialize
- - Possibly also add asserts to CoreThread::queueCommand and CoreThread::queueReturnCommand
-
  New issues:
  New issues:
   - Since Mesh refactor when I select/deselect a gizmo the entire render texture flashes white for one frame (might be related to RT refactor instead)
   - Since Mesh refactor when I select/deselect a gizmo the entire render texture flashes white for one frame (might be related to RT refactor instead)
 
 
-HANDLE RESOURCE LOADING AND CORE OBJECT SYNC:
-How to handle RenderableHandler (and other types) that reference resource handles? What if the handle is invalid and in the process 
-of being loaded? The core thread needs to be notified when loading is complete so it retrieves the proper Core object.
- - Add getResourceDependencies() and markDependenciesDirty() to CoreObject
-   - CoreObjectManager then keeps track of all resource handles and receives notifications from 
-     Resources when a resource finishes loading or gets modified due to reimport
-   - When such notification is issued CoreObjectManager performs a lookup among its resource handles to find 
-     CoreObject (store CoreObject as a raw pointer or an ID, but make sure to handle deletion of CoreObject properly in either case)
-   - That object then receives notifyResourceUpdated(const HResource& resource) which by default makes the object dirty
- - This can then be used for resource hot-swap functionality as well
-
-BETTER SYNC IN GENERAL:
- - Extend CoreObject with getDependencies() that returns all child CoreObjects
- - Then when performing syncToCore I can iterate through all dependencies and sync them all
-   - e.g. Material references GpuParams which reference GpuParamBlockBuffer, all of which require syncing
- - Add a method similar to syncToCore that just appends CoreSyncData to a larger object, so many objects
-   can be synced at once.
- - Get rid of syncUpload/syncDownload methods and replace them with:
-   - Custom sync in BansheeRenderer that iterates over all Renderable and Camera objects and syncs the dirty ones
-     - Will likely want to remove core->sim sync with mesh bounds and other similar core->sync transfers
-   - Custom sync for RenderTargets that happens both ways, likely handled by RenderableTargetManager
+Fix crash on shutdown with OpenGL
+
+Try to figure out how to deal with material and its references:
+ - It references a ShaderPtr but it should be referencing a HShader
+ - It calls synchronize on all GpuPrograms before getting initialized because (same would apply to shader if it was using a HShader as it should):
+    - It needs to ensure they're loaded in case they were loaded asynchronously
+	- It needs to ensure they're initialize on core thread to retrieve gpu param desc which is used for creating gpu param buffers and param name/type checking
+ - I cannot delay this init until those are initialized because user expects to be able to set material params right after creation
+ - OPTIMAL approach would be to generate shader desc on sim thread or during import and allow delayed initialization
+   - If user uses async loading then it's his fault if material hasn't been initialized
 
 
 -----------------
 -----------------