From 15740faf9fe9fe4be08965098bbf2947e096aeeb Mon Sep 17 00:00:00 2001 From: chai Date: Wed, 14 Aug 2019 22:50:43 +0800 Subject: +Unity Runtime code --- Runtime/Misc/PreloadManager.cpp | 1013 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 1013 insertions(+) create mode 100644 Runtime/Misc/PreloadManager.cpp (limited to 'Runtime/Misc/PreloadManager.cpp') diff --git a/Runtime/Misc/PreloadManager.cpp b/Runtime/Misc/PreloadManager.cpp new file mode 100644 index 0000000..a5e1fc6 --- /dev/null +++ b/Runtime/Misc/PreloadManager.cpp @@ -0,0 +1,1013 @@ +#include "UnityPrefix.h" +#include "PreloadManager.h" +#include "Configuration/UnityConfigure.h" +#include "Runtime/Serialize/PersistentManager.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +///////// FUSIONFALL todo: Fix this include by moving it to player i guess +#include "Runtime/Misc/SaveAndLoadHelper.h" +#include "Runtime/Misc/Player.h" +#include "Runtime/Misc/AssetBundle.h" +#include "Runtime/Threads/ThreadUtility.h" +#include "Runtime/Serialize/SerializedFile.h" +#include "Runtime/Profiler/Profiler.h" +#include "Runtime/Graphics/LightmapSettings.h" +#include "Runtime/Misc/ReproductionLog.h" +#include "Runtime/Input/TimeManager.h" +#include "Runtime/Misc/BuildSettings.h" +#include "Runtime/Mono/MonoIncludes.h" +#include "Runtime/Graphics/SubstanceSystem.h" +#include "Runtime/Misc/AssetBundleUtility.h" + +#if UNITY_ANDROID + #include "PlatformDependent/AndroidPlayer/EntryPoint.h" +#endif +#if UNITY_WII +#include "PlatformDependent/Wii/WiiLoadingScreen.h" +#endif + + +PROFILER_INFORMATION (gPreloadLevel, "Application.LoadLevelAsync Integrate", kProfilerLoading); +PROFILER_INFORMATION (gPreloadBundle, "AssetBundle.LoadAsync Integrate", kProfilerLoading); +PROFILER_INFORMATION (gIntegrateAssetsInBackground, "Application.Integrate Assets in Background", kProfilerLoading); +PROFILER_INFORMATION (gAsyncOperationComplete, "Application.WaitForAsyncOperationToComplete", kProfilerLoading); + +#if THREADED_LOADING + #define THREADED_LOADING_MUTEX_AUTOLOCK(x) Mutex::AutoLock lock (x) +#else + #define THREADED_LOADING_MUTEX_AUTOLOCK(x) {} +#endif + +#define PROFILE_PRELOAD_MANAGER 0 +#if PROFILE_PRELOAD_MANAGER +#include "Runtime/Input/TimeManager.h" +#endif + +static void GetFileIDsForLoadingScene (const string& pathName, PreloadLevelOperation::LoadingMode loadingMode, vector& fileIDs, vector& managerIndices); +PreloadData::~PreloadData () +{} + +template +void PreloadData::Transfer (TransferFunction& transfer) +{ + Super::Transfer (transfer); + transfer.Transfer (m_Assets, "m_Assets"); +} + +IMPLEMENT_OBJECT_SERIALIZE(PreloadData) +IMPLEMENT_CLASS(PreloadData) + +PreloadManager* gPreloadManager = NULL; + +PreloadManager& GetPreloadManager() +{ + if (gPreloadManager != NULL) + return *gPreloadManager; + else + { + gPreloadManager = new PreloadManager(); + return *gPreloadManager; + } +} + +void ReleasePreloadManager() +{ + delete gPreloadManager; + gPreloadManager = NULL; +} + +void StopPreloadManager() +{ + if (gPreloadManager) + gPreloadManager->Stop(); +} + +PreloadManager::PreloadManager() +: m_ProcessingOperation (NULL) +{ + m_IntegrationOperation = NULL; +#if ENABLE_MONO + m_InitDomain = NULL; +#endif +#if THREADED_LOADING + m_Thread.SetName ("UnityPreload"); +#endif +} + +PreloadManager::~PreloadManager() +{ + Stop(); +} + +void PreloadManager::SetThreadPriority(ThreadPriority p) +{ +#if THREADED_LOADING + m_Thread.SetPriority(p); +#endif +} + +ThreadPriority PreloadManager::GetThreadPriority () +{ +#if THREADED_LOADING + return m_Thread.GetPriority(); +#else + return kNormalPriority; +#endif +} + +#if UNITY_EDITOR +void PreloadManager::RemoveStopPlaymodeOperations () +{ + THREADED_LOADING_MUTEX_AUTOLOCK(m_QueueMutex); + for (int i=0;iIsPlaymodeLoadLevel()) + { + op->CleanupCoroutine(); + op->Release(); + m_PreloadQueue.erase(m_PreloadQueue.begin() + i); + i--; + } + } +} +#endif + +void PreloadManager::Stop () +{ +#if THREADED_LOADING + m_Thread.SignalQuit(); + + { + PROFILER_AUTO(gIntegrateAssetsInBackground, NULL) + // Wait until loading is complete, to make sure everything is integrated so that we don't leak anything. + while (m_Thread.IsRunning()) + { + UpdatePreloadingSingleStep(true); + Thread::Sleep(0.01F); + } + } + + m_Thread.WaitForExit(); +#endif + + InvokeCoroutineCallbacks (); + + AssertIf(!m_CallCoroutineCallbackQueue.empty()); + + { + THREADED_LOADING_MUTEX_AUTOLOCK(m_QueueMutex); + + for (int i=0;iCleanupCoroutine(); + m_PreloadQueue[i]->Release(); + } + m_PreloadQueue.clear(); + m_CallCoroutineCallbackQueue.clear(); + } + + Assert(m_IntegrationOperation == NULL); + Assert(m_ProcessingOperation == NULL); + #if ENABLE_MONO + m_InitDomain = NULL; + #endif +} + +#if THREADED_LOADING +void* PreloadManager::Run (void* managerPtr) +{ +#if ENABLE_PROFILER + profiler_initialize_thread ("PreloadManager Thread", true); +#endif + + PreloadManager& manager = *static_cast (managerPtr); + manager.Run(); + +#if ENABLE_PROFILER + profiler_cleanup_thread(); +#endif + + return NULL; +} + +void PreloadManager::Run () +{ + #if ENABLE_MONO + mono_thread_attach(m_InitDomain); + m_InitDomain = NULL; + #endif + +#if PROFILE_PRELOAD_MANAGER + float startTime = GetTimeSinceStartup(); + #define LOG_PROFILER(x) printf_console(x, m_ProcessingOperation->GetDebugName().c_str(), (GetTimeSinceStartup() - startTime) * 1000.0F); startTime = GetTimeSinceStartup(); +#else + #define LOG_PROFILER(x) +#endif + + while (!m_Thread.IsQuitSignaled()) + { + m_QueueMutex.Lock(); + + if (!m_PreloadQueue.empty()) + { + Assert(m_IntegrationOperation == NULL); + + // Find highest priority queued item + int index = PreloadManager::FindTopPriorityOperation (m_PreloadQueue); + + // Grab item and remove it from queue + Assert(m_ProcessingOperation == NULL); + m_ProcessingOperation = m_PreloadQueue[index]; + + LOG_PROFILER("Begin Background load: %s [idle time: %f]\n"); + + m_PreloadQueue.erase(m_PreloadQueue.begin() + index); + + m_QueueMutex.Unlock(); + + m_LoadingMutex.Lock(); + + // Process it + m_ProcessingOperation->Perform(); + + LOG_PROFILER("Async background load complete: %s - %f ms\n"); + + bool hasIntegrate = m_ProcessingOperation->HasIntegrateMainThread(); + + // Integrate any work into main thread, wait for completion, + // so that the persistentmanager is kept clear until the integration thread is done + if (hasIntegrate) + { + AssertIf(m_IntegrationOperation != NULL); + + m_IntegrationOperation = m_ProcessingOperation; + + // This is needed in order to make sure that m_IntegrationOperation is properly synced when + // the signaled thread reads the value (otherwise we get an assert - so this is actually to prevent the assert. + UnityMemoryBarrier(); + + // Temporarily release the loading mutex so that the main thread can do + // LockPreloading() without deadlocking. + m_LoadingMutex.Unlock (); + + m_IntegrationSemaphore.WaitForSignal(); + Assert (NULL == m_IntegrationOperation); + + // Re-acquire the loading mutex while we finish the current operation. + m_LoadingMutex.Lock (); + } + + m_QueueMutex.Lock(); + // Pass operation over into m_CallCoroutineCallbackQueue, which will finally release it + m_CallCoroutineCallbackQueue.push_back(m_ProcessingOperation); + //m_ProcessingOperation->Release(); + LOG_PROFILER("Completed Integration step: %s - %f ms\n"); + m_ProcessingOperation = NULL; + + m_QueueMutex.Unlock(); + + m_LoadingMutex.Unlock(); + } + else + { + m_QueueMutex.Unlock(); + Thread::Sleep(0.1F); + } + } + #if ENABLE_MONO + mono_thread_detach(mono_thread_current ()); + #endif + +#undef PROFILE_PRELOAD_MANAGER +} + +#endif + +bool PreloadManager::IsLoading() +{ + ASSERT_RUNNING_ON_MAIN_THREAD +#if THREADED_LOADING + return m_LoadingMutex.IsLocked(); +#else + return false; +#endif +} + +bool PreloadManager::IsLoadingOrQueued() +{ + ASSERT_RUNNING_ON_MAIN_THREAD + + if (IsLoading()) + return true; + + THREADED_LOADING_MUTEX_AUTOLOCK(m_QueueMutex); + if (m_ProcessingOperation) + return true; + return !m_PreloadQueue.empty(); +} + +size_t PreloadManager::FindTopPriorityOperation (std::vector& ops) +{ + Assert (!ops.empty ()); + int index = 0; + int highestPriority = ops[0]->GetPriority(); + for (int i=1;iGetPriority() > highestPriority) + { + index = i; + highestPriority = ops[i]->GetPriority(); + } + } + + return index; +} + +#if !THREADED_LOADING +void PreloadManager::ProcessPreloadOperation () +{ + + #if PROFILE_PRELOAD_MANAGER + float startTime = GetTimeSinceStartup(); + #define LOG_PROFILER(x) printf_console(x, m_ProcessingOperation->GetDebugName().c_str(), (GetTimeSinceStartup() - startTime) * 1000.0F); startTime = GetTimeSinceStartup(); + #else + #define LOG_PROFILER(x) + #endif + + if (!m_PreloadQueue.empty()) + { + Assert (m_IntegrationOperation == NULL); + + int index = PreloadManager::FindTopPriorityOperation (m_PreloadQueue); + + // Grab item and remove it from queue + Assert(m_ProcessingOperation == NULL); + m_ProcessingOperation = m_PreloadQueue[index]; + + LOG_PROFILER("Begin background load (FAKE): %s [idle time: %f]\n"); + + m_PreloadQueue.erase(m_PreloadQueue.begin() + index); + m_ProcessingOperation->Perform(); + + LOG_PROFILER("Async background load complete (FAKE): %s - %f ms\n"); + + // Integrate any work into main thread, wait for completion, + // so that the persistentmanager is kept clear until the integration thread is done + if (m_ProcessingOperation->HasIntegrateMainThread()) + { + Assert (m_IntegrationOperation == NULL); + m_IntegrationOperation = m_ProcessingOperation; + } + + m_CallCoroutineCallbackQueue.push_back(m_ProcessingOperation); + + LOG_PROFILER("Completed Integration step: %s - %f ms\n"); + + m_ProcessingOperation = NULL; + } + + #undef PROFILE_PRELOAD_MANAGER +} + +void PreloadManager::UpdatePreloadingSingleStep (bool stopPreloading) +{ + // Do loading + ProcessPreloadOperation (); + + // Upload texture data immediately after texture asset was loaded + // On some platforms helps to avoid memory peak during level load + Texture2D::IntegrateLoadedImmediately(); + + // Integrate threaded objects for some time + GetPersistentManager().IntegrateThreadedObjects (20.0F); + + // Perform final main thread integration step + if (m_IntegrationOperation != NULL && (m_IntegrationOperation->GetAllowSceneActivation() || stopPreloading)) + { + PreloadManagerOperation* operation = m_IntegrationOperation; + m_IntegrationOperation = NULL; + operation->IntegrateMainThread(); + } + + // Potentially call out coroutines + InvokeCoroutineCallbacks(); +} + +#else + +void PreloadManager::UpdatePreloadingSingleStep (bool stopPreloading) +{ + // Should we start up the preload manager thread? Do it as soon as something needs to be processed + if (!m_Thread.IsRunning()) + { + ////@TODO: Also use stopPreloading here... + if (!m_PreloadQueue.empty()) + { + AssertIf(m_IntegrationOperation); + #if ENABLE_MONO + Assert(m_InitDomain == NULL); + m_InitDomain = mono_domain_get(); + #endif +#if UNITY_WII + // On Wii if we set thread priority below normal and don't call Sleep on MainThread, the child thread will never run! + // Update : Threaded loading actually works now on Wii, but it requires that allocators should be thread safe, and + // it's not the case on Wii, so if there will be plans to make threaded loading work, allocators should be made thread-safe + m_Thread.SetPriority(kNormalPriority); +#else + m_Thread.SetPriority(kBelowNormalPriority); +#endif + + ////@TODO: This is currently a very high value because MonoBehaviours might do funky recursion! + // Reduce stacksize sensibly when we stop the madness. + + unsigned stackSize = 512 * 1024; + if (UNITY_EDITOR) + stackSize = 2 * 1024 * 1024; + +#if UNITY_XENON + const int kProcessor = 4; +#else + const int kProcessor = 1; +#endif + m_Thread.Run(&PreloadManager::Run, this, stackSize, kProcessor); + } + } + + // Upload texture data immediately after texture asset was loaded + // On some platforms helps to avoid memory peak during level load + Texture2D::IntegrateLoadedImmediately(); + + // Update substance integration, since we are not in the gameloop here + #if ENABLE_SUBSTANCE + SubstanceSystem* system = GetSubstanceSystemPtr (); + if (system != NULL && !system->AreIntegratingQueuesEmpty()) + system->Update(stopPreloading); + #endif + + // Figure out sensible time for how long we are allowed to integrate assets on the main thread each frame + float ms = 4.0F; + ThreadPriority p = GetThreadPriority(); + if (p == kLowPriority) + ms = 2.0F; + else if (p == kBelowNormalPriority) + ms = 4.0F; + else if (p == kNormalPriority) + ms = 10.0F; + else if (p == kHighPriority) + ms = 50.0F; + + // Integrate threaded objects for some time + GetPersistentManager().IntegrateThreadedObjects(ms / 1000.0F); + + // Perform final main thread integration step + if (m_IntegrationOperation != NULL && (m_IntegrationOperation->GetAllowSceneActivation() || stopPreloading)) + { + PreloadManagerOperation* operation = m_IntegrationOperation; + m_IntegrationOperation = NULL; + operation->IntegrateMainThread(); + + // This is needed in order to make sure that m_IntegrationOperation is properly synced when + // the signaled thread reads the value + UnityMemoryBarrier(); + m_IntegrationSemaphore.Signal(); + } + + // Potentially call out coroutines + InvokeCoroutineCallbacks(); +} +#endif + +void PreloadManager::InvokeCoroutineCallbacks () +{ + // Grab coroutines that need to be invoked this frame + // And invoke them + std::vector callbacks; + + { + THREADED_LOADING_MUTEX_AUTOLOCK(m_QueueMutex); + callbacks.swap(m_CallCoroutineCallbackQueue); + } + + for (int i=0;iInvokeCoroutine(); + callbacks[i]->Release(); + } +} + + +void PreloadManager::AddToQueue (PreloadManagerOperation* operation) +{ + THREADED_LOADING_MUTEX_AUTOLOCK(m_QueueMutex); + operation->Retain(); + m_PreloadQueue.push_back(operation); +} + +void PreloadManager::LockPreloading () +{ + #if THREADED_LOADING + PROFILER_AUTO(gIntegrateAssetsInBackground, NULL) + while (!m_LoadingMutex.TryLock()) + { + UpdatePreloadingSingleStep(false); + Thread::Sleep(0.004F); + } + #endif +} + +void PreloadManager::UnlockPreloading () +{ +#if THREADED_LOADING + m_LoadingMutex.Unlock(); +#endif +} + +void PreloadManager::WaitForAllAsyncOperationsToComplete() +{ + PROFILER_AUTO(gAsyncOperationComplete, NULL) + + while(IsLoadingOrQueued()) + { + UpdatePreloadingSingleStep(false); + + #if THREADED_LOADING + if (!GetPersistentManager ().HasThreadedObjectsToIntegrate ()) + LevelLoadingLoop(); + #endif + } +} + +void PreloadManager::UpdatePreloading() +{ + // Check if there are any operation that must be completed right away + bool mustCompleteNow = false; + { + THREADED_LOADING_MUTEX_AUTOLOCK(m_QueueMutex); + for (int i=0;iMustCompleteNextFrame(); + if (m_ProcessingOperation != NULL) + mustCompleteNow |= m_ProcessingOperation->MustCompleteNextFrame(); + } + + if (mustCompleteNow) + { + WaitForAllAsyncOperationsToComplete (); +#if UNITY_ANDROID + StopActivityIndicator(); +#endif + } + else + { + PROFILER_AUTO(gIntegrateAssetsInBackground, NULL) + UpdatePreloadingSingleStep (false); + } +} + + +float PreloadManagerOperation::GetProgress () +{ + return m_Progress; +} + +bool PreloadManagerOperation::IsDone () +{ + return m_Complete; +} + +void PreloadLevelOperation::Perform () +{ + // Should indicate threaded loading + //GetGlobalAllocators().ActivateLoadingAllocator(); + + PersistentManager& pm = GetPersistentManager(); + +#if UNITY_WII + wii::StartLoadingScreen(); +#endif + +#if ENABLE_SUBSTANCE + // Notify for preload operation + SubstanceSystem* system = GetSubstanceSystemPtr (); + if (system != NULL) + system->BeginPreloading(); +#endif + + // When loading a level, grab the PreloadData and extract PPtrs of all assets not contained in the primary assets file + if (!m_LevelAssetDataPath.empty()) + { + int preloadInstanceID = pm.GetInstanceIDFromPathAndFileID(m_LevelAssetDataPath, 1); + // @TODO: This should be moved to main thread or the preload data should always be killed immediately + Object* obj = Object::IDToPointerThreadSafe (preloadInstanceID); + if (obj == NULL) + obj = pm.ReadObjectThreaded(preloadInstanceID); + + PreloadData* preload = dynamic_pptr_cast (obj); + if (preload) + { + int size = preload->m_Assets.size(); + m_PreloadAssets.resize_uninitialized(size); + if (size > 0) + { + SInt32* target = &m_PreloadAssets[0]; + PPtr* source = &preload->m_Assets[0]; + for (int i=0;i fileIDs; + vector managerIndices; + if (!m_LevelPath.empty()) + GetFileIDsForLoadingScene(m_LevelPath, m_LoadMode, fileIDs, managerIndices); + + LoadProgress loadProgress (fileIDs.size () + m_PreloadAssets.size (), 0.9f, &m_Progress); + // Preload bundle assets & preload external level assets + GetPersistentManager().LoadObjectsThreaded(m_PreloadAssets.begin(), m_PreloadAssets.size(), &loadProgress); + + // Load Level + if (!m_LevelPath.empty()) + { + /// Load all assets + pm.LoadFileCompletelyThreaded(m_LevelAssetDataPath, NULL, NULL, -1, false, &loadProgress); + + // Wait for all assets to be integrated using timeslicing from the main thread + pm.AllowIntegrationWithTimeoutAndWait (); + + + ///@TODO: these two are not needed anymore... + std::vector instanceIDs; + instanceIDs.resize(fileIDs.size()); + + // Lock up to extracting all objects into the AwakeFromLoadQueue + // This prevents the main thread from loading and integrating random objects during the level load + pm.Lock(); + + Assert(!pm.HasThreadedObjectsToIntegrate()); + + // Load level + pm.LoadFileCompletelyThreaded(m_LevelPath, &fileIDs[0], &instanceIDs[0], fileIDs.size(), true, &loadProgress); + + pm.PrepareAllThreadedObjectsStep1 (m_AwakeFromLoadQueue); + + // Unlock - See above + pm.Unlock(); + + + #if ENABLE_SUBSTANCE + // Wait Substance finish to integrate + SubstanceSystem* system = GetSubstanceSystemPtr (); + if (system != NULL) + system->WaitFinished(&loadProgress); + #endif + + m_Progress = 0.9F; + } + else + { + // Wait for all assets to be integrated using timeslicing from the main thread + pm.AllowIntegrationWithTimeoutAndWait (); + + #if ENABLE_SUBSTANCE + // Wait Substance finish to integrate + SubstanceSystem* system = GetSubstanceSystemPtr (); + if (system != NULL) + system->WaitFinished(&loadProgress); + #endif + + m_Progress = 1.0F; + UnityMemoryBarrier(); + m_Complete = true; + } +#if UNITY_WII + wii::EndLoadingScreen(); +#endif +} + +void PreloadLevelOperation::PreloadBundleSync (AssetBundle& bundle, const std::string& name) +{ + // Calculate preload index & size into preload table of asset bundle + int index = 0, size = 0; + if (name.empty()) + { + index = bundle.m_MainAsset.preloadIndex; + size = bundle.m_MainAsset.preloadSize; + if (Object::IDToPointer(bundle.m_MainAsset.asset.GetInstanceID())) + return; + } + else + { + AssetBundle::range range = bundle.GetPathRange(name); + if (range.first != range.second) + { + index = range.first->second.preloadIndex; + size = range.first->second.preloadSize; + if (Object::IDToPointer(range.first->second.asset.GetInstanceID())) + return; + } + } + + if (size == 0) + return; + + PPtr* source = &bundle.m_PreloadTable[index]; + for (int i=0;im_LevelPath = levelPath; + operation->m_LevelAssetDataPath = levelAssetPath; + operation->m_LoadMode = mode; + operation->m_LoadLevelIndex = levelIndex; + operation->m_MustCompleteNextFrame = mustCompleteNextFrame; + #if ENABLE_PROFILER || PROFILE_PRELOAD_MANAGER + operation->m_DebugName = "Loading " + levelPath; + #endif + GetPreloadManager().AddToQueue(operation); + + return operation; +} + +UnloadUnusedAssetsOperation* UnloadUnusedAssetsOperation::UnloadUnusedAssets () +{ + UnloadUnusedAssetsOperation* operation = new UnloadUnusedAssetsOperation (); + GetPreloadManager().AddToQueue(operation); + + return operation; +} + +void UnloadUnusedAssetsOperation::IntegrateMainThread () +{ + GarbageCollectSharedAssets(true); + m_Progress = 1.0F; + UnityMemoryBarrier(); + m_Complete = true; +} + +PreloadLevelOperation* PreloadLevelOperation::CreateDummy () +{ + PreloadLevelOperation* operation = new PreloadLevelOperation (); + operation->m_Progress = 1.0F; + UnityMemoryBarrier(); + operation->m_Complete = true; + return operation; +} + +/// @TODO: one string can identify multiple different resources. +/// We must somehow preload all or make preload tables be shared by name +PreloadLevelOperation* PreloadLevelOperation::LoadAssetBundle (AssetBundle& bundle, const std::string& name) +{ + if (!GetBuildSettings().hasPROVersion) + { + ErrorString("Asynchronous Background loading is only supported in Unity Pro.\nPlease use AssetBundle.Load instead"); + return CreateDummy (); + } + + PreloadLevelOperation* operation = NULL; + + // Calculate preload index & size into preload table of asset bundle + int index = 0, size = 0; + if (name.empty()) + { + index = bundle.m_MainAsset.preloadIndex; + size = bundle.m_MainAsset.preloadSize; + if (Object::IDToPointer(bundle.m_MainAsset.asset.GetInstanceID())) + return CreateDummy (); + } + else + { + AssetBundle::range range = bundle.GetPathRange(name); + if (range.first != range.second) + { + index = range.first->second.preloadIndex; + size = range.first->second.preloadSize; + if (Object::IDToPointer(range.first->second.asset.GetInstanceID())) + return CreateDummy (); + } + } + + if (size == 0) + return CreateDummy (); + + operation = new PreloadLevelOperation (); + #if ENABLE_PROFILER || PROFILE_PRELOAD_MANAGER + operation->m_DebugName = "Loading asset bundle asset: " + name; + #endif + + operation->m_PreloadAssets.resize_uninitialized(size); + operation->m_LoadMode = kLoadAssetBundle; + + SInt32* target = &operation->m_PreloadAssets[0]; + PPtr* source = &bundle.m_PreloadTable[index]; + for (int i=0;i& fileIDs, vector& managerIndices) +{ + GetPersistentManager ().Lock(); + SerializedFile* stream = GetPersistentManager ().GetSerializedFileInternal(pathName); + if (stream == NULL) + { + GetPersistentManager ().Unlock(); + return; + } + + vector sourceFileIDs; + stream->GetAllFileIDs(&sourceFileIDs); + fileIDs.reserve(sourceFileIDs.size()); + + #if UNITY_EDITOR + int editorExtensionImplClassID = Object::StringToClassID("EditorExtensionImpl"); + #endif + + + // GameManager no longer references EditorExtensionImpl. We need to not load EditorExtensionImpl related to game managers. + // But we do need to load EditorExtensionImpl of game objects & components in order to grab deprecated data describing prefabs from it. + // If we dont they might in turn load the manager, which we need very tight control over when we want them to be loaded. + // Sometime in the past EditorExtensionImpl were referenced by GameManager. + // So we skip over loading any editor extension impl objects before any scene objects are being loaded. + // Loading the editorextension impl of a manager is dangerous because it will load it by dereferencing it's m_Object reference. + // Don't load any editor extension impl for any managers + // All editor extension impl's in scenes are always serialized directly after their related object. + int baseSceneDataIndex = 0; + for (int i=0;iGetClassID (fileID); + + if (Object::IsDerivedFromClassID (classID, ClassID (GameManager))) + baseSceneDataIndex = i + 1; + #if UNITY_EDITOR + else if (classID == editorExtensionImplClassID) + continue; + #endif + else + break; + } + + for (int i=0;iGetClassID (fileID); + + if (Object::IsDerivedFromClassID (classID, ClassID (GlobalGameManager))) + continue; + + if (Object::IsDerivedFromClassID (classID, ClassID (LevelGameManager))) + { + // Additive loaded levels need lightmap settings from the scene file. It will be merged into the active scene. + bool loadAdditive = operation == PreloadLevelOperation::kLoadEditorAdditiveLevel || operation == PreloadLevelOperation::kLoadAdditiveLevel; + if (loadAdditive && classID != ClassID(LightmapSettings)) + continue; + + managerIndices.push_back(fileIDs.size()); + } + + #if UNITY_EDITOR + if (classID == editorExtensionImplClassID && i < baseSceneDataIndex) + continue; + #endif + + fileIDs.push_back(fileID); + } + + GetPersistentManager ().Unlock(); +} + + +#undef THREADED_LOADING_MUTEX_AUTOLOCK // Mutex::AutoLock lock (x) / {} + +void UnloadUnusedAssetsImmediate (bool includeMonoReferencesAsRoots) +{ + GetPreloadManager().LockPreloading(); + GarbageCollectSharedAssets(includeMonoReferencesAsRoots); + GetPreloadManager().UnlockPreloading(); +} -- cgit v1.1-26-g67d0