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

Added resource listener to material and solved the problem where waiting for resource load didn't trigger the listener

Marko Pintera 11 лет назад
Родитель
Сommit
217b06bd3f

+ 37 - 1
BansheeCore/Include/BsMaterial.h

@@ -2,6 +2,7 @@
 
 #include "BsCorePrerequisites.h"
 #include "BsResource.h"
+#include "BsIResourceListener.h"
 #include "BsMaterialParam.h"
 #include "BsVector2.h"
 #include "BsVector3.h"
@@ -119,6 +120,11 @@ namespace BansheeEngine
 		 */
 		virtual void _markCoreDirty() { }
 
+		/**
+		 * @copydoc	IResourceListener::markResourcesDirty
+		 */
+		virtual void _markResourcesDirty() { }
+
 		/**
 		 * @brief	Returns all GPU parameter descriptions in the specified technique.
 		 */
@@ -598,7 +604,7 @@ namespace BansheeEngine
 	/**
 	 * @copydoc	MaterialBase
 	 */
-	class BS_CORE_EXPORT Material : public Resource, public TMaterial<false>
+	class BS_CORE_EXPORT Material : public Resource, public TMaterial<false>, public IResourceListener
 	{
 	public:
 		~Material() { }
@@ -609,6 +615,11 @@ namespace BansheeEngine
 		 */
 		SPtr<MaterialCore> getCore() const;
 
+		/**
+		 * @copydoc	CoreObject::initialize
+		 */
+		void initialize();
+
 		/**
 		 * @brief	Creates a new empty material.
 		 * 			
@@ -642,6 +653,31 @@ namespace BansheeEngine
 		 */
 		void _markCoreDirty();
 
+		/**
+		 * @copydoc	IResourceListener::markResourcesDirty
+		 */
+		void _markResourcesDirty();
+
+		/**
+		 * @copydoc	IResourceListener::getResourceDependencies
+		 */
+		void getResourceDependencies(Vector<HResource>& resources);
+
+		/**
+		 * @copydoc IResourceListener::notifyResourceLoaded
+		 */
+		void notifyResourceLoaded(const HResource& resource);
+
+		/**
+		 * @copydoc IResourceListener::notifyResourceDestroyed
+		 */
+		void notifyResourceDestroyed(const HResource& resource);
+
+		/**
+		 * @copydoc IResourceListener::notifyResourceChanged
+		 */
+		void notifyResourceChanged(const HResource& resource);
+
 		/************************************************************************/
 		/* 								RTTI		                     		*/
 		/************************************************************************/

+ 9 - 3
BansheeCore/Include/BsResourceListenerManager.h

@@ -38,6 +38,12 @@ namespace BansheeEngine
 		 */
 		void update();
 
+		/**
+		 * @brief	Forces the listener to send out events about the specified resource immediately, instead
+		 *			of waiting for the next "update()" call.
+		 */
+		void notifyListeners(const String& resourceUUID);
+
 	private:
 		/**
 		 * @brief	Triggered by the resources system when a resource has finished loading.
@@ -77,12 +83,12 @@ namespace BansheeEngine
 		Map<UINT64, Vector<IResourceListener*>> mResourceToListenerMap;
 		Map<IResourceListener*, Vector<UINT64>> mListenerToResourceMap;
 
-		Vector<HResource> mLoadedResources;
-		Vector<HResource> mDestroyedResources;
+		Map<String, HResource> mLoadedResources;
+		Map<String, HResource> mDestroyedResources;
 
 		Vector<HResource> mTempResourceBuffer;
 
-		BS_MUTEX(mMutex)
+		BS_RECURSIVE_MUTEX(mMutex);
 
 #if BS_DEBUG_MODE
 		Set<IResourceListener*> mActiveListeners;

+ 42 - 1
BansheeCore/Source/BsMaterial.cpp

@@ -455,6 +455,8 @@ namespace BansheeEngine
 	void TMaterial<false>::setShader(ShaderType shader)
 	{
 		mShader = shader;
+		_markResourcesDirty();
+
 		if (mShader)
 		{
 			if (!mShader.isLoaded())
@@ -899,7 +901,14 @@ namespace BansheeEngine
 
 	Material::Material(const HShader& shader)
 	{
-		setShader(shader);
+		mShader = shader;
+	}
+
+	void Material::initialize()
+	{
+		setShader(mShader); // Not calling directly in constructor because it calls virtual methods
+
+		Resource::initialize();
 	}
 
 	void Material::_markCoreDirty()
@@ -907,6 +916,11 @@ namespace BansheeEngine
 		markCoreDirty();
 	}
 
+	void Material::_markResourcesDirty()
+	{
+		markResourcesDirty();
+	}
+
 	SPtr<MaterialCore> Material::getCore() const
 	{
 		return std::static_pointer_cast<MaterialCore>(mCoreSpecific);
@@ -983,6 +997,33 @@ namespace BansheeEngine
 		return CoreSyncData(buffer, size);
 	}
 
+	void Material::getResourceDependencies(Vector<HResource>& resources)
+	{
+		if (mShader != nullptr)
+			resources.push_back(mShader);
+	}
+
+	void Material::notifyResourceLoaded(const HResource& resource)
+	{
+		initBestTechnique();
+
+		markCoreDirty();
+	}
+
+	void Material::notifyResourceDestroyed(const HResource& resource)
+	{
+		initBestTechnique();
+
+		markCoreDirty();
+	}
+
+	void Material::notifyResourceChanged(const HResource& resource)
+	{
+		initBestTechnique();
+
+		markCoreDirty();
+	}
+
 	HMaterial Material::create()
 	{
 		MaterialPtr materialPtr = MaterialManager::instance().create();

+ 5 - 0
BansheeCore/Source/BsResourceHandle.cpp

@@ -3,6 +3,7 @@
 #include "BsResource.h"
 #include "BsResourceHandleRTTI.h"
 #include "BsResources.h"
+#include "BsResourceListenerManager.h"
 
 namespace BansheeEngine
 {
@@ -36,6 +37,10 @@ namespace BansheeEngine
 			{
 				BS_THREAD_WAIT(mResourceCreatedCondition, mResourceCreatedMutex, lock);
 			}
+
+			// Send out ResourceListener events right away, as whatever called this method
+			// probably also expects the listener events to trigger immediately as well
+			ResourceListenerManager::instance().notifyListeners(mData->mUUID);
 		}
 	}
 

+ 32 - 11
BansheeCore/Source/BsResourceListenerManager.cpp

@@ -23,7 +23,7 @@ namespace BansheeEngine
 	void ResourceListenerManager::registerListener(IResourceListener* listener)
 	{
 #if BS_DEBUG_MODE
-		BS_LOCK_MUTEX(mMutex);
+		BS_LOCK_RECURSIVE_MUTEX(mMutex);
 		mActiveListeners.insert(listener);
 #endif
 	}
@@ -32,7 +32,7 @@ namespace BansheeEngine
 	{
 #if BS_DEBUG_MODE
 		{
-			BS_LOCK_MUTEX(mMutex);
+			BS_LOCK_RECURSIVE_MUTEX(mMutex);
 			mActiveListeners.erase(listener);
 		}
 #endif
@@ -58,31 +58,52 @@ namespace BansheeEngine
 		mDirtyListeners.clear();
 
 		{
-			BS_LOCK_MUTEX(mMutex);
+			BS_LOCK_RECURSIVE_MUTEX(mMutex);
 
-			for (auto& resource : mLoadedResources)
-				sendResourceLoaded(resource);
+			for (auto& entry : mLoadedResources)
+				sendResourceLoaded(entry.second);
 
-			for (auto& resource : mDestroyedResources)
-				sendResourceDestroyed(resource);
+			for (auto& entry : mDestroyedResources)
+				sendResourceDestroyed(entry.second);
 
 			mLoadedResources.clear();
 			mDestroyedResources.clear();
 		}
 	}
 
+	void ResourceListenerManager::notifyListeners(const String& resourceUUID)
+	{
+		BS_LOCK_RECURSIVE_MUTEX(mMutex);
+
+		auto iterFindLoaded = mLoadedResources.find(resourceUUID);
+		if (iterFindLoaded != mLoadedResources.end())
+		{
+			sendResourceLoaded(iterFindLoaded->second);
+
+			mLoadedResources.erase(iterFindLoaded);
+		}
+
+		auto iterFindDestroyed = mDestroyedResources.find(resourceUUID);
+		if (iterFindDestroyed != mDestroyedResources.end())
+		{
+			sendResourceDestroyed(iterFindDestroyed->second);
+
+			mDestroyedResources.erase(iterFindDestroyed);
+		}
+	}
+
 	void ResourceListenerManager::onResourceLoaded(const HResource& resource)
 	{
-		BS_LOCK_MUTEX(mMutex);
+		BS_LOCK_RECURSIVE_MUTEX(mMutex);
 
-		mLoadedResources.push_back(resource);
+		mLoadedResources[resource.getUUID()] = resource;
 	}
 
 	void ResourceListenerManager::onResourceDestroyed(const HResource& resource)
 	{
-		BS_LOCK_MUTEX(mMutex);
+		BS_LOCK_RECURSIVE_MUTEX(mMutex);
 
-		mDestroyedResources.push_back(resource);
+		mDestroyedResources[resource.getUUID()] = resource;
 	}
 
 	void ResourceListenerManager::sendResourceLoaded(const HResource& resource)

+ 5 - 7
TODO.txt

@@ -1,14 +1,12 @@
 --------- ALL LONG TERM TASKS / FIXES BELONG TO GOOGLE DOCS: ImplementationTODO OR PossibleImprovements ----------
 
-1. Make sure shader create() methods returns a HShader
-2. Make sure material uses HShader
-3. Make sure material listens for HShader load event (derives from IResourceListener)
-4. Delay material initialization until its shader and gpu programs are fully loaded (keep the core thread sync there as is for now)
---
-5. Make RenderTexture accept a HTexture
-
 See GDrive/Resources doc for resources refactor
 
+TODO - Material waits to Shader to be loaded but doesn't wait for shader GpuPrograms. 
+ - What's the best way to ensure initialization is done when all these are loaded?
+
+Make RenderTexture accept a HTexture
+
 ManagedComponent deserialization IS WRONG
  - I register the component with ScriptGameObjectManager during deserialization when its ID is wrong