public LiveLinkScene(World destinationWorld, LiveLinkMode mode)
 {
     DestinationWorld        = destinationWorld;
     ConvertedWorld          = new World("Clean Entity Conversion World");
     LiveLinkDiffer          = new EntityManagerDiffer(ConvertedWorld, Allocator.Persistent);
     LiveLinkPatcher         = new EntityManagerPatcher(destinationWorld, Allocator.Persistent);
     _RequestCleanConversion = true;
     _LiveLinkMode           = mode;
 }
示例#2
0
        public static LiveLinkChangeSet UpdateLiveLink(Scene scene, Hash128 sceneGUID, ref LiveLinkDiffGenerator liveLinkData, int sceneDirtyID, LiveLinkMode mode, BuildConfiguration config, out NativeArray <GUID> assetDependencies)
        {
            //Debug.Log("ApplyLiveLink: " + scene.SceneName);

            int framesToRetainBlobAssets = RetainBlobAssetsSetting.GetFramesToRetainBlobAssets(config);

            var liveLinkEnabled = mode != LiveLinkMode.Disabled;

            if (liveLinkData != null && liveLinkData._LiveLinkEnabled != liveLinkEnabled)
            {
                liveLinkData.Dispose();
                liveLinkData = null;
            }

            var unloadAllPreviousEntities = liveLinkData == null;

            if (liveLinkData == null)
            {
                liveLinkData = new LiveLinkDiffGenerator(sceneGUID, liveLinkEnabled);
            }

            if (!liveLinkEnabled)
            {
                assetDependencies = default;
                return(new LiveLinkChangeSet
                {
                    UnloadAllPreviousEntities = unloadAllPreviousEntities,
                    SceneName = scene.name,
                    SceneGUID = sceneGUID,
                    FramesToRetainBlobAssets = framesToRetainBlobAssets
                });
            }

            var flags = GameObjectConversionUtility.ConversionFlags.AddEntityGUID | GameObjectConversionUtility.ConversionFlags.AssignName | GameObjectConversionUtility.ConversionFlags.GameViewLiveLink;

            if (mode == LiveLinkMode.LiveConvertSceneView)
            {
                flags |= GameObjectConversionUtility.ConversionFlags.SceneViewLiveLink;
            }

            liveLinkData.Convert(scene, sceneGUID, flags, config, out assetDependencies);

            var convertedEntityManager = liveLinkData._ConvertedWorld.EntityManager;

            // We don't know the scene tag of the destination world, so we create a null Scene Tag.
            // In the patching code this will be translated into the final scene entity.
            convertedEntityManager.AddSharedComponentData(liveLinkData._MissingSceneQuery, new SceneTag {
                SceneEntity = Entity.Null
            });

#if UNITY_2020_1_OR_NEWER
            convertedEntityManager.AddSharedComponentData(liveLinkData._MissingRenderDataQuery, new EditorRenderData {
                SceneCullingMask = UnityEditor.SceneManagement.SceneCullingMasks.GameViewObjects, PickableObject = null
            });
#else
            convertedEntityManager.AddSharedComponentData(liveLinkData._MissingRenderDataQuery, new EditorRenderData {
                SceneCullingMask = EditorRenderData.LiveLinkEditGameViewMask, PickableObject = null
            });
#endif

            var options = EntityManagerDifferOptions.IncludeForwardChangeSet |
                          EntityManagerDifferOptions.FastForwardShadowWorld |
                          EntityManagerDifferOptions.ValidateUniqueEntityGuid |
                          EntityManagerDifferOptions.ClearMissingReferences;

            var changes = new LiveLinkChangeSet
            {
                Changes = liveLinkData._LiveLinkDiffer.GetChanges(options, Allocator.TempJob).ForwardChangeSet,
                UnloadAllPreviousEntities = unloadAllPreviousEntities,
                SceneName = scene.name,
                SceneGUID = sceneGUID,
                FramesToRetainBlobAssets = framesToRetainBlobAssets
            };


            liveLinkData.LiveLinkDirtyID = sceneDirtyID;
            // convertedEntityManager.Debug.CheckInternalConsistency();

            return(changes);
        }
        public static LiveLinkChangeSet UpdateLiveLink(Scene scene, Hash128 sceneGUID, ref LiveLinkDiffGenerator liveLinkData, int sceneDirtyID, LiveLinkMode mode, GUID configGUID, BuildConfiguration config)
        {
            //Debug.Log("ApplyLiveLink: " + scene.SceneName);

            int framesToRetainBlobAssets = RetainBlobAssetsSetting.GetFramesToRetainBlobAssets(config);

            var liveLinkEnabled = mode != LiveLinkMode.Disabled;

            if (liveLinkData != null && liveLinkData._LiveLinkEnabled != liveLinkEnabled)
            {
                liveLinkData.Dispose();
                liveLinkData = null;
            }

            var unloadAllPreviousEntities = liveLinkData == null;

            if (liveLinkData == null)
            {
                liveLinkData = new LiveLinkDiffGenerator(scene, sceneGUID, configGUID, config, liveLinkEnabled);
            }
            else if (liveLinkData._Scene != scene || !ReferenceEquals(liveLinkData._buildConfiguration, config) || liveLinkData._buildConfigurationGUID != configGUID)
            {
                liveLinkData._Scene = scene;
                liveLinkData._buildConfigurationGUID = configGUID;
                liveLinkData._buildConfiguration     = config;
                liveLinkData._RequestCleanConversion = true;
            }

            if (!liveLinkEnabled)
            {
                return(new LiveLinkChangeSet
                {
                    UnloadAllPreviousEntities = unloadAllPreviousEntities,
                    SceneName = scene.name,
                    SceneGUID = sceneGUID,
                    FramesToRetainBlobAssets = framesToRetainBlobAssets
                });
            }

            var flags = GameObjectConversionUtility.ConversionFlags.AddEntityGUID | GameObjectConversionUtility.ConversionFlags.AssignName | GameObjectConversionUtility.ConversionFlags.GameViewLiveLink;

            if (mode == LiveLinkMode.LiveConvertSceneView)
            {
                flags |= GameObjectConversionUtility.ConversionFlags.SceneViewLiveLink;
            }
            if (mode == LiveLinkMode.LiveConvertStandalonePlayer)
            {
                flags |= GameObjectConversionUtility.ConversionFlags.IsBuildingForPlayer;
            }

            liveLinkData.Convert(flags);
            const EntityManagerDifferOptions options =
                EntityManagerDifferOptions.IncludeForwardChangeSet |
                EntityManagerDifferOptions.FastForwardShadowWorld |
                EntityManagerDifferOptions.ValidateUniqueEntityGuid |
                EntityManagerDifferOptions.ClearMissingReferences;

            var changes = new LiveLinkChangeSet
            {
                Changes = liveLinkData._LiveLinkDiffer.GetChanges(options, Allocator.TempJob).ForwardChangeSet,
                UnloadAllPreviousEntities = unloadAllPreviousEntities,
                SceneName = scene.name,
                SceneGUID = sceneGUID,
                FramesToRetainBlobAssets = framesToRetainBlobAssets
            };

#if !UNITY_2020_2_OR_NEWER
            liveLinkData.LiveLinkDirtyID = sceneDirtyID;
#endif
            // convertedEntityManager.Debug.CheckInternalConsistency();

            return(changes);
        }
        void AddLiveLinkChangeSet(ref LiveLinkDiffGenerator liveLink, Hash128 sceneGUID, List <LiveLinkChangeSet> changeSets, LiveLinkMode mode)
        {
            var editScene = _GUIDToEditScene[sceneGUID];

            int sceneDirtyID = 0;

#if !UNITY_2020_2_OR_NEWER
            // The current behaviour is that we do incremental conversion until we release the hot control
            // This is to avoid any unexpected stalls
            if (IsHotControlActive())
            {
                if (liveLink == null)
                {
                    EditorUpdateUtility.EditModeQueuePlayerLoopUpdate();
                    return;
                }
                sceneDirtyID = liveLink.LiveLinkDirtyID;
            }
            else
            {
                sceneDirtyID = GetSceneDirtyID(editScene);
                if (liveLink != null && liveLink.LiveLinkDirtyID != sceneDirtyID)
                {
                    liveLink.RequestCleanConversion();
                }
            }
#endif

            //@TODO: need one place that LiveLinkDiffGenerators are managed. UpdateLiveLink does a Dispose()
            // but this must be paired with membership in _SceneGUIDToLiveLink. not good to have multiple places
            // doing ownership management.
            //
            // also: when implementing an improvement to this, be sure to deal with exceptions, which can occur
            // during conversion.

            if (liveLink != null)
            {
                _SceneGUIDToLiveLink.Remove(sceneGUID);
            }

            try
            {
                changeSets.Add(LiveLinkDiffGenerator.UpdateLiveLink(editScene, sceneGUID, ref liveLink, sceneDirtyID, mode, _BuildConfigurationGUID, _BuildConfiguration));
            }
            finally
            {
                if (liveLink != null)
                {
                    _SceneGUIDToLiveLink.Add(sceneGUID, liveLink);
                }
            }
        }
        public void Update(List <LiveLinkChangeSet> changeSets, NativeList <Hash128> loadScenes, NativeList <Hash128> unloadScenes, LiveLinkMode mode)
        {
            if (_LoadedScenes.Count == 0 && _SceneGUIDToLiveLink.Count == 0 && _RemovedScenes.Length == 0)
            {
                return;
            }

            // If build configuration changed, we need to trigger a full conversion
            if (_BuildConfigurationGUID != default)
            {
                var buildConfigurationDependencyHash = AssetDatabaseCompatibility.GetAssetDependencyHash(_BuildConfigurationGUID);
                if (_BuildConfigurationArtifactHash != buildConfigurationDependencyHash)
                {
                    _BuildConfigurationArtifactHash = buildConfigurationDependencyHash;
                    RequestCleanConversion();
                }
            }

            if (_PreviousGlobalDirtyID != GlobalDirtyID)
            {
                RequestCleanConversion();
                _PreviousGlobalDirtyID = GlobalDirtyID;
            }

            // By default all scenes need to have m_GameObjectSceneCullingMask, otherwise they won't show up in game view
            _GUIDToEditScene.Clear();
            for (int i = 0; i != EditorSceneManager.sceneCount; i++)
            {
                var scene     = EditorSceneManager.GetSceneAt(i);
                var sceneGUID = AssetDatabaseCompatibility.PathToGUID(scene.path);

                if (_LoadedScenes.Contains(sceneGUID))
                {
                    if (scene.isLoaded && sceneGUID != default(GUID))
                    {
                        _GUIDToEditScene.Add(sceneGUID, scene);
                    }
                }
            }

            foreach (var scene in _SceneGUIDToLiveLink)
            {
                if (!_GUIDToEditScene.ContainsKey(scene.Key))
                {
                    unloadScenes.Add(scene.Key);
                }
            }

            // Process scenes that are no longer loaded
            foreach (var scene in unloadScenes)
            {
                var liveLink = _SceneGUIDToLiveLink[scene];
                liveLink.Dispose();
                _SceneGUIDToLiveLink.Remove(scene);
                _SentLoadScenes.Remove(scene);
            }
            foreach (var scene in _RemovedScenes)
            {
                if (_SceneGUIDToLiveLink.TryGetValue(scene, out var liveLink))
                {
                    liveLink.Dispose();
                    _SceneGUIDToLiveLink.Remove(scene);
                }

                unloadScenes.Add(scene);
                _SentLoadScenes.Remove(scene);
            }
            _RemovedScenes.Clear();

            _SentLoadScenes.RemoveWhere(scene => !_LoadedScenes.Contains(scene));

            // Process all scenes that the player needs
            var conversionMode = LiveConversionSettings.Mode;

            foreach (var sceneGuid in _LoadedScenes)
            {
                var isLoaded = _GUIDToEditScene.TryGetValue(sceneGuid, out var scene);

                // We are editing with live link. Ensure it is active & up to date
                if (isLoaded)
                {
                    var liveLink = GetLiveLink(sceneGuid);

                    if (liveLink == null || liveLink.DidRequestUpdate())
                    {
                        AddLiveLinkChangeSet(ref liveLink, sceneGuid, changeSets, mode);
                    }
#if !UNITY_2020_2_OR_NEWER
                    else if (liveLink.LiveLinkDirtyID != GetSceneDirtyID(scene))
                    {
                        AddLiveLinkChangeSet(ref liveLink, sceneGuid, changeSets, mode);
                    }
#endif

#if UNITY_2020_2_OR_NEWER
                    if (conversionMode == LiveConversionSettings.ConversionMode.IncrementalConversionWithDebug)
                    {
                        if (liveLink != null && liveLink.DidRequestDebugConversion())
                        {
                            if (IsHotControlActive())
                            {
                                EditorUpdateUtility.EditModeQueuePlayerLoopUpdate();
                            }
                            else
                            {
                                liveLink.DebugIncrementalConversion();
                            }
                        }
                    }
#endif
                }
                else
                {
                    if (_SentLoadScenes.Add(sceneGuid))
                    {
                        loadScenes.Add(sceneGuid);
                    }
                }
            }
        }
        void AddLiveLinkChangeSet(Hash128 sceneGUID, List <LiveLinkChangeSet> changeSets, LiveLinkMode mode)
        {
            var liveLink  = GetLiveLink(sceneGUID);
            var editScene = _GUIDToEditScene[sceneGUID];

            // The current behaviour is that we do incremental conversion until we release the hot control
            // This is to avoid any unexpected stalls
            // Optimally the undo system would tell us if only properties have changed, but currently we don't have such an event stream.
            var sceneDirtyID   = GetSceneDirtyID(editScene);
            var updateLiveLink = true;

            if (IsHotControlActive())
            {
                if (liveLink != null)
                {
                    sceneDirtyID = liveLink.LiveLinkDirtyID;
                }
                else
                {
                    updateLiveLink = false;
                    EditorUpdateUtility.EditModeQueuePlayerLoopUpdate();
                }
            }
            else
            {
                if (liveLink != null && liveLink.LiveLinkDirtyID != sceneDirtyID)
                {
                    liveLink.RequestCleanConversion();
                }
            }

            if (updateLiveLink)
            {
                //@TODO: need one place that LiveLinkDiffGenerators are managed. UpdateLiveLink does a Dispose()
                // but this must be paired with membership in _SceneGUIDToLiveLink. not good to have multiple places
                // doing ownership management.
                //
                // also: when implementing an improvement to this, be sure to deal with exceptions, which can occur
                // during conversion.

                if (liveLink != null)
                {
                    _SceneGUIDToLiveLink.Remove(sceneGUID);
                }

                try
                {
                    changeSets.Add(LiveLinkDiffGenerator.UpdateLiveLink(editScene, sceneGUID, ref liveLink, sceneDirtyID, mode, _BuildConfiguration, out var assetDependencies));
                    if (assetDependencies.IsCreated)
                    {
                        m_AssetDependencies.Clear();
                        foreach (var asset in assetDependencies)
                        {
                            m_AssetDependencies.Add(asset, 1);
                        }
                        assetDependencies.Dispose();
                    }
                }
                finally
                {
                    if (liveLink != null)
                    {
                        _SceneGUIDToLiveLink.Add(sceneGUID, liveLink);
                    }
                }
            }
        }
        public void Update(List <LiveLinkChangeSet> changeSets, NativeList <Hash128> loadScenes, NativeList <Hash128> unloadScenes, LiveLinkMode mode)
        {
            if (_LoadedScenes.Count == 0 && _SceneGUIDToLiveLink.Count == 0 && _RemovedScenes.Length == 0)
            {
                return;
            }

            // If build configuration changed, we need to trigger a full conversion
            if (_BuildConfigurationGUID != default)
            {
                // TODO: Allocs, needs better API
                var buildConfigurationDependencyHash = AssetDatabase.GetAssetDependencyHash(AssetDatabase.GUIDToAssetPath(_BuildConfigurationGUID.ToString()));
                if (_BuildConfigurationArtifactHash != buildConfigurationDependencyHash)
                {
                    _BuildConfigurationArtifactHash = buildConfigurationDependencyHash;
                    RequestCleanConversion();
                }
            }

            if (_PreviousGlobalDirtyID != GlobalDirtyID)
            {
                RequestCleanConversion();
                _PreviousGlobalDirtyID = GlobalDirtyID;
            }

            // By default all scenes need to have m_GameObjectSceneCullingMask, otherwise they won't show up in game view
            _GUIDToEditScene.Clear();
            for (int i = 0; i != EditorSceneManager.sceneCount; i++)
            {
                var scene     = EditorSceneManager.GetSceneAt(i);
                var sceneGUID = new GUID(AssetDatabase.AssetPathToGUID(scene.path));

                if (_LoadedScenes.Contains(sceneGUID))
                {
                    if (scene.isLoaded && sceneGUID != default(GUID))
                    {
                        _GUIDToEditScene.Add(sceneGUID, scene);
                    }
                }
            }

            foreach (var scene in _SceneGUIDToLiveLink)
            {
                if (!_GUIDToEditScene.ContainsKey(scene.Key))
                {
                    unloadScenes.Add(scene.Key);
                }
            }

            // Process scenes that are no longer loaded
            foreach (var scene in unloadScenes)
            {
                var liveLink = _SceneGUIDToLiveLink[scene];
                liveLink.Dispose();
                _SceneGUIDToLiveLink.Remove(scene);
                _SentLoadScenes.Remove(scene);
            }
            foreach (var scene in _RemovedScenes)
            {
                if (_SceneGUIDToLiveLink.TryGetValue(scene, out var liveLink))
                {
                    liveLink.Dispose();
                    _SceneGUIDToLiveLink.Remove(scene);
                }

                unloadScenes.Add(scene);
                _SentLoadScenes.Remove(scene);
            }
            _RemovedScenes.Clear();

            _SentLoadScenes.RemoveWhere(scene => !_LoadedScenes.Contains(scene));

            // Process all scenes that the player needs
            foreach (var sceneGuid in _LoadedScenes)
            {
                var isLoaded = _GUIDToEditScene.TryGetValue(sceneGuid, out var scene);

                // We are editing with live link. Ensure it is active & up to date
                if (isLoaded)
                {
                    var liveLink = GetLiveLink(sceneGuid);
                    if (liveLink == null || liveLink.DidRequestUpdate() || liveLink.LiveLinkDirtyID != GetSceneDirtyID(scene))
                    {
                        AddLiveLinkChangeSet(sceneGuid, changeSets, mode);
                    }
                }
                else
                {
                    if (_SentLoadScenes.Add(sceneGuid))
                    {
                        loadScenes.Add(sceneGuid);
                    }
                }
            }
        }
        public static void ApplyLiveLink(SubScene scene, World dstWorld, int sceneDirtyID, LiveLinkMode mode)
        {
            //Debug.Log("ApplyLiveLink: " + scene.SceneName);

            #if ENABLE_UNITY_COLLECTIONS_CHECKS
            if (mode == LiveLinkMode.Disabled)
            {
                throw new System.ArgumentException("LiveLinkMode must not be disabled");
            }
            #endif

            // When switching mode, completely unload the previous scene
            if (scene.LiveLinkData != null && (scene.LiveLinkData._LiveLinkMode == LiveLinkMode.ConvertWithoutDiff || mode == LiveLinkMode.ConvertWithoutDiff || scene.LiveLinkData.DestinationWorld != dstWorld))
            {
                scene.LiveLinkData.Dispose();
                scene.LiveLinkData = null;
            }

            var unloadAllPreviousEntities = scene.LiveLinkData == null;
            if (scene.LiveLinkData == null)
            {
                scene.LiveLinkData = new LiveLinkScene(dstWorld, mode);
            }
            var liveLink = scene.LiveLinkData;


            var flags = GameObjectConversionUtility.ConversionFlags.AddEntityGUID | GameObjectConversionUtility.ConversionFlags.AssignName | GameObjectConversionUtility.ConversionFlags.GameViewLiveLink;
            if (mode == LiveLinkMode.LiveConvertSceneView)
            {
                flags |= GameObjectConversionUtility.ConversionFlags.SceneViewLiveLink;
            }

            liveLink.Convert(scene.LoadedScene, scene.SceneGUID, flags);


            var streamingSystem = dstWorld.GetExistingSystem <SubSceneStreamingSystem>();
            var dstEntities     = dstWorld.EntityManager;

            // Unload scene
            if (unloadAllPreviousEntities)
            {
                foreach (var s in scene._SceneEntities)
                {
                    streamingSystem.UnloadSceneImmediate(s);
                    dstEntities.DestroyEntity(s);
                }

                var sceneEntity = dstEntities.CreateEntity();
                dstEntities.SetName(sceneEntity, "Scene (LiveLink): " + scene.SceneName);
                dstEntities.AddComponentObject(sceneEntity, scene);
                dstEntities.AddComponentData(sceneEntity, new SubSceneStreamingSystem.StreamingState {
                    Status = SubSceneStreamingSystem.StreamingStatus.Loaded
                });
                dstEntities.AddComponentData(sceneEntity, new SubSceneStreamingSystem.IgnoreTag( ));

                scene._SceneEntities = new List <Entity>();
                scene._SceneEntities.Add(sceneEntity);
            }


            var convertedEntityManager = liveLink.ConvertedWorld.EntityManager;

            var liveLinkSceneEntity = scene._SceneEntities[0];


            /// We want to let the live linked scene be able to reference the already existing Scene Entity (Specifically SceneTag should point to the scene Entity after live link completes)
            // Add Scene tag to all entities using the convertedSceneEntity that will map to the already existing scene entity.
            using (var missingSceneQuery = convertedEntityManager.CreateEntityQuery(new EntityQueryDesc
            {
                None = new ComponentType[] { typeof(SceneTag) },
                Options = EntityQueryOptions.IncludePrefab | EntityQueryOptions.IncludeDisabled
            }))
            {
                convertedEntityManager.AddSharedComponentData(missingSceneQuery, new SceneTag {
                    SceneEntity = liveLinkSceneEntity
                });
            }

            if (mode != LiveLinkMode.ConvertWithoutDiff)
            {
                var options = EntityManagerDifferOptions.IncludeForwardChangeSet |
                              EntityManagerDifferOptions.FastForwardShadowWorld |
                              EntityManagerDifferOptions.ValidateUniqueEntityGuid |
                              EntityManagerDifferOptions.ClearMissingReferences;

                using (var changes = liveLink.LiveLinkDiffer.GetChanges(options, Allocator.TempJob))
                {
                    liveLink.LiveLinkPatcher.ApplyChangeSet(changes.ForwardChangeSet);
                }
            }
            else
            {
                dstWorld.EntityManager.MoveEntitiesFrom(liveLink.ConvertedWorld.EntityManager);
            }

            // convertedEntityManager.Debug.CheckInternalConsistency();
            //liveLink.ConvertedShadowWorld.EntityManager.Debug.CheckInternalConsistency();

            using (var missingRenderData = dstEntities.CreateEntityQuery(new EntityQueryDesc
            {
                All = new ComponentType[] { typeof(SceneTag) },
                None = new ComponentType[] { typeof(EditorRenderData) },
                Options = EntityQueryOptions.IncludePrefab | EntityQueryOptions.IncludeDisabled
            }))
            {
                missingRenderData.SetFilter(new SceneTag {
                    SceneEntity = liveLinkSceneEntity
                });
                dstEntities.AddSharedComponentData(missingRenderData, new EditorRenderData()
                {
                    SceneCullingMask = EditorRenderData.LiveLinkEditGameViewMask, PickableObject = scene.gameObject
                });
            }

            liveLink.LiveLinkDirtyID = sceneDirtyID;
            EditorUpdateUtility.EditModeQueuePlayerLoopUpdate();
        }