Lots of new optimizations utilizing prepatcher

To start, this mod now requires Prepatcher and Fishery.
New optimizations include: gene ticking improvements, world object improvements, hediff optimizations, world pawn gc optimizations, better mothballing, dds support, ContentFinder caching, additions and changes to reflection caching, as well as various other additions. Multiplayer support has been improved too, but should be considered highly experimental. The WorkGiver_DoBill bug is fixed in this build. Multiple patches aiming to improve RimWorld's load time have been added too.
This commit is contained in:
bbradson
2023-06-03 20:24:38 +02:00
parent cb0a407425
commit ca9321add8
125 changed files with 11272 additions and 3681 deletions
+73 -39
View File
@@ -1,34 +1,41 @@
// Copyright (c) 2022 bradson
// 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/.
using RecipeUsersCache = PerformanceFish.Cache.ByIndex<Verse.RecipeDef, PerformanceFish.RecipeDefCaching.AllRecipeUsers_Patch.RecipeUsersCacheValue>;
using RecipeUsersCache
= PerformanceFish.Cache.ByInt<Verse.RecipeDef,
PerformanceFish.RecipeDefCaching.AllRecipeUsers_Patch.RecipeUsersCacheValue>;
namespace PerformanceFish;
public class RecipeDefCaching : ClassWithFishPatches
{
public override bool RequiresLoadedGameForPatching => true;
public class AllRecipeUsers_Patch : FirstPriorityFishPatch
{
public override string Description => "Caches results of the AllRecipeUsers method. Becomes impactful with lots of unfinished things around, as those check everything for potentially accepting them";
public override MethodBase TargetMethodInfo => AccessTools.PropertyGetter(typeof(RecipeDef), nameof(RecipeDef.AllRecipeUsers));
public override string Description { get; }
= "Caches results of the AllRecipeUsers method. Becomes impactful with lots of unfinished things around, "
+ "as those check everything for potentially accepting them";
public override MethodBase TargetMethodInfo { get; }
= AccessTools.PropertyGetter(typeof(RecipeDef), nameof(RecipeDef.AllRecipeUsers));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Prefix(RecipeDef __instance, ref IEnumerable<ThingDef> __result, out bool __state)
public static bool Prefix(RecipeDef? __instance, ref IEnumerable<ThingDef> __result, out bool __state)
{
if (__instance is null || __instance.GetType() != typeof(RecipeDef))
if (__instance is null)
{
__state = false;
return true;
}
ref var cache = ref RecipeUsersCache.Get.GetReference(__instance);
if (cache.ShouldRefreshNow)
ref var cache = ref RecipeUsersCache.GetOrAddReference(__instance.shortHash);
if (cache.Dirty)
return __state = true;
__result = cache.recipeUsers;
__result = cache.RecipeUsers;
return __state = false;
}
@@ -38,43 +45,58 @@ public class RecipeDefCaching : ClassWithFishPatches
if (!__state)
return;
var cache = RecipeUsersCache.Get[__instance];
if (cache.recipeUsers is List<ThingDef> list)
{
list.Clear();
list.AddRange(__result);
}
else
{
cache.recipeUsers = new List<ThingDef>(__result);
}
cache.ShouldRefreshNow = false;
RecipeUsersCache.Get[__instance] = cache;
UpdateCache(__instance, __result);
}
public struct RecipeUsersCacheValue : Cache.IIsRefreshable<RecipeDef, RecipeUsersCacheValue>
{
public IEnumerable<ThingDef> recipeUsers;
private int _nextRefreshFrame;
[MethodImpl(MethodImplOptions.NoInlining)]
private static void UpdateCache(RecipeDef __instance, IEnumerable<ThingDef> __result)
=> RecipeUsersCache.GetExistingReference(__instance).Update(__instance, __result);
public bool ShouldRefreshNow
public record struct RecipeUsersCacheValue
{
private int _defDatabaseVersion;
private int _defRecipeUsersVersion;
private RecipeDef _def;
public List<ThingDef> RecipeUsers;
public void Update(RecipeDef instance, IEnumerable<ThingDef> result)
{
get => Time.frameCount > _nextRefreshFrame;
set => _nextRefreshFrame = value ? 0 : Time.frameCount + 4096 + Math.Abs(Rand.Int % 2048);
if (RecipeUsers is { } list)
{
list.Clear();
list.AddRange(result);
}
else
{
RecipeUsers = new(result);
}
_def = instance;
_defDatabaseVersion = DefDatabase<ThingDef>.defsList._version;
_defRecipeUsersVersion = instance.recipeUsers?._version ?? 0;
}
public bool Dirty
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
=> _defDatabaseVersion != DefDatabase<ThingDef>.defsList._version
|| _defRecipeUsersVersion != (_def.recipeUsers?._version ?? 0);
}
public RecipeUsersCacheValue SetNewValue(RecipeDef key) => throw new NotImplementedException();
}
}
/*public class PotentiallyMissingIngredients_Patch : FirstPriorityFishPatch
{
public override Expression<Action> TargetMethod => () => new RecipeDef().PotentiallyMissingIngredients(null, null);
public override Expression<Action> TargetMethod { get; }
= () => new RecipeDef().PotentiallyMissingIngredients(null, null);
public static bool Prefix(RecipeDef __instance, Pawn billDoer, Map map, ref IEnumerable<ThingDef> __result)
{
if (!Cache<RecipeDef, Pawn, Map, MissingIngredientsCache>.TryGetValue(__instance, billDoer, map, out var cache))
if (!Cache.ByReference<RecipeDef, Pawn, Map, MissingIngredientsCache>.TryGetValue(__instance, billDoer, map,
out var cache))
return true;
if (__instance.ingredients.Count != 0)
{
Log.Warning("RecipeDef ingredients mismatched its cache in Performance Fish, forcing a refresh");
@@ -84,16 +106,26 @@ public class RecipeDefCaching : ClassWithFishPatches
__result = cache.ingredientThingDefs;
return false;
}
public static void Postfix(RecipeDef __instance, Pawn billDoer, Map map, IEnumerable<ThingDef> __result, bool __runOriginal)
public static void Postfix(RecipeDef __instance, Pawn billDoer, Map map, IEnumerable<ThingDef> __result,
bool __runOriginal)
{
if (!__runOriginal)
return;
Cache<RecipeDef, Pawn, Map, MissingIngredientsCache>.Get[new(__instance, billDoer, map)]
= new() { ingredientThingDefs = __result.ToArray(), firstIngredientsItemCount = __instance.ingredients.FirstOrDefault()?.count ?? 0, ShouldRefreshNow = false };
Cache.ByReference<RecipeDef, Pawn, Map, MissingIngredientsCache>.Get[new(__instance, billDoer, map)]
= new()
{
ingredientThingDefs = __result.ToArray(),
firstIngredientsItemCount = __instance.ingredients.FirstOrDefault()?.count ?? 0,
ShouldRefreshNow = false
};
}
public static MissingIngredientsCache GetCache(RecipeDef def, Pawn billDoer, Map map) => Cache<RecipeDef, Pawn, Map, MissingIngredientsCache>.GetValue(def, billDoer, map);
public struct MissingIngredientsCache : ICanRefresh<Cache<RecipeDef, Pawn, Map, MissingIngredientsCache>, MissingIngredientsCache>
public static MissingIngredientsCache GetCache(RecipeDef def, Pawn billDoer, Map map)
=> Cache.ByReference<RecipeDef, Pawn, Map, MissingIngredientsCache>.GetValue(def, billDoer, map);
public record struct MissingIngredientsCache
{
public ThingDef[] ingredientThingDefs;
private int _nextRefreshFrame;
@@ -104,7 +136,9 @@ public class RecipeDefCaching : ClassWithFishPatches
get => Time.frameCount > _nextRefreshFrame;
set => _nextRefreshFrame = value ? 0 : Time.frameCount + 2048 + Math.Abs(Rand.Int % 1024);
}
public MissingIngredientsCache SetNewValue(Cache<RecipeDef, Pawn, Map, MissingIngredientsCache> key) => throw new NotImplementedException();
public MissingIngredientsCache SetNewValue(Cache<RecipeDef, Pawn, Map, MissingIngredientsCache> key)
=> throw new NotImplementedException();
}
}*/
}