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
+135 -121
View File
@@ -1,25 +1,110 @@
// 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 ThingRequestCache = PerformanceFish.Cache.ByReferenceRefreshable<int, byte, System.RuntimeTypeHandle, PerformanceFish.ThingRequestInfo>;
using PerformanceFish.Prepatching;
/*using ThingRequestCache
= PerformanceFish.Cache.ByReference<int, byte, System.RuntimeTypeHandle,
PerformanceFish.ThingRequestInfo>;*/
namespace PerformanceFish;
public class ThingOwnerUtilityCaching : ClassWithFishPatches
{
public class GetAllThingsRecursively_Patch : FishPatch
{
public override string Description => "Caching of information about mostly unspawned pawns and things. Relatively large performance impact";
public override Delegate TargetMethodGroup => ThingOwnerUtility.GetAllThingsRecursively<Thing>;
public static bool Prefix(Map map, ThingRequest request, IList outThings, bool allowUnreal, Predicate<IThingHolder> passCheck, bool alsoGetSpawnedThings, out bool __state)
public class ThingOwnerUtilityPreCaching : ClassWithFishPrepatches
{
public class GetAllThingsRecursively_Patch : FishPrepatch
{
public override string Description { get; }
= "Caching of information about mostly unspawned pawns and things. Relatively large performance impact";
public override MethodBase TargetMethodBase { get; }
= methodof(ThingOwnerUtility.GetAllThingsRecursively<Thing>);
public static bool Prefix<T>(Map map, ThingRequest request, List<T>? outThings, bool allowUnreal,
Predicate<IThingHolder>? passCheck, bool alsoGetSpawnedThings, out bool __state)
where T : Thing
{
if (request.singleDef != null || request.group == 0 || !allowUnreal || passCheck != null || alsoGetSpawnedThings || outThings == null)
if (request.singleDef != null
|| request.group == 0
|| !allowUnreal
|| passCheck != null
|| alsoGetSpawnedThings
|| outThings == null)
{
__state = false;
return true;
}
var listsByGroup = map.listerThings.listsByGroup[(int)request.group];
if (listsByGroup is null)
{
__state = false;
return true;
}
var key = new Cache.ByInt<Map, ThingRequestGroup, ThingRequestInfo<T>>(map.uniqueID, (byte)request.group);
ref var cache = ref Cache.ByInt<Map, ThingRequestGroup, ThingRequestInfo<T>>.Get.GetOrAddReference(ref key);
if (cache.Dirty
|| cache.ListsByGroupCount != listsByGroup.Count
|| cache.ThingHolderCount != ThingHolderCount(map))
{
return __state = true;
}
outThings.ReplaceContentsWith(cache.Things!);
return __state = false;
}
public static void Postfix<T>(Map map, ThingRequest request, List<T> outThings, bool __state)
where T : Thing
{
if (!__state)
return;
ref var cache = ref Cache.ByInt<Map, ThingRequestGroup, ThingRequestInfo<T>>.Get.GetReference(new(map.uniqueID,
(byte)request.group));
if (cache.Things is null)
cache.Things = outThings.Copy();
else
cache.Things.ReplaceContentsWith(outThings);
cache.ListsByGroupCount = map.listerThings.listsByGroup[(int)request.group]?.Count ?? -1;
cache.ThingHolderCount = ThingHolderCount(map);
cache.SetDirty(false, map.uniqueID + (int)request.group + cache.ListsByGroupCount + cache.ThingHolderCount);
}
}
public static int ThingHolderCount(Map map)
=> (map.passingShipManager?.passingShips.Count ?? 0)
+ map.components.Count
+ (map.listerThings.listsByGroup[(int)ThingRequestGroup.ThingHolder]?.Count ?? 0);
}
/*public class ThingOwnerUtilityCaching : ClassWithFishPatches
{
public class GetAllThingsRecursively_Patch : FishPatch
{
public override string Description { get; }
= "Caching of information about mostly unspawned pawns and things. Relatively large performance impact";
public override Delegate TargetMethodGroup { get; } = ThingOwnerUtility.GetAllThingsRecursively<Thing>;
public static bool Prefix(Map map, ThingRequest request, IList? outThings, bool allowUnreal,
Predicate<IThingHolder>? passCheck, bool alsoGetSpawnedThings, out bool __state)
{
if (request.singleDef != null
|| request.group == 0
|| !allowUnreal
|| passCheck != null
|| alsoGetSpawnedThings
|| outThings == null)
{
__state = false;
return true;
}
var listByGroup = map.listerThings.listsByGroup[(int)request.group];
if (listByGroup is null)
{
@@ -28,18 +113,20 @@ public class ThingOwnerUtilityCaching : ClassWithFishPatches
}
var type = outThings.GetType().GetGenericArguments()[0];
var cache = GetAndCheckCache(map, request.group, type);
if (cache.count == listByGroup.Count)
{
if (ThingHolderCount(map) != cache.thingHolderCount)
return __state = true;
var key = new ThingRequestCache(map.uniqueID, (byte)request.group, type.TypeHandle);
ref var cache = ref ThingRequestCache.Get.GetOrAddReference(ref key);
outThings.Clear();
outThings.TryAddRange(cache.things, type);
return __state = false;
if (cache.Dirty
|| cache.count != listByGroup.Count
|| cache.thingHolderCount != ThingHolderCount(map))
{
return __state = true;
}
return __state = true;
outThings.Clear();
outThings.TryAddRange(cache.things!, type);
return __state = false;
}
public static void Postfix(Map map, ThingRequest request, IList outThings, bool __state)
@@ -48,7 +135,9 @@ public class ThingOwnerUtilityCaching : ClassWithFishPatches
return;
var type = outThings.GetType().GetGenericArguments()[0];
var cache = ThingRequestCache.Get[new(map.uniqueID, (byte)request.group, type.TypeHandle)];
ref var cache
= ref ThingRequestCache.Get.GetReference(new(map.uniqueID, (byte)request.group, type.TypeHandle));
if (cache.things is null)
{
cache.things = FisheryLib.CollectionExtensions.TryNew(outThings, type);
@@ -58,116 +147,41 @@ public class ThingOwnerUtilityCaching : ClassWithFishPatches
cache.things.Clear();
cache.things.TryAddRange(outThings, type);
}
cache.ShouldRefreshNow = false;
cache.count = map.listerThings.listsByGroup[(int)request.group]?.Count ?? -1;
cache.thingHolderCount = ThingHolderCount(map);
ThingRequestCache.Get[new(map.uniqueID, (byte)request.group, type.TypeHandle)] = cache;
}
public static ref ThingRequestInfo GetAndCheckCache(Map map, ThingRequestGroup group, Type type)
{
var key = new ThingRequestCache(map.uniqueID, (byte)group, type.TypeHandle);
ref var cache = ref ThingRequestCache.Get.TryGetReferenceUnsafe(ref key);
if (Unsafe.IsNullRef(ref cache))
{
ThingRequestCache.Get[key] = new();
cache = ref ThingRequestCache.Get.TryGetReferenceUnsafe(ref key);
}
if (cache.ShouldRefreshNow)
{
cache.count = map.listerThings.listsByGroup[(int)group]?.Count ?? -1;
cache.thingHolderCount = ThingHolderCount(map);
cache.ShouldRefreshNow = false;
ThingRequestCache.Get[new(map.uniqueID, (byte)group, type.TypeHandle)] = cache;
cache.count = -1;
}
unsafe
{
return ref Unsafe.AsRef<ThingRequestInfo>(Unsafe.AsPointer(ref cache));
}
cache.SetDirty(false, map.uniqueID + (int)request.group + cache.count + cache.thingHolderCount);
}
}
public static int ThingHolderCount(Map map) => (map.passingShipManager?.passingShips.Count ?? 0) + map.components.Count + (map.listerThings.listsByGroup[(int)ThingRequestGroup.ThingHolder]?.Count ?? 0);
public static int ThingHolderCount(Map map)
=> (map.passingShipManager?.passingShips.Count ?? 0)
+ map.components.Count
+ (map.listerThings.listsByGroup[(int)ThingRequestGroup.ThingHolder]?.Count ?? 0);
}*/
/*public static void GetAllThingsRecursively(IThingHolder holder, List<Thing> outThings)
{
outThings.Clear();
ThingOwnerUtility.tmpStack.Clear();
ThingOwnerUtility.tmpStack.Push(holder);
while (ThingOwnerUtility.tmpStack.Count != 0)
{
IThingHolder thingHolder = ThingOwnerUtility.tmpStack.Pop();
ThingOwner directlyHeldThings = thingHolder.GetDirectlyHeldThings();
if (directlyHeldThings != null)
{
outThings.AddRange(directlyHeldThings);
}
ThingOwnerUtility.tmpHolders.Clear();
thingHolder.GetChildHolders(ThingOwnerUtility.tmpHolders);
for (int i = 0; i < ThingOwnerUtility.tmpHolders.Count; i++)
{
ThingOwnerUtility.tmpStack.Push(ThingOwnerUtility.tmpHolders[i]);
}
}
ThingOwnerUtility.tmpStack.Clear();
ThingOwnerUtility.tmpHolders.Clear();
}
public static void AppendThingHoldersFromThings(List<IThingHolder> outThingsHolders, IList<Thing> container)
{
if (container == null)
{
return;
}
int i = 0;
for (int count = container.Count; i < count; i++)
{
IThingHolder thingHolder = container[i] as IThingHolder;
if (thingHolder != null)
{
outThingsHolders.Add(thingHolder);
}
ThingWithComps thingWithComps = container[i] as ThingWithComps;
if (thingWithComps == null)
{
continue;
}
List<ThingComp> allComps = thingWithComps.AllComps;
for (int j = 0; j < allComps.Count; j++)
{
IThingHolder thingHolder2 = allComps[j] as IThingHolder;
if (thingHolder2 != null)
{
outThingsHolders.Add(thingHolder2);
}
}
}
}*/
}
public struct ThingRequestInfo : Cache.IIsRefreshable<ThingRequestCache, ThingRequestInfo>
/*public record struct ThingRequestInfo
{
public int nextRefreshTick;
private int _nextRefreshTick;
public int count;
public int thingHolderCount;
public IList things;
public IList? things;
public bool ShouldRefreshNow
{
get => nextRefreshTick < Current.Game.tickManager.TicksGame;
set => nextRefreshTick = value ? 0 : Current.Game.tickManager.TicksGame + 3072 + Math.Abs(Rand.Int % 2048);
}
public void SetDirty(bool value, int offset)
=> _nextRefreshTick = value ? 0 : TickHelper.Add(3072, offset, 2048);
public ThingRequestInfo SetNewValue(ThingRequestCache key) => throw new NotImplementedException();
public bool Dirty => _nextRefreshTick < Current.Game.tickManager.TicksGame;
}*/
public record struct ThingRequestInfo<T> where T : Thing
{
private int _nextUpdateTick;
public int ListsByGroupCount;
public int ThingHolderCount;
public List<T>? Things;
public void SetDirty(bool value, int offset)
=> _nextUpdateTick = value ? 0 : TickHelper.Add(3072, offset, 2048);
public bool Dirty => TickHelper.Past(_nextUpdateTick);
}