瀏覽代碼

Refactored resource loading so the loaded resource can decide whether to keep original loaded data or discard it

BearishSun 9 年之前
父節點
當前提交
21b68b1870

+ 10 - 1
Source/BansheeCore/Include/BsAudioClip.h

@@ -56,6 +56,16 @@ namespace BansheeEngine
 
 		/** Determines should the audio clip be played using 3D positioning. Only valid for mono audio. */
 		bool is3D = true;
+
+		/** 
+		 * Determines should the audio clip keep the original data in memory after creation. For example if the audio data 
+		 * is normally compressed, but audio clip uncompresses it on load, the original compressed data will be lost unless
+		 * this is enabled. This will cause extra memory to be used, but can be useful in certain circumstances (for example
+		 * you might require that data to save the audio clip on disk).
+		 *
+		 * When loading audio clip directly from disk, this properly is controlled by the ResourceLoadFlag::KeepSourceData.
+		 */
+		bool keepSourceData = true;
 	};
 
 	/** 
@@ -128,7 +138,6 @@ namespace BansheeEngine
 		UINT32 mStreamSize;
 		UINT32 mStreamOffset;
 		float mLength;
-		bool mKeepSourceData;
 		SPtr<DataStream> mStreamData;
 
 		/************************************************************************/

+ 8 - 1
Source/BansheeCore/Include/BsResource.h

@@ -44,13 +44,20 @@ namespace BansheeEngine
 		UINT32 mSize;
 		SPtr<ResourceMetaData> mMetaData;
 
+		/** 
+		 * Signal to the resource implementation if original data should be kept in memory. This is sometimes needed if
+		 * the resource destroys original data during normal usage, but it might still be required for special purposes
+		 * (like saving in the editor).
+		 */
+		bool mKeepSourceData; 
+		
 	/************************************************************************/
 	/* 								SERIALIZATION                      		*/
 	/************************************************************************/
 	public:
 		friend class ResourceRTTI;
 		static RTTITypeBase* getRTTIStatic();
-		virtual RTTITypeBase* getRTTI() const override;
+		RTTITypeBase* getRTTI() const override;
 	};
 
 	/** @} */

+ 8 - 0
Source/BansheeCore/Include/BsResourceRTTI.h

@@ -30,6 +30,14 @@ namespace BansheeEngine
 			addReflectablePtrField("mMetaData", 1, &ResourceRTTI::getMetaData, &ResourceRTTI::setMetaData);
 		}
 
+		void onDeserializationStarted(IReflectable* obj, const UnorderedMap<bool, UINT64>& params)
+		{
+			Resource* resource = static_cast<Resource*>(obj);
+
+			auto iterFind = params.find("keepSourceData");
+			resource->mKeepSourceData = iterFind != params.end() && iterFind->second > 0;
+		}
+
 		const String& getRTTIName() override
 		{
 			static String name = "Resource";

+ 55 - 49
Source/BansheeCore/Include/BsResources.h

@@ -11,6 +11,34 @@ namespace BansheeEngine
 	 *  @{
 	 */
 
+	/** Flags that can be used to control resource loading. */
+	enum class ResourceLoadFlag
+	{
+		/** No flags. */
+		None = 0,
+		/** If enabled all resources referenced by the root resource will be loaded as well. */
+		LoadDependencies = 1 << 0, 
+		/**
+		 * If enabled 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 Resources::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.
+		 */
+		KeepInternalRef = 1 << 1,
+		/**
+		 * Determines if the loaded resource keeps original data loaded. Sometime resources will process loaded data
+		 * and discard the original (e.g. uncompressing audio on load). This flag can prevent the resource from discarding
+		 * the original data. The original data might be required for saving the resource (via Resources::save), but will
+		 * use up extra memory. Normally you want to keep this enabled if you plan on saving the resource to disk.
+		 */
+		KeepSourceData = 1 << 2,
+		/** Default set of flags used for resource loading. */
+		Default = LoadDependencies | KeepInternalRef
+	};
+
+	typedef Flags<ResourceLoadFlag> ResourceLoadFlags;
+	BS_FLAGS_OPERATORS(ResourceLoadFlag);
+
 	/**
 	 * Manager for dealing with all engine resources. It allows you to save new resources and load existing ones.
 	 *
@@ -18,6 +46,7 @@ namespace BansheeEngine
 	 */
 	class BS_CORE_EXPORT Resources : public Module<Resources>
 	{
+		/** Information about a loaded resource. */
 		struct LoadedResourceData
 		{
 			LoadedResourceData()
@@ -32,6 +61,7 @@ namespace BansheeEngine
 			UINT32 numInternalRefs;
 		};
 
+		/** Information about a resource that's currently being loaded. */
 		struct ResourceLoadData
 		{
 			ResourceLoadData(const WeakResourceHandle<Resource>& resource, UINT32 numDependencies)
@@ -56,40 +86,33 @@ namespace BansheeEngine
 		 * All loaded resources are reference counted and will be automatically unloaded when all of their references go out
 		 * of scope. 
 		 *			
-		 * @param[in]	filePath				File path to the resource to load. This can be absolute or relative to 
-		 *										the working folder.
-		 * @param[in]	loadDependencies		If true all resources referenced by the root resource will be loaded as well.
-		 * @param[in]	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.
+		 * @param[in]	filePath	File path to the resource to load. This can be absolute or relative to the working 
+		 *							folder.
+		 * @param[in]	flags		Flags used to control the load process.
 		 *			
 		 * @see		release(ResourceHandleBase&), unloadAllUnused()
 		 */
-		HResource load(const Path& filePath, bool loadDependencies = true, bool keepInternalReference = true);
+		HResource load(const Path& filePath, ResourceLoadFlags loadFlags = ResourceLoadFlag::Default);
 
-		/** @copydoc load(const Path&, bool, bool) */
+		/** @copydoc load(const Path&, ResourceLoadFlags) */
 		template <class T>
-		ResourceHandle<T> load(const Path& filePath, bool loadDependencies = true, bool keepInternalReference = true)
+		ResourceHandle<T> load(const Path& filePath, ResourceLoadFlags loadFlags = ResourceLoadFlag::Default)
 		{
-			return static_resource_cast<T>(load(filePath, loadDependencies, keepInternalReference));
+			return static_resource_cast<T>(load(filePath, loadFlags));
 		}
 
 		/**
 		 * Loads the resource for the provided weak resource handle, or returns a loaded resource if already loaded.
 		 * 			
-		 * @see		load(const Path&, bool, bool)
+		 * @see		load(const Path&, ResourceLoadFlags)
 		 */
-		HResource load(const WeakResourceHandle<Resource>& handle, bool loadDependencies = true, bool keepInternalReference = true);
+		HResource load(const WeakResourceHandle<Resource>& handle, ResourceLoadFlags loadFlags = ResourceLoadFlag::Default);
 
-		/** @copydoc load(const WeakResourceHandle<Resource>&, bool, bool) */
+		/** @copydoc load(const WeakResourceHandle<Resource>&, ResourceLoadFlags) */
 		template <class T>
-		ResourceHandle<T> load(const WeakResourceHandle<T>& handle, bool loadDependencies = true, bool keepInternalReference = true)
+		ResourceHandle<T> load(const WeakResourceHandle<T>& handle, ResourceLoadFlags loadFlags = ResourceLoadFlag::Default)
 		{
-			return static_resource_cast<T>(load((const WeakResourceHandle<Resource>&)handle, loadDependencies, keepInternalReference));
+			return static_resource_cast<T>(load((const WeakResourceHandle<Resource>&)handle, loadFlags));
 		}
 
 		/**
@@ -97,49 +120,34 @@ namespace BansheeEngine
 		 * done.
 		 *
 		 * @param[in]	filePath	Full pathname of the file.
-		 * @param[in]	loadDependencies		If true all resources referenced by the root resource will be loaded as well.
-		 * @param[in]	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.
+		 * @param[in]	flags		Flags used to control the load process.
 		 *
 		 * @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)
+		 * @see		load(const Path&, ResourceLoadFlags)
 		 */
-		HResource loadAsync(const Path& filePath, bool loadDependencies = true, bool keepInternalReference = true);
+		HResource loadAsync(const Path& filePath, ResourceLoadFlags loadFlags = ResourceLoadFlag::Default);
 
 		/** @copydoc loadAsync */
 		template <class T>
-		ResourceHandle<T> loadAsync(const Path& filePath, bool loadDependencies = true, bool keepInternalReference = true)
+		ResourceHandle<T> loadAsync(const Path& filePath, ResourceLoadFlags loadFlags = ResourceLoadFlag::Default)
 		{
-			return static_resource_cast<T>(loadAsync(filePath, loadDependencies, keepInternalReference));
+			return static_resource_cast<T>(loadAsync(filePath, loadFlags));
 		}
 
 		/**
 		 * Loads the resource with the given UUID. Returns an empty handle if resource can't be loaded.
 		 *
-		 * @param[in]	uuid					UUID of the resource to load.
-		 * @param[in]	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[in]	loadDependencies		If true all resources referenced by the root resource will be loaded as well.
-		 * @param[in]	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.	
+		 * @param[in]	uuid	UUID of the resource to load.
+		 * @param[in]	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[in]	flags	Flags used to control the load process.
 		 *													
 		 * @see		load(const Path&, bool)
 		 */
-		HResource loadFromUUID(const String& uuid, bool async = false, bool loadDependencies = true, bool keepInternalReference = true);
+		HResource loadFromUUID(const String& uuid, bool async = false, ResourceLoadFlags loadFlags = ResourceLoadFlag::Default);
 
 		/**
 		 * Releases an internal reference to the resource held by the resources system. This allows the resource to be 
@@ -292,19 +300,17 @@ namespace BansheeEngine
 		 * 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[in]	incrementRef	Determines should the internal reference count be incremented.
 		 */
-		HResource loadInternal(const String& UUID, const Path& filePath, bool synchronous, bool loadDependencies, bool incrementRef);
+		HResource loadInternal(const String& UUID, const Path& filePath, bool synchronous, ResourceLoadFlags loadFlags);
 
 		/** Performs actually reading and deserializing of the resource file. Called from various worker threads. */
-		SPtr<Resource> loadFromDiskAndDeserialize(const Path& filePath);
+		SPtr<Resource> loadFromDiskAndDeserialize(const Path& filePath, bool loadWithSaveData);
 
 		/**	Triggered when individual resource has finished loading. */
 		void loadComplete(HResource& resource);
 
 		/**	Callback triggered when the task manager is ready to process the loading task. */
-		void loadCallback(const Path& filePath, HResource& resource);
+		void loadCallback(const Path& filePath, HResource& resource, bool loadWithSaveData);
 
 		/**	Destroys a resource, freeing its memory. */
 		void destroy(ResourceHandleBase& resource);

+ 3 - 1
Source/BansheeCore/Source/BsAudioClip.cpp

@@ -10,10 +10,12 @@ namespace BansheeEngine
 {
 	AudioClip::AudioClip(const SPtr<DataStream>& samples, UINT32 streamSize, UINT32 numSamples, const AUDIO_CLIP_DESC& desc)
 		: Resource(false), mDesc(desc), mNumSamples(numSamples), mStreamData(samples), mStreamSize(streamSize)
-		, mStreamOffset(0), mLength(0.0f), mKeepSourceData(true)
+		, mStreamOffset(0), mLength(0.0f)
 	{
 		if (samples != nullptr)
 			mStreamOffset = (UINT32)samples->tell();
+
+		mKeepSourceData = desc.keepSourceData;
 	}
 
 	void AudioClip::initialize()

+ 4 - 4
Source/BansheeCore/Source/BsPrefabUtility.cpp

@@ -11,9 +11,9 @@ namespace BansheeEngine
 	void PrefabUtility::revertToPrefab(const HSceneObject& so)
 	{
 		String prefabLinkUUID = so->getPrefabLink();
-		HPrefab prefabLink = static_resource_cast<Prefab>(gResources().loadFromUUID(prefabLinkUUID, false, false));
+		HPrefab prefabLink = static_resource_cast<Prefab>(gResources().loadFromUUID(prefabLinkUUID, false, ResourceLoadFlag::None));
 
-		if (prefabLink == nullptr)
+		if (!prefabLink.isLoaded(false))
 			return;
 
 		// Save IDs, destroy original, create new, restore IDs
@@ -93,7 +93,7 @@ namespace BansheeEngine
 		for (auto iter = prefabInstanceRoots.rbegin(); iter != prefabInstanceRoots.rend(); ++iter)
 		{
 			HSceneObject current = *iter;
-			HPrefab prefabLink = static_resource_cast<Prefab>(gResources().loadFromUUID(current->mPrefabLinkUUID, false, false));
+			HPrefab prefabLink = static_resource_cast<Prefab>(gResources().loadFromUUID(current->mPrefabLinkUUID, false, ResourceLoadFlag::None));
 
 			if (prefabLink.isLoaded(false) && prefabLink->getHash() != current->mPrefabHash)
 			{
@@ -231,7 +231,7 @@ namespace BansheeEngine
 			{
 				current->mPrefabDiff = nullptr;
 
-				HPrefab prefabLink = static_resource_cast<Prefab>(gResources().loadFromUUID(current->mPrefabLinkUUID, false, false));
+				HPrefab prefabLink = static_resource_cast<Prefab>(gResources().loadFromUUID(current->mPrefabLinkUUID, false, ResourceLoadFlag::None));
 				if (prefabLink.isLoaded(false))
 					current->mPrefabDiff = PrefabDiff::create(prefabLink->_getRoot(), current->getHandle());
 			}

+ 1 - 1
Source/BansheeCore/Source/BsResource.cpp

@@ -7,7 +7,7 @@
 namespace BansheeEngine
 {
 	Resource::Resource(bool initializeOnRenderThread)
-		:CoreObject(initializeOnRenderThread), mSize(0)
+		:CoreObject(initializeOnRenderThread), mSize(0), mKeepSourceData(true)
 	{ 
 		mMetaData = bs_shared_ptr_new<ResourceMetaData>();
 	}

+ 44 - 24
Source/BansheeCore/Source/BsResources.cpp

@@ -35,7 +35,7 @@ namespace BansheeEngine
 			destroy(loadedResourcePair.second.resource);
 	}
 
-	HResource Resources::load(const Path& filePath, bool loadDependencies, bool keepInternalReference)
+	HResource Resources::load(const Path& filePath, ResourceLoadFlags loadFlags)
 	{
 		if (!FileSystem::isFile(filePath))
 		{
@@ -50,19 +50,19 @@ namespace BansheeEngine
 		if (!foundUUID)
 			uuid = UUIDGenerator::generateRandom();
 
-		return loadInternal(uuid, filePath, true, loadDependencies, keepInternalReference);
+		return loadInternal(uuid, filePath, true, loadFlags);
 	}
 
-	HResource Resources::load(const WeakResourceHandle<Resource>& handle, bool loadDependencies, bool keepInternalReference)
+	HResource Resources::load(const WeakResourceHandle<Resource>& handle, ResourceLoadFlags loadFlags)
 	{
 		if (handle.mData == nullptr)
 			return HResource();
 
 		String uuid = handle.getUUID();
-		return loadFromUUID(uuid, false, loadDependencies, keepInternalReference);
+		return loadFromUUID(uuid, false, loadFlags);
 	}
 
-	HResource Resources::loadAsync(const Path& filePath, bool loadDependencies, bool keepInternalReference)
+	HResource Resources::loadAsync(const Path& filePath, ResourceLoadFlags loadFlags)
 	{
 		if (!FileSystem::isFile(filePath))
 		{
@@ -77,10 +77,10 @@ namespace BansheeEngine
 		if (!foundUUID)
 			uuid = UUIDGenerator::generateRandom();
 
-		return loadInternal(uuid, filePath, false, loadDependencies, keepInternalReference);
+		return loadInternal(uuid, filePath, false, loadFlags);
 	}
 
-	HResource Resources::loadFromUUID(const String& uuid, bool async, bool loadDependencies, bool keepInternalReference)
+	HResource Resources::loadFromUUID(const String& uuid, bool async, ResourceLoadFlags loadFlags)
 	{
 		Path filePath;
 
@@ -92,10 +92,10 @@ namespace BansheeEngine
 				break;
 		}
 
-		return loadInternal(uuid, filePath, !async, loadDependencies, keepInternalReference);
+		return loadInternal(uuid, filePath, !async, loadFlags);
 	}
 
-	HResource Resources::loadInternal(const String& UUID, const Path& filePath, bool synchronous, bool loadDependencies, bool keepInternalReference)
+	HResource Resources::loadInternal(const String& UUID, const Path& filePath, bool synchronous, ResourceLoadFlags loadFlags)
 	{
 		HResource outputResource;
 
@@ -110,7 +110,7 @@ namespace BansheeEngine
 				LoadedResourceData& resData = iterFind2->second->resData;
 				outputResource = resData.resource.lock();
 
-				if (keepInternalReference)
+				if (loadFlags.isSet(ResourceLoadFlag::KeepInternalRef))
 				{
 					resData.numInternalRefs++;
 					outputResource.addInternalRef();
@@ -133,7 +133,7 @@ namespace BansheeEngine
 					LoadedResourceData& resData = iterFind->second;
 					outputResource = resData.resource.lock();
 
-					if (keepInternalReference)
+					if (loadFlags.isSet(ResourceLoadFlag::KeepInternalRef))
 					{
 						resData.numInternalRefs++;
 						outputResource.addInternalRef();
@@ -204,7 +204,7 @@ namespace BansheeEngine
 				mInProgressResources[UUID] = loadData;
 				loadData->resData = outputResource.getWeak();
 
-				if (keepInternalReference)
+				if (loadFlags.isSet(ResourceLoadFlag::KeepInternalRef))
 				{
 					loadData->resData.numInternalRefs++;
 					outputResource.addInternalRef();
@@ -214,7 +214,7 @@ namespace BansheeEngine
 				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)
+				if (loadFlags.isSet(ResourceLoadFlag::LoadDependencies) && savedResourceData != nullptr)
 				{
 					for (auto& dependency : savedResourceData->getDependencies())
 					{
@@ -227,14 +227,18 @@ namespace BansheeEngine
 				}
 			}
 
-			if (loadDependencies && savedResourceData != nullptr)
+			if (loadFlags.isSet(ResourceLoadFlag::LoadDependencies) && savedResourceData != nullptr)
 			{
 				const Vector<String>& dependencyUUIDs = savedResourceData->getDependencies();
 				UINT32 numDependencies = (UINT32)dependencyUUIDs.size();
 				Vector<HResource> dependencies(numDependencies);
 
+				ResourceLoadFlags depLoadFlags = ResourceLoadFlag::LoadDependencies;
+				if (loadFlags.isSet(ResourceLoadFlag::KeepSourceData))
+					depLoadFlags |= ResourceLoadFlag::KeepSourceData;
+
 				for (UINT32 i = 0; i < numDependencies; i++)
-					dependencies[i] = loadFromUUID(dependencyUUIDs[i], !synchronous, true, false);
+					dependencies[i] = loadFromUUID(dependencyUUIDs[i], !synchronous, depLoadFlags);
 
 				// Keep dependencies alive until the parent is done loading
 				{
@@ -245,7 +249,7 @@ namespace BansheeEngine
 				}
 			}
 		}
-		else if (loadDependencies && savedResourceData != nullptr) // Queue dependencies in case they aren't already loaded
+		else if (loadFlags.isSet(ResourceLoadFlag::LoadDependencies) && savedResourceData != nullptr) // Queue dependencies in case they aren't already loaded
 		{
 			const Vector<String>& dependencies = savedResourceData->getDependencies();
 			if (!dependencies.empty())
@@ -300,8 +304,12 @@ namespace BansheeEngine
 					}
 				}
 
+				ResourceLoadFlags depLoadFlags = ResourceLoadFlag::LoadDependencies;
+				if (loadFlags.isSet(ResourceLoadFlag::KeepSourceData))
+					depLoadFlags |= ResourceLoadFlag::KeepSourceData;
+
 				for (auto& dependency : dependencies)
-					loadFromUUID(dependency, !synchronous, true, false);
+					loadFromUUID(dependency, !synchronous, depLoadFlags);
 			}
 		}
 
@@ -311,14 +319,16 @@ namespace BansheeEngine
 			// Synchronous or the resource doesn't support async, read the file immediately
 			if (synchronous || !savedResourceData->allowAsyncLoading())
 			{
-				loadCallback(filePath, outputResource);
+				loadCallback(filePath, outputResource, loadFlags.isSet(ResourceLoadFlag::KeepSourceData));
 			}
 			else // Asynchronous, read the file on a worker thread
 			{
 				String fileName = filePath.getFilename();
 				String taskName = "Resource load: " + fileName;
 
-				SPtr<Task> task = Task::create(taskName, std::bind(&Resources::loadCallback, this, filePath, outputResource));
+				bool keepSourceData = loadFlags.isSet(ResourceLoadFlag::KeepSourceData);
+				SPtr<Task> task = Task::create(taskName, 
+					std::bind(&Resources::loadCallback, this, filePath, outputResource, keepSourceData));
 				TaskScheduler::instance().addTask(task);
 			}
 		}
@@ -343,11 +353,16 @@ namespace BansheeEngine
 		return outputResource;
 	}
 
-	SPtr<Resource> Resources::loadFromDiskAndDeserialize(const Path& filePath)
+	SPtr<Resource> Resources::loadFromDiskAndDeserialize(const Path& filePath, bool loadWithSaveData)
 	{
 		FileDecoder fs(filePath);
 		fs.skip(); // Skipped over saved resource data
-		SPtr<IReflectable> loadedData = fs.decode();
+
+		UnorderedMap<String, UINT64> loadParams;
+		if(loadWithSaveData)
+			loadParams["keepSourceData"] = 1;
+
+		SPtr<IReflectable> loadedData = fs.decode(loadParams);
 
 		if (loadedData == nullptr)
 		{
@@ -503,10 +518,15 @@ namespace BansheeEngine
 				BS_EXCEPT(InvalidParametersException, "Another file exists at the specified location.");
 		}
 
+		if (!resource->mKeepSourceData)
+		{
+			LOGWRN("Saving a resource that was created/loaded without ResourceLoadFlag::KeepSourceData. Some data might "
+				"not be available for saving. File path: " + filePath.toString());
+		}
+
 		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();
@@ -735,9 +755,9 @@ namespace BansheeEngine
 		}
 	}
 
-	void Resources::loadCallback(const Path& filePath, HResource& resource)
+	void Resources::loadCallback(const Path& filePath, HResource& resource, bool loadWithSaveData)
 	{
-		SPtr<Resource> rawResource = loadFromDiskAndDeserialize(filePath);
+		SPtr<Resource> rawResource = loadFromDiskAndDeserialize(filePath, loadWithSaveData);
 
 		{
 			Lock lock(mInProgressResourcesMutex);

+ 3 - 2
Source/BansheeEditor/Source/BsEditorShaderIncludeHandler.cpp

@@ -16,10 +16,11 @@ namespace BansheeEngine
 		if (path.isEmpty())
 			return HShaderInclude();
 
+		ResourceLoadFlags loadFlags = ResourceLoadFlag::Default | ResourceLoadFlag::KeepSourceData;
 		if (name.size() >= 8)
 		{
 			if (name.substr(0, 8) == "$ENGINE$" || name.substr(0, 8) == "$EDITOR$")
-				return static_resource_cast<ShaderInclude>(Resources::instance().load(path));
+				return static_resource_cast<ShaderInclude>(Resources::instance().load(path, loadFlags));
 		}
 
 		ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(path);
@@ -33,7 +34,7 @@ namespace BansheeEngine
 				for(auto& resMeta : resourceMetas)
 				{
 					if(resMeta->getTypeID() == TID_ShaderInclude)
-						return static_resource_cast<ShaderInclude>(Resources::instance().loadFromUUID(resMeta->getUUID()));
+						return static_resource_cast<ShaderInclude>(Resources::instance().loadFromUUID(resMeta->getUUID(), false, loadFlags));
 				}
 			}
 		}

+ 4 - 2
Source/BansheeEditor/Source/BsProjectLibrary.cpp

@@ -423,7 +423,7 @@ namespace BansheeEngine
 
 				// 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.
-				importedResources.push_back({ L"primary", gResources().load(fileEntry->path, false, false) });
+				importedResources.push_back({ L"primary", gResources().load(fileEntry->path, ResourceLoadFlag::KeepSourceData) });
 			}
 
 			if(fileEntry->meta == nullptr)
@@ -1177,8 +1177,10 @@ namespace BansheeEngine
 		if (meta == nullptr)
 			return HResource();
 
+		ResourceLoadFlags loadFlags = ResourceLoadFlag::Default | ResourceLoadFlag::KeepSourceData;
+
 		String resUUID = meta->getUUID();
-		return gResources().loadFromUUID(resUUID);
+		return gResources().loadFromUUID(resUUID, false, loadFlags);
 	}
 
 	void ProjectLibrary::createInternalParentHierarchy(const Path& fullPath, DirectoryEntry** newHierarchyRoot, DirectoryEntry** newHierarchyLeaf)

+ 6 - 2
Source/BansheeEngine/Source/BsGameResourceManager.cpp

@@ -29,11 +29,15 @@ namespace BansheeEngine
 
 	HResource StandaloneResourceLoader::load(const Path& path, bool keepLoaded) const
 	{
+		ResourceLoadFlags loadFlags = ResourceLoadFlag::LoadDependencies;
+		if (keepLoaded)
+			loadFlags |= ResourceLoadFlag::KeepInternalRef;
+
 		auto iterFind = mMapping.find(path);
 		if(iterFind != mMapping.end())
-			return gResources().load(iterFind->second, true, keepLoaded);
+			return gResources().load(iterFind->second, loadFlags);
 		
-		return gResources().load(path, true, keepLoaded);
+		return gResources().load(path, loadFlags);
 	}
 
 	void StandaloneResourceLoader::setMapping(const SPtr<ResourceMapping>& mapping)

+ 2 - 24
Source/BansheeOpenAudio/Source/BsOAAudioClip.cpp

@@ -189,41 +189,19 @@ namespace BansheeEngine
 				OAOggVorbisReader reader;
 				if (reader.open(stream, info))
 				{
-					bs_frame_mark();
-
 					UINT32 bufferSize = info.numSamples * info.bitDepth;
-					UINT8* sampleBuffer = (UINT8*)bs_frame_alloc(bufferSize);
+					UINT8* sampleBuffer = (UINT8*)bs_stack_alloc(bufferSize);
 
 					reader.read(sampleBuffer, info.numSamples);
 
 					alGenBuffers(1, &mBufferId);
-
-
-
-					if(info.bitDepth > 16)
-					{
-						LOGWRN("OpenAL doesn't support bit depth larger than 16. Your audio data will be truncated. "
-							"Consider re-importing the audio clip as 16-bit. Audio clip: " + toString(getName()) + ".");
-
-						bufferSize = info.numSamples * 16;
-						UINT8* sampleBuffer16 = (UINT8*)bs_frame_alloc(bufferSize);
-
-						AudioUtility::convertBitDepth(sampleBuffer, info.bitDepth, sampleBuffer16, 16, info.numSamples);
-
-						info.bitDepth = 16;
-
-						bs_frame_free(sampleBuffer);
-						sampleBuffer = sampleBuffer16;
-					}
-
 					writeToSoundBuffer(mBufferId, sampleBuffer, info);
 
 					mStreamData = nullptr;
 					mStreamOffset = 0;
 					mStreamSize = 0;
 
-					bs_frame_free(sampleBuffer);
-					bs_frame_clear();
+					bs_stack_free(sampleBuffer);
 				}
 				else
 					LOGERR("Failed decompressing AudioClip stream.");

+ 2 - 5
Source/BansheeUtility/Include/BsDataStream.h

@@ -229,12 +229,11 @@ namespace BansheeEngine
 		 * Construct a file stream.
 		 *
 		 * @param[in]	filePath	Path of the file to open.
-		 * @param[in]	readOnly	Determines should the file be opened in read-only mode, or should writing be supported 
-		 *							as well.
+		 * @param[in]	accessMode	Determines should the file be opened in read, write or read/write mode.
 		 * @param[in]	freeOnClose	Determines should the internal stream be freed once the data stream is closed or goes 
 		 *							out of scope.
 		 */
-		FileDataStream(const Path& filePath, bool readOnly = true, bool freeOnClose = true);
+		FileDataStream(const Path& filePath, AccessMode accessMode = READ, bool freeOnClose = true);
 
 		~FileDataStream();
 
@@ -270,8 +269,6 @@ namespace BansheeEngine
 		SPtr<std::ifstream> mFStreamRO;
 		SPtr<std::fstream> mFStream;
 		bool mFreeOnClose;	
-
-		void determineAccess();
 	};
 
 	/** @} */

+ 32 - 44
Source/BansheeUtility/Source/BsDataStream.cpp

@@ -368,55 +368,45 @@ namespace BansheeEngine
         }
     }
 
-    FileDataStream::FileDataStream(const Path& path, bool readOnly, bool freeOnClose)
-        : DataStream(READ), mPath(path), mFreeOnClose(freeOnClose)
+    FileDataStream::FileDataStream(const Path& path, AccessMode accessMode, bool freeOnClose)
+        : DataStream(accessMode), mPath(path), mFreeOnClose(freeOnClose)
     {
-		WString pathWString = path.toWString();
+		WString pathWString = path.toWString();
 		const wchar_t* pathString = pathWString.c_str();
-
-		// Always open in binary mode
-		// Also, always include reading
-		std::ios::openmode mode = std::ios::in | std::ios::binary;
-
-		if (!readOnly)
-		{
-			mode |= std::ios::out;
-			mFStream = bs_shared_ptr_new<std::fstream>();
-			mFStream->open(pathString, mode);
-			mInStream = mFStream;
-		}
-		else
-		{
-			mFStreamRO = bs_shared_ptr_new<std::ifstream>();
-			mFStreamRO->open(pathString, mode);
-			mInStream = mFStreamRO;
-		}
-
-		// Should check ensure open succeeded, in case fail for some reason.
-		if (mInStream->fail())
-		{
-			LOGWRN("Cannot open file: " + path.toString());
-			return;
-		}
+
+		// Always open in binary mode
+		// Also, always include reading
+		std::ios::openmode mode = std::ios::binary;
+
+		if ((accessMode & READ) != 0)
+			mode |= std::ios::in;
+
+		if (((accessMode & WRITE) != 0))
+		{
+			mode |= std::ios::out;
+			mFStream = bs_shared_ptr_new<std::fstream>();
+			mFStream->open(pathString, mode);
+			mInStream = mFStream;
+		}
+		else
+		{
+			mFStreamRO = bs_shared_ptr_new<std::ifstream>();
+			mFStreamRO->open(pathString, mode);
+			mInStream = mFStreamRO;
+		}
+
+		// Should check ensure open succeeded, in case fail for some reason.
+		if (mInStream->fail())
+		{
+			LOGWRN("Cannot open file: " + path.toString());
+			return;
+		}
 		
         mInStream->seekg(0, std::ios_base::end);
         mSize = (size_t)mInStream->tellg();
         mInStream->seekg(0, std::ios_base::beg);
-
-		determineAccess();
     }
 
-	void FileDataStream::determineAccess()
-	{
-		mAccess = 0;
-
-		if (mInStream)
-			mAccess |= READ;
-
-		if (mFStream)
-			mAccess |= WRITE;
-	}
-
     FileDataStream::~FileDataStream()
     {
         close();
@@ -466,9 +456,7 @@ namespace BansheeEngine
 
 	SPtr<DataStream> FileDataStream::clone(bool copyData) const
 	{
-		bool readOnly = (getAccessMode() & WRITE) == 0;
-
-		return bs_shared_ptr_new<FileDataStream>(mPath, readOnly, true);		
+		return bs_shared_ptr_new<FileDataStream>(mPath, (AccessMode)getAccessMode(), true);
 	}
 
     void FileDataStream::close()

+ 6 - 2
Source/BansheeUtility/Source/Win32/BsWin32FileSystem.cpp

@@ -274,12 +274,16 @@ namespace BansheeEngine
 			return nullptr;
 		}
 
-		return bs_shared_ptr_new<FileDataStream>(fullPath, readOnly, true);
+		DataStream::AccessMode accessMode = DataStream::READ;
+		if (!readOnly)
+			accessMode = (DataStream::AccessMode)(accessMode | (UINT32)DataStream::WRITE);
+
+		return bs_shared_ptr_new<FileDataStream>(fullPath, accessMode, true);
 	}
 
 	SPtr<DataStream> FileSystem::createAndOpenFile(const Path& fullPath)
 	{
-		return bs_shared_ptr_new<FileDataStream>(fullPath, false, true);
+		return bs_shared_ptr_new<FileDataStream>(fullPath, DataStream::AccessMode::WRITE, true);
 	}
 
 	UINT64 FileSystem::getFileSize(const Path& fullPath)

+ 2 - 1
Source/Game/Source/Main.cpp

@@ -135,7 +135,8 @@ void runApplication()
 	}
 
 	{
-		HPrefab mainScene = static_resource_cast<Prefab>(gResources().loadFromUUID(gameSettings->mainSceneUUID, false, true, false));
+		HPrefab mainScene = static_resource_cast<Prefab>(gResources().loadFromUUID(gameSettings->mainSceneUUID, 
+			false, ResourceLoadFlag::LoadDependencies));
 		if (mainScene.isLoaded(false))
 		{
 			HSceneObject root = mainScene->instantiate();

+ 5 - 1
Source/SBansheeEditor/Source/BsEditorResourceLoader.cpp

@@ -36,6 +36,10 @@ namespace BansheeEngine
 					isn't flagged to be included in the build. It may not be available outside of the editor.");
 		}
 
-		return gResources().loadFromUUID(resUUID, false, true, keepLoaded);
+		ResourceLoadFlags loadFlags = ResourceLoadFlag::LoadDependencies | ResourceLoadFlag::KeepSourceData;
+		if (keepLoaded)
+			loadFlags |= ResourceLoadFlag::KeepInternalRef;
+
+		return gResources().loadFromUUID(resUUID, false, loadFlags);
 	}
 }

+ 1 - 1
Source/SBansheeEditor/Source/BsGUIResourceField.cpp

@@ -167,7 +167,7 @@ namespace BansheeEngine
 	HResource GUIResourceField::getValue() const
 	{
 		if (!mUUID.empty())
-			return gResources().loadFromUUID(mUUID);
+			return gResources().loadFromUUID(mUUID, false, ResourceLoadFlag::Default | ResourceLoadFlag::KeepSourceData);
 
 		return HResource();
 	}

+ 2 - 1
Source/SBansheeEditor/Source/BsGUITextureField.cpp

@@ -169,7 +169,8 @@ namespace BansheeEngine
 
 	HTexture GUITextureField::getValue() const
 	{
-		return static_resource_cast<Texture>(Resources::instance().loadFromUUID(mUUID));
+		ResourceLoadFlags loadFlags = ResourceLoadFlag::Default | ResourceLoadFlag::KeepSourceData;
+		return static_resource_cast<Texture>(Resources::instance().loadFromUUID(mUUID, false, loadFlags));
 	}
 
 	void GUITextureField::setValue(const HTexture& value)

+ 3 - 1
Source/SBansheeEditor/Source/BsScriptPrefabUtility.cpp

@@ -38,8 +38,10 @@ namespace BansheeEngine
 		if (ScriptSceneObject::checkIfDestroyed(nativeInstance))
 			return;
 
+		ResourceLoadFlags loadFlags = ResourceLoadFlag::KeepInternalRef | ResourceLoadFlag::KeepSourceData;
+
 		String prefabLinkUUID = nativeInstance->getNativeSceneObject()->getPrefabLink();
-		HPrefab prefab = static_resource_cast<Prefab>(gResources().loadFromUUID(prefabLinkUUID, false, false));
+		HPrefab prefab = static_resource_cast<Prefab>(gResources().loadFromUUID(prefabLinkUUID, false, loadFlags));
 
 		if (prefab != nullptr)
 			prefab->update(nativeInstance->getNativeSceneObject());

+ 7 - 5
Source/SBansheeEngine/Source/BsScriptResourceRef.cpp

@@ -2,16 +2,13 @@
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #include "BsScriptResourceRef.h"
 #include "BsScriptMeta.h"
-#include "BsMonoField.h"
 #include "BsMonoClass.h"
-#include "BsMonoManager.h"
 #include "BsMonoUtil.h"
 #include "BsResources.h"
 #include "BsScriptResource.h"
 #include "BsScriptTexture2D.h"
-#include "BsScriptTexture3D.h"
-#include "BsScriptTextureCube.h"
 #include "BsScriptResourceManager.h"
+#include "BsApplication.h"
 
 namespace BansheeEngine
 {
@@ -58,7 +55,12 @@ namespace BansheeEngine
 
 	MonoObject* ScriptResourceRef::internal_GetResource(ScriptResourceRef* nativeInstance)
 	{
-		HResource resource = gResources().load(nativeInstance->mResource);
+		ResourceLoadFlags loadFlags = ResourceLoadFlag::LoadDependencies | ResourceLoadFlag::KeepInternalRef;
+
+		if (gApplication().isEditor())
+			loadFlags |= ResourceLoadFlag::KeepSourceData;
+
+		HResource resource = gResources().load(nativeInstance->mResource, loadFlags);
 		ScriptResourceBase* scriptResource;
 
 		ScriptResourceManager::instance().getScriptResource(resource, &scriptResource, true);

+ 9 - 1
Source/SBansheeEngine/Source/BsScriptResources.cpp

@@ -10,6 +10,7 @@
 #include "BsScriptResourceManager.h"
 #include "BsScriptResource.h"
 #include "BsScriptResourceRef.h"
+#include "BsApplication.h"
 
 namespace BansheeEngine
 {
@@ -46,7 +47,14 @@ namespace BansheeEngine
 		if (scriptRef == nullptr)
 			return nullptr;
 
-		HResource resource = gResources().load(scriptRef->getHandle(), true, keepLoaded);
+		ResourceLoadFlags loadFlags = ResourceLoadFlag::LoadDependencies;
+		if (keepLoaded)
+			loadFlags |= ResourceLoadFlag::KeepInternalRef;
+
+		if (gApplication().isEditor())
+			loadFlags |= ResourceLoadFlag::KeepSourceData;
+
+		HResource resource = gResources().load(scriptRef->getHandle(), loadFlags);
 		if (resource == nullptr)
 			return nullptr;