Browse Source

Optimize resource reference search so it does retrieve values of fields that have types that can not contain a resource reference
Add a RTTI field flag that notifies the reference search to skip the field
Fixed crash on shutdown that was caused by resource reference search trying to read pixels from a depth buffer on resource unload

Marko Pintera 11 years ago
parent
commit
f4f71d0292

+ 1 - 1
BansheeCore/Include/BsTextureRTTI.h

@@ -105,7 +105,7 @@ namespace BansheeEngine
 			addPlainField("mUsage", 11, &TextureRTTI::getUsage, &TextureRTTI::setUsage);
 			addPlainField("mUsage", 11, &TextureRTTI::getUsage, &TextureRTTI::setUsage);
 
 
 			addReflectablePtrArrayField("mPixelData", 12, &TextureRTTI::getPixelData, &TextureRTTI::getPixelDataArraySize, 
 			addReflectablePtrArrayField("mPixelData", 12, &TextureRTTI::getPixelData, &TextureRTTI::getPixelDataArraySize, 
-				&TextureRTTI::setPixelData, &TextureRTTI::setPixelDataArraySize);
+				&TextureRTTI::setPixelData, &TextureRTTI::setPixelDataArraySize, RTTI_Flag_SkipInReferenceSearch);
 		}
 		}
 
 
 		virtual void onDeserializationStarted(IReflectable* obj)
 		virtual void onDeserializationStarted(IReflectable* obj)

+ 6 - 0
BansheeCore/Include/BsUtility.h

@@ -43,5 +43,11 @@ namespace BansheeEngine
 		 * @see	findDependencies
 		 * @see	findDependencies
 		 */
 		 */
 		static void findResourceDependenciesInternal(IReflectable& object, bool recursive, Map<String, ResourceDependency>& dependencies);
 		static void findResourceDependenciesInternal(IReflectable& object, bool recursive, Map<String, ResourceDependency>& dependencies);
+
+		/**
+		 * @brief	Checks if the specified type (or any of its derived classes) have any IReflectable pointer or value
+		 *			types as their fields.
+		 */
+		static bool hasReflectableChildren(RTTITypeBase* type);
 	};
 	};
 }
 }

+ 57 - 20
BansheeCore/Source/BsUtility.cpp

@@ -28,6 +28,8 @@ namespace BansheeEngine
 		for (UINT32 i = 0; i < numFields; i++)
 		for (UINT32 i = 0; i < numFields; i++)
 		{
 		{
 			RTTIField* field = rtti->getField(i);
 			RTTIField* field = rtti->getField(i);
+			if ((field->getFlags() & RTTI_Flag_SkipInReferenceSearch) != 0)
+				continue;
 
 
 			if (field->isReflectableType())
 			if (field->isReflectableType())
 			{
 			{
@@ -62,47 +64,82 @@ namespace BansheeEngine
 				}
 				}
 				else if (recursive)
 				else if (recursive)
 				{
 				{
-					if (reflectableField->isArray())
+					// Optimization, no need to retrieve its value and go deeper if it has no 
+					// reflectable children that may hold the reference.
+					if (hasReflectableChildren(reflectableField->getType())) 
 					{
 					{
-						UINT32 numElements = reflectableField->getArraySize(&obj);
-						for (UINT32 j = 0; j < numElements; j++)
+						if (reflectableField->isArray())
+						{
+							UINT32 numElements = reflectableField->getArraySize(&obj);
+							for (UINT32 j = 0; j < numElements; j++)
+							{
+								IReflectable& childObj = reflectableField->getArrayValue(&obj, j);
+								findResourceDependenciesInternal(childObj, true, dependencies);
+							}
+						}
+						else
 						{
 						{
-							IReflectable& childObj = reflectableField->getArrayValue(&obj, j);
+							IReflectable& childObj = reflectableField->getValue(&obj);
 							findResourceDependenciesInternal(childObj, true, dependencies);
 							findResourceDependenciesInternal(childObj, true, dependencies);
 						}
 						}
 					}
 					}
-					else
-					{
-						IReflectable& childObj = reflectableField->getValue(&obj);
-						findResourceDependenciesInternal(childObj, true, dependencies);
-					}
 				}
 				}
 			}
 			}
 			else if (field->isReflectablePtrType() && recursive)
 			else if (field->isReflectablePtrType() && recursive)
 			{
 			{
 				RTTIReflectablePtrFieldBase* reflectablePtrField = static_cast<RTTIReflectablePtrFieldBase*>(field);
 				RTTIReflectablePtrFieldBase* reflectablePtrField = static_cast<RTTIReflectablePtrFieldBase*>(field);
 
 
-				if (reflectablePtrField->isArray())
+				// Optimization, no need to retrieve its value and go deeper if it has no 
+				// reflectable children that may hold the reference.
+				if (hasReflectableChildren(reflectablePtrField->getType()))
 				{
 				{
-					UINT32 numElements = reflectablePtrField->getArraySize(&obj);
-					for (UINT32 j = 0; j < numElements; j++)
+					if (reflectablePtrField->isArray())
+					{
+						UINT32 numElements = reflectablePtrField->getArraySize(&obj);
+						for (UINT32 j = 0; j < numElements; j++)
+						{
+							SPtr<IReflectable> childObj = reflectablePtrField->getArrayValue(&obj, j);
+
+							if (childObj != nullptr)
+								findResourceDependenciesInternal(*childObj, true, dependencies);
+						}
+					}
+					else
 					{
 					{
-						SPtr<IReflectable> childObj = reflectablePtrField->getArrayValue(&obj, j);
+						SPtr<IReflectable> childObj = reflectablePtrField->getValue(&obj);
 
 
 						if (childObj != nullptr)
 						if (childObj != nullptr)
 							findResourceDependenciesInternal(*childObj, true, dependencies);
 							findResourceDependenciesInternal(*childObj, true, dependencies);
 					}
 					}
 				}
 				}
-				else
-				{
-					SPtr<IReflectable> childObj = reflectablePtrField->getValue(&obj);
-
-					if (childObj != nullptr)
-						findResourceDependenciesInternal(*childObj, true, dependencies);
-				}
 			}
 			}
 		}
 		}
 
 
 		rtti->onSerializationEnded(&obj);
 		rtti->onSerializationEnded(&obj);
 	}
 	}
+
+	bool Utility::hasReflectableChildren(RTTITypeBase* type)
+	{
+		UINT32 numFields = type->getNumFields();
+		for (UINT32 i = 0; i < numFields; i++)
+		{
+			RTTIField* field = type->getField(i);
+			if (field->isReflectableType() || field->isReflectablePtrType())
+				return true;
+		}
+
+		const Vector<RTTITypeBase*>& derivedClasses = type->getDerivedClasses();
+		for (auto& derivedClass : derivedClasses)
+		{
+			numFields = derivedClass->getNumFields();
+			for (UINT32 i = 0; i < numFields; i++)
+			{
+				RTTIField* field = derivedClass->getField(i);
+				if (field->isReflectableType() || field->isReflectablePtrType())
+					return true;
+			}
+		}
+
+		return false;
+	}
 }
 }

+ 8 - 1
BansheeUtility/Include/BsRTTIField.h

@@ -56,7 +56,14 @@ namespace BansheeEngine
 		// it is not guaranteed the value provided is fully initialized, so you should not access any of its
 		// it is not guaranteed the value provided is fully initialized, so you should not access any of its
 		// data until deserialization is fully complete. You only need to use this flag if the RTTI system
 		// data until deserialization is fully complete. You only need to use this flag if the RTTI system
 		// complains that is has found a circular reference.
 		// complains that is has found a circular reference.
-		RTTI_Flag_WeakRef = 0x01
+		RTTI_Flag_WeakRef = 0x01,
+		// This flags signals various systems that the flagged field should not be searched when looking for 
+		// object references. This normally means the value of this field will no be retrieved during reference
+		// searches but it will likely still be retrieved during other operations (e.g. serialization).
+		// This is used as an optimization to avoid retrieving values of potentially very expensive fields that
+		// would not contribute to the reference search anyway. Whether or not a field contributes to the reference
+		// search depends on the search and should be handled on a case by case basis.
+		RTTI_Flag_SkipInReferenceSearch = 0x02
 	};
 	};
 
 
 	/**
 	/**

+ 1 - 1
BansheeUtility/Include/BsRTTIType.h

@@ -608,7 +608,7 @@ namespace BansheeEngine
 				if (currentType->getRTTIId() == getRTTIId())
 				if (currentType->getRTTIId() == getRTTIId())
 					return true;
 					return true;
 
 
-				Vector<RTTITypeBase*>& derivedClasses = currentType->getDerivedClasses();
+				const Vector<RTTITypeBase*>& derivedClasses = currentType->getDerivedClasses();
 				for (auto iter = derivedClasses.begin(); iter != derivedClasses.end(); ++iter)
 				for (auto iter = derivedClasses.begin(); iter != derivedClasses.end(); ++iter)
 					todo.push(*iter);
 					todo.push(*iter);
 			}
 			}

+ 2 - 14
TODO.txt

@@ -5,19 +5,6 @@
 When serializing Camera I cannot save the reference to RenderTexture. Make it a Resource?
 When serializing Camera I cannot save the reference to RenderTexture. Make it a Resource?
 Possibly set up automatic refresh in debug mode after initialization? As an ad-hoc unit test
 Possibly set up automatic refresh in debug mode after initialization? As an ad-hoc unit test
 
 
-Scene grid disappears and scene view doesn't work after refresh
- - This might be related to GUI issue above, as the old GUI still remains so it might be rendering above the new stuff
-
-Assembly refresh issues:
-When managed component is destroyed its ScriptComponent will only get destroyed when last reference to the managed component is lost. With assembly refresh this happens during refresh, which avoids destroying the ScriptComponent.
- - I need a way to mark GameObject as destroyed so I can then avoid backing it up and restoring its instance.
-
-New Camera gets its OnReset called twice
-
-Make sure to remove all debug logs
-
-After refresh BansheeRenderer has two extra cameras (4 new ones total, 2 old ones got deleted). 2 of the new ones have empty targets.
-
 <<<<Multi-resource saving>>>>:
 <<<<Multi-resource saving>>>>:
  - Modify Font so it doesn't contain a texture, but instead keeps a handle to it
  - Modify Font so it doesn't contain a texture, but instead keeps a handle to it
  - Register it in its meta file
  - Register it in its meta file
@@ -35,7 +22,8 @@ I can get mono errors by checking g_print calls in goutput.c
 Running embedded mono with VS attached causes managed null refs to be registered as access violations
 Running embedded mono with VS attached causes managed null refs to be registered as access violations
 
 
 Crash on GLTextureBuffer::download on shutdown (OpenGL error invalid enum)
 Crash on GLTextureBuffer::download on shutdown (OpenGL error invalid enum)
-Create a stack allocatable custom vector implementation and make getResourceDependencies and getCoreDependencies use it
+Create a stack allocatable custom vector implementation and make getResourceDependencies and getCoreDependencies use it.
+ - These methods are called often and cause allocations whenever they are.
 
 
 C# SceneView:
 C# SceneView:
 Test new Scene window and fix every issue
 Test new Scene window and fix every issue