Browse Source

Managed resource unloading works properly

Marko Pintera 11 years ago
parent
commit
35b5f1e6f3

+ 2 - 0
BansheeCore/Source/BsResources.cpp

@@ -195,6 +195,8 @@ namespace BansheeEngine
 			BS_LOCK_MUTEX(mLoadedResourceMutex);
 			BS_LOCK_MUTEX(mLoadedResourceMutex);
 			mLoadedResources.erase(resource.getUUID());
 			mLoadedResources.erase(resource.getUUID());
 		}
 		}
+
+		resource._setHandleData(nullptr, "");
 	}
 	}
 
 
 	void Resources::unloadAllUnused()
 	void Resources::unloadAllUnused()

+ 0 - 2
BansheeEngine/Source/BsApplication.cpp

@@ -62,8 +62,6 @@ namespace BansheeEngine
 
 
 	Application::~Application()
 	Application::~Application()
 	{
 	{
-		ScriptManager::instance().destroy();
-
 #if BS_VER == BS_VER_DEV
 #if BS_VER == BS_VER_DEV
 		unloadPlugin(mSBansheeEnginePlugin);
 		unloadPlugin(mSBansheeEnginePlugin);
 		unloadPlugin(mMonoPlugin);
 		unloadPlugin(mMonoPlugin);

+ 11 - 18
Inspector.txt

@@ -3,7 +3,10 @@ Update GUIFoldout with sub styles
 
 
 Test if drag and dropping scene objects works with object and resource fields. Especially custom resources and components.
 Test if drag and dropping scene objects works with object and resource fields. Especially custom resources and components.
 
 
-There might be an issue with unreleased TransientMeshes (or MeshHeap) after editor widgets are docked (to test disable layout loading, dock manually and then close)
+Transient meshes don't seem to be released properly in 100% of the cases
+Finalizers get called from a different thread
+ - This is especially noticeable in ScriptManagedResource where I call gResources().unload from that thread. I should probably not be doing that and instead queuing that for later on main thread.
+ - I added some code for handling this. It needs testing
 
 
 Test custom resources:
 Test custom resources:
  - Can I load them? (Will likely need ProjectLIbrary::load)
  - Can I load them? (Will likely need ProjectLIbrary::load)
@@ -14,30 +17,20 @@ ARRAY TODO:
  - Need a GUIFoldout that doesn't have BG and is just a single button.
  - Need a GUIFoldout that doesn't have BG and is just a single button.
 
 
 TODO:
 TODO:
- - Hook up int field set/get callbacks
-    - Ensure int field isn't updated from app when in focus
- - Think about how to handle arrays, adding and deleting elements from them.
-   - Will likely need GUILayout::GetElementIndex()
+ - Add inspector support for lists and objects
+ - Add all remaining field type Inspectable* classes
+ - Ensure fields aren't updated from the app when in focus
  - Entire foldout should be clickable, not just the toggle button
  - Entire foldout should be clickable, not just the toggle button
  - Extend text field so it can be multi-line
  - Extend text field so it can be multi-line
- - Port to C#:
-   - Create InspectableObjects for all different field types
- - Ensure get/set value from inspector fields works 
- - Add array fields and ensure they work/update properly
- - Ensure Undo/redo works as intended
-   - This task needs decomposing. Likely need to port UndoRedo to C# first.
+ - Properly hook up UndoRedo, for both in-field and object-wide changes
  - GUIColor needs to be hooked up to a window that actually changes its value.
  - GUIColor needs to be hooked up to a window that actually changes its value.
- - I need to register UndoRedo command after user finishes modifying a field. This should be referencing an object using an URI?
+ - Inspector scrolling
 
 
 KEEP IN MIND:
 KEEP IN MIND:
  - Clicking on an object/resource in inspector should ping it in their window
  - Clicking on an object/resource in inspector should ping it in their window
  - Inspector needs to be organized in such a way that scroll areas work. That should be possible with GUIPanelContainer.
  - Inspector needs to be organized in such a way that scroll areas work. That should be possible with GUIPanelContainer.
- - When inspector object changes I need to rebuild that inspector element
-   - This can happen if user drags a new object
-   - Or while the application is running objects might change from code
-     - Technically objects will never change their structure during runtime, this is only relevant for array sizes
- - Modify C++ Editor fields so that calling setValue doesn't update the visual value until focus is lost
-  - When user is currently writing in an input box I don't want refresh to overwrite that value.
+
+
 
 
 
 
 
 

+ 1 - 1
SBansheeEditor/Source/BsScriptGUIResourceField.cpp

@@ -113,7 +113,7 @@ namespace BansheeEngine
 		}
 		}
 		else
 		else
 		{
 		{
-			ScriptResourceBase* scriptResource = ScriptResourceManager::instance().getScriptResource(instance);
+			ScriptResourceBase* scriptResource = ScriptResourceManager::instance().getScriptResource(instance.getUUID());
 			if (scriptResource == nullptr)
 			if (scriptResource == nullptr)
 				return nullptr;
 				return nullptr;
 			else
 			else

+ 2 - 1
SBansheeEngine/Include/BsManagedResource.h

@@ -10,7 +10,7 @@ namespace BansheeEngine
 	class BS_SCR_BE_EXPORT ManagedResource : public Resource
 	class BS_SCR_BE_EXPORT ManagedResource : public Resource
 	{
 	{
 	public:
 	public:
-		void construct(MonoObject* object);
+		void construct(MonoObject* object, const HManagedResource& myHandle);
 
 
 		MonoObject* getManagedInstance() const { return mManagedInstance; }
 		MonoObject* getManagedInstance() const { return mManagedInstance; }
 
 
@@ -25,6 +25,7 @@ namespace BansheeEngine
 
 
 		MonoObject* mManagedInstance;
 		MonoObject* mManagedInstance;
 		uint32_t mManagedHandle;
 		uint32_t mManagedHandle;
+		HManagedResource mMyHandle;
 
 
 		/************************************************************************/
 		/************************************************************************/
 		/* 								RTTI		                     		*/
 		/* 								RTTI		                     		*/

+ 20 - 0
SBansheeEngine/Include/BsManagedResourceManager.h

@@ -0,0 +1,20 @@
+#pragma once
+
+#include "BsScriptEnginePrerequisites.h"
+#include "BsModule.h"
+
+namespace BansheeEngine
+{
+	class BS_SCR_BE_EXPORT ManagedResourceManager : public Module <ManagedResourceManager>
+	{
+	public:
+		ManagedResourceManager();
+		~ManagedResourceManager();
+
+		void registerManagedResource(const HManagedResource& resource);
+		void unregisterManagedResource(const HManagedResource& resource);
+
+	private:
+		UnorderedMap<String, HManagedResource> mResources;
+	};
+}

+ 6 - 3
SBansheeEngine/Include/BsManagedResourceRTTI.h

@@ -6,6 +6,7 @@
 #include "BsManagedResource.h"
 #include "BsManagedResource.h"
 #include "BsMonoManager.h"
 #include "BsMonoManager.h"
 #include "BsManagedSerializableObject.h"
 #include "BsManagedSerializableObject.h"
+#include "BsResources.h"
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
@@ -37,10 +38,12 @@ namespace BansheeEngine
 
 
 		virtual void onDeserializationEnded(IReflectable* obj)
 		virtual void onDeserializationEnded(IReflectable* obj)
 		{
 		{
-			ManagedResource* mc = static_cast<ManagedResource*>(obj);
-			ManagedSerializableObjectPtr serializableObject = any_cast<ManagedSerializableObjectPtr>(mc->mRTTIData);
+			ManagedResource* mr = static_cast<ManagedResource*>(obj);
+			ManagedSerializableObjectPtr serializableObject = any_cast<ManagedSerializableObjectPtr>(mr->mRTTIData);
 
 
-			mc->construct(serializableObject->getManagedInstance());
+			ResourcePtr mrPtr = std::static_pointer_cast<Resource>(mr->getThisPtr());
+			HManagedResource handle = static_resource_cast<ManagedResource>(gResources()._createResourceHandle(mrPtr));
+			mr->construct(serializableObject->getManagedInstance(), handle);
 		}
 		}
 
 
 		virtual const String& getRTTIName()
 		virtual const String& getRTTIName()

+ 1 - 1
SBansheeEngine/Include/BsScriptResourceManager.h

@@ -59,7 +59,7 @@ namespace BansheeEngine
 		/**
 		/**
 		 * @note Returns nullptr if script resource doesn't exist.
 		 * @note Returns nullptr if script resource doesn't exist.
 		 */
 		 */
-		ScriptResourceBase* getScriptResource(const HResource& resourceHandle);
+		ScriptResourceBase* getScriptResource(const String& UUID);
 
 
 		void destroyScriptResource(ScriptResourceBase* resource);
 		void destroyScriptResource(ScriptResourceBase* resource);
 
 

+ 2 - 0
SBansheeEngine/SBansheeEngine.vcxproj

@@ -229,6 +229,7 @@
     <ClInclude Include="Include\BsManagedComponent.h" />
     <ClInclude Include="Include\BsManagedComponent.h" />
     <ClInclude Include="Include\BsManagedComponentRTTI.h" />
     <ClInclude Include="Include\BsManagedComponentRTTI.h" />
     <ClInclude Include="Include\BsManagedResource.h" />
     <ClInclude Include="Include\BsManagedResource.h" />
+    <ClInclude Include="Include\BsManagedResourceManager.h" />
     <ClInclude Include="Include\BsManagedResourceMetaData.h" />
     <ClInclude Include="Include\BsManagedResourceMetaData.h" />
     <ClInclude Include="Include\BsManagedResourceMetaDataRTTI.h" />
     <ClInclude Include="Include\BsManagedResourceMetaDataRTTI.h" />
     <ClInclude Include="Include\BsManagedResourceRTTI.h" />
     <ClInclude Include="Include\BsManagedResourceRTTI.h" />
@@ -288,6 +289,7 @@
   <ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsManagedComponent.cpp" />
     <ClCompile Include="Source\BsManagedComponent.cpp" />
     <ClCompile Include="Source\BsManagedResource.cpp" />
     <ClCompile Include="Source\BsManagedResource.cpp" />
+    <ClCompile Include="Source\BsManagedResourceManager.cpp" />
     <ClCompile Include="Source\BsManagedResourceMetaData.cpp" />
     <ClCompile Include="Source\BsManagedResourceMetaData.cpp" />
     <ClCompile Include="Source\BsRuntimeScriptObjects.cpp" />
     <ClCompile Include="Source\BsRuntimeScriptObjects.cpp" />
     <ClCompile Include="Source\BsScriptComponent.cpp" />
     <ClCompile Include="Source\BsScriptComponent.cpp" />

+ 6 - 0
SBansheeEngine/SBansheeEngine.vcxproj.filters

@@ -207,6 +207,9 @@
     <ClInclude Include="Include\BsScriptManagedResource.h">
     <ClInclude Include="Include\BsScriptManagedResource.h">
       <Filter>Header Files</Filter>
       <Filter>Header Files</Filter>
     </ClInclude>
     </ClInclude>
+    <ClInclude Include="Include\BsManagedResourceManager.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsScriptTexture2D.cpp">
     <ClCompile Include="Source\BsScriptTexture2D.cpp">
@@ -347,5 +350,8 @@
     <ClCompile Include="Source\BsScriptManagedResource.cpp">
     <ClCompile Include="Source\BsScriptManagedResource.cpp">
       <Filter>Source Files</Filter>
       <Filter>Source Files</Filter>
     </ClCompile>
     </ClCompile>
+    <ClCompile Include="Source\BsManagedResourceManager.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   </ItemGroup>
 </Project>
 </Project>

+ 13 - 6
SBansheeEngine/Source/BsManagedResource.cpp

@@ -4,16 +4,17 @@
 #include "BsMonoManager.h"
 #include "BsMonoManager.h"
 #include "BsMonoClass.h"
 #include "BsMonoClass.h"
 #include "BsResources.h"
 #include "BsResources.h"
+#include "BsManagedResourceManager.h"
 #include "BsDebug.h"
 #include "BsDebug.h"
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
 	ManagedResource::ManagedResource()
 	ManagedResource::ManagedResource()
-		:mManagedInstance(nullptr)
+		:Resource(false), mManagedInstance(nullptr)
 	{ }
 	{ }
 
 
 	ManagedResource::ManagedResource(MonoObject* managedInstance)
 	ManagedResource::ManagedResource(MonoObject* managedInstance)
-		:mManagedInstance(nullptr)
+		:Resource(false), mManagedInstance(nullptr)
 	{
 	{
 		ManagedResourceMetaDataPtr metaData = bs_shared_ptr<ManagedResourceMetaData>();
 		ManagedResourceMetaDataPtr metaData = bs_shared_ptr<ManagedResourceMetaData>();
 		mMetaData = metaData;
 		mMetaData = metaData;
@@ -29,8 +30,6 @@ namespace BansheeEngine
 			LOGWRN("Cannot create managed component: " + metaData->typeNamespace + "." + metaData->typeName + " because that type doesn't exist.");
 			LOGWRN("Cannot create managed component: " + metaData->typeNamespace + "." + metaData->typeName + " because that type doesn't exist.");
 			return;
 			return;
 		}
 		}
-
-		construct(managedInstance);
 	}
 	}
 
 
 	HManagedResource ManagedResource::create(MonoObject* managedResource)
 	HManagedResource ManagedResource::create(MonoObject* managedResource)
@@ -39,7 +38,10 @@ namespace BansheeEngine
 		newRes->_setThisPtr(newRes);
 		newRes->_setThisPtr(newRes);
 		newRes->initialize();
 		newRes->initialize();
 
 
-		return static_resource_cast<ManagedResource>(gResources()._createResourceHandle(newRes));
+		HManagedResource handle = static_resource_cast<ManagedResource>(gResources()._createResourceHandle(newRes));
+		newRes->construct(managedResource, handle);
+
+		return handle;
 	}
 	}
 
 
 	ManagedResourcePtr ManagedResource::createEmpty()
 	ManagedResourcePtr ManagedResource::createEmpty()
@@ -51,10 +53,13 @@ namespace BansheeEngine
 		return newRes;
 		return newRes;
 	}
 	}
 
 
-	void ManagedResource::construct(MonoObject* object)
+	void ManagedResource::construct(MonoObject* object, const HManagedResource& myHandle)
 	{
 	{
 		mManagedInstance = object;
 		mManagedInstance = object;
 		mManagedHandle = mono_gchandle_new(mManagedInstance, false);
 		mManagedHandle = mono_gchandle_new(mManagedInstance, false);
+		mMyHandle = myHandle;
+
+		ManagedResourceManager::instance().registerManagedResource(mMyHandle);
 	}
 	}
 
 
 	void ManagedResource::destroy_internal()
 	void ManagedResource::destroy_internal()
@@ -66,6 +71,8 @@ namespace BansheeEngine
 			mManagedInstance = nullptr;
 			mManagedInstance = nullptr;
 			mono_gchandle_free(mManagedHandle);
 			mono_gchandle_free(mManagedHandle);
 		}
 		}
+
+		ManagedResourceManager::instance().unregisterManagedResource(mMyHandle);
 	}
 	}
 
 
 	RTTITypeBase* ManagedResource::getRTTIStatic()
 	RTTITypeBase* ManagedResource::getRTTIStatic()

+ 33 - 0
SBansheeEngine/Source/BsManagedResourceManager.cpp

@@ -0,0 +1,33 @@
+#include "BsManagedResourceManager.h"
+#include "BsResources.h"
+
+namespace BansheeEngine
+{
+	ManagedResourceManager::ManagedResourceManager()
+	{
+
+	}
+
+	ManagedResourceManager::~ManagedResourceManager()
+	{
+		UnorderedMap<String, HManagedResource> resourceCopy = mResources;
+		for (auto& resourcePair : resourceCopy)
+		{
+			HManagedResource resource = resourcePair.second;
+			if (resource != nullptr && resource.isLoaded())
+				gResources().unload(resource);
+		}
+
+		mResources.clear();
+	}
+
+	void ManagedResourceManager::registerManagedResource(const HManagedResource& resource)
+	{
+		mResources.insert(std::make_pair(resource.getUUID(), resource));
+	}
+
+	void ManagedResourceManager::unregisterManagedResource(const HManagedResource& resource)
+	{
+		mResources.erase(resource.getUUID());
+	}
+}

+ 5 - 0
SBansheeEngine/Source/BsScriptEnginePlugin.cpp

@@ -4,6 +4,8 @@
 #include "BsRuntimeScriptObjects.h"
 #include "BsRuntimeScriptObjects.h"
 #include "BsScriptResourceManager.h"
 #include "BsScriptResourceManager.h"
 #include "BsScriptGameObjectManager.h"
 #include "BsScriptGameObjectManager.h"
+#include "BsManagedResourceManager.h"
+#include "BsScriptManager.h"
 
 
 // DEBUG ONLY
 // DEBUG ONLY
 #include "BsScriptSceneObject.h"
 #include "BsScriptSceneObject.h"
@@ -39,6 +41,7 @@ namespace BansheeEngine
 		// DEBUG ONLY
 		// DEBUG ONLY
 		mono_add_internal_call("BansheeEngine.Program::UnitTest1_GameObjectClone", &unitTest1_GameObjectClone);
 		mono_add_internal_call("BansheeEngine.Program::UnitTest1_GameObjectClone", &unitTest1_GameObjectClone);
 
 
+		ManagedResourceManager::startUp();
 		RuntimeScriptObjects::startUp();
 		RuntimeScriptObjects::startUp();
 		ScriptResourceManager::startUp();
 		ScriptResourceManager::startUp();
 		ScriptGameObjectManager::startUp();
 		ScriptGameObjectManager::startUp();
@@ -52,6 +55,8 @@ namespace BansheeEngine
 
 
 	extern "C" BS_SCR_BE_EXPORT void unloadPlugin()
 	extern "C" BS_SCR_BE_EXPORT void unloadPlugin()
 	{
 	{
+		ManagedResourceManager::shutDown();
+		ScriptManager::instance().destroy();
 		ScriptGameObjectManager::shutDown();
 		ScriptGameObjectManager::shutDown();
 		ScriptResourceManager::shutDown();
 		ScriptResourceManager::shutDown();
 		RuntimeScriptObjects::shutDown();
 		RuntimeScriptObjects::shutDown();

+ 7 - 6
SBansheeEngine/Source/BsScriptManagedResource.cpp

@@ -32,12 +32,13 @@ namespace BansheeEngine
 	{
 	{
 		mManagedInstance = nullptr;
 		mManagedInstance = nullptr;
 		
 		
-		if (mResource != nullptr && mResource.isLoaded())
-		{
-			mResource->mManagedInstance = nullptr;
-			gResources().unload(mResource);
-		}
-
+		// The only way this method should be reachable is when Resource::unload is called, which means the resource
+		// has had to been already freed. Even if all managed instances are released ManagedResource itself holds the last
+		// instance which is only freed on unload().
+		// Note: During domain unload this could get called even if not all instances are released, but ManagedResourceManager
+		// should make sure all instances are unloaded before that happens.
+		assert(mResource == nullptr || !mResource.isLoaded()); 
+		
 		ScriptResourceManager::instance().destroyScriptResource(this);
 		ScriptResourceManager::instance().destroyScriptResource(this);
 	}
 	}
 
 

+ 4 - 6
SBansheeEngine/Source/BsScriptResourceManager.cpp

@@ -74,23 +74,21 @@ namespace BansheeEngine
 
 
 	ScriptTexture2D* ScriptResourceManager::getScriptTexture(const HTexture& resourceHandle)
 	ScriptTexture2D* ScriptResourceManager::getScriptTexture(const HTexture& resourceHandle)
 	{
 	{
-		return static_cast<ScriptTexture2D*>(getScriptResource(resourceHandle));
+		return static_cast<ScriptTexture2D*>(getScriptResource(resourceHandle.getUUID()));
 	}
 	}
 
 
 	ScriptSpriteTexture* ScriptResourceManager::getScriptSpriteTexture(const HSpriteTexture& resourceHandle)
 	ScriptSpriteTexture* ScriptResourceManager::getScriptSpriteTexture(const HSpriteTexture& resourceHandle)
 	{
 	{
-		return static_cast<ScriptSpriteTexture*>(getScriptResource(resourceHandle));
+		return static_cast<ScriptSpriteTexture*>(getScriptResource(resourceHandle.getUUID()));
 	}
 	}
 
 
 	ScriptManagedResource* ScriptResourceManager::getScriptManagedResource(const HManagedResource& resourceHandle)
 	ScriptManagedResource* ScriptResourceManager::getScriptManagedResource(const HManagedResource& resourceHandle)
 	{
 	{
-		return static_cast<ScriptManagedResource*>(getScriptResource(resourceHandle));
+		return static_cast<ScriptManagedResource*>(getScriptResource(resourceHandle.getUUID()));
 	}
 	}
 
 
-	ScriptResourceBase* ScriptResourceManager::getScriptResource(const HResource& resourceHandle)
+	ScriptResourceBase* ScriptResourceManager::getScriptResource(const String& uuid)
 	{
 	{
-		const String& uuid = resourceHandle.getUUID();
-
 		if(uuid == "")
 		if(uuid == "")
 			BS_EXCEPT(InvalidParametersException, "Provided resource handle has an undefined resource UUID.");
 			BS_EXCEPT(InvalidParametersException, "Provided resource handle has an undefined resource UUID.");