profiling nh (#1030)

semi-automated profile marker generation. disable-able with a
project-wide compiler flag
This commit is contained in:
xen-42 2025-02-15 23:38:55 -05:00 committed by GitHub
commit 7697e5bb49
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 196 additions and 63 deletions

View File

@ -56,6 +56,7 @@ namespace NewHorizons.Builder.Props
private static void SceneManager_sceneUnloaded(Scene scene) private static void SceneManager_sceneUnloaded(Scene scene)
{ {
// would be nice to only clear when system changes, but fixed prefabs rely on stuff in the scene
foreach (var prefab in _fixedPrefabCache.Values) foreach (var prefab in _fixedPrefabCache.Values)
{ {
UnityEngine.Object.Destroy(prefab.prefab); UnityEngine.Object.Destroy(prefab.prefab);
@ -230,15 +231,10 @@ namespace NewHorizons.Builder.Props
if (detail.removeChildren != null) if (detail.removeChildren != null)
{ {
var detailPath = prop.transform.GetPath();
var transforms = prop.GetComponentsInChildren<Transform>(true);
foreach (var childPath in detail.removeChildren) foreach (var childPath in detail.removeChildren)
{ {
// Multiple children can have the same path so we delete all that match
var path = $"{detailPath}/{childPath}";
var flag = true; var flag = true;
foreach (var childObj in transforms.Where(x => x.GetPath() == path)) foreach (var childObj in prop.transform.FindAll(childPath))
{ {
flag = false; flag = false;
childObj.gameObject.SetActive(false); childObj.gameObject.SetActive(false);

View File

@ -147,7 +147,7 @@ namespace NewHorizons.Builder.ShipLog
Rect rect = new Rect(0, 0, texture.width, texture.height); Rect rect = new Rect(0, 0, texture.width, texture.height);
Vector2 pivot = new Vector2(texture.width / 2, texture.height / 2); Vector2 pivot = new Vector2(texture.width / 2, texture.height / 2);
newImage.sprite = Sprite.Create(texture, rect, pivot); newImage.sprite = Sprite.Create(texture, rect, pivot, 100, 0, SpriteMeshType.FullRect, Vector4.zero, false);
return newImageGO; return newImageGO;
} }

View File

@ -246,7 +246,7 @@ namespace NewHorizons.Builder.ShipLog
Texture2D newTexture = ImageUtilities.GetTexture(body.Mod, relativePath); Texture2D newTexture = ImageUtilities.GetTexture(body.Mod, relativePath);
Rect rect = new Rect(0, 0, newTexture.width, newTexture.height); Rect rect = new Rect(0, 0, newTexture.width, newTexture.height);
Vector2 pivot = new Vector2(newTexture.width / 2, newTexture.height / 2); Vector2 pivot = new Vector2(newTexture.width / 2, newTexture.height / 2);
return Sprite.Create(newTexture, rect, pivot); return Sprite.Create(newTexture, rect, pivot, 100, 0, SpriteMeshType.FullRect, Vector4.zero, false);
} }
catch (Exception) catch (Exception)
{ {

View File

@ -13,7 +13,7 @@ namespace NewHorizons.Components.Props
public bool CloseEyes; public bool CloseEyes;
public bool SetActiveWithCondition; public bool SetActiveWithCondition;
private PlayerCameraEffectController _playerCameraEffectController; private static PlayerCameraEffectController _playerCameraEffectController;
private bool _changeConditionOnExitConversation; private bool _changeConditionOnExitConversation;
private bool _inConversation; private bool _inConversation;
@ -45,7 +45,7 @@ namespace NewHorizons.Components.Props
public void Awake() public void Awake()
{ {
_playerCameraEffectController = GameObject.FindObjectOfType<PlayerCameraEffectController>(); if (_playerCameraEffectController == null) _playerCameraEffectController = GameObject.FindObjectOfType<PlayerCameraEffectController>();
GlobalMessenger<string, bool>.AddListener("DialogueConditionChanged", OnDialogueConditionChanged); GlobalMessenger<string, bool>.AddListener("DialogueConditionChanged", OnDialogueConditionChanged);
GlobalMessenger.AddListener("ExitConversation", OnExitConversation); GlobalMessenger.AddListener("ExitConversation", OnExitConversation);
GlobalMessenger.AddListener("EnterConversation", OnEnterConversation); GlobalMessenger.AddListener("EnterConversation", OnEnterConversation);

View File

@ -279,7 +279,7 @@ namespace NewHorizons.Components.ShipLog
{ {
var rect = new Rect(0, 0, texture.width, texture.height); var rect = new Rect(0, 0, texture.width, texture.height);
var pivot = new Vector2(texture.width / 2, texture.height / 2); var pivot = new Vector2(texture.width / 2, texture.height / 2);
return Sprite.Create(texture, rect, pivot); return Sprite.Create(texture, rect, pivot, 100, 0, SpriteMeshType.FullRect, Vector4.zero, false);
} }
private void OnTargetReferenceFrame(ReferenceFrame referenceFrame) private void OnTargetReferenceFrame(ReferenceFrame referenceFrame)

View File

@ -64,6 +64,11 @@ namespace NewHorizons.External.Configs
/// </summary> /// </summary>
public string[] removeChildren; public string[] removeChildren;
/// <summary>
/// optimization. turn this off if you know you're generating a new body and aren't worried about other addons editing it.
/// </summary>
[DefaultValue(true)] public bool checkForExisting = true;
#endregion #endregion
#region Modules #region Modules

View File

@ -172,26 +172,29 @@ namespace NewHorizons.Handlers
// I don't remember doing this why is it exceptions what am I doing // I don't remember doing this why is it exceptions what am I doing
GameObject existingPlanet = null; GameObject existingPlanet = null;
try if (body.Config.checkForExisting) // TODO: remove this when we cache name->fullpath in Find
{ {
existingPlanet = AstroObjectLocator.GetAstroObject(body.Config.name).gameObject; try
}
catch (Exception)
{
if (body?.Config?.name == null)
{ {
NHLogger.LogError($"How is there no name for {body}"); existingPlanet = AstroObjectLocator.GetAstroObject(body.Config.name).gameObject;
} }
else catch (Exception)
{ {
existingPlanet = SearchUtilities.Find(body.Config.name.Replace(" ", "") + "_Body", false); if (body?.Config?.name == null)
{
NHLogger.LogError($"How is there no name for {body}");
}
else
{
existingPlanet = SearchUtilities.Find(body.Config.name.Replace(" ", "") + "_Body", false);
}
} }
}
if (existingPlanet == null && body.Config.destroy) if (existingPlanet == null && body.Config.destroy)
{ {
NHLogger.LogError($"{body.Config.name} was meant to be destroyed, but was not found"); NHLogger.LogError($"{body.Config.name} was meant to be destroyed, but was not found");
return false; return false;
}
} }
if (existingPlanet != null) if (existingPlanet != null)
@ -1003,15 +1006,10 @@ namespace NewHorizons.Handlers
private static void RemoveChildren(GameObject go, NewHorizonsBody body) private static void RemoveChildren(GameObject go, NewHorizonsBody body)
{ {
var goPath = go.transform.GetPath();
var transforms = go.GetComponentsInChildren<Transform>(true);
foreach (var childPath in body.Config.removeChildren) foreach (var childPath in body.Config.removeChildren)
{ {
// Multiple children can have the same path so we delete all that match
var path = $"{goPath}/{childPath}";
var flag = true; var flag = true;
foreach (var childObj in transforms.Where(x => x.GetPath() == path)) foreach (var childObj in go.transform.FindAll(childPath))
{ {
flag = false; flag = false;
// idk why we wait here but we do // idk why we wait here but we do

View File

@ -2,6 +2,7 @@ using NewHorizons.Utility;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using UnityEngine; using UnityEngine;
using UnityEngine.Profiling;
namespace NewHorizons.Handlers namespace NewHorizons.Handlers
{ {
@ -51,6 +52,9 @@ namespace NewHorizons.Handlers
/// </summary> /// </summary>
public static void SetUpStreaming(GameObject obj, Sector sector) public static void SetUpStreaming(GameObject obj, Sector sector)
{ {
// TODO: used OFTEN by detail builder. 20-40ms adds up to seconds. speed up!
Profiler.BeginSample("get bundles");
// find the asset bundles to load // find the asset bundles to load
// tries the cache first, then builds // tries the cache first, then builds
if (!_objectCache.TryGetValue(obj, out var assetBundles)) if (!_objectCache.TryGetValue(obj, out var assetBundles))
@ -94,7 +98,9 @@ namespace NewHorizons.Handlers
assetBundles = assetBundlesList.ToArray(); assetBundles = assetBundlesList.ToArray();
_objectCache[obj] = assetBundles; _objectCache[obj] = assetBundles;
} }
Profiler.EndSample();
Profiler.BeginSample("get sectors");
foreach (var assetBundle in assetBundles) foreach (var assetBundle in assetBundles)
{ {
// Track the sector even if its null. null means stay loaded forever // Track the sector even if its null. null means stay loaded forever
@ -105,7 +111,9 @@ namespace NewHorizons.Handlers
} }
sectors.SafeAdd(sector); sectors.SafeAdd(sector);
} }
Profiler.EndSample();
Profiler.BeginSample("load assets");
if (sector) if (sector)
{ {
sector.OnOccupantEnterSector += _ => sector.OnOccupantEnterSector += _ =>
@ -128,6 +136,7 @@ namespace NewHorizons.Handlers
foreach (var assetBundle in assetBundles) foreach (var assetBundle in assetBundles)
StreamingManager.LoadStreamingAssets(assetBundle); StreamingManager.LoadStreamingAssets(assetBundle);
} }
Profiler.EndSample();
} }
public static bool IsBundleInUse(string assetBundle) public static bool IsBundleInUse(string assetBundle)

View File

@ -109,7 +109,7 @@ namespace NewHorizons.Handlers
var tex = ImageUtilities.GetTexture(mod, filepath, false); var tex = ImageUtilities.GetTexture(mod, filepath, false);
if (tex == null) return; if (tex == null) return;
var sprite = Sprite.Create(tex, new Rect(0.0f, 0.0f, tex.width, tex.height), new Vector2(0.5f, 0.5f), 100.0f); var sprite = Sprite.Create(tex, new Rect(0.0f, 0.0f, tex.width, tex.height), new Vector2(0.5f, 0.5f), 100, 0, SpriteMeshType.FullRect, Vector4.zero, false);
AddSubtitle(sprite); AddSubtitle(sprite);
} }

View File

@ -4,6 +4,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using UnityEngine; using UnityEngine;
using UnityEngine.UI;
using static NewHorizons.External.Configs.StarSystemConfig; using static NewHorizons.External.Configs.StarSystemConfig;
namespace NewHorizons.Handlers namespace NewHorizons.Handlers
@ -47,7 +48,7 @@ namespace NewHorizons.Handlers
if (_textureCache == null) _textureCache = new List<Texture2D>(); if (_textureCache == null) _textureCache = new List<Texture2D>();
_textureCache.Add(texture); _textureCache.Add(texture);
var sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(texture.width / 2f, texture.height / 2f)); var sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(texture.width / 2f, texture.height / 2f), 100, 0, SpriteMeshType.FullRect, Vector4.zero, false);
var name = ShipLogStarChartMode.UniqueIDToName(systemID); var name = ShipLogStarChartMode.UniqueIDToName(systemID);

View File

@ -10,6 +10,8 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch> <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
<NoWarn>1701;1702;1591</NoWarn> <NoWarn>1701;1702;1591</NoWarn>
<!-- <DefineConstants>ENABLE_PROFILER</DefineConstants>-->
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DebugType>none</DebugType> <DebugType>none</DebugType>

View File

@ -0,0 +1,73 @@
#if ENABLE_PROFILER
using HarmonyLib;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using UnityEngine.Profiling;
namespace NewHorizons.Patches;
/// <summary>
/// attach profiler markers to important methods
/// </summary>
[HarmonyPatch]
public static class ProfilerPatch
{
private static string FriendlyName(this MethodBase @this) => $"{@this.DeclaringType.Name}.{@this.Name}";
[HarmonyTargetMethods]
public static IEnumerable<MethodBase> TargetMethods()
{
foreach (var type in Assembly.GetExecutingAssembly().GetTypes())
{
if (!(
type.Name == "Main" ||
type.Name.EndsWith("Builder") ||
type.Name.EndsWith("Handler") ||
type.Name.EndsWith("Utilities")
)) continue;
foreach (var method in type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly))
{
if (method.ContainsGenericParameters) continue;
// Main.Instance.ModHelper.Console.WriteLine($"[profiler] profiling {method.FriendlyName()}");
yield return method;
}
}
}
[HarmonyPrefix]
public static void Prefix(MethodBase __originalMethod /*, out Stopwatch __state*/)
{
Profiler.BeginSample(__originalMethod.FriendlyName());
// __state = new Stopwatch();
// __state.Start();
}
[HarmonyPostfix]
public static void Postfix( /*MethodBase __originalMethod, Stopwatch __state*/)
{
Profiler.EndSample();
// __state.Stop();
// Main.Instance.ModHelper.Console.WriteLine($"[profiler] {__originalMethod.MethodName()} took {__state.Elapsed.TotalMilliseconds:f1} ms");
}
}
/// <summary>
/// bundle loading causes log spam that slows loading, but only in unity dev profiler mode.
/// patch it out so it doesnt do false-positive slowness.
/// </summary>
[HarmonyPatch]
public static class DisableShaderLogSpamPatch
{
[HarmonyPrefix]
[HarmonyPatch(typeof(StackTraceUtility), "ExtractStackTrace")]
[HarmonyPatch(typeof(Application), "CallLogCallback")]
private static bool DisableShaderLogSpam() => false;
}
#endif

View File

@ -43,6 +43,11 @@
"type": "string" "type": "string"
} }
}, },
"checkForExisting": {
"type": "boolean",
"description": "optimization. turn this off if you know you're generating a new body and aren't worried about other addons editing it.",
"default": true
},
"AmbientLights": { "AmbientLights": {
"type": "array", "type": "array",
"description": "Add ambient lights to this body", "description": "Add ambient lights to this body",

View File

@ -12,6 +12,7 @@ namespace NewHorizons.Utility.Files
public static class AssetBundleUtilities public static class AssetBundleUtilities
{ {
public static Dictionary<string, (AssetBundle bundle, bool keepLoaded)> AssetBundles = new(); public static Dictionary<string, (AssetBundle bundle, bool keepLoaded)> AssetBundles = new();
private static Dictionary<string, GameObject> _prefabCache = new();
private static readonly List<AssetBundleCreateRequest> _loadingBundles = new(); private static readonly List<AssetBundleCreateRequest> _loadingBundles = new();
@ -52,6 +53,7 @@ namespace NewHorizons.Utility.Files
} }
AssetBundles = AssetBundles.Where(x => x.Value.keepLoaded).ToDictionary(x => x.Key, x => x.Value); AssetBundles = AssetBundles.Where(x => x.Value.keepLoaded).ToDictionary(x => x.Key, x => x.Value);
_prefabCache.Clear();
} }
public static void PreloadBundle(string assetBundleRelativeDir, IModBehaviour mod) public static void PreloadBundle(string assetBundleRelativeDir, IModBehaviour mod)
@ -113,12 +115,18 @@ namespace NewHorizons.Utility.Files
public static GameObject LoadPrefab(string assetBundleRelativeDir, string pathInBundle, IModBehaviour mod) public static GameObject LoadPrefab(string assetBundleRelativeDir, string pathInBundle, IModBehaviour mod)
{ {
var prefab = Load<GameObject>(assetBundleRelativeDir, pathInBundle, mod); if (_prefabCache.TryGetValue(assetBundleRelativeDir + pathInBundle, out var prefab))
return prefab;
prefab = Load<GameObject>(assetBundleRelativeDir, pathInBundle, mod);
prefab.SetActive(false); prefab.SetActive(false);
ReplaceShaders(prefab); ReplaceShaders(prefab);
// replacing shaders is expensive, so cache it
_prefabCache.Add(assetBundleRelativeDir + pathInBundle, prefab);
return prefab; return prefab;
} }

View File

@ -2,6 +2,7 @@ using NewHorizons.Utility.OWML;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using UnityEngine; using UnityEngine;
using UnityEngine.Profiling;
using UnityEngine.SceneManagement; using UnityEngine.SceneManagement;
using Object = UnityEngine.Object; using Object = UnityEngine.Object;
@ -116,13 +117,17 @@ namespace NewHorizons.Utility
if (CachedGameObjects.TryGetValue(path, out var go)) return go; if (CachedGameObjects.TryGetValue(path, out var go)) return go;
// 1: normal find // 1: normal find
Profiler.BeginSample("1");
go = GameObject.Find(path); go = GameObject.Find(path);
if (go) if (go)
{ {
CachedGameObjects.Add(path, go); CachedGameObjects.Add(path, go);
Profiler.EndSample();
return go; return go;
} }
Profiler.EndSample();
Profiler.BeginSample("2");
// 2: find inactive using root + transform.find // 2: find inactive using root + transform.find
var names = path.Split('/'); var names = path.Split('/');
@ -142,9 +147,12 @@ namespace NewHorizons.Utility
if (go) if (go)
{ {
CachedGameObjects.Add(path, go); CachedGameObjects.Add(path, go);
Profiler.EndSample();
return go; return go;
} }
Profiler.EndSample();
Profiler.BeginSample("3");
var name = names.Last(); var name = names.Last();
if (warn) NHLogger.LogWarning($"Couldn't find object in path {path}, will look for potential matches for name {name}"); if (warn) NHLogger.LogWarning($"Couldn't find object in path {path}, will look for potential matches for name {name}");
// 3: find resource to include inactive objects (but skip prefabs) // 3: find resource to include inactive objects (but skip prefabs)
@ -153,10 +161,12 @@ namespace NewHorizons.Utility
if (go) if (go)
{ {
CachedGameObjects.Add(path, go); CachedGameObjects.Add(path, go);
Profiler.EndSample();
return go; return go;
} }
if (warn) NHLogger.LogWarning($"Couldn't find object with name {name}"); if (warn) NHLogger.LogWarning($"Couldn't find object with name {name}");
Profiler.EndSample();
return null; return null;
} }
@ -169,5 +179,31 @@ namespace NewHorizons.Utility
} }
return children; return children;
} }
/// <summary>
/// transform.find but works for gameobjects with same name
/// </summary>
public static List<Transform> FindAll(this Transform @this, string path)
{
var names = path.Split('/');
var currentTransforms = new List<Transform> { @this };
foreach (var name in names)
{
var newTransforms = new List<Transform>();
foreach (var currentTransform in currentTransforms)
{
foreach (Transform child in currentTransform)
{
if (child.name == name)
{
newTransforms.Add(child);
}
}
}
currentTransforms = newTransforms;
}
return currentTransforms;
}
} }
} }