diff --git a/.gitignore b/.gitignore index a34df41..b5ff8a8 100644 --- a/.gitignore +++ b/.gitignore @@ -481,3 +481,6 @@ $RECYCLE.BIN/ # Vim temporary swap files *.swp + +# Cache folders used in disk caching +Cache/* \ No newline at end of file diff --git a/1.6/Assemblies/PerformanceFish.dll b/1.6/Assemblies/PerformanceFish.dll index 8c95aa2..518d514 100644 Binary files a/1.6/Assemblies/PerformanceFish.dll and b/1.6/Assemblies/PerformanceFish.dll differ diff --git a/1.6/Assemblies/PerformanceFish.pdb b/1.6/Assemblies/PerformanceFish.pdb index d4c43d0..8e47800 100644 Binary files a/1.6/Assemblies/PerformanceFish.pdb and b/1.6/Assemblies/PerformanceFish.pdb differ diff --git a/Source/PerformanceFish/Defs/RecipeDefCaching.cs b/Source/PerformanceFish/Defs/RecipeDefCaching.cs index 868047e..7720c4a 100644 --- a/Source/PerformanceFish/Defs/RecipeDefCaching.cs +++ b/Source/PerformanceFish/Defs/RecipeDefCaching.cs @@ -56,8 +56,8 @@ public sealed class RecipeDefCaching : ClassWithFishPatches { private int _defDatabaseVersion = -2; private int _defRecipeUsersVersion = -2; - private RecipeDef _def; - public List RecipeUsers; + private RecipeDef? _def; + public List RecipeUsers = []; public void Update(RecipeDef instance, IEnumerable result) { @@ -81,7 +81,7 @@ public sealed class RecipeDefCaching : ClassWithFishPatches [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _defDatabaseVersion != DefDatabase.defsList._version - || _defRecipeUsersVersion != (_def.recipeUsers?._version ?? 0); + || _defRecipeUsersVersion != (_def?.recipeUsers?._version ?? 0); } } } @@ -141,4 +141,4 @@ public sealed class RecipeDefCaching : ClassWithFishPatches => throw new NotImplementedException(); } }*/ -} \ No newline at end of file +} diff --git a/Source/PerformanceFish/GenLocalDateCaching.cs b/Source/PerformanceFish/GenLocalDateCaching.cs index a8ed3bc..29fb66e 100644 --- a/Source/PerformanceFish/GenLocalDateCaching.cs +++ b/Source/PerformanceFish/GenLocalDateCaching.cs @@ -19,45 +19,17 @@ public sealed class GenLocalDateCaching : ClassWithFishPatches + "optimization, but yields accurate results instead of a placeholder value to avoid issues"; public override Delegate TargetMethodGroup { get; } = (Func)GenLocalDate.DayTick; - public override int TranspilerMethodPriority => Priority.First; + public override int PrefixMethodPriority => Priority.First; - public static CodeInstructions Transpiler(CodeInstructions codes, MethodBase method, ILGenerator generator) - //=> Reflection.GetCodeInstructions(Replacement); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Prefix(Thing thing, ref int __result) { - var labelToStartOfMethod = generator.DefineLabel(); - codes.First().labels.Add(labelToStartOfMethod); + if (thing.TryGetMap() is not { } map) + return true; - yield return FishTranspiler.Argument(method, "thing"); - yield return FishTranspiler.Field(typeof(Thing), nameof(Thing.mapIndexOrState)); - yield return FishTranspiler.Constant(0); - yield return FishTranspiler.IfLessThan(labelToStartOfMethod); - - yield return FishTranspiler.Field(typeof(Current), nameof(Current.gameInt)); - yield return FishTranspiler.Field(typeof(Game), nameof(Game.maps)); - yield return FishTranspiler.Argument(method, "thing"); - yield return FishTranspiler.Field(typeof(Thing), nameof(Thing.mapIndexOrState)); - yield return FishTranspiler.Call(typeof(List), "get_Item"); - yield return FishTranspiler.Call>(GenLocalDate.DayTick); - yield return FishTranspiler.Return; - - foreach (var code in codes) - yield return code; + __result = GenLocalDate.DayTick(map); + return false; } - - //[MethodImpl(MethodImplOptions.AggressiveInlining)] - //public static int Replacement(Thing thing) - // => thing.mapIndexOrState < 0 ? GenDate.DayTick(GenTicks.TicksAbs, GenLocalDate.LongitudeForDate(thing)) - // : GenLocalDate.DayTick(Current.gameInt.maps[thing.mapIndexOrState]); - - //[MethodImpl(MethodImplOptions.AggressiveInlining)] - //public static bool Prefix(Thing thing, ref int __result) - //{ - // if (thing.mapIndexOrState < 0) - // return true; - - // __result = GenLocalDate.DayTick(Current.gameInt.maps[thing.mapIndexOrState]); - // return false; - //} } public sealed class DayTickByMap_Patch : FishPatch @@ -100,4 +72,4 @@ public sealed class GenLocalDateCaching : ClassWithFishPatches public static Map? SavedMap; public static int SavedTick = -2; public static int DayTick = -2; -} \ No newline at end of file +} diff --git a/Source/PerformanceFish/GeneTrackerOptimization.cs b/Source/PerformanceFish/GeneTrackerOptimization.cs index 87d88d8..88ca881 100644 --- a/Source/PerformanceFish/GeneTrackerOptimization.cs +++ b/Source/PerformanceFish/GeneTrackerOptimization.cs @@ -98,10 +98,59 @@ public sealed class GeneTrackerOptimization : ClassWithFishPrepatches private static bool IsSkippableType(Type type) => _skippableTypes.GetOrAdd(type); private static bool ComputeIsSkippableType(Type type) - => type == typeof(Gene) - || type.GetMethod(nameof(Gene.Tick), global::System.Reflection.BindingFlags.Instance - | global::System.Reflection.BindingFlags.Public - | global::System.Reflection.BindingFlags.NonPublic - | global::System.Reflection.BindingFlags.DeclaredOnly, null, Type.EmptyTypes, null) == null; + { + if (type == typeof(Gene)) + return true; + + var tickMethod = type.GetMethod(nameof(Gene.Tick), global::System.Reflection.BindingFlags.Instance + | global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic, null, Type.EmptyTypes, null); + var tickIntervalMethod = type.GetMethod(nameof(Gene.TickInterval), + global::System.Reflection.BindingFlags.Instance + | global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic, null, [typeof(int)], null); + + return tickMethod?.DeclaringType == typeof(Gene) + && tickIntervalMethod?.DeclaringType == typeof(Gene); + } + } + + public sealed class GeneTrackerTickIntervalPatch : FishPrepatch + { + // i am iffy about this + public override string? Description { get; } + = "Every pawn has a gene tracker, which is responsible for ticking each of their genes. Normally it ticks " + + "all of them equally, including those don't change or affect anything through ticking, like skin colors " + + "or basic stat modifiers. This patch improves the gene tracker to determine genes that need ticking in " + + "advance, cache the list of them, and only tick those, skipping all the others."; + + public override MethodBase TargetMethodBase { get; } + = AccessTools.Method(typeof(Pawn_GeneTracker), nameof(Pawn_GeneTracker.GeneTrackerTickInterval)); + + public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module) + => ilProcessor.ReplaceBodyWith(GeneTrackerTickInterval); + + public static void GeneTrackerTickInterval(Pawn_GeneTracker instance, int delta) + { + if (!ModLister.BiotechInstalled) + return; + + var genesToTick = instance.GenesToTick(); + if (GeneTrackerTickPatch.Dirty(instance, genesToTick)) + GeneTrackerTickPatch.Update(instance, genesToTick); + + for (var i = genesToTick.Count - 1; i >= 0; i--) + { + if (genesToTick[i].Active) + genesToTick[i].TickInterval(delta); + } + + if (instance.pawn.IsSpawned() + && instance.Xenotype != XenotypeDefOf.Baseliner + && instance.pawn.IsHashIntervalTick(300, delta)) + { + LessonAutoActivator.TeachOpportunity(ConceptDefOf.GenesAndXenotypes, OpportunityType.Important); + } + } } } diff --git a/Source/PerformanceFish/Hauling/LwmCompCacheValue.cs b/Source/PerformanceFish/Hauling/LwmCompCacheValue.cs index a0ef995..9565317 100644 --- a/Source/PerformanceFish/Hauling/LwmCompCacheValue.cs +++ b/Source/PerformanceFish/Hauling/LwmCompCacheValue.cs @@ -3,7 +3,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -global using LwmCompCache = PerformanceFish.Cache.ByReference; +global using LwmCompCache = PerformanceFish.Cache.ByReference; namespace PerformanceFish.Hauling; @@ -11,9 +11,9 @@ public record struct LwmCompCacheValue { public CompProperties? CompProperties; - public static CompProperties? TryGetLwmProps(Thing slotGroupParent) + public static CompProperties? TryGetLwmProps(ThingDef thingDef) => ModCompatibility.Types.LWM.CompProperties != null - ? slotGroupParent.def.comps?.Find(static props + ? thingDef.comps?.Find(static props => props.GetType() == ModCompatibility.Types.LWM.CompProperties) : null; @@ -35,4 +35,4 @@ public record struct LwmCompCacheValue => ModCompatibility.Types.LWM.CompProperties is { } propsType ? AccessTools.FieldRefAccess(AccessTools.Field(propsType, name)) : null; -} \ No newline at end of file +} diff --git a/Source/PerformanceFish/Hauling/RimfactoryExtensionCacheValue.cs b/Source/PerformanceFish/Hauling/RimfactoryExtensionCacheValue.cs index fce3331..2c3e5e3 100644 --- a/Source/PerformanceFish/Hauling/RimfactoryExtensionCacheValue.cs +++ b/Source/PerformanceFish/Hauling/RimfactoryExtensionCacheValue.cs @@ -4,7 +4,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. global using RimfactoryExtensionCache - = PerformanceFish.Cache.ByReference; + = PerformanceFish.Cache.ByReference; using RimfactoryTypes = PerformanceFish.ModCompatibility.Types.Rimfactory; namespace PerformanceFish.Hauling; @@ -13,17 +13,17 @@ public record struct RimfactoryExtensionCacheValue { public DefModExtension? ModExtension; - public static DefModExtension? TryGetExtension(Thing slotGroupParent) + public static DefModExtension? TryGetExtension(ThingDef thingDef) => RimfactoryTypes.ModExtension != null - ? slotGroupParent.def.modExtensions?.Find(_extensionPredicate) + ? thingDef.modExtensions?.Find(_extensionPredicate) : null; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool HasExtension(Thing slotGroupParent) - => RimfactoryTypes.ModExtension != null && TestExtension(slotGroupParent); + public static bool HasExtension(Thing thing) + => RimfactoryTypes.ModExtension != null && TestExtension(thing.def); [MethodImpl(MethodImplOptions.NoInlining)] - private static bool TestExtension(Thing slotGroupParent) => slotGroupParent.TryGetRimfactoryExtension() != null; + private static bool TestExtension(ThingDef thingDef) => TryGetExtension(thingDef) != null; static RimfactoryExtensionCacheValue() => EnsureInitialized(); @@ -44,4 +44,4 @@ public record struct RimfactoryExtensionCacheValue => RimfactoryTypes.ModExtension is { } extensionType ? AccessTools.FieldRefAccess(AccessTools.Field(extensionType, name)) : null; -} \ No newline at end of file +} diff --git a/Source/PerformanceFish/Hauling/StorageExtensions.cs b/Source/PerformanceFish/Hauling/StorageExtensions.cs index e176ac0..5a85c04 100644 --- a/Source/PerformanceFish/Hauling/StorageExtensions.cs +++ b/Source/PerformanceFish/Hauling/StorageExtensions.cs @@ -24,11 +24,11 @@ public static class StorageExtensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static CompProperties? TryGetLwmCompProperties(this Thing thing) - => LwmCompCache.GetOrAdd(thing).CompProperties; + => LwmCompCache.GetOrAdd(thing.def).CompProperties; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static DefModExtension? TryGetRimfactoryExtension(this Thing thing) - => RimfactoryExtensionCache.GetOrAdd(thing).ModExtension; + => RimfactoryExtensionCache.GetOrAdd(thing.def).ModExtension; public static int GetTotalSlots(this SlotGroup slotGroup) => slotGroup.GetTotalSlots(slotGroup.CellsList.Count); @@ -57,4 +57,4 @@ public static class StorageExtensions LwmCompCacheValue.EnsureInitialized(); RimfactoryExtensionCacheValue.EnsureInitialized(); } -} \ No newline at end of file +} diff --git a/Source/PerformanceFish/JobSystem/JobGiver_HaulOptimization.cs b/Source/PerformanceFish/JobSystem/JobGiver_HaulOptimization.cs index 6cf45ab..84c6a93 100644 --- a/Source/PerformanceFish/JobSystem/JobGiver_HaulOptimization.cs +++ b/Source/PerformanceFish/JobSystem/JobGiver_HaulOptimization.cs @@ -3,6 +3,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. +using System.Linq; using PerformanceFish.ModCompatibility; namespace PerformanceFish.JobSystem; @@ -41,13 +42,21 @@ public sealed class JobGiver_HaulOptimization : ClassWithFishPatches } } - public static List SortedThingsPotentiallyNeedingHauling(List things, Pawn pawn) + public static ICollection SortedThingsPotentiallyNeedingHauling(ICollection things, Pawn pawn) { ((ThingPositionComparer)_comparer.Target).rootCell = pawn.Position; - things.Sort(_comparer); - return things; + + if (things is List list) + { + list.Sort(_comparer); + return list; + } + + var sortedThings = things.ToList(); + sortedThings.Sort(_comparer); + return sortedThings; } private static Comparison _comparer = new ThingPositionComparer().Compare; } -} \ No newline at end of file +} diff --git a/Source/PerformanceFish/Listers/Things.cs b/Source/PerformanceFish/Listers/Things.cs index 968a7d7..000fec3 100644 --- a/Source/PerformanceFish/Listers/Things.cs +++ b/Source/PerformanceFish/Listers/Things.cs @@ -12,6 +12,9 @@ namespace PerformanceFish.Listers; public sealed class ThingsPrepatches : ClassWithFishPrepatches { + private static readonly AccessTools.FieldRef> _haulSources + = AccessTools.FieldRefAccess>("haulSources"); + public sealed class AddPatch : FishPrepatch { public override string? Description { get; } @@ -31,6 +34,8 @@ public sealed class ThingsPrepatches : ClassWithFishPrepatches lock (__instance.LockObject()) { AddToDefList(__instance, t); + if (t is IHaulSource haulSource) + _haulSources(__instance).Add(haulSource); var allGroups = ThingListGroupHelper.AllGroups; foreach (var thingRequestGroup in allGroups) @@ -74,6 +79,8 @@ public sealed class ThingsPrepatches : ClassWithFishPrepatches lock (__instance.LockObject()) { RemoveFromDefList(__instance, t); + if (t is IHaulSource haulSource) + _haulSources(__instance).Remove(haulSource); var allGroups = ThingListGroupHelper.AllGroups; for (var i = 0; i < allGroups.Length; i++) @@ -268,6 +275,7 @@ public sealed class ThingsPrepatches : ClassWithFishPrepatches __instance.IndexMapByDef().Clear(); __instance.IndexMapByGroup().Clear(); + _haulSources(__instance).Clear(); ClearTypeLists(__instance); } diff --git a/Source/PerformanceFish/LogPatches.cs b/Source/PerformanceFish/LogPatches.cs index d2ac7f7..e49238d 100644 --- a/Source/PerformanceFish/LogPatches.cs +++ b/Source/PerformanceFish/LogPatches.cs @@ -59,15 +59,16 @@ public sealed class LogPatches : ClassWithFishPrepatches public static void InsertEarlyMessages() { var unhandledMessages = Log.UnhandledMessages; - if (unhandledMessages is null) - Debug.LogError("Message queue is null"); - + Log.Ready = true; - + Verse.Log.messageQueue.Enqueue(new(LogMessageType.Message, PERFORMANCE_FISH_WELCOME_MESSAGE, PERFORMANCE_FISH_WELCOME_STACKTRACE)); - while (unhandledMessages!.TryDequeue(out var message)) + if (unhandledMessages is null) + return; + + while (unhandledMessages.TryDequeue(out var message)) { try { diff --git a/Source/PerformanceFish/Patching/FishPatch.cs b/Source/PerformanceFish/Patching/FishPatch.cs index 4bd0b6a..84d3ae5 100644 --- a/Source/PerformanceFish/Patching/FishPatch.cs +++ b/Source/PerformanceFish/Patching/FishPatch.cs @@ -193,8 +193,6 @@ public abstract class FishPatch : SingletonFactory, IExposable, IHasD Patched = true; foreach (var method in TargetMethodInfos) { - DebugLog.Message($"Performance Fish is applying {GetType().Name} on {method.FullDescription()}"); - try { if (IsBenchmarking) @@ -232,6 +230,7 @@ public abstract class FishPatch : SingletonFactory, IExposable, IHasD finalizer: TryMakeHarmonyMethod(FinalizerMethodInfo, FinalizerMethodPriority))); } } + } catch (Exception e) { @@ -291,6 +290,9 @@ public abstract class FishPatch : SingletonFactory, IExposable, IHasD private MethodInfo? TryGetMethod(string nameCaseInsensitive, Predicate? predicate = null) => GetType().TryGetMethod(nameCaseInsensitive, predicate); + private static string GetTargetMethodName(MethodBase method) + => $"{method.DeclaringType?.FullName ?? ""}.{method.Name}"; + private static int TryGetPriority(MethodInfo? info) => info?.TryGetAttribute()?.info.priority ?? Priority.Normal; @@ -378,4 +380,4 @@ internal static class Benchmarking Method = method; } } -} \ No newline at end of file +} diff --git a/Source/PerformanceFish/Planet/WorldObjectsOptimization.cs b/Source/PerformanceFish/Planet/WorldObjectsOptimization.cs index 0e7c1e4..eefb747 100644 --- a/Source/PerformanceFish/Planet/WorldObjectsOptimization.cs +++ b/Source/PerformanceFish/Planet/WorldObjectsOptimization.cs @@ -17,6 +17,7 @@ public sealed class WorldObjectsOptimization : ClassWithFishPrepatches { private static FishTable _skippableCompTypes = null!; private static FishTable _skippableWorldObjectTypes = null!; + private static readonly List _tmpThingHolders = []; public override string? Description { get; } = "The world objects holder is responsible for ticking every world object. This includes settlements, " @@ -73,16 +74,44 @@ public sealed class WorldObjectsOptimization : ClassWithFishPrepatches private static bool CanSkipWorldObjectTick(WorldObject worldObject) { -#if V1_6 - if (worldObject is IThingHolder) + return IsSkippableWorldObjectType(worldObject.GetType()) && !HasTickableContents(worldObject); + } + + private static bool HasTickableContents(WorldObject worldObject) + { + if (worldObject is not IThingHolder holder) return false; -#endif - return IsSkippableWorldObjectType(worldObject.GetType()); + + lock (_tmpThingHolders) + { + _tmpThingHolders.Clear(); + _tmpThingHolders.Add(holder); + holder.GetChildHolders(_tmpThingHolders); + + for (var i = 0; i < _tmpThingHolders.Count; i++) + { + var thingHolder = _tmpThingHolders[i]; + if (thingHolder is IThingHolderTickable tickable && !tickable.ShouldTickContents) + continue; + + var directlyHeldThings = thingHolder.GetDirectlyHeldThings(); + if (directlyHeldThings?.Owner is not null and not Map and not Caravan) + { + _tmpThingHolders.Clear(); + return true; + } + } + + _tmpThingHolders.Clear(); + return false; + } } private static bool CanSkipCompTick(WorldObject worldObject) { var comps = worldObject.comps; + if (comps.Count == 0) + return true; for (var i = comps.Count; i-- > 0;) { @@ -92,8 +121,6 @@ public sealed class WorldObjectsOptimization : ClassWithFishPrepatches if (!IsSkippableCompType(comp.GetType())) return false; - - comp.CompTick(); } return true; @@ -106,10 +133,7 @@ public sealed class WorldObjectsOptimization : ClassWithFishPrepatches private static Type?[] _whitelistedTickingCompTypes = [ - typeof(WorldObjectComp), - typeof(FormCaravanComp), - typeof(TimedDetectionRaids), - typeof(EnterCooldownComp) + typeof(WorldObjectComp) ], _whitelistedWorldObjectTypes = [ diff --git a/Source/PerformanceFish/Prepatching/FishPrepatch.cs b/Source/PerformanceFish/Prepatching/FishPrepatch.cs index 8c87d7a..edef402 100644 --- a/Source/PerformanceFish/Prepatching/FishPrepatch.cs +++ b/Source/PerformanceFish/Prepatching/FishPrepatch.cs @@ -105,7 +105,7 @@ public abstract class FishPrepatch : FishPrepatchBase Log.Error($"Exception while applying postfix for {GetType().FullName}\n{e}\n{new StackTrace(true)}"); } } - + targetMethodBody.SimplifyMacros(); targetMethodBody.OptimizeMacros(); } diff --git a/Source/PerformanceFish/Prepatching/FishPrepatchExtensions.cs b/Source/PerformanceFish/Prepatching/FishPrepatchExtensions.cs index 50a2373..a1bb787 100644 --- a/Source/PerformanceFish/Prepatching/FishPrepatchExtensions.cs +++ b/Source/PerformanceFish/Prepatching/FishPrepatchExtensions.cs @@ -13,8 +13,9 @@ public static class FishPrepatchExtensions { var prepatches = ToSortedList(allPrepatchClasses, true); - foreach (var prepatch in prepatches) + for (var i = 0; i < prepatches.Count; i++) { + var prepatch = prepatches[i]; if (!prepatch.Enabled) continue; @@ -40,8 +41,9 @@ public static class FishPrepatchExtensions { var prepatches = ToSortedList(allPrepatchClasses, false); - foreach (var prepatch in prepatches) + for (var i = 0; i < prepatches.Count; i++) { + var prepatch = prepatches[i]; if (!prepatch.IsActive) continue; @@ -70,4 +72,5 @@ public static class FishPrepatchExtensions prepatches.Sort(FishPrepatchBase.PriorityComparer); return prepatches; } -} \ No newline at end of file + +} diff --git a/Source/PerformanceFish/Rendering/TextureAtlasPatches.cs b/Source/PerformanceFish/Rendering/TextureAtlasPatches.cs new file mode 100644 index 0000000..1c9d9e4 --- /dev/null +++ b/Source/PerformanceFish/Rendering/TextureAtlasPatches.cs @@ -0,0 +1,50 @@ +// Copyright (c) 2023 bradson +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +namespace PerformanceFish.Rendering; + +public sealed class TextureAtlasPatches : ClassWithFishPatches +{ + public sealed class StaticTextureAtlas_ApplyTextureCompression : FishPatch + { + public override string? Description { get; } + = "Routes texture atlas compression through the GPU path when compute shaders are available, " + + "regardless of the VRAM threshold check in UnityData.ComputeShadersSupported. Prevents native " + + "crashes in debug Mono builds."; + + public override MethodBase TargetMethodInfo + => AccessTools.DeclaredMethod(typeof(StaticTextureAtlas), "ApplyTextureCompression"); + + public static bool Prefix(StaticTextureAtlas __instance, bool noGpuCompressionSupport) + { + if (!noGpuCompressionSupport || !SystemInfo.supportsComputeShaders) + return true; + + if (__instance.ColorTexture != null) + { + var name = __instance.ColorTexture.name; + var compressed = StaticTextureAtlas.FastCompressDXT(__instance.ColorTexture, deleteOriginal: true); + compressed.name = name; + _colorTextureField.SetValue(__instance, compressed); + } + + if (__instance.MaskTexture != null) + { + var name = __instance.MaskTexture.name; + var compressed = StaticTextureAtlas.FastCompressDXT(__instance.MaskTexture, deleteOriginal: true); + compressed.name = name; + _maskTextureField.SetValue(__instance, compressed); + } + + return false; + } + + private static readonly FieldInfo _colorTextureField + = AccessTools.DeclaredField(typeof(StaticTextureAtlas), "colorTexture"); + + private static readonly FieldInfo _maskTextureField + = AccessTools.DeclaredField(typeof(StaticTextureAtlas), "maskTexture"); + } +} diff --git a/Source/PerformanceFish/Rendering/TextureLoadingPatches.cs b/Source/PerformanceFish/Rendering/TextureLoadingPatches.cs index a261112..0a8c90c 100644 --- a/Source/PerformanceFish/Rendering/TextureLoadingPatches.cs +++ b/Source/PerformanceFish/Rendering/TextureLoadingPatches.cs @@ -93,9 +93,9 @@ public sealed class TextureLoadingPatches : ClassWithFishPrepatches var data = file.ReadAllBytes(); texture2D = new(2, 2, TextureFormat.Alpha8, true); texture2D.LoadImage(data); - FixMipMapsIfNeeded(texture2D, data, file); + FixMipMapsIfNeeded(ref texture2D, data, file); } - + if (!isDds && Prefs.TextureCompression) texture2D.Compress(true); @@ -120,11 +120,11 @@ public sealed class TextureLoadingPatches : ClassWithFishPrepatches var texture2D = new Texture2D(2, 2, TextureFormat.Alpha8, true); texture2D.LoadImage(data); - FixMipMapsIfNeeded(texture2D, data, file); + FixMipMapsIfNeeded(ref texture2D, data, file); return texture2D; } - public static void FixMipMapsIfNeeded(Texture2D texture2D, byte[] data, VirtualFile file) + public static void FixMipMapsIfNeeded(ref Texture2D texture2D, byte[] data, VirtualFile file) { if (((texture2D.width & 3) == 0) & ((texture2D.height & 3) == 0)) return; diff --git a/Source/PerformanceFish/System/ReflectionCaching.cs b/Source/PerformanceFish/System/ReflectionCaching.cs index e92a7ec..b793037 100644 --- a/Source/PerformanceFish/System/ReflectionCaching.cs +++ b/Source/PerformanceFish/System/ReflectionCaching.cs @@ -10,6 +10,7 @@ using System.Security; using System.Threading.Tasks; using PerformanceFish.Cache; using PerformanceFish.ModCompatibility; +#if false using ConstructorInfoCache = PerformanceFish.Cache.ByReference, @@ -56,11 +57,13 @@ using ActivatorCache using CustomAttributeCache = PerformanceFish.Cache.ByReferenceClassic; +#endif namespace PerformanceFish.System; public sealed class ReflectionCaching : ClassWithFishPatches { +#if false static ReflectionCaching() // necessary to prevent recursion between patches and function pointer creation { try @@ -86,6 +89,7 @@ public sealed class ReflectionCaching : ClassWithFishPatches ex}"); } } +#endif public record struct StateAndFlags { @@ -93,6 +97,7 @@ public sealed class ReflectionCaching : ClassWithFishPatches public BindingFlags Flags; } +#if false public sealed class TypePatches { public sealed class GetField_Patch : FishPatch @@ -513,9 +518,17 @@ public sealed class ReflectionCaching : ClassWithFishPatches var key = new CustomAttributeCache(obj, attributeType.TypeHandle, inherit); __state = new() { Key = key }; - if (TryGetCachedAttributes(ref key, out __result)) + var cacheLookup = TryGetCachedAttributes(ref key, out __result); + + if (cacheLookup == CustomAttributeCacheLookup.Hit) return false; + if (cacheLookup == CustomAttributeCacheLookup.InProgress) + { + __result = null; + return true; + } + MarkInProgress(ref key); __state.ShouldStore = true; __result = null; @@ -523,14 +536,17 @@ public sealed class ReflectionCaching : ClassWithFishPatches } [MethodImpl(MethodImplOptions.NoInlining)] - private static bool TryGetCachedAttributes(ref CustomAttributeCache key, out object[]? result) + private static CustomAttributeCacheLookup TryGetCachedAttributes(ref CustomAttributeCache key, + out object[]? result) { lock (_lock) { - if (TryUseCache(CustomAttributeCache.Get, ref key, out result)) - return true; + var localLookup = TryUseCache(CustomAttributeCache.Get, ref key, out result); + if (localLookup != CustomAttributeCacheLookup.Miss) + return localLookup; - if (TryUseCache(CustomAttributeCache.GetDirectly, ref key, out var centralResult)) + var centralLookup = TryUseCache(CustomAttributeCache.GetDirectly, ref key, out var centralResult); + if (centralLookup == CustomAttributeCacheLookup.Hit) { result = centralResult; CustomAttributeCache.Get[key] = new() @@ -538,12 +554,15 @@ public sealed class ReflectionCaching : ClassWithFishPatches Attributes = centralResult, Status = CustomAttributeCacheStatus.Completed }; - return true; + return CustomAttributeCacheLookup.Hit; } + + if (centralLookup == CustomAttributeCacheLookup.InProgress) + return CustomAttributeCacheLookup.InProgress; } result = null; - return false; + return CustomAttributeCacheLookup.Miss; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -566,27 +585,28 @@ public sealed class ReflectionCaching : ClassWithFishPatches } [MethodImpl(MethodImplOptions.NoInlining)] - private static bool TryUseCache(Dictionary cache, + private static CustomAttributeCacheLookup TryUseCache( + Dictionary cache, ref CustomAttributeCache key, out object[]? result) { if (!cache.TryGetValue(key, out var value)) { result = null; - return false; + return CustomAttributeCacheLookup.Miss; } switch (value.Status) { case CustomAttributeCacheStatus.Completed: result = value.Attributes; - return true; + return CustomAttributeCacheLookup.Hit; case CustomAttributeCacheStatus.InProgress: LogInProgress(ref key); result = null; - return false; + return CustomAttributeCacheLookup.InProgress; default: result = null; - return false; + return CustomAttributeCacheLookup.Miss; } } @@ -666,6 +686,13 @@ public sealed class ReflectionCaching : ClassWithFishPatches InProgress, Completed } + + private enum CustomAttributeCacheLookup : byte + { + Miss, + Hit, + InProgress + } } public record struct RecordArray @@ -854,9 +881,11 @@ public sealed class ReflectionCaching : ClassWithFishPatches // Log.Message(types.ToStringSafeEnumerable()); //} - public override MethodBase TargetMethodInfo { get; } - = AccessTools.DeclaredMethod(AccessTools.TypeByName("System.Reflection.RuntimeFieldInfo"), - nameof(FieldInfo.GetValue), [typeof(object)])!; + public override MethodBase? TargetMethodInfo { get; } + = AccessTools.DeclaredMethod( + AccessTools.TypeByName("System.Reflection.RuntimeFieldInfo") + ?? AccessTools.TypeByName("System.Reflection.MonoField"), + nameof(FieldInfo.GetValue), [typeof(object)]); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool Prefix(FieldInfo __instance, object? obj, ref object? __result) @@ -1285,4 +1314,5 @@ public sealed class ReflectionCaching : ClassWithFishPatches // populated with dynamic methods by MethodBasePatches.MakeInvokeDelegate, MakeGetterDelegate and // MakeSetterDelegate } +#endif } diff --git a/Source/PerformanceFish/Utility/MiscExtensions.cs b/Source/PerformanceFish/Utility/MiscExtensions.cs index 04b894e..b2d6e13 100644 --- a/Source/PerformanceFish/Utility/MiscExtensions.cs +++ b/Source/PerformanceFish/Utility/MiscExtensions.cs @@ -101,7 +101,10 @@ public static class MiscExtensions public static Map? TryGetMapHeld(this Thing thing) { - var maps = Current.Game.Maps; + var maps = Find.Maps; + if (maps == null) + return thing.ParentHolder is { } rootHolder ? ThingOwnerUtility.GetRootMap(rootHolder) : null; + var mapIndex = (uint)thing.mapIndexOrState; return mapIndex < (uint)maps.Count @@ -114,7 +117,10 @@ public static class MiscExtensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Map? TryGetMap(this Thing thing) { - var maps = Current.Game.Maps; + var maps = Find.Maps; + if (maps == null) + return null; + var mapIndex = (uint)thing.mapIndexOrState; return mapIndex < (uint)maps.Count ? maps[(int)mapIndex] : null; @@ -127,7 +133,18 @@ public static class MiscExtensions public static Map GetMap(this Region region) => Current.Game.Maps[region.mapIndex]; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsSpawned(this Thing thing) => (uint)thing.mapIndexOrState < (uint)Current.Game.Maps.Count; + public static bool IsSpawned(this Thing thing) + { + var maps = Find.Maps; + if (thing.mapIndexOrState < 0 || maps == null) + return false; + + if (thing.mapIndexOrState < maps.Count) + return true; + + Verse.Log.ErrorOnce($"Thing {thing.ThingID} is associated with invalid map index {thing.mapIndexOrState}", 64664487); + return false; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetLoadID(this Bill bill) => bill.loadID;