Explorar el Código

Fixes to resources import
Fixed a major issus in stack allocator that happened when allocating more than one block

BearishSun hace 10 años
padre
commit
8d5d71a0f0

+ 335 - 335
BansheeCore/Include/BsResources.h

@@ -1,336 +1,336 @@
-#pragma once
-
-#include "BsCorePrerequisites.h"
-#include "BsModule.h"
-
-namespace BansheeEngine
-{
-	/**
-	 * @brief	Manager for dealing with all engine resources. It allows you to save 
-	 *			new resources and load existing ones.
-	 *
-	 *			Used for manually dealing with resources but also for automatic resolving of
-	 *			resource handles.
-	 *
-	 * @note	Sim thread only.
-	 */
-	class BS_CORE_EXPORT Resources : public Module<Resources>
-	{
-		struct LoadedResourceData
-		{
-			LoadedResourceData()
-				:numInternalRefs(0)
-			{ }
-
-			LoadedResourceData(const WeakResourceHandle<Resource>& resource)
-				:resource(resource), numInternalRefs(0)
-			{ }
-
-			WeakResourceHandle<Resource> resource;
-			UINT32 numInternalRefs;
-		};
-
-		struct ResourceLoadData
-		{
-			ResourceLoadData(const WeakResourceHandle<Resource>& resource, UINT32 numDependencies)
-				:resData(resource), remainingDependencies(numDependencies)
-			{ }
-
-			LoadedResourceData resData;
-			ResourcePtr loadedData;
-			UINT32 remainingDependencies;
-			Vector<HResource> dependencies;
-			bool notifyImmediately;
-		};
-
-	public:
-		Resources();
-		~Resources();
-
-		/**
-		 * @brief	Loads the resource from a given path. Returns an empty handle if resource can't be loaded.
-		 *			Resource is loaded synchronously.
-		 *			
-		 *			All loaded resources are reference counted and will be automatically unloaded when all of their references go out
-		 *			of scope. 
-		 *			
-		 * @param	filePath				File path to the resource to load. This can be absolute or relative to the working folder.
-		 * @param	loadDependencies		If true all resources referenced by the root resource will be loaded as well.
-		 * @param	keepInternalReference	If true the resource system will keep an internal reference to the resource so it
-		 *									doesn't get destroyed with it goes out of scope. You can call ::release to release 
-		 *									the internal reference. Each call to load will create a new internal reference and 
-		 *									therefore must be followed by the same number of release calls. 
-		 *									
-		 *									If dependencies are being loaded, they will not have internal references created regardless
-		 *									of this parameter.
-		 *			
-		 * @see		release(ResourceHandleBase&), unloadAllUnused()
-		 */
-		HResource load(const Path& filePath, bool loadDependencies = true, bool keepInternalReference = true);
-
-		/**
-		 * @copydoc	load(const Path&, bool)
-		 */
-		template <class T>
-		ResourceHandle<T> load(const Path& filePath, bool loadDependencies = true, bool keepInternalReference = true)
-		{
-			return static_resource_cast<T>(load(filePath, loadDependencies, keepInternalReference));
-		}
-
-		/**
-		 * @brief	Loads the resource for the provided weak resource handle, or returns a loaded resource if already loaded.
-		 * 			
-		 * @see		load(const Path&, bool)
-		 */
-		HResource load(const WeakResourceHandle<Resource>& handle, bool loadDependencies = true, bool keepInternalReference = true);
-
-		/**
-		 * @copydoc	load(const WeakResourceHandle<T>&, bool)
-		 */
-		template <class T>
-		ResourceHandle<T> load(const WeakResourceHandle<T>& handle, bool loadDependencies = true, bool keepInternalReference = true)
-		{
-			return static_resource_cast<T>(load((const WeakResourceHandle<Resource>&)handle, loadDependencies, keepInternalReference));
-		}
-
-		/**
-		 * @brief	Loads the resource asynchronously. Initially returned resource handle will be invalid
-		 *			until resource loading is done.
-		 *
-		 * @param	filePath	Full pathname of the file.
-		 * 						
-		 * @note	You can use returned invalid handle in many engine systems as the engine will check for handle 
-		 *			validity before using it.
-		 *			
-		 * @see		load(const Path&, bool)
-		 */
-		HResource loadAsync(const Path& filePath, bool loadDependencies = true, bool keepInternalReference = true);
-
-		/**
-		 * @copydoc	loadAsync
-		 */
-		template <class T>
-		ResourceHandle<T> loadAsync(const Path& filePath, bool loadDependencies = true, bool keepInternalReference = true)
-		{
-			return static_resource_cast<T>(loadAsync(filePath, loadDependencies, keepInternalReference));
-		}
-
-		/**
-		 * @brief	Loads the resource with the given UUID. Returns an empty handle if resource can't be loaded.
-		 *
-		 * @param	uuid					UUID of the resource to load.
-		 * @param	async					If true resource will be loaded asynchronously. Handle to non-loaded
-		 *									resource will be returned immediately while loading will continue in the background.		
-		 * @param	loadDependencies		If true all resources referenced by the root resource will be loaded as well.
-		 * @param	keepInternalReference	If true the resource system will keep an internal reference to the resource so it
-		 *									doesn't get destroyed with it goes out of scope. You can call ::release to release 
-		 *									the internal reference. Each call to load will create a new internal reference and 
-		 *									therefore must be followed by the same number of release calls. 
-		 *									
-		 *									If dependencies are being loaded, they will not have internal references created regardless
-		 *									of this parameter.	
-		 *													
-		 * @see		load(const Path&, bool)
-		 */
-		HResource loadFromUUID(const String& uuid, bool async = false, bool loadDependencies = true, bool keepInternalReference = true);
-
-		/**
-		 * @brief	Releases an internal reference to the resource held by the resources system. This allows the resource
-		 * 			to be unloaded when it goes out of scope, if the resource was loaded with "keepInternalReference" parameter.
-		 *
-		 *			Alternatively you can also skip manually calling release() and call ::unloadAllUnused which will unload 
-		 *			all resources that do not have any external references, but you lose the fine grained control of what 
-		 *			will be unloaded.
-		 *			
-		 * @param	resourceHandle	Handle of the resource to release.
-		 */
-		void release(ResourceHandleBase& resource);
-
-		/**
-		 * @brief	Finds all resources that aren't being referenced outside of the resources system and unloads them.
-		 * 			
-		 * @see		release(ResourceHandleBase&)
-		 */
-		void unloadAllUnused();
-
-		/**
-		 * @brief	Saves the resource at the specified location.
-		 *
-		 * @param	resource 	Handle to the resource.
-		 * @param	filePath 	Full pathname of the file to save as.
-		 * @param	overwrite	(optional) If true, any existing resource at the specified location will
-		 * 						be overwritten.
-		 * 						
-		 * @note	If the resource is a GpuResource and you are in some way modifying it from the Core thread, make
-		 * 			sure all those commands are submitted before you call this method. Otherwise an obsolete
-		 * 			version of the resource might get saved.
-		 *
-		 *			If saving a core thread resource this is a potentially very slow operation as we must wait on the 
-		 *			core thread and the GPU in order to read the resource.
-		 */
-		void save(const HResource& resource, const Path& filePath, bool overwrite);
-
-		/**
-		 * @brief	Saves an existing resource to its previous location.
-		 *
-		 * @param	resource 	Handle to the resource.
-		 *
-		 * @note	If the resource is a GpuResource and you are in some way modifying it from the Core thread, make
-		 * 			sure all those commands are submitted before you call this method. Otherwise an obsolete
-		 * 			version of the resource might get saved.
-		 *
-		 *			If saving a core thread resource this is a potentially very slow operation as we must wait on the
-		 *			core thread and the GPU in order to read the resource.
-		 */
-		void save(const HResource& resource);
-
-		/**
-		 * @brief	Updates an existing resource handle with a new resource. Caller must ensure that
-		 * 			new resource type matches the original resource type.
-		 */
-		void update(HResource& handle, const ResourcePtr& resource);
-
-		/**
-		 * @brief	Returns a list of dependencies from the resources at the specified path. Resource will not be loaded
-		 *			or parsed, but instead the saved list of dependencies will be read from the file and returned.
-		 *
-		 * @param	filePath	Full path to the resource to get dependencies for.
-		 *
-		 * @returns	List of dependencies represented as UUIDs.
-		 */
-		Vector<String> getDependencies(const Path& filePath);
-
-		/**
-		 * @brief	Checks is the resource with the specified UUID loaded.
-		 *
-		 * @param	uuid			UUID of the resource to check.
-		 * @param	checkInProgress	Should this method also check resources that are in progress of being asynchronously loaded.
-		 * 							
-		 * @return	True if loaded or loading in progress, false otherwise.
-		 */
-		bool isLoaded(const String& uuid, bool checkInProgress = true);
-
-		/**
-		 * @brief	Creates a new resource handle from a resource pointer. 
-		 *
-		 * @note	Internal method used primarily be resource factory methods.
-		 */
-		HResource _createResourceHandle(const ResourcePtr& obj);
-
-		/**
-		 * @brief	Returns an existing handle for the specified UUID if one exists, or creates a new one.
-		 */
-		HResource _getResourceHandle(const String& uuid);
-
-		/**
-		 * @brief	Allows you to set a resource manifest containing UUID <-> file path mapping that is
-		 * 			used when resolving resource references.
-		 *
-		 * @note	If you want objects that reference resources (using ResourceHandles) to be able to
-		 * 			find that resource even after application restart, then you must save the resource
-		 * 			manifest before closing the application and restore it upon startup.
-		 * 			Otherwise resources will be assigned brand new UUIDs and references will be broken.
-		 */
-		void registerResourceManifest(const ResourceManifestPtr& manifest);
-
-		/**
-		 * @brief	Unregisters a resource manifest previously registered with ::registerResourceManifest.
-		 */
-		void unregisterResourceManifest(const ResourceManifestPtr& manifest);
-
-		/**
-		 * @brief	Allows you to retrieve resource manifest containing UUID <-> file path mapping that is
-		 * 			used when resolving resource references.
-		 * 			
-		 * @note	Resources module internally holds a "Default" manifest that it automatically updated whenever
-		 * 			a resource is saved.
-		 *
-		 * @see		registerResourceManifest
-		 */
-		ResourceManifestPtr getResourceManifest(const String& name) const;
-
-		/**
-		 * @brief	Attempts to retrieve file path from the provided UUID. Returns true
-		 *			if successful, false otherwise.
-		 */
-		bool getFilePathFromUUID(const String& uuid, Path& filePath) const;
-
-		/**
-		 * @brief	Attempts to retrieve UUID from the provided file path. Returns true
-		 *			if successful, false otherwise.
-		 */
-		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. Provides UUID of the destroyed resource.
-		 *
-		 * @note	It is undefined from which thread this will get called from.
-		 */
-		Event<void(const String&)> 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;
-	private:
-		friend class ResourceHandleBase;
-
-		/**
-		 * @brief	Starts resource loading or returns an already loaded resource. Both UUID and filePath must match the
-		 * 			same resource, although you may provide an empty path in which case the resource will be retrieved
-		 * 			from memory if its currently loaded.
-		 * 			
-		 * @param	incrementRef	Determines should the internal reference count be incremented.
-		 */
-		HResource loadInternal(const String& UUID, const Path& filePath, bool synchronous, bool loadDependencies, bool incrementRef);
-
-		/**
-		 * @brief	Performs actually reading and deserializing of the resource file. 
-		 *			Called from various worker threads.
-		 */
-		ResourcePtr loadFromDiskAndDeserialize(const Path& filePath);
-
-		/**
-		 * @brief	Triggered when individual resource has finished loading.
-		 */
-		void loadComplete(HResource& resource);
-
-		/**
-		 * @brief	Callback triggered when the task manager is ready to process the loading task.
-		 */
-		void loadCallback(const Path& filePath, HResource& resource);
-
-		/**
-		 * @brief	Destroys a resource, freeing its memory.
-		 */
-		void destroy(ResourceHandleBase& resource);
-
-	private:
-		Vector<ResourceManifestPtr> mResourceManifests;
-		ResourceManifestPtr mDefaultResourceManifest;
-
-		BS_MUTEX(mInProgressResourcesMutex);
-		BS_MUTEX(mLoadedResourceMutex);
-
-		UnorderedMap<String, WeakResourceHandle<Resource>> mHandles;
-		UnorderedMap<String, LoadedResourceData> mLoadedResources;
-		UnorderedMap<String, ResourceLoadData*> mInProgressResources; // Resources that are being asynchronously loaded
-		UnorderedMap<String, Vector<ResourceLoadData*>> mDependantLoads; // Allows dependency to be notified when a dependant is loaded
-	};
-
-	/**
-	 * @brief	Provides global access to the resource manager.
-	 */
-	BS_CORE_EXPORT Resources& gResources();
+#pragma once
+
+#include "BsCorePrerequisites.h"
+#include "BsModule.h"
+
+namespace BansheeEngine
+{
+	/**
+	 * @brief	Manager for dealing with all engine resources. It allows you to save 
+	 *			new resources and load existing ones.
+	 *
+	 *			Used for manually dealing with resources but also for automatic resolving of
+	 *			resource handles.
+	 *
+	 * @note	Sim thread only.
+	 */
+	class BS_CORE_EXPORT Resources : public Module<Resources>
+	{
+		struct LoadedResourceData
+		{
+			LoadedResourceData()
+				:numInternalRefs(0)
+			{ }
+
+			LoadedResourceData(const WeakResourceHandle<Resource>& resource)
+				:resource(resource), numInternalRefs(0)
+			{ }
+
+			WeakResourceHandle<Resource> resource;
+			UINT32 numInternalRefs;
+		};
+
+		struct ResourceLoadData
+		{
+			ResourceLoadData(const WeakResourceHandle<Resource>& resource, UINT32 numDependencies)
+				:resData(resource), remainingDependencies(numDependencies)
+			{ }
+
+			LoadedResourceData resData;
+			ResourcePtr loadedData;
+			UINT32 remainingDependencies;
+			Vector<HResource> dependencies;
+			bool notifyImmediately;
+		};
+
+	public:
+		Resources();
+		~Resources();
+
+		/**
+		 * @brief	Loads the resource from a given path. Returns an empty handle if resource can't be loaded.
+		 *			Resource is loaded synchronously.
+		 *			
+		 *			All loaded resources are reference counted and will be automatically unloaded when all of their references go out
+		 *			of scope. 
+		 *			
+		 * @param	filePath				File path to the resource to load. This can be absolute or relative to the working folder.
+		 * @param	loadDependencies		If true all resources referenced by the root resource will be loaded as well.
+		 * @param	keepInternalReference	If true the resource system will keep an internal reference to the resource so it
+		 *									doesn't get destroyed with it goes out of scope. You can call release() to release 
+		 *									the internal reference. Each call to load will create a new internal reference and 
+		 *									therefore must be followed by the same number of release calls. 
+		 *									
+		 *									If dependencies are being loaded, they will not have internal references created regardless
+		 *									of this parameter.
+		 *			
+		 * @see		release(ResourceHandleBase&), unloadAllUnused()
+		 */
+		HResource load(const Path& filePath, bool loadDependencies = true, bool keepInternalReference = true);
+
+		/**
+		 * @copydoc	load(const Path&, bool)
+		 */
+		template <class T>
+		ResourceHandle<T> load(const Path& filePath, bool loadDependencies = true, bool keepInternalReference = true)
+		{
+			return static_resource_cast<T>(load(filePath, loadDependencies, keepInternalReference));
+		}
+
+		/**
+		 * @brief	Loads the resource for the provided weak resource handle, or returns a loaded resource if already loaded.
+		 * 			
+		 * @see		load(const Path&, bool)
+		 */
+		HResource load(const WeakResourceHandle<Resource>& handle, bool loadDependencies = true, bool keepInternalReference = true);
+
+		/**
+		 * @copydoc	load(const WeakResourceHandle<T>&, bool)
+		 */
+		template <class T>
+		ResourceHandle<T> load(const WeakResourceHandle<T>& handle, bool loadDependencies = true, bool keepInternalReference = true)
+		{
+			return static_resource_cast<T>(load((const WeakResourceHandle<Resource>&)handle, loadDependencies, keepInternalReference));
+		}
+
+		/**
+		 * @brief	Loads the resource asynchronously. Initially returned resource handle will be invalid
+		 *			until resource loading is done.
+		 *
+		 * @param	filePath	Full pathname of the file.
+		 * 						
+		 * @note	You can use returned invalid handle in many engine systems as the engine will check for handle 
+		 *			validity before using it.
+		 *			
+		 * @see		load(const Path&, bool)
+		 */
+		HResource loadAsync(const Path& filePath, bool loadDependencies = true, bool keepInternalReference = true);
+
+		/**
+		 * @copydoc	loadAsync
+		 */
+		template <class T>
+		ResourceHandle<T> loadAsync(const Path& filePath, bool loadDependencies = true, bool keepInternalReference = true)
+		{
+			return static_resource_cast<T>(loadAsync(filePath, loadDependencies, keepInternalReference));
+		}
+
+		/**
+		 * @brief	Loads the resource with the given UUID. Returns an empty handle if resource can't be loaded.
+		 *
+		 * @param	uuid					UUID of the resource to load.
+		 * @param	async					If true resource will be loaded asynchronously. Handle to non-loaded
+		 *									resource will be returned immediately while loading will continue in the background.		
+		 * @param	loadDependencies		If true all resources referenced by the root resource will be loaded as well.
+		 * @param	keepInternalReference	If true the resource system will keep an internal reference to the resource so it
+		 *									doesn't get destroyed with it goes out of scope. You can call ::release to release 
+		 *									the internal reference. Each call to load will create a new internal reference and 
+		 *									therefore must be followed by the same number of release calls. 
+		 *									
+		 *									If dependencies are being loaded, they will not have internal references created regardless
+		 *									of this parameter.	
+		 *													
+		 * @see		load(const Path&, bool)
+		 */
+		HResource loadFromUUID(const String& uuid, bool async = false, bool loadDependencies = true, bool keepInternalReference = true);
+
+		/**
+		 * @brief	Releases an internal reference to the resource held by the resources system. This allows the resource
+		 * 			to be unloaded when it goes out of scope, if the resource was loaded with "keepInternalReference" parameter.
+		 *
+		 *			Alternatively you can also skip manually calling release() and call ::unloadAllUnused which will unload 
+		 *			all resources that do not have any external references, but you lose the fine grained control of what 
+		 *			will be unloaded.
+		 *			
+		 * @param	resourceHandle	Handle of the resource to release.
+		 */
+		void release(ResourceHandleBase& resource);
+
+		/**
+		 * @brief	Finds all resources that aren't being referenced outside of the resources system and unloads them.
+		 * 			
+		 * @see		release(ResourceHandleBase&)
+		 */
+		void unloadAllUnused();
+
+		/**
+		 * @brief	Saves the resource at the specified location.
+		 *
+		 * @param	resource 	Handle to the resource.
+		 * @param	filePath 	Full pathname of the file to save as.
+		 * @param	overwrite	(optional) If true, any existing resource at the specified location will
+		 * 						be overwritten.
+		 * 						
+		 * @note	If the resource is a GpuResource and you are in some way modifying it from the Core thread, make
+		 * 			sure all those commands are submitted before you call this method. Otherwise an obsolete
+		 * 			version of the resource might get saved.
+		 *
+		 *			If saving a core thread resource this is a potentially very slow operation as we must wait on the 
+		 *			core thread and the GPU in order to read the resource.
+		 */
+		void save(const HResource& resource, const Path& filePath, bool overwrite);
+
+		/**
+		 * @brief	Saves an existing resource to its previous location.
+		 *
+		 * @param	resource 	Handle to the resource.
+		 *
+		 * @note	If the resource is a GpuResource and you are in some way modifying it from the Core thread, make
+		 * 			sure all those commands are submitted before you call this method. Otherwise an obsolete
+		 * 			version of the resource might get saved.
+		 *
+		 *			If saving a core thread resource this is a potentially very slow operation as we must wait on the
+		 *			core thread and the GPU in order to read the resource.
+		 */
+		void save(const HResource& resource);
+
+		/**
+		 * @brief	Updates an existing resource handle with a new resource. Caller must ensure that
+		 * 			new resource type matches the original resource type.
+		 */
+		void update(HResource& handle, const ResourcePtr& resource);
+
+		/**
+		 * @brief	Returns a list of dependencies from the resources at the specified path. Resource will not be loaded
+		 *			or parsed, but instead the saved list of dependencies will be read from the file and returned.
+		 *
+		 * @param	filePath	Full path to the resource to get dependencies for.
+		 *
+		 * @returns	List of dependencies represented as UUIDs.
+		 */
+		Vector<String> getDependencies(const Path& filePath);
+
+		/**
+		 * @brief	Checks is the resource with the specified UUID loaded.
+		 *
+		 * @param	uuid			UUID of the resource to check.
+		 * @param	checkInProgress	Should this method also check resources that are in progress of being asynchronously loaded.
+		 * 							
+		 * @return	True if loaded or loading in progress, false otherwise.
+		 */
+		bool isLoaded(const String& uuid, bool checkInProgress = true);
+
+		/**
+		 * @brief	Creates a new resource handle from a resource pointer. 
+		 *
+		 * @note	Internal method used primarily be resource factory methods.
+		 */
+		HResource _createResourceHandle(const ResourcePtr& obj);
+
+		/**
+		 * @brief	Returns an existing handle for the specified UUID if one exists, or creates a new one.
+		 */
+		HResource _getResourceHandle(const String& uuid);
+
+		/**
+		 * @brief	Allows you to set a resource manifest containing UUID <-> file path mapping that is
+		 * 			used when resolving resource references.
+		 *
+		 * @note	If you want objects that reference resources (using ResourceHandles) to be able to
+		 * 			find that resource even after application restart, then you must save the resource
+		 * 			manifest before closing the application and restore it upon startup.
+		 * 			Otherwise resources will be assigned brand new UUIDs and references will be broken.
+		 */
+		void registerResourceManifest(const ResourceManifestPtr& manifest);
+
+		/**
+		 * @brief	Unregisters a resource manifest previously registered with ::registerResourceManifest.
+		 */
+		void unregisterResourceManifest(const ResourceManifestPtr& manifest);
+
+		/**
+		 * @brief	Allows you to retrieve resource manifest containing UUID <-> file path mapping that is
+		 * 			used when resolving resource references.
+		 * 			
+		 * @note	Resources module internally holds a "Default" manifest that it automatically updated whenever
+		 * 			a resource is saved.
+		 *
+		 * @see		registerResourceManifest
+		 */
+		ResourceManifestPtr getResourceManifest(const String& name) const;
+
+		/**
+		 * @brief	Attempts to retrieve file path from the provided UUID. Returns true
+		 *			if successful, false otherwise.
+		 */
+		bool getFilePathFromUUID(const String& uuid, Path& filePath) const;
+
+		/**
+		 * @brief	Attempts to retrieve UUID from the provided file path. Returns true
+		 *			if successful, false otherwise.
+		 */
+		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. Provides UUID of the destroyed resource.
+		 *
+		 * @note	It is undefined from which thread this will get called from.
+		 */
+		Event<void(const String&)> 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;
+	private:
+		friend class ResourceHandleBase;
+
+		/**
+		 * @brief	Starts resource loading or returns an already loaded resource. Both UUID and filePath must match the
+		 * 			same resource, although you may provide an empty path in which case the resource will be retrieved
+		 * 			from memory if its currently loaded.
+		 * 			
+		 * @param	incrementRef	Determines should the internal reference count be incremented.
+		 */
+		HResource loadInternal(const String& UUID, const Path& filePath, bool synchronous, bool loadDependencies, bool incrementRef);
+
+		/**
+		 * @brief	Performs actually reading and deserializing of the resource file. 
+		 *			Called from various worker threads.
+		 */
+		ResourcePtr loadFromDiskAndDeserialize(const Path& filePath);
+
+		/**
+		 * @brief	Triggered when individual resource has finished loading.
+		 */
+		void loadComplete(HResource& resource);
+
+		/**
+		 * @brief	Callback triggered when the task manager is ready to process the loading task.
+		 */
+		void loadCallback(const Path& filePath, HResource& resource);
+
+		/**
+		 * @brief	Destroys a resource, freeing its memory.
+		 */
+		void destroy(ResourceHandleBase& resource);
+
+	private:
+		Vector<ResourceManifestPtr> mResourceManifests;
+		ResourceManifestPtr mDefaultResourceManifest;
+
+		BS_MUTEX(mInProgressResourcesMutex);
+		BS_MUTEX(mLoadedResourceMutex);
+
+		UnorderedMap<String, WeakResourceHandle<Resource>> mHandles;
+		UnorderedMap<String, LoadedResourceData> mLoadedResources;
+		UnorderedMap<String, ResourceLoadData*> mInProgressResources; // Resources that are being asynchronously loaded
+		UnorderedMap<String, Vector<ResourceLoadData*>> mDependantLoads; // Allows dependency to be notified when a dependant is loaded
+	};
+
+	/**
+	 * @brief	Provides global access to the resource manager.
+	 */
+	BS_CORE_EXPORT Resources& gResources();
 }
 }

+ 733 - 722
BansheeCore/Source/BsResources.cpp

@@ -1,723 +1,734 @@
-#include "BsResources.h"
-#include "BsResource.h"
-#include "BsResourceManifest.h"
-#include "BsException.h"
-#include "BsFileSerializer.h"
-#include "BsFileSystem.h"
-#include "BsTaskScheduler.h"
-#include "BsUUID.h"
-#include "BsDebug.h"
-#include "BsUtility.h"
-#include "BsSavedResourceData.h"
-#include "BsResourceListenerManager.h"
-
-namespace BansheeEngine
-{
-	Resources::Resources()
-	{
-		mDefaultResourceManifest = ResourceManifest::create("Default");
-		mResourceManifests.push_back(mDefaultResourceManifest);
-	}
-
-	Resources::~Resources()
-	{
-		// Unload and invalidate all resources
-		UnorderedMap<String, LoadedResourceData> loadedResourcesCopy = mLoadedResources;
-
-		for (auto& loadedResourcePair : loadedResourcesCopy)
-			destroy(loadedResourcePair.second.resource);
-	}
-
-	HResource Resources::load(const Path& filePath, bool loadDependencies, bool keepInternalReference)
-	{
-		String uuid;
-		bool foundUUID = getUUIDFromFilePath(filePath, uuid);
-
-		if (!foundUUID)
-			uuid = UUIDGenerator::generateRandom();
-
-		return loadInternal(uuid, filePath, true, loadDependencies, keepInternalReference);
-	}
-
-	HResource Resources::load(const WeakResourceHandle<Resource>& handle, bool loadDependencies, bool keepInternalReference)
-	{
-		if (handle.mData == nullptr)
-			return HResource();
-
-		String uuid = handle.getUUID();
-		return loadFromUUID(uuid, false, loadDependencies, keepInternalReference);
-	}
-
-	HResource Resources::loadAsync(const Path& filePath, bool loadDependencies, bool keepInternalReference)
-	{
-		String uuid;
-		bool foundUUID = getUUIDFromFilePath(filePath, uuid);
-
-		if (!foundUUID)
-			uuid = UUIDGenerator::generateRandom();
-
-		return loadInternal(uuid, filePath, false, loadDependencies, keepInternalReference);
-	}
-
-	HResource Resources::loadFromUUID(const String& uuid, bool async, bool loadDependencies, bool keepInternalReference)
-	{
-		Path filePath;
-
-		// Default manifest is at 0th index but all other take priority since Default manifest could
-		// contain obsolete data. 
-		for (auto iter = mResourceManifests.rbegin(); iter != mResourceManifests.rend(); ++iter)
-		{
-			if ((*iter)->uuidToFilePath(uuid, filePath))
-				break;
-		}
-
-		return loadInternal(uuid, filePath, !async, loadDependencies, keepInternalReference);
-	}
-
-	HResource Resources::loadInternal(const String& UUID, const Path& filePath, bool synchronous, bool loadDependencies, bool keepInternalReference)
-	{
-		HResource outputResource;
-
-		bool alreadyLoading = false;
-		bool loadInProgress = false;
-		{
-			// Check if resource is already being loaded on a worker thread
-			BS_LOCK_MUTEX(mInProgressResourcesMutex);
-			auto iterFind2 = mInProgressResources.find(UUID);
-			if (iterFind2 != mInProgressResources.end())
-			{
-				LoadedResourceData& resData = iterFind2->second->resData;
-				outputResource = resData.resource.lock();
-
-				if (keepInternalReference)
-				{
-					resData.numInternalRefs++;
-					outputResource.addInternalRef();
-				}
-
-				alreadyLoading = true;
-				loadInProgress = true;
-			}
-
-			// Previously being loaded as async but now we want it synced, so we wait
-			if (loadInProgress && synchronous)
-				outputResource.blockUntilLoaded();
-
-			if (!alreadyLoading)
-			{
-				BS_LOCK_MUTEX(mLoadedResourceMutex);
-				auto iterFind = mLoadedResources.find(UUID);
-				if (iterFind != mLoadedResources.end()) // Resource is already loaded
-				{
-					LoadedResourceData& resData = iterFind->second;
-					outputResource = resData.resource.lock();
-
-					if (keepInternalReference)
-					{
-						resData.numInternalRefs++;
-						outputResource.addInternalRef();
-					}
-
-					alreadyLoading = true;
-				}
-			}
-		}
-
-
-		// Not loaded and not in progress, start loading of new resource
-		// (or if already loaded or in progress, load any dependencies)
-		if (!alreadyLoading)
-		{
-			// Check if the handle already exists
-			BS_LOCK_MUTEX(mLoadedResourceMutex);
-			auto iterFind = mHandles.find(UUID);
-			if (iterFind != mHandles.end())
-				outputResource = iterFind->second.lock();
-			else
-			{
-				outputResource = HResource(UUID);
-				mHandles[UUID] = outputResource.getWeak();
-			}			
-		}
-
-		// We have nowhere to load from, warn and complete load if a file path was provided,
-		// otherwise pass through as we might just want to load from memory. 
-		if (filePath.isEmpty())
-		{
-			if (!alreadyLoading)
-			{
-				LOGWRN_VERBOSE("Cannot load resource. Resource with UUID '" + UUID + "' doesn't exist.");
-
-				// Complete the load as that the depedency counter is properly reduced, in case this 
-				// is a dependency of some other resource.
-				loadComplete(outputResource);
-				return outputResource;
-			}
-		}
-		else if (!FileSystem::isFile(filePath))
-		{
-			LOGWRN_VERBOSE("Cannot load resource. Specified file: " + filePath.toString() + " doesn't exist.");
-
-			// Complete the load as that the depedency counter is properly reduced, in case this 
-			// is a dependency of some other resource.
-			loadComplete(outputResource);
-			assert(!loadInProgress); // Resource already being loaded but we can't find its path now?
-
-			return outputResource;
-		}
-
-		// Load dependency data if a file path is provided
-		SPtr<SavedResourceData> savedResourceData;
-		if (!filePath.isEmpty())
-		{
-			FileDecoder fs(filePath);
-			savedResourceData = std::static_pointer_cast<SavedResourceData>(fs.decode());
-		}
-
-		// If already loading keep the old load operation active, otherwise create a new one
-		if (!alreadyLoading)
-		{
-			{
-				BS_LOCK_MUTEX(mInProgressResourcesMutex);
-
-				ResourceLoadData* loadData = bs_new<ResourceLoadData>(outputResource.getWeak(), 0);
-				mInProgressResources[UUID] = loadData;
-				loadData->resData = outputResource.getWeak();
-
-				if (keepInternalReference)
-				{
-					loadData->resData.numInternalRefs++;
-					outputResource.addInternalRef();
-				}
-
-				loadData->remainingDependencies = 1;
-				loadData->notifyImmediately = synchronous; // Make resource listener trigger before exit if loading synchronously
-
-				// Register dependencies and count them so we know when the resource is fully loaded
-				if (loadDependencies && savedResourceData != nullptr)
-				{
-					for (auto& dependency : savedResourceData->getDependencies())
-					{
-						if (dependency != UUID)
-						{
-							mDependantLoads[dependency].push_back(loadData);
-							loadData->remainingDependencies++;
-						}
-					}
-				}
-			}
-
-			if (loadDependencies && savedResourceData != nullptr)
-			{
-				const Vector<String>& dependencyUUIDs = savedResourceData->getDependencies();
-				UINT32 numDependencies = (UINT32)dependencyUUIDs.size();
-				Vector<HResource> dependencies(numDependencies);
-
-				for (UINT32 i = 0; i < numDependencies; i++)
-					dependencies[i] = loadFromUUID(dependencyUUIDs[i], !synchronous, true, false);
-
-				// Keep dependencies alive until the parent is done loading
-				{
-					BS_LOCK_MUTEX(mInProgressResourcesMutex);
-
-					// At this point the resource is guaranteed to still be in-progress, so it's safe to update its dependency list
-					mInProgressResources[UUID]->dependencies = dependencies;
-				}
-			}
-		}
-		else if (loadDependencies && savedResourceData != nullptr) // Queue dependencies in case they aren't already loaded
-		{
-			const Vector<String>& dependencies = savedResourceData->getDependencies();
-			if (!dependencies.empty())
-			{
-				{
-					BS_LOCK_MUTEX(mInProgressResourcesMutex);
-
-					ResourceLoadData* loadData = nullptr;
-
-					auto iterFind = mInProgressResources.find(UUID);
-					if (iterFind == mInProgressResources.end()) // Fully loaded
-					{
-						loadData = bs_new<ResourceLoadData>(outputResource.getWeak(), 0);
-						loadData->resData = outputResource.getWeak();
-						loadData->remainingDependencies = 0;
-						loadData->notifyImmediately = synchronous; // Make resource listener trigger before exit if loading synchronously
-
-						mInProgressResources[UUID] = loadData;
-					}
-					else
-					{
-						loadData = iterFind->second;
-					}
-
-					// Register dependencies and count them so we know when the resource is fully loaded
-					for (auto& dependency : dependencies)
-					{
-						if (dependency != UUID)
-						{
-							bool registerDependency = true;
-
-							auto iterFind2 = mDependantLoads.find(dependency);
-							if (iterFind2 != mDependantLoads.end())
-							{
-								Vector<ResourceLoadData*>& dependantData = iterFind2->second;
-								auto iterFind3 = std::find_if(dependantData.begin(), dependantData.end(),
-									[&](ResourceLoadData* x)
-								{
-									return x->resData.resource.getUUID() == outputResource.getUUID();
-								});
-
-								registerDependency = iterFind3 == dependantData.end();
-							}
-
-							if (registerDependency)
-							{
-								mDependantLoads[dependency].push_back(loadData);
-								loadData->remainingDependencies++;
-								loadData->dependencies.push_back(_getResourceHandle(dependency));
-							}
-						}
-					}
-				}
-
-				for (auto& dependency : dependencies)
-					loadFromUUID(dependency, !synchronous, true, false);
-			}
-		}
-
-		// Actually start the file read operation if not already loaded or in progress
-		if (!alreadyLoading && !filePath.isEmpty())
-		{
-			// Synchronous or the resource doesn't support async, read the file immediately
-			if (synchronous || !savedResourceData->allowAsyncLoading())
-			{
-				loadCallback(filePath, outputResource);
-			}
-			else // Asynchronous, read the file on a worker thread
-			{
-				String fileName = filePath.getFilename();
-				String taskName = "Resource load: " + fileName;
-
-				TaskPtr task = Task::create(taskName, std::bind(&Resources::loadCallback, this, filePath, outputResource));
-				TaskScheduler::instance().addTask(task);
-			}
-		}
-		else // File already loaded or in progress
-		{
-			// Complete the load unless its in progress in which case we wait for its worker thread to complete it.
-			// In case file is already loaded this will only decrement dependency count in case this resource is a dependency.
-			if (!loadInProgress)
-				loadComplete(outputResource);
-			else
-			{
-				// In case loading finished in the meantime we cannot be sure at what point ::loadComplete was triggered,
-				// so trigger it manually so that the dependency count is properly decremented in case this resource
-				// is a dependency.
-				BS_LOCK_MUTEX(mLoadedResourceMutex);
-				auto iterFind = mLoadedResources.find(UUID);
-				if (iterFind != mLoadedResources.end())
-					loadComplete(outputResource);
-			}
-		}
-
-		return outputResource;
-	}
-
-	ResourcePtr Resources::loadFromDiskAndDeserialize(const Path& filePath)
-	{
-		FileDecoder fs(filePath);
-		fs.skip(); // Skipped over saved resource data
-		std::shared_ptr<IReflectable> loadedData = fs.decode();
-
-		if(loadedData == nullptr)
-			BS_EXCEPT(InternalErrorException, "Unable to load resource.");
-
-		if(!loadedData->isDerivedFrom(Resource::getRTTIStatic()))
-			BS_EXCEPT(InternalErrorException, "Loaded class doesn't derive from Resource.");
-
-		ResourcePtr resource = std::static_pointer_cast<Resource>(loadedData);
-		return resource;
-	}
-
-	void Resources::release(ResourceHandleBase& resource)
-	{
-		const String& UUID = resource.getUUID();
-
-		{
-			bool loadInProgress = false;
-
-			BS_LOCK_MUTEX(mInProgressResourcesMutex);
-			auto iterFind2 = mInProgressResources.find(UUID);
-			if (iterFind2 != mInProgressResources.end())
-				loadInProgress = true;
-
-			// Technically we should be able to just cancel a load in progress instead of blocking until it finishes.
-			// However that would mean the last reference could get lost on whatever thread did the loading, which
-			// isn't something that's supported. If this ends up being a problem either make handle counting atomic
-			// or add a separate queue for objects destroyed from the load threads.
-			if (loadInProgress)
-				resource.blockUntilLoaded();
-
-			{
-				BS_LOCK_MUTEX(mLoadedResourceMutex);
-				auto iterFind = mLoadedResources.find(UUID);
-				if (iterFind != mLoadedResources.end()) // Resource is already loaded
-				{
-					LoadedResourceData& resData = iterFind->second;
-
-					assert(resData.numInternalRefs > 0);
-					resData.numInternalRefs--;
-					resource.removeInternalRef();
-
-					return;
-				}
-			}
-		}
-	}
-
-	void Resources::unloadAllUnused()
-	{
-		Vector<HResource> resourcesToUnload;
-
-		{
-			BS_LOCK_MUTEX(mLoadedResourceMutex);
-			for(auto iter = mLoadedResources.begin(); iter != mLoadedResources.end(); ++iter)
-			{
-				const LoadedResourceData& resData = iter->second;
-
-				if (resData.resource.mData->mRefCount == resData.numInternalRefs) // Only internal references exist, free it
-					resourcesToUnload.push_back(resData.resource.lock());
-			}
-		}
-
-		// Note: When unloading multiple resources it's possible that unloading one will also unload
-		// another resource in "resourcesToUnload". This is fine because "unload" deals with invalid
-		// handles gracefully.
-		for(auto iter = resourcesToUnload.begin(); iter != resourcesToUnload.end(); ++iter)
-		{
-			release(*iter);
-		}
-	}
-
-	void Resources::destroy(ResourceHandleBase& resource)
-	{
-		if (resource.mData == nullptr)
-			return;
-
-		const String& uuid = resource.getUUID();
-		if (!resource.isLoaded(false))
-		{
-			bool loadInProgress = false;
-			{
-				BS_LOCK_MUTEX(mInProgressResourcesMutex);
-				auto iterFind2 = mInProgressResources.find(uuid);
-				if (iterFind2 != mInProgressResources.end())
-					loadInProgress = true;
-			}
-
-			if (loadInProgress) // If it's still loading wait until that finishes
-				resource.blockUntilLoaded();
-			else
-				return; // Already unloaded
-		}
-
-		// Notify external systems before we actually destroy it
-		onResourceDestroyed(uuid);
-		resource.mData->mPtr->destroy();
-
-		{
-			BS_LOCK_MUTEX(mLoadedResourceMutex);
-
-			auto iterFind = mLoadedResources.find(uuid);
-			if (iterFind != mLoadedResources.end())
-			{
-				LoadedResourceData& resData = iterFind->second;
-				while (resData.numInternalRefs > 0)
-				{
-					resData.numInternalRefs--;
-					resData.resource.removeInternalRef();
-				}
-
-				mLoadedResources.erase(iterFind);
-			}
-			else
-			{
-				assert(false); // This should never happen but in case it does fail silently in release mode
-			}
-		}
-
-		resource.setHandleData(nullptr, uuid);
-	}
-
-	void Resources::save(const HResource& resource, const Path& filePath, bool overwrite)
-	{
-		if (resource == nullptr)
-			return;
-
-		if (!resource.isLoaded(false))
-		{
-			bool loadInProgress = false;
-			{
-				BS_LOCK_MUTEX(mInProgressResourcesMutex);
-				auto iterFind2 = mInProgressResources.find(resource.getUUID());
-				if (iterFind2 != mInProgressResources.end())
-					loadInProgress = true;
-			}
-
-			if (loadInProgress) // If it's still loading wait until that finishes
-				resource.blockUntilLoaded();
-			else
-				return; // Nothing to save
-		}
-
-		bool fileExists = FileSystem::isFile(filePath);
-		if(fileExists)
-		{
-			if(overwrite)
-				FileSystem::remove(filePath);
-			else
-				BS_EXCEPT(InvalidParametersException, "Another file exists at the specified location.");
-		}
-
-		mDefaultResourceManifest->registerResource(resource.getUUID(), filePath);
-
-		Vector<ResourceDependency> dependencyList = Utility::findResourceDependencies(*resource.get());
-
-		Vector<String> dependencyUUIDs(dependencyList.size());
-		for (UINT32 i = 0; i < (UINT32)dependencyList.size(); i++)
-			dependencyUUIDs[i] = dependencyList[i].resource.getUUID();
-
-		SPtr<SavedResourceData> resourceData = bs_shared_ptr_new<SavedResourceData>(dependencyUUIDs, resource->allowAsyncLoading());
-
-		FileEncoder fs(filePath);
-		fs.encode(resourceData.get());
-		fs.encode(resource.get());
-	}
-
-	void Resources::save(const HResource& resource)
-	{
-		if (resource == nullptr)
-			return;
-
-		Path path;
-		if (getFilePathFromUUID(resource.getUUID(), path))
-			save(resource, path, true);
-	}
-
-	void Resources::update(HResource& handle, const ResourcePtr& resource)
-	{
-		handle.setHandleData(resource, handle.getUUID());
-
-		onResourceModified(handle);
-	}
-
-	Vector<String> Resources::getDependencies(const Path& filePath)
-	{
-		SPtr<SavedResourceData> savedResourceData;
-		if (!filePath.isEmpty())
-		{
-			FileDecoder fs(filePath);
-			savedResourceData = std::static_pointer_cast<SavedResourceData>(fs.decode());
-		}
-
-		return savedResourceData->getDependencies();
-	}
-
-	void Resources::registerResourceManifest(const ResourceManifestPtr& manifest)
-	{
-		if(manifest->getName() == "Default")
-			return;
-
-		auto findIter = std::find(mResourceManifests.begin(), mResourceManifests.end(), manifest);
-		if(findIter == mResourceManifests.end())
-			mResourceManifests.push_back(manifest);
-		else
-			*findIter = manifest;
-	}
-
-	void Resources::unregisterResourceManifest(const ResourceManifestPtr& manifest)
-	{
-		if (manifest->getName() == "Default")
-			return;
-
-		auto findIter = std::find(mResourceManifests.begin(), mResourceManifests.end(), manifest);
-		if (findIter != mResourceManifests.end())
-			mResourceManifests.erase(findIter);
-	}
-
-	ResourceManifestPtr Resources::getResourceManifest(const String& name) const
-	{
-		for(auto iter = mResourceManifests.rbegin(); iter != mResourceManifests.rend(); ++iter) 
-		{
-			if(name == (*iter)->getName())
-				return (*iter);
-		}
-
-		return nullptr;
-	}
-
-	bool Resources::isLoaded(const String& uuid, bool checkInProgress)
-	{
-		if (checkInProgress)
-		{
-			BS_LOCK_MUTEX(mInProgressResourcesMutex);
-			auto iterFind2 = mInProgressResources.find(uuid);
-			if (iterFind2 != mInProgressResources.end())
-			{
-				return true;
-			}
-
-			{
-				BS_LOCK_MUTEX(mLoadedResourceMutex);
-				auto iterFind = mLoadedResources.find(uuid);
-				if (iterFind != mLoadedResources.end())
-				{
-					return true;
-				}
-			}
-		}
-
-		return false;
-	}
-
-	HResource Resources::_createResourceHandle(const ResourcePtr& obj)
-	{
-		String uuid = UUIDGenerator::generateRandom();
-		HResource newHandle(obj, uuid);
-
-		{
-			BS_LOCK_MUTEX(mLoadedResourceMutex);
-
-			LoadedResourceData& resData = mLoadedResources[uuid];
-			resData.resource = newHandle.getWeak();
-			mHandles[uuid] = newHandle.getWeak();
-		}
-	
-		return newHandle;
-	}
-
-	HResource Resources::_getResourceHandle(const String& uuid)
-	{
-		BS_LOCK_MUTEX(mLoadedResourceMutex);
-		auto iterFind3 = mHandles.find(uuid);
-		if (iterFind3 != mHandles.end()) // Not loaded, but handle does exist
-		{
-			return iterFind3->second.lock();
-		}
-
-		// Create new handle
-		HResource handle(uuid);
-		mHandles[uuid] = handle.getWeak();
-
-		return handle;
-	}
-
-	bool Resources::getFilePathFromUUID(const String& uuid, Path& filePath) const
-	{
-		for(auto iter = mResourceManifests.rbegin(); iter != mResourceManifests.rend(); ++iter) 
-		{
-			if((*iter)->uuidToFilePath(uuid, filePath))
-				return true;
-		}
-
-		return false;
-	}
-
-	bool Resources::getUUIDFromFilePath(const Path& path, String& uuid) const
-	{
-		Path manifestPath = path;
-		if (!manifestPath.isAbsolute())
-			manifestPath.makeAbsolute(FileSystem::getWorkingDirectoryPath());
-
-		for(auto iter = mResourceManifests.rbegin(); iter != mResourceManifests.rend(); ++iter) 
-		{
-			if ((*iter)->filePathToUUID(manifestPath, uuid))
-				return true;
-		}
-
-		return false;
-	}
-
-	void Resources::loadComplete(HResource& resource)
-	{
-		String uuid = resource.getUUID();
-
-		ResourceLoadData* myLoadData = nullptr;
-		bool finishLoad = true;
-		Vector<ResourceLoadData*> dependantLoads;
-		{
-			BS_LOCK_MUTEX(mInProgressResourcesMutex);
-
-			auto iterFind = mInProgressResources.find(uuid);
-			if (iterFind != mInProgressResources.end())
-			{
-				myLoadData = iterFind->second;
-				finishLoad = myLoadData->remainingDependencies == 0;
-				
-				if (finishLoad)
-					mInProgressResources.erase(iterFind);
-			}
-
-			auto iterFind2 = mDependantLoads.find(uuid);
-
-			if (iterFind2 != mDependantLoads.end())
-				dependantLoads = iterFind2->second;
-
-			if (finishLoad)
-			{
-				mDependantLoads.erase(uuid);
-
-				// If loadedData is null then we're probably completing load on an already loaded resource, triggered
-				// by its dependencies.
-				if (myLoadData != nullptr && myLoadData->loadedData != nullptr)
-				{
-					BS_LOCK_MUTEX(mLoadedResourceMutex);
-
-					mLoadedResources[uuid] = myLoadData->resData;
-					resource.setHandleData(myLoadData->loadedData, uuid);
-				}
-
-				for (auto& dependantLoad : dependantLoads)
-					dependantLoad->remainingDependencies--;
-			}
-		}
-
-		for (auto& dependantLoad : dependantLoads)
-		{
-			HResource dependant = dependantLoad->resData.resource.lock();
-			loadComplete(dependant);
-		}
-
-		if (finishLoad && myLoadData != nullptr)
-		{
-			onResourceLoaded(resource);
-
-			// This should only ever be true on the main thread
-			if (myLoadData->notifyImmediately)
-				ResourceListenerManager::instance().notifyListeners(uuid);
-
-			bs_delete(myLoadData);
-		}
-	}
-
-	void Resources::loadCallback(const Path& filePath, HResource& resource)
-	{
-		ResourcePtr rawResource = loadFromDiskAndDeserialize(filePath);
-
-		{
-			BS_LOCK_MUTEX(mInProgressResourcesMutex);
-
-			// Check if all my dependencies are loaded
-			ResourceLoadData* myLoadData = mInProgressResources[resource.getUUID()];
-			myLoadData->loadedData = rawResource;
-			myLoadData->remainingDependencies--;
-		}
-
-		loadComplete(resource);
-	}
-
-	BS_CORE_EXPORT Resources& gResources()
-	{
-		return Resources::instance();
-	}
+#include "BsResources.h"
+#include "BsResource.h"
+#include "BsResourceManifest.h"
+#include "BsException.h"
+#include "BsFileSerializer.h"
+#include "BsFileSystem.h"
+#include "BsTaskScheduler.h"
+#include "BsUUID.h"
+#include "BsDebug.h"
+#include "BsUtility.h"
+#include "BsSavedResourceData.h"
+#include "BsResourceListenerManager.h"
+
+namespace BansheeEngine
+{
+	Resources::Resources()
+	{
+		mDefaultResourceManifest = ResourceManifest::create("Default");
+		mResourceManifests.push_back(mDefaultResourceManifest);
+	}
+
+	Resources::~Resources()
+	{
+		// Unload and invalidate all resources
+		UnorderedMap<String, LoadedResourceData> loadedResourcesCopy = mLoadedResources;
+
+		for (auto& loadedResourcePair : loadedResourcesCopy)
+			destroy(loadedResourcePair.second.resource);
+	}
+
+	HResource Resources::load(const Path& filePath, bool loadDependencies, bool keepInternalReference)
+	{
+		String uuid;
+		bool foundUUID = getUUIDFromFilePath(filePath, uuid);
+
+		if (!foundUUID)
+			uuid = UUIDGenerator::generateRandom();
+
+		return loadInternal(uuid, filePath, true, loadDependencies, keepInternalReference);
+	}
+
+	HResource Resources::load(const WeakResourceHandle<Resource>& handle, bool loadDependencies, bool keepInternalReference)
+	{
+		if (handle.mData == nullptr)
+			return HResource();
+
+		String uuid = handle.getUUID();
+		return loadFromUUID(uuid, false, loadDependencies, keepInternalReference);
+	}
+
+	HResource Resources::loadAsync(const Path& filePath, bool loadDependencies, bool keepInternalReference)
+	{
+		String uuid;
+		bool foundUUID = getUUIDFromFilePath(filePath, uuid);
+
+		if (!foundUUID)
+			uuid = UUIDGenerator::generateRandom();
+
+		return loadInternal(uuid, filePath, false, loadDependencies, keepInternalReference);
+	}
+
+	HResource Resources::loadFromUUID(const String& uuid, bool async, bool loadDependencies, bool keepInternalReference)
+	{
+		Path filePath;
+
+		// Default manifest is at 0th index but all other take priority since Default manifest could
+		// contain obsolete data. 
+		for (auto iter = mResourceManifests.rbegin(); iter != mResourceManifests.rend(); ++iter)
+		{
+			if ((*iter)->uuidToFilePath(uuid, filePath))
+				break;
+		}
+
+		return loadInternal(uuid, filePath, !async, loadDependencies, keepInternalReference);
+	}
+
+	HResource Resources::loadInternal(const String& UUID, const Path& filePath, bool synchronous, bool loadDependencies, bool keepInternalReference)
+	{
+		HResource outputResource;
+
+		bool alreadyLoading = false;
+		bool loadInProgress = false;
+		{
+			// Check if resource is already being loaded on a worker thread
+			BS_LOCK_MUTEX(mInProgressResourcesMutex);
+			auto iterFind2 = mInProgressResources.find(UUID);
+			if (iterFind2 != mInProgressResources.end())
+			{
+				LoadedResourceData& resData = iterFind2->second->resData;
+				outputResource = resData.resource.lock();
+
+				if (keepInternalReference)
+				{
+					resData.numInternalRefs++;
+					outputResource.addInternalRef();
+				}
+
+				alreadyLoading = true;
+				loadInProgress = true;
+			}
+
+			// Previously being loaded as async but now we want it synced, so we wait
+			if (loadInProgress && synchronous)
+				outputResource.blockUntilLoaded();
+
+			if (!alreadyLoading)
+			{
+				BS_LOCK_MUTEX(mLoadedResourceMutex);
+				auto iterFind = mLoadedResources.find(UUID);
+				if (iterFind != mLoadedResources.end()) // Resource is already loaded
+				{
+					LoadedResourceData& resData = iterFind->second;
+					outputResource = resData.resource.lock();
+
+					if (keepInternalReference)
+					{
+						resData.numInternalRefs++;
+						outputResource.addInternalRef();
+					}
+
+					alreadyLoading = true;
+				}
+			}
+		}
+
+
+		// Not loaded and not in progress, start loading of new resource
+		// (or if already loaded or in progress, load any dependencies)
+		if (!alreadyLoading)
+		{
+			// Check if the handle already exists
+			BS_LOCK_MUTEX(mLoadedResourceMutex);
+			auto iterFind = mHandles.find(UUID);
+			if (iterFind != mHandles.end())
+				outputResource = iterFind->second.lock();
+			else
+			{
+				outputResource = HResource(UUID);
+				mHandles[UUID] = outputResource.getWeak();
+			}			
+		}
+
+		// We have nowhere to load from, warn and complete load if a file path was provided,
+		// otherwise pass through as we might just want to load from memory. 
+		if (filePath.isEmpty())
+		{
+			if (!alreadyLoading)
+			{
+				LOGWRN_VERBOSE("Cannot load resource. Resource with UUID '" + UUID + "' doesn't exist.");
+
+				// Complete the load as that the depedency counter is properly reduced, in case this 
+				// is a dependency of some other resource.
+				loadComplete(outputResource);
+				return outputResource;
+			}
+		}
+		else if (!FileSystem::isFile(filePath))
+		{
+			LOGWRN_VERBOSE("Cannot load resource. Specified file: " + filePath.toString() + " doesn't exist.");
+
+			// Complete the load as that the depedency counter is properly reduced, in case this 
+			// is a dependency of some other resource.
+			loadComplete(outputResource);
+			assert(!loadInProgress); // Resource already being loaded but we can't find its path now?
+
+			return outputResource;
+		}
+
+		// Load dependency data if a file path is provided
+		SPtr<SavedResourceData> savedResourceData;
+		if (!filePath.isEmpty())
+		{
+			FileDecoder fs(filePath);
+			savedResourceData = std::static_pointer_cast<SavedResourceData>(fs.decode());
+		}
+
+		// If already loading keep the old load operation active, otherwise create a new one
+		if (!alreadyLoading)
+		{
+			{
+				BS_LOCK_MUTEX(mInProgressResourcesMutex);
+
+				ResourceLoadData* loadData = bs_new<ResourceLoadData>(outputResource.getWeak(), 0);
+				mInProgressResources[UUID] = loadData;
+				loadData->resData = outputResource.getWeak();
+
+				if (keepInternalReference)
+				{
+					loadData->resData.numInternalRefs++;
+					outputResource.addInternalRef();
+				}
+
+				loadData->remainingDependencies = 1;
+				loadData->notifyImmediately = synchronous; // Make resource listener trigger before exit if loading synchronously
+
+				// Register dependencies and count them so we know when the resource is fully loaded
+				if (loadDependencies && savedResourceData != nullptr)
+				{
+					for (auto& dependency : savedResourceData->getDependencies())
+					{
+						if (dependency != UUID)
+						{
+							mDependantLoads[dependency].push_back(loadData);
+							loadData->remainingDependencies++;
+						}
+					}
+				}
+			}
+
+			if (loadDependencies && savedResourceData != nullptr)
+			{
+				const Vector<String>& dependencyUUIDs = savedResourceData->getDependencies();
+				UINT32 numDependencies = (UINT32)dependencyUUIDs.size();
+				Vector<HResource> dependencies(numDependencies);
+
+				for (UINT32 i = 0; i < numDependencies; i++)
+					dependencies[i] = loadFromUUID(dependencyUUIDs[i], !synchronous, true, false);
+
+				// Keep dependencies alive until the parent is done loading
+				{
+					BS_LOCK_MUTEX(mInProgressResourcesMutex);
+
+					// At this point the resource is guaranteed to still be in-progress, so it's safe to update its dependency list
+					mInProgressResources[UUID]->dependencies = dependencies;
+				}
+			}
+		}
+		else if (loadDependencies && savedResourceData != nullptr) // Queue dependencies in case they aren't already loaded
+		{
+			const Vector<String>& dependencies = savedResourceData->getDependencies();
+			if (!dependencies.empty())
+			{
+				{
+					BS_LOCK_MUTEX(mInProgressResourcesMutex);
+
+					ResourceLoadData* loadData = nullptr;
+
+					auto iterFind = mInProgressResources.find(UUID);
+					if (iterFind == mInProgressResources.end()) // Fully loaded
+					{
+						loadData = bs_new<ResourceLoadData>(outputResource.getWeak(), 0);
+						loadData->resData = outputResource.getWeak();
+						loadData->remainingDependencies = 0;
+						loadData->notifyImmediately = synchronous; // Make resource listener trigger before exit if loading synchronously
+
+						mInProgressResources[UUID] = loadData;
+					}
+					else
+					{
+						loadData = iterFind->second;
+					}
+
+					// Register dependencies and count them so we know when the resource is fully loaded
+					for (auto& dependency : dependencies)
+					{
+						if (dependency != UUID)
+						{
+							bool registerDependency = true;
+
+							auto iterFind2 = mDependantLoads.find(dependency);
+							if (iterFind2 != mDependantLoads.end())
+							{
+								Vector<ResourceLoadData*>& dependantData = iterFind2->second;
+								auto iterFind3 = std::find_if(dependantData.begin(), dependantData.end(),
+									[&](ResourceLoadData* x)
+								{
+									return x->resData.resource.getUUID() == outputResource.getUUID();
+								});
+
+								registerDependency = iterFind3 == dependantData.end();
+							}
+
+							if (registerDependency)
+							{
+								mDependantLoads[dependency].push_back(loadData);
+								loadData->remainingDependencies++;
+								loadData->dependencies.push_back(_getResourceHandle(dependency));
+							}
+						}
+					}
+				}
+
+				for (auto& dependency : dependencies)
+					loadFromUUID(dependency, !synchronous, true, false);
+			}
+		}
+
+		// Actually start the file read operation if not already loaded or in progress
+		if (!alreadyLoading && !filePath.isEmpty())
+		{
+			// Synchronous or the resource doesn't support async, read the file immediately
+			if (synchronous || !savedResourceData->allowAsyncLoading())
+			{
+				loadCallback(filePath, outputResource);
+			}
+			else // Asynchronous, read the file on a worker thread
+			{
+				String fileName = filePath.getFilename();
+				String taskName = "Resource load: " + fileName;
+
+				TaskPtr task = Task::create(taskName, std::bind(&Resources::loadCallback, this, filePath, outputResource));
+				TaskScheduler::instance().addTask(task);
+			}
+		}
+		else // File already loaded or in progress
+		{
+			// Complete the load unless its in progress in which case we wait for its worker thread to complete it.
+			// In case file is already loaded this will only decrement dependency count in case this resource is a dependency.
+			if (!loadInProgress)
+				loadComplete(outputResource);
+			else
+			{
+				// In case loading finished in the meantime we cannot be sure at what point ::loadComplete was triggered,
+				// so trigger it manually so that the dependency count is properly decremented in case this resource
+				// is a dependency.
+				BS_LOCK_MUTEX(mLoadedResourceMutex);
+				auto iterFind = mLoadedResources.find(UUID);
+				if (iterFind != mLoadedResources.end())
+					loadComplete(outputResource);
+			}
+		}
+
+		return outputResource;
+	}
+
+	ResourcePtr Resources::loadFromDiskAndDeserialize(const Path& filePath)
+	{
+		FileDecoder fs(filePath);
+		fs.skip(); // Skipped over saved resource data
+		std::shared_ptr<IReflectable> loadedData = fs.decode();
+
+		if(loadedData == nullptr)
+			BS_EXCEPT(InternalErrorException, "Unable to load resource.");
+
+		if(!loadedData->isDerivedFrom(Resource::getRTTIStatic()))
+			BS_EXCEPT(InternalErrorException, "Loaded class doesn't derive from Resource.");
+
+		ResourcePtr resource = std::static_pointer_cast<Resource>(loadedData);
+		return resource;
+	}
+
+	void Resources::release(ResourceHandleBase& resource)
+	{
+		const String& UUID = resource.getUUID();
+
+		{
+			bool loadInProgress = false;
+
+			BS_LOCK_MUTEX(mInProgressResourcesMutex);
+			auto iterFind2 = mInProgressResources.find(UUID);
+			if (iterFind2 != mInProgressResources.end())
+				loadInProgress = true;
+
+			// Technically we should be able to just cancel a load in progress instead of blocking until it finishes.
+			// However that would mean the last reference could get lost on whatever thread did the loading, which
+			// isn't something that's supported. If this ends up being a problem either make handle counting atomic
+			// or add a separate queue for objects destroyed from the load threads.
+			if (loadInProgress)
+				resource.blockUntilLoaded();
+
+			{
+				BS_LOCK_MUTEX(mLoadedResourceMutex);
+				auto iterFind = mLoadedResources.find(UUID);
+				if (iterFind != mLoadedResources.end()) // Resource is already loaded
+				{
+					LoadedResourceData& resData = iterFind->second;
+
+					assert(resData.numInternalRefs > 0);
+					resData.numInternalRefs--;
+					resource.removeInternalRef();
+
+					return;
+				}
+			}
+		}
+	}
+
+	void Resources::unloadAllUnused()
+	{
+		Vector<HResource> resourcesToUnload;
+
+		{
+			BS_LOCK_MUTEX(mLoadedResourceMutex);
+			for(auto iter = mLoadedResources.begin(); iter != mLoadedResources.end(); ++iter)
+			{
+				const LoadedResourceData& resData = iter->second;
+
+				if (resData.resource.mData->mRefCount == resData.numInternalRefs) // Only internal references exist, free it
+					resourcesToUnload.push_back(resData.resource.lock());
+			}
+		}
+
+		// Note: When unloading multiple resources it's possible that unloading one will also unload
+		// another resource in "resourcesToUnload". This is fine because "unload" deals with invalid
+		// handles gracefully.
+		for(auto iter = resourcesToUnload.begin(); iter != resourcesToUnload.end(); ++iter)
+		{
+			release(*iter);
+		}
+	}
+
+	void Resources::destroy(ResourceHandleBase& resource)
+	{
+		if (resource.mData == nullptr)
+			return;
+
+		const String& uuid = resource.getUUID();
+		if (!resource.isLoaded(false))
+		{
+			bool loadInProgress = false;
+			{
+				BS_LOCK_MUTEX(mInProgressResourcesMutex);
+				auto iterFind2 = mInProgressResources.find(uuid);
+				if (iterFind2 != mInProgressResources.end())
+					loadInProgress = true;
+			}
+
+			if (loadInProgress) // If it's still loading wait until that finishes
+				resource.blockUntilLoaded();
+			else
+				return; // Already unloaded
+		}
+
+		// Notify external systems before we actually destroy it
+		onResourceDestroyed(uuid);
+		resource.mData->mPtr->destroy();
+
+		{
+			BS_LOCK_MUTEX(mLoadedResourceMutex);
+
+			auto iterFind = mLoadedResources.find(uuid);
+			if (iterFind != mLoadedResources.end())
+			{
+				LoadedResourceData& resData = iterFind->second;
+				while (resData.numInternalRefs > 0)
+				{
+					resData.numInternalRefs--;
+					resData.resource.removeInternalRef();
+				}
+
+				mLoadedResources.erase(iterFind);
+			}
+			else
+			{
+				assert(false); // This should never happen but in case it does fail silently in release mode
+			}
+		}
+
+		resource.setHandleData(nullptr, uuid);
+	}
+
+	void Resources::save(const HResource& resource, const Path& filePath, bool overwrite)
+	{
+		if (resource == nullptr)
+			return;
+
+		if (!resource.isLoaded(false))
+		{
+			bool loadInProgress = false;
+			{
+				BS_LOCK_MUTEX(mInProgressResourcesMutex);
+				auto iterFind2 = mInProgressResources.find(resource.getUUID());
+				if (iterFind2 != mInProgressResources.end())
+					loadInProgress = true;
+			}
+
+			if (loadInProgress) // If it's still loading wait until that finishes
+				resource.blockUntilLoaded();
+			else
+				return; // Nothing to save
+		}
+
+		bool fileExists = FileSystem::isFile(filePath);
+		if(fileExists)
+		{
+			if(overwrite)
+				FileSystem::remove(filePath);
+			else
+				BS_EXCEPT(InvalidParametersException, "Another file exists at the specified location.");
+		}
+
+		mDefaultResourceManifest->registerResource(resource.getUUID(), filePath);
+
+		Vector<ResourceDependency> dependencyList = Utility::findResourceDependencies(*resource.get());
+
+		Vector<String> dependencyUUIDs(dependencyList.size());
+		for (UINT32 i = 0; i < (UINT32)dependencyList.size(); i++)
+			dependencyUUIDs[i] = dependencyList[i].resource.getUUID();
+
+		SPtr<SavedResourceData> resourceData = bs_shared_ptr_new<SavedResourceData>(dependencyUUIDs, resource->allowAsyncLoading());
+
+		FileEncoder fs(filePath);
+		fs.encode(resourceData.get());
+		fs.encode(resource.get());
+	}
+
+	void Resources::save(const HResource& resource)
+	{
+		if (resource == nullptr)
+			return;
+
+		Path path;
+		if (getFilePathFromUUID(resource.getUUID(), path))
+			save(resource, path, true);
+	}
+
+	void Resources::update(HResource& handle, const ResourcePtr& resource)
+	{
+		const String& uuid = handle.getUUID();
+		handle.setHandleData(resource, uuid);
+
+		{
+			BS_LOCK_MUTEX(mLoadedResourceMutex);
+			auto iterFind = mLoadedResources.find(uuid);
+			if (iterFind == mLoadedResources.end())
+			{
+				LoadedResourceData& resData = mLoadedResources[uuid];
+				resData.resource = handle.getWeak();
+			}
+		}
+
+		onResourceModified(handle);
+	}
+
+	Vector<String> Resources::getDependencies(const Path& filePath)
+	{
+		SPtr<SavedResourceData> savedResourceData;
+		if (!filePath.isEmpty())
+		{
+			FileDecoder fs(filePath);
+			savedResourceData = std::static_pointer_cast<SavedResourceData>(fs.decode());
+		}
+
+		return savedResourceData->getDependencies();
+	}
+
+	void Resources::registerResourceManifest(const ResourceManifestPtr& manifest)
+	{
+		if(manifest->getName() == "Default")
+			return;
+
+		auto findIter = std::find(mResourceManifests.begin(), mResourceManifests.end(), manifest);
+		if(findIter == mResourceManifests.end())
+			mResourceManifests.push_back(manifest);
+		else
+			*findIter = manifest;
+	}
+
+	void Resources::unregisterResourceManifest(const ResourceManifestPtr& manifest)
+	{
+		if (manifest->getName() == "Default")
+			return;
+
+		auto findIter = std::find(mResourceManifests.begin(), mResourceManifests.end(), manifest);
+		if (findIter != mResourceManifests.end())
+			mResourceManifests.erase(findIter);
+	}
+
+	ResourceManifestPtr Resources::getResourceManifest(const String& name) const
+	{
+		for(auto iter = mResourceManifests.rbegin(); iter != mResourceManifests.rend(); ++iter) 
+		{
+			if(name == (*iter)->getName())
+				return (*iter);
+		}
+
+		return nullptr;
+	}
+
+	bool Resources::isLoaded(const String& uuid, bool checkInProgress)
+	{
+		if (checkInProgress)
+		{
+			BS_LOCK_MUTEX(mInProgressResourcesMutex);
+			auto iterFind2 = mInProgressResources.find(uuid);
+			if (iterFind2 != mInProgressResources.end())
+			{
+				return true;
+			}
+
+			{
+				BS_LOCK_MUTEX(mLoadedResourceMutex);
+				auto iterFind = mLoadedResources.find(uuid);
+				if (iterFind != mLoadedResources.end())
+				{
+					return true;
+				}
+			}
+		}
+
+		return false;
+	}
+
+	HResource Resources::_createResourceHandle(const ResourcePtr& obj)
+	{
+		String uuid = UUIDGenerator::generateRandom();
+		HResource newHandle(obj, uuid);
+
+		{
+			BS_LOCK_MUTEX(mLoadedResourceMutex);
+
+			LoadedResourceData& resData = mLoadedResources[uuid];
+			resData.resource = newHandle.getWeak();
+			mHandles[uuid] = newHandle.getWeak();
+		}
+	
+		return newHandle;
+	}
+
+	HResource Resources::_getResourceHandle(const String& uuid)
+	{
+		BS_LOCK_MUTEX(mLoadedResourceMutex);
+		auto iterFind3 = mHandles.find(uuid);
+		if (iterFind3 != mHandles.end()) // Not loaded, but handle does exist
+		{
+			return iterFind3->second.lock();
+		}
+
+		// Create new handle
+		HResource handle(uuid);
+		mHandles[uuid] = handle.getWeak();
+
+		return handle;
+	}
+
+	bool Resources::getFilePathFromUUID(const String& uuid, Path& filePath) const
+	{
+		for(auto iter = mResourceManifests.rbegin(); iter != mResourceManifests.rend(); ++iter) 
+		{
+			if((*iter)->uuidToFilePath(uuid, filePath))
+				return true;
+		}
+
+		return false;
+	}
+
+	bool Resources::getUUIDFromFilePath(const Path& path, String& uuid) const
+	{
+		Path manifestPath = path;
+		if (!manifestPath.isAbsolute())
+			manifestPath.makeAbsolute(FileSystem::getWorkingDirectoryPath());
+
+		for(auto iter = mResourceManifests.rbegin(); iter != mResourceManifests.rend(); ++iter) 
+		{
+			if ((*iter)->filePathToUUID(manifestPath, uuid))
+				return true;
+		}
+
+		return false;
+	}
+
+	void Resources::loadComplete(HResource& resource)
+	{
+		String uuid = resource.getUUID();
+
+		ResourceLoadData* myLoadData = nullptr;
+		bool finishLoad = true;
+		Vector<ResourceLoadData*> dependantLoads;
+		{
+			BS_LOCK_MUTEX(mInProgressResourcesMutex);
+
+			auto iterFind = mInProgressResources.find(uuid);
+			if (iterFind != mInProgressResources.end())
+			{
+				myLoadData = iterFind->second;
+				finishLoad = myLoadData->remainingDependencies == 0;
+				
+				if (finishLoad)
+					mInProgressResources.erase(iterFind);
+			}
+
+			auto iterFind2 = mDependantLoads.find(uuid);
+
+			if (iterFind2 != mDependantLoads.end())
+				dependantLoads = iterFind2->second;
+
+			if (finishLoad)
+			{
+				mDependantLoads.erase(uuid);
+
+				// If loadedData is null then we're probably completing load on an already loaded resource, triggered
+				// by its dependencies.
+				if (myLoadData != nullptr && myLoadData->loadedData != nullptr)
+				{
+					BS_LOCK_MUTEX(mLoadedResourceMutex);
+
+					mLoadedResources[uuid] = myLoadData->resData;
+					resource.setHandleData(myLoadData->loadedData, uuid);
+				}
+
+				for (auto& dependantLoad : dependantLoads)
+					dependantLoad->remainingDependencies--;
+			}
+		}
+
+		for (auto& dependantLoad : dependantLoads)
+		{
+			HResource dependant = dependantLoad->resData.resource.lock();
+			loadComplete(dependant);
+		}
+
+		if (finishLoad && myLoadData != nullptr)
+		{
+			onResourceLoaded(resource);
+
+			// This should only ever be true on the main thread
+			if (myLoadData->notifyImmediately)
+				ResourceListenerManager::instance().notifyListeners(uuid);
+
+			bs_delete(myLoadData);
+		}
+	}
+
+	void Resources::loadCallback(const Path& filePath, HResource& resource)
+	{
+		ResourcePtr rawResource = loadFromDiskAndDeserialize(filePath);
+
+		{
+			BS_LOCK_MUTEX(mInProgressResourcesMutex);
+
+			// Check if all my dependencies are loaded
+			ResourceLoadData* myLoadData = mInProgressResources[resource.getUUID()];
+			myLoadData->loadedData = rawResource;
+			myLoadData->remainingDependencies--;
+		}
+
+		loadComplete(resource);
+	}
+
+	BS_CORE_EXPORT Resources& gResources()
+	{
+		return Resources::instance();
+	}
 }
 }

+ 1 - 14
BansheeEditor/Source/BsProjectLibrary.cpp

@@ -392,7 +392,6 @@ namespace BansheeEngine
 			else
 			else
 				curImportOptions = importOptions;
 				curImportOptions = importOptions;
 
 
-			bool unloadWhenDone = false;
 			HResource importedResource;
 			HResource importedResource;
 			if (isNativeResource)
 			if (isNativeResource)
 			{
 			{
@@ -401,21 +400,17 @@ namespace BansheeEngine
 				if (resource->meta != nullptr)
 				if (resource->meta != nullptr)
 				{
 				{
 					mResourceManifest->registerResource(resource->meta->getUUID(), resource->path);
 					mResourceManifest->registerResource(resource->meta->getUUID(), resource->path);
-					unloadWhenDone = !gResources().isLoaded(resource->meta->getUUID());
 				}
 				}
 
 
 				// Don't load dependencies because we don't need them, but also because they might not be in the manifest
 				// Don't load dependencies because we don't need them, but also because they might not be in the manifest
 				// which would screw up their UUIDs.
 				// which would screw up their UUIDs.
-				importedResource = gResources().load(resource->path, false);
+				importedResource = gResources().load(resource->path, false, false);
 			}
 			}
 
 
 			if(resource->meta == nullptr)
 			if(resource->meta == nullptr)
 			{
 			{
 				if (!isNativeResource)
 				if (!isNativeResource)
-				{
 					importedResource = Importer::instance().import(resource->path, curImportOptions);
 					importedResource = Importer::instance().import(resource->path, curImportOptions);
-					unloadWhenDone = true;
-				}
 
 
 				if (importedResource != nullptr)
 				if (importedResource != nullptr)
 				{
 				{
@@ -438,10 +433,6 @@ namespace BansheeEngine
 				if (!isNativeResource)
 				if (!isNativeResource)
 				{
 				{
 					importedResource = gResources()._getResourceHandle(resource->meta->getUUID());
 					importedResource = gResources()._getResourceHandle(resource->meta->getUUID());
-
-					if (!importedResource.isLoaded(false))
-						unloadWhenDone = true;
-
 					Importer::instance().reimport(importedResource, resource->path, curImportOptions);
 					Importer::instance().reimport(importedResource, resource->path, curImportOptions);
 				}
 				}
 
 
@@ -464,10 +455,6 @@ namespace BansheeEngine
 				gResources().save(importedResource, internalResourcesPath, true);
 				gResources().save(importedResource, internalResourcesPath, true);
 
 
 				String uuid = importedResource.getUUID();
 				String uuid = importedResource.getUUID();
-
-				if (unloadWhenDone)
-					gResources().release(importedResource);
-
 				mResourceManifest->registerResource(uuid, internalResourcesPath);
 				mResourceManifest->registerResource(uuid, internalResourcesPath);
 			}
 			}
 
 

+ 7 - 1
BansheeUtility/Include/BsMemStack.h

@@ -171,7 +171,7 @@ namespace BansheeEngine
 			while (curBlock != nullptr)
 			while (curBlock != nullptr)
 			{
 			{
 				MemBlock* nextBlock = curBlock->mNextBlock;
 				MemBlock* nextBlock = curBlock->mNextBlock;
-				if (nextBlock->mSize >= blockSize)
+				if (nextBlock != nullptr && nextBlock->mSize >= blockSize)
 				{
 				{
 					newBlock = nextBlock;
 					newBlock = nextBlock;
 					break;
 					break;
@@ -190,7 +190,13 @@ namespace BansheeEngine
 				newBlock->mPrevBlock = mFreeBlock;
 				newBlock->mPrevBlock = mFreeBlock;
 
 
 				if (mFreeBlock != nullptr)
 				if (mFreeBlock != nullptr)
+				{
+					if(mFreeBlock->mNextBlock != nullptr)
+						mFreeBlock->mNextBlock->mPrevBlock = newBlock;
+
+					newBlock->mNextBlock = mFreeBlock->mNextBlock;
 					mFreeBlock->mNextBlock = newBlock;
 					mFreeBlock->mNextBlock = newBlock;
+				}
 			}
 			}
 
 
 			mFreeBlock = newBlock;
 			mFreeBlock = newBlock;