diff options
author | chai <chaifix@163.com> | 2019-08-14 22:50:43 +0800 |
---|---|---|
committer | chai <chaifix@163.com> | 2019-08-14 22:50:43 +0800 |
commit | 15740faf9fe9fe4be08965098bbf2947e096aeeb (patch) | |
tree | a730ec236656cc8cab5b13f088adfaed6bb218fb /Runtime/BaseClasses/BaseObject.h |
Diffstat (limited to 'Runtime/BaseClasses/BaseObject.h')
-rw-r--r-- | Runtime/BaseClasses/BaseObject.h | 1162 |
1 files changed, 1162 insertions, 0 deletions
diff --git a/Runtime/BaseClasses/BaseObject.h b/Runtime/BaseClasses/BaseObject.h new file mode 100644 index 0000000..52613be --- /dev/null +++ b/Runtime/BaseClasses/BaseObject.h @@ -0,0 +1,1162 @@ +#ifndef BASEOBJECT_H +#define BASEOBJECT_H + +#include "Runtime/Utilities/LogAssert.h" +#include "Runtime/Serialize/SerializeUtility.h" +#include "Runtime/Serialize/SerializationMetaFlags.h" +#include "Configuration/UnityConfigure.h" +#include "Runtime/Utilities/Prefetch.h" +#include "Runtime/Scripting/Backend/ScriptingTypes.h" +#include "Runtime/Misc/Allocator.h" +#include "Runtime/Utilities/dynamic_array.h" + +#include <string> +#include <vector> + +#include "Runtime/Utilities/dense_hash_map.h" +#include "Runtime/Utilities/HashFunctions.h" + +#include "Runtime/BaseClasses/ClassIDs.h" + +class ProxyTransfer; +class SafeBinaryRead; +template<bool kSwap> +class StreamedBinaryRead; +template<bool kSwap> +class StreamedBinaryWrite; +class RemapPPtrTransfer; +class TypeTree; +class Object; +struct EventEntry; +#if SUPPORT_TEXT_SERIALIZATION +class YAMLRead; +class YAMLWrite; +#endif + +#include "ObjectDefines.h" +#include <string> +#include <typeinfo> + +//#define DefineClassID( x, classID ) +//#define ClassID( x ) + +// Every non-abstract class that is derived from object has to place this inside the class Declaration +// (REGISTER_DERIVED_CLASS (Foo, Object)) + +// Every abstract class that is derived from object has to place this inside the class Declaration +// (REGISTER_DERIVED_ABSTRACT_CLASS (Foo, Object)) + +//In the cpp file of every object derived class you have to place eg. IMPLEMENT_CLASS (Foo) +//#define IMPLEMENT_CLASS(x) +// or IMPLEMENT_CLASS_HAS_INIT (x) which will call the static class Function InitializeClass (); on startup. + +using std::string; + +template<class T> +class PPtr +{ + SInt32 m_InstanceID; + #if !UNITY_RELEASE + mutable T* m_DEBUGPtr; + #endif + + protected: + + inline void AssignObject (const Object* o); + + private: + static string s_TypeString; + + public: + + static const char* GetTypeString (); + static bool IsAnimationChannel () { return false; } + static bool MightContainPPtr () { return true; } + static bool AllowTransferOptimization () { return false; } + + template<class TransferFunction> + void Transfer (TransferFunction& transfer); + + // Assignment + explicit PPtr (int instanceID) + { + m_InstanceID = instanceID; + #if !UNITY_RELEASE + m_DEBUGPtr = NULL; + #endif + } + PPtr (const T* o) { AssignObject (o); } + PPtr (const PPtr<T>& o) + { + m_InstanceID = o.m_InstanceID; + #if !UNITY_RELEASE + m_DEBUGPtr = NULL; + #endif + } + + PPtr () + { + #if !UNITY_RELEASE + m_DEBUGPtr = NULL; + #endif + m_InstanceID = 0; + } + + PPtr& operator = (const T* o) { AssignObject (o); return *this; } + PPtr& operator = (const PPtr<T>& o) + { + #if !UNITY_RELEASE + m_DEBUGPtr = NULL; + #endif + m_InstanceID = o.m_InstanceID; return *this; + } + + void SetInstanceID (int instanceID) { m_InstanceID = instanceID; } + int GetInstanceID ()const { return m_InstanceID; } + + // Comparison + bool operator < (const PPtr& p)const { return m_InstanceID < p.m_InstanceID; } + bool operator == (const PPtr& p)const { return m_InstanceID == p.m_InstanceID; } + bool operator != (const PPtr& p)const { return m_InstanceID != p.m_InstanceID; } + + // MSVC gets confused whether it should use operator bool(), or operator T* with implicit + // comparison to NULL. So we add explicit functions and use them instead. + bool IsNull() const; + bool IsValid() const; + + operator T* () const; + T* operator -> () const; + T& operator * () const; +}; + +template<class T> +class ImmediatePtr +{ + mutable intptr_t m_Ptr; + #if !UNITY_RELEASE + mutable T* m_DEBUGPtr; + #endif + + void AssignInstanceID (int instanceID) + { + AssertIf (instanceID & 1); m_Ptr = instanceID | 1; AssertIf ((m_Ptr & 1) == 0); + #if !UNITY_RELEASE + m_DEBUGPtr = NULL; + #endif + } + void AssignObject (const T* o) + { + m_Ptr = (intptr_t)o; AssertIf (m_Ptr & 1); + #if !UNITY_RELEASE + m_DEBUGPtr = const_cast<T*>(o); + #endif + } + void Load () const + { + AssertIf ((m_Ptr & 1) == 0); + T* loaded = PPtr<T> (m_Ptr & (~1)); + m_Ptr = (intptr_t)(loaded); + AssertIf (m_Ptr & 1); + #if !UNITY_RELEASE + m_DEBUGPtr = loaded; + #endif + } + + inline T* GetPtr () const + { + if ((m_Ptr & 1) == 0) + { + return (T*)(m_Ptr); + } + else + { + Load (); + return (T*)(m_Ptr); + } + } + + static string s_TypeString; + + public: + + bool IsLoaded () const; + + static const char* GetTypeString (); + static bool IsAnimationChannel () { return false; } + static bool MightContainPPtr () { return true; } + static bool AllowTransferOptimization () { return false; } + + template<class TransferFunction> + void Transfer (TransferFunction& transfer); + + // Assignment + ImmediatePtr (const T* o) { AssignObject (o); } + ImmediatePtr (const ImmediatePtr<T>& o) { m_Ptr = o.m_Ptr; } + ImmediatePtr () { m_Ptr = 0; } + + ImmediatePtr& operator = (const T* o) { AssignObject (o); return *this; } + + void SetInstanceID (int instanceID) { AssignInstanceID (instanceID); } + int GetInstanceID ()const + { + if ((m_Ptr & 1) == 0 && m_Ptr != 0) + { + T* o = (T*)(m_Ptr); + SInt32 instanceID = o->GetInstanceID (); + AssertIf (instanceID & 1); + return instanceID; + } + else + return m_Ptr & (~1); + } + + inline bool operator == (const T* p)const { return GetPtr () == p; } + inline bool operator != (const T* p)const { return GetPtr () != p; } + + inline operator T* () const { return GetPtr (); } + inline T* operator -> () const { T* o = GetPtr (); AssertIf (o == NULL); return o; } + inline T& operator * () const { T* o = GetPtr (); AssertIf (o == NULL); ANALYSIS_ASSUME(o); return *o; } +}; + +template<typename T> class PtrToType; +template<typename T> class PtrToType<T*> +{ +public: + typedef T value_type; +}; + +template<class T, class U> +T dynamic_pptr_cast (U* ptr) +{ + typedef typename PtrToType<T>::value_type Type; + T castedPtr = (T)(ptr); + if (castedPtr && castedPtr->IsDerivedFrom ( Type::GetClassIDStatic ())) + return castedPtr; + else + return NULL; +} + +template<class T, class U> +T dynamic_pptr_cast (const PPtr<U>& ptr) +{ + U* o = ptr; + return dynamic_pptr_cast<T> (o); +} + +template<class T> inline +T dynamic_instanceID_cast (int instanceID) +{ + Object* o = PPtr<Object> (instanceID); + return dynamic_pptr_cast<T> (o); +} + +template<class T, class U> +PPtr<T> assert_pptr_cast (const PPtr<U>& ptr) +{ + #if DEBUGMODE + U* u = ptr; + AssertIf (dynamic_pptr_cast<U*> (u) == NULL && u != NULL); + #endif + return PPtr<T> (ptr.GetInstanceID ()); +} + +// Enables boost::mem_fn to use PPtr properly, needed for boost::bind +template<typename T> inline T * get_pointer(PPtr<T> const & p) +{ + return p; +} + + +enum ObjectCreationMode +{ + // Create the object from the main thread in a perfectly normal way + kCreateObjectDefault = 0, + // Create the object from another thread. Might assign an instance ID but will not register with IDToPointer map. + // Objects created like this, need to call, AwakeFromLoadThraded, and Object::RegisterInstanceID and AwakeFromLoad (kDidLoadThreaded); from the main thread + kCreateObjectFromNonMainThread = 1, + // Create the object and register the instance id but do not lock the object + // creation mutex because the code calling it already called LockObjectCreation mutex. + kCreateObjectDefaultNoLock = 2 +}; + + +enum AwakeFromLoadMode +{ + // This is the default, usually called from the inspector or various serialization methods + kDefaultAwakeFromLoad = 0, + // The object was loaded from disk + kDidLoadFromDisk = 1 << 0, + // The object was loaded from a loading thread (in almost all cases through loading from disk asynchronously) + kDidLoadThreaded = 1 << 1, + // Object was instantiated and is now gettings it's first Awake function or it was created from code and gets the Awake function called + kInstantiateOrCreateFromCodeAwakeFromLoad = 1 << 2, + // GameObject was made active or a component was added to an active game object + kActivateAwakeFromLoad = 1 << 3, + + kDefaultAwakeFromLoadInvalid = -1 +}; + +class BaseAllocator; + +enum ObjectDeleteMode +{ + kUnknownMode = 0 +}; + +class EXPORT_COREMODULE Object +{ + protected: + virtual ~Object (); + + public: + + Object (MemLabelId label, ObjectCreationMode mode); + + + /// By default the destructor might get executed on another thread. + /// This lets us distribute large level unloads to another thread and thus avoid hiccups. + /// Some classes need to deallocate resources on the main thread. + /// You can implement this function to delete the resources. + /// + /// The destructor will still be called in this case, thus you need to ensure that values are set to NULL. + /// MainThreadCleanup is only called if the deallocations are done on another thread. + /// Thus the destructor needs to handle the case where it is not called correctly. + /// + /// If you override the function return true, since it indicates that the class requires the function to be called. + /// SA: DoesClassRequireMainThreadDeallocation + virtual bool MainThreadCleanup (); + + /// To destroy objects use delete_object instead of delete operator + /// The default way to destroy objects is using the DestroyObject Function, which also destroys the object from it's file + /// Must be protected by LockObjectCreation / UnlockObjectCreation + friend void delete_object_internal_step1 (Object* p); + friend void delete_object_internal_step2 (Object* p); + + /// AwakeFromLoad is called after an object was read using Transfer (Either from disk or a vector) + /// This means it is called after the inspector has been updated, after it is loaded from disk, after a prefab has been modified or + /// the animation system has changed values behind your back. + virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode) + { + #if !UNITY_RELEASE + m_AwakeCalled = 1; + + if( awakeMode & kDidLoadThreaded ) + m_AwakeDidLoadThreadedCalled = 1; + #endif + } + + virtual void AwakeFromLoadThreaded () + { + #if !UNITY_RELEASE + m_AwakeThreadedCalled = 1; + m_AwakeCalled = 0; + m_AwakeDidLoadThreadedCalled = 0; + #endif + } + + /// For Subclasses: Makes sure that persistent variables are correct and if not corrects them + /// It is called after Prefab propagation, SafeBinaryRead and PropertyEditor changes. + /// It is called before AwakeFromLoad + virtual void CheckConsistency () { } + + /// Override Reset in order to setup default values for the Object + + /// The difference between setting up default values in the constructor + /// and Reset is that Reset is only called when the editor creates a new object + /// or when the object uses SerializeSafeBinary read. + /// Thus Reset can be used as a performance optimization for touching variables which are serialized only once during load. + /// * All variables that are serialized and Reset in the Reset function, do not have to be initialized in the constructor* + /// Reset functions might get called from different threads during loading, thus they may not derefence other objects, in that case use SmartReset. + /// You can always rely on that AwakeFromLoad is called after Reset has been called. + virtual void Reset () + { + #if !UNITY_RELEASE + m_ResetCalled = 1; + m_AwakeCalled = 0; + m_AwakeThreadedCalled = 0; + m_AwakeDidLoadThreadedCalled = 0; + #endif + } + + // Smart Reset is called when Reset is selected or when AddComponent is called and a new ScriptableObject is created. + // If you want to for example adjust a collider bounding volume by the renderers mesh, use SmartReset, you can not use Reset for this! + virtual void SmartReset () + { + #if !UNITY_RELEASE + m_ResetCalled = 1; + m_AwakeCalled = 0; + m_AwakeThreadedCalled = 0; + m_AwakeDidLoadThreadedCalled = 0; + #endif + } + +#if !UNITY_RELEASE + // use it to check AwakeFromLoad/AwakeFromLoadThreaded/Reset/SmartReset were correctly called + void CheckCorrectAwakeUsage(); + + // hacks to set debug flags in cases you REALLY know what you are doing + + // call when you don't want to Reset in case of object fully inited and don't need to be reset to default state + // e.g. if you de-serialize object - you don't need reset + inline void HackSetResetWasCalled() { m_ResetCalled = 1; } + + // call when AwakeFromLoad has some side-effects so you need to postpone that call for indefinite time + // e.g. AudioClip will try to load sound in AwakeFromLoad, so you better do this only when needed + inline void HackSetAwakeWasCalled() { m_AwakeCalled = 1; } + + // same as HackSetAwakeWasCalled but for Awake with kDidLoadThreaded param + inline void HackSetAwakeDidLoadThreadedWasCalled () { m_AwakeDidLoadThreadedCalled = true; } + #else + inline void HackSetResetWasCalled() {} + inline void HackSetAwakeWasCalled() {} + inline void HackSetAwakeDidLoadThreadedWasCalled() {} + #endif + + /// Get and set the name + virtual char const* GetName () const { return ""; }; + virtual void SetName (char const* /*name*/) { } + void SetNameCpp (const std::string& name) { SetName(name.c_str()); } + + #if UNITY_EDITOR + /// Return true if you want the inspector to automatically refresh without SetDirty being called. + virtual bool HasDebugmodeAutoRefreshInspector () { return false; } + virtual void WarnInstantiateDisallowed () {} + #endif + + /// Returns the classID of the class + static int GetClassIDStatic () { return ClassID (Object); } + + // Is the class sealed (No other class can inherit from it) + // A sealed class can perform a GetComponent call faster, + // since it can compare the ClassID directly instead of using the RTTI system. + static bool IsSealedClass () { return false; } + + /// Returns true if the class is abstract + static bool IsAbstract () { return true; } + + /// Creates an object of type classID. + /// if instanceID is 0 a unique id will be generated if its non 0 the object will have the specified instanceID + static Object* Produce (int classID, int instanceID = 0, MemLabelId = kMemBaseObject, ObjectCreationMode mode = kCreateObjectDefault); + + + // Static initializa and destroy for BaseObject + static void StaticInitialize(); + static void StaticDestroy(); + + /// Registers instance id with IDToPointerMap + /// useful for thread loading with delayed activation from main thread + /// Can only be called from main thead + static void RegisterInstanceID (Object* obj); + static void RegisterInstanceIDNoLock (Object* obj); + + /// Allocates new instanceID and registers it with IDToPointerMap + /// Can only be called from main thead + static Object* AllocateAndAssignInstanceID (Object* obj); + static Object* AllocateAndAssignInstanceIDNoLock (Object* obj); + + #if UNITY_EDITOR + virtual void CloneAdditionalEditorProperties (Object& /*source*/) { } + + /// Can assign variable allows you to do additional type checking when assiging a variable + /// in the property inspector. + /// Eg. MonoBehaviours checks if a monobehaviour can be assigned based on the actual Mono class + virtual bool CanAssignMonoVariable (const char* /*property*/, Object* /*object*/) { return false; } + + #endif + + virtual bool ShouldIgnoreInGarbageDependencyTracking () { return false; } + + /// Gets the class ID + ClassIDType GetClassID () const { Assert(m_CachedClassID != 0); return (ClassIDType)m_CachedClassID; } + /// Gets the instance ID + int GetInstanceID () const { AssertIf(m_InstanceID == 0); return m_InstanceID; } + bool IsInstanceIDCreated () const { return m_InstanceID != 0; } + + /// Is this instance derived from compareClassID + bool IsDerivedFrom (int compareClassID)const { return IsDerivedFromClassID (GetClassID (), compareClassID); } + + #if UNITY_EDITOR + + /// Has this object been synced with the PersistentManager + bool IsPersistentDirty () const { return m_DirtyIndex != 0; } + + void SetPersistentDirtyIndex (UInt32 dirtyIndex); + UInt32 GetPersistentDirtyIndex () { return m_DirtyIndex; } + + ////@TODO: Rename this to SetPersistentDirty + + /// Whenever variables that are being serialized in Transfer change, SetDirty () should be called + /// This will allow tracking of objects that have changed since the last saving to disk or over the network + void SetDirty (); + + /// This method can be called if you need to unload an object from memory even if it's dirty. + void ClearPersistentDirty (); + + // Callback support for callbacks when SetDirty is called + typedef void ObjectDirtyCallbackFunction (Object* ptr); + static void RegisterDirtyCallback (ObjectDirtyCallbackFunction* callback); + static ObjectDirtyCallbackFunction* GetDirtyCallback (); + + void SetFileIDHint (LocalIdentifierInFileType hint) { m_FileIDHint = hint; } + LocalIdentifierInFileType GetFileIDHint () const { return m_FileIDHint; } + + #else + void SetDirty () { } + void ClearPersistentDirty () { } + + #endif + + + // The name of the class + const std::string& GetClassName () const; + + enum + { + kHideInHierarchy = 1 << 0, + kHideInspector = 1 << 1, + kDontSave = 1 << 2, + kNotEditable = 1 << 3, + kHideAndDontSave = kDontSave | kHideInHierarchy | kNotEditable + }; + + int GetHideFlags () const { return m_HideFlags; } + bool TestHideFlag (int mask) const { return (m_HideFlags & mask) == mask; } + bool TestHideFlagAny (int mask) const { return (m_HideFlags & mask) != 0; } + + virtual void SetHideFlags (int flags) { m_HideFlags = flags; } + void SetHideFlagsObjectOnly (int flags) { Assert(flags < (1 << kHideFlagsBits)); m_HideFlags = flags; } + + /// You must document all usage here in order to provide clear overview and avoid overlaps + /// - Transform root calculation for animation component, when binding animation states (Runtime only) + void SetTemporaryFlags (int flags) { Assert(flags < (1 << kTemporaryFlagsBits)); m_TemporaryFlags = flags; } + int GetTemporaryFlags () const { return m_TemporaryFlags; } +#if UNITY_WINRT + /// Used by WinRT's GarbageCollectSharedAssets + void SetTemporaryUnusedAssetsFlags (int flags) { m_TemporaryUnusedAssetsFlags = flags; } + int GetTemporaryUnusedAssetsFlags () const { return m_TemporaryUnusedAssetsFlags; } +#endif + +#if ENABLE_SCRIPTING + int GetGCHandle () const { return m_MonoReference; } +#endif + + /// Overall memory allocated for this object. Should calculate any memory allocated by other subystems as well. + /// For example if OpenGL allocates memory for a texture it must return how much memory we "think" OpenGL will allocate for the texture. + virtual int GetRuntimeMemorySize () const; + + /// Is this object persistent? + bool IsPersistent () const { return m_IsPersistent; } + + typedef InstanceIdToObjectPtrHashMap IDToPointerMap; + + /// How many objects are there in memory? + static IDToPointerMap::size_type GetLoadedObjectCount () { return ms_IDToPointer->size (); } + + // Finds the pointer to the object referenced by instanceID (NULL if none found in memory) + static Object* IDToPointer (int inInstanceID); + static Object* IDToPointerThreadSafe (int inInstanceID); + + /// This function may not be called unless you use LockObjectCreation / UnlockObjectCreation from another thread first... + /// If you don't know 100% what you are doing use: IDToPointerThreadSafe instead + static Object* IDToPointerNoThreadCheck (int inInstanceID); + + /// Finds out if classID is derived from compareClassID + static bool IsDerivedFromClassID (int classID, int derivedFromClassID); + + /// Returns the super Class ID of classID. + /// if classID doesnt have any super Class it will return ClassID (Object) + static int GetSuperClassID (int classID); + + /// Returns all classIDs that are derived from ClassID + static void FindAllDerivedClasses (int classID, std::vector<SInt32>* allDerivedClasses, bool returnOnlyNonAbstractClasses = true); + /// Returns how many objects are derived from classID + /// If allDerivedObjects != NULL, adds all derived object instanceIDs to the container + static int FindAllDerivedObjects (int classID, std::vector<SInt32>* derivedObjects, bool sorted = false); + + static int FindObjectsOfType (int classID, std::vector<Object*>* derivedObjects, bool sorted = false); + template<class T> + static int FindObjectsOfType (std::vector<T*>* derivedObjects) + { + std::vector<Object*>* casted = reinterpret_cast<std::vector<Object*>*> (derivedObjects); + return FindObjectsOfType (T::GetClassIDStatic (), casted); + } + + static int FindObjectsOfType (int classID, dynamic_array<Object*>* derivedObjects, bool sorted = false); + template<class T> + static int FindObjectsOfType (dynamic_array<T*>* derivedObjects) + { + dynamic_array<Object*>* casted = reinterpret_cast<dynamic_array<Object*>*> (derivedObjects); + return FindObjectsOfType (T::GetClassIDStatic (), casted); + } + + template<class T> + static int FindObjectsOfTypeSorted (std::vector<T*>* derivedObjects) + { + std::vector<Object*>* casted = reinterpret_cast<std::vector<Object*>*> (derivedObjects); + return FindObjectsOfType (T::GetClassIDStatic (), casted, true); + } + + + + /// Get the class name from the classID + static const std::string& ClassIDToString (int classID); + /// Get the classID from the class name, returns -1 if no classID was found + static int StringToClassID (const std::string& classString); + static int StringToClassIDCaseInsensitive (const std::string& classString); + static int StringToClassID (const char* classString); + + /// Callback support for callbacks when an object is destroyed + typedef void ObjectDestroyCallbackFunction (int instanceID); + static void RegisterDestroyedCallback (ObjectDestroyCallbackFunction* callback); + + /// Sets up the rtti for all classes that are derived from Object and + /// use the macro IMPLEMENT_CLASS or IMPLEMENT_CLASS_HAS_INIT + /// Calls the static function InitializeClass on every class that used + /// IMPLEMENT_CLASS_HAS_INIT instead of IMPLEMENT_CLASS + static void InitializeAllClasses (); + static void CallInitializeClassEarly(); + static void CallInitializeClass(); + static void CallPostInitializeClass(); + + static void CleanupAllClasses (); + + /// Checks if an array of instance id's are loaded. + /// If an instanceID is loaded it is set to 0. + static void CheckInstanceIDsLoaded (SInt32* instanceIDs, int size); + + + typedef Object* FactoryFunction (MemLabelId label, ObjectCreationMode mode); + struct RTTI + { + RTTI* base;// super rtti class + Object::FactoryFunction* factory;// the factory function of the class + int classID;// the class ID of the class + std::string className;// the name of the class + int size;// sizeof (Class) + bool isAbstract;// is the class Abstract? + }; + + /// Returns the RTTI information for a classID + static RTTI* ClassIDToRTTI (int classID); + + MemLabelId GetMemoryLabel () const; + + static void DoneLoadingManagers (); + + static IDToPointerMap& GetIDToPointerMapInternal () { return *ms_IDToPointer; } + + virtual int GetClassIDVirtualInternal () const { AssertString("Bad"); return ClassID(Object); } + void PreCleanupObject (); + + // Generic Event callback support. + typedef void EventCallback (void* userData, void* sender, int eventType); + + void AddEvent (EventCallback* callback, void* userData); + void RemoveEvent (EventCallback* callback, void* userData); + bool HasEvent (EventCallback* callback, const void* userData) const; + void InvokeEvent (int eventType); + +private: + + static UInt32* ms_IsDerivedFromBitMap; + static unsigned ms_MaxClassID; + static IDToPointerMap* ms_IDToPointer; + static UInt32* ms_ClassIDMask; + static UInt32* ms_ClassIsDerivedFrom; + + SInt32 m_InstanceID; + + enum Bits + { + kMemLabelBits = 13, + kIsRootOwnerBits = 1, + kTemporaryFlagsBits = 1, + kHideFlagsBits = 4, + kIsPersistentBits = 1, + kCachedClassIDBits = 12 + }; + + + UInt32 m_MemLabel : kMemLabelBits; // 13 bits + UInt32 m_IsRootOwner : kIsRootOwnerBits; // 14 bits + UInt32 m_TemporaryFlags: kTemporaryFlagsBits; // 15 bits + UInt32 m_HideFlags : kHideFlagsBits; // 19 bits + UInt32 m_IsPersistent : kIsPersistentBits; // 20 bits + UInt32 m_CachedClassID : kCachedClassIDBits; // 32 bits + + EventEntry* m_EventIndex; + + + #if !UNITY_RELEASE + UInt32 m_DEBUGCLASSID:16; + UInt32 m_AwakeCalled:1; + UInt32 m_ResetCalled:1; + UInt32 m_AwakeThreadedCalled:1; + UInt32 m_AwakeDidLoadThreadedCalled:1; + #endif + + #if ENABLE_MONO + UInt32 m_MonoReference; + #elif UNITY_FLASH + SInt32 m_MonoReference; + #elif UNITY_WINRT + SInt32 m_MonoReference; + UInt32 m_TemporaryUnusedAssetsFlags; + #endif + #if ENABLE_SCRIPTING + ScriptingObjectPtr m_ScriptingObjectPointer; + #endif + + #if UNITY_EDITOR + UInt32 m_DirtyIndex; + LocalIdentifierInFileType m_FileIDHint; + #endif + + public: + + #if ENABLE_SCRIPTING + void SetupWeakHandle (); + bool RevertWeakHandle (); + + void SetCachedScriptingObject (ScriptingObjectPtr cachedPointer); + ScriptingObjectPtr GetCachedScriptingObject () { return m_ScriptingObjectPointer; } + #endif + +private: + + static void CalculateCachedClassID (Object* obj); + static void InsertObjectInMap (Object* obj); + + void SetIsPersistent (bool p); + + Object (const Object& o); // Disallow copy constructor + Object& operator = (const Object& o); // Disallow assignment + + void SetInstanceID (int inID) { m_InstanceID = inID; } + + protected: + + static void RegisterClass (int inClassID, int inBaseClass, const std::string& inName, int size, FactoryFunction* inFunc, bool isAbstract); + + static Object* PRODUCE (MemLabelId /*label*/, ObjectCreationMode /*mode*/) { AssertString ("Can't produce abstract class"); return NULL; } + + template<class TransferFunction> + void Transfer (TransferFunction& transfer); + + public: + + /// Returns whether or not the class needs one typetree per object, not per classID + /// Having a per object typetree makes serialization considerably slower because safeBinaryTransfer is always used + /// Since no TypeTree can be generated before reading the object. + /// The File size will also increase because the typetree is not shared among the same classes. + /// It is used for example in PythonBehaviour + /// Also for one class you have to always returns true or always false. + virtual bool GetNeedsPerObjectTypeTree () const { return false; } + + // Sets up RTTI, the object factory (Produce) and string <-> classID + // conversion. RegisterClass() has to be called once for every class + // derived from object, before any Objects are allocated + static void RegisterClass (); + + // Required by serialization + virtual void VirtualRedirectTransfer (StreamedBinaryWrite<false>&){ AssertString ("Serialization not implemented for type " + Object::ClassIDToString (GetClassID ())); } + virtual void VirtualRedirectTransfer (StreamedBinaryRead<false>&) { AssertString ("Serialization not implemented for type " + Object::ClassIDToString (GetClassID ())); } + virtual void VirtualRedirectTransfer (RemapPPtrTransfer&) { AssertString ("Serialization not implemented for type " + Object::ClassIDToString (GetClassID ())); } + virtual void VirtualRedirectTransfer (ProxyTransfer&) { AssertString ("Serialization not implemented for type " + Object::ClassIDToString (GetClassID ())); } +#if SUPPORT_SERIALIZED_TYPETREES + virtual void VirtualRedirectTransfer (StreamedBinaryRead<true>&) { AssertString ("Serialization not implemented for type " + Object::ClassIDToString (GetClassID ())); } + virtual void VirtualRedirectTransfer (SafeBinaryRead&) { AssertString ("Serialization not implemented for type " + Object::ClassIDToString (GetClassID ())); } + virtual void VirtualRedirectTransfer (StreamedBinaryWrite<true>&){ AssertString ("Serialization not implemented for type " + Object::ClassIDToString (GetClassID ())); } +#endif +#if SUPPORT_TEXT_SERIALIZATION + virtual void VirtualRedirectTransfer (YAMLRead&) { AssertString ("Serialization not implemented for type " + Object::ClassIDToString (GetClassID ())); } + virtual void VirtualRedirectTransfer (YAMLWrite&) { AssertString ("Serialization not implemented for type " + Object::ClassIDToString (GetClassID ())); } + virtual void VirtualStrippedRedirectTransfer (YAMLWrite& t) { VirtualRedirectTransfer(t); } +#endif +#if ENABLE_SERIALIZATION_BY_CODEGENERATION + virtual void DoLivenessCheck (RemapPPtrTransfer&) { AssertString ("DoLivenessCheck not implemented for type " + Object::ClassIDToString (GetClassID ())); } +#endif + static const char* GetClassStringStatic (){ return "Object"; } + static const char* GetPPtrTypeString (){ return "PPtr<Object>"; } + + friend class PersistentManager; + friend class SerializedFile; +}; + +struct LocalSerializedObjectIdentifier +{ + SInt32 localSerializedFileIndex; + #if LOCAL_IDENTIFIER_IN_FILE_SIZE == 64 + UInt64 localIdentifierInFile; + #else + SInt32 localIdentifierInFile; + #endif + + LocalSerializedObjectIdentifier() + { + localIdentifierInFile = 0; + localSerializedFileIndex = 0; + } +}; + +typedef void InstanceIDResolveCallback (SInt32 id, LocalSerializedObjectIdentifier& localIdentifier, void* context); +void SetInstanceIDResolveCallback (InstanceIDResolveCallback* callback, const void* context = NULL); + +void EXPORT_COREMODULE InstanceIDToLocalSerializedObjectIdentifier (SInt32 id, LocalSerializedObjectIdentifier& localIdentifier); +void EXPORT_COREMODULE LocalSerializedObjectIdentifierToInstanceID (const LocalSerializedObjectIdentifier& fileID, SInt32& memoryID); +EXPORT_COREMODULE Object* ReadObjectFromPersistentManager (int instanceID); + +#if THREADED_LOADING +EXPORT_COREMODULE Object* InstanceIDToObjectThreadSafe (int instanceID); +#else +# define InstanceIDToObjectThreadSafe PPtr<Object> +#endif + +// This is used by the build game process. When writing for game release +// we want to null all pptrs that can't be loaded anymore. +// And when building default resources (culls all external references) +enum { kWriteNULLWhenNotLoaded = 1 << 0, kConstrainedExternalReferences = 1 << 1 }; +void SetSerializeWritePPtrFlags (int flags, const std::set<string>& paths); + +void EXPORT_COREMODULE SetDisableImmediateDestruction (bool disable); +bool EXPORT_COREMODULE GetDisableImmediateDestruction (); + + +/// Returns if the object can possibly be loaded or is already in memory, without actually performing the loading. +bool IsObjectAvailable (int instanceID); + +//Implementation +#if UNITY_RELEASE +# if UNITY_PS3 + __attribute__((always_inline)) inline Object* Object::IDToPointer (int inInstanceID) +#else + inline Object* Object::IDToPointer (int inInstanceID) +# endif +{ + if( !ms_IDToPointer ) return NULL; + IDToPointerMap::const_iterator i = ms_IDToPointer->find (inInstanceID); + if (i != ms_IDToPointer->end ()) + return i->second; + else + return NULL; +} +#endif + +template<class T> +inline void PPtr<T>::AssignObject (const Object* o) +{ + if (o == NULL) + m_InstanceID = 0; + else + m_InstanceID = o->GetInstanceID (); + #if !UNITY_RELEASE + m_DEBUGPtr = (T*) (o); + #endif +} + +template<class T> inline +PPtr<T>::operator T* () const +{ + if (GetInstanceID () == 0) + return NULL; + + Object* temp = Object::IDToPointer (GetInstanceID ()); + if (temp == NULL) + temp = ReadObjectFromPersistentManager (GetInstanceID ()); + + #if !UNITY_RELEASE + m_DEBUGPtr = (T*) (temp); + #endif + + #if DEBUGMODE || UNITY_EDITOR + T* casted = dynamic_pptr_cast<T*> (temp); + if (casted == temp) + return casted; + else + { + ErrorStringObject ("PPtr cast failed when dereferencing! Casting from " + temp->GetClassName () + " to " + T::GetClassStringStatic () + "!", temp); + return casted; + } + #else + return static_cast<T*> (temp); + #endif +} + +template<class T> inline +T* PPtr<T>::operator -> () const +{ + Object* temp = Object::IDToPointer (GetInstanceID ()); + if (temp == NULL) + temp = ReadObjectFromPersistentManager (GetInstanceID ()); + + #if !UNITY_RELEASE + m_DEBUGPtr = (T*) (temp); + #endif + + #if DEBUGMODE || !GAMERELEASE + T* casted = dynamic_pptr_cast<T*> (temp); + if (casted != NULL) + return casted; + else + { + if (temp != NULL) + { + ErrorStringObject ("PPtr cast failed when dereferencing! Casting from " + temp->GetClassName () + " to " + T::GetClassStringStatic () + "!", temp); + } + else + { + ErrorString ("Dereferencing NULL PPtr!"); + } + return casted; + } + #else + return static_cast<T*> (temp); + #endif +} + +template<class T> inline +T& PPtr<T>::operator * () const +{ + Object* temp = Object::IDToPointer (GetInstanceID ()); + if (temp == NULL) + temp = ReadObjectFromPersistentManager (GetInstanceID ()); + + #if !UNITY_RELEASE + m_DEBUGPtr = (T*) (temp); + #endif + + #if DEBUGMODE || !GAMERELEASE + T* casted = dynamic_pptr_cast<T*> (temp); + if (casted != NULL) + return *casted; + else + { + if (temp != NULL) + { + ErrorStringObject ("PPtr cast failed when dereferencing! Casting from " + temp->GetClassName () + " to " + T::GetClassStringStatic () + "!", temp); + } + else + { + ErrorString ("Dereferencing NULL PPtr!"); + } + ANALYSIS_ASSUME(casted); + return *casted; + } + #else + return *static_cast<T*> (temp); + #endif +} + +template<class T> inline +bool PPtr<T>::IsNull() const +{ + T* casted = *this; + return casted == NULL; +} + +template<class T> inline +bool PPtr<T>::IsValid() const +{ + T* casted = *this; + return casted != NULL; +} + +template<class T> +string PPtr<T>::s_TypeString; + +template<class T> inline +const char* PPtr<T>::GetTypeString () +{ + return T::GetPPtrTypeString (); +} + +template<class T> +template<class TransferFunction> inline +void PPtr<T>::Transfer (TransferFunction& transfer) +{ + LocalSerializedObjectIdentifier localIdentifier; + + if (transfer.NeedsInstanceIDRemapping ()) + { + AssertIf (!transfer.IsWriting () && !transfer.IsReading ()); + + if (transfer.IsReading ()) + { + transfer.Transfer (localIdentifier.localSerializedFileIndex, "m_FileID", kHideInEditorMask); + transfer.Transfer (localIdentifier.localIdentifierInFile, "m_PathID", kHideInEditorMask); + LocalSerializedObjectIdentifierToInstanceID (localIdentifier, m_InstanceID); + } + else if (transfer.IsWriting ()) + { + InstanceIDToLocalSerializedObjectIdentifier (m_InstanceID, localIdentifier); + transfer.Transfer (localIdentifier.localSerializedFileIndex, "m_FileID", kHideInEditorMask); + transfer.Transfer (localIdentifier.localIdentifierInFile, "m_PathID", kHideInEditorMask); + } + else + { + transfer.Transfer (localIdentifier.localSerializedFileIndex, "m_FileID", kHideInEditorMask); + transfer.Transfer (localIdentifier.localIdentifierInFile, "m_PathID", kHideInEditorMask); + } + } + else + { + transfer.Transfer (m_InstanceID, "m_FileID", kHideInEditorMask); + transfer.Transfer (localIdentifier.localIdentifierInFile, "m_PathID", kHideInEditorMask); + } +} + +template<class T> inline +bool ImmediatePtr<T>::IsLoaded () const +{ + if (m_Ptr & 1) + { + return Object::IDToPointer(m_Ptr & (~1)) != NULL; + } + else + { + AssertIf(Object::IDToPointer(GetInstanceID()) == NULL); + return true; + } +} +template<class T> +string ImmediatePtr<T>::s_TypeString; + +template<class T> inline +const char* ImmediatePtr<T>::GetTypeString () +{ + if(s_TypeString.empty()) + { + SET_ALLOC_OWNER(NULL); + s_TypeString = string ("PPtr<") + T::GetClassStringStatic () + ">"; + } + return s_TypeString.c_str (); +} + +template<class T> +template<class TransferFunction> inline +void ImmediatePtr<T>::Transfer (TransferFunction& transfer) +{ + LocalSerializedObjectIdentifier localIdentifier; + + if (transfer.NeedsInstanceIDRemapping ()) + { + AssertIf (!transfer.IsWriting () && !transfer.IsReading ()); + + if (transfer.IsReading ()) + { + transfer.Transfer (localIdentifier.localSerializedFileIndex, "m_FileID", kHideInEditorMask); + transfer.Transfer (localIdentifier.localIdentifierInFile, "m_PathID", kHideInEditorMask); + SInt32 instanceID; + LocalSerializedObjectIdentifierToInstanceID (localIdentifier, instanceID); + AssignInstanceID (instanceID); + } + else if (transfer.IsWriting ()) + { + InstanceIDToLocalSerializedObjectIdentifier (GetInstanceID (), localIdentifier); + transfer.Transfer (localIdentifier.localSerializedFileIndex, "m_FileID", kHideInEditorMask); + transfer.Transfer (localIdentifier.localIdentifierInFile, "m_PathID", kHideInEditorMask); + } + else + { + transfer.Transfer (localIdentifier.localSerializedFileIndex, "m_FileID", kHideInEditorMask); + transfer.Transfer (localIdentifier.localIdentifierInFile, "m_PathID", kHideInEditorMask); + } + } + else + { + if (transfer.IsReading ()) + { + transfer.Transfer (localIdentifier.localSerializedFileIndex, "m_FileID", kHideInEditorMask); + transfer.Transfer (localIdentifier.localIdentifierInFile, "m_PathID", kHideInEditorMask); + SetInstanceID (localIdentifier.localSerializedFileIndex); + } + else if (transfer.IsWriting ()) + { + localIdentifier.localSerializedFileIndex = GetInstanceID (); + localIdentifier.localIdentifierInFile = 0; + transfer.Transfer (localIdentifier.localSerializedFileIndex, "m_FileID", kHideInEditorMask); + transfer.Transfer (localIdentifier.localIdentifierInFile, "m_PathID", kHideInEditorMask); + AssertIf (localIdentifier.localSerializedFileIndex != GetInstanceID ()); + } + else + { + transfer.Transfer (localIdentifier.localSerializedFileIndex, "m_FileID", kHideInEditorMask); + transfer.Transfer (localIdentifier.localIdentifierInFile, "m_PathID", kHideInEditorMask); + } + } +} +#if UNITY_PS3 +#define USE_NEW_IS_DERIVED_FROM 1 +#else +#define USE_NEW_IS_DERIVED_FROM 0 +#endif + +#if !USE_NEW_IS_DERIVED_FROM +#if UNITY_RELEASE +inline bool Object::IsDerivedFromClassID (int classID, int compareClass) +{ + AssertIf (classID >= ms_MaxClassID || classID < 0); + AssertIf (compareClass >= ms_MaxClassID || compareClass < 0); + int index = classID * ms_MaxClassID + compareClass; + int block = index >> 5; + int bit = index - (block << 5); + return (ms_IsDerivedFromBitMap[block]) & (1 << bit); +} +#endif +#else +#define CLASS_ID_MASK_BITS 4 +#define CLASS_ID_MASK_IDS 0x0fffffff +inline bool Object::IsDerivedFromClassID(int klass, int base) +{ + int klassId = ms_ClassIsDerivedFrom[klass]; + int baseId = ms_ClassIsDerivedFrom[base]; + int mask = ms_ClassIDMask[ 0xf&(baseId >> (32-CLASS_ID_MASK_BITS))]; + return (klassId&mask) == (baseId&CLASS_ID_MASK_IDS); +} +#endif + +void LockObjectCreation (); +void UnlockObjectCreation (); + +// Destroys a Object removing from memory and disk when needed. +// Might load the object as part of destruction which is probably unwanted. +// @TODO: Refactor code to not do that +void EXPORT_COREMODULE DestroySingleObject (Object* o); +void UnloadObject (Object* o); + +/// Destroys the object if it is loaded. (Will not load the object from disk if it is not loaded at the moment) +/// Will remove it from any remapping tables +/// Will not removed it from the actual serialized file, with the assumption that the file will be unloaded from disk later. +void DestroyWithoutLoadingButDontDestroyFromFile (int instanceID); + +#if DEBUGMODE +typedef std::set<int, std::less<int>, STL_ALLOCATOR(kMemPermanent,int) > VerifyRegisteredClass; +void EXPORT_COREMODULE AddVerifyClassRegistration (int classID); +typedef std::set<int, std::less<int>, STL_ALLOCATOR(kMemBaseObject, int) > RegisteredClassSet; +const RegisteredClassSet& GetVerifyClassRegistration (); +#endif + +/// Helper to create object correctly from code. Will call Reset and AwakeFromLoad +template <typename T> T* CreateObjectFromCode( AwakeFromLoadMode awakeMode=kInstantiateOrCreateFromCodeAwakeFromLoad, MemLabelId label = kMemBaseObject ) +{ + Assert(Object::ClassIDToRTTI(T::GetClassIDStatic()) != NULL); + T* obj = NEW_OBJECT_USING_MEMLABEL(T, label); + SET_ALLOC_OWNER(obj); + obj->Reset(); + obj->AwakeFromLoad(awakeMode); + return obj; +} + +template<typename T> +inline T* ResetAndAwake (T* object) +{ + object->Reset(); + object->AwakeFromLoad (kDefaultAwakeFromLoad); + return object; +} + +void delete_object_internal (Object* p); +void delete_object_internal_step1 (Object* object); +void delete_object_internal_step2 (Object* object); + +#endif |