1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
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
|