Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of REPOFidelity v1.6.3
REPOFidelity.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using MenuLib; using MenuLib.MonoBehaviors; using Microsoft.CodeAnalysis; using REPOFidelity.Patches; using REPOFidelity.Shaders; using REPOFidelity.Upscalers; using TMPro; using Unity.Profiling; using Unity.Profiling.LowLevel.Unsafe; using UnityEngine; using UnityEngine.Profiling; using UnityEngine.Rendering; using UnityEngine.Rendering.PostProcessing; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: AssemblyCompany("Vippy")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.6.3.0")] [assembly: AssemblyInformationalVersion("1.6.3+5b124e5e41855e590e49024ae9fec305860b5997")] [assembly: AssemblyProduct("REPOFidelity")] [assembly: AssemblyTitle("REPOFidelity")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.6.3.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace REPOFidelity { internal static class BuildInfo { public const string Version = "1.6.3"; } internal class CostProbe : MonoBehaviour { private class CamTiming { public readonly Stopwatch Sw = new Stopwatch(); public double TotalMs; public int Frames; } internal static class ScriptTiming { internal class Acc { public long Ticks; public int Calls; } private static readonly Dictionary<MethodBase, Acc> _accs = new Dictionary<MethodBase, Acc>(); internal static void Clear() { _accs.Clear(); } internal static void Reset() { foreach (Acc value in _accs.Values) { value.Ticks = 0L; value.Calls = 0; } } internal static void Register(MethodInfo m) { if (!_accs.ContainsKey(m)) { _accs[m] = new Acc(); } } internal static (long ticks, int calls) Read(MethodInfo m) { if (_accs.TryGetValue(m, out Acc value)) { return (value.Ticks, value.Calls); } return (0L, 0); } public static void Prefix(out long __state) { __state = Stopwatch.GetTimestamp(); } public static void Postfix(MethodBase __originalMethod, long __state) { if (_accs.TryGetValue(__originalMethod, out Acc value)) { value.Ticks += Stopwatch.GetTimestamp() - __state; value.Calls++; } } } private struct Sample { public float AvgFps; public float AvgMs; public float P1Low; public float P01Low; public int FrameCount; } private struct RendererBreakdown { public int Total; public int Visible; public int ShadowCasting; public int ShadowCastingHidden; public int NoLODGroup; } private struct LightBreakdown { public int Active; public int Shadowing; public int Directional; public int PointShadow; public int SpotShadow; } private struct LightInfo { public string Name; public string Type; public string ShadowMode; public float Range; public int Resolution; public float CostScore; } private struct SceneMetrics { public RendererBreakdown Renderers; public LightBreakdown Lights; public long TotalSceneTris; public int SkinnedRenderers; public int TotalParticles; public int ActiveParticles; public int ActiveParticleCount; public int ReflectionProbes; public int TrailRenderers; public int LineRenderers; public int AudioSources; public int AudioSourcesPlaying; public int UpdatingBehaviours; public int LateUpdatingBehaviours; public int FixedUpdatingBehaviours; public List<LightInfo> LightDetail; public List<(string typeName, int count)> TopBehaviourTypes; } [CompilerGenerated] private sealed class <>c__DisplayClass39_0 { public double tickToMsMod; public int baselineFrames; public List<(string name, string cat, double n)> countStats; public StringBuilder report; public bool anyCount; public int cellIdx; public int totalCells; public Sample autoSample; public Sample vanillaSample; internal (string name, double ms, float callsPerFrame, int calls) <Run>b__3((string name, long ticks, int calls) r) { return (r.name, (double)r.ticks * tickToMsMod / (double)baselineFrames, (float)r.calls / (float)baselineFrames, r.calls); } internal void <Run>b__9(Sample v) { autoSample = v; } internal void <Run>b__10(Sample v) { vanillaSample = v; } } [CompilerGenerated] private sealed class <>c__DisplayClass39_1 { public HashSet<string> topSet; internal bool <Run>b__18(Type t) { return topSet.Contains(t.Name); } } [CompilerGenerated] private sealed class <>c__DisplayClass39_2 { public Sample s; internal void <Run>b__21(Sample v) { s = v; } } [CompilerGenerated] private sealed class <>c__DisplayClass39_3 { public Sample ps; internal void <Run>b__22(Sample v) { ps = v; } } [CompilerGenerated] private sealed class <Run>d__39 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public CostProbe <>4__this; private <>c__DisplayClass39_0 <>8__1; private <>c__DisplayClass39_2 <>8__2; private <>c__DisplayClass39_3 <>8__3; private CursorLockMode <savedLockState>5__2; private bool <savedVisible>5__3; private QualityPreset <origPreset>5__4; private UpscaleMode <origUpscaler>5__5; private float <origFog>5__6; private bool <origModEnabled>5__7; private List<(ProfilerRecorder rec, string name, string cat)> <timeRecs>5__8; private List<(ProfilerRecorder rec, string name, string cat)> <countRecs>5__9; private List<(ProfilerRecorder rec, string name, string cat)> <memRecs>5__10; private double[] <timeTotals>5__11; private long[] <countTotals>5__12; private long[] <memTotals>5__13; private int <gen0Start>5__14; private int <gen1Start>5__15; private long <monoStart>5__16; private float <worstFrameMs>5__17; private List<float> <frames>5__18; private float <elapsed>5__19; private List<(string name, long ticks, int calls)> <modTimingSnapshot>5__20; private List<(string name, string cat, double ms)> <timeStats>5__21; private List<(string name, string cat, double bytes)> <memStats>5__22; private float[] <abMs>5__23; private float[] <abFps>5__24; private float[] <abWorst>5__25; private int[] <abGen1Arr>5__26; private long[] <abMonoArr>5__27; private bool[] <abOn>5__28; private List<(string name, string cat, double bytes)> <perFrameMem>5__29; private SceneMetrics <scene>5__30; private List<(string label, double totalMs, double perCallUs, int calls)> <scriptTimings>5__31; private List<UpscaleMode> <upscalers>5__32; private QualityPreset[] <presetLadder>5__33; private float[] <fogPasses>5__34; private List<(string label, float ms, float shadowD, float lightD, bool isActive)> <sweepResults>5__35; private Sample <potatoSample>5__36; private int <s>5__37; private int <g1Start>5__38; private long <mStart>5__39; private float <worst>5__40; private List<float> <fr>5__41; private float <el>5__42; private Harmony <harmony>5__43; private List<MethodInfo> <patched>5__44; private List<UpscaleMode>.Enumerator <>7__wrap44; private UpscaleMode <mode>5__46; private float[] <>7__wrap46; private QualityPreset[] <>7__wrap47; private QualityPreset <p>5__49; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <Run>d__39(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || (uint)(num - 12) <= 1u) { try { } finally { <>m__Finally1(); } } <>8__1 = null; <>8__2 = null; <>8__3 = null; <timeRecs>5__8 = null; <countRecs>5__9 = null; <memRecs>5__10 = null; <timeTotals>5__11 = null; <countTotals>5__12 = null; <memTotals>5__13 = null; <frames>5__18 = null; <modTimingSnapshot>5__20 = null; <timeStats>5__21 = null; <memStats>5__22 = null; <abMs>5__23 = null; <abFps>5__24 = null; <abWorst>5__25 = null; <abGen1Arr>5__26 = null; <abMonoArr>5__27 = null; <abOn>5__28 = null; <perFrameMem>5__29 = null; <scene>5__30 = default(SceneMetrics); <scriptTimings>5__31 = null; <upscalers>5__32 = null; <presetLadder>5__33 = null; <fogPasses>5__34 = null; <sweepResults>5__35 = null; <fr>5__41 = null; <harmony>5__43 = null; <patched>5__44 = null; <>7__wrap44 = default(List<UpscaleMode>.Enumerator); <>7__wrap46 = null; <>7__wrap47 = null; <>1__state = -2; } private bool MoveNext() { //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_25d8: Unknown result type (might be due to invalid IL or missing references) //IL_25e2: Expected O, but got Unknown //IL_2a6f: Unknown result type (might be due to invalid IL or missing references) //IL_2a79: Expected O, but got Unknown //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_0742: Unknown result type (might be due to invalid IL or missing references) //IL_074c: Expected O, but got Unknown //IL_074c: Unknown result type (might be due to invalid IL or missing references) //IL_0756: Expected O, but got Unknown //IL_0762: Unknown result type (might be due to invalid IL or missing references) //IL_076c: Expected O, but got Unknown //IL_076c: Unknown result type (might be due to invalid IL or missing references) //IL_0776: Expected O, but got Unknown //IL_265d: Unknown result type (might be due to invalid IL or missing references) //IL_2667: Expected O, but got Unknown //IL_2da6: Unknown result type (might be due to invalid IL or missing references) //IL_0395: Unknown result type (might be due to invalid IL or missing references) //IL_039f: Expected O, but got Unknown //IL_03ee: Unknown result type (might be due to invalid IL or missing references) //IL_03f3: Unknown result type (might be due to invalid IL or missing references) //IL_03f8: Unknown result type (might be due to invalid IL or missing references) //IL_03fc: Unknown result type (might be due to invalid IL or missing references) //IL_040f: Unknown result type (might be due to invalid IL or missing references) //IL_0414: Unknown result type (might be due to invalid IL or missing references) //IL_0418: Unknown result type (might be due to invalid IL or missing references) //IL_0423: Unknown result type (might be due to invalid IL or missing references) //IL_0428: Unknown result type (might be due to invalid IL or missing references) //IL_043e: Unknown result type (might be due to invalid IL or missing references) //IL_0443: Unknown result type (might be due to invalid IL or missing references) //IL_0445: Unknown result type (might be due to invalid IL or missing references) //IL_0448: Unknown result type (might be due to invalid IL or missing references) //IL_045a: Expected I4, but got Unknown //IL_2816: Unknown result type (might be due to invalid IL or missing references) //IL_2820: Expected O, but got Unknown //IL_04af: Unknown result type (might be due to invalid IL or missing references) //IL_04b9: Expected O, but got Unknown //IL_04b9: Unknown result type (might be due to invalid IL or missing references) //IL_04c3: Expected O, but got Unknown //IL_04cf: Unknown result type (might be due to invalid IL or missing references) //IL_04d9: Expected O, but got Unknown //IL_04d9: Unknown result type (might be due to invalid IL or missing references) //IL_04e3: Expected O, but got Unknown //IL_04e9: Unknown result type (might be due to invalid IL or missing references) //IL_04f3: Expected O, but got Unknown //IL_2977: Unknown result type (might be due to invalid IL or missing references) //IL_2981: Expected O, but got Unknown //IL_2de6: Unknown result type (might be due to invalid IL or missing references) //IL_2df0: Expected O, but got Unknown //IL_0d38: Unknown result type (might be due to invalid IL or missing references) //IL_0d42: Expected O, but got Unknown //IL_24fa: Unknown result type (might be due to invalid IL or missing references) //IL_2504: Expected O, but got Unknown //IL_1eb1: Unknown result type (might be due to invalid IL or missing references) //IL_1ebb: Expected O, but got Unknown //IL_1eeb: Unknown result type (might be due to invalid IL or missing references) //IL_1ef2: Expected O, but got Unknown //IL_1ef2: Unknown result type (might be due to invalid IL or missing references) //IL_1ef9: Expected O, but got Unknown //IL_1fde: Unknown result type (might be due to invalid IL or missing references) //IL_1fe8: Expected O, but got Unknown try { int num = <>1__state; CostProbe costProbe = <>4__this; Sample sample; double num10; int num11; (ProfilerRecorder, string, string) tuple3; int num18; int num19; long monoUsedSizeLong; int num20; Sample sample2; double num24; double num25; double num26; double num27; string text6; long num28; float num29; float num30; float num31; float num32; float num33; long num34; long num35; float num36; int num38; List<(string, double, float, int)> list; switch (num) { default: return false; case 0: <>1__state = -1; <>8__1 = new <>c__DisplayClass39_0(); Running = true; Progress = 0f; costProbe._camTimings.Clear(); <>2__current = costProbe.WaitForAutotuneIfActive(); <>1__state = 1; return true; case 1: { <>1__state = -1; <savedLockState>5__2 = Cursor.lockState; <savedVisible>5__3 = Cursor.visible; Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; <origPreset>5__4 = Settings.Preset; <origUpscaler>5__5 = Settings.UpscaleModeSetting; <origFog>5__6 = Settings.FogDistanceMultiplier; <origModEnabled>5__7 = Settings.ModEnabled; if (!<origModEnabled>5__7) { Settings.ModEnabled = true; SceneOptimizer.Apply(); QualityPatch.ApplyQualitySettings(); } <>8__1.report = new StringBuilder(); <>8__1.report.AppendLine(string.Format("== REPOFidelity frame cost — {0:yyyy-MM-dd HH:mm:ss} (v{1}) ==", DateTime.Now, "1.6.3")); <>8__1.report.AppendLine($"GPU: {SystemInfo.graphicsDeviceName} ({SystemInfo.graphicsMemorySize}MB, {SystemInfo.graphicsDeviceType})"); <>8__1.report.AppendLine($"CPU: {SystemInfo.processorType} ({SystemInfo.processorCount} cores, {SystemInfo.systemMemorySize}MB RAM)"); <>8__1.report.AppendLine("OS: " + SystemInfo.operatingSystem); StringBuilder report = <>8__1.report; object[] obj = new object[4] { Screen.width, Screen.height, null, null }; Resolution currentResolution = Screen.currentResolution; RefreshRate refreshRateRatio = ((Resolution)(ref currentResolution)).refreshRateRatio; obj[2] = ((RefreshRate)(ref refreshRateRatio)).value; obj[3] = (Screen.fullScreen ? "fullscreen" : "windowed"); report.AppendLine(string.Format("Screen: {0}x{1} @ {2:F0}Hz ({3})", obj)); <>8__1.report.AppendLine($"Mod: [{Settings.Preset}] upscaler={Settings.ResolvedUpscaleMode} " + $"scale={Settings.ResolvedRenderScale}% shadowQ={Settings.ResolvedShadowQuality} " + $"shadowD={Settings.ResolvedShadowDistance:F0}m lights={Settings.ResolvedPixelLightCount} " + $"lod={Settings.ResolvedLODBias:F1} AF={Settings.ResolvedAnisotropicFiltering}x"); <>8__1.report.AppendLine($"Range: fog={Settings.ResolvedFogMultiplier:F2}x " + $"effectiveFogEnd={Settings.ResolvedEffectiveFogEnd:F0}m " + $"lightD={Settings.ResolvedLightDistance:F0}m shadowBudget={Settings.ResolvedShadowBudget}"); <>8__1.report.AppendLine($"Flags: modEnabled={Settings.ModEnabled} optEnabled={Settings.OptimizationsEnabled} cpuPatches={Settings.CpuPatchesActive}"); <>8__1.report.AppendLine(); Status = "Measuring (all markers + per-camera + scene)"; <>2__current = (object)new WaitForSeconds(1.5f); <>1__state = 2; return true; } case 2: { <>1__state = -1; List<ProfilerRecorderHandle> list3 = new List<ProfilerRecorderHandle>(); ProfilerRecorderHandle.GetAvailable(list3); <timeRecs>5__8 = new List<(ProfilerRecorder, string, string)>(); <countRecs>5__9 = new List<(ProfilerRecorder, string, string)>(); <memRecs>5__10 = new List<(ProfilerRecorder, string, string)>(); foreach (ProfilerRecorderHandle item13 in list3) { ProfilerRecorderDescription description = ProfilerRecorderHandle.GetDescription(item13); ProfilerRecorder item11 = ProfilerRecorder.StartNew(((ProfilerRecorderDescription)(ref description)).Category, ((ProfilerRecorderDescription)(ref description)).Name, 256, (ProfilerRecorderOptions)24); string name = ((ProfilerRecorderDescription)(ref description)).Name; ProfilerCategory category = ((ProfilerRecorderDescription)(ref description)).Category; (ProfilerRecorder, string, string) item12 = (item11, name, ((object)(ProfilerCategory)(ref category)).ToString()); ProfilerMarkerDataUnit unitType = ((ProfilerRecorderDescription)(ref description)).UnitType; switch (unitType - 1) { case 0: <timeRecs>5__8.Add(item12); break; case 2: <countRecs>5__9.Add(item12); break; case 1: <memRecs>5__10.Add(item12); break; } } Camera.onPreRender = (CameraCallback)Delegate.Combine((Delegate?)(object)Camera.onPreRender, (Delegate?)new CameraCallback(costProbe.OnCamPre)); Camera.onPostRender = (CameraCallback)Delegate.Combine((Delegate?)(object)Camera.onPostRender, (Delegate?)new CameraCallback(costProbe.OnCamPost)); <>2__current = (object)new WaitForSeconds(1.5f); <>1__state = 3; return true; } case 3: <>1__state = -1; Progress = 0.03f; <timeTotals>5__11 = new double[<timeRecs>5__8.Count]; <countTotals>5__12 = new long[<countRecs>5__9.Count]; <memTotals>5__13 = new long[<memRecs>5__10.Count]; <gen0Start>5__14 = GC.CollectionCount(0); <gen1Start>5__15 = GC.CollectionCount(1); <monoStart>5__16 = Profiler.GetMonoUsedSizeLong(); <worstFrameMs>5__17 = 0f; ModTiming.Reset(); <frames>5__18 = new List<float>(2048); <elapsed>5__19 = 0f; goto IL_06f2; case 4: <>1__state = -1; goto IL_06f2; case 5: <>1__state = -1; <g1Start>5__38 = GC.CollectionCount(1); <mStart>5__39 = Profiler.GetMonoUsedSizeLong(); <worst>5__40 = 0f; <fr>5__41 = new List<float>(2048); <el>5__42 = 0f; goto IL_0e1b; case 6: <>1__state = -1; goto IL_0e1b; case 7: <>1__state = -1; ScriptTiming.Reset(); <s>5__37 = 0; <el>5__42 = 0f; goto IL_2073; case 8: <>1__state = -1; goto IL_2073; case 9: <>1__state = -1; <>8__1.autoSample = default(Sample); <>2__current = costProbe.SampleFrames(3f, delegate(Sample v) { <>8__1.autoSample = v; }); <>1__state = 10; return true; case 10: <>1__state = -1; <sweepResults>5__35.Add(("Auto", <>8__1.autoSample.AvgMs, Settings.ResolvedShadowDistance, Settings.ResolvedLightDistance, <origPreset>5__4 == QualityPreset.Auto)); SweepProgress(); Settings.ApplyPreset(QualityPreset.Ultra); Settings.ResolvedUpscaleMode = UpscaleMode.DLAA; Settings.ResolvedRenderScale = 100; Settings.ResolvedFogMultiplier = 1f; QualityPatch.ApplyFogAndDrawDistance(); SceneOptimizer.Apply(); QualityPatch.ApplyQualitySettings(); <>2__current = (object)new WaitForSeconds(1.5f); <>1__state = 11; return true; case 11: <>1__state = -1; <>7__wrap44 = <upscalers>5__32.GetEnumerator(); <>1__state = -3; goto IL_2737; case 12: <>1__state = -3; <>8__2.s = default(Sample); <>2__current = costProbe.SampleFrames(3f, delegate(Sample v) { <>8__2.s = v; }); <>1__state = 13; return true; case 13: <>1__state = -3; <sweepResults>5__35.Add(($"{<mode>5__46}", <>8__2.s.AvgMs, Settings.ResolvedShadowDistance, Settings.ResolvedLightDistance, <mode>5__46 == <origUpscaler>5__5 && <origPreset>5__4 == Settings.Preset)); SweepProgress(); <>8__2 = null; goto IL_2737; case 14: <>1__state = -1; <>8__3.ps = default(Sample); <>2__current = costProbe.SampleFrames(3f, delegate(Sample v) { <>8__3.ps = v; }); <>1__state = 15; return true; case 15: <>1__state = -1; <sweepResults>5__35.Add(($"{<p>5__49}@{<el>5__42:F1}x", <>8__3.ps.AvgMs, Settings.ResolvedShadowDistance, Settings.ResolvedLightDistance, false)); if (<p>5__49 == QualityPreset.Potato && Mathf.Approximately(<el>5__42, 1.1f)) { <potatoSample>5__36 = <>8__3.ps; } SweepProgress(); <>8__3 = null; <g1Start>5__38++; goto IL_2915; case 16: <>1__state = -1; <>8__1.vanillaSample = default(Sample); <>2__current = costProbe.SampleFrames(3f, delegate(Sample v) { <>8__1.vanillaSample = v; }); <>1__state = 17; return true; case 17: <>1__state = -1; <sweepResults>5__35.Add(("Vanilla (F10)", <>8__1.vanillaSample.AvgMs, QualitySettings.shadowDistance, 0f, false)); SweepProgress(); Settings.ModEnabled = <origModEnabled>5__7; Settings.Preset = <origPreset>5__4; Settings.UpscaleModeSetting = <origUpscaler>5__5; Settings.FogDistanceMultiplier = <origFog>5__6; SceneOptimizer.Apply(); QualityPatch.ApplyQualitySettings(); QualityPatch.ApplyFogAndDrawDistance(); costProbe.RestoreFrameLimit(); costProbe.RestorePresetRevertSuppression(); costProbe._sweepSmoothActive = false; <>2__current = (object)new WaitForSeconds(1.5f); <>1__state = 18; return true; case 18: { <>1__state = -1; float num2 = <sweepResults>5__35.Min<(string, float, float, float, bool)>(((string label, float ms, float shadowD, float lightD, bool isActive) r) => r.ms); foreach (var item14 in <sweepResults>5__35) { string item = item14.label; float item2 = item14.ms; float item3 = item14.shadowD; float item4 = item14.lightD; string text = (item14.isActive ? " (active)" : ""); string text2 = (Mathf.Approximately(item2, num2) ? " ← fastest" : $" {item2 - num2:+0.00;-0.00}"); string text3 = ((item4 > 0f) ? $" shadowD={item3:F0}m lightD={item4:F0}m" : $" shadowD={item3:F0}m"); <>8__1.report.AppendLine($" {item,-16} {item2,6:F2} ms{text3}{text}{text2}"); } float num3 = <sweepResults>5__35.Where<(string, float, float, float, bool)>(((string label, float ms, float shadowD, float lightD, bool isActive) r) => r.label != "Vanilla (F10)").Min<(string, float, float, float, bool)>(((string label, float ms, float shadowD, float lightD, bool isActive) r) => r.ms) - <>8__1.vanillaSample.AvgMs; float num4 = <potatoSample>5__36.AvgMs - <>8__1.vanillaSample.AvgMs; <>8__1.report.AppendLine($" Mod overhead vs vanilla (best mod mode): {num3:+0.00;-0.00} ms"); <>8__1.report.AppendLine($" Potato vs vanilla: {num4:+0.00;-0.00} ms (negative = Potato wins)"); <>8__1.report.AppendLine(); <>8__1.report.AppendLine("== Optimization opportunities (ranked by likely impact) =="); BuildOpportunities(<>8__1.report, <timeStats>5__21, <>8__1.countStats, <perFrameMem>5__29, <scene>5__30, <scriptTimings>5__31); <>8__1.report.AppendLine(); AppendConfigFiles(<>8__1.report); string text4 = <>8__1.report.ToString(); bool flag = false; try { File.AppendAllText(OutputPath, text4); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Cost probe save failed: " + ex.Message)); } try { flag = TrySystemClipboard(text4); } catch (Exception ex2) { Plugin.Log.LogWarning((object)("Clipboard copy failed: " + ex2.Message)); } Plugin.Log.LogInfo((object)(flag ? ("Cost probe: appended to " + OutputPath + " (copied to clipboard)") : ("Cost probe: appended to " + OutputPath + " (clipboard unavailable — read the file)"))); Cursor.lockState = <savedLockState>5__2; Cursor.visible = <savedVisible>5__3; Status = (flag ? "Done (clipboard)" : "Done (file only)"); Progress = 1f; Running = false; <>2__current = (object)new WaitForSeconds(5f); <>1__state = 19; return true; } case 19: { <>1__state = -1; Status = ""; return false; } IL_2737: if (<>7__wrap44.MoveNext()) { <mode>5__46 = <>7__wrap44.Current; <>8__2 = new <>c__DisplayClass39_2(); Status = $"Sweep: {<mode>5__46}"; Settings.UpscaleModeSetting = <mode>5__46; <>2__current = (object)new WaitForSeconds(1.5f); <>1__state = 12; return true; } <>m__Finally1(); <>7__wrap44 = default(List<UpscaleMode>.Enumerator); <potatoSample>5__36 = default(Sample); <>7__wrap46 = <fogPasses>5__34; <s>5__37 = 0; goto IL_293d; IL_0e1b: if (<el>5__42 < 8f) { float unscaledDeltaTime = Time.unscaledDeltaTime; <fr>5__41.Add(unscaledDeltaTime); <el>5__42 += unscaledDeltaTime; float num5 = unscaledDeltaTime * 1000f; if (num5 > <worst>5__40) { <worst>5__40 = num5; } Progress = 0.25f + 0.04f * ((float)(<s>5__37 - 1) + <el>5__42 / 8f); <>2__current = null; <>1__state = 6; return true; } sample = ComputeSample(<fr>5__41); <abMs>5__23[<s>5__37] = sample.AvgMs; <abFps>5__24[<s>5__37] = sample.AvgFps; <abWorst>5__25[<s>5__37] = <worst>5__40; <abGen1Arr>5__26[<s>5__37] = GC.CollectionCount(1) - <g1Start>5__38; <abMonoArr>5__27[<s>5__37] = (Profiler.GetMonoUsedSizeLong() - <mStart>5__39) / 1024; <fr>5__41 = null; <s>5__37++; goto IL_0ec6; IL_293d: if (<s>5__37 < <>7__wrap46.Length) { <el>5__42 = <>7__wrap46[<s>5__37]; <>7__wrap47 = <presetLadder>5__33; <g1Start>5__38 = 0; goto IL_2915; } <>7__wrap46 = null; Status = "Sweep: Vanilla (mod OFF)"; Settings.ModEnabled = false; QualityPatch.RestoreVanillaQuality(); SceneOptimizer.Apply(); <>2__current = (object)new WaitForSeconds(1.5f); <>1__state = 16; return true; IL_2915: if (<g1Start>5__38 < <>7__wrap47.Length) { <p>5__49 = <>7__wrap47[<g1Start>5__38]; <>8__3 = new <>c__DisplayClass39_3(); Status = $"Sweep: {<p>5__49} @ fog {<el>5__42:F1}x"; Settings.ApplyPreset(<p>5__49); Settings.ResolvedFogMultiplier = <el>5__42; QualityPatch.ApplyFogAndDrawDistance(); SceneOptimizer.Apply(); QualityPatch.ApplyQualitySettings(); <>2__current = (object)new WaitForSeconds(1.5f); <>1__state = 14; return true; } <>7__wrap47 = null; <s>5__37++; goto IL_293d; IL_220b: <>8__1.report.AppendLine($"== Script cost per type (Harmony-instrumented, {4f:F0}s, top {20} types) =="); if (<scriptTimings>5__31.Count == 0) { <>8__1.report.AppendLine(" (no script methods captured)"); } else { <>8__1.report.AppendLine(" ms/frame calls/f µs/call method"); foreach (var (text5, num6, num7, num8) in <scriptTimings>5__31) { <>8__1.report.AppendLine($" {num6,7:F3} {num8,5} {num7,7:F2} {text5}"); } double num9 = <scriptTimings>5__31.Sum<(string, double, double, int)>(((string label, double totalMs, double perCallUs, int calls) x) => x.totalMs); <>8__1.report.AppendLine(" ──────"); <>8__1.report.AppendLine($" {num9,7:F3} (sum of instrumented methods)"); } <>8__1.report.AppendLine(); costProbe._savedVSyncCount = QualitySettings.vSyncCount; costProbe._savedTargetFrameRate = Application.targetFrameRate; QualitySettings.vSyncCount = 0; Application.targetFrameRate = -1; costProbe._frameLimitUncapped = true; Settings.PushPresetRevertSuppression(); costProbe._presetRevertSuppressed = true; Status = "Sweep: Auto / DLSS / FSR / Off / Potato / Vanilla"; <>8__1.report.AppendLine($"== Sweep (Auto = autotune's picks; rest normalized Ultra + DLAA + fog 1.0×, VSync off + uncapped, {3f}s each) =="); <upscalers>5__32 = new List<UpscaleMode>(); if (GPUDetector.IsUpscalerSupported(UpscaleMode.DLSS)) { <upscalers>5__32.Add(UpscaleMode.DLSS); } if (GPUDetector.IsUpscalerSupported(UpscaleMode.FSR_Temporal)) { <upscalers>5__32.Add(UpscaleMode.FSR_Temporal); } <upscalers>5__32.Add(UpscaleMode.Off); <presetLadder>5__33 = new QualityPreset[5] { QualityPreset.Potato, QualityPreset.Low, QualityPreset.Medium, QualityPreset.High, QualityPreset.Ultra }; <fogPasses>5__34 = new float[2] { 1.1f, 0.3f }; <sweepResults>5__35 = new List<(string, float, float, float, bool)>(); <>8__1.totalCells = 1 + <upscalers>5__32.Count + <presetLadder>5__33.Length * <fogPasses>5__34.Length + 1; <>8__1.cellIdx = 0; costProbe._sweepStartProgress = 0.38f; costProbe._sweepEndProgress = 0.98f; costProbe._sweepExpectedDuration = (float)<>8__1.totalCells * 4.5f; costProbe._sweepStartTime = Time.unscaledTime; costProbe._sweepSmoothActive = true; Status = "Sweep: Auto (autotune's picks)"; Settings.ApplyPreset(QualityPreset.Auto); QualityPatch.ApplyFogAndDrawDistance(); SceneOptimizer.Apply(); QualityPatch.ApplyQualitySettings(); <>2__current = (object)new WaitForSeconds(1.5f); <>1__state = 9; return true; IL_2073: if (<el>5__42 < 4f) { <el>5__42 += Time.unscaledDeltaTime; <s>5__37++; Progress = 0.28f + 0.09f * (<el>5__42 / 4f); <>2__current = null; <>1__state = 8; return true; } foreach (MethodInfo item15 in <patched>5__44) { try { <harmony>5__43.Unpatch((MethodBase)item15, (HarmonyPatchType)1, <harmony>5__43.Id); } catch { } try { <harmony>5__43.Unpatch((MethodBase)item15, (HarmonyPatchType)2, <harmony>5__43.Id); } catch { } } num10 = 1000.0 / (double)Stopwatch.Frequency; num11 = Mathf.Max(1, <s>5__37); foreach (MethodInfo item16 in <patched>5__44) { var (num12, num13) = ScriptTiming.Read(item16); if (num13 != 0) { double item5 = (double)num12 * num10 / (double)num11; double item6 = ((num13 == 0) ? 0.0 : ((double)num12 * 1000000.0 / (double)Stopwatch.Frequency / (double)num13)); string item7 = item16.DeclaringType.Name + "." + item16.Name; <scriptTimings>5__31.Add((item7, item5, item6, num13 / num11)); } } <scriptTimings>5__31.Sort(((string label, double totalMs, double perCallUs, int calls) a, (string label, double totalMs, double perCallUs, int calls) b) => b.totalMs.CompareTo(a.totalMs)); ScriptTiming.Clear(); <harmony>5__43 = null; <patched>5__44 = null; goto IL_220b; IL_06f2: if (<elapsed>5__19 < 8f) { float unscaledDeltaTime2 = Time.unscaledDeltaTime; <frames>5__18.Add(unscaledDeltaTime2); <elapsed>5__19 += unscaledDeltaTime2; float num14 = unscaledDeltaTime2 * 1000f; if (num14 > <worstFrameMs>5__17) { <worstFrameMs>5__17 = num14; } Progress = 0.03f + 0.22f * (<elapsed>5__19 / 8f); for (int i = 0; i < <timeRecs>5__8.Count; i++) { ref double reference = ref <timeTotals>5__11[i]; double num15 = reference; tuple3 = <timeRecs>5__8[i]; reference = num15 + (double)((ProfilerRecorder)(ref tuple3.Item1)).LastValue; } for (int j = 0; j < <countRecs>5__9.Count; j++) { ref long reference2 = ref <countTotals>5__12[j]; long num16 = reference2; tuple3 = <countRecs>5__9[j]; reference2 = num16 + ((ProfilerRecorder)(ref tuple3.Item1)).LastValue; } for (int k = 0; k < <memRecs>5__10.Count; k++) { ref long reference3 = ref <memTotals>5__13[k]; long num17 = reference3; tuple3 = <memRecs>5__10[k]; reference3 = num17 + ((ProfilerRecorder)(ref tuple3.Item1)).LastValue; } <>2__current = null; <>1__state = 4; return true; } <modTimingSnapshot>5__20 = ModTiming.Read().ToList(); num18 = GC.CollectionCount(0) - <gen0Start>5__14; num19 = GC.CollectionCount(1) - <gen1Start>5__15; monoUsedSizeLong = Profiler.GetMonoUsedSizeLong(); Camera.onPreRender = (CameraCallback)Delegate.Remove((Delegate?)(object)Camera.onPreRender, (Delegate?)new CameraCallback(costProbe.OnCamPre)); Camera.onPostRender = (CameraCallback)Delegate.Remove((Delegate?)(object)Camera.onPostRender, (Delegate?)new CameraCallback(costProbe.OnCamPost)); Progress = 0.25f; num20 = Mathf.Max(1, <frames>5__18.Count); <timeStats>5__21 = new List<(string, string, double)>(<timeRecs>5__8.Count); <>8__1.countStats = new List<(string, string, double)>(<countRecs>5__9.Count); <memStats>5__22 = new List<(string, string, double)>(<memRecs>5__10.Count); for (int l = 0; l < <timeRecs>5__8.Count; l++) { <timeStats>5__21.Add((<timeRecs>5__8[l].name, <timeRecs>5__8[l].cat, <timeTotals>5__11[l] / (double)num20 / 1000000.0)); } for (int m = 0; m < <countRecs>5__9.Count; m++) { <>8__1.countStats.Add((<countRecs>5__9[m].name, <countRecs>5__9[m].cat, (double)<countTotals>5__12[m] / (double)num20)); } for (int n = 0; n < <memRecs>5__10.Count; n++) { <memStats>5__22.Add((<memRecs>5__10[n].name, <memRecs>5__10[n].cat, (double)<memTotals>5__13[n] / (double)num20)); } for (int num21 = 0; num21 < <timeRecs>5__8.Count; num21++) { tuple3 = <timeRecs>5__8[num21]; ((ProfilerRecorder)(ref tuple3.Item1)).Dispose(); } for (int num22 = 0; num22 < <countRecs>5__9.Count; num22++) { tuple3 = <countRecs>5__9[num22]; ((ProfilerRecorder)(ref tuple3.Item1)).Dispose(); } for (int num23 = 0; num23 < <memRecs>5__10.Count; num23++) { tuple3 = <memRecs>5__10[num23]; ((ProfilerRecorder)(ref tuple3.Item1)).Dispose(); } sample2 = ComputeSample(<frames>5__18); <timeStats>5__21.Sort(((string name, string cat, double ms) a, (string name, string cat, double ms) b) => b.ms.CompareTo(a.ms)); <>8__1.countStats.Sort(((string name, string cat, double n) a, (string name, string cat, double n) b) => b.n.CompareTo(a.n)); <memStats>5__22.Sort(((string name, string cat, double bytes) a, (string name, string cat, double bytes) b) => b.bytes.CompareTo(a.bytes)); num24 = LookupMs(<timeStats>5__21, "CPU Main Thread Frame Time"); num25 = LookupMs(<timeStats>5__21, "CPU Render Thread Frame Time"); num26 = LookupMs(<timeStats>5__21, "CPU Total Frame Time"); num27 = LookupMs(<timeStats>5__21, "GPU Frame Time"); if (num27 > 100.0 || num27 < 0.0) { num27 = 0.0; } text6 = ((num27 <= 0.0) ? "CPU (main thread) [GPU marker unavailable]" : ((num27 > num24 + 0.2) ? "GPU" : ((num24 > num27 + 0.2) ? "CPU (main thread)" : "balanced CPU/GPU"))); <>8__1.report.AppendLine($"Baseline: {sample2.AvgMs:F2} ms ({sample2.AvgFps:F0} fps) 1%={sample2.P1Low:F0} fps 0.1%={sample2.P01Low:F0} fps worstFrame={<worstFrameMs>5__17:F1} ms ({sample2.FrameCount} frames)"); if (num26 > 0.0) { <>8__1.report.AppendLine($"CPU total: {num26:F2} ms main={num24:F2} render={num25:F2}"); } if (num27 > 0.0) { <>8__1.report.AppendLine($"GPU total: {num27:F2} ms"); } <>8__1.report.AppendLine("Bottleneck: " + text6); num28 = (monoUsedSizeLong - <monoStart>5__16) / 1024; num29 = (float)num18 / 8f; <>8__1.report.AppendLine($"GC: gen0={num18} gen1={num19} over {8f:F0}s ({num29:F2}/s) mono Δ={num28:+0;-0} KB"); <>8__1.report.AppendLine(); <abMs>5__23 = new float[4] { sample2.AvgMs, 0f, 0f, 0f }; <abFps>5__24 = new float[4] { sample2.AvgFps, 0f, 0f, 0f }; <abWorst>5__25 = new float[4] { <worstFrameMs>5__17, 0f, 0f, 0f }; <abGen1Arr>5__26 = new int[4] { num19, 0, 0, 0 }; <abMonoArr>5__27 = new long[4] { num28, 0L, 0L, 0L }; <abOn>5__28 = new bool[4] { true, false, true, false }; <s>5__37 = 1; goto IL_0ec6; IL_0ec6: if (<s>5__37 < 4) { Status = string.Format("A/B sample {0}/4 (patches {1})", <s>5__37 + 1, <abOn>5__28[<s>5__37] ? "ON" : "OFF"); Settings.AllocationFixesEnabled = <abOn>5__28[<s>5__37]; <>2__current = (object)new WaitForSeconds(1.5f); <>1__state = 5; return true; } Settings.AllocationFixesEnabled = true; num30 = (<abMs>5__23[0] + <abMs>5__23[2]) / 2f; num31 = (<abMs>5__23[1] + <abMs>5__23[3]) / 2f; num32 = (<abWorst>5__25[0] + <abWorst>5__25[2]) / 2f; num33 = (<abWorst>5__25[1] + <abWorst>5__25[3]) / 2f; num34 = (<abMonoArr>5__27[0] + <abMonoArr>5__27[2]) / 2; num35 = (<abMonoArr>5__27[1] + <abMonoArr>5__27[3]) / 2; num36 = <abMs>5__23[2] - <abMs>5__23[0]; <>8__1.report.AppendLine("== A/B compare (allocation patches on/off, ON-OFF-ON-OFF, same scene) =="); for (int num37 = 0; num37 < 4; num37++) { string text7 = (<abOn>5__28[num37] ? "ON " : "OFF"); <>8__1.report.AppendLine($" #{num37 + 1} {text7}: {<abMs>5__23[num37]:F2} ms ({<abFps>5__24[num37]:F0} fps) worst={<abWorst>5__25[num37]:F1} gen1={<abGen1Arr>5__26[num37]} mono Δ={<abMonoArr>5__27[num37]:+0;-0} KB"); } <>8__1.report.AppendLine($" ON avg : {num30:F2} ms worst {num32:F1} mono {num34:+0;-0} KB"); <>8__1.report.AppendLine($" OFF avg : {num31:F2} ms worst {num33:F1} mono {num35:+0;-0} KB"); <>8__1.report.AppendLine($" Patches effect (OFF − ON): {num31 - num30:+0.00;-0.00} ms worst {num33 - num32:+0.0;-0.0} ms mono {num35 - num34:+0;-0} KB (positive = patches helped)"); <>8__1.report.AppendLine($" Order bias check (ON#2 − ON#1): {num36:+0.00;-0.00} ms (large negative = consistent second-window-faster bias, treat OFF − ON delta with caution)"); <>8__1.report.AppendLine(); <>8__1.report.AppendLine("== Frame cost — profiler markers ranked highest→lowest (ms/frame) =="); num38 = 0; foreach (var (arg, arg2, num39) in <timeStats>5__21) { if (!(num39 < 0.01)) { <>8__1.report.AppendLine($" {num39,7:F2} ms [{arg2,-12}] {arg}"); if (++num38 >= 80) { break; } } } if (num38 == 0) { <>8__1.report.AppendLine(" (no profiler time markers captured — Unity build may strip them)"); } <>8__1.report.AppendLine(); <>8__1.tickToMsMod = 1000.0 / (double)Stopwatch.Frequency; <>8__1.baselineFrames = Mathf.Max(1, <frames>5__18.Count); list = (from r in <modTimingSnapshot>5__20 select (r.name, (double)r.ticks * <>8__1.tickToMsMod / (double)<>8__1.baselineFrames, (float)r.calls / (float)<>8__1.baselineFrames, r.calls) into r where r.calls > 0 orderby r.ms descending select r).ToList(); <>8__1.report.AppendLine($"== Mod-internal cost (Stopwatch spans over {8f:F0}s baseline, ms/frame) =="); if (list.Count == 0) { <>8__1.report.AppendLine(" (no mod spans hit — mod may be disabled or pipeline idle)"); } else { <>8__1.report.AppendLine(" ms/frame calls/f name"); double num40 = 0.0; foreach (var item17 in list) { <>8__1.report.AppendLine(string.Format(" {0,7:F3} {1,5:F1} {2}", item17.Item2, item17.Item3, item17.Item1.Substring("REPOFidelity.".Length))); num40 += item17.Item2; } <>8__1.report.AppendLine(" ──────"); <>8__1.report.AppendLine($" {num40,7:F3} (sum of instrumented mod spans)"); } <>8__1.report.AppendLine(); <>8__1.report.AppendLine("== Draw stats (per frame avg) =="); <>8__1.anyCount = false; EmitCount("Draw calls:", "Draw Calls Count"); EmitCount("Batches:", "Batches Count"); EmitCount("SetPass calls:", "SetPass Calls Count"); EmitCount("Triangles:", "Triangles Count"); EmitCount("Vertices:", "Vertices Count"); EmitCount("Shadow casters:", "Shadow Casters Count"); EmitCount("Used textures:", "Used Textures Count"); EmitCount("Render textures:", "Render Textures Count"); if (!<>8__1.anyCount) { <>8__1.report.AppendLine(" (no count markers captured)"); } <>8__1.report.AppendLine(); if (<>8__1.countStats.Count > 0) { <>8__1.report.AppendLine("== All count markers, ranked (top 20) =="); int num41 = 0; foreach (var (arg3, arg4, num42) in <>8__1.countStats) { if (!(num42 < 1.0)) { <>8__1.report.AppendLine($" {FmtBig(num42),10} [{arg4,-12}] {arg3}"); if (++num41 >= 20) { break; } } } <>8__1.report.AppendLine(); } <perFrameMem>5__29 = <memStats>5__22.Where<(string, string, double)>(((string name, string cat, double bytes) x) => x.name.IndexOf("In Frame", StringComparison.OrdinalIgnoreCase) >= 0 || x.name.IndexOf("Alloc", StringComparison.OrdinalIgnoreCase) >= 0).ToList(); if (<perFrameMem>5__29.Count > 0) { <>8__1.report.AppendLine("== Allocations per frame (GC / buffer uploads) =="); foreach (var (arg5, arg6, num43) in <perFrameMem>5__29) { if (!(num43 < 1.0)) { <>8__1.report.AppendLine($" {FmtBytes(num43),10} [{arg6,-12}] {arg5}"); } } <>8__1.report.AppendLine(); } <>8__1.report.AppendLine("== Per-camera render cost (CPU Stopwatch around onPreRender/onPostRender) =="); if (costProbe._camTimings.Count == 0) { <>8__1.report.AppendLine(" (no camera events fired during sample — unusual)"); } else { foreach (var item18 in (from kv in costProbe._camTimings select (kv.Key, kv.Value.TotalMs / (double)Mathf.Max(1, kv.Value.Frames), kv.Value.Frames) into x orderby x.ms descending select x).ToList()) { Camera item8 = item18.Item1; double item9 = item18.Item2; int item10 = item18.Item3; string arg7 = (((Object)(object)item8 != (Object)null) ? ((Object)item8).name : "(destroyed)"); <>8__1.report.AppendLine($" {item9,7:F2} ms {arg7} ({item10} renders)"); } } costProbe._camTimings.Clear(); <>8__1.report.AppendLine(); <scene>5__30 = GatherSceneMetrics(); <>8__1.report.AppendLine("== Scene composition =="); <>8__1.report.AppendLine($" Renderers: {<scene>5__30.Renderers.Total}"); <>8__1.report.AppendLine($" Visible: {<scene>5__30.Renderers.Visible} ({Pct(<scene>5__30.Renderers.Visible, <scene>5__30.Renderers.Total)})"); <>8__1.report.AppendLine($" Shadow casting: {<scene>5__30.Renderers.ShadowCasting} ({Pct(<scene>5__30.Renderers.ShadowCasting, <scene>5__30.Renderers.Total)})"); <>8__1.report.AppendLine($" Shadow casting + off-screen: {<scene>5__30.Renderers.ShadowCastingHidden} ({Pct(<scene>5__30.Renderers.ShadowCastingHidden, <scene>5__30.Renderers.Total)}) ← off-screen shadow work"); <>8__1.report.AppendLine($" No LODGroup: {<scene>5__30.Renderers.NoLODGroup} ({Pct(<scene>5__30.Renderers.NoLODGroup, <scene>5__30.Renderers.Total)})"); <>8__1.report.AppendLine(" Tris (sum of sharedMesh): " + FmtBig(<scene>5__30.TotalSceneTris)); <>8__1.report.AppendLine($" Skinned mesh renderers: {<scene>5__30.SkinnedRenderers}"); <>8__1.report.AppendLine($" Particle systems: {<scene>5__30.TotalParticles} total, {<scene>5__30.ActiveParticles} emitting, {FmtBig(<scene>5__30.ActiveParticleCount)} particles alive"); <>8__1.report.AppendLine($" Reflection probes: {<scene>5__30.ReflectionProbes}"); <>8__1.report.AppendLine($" Trail / Line renderers: {<scene>5__30.TrailRenderers} / {<scene>5__30.LineRenderers}"); <>8__1.report.AppendLine($" Audio sources (playing): {<scene>5__30.AudioSourcesPlaying} / {<scene>5__30.AudioSources}"); <>8__1.report.AppendLine($" Lights: {<scene>5__30.Lights.Active} active, {<scene>5__30.Lights.Shadowing} casting shadow"); <>8__1.report.AppendLine($" Directional + shadow: {<scene>5__30.Lights.Directional}"); <>8__1.report.AppendLine($" Point + shadow: {<scene>5__30.Lights.PointShadow} (×6 cubemap faces)"); <>8__1.report.AppendLine($" Spot + shadow: {<scene>5__30.Lights.SpotShadow}"); <>8__1.report.AppendLine($" MonoBehaviours w/ Update: {<scene>5__30.UpdatingBehaviours}"); <>8__1.report.AppendLine($" MonoBehaviours w/ LateUpdate: {<scene>5__30.LateUpdatingBehaviours}"); <>8__1.report.AppendLine($" MonoBehaviours w/ FixedUpdate: {<scene>5__30.FixedUpdatingBehaviours}"); <>8__1.report.AppendLine(); AppendMultiplayerSection(<>8__1.report); if (<scene>5__30.TopBehaviourTypes.Count > 0) { <>8__1.report.AppendLine("== MonoBehaviour types with Update, ranked by live instance count (top 20) =="); foreach (var (arg8, num44) in <scene>5__30.TopBehaviourTypes.Take(20)) { <>8__1.report.AppendLine($" {num44,5}× {arg8}"); } <>8__1.report.AppendLine(); } if (<scene>5__30.LightDetail.Count > 0) { <>8__1.report.AppendLine("== Per-light shadow cost proxy, ranked (top 20) =="); <>8__1.report.AppendLine(" score type range res shadow name"); foreach (LightInfo item19 in <scene>5__30.LightDetail.OrderByDescending((LightInfo x) => x.CostScore).Take(20)) { <>8__1.report.AppendLine($" {item19.CostScore,5:F0} {item19.Type,-12} {item19.Range,5:F0}m {item19.Resolution,5} {item19.ShadowMode,-8} {item19.Name}"); } <>8__1.report.AppendLine(" (proxy = faces × res² × range² for shadowers, 0 otherwise — higher = more shadow map work)"); <>8__1.report.AppendLine(); } <scriptTimings>5__31 = new List<(string, double, double, int)>(); if (<scene>5__30.TopBehaviourTypes.Count > 0) { <>c__DisplayClass39_1 CS$<>8__locals0 = new <>c__DisplayClass39_1(); Status = "Script profiling (Harmony)"; List<Type> list2 = new List<Type>(); MonoBehaviour[] array = Object.FindObjectsOfType<MonoBehaviour>(); foreach (MonoBehaviour val in array) { if (((Behaviour)val).isActiveAndEnabled) { Type type = ((object)val).GetType(); if (!list2.Contains(type)) { list2.Add(type); } } } CS$<>8__locals0.topSet = (from x in <scene>5__30.TopBehaviourTypes.Take(20) select x.typeName).ToHashSet(); list2 = list2.Where((Type t) => CS$<>8__locals0.topSet.Contains(t.Name)).ToList(); <harmony>5__43 = new Harmony("REPOFidelity.CostProbe.ScriptTiming"); MethodInfo method = typeof(ScriptTiming).GetMethod("Prefix", BindingFlags.Static | BindingFlags.Public); MethodInfo? method2 = typeof(ScriptTiming).GetMethod("Postfix", BindingFlags.Static | BindingFlags.Public); HarmonyMethod prefix = new HarmonyMethod(method); HarmonyMethod postfix = new HarmonyMethod(method2); ScriptTiming.Clear(); <patched>5__44 = new List<MethodInfo>(); foreach (Type item20 in list2) { PatchIfPresent(item20, "Update", <harmony>5__43, prefix, postfix, <patched>5__44); } foreach (Type item21 in list2) { PatchIfPresent(item21, "LateUpdate", <harmony>5__43, prefix, postfix, <patched>5__44); } foreach (Type item22 in list2) { PatchIfPresent(item22, "FixedUpdate", <harmony>5__43, prefix, postfix, <patched>5__44); } <>2__current = (object)new WaitForSeconds(1.5f); <>1__state = 7; return true; } goto IL_220b; } } catch { //try-fault ((IDisposable)this).Dispose(); throw; } void EmitCount(string label, string marker) { double num46 = LookupCount(((<>c__DisplayClass39_0)this).countStats, marker); if (!(num46 <= 0.0)) { ((<>c__DisplayClass39_0)this).report.AppendLine($" {label,-22} {FmtBig(num46)}"); ((<>c__DisplayClass39_0)this).anyCount = true; } } void SweepProgress() { ((<>c__DisplayClass39_0)this).cellIdx++; Progress = 0.38f + 0.6f * ((float)((<>c__DisplayClass39_0)this).cellIdx / (float)((<>c__DisplayClass39_0)this).totalCells); } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; ((IDisposable)<>7__wrap44).Dispose(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <RunSafe>d__36 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public CostProbe <>4__this; private IEnumerator <inner>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <RunSafe>d__36(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <inner>5__2 = null; <>1__state = -2; } private bool MoveNext() { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Expected O, but got Unknown //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Expected O, but got Unknown //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Expected O, but got Unknown int num = <>1__state; CostProbe costProbe = <>4__this; if (num != 0) { if (num != 1) { return false; } <>1__state = -1; } else { <>1__state = -1; <inner>5__2 = costProbe.Run(); } bool flag; try { flag = <inner>5__2.MoveNext(); } catch (Exception arg) { Plugin.Log.LogError((object)$"Cost probe failed: {arg}"); Camera.onPreRender = (CameraCallback)Delegate.Remove((Delegate?)(object)Camera.onPreRender, (Delegate?)new CameraCallback(costProbe.OnCamPre)); Camera.onPostRender = (CameraCallback)Delegate.Remove((Delegate?)(object)Camera.onPostRender, (Delegate?)new CameraCallback(costProbe.OnCamPost)); costProbe.RestoreFrameLimit(); costProbe.RestorePresetRevertSuppression(); Settings.AllocationFixesEnabled = true; Running = false; Status = "ERROR"; return false; } if (!flag) { return false; } <>2__current = <inner>5__2.Current; <>1__state = 1; return true; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <SampleFrames>d__52 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public float seconds; public Action<Sample> onDone; private List<float> <frames>5__2; private float <elapsed>5__3; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <SampleFrames>d__52(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <frames>5__2 = null; <>1__state = -2; } private bool MoveNext() { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(1.5f); <>1__state = 1; return true; case 1: <>1__state = -1; <frames>5__2 = new List<float>(512); <elapsed>5__3 = 0f; break; case 2: <>1__state = -1; break; } if (<elapsed>5__3 < seconds) { float unscaledDeltaTime = Time.unscaledDeltaTime; <frames>5__2.Add(unscaledDeltaTime); <elapsed>5__3 += unscaledDeltaTime; <>2__current = null; <>1__state = 2; return true; } onDone(ComputeSample(<frames>5__2)); return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <WaitForAutotuneIfActive>d__29 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <WaitForAutotuneIfActive>d__29(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; if (!UpscalerManager.BenchmarkActive) { return false; } Status = "Waiting for autotune to finish"; goto IL_004e; case 1: <>1__state = -1; goto IL_004e; case 2: { <>1__state = -1; return false; } IL_004e: if (UpscalerManager.BenchmarkActive) { <>2__current = null; <>1__state = 1; return true; } <>2__current = (object)new WaitForSeconds(1.5f); <>1__state = 2; return true; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } internal static string Status = ""; internal static float Progress; private const float WarmupSeconds = 1.5f; private const float SampleSeconds = 8f; private const float SettleSeconds = 1.5f; private const float UpscalerSampleSeconds = 3f; private const float ScriptSampleSeconds = 4f; private const int ScriptTopN = 20; private static readonly string OutputPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? "", "frame_cost.txt"); private readonly Dictionary<Camera, CamTiming> _camTimings = new Dictionary<Camera, CamTiming>(); private int _savedVSyncCount; private int _savedTargetFrameRate; private bool _frameLimitUncapped; private bool _presetRevertSuppressed; private float _sweepStartTime; private float _sweepExpectedDuration; private float _sweepStartProgress; private float _sweepEndProgress; private bool _sweepSmoothActive; private static readonly Dictionary<Type, (bool upd, bool late, bool fixedU)> _methodCache = new Dictionary<Type, (bool, bool, bool)>(); internal static CostProbe? Instance { get; private set; } internal static bool Running { get; private set; } internal static void Toggle() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown if (Running) { Abort(); return; } if ((Object)(object)Instance == (Object)null) { GameObject val = new GameObject("REPOFidelity_CostProbe"); Object.DontDestroyOnLoad((Object)val); Instance = val.AddComponent<CostProbe>(); } ((MonoBehaviour)Instance).StartCoroutine(Instance.RunSafe()); } [IteratorStateMachine(typeof(<WaitForAutotuneIfActive>d__29))] private IEnumerator WaitForAutotuneIfActive() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <WaitForAutotuneIfActive>d__29(0); } internal static void Abort() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown if (Running) { if ((Object)(object)Instance != (Object)null) { ((MonoBehaviour)Instance).StopAllCoroutines(); Camera.onPreRender = (CameraCallback)Delegate.Remove((Delegate?)(object)Camera.onPreRender, (Delegate?)new CameraCallback(Instance.OnCamPre)); Camera.onPostRender = (CameraCallback)Delegate.Remove((Delegate?)(object)Camera.onPostRender, (Delegate?)new CameraCallback(Instance.OnCamPost)); Instance._camTimings.Clear(); Instance.RestoreFrameLimit(); Instance.RestorePresetRevertSuppression(); Settings.AllocationFixesEnabled = true; } Cursor.lockState = (CursorLockMode)1; Cursor.visible = false; Running = false; Status = ""; Plugin.Log.LogInfo((object)"Cost probe cancelled"); } } private void RestoreFrameLimit() { if (_frameLimitUncapped) { QualitySettings.vSyncCount = _savedVSyncCount; Application.targetFrameRate = _savedTargetFrameRate; _frameLimitUncapped = false; } } private void RestorePresetRevertSuppression() { if (_presetRevertSuppressed) { Settings.PopPresetRevertSuppression(); _presetRevertSuppressed = false; } } private static void AppendConfigFiles(StringBuilder report) { string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? ""; AppendFileSection(report, "autotune.json", Path.Combine(path, "autotune.json")); AppendFileSection(report, "settings.json", Path.Combine(path, "settings.json")); } private static void AppendFileSection(StringBuilder report, string label, string path) { report.AppendLine("== " + label + " =="); try { report.AppendLine(File.Exists(path) ? File.ReadAllText(path).TrimEnd() : "(missing)"); } catch (Exception ex) { report.AppendLine("(read failed: " + ex.Message + ")"); } report.AppendLine(); } private void Update() { if (!Running) { return; } if ((Object)(object)GameDirector.instance != (Object)null) { GameDirector.instance.SetDisableInput(1f); } if (_sweepSmoothActive && _sweepExpectedDuration > 0f) { float num = Mathf.Clamp01((Time.unscaledTime - _sweepStartTime) / _sweepExpectedDuration); float num2 = Mathf.Lerp(_sweepStartProgress, _sweepEndProgress, num); if (num2 > Progress) { Progress = num2; } } } [IteratorStateMachine(typeof(<RunSafe>d__36))] private IEnumerator RunSafe() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <RunSafe>d__36(0) { <>4__this = this }; } private void OnCamPre(Camera cam) { if (Running && !((Object)(object)cam == (Object)null)) { if (!_camTimings.TryGetValue(cam, out CamTiming value)) { value = new CamTiming(); _camTimings[cam] = value; } value.Sw.Restart(); } } private void OnCamPost(Camera cam) { if (Running && !((Object)(object)cam == (Object)null) && _camTimings.TryGetValue(cam, out CamTiming value)) { value.Sw.Stop(); value.TotalMs += value.Sw.Elapsed.TotalMilliseconds; value.Frames++; } } [IteratorStateMachine(typeof(<Run>d__39))] private IEnumerator Run() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <Run>d__39(0) { <>4__this = this }; } private static bool TrySystemClipboard(string text) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Invalid comparison between Unknown and I4 GUIUtility.systemCopyBuffer = text; if (GUIUtility.systemCopyBuffer == text) { return true; } if ((int)Application.platform == 13) { return TryLinuxClipboardFallback(text); } return false; } private static bool TryLinuxClipboardFallback(string text) { (string, string)[] array = new(string, string)[3] { ("wl-copy", ""), ("xclip", "-selection clipboard"), ("xsel", "--clipboard --input") }; for (int i = 0; i < array.Length; i++) { var (fileName, arguments) = array[i]; try { using Process process = Process.Start(new ProcessStartInfo(fileName, arguments) { RedirectStandardInput = true, UseShellExecute = false, CreateNoWindow = true }); if (process != null) { process.StandardInput.Write(text); process.StandardInput.Close(); if (process.WaitForExit(2000) && process.ExitCode == 0) { return true; } } } catch { } } return false; } private static void PatchIfPresent(Type type, string methodName, Harmony harmony, HarmonyMethod prefix, HarmonyMethod postfix, List<MethodInfo> patched) { MethodInfo method = type.GetMethod(methodName, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method == null) { return; } try { ScriptTiming.Register(method); harmony.Patch((MethodBase)method, prefix, postfix, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); patched.Add(method); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Probe: can't patch " + type.Name + "." + methodName + ": " + ex.Message)); } } private static void AppendMultiplayerSection(StringBuilder report) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) PlayerAvatar[] array = Object.FindObjectsOfType<PlayerAvatar>(); if (array.Length == 0) { return; } Camera main = Camera.main; Vector3 val = (((Object)(object)main != (Object)null) ? ((Component)main).transform.position : Vector3.zero); float resolvedEffectiveFogEnd = Settings.ResolvedEffectiveFogEnd; float num = ((resolvedEffectiveFogEnd > 0f) ? (resolvedEffectiveFogEnd * 1.1f) : float.PositiveInfinity); report.AppendLine($"== Multiplayer / per-player (fog cutoff {num:F0}m) =="); report.AppendLine($" PlayerAvatars: {array.Length}"); int num2 = 0; int num3 = 0; PlayerAvatar[] array2 = array; foreach (PlayerAvatar val2 in array2) { if ((Object)(object)val2 == (Object)null) { continue; } float num4 = (((Object)(object)main != (Object)null) ? Vector3.Distance(((Component)val2).transform.position, val) : 0f); bool flag = num4 > num; if (flag) { num2++; } int num5 = 0; Renderer[] componentsInChildren = ((Component)val2).GetComponentsInChildren<Renderer>(true); for (int j = 0; j < componentsInChildren.Length; j++) { if ((int)componentsInChildren[j].shadowCastingMode != 0) { num5++; } } num3 += num5; string text = (val2.isLocal ? "local" : "remote"); string text2 = (flag ? " past-fog" : ""); report.AppendLine(string.Format(" [{0,-6}] {1,-20} {2,5:F0}m {3,3} casters{4}", text, val2.playerName ?? "(unknown)", num4, num5, text2)); } report.AppendLine($" Total shadow-casting player renderers: {num3} ({num2} players past fog)"); FlashlightController[] array3 = Object.FindObjectsOfType<FlashlightController>(); if (array3.Length != 0) { List<(FlashlightController, float)> list = new List<(FlashlightController, float)>(); FlashlightController[] array4 = array3; foreach (FlashlightController val3 in array4) { if (!((Object)(object)val3.spotlight == (Object)null)) { float item = (((Object)(object)main != (Object)null) ? Vector3.Distance(((Component)val3.spotlight).transform.position, val) : 0f); list.Add((val3, item)); } } list.Sort(((FlashlightController fl, float dist) a, (FlashlightController fl, float dist) b) => a.dist.CompareTo(b.dist)); int num6 = 0; int num7 = 0; int num8 = 0; foreach (var item2 in list) { if (item2.Item2 > num) { num8++; } else if (num6 < 4) { num6++; } else { num7++; } } report.AppendLine($" Flashlights: {list.Count} total — {num6} within budget, {num7} culled over budget, {num8} past fog"); } int num9 = Object.FindObjectsOfType<PlayerAvatarEyelids>().Length; int num10 = Object.FindObjectsOfType<PlayerExpression>().Length; int num11 = Object.FindObjectsOfType<PlayerAvatarOverchargeVisuals>().Length; report.AppendLine($" Cosmetic components: Eyelids×{num9} Expression×{num10} Overcharge×{num11}"); report.AppendLine(" (components past fog cutoff skip their Update — see per-player distances above)"); report.AppendLine(); } private static double LookupMs(List<(string name, string cat, double ms)> list, string marker) { foreach (var (text, _, result) in list) { if (text == marker) { return result; } } return 0.0; } private static double LookupCount(List<(string name, string cat, double n)> list, string marker) { foreach (var (text, _, result) in list) { if (text == marker) { return result; } } return 0.0; } private static double LookupBytes(List<(string name, string cat, double bytes)> list, string marker) { foreach (var (text, _, result) in list) { if (text == marker) { return result; } } return 0.0; } private static string FmtBig(double n) { if (n >= 1000000.0) { return $"{n / 1000000.0:F2}M"; } if (n >= 1000.0) { return $"{n / 1000.0:F1}K"; } return $"{n:F0}"; } private static string FmtBytes(double b) { if (b >= 1048576.0) { return $"{b / 1048576.0:F2} MB"; } if (b >= 1024.0) { return $"{b / 1024.0:F1} KB"; } return $"{b:F0} B"; } private static string Pct(int n, int total) { if (total <= 0) { return "-"; } return $"{100.0 * (double)n / (double)total:F0}%"; } [IteratorStateMachine(typeof(<SampleFrames>d__52))] private IEnumerator SampleFrames(float seconds, Action<Sample> onDone) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <SampleFrames>d__52(0) { seconds = seconds, onDone = onDone }; } private static Sample ComputeSample(List<float> frames) { if (frames.Count == 0) { return default(Sample); } float num = 0f; for (int i = 0; i < frames.Count; i++) { num += frames[i]; } float num2 = num / (float)frames.Count * 1000f; frames.Sort(); Sample result = default(Sample); result.AvgFps = 1000f / num2; result.AvgMs = num2; result.P1Low = PercentileLow(frames, 0.01f); result.P01Low = PercentileLow(frames, 0.001f); result.FrameCount = frames.Count; return result; } private static float PercentileLow(List<float> sorted, float percentile) { int num = Mathf.Max(1, Mathf.CeilToInt((float)sorted.Count * percentile)); float num2 = 0f; for (int num3 = sorted.Count - 1; num3 >= sorted.Count - num; num3--) { num2 += sorted[num3]; } return 1f / (num2 / (float)num); } private static (bool upd, bool late, bool fixedU) GetMethodFlags(Type t) { if (_methodCache.TryGetValue(t, out (bool, bool, bool) value)) { return value; } bool flag = false; bool flag2 = false; bool flag3 = false; Type type = t; while (type != null && type != typeof(MonoBehaviour) && type != typeof(Behaviour) && type != typeof(object)) { if (!flag && type.GetMethod("Update", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null) != null) { flag = true; } if (!flag2 && type.GetMethod("LateUpdate", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null) != null) { flag2 = true; } if (!flag3 && type.GetMethod("FixedUpdate", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null) != null) { flag3 = true; } if (flag && flag2 && flag3) { break; } type = type.BaseType; } (bool, bool, bool) tuple = (flag, flag2, flag3); _methodCache[t] = tuple; return tuple; } private static SceneMetrics GatherSceneMetrics() { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Invalid comparison between Unknown and I4 //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Invalid comparison between Unknown and I4 //IL_0262: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Unknown result type (might be due to invalid IL or missing references) //IL_027c: Expected I4, but got Unknown //IL_031e: Unknown result type (might be due to invalid IL or missing references) //IL_0324: Invalid comparison between Unknown and I4 //IL_02d1: Unknown result type (might be due to invalid IL or missing references) //IL_02d6: Unknown result type (might be due to invalid IL or missing references) //IL_02d8: Unknown result type (might be due to invalid IL or missing references) //IL_02ef: Expected I4, but got Unknown //IL_0328: Unknown result type (might be due to invalid IL or missing references) //IL_032e: Invalid comparison between Unknown and I4 //IL_0348: Unknown result type (might be due to invalid IL or missing references) //IL_034e: Invalid comparison between Unknown and I4 //IL_03b4: Unknown result type (might be due to invalid IL or missing references) //IL_03b9: Unknown result type (might be due to invalid IL or missing references) //IL_03f3: Unknown result type (might be due to invalid IL or missing references) //IL_03f8: Unknown result type (might be due to invalid IL or missing references) SceneMetrics sceneMetrics = default(SceneMetrics); sceneMetrics.LightDetail = new List<LightInfo>(); sceneMetrics.TopBehaviourTypes = new List<(string, int)>(); SceneMetrics result = sceneMetrics; MeshRenderer[] array = Object.FindObjectsOfType<MeshRenderer>(); foreach (MeshRenderer obj in array) { result.Renderers.Total++; bool isVisible = ((Renderer)obj).isVisible; bool num = (int)((Renderer)obj).shadowCastingMode > 0; if (isVisible) { result.Renderers.Visible++; } if (num) { result.Renderers.ShadowCasting++; } if (num && !isVisible) { result.Renderers.ShadowCastingHidden++; } if ((Object)(object)((Component)obj).GetComponentInParent<LODGroup>() == (Object)null) { result.Renderers.NoLODGroup++; } MeshFilter component = ((Component)obj).GetComponent<MeshFilter>(); if ((Object)(object)component != (Object)null && (Object)(object)component.sharedMesh != (Object)null) { Mesh sharedMesh = component.sharedMesh; long num2 = 0L; for (int j = 0; j < sharedMesh.subMeshCount; j++) { num2 += sharedMesh.GetIndexCount(j); } result.TotalSceneTris += num2 / 3; } } result.SkinnedRenderers = Object.FindObjectsOfType<SkinnedMeshRenderer>().Length; ParticleSystem[] array2 = Object.FindObjectsOfType<ParticleSystem>(); foreach (ParticleSystem val in array2) { result.TotalParticles++; if (val.isPlaying && val.particleCount > 0) { result.ActiveParticles++; result.ActiveParticleCount += val.particleCount; } } result.ReflectionProbes = Object.FindObjectsOfType<ReflectionProbe>().Length; result.TrailRenderers = Object.FindObjectsOfType<TrailRenderer>().Length; result.LineRenderers = Object.FindObjectsOfType<LineRenderer>().Length; AudioSource[] array3 = Object.FindObjectsOfType<AudioSource>(); foreach (AudioSource obj2 in array3) { result.AudioSources++; if (obj2.isPlaying) { result.AudioSourcesPlaying++; } } Light[] array4 = Object.FindObjectsOfType<Light>(); foreach (Light val2 in array4) { if (!((Behaviour)val2).enabled || !((Component)val2).gameObject.activeInHierarchy) { continue; } result.Lights.Active++; bool flag = (int)val2.shadows > 0; if (flag) { result.Lights.Shadowing++; } LightType type = val2.type; switch ((int)type) { case 1: if (flag) { result.Lights.Directional++; } break; case 2: if (flag) { result.Lights.PointShadow++; } break; case 0: if (flag) { result.Lights.SpotShadow++; } break; } int num3 = val2.shadowCustomResolution; if (num3 <= 0) { LightShadowResolution shadowResolution = val2.shadowResolution; num3 = (int)shadowResolution switch { 3 => 4096, 2 => 2048, 1 => 1024, 0 => 512, _ => 1024, }; } float num4 = (((int)val2.type == 2) ? 6f : (((int)val2.type == 1) ? ((float)QualitySettings.shadowCascades) : 1f)); float num5 = (((int)val2.type == 1) ? 2500f : (val2.range * val2.range)); float costScore = (flag ? (num4 * ((float)num3 / 1024f) * ((float)num3 / 1024f) * (num5 / 100f)) : 0f); List<LightInfo> lightDetail = result.LightDetail; LightInfo item = new LightInfo { Name = ((Object)val2).name }; type = val2.type; item.Type = ((object)(LightType)(ref type)).ToString(); item.Range = val2.range; item.Resolution = num3; object shadowMode; if (!flag) { shadowMode = "Off"; } else { LightShadows shadows = val2.shadows; shadowMode = ((object)(LightShadows)(ref shadows)).ToString(); } item.ShadowMode = (string)shadowMode; item.CostScore = costScore; lightDetail.Add(item); } Dictionary<Type, int> dictionary = new Dictionary<Type, int>(); MonoBehaviour[] array5 = Object.FindObjectsOfType<MonoBehaviour>(); foreach (MonoBehaviour val3 in array5) { if (!((Behaviour)val3).isActiveAndEnabled) { continue; } Type type2 = ((object)val3).GetType(); var (flag2, flag3, flag4) = GetMethodFlags(type2); if (flag2) { result.UpdatingBehaviours++; } if (flag3) { result.LateUpdatingBehaviours++; } if (flag4) { result.FixedUpdatingBehaviours++; } if (flag2) { if (!dictionary.TryGetValue(type2, out var value)) { value = 0; } dictionary[type2] = value + 1; } } foreach (KeyValuePair<Type, int> item2 in dictionary.OrderByDescending((KeyValuePair<Type, int> k) => k.Value)) { result.TopBehaviourTypes.Add((item2.Key.Name, item2.Value)); } return result; } private static void BuildOpportunities(StringBuilder r, List<(string name, string cat, double ms)> timeStats, List<(string name, string cat, double n)> countStats, List<(string name, string cat, double bytes)> memStats, SceneMetrics scene, List<(string label, double totalMs, double perCallUs, int calls)> scriptTimings) { List<(double, string)> list = new List<(double, string)>(); double num = LookupMs(timeStats, "Physics.Simulate") + LookupMs(timeStats, "FixedUpdate.PhysicsFixedUpdate"); double num2 = LookupMs(timeStats, "Animators.Update"); double num3 = LookupMs(timeStats, "Particles.Update"); double num4 = LookupMs(timeStats, "Camera.ImageEffects"); double num5 = LookupBytes(memStats, "GC Allocated In Frame"); int num6 = 0; foreach (var (text, num7, num8, num9) in scriptTimings) { if (num7 < 0.05 || num6 >= 3) { break; } list.Add((num7 * 200.0, $"Top script cost: {text} — {num7:F3} ms/frame ({num9} calls at {num8:F1} µs each). " + "Rate-limit, cache, or Harmony-prefix-skip when state hasn't changed.")); num6++; } if (scene.Renderers.ShadowCastingHidden > 0 && scene.Renderers.Total > 0) { double num10 = (double)scene.Renderers.ShadowCastingHidden / (double)scene.Renderers.Total; list.Add((num10 * 300.0, $"{scene.Renderers.ShadowCastingHidden} off-screen shadow casters ({num10 * 100.0:F0}% of scene renderers). " + "Prefabs that don't need to cast shadows should set shadowCastingMode=Off — cost isn't directly visible in retail markers but cubemap passes are real.")); } if (scene.Renderers.NoLODGroup > 300) { list.Add(((double)scene.Renderers.NoLODGroup / 4.0, $"{scene.Renderers.NoLODGroup} renderers have no LODGroup ({Pct(scene.Renderers.NoLODGroup, scene.Renderers.Total)}). Distant small objects render at full detail — most impactful when main-cam render cost is high.")); } if (scene.Lights.PointShadow > 8) { list.Add((scene.Lights.PointShadow * 10, $"{scene.Lights.PointShadow} point lights with shadows × 6 faces = {scene.Lights.PointShadow * 6} cubemap passes/frame. Shadow budget + resolution tiering are the highest-leverage knobs.")); } if (num > 0.3) { list.Add((num * 70.0, $"Physics costs {num:F2} ms/frame. Trim layer collision matrix, raise fixed timestep interval, switch non-dynamic rigidbodies to kinematic.")); } if (num2 > 0.2) { list.Add((num2 * 60.0, $"Animators.Update costs {num2:F2} ms/frame across {scene.SkinnedRenderers} skinned renderers. Set Animator.cullingMode=BasedOnRenderers / CullCompletely on distant characters.")); } if (num3 > 0.2) { list.Add((num3 * 60.0, $"Particles.Update costs {num3:F2} ms/frame across {scene.ActiveParticles} active systems ({FmtBig(scene.ActiveParticleCount)} particles alive). Distance-cull emit rate.")); } if (num4 > 0.3) { list.Add((num4 * 50.0, $"Camera.ImageEffects (post-processing stack) costs {num4:F2} ms/frame. Audit which OnRenderImage passes are doing work.")); } if (num5 > 1024.0) { list.Add((num5 / 64.0, "GC allocates " + FmtBytes(num5) + "/frame — hunt per-frame boxing, closures, and ToArray/ToList calls.")); } double num11 = LookupCount(countStats, "Draw Calls Count"); if (num11 > 1500.0) { list.Add((num11 / 40.0, $"{num11:F0} draw calls / frame. Static batching or GPU instancing on recurring prefabs would cut CPU cost.")); } if (scene.ReflectionProbes > 1) { list.Add((scene.ReflectionProbes * 3, $"{scene.ReflectionProbes} reflection probes — each realtime probe updating adds CPU+GPU cost. Mark as baked or type=Custom when possible.")); } if (list.Count == 0) { r.AppendLine(" (no high-cost categories detected — baseline looks clean)"); return; } list.Sort(((double priority, string text) a, (double priority, string text) b) => b.priority.CompareTo(a.priority)); int num12 = 1; foreach (var item2 in list) { string item = item2.Item2; r.AppendLine($" {num12++}. {item}"); } } } internal static class DLSSDownloader { private const string DllName = "nvngx_dlss.dll"; private const int MinDllSize = 10000000; internal static string GetDllPath() { return Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? "", "nvngx_dlss.dll"); } internal static bool EnsureAvailable() { string dllPath = GetDllPath(); int num; if (File.Exists(dllPath)) { num = ((new FileInfo(dllPath).Length >= 10000000) ? 1 : 0); if (num != 0) { Plugin.Log.LogDebug((object)$"DLSS DLL: {dllPath} ({new FileInfo(dllPath).Length / 1024 / 1024}MB)"); return (byte)num != 0; } } else { num = 0; } Plugin.Log.LogWarning((object)"nvngx_dlss.dll missing or invalid — DLSS/DLAA disabled. Reinstall the mod."); return (byte)num != 0; } } internal static class FrameTimeMeter { internal class Meter { public readonly string Name; public readonly string ShortName; private readonly float[] _samples; private int _index; private int _count; private float _sum; public float LastUs; public float AverageUs { get { if (_count <= 0) { return 0f; } return _sum / (float)_count; } } public float AverageMs => AverageUs / 1000f; public Meter(string name, string shortName, int windowSize = 120) { Name = name; ShortName = shortName; _samples = new float[windowSize]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Record(float microseconds) { LastUs = microseconds; _sum -= _samples[_index]; _samples[_index] = microseconds; _sum += microseconds; _index = (_index + 1) % _samples.Length; if (_count < _samples.Length) { _count++; } } } private static readonly Stopwatch _sw = Stopwatch.StartNew(); internal static readonly Meter EnemyDirector = new Meter("EnemyDirector Throttle", "EnemyDir"); internal static readonly Meter RoomVolumeCheck = new Meter("RoomVolume NonAlloc", "RoomVol"); internal static readonly Meter SemiFuncCache = new Meter("SemiFunc Cache", "SemiFunc"); internal static readonly Meter PhysGrabObjectFix = new Meter("PhysGrabObject Fix", "PhysGrab"); internal static readonly Meter LightManagerBatch = new Meter("LightManager Batch", "LightMgr"); internal static readonly Meter SceneApply = new Meter("SceneOptimizer Apply", "SceneApl"); internal static readonly Meter[] All = new Meter[6] { EnemyDirector, RoomVolumeCheck, SemiFuncCache, PhysGrabObjectFix, LightManagerBatch, SceneApply }; internal static bool Active { get { if (!Settings.DebugOverlay) { return OptimizerBenchmark.Running; } return true; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static long Begin() { if (!Active) { return 0L; } return _sw.ElapsedTicks; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void End(Meter meter, long startTicks) { if (startTicks != 0L) { float microseconds = (float)((double)(_sw.ElapsedTicks - startTicks) / 10.0); meter.Record(microseconds); } } } internal enum GpuVendor { Nvidia, Amd, Intel, Apple, Unknown } internal enum GpuTier { High, Mid, Low } internal static class GPUDetector { private static readonly Regex ArcDiscretePattern = new Regex("\\b[AB]\\d{3}\\b", RegexOptions.Compiled); public static string GpuName { get; private set; } = "Unknown"; public static GpuVendor Vendor { get; private set; } = GpuVendor.Unknown; public static GpuTier Tier { get; private set; } = GpuTier.Low; public static bool DlssAvailable { get; private set; } public static int VramMb { get; private set; } public static bool IsD3D11 { get; private set; } public static bool IsIntegratedGpu { get; private set; } public static void Detect() { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Invalid comparison between Unknown and I4 //IL_00c4: Unknown result type (might be due to invalid IL or missing references) GpuName = SystemInfo.graphicsDeviceName ?? "Unknown"; VramMb = SystemInfo.graphicsMemorySize; Vendor = DetectVendor(GpuName, SystemInfo.graphicsDeviceVendor); Tier = DetectTier(VramMb); IsD3D11 = (int)SystemInfo.graphicsDeviceType == 2; IsIntegratedGpu = DetectIntegrated(GpuName, VramMb, Vendor); DlssAvailable = Vendor == GpuVendor.Nvidia && IsD3D11 && !IsIntegratedGpu && GpuName.ToUpperInvariant().Contains("RTX"); Plugin.Log.LogInfo((object)($"GPU: {GpuName} | Vendor: {Vendor} | VRAM: {VramMb}MB | " + $"API: {SystemInfo.graphicsDeviceType} | iGPU: {IsIntegratedGpu} | Tier: {Tier}")); } public static bool IsUpscalerSupported(UpscaleMode mode) { switch (mode) { case UpscaleMode.Auto: return true; case UpscaleMode.Off: return true; case UpscaleMode.DLAA: case UpscaleMode.DLSS: return DlssAvailable; case UpscaleMode.FSR_Temporal: return true; default: return false; } } public static string[] GetAvailableUpscalerNames() { List<string> list = new List<string>(); foreach (UpscaleMode value in Enum.GetValues(typeof(UpscaleMode))) { if (value != UpscaleMode.FSR && value != UpscaleMode.FSR4 && value != UpscaleMode.DLAA && IsUpscalerSupported(value)) { string item = ((value == UpscaleMode.FSR_Temporal) ? "FSR" : value.ToString()); list.Add(item); } } return list.ToArray(); } private static GpuVendor DetectVendor(string name, string vendorString) { string text = (name + " " + vendorString).ToUpperInvariant(); if (text.Contains("NVIDIA")) { return GpuVendor.Nvidia; } if (text.Contains("AMD") || text.Contains("ATI")) { return GpuVendor.Amd; } if (text.Contains("INTEL")) { return GpuVendor.Intel; } if (text.Contains("APPLE")) { return GpuVendor.Apple; } return GpuVendor.Unknown; } private static GpuTier DetectTier(int vramMb) { if (vramMb >= 8000) { return GpuTier.High; } if (vramMb >= 6000) { return GpuTier.Mid; } return GpuTier.Low; } private static bool DetectIntegrated(string name, int vramMb, GpuVendor vendor) { string text = name.ToUpperInvariant(); if (vendor == GpuVendor.Intel && (text.Contains("UHD") || text.Contains("IRIS") || text.Contains("HD GRAPHICS") || (text.Contains("ARC") && !ArcDiscretePattern.IsMatch(text)))) { return true; } if (vendor == GpuVendor.Amd && ((text.Contains("VEGA") && !text.Contains("VEGA 56") && !text.Contains("VEGA 64")) || (text.Contains("RADEON GRAPHICS") && !text.Contains("RX")))) { return true; } if (vramMb > 0 && vramMb < 2048) { return true; } return false; } } internal static class LightDiagnostics { private struct LightEntry { public string Name; public LightType Type; public LightShadows ShadowMode; public int ShadowRes; public ShadowResolution GlobalShadowRes; public int EffectiveRes; public float Range; public float Intensity; public Color Color; public bool Enabled; public bool CastsShadows; public LightRenderMode RenderMode; public bool HasLightAnimator; public bool HasItemLight; public bool HasFlashlight; public bool HasExplosion; public int ShadowFaces; public float ShadowCost; public int RenderersInRange; public int EstDrawCalls; } private static readonly string OutputPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? "", "light_diagnostics.txt"); internal static void Run() { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Invalid comparison between Unknown and I4 //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Expected I4, but got Unknown //IL_04ff: Unknown result type (might be due to invalid IL or missing references) //IL_037c: Unknown result type (might be due to invalid IL or missing references) //IL_0381: Unknown result type (might be due to invalid IL or missing references) //IL_0383: Unknown result type (might be due to invalid IL or missing references) //IL_0396: Expected I4, but got Unknown //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_0830: Unknown result type (might be due to invalid IL or missing references) //IL_0849: Unknown result type (might be due to invalid IL or missing references) //IL_088d: Unknown result type (might be due to invalid IL or missing references) //IL_08a1: Unknown result type (might be due to invalid IL or missing references) //IL_08b5: Unknown result type (might be due to invalid IL or missing references) //IL_08f8: Unknown result type (might be due to invalid IL or missing references) Light[] array = Object.FindObjectsOfType<Light>(); if (array.Length == 0) { Plugin.Log.LogInfo((object)"Light diagnostics: no lights found"); return; } List<LightEntry> list = new List<LightEntry>(); Light[] array2 = array; foreach (Light val in array2) { LightEntry lightEntry = default(LightEntry); lightEntry.Name = GetPath(((Component)val).transform); lightEntry.Type = val.type; lightEntry.ShadowMode = val.shadows; lightEntry.ShadowRes = val.shadowCustomResolution; lightEntry.GlobalShadowRes = QualitySettings.shadowResolution; lightEntry.Range = val.range; lightEntry.Intensity = val.intensity; lightEntry.Color = val.color; lightEntry.Enabled = ((Behaviour)val).enabled && ((Component)val).gameObject.activeInHierarchy; lightEntry.CastsShadows = (int)val.shadows > 0; lightEntry.RenderMode = val.renderMode; LightEntry item = lightEntry; GameObject gameObject = ((Component)val).gameObject; item.HasLightAnimator = (Object)(object)gameObject.GetComponent<LightAnimator>() != (Object)null; item.HasItemLight = (Object)(object)gameObject.GetComponent<ItemLight>() != (Object)null; item.HasFlashlight = (Object)(object)gameObject.GetComponent<FlashlightController>() != (Object)null; item.HasExplosion = (Object)(object)gameObject.GetComponentInParent<ParticlePrefabExplosion>() != (Object)null; LightType type = val.type; item.ShadowFaces = (int)type switch { 2 => 6, 0 => 1, 1 => QualitySettings.shadowCascades, _ => 0, }; int num = (item.EffectiveRes = ((item.ShadowRes > 0) ? item.ShadowRes : GlobalResValue())); if (item.CastsShadows && item.Enabled) { item.ShadowCost = (float)item.ShadowFaces * ((float)num * (float)num / 1048576f); } else { item.ShadowCost = 0f; } if (item.CastsShadows && item.Enabled) { int num2 = 0; Collider[] array3 = Physics.OverlapSphere(((Component)val).transform.position, val.range); HashSet<Renderer> hashSet = new HashSet<Renderer>(); Collider[] array4 = array3; for (int j = 0; j < array4.Length; j++) { Renderer component = ((Component)array4[j]).GetComponent<Renderer>(); if ((Object)(object)component != (Object)null && (int)component.shadowCastingMode != 0 && hashSet.Add(component)) { num2++; } } item.RenderersInRange = num2; item.EstDrawCalls = num2 * item.ShadowFaces; } list.Add(item); } list.Sort((LightEntry a, LightEntry b) => b.ShadowCost.CompareTo(a.ShadowCost)); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("══════════════════════════════════════════════════════════════"); stringBuilder.AppendLine($" LIGHT DIAGNOSTICS — {DateTime.Now:yyyy-MM-dd HH:mm:ss}"); stringBuilder.AppendLine("══════════════════════════════════════════════════════════════"); stringBuilder.AppendLine(); stringBuilder.AppendLine($" Total lights: {array.Length}"); int num3 = 0; int num4 = 0; int num5 = 0; int num6 = 0; int num7 = 0; int num8 = 0; int num9 = 0; int num10 = 0; int num11 = 0; int num12 = 0; int num13 = 0; float num14 = 0f; int num15 = 0; foreach (LightEntry item2 in list) { if (item2.Enabled) { num3++; } if (item2.CastsShadows && item2.Enabled) { num4++; } LightType type = item2.Type; switch ((int)type) { case 2: num5++; break; case 0: num6++; break; case 1: num7++; break; default: num8++; break; } if (item2.HasLightAnimator) { num9++; } else if (item2.HasItemLight) { num10++; } else if (item2.HasFlashlight) { num11++; } else if (item2.HasExplosion) { num12++; } else { num13++; } num14 += item2.ShadowCost; num15 += item2.EstDrawCalls; } stringBuilder.AppendLine($" Active: {num3}"); stringBuilder.AppendLine($" Casting shadows: {num4}"); stringBuilder.AppendLine(); stringBuilder.AppendLine($" By type: Point={num5} Spot={num6} Directional={num7} Area={num8}"); stringBuilder.AppendLine($" By component: Plain={num13} LightAnimator={num9} ItemLight={num10} Flashlight={num11} Explosion={num12}"); stringBuilder.AppendLine(); stringBuilder.AppendLine($" Global shadow res: {QualitySettings.shadowResolution}"); stringBuilder.AppendLine($" Shadow distance: {QualitySettings.shadowDistance:F0}m"); stringBuilder.AppendLine($" Shadow cascades: {QualitySettings.shadowCascades}"); stringBuilder.AppendLine(); stringBuilder.AppendLine($" Total shadow cost: {num14:F1} (relative units: faces x res²/1M)"); stringBuilder.AppendLine($" Est shadow draw calls:{num15}"); stringBuilder.AppendLine(); stringBuilder.AppendLine("──────────────────────────────────────────────────────────────"); stringBuilder.AppendLine(" COST BY CATEGORY"); stringBuilder.AppendLine("──────────────────────────────────────────────────────────────"); float num16 = 0f; float num17 = 0f; float num18 = 0f; float num19 = 0f; float num20 = 0f; int num21 = 0; int num22 = 0; int num23 = 0; int num24 = 0; int num25 = 0; foreach (LightEntry item3 in list) { if (item3.HasLightAnimator) { num17 += item3.ShadowCost; num22 += item3.EstDrawCalls; } else if (item3.HasItemLight) { num18 += item3.ShadowCost; num23 += item3.EstDrawCalls; } else if (item3.HasFlashlight) { num19 += item3.ShadowCost; num24 += item3.EstDrawCalls; } else if (item3.HasExplosion) { num20 += item3.ShadowCost; num25 += item3.EstDrawCalls; } else { num16 += item3.ShadowCost; num21 += item3.EstDrawCalls; } } stringBuilder.AppendLine($" Plain Light: cost={num16,8:F1} est draw calls={num21,6}"); stringBuilder.AppendLine($" LightAnimator: cost={num17,8:F1} est dra