summaryrefslogtreecommitdiff
path: root/Runtime/Serialize/PersistentManager.cpp
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.cpp
+Unity Runtime codeHEADmaster
Diffstat (limited to 'Runtime/Serialize/PersistentManager.cpp')
-rw-r--r--Runtime/Serialize/PersistentManager.cpp2291
1 files changed, 2291 insertions, 0 deletions
diff --git a/Runtime/Serialize/PersistentManager.cpp b/Runtime/Serialize/PersistentManager.cpp
new file mode 100644
index 0000000..3f596f4
--- /dev/null
+++ b/Runtime/Serialize/PersistentManager.cpp
@@ -0,0 +1,2291 @@
+#include "UnityPrefix.h"
+#include "PersistentManager.h"
+#include "Runtime/BaseClasses/BaseObject.h"
+#include "Runtime/Utilities/FileUtilities.h"
+#include "SerializedFile.h"
+#include "Runtime/Utilities/LogAssert.h"
+#include "Remapper.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Utilities/File.h"
+#include "AwakeFromLoadQueue.h"
+
+#define DEBUG_THREAD_LOAD 0
+#define DEBUG_THREAD_LOAD_LONG_ACTIVATE !UNITY_RELEASE
+#define DEBUG_MAINTHREAD_LOADING 0
+
+
+#if DEBUG_THREAD_LOAD
+#define printf_debug_thread printf_console
+#else
+#define printf_debug_thread
+#endif
+
+#include "Runtime/Threads/ProfilerMutex.h"
+#if ENABLE_PROFILER
+#include "Runtime/Profiler/MemoryProfiler.h"
+#endif
+PROFILER_INFORMATION(gMakeObjectUnpersistentProfiler, "Loading.MakeObjectUnpersistent", kProfilerLoading)
+PROFILER_INFORMATION(gMakeObjectPersistentProfiler, "Loading.MakeObjectUnpersistent", kProfilerLoading)
+PROFILER_INFORMATION(gAwakeFromLoadManager, "Loading.AwakeFromLoad", kProfilerLoading)
+PROFILER_INFORMATION(gIDRemappingProfiler, "Loading.IDRemapping", kProfilerLoading)
+PROFILER_INFORMATION(gWriteFileProfiler, "Loading.WriteFile", kProfilerLoading)
+PROFILER_INFORMATION(gFindInActivationQueueProfiler, "Loading.FindInThreadedActivationQueue", kProfilerLoading)
+PROFILER_INFORMATION(gReadObjectProfiler, "Loading.ReadObject", kProfilerLoading)
+PROFILER_INFORMATION(gLoadFileProfiler, "Loading.LoadFile", kProfilerLoading)
+PROFILER_INFORMATION(gIsObjectAvailable, "Loading.IsObjectAvailaable", kProfilerLoading)
+PROFILER_INFORMATION(gLoadStreamNameSpaceProfiler, "Loading.LoadFileHeaders", kProfilerLoading)
+PROFILER_INFORMATION(gLoadLockPersistentManager, "Loading.LockPersistentManager", kProfilerLoading)
+PROFILER_INFORMATION(gLoadFromActivationQueueStall, "Loading.LoadFromActivationQueue stalled [wait for loading operation to finish]", kProfilerLoading)
+// @TODO: Write test for cross references between monobehaviours in prefab and in scene!
+
+
+static PersistentManager* gPersistentManager = NULL;
+static PersistentManager::InOrderDeleteCallbackFunction* gInOrderDeleteCallback = NULL;
+static PersistentManager::SafeBinaryReadCallbackFunction* gSafeBinaryReadCallback = NULL;
+
+static const char* kSerializedFileArea = "SerializedFile";
+static const char* kRemapperAllocArea = "PersistentManager.Remapper";
+
+#if UNITY_EDITOR || SUPPORT_RESOURCE_IMAGE_LOADING
+static const char* kResourceImageExtensions[] = { "resG", "res", "resS" };
+#endif
+
+double GetTimeSinceStartup ();
+
+using namespace std;
+
+bool PersistentManager::InstanceIDToSerializedObjectIdentifier (int instanceID, SerializedObjectIdentifier& identifier)
+{
+ PROFILER_AUTO_THREAD_SAFE(gIDRemappingProfiler, NULL);
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+
+ return m_Remapper->InstanceIDToSerializedObjectIdentifier(instanceID, identifier);
+}
+
+int PersistentManager::SerializedObjectIdentifierToInstanceID (const SerializedObjectIdentifier& identifier)
+{
+ PROFILER_AUTO_THREAD_SAFE(gIDRemappingProfiler, NULL);
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+
+ return m_Remapper->GetOrGenerateMemoryID (identifier);
+}
+
+
+LocalIdentifierInFileType PersistentManager::GetLocalFileID(SInt32 instanceID)
+{
+ SerializedObjectIdentifier identifier;
+ InstanceIDToSerializedObjectIdentifier (instanceID, identifier);
+ return identifier.localIdentifierInFile;
+}
+
+SInt32 PersistentManager::GetInstanceIDFromPathAndFileID (const string& path, LocalIdentifierInFileType localIdentifierInFile)
+{
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+
+ SerializedObjectIdentifier identifier;
+ identifier.serializedFileIndex = InsertPathNameInternal (path, true);
+ identifier.localIdentifierInFile = localIdentifierInFile;
+ return m_Remapper->GetOrGenerateMemoryID (identifier);
+}
+
+int PersistentManager::GetClassIDFromPathAndFileID (const string& path, LocalIdentifierInFileType localIdentifierInFile)
+{
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+
+ SerializedObjectIdentifier identifier;
+ identifier.serializedFileIndex = InsertPathNameInternal (path, true);
+ identifier.localIdentifierInFile = localIdentifierInFile;
+
+ SerializedFile* stream = GetSerializedFileInternal (identifier.serializedFileIndex);
+ if (stream == NULL)
+ return -1;
+
+ if (!stream->IsAvailable (identifier.localIdentifierInFile))
+ return -1;
+
+ return stream->GetClassID (identifier.localIdentifierInFile);
+}
+
+static void CleanupStream (StreamNameSpace& stream)
+{
+ SerializedFile* oldFile = stream.stream;
+ stream.stream = NULL;
+
+ UNITY_DELETE (oldFile, kMemSerialization);
+}
+
+int PersistentManager::GetSerializedClassID (int instanceID)
+{
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+
+ SerializedObjectIdentifier identifier;
+ if (!m_Remapper->InstanceIDToSerializedObjectIdentifier(instanceID, identifier))
+ return -1;
+
+ SerializedFile* stream = GetSerializedFileInternal (identifier.serializedFileIndex);
+ if (stream == NULL)
+ return -1;
+
+ if (!stream->IsAvailable (identifier.localIdentifierInFile))
+ return -1;
+
+ return stream->GetClassID (identifier.localIdentifierInFile);
+}
+
+void PersistentManager::GetAllFileIDs (const string& pathName, vector<LocalIdentifierInFileType>* objects)
+{
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+
+ AssertIf (objects == NULL);
+
+ int serializedFileIndex = InsertPathNameInternal (pathName, true);
+ SerializedFile* stream = GetSerializedFileInternal (serializedFileIndex);
+ if (stream == NULL)
+ return;
+
+ stream->GetAllFileIDs (objects);
+}
+
+bool PersistentManager::RemoveObjectsFromPath (const std::string& pathName)
+{
+#if DEBUGMODE
+ AssertIf(!m_AllowLoadingFromDisk);
+#endif
+
+ ASSERT_RUNNING_ON_MAIN_THREAD
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+
+ SInt32 serializedFileIndex = InsertPathNameInternal (pathName, false);
+ if (serializedFileIndex == -1)
+ return false;
+
+ vector<SInt32> temp;
+ m_Remapper->RemoveCompletePathID(serializedFileIndex, temp);
+
+ return true;
+}
+
+void PersistentManager::MakeObjectUnpersistent (int memoryID, UnpersistMode mode)
+{
+ PROFILER_AUTO_THREAD_SAFE(gMakeObjectUnpersistentProfiler, NULL);
+
+#if DEBUGMODE
+ AssertIf(!m_AllowLoadingFromDisk);
+#endif
+
+ ASSERT_RUNNING_ON_MAIN_THREAD
+
+ Object* o = Object::IDToPointer (memoryID);
+ if (o && !o->IsPersistent ())
+ {
+// #if DEBUGMODE && !UNITY_RELEASE
+// AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, GetMainThreadID(), "PersistentManager.MakeObjectUnpersistent");
+// AssertIf (m_Remapper->GetPathID (memoryID) != -1);
+// #endif
+ return;
+ }
+
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+ if (mode == kDestroyFromFile)
+ DestroyFromFileInternal (memoryID);
+
+ m_Remapper->Remove (memoryID);
+
+ if (o)
+ o->SetIsPersistent (false);
+}
+
+#if UNITY_EDITOR
+void PersistentManager::MakeObjectPersistent (int heapID, const string& pathName)
+{
+ MakeObjectPersistentAtFileID (heapID, 0, pathName);
+}
+
+void PersistentManager::MakeObjectPersistentAtFileID (int heapID, LocalIdentifierInFileType fileID, const string& pathName)
+{
+ MakeObjectsPersistent (&heapID, &fileID, 1, pathName);
+}
+
+void PersistentManager::MakeObjectsPersistent (const int* heapIDs, LocalIdentifierInFileType* fileIDs, int size, const string& pathName, int options)
+{
+ PROFILER_AUTO_THREAD_SAFE(gMakeObjectPersistentProfiler, NULL);
+
+ AssertIf(!m_AllowLoadingFromDisk);
+ AssertIf(!Thread::EqualsCurrentThreadID(GetMainThreadID()));
+
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+
+ AssertIf(pathName.empty());
+ SInt32 globalNameSpace = InsertPathNameInternal (pathName, true);
+ StreamNameSpace* streamNameSpace = NULL;
+
+ for (int i=0;i<size;i++)
+ {
+ int heapID = heapIDs[i];
+ LocalIdentifierInFileType fileID = fileIDs[i];
+
+ Object* o = Object::IDToPointer (heapID);
+
+ if ((options & kMakePersistentDontRequireToBeLoadedAndDontUnpersist) == 0)
+ {
+ // Making an object that is not in memory persistent
+ if (o == NULL)
+ {
+ ErrorString("Make Objects Persistent failed because the object can not be loaded");
+ continue;
+ }
+
+ // Make Object unpersistent first
+ if (o->IsPersistent ())
+ {
+ SerializedObjectIdentifier identifier;
+ InstanceIDToSerializedObjectIdentifier(heapID, identifier);
+ AssertIf (identifier.serializedFileIndex == -1);
+
+ // Return if the file and serializedFileIndex is not going to change
+ if (globalNameSpace == identifier.serializedFileIndex)
+ {
+ if (fileID == 0 || identifier.localIdentifierInFile == fileID)
+ continue;
+ }
+
+ MakeObjectUnpersistent (heapID, kDestroyFromFile);
+ }
+ }
+
+ if (streamNameSpace == NULL)
+ streamNameSpace = &GetStreamNameSpaceInternal (globalNameSpace);
+
+ // Allocate an fileID for this object in the File
+ if (fileID == 0)
+ {
+ fileID = streamNameSpace->highestID;
+ if (streamNameSpace->stream)
+ fileID = max (streamNameSpace->highestID, streamNameSpace->stream->GetHighestID ());
+ fileID++;
+ }
+ streamNameSpace->highestID = max (streamNameSpace->highestID, fileID);
+
+ SerializedObjectIdentifier identifier;
+ identifier.serializedFileIndex = globalNameSpace;
+ identifier.localIdentifierInFile = fileID;
+ m_Remapper->SetupRemapping (heapID, identifier);
+ fileIDs[i] = fileID;
+
+ if (o)
+ {
+
+ AssertIf (o->TestHideFlag (Object::kDontSave) && (options & kAllowDontSaveObjectsToBePersistent) == 0);
+ o->SetIsPersistent (true);
+ o->SetDirty ();
+ }
+ }
+}
+#endif
+
+
+void PersistentManager::LocalSerializedObjectIdentifierToInstanceIDInternal (const LocalSerializedObjectIdentifier& localIdentifier, SInt32& outInstanceID)
+{
+ int activeNameSpace = m_ActiveNameSpace.top();
+ LocalSerializedObjectIdentifierToInstanceIDInternal (activeNameSpace, localIdentifier, outInstanceID);
+}
+
+void PersistentManager::LocalSerializedObjectIdentifierToInstanceIDInternal (int activeNameSpace, const LocalSerializedObjectIdentifier& localIdentifier, SInt32& outInstanceID)
+{
+ PROFILER_AUTO_THREAD_SAFE(gIDRemappingProfiler, NULL);
+
+ LocalIdentifierInFileType localIdentifierInFile = localIdentifier.localIdentifierInFile;
+ int localSerializedFileIndex = localIdentifier.localSerializedFileIndex;
+
+ if (localIdentifierInFile == 0)
+ {
+ outInstanceID = 0;
+ return;
+ }
+
+ AssertIf (localSerializedFileIndex == -1);
+
+ int globalFileIndex;
+ if (localSerializedFileIndex == 0)
+ globalFileIndex = activeNameSpace;
+ else
+ {
+ AssertIf (m_Streams[activeNameSpace].stream == NULL);
+
+ AssertIf(activeNameSpace >= m_LocalToGlobalNameSpace.size() || activeNameSpace < 0);
+
+ IDRemap::iterator found = m_LocalToGlobalNameSpace[activeNameSpace].find (localSerializedFileIndex);
+
+ if (found != m_LocalToGlobalNameSpace[activeNameSpace].end ())
+ {
+ globalFileIndex = found->second;
+ }
+ else
+ {
+ AssertString ("illegal LocalPathID in persistentmanager");
+ outInstanceID = 0;
+ return;
+ }
+ }
+
+ SerializedObjectIdentifier globalIdentifier;
+ globalIdentifier.serializedFileIndex = globalFileIndex;
+ globalIdentifier.localIdentifierInFile = localIdentifierInFile;
+
+ #if SUPPORT_INSTANCE_ID_REMAP_ON_LOAD
+ ApplyInstanceIDRemap(globalIdentifier);
+ #endif
+
+ outInstanceID = m_Remapper->GetOrGenerateMemoryID (globalIdentifier);
+}
+
+#if SUPPORT_INSTANCE_ID_REMAP_ON_LOAD
+void PersistentManager::ApplyInstanceIDRemap(SerializedObjectIdentifier& id)
+{
+ InstanceIDRemap::iterator foundIDRemap = m_InstanceIDRemap.find(id);
+ if (foundIDRemap != m_InstanceIDRemap.end())
+ id = foundIDRemap->second;
+}
+#endif // #if SUPPORT_INSTANCE_ID_REMAP_ON_LOAD
+
+
+LocalSerializedObjectIdentifier PersistentManager::GlobalToLocalSerializedFileIndexInternal (const SerializedObjectIdentifier& globalIdentifier)
+{
+ LocalIdentifierInFileType localIdentifierInFile = globalIdentifier.localIdentifierInFile;
+ int localSerializedFileIndex;
+
+ // Remap globalPathID to localPathID
+ int activeNameSpace = m_ActiveNameSpace.top ();
+
+ IDRemap& globalToLocalNameSpace = m_GlobalToLocalNameSpace[activeNameSpace];
+ IDRemap& localToGlobalNameSpace = m_LocalToGlobalNameSpace[activeNameSpace];
+
+ IDRemap::iterator found = globalToLocalNameSpace.find (globalIdentifier.serializedFileIndex);
+ if (found == globalToLocalNameSpace.end ())
+ {
+ SET_ALLOC_OWNER(NULL);
+ AssertIf (activeNameSpace >= m_Streams.size());
+ AssertIf (m_Streams[activeNameSpace].stream == NULL);
+ SerializedFile& serialize = *m_Streams[activeNameSpace].stream;
+
+ serialize.AddExternalRef (PathIDToFileIdentifierInternal (globalIdentifier.serializedFileIndex));
+
+ localSerializedFileIndex = serialize.GetExternalRefs ().size ();
+ globalToLocalNameSpace[globalIdentifier.serializedFileIndex] = localSerializedFileIndex;
+ localToGlobalNameSpace[localSerializedFileIndex] = globalIdentifier.serializedFileIndex;
+ }
+ else
+ localSerializedFileIndex = found->second;
+
+ // Setup local identifier
+ LocalSerializedObjectIdentifier localIdentifier;
+
+ localIdentifier.localSerializedFileIndex = localSerializedFileIndex;
+ localIdentifier.localIdentifierInFile = localIdentifierInFile;
+
+ return localIdentifier;
+}
+
+void PersistentManager::InstanceIDToLocalSerializedObjectIdentifierInternal (SInt32 instanceID, LocalSerializedObjectIdentifier& localIdentifier)
+{
+ PROFILER_AUTO_THREAD_SAFE(gIDRemappingProfiler, NULL);
+
+ AssertIf (m_ActiveNameSpace.empty ());
+ if (instanceID == 0)
+ {
+ localIdentifier.localSerializedFileIndex = 0;
+ localIdentifier.localIdentifierInFile = 0;
+ return;
+ }
+
+ SerializedObjectIdentifier globalIdentifier;
+ if (!m_Remapper->InstanceIDToSerializedObjectIdentifier (instanceID, globalIdentifier))
+ {
+ localIdentifier.localSerializedFileIndex = 0;
+ localIdentifier.localIdentifierInFile = 0;
+ return;
+ }
+
+ localIdentifier = GlobalToLocalSerializedFileIndexInternal(globalIdentifier);
+}
+
+bool PersistentManager::IsInstanceIDFromCurrentFileInternal (SInt32 instanceID)
+{
+ if (instanceID == 0)
+ return false;
+
+ SerializedObjectIdentifier globalIdentifier;
+
+ if (!m_Remapper->InstanceIDToSerializedObjectIdentifier (instanceID, globalIdentifier))
+ return false;
+
+ int activeNameSpace = m_ActiveNameSpace.top ();
+ return globalIdentifier.serializedFileIndex == activeNameSpace;
+}
+
+#if UNITY_EDITOR
+
+int PersistentManager::GetSerializedFileIndexFromPath (const std::string& path)
+{
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+ return InsertPathNameInternal (path, true);
+}
+
+bool PersistentManager::TestNeedWriteFile (const string& pathName, const std::set<int>* cachedDirtyPathsHint)
+{
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+
+ int serializedFileIndex = InsertPathNameInternal (pathName, false);
+ return TestNeedWriteFileInternal(serializedFileIndex, cachedDirtyPathsHint);
+}
+
+bool PersistentManager::TestNeedWriteFile (int globalFileIndex, const std::set<int>* cachedDirtyPathsHint)
+{
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+ return TestNeedWriteFileInternal(globalFileIndex, cachedDirtyPathsHint);
+}
+
+bool PersistentManager::TestNeedWriteFileInternal (int globalFileIndex, const std::set<int>* cachedDirtyPathsHint)
+{
+ if (globalFileIndex == -1)
+ return false;
+
+ SerializedFile* stream = m_Streams[globalFileIndex].stream;
+
+ bool isFileDirty = stream != NULL && stream->IsFileDirty ();
+
+ // Something was deleted from the file. Must write it!
+ if (isFileDirty)
+ return true;
+
+ // Use Dirty path indices to quickly determine if a file needs writing
+ if (cachedDirtyPathsHint != NULL)
+ return cachedDirtyPathsHint->count (globalFileIndex);
+
+ Object* o;
+ // Find out if file needs to write to disk
+ // - Get all loaded objects that have registered themselves for being at that file
+ set<SInt32> loadedWriteObjects;
+ m_Remapper->GetAllLoadedObjectsAtPath (globalFileIndex, &loadedWriteObjects);
+ for (set<SInt32>::iterator i=loadedWriteObjects.begin ();i != loadedWriteObjects.end ();i++)
+ {
+ o = Object::IDToPointer (*i);
+ if (o && o->IsPersistent () && o->IsPersistentDirty ())
+ return true;
+ }
+
+ return false;
+}
+
+void PersistentManager::CleanupStreamAndNameSpaceMapping (unsigned serializedFileIndex)
+{
+ // Unload the file any way
+ // This saves memory - especially when reimporting lots of assets like when rebuilding the library
+ CleanupStream(m_Streams[serializedFileIndex]);
+
+ m_GlobalToLocalNameSpace[serializedFileIndex].clear ();
+ m_LocalToGlobalNameSpace[serializedFileIndex].clear ();
+}
+
+static bool InitTempWriteFile (FileCacherWrite& writer, const std::string& path, unsigned cacheSize)
+{
+ string tempWriteFileName = GenerateUniquePathSafe (path);
+ if (tempWriteFileName.empty())
+ return false;
+
+ writer.InitWriteFile(path, cacheSize);
+
+ return true;
+}
+
+int PersistentManager::WriteFile (const std::string& path, BuildTargetSelection target, int options)
+{
+ PROFILER_AUTO_THREAD_SAFE(gWriteFileProfiler, NULL);
+
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+
+ int serializedFileIndex;
+ serializedFileIndex = InsertPathNameInternal(path, false);
+ if (serializedFileIndex == -1)
+ return kNoError;
+
+ bool needsWrite = TestNeedWriteFile(serializedFileIndex);
+
+ // Early out
+ if (!needsWrite)
+ {
+ // @TODO: THIS SHOULD NOT BE HACKED IN HERE. Make test coverage against increased leaking then remove this and call CleanupStream explicitly.
+ CleanupStreamAndNameSpaceMapping(serializedFileIndex);
+ return kNoError;
+ }
+
+ set<SInt32> writeObjects;
+ if (options & kDontReadObjectsFromDiskBeforeWriting)
+ {
+ GetLoadedInstanceIDsAtPath (path, &writeObjects);
+ Assert(!writeObjects.empty());
+ }
+ else
+ {
+ // Load all writeobjects into memory
+ // (dont use LoadFileCompletely, since that reads all objects
+ // even those that might have been changed in memory)
+ GetInstanceIDsAtPath (path, &writeObjects);
+ }
+
+ vector<WriteData> writeData;
+
+ for (set<SInt32>::iterator i=writeObjects.begin ();i != writeObjects.end ();i++)
+ {
+ SInt32 instanceID = *i;
+
+ // Force load object from disk.
+ Object* o = dynamic_instanceID_cast<Object*> (instanceID);
+
+ if (o == NULL)
+ continue;
+
+ #if UNITY_EDITOR
+ // Disable text serialization for terrain data. Just too much data, you'd never want to merge.
+ int cid = o->GetClassID();
+ if (IsClassNonTextSerialized(cid))
+ options &= ~kAllowTextSerialization;
+ #endif
+
+ AssertIf (o != NULL && !o->IsPersistent ());
+
+ SerializedObjectIdentifier identifier;
+ m_Remapper->InstanceIDToSerializedObjectIdentifier(instanceID, identifier);
+
+ Assert (identifier.serializedFileIndex == serializedFileIndex);
+
+ DebugAssertIf (!o->IsPersistent ());
+ DebugAssertIf (m_Remapper->GetSerializedFileIndex (instanceID) != serializedFileIndex);
+ DebugAssertIf (!m_Remapper->IsSetup (identifier));
+
+ writeData.push_back(WriteData (identifier.localIdentifierInFile, instanceID, BuildUsageTag()));
+ }
+
+ sort(writeData.begin(), writeData.end());
+
+ int result = WriteFileInternal(path, serializedFileIndex, &writeData[0], writeData.size(), NULL, target, options);
+ if (result != kNoError && options & kAllowTextSerialization)
+ // Try binary serialization as a fallback.
+ result = WriteFileInternal(path, serializedFileIndex, &writeData[0], writeData.size(), NULL, target, options &~kAllowTextSerialization);
+
+ return result;
+}
+
+int PersistentManager::WriteFileInternal (const string& path, int serializedFileIndex, const WriteData* writeObjectData, int size, VerifyWriteObjectCallback* verifyCallback, BuildTargetSelection target, int options)
+{
+ //printf_console("Writing file %s\n", pathName.c_str());
+
+ // Create writing tools
+ CachedWriter writer;
+
+ FileCacherWrite serializedFileWriter;
+ FileCacherWrite resourceImageWriters[kNbResourceImages];
+ if (!InitTempWriteFile (serializedFileWriter, "Temp/tempFile", kCacheSize))
+ return kFileCouldNotBeWritten;
+ writer.InitWrite(serializedFileWriter);
+
+ if (options & kBuildResourceImage)
+ {
+ for (int i=0;i<kNbResourceImages;i++)
+ {
+ string path = AppendPathNameExtension("Temp/tempFile", kResourceImageExtensions[i]);
+ if (!InitTempWriteFile (resourceImageWriters[i], path, kCacheSize))
+ return kFileCouldNotBeWritten;
+ writer.InitResourceImage((ActiveResourceImage)i, resourceImageWriters[i]);
+ }
+ }
+
+ // Cleanup old stream and mapping
+ CleanupStreamAndNameSpaceMapping(serializedFileIndex);
+
+ // Setup global to self namespace mapping
+ m_GlobalToLocalNameSpace[serializedFileIndex][serializedFileIndex] = 0;
+ m_LocalToGlobalNameSpace[serializedFileIndex][0] = serializedFileIndex;
+
+ // Create writable stream
+ //@TODO: Object name might want to be
+ SerializedFile* tempSerialize = UNITY_NEW_AS_ROOT(SerializedFile, kMemSerialization, kSerializedFileArea, "");
+ #if ENABLE_MEM_PROFILER
+ tempSerialize->SetDebugPath(PathIDToPathNameInternal(serializedFileIndex));
+ GetMemoryProfiler()->SetRootAllocationObjectName(tempSerialize, tempSerialize->GetDebugPath().c_str());
+ #endif
+
+ tempSerialize->InitializeWrite (writer, target, options);
+ m_Streams[serializedFileIndex].stream = tempSerialize;
+
+ m_ActiveNameSpace.push (serializedFileIndex);
+
+ bool writeSuccess = true;
+ // Write Objects in fileID order
+ for (int i=0;i<size;i++)
+ {
+ LocalIdentifierInFileType localIdentifierInFile = writeObjectData[i].localIdentifierInFile;
+ SInt32 instanceID = writeObjectData[i].instanceID;
+
+ SerializedObjectIdentifier identifier (serializedFileIndex, localIdentifierInFile);
+
+ bool shouldUnloadImmediately = false;
+
+ Object* o = Object::IDToPointer (instanceID);;
+ if (o == NULL)
+ {
+ if (options & kLoadAndUnloadAssetsDuringBuild)
+ {
+ o = dynamic_instanceID_cast<Object*> (instanceID);
+ shouldUnloadImmediately = true;
+ }
+
+ // Object can not be loaded, don't write it
+ if (o == NULL)
+ {
+ continue;
+ }
+ }
+
+ if (verifyCallback != NULL && !verifyCallback (o, target.platform))
+ writeSuccess = false;
+
+ tempSerialize->WriteObject (*o, localIdentifierInFile, writeObjectData[i].buildUsage);
+ o->ClearPersistentDirty ();
+
+ if (shouldUnloadImmediately)
+ UnloadObject(o);
+ }
+
+ m_ActiveNameSpace.pop();
+
+ writeSuccess = writeSuccess && tempSerialize->FinishWriting() && !tempSerialize->HasErrors();
+
+ // Delete temp stream
+ if (m_Streams[serializedFileIndex].stream != tempSerialize)
+ {
+ writeSuccess = false;
+ UNITY_DELETE (tempSerialize, kMemSerialization);
+ tempSerialize = NULL;
+ }
+
+ // Delete mappings
+ CleanupStreamAndNameSpaceMapping(serializedFileIndex);
+
+ if (!writeSuccess)
+ {
+// ErrorString ("Writing file: " + path + " failed. The temporary file " + serializedFileWriter.GetPathName() + " couldn't be written.");
+ return kFileCouldNotBeWritten;
+ }
+
+ // Atomically move the serialized file into the target location
+ string actualNewPathName = RemapToAbsolutePath (path);
+ if (!MoveReplaceFile (serializedFileWriter.GetPathName(), actualNewPathName))
+ {
+ ErrorString ("File " + path + " couldn't be written. Because moving " + serializedFileWriter.GetPathName() + " to " + actualNewPathName + " failed.");
+ return kFileCouldNotBeWritten;
+ }
+ SetFileFlags(actualNewPathName, kFileFlagTemporary, 0);
+
+
+ if (options & kBuildResourceImage)
+ {
+ // Move the resource images into the target location
+ for (int i=0;i<kNbResourceImages;i++)
+ {
+ string targetPath = AppendPathNameExtension(actualNewPathName, kResourceImageExtensions[i]);
+
+ ::DeleteFile(targetPath);
+
+ string tempWriteFileName = resourceImageWriters[i].GetPathName();
+
+ if (GetFileLength(tempWriteFileName) > 0)
+ {
+ if (!MoveReplaceFile (tempWriteFileName, targetPath))
+ {
+ ErrorString ("File " + path + " couldn't be written. Because moving " + tempWriteFileName + " to " + actualNewPathName + " failed.");
+ return kFileCouldNotBeWritten;
+ }
+ SetFileFlags(targetPath, kFileFlagTemporary, 0);
+ }
+ }
+ }
+
+ return kNoError;
+}
+
+#endif
+
+string PersistentManager::GetPathName (SInt32 memoryID)
+{
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+
+ SInt32 serializedFileIndex = m_Remapper->GetSerializedFileIndex (memoryID);
+ if (serializedFileIndex == -1)
+ return string ();
+ else
+ return PathIDToPathNameInternal (serializedFileIndex);
+}
+
+void PersistentManager::RegisterAndAwakeThreadedObjectAndUnlockIntegrationMutex (const ThreadedAwakeData& awake)
+{
+ // Register instance ID first and then unlock integration mutex so there is no chance of an object being loaded twice
+ AssertIf(!awake.completedThreadAwake);
+ if (awake.object != NULL)
+ {
+ Object::RegisterInstanceID(awake.object);
+
+ m_IntegrationMutex.Unlock();
+
+ AwakeFromLoadMode mode = (AwakeFromLoadMode)(kDidLoadFromDisk | kDidLoadThreaded);
+ AwakeFromLoadQueue::PersistentManagerAwakeSingleObject(*awake.object, awake.oldType, mode, awake.checkConsistency, gSafeBinaryReadCallback);
+}
+ else
+ {
+ m_IntegrationMutex.Unlock();
+ }
+}
+
+
+#if THREADED_LOADING
+void PersistentManager::AllowIntegrationWithTimeoutAndWait ()
+{
+ m_IntegrationMutex.Lock();
+ m_AllowIntegrateThreadedObjectsWithTimeout = true;
+ m_IntegrationMutex.Unlock();
+
+ // Wait until the integration thread has integrated all assets
+ while (true)
+ {
+ m_IntegrationMutex.Lock();
+
+ if (m_ThreadedObjectActivationQueue.empty())
+ {
+ m_IntegrationMutex.Unlock();
+ break;
+ }
+ else
+ {
+ m_IntegrationMutex.Unlock();
+ }
+
+ Thread::Sleep(0.1F);
+ }
+
+ m_IntegrationMutex.Lock();
+ m_AllowIntegrateThreadedObjectsWithTimeout = false;
+ m_IntegrationMutex.Unlock();
+}
+#else
+void PersistentManager::AllowIntegrationWithTimeoutAndWait ()
+{
+ IntegrateAllThreadedObjects();
+}
+#endif
+
+#if DEBUG_THREAD_LOAD
+int gDependencyCounter = 0;
+int gDependencyCounterCost = 0;
+int gDependencyCounterCostActivation = 0;
+int gDependencyCounterCostNonActivation = 0;
+int gDependencyCounterCostNotFound = 0;
+#endif
+
+Object* PersistentManager::LoadFromActivationQueue (int heapID)
+{
+ PROFILER_AUTO_THREAD_SAFE(gFindInActivationQueueProfiler, NULL);
+
+ ASSERT_RUNNING_ON_MAIN_THREAD
+
+ LOCK_MUTEX(m_IntegrationMutex, gLoadFromActivationQueueStall);
+
+ ThreadedObjectActivationMap::iterator imap = m_ThreadedObjectActivationMap.find(heapID);
+
+ if (imap != m_ThreadedObjectActivationMap.end())
+ {
+ ThreadedObjectActivationQueue::iterator i = imap->second;
+ if (!i->completedThreadAwake)
+ {
+ ErrorString("Internal thread activation error. Activating object that has not been fully thread loaded.");
+ m_IntegrationMutex.Unlock();
+
+ return NULL;
+ }
+
+ ThreadedAwakeData data = *i;
+ m_ThreadedObjectActivationQueue.erase(i);
+ m_ThreadedObjectActivationMap.erase(imap);
+ #if DEBUG_THREAD_LOAD
+// printf_debug_thread("Activating dependency %d / %d [%d]\n", ++gDependencyCounter, gDependencyCounterCost, thisCost);
+// if (thisCost > 300)
+// {
+// printf_debug_thread("");
+// }
+ #endif
+
+ RegisterAndAwakeThreadedObjectAndUnlockIntegrationMutex(data);
+
+ return data.object;
+ }
+
+ #if DEBUG_THREAD_LOAD
+// if (thisCost > 300)
+// {
+// printf_debug_thread("Expensive load from activation queue and not found [%d]\n", thisCost);
+// }
+ #endif
+
+ m_IntegrationMutex.Unlock();
+
+ return NULL;
+}
+
+// GetFromActivationQueue is called a lot, so this function must be very efficient.
+// In one of the games called Harvest, m_ThreadedObjectActivationQueue contained 70000 items, and this function was called 5678 times
+// so linear searching was ubber slow!!!
+Object* PersistentManager::GetFromActivationQueue (int heapID)
+{
+ PROFILER_AUTO_THREAD_SAFE(gFindInActivationQueueProfiler, NULL);
+
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_IntegrationMutex,gLoadFromActivationQueueStall);
+
+ ThreadedObjectActivationMap::iterator item = m_ThreadedObjectActivationMap.find(heapID);
+ if (item != m_ThreadedObjectActivationMap.end())
+ {
+ return item->second->object;
+ }
+
+ AssertIf(m_OnDemandThreadLoadedObjects.count(heapID));
+
+ return NULL;
+}
+
+bool PersistentManager::FindInActivationQueue (int heapID)
+{
+ PROFILER_AUTO_THREAD_SAFE(gFindInActivationQueueProfiler, NULL);
+
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_IntegrationMutex, gLoadFromActivationQueueStall);
+
+ ThreadedObjectActivationMap::iterator item = m_ThreadedObjectActivationMap.find(heapID);
+ if (item != m_ThreadedObjectActivationMap.end())
+ {
+ return true;
+ }
+
+ if (m_OnDemandThreadLoadedObjects.count(heapID))
+ return true;
+
+ return false;
+}
+
+bool PersistentManager::HasThreadedObjectsToIntegrate ()
+{
+ Mutex::AutoLock lock (m_IntegrationMutex);
+ return !m_ThreadedObjectActivationQueue.empty ();
+}
+
+///@TODO: Prevent Object destruction during AwakeFromLoad!!!
+
+void PersistentManager::IntegrateThreadedObjects (float timeout)
+{
+ // Early out if there is nothing to be integrated.
+ if (!m_AllowIntegrateThreadedObjectsWithTimeout)
+ return;
+
+ if (!m_IntegrationMutex.TryLock())
+ {
+ //printf_debug_thread("\nINTEGRATION THREAD IS LOCKED\n");
+ return;
+ }
+
+ if (m_ThreadedObjectActivationQueue.empty() || !m_AllowIntegrateThreadedObjectsWithTimeout)
+ {
+ m_IntegrationMutex.Unlock();
+ return;
+ }
+
+ double startTime = GetTimeSinceStartup();
+ #if DEBUG_THREAD_LOAD
+ int integratedObjects = 0;
+ #endif
+
+ while (true)
+ {
+ ThreadedAwakeData awake = m_ThreadedObjectActivationQueue.front();
+
+ if (!awake.completedThreadAwake)
+ {
+ ErrorString("Stalling Integration because ThreadAwake for object is not completed");
+ break;
+ }
+
+ #if DEBUG_THREAD_LOAD
+ integratedObjects++;
+ #endif
+ m_ThreadedObjectActivationMap.erase(awake.instanceID);
+ m_ThreadedObjectActivationQueue.pop_front();
+
+ #if DEBUG_THREAD_LOAD_LONG_ACTIVATE
+ double time = GetTimeSinceStartup();
+ #endif
+
+ RegisterAndAwakeThreadedObjectAndUnlockIntegrationMutex(awake);
+
+ #if DEBUG_THREAD_LOAD_LONG_ACTIVATE
+ time = GetTimeSinceStartup() - time;
+ time *= 1000.0F;
+ if (time > 30.0F)
+ {
+ printf_console("Long thread activation time (%d ms) for object %s (%s)\n", (int)time, awake.object->GetName(), awake.object->GetClassName().c_str());
+ }
+ #endif
+
+ if (!m_AllowIntegrateThreadedObjectsWithTimeout || !m_IntegrationMutex.TryLock())
+ {
+ #if DEBUG_THREAD_LOAD
+ startTime = (GetTimeSinceStartup() - startTime) * 1000.0F;
+ printf_debug_thread("Integrate Threaded Objects ms: %f %d\n\n", (float)startTime, integratedObjects);
+ #endif
+ return;
+ }
+
+ double delta = (GetTimeSinceStartup() - startTime);
+ if (m_ThreadedObjectActivationQueue.empty() || delta > timeout)
+ break;
+ }
+
+ #if DEBUG_THREAD_LOAD
+ startTime = (GetTimeSinceStartup() - startTime) * 1000.0F;
+ printf_debug_thread("Integrate Threaded Objects ms: %f %d\n\n", (float)startTime, integratedObjects);
+ #endif
+
+ m_IntegrationMutex.Unlock();
+}
+
+void PersistentManager::IntegrateAllThreadedObjects ()
+{
+ AwakeFromLoadQueue awakeQueue (kMemTempAlloc);
+
+ PrepareAllThreadedObjectsStep1 (awakeQueue);
+ awakeQueue.RegisterObjectInstanceIDs();
+ IntegrateAllThreadedObjectsStep2 (awakeQueue);
+}
+
+void PersistentManager::PrepareAllThreadedObjectsStep1 (AwakeFromLoadQueue& awakeQueue)
+{
+ AQUIRE_AUTOLOCK (m_IntegrationMutex, gLoadFromActivationQueueStall);
+
+ // Add to AwakeFromLoadQueue - this will take care of ensuring sort order
+ awakeQueue.Reserve(m_ThreadedObjectActivationQueue.size());
+
+ ThreadedObjectActivationQueue::iterator end = m_ThreadedObjectActivationQueue.end();
+ for (ThreadedObjectActivationQueue::iterator i=m_ThreadedObjectActivationQueue.begin();i != end;++i )
+ {
+ ThreadedAwakeData& awake = *i;
+
+ if (awake.object)
+ {
+ Assert (awake.completedThreadAwake);
+ awakeQueue.Add(*awake.object, awake.oldType, awake.checkConsistency);
+ }
+ }
+ m_ThreadedObjectActivationQueue.clear();
+ m_ThreadedObjectActivationMap.clear();
+}
+
+void PersistentManager::IntegrateAllThreadedObjectsStep2 (AwakeFromLoadQueue& awakeQueue)
+{
+ Assert(m_ThreadedObjectActivationQueue.empty() && m_ThreadedObjectActivationMap.empty());
+
+ // Invoke AwakeFromLoadQueue in sorted order
+ AwakeFromLoadMode awakeMode = (AwakeFromLoadMode)(kDidLoadFromDisk | kDidLoadThreaded);
+
+ awakeQueue.PersistentManagerAwakeFromLoad(awakeMode, gSafeBinaryReadCallback);
+
+ Assert(m_ThreadedObjectActivationQueue.empty() && m_ThreadedObjectActivationMap.empty());
+}
+
+
+bool PersistentManager::ReloadFromDisk (Object* obj)
+{
+ PROFILER_AUTO_THREAD_SAFE(gReadObjectProfiler, obj);
+
+#if DEBUGMODE
+ AssertIf(!m_AllowLoadingFromDisk);
+#endif
+
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+
+ SerializedObjectIdentifier identifier;
+ if (!m_Remapper->InstanceIDToSerializedObjectIdentifier(obj->GetInstanceID(), identifier))
+ {
+ ErrorString("Trying to reload asset from disk that is not stored on disk");
+ return false;
+ }
+
+ SerializedFile* stream = GetSerializedFileInternal(identifier.serializedFileIndex);
+ if (stream == NULL)
+ {
+ ErrorString("Trying to reload asset but can't find object on disk");
+ return false;
+ }
+
+ m_ActiveNameSpace.push (identifier.serializedFileIndex);
+ TypeTree* oldType;
+ bool didTypeTreeChange;
+ stream->ReadObject (identifier.localIdentifierInFile, obj->GetInstanceID(), kCreateObjectDefault, true, &oldType, &didTypeTreeChange, &obj);
+ m_ActiveNameSpace.pop ();
+
+ // Awake the object
+ if (obj)
+ {
+ AwakeFromLoadQueue::PersistentManagerAwakeSingleObject (*obj, oldType, kDidLoadFromDisk, didTypeTreeChange, gSafeBinaryReadCallback);
+ }
+
+ return true;
+}
+
+Object* PersistentManager::ReadObject (int heapID)
+{
+ PROFILER_AUTO_THREAD_SAFE(gReadObjectProfiler, NULL);
+
+#if DEBUGMODE
+ AssertIf(!m_AllowLoadingFromDisk);
+#endif
+
+ #if !UNITY_EDITOR
+ AssertIf(heapID < 0);
+ #endif
+
+ #if DEBUG_MAINTHREAD_LOADING
+ double time = GetTimeSinceStartup ();
+ #endif
+
+ ASSERT_RUNNING_ON_MAIN_THREAD
+
+ LOCK_MUTEX(m_Mutex, gLoadLockPersistentManager);
+
+ Object* o = LoadFromActivationQueue(heapID);
+ if (o != NULL)
+{
+ m_Mutex.Unlock();
+ return o;
+ }
+
+ // Find and load the right stream
+ SerializedObjectIdentifier identifier;
+ if (!m_Remapper->InstanceIDToSerializedObjectIdentifier(heapID, identifier))
+ {
+ m_Mutex.Unlock();
+ return NULL;
+ }
+
+#if DEBUGMODE
+ AssertIf(!m_IsLoadingSceneFile && StrICmp(GetPathNameExtension(PathIDToPathNameInternal(identifier.serializedFileIndex)),"unity") == 0);
+#endif
+
+ SerializedFile* stream = GetSerializedFileInternal (identifier.serializedFileIndex);
+ if (stream == NULL)
+ {
+ #if DEBUG_MAINTHREAD_LOADING
+ LogString(Format("--- Loading from main thread failed loading stream %f", (GetTimeSinceStartup () - time) * 1000.0F));
+ #endif
+
+ m_Mutex.Unlock();
+ return NULL;
+}
+
+ AssertIf(Object::IDToPointer (heapID) != NULL);
+
+ // Find file id in stream and read the object
+
+ m_ActiveNameSpace.push (identifier.serializedFileIndex);
+ TypeTree* oldType;
+ bool didTypeTreeChange;
+ o = NULL;
+ stream->ReadObject (identifier.localIdentifierInFile, heapID, kCreateObjectDefault, true, &oldType, &didTypeTreeChange, &o);
+ m_ActiveNameSpace.pop ();
+
+ // Awake the object
+ if (o)
+ {
+ AwakeFromLoadQueue::PersistentManagerAwakeSingleObject (*o, oldType, kDidLoadFromDisk, didTypeTreeChange, gSafeBinaryReadCallback);
+ }
+
+// printf_console ("Read %d %s (%s)\n", heapID, o ? o->GetName () : "<null>", o ? o->GetClassName ().c_str() : "");
+
+ m_Mutex.Unlock();
+
+ #if DEBUG_MAINTHREAD_LOADING
+ if (o)
+ {
+ LogString(Format("--- Loading from main thread %s (%s) %f ms", o->GetName(), o->GetClassName().c_str(), (GetTimeSinceStartup () - time) * 1000.0F));
+ }
+ else
+ {
+ LogString(Format("--- Loading from main thread (NULL) %f ms", (GetTimeSinceStartup () - time) * 1000.0F));
+ }
+ #endif
+
+ return o;
+}
+
+void PersistentManager::SetupThreadActivationQueueObject (ThreadedAwakeData& awakeData, TypeTree* oldType, bool didTypeTreeChange)
+{
+ Object* obj = awakeData.object;
+ if (obj != NULL)
+{
+ awakeData.oldType = oldType;
+ awakeData.checkConsistency = didTypeTreeChange;
+ obj->AwakeFromLoadThreaded();
+ }
+ awakeData.completedThreadAwake = true;
+}
+
+ThreadedAwakeData* PersistentManager::CreateThreadActivationQueueEntry (SInt32 instanceID)
+{
+ DebugAssertIf(Object::IDToPointerThreadSafe(instanceID) != NULL);
+
+ ThreadedAwakeData awake;
+ awake.instanceID = instanceID;
+ awake.checkConsistency = false;
+ awake.completedThreadAwake = false;
+ awake.oldType = NULL;
+ awake.object = NULL;
+
+ ThreadedAwakeData* result;
+ m_IntegrationMutex.Lock();
+
+ m_ThreadedObjectActivationQueue.push_back(awake);
+ result = &m_ThreadedObjectActivationQueue.back();
+ m_ThreadedObjectActivationMap[instanceID] = --m_ThreadedObjectActivationQueue.end();
+
+ m_IntegrationMutex.Unlock();
+ DebugAssertIf(Object::IDToPointerThreadSafe(instanceID) != NULL);
+ return result;
+}
+
+void PersistentManager::CheckInstanceIDsLoaded (SInt32* heapIDs, int size)
+{
+ m_IntegrationMutex.Lock();
+
+ set<SInt32> ids;
+
+ // Search through threaded object activation queue and activate the object if necessary.
+ ThreadedObjectActivationQueue::iterator i, end;
+ end = m_ThreadedObjectActivationQueue.end();
+ for (i=m_ThreadedObjectActivationQueue.begin();i != end;i++)
+ ids.insert(i->instanceID);
+
+ for (int j=0;j<size;j++)
+ {
+ if (ids.count(heapIDs[j]))
+ heapIDs[j] = 0;
+ }
+
+ m_IntegrationMutex.Unlock();
+
+ // Check which objects are already loaded all at once to lock object creation only once for a short amount of time
+ // Since we have locked persistentmanager already no objects can be loaded in the mean time
+ LockObjectCreation();
+ Object::CheckInstanceIDsLoaded(heapIDs, size);
+ UnlockObjectCreation();
+}
+
+Object* PersistentManager::ReadObjectThreaded (int heapID)
+{
+#if DEBUGMODE
+ AssertIf(!m_AllowLoadingFromDisk);
+#endif
+
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ Object* o = GetFromActivationQueue(heapID);
+ if (o != NULL)
+ return o;
+
+ // Find and load the right stream
+ SerializedObjectIdentifier identifier;
+ if (!m_Remapper->InstanceIDToSerializedObjectIdentifier(heapID, identifier))
+ return NULL;
+
+ SerializedFile* stream = GetSerializedFileInternal (identifier.serializedFileIndex);
+ if (stream == NULL)
+ return NULL;
+#if DEBUGMODE
+ AssertIf(!m_IsLoadingSceneFile && PathIDToPathNameInternal(identifier.serializedFileIndex).find(".unity") != string::npos);
+#endif
+ AssertIf(Object::IDToPointerThreadSafe (heapID) != NULL);
+
+ // Find file id in stream and read the object
+
+ m_ActiveNameSpace.push (identifier.serializedFileIndex);
+ TypeTree* oldType;
+ bool didTypeTreeChange;
+
+ ThreadedAwakeData* awakeData = CreateThreadActivationQueueEntry(heapID);
+
+ // Inject into m_OnDemandThreadLoadedObjects so we can track which objects have been read during a LoadFileCompletelyThreaded or LoadThreadedObjects.
+ // This prevents loading objects twice
+ m_IntegrationMutex.Lock();
+ m_OnDemandThreadLoadedObjects.insert(heapID);
+ m_IntegrationMutex.Unlock();
+
+ awakeData->object = NULL;
+ stream->ReadObject (identifier.localIdentifierInFile, heapID, kCreateObjectFromNonMainThread, !m_Remapper->IsSceneID (heapID), &oldType, &didTypeTreeChange, &awakeData->object);
+
+ m_ActiveNameSpace.pop ();
+
+ o = awakeData->object;
+ SetupThreadActivationQueueObject(*awakeData, oldType, didTypeTreeChange);
+
+ return o;
+}
+
+void PersistentManager::LoadObjectsThreaded (SInt32* heapIDs, int size, LoadProgress* loadProgress)
+{
+ if (size == 0)
+ return;
+
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ vector<SInt32> heapIDsCopy;
+ heapIDsCopy.assign(heapIDs, heapIDs + size);
+
+ // Check which objects are already loaded all at once to lock object creation only once for a short amount of time
+ // Since we have locked persistentmanager already no objects can be loaded in the mean time
+ CheckInstanceIDsLoaded(&heapIDsCopy[0], size);
+
+ #if DEBUGMODE
+ m_IsLoadingSceneFile = true;
+ #endif
+
+ for (int i=0;i<size;i++)
+ {
+ SInt32 heapID = heapIDsCopy[i];
+ if (heapID == 0)
+ {
+ if (loadProgress)
+ loadProgress->ItemProcessed ();
+ continue;
+ }
+
+ m_IntegrationMutex.Lock();
+ if (m_OnDemandThreadLoadedObjects.count(heapID))
+ {
+ if (loadProgress)
+ loadProgress->ItemProcessed ();
+ m_IntegrationMutex.Unlock();
+ continue;
+ }
+ m_IntegrationMutex.Unlock();
+
+ DebugAssertIf(Object::IDToPointerThreadSafe(heapID) != NULL);
+ //DebugAssertIf(FindInActivationQueue(heapID));
+
+ // Find and load the right stream
+ SerializedObjectIdentifier identifier;
+ if (!m_Remapper->InstanceIDToSerializedObjectIdentifier(heapID, identifier))
+ {
+ if (loadProgress)
+ loadProgress->ItemProcessed ();
+ continue;
+ }
+
+ SerializedFile* stream = GetSerializedFileInternal (identifier.serializedFileIndex);
+ if (stream == NULL)
+ {
+ if (loadProgress)
+ loadProgress->ItemProcessed ();
+ continue;
+ }
+
+ // Find file id in stream and read the object
+
+ m_ActiveNameSpace.push (identifier.serializedFileIndex);
+ TypeTree* oldType;
+ bool didTypeTreeChange;
+
+ ThreadedAwakeData* awakeData = CreateThreadActivationQueueEntry (heapID);
+ awakeData->object = NULL;
+ stream->ReadObject (identifier.localIdentifierInFile, heapID, kCreateObjectFromNonMainThread, true, &oldType, &didTypeTreeChange, &awakeData->object);
+ if (loadProgress)
+ loadProgress->ItemProcessed ();
+
+ AssertIf (m_Remapper->IsSceneID (heapID));
+
+ SetupThreadActivationQueueObject(*awakeData, oldType, didTypeTreeChange);
+
+ m_ActiveNameSpace.pop ();
+}
+
+ m_IntegrationMutex.Lock();
+ m_OnDemandThreadLoadedObjects.clear();
+ m_IntegrationMutex.Unlock();
+
+ #if DEBUGMODE
+ m_IsLoadingSceneFile = false;
+ #endif
+ }
+
+int PersistentManager::LoadFileCompletelyThreaded (const std::string& pathname, LocalIdentifierInFileType* fileIDs, SInt32* instanceIDs, int size, bool loadScene, LoadProgress* loadProgress)
+{
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadFromActivationQueueStall);
+
+ // Find Stream
+ int pathID = InsertPathNameInternal (pathname, true);
+ SerializedFile* stream = GetSerializedFileInternal (pathID);
+ if (stream == NULL)
+ return kFileCouldNotBeRead;
+
+ AssertIf(fileIDs != NULL && size == -1);
+ AssertIf(instanceIDs != NULL && size == -1);
+
+ // Get all file IDs we want to load and generate instance ids
+ vector<LocalIdentifierInFileType> fileIDsVector;
+ vector<SInt32> instanceIDsVector;
+ if (size == -1)
+ {
+ stream->GetAllFileIDs (&fileIDsVector);
+ fileIDs = &fileIDsVector[0];
+ size = fileIDsVector.size();
+ if (loadProgress)
+ loadProgress->totalItems += size;
+ instanceIDsVector.resize(size);
+ instanceIDs = &instanceIDsVector[0];
+ }
+
+ // In the editor we can not use preallocate ranges since fileID's might be completely arbitrary ranges
+ if (loadScene && !UNITY_EDITOR)
+ {
+ LocalIdentifierInFileType highestFileID = 0;
+ for (int i=0;i<size;i++)
+ {
+ AssertIf(fileIDs[i] < 0);
+ highestFileID = max(highestFileID, fileIDs[i]);
+ }
+
+ m_Remapper->PreallocateIDs(highestFileID, pathID);
+
+ for (int i=0;i<size;i++)
+ {
+ LocalIdentifierInFileType fileID = fileIDs[i];
+ AssertIf(m_Remapper->IsSetup(SerializedObjectIdentifier(pathID, fileID)));
+ instanceIDs[i] = m_Remapper->m_ActivePreallocatedIDBase + fileID * 2;
+ }
+
+ #if DEBUGMODE
+ //SHOULDN"T BE NEEDED!!!!!! - TAKE THIS OUT DEBUG ONLY!!!!
+ CheckInstanceIDsLoaded(&instanceIDs[0], size);
+ for (int i=0;i<size;i++)
+ {
+ AssertIf(instanceIDs[i] == 0);
+ }
+ #endif
+ }
+ else
+ {
+ for (int i=0;i<size;i++)
+ {
+ LocalIdentifierInFileType fileID = fileIDs[i];
+ SInt32 heapID = m_Remapper->GetOrGenerateMemoryID (SerializedObjectIdentifier(pathID, fileID));
+
+ if (heapID == 0)
+ {
+ AssertString ("Loading an object that was made unpersistent but wasn't destroyed before reloading it");
+ }
+ instanceIDs[i] = heapID;
+ }
+ // - Figure out which ones are already loaded
+ CheckInstanceIDsLoaded(&instanceIDs[0], size);
+
+ #if UNITY_EDITOR
+ // Ugly hack to prevent IsPersistent to be on for scene objects.
+ // Recursive serialization is to be blamed for this. When that is fixed this can be removed.
+ if (loadScene)
+ {
+ m_Remapper->m_LoadingSceneInstanceIDs.assign_clear_duplicates(instanceIDs, instanceIDs + size);
+ }
+ #endif
+ }
+
+ // Load all objects
+ m_ActiveNameSpace.push (pathID);
+ #if DEBUGMODE
+ m_IsLoadingSceneFile = true;
+ #endif
+
+ for (int i=0;i<size;i++)
+ {
+ SInt32 heapID = instanceIDs[i];
+ SInt32 fileID = fileIDs[i];
+
+ if (heapID == 0)
+ {
+ if (loadProgress)
+ loadProgress->ItemProcessed ();
+ continue;
+ }
+
+ m_IntegrationMutex.Lock();
+ if (m_OnDemandThreadLoadedObjects.count(heapID))
+ {
+ if (loadProgress)
+ loadProgress->ItemProcessed ();
+ m_IntegrationMutex.Unlock();
+ continue;
+ }
+ m_IntegrationMutex.Unlock();
+
+ DebugAssertIf(Object::IDToPointerThreadSafe (heapID) != NULL);
+
+ TypeTree* oldType;
+ bool didTypeTreeChange;
+ ThreadedAwakeData* awakeData = CreateThreadActivationQueueEntry(heapID);
+ awakeData->object = NULL;
+ stream->ReadObject (fileID, heapID, kCreateObjectFromNonMainThread, !loadScene, &oldType, &didTypeTreeChange, &awakeData->object);
+ if (loadProgress)
+ loadProgress->ItemProcessed ();
+
+ ////@TODO: NEED TO HANDLE DELETION OF OBJECTS WHEN THAT HAPPENS BETWEEN threaded loading and activation
+ SetupThreadActivationQueueObject(*awakeData, oldType, didTypeTreeChange);
+ }
+
+ #if DEBUGMODE
+ m_IsLoadingSceneFile = false;
+ #endif
+
+ m_ActiveNameSpace.pop ();
+
+ m_IntegrationMutex.Lock();
+ m_OnDemandThreadLoadedObjects.clear();
+ m_IntegrationMutex.Unlock();
+ if (loadScene)
+ {
+ #if UNITY_EDITOR
+ for (int i=0;i<size;i++)
+ m_Remapper->Remove (instanceIDs[i]);
+ m_Remapper->m_LoadingSceneInstanceIDs.clear();
+ #else
+ m_Remapper->ClearPreallocateIDs();
+ #endif
+
+ }
+
+ return kNoError;
+}
+
+int PersistentManager::LoadFileCompletely (const string& path)
+{
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ int result = GetPersistentManager().LoadFileCompletelyThreaded(path, NULL, NULL, -1, false, (LoadProgress*)NULL);
+ GetPersistentManager().IntegrateAllThreadedObjects ();
+
+ return result;
+}
+
+
+#if SUPPORT_INSTANCE_ID_REMAP_ON_LOAD
+void PersistentManager::RemapInstanceIDOnLoad (const std::string& srcPath, LocalIdentifierInFileType srcLocalFileID, const std::string& dstPath, LocalIdentifierInFileType dstLocalFileID)
+{
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ SerializedObjectIdentifier src;
+ SerializedObjectIdentifier dst;
+
+ src.serializedFileIndex = InsertPathNameInternal (srcPath, true);
+ src.localIdentifierInFile = srcLocalFileID;
+ Assert(src.serializedFileIndex != -1);
+
+ // Destination path needs to be inserted (in unity_web_old case)
+ dst.serializedFileIndex = InsertPathNameInternal (dstPath, true);
+ dst.localIdentifierInFile = dstLocalFileID;
+ Assert(dst.serializedFileIndex != -1);
+
+ m_InstanceIDRemap[src] = dst;
+}
+#endif // #if SUPPORT_INSTANCE_ID_REMAP_ON_LOAD
+
+
+
+#if UNITY_EDITOR
+
+void PersistentManager::SuggestFileIDToHeapIDs (const string& pathname, map<LocalIdentifierInFileType, SInt32>& fileIDToHeapIDHint)
+{
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ int serializedFileIndex = InsertPathNameInternal (pathname, true);
+
+ SerializedFile* stream = GetSerializedFileInternal (serializedFileIndex);
+ if (stream == NULL)
+ return;
+
+ vector<LocalIdentifierInFileType> fileIDs;
+ stream->GetAllFileIDs (&fileIDs);
+
+ for (vector<LocalIdentifierInFileType>::iterator i=fileIDs.begin ();i!=fileIDs.end ();++i)
+ {
+ LocalIdentifierInFileType fileID = *i;
+ SerializedObjectIdentifier identifier (serializedFileIndex, fileID);
+
+ // fileIDToHeapIDHint is a hint which is used to keep the same fileID when entering/exiting playmode for example.
+ // It is only a hint and may not be fulfilled
+ if (!m_Remapper->IsSetup(identifier))
+ {
+ if (fileIDToHeapIDHint.count(fileID))
+ {
+ int suggestedHeapID = fileIDToHeapIDHint.find(fileID)->second;
+ if (Object::IDToPointer (suggestedHeapID) == NULL && !m_Remapper->IsHeapIDSetup(suggestedHeapID))
+ {
+ m_Remapper->SetupRemapping(suggestedHeapID, identifier);
+ }
+ }
+ }
+ }
+}
+#endif
+
+void PersistentManager::GetLoadedInstanceIDsAtPath (const string& pathName, set<SInt32>* objects)
+{
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ AssertIf (objects == NULL);
+
+ int serializedFileIndex = InsertPathNameInternal (pathName, false);
+ if (serializedFileIndex != -1)
+ {
+ // Get all objects that were made persistent but might not already be written to the file
+ m_Remapper->GetAllLoadedObjectsAtPath (serializedFileIndex, objects);
+ }
+}
+
+void PersistentManager::GetPersistentInstanceIDsAtPath (const string& pathName, set<SInt32>* objects)
+{
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ AssertIf (objects == NULL);
+
+ int pathID = InsertPathNameInternal (pathName, true);
+ if (pathID == -1)
+ return;
+
+ // Get all objects that were made persistent but might not already be written to the file
+ m_Remapper->GetAllPersistentObjectsAtPath (pathID, objects);
+}
+
+void PersistentManager::GetInstanceIDsAtPath (const string& pathName, set<SInt32>* objects)
+{
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ AssertIf (objects == NULL);
+
+ int serializedFileIndex = InsertPathNameInternal (pathName, true);
+ if (serializedFileIndex == -1)
+ return;
+
+ SerializedFile* serialize = GetSerializedFileInternal (serializedFileIndex);
+ if (serialize)
+ {
+ // Get all objects in the file
+ vector<LocalIdentifierInFileType> fileIDs;
+ serialize->GetAllFileIDs (&fileIDs);
+ for (vector<LocalIdentifierInFileType>::iterator i=fileIDs.begin ();i!=fileIDs.end ();++i)
+ {
+ SerializedObjectIdentifier identifier (serializedFileIndex, *i);
+ SInt32 memoryID = m_Remapper->GetOrGenerateMemoryID (identifier);
+
+ if (memoryID != 0)
+ objects->insert (memoryID);
+ }
+ }
+
+ // Get all objects that were made persistent but might not already be written to the file
+ m_Remapper->GetAllLoadedObjectsAtPath (serializedFileIndex, objects);
+}
+
+void PersistentManager::GetInstanceIDsAtPath (const string& pathName, vector<SInt32>* objects)
+{
+ set<SInt32> temp;
+ GetInstanceIDsAtPath(pathName, &temp);
+ objects->assign(temp.begin(), temp.end());
+}
+
+int PersistentManager::CountInstanceIDsAtPath (const string& pathName)
+{
+ set<SInt32> objects;
+ GetInstanceIDsAtPath (pathName, &objects);
+ return objects.size ();
+}
+
+bool PersistentManager::IsObjectAvailable (int heapID)
+{
+ PROFILER_AUTO_THREAD_SAFE(gIsObjectAvailable, NULL);
+
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ //////////////////////// DO WE WANT THIS!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ if (FindInActivationQueue(heapID))
+ return true;
+
+ SerializedObjectIdentifier identifier;
+
+ if (!m_Remapper->InstanceIDToSerializedObjectIdentifier(heapID, identifier))
+ return false;
+
+ SerializedFile* stream = GetSerializedFileInternal (identifier.serializedFileIndex);
+ // Stream can't be found
+ if (stream == NULL)
+ return false;
+
+ if (!stream->IsAvailable (identifier.localIdentifierInFile))
+ return false;
+
+ // Check if the class can be produced
+ int classID = stream->GetClassID (identifier.localIdentifierInFile);
+ Object::RTTI* rtti = Object::ClassIDToRTTI (classID);
+ if (rtti && !rtti->isAbstract)
+ return true;
+ else
+ return false;
+}
+
+bool PersistentManager::IsObjectAvailableDontCheckActualFile (int heapID)
+{
+ PROFILER_AUTO_THREAD_SAFE(gIsObjectAvailable, NULL);
+
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ int serializedFileIndex = m_Remapper->GetSerializedFileIndex (heapID);
+ if (serializedFileIndex == -1)
+ return false;
+ else
+ return true;
+}
+
+
+void PersistentManager::Lock()
+{
+ LOCK_MUTEX(m_Mutex, gLoadLockPersistentManager);
+}
+
+void PersistentManager::Unlock()
+{
+ m_Mutex.Unlock();
+}
+
+void PersistentManager::DoneLoadingManagers()
+{
+ // We are done loading managers. Start instance IDs from a high constant value here,
+ // so new managers and built-in resources can be added without changed instanceIDs
+ // used by the content.
+
+ //AssertIf(m_Remapper->GetHighestInUseHeapID() > 10000);
+
+ if (m_Remapper->m_HighestMemoryID < 10000)
+ {
+ m_Remapper->m_HighestMemoryID = 10000;
+ }
+
+ Object::DoneLoadingManagers();
+}
+
+
+PersistentManager& GetPersistentManager ()
+{
+ AssertIf (gPersistentManager == NULL);
+ return *gPersistentManager;
+}
+
+PersistentManager* GetPersistentManagerPtr ()
+{
+ return gPersistentManager;
+}
+
+void CleanupPersistentManager()
+{
+ UNITY_DELETE( gPersistentManager, kMemManager);
+ gPersistentManager = NULL;
+}
+
+PersistentManager::PersistentManager (int options, int cacheCount)
+// list node size is base data type size + two node pointers.
+:
+#if ENABLE_CUSTOM_ALLOCATORS_FOR_STDMAP
+ m_ThreadedAwakeDataPool (false, "Remapper pool", sizeof (ThreadedAwakeData) + sizeof(void*)*2, 16 * 1024),
+ m_ThreadedAwakeDataPoolMap (false, "RemapperMap pool", sizeof (ThreadedObjectActivationQueue::iterator) + sizeof(void*)*5, 16 * 1024),
+ m_ThreadedObjectActivationQueue (m_ThreadedAwakeDataPool),
+ m_ThreadedObjectActivationMap (std::less<SInt32>(), m_ThreadedAwakeDataPoolMap),
+#endif
+ m_AllowIntegrateThreadedObjectsWithTimeout (false)
+{
+ AssertIf (gPersistentManager);
+ gPersistentManager = this;
+ m_Options = options;
+ m_CacheCount = cacheCount;
+ m_Remapper = UNITY_NEW_AS_ROOT(Remapper (),kMemSerialization, kRemapperAllocArea, "");
+ #if DEBUGMODE
+ m_IsLoadingSceneFile = true;
+ #endif
+ #if DEBUGMODE
+ m_PreventLoadingFromFile = -1;
+ m_AllowLoadingFromDisk = true;
+ #endif
+
+ InitializeStdConverters ();
+}
+
+PersistentManager::~PersistentManager ()
+{
+ AssertIf(m_Mutex.IsLocked());
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ AssertIf (!m_ActiveNameSpace.empty ());
+
+ for (StreamContainer::iterator i=m_Streams.begin ();i!=m_Streams.end ();++i)
+ CleanupStream(*i);
+
+ UNITY_DELETE(m_Remapper, kMemSerialization);
+ CleanupStdConverters();
+}
+
+#if DEBUGMODE
+void PersistentManager::SetDebugAssertLoadingFromFile (const std::string& path)
+{
+ if (path.empty())
+ {
+ m_PreventLoadingFromFile = -1;
+ }
+ else
+ {
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+ m_PreventLoadingFromFile = InsertPathNameInternal (path, false);
+ }
+}
+#endif
+
+
+static bool IsPathBuiltinResourceFile (const std::string& pathName)
+{
+ return StrICmp(pathName, "library/unity default resources") == 0 || StrICmp(pathName, "library/unity_web_old") == 0
+ || StrICmp(pathName, "library/unity editor resources") == 0;
+}
+
+StreamNameSpace& PersistentManager::GetStreamNameSpaceInternal (int nameSpaceID)
+{
+#if DEBUGMODE
+ AssertIf(m_PreventLoadingFromFile == nameSpaceID);
+#endif
+
+ StreamNameSpace& nameSpace = m_Streams[nameSpaceID];
+
+ // Stream already loaded
+ if (nameSpace.stream)
+ return nameSpace;
+
+ #if SUPPORT_SERIALIZATION_FROM_DISK
+
+ PROFILER_AUTO_THREAD_SAFE(gLoadStreamNameSpaceProfiler, NULL);
+
+ // Load Stream
+ string pathName = PathIDToPathNameInternal (nameSpaceID);
+ if (pathName.empty ())
+ return nameSpace;
+
+ // File not found
+ string absolutePath = RemapToAbsolutePath (pathName);
+ if (!IsFileCreated (absolutePath))
+ {
+ #if !UNITY_EDITOR
+ AssertString("PersistentManager: Failed to open file at path: " + pathName);
+ #endif
+ return nameSpace;
+ }
+
+ // Is Builtin resource file?
+ int options = 0;
+ if (IsPathBuiltinResourceFile(pathName))
+ options |= kIsBuiltinResourcesFile;
+
+ nameSpace.stream = UNITY_NEW_AS_ROOT(SerializedFile, kMemSerialization, kSerializedFileArea, "");
+ SET_ALLOC_OWNER(nameSpace.stream);
+ #if ENABLE_MEM_PROFILER
+ nameSpace.stream->SetDebugPath(pathName);
+ GetMemoryProfiler()->SetRootAllocationObjectName(nameSpace.stream, nameSpace.stream->GetDebugPath().c_str());
+ #endif
+
+ // Resource image loading is only supported in the player!
+ ResourceImageGroup group;
+ #if SUPPORT_RESOURCE_IMAGE_LOADING
+ for (int i=0;i<kNbResourceImages;i++)
+ {
+ string resourceImagePath = AppendPathNameExtension(absolutePath, kResourceImageExtensions[i]);
+ if (IsFileCreated (resourceImagePath))
+ group.resourceImages[i] = new ResourceImage(resourceImagePath, i == kStreamingResourceImage);
+ }
+ #endif
+
+ if (!nameSpace.stream->InitializeRead (RemapToAbsolutePath (pathName), group, kCacheSize, m_CacheCount, options))
+ {
+ CleanupStream(nameSpace);
+ return nameSpace;
+ }
+
+ PostLoadStreamNameSpace(nameSpace, nameSpaceID);
+#endif
+
+ return m_Streams[nameSpaceID];
+}
+
+#if SUPPORT_SERIALIZATION_FROM_DISK
+bool PersistentManager::LoadCachedFile (const std::string& pathName, const std::string actualAbsolutePath)
+{
+ PROFILER_AUTO_THREAD_SAFE(gLoadStreamNameSpaceProfiler, NULL);
+
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ int nameSpaceID = InsertPathNameInternal (pathName, true);
+ if (nameSpaceID == -1)
+ return false;
+
+ StreamNameSpace& nameSpace = m_Streams[nameSpaceID];
+
+ // Stream already loaded
+ if (nameSpace.stream)
+ ErrorString ("Tryng to load a stream which is already loaded.");
+
+ // File not found
+ if (!IsFileCreated (actualAbsolutePath))
+ return false;
+
+ ResourceImageGroup group;
+ nameSpace.stream = UNITY_NEW_AS_ROOT(SerializedFile,kMemSerialization, kSerializedFileArea, "");
+ #if ENABLE_MEM_PROFILER
+ nameSpace.stream->SetDebugPath(actualAbsolutePath);
+ GetMemoryProfiler()->SetRootAllocationObjectName(nameSpace.stream, nameSpace.stream->GetDebugPath().c_str());
+ #endif
+ if (!nameSpace.stream->InitializeRead (actualAbsolutePath, group, kCacheSize, m_CacheCount, kSerializeGameRelease))
+ {
+ CleanupStream(nameSpace);
+ return false;
+ }
+
+ nameSpace.stream->SetIsCachedFileStream(true);
+ PostLoadStreamNameSpace(nameSpace, nameSpaceID);
+
+ Mutex::AutoLock lock2 (m_MemoryLoadedOrCachedPathsMutex);
+ m_MemoryLoadedOrCachedPaths.insert(pathName);
+
+ return true;
+}
+#endif
+
+bool PersistentManager::LoadMemoryBlockStream (const std::string& pathName, UInt8** data, int offset, int end, const char* url)
+{
+ PROFILER_AUTO_THREAD_SAFE(gLoadStreamNameSpaceProfiler, NULL);
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ int nameSpaceID = InsertPathNameInternal (pathName, true);
+ if (nameSpaceID == -1)
+ return false;
+
+ StreamNameSpace& nameSpace = m_Streams[nameSpaceID];
+
+ // Stream already loaded
+ AssertIf (nameSpace.stream);
+
+ nameSpace.stream = UNITY_NEW_AS_ROOT(SerializedFile,kMemSerialization, kSerializedFileArea, "");
+
+#if ENABLE_MEM_PROFILER
+ nameSpace.stream->SetDebugPath(url?url:pathName);
+ GetMemoryProfiler()->SetRootAllocationObjectName(nameSpace.stream, nameSpace.stream->GetDebugPath().c_str());
+#endif
+
+ int options = kSerializeGameRelease;
+
+ // In NaCl & Flash, default resources are not loaded from disk,
+ // but also from a web stream.
+ // Need to set the proper flag here, so they don't get unloaded
+ // in Garbage Collection.
+ if (IsPathBuiltinResourceFile(pathName))
+ options |= kIsBuiltinResourcesFile;
+
+ if (!nameSpace.stream->InitializeMemoryBlocks (RemapToAbsolutePath(pathName), data, end, offset, options))
+ {
+ CleanupStream(nameSpace);
+ return false;
+ }
+
+ PostLoadStreamNameSpace(nameSpace, nameSpaceID);
+
+ m_MemoryLoadedOrCachedPathsMutex.Lock();
+ m_MemoryLoadedOrCachedPaths.insert(pathName);
+ m_MemoryLoadedOrCachedPathsMutex.Unlock();
+
+ return true;
+}
+
+
+#if SUPPORT_SERIALIZATION_FROM_DISK
+bool PersistentManager::LoadExternalStream (const std::string& pathName, const std::string& absolutePath, int flags, int readOffset)
+{
+ PROFILER_AUTO_THREAD_SAFE(gLoadStreamNameSpaceProfiler, NULL);
+
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ int nameSpaceID = InsertPathNameInternal (pathName, true);
+ if (nameSpaceID == -1)
+ return false;
+
+ StreamNameSpace& nameSpace = m_Streams[nameSpaceID];
+
+ // Stream already loaded
+ if (nameSpace.stream)
+ return false;
+
+ // File not found
+ if (!IsFileCreated (absolutePath))
+ return false;
+
+ ResourceImageGroup group;
+ nameSpace.stream = UNITY_NEW_AS_ROOT(SerializedFile,kMemSerialization, kSerializedFileArea, "");
+
+#if ENABLE_MEM_PROFILER
+ nameSpace.stream->SetDebugPath(absolutePath);
+ GetMemoryProfiler()->SetRootAllocationObjectName(nameSpace.stream, nameSpace.stream->GetDebugPath().c_str());
+#endif
+
+ if (!nameSpace.stream->InitializeRead (absolutePath, group, kCacheSize, m_CacheCount, flags, readOffset))
+ {
+ CleanupStream(nameSpace);
+ return false;
+ }
+
+ nameSpace.stream->SetIsCachedFileStream(true);
+ PostLoadStreamNameSpace(nameSpace, nameSpaceID);
+
+ m_MemoryLoadedOrCachedPathsMutex.Lock();
+ m_MemoryLoadedOrCachedPaths.insert(pathName);
+ m_MemoryLoadedOrCachedPathsMutex.Unlock();
+
+ return true;
+}
+#endif
+
+void PersistentManager::PostLoadStreamNameSpace (StreamNameSpace& nameSpace, int nameSpaceID)
+{
+ nameSpace.highestID = std::max (nameSpace.highestID, nameSpace.stream->GetHighestID ());
+ SET_ALLOC_OWNER ( this );
+ const dynamic_block_vector<FileIdentifier>& externalRefs = nameSpace.stream->GetExternalRefs ();
+ // Read all local pathnames and generate global<->localnamespace mapping
+ for (unsigned int i=0;i!=externalRefs.size ();i++)
+ {
+ int serializedFileIndex = InsertFileIdentifierInternal (externalRefs[i], true);
+ m_GlobalToLocalNameSpace[nameSpaceID][serializedFileIndex] = i + 1;
+ m_LocalToGlobalNameSpace[nameSpaceID][i + 1] = serializedFileIndex;
+ }
+
+ // Setup global to self namespace mapping
+ m_GlobalToLocalNameSpace[nameSpaceID][nameSpaceID] = 0;
+ m_LocalToGlobalNameSpace[nameSpaceID][0] = nameSpaceID;
+}
+
+bool PersistentManager::IsFileEmpty (const string& pathName)
+{
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ SerializedFile* serialize = GetSerializedFileInternal (InsertPathNameInternal (pathName, true));
+ if (serialize == NULL)
+ return true;
+ else
+ return serialize->IsEmpty ();
+}
+
+#if UNITY_EDITOR
+bool PersistentManager::DeleteFile (const string& pathName, DeletionFlags flag)
+{
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ int globalNameSpace = InsertPathNameInternal (pathName, true);
+ if (globalNameSpace == -1)
+ return false;
+
+ StreamNameSpace& stream = GetStreamNameSpaceInternal (globalNameSpace);
+
+ set<SInt32> objectsInFile;
+ GetInstanceIDsAtPath (pathName, &objectsInFile);
+
+ for (set<SInt32>::iterator i=objectsInFile.begin ();i != objectsInFile.end ();++i)
+ MakeObjectUnpersistent (*i, kDontDestroyFromFile);
+
+ if (flag & kDeleteLoadedObjects)
+ {
+ for (set<SInt32>::iterator i=objectsInFile.begin ();i != objectsInFile.end ();++i)
+ {
+ Object* obj = Object::IDToPointer(*i);
+ UnloadObject(obj);
+ }
+
+ #if DEBUGMODE
+ for (set<SInt32>::iterator i=objectsInFile.begin ();i != objectsInFile.end ();++i)
+ AssertIf (PPtr<Object> (*i));
+ #endif
+ }
+
+ #if DEBUGMODE
+ for (set<SInt32>::iterator i=objectsInFile.begin ();i != objectsInFile.end ();++i)
+ {
+ if (m_Remapper->GetSerializedFileIndex (*i) != -1)
+ {
+ Object* obj = PPtr<Object>(*i);
+ ErrorStringObject("NOT CORRECTLY UNLOADED!!!!", obj);
+ }
+ }
+ #endif
+
+ if (stream.stream)
+ CleanupStream(stream);
+
+ m_GlobalToLocalNameSpace[globalNameSpace].clear ();
+ m_LocalToGlobalNameSpace[globalNameSpace].clear ();
+
+ string absolutePath = RemapToAbsolutePath (pathName);
+ if (IsFileCreated (absolutePath))
+ {
+ if (!::DeleteFile (absolutePath))
+ return false;
+ }
+ return true;
+}
+
+#endif
+
+void PersistentManager::UnloadNonDirtyStreams ()
+{
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+ // printf_console("------ Unloading non dirty stream\n");
+ int dirtyStreams = 0;
+ int loadedStreams = 0;
+ int unloadedStreams = 0;
+ for (int i=0;i<m_Streams.size ();i++)
+ {
+ StreamNameSpace& nameSpace = m_Streams[i];
+ AssertIf (nameSpace.stream == NULL && !m_GlobalToLocalNameSpace[i].empty ());
+ if (nameSpace.stream == NULL)
+ continue;
+
+ if (nameSpace.stream->IsMemoryStream () || nameSpace.stream->IsCachedFileStream())
+ {
+ loadedStreams++;
+ continue;
+ }
+
+ bool unloadStream = true;
+ #if UNITY_EDITOR
+ unloadStream = !nameSpace.stream->IsFileDirty();
+ #endif
+
+ if (unloadStream)
+ {
+ unloadedStreams++;
+ CleanupStream(nameSpace);
+ m_GlobalToLocalNameSpace[i].clear ();
+ m_LocalToGlobalNameSpace[i].clear ();
+ }
+ else
+ {
+ FileIdentifier identifier = PathIDToFileIdentifierInternal(i);
+ printf_console("Can't unload serialized file because it is dirty: %s\n", identifier.pathName.c_str());
+ dirtyStreams++;
+ loadedStreams++;
+ }
+ }
+
+ printf_console("Unloading %d Unused Serialized files (Serialized files now loaded: %d / Dirty serialized files: %d)\n", unloadedStreams, loadedStreams, dirtyStreams);
+// printf_console("Streams that can't be unloaded Files %d\n", loadedStreams);
+// printf_console("ID mapping count %d -- %d \n", m_Remapper->m_FileToHeapID.size(), m_Remapper->m_FileToHeapID.size() * 24);
+// printf_console("RESURRRECT count %d -- %d \n", m_Remapper->m_ResurrectHeapID.size(), m_Remapper->m_ResurrectHeapID.size() * 24);
+}
+
+
+void PersistentManager::UnloadStreams ()
+{
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ for (int i=0;i<m_Streams.size ();i++)
+ {
+ StreamNameSpace& nameSpace = m_Streams[i];
+ AssertIf (nameSpace.stream == NULL && !m_GlobalToLocalNameSpace[i].empty ());
+ if (nameSpace.stream == NULL)
+ continue;
+/*
+ #if DEBUGMODE
+ set<SInt32> debug;
+ m_Remapper->GetAllLoadedObjectsAtPath (i, &debug);
+ AssertIf (!debug.empty ());
+ #endif
+*/
+ CleanupStream(nameSpace);
+
+ m_GlobalToLocalNameSpace[i].clear ();
+ m_LocalToGlobalNameSpace[i].clear ();
+ }
+}
+
+void PersistentManager::UnloadMemoryStreams ()
+{
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ for (int i=0;i<m_Streams.size ();i++)
+ {
+ StreamNameSpace& nameSpace = m_Streams[i];
+ AssertIf (nameSpace.stream == NULL && !m_GlobalToLocalNameSpace[i].empty ());
+ if (nameSpace.stream == NULL )
+ continue;
+
+ if (!nameSpace.stream->IsMemoryStream () && !nameSpace.stream->IsCachedFileStream())
+ continue;
+
+ #if DEBUGMODE
+ set<SInt32> debug;
+ m_Remapper->GetAllLoadedObjectsAtPath (i, &debug);
+ AssertIf (!debug.empty ());
+ #endif
+
+ CleanupStream(nameSpace);
+
+ m_GlobalToLocalNameSpace[i].clear ();
+ m_LocalToGlobalNameSpace[i].clear ();
+ }
+
+ Mutex::AutoLock lock2 (m_MemoryLoadedOrCachedPathsMutex);
+ m_MemoryLoadedOrCachedPaths.clear();
+}
+
+void PersistentManager::UnloadStream (const std::string& pathName)
+{
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ int nameSpaceID = InsertPathNameInternal (pathName, false);
+ if (nameSpaceID == -1)
+ return;
+
+ StreamNameSpace& nameSpace = m_Streams[nameSpaceID];
+ if (nameSpace.stream == NULL)
+ return;
+
+ CleanupStream(nameSpace);
+
+ m_GlobalToLocalNameSpace[nameSpaceID].clear ();
+ m_LocalToGlobalNameSpace[nameSpaceID].clear ();
+
+ Mutex::AutoLock lock2 (m_MemoryLoadedOrCachedPathsMutex);
+ m_MemoryLoadedOrCachedPaths.erase(pathName);
+}
+
+bool PersistentManager::HasMemoryOrCachedSerializedFile (const std::string& path)
+{
+ Mutex::AutoLock lock2 (m_MemoryLoadedOrCachedPathsMutex);
+ return m_MemoryLoadedOrCachedPaths.count(path) == 1;
+}
+
+void PersistentManager::ResetHighestFileIDAtPath (const string& pathName)
+{
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ int globalNameSpace = InsertPathNameInternal (pathName, true);
+ if (globalNameSpace == -1)
+ return;
+
+ AssertIf (m_Streams[globalNameSpace].stream != NULL);
+ m_Streams[globalNameSpace].highestID = 0;
+}
+
+#if UNITY_EDITOR
+// AwakeFromLoadQueue only supports this in the editor
+void PersistentManager::RegisterSafeBinaryReadCallback (SafeBinaryReadCallbackFunction* callback)
+{
+ AssertIf (gSafeBinaryReadCallback);
+ gSafeBinaryReadCallback = callback;
+}
+#endif
+
+void PersistentManager::RegisterInOrderDeleteCallback (InOrderDeleteCallbackFunction* callback)
+{
+ AssertIf (gInOrderDeleteCallback);
+ gInOrderDeleteCallback = callback;
+}
+
+SerializedFile* PersistentManager::GetSerializedFileInternal (const string& path)
+{
+ return GetSerializedFileInternal(InsertPathNameInternal (path, true));
+}
+
+SerializedFile* PersistentManager::GetSerializedFileInternal (int serializedFileIndex)
+{
+ if (serializedFileIndex == -1)
+ return NULL;
+
+ StreamNameSpace& stream = GetStreamNameSpaceInternal (serializedFileIndex);
+ return stream.stream;
+}
+
+
+bool PersistentManager::IsStreamLoaded (const std::string& pathName)
+{
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+
+ int globalNameSpace = InsertPathNameInternal (pathName, false);
+ if (globalNameSpace == -1)
+ return false;
+
+ StreamNameSpace& nameSpace = m_Streams[globalNameSpace];
+ return nameSpace.stream != NULL;
+}
+
+void PersistentManager::AddStream ()
+{
+ m_Streams.push_back (StreamNameSpace ());
+ m_GlobalToLocalNameSpace.push_back (IDRemap ());
+ m_LocalToGlobalNameSpace.push_back (IDRemap ());
+}
+
+void PersistentManager::DestroyFromFileInternal (int memoryID)
+{
+ SerializedObjectIdentifier identifier;
+ m_Remapper->InstanceIDToSerializedObjectIdentifier(memoryID, identifier);
+ SerializedFile* serialize = GetSerializedFileInternal (identifier.serializedFileIndex);
+ if (serialize)
+ serialize->DestroyObject (identifier.localIdentifierInFile);
+}
+
+string PersistentManager::RemapToAbsolutePath (const string& path)
+{
+ UserPathRemap::iterator found = m_UserPathRemap.find(path);
+ if (found != m_UserPathRemap.end())
+ return found->second;
+
+ return PathToAbsolutePath(path);
+}
+
+void PersistentManager::SetPathRemap (const string& path, const string& absoluteRemappedPath)
+{
+ if (!absoluteRemappedPath.empty())
+ {
+ Assert (m_UserPathRemap.count(path) == 0);
+ m_UserPathRemap.insert(make_pair(path, absoluteRemappedPath));
+ }
+ else
+ {
+ Assert (m_UserPathRemap.count(path) == 1);
+ m_UserPathRemap.erase(path);
+ }
+}
+
+#if UNITY_EDITOR
+bool PersistentManager::IsClassNonTextSerialized( int cid )
+{
+ return m_NonTextSerializedClasses.find (cid) != m_NonTextSerializedClasses.end();
+}
+#endif