Răsfoiți Sursa

Updated builtin resource import so it only imports file that have been modified (instead of everything)

BearishSun 8 ani în urmă
părinte
comite
5080f1b35a

+ 1 - 1
Source/BansheeEditor/Include/BsBuiltinEditorResources.h

@@ -182,7 +182,7 @@ namespace bs
 		 * @note	
 		 * @note	
 		 * Normally you only want to use this during development phase and then ship with engine-ready format only.
 		 * Normally you only want to use this during development phase and then ship with engine-ready format only.
 		 */
 		 */
-		void preprocess(bool forceImport);
+		void preprocess(bool forceImport, time_t lastUpdateTime);
 
 
 		/**	Generates the default editor skin and all GUI element styles. */
 		/**	Generates the default editor skin and all GUI element styles. */
 		SPtr<GUISkin> generateGUISkin();
 		SPtr<GUISkin> generateGUISkin();

+ 67 - 12
Source/BansheeEditor/Source/BsBuiltinEditorResources.cpp

@@ -327,8 +327,10 @@ namespace bs
 
 
 		// Update from raw assets if needed
 		// Update from raw assets if needed
 #if BS_DEBUG_MODE
 #if BS_DEBUG_MODE
-		UINT32 modifications =
-			BuiltinResourcesHelper::checkForModifications(BuiltinRawDataFolder, BuiltinDataFolder + L"Timestamp.asset");
+		time_t lastUpdateTime;
+		UINT32 modifications = BuiltinResourcesHelper::checkForModifications(BuiltinRawDataFolder,
+			BuiltinDataFolder + L"Timestamp.asset", lastUpdateTime);
+
 		if (modifications > 0)
 		if (modifications > 0)
 		{
 		{
 			SPtr<ResourceManifest> oldResourceManifest;
 			SPtr<ResourceManifest> oldResourceManifest;
@@ -342,7 +344,7 @@ namespace bs
 			mResourceManifest = ResourceManifest::create("BuiltinResources");
 			mResourceManifest = ResourceManifest::create("BuiltinResources");
 			gResources().registerResourceManifest(mResourceManifest);
 			gResources().registerResourceManifest(mResourceManifest);
 
 
-			preprocess(modifications == 2);
+			preprocess(modifications == 2, lastUpdateTime);
 			BuiltinResourcesHelper::writeTimestamp(BuiltinDataFolder + L"Timestamp.asset");
 			BuiltinResourcesHelper::writeTimestamp(BuiltinDataFolder + L"Timestamp.asset");
 
 
 			ResourceManifest::save(mResourceManifest, ResourceManifestPath, BuiltinDataFolder);
 			ResourceManifest::save(mResourceManifest, ResourceManifestPath, BuiltinDataFolder);
@@ -393,7 +395,7 @@ namespace bs
 	BuiltinEditorResources::~BuiltinEditorResources()
 	BuiltinEditorResources::~BuiltinEditorResources()
 	{ }
 	{ }
 
 
-	void BuiltinEditorResources::preprocess(bool forceImport)
+	void BuiltinEditorResources::preprocess(bool forceImport, time_t lastUpdateTime)
 	{
 	{
 		Resources::instance().unloadAllUnused();
 		Resources::instance().unloadAllUnused();
 
 
@@ -431,14 +433,67 @@ namespace bs
 			dataListStream->close();
 			dataListStream->close();
 		}
 		}
 
 
-		BuiltinResourcesHelper::importAssets(iconsJSON, EditorRawIconsFolder, EditorIconFolder, mResourceManifest, 
-			BuiltinResourcesHelper::AssetType::Sprite, true);
-		BuiltinResourcesHelper::importAssets(includesJSON, EditorRawShaderIncludeFolder, EditorShaderIncludeFolder, 
-			mResourceManifest, BuiltinResourcesHelper::AssetType::Normal, true); // Hidden dependency: Includes must be imported before shaders
-		BuiltinResourcesHelper::importAssets(shadersJSON, EditorRawShaderFolder, EditorShaderFolder, mResourceManifest,
-			BuiltinResourcesHelper::AssetType::Normal, true);
-		BuiltinResourcesHelper::importAssets(skinJSON, EditorRawSkinFolder, EditorSkinFolder, mResourceManifest, 
-			BuiltinResourcesHelper::AssetType::Sprite, true);
+		// If forcing import, clear all data folders since everything will be recreated anyway
+		Path shaderDependenciesFile = BuiltinDataFolder + "ShaderDependencies.json";
+		if(forceImport)
+		{
+			FileSystem::remove(EditorIconFolder);
+			FileSystem::remove(EditorShaderIncludeFolder);
+			FileSystem::remove(EditorShaderFolder);
+			FileSystem::remove(EditorSkinFolder);
+			
+			FileSystem::remove(shaderDependenciesFile);
+		}
+
+		// Read shader dependencies JSON
+		json shaderDependenciesJSON;
+		if(FileSystem::exists(shaderDependenciesFile))
+		{
+			SPtr<DataStream> stream = FileSystem::openFile(shaderDependenciesFile);
+			shaderDependenciesJSON = json::parse(stream->getAsString().c_str());
+			stream->close();
+		}
+
+		{
+			Vector<bool> importFlags = BuiltinResourcesHelper::generateImportFlags(iconsJSON, EditorRawIconsFolder,
+				lastUpdateTime, forceImport);
+
+			BuiltinResourcesHelper::importAssets(iconsJSON, importFlags, EditorRawIconsFolder, EditorIconFolder, 
+				mResourceManifest, BuiltinResourcesHelper::AssetType::Sprite);
+		}
+
+		{
+			Vector<bool> includeImportFlags = BuiltinResourcesHelper::generateImportFlags(includesJSON, 
+				EditorRawShaderIncludeFolder, lastUpdateTime, forceImport);
+
+			Vector<bool> shaderImportFlags = BuiltinResourcesHelper::generateImportFlags(shadersJSON, EditorRawShaderFolder,
+				lastUpdateTime, forceImport, &shaderDependenciesJSON, EditorRawShaderIncludeFolder);
+
+			// Hidden dependency: Includes must be imported before shaders, but import flags for shaders must be generated
+			// before includes are imported, since the process checks if imports changed
+			BuiltinResourcesHelper::importAssets(includesJSON, includeImportFlags, EditorRawShaderIncludeFolder, 
+				EditorShaderIncludeFolder, mResourceManifest, BuiltinResourcesHelper::AssetType::Normal);
+		
+			BuiltinResourcesHelper::importAssets(shadersJSON, shaderImportFlags, EditorRawShaderFolder, EditorShaderFolder, 
+				mResourceManifest, BuiltinResourcesHelper::AssetType::Normal, &shaderDependenciesJSON);
+		}
+
+		{
+			Vector<bool> includeImportFlags = BuiltinResourcesHelper::generateImportFlags(skinJSON, EditorRawSkinFolder, 
+				lastUpdateTime, forceImport);
+
+			BuiltinResourcesHelper::importAssets(skinJSON, includeImportFlags, EditorRawSkinFolder, EditorSkinFolder, 
+				mResourceManifest, BuiltinResourcesHelper::AssetType::Sprite);
+		}
+
+		// Update shader dependencies JSON
+		{
+			String jsonString = shaderDependenciesJSON.dump(4).c_str();
+
+			dataListStream = FileSystem::createAndOpenFile(shaderDependenciesFile);
+			dataListStream->writeString(jsonString);
+			dataListStream->close();
+		}
 
 
 		// Import fonts
 		// Import fonts
 		BuiltinResourcesHelper::importFont(BuiltinRawDataFolder + DefaultFontFilename, DefaultFontFilename, 
 		BuiltinResourcesHelper::importFont(BuiltinRawDataFolder + DefaultFontFilename, DefaultFontFilename, 

+ 1 - 1
Source/BansheeEngine/Include/BsBuiltinResources.h

@@ -142,7 +142,7 @@ namespace bs
 		 * @note	
 		 * @note	
 		 * Normally you only want to use this during development phase and then ship with engine-ready format only.
 		 * Normally you only want to use this during development phase and then ship with engine-ready format only.
 		 */
 		 */
-		void preprocess(bool forceImport);
+		void preprocess(bool forceImport, time_t lastUpdateTime);
 
 
 		/**	Generates the default engine skin and all GUI element styles. */
 		/**	Generates the default engine skin and all GUI element styles. */
 		SPtr<GUISkin> generateGUISkin();
 		SPtr<GUISkin> generateGUISkin();

+ 29 - 7
Source/BansheeEngine/Include/BsBuiltinResourcesHelper.h

@@ -33,16 +33,17 @@ namespace bs
 		 * 
 		 * 
 		 * @param[in]	entries			JSON array containing the entries to parse, with each entry containing
 		 * @param[in]	entries			JSON array containing the entries to parse, with each entry containing
 		 *								data determine by set ImportMode. 
 		 *								data determine by set ImportMode. 
+		 * @param[in]	importFlags		A set of import flags (one for each entry) that specify which entries need to be
+		 *								imported.
 		 * @param[in]	inputFolder		Folder in which to look for the input files.
 		 * @param[in]	inputFolder		Folder in which to look for the input files.
 		 * @param[in]	outputFolder	Folder in which to store the imported resources.
 		 * @param[in]	outputFolder	Folder in which to store the imported resources.
 		 * @param[in]	manifest		Manifest in which to register the imported resources in.
 		 * @param[in]	manifest		Manifest in which to register the imported resources in.
 		 * @param[in]	mode			Mode that controls how are files imported.
 		 * @param[in]	mode			Mode that controls how are files imported.
-		 * @param[in]	forceImport		If true, all assets will be imported regardless if they have been modified or not.
-		 *								If false, assets will be imported only if the source is newer than the imported file.
-		 * @return						True if the process was sucessful.
+		 * @param[in]	dependencies	Optional map that be updated with any dependencies the imported assets depend on.
 		 */
 		 */
-		static bool importAssets(const nlohmann::json& entries, const Path& inputFolder, const Path& outputFolder, 
-			const SPtr<ResourceManifest>& manifest, AssetType mode = AssetType::Normal, bool forceImport = false);
+		static void importAssets(const nlohmann::json& entries, const Vector<bool>& importFlags, const Path& inputFolder, 
+			const Path& outputFolder, const SPtr<ResourceManifest>& manifest, AssetType mode = AssetType::Normal,
+			nlohmann::json* dependencies = nullptr);
 
 
 		/**
 		/**
 		 * Imports a font from the specified file. Imported font assets are saved in the output folder. All saved resources
 		 * Imports a font from the specified file. Imported font assets are saved in the output folder. All saved resources
@@ -51,6 +52,27 @@ namespace bs
 		static void importFont(const Path& inputFile, const WString& outputName, const Path& outputFolder, 
 		static void importFont(const Path& inputFile, const WString& outputName, const Path& outputFolder, 
 			const Vector<UINT32>& fontSizes, bool antialiasing, const String& UUID, const SPtr<ResourceManifest>& manifest);
 			const Vector<UINT32>& fontSizes, bool antialiasing, const String& UUID, const SPtr<ResourceManifest>& manifest);
 
 
+		/** 
+		 * Iterates over all the provided entries and generates a list of flags that determine should the asset be imported
+		 * or not. This is done by comparing file modification dates with the last update time and/or checking if any
+		 * dependencies require import.
+		 * 
+		 * @param[in]	entries				JSON array containing entries to iterate over.
+		 * @param[in]	inputFolder			Folder in which to look for the input files.
+		 * @param[in]	lastUpdateTime		Timestamp of when the last asset import occurred.
+		 * @param[in]	forceImport			If true, all entries will be marked for import.
+		 * @param[in]	dependencies		Optional map of entries that map each entry in the @p entries array, to a list
+		 *									of dependencies. The dependencies will then also be checked for modifications
+		 *									and if modified the entry will be marked for reimport.
+		 * @param[in]	dependencyFolder	Folder in which dependeny files reside. Only relevant if @p dependencies is
+		 *									provided.
+		 * @return							An array of the same size as the @p entries array, containing value true if
+		 *									an asset should be imported, or false otherwise.
+		 */
+		static Vector<bool> generateImportFlags(const nlohmann::json& entries, const Path& inputFolder,
+			time_t lastUpdateTime, bool forceImport, const nlohmann::json* dependencies = nullptr, 
+			const Path& dependencyFolder = Path::BLANK);
+
 		/** 
 		/** 
 		 * Scans the provided folder for any files that are currently not part of the provided JSON entries. If some are
 		 * Scans the provided folder for any files that are currently not part of the provided JSON entries. If some are
 		 * found they are appended to the JSON entry array. Returns true if any new files were found, false otherwise.
 		 * found they are appended to the JSON entry array. Returns true if any new files were found, false otherwise.
@@ -67,9 +89,9 @@ namespace bs
 		/**
 		/**
 		 * Checks all files in the specified folder for modifications compared to the time stored in the timestamp file. 
 		 * Checks all files in the specified folder for modifications compared to the time stored in the timestamp file. 
 		 * Timestamp file must have been saved using writeTimestamp(). Returns 0 if no changes, 1 if timestamp is out date,
 		 * Timestamp file must have been saved using writeTimestamp(). Returns 0 if no changes, 1 if timestamp is out date,
-		 * or 2 if timestamp doesn't exist.
+		 * or 2 if timestamp doesn't exist. @p lastUpdateTime will contain the time stored in the timestamp, if it exist.
 		 */
 		 */
-		static UINT32 checkForModifications(const Path& folder, const Path& timeStampFile);
+		static UINT32 checkForModifications(const Path& folder, const Path& timeStampFile, time_t& lastUpdateTime);
 
 
 		/** Checks if the shader compiled properly and reports the problem if it hasn't. Returns true if shader is valid. */
 		/** Checks if the shader compiled properly and reports the problem if it hasn't. Returns true if shader is valid. */
 		static bool verifyAndReportShader(const HShader& shader);
 		static bool verifyAndReportShader(const HShader& shader);

+ 77 - 15
Source/BansheeEngine/Source/BsBuiltinResources.cpp

@@ -238,8 +238,10 @@ namespace bs
 #if BS_DEBUG_MODE
 #if BS_DEBUG_MODE
 		if (FileSystem::exists(mBuiltinRawDataFolder))
 		if (FileSystem::exists(mBuiltinRawDataFolder))
 		{
 		{
-			UINT32 modifications = 
-				BuiltinResourcesHelper::checkForModifications(mBuiltinRawDataFolder, mBuiltinDataFolder + L"Timestamp.asset");
+			time_t lastUpdateTime;
+			UINT32 modifications = BuiltinResourcesHelper::checkForModifications(mBuiltinRawDataFolder, 
+				mBuiltinDataFolder + L"Timestamp.asset", lastUpdateTime);
+
 			if (modifications > 0)
 			if (modifications > 0)
 			{
 			{
 				SPtr<ResourceManifest> oldResourceManifest;
 				SPtr<ResourceManifest> oldResourceManifest;
@@ -253,7 +255,7 @@ namespace bs
 				mResourceManifest = ResourceManifest::create("BuiltinResources");
 				mResourceManifest = ResourceManifest::create("BuiltinResources");
 				gResources().registerResourceManifest(mResourceManifest);
 				gResources().registerResourceManifest(mResourceManifest);
 
 
-				preprocess(modifications == 2);
+				preprocess(modifications == 2, lastUpdateTime);
 				BuiltinResourcesHelper::writeTimestamp(mBuiltinDataFolder + L"Timestamp.asset");
 				BuiltinResourcesHelper::writeTimestamp(mBuiltinDataFolder + L"Timestamp.asset");
 
 
 				ResourceManifest::save(mResourceManifest, ResourceManifestPath, mBuiltinDataFolder);
 				ResourceManifest::save(mResourceManifest, ResourceManifestPath, mBuiltinDataFolder);
@@ -360,7 +362,7 @@ namespace bs
 		gCoreThread().submit(true);
 		gCoreThread().submit(true);
 	}
 	}
 
 
-	void BuiltinResources::preprocess(bool forceImport)
+	void BuiltinResources::preprocess(bool forceImport, time_t lastUpdateTime)
 	{
 	{
 		// Hidden dependency: Textures need to be generated before shaders as they may use the default textures
 		// Hidden dependency: Textures need to be generated before shaders as they may use the default textures
 		generateTextures();
 		generateTextures();
@@ -406,21 +408,81 @@ namespace bs
 			dataListStream->writeString(jsonString);
 			dataListStream->writeString(jsonString);
 			dataListStream->close();
 			dataListStream->close();
 		}
 		}
-		
+
 		Path skinFolder = mBuiltinDataFolder + SkinFolder;
 		Path skinFolder = mBuiltinDataFolder + SkinFolder;
 		Path iconFolder = mBuiltinDataFolder + IconFolder;
 		Path iconFolder = mBuiltinDataFolder + IconFolder;
 		Path shaderIncludeFolder = mBuiltinDataFolder + ShaderIncludeFolder;
 		Path shaderIncludeFolder = mBuiltinDataFolder + ShaderIncludeFolder;
+		Path shaderDependenciesFile = mBuiltinDataFolder + "ShaderDependencies.json";
+
+		// If forcing import, clear all data folders since everything will be recreated anyway
+		if(forceImport)
+		{
+			FileSystem::remove(mEngineCursorFolder);
+			FileSystem::remove(iconFolder);
+			FileSystem::remove(shaderIncludeFolder);
+			FileSystem::remove(mEngineShaderFolder);
+			FileSystem::remove(skinFolder);
+			
+			FileSystem::remove(shaderDependenciesFile);
+		}
+
+		// Read shader dependencies JSON
+		json shaderDependenciesJSON;
+		if(FileSystem::exists(shaderDependenciesFile))
+		{
+			SPtr<DataStream> stream = FileSystem::openFile(shaderDependenciesFile);
+			shaderDependenciesJSON = json::parse(stream->getAsString().c_str());
+			stream->close();
+		}
+
+		{
+			Vector<bool> importFlags = BuiltinResourcesHelper::generateImportFlags(cursorsJSON, rawCursorFolder,
+				lastUpdateTime, forceImport);
+
+			BuiltinResourcesHelper::importAssets(cursorsJSON, importFlags, rawCursorFolder, mEngineCursorFolder, 
+				mResourceManifest, BuiltinResourcesHelper::AssetType::Normal);
+		}
+
+		{
+			Vector<bool> importFlags = BuiltinResourcesHelper::generateImportFlags(iconsJSON, rawIconFolder,
+				lastUpdateTime, forceImport);
+
+			BuiltinResourcesHelper::importAssets(iconsJSON, importFlags, rawIconFolder, iconFolder, mResourceManifest, 
+				BuiltinResourcesHelper::AssetType::Normal);
+		}
+
+		{
+			Vector<bool> includeImportFlags = BuiltinResourcesHelper::generateImportFlags(includesJSON, 
+				rawShaderIncludeFolder, lastUpdateTime, forceImport);
+
+			Vector<bool> shaderImportFlags = BuiltinResourcesHelper::generateImportFlags(shadersJSON, rawShaderFolder,
+				lastUpdateTime, forceImport, &shaderDependenciesJSON, rawShaderIncludeFolder);
+
+			// Hidden dependency: Includes must be imported before shaders, but import flags for shaders must be generated
+			// before includes are imported, since the process checks if imports changed
+			BuiltinResourcesHelper::importAssets(includesJSON, includeImportFlags, rawShaderIncludeFolder, 
+				shaderIncludeFolder, mResourceManifest, BuiltinResourcesHelper::AssetType::Normal);
+		
+			BuiltinResourcesHelper::importAssets(shadersJSON, shaderImportFlags, rawShaderFolder, mEngineShaderFolder, 
+				mResourceManifest, BuiltinResourcesHelper::AssetType::Normal, &shaderDependenciesJSON);
+		}
+
+		{
+			Vector<bool> includeImportFlags = BuiltinResourcesHelper::generateImportFlags(skinJSON, 
+				rawSkinFolder, lastUpdateTime, forceImport);
+
+			BuiltinResourcesHelper::importAssets(skinJSON, includeImportFlags, rawSkinFolder, skinFolder, mResourceManifest, 
+				BuiltinResourcesHelper::AssetType::Sprite);
+		}
+
+		// Update shader dependencies JSON
+		{
+			String jsonString = shaderDependenciesJSON.dump(4).c_str();
 
 
-		BuiltinResourcesHelper::importAssets(cursorsJSON, rawCursorFolder, mEngineCursorFolder, mResourceManifest, 
-			BuiltinResourcesHelper::AssetType::Normal, true);
-		BuiltinResourcesHelper::importAssets(iconsJSON, rawIconFolder, iconFolder, mResourceManifest, 
-			BuiltinResourcesHelper::AssetType::Normal, true);
-		BuiltinResourcesHelper::importAssets(includesJSON, rawShaderIncludeFolder, shaderIncludeFolder, mResourceManifest,
-			BuiltinResourcesHelper::AssetType::Normal, true); // Hidden dependency: Includes must be imported before shaders
-		BuiltinResourcesHelper::importAssets(shadersJSON, rawShaderFolder, mEngineShaderFolder, mResourceManifest,
-			BuiltinResourcesHelper::AssetType::Normal, true);
-		BuiltinResourcesHelper::importAssets(skinJSON, rawSkinFolder, skinFolder, mResourceManifest, 
-			BuiltinResourcesHelper::AssetType::Sprite, true);
+			dataListStream = FileSystem::createAndOpenFile(shaderDependenciesFile);
+			dataListStream->writeString(jsonString);
+			dataListStream->close();
+		}
 
 
 		// Import font
 		// Import font
 		BuiltinResourcesHelper::importFont(mBuiltinRawDataFolder + DefaultFontFilename, DefaultFontFilename, 
 		BuiltinResourcesHelper::importFont(mBuiltinRawDataFolder + DefaultFontFilename, DefaultFontFilename, 

+ 115 - 47
Source/BansheeEngine/Source/BsBuiltinResourcesHelper.cpp

@@ -25,17 +25,15 @@ using json = nlohmann::json;
 
 
 namespace bs
 namespace bs
 {
 {
-	bool BuiltinResourcesHelper::importAssets(const nlohmann::json& entries, const Path& inputFolder, 
-		const Path& outputFolder, const SPtr<ResourceManifest>& manifest, AssetType mode, bool forceImport)
+	void BuiltinResourcesHelper::importAssets(const nlohmann::json& entries, const Vector<bool>& importFlags, 
+		const Path& inputFolder, const Path& outputFolder, const SPtr<ResourceManifest>& manifest, AssetType mode,
+		nlohmann::json* dependencies)
 	{
 	{
 		if (!FileSystem::exists(inputFolder))
 		if (!FileSystem::exists(inputFolder))
-			return true;
+			return;
 
 
 		bool outputExists = FileSystem::exists(outputFolder);
 		bool outputExists = FileSystem::exists(outputFolder);
-		if (forceImport && outputExists)
-			FileSystem::remove(outputFolder);
-		
-		if(!outputExists || forceImport)
+		if(!outputExists)
 			FileSystem::createDir(outputFolder);
 			FileSystem::createDir(outputFolder);
 
 
 		Path spriteOutputFolder = outputFolder + "/Sprites/";
 		Path spriteOutputFolder = outputFolder + "/Sprites/";
@@ -101,54 +99,33 @@ namespace bs
 					resourcesToSave.push_back(std::make_pair(relativeAssetPath, nullptr));
 					resourcesToSave.push_back(std::make_pair(relativeAssetPath, nullptr));
 			}
 			}
 
 
-			// Check if this resource actually changed
-			bool import = true;
-			if(!forceImport)
+			// Use the provided UUID if just one resource, otherwise we ignore the UUID. The current assumption is that
+			// such resources don't require persistent UUIDs. If that changes then this method needs to be updated.
+			if (resourcesToSave.size() == 1)
 			{
 			{
-				import = false;
+				Path outputPath = outputFolder + resourcesToSave[0].first;
 
 
-				time_t lastModifiedSrc = FileSystem::getLastModifiedTime(filePath);
-				for(auto& entry : resourcesToSave)
+				HResource resource = Importer::instance().import(filePath, resourcesToSave[0].second, UUID);
+				if (resource != nullptr)
 				{
 				{
-					Path outputPath = outputFolder + entry.first;
-					if(lastModifiedSrc > FileSystem::getLastModifiedTime(outputPath))
-					{
-						import = true;
-						break;
-					}
+					Resources::instance().save(resource, outputPath, true);
+					manifest->registerResource(resource.getUUID(), outputPath);
 				}
 				}
-			}
 
 
-			if (import)
+				return resource;
+			}
+			else
 			{
 			{
-				// Use the provided UUID if just one resource, otherwise we ignore the UUID. The current assumption is that
-				// such resources don't require persistent UUIDs. If that changes then this method needs to be updated.
-				if (resourcesToSave.size() == 1)
+				for (auto& entry : resourcesToSave)
 				{
 				{
-					Path outputPath = outputFolder + resourcesToSave[0].first;
+					Path outputPath = outputFolder + entry.first;;
 
 
-					HResource resource = Importer::instance().import(filePath, resourcesToSave[0].second, UUID);
+					HResource resource = Importer::instance().import(filePath, entry.second);
 					if (resource != nullptr)
 					if (resource != nullptr)
 					{
 					{
 						Resources::instance().save(resource, outputPath, true);
 						Resources::instance().save(resource, outputPath, true);
 						manifest->registerResource(resource.getUUID(), outputPath);
 						manifest->registerResource(resource.getUUID(), outputPath);
 					}
 					}
-
-					return resource;
-				}
-				else
-				{
-					for (auto& entry : resourcesToSave)
-					{
-						Path outputPath = outputFolder + entry.first;;
-
-						HResource resource = Importer::instance().import(filePath, entry.second);
-						if (resource != nullptr)
-						{
-							Resources::instance().save(resource, outputPath, true);
-							manifest->registerResource(resource.getUUID(), outputPath);
-						}
-					}
 				}
 				}
 			}
 			}
 
 
@@ -180,8 +157,15 @@ namespace bs
 
 
 		Vector<IconData> iconsToGenerate;
 		Vector<IconData> iconsToGenerate;
 
 
+		int idx = 0;
 		for(auto& entry : entries)
 		for(auto& entry : entries)
 		{
 		{
+			if(!importFlags[idx])
+			{
+				idx++;
+				continue;
+			}
+
 			std::string name = entry["Path"];
 			std::string name = entry["Path"];
 			std::string uuid;
 			std::string uuid;
 
 
@@ -199,13 +183,44 @@ namespace bs
 
 
 			HResource outputRes = importResource(name.c_str(), uuid.c_str());
 			HResource outputRes = importResource(name.c_str(), uuid.c_str());
 			if (outputRes == nullptr)
 			if (outputRes == nullptr)
+			{
+				idx++;
 				continue;
 				continue;
+			}
 
 
 			if (rtti_is_of_type<Shader>(outputRes.get()))
 			if (rtti_is_of_type<Shader>(outputRes.get()))
 			{
 			{
 				HShader shader = static_resource_cast<Shader>(outputRes);
 				HShader shader = static_resource_cast<Shader>(outputRes);
 				if (!verifyAndReportShader(shader))
 				if (!verifyAndReportShader(shader))
-					return false;
+					return;
+
+				if(dependencies != nullptr)
+				{
+					SPtr<ShaderMetaData> shaderMetaData = std::static_pointer_cast<ShaderMetaData>(shader->getMetaData());
+
+					nlohmann::json dependencyEntries;
+					if(shaderMetaData != nullptr && shaderMetaData->includes.size() > 0)
+					{
+						for(auto& include : shaderMetaData->includes)
+						{
+							Path includePath = include.c_str();
+							if (include.substr(0, 8) == "$ENGINE$" || include.substr(0, 8) == "$EDITOR$")
+							{
+								if (include.size() > 8)
+									includePath = include.substr(9, include.size() - 9);
+							}
+
+							nlohmann::json newDependencyEntry =
+							{ 
+								{ "Path", includePath.toString().c_str() }
+							};
+
+							dependencyEntries.push_back(newDependencyEntry);
+						}
+					}
+
+					(*dependencies)[name] = dependencyEntries;
+				}
 			}
 			}
 
 
 			if (mode == AssetType::Sprite)
 			if (mode == AssetType::Sprite)
@@ -241,6 +256,8 @@ namespace bs
 
 
 				iconsToGenerate.push_back(iconData);
 				iconsToGenerate.push_back(iconData);
 			}
 			}
+
+			idx++;
 		}
 		}
 
 
 		for(UINT32 i = 0; i < (UINT32)iconsToGenerate.size(); i++)
 		for(UINT32 i = 0; i < (UINT32)iconsToGenerate.size(); i++)
@@ -292,8 +309,6 @@ namespace bs
 				generateSprite(tex16, iconsToGenerate[i].name + "16", iconsToGenerate[i].SpriteUUIDs[2].c_str());
 				generateSprite(tex16, iconsToGenerate[i].name + "16", iconsToGenerate[i].SpriteUUIDs[2].c_str());
 			}
 			}
 		}
 		}
-
-		return true;
 	}
 	}
 
 
 	void BuiltinResourcesHelper::importFont(const Path& inputFile, const WString& outputName, const Path& outputFolder,
 	void BuiltinResourcesHelper::importFont(const Path& inputFile, const WString& outputName, const Path& outputFolder,
@@ -338,6 +353,57 @@ namespace bs
 		}
 		}
 	}
 	}
 
 
+	Vector<bool> BuiltinResourcesHelper::generateImportFlags(const nlohmann::json& entries, const Path& inputFolder,
+		time_t lastUpdateTime, bool forceImport, const nlohmann::json* dependencies, const Path& dependencyFolder)
+	{
+		Vector<bool> output(entries.size());
+		UINT32 idx = 0;
+		for (auto& entry : entries)
+		{
+			std::string name = entry["Path"];
+
+			if (forceImport)
+				output[idx] = true;
+			else
+			{
+				Path filePath = inputFolder + Path(name.c_str());
+
+				// Check timestamp
+				time_t lastModifiedSrc = FileSystem::getLastModifiedTime(filePath);
+				if (lastModifiedSrc > lastUpdateTime)
+					output[idx] = true;
+				else if (dependencies != nullptr) // Check dependencies
+				{
+					bool anyDepModified = false;
+					auto iterFind = dependencies->find(name);
+					if(iterFind != dependencies->end())
+					{
+						for(auto& dependency : *iterFind)
+						{
+							std::string dependencyName = dependency["Path"];
+							Path dependencyPath = dependencyFolder + Path(dependencyName.c_str());
+
+							time_t lastModifiedDep = FileSystem::getLastModifiedTime(dependencyPath);
+							if(lastModifiedDep > lastUpdateTime)
+							{
+								anyDepModified = true;
+								break;
+							}
+						}
+					}
+					
+					output[idx] = anyDepModified;
+				}
+				else
+					output[idx] = false;
+			}
+
+			idx++;
+		}
+
+		return output;
+	}
+
 	bool BuiltinResourcesHelper::updateJSON(const Path& folder, AssetType type, nlohmann::json& entries)
 	bool BuiltinResourcesHelper::updateJSON(const Path& folder, AssetType type, nlohmann::json& entries)
 	{
 	{
 		UnorderedSet<Path> existingEntries;
 		UnorderedSet<Path> existingEntries;
@@ -419,13 +485,15 @@ namespace bs
 		fileStream->close();
 		fileStream->close();
 	}
 	}
 
 
-	UINT32 BuiltinResourcesHelper::checkForModifications(const Path& folder, const Path& timeStampFile)
+	UINT32 BuiltinResourcesHelper::checkForModifications(const Path& folder, const Path& timeStampFile, 
+		time_t& lastUpdateTime)
 	{
 	{
+		lastUpdateTime = 0;
+
 		if (!FileSystem::exists(timeStampFile))
 		if (!FileSystem::exists(timeStampFile))
 			return 2;
 			return 2;
 
 
 		SPtr<DataStream> fileStream = FileSystem::openFile(timeStampFile);
 		SPtr<DataStream> fileStream = FileSystem::openFile(timeStampFile);
-		time_t lastUpdateTime = 0;
 		fileStream->read(&lastUpdateTime, sizeof(lastUpdateTime));
 		fileStream->read(&lastUpdateTime, sizeof(lastUpdateTime));
 		fileStream->close();
 		fileStream->close();
 
 

+ 59 - 59
Source/BansheeUtility/Source/BsDynLib.cpp

@@ -4,18 +4,18 @@
 #include "BsException.h"
 #include "BsException.h"
 
 
 #if BS_PLATFORM == BS_PLATFORM_WIN32
 #if BS_PLATFORM == BS_PLATFORM_WIN32
-#  define WIN32_LEAN_AND_MEAN
-#  if !defined(NOMINMAX) && defined(_MSC_VER)
-#	define NOMINMAX // required to stop windows.h messing up std::min
-#  endif
-#  include <windows.h>
+	#define WIN32_LEAN_AND_MEAN
+	#if !defined(NOMINMAX) && defined(_MSC_VER)
+		#define NOMINMAX // required to stop windows.h messing up std::min
+	#endif
+	#include <windows.h>
 #endif
 #endif
 
 
 #if BS_PLATFORM == BS_PLATFORM_OSX
 #if BS_PLATFORM == BS_PLATFORM_OSX
-#   include <dlfcn.h>
+	#include <dlfcn.h>
 #endif
 #endif
 
 
-namespace bs 
+namespace bs
 {
 {
 
 
 #if BS_PLATFORM == BS_PLATFORM_LINUX
 #if BS_PLATFORM == BS_PLATFORM_LINUX
@@ -25,80 +25,80 @@ namespace bs
 #elif BS_PLATFORM == BS_PLATFORM_WIN32
 #elif BS_PLATFORM == BS_PLATFORM_WIN32
 	const char* DynLib::EXTENSION = "dll";
 	const char* DynLib::EXTENSION = "dll";
 #else
 #else
-#  error Unhandled platform
+	#error Unhandled platform
 #endif
 #endif
 
 
-    DynLib::DynLib(const String& name)
-    {
-        mName = name;
-        m_hInst = nullptr;
+	DynLib::DynLib(const String& name)
+	{
+		mName = name;
+		m_hInst = nullptr;
 
 
 		load();
 		load();
-    }
+	}
 
 
-    DynLib::~DynLib()
-    {
-    }
+	DynLib::~DynLib()
+	{
+	}
 
 
-    void DynLib::load()
-    {
-		if(m_hInst)
+	void DynLib::load()
+	{
+		if (m_hInst)
 			return;
 			return;
 
 
-        m_hInst = (DYNLIB_HANDLE)DYNLIB_LOAD(mName.c_str());
+		m_hInst = (DYNLIB_HANDLE)DYNLIB_LOAD(mName.c_str());
 
 
-        if(!m_hInst)
+		if (!m_hInst)
 		{
 		{
-            BS_EXCEPT(InternalErrorException,  
-				"Could not load dynamic library " + mName + 
-                ".  System Error: " + dynlibError());
+			BS_EXCEPT(InternalErrorException,
+				"Could not load dynamic library " + mName +
+				".  System Error: " + dynlibError());
 		}
 		}
-    }
+	}
 
 
-    void DynLib::unload()
-    {
-		if(!m_hInst)
+	void DynLib::unload()
+	{
+		if (!m_hInst)
 			return;
 			return;
 
 
-        if(DYNLIB_UNLOAD(m_hInst))
+		if (DYNLIB_UNLOAD(m_hInst))
 		{
 		{
-			BS_EXCEPT(InternalErrorException, 
-                "Could not unload dynamic library " + mName +
-                ".  System Error: " + dynlibError());
+			BS_EXCEPT(InternalErrorException,
+				"Could not unload dynamic library " + mName +
+				".  System Error: " + dynlibError());
 		}
 		}
-    }
+	}
 
 
-    void* DynLib::getSymbol(const String& strName) const
-    {
-		if(!m_hInst)
+	void* DynLib::getSymbol(const String& strName) const
+	{
+		if (!m_hInst)
 			return nullptr;
 			return nullptr;
 
 
-        return (void*)DYNLIB_GETSYM(m_hInst, strName.c_str());
-    }
+		return (void*)DYNLIB_GETSYM(m_hInst, strName.c_str());
+	}
 
 
-    String DynLib::dynlibError() 
-    {
+	String DynLib::dynlibError()
+	{
 #if BS_PLATFORM == BS_PLATFORM_WIN32
 #if BS_PLATFORM == BS_PLATFORM_WIN32
-        LPVOID lpMsgBuf; 
-        FormatMessage( 
-            FORMAT_MESSAGE_ALLOCATE_BUFFER | 
-            FORMAT_MESSAGE_FROM_SYSTEM | 
-            FORMAT_MESSAGE_IGNORE_INSERTS, 
-            NULL, 
-            GetLastError(), 
-            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
-            (LPTSTR) &lpMsgBuf, 
-            0, 
-            NULL 
-            ); 
-        String ret = (char*)lpMsgBuf;
-        // Free the buffer.
-        LocalFree(lpMsgBuf);
-        return ret;
+		LPVOID lpMsgBuf;
+		FormatMessage(
+			FORMAT_MESSAGE_ALLOCATE_BUFFER |
+			FORMAT_MESSAGE_FROM_SYSTEM |
+			FORMAT_MESSAGE_IGNORE_INSERTS,
+			NULL,
+			GetLastError(),
+			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+			(LPTSTR)&lpMsgBuf,
+			0,
+			NULL
+		);
+		String ret = (char*)lpMsgBuf;
+		// Free the buffer.
+		LocalFree(lpMsgBuf);
+		return ret;
 #elif BS_PLATFORM == BS_PLATFORM_LINUX || BS_PLATFORM == BS_PLATFORM_OSX
 #elif BS_PLATFORM == BS_PLATFORM_LINUX || BS_PLATFORM == BS_PLATFORM_OSX
-        return String(dlerror());
+		return String(dlerror());
 #else
 #else
-        return String("");
+		return String("");
 #endif
 #endif
-    }
+	}
 }
 }