summaryrefslogtreecommitdiff
path: root/Runtime/Serialize/PersistentManager.h
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2019-08-14 22:50:43 +0800
committerchai <chaifix@163.com>2019-08-14 22:50:43 +0800
commit15740faf9fe9fe4be08965098bbf2947e096aeeb (patch)
treea730ec236656cc8cab5b13f088adfaed6bb218fb /Runtime/Serialize/PersistentManager.h
+Unity Runtime codeHEADmaster
Diffstat (limited to 'Runtime/Serialize/PersistentManager.h')
-rw-r--r--Runtime/Serialize/PersistentManager.h473
1 files changed, 473 insertions, 0 deletions
diff --git a/Runtime/Serialize/PersistentManager.h b/Runtime/Serialize/PersistentManager.h
new file mode 100644
index 0000000..73f3f67
--- /dev/null
+++ b/Runtime/Serialize/PersistentManager.h
@@ -0,0 +1,473 @@
+#ifndef PERSISTENTMANAGER_H
+#define PERSISTENTMANAGER_H
+
+
+#define SUPPORT_INSTANCE_ID_REMAP_ON_LOAD (UNITY_EDITOR || WEBPLUG)
+
+class SerializedFile;
+class Object;
+class TypeTree;
+struct FileIdentifier;
+
+#include <map>
+#include <string>
+#include <vector>
+#include <stack>
+#include <deque>
+#include <set>
+#include "Runtime/Utilities/vector_map.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Utilities/CStringHash.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Threads/Mutex.h"
+#include "WriteData.h"
+#include "Runtime/Utilities/MemoryPool.h"
+#include "LoadProgress.h"
+
+using std::map;
+using std::set;
+using std::vector;
+using std::string;
+using std::stack;
+
+class AwakeFromLoadQueue;
+class Remapper;
+
+struct StreamNameSpace
+{
+ SerializedFile* stream;
+ LocalIdentifierInFileType highestID;
+
+ StreamNameSpace () { stream = NULL; highestID = 0; }
+};
+
+enum
+{
+ kNoError = 0,
+ kFileCouldNotBeRead = 1,
+ kTypeTreeIsDifferent = 2,
+ kFileCouldNotBeWritten = 3
+};
+
+enum UnpersistMode {
+ kDontDestroyFromFile = 0,
+ kDestroyFromFile = 1
+};
+
+
+struct ThreadedAwakeData
+{
+ SInt32 instanceID;
+ TypeTree* oldType;
+ Object* object;
+ bool checkConsistency; /// refactor to safeLoaded
+
+ // Has the object been fully loaded with AwakeFromLoadThreaded.
+ // We have to make sure the Object* is available already, so that recursive PPtr's to each other from Mono can correctly be resolved.
+ // In this case, neither object has been fully created, but we can setup pointers between them already.
+ bool completedThreadAwake;
+};
+
+struct SerializedObjectIdentifier
+{
+ SInt32 serializedFileIndex;
+ LocalIdentifierInFileType localIdentifierInFile;
+
+ SerializedObjectIdentifier (SInt32 inSerializedFileIndex, LocalIdentifierInFileType inLocalIdentifierInFile)
+ : serializedFileIndex (inSerializedFileIndex)
+ , localIdentifierInFile (inLocalIdentifierInFile)
+ { }
+
+ SerializedObjectIdentifier ()
+ : serializedFileIndex (0)
+ , localIdentifierInFile (0)
+ { }
+
+
+ friend bool operator < (const SerializedObjectIdentifier& lhs, const SerializedObjectIdentifier& rhs)
+ {
+ if (lhs.serializedFileIndex < rhs.serializedFileIndex)
+ return true;
+ else if (lhs.serializedFileIndex > rhs.serializedFileIndex)
+ return false;
+ else
+ return lhs.localIdentifierInFile < rhs.localIdentifierInFile;
+ }
+
+ friend bool operator != (const SerializedObjectIdentifier& lhs, const SerializedObjectIdentifier& rhs)
+ {
+ return lhs.serializedFileIndex != rhs.serializedFileIndex || lhs.localIdentifierInFile != rhs.localIdentifierInFile;
+ }
+};
+
+struct LocalSerializedObjectIdentifier;
+
+// There are three types of ids.
+// fileID, is an id that is local to a file. It ranges from [1 ... kNameSpaceSize]
+// heapID, is an id that was allocated for an object that was not loaded from disk. [1 ... infinity]
+// pathID, is an id to a file. Every file has a unique id. They are not recycled unless you delete the PersistentManager
+
+class PersistentManager
+{
+ protected:
+ enum
+ {
+// Recursive serialization causes the deflated stream to be reset repeatedly - use bigger cache chunks to limit the impact on load times.
+#if UNITY_ANDROID
+ kCacheSize = 1024 * 256
+#elif UNITY_XBOX360
+ kCacheSize = 1024 * 32
+#elif UNITY_WINRT || UNITY_BB10
+ kCacheSize = 1024 * 64
+#else
+ kCacheSize = 1024 * 7
+#endif
+ };
+
+ typedef std::pair<SInt32, SInt32> IntPair;
+ typedef vector_map<SInt32, SInt32, std::less<SInt32>, STL_ALLOCATOR(kMemSerialization, IntPair) > IDRemap;
+ typedef UNITY_VECTOR(kMemSerialization,StreamNameSpace) StreamContainer;
+
+ StreamContainer m_Streams;
+ UNITY_VECTOR(kMemSerialization,IDRemap) m_GlobalToLocalNameSpace;
+ UNITY_VECTOR(kMemSerialization,IDRemap) m_LocalToGlobalNameSpace;
+ Remapper* m_Remapper;
+
+ typedef std::pair<std::string,std::string> StringPair;
+ typedef vector_map<std::string, std::string, compare_string_insensitive,STL_ALLOCATOR(kMemSerialization,StringPair)> UserPathRemap;
+ UserPathRemap m_UserPathRemap;
+
+ #if SUPPORT_INSTANCE_ID_REMAP_ON_LOAD
+ typedef vector_map<SerializedObjectIdentifier, SerializedObjectIdentifier,
+ std::less<SerializedObjectIdentifier>,STL_ALLOCATOR(kMemSerialization,SerializedObjectIdentifier) > InstanceIDRemap;
+ InstanceIDRemap m_InstanceIDRemap;
+ #endif // #if SUPPORT_INSTANCE_ID_REMAP_ON_LOAD
+
+ #if UNITY_EDITOR
+ UNITY_SET(kMemSerialization,int) m_NonTextSerializedClasses;
+ #endif
+
+ SInt32 m_CacheCount;
+
+ stack<SInt32, std::deque<SInt32, STL_ALLOCATOR(kMemSerialization,SInt32) > > m_ActiveNameSpace;
+ int m_Options;
+
+ #if DEBUGMODE
+ bool m_AllowLoadingFromDisk;
+ bool m_IsLoadingSceneFile;
+ int m_PreventLoadingFromFile;
+ #endif
+
+ UNITY_SET(kMemSerialization, std::string) m_MemoryLoadedOrCachedPaths;
+
+ Mutex m_Mutex;
+ Mutex m_IntegrationMutex;
+
+ Mutex m_MemoryLoadedOrCachedPathsMutex;
+
+ bool m_AllowIntegrateThreadedObjectsWithTimeout; // Mutex protected by m_IntegrationMutex
+
+#if ENABLE_CUSTOM_ALLOCATORS_FOR_STDMAP
+ MemoryPool m_ThreadedAwakeDataPool;
+ MemoryPool m_ThreadedAwakeDataPoolMap;
+ typedef std::list<ThreadedAwakeData, memory_pool_explicit<ThreadedAwakeData> > ThreadedObjectActivationQueue;
+ typedef std::map<SInt32, ThreadedObjectActivationQueue::iterator, std::less<SInt32>, memory_pool_explicit<ThreadedObjectActivationQueue::iterator> > ThreadedObjectActivationMap;
+#else
+ typedef std::list<ThreadedAwakeData> ThreadedObjectActivationQueue;
+ typedef std::map<SInt32, ThreadedObjectActivationQueue::iterator> ThreadedObjectActivationMap;
+#endif
+ ThreadedObjectActivationQueue m_ThreadedObjectActivationQueue; // protected by m_IntegrationMutex
+ ThreadedObjectActivationMap m_ThreadedObjectActivationMap; // protected by m_IntegrationMutex
+ UNITY_SET(kMemSerialization, SInt32) m_OnDemandThreadLoadedObjects; /// DONT USE POOL HERE NOT THREAD SAFE
+
+ StreamNameSpace& GetStreamNameSpaceInternal (int nameSpaceID);
+ void DestroyFromFileInternal (int memoryID);
+ public:
+
+ PersistentManager (int options, int cacheCount);
+ virtual ~PersistentManager ();
+
+ /// Loads all objects in pathName
+ /// Returns kNoError, kFileCouldNotBeRead
+ int LoadFileCompletely (const string& pathname);
+
+ #if UNITY_EDITOR
+ // Makes an object persistent and generates a unique fileID in pathName
+ // The object can now be referenced by other objects that write to disk
+ void MakeObjectPersistent (int heapID, const string& pathName);
+ // Makes an object persistent if fileID == 0 a new unique fileID in pathName will be generated
+ // The object can now be referenced by other objects that write to disk
+ // If the object is already persistent in another file or another fileID it will be destroyed from that file.
+ void MakeObjectPersistentAtFileID (int heapID, LocalIdentifierInFileType fileID, const string& pathName);
+
+ /// Batch multiple heapID's and fileID's into one path name.
+ /// on return fileID's will contain the file id's that were generated (if fileIds[i] is non-zero that fileID will be used instead)
+ enum { kMakePersistentDontRequireToBeLoadedAndDontUnpersist = 1 << 0, kAllowDontSaveObjectsToBePersistent = 1 << 1 };
+ void MakeObjectsPersistent (const int* heapIDs, LocalIdentifierInFileType* fileIDs, int size, const string& pathName, int options = 0);
+ #endif
+
+ // Makes an object unpersistent
+ void MakeObjectUnpersistent (int memoryID, UnpersistMode unpersistMode);
+
+ bool RemoveObjectsFromPath (const std::string& pathName);
+
+ // Returns the pathname the referenced object is stored at, if its not persistent empty string is returned
+ string GetPathName (SInt32 memoryID);
+ // Returns the localFileID the referenced object has inside its file.
+
+ bool InstanceIDToSerializedObjectIdentifier (int instanceID, SerializedObjectIdentifier& identifier);
+ int SerializedObjectIdentifierToInstanceID (const SerializedObjectIdentifier& identifier);
+
+ LocalIdentifierInFileType GetLocalFileID(SInt32 instanceID);
+
+ // Generates or returns an instanceID from path and fileID which can then
+ // be used to load the object at that instanceID
+ ///@TODO: RENAME TO LocalIdentifierInFile
+ SInt32 GetInstanceIDFromPathAndFileID (const string& path, LocalIdentifierInFileType localIdentifierInFile);
+
+ // Returns classID from path and fileID.
+ int GetClassIDFromPathAndFileID (const string& path, LocalIdentifierInFileType localIdentifierInFile);
+
+ // Reads the object referenced by heapID, if there is no object with heapID, the object will first be produced.
+ // Returns the created and read object, or NULL if the object couldnt be found or was destroyed.
+ Object* ReadObject (int heapID);
+
+ // Unloads all streams that are open.
+ // After UnloadStreams is called files may be safely replaced.
+ // May only be called if there are no dirty files open.
+ void UnloadStreams ();
+ void UnloadStream (const std::string& pathName);
+
+ bool IsStreamLoaded (const std::string& pathName);
+
+ #if UNITY_EDITOR
+
+ typedef bool VerifyWriteObjectCallback (Object* verifyDeployment, BuildTargetPlatform target);
+
+ // Writes all persistent objects in memory that are made peristent at pathname to the file
+ // And completes all write operation (including writing the header)
+ // Returns the error (kNoError)
+ // options: kSerializeGameRelease, kSwapEndianess, kBuildPlayerOnlySerializeBuildProperties
+ int WriteFile (const string& pathName, BuildTargetSelection target = BuildTargetSelection::NoTarget(), int options = 0);
+
+ bool IsClassNonTextSerialized(int cid);
+
+ int WriteFileInternal (const std::string& path, int serializedFileIndex, const WriteData* writeData, int size, VerifyWriteObjectCallback* verifyCallback, BuildTargetSelection target, int options);
+
+ #if UNITY_EDITOR
+ bool TestNeedWriteFile (const std::string& pathName, const std::set<int>* dirtyPaths = NULL);
+ bool TestNeedWriteFile (int pathID, const std::set<int>* dirtyPaths = NULL);
+ #endif
+
+ // Delete file deletes the file referenced by pathName
+ // Makes all loaded objects unpersistent
+ // deleteLoadedObjects & kDeleteLoadedObjects -> All objects on the disk will be attempted to be destroyed
+ // deleteLoadedObjects & kDontDeleteLoadedObjects -> Doesn't delete any loaded objects, but marks them unpersistent from the file.
+ enum DeletionFlags { kDontDeleteLoadedObjects = 0, kDeleteLoadedObjects = 1 << 0 };
+ bool DeleteFile (const string& pathName, DeletionFlags flag);
+
+ void AddNonTextSerializedClass (int classID) { m_NonTextSerializedClasses.insert (classID); }
+ #endif
+
+ // On return: objects are the instanceIDs of all objects resident in the file referenced by pathName
+ typedef std::set<SInt32> ObjectIDs;
+ void GetInstanceIDsAtPath (const string& pathName, ObjectIDs* objects);
+ void GetInstanceIDsAtPath (const string& pathName, vector<SInt32>* objects);
+
+ void GetLoadedInstanceIDsAtPath (const string& pathName, ObjectIDs* objects);
+ void GetPersistentInstanceIDsAtPath (const string& pathName, std::set<SInt32>* objects);
+
+ int CountInstanceIDsAtPath (const string& pathName);
+
+ void SetAllowIntegrateThreadedObjectsWithTimeout (bool value);
+
+ bool IsFileEmpty (const string& pathName);
+
+ // Finds out if the referenced object can be loaded.
+ // By looking for it on the disk. And checking if the classID can be produced.
+ bool IsObjectAvailable (int heapID);
+ bool IsObjectAvailableDontCheckActualFile (int heapID);
+
+ void GetAllFileIDs (const string& pathName, vector<LocalIdentifierInFileType>* objects);
+
+ // Finds the
+ int GetSerializedClassID (int instanceID);
+
+
+
+
+ // Resets the fileIDs. This can only be used if the file has just been deleted.
+ void ResetHighestFileIDAtPath (const string& pathName);
+
+ // Computes the memoryID (object->GetInstanceID ()) from fileID
+ // fileID is relative to the file we are currently writing/reading from.
+ // It can only be called when reading/writing objects in order to
+ // convert ptrs from file space to global space
+ void LocalSerializedObjectIdentifierToInstanceIDInternal (const LocalSerializedObjectIdentifier& identifier, SInt32& memoryID);
+ void LocalSerializedObjectIdentifierToInstanceIDInternal (int activeNameSpace, const LocalSerializedObjectIdentifier& localIdentifier, SInt32& outInstanceID);
+
+ // fileID from memory ID (object->GetInstanceID ())
+ // It can only be called when reading/writing objects in order
+ // to convert ptrs from global space to file space
+ void InstanceIDToLocalSerializedObjectIdentifierInternal (SInt32 memoryID, LocalSerializedObjectIdentifier& identifier);
+
+ // Translates globalIdentifier.serializedFileIndex from a global index into the local file index based on what file we are currently writing.
+ // It can only be called when reading/writing objects in order
+ // to convert ptrs from global space to file space
+ LocalSerializedObjectIdentifier GlobalToLocalSerializedFileIndexInternal (const SerializedObjectIdentifier& globalIdentifier);
+
+ /// Is this instanceID mapped to the file we are currently writing,
+ /// in other words is the referenced instanceID read or written from the same file then this will return true.
+ bool IsInstanceIDFromCurrentFileInternal (SInt32 memoryID);
+
+ #if UNITY_EDITOR
+ // Hints a fileID to heap id mapping.
+ // This i used to keep similar instanceID's when entering / exiting playmode
+ void SuggestFileIDToHeapIDs (const string& pathname, std::map<LocalIdentifierInFileType, SInt32>& fileIDToHeapIDHint);
+
+ int GetSerializedFileIndexFromPath (const std::string& path);
+
+ #endif
+
+ #if SUPPORT_INSTANCE_ID_REMAP_ON_LOAD
+ void RemapInstanceIDOnLoad (const std::string& srcPath, LocalIdentifierInFileType srcLocalFileID, const std::string& dstPath, LocalIdentifierInFileType dstLocalFileID);
+ void ApplyInstanceIDRemap(SerializedObjectIdentifier& id);
+ #endif // #if SUPPORT_INSTANCE_ID_REMAP_ON_LOAD
+
+ /// NOTE: Returns an object that has not been completely initialized (Awake has not been called yet)
+ Object* ReadObjectThreaded (int heapID);
+
+ /// Check if we have objects to integrate, ie. if calling IntegrateThreadedObjects will perform some useful work
+ bool HasThreadedObjectsToIntegrate ();
+
+ /// Integrates all thread loaded objects into the world (Called from PlayerLoop)
+ void IntegrateThreadedObjects (float timeout);
+
+ /// Called from outise the loading thread (non-main thread), allows IntegrateThreadedObjects to be called with time slicing
+ /// Stalls until all objects have been integrated
+ void AllowIntegrationWithTimeoutAndWait ();
+
+ void IntegrateAllThreadedObjects ();
+
+
+ void PrepareAllThreadedObjectsStep1 (AwakeFromLoadQueue& awakeQueue);
+ void IntegrateAllThreadedObjectsStep2 (AwakeFromLoadQueue& awakeQueue/*, bool loadScene*/);
+
+
+
+ /// Load the entire file from a different thread
+ int LoadFileCompletelyThreaded (const std::string& pathname, LocalIdentifierInFileType* fileIDs, SInt32* instanceIDs, int size, bool loadScene, LoadProgress* loadProgress);
+
+ /// Loads a number of objects threaded
+ void LoadObjectsThreaded (SInt32* heapIDs, int size, LoadProgress* loadProgress);
+
+ #if SUPPORT_THREADS
+ const Mutex& GetMutex () { return m_Mutex; }
+ Thread::ThreadID GetMainThreadID () { return Thread::mainThreadId; }
+ #endif
+
+ #if DEBUGMODE
+ /// Allows you to assert on any implicit loading operations for a specific file
+ void SetDebugAssertLoadingFromFile (const std::string& path);
+ /// Allows you to assert on any implicit loading operations, because for example when unloading objects that is usually not desired.
+ void SetDebugAssertAllowLoadingAnything (bool allowLoading) { m_AllowLoadingFromDisk = allowLoading; }
+
+ void SetIsLoadingSceneFile(bool inexplicit) { m_IsLoadingSceneFile = inexplicit; }
+ #endif
+
+ void Lock();
+ void Unlock();
+ /// Loads the contents of the object from disk again
+ /// Performs serialization and calls AwakeFromLoad
+ bool ReloadFromDisk (Object* obj);
+
+ /// Load a memory stream directly from memory
+ /// - You should call this function only on assets that have been writting using kSerializeGameReleaswe
+ bool LoadMemoryBlockStream (const std::string& pathName, UInt8** data, int offset, int end, const char* url = NULL);
+
+ // Loads a file at actualAbsolutePath and pretends that it is actually at path.
+ /// - You should call this function only on assets that have been writting using kSerializeGameReleaswe
+ bool LoadCachedFile (const std::string& path, const std::string actualAbsolutePath);
+
+ void UnloadMemoryStreams ();
+
+ // A registered SafeBinaryReadCallbackFunction will be called when an objects old typetree is different from the new one
+ // and variables might have gone away, added, or changed
+ typedef void SafeBinaryReadCallbackFunction (Object& object, const TypeTree& oldTypeTree);
+ static void RegisterSafeBinaryReadCallback (SafeBinaryReadCallbackFunction* callback);
+
+ // In order for DeleteFile to work you have to support a callback that deletes the objects referenced by instanceID
+ typedef void InOrderDeleteCallbackFunction (const set<SInt32>& objects, bool safeDestruction);
+ static void RegisterInOrderDeleteCallback (InOrderDeleteCallbackFunction* callback);
+
+ /// Thread locking must be performed from outside using Lock/Unlock
+ SerializedFile* GetSerializedFileInternal (const string& path);
+ SerializedFile* GetSerializedFileInternal (int serializedFileIndex);
+
+ void SetPathRemap (const string& path, const string& absoluteRemappedPath);
+
+ // Non-Locking method to find out if a memorystream or cached file is set up.
+ bool HasMemoryOrCachedSerializedFile (const std::string& path);
+
+ #if SUPPORT_SERIALIZATION_FROM_DISK
+ bool LoadExternalStream (const std::string& pathName, const std::string& absolutePath, int flags, int readOffset = 0);
+ #endif
+
+ void UnloadNonDirtyStreams ();
+
+ /// NOTE: Function postfixed Internal are not thread safe and you have to call PersistentManager.Lock / Unlock prior to calling them from outside persistentmanager
+
+
+ //// Subclasses have to override these methods which map from PathIDs to FileIdentifier
+ /// Maps a pathname/fileidentifier to a pathID. If the pathname is not yet known, you have to call AddStream ().
+ /// The pathIDs start at 0 and increment by 1
+ virtual int InsertFileIdentifierInternal (FileIdentifier file, bool create) = 0;
+
+ std::string RemapToAbsolutePath (const std::string& path);
+
+ void DoneLoadingManagers();
+
+ protected:
+
+ virtual int InsertPathNameInternal (const std::string& pathname, bool create) = 0;
+
+ /// maps a pathID to a pathname/file guid/fileidentifier.
+ /// (pathID can be assumed to be allocated before with InsertPathName)
+ virtual string PathIDToPathNameInternal (int pathID) = 0;
+ virtual FileIdentifier PathIDToFileIdentifierInternal (int pathID) = 0;
+
+ /// Adds a new empty stream. Used by subclasses inside InsertPathName when a new pathID has to be added
+ void AddStream ();
+
+ private:
+
+ void CleanupStreamAndNameSpaceMapping (unsigned pathID);
+
+ void RegisterAndAwakeThreadedObjectAndUnlockIntegrationMutex (const ThreadedAwakeData& awake);
+
+ ThreadedAwakeData* CreateThreadActivationQueueEntry (SInt32 instanceID);
+ void SetupThreadActivationQueueObject (ThreadedAwakeData& data, TypeTree* oldType, bool didTypeTreeChange);
+
+ /// Goes through object activation queue and calls AwakeFromLoad if it has been serialized already but not AwakeFromLoad called.
+ Object* LoadFromActivationQueue (int heapID);
+ Object* GetFromActivationQueue (int heapID);
+ bool FindInActivationQueue (int heapID);
+ void CheckInstanceIDsLoaded (SInt32* heapIDs, int size);
+
+ protected:
+
+ void PostLoadStreamNameSpace (StreamNameSpace& nameSpace, int namespaceID);
+
+#if UNITY_EDITOR
+ bool TestNeedWriteFileInternal (int pathID, const std::set<int>* cachedDirtyPathsHint);
+#endif
+
+ friend class Object;
+};
+
+PersistentManager& GetPersistentManager ();
+PersistentManager* GetPersistentManagerPtr ();
+
+void CleanupPersistentManager();
+
+#endif