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);
 			mLoadedResources.erase(resource.getUUID());
 		}
+
+		resource._setHandleData(nullptr, "");
 	}
 
 	void Resources::unloadAllUnused()

+ 0 - 2
BansheeEngine/Source/BsApplication.cpp

@@ -62,8 +62,6 @@ namespace BansheeEngine
 
 	Application::~Application()
 	{
-		ScriptManager::instance().destroy();
-
 #if BS_VER == BS_VER_DEV
 		unloadPlugin(mSBansheeEnginePlugin);
 		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.
 
-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:
  - 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.
 
 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
  - 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.
- - 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:
  - 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.
- - 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
 		{
-			ScriptResourceBase* scriptResource = ScriptResourceManager::instance().getScriptResource(instance);
+			ScriptResourceBase* scriptResource = ScriptResourceManager::instance().getScriptResource(instance.getUUID());
 			if (scriptResource == nullptr)
 				return nullptr;
 			else

+ 2 - 1
SBansheeEngine/Include/BsManagedResource.h

@@ -10,7 +10,7 @@ namespace BansheeEngine
 	class BS_SCR_BE_EXPORT ManagedResource : public Resource
 	{
 	public:
-		void construct(MonoObject* object);
+		void construct(MonoObject* object, const HManagedResource& myHandle);
 
 		MonoObject* getManagedInstance() const { return mManagedInstance; }
 
@@ -25,6 +25,7 @@ namespace BansheeEngine
 
 		MonoObject* mManagedInstance;
 		uint32_t mManagedHandle;
+		HManagedResource mMyHandle;
 
 		/************************************************************************/
 		/* 								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 "BsMonoManager.h"
 #include "BsManagedSerializableObject.h"
+#include "BsResources.h"
 
 namespace BansheeEngine
 {
@@ -37,10 +38,12 @@ namespace BansheeEngine
 
 		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()

+ 1 - 1
SBansheeEngine/Include/BsScriptResourceManager.h

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

+ 2 - 0
SBansheeEngine/SBansheeEngine.vcxproj

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

+ 6 - 0
SBansheeEngine/SBansheeEngine.vcxproj.filters

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

+ 13 - 6
SBansheeEngine/Source/BsManagedResource.cpp

@@ -4,16 +4,17 @@
 #include "BsMonoManager.h"
 #include "BsMonoClass.h"
 #include "BsResources.h"
+#include "BsManagedResourceManager.h"
 #include "BsDebug.h"
 
 namespace BansheeEngine
 {
 	ManagedResource::ManagedResource()
-		:mManagedInstance(nullptr)
+		:Resource(false), mManagedInstance(nullptr)
 	{ }
 
 	ManagedResource::ManagedResource(MonoObject* managedInstance)
-		:mManagedInstance(nullptr)
+		:Resource(false), mManagedInstance(nullptr)
 	{
 		ManagedResourceMetaDataPtr metaData = bs_shared_ptr<ManagedResourceMetaData>();
 		mMetaData = metaData;
@@ -29,8 +30,6 @@ namespace BansheeEngine
 			LOGWRN("Cannot create managed component: " + metaData->typeNamespace + "." + metaData->typeName + " because that type doesn't exist.");
 			return;
 		}
-
-		construct(managedInstance);
 	}
 
 	HManagedResource ManagedResource::create(MonoObject* managedResource)
@@ -39,7 +38,10 @@ namespace BansheeEngine
 		newRes->_setThisPtr(newRes);
 		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()
@@ -51,10 +53,13 @@ namespace BansheeEngine
 		return newRes;
 	}
 
-	void ManagedResource::construct(MonoObject* object)
+	void ManagedResource::construct(MonoObject* object, const HManagedResource& myHandle)
 	{
 		mManagedInstance = object;
 		mManagedHandle = mono_gchandle_new(mManagedInstance, false);
+		mMyHandle = myHandle;
+
+		ManagedResourceManager::instance().registerManagedResource(mMyHandle);
 	}
 
 	void ManagedResource::destroy_internal()
@@ -66,6 +71,8 @@ namespace BansheeEngine
 			mManagedInstance = nullptr;
 			mono_gchandle_free(mManagedHandle);
 		}
+
+		ManagedResourceManager::instance().unregisterManagedResource(mMyHandle);
 	}
 
 	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 "BsScriptResourceManager.h"
 #include "BsScriptGameObjectManager.h"
+#include "BsManagedResourceManager.h"
+#include "BsScriptManager.h"
 
 // DEBUG ONLY
 #include "BsScriptSceneObject.h"
@@ -39,6 +41,7 @@ namespace BansheeEngine
 		// DEBUG ONLY
 		mono_add_internal_call("BansheeEngine.Program::UnitTest1_GameObjectClone", &unitTest1_GameObjectClone);
 
+		ManagedResourceManager::startUp();
 		RuntimeScriptObjects::startUp();
 		ScriptResourceManager::startUp();
 		ScriptGameObjectManager::startUp();
@@ -52,6 +55,8 @@ namespace BansheeEngine
 
 	extern "C" BS_SCR_BE_EXPORT void unloadPlugin()
 	{
+		ManagedResourceManager::shutDown();
+		ScriptManager::instance().destroy();
 		ScriptGameObjectManager::shutDown();
 		ScriptResourceManager::shutDown();
 		RuntimeScriptObjects::shutDown();

+ 7 - 6
SBansheeEngine/Source/BsScriptManagedResource.cpp

@@ -32,12 +32,13 @@ namespace BansheeEngine
 	{
 		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);
 	}
 

+ 4 - 6
SBansheeEngine/Source/BsScriptResourceManager.cpp

@@ -74,23 +74,21 @@ namespace BansheeEngine
 
 	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)
 	{
-		return static_cast<ScriptSpriteTexture*>(getScriptResource(resourceHandle));
+		return static_cast<ScriptSpriteTexture*>(getScriptResource(resourceHandle.getUUID()));
 	}
 
 	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 == "")
 			BS_EXCEPT(InvalidParametersException, "Provided resource handle has an undefined resource UUID.");