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)
{
// would be nice to only clear when system changes, but fixed prefabs rely on stuff in the scene
foreach (var prefab in _fixedPrefabCache.Values)
{
UnityEngine.Object.Destroy(prefab.prefab);
@ -230,15 +231,10 @@ namespace NewHorizons.Builder.Props
if (detail.removeChildren != null)
{
var detailPath = prop.transform.GetPath();
var transforms = prop.GetComponentsInChildren<Transform>(true);
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;
foreach (var childObj in transforms.Where(x => x.GetPath() == path))
foreach (var childObj in prop.transform.FindAll(childPath))
{
flag = false;
childObj.gameObject.SetActive(false);
@ -274,7 +270,7 @@ namespace NewHorizons.Builder.Props
UnityEngine.Object.DestroyImmediate(prop);
prop = newDetailGO;
}
if (isItem)
{
// Else when you put them down you can't pick them back up
@ -286,7 +282,7 @@ namespace NewHorizons.Builder.Props
// For DLC related props
// Make sure to do this before its set active
if (!string.IsNullOrEmpty(detail?.path) &&
if (!string.IsNullOrEmpty(detail?.path) &&
(detail.path.ToLowerInvariant().StartsWith("ringworld") || detail.path.ToLowerInvariant().StartsWith("dreamworld")))
{
prop.AddComponent<DestroyOnDLC>()._destroyOnDLCNotOwned = true;
@ -305,7 +301,7 @@ namespace NewHorizons.Builder.Props
if (!string.IsNullOrEmpty(detail.activationCondition))
{
ConditionalObjectActivation.SetUp(prop, detail.activationCondition, detail.blinkWhenActiveChanged, true);
ConditionalObjectActivation.SetUp(prop, detail.activationCondition, detail.blinkWhenActiveChanged, true);
}
if (!string.IsNullOrEmpty(detail.deactivationCondition))
{
@ -579,22 +575,22 @@ namespace NewHorizons.Builder.Props
// Manually copied these values from a artifact lantern so that we don't have to find it (works in Eye)
lantern._origLensFlareBrightness = 0f;
lantern._focuserPetalsBaseEulerAngles = new Vector3[]
{
new Vector3(0.7f, 270.0f, 357.5f),
new Vector3(288.7f, 270.1f, 357.4f),
lantern._focuserPetalsBaseEulerAngles = new Vector3[]
{
new Vector3(0.7f, 270.0f, 357.5f),
new Vector3(288.7f, 270.1f, 357.4f),
new Vector3(323.3f, 90.0f, 177.5f),
new Vector3(35.3f, 90.0f, 177.5f),
new Vector3(72.7f, 270.1f, 357.5f)
new Vector3(35.3f, 90.0f, 177.5f),
new Vector3(72.7f, 270.1f, 357.5f)
};
lantern._dirtyFlag_focus = true;
lantern._concealerRootsBaseScale = new Vector3[]
lantern._concealerRootsBaseScale = new Vector3[]
{
Vector3.one,
Vector3.one,
Vector3.one
};
lantern._concealerCoversStartPos = new Vector3[]
lantern._concealerCoversStartPos = new Vector3[]
{
new Vector3(0.0f, 0.0f, 0.0f),
new Vector3(0.0f, -0.1f, 0.0f),
@ -605,7 +601,7 @@ namespace NewHorizons.Builder.Props
};
lantern._dirtyFlag_concealment = true;
lantern.UpdateVisuals();
Destroy(this);
}
}

View File

@ -26,7 +26,7 @@ namespace NewHorizons.Builder.ShipLog
if (_astroObjectToMapModeInfo.TryGetValue(slao, out var mapModeInfo))
{
return mapModeInfo;
}
}
else
{
return null;
@ -147,7 +147,7 @@ namespace NewHorizons.Builder.ShipLog
Rect rect = new Rect(0, 0, texture.width, texture.height);
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;
}
@ -188,7 +188,7 @@ namespace NewHorizons.Builder.ShipLog
Texture2D image = null;
Texture2D outline = null;
string imagePath = body.Config.ShipLog?.mapMode?.revealedSprite;
string outlinePath = body.Config.ShipLog?.mapMode?.outlineSprite;
@ -589,7 +589,7 @@ namespace NewHorizons.Builder.ShipLog
GameObject newNodeGO = CreateMapModeGameObject(node.mainBody, parent, layer, position);
ShipLogAstroObject astroObject = AddShipLogAstroObject(newNodeGO, node.mainBody, greyScaleMaterial, layer);
if (node.mainBody.Config.FocalPoint != null)
{
{
astroObject._imageObj.GetComponent<Image>().enabled = false;
astroObject._outlineObj.GetComponent<Image>().enabled = false;
astroObject._unviewedObj.GetComponent<Image>().enabled = false;
@ -623,7 +623,7 @@ namespace NewHorizons.Builder.ShipLog
}
private static void ReplaceExistingMapModeIcon(NewHorizonsBody body, ModBehaviour mod, MapModeInfo info)
{
{
var astroObject = _astroObjectToShipLog[body.Object];
var gameObject = astroObject.gameObject;
var layer = gameObject.layer;

View File

@ -246,7 +246,7 @@ namespace NewHorizons.Builder.ShipLog
Texture2D newTexture = ImageUtilities.GetTexture(body.Mod, relativePath);
Rect rect = new Rect(0, 0, newTexture.width, newTexture.height);
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)
{

View File

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

View File

@ -63,7 +63,7 @@ namespace NewHorizons.Components.ShipLog
}
}
/*
/*
if(VesselCoordinatePromptHandler.KnowsEyeCoordinates())
{
AddSystemCard("EyeOfTheUniverse");
@ -279,7 +279,7 @@ namespace NewHorizons.Components.ShipLog
{
var rect = new Rect(0, 0, texture.width, texture.height);
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)

View File

@ -64,6 +64,11 @@ namespace NewHorizons.External.Configs
/// </summary>
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
#region Modules
@ -530,7 +535,7 @@ namespace NewHorizons.External.Configs
Spawn.shipSpawnPoints = new SpawnModule.ShipSpawnPoint[] { Spawn.shipSpawn };
}
// Because these guys put TWO spawn points
// Because these guys put TWO spawn points
if (starSystem == "2walker2.OogaBooga" && name == "The Campground")
{
Spawn.playerSpawnPoints[0].isDefault = true;
@ -761,4 +766,4 @@ namespace NewHorizons.External.Configs
}
#endregion
}
}
}

View File

@ -172,26 +172,29 @@ namespace NewHorizons.Handlers
// I don't remember doing this why is it exceptions what am I doing
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;
}
catch (Exception)
{
if (body?.Config?.name == null)
try
{
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)
{
NHLogger.LogError($"{body.Config.name} was meant to be destroyed, but was not found");
return false;
if (existingPlanet == null && body.Config.destroy)
{
NHLogger.LogError($"{body.Config.name} was meant to be destroyed, but was not found");
return false;
}
}
if (existingPlanet != null)
@ -291,9 +294,9 @@ namespace NewHorizons.Handlers
try
{
NHLogger.Log($"Creating [{body.Config.name}]");
var planetObject = GenerateBody(body, defaultPrimaryToSun)
var planetObject = GenerateBody(body, defaultPrimaryToSun)
?? throw new NullReferenceException("Something went wrong when generating the body but no errors were logged.");
planetObject.SetActive(true);
var ao = planetObject.GetComponent<NHAstroObject>();
@ -320,7 +323,7 @@ namespace NewHorizons.Handlers
{
NHLogger.LogError($"Error in event handler for OnPlanetLoaded on body {body.Config.name}: {e}");
}
body.UnloadCache(true);
_loadedBodies.Add(body);
return true;
@ -394,7 +397,7 @@ namespace NewHorizons.Handlers
body.Config.MapMarker.enabled = false;
const float sphereOfInfluence = 2000f;
var owRigidBody = RigidBodyBuilder.Make(go, sphereOfInfluence, body.Config);
var ao = AstroObjectBuilder.Make(go, null, body, false);
@ -406,7 +409,7 @@ namespace NewHorizons.Handlers
BrambleDimensionBuilder.Make(body, go, ao, sector, body.Mod, owRigidBody);
go = SharedGenerateBody(body, go, sector, owRigidBody);
// Not included in SharedGenerate to not mess up gravity on base game planets
if (body.Config.Base.surfaceGravity != 0)
{
@ -471,7 +474,7 @@ namespace NewHorizons.Handlers
}
var sphereOfInfluence = GetSphereOfInfluence(body);
var owRigidBody = RigidBodyBuilder.Make(go, sphereOfInfluence, body.Config);
var ao = AstroObjectBuilder.Make(go, primaryBody, body, false);
@ -690,7 +693,7 @@ namespace NewHorizons.Handlers
SunOverrideBuilder.Make(go, sector, body.Config.Atmosphere, body.Config.Water, surfaceSize);
}
}
if (body.Config.Atmosphere.fogSize != 0)
{
fog = FogBuilder.Make(go, sector, body.Config.Atmosphere, body.Mod);
@ -1003,15 +1006,10 @@ namespace NewHorizons.Handlers
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)
{
// Multiple children can have the same path so we delete all that match
var path = $"{goPath}/{childPath}";
var flag = true;
foreach (var childObj in transforms.Where(x => x.GetPath() == path))
foreach (var childObj in go.transform.FindAll(childPath))
{
flag = false;
// idk why we wait here but we do

View File

@ -2,6 +2,7 @@ using NewHorizons.Utility;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Profiling;
namespace NewHorizons.Handlers
{
@ -51,6 +52,9 @@ namespace NewHorizons.Handlers
/// </summary>
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
// tries the cache first, then builds
if (!_objectCache.TryGetValue(obj, out var assetBundles))
@ -94,7 +98,9 @@ namespace NewHorizons.Handlers
assetBundles = assetBundlesList.ToArray();
_objectCache[obj] = assetBundles;
}
Profiler.EndSample();
Profiler.BeginSample("get sectors");
foreach (var assetBundle in assetBundles)
{
// Track the sector even if its null. null means stay loaded forever
@ -105,7 +111,9 @@ namespace NewHorizons.Handlers
}
sectors.SafeAdd(sector);
}
Profiler.EndSample();
Profiler.BeginSample("load assets");
if (sector)
{
sector.OnOccupantEnterSector += _ =>
@ -128,6 +136,7 @@ namespace NewHorizons.Handlers
foreach (var assetBundle in assetBundles)
StreamingManager.LoadStreamingAssets(assetBundle);
}
Profiler.EndSample();
}
public static bool IsBundleInUse(string assetBundle)
@ -152,4 +161,4 @@ namespace NewHorizons.Handlers
}
}
}
}
}

View File

@ -109,7 +109,7 @@ namespace NewHorizons.Handlers
var tex = ImageUtilities.GetTexture(mod, filepath, false);
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);
}

View File

@ -4,6 +4,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;
using static NewHorizons.External.Configs.StarSystemConfig;
namespace NewHorizons.Handlers
@ -47,7 +48,7 @@ namespace NewHorizons.Handlers
if (_textureCache == null) _textureCache = new List<Texture2D>();
_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);

View File

@ -10,6 +10,8 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
<NoWarn>1701;1702;1591</NoWarn>
<!-- <DefineConstants>ENABLE_PROFILER</DefineConstants>-->
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DebugType>none</DebugType>
@ -39,4 +41,4 @@
<ItemGroup>
<Content Include="NewHorizons.csproj.user" />
</ItemGroup>
</Project>
</Project>

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"
}
},
"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": {
"type": "array",
"description": "Add ambient lights to this body",

View File

@ -12,6 +12,7 @@ namespace NewHorizons.Utility.Files
public static class AssetBundleUtilities
{
public static Dictionary<string, (AssetBundle bundle, bool keepLoaded)> AssetBundles = new();
private static Dictionary<string, GameObject> _prefabCache = 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);
_prefabCache.Clear();
}
public static void PreloadBundle(string assetBundleRelativeDir, IModBehaviour mod)
@ -113,11 +115,17 @@ namespace NewHorizons.Utility.Files
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);
ReplaceShaders(prefab);
// replacing shaders is expensive, so cache it
_prefabCache.Add(assetBundleRelativeDir + pathInBundle, prefab);
return prefab;
}

View File

@ -2,6 +2,7 @@ using NewHorizons.Utility.OWML;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Profiling;
using UnityEngine.SceneManagement;
using Object = UnityEngine.Object;
@ -116,19 +117,23 @@ namespace NewHorizons.Utility
if (CachedGameObjects.TryGetValue(path, out var go)) return go;
// 1: normal find
Profiler.BeginSample("1");
go = GameObject.Find(path);
if (go)
{
CachedGameObjects.Add(path, go);
Profiler.EndSample();
return go;
}
Profiler.EndSample();
Profiler.BeginSample("2");
// 2: find inactive using root + transform.find
var names = path.Split('/');
// Cache the root objects so we don't loop through all of them each time
var rootName = names[0];
if (!CachedRootGameObjects.TryGetValue(rootName, out var root))
if (!CachedRootGameObjects.TryGetValue(rootName, out var root))
{
root = SceneManager.GetActiveScene().GetRootGameObjects().FirstOrDefault(x => x.name == rootName);
if (root != null)
@ -142,9 +147,12 @@ namespace NewHorizons.Utility
if (go)
{
CachedGameObjects.Add(path, go);
Profiler.EndSample();
return go;
}
Profiler.EndSample();
Profiler.BeginSample("3");
var name = names.Last();
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)
@ -153,10 +161,12 @@ namespace NewHorizons.Utility
if (go)
{
CachedGameObjects.Add(path, go);
Profiler.EndSample();
return go;
}
if (warn) NHLogger.LogWarning($"Couldn't find object with name {name}");
Profiler.EndSample();
return null;
}
@ -169,5 +179,31 @@ namespace NewHorizons.Utility
}
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;
}
}
}