fix crashes, temp disable reflection caching, possibly reduce optimization in some areas, likely break 1.5
This commit is contained in:
@@ -481,3 +481,6 @@ $RECYCLE.BIN/
|
||||
|
||||
# Vim temporary swap files
|
||||
*.swp
|
||||
|
||||
# Cache folders used in disk caching
|
||||
Cache/*
|
||||
Binary file not shown.
Binary file not shown.
@@ -56,8 +56,8 @@ public sealed class RecipeDefCaching : ClassWithFishPatches
|
||||
{
|
||||
private int _defDatabaseVersion = -2;
|
||||
private int _defRecipeUsersVersion = -2;
|
||||
private RecipeDef _def;
|
||||
public List<ThingDef> RecipeUsers;
|
||||
private RecipeDef? _def;
|
||||
public List<ThingDef> RecipeUsers = [];
|
||||
|
||||
public void Update(RecipeDef instance, IEnumerable<ThingDef> result)
|
||||
{
|
||||
@@ -81,7 +81,7 @@ public sealed class RecipeDefCaching : ClassWithFishPatches
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
=> _defDatabaseVersion != DefDatabase<ThingDef>.defsList._version
|
||||
|| _defRecipeUsersVersion != (_def.recipeUsers?._version ?? 0);
|
||||
|| _defRecipeUsersVersion != (_def?.recipeUsers?._version ?? 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Thing, int>)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<Map>), "get_Item");
|
||||
yield return FishTranspiler.Call<Func<Map, int>>(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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Verse.Thing, PerformanceFish.Hauling.LwmCompCacheValue>;
|
||||
global using LwmCompCache = PerformanceFish.Cache.ByReference<Verse.ThingDef, PerformanceFish.Hauling.LwmCompCacheValue>;
|
||||
|
||||
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;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
global using RimfactoryExtensionCache
|
||||
= PerformanceFish.Cache.ByReference<Verse.Thing, PerformanceFish.Hauling.RimfactoryExtensionCacheValue>;
|
||||
= PerformanceFish.Cache.ByReference<Verse.ThingDef, PerformanceFish.Hauling.RimfactoryExtensionCacheValue>;
|
||||
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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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,11 +42,19 @@ public sealed class JobGiver_HaulOptimization : ClassWithFishPatches
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Thing> SortedThingsPotentiallyNeedingHauling(List<Thing> things, Pawn pawn)
|
||||
public static ICollection<Thing> SortedThingsPotentiallyNeedingHauling(ICollection<Thing> things, Pawn pawn)
|
||||
{
|
||||
((ThingPositionComparer)_comparer.Target).rootCell = pawn.Position;
|
||||
things.Sort(_comparer);
|
||||
return things;
|
||||
|
||||
if (things is List<Thing> list)
|
||||
{
|
||||
list.Sort(_comparer);
|
||||
return list;
|
||||
}
|
||||
|
||||
var sortedThings = things.ToList();
|
||||
sortedThings.Sort(_comparer);
|
||||
return sortedThings;
|
||||
}
|
||||
|
||||
private static Comparison<Thing> _comparer = new ThingPositionComparer().Compare;
|
||||
|
||||
@@ -12,6 +12,9 @@ namespace PerformanceFish.Listers;
|
||||
|
||||
public sealed class ThingsPrepatches : ClassWithFishPrepatches
|
||||
{
|
||||
private static readonly AccessTools.FieldRef<ListerThings, List<IHaulSource>> _haulSources
|
||||
= AccessTools.FieldRefAccess<ListerThings, List<IHaulSource>>("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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -193,8 +193,6 @@ public abstract class FishPatch : SingletonFactory<FishPatch>, 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<FishPatch>, IExposable, IHasD
|
||||
finalizer: TryMakeHarmonyMethod(FinalizerMethodInfo, FinalizerMethodPriority)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -291,6 +290,9 @@ public abstract class FishPatch : SingletonFactory<FishPatch>, IExposable, IHasD
|
||||
private MethodInfo? TryGetMethod(string nameCaseInsensitive, Predicate<MethodInfo>? predicate = null)
|
||||
=> GetType().TryGetMethod(nameCaseInsensitive, predicate);
|
||||
|
||||
private static string GetTargetMethodName(MethodBase method)
|
||||
=> $"{method.DeclaringType?.FullName ?? "<no-type>"}.{method.Name}";
|
||||
|
||||
private static int TryGetPriority(MethodInfo? info)
|
||||
=> info?.TryGetAttribute<HarmonyPriority>()?.info.priority ?? Priority.Normal;
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ public sealed class WorldObjectsOptimization : ClassWithFishPrepatches
|
||||
{
|
||||
private static FishTable<Type, bool> _skippableCompTypes = null!;
|
||||
private static FishTable<Type, bool> _skippableWorldObjectTypes = null!;
|
||||
private static readonly List<IThingHolder> _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 =
|
||||
[
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -93,7 +93,7 @@ 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)
|
||||
@@ -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;
|
||||
|
||||
@@ -10,6 +10,7 @@ using System.Security;
|
||||
using System.Threading.Tasks;
|
||||
using PerformanceFish.Cache;
|
||||
using PerformanceFish.ModCompatibility;
|
||||
#if false
|
||||
using ConstructorInfoCache
|
||||
= PerformanceFish.Cache.ByReference<System.RuntimeTypeHandle, System.Reflection.BindingFlags,
|
||||
PerformanceFish.System.ReflectionCaching.RecordArray<System.Type>,
|
||||
@@ -56,11 +57,13 @@ using ActivatorCache
|
||||
using CustomAttributeCache
|
||||
= PerformanceFish.Cache.ByReferenceClassic<System.Reflection.ICustomAttributeProvider, System.RuntimeTypeHandle, bool,
|
||||
PerformanceFish.System.ReflectionCaching.MonoCustomAttrs.CustomAttributeCacheValue>;
|
||||
#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<CustomAttributeCache, CustomAttributeCacheValue> cache,
|
||||
private static CustomAttributeCacheLookup TryUseCache(
|
||||
Dictionary<CustomAttributeCache, CustomAttributeCacheValue> 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<T>
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user