소스 검색

Update material when shader changes
Updates FileMonitor so that it attempts to avoid reporting actions for partially written files

BearishSun 10 년 전
부모
커밋
53835849ad

+ 12 - 1
BansheeCore/Include/BsResourceListenerManager.h

@@ -55,6 +55,11 @@ namespace BansheeEngine
 		 */
 		 */
 		void onResourceDestroyed(const HResource& resource);
 		void onResourceDestroyed(const HResource& resource);
 
 
+		/**
+		 * @brief	Triggered by the resources system after a resource handle is modified (i.e. points to a new resource).
+		 */
+		void onResourceModified(const HResource& resource);
+
 		/**
 		/**
 		 * @brief	Sends resource loaded event to all listeners referencing this resource.
 		 * @brief	Sends resource loaded event to all listeners referencing this resource.
 		 */
 		 */
@@ -65,6 +70,11 @@ namespace BansheeEngine
 		 */
 		 */
 		void sendResourceDestroyed(const HResource& resource);
 		void sendResourceDestroyed(const HResource& resource);
 
 
+		/**
+		 * @brief	Sends resource modified event to all listeners referencing this resource.
+		 */
+		void sendResourceModified(const HResource& resource);
+
 		/**
 		/**
 		 * @brief	Clears all the stored dependencies for the listener.
 		 * @brief	Clears all the stored dependencies for the listener.
 		 */
 		 */
@@ -77,7 +87,7 @@ namespace BansheeEngine
 
 
 		HEvent mResourceLoadedConn;
 		HEvent mResourceLoadedConn;
 		HEvent mResourceDestroyedConn;
 		HEvent mResourceDestroyedConn;
-
+		HEvent mResourceModifiedConn;
 		
 		
 		Set<IResourceListener*> mDirtyListeners;
 		Set<IResourceListener*> mDirtyListeners;
 		Map<UINT64, Vector<IResourceListener*>> mResourceToListenerMap;
 		Map<UINT64, Vector<IResourceListener*>> mResourceToListenerMap;
@@ -85,6 +95,7 @@ namespace BansheeEngine
 
 
 		Map<String, HResource> mLoadedResources;
 		Map<String, HResource> mLoadedResources;
 		Map<String, HResource> mDestroyedResources;
 		Map<String, HResource> mDestroyedResources;
+		Map<String, HResource> mModifiedResources;
 
 
 		Vector<HResource> mTempResourceBuffer;
 		Vector<HResource> mTempResourceBuffer;
 
 

+ 159 - 11
BansheeCore/Source/BsMaterial.cpp

@@ -1193,17 +1193,8 @@ namespace BansheeEngine
 
 
 	void Material::getListenerResources(Vector<HResource>& resources)
 	void Material::getListenerResources(Vector<HResource>& resources)
 	{
 	{
-		bs_frame_mark();
-
-		{
-			FrameVector<HResource> temp;
-			getResourceDependencies(temp);
-
-			for (auto& entry : temp)
-				resources.push_back(entry);
-		}
-
-		bs_frame_clear();
+		if (mShader != nullptr)
+			resources.push_back(mShader);
 	}
 	}
 
 
 	void Material::getResourceDependencies(FrameVector<HResource>& dependencies) const
 	void Material::getResourceDependencies(FrameVector<HResource>& dependencies) const
@@ -1259,9 +1250,166 @@ namespace BansheeEngine
 		initializeIfLoaded();
 		initializeIfLoaded();
 	}
 	}
 
 
+	template<class T>
+	void copyParam(SPtr<GpuParams>& from, SPtr<GpuParams>& to, const String& name, UINT32 arraySize)
+	{
+		TGpuDataParam<T, false> newParam;
+		to->getParam(name, newParam);
+
+		TGpuDataParam<T, false> oldParam;
+		from->getParam(name, oldParam);
+
+		for (UINT32 i = 0; i < arraySize; i++)
+			newParam.set(oldParam.get(i), i);
+	}
+
 	void Material::notifyResourceChanged(const HResource& resource)
 	void Material::notifyResourceChanged(const HResource& resource)
 	{
 	{
+		// Shader changed, so save parameters, rebuild material and restore parameters
+		Vector<SPtr<PassParameters>> oldPassParams = mParametersPerPass;
+
+		mLoadFlags = Load_None;
 		initializeIfLoaded();
 		initializeIfLoaded();
+
+		for (UINT32 i = 0; i < (UINT32)oldPassParams.size(); i++)
+		{
+			if (i >= (UINT32)mParametersPerPass.size())
+				continue;
+
+			SPtr<PassParameters> oldParams = oldPassParams[i];
+			SPtr<PassParameters> newParams = mParametersPerPass[i];
+
+			for (UINT32 j = 0; j < PassParameters::NUM_PARAMS; j++)
+			{
+				SPtr<GpuParams>& oldGpuParams = oldParams->getParamByIdx(j);
+				SPtr<GpuParams>& newGpuParams = newParams->getParamByIdx(j);
+				if (oldGpuParams == nullptr || newGpuParams == nullptr)
+					continue;
+
+				const GpuParamDesc& oldDesc = oldGpuParams->getParamDesc();
+				const GpuParamDesc& newDesc = newGpuParams->getParamDesc();
+
+				for (auto& param : oldDesc.params)
+				{
+					auto iterFind = newDesc.params.find(param.first);
+					if (iterFind == newDesc.params.end())
+						continue;
+
+					if (param.second.type != iterFind->second.type)
+						continue;
+
+					UINT32 arraySize = std::min(param.second.arraySize, iterFind->second.arraySize);
+					
+					switch (param.second.type)
+					{
+					case GPDT_FLOAT1:
+						copyParam<float>(oldGpuParams, newGpuParams, param.first, arraySize);
+						break;
+					case GPDT_FLOAT2:
+						copyParam<Vector2>(oldGpuParams, newGpuParams, param.first, arraySize);
+						break;
+					case GPDT_FLOAT3:
+						copyParam<Vector3>(oldGpuParams, newGpuParams, param.first, arraySize);
+						break;
+					case GPDT_FLOAT4:
+						copyParam<Vector4>(oldGpuParams, newGpuParams, param.first, arraySize);
+						break;
+					case GPDT_MATRIX_2X2:
+						copyParam<Matrix2>(oldGpuParams, newGpuParams, param.first, arraySize);
+						break;
+					case GPDT_MATRIX_2X3:
+						copyParam<Matrix2x3>(oldGpuParams, newGpuParams, param.first, arraySize);
+						break;
+					case GPDT_MATRIX_2X4:
+						copyParam<Matrix2x4>(oldGpuParams, newGpuParams, param.first, arraySize);
+						break;
+					case GPDT_MATRIX_3X2:
+						copyParam<Matrix3x2>(oldGpuParams, newGpuParams, param.first, arraySize);
+						break;
+					case GPDT_MATRIX_3X3:
+						copyParam<Matrix3>(oldGpuParams, newGpuParams, param.first, arraySize);
+						break;
+					case GPDT_MATRIX_3X4:
+						copyParam<Matrix3x4>(oldGpuParams, newGpuParams, param.first, arraySize);
+						break;
+					case GPDT_MATRIX_4X2:
+						copyParam<Matrix4x2>(oldGpuParams, newGpuParams, param.first, arraySize);
+						break;
+					case GPDT_MATRIX_4X3:
+						copyParam<Matrix4x3>(oldGpuParams, newGpuParams, param.first, arraySize);
+						break;
+					case GPDT_MATRIX_4X4:
+						copyParam<Matrix4>(oldGpuParams, newGpuParams, param.first, arraySize);
+						break;
+					case GPDT_INT1:
+					case GPDT_BOOL:
+						copyParam<int>(oldGpuParams, newGpuParams, param.first, arraySize);
+						break;
+					case GPDT_INT2:
+						copyParam<Vector2I>(oldGpuParams, newGpuParams, param.first, arraySize);
+						break;
+					case GPDT_INT3:
+						copyParam<Vector3I>(oldGpuParams, newGpuParams, param.first, arraySize);
+						break;
+					case GPDT_INT4:
+						copyParam<Vector4I>(oldGpuParams, newGpuParams, param.first, arraySize);
+						break;
+					case GPDT_COLOR:
+						copyParam<Color>(oldGpuParams, newGpuParams, param.first, arraySize);
+						break;
+					case GPDT_STRUCT:
+					{
+						GpuParamStruct newParam;
+						newGpuParams->getStructParam(param.first, newParam);
+
+						GpuParamStruct oldParam;
+						oldGpuParams->getStructParam(param.first, oldParam);
+
+						if (param.second.elementSize == iterFind->second.elementSize)
+						{
+							for (UINT32 k = 0; k < arraySize; k++)
+							{
+								UINT8* data = (UINT8*)bs_stack_alloc(param.second.elementSize);
+								oldParam.get(data, param.second.elementSize, k);
+								newParam.set(data, param.second.elementSize, k);
+								bs_stack_free(data);
+							}
+						}
+					}
+					break;
+					}
+				}
+
+				for (auto& param : oldDesc.textures)
+				{
+					auto iterFind = newDesc.textures.find(param.first);
+					if (iterFind == newDesc.textures.end())
+						continue;
+
+					UINT32 oldSlot = param.second.slot;
+					UINT32 newSlot = iterFind->second.slot;
+
+					bool isLoadStore = oldGpuParams->isLoadStoreTexture(oldSlot);
+
+					if (!isLoadStore)
+						newGpuParams->setTexture(newSlot, oldGpuParams->getTexture(oldSlot));
+					else
+					{
+						newGpuParams->setIsLoadStoreTexture(newSlot, true);
+						newGpuParams->setLoadStoreSurface(newSlot, oldGpuParams->getLoadStoreSurface(oldSlot));
+					}
+				}
+
+				for (auto& param : oldDesc.samplers)
+				{
+					auto iterFind = newDesc.samplers.find(param.first);
+					if (iterFind == newDesc.samplers.end())
+						continue;
+
+					newGpuParams->setSamplerState(iterFind->second.slot, oldGpuParams->getSamplerState(param.second.slot));
+				}
+			}
+		}
 	}
 	}
 
 
 	HMaterial Material::clone()
 	HMaterial Material::clone()

+ 39 - 0
BansheeCore/Source/BsResourceListenerManager.cpp

@@ -10,6 +10,7 @@ namespace BansheeEngine
 	{
 	{
 		mResourceLoadedConn = gResources().onResourceLoaded.connect(std::bind(&ResourceListenerManager::onResourceLoaded, this, _1));
 		mResourceLoadedConn = gResources().onResourceLoaded.connect(std::bind(&ResourceListenerManager::onResourceLoaded, this, _1));
 		mResourceDestroyedConn = gResources().onResourceDestroyed.connect(std::bind(&ResourceListenerManager::onResourceDestroyed, this, _1));
 		mResourceDestroyedConn = gResources().onResourceDestroyed.connect(std::bind(&ResourceListenerManager::onResourceDestroyed, this, _1));
+		mResourceModifiedConn = gResources().onResourceModified.connect(std::bind(&ResourceListenerManager::onResourceModified, this, _1));
 	}
 	}
 
 
 	ResourceListenerManager::~ResourceListenerManager()
 	ResourceListenerManager::~ResourceListenerManager()
@@ -66,8 +67,12 @@ namespace BansheeEngine
 			for (auto& entry : mDestroyedResources)
 			for (auto& entry : mDestroyedResources)
 				sendResourceDestroyed(entry.second);
 				sendResourceDestroyed(entry.second);
 
 
+			for (auto& entry : mModifiedResources)
+				sendResourceModified(entry.second);
+
 			mLoadedResources.clear();
 			mLoadedResources.clear();
 			mDestroyedResources.clear();
 			mDestroyedResources.clear();
+			mModifiedResources.clear();
 		}
 		}
 	}
 	}
 
 
@@ -90,6 +95,14 @@ namespace BansheeEngine
 
 
 			mDestroyedResources.erase(iterFindDestroyed);
 			mDestroyedResources.erase(iterFindDestroyed);
 		}
 		}
+
+		auto iterFindModified = mModifiedResources.find(resourceUUID);
+		if (iterFindModified != mModifiedResources.end())
+		{
+			sendResourceModified(iterFindModified->second);
+
+			mModifiedResources.erase(iterFindModified);
+		}
 	}
 	}
 
 
 	void ResourceListenerManager::onResourceLoaded(const HResource& resource)
 	void ResourceListenerManager::onResourceLoaded(const HResource& resource)
@@ -106,6 +119,13 @@ namespace BansheeEngine
 		mDestroyedResources[resource.getUUID()] = resource;
 		mDestroyedResources[resource.getUUID()] = resource;
 	}
 	}
 
 
+	void ResourceListenerManager::onResourceModified(const HResource& resource)
+	{
+		BS_LOCK_RECURSIVE_MUTEX(mMutex);
+
+		mModifiedResources[resource.getUUID()] = resource;
+	}
+
 	void ResourceListenerManager::sendResourceLoaded(const HResource& resource)
 	void ResourceListenerManager::sendResourceLoaded(const HResource& resource)
 	{
 	{
 		UINT64 handleId = (UINT64)resource.getHandleData().get();
 		UINT64 handleId = (UINT64)resource.getHandleData().get();
@@ -144,6 +164,25 @@ namespace BansheeEngine
 		}
 		}
 	}
 	}
 
 
+	void ResourceListenerManager::sendResourceModified(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)
+		{
+#if BS_DEBUG_MODE
+			assert(mActiveListeners.find(listener) != mActiveListeners.end() && "Attempting to notify a destroyed IResourceListener");
+#endif
+
+			listener->notifyResourceChanged(resource);
+		}
+	}
+
 	void ResourceListenerManager::clearDependencies(IResourceListener* listener)
 	void ResourceListenerManager::clearDependencies(IResourceListener* listener)
 	{
 	{
 		auto iterFind = mListenerToResourceMap.find(listener);
 		auto iterFind = mListenerToResourceMap.find(listener);

+ 50 - 10
BansheeCore/Source/Win32/BsWin32FolderMonitor.cpp

@@ -211,6 +211,8 @@ namespace BansheeEngine
 
 
 			memcpy(action->newName, fileName.data(), fileName.size() * sizeof(WString::value_type));
 			memcpy(action->newName, fileName.data(), fileName.size() * sizeof(WString::value_type));
 			action->newName[fileName.size()] = L'\0';
 			action->newName[fileName.size()] = L'\0';
+			action->lastSize = 0;
+			action->checkForWriteStarted = false;
 
 
 			return action;
 			return action;
 		}
 		}
@@ -228,6 +230,8 @@ namespace BansheeEngine
 
 
 			memcpy(action->newName, fileName.data(), fileName.size() * sizeof(WString::value_type));
 			memcpy(action->newName, fileName.data(), fileName.size() * sizeof(WString::value_type));
 			action->newName[fileName.size()] = L'\0';
 			action->newName[fileName.size()] = L'\0';
+			action->lastSize = 0;
+			action->checkForWriteStarted = false;
 
 
 			return action;
 			return action;
 		}
 		}
@@ -245,6 +249,8 @@ namespace BansheeEngine
 
 
 			memcpy(action->newName, fileName.data(), fileName.size() * sizeof(WString::value_type));
 			memcpy(action->newName, fileName.data(), fileName.size() * sizeof(WString::value_type));
 			action->newName[fileName.size()] = L'\0';
 			action->newName[fileName.size()] = L'\0';
+			action->lastSize = 0;
+			action->checkForWriteStarted = false;
 
 
 			return action;
 			return action;
 		}
 		}
@@ -268,6 +274,8 @@ namespace BansheeEngine
 
 
 			memcpy(action->newName, newfileName.data(), newfileName.size() * sizeof(WString::value_type));
 			memcpy(action->newName, newfileName.data(), newfileName.size() * sizeof(WString::value_type));
 			action->newName[newfileName.size()] = L'\0';
 			action->newName[newfileName.size()] = L'\0';
+			action->lastSize = 0;
+			action->checkForWriteStarted = false;
 
 
 			return action;
 			return action;
 		}
 		}
@@ -280,6 +288,9 @@ namespace BansheeEngine
 		WString::value_type* oldName;
 		WString::value_type* oldName;
 		WString::value_type* newName;
 		WString::value_type* newName;
 		FileActionType type;
 		FileActionType type;
+
+		UINT64 lastSize;
+		bool checkForWriteStarted;
 	};
 	};
 
 
 	struct FolderMonitor::Pimpl
 	struct FolderMonitor::Pimpl
@@ -288,7 +299,7 @@ namespace BansheeEngine
 		HANDLE mCompPortHandle;
 		HANDLE mCompPortHandle;
 
 
 		Queue<FileAction*> mFileActions;
 		Queue<FileAction*> mFileActions;
-		Queue<FileAction*> mActiveFileActions;
+		List<FileAction*> mActiveFileActions;
 
 
 		BS_MUTEX(mMainMutex);
 		BS_MUTEX(mMainMutex);
 		BS_THREAD_TYPE* mWorkerThread;
 		BS_THREAD_TYPE* mWorkerThread;
@@ -620,34 +631,63 @@ namespace BansheeEngine
 		{
 		{
 			BS_LOCK_MUTEX(mPimpl->mMainMutex);
 			BS_LOCK_MUTEX(mPimpl->mMainMutex);
 
 
-			mPimpl->mActiveFileActions.swap(mPimpl->mFileActions);
+			while (!mPimpl->mFileActions.empty())
+			{
+				FileAction* action = mPimpl->mFileActions.front();
+				mPimpl->mFileActions.pop();
+
+				mPimpl->mActiveFileActions.push_back(action);
+			}
 		}
 		}
 
 
-		while(!mPimpl->mActiveFileActions.empty())
+		for (auto iter = mPimpl->mActiveFileActions.begin(); iter != mPimpl->mActiveFileActions.end();)
 		{
 		{
-			FileAction* action = mPimpl->mActiveFileActions.front();
-			mPimpl->mActiveFileActions.pop();
+			FileAction* action = *iter;
+			
+			// Reported file actions might still be in progress (i.e. something might still be writing to those files).
+			// Sadly there doesn't seem to be a way to properly determine when those files are done being written, so instead
+			// we check for at least a couple of frames if the file's size hasn't changed before reporting a file action.
+			// This takes care of most of the issues and avoids reporting partially written files in almost all cases.
+			UINT64 size = FileSystem::getFileSize(action->newName);
+			if (!action->checkForWriteStarted)
+			{
+				action->checkForWriteStarted = true;
+				action->lastSize = size;
+
+				++iter;
+				continue;
+			}
+			else
+			{
+				if (action->lastSize != size)
+				{
+					action->lastSize = size;
+					++iter;
+					continue;
+				}
+			}
 
 
-			switch(action->type)
+			switch (action->type)
 			{
 			{
 			case FileActionType::Added:
 			case FileActionType::Added:
-				if(!onAdded.empty())
+				if (!onAdded.empty())
 					onAdded(Path(action->newName));
 					onAdded(Path(action->newName));
 				break;
 				break;
 			case FileActionType::Removed:
 			case FileActionType::Removed:
-				if(!onRemoved.empty())
+				if (!onRemoved.empty())
 					onRemoved(Path(action->newName));
 					onRemoved(Path(action->newName));
 				break;
 				break;
 			case FileActionType::Modified:
 			case FileActionType::Modified:
-				if(!onModified.empty())
+				if (!onModified.empty())
 					onModified(Path(action->newName));
 					onModified(Path(action->newName));
 				break;
 				break;
 			case FileActionType::Renamed:
 			case FileActionType::Renamed:
-				if(!onRenamed.empty())
+				if (!onRenamed.empty())
 					onRenamed(Path(action->oldName), Path(action->newName));
 					onRenamed(Path(action->oldName), Path(action->newName));
 				break;
 				break;
 			}
 			}
 
 
+			mPimpl->mActiveFileActions.erase(iter++);
 			FileAction::destroy(action);
 			FileAction::destroy(action);
 		}
 		}
 	}
 	}

+ 9 - 1
SBansheeEngine/Source/BsScriptResources.cpp

@@ -18,6 +18,7 @@ namespace BansheeEngine
 	void ScriptResources::initRuntimeData()
 	void ScriptResources::initRuntimeData()
 	{
 	{
 		metaData.scriptClass->addInternalCall("Internal_Load", &ScriptResources::internal_Load);
 		metaData.scriptClass->addInternalCall("Internal_Load", &ScriptResources::internal_Load);
+		metaData.scriptClass->addInternalCall("Internal_LoadRef", &ScriptResources::internal_LoadRef);
 		metaData.scriptClass->addInternalCall("Internal_UnloadUnused", &ScriptResources::internal_UnloadUnused);
 		metaData.scriptClass->addInternalCall("Internal_UnloadUnused", &ScriptResources::internal_UnloadUnused);
 	}
 	}
 
 
@@ -41,7 +42,14 @@ namespace BansheeEngine
 		if (scriptRef == nullptr)
 		if (scriptRef == nullptr)
 			return nullptr;
 			return nullptr;
 
 
-		return scriptRef->getManagedInstance();
+		HResource resource = gResources().load(scriptRef->getHandle());
+		if (resource == nullptr)
+			return nullptr;
+
+		ScriptResourceBase* scriptResource;
+		ScriptResourceManager::instance().getScriptResource(resource, &scriptResource, true);
+
+		return scriptResource->getManagedInstance();
 	}
 	}
 
 
 	void ScriptResources::internal_UnloadUnused()
 	void ScriptResources::internal_UnloadUnused()

+ 1 - 2
TODO.txt

@@ -38,14 +38,13 @@ Optional:
  - Add tooltips to toolbar items and other buttons with icons
  - Add tooltips to toolbar items and other buttons with icons
  - Either disable light tool icons before release or make them functional (With gizmos)
  - Either disable light tool icons before release or make them functional (With gizmos)
  - Test VS 2015 Community as code editor (possibly also move the code to VS 2015)
  - Test VS 2015 Community as code editor (possibly also move the code to VS 2015)
+ - Clearing the console should also clear the status bar
 
 
  More optional:
  More optional:
  - Add a way to use GUI elements in game window (Default GUI available to all, plus GUIWidget component for custom ones. Make sure skin can be changed for both.)
  - Add a way to use GUI elements in game window (Default GUI available to all, plus GUIWidget component for custom ones. Make sure skin can be changed for both.)
- - Clearing the console should also clear the status bar
  - When starting drag from hierarchy tree view it tends to select another object (can't repro)
  - When starting drag from hierarchy tree view it tends to select another object (can't repro)
  - Handle seems to lag behind the selected mesh
  - Handle seems to lag behind the selected mesh
  - When starting play-in-editor, automatically make the Game Window active
  - When starting play-in-editor, automatically make the Game Window active
- - Console message warning/info icons seem misaligned compared to each other
  - When resizing library window while docked, selection area appears
  - When resizing library window while docked, selection area appears
  - Move all the code files into subfolders so their hierarchy is similar to VS filters
  - Move all the code files into subfolders so their hierarchy is similar to VS filters
  - MenuBar - will likely need a way to mark elements as disabled when not appropriate (e.g. no "frame selected unless scene is focused")
  - MenuBar - will likely need a way to mark elements as disabled when not appropriate (e.g. no "frame selected unless scene is focused")