From 6667f013bf561fdf6c04d6869489581f3cc0bb13 Mon Sep 17 00:00:00 2001 From: max Date: Sun, 16 Jul 2023 19:58:37 +0200 Subject: [PATCH] Profiling scopes - Added profiling scope utility - Added profiling scopes around some functions - Testing StreamWriter and FileStream --- Editor/ScenePartitionSO.cs | 326 ++++++++++++++++++-------------- Editor/ScenePartitionUtils.cs | 54 +++--- Runtime/ProfilerUtility.cs | 26 +++ Runtime/ProfilerUtility.cs.meta | 11 ++ 4 files changed, 248 insertions(+), 169 deletions(-) create mode 100644 Runtime/ProfilerUtility.cs create mode 100644 Runtime/ProfilerUtility.cs.meta diff --git a/Editor/ScenePartitionSO.cs b/Editor/ScenePartitionSO.cs index 94c585f..849bd33 100644 --- a/Editor/ScenePartitionSO.cs +++ b/Editor/ScenePartitionSO.cs @@ -82,33 +82,54 @@ private void CreateScenePartitions() private void LoadScenePartitions(SortedSet partitionIds) { - if (!Data.HasCreatedPartitions) return; - - string scenePath = ScenePartitionUtils.GetScenePath(this); - - List sceneData = new List(); - - Data.LoadedScenePartitions.Clear(); - - // Add always load ids. - SortedSet baseIds = GetAlwaysLoadIds(); - foreach (var id in baseIds) + using (new ProfilerUtility.ProfilerScope($"{nameof(LoadScenePartitions)}")) { - partitionIds.Add(id); + if (!Data.HasCreatedPartitions) return; + + string scenePath = ScenePartitionUtils.GetScenePath(this); + + Data.LoadedScenePartitions.Clear(); + + // Add always load ids. + SortedSet baseIds = GetAlwaysLoadIds(); + foreach (var id in baseIds) + { + partitionIds.Add(id); + } + + // Clear file. + File.WriteAllText(scenePath, string.Empty); + + // Create scene data. + try + { + using (FileStream outputStream = new FileStream(scenePath, FileMode.Append, FileAccess.Write)) + { + foreach (ulong id in partitionIds) + { + ScenePartition p = Data.ScenePartitions[id]; + + using (FileStream inputStream = new FileStream(p.filePath, FileMode.Open, FileAccess.Read)) + { + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) > 0) + { + outputStream.Write(buffer, 0, bytesRead); + } + } + + Data.LoadedScenePartitions.Add(p.id, p); + } + } + } + catch (System.Exception ex) + { + Debug.LogException(ex); + } + + AssetDatabase.Refresh(); } - - // Create scene data. - foreach (ulong id in partitionIds) - { - ScenePartition p = Data.ScenePartitions[id]; - sceneData.AddRange(File.ReadAllLines(p.filePath)); - Data.LoadedScenePartitions.Add(p.id, p); - } - - // Create scene. - File.WriteAllLines(scenePath, sceneData); - - AssetDatabase.Refresh(); } /// @@ -116,38 +137,41 @@ private void LoadScenePartitions(SortedSet partitionIds) /// public void Save() { - DeleteLoadedPartitions(); // Delete the loaded partitions from disk so we can write the new ones. - - string pattern = @"&(\d+)"; - string dataPath = ScenePartitionUtils.GetDataPath(this); - string scenePath = ScenePartitionUtils.GetScenePath(this); - - // Read the data from the scene file. - string[] sceneData = File.ReadAllLines(scenePath); - - // Split it into blocks. - int lastIndex = sceneData.Length; - for (int i = sceneData.Length - 1; i >= 0; i--) + using (new ProfilerUtility.ProfilerScope($"{nameof(Save)}")) { - if (sceneData[i].StartsWith("---")) // --- is the start of a new yaml document. + DeleteLoadedPartitions(); // Delete the loaded partitions from disk so we can write the new ones. + + string pattern = @"&(\d+)"; + string dataPath = ScenePartitionUtils.GetDataPath(this); + string scenePath = ScenePartitionUtils.GetScenePath(this); + + // Read the data from the scene file. + string[] sceneData = File.ReadAllLines(scenePath); + + // Split it into blocks. + int lastIndex = sceneData.Length; + for (int i = sceneData.Length - 1; i >= 0; i--) { - Match match = Regex.Match(sceneData[i], pattern); - - if (match.Success) + if (sceneData[i].StartsWith("---")) // --- is the start of a new yaml document. { - // Extract the file number - string id = match.Groups[1].Value; + Match match = Regex.Match(sceneData[i], pattern); - // Write data to disk. - File.WriteAllLines($"{dataPath}/{SceneName}-{id}.yaml", sceneData[i..lastIndex]); + if (match.Success) + { + // Extract the file number + string id = match.Groups[1].Value; + + // Write data to disk. + File.WriteAllLines($"{dataPath}/{SceneName}-{id}.yaml", sceneData[i..lastIndex]); + } + + lastIndex = i; } - - lastIndex = i; } - } - // Write header to disk. - File.WriteAllLines($"{dataPath}/{SceneName}.yaml", sceneData[0..lastIndex]); + // Write header to disk. + File.WriteAllLines($"{dataPath}/{SceneName}.yaml", sceneData[0..lastIndex]); + } } /// @@ -155,51 +179,60 @@ public void Save() /// public void Unload() { - string dataPath = ScenePartitionUtils.GetDataPath(this); - string scenePath = ScenePartitionUtils.GetScenePath(this); - - Scene scene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Single); - - GameObject[] gameObjects = scene.GetRootGameObjects(); - - for (int i = gameObjects.Length - 1; i >= 0; i--) + using (new ProfilerUtility.ProfilerScope($"{nameof(Unload)}")) { - DestroyImmediate(gameObjects[i]); + string dataPath = ScenePartitionUtils.GetDataPath(this); + string scenePath = ScenePartitionUtils.GetScenePath(this); + + Scene scene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Single); + + GameObject[] gameObjects = scene.GetRootGameObjects(); + + for (int i = gameObjects.Length - 1; i >= 0; i--) + { + DestroyImmediate(gameObjects[i]); + } + + EditorSceneManager.SaveScene(scene); + + Data.LoadedScenePartitions.Clear(); + + AssetDatabase.Refresh(); } - - EditorSceneManager.SaveScene(scene); - - Data.LoadedScenePartitions.Clear(); - - AssetDatabase.Refresh(); } private void DeleteLoadedPartitions() { - if (!Data.HasLoadedPartitions) return; - - foreach (KeyValuePair scenePartition in Data.LoadedScenePartitions) + using (new ProfilerUtility.ProfilerScope($"{nameof(DeleteLoadedPartitions)}")) { - if (!File.Exists(scenePartition.Value.filePath)) continue; + if (!Data.HasLoadedPartitions) return; - File.Delete(scenePartition.Value.filePath); + foreach (KeyValuePair scenePartition in Data.LoadedScenePartitions) + { + if (!File.Exists(scenePartition.Value.filePath)) continue; + + File.Delete(scenePartition.Value.filePath); + } } } public void LoadPartitions(ulong[] ids) { - SortedSet partitionIds = new SortedSet(); - - for (int i = 0; i < ids.Length; i++) + using (new ProfilerUtility.ProfilerScope($"{nameof(LoadPartitions)}")) { - SortedSet connections = ScenePartitionUtils.FindDeeplyLinkedObjects(Data.ScenePartitions, ids[i]); - foreach (ulong c in connections) - { - partitionIds.Add(c); - } - } + SortedSet partitionIds = new SortedSet(); - LoadScenePartitions(partitionIds); + for (int i = 0; i < ids.Length; i++) + { + SortedSet connections = ScenePartitionUtils.FindDeeplyLinkedObjects(Data.ScenePartitions, ids[i]); + foreach (ulong c in connections) + { + partitionIds.Add(c); + } + } + + LoadScenePartitions(partitionIds); + } } private SortedSet GetAlwaysLoadIds() @@ -220,76 +253,79 @@ private SortedSet GetAlwaysLoadIds() public void GenerateSceneGridData() { - LoadAll(); - - if (!Data.HasCreatedPartitions) return; - - Scene scene = EditorSceneManager.OpenScene(ScenePartitionUtils.GetScenePath(this), OpenSceneMode.Single); - - GameObject[] rootGameObjects = scene.GetRootGameObjects(); - - Data.SceneGrid.Grid.Clear(); - - //// Maybe later switch to getting the data from disk instead of loading the scene and then unloading it again. - //foreach (GameObject gameObject in rootGameObjects) - //{ - // // https://forum.unity.com/threads/how-to-get-the-local-identifier-in-file-for-scene-objects.265686/ - // PropertyInfo inspectorModeInfo = typeof(SerializedObject).GetProperty("inspectorMode", BindingFlags.NonPublic | BindingFlags.Instance); - // SerializedObject serializedObject = new SerializedObject(gameObject.transform); - // inspectorModeInfo.SetValue(serializedObject, InspectorMode.Debug, null); - // SerializedProperty localIdProp = serializedObject.FindProperty("m_LocalIdentfierInFile"); - - // long localId = localIdProp.longValue; - - // if (localId == 0) - // { - // if (PrefabUtility.IsPartOfPrefabInstance(gameObject)) - // { - // GlobalObjectId id = GlobalObjectId.GetGlobalObjectIdSlow(gameObject); // We could use this funtion for all objects. Might be a bit slower but is also simple. - // localId = long.Parse(id.targetPrefabId.ToString()); - - // Debug.Log($"{id.assetGUID} | {id.identifierType} | {id.targetObjectId} | {id.targetPrefabId}"); - - // if (id.targetObjectId == 0 && id.targetPrefabId == 0) - // { - // Debug.LogWarning($"Could not find LocalIdentfierInFile for {gameObject.transform} {gameObject.name} {gameObject.transform.GetInstanceID()}"); - // continue; - // } - // } - // else - // { - // Debug.LogWarning($"Could not find LocalIdentfierInFile for {gameObject.transform} {gameObject.name} {gameObject.transform.GetInstanceID()}"); - // continue; - // } - // } - - // if (!Data.ScenePartitions.ContainsKey(localId)) - // { - // Debug.LogWarning($"Could not find LocalIdentfierInFile for {gameObject.transform} {gameObject.name} {gameObject.transform.GetInstanceID()}"); - // continue; - // } - - // Data.SceneGrid.Insert(localId, gameObject.transform.position); - //} - - GlobalObjectId[] ids = new GlobalObjectId[rootGameObjects.Length]; - GlobalObjectId.GetGlobalObjectIdsSlow(rootGameObjects, ids); - - for (int i = 0; i < ids.Length; i++) + using (new ProfilerUtility.ProfilerScope($"{nameof(GenerateSceneGridData)}")) { - Debug.Log($"{ids[i].assetGUID} | {ids[i].identifierType} | {ids[i].targetObjectId} | {ids[i].targetPrefabId}"); + LoadAll(); - if (ids[i].targetPrefabId == 0) // 0 = no prefab. + if (!Data.HasCreatedPartitions) return; + + Scene scene = EditorSceneManager.OpenScene(ScenePartitionUtils.GetScenePath(this), OpenSceneMode.Single); + + GameObject[] rootGameObjects = scene.GetRootGameObjects(); + + Data.SceneGrid.Grid.Clear(); + + //// Maybe later switch to getting the data from disk instead of loading the scene and then unloading it again. + //foreach (GameObject gameObject in rootGameObjects) + //{ + // // https://forum.unity.com/threads/how-to-get-the-local-identifier-in-file-for-scene-objects.265686/ + // PropertyInfo inspectorModeInfo = typeof(SerializedObject).GetProperty("inspectorMode", BindingFlags.NonPublic | BindingFlags.Instance); + // SerializedObject serializedObject = new SerializedObject(gameObject.transform); + // inspectorModeInfo.SetValue(serializedObject, InspectorMode.Debug, null); + // SerializedProperty localIdProp = serializedObject.FindProperty("m_LocalIdentfierInFile"); + + // long localId = localIdProp.longValue; + + // if (localId == 0) + // { + // if (PrefabUtility.IsPartOfPrefabInstance(gameObject)) + // { + // GlobalObjectId id = GlobalObjectId.GetGlobalObjectIdSlow(gameObject); // We could use this funtion for all objects. Might be a bit slower but is also simple. + // localId = long.Parse(id.targetPrefabId.ToString()); + + // Debug.Log($"{id.assetGUID} | {id.identifierType} | {id.targetObjectId} | {id.targetPrefabId}"); + + // if (id.targetObjectId == 0 && id.targetPrefabId == 0) + // { + // Debug.LogWarning($"Could not find LocalIdentfierInFile for {gameObject.transform} {gameObject.name} {gameObject.transform.GetInstanceID()}"); + // continue; + // } + // } + // else + // { + // Debug.LogWarning($"Could not find LocalIdentfierInFile for {gameObject.transform} {gameObject.name} {gameObject.transform.GetInstanceID()}"); + // continue; + // } + // } + + // if (!Data.ScenePartitions.ContainsKey(localId)) + // { + // Debug.LogWarning($"Could not find LocalIdentfierInFile for {gameObject.transform} {gameObject.name} {gameObject.transform.GetInstanceID()}"); + // continue; + // } + + // Data.SceneGrid.Insert(localId, gameObject.transform.position); + //} + + GlobalObjectId[] ids = new GlobalObjectId[rootGameObjects.Length]; + GlobalObjectId.GetGlobalObjectIdsSlow(rootGameObjects, ids); + + for (int i = 0; i < ids.Length; i++) { - Data.SceneGrid.Insert(ids[i].targetObjectId, rootGameObjects[i].transform.position, ScenePartitionSceneViewEditor.cellSize); - } - else - { - Data.SceneGrid.Insert(ids[i].targetPrefabId, rootGameObjects[i].transform.position, ScenePartitionSceneViewEditor.cellSize); + //Debug.Log($"{ids[i].assetGUID} | {ids[i].identifierType} | {ids[i].targetObjectId} | {ids[i].targetPrefabId}"); + + if (ids[i].targetPrefabId == 0) // 0 = no prefab. + { + Data.SceneGrid.Insert(ids[i].targetObjectId, rootGameObjects[i].transform.position, ScenePartitionSceneViewEditor.cellSize); + } + else + { + Data.SceneGrid.Insert(ids[i].targetPrefabId, rootGameObjects[i].transform.position, ScenePartitionSceneViewEditor.cellSize); + } } + + Unload(); } - - Unload(); } public void LoadCell(int gridId) diff --git a/Editor/ScenePartitionUtils.cs b/Editor/ScenePartitionUtils.cs index 687d050..29d32ec 100644 --- a/Editor/ScenePartitionUtils.cs +++ b/Editor/ScenePartitionUtils.cs @@ -33,9 +33,12 @@ public static string GetScenePath(ScenePartitionSO scenePartitionSO) public static SortedSet FindDeeplyLinkedObjects(SortedList scenePartitions, ulong partitionId) { - SortedSet linkedObjects = new SortedSet(); - FindDeeplyLinkedObjectsRecursive(scenePartitions, partitionId, linkedObjects); - return linkedObjects; + using (new ProfilerUtility.ProfilerScope($"{nameof(FindDeeplyLinkedObjects)}")) + { + SortedSet linkedObjects = new SortedSet(); + FindDeeplyLinkedObjectsRecursive(scenePartitions, partitionId, linkedObjects); + return linkedObjects; + } } private static void FindDeeplyLinkedObjectsRecursive(SortedList scenePartitions, ulong partitionId, SortedSet linkedObjects) @@ -60,32 +63,35 @@ private static void FindDeeplyLinkedObjectsRecursive(SortedList(path); + scenePartitionSO = null; - if (scenePartition == null) continue; + UnityEngine.SceneManagement.Scene activeScene = UnityEngine.SceneManagement.SceneManager.GetActiveScene(); - if (scenePartition.SceneName == activeScene.name) + if (activeScene == null) return false; + + string filter = $"t:{nameof(ScenePartitionSO)}"; + string[] assets = AssetDatabase.FindAssets(filter); + + if (assets == null || assets.Length <= 0) return false; + + for (int i = 0; i < assets.Length; i++) { - scenePartitionSO = scenePartition; - return true; - } - } + string path = AssetDatabase.GUIDToAssetPath(assets[i]); + ScenePartitionSO scenePartition = AssetDatabase.LoadAssetAtPath(path); - return false; + if (scenePartition == null) continue; + + if (scenePartition.SceneName == activeScene.name) + { + scenePartitionSO = scenePartition; + return true; + } + } + + return false; + } } } } \ No newline at end of file diff --git a/Runtime/ProfilerUtility.cs b/Runtime/ProfilerUtility.cs new file mode 100644 index 0000000..b2d70bf --- /dev/null +++ b/Runtime/ProfilerUtility.cs @@ -0,0 +1,26 @@ +using UnityEngine; +using UnityEngine.Profiling; + +namespace VertexColor.ScenePartition +{ + public static class ProfilerUtility + { + public class ProfilerScope : System.IDisposable + { + public ProfilerScope(string name) + { + Profiler.BeginSample(name); + } + + public ProfilerScope(string name, Object target) + { + Profiler.BeginSample(name, target); + } + + public void Dispose() + { + Profiler.EndSample(); + } + } + } +} \ No newline at end of file diff --git a/Runtime/ProfilerUtility.cs.meta b/Runtime/ProfilerUtility.cs.meta new file mode 100644 index 0000000..677d778 --- /dev/null +++ b/Runtime/ProfilerUtility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7338cbb3bcd0a224fbfca184c7efcd7e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: