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:
@@ -1,10 +1,11 @@
|
||||
// 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 System.Threading.Tasks;
|
||||
using SharedAltarCache = PerformanceFish.Cache.ByIndex<RimWorld.Ideo, PerformanceFish.ThoughtWorker_Precept_AltarSharingOptimization.SharedAltarCacheValue>;
|
||||
using SharedAltarCache
|
||||
= PerformanceFish.Cache.ByIndex<RimWorld.Ideo,
|
||||
PerformanceFish.ThoughtWorker_Precept_AltarSharingOptimization.SharedAltarCacheValue>;
|
||||
|
||||
namespace PerformanceFish;
|
||||
|
||||
@@ -12,92 +13,43 @@ public class ThoughtWorker_Precept_AltarSharingOptimization : ClassWithFishPatch
|
||||
{
|
||||
public class SharedAltar_Patch : FirstPriorityFishPatch
|
||||
{
|
||||
public override string Description => "Caches results of ideology's shared altar thoughtworker, throttles it to wait a minimum of 128 ticks between refreshes and makes refreshing happen async on a different thread. Quite impactful on performance";
|
||||
public override Expression<Action> TargetMethod => () => default(ThoughtWorker_Precept_AltarSharing)!.SharedAltar(null);
|
||||
public static void AsyncThoughtWorker(ThoughtWorker_Precept_AltarSharing __instance, Pawn pawn)
|
||||
{
|
||||
var allThings = pawn.Map.listerThings.listsByGroup[(int)ThingRequestGroup.BuildingArtificial].ToArray();
|
||||
Task.Run(() =>
|
||||
{
|
||||
var newThing = SharedAltarParallel(pawn, allThings);
|
||||
lock (Lock)
|
||||
SharedAltarCache.GetDirectly[pawn.Ideo] = new(pawn, newThing);
|
||||
});
|
||||
}
|
||||
public static bool Prefix(ThoughtWorker_Precept_AltarSharing __instance, Pawn pawn, ref Thing? __result, out bool __state)
|
||||
public override string Description { get; }
|
||||
= "Caches results of ideology's shared altar thoughtworker and throttles it to wait a minimum of 128 ticks "
|
||||
+ "between refreshes. Quite impactful on performance";
|
||||
|
||||
public override MethodBase TargetMethodInfo { get; } = AccessTools.Method(
|
||||
typeof(ThoughtWorker_Precept_AltarSharing), nameof(ThoughtWorker_Precept_AltarSharing.SharedAltar));
|
||||
|
||||
public static bool Prefix(ThoughtWorker_Precept_AltarSharing __instance, Pawn pawn, ref Thing? __result,
|
||||
out bool __state)
|
||||
{
|
||||
if (!pawn.Spawned)
|
||||
{
|
||||
__result = null;
|
||||
return __state = false;
|
||||
}
|
||||
SharedAltarCacheValue cache;
|
||||
var shouldStartRefreshing = false;
|
||||
lock (Lock)
|
||||
{
|
||||
if (!SharedAltarCache.GetDirectly.TryGetValue(pawn.Ideo, out cache) || (!FishSettings.ThreadingEnabled && cache.ShouldRefreshNow))
|
||||
{
|
||||
return __state = true;
|
||||
}
|
||||
else if (cache.ShouldRefreshNow)
|
||||
{
|
||||
cache.currentlyRefreshing = true;
|
||||
SharedAltarCache.GetDirectly[pawn.Ideo] = cache;
|
||||
shouldStartRefreshing = true;
|
||||
}
|
||||
}
|
||||
|
||||
ref var cache = ref SharedAltarCache.Get.GetReference(pawn.Ideo);
|
||||
|
||||
if (cache.Dirty)
|
||||
return __state = true;
|
||||
|
||||
__result = cache.thing;
|
||||
if (shouldStartRefreshing)
|
||||
AsyncThoughtWorker(__instance, pawn);
|
||||
return __state = false;
|
||||
}
|
||||
|
||||
public static void Postfix(Pawn pawn, Thing __result, bool __state)
|
||||
{
|
||||
if (!__state)
|
||||
return;
|
||||
lock (Lock)
|
||||
SharedAltarCache.GetDirectly[pawn.Ideo] = new(pawn, __result);
|
||||
|
||||
SharedAltarCache.Get[pawn.Ideo] = new(pawn, __result);
|
||||
}
|
||||
public static Thing? SharedAltarParallel(Pawn pawn, Thing[] allThings)
|
||||
{
|
||||
if (!pawn.Spawned || pawn.Ideo == null)
|
||||
return null;
|
||||
|
||||
for (var i = 0; i < allThings.Length; i++)
|
||||
{
|
||||
var item = allThings[i];
|
||||
var compStyleable = item.TryGetComp<CompStyleable>();
|
||||
if (compStyleable == null || compStyleable.SourcePrecept == null || compStyleable.SourcePrecept.ideo != pawn.Ideo)
|
||||
continue;
|
||||
|
||||
var room = item.GetRoom();
|
||||
if (room == null || room.TouchesMapEdge)
|
||||
continue;
|
||||
|
||||
var containedAndAdjacentThings = room.ContainedAndAdjacentThings;
|
||||
for (var k = 0; k < containedAndAdjacentThings.Count; k++)
|
||||
{
|
||||
var containedAndAdjacentThing = containedAndAdjacentThings[k];
|
||||
if (containedAndAdjacentThing != item)
|
||||
{
|
||||
var compStyleable2 = containedAndAdjacentThing.TryGetComp<CompStyleable>();
|
||||
if (compStyleable2 != null && compStyleable2.SourcePrecept != null && compStyleable2.SourcePrecept.ideo != pawn.Ideo)
|
||||
{
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private static object Lock { get; } = new();
|
||||
}
|
||||
|
||||
public struct SharedAltarCacheValue : Cache.IIsRefreshable<Ideo, SharedAltarCacheValue>
|
||||
public record struct SharedAltarCacheValue
|
||||
{
|
||||
public Thing? thing;
|
||||
public bool currentlyRefreshing;
|
||||
private int _nextLateRefreshTick;
|
||||
private int _nextEarlyRefreshTick;
|
||||
private int _allStructuresListVersion;
|
||||
@@ -106,30 +58,27 @@ public class ThoughtWorker_Precept_AltarSharingOptimization : ClassWithFishPatch
|
||||
public SharedAltarCacheValue(Pawn pawn, Thing? result)
|
||||
{
|
||||
thing = result;
|
||||
var ticks = Current.Game.tickManager.TicksGame;
|
||||
_nextLateRefreshTick = ticks + 3072 + Math.Abs(Rand.Int % 2048);
|
||||
_nextEarlyRefreshTick = ticks + 128 + Math.Abs(Rand.Int % 128);
|
||||
_nextLateRefreshTick = TickHelper.Add(3072, pawn.thingIDNumber, 2048);
|
||||
_nextEarlyRefreshTick = TickHelper.Add(128, pawn.thingIDNumber, 128);
|
||||
_lister = pawn.Map.listerThings;
|
||||
_allStructuresListVersion = _lister.listsByGroup[(int)ThingRequestGroup.BuildingArtificial].Version();
|
||||
currentlyRefreshing = false;
|
||||
_allStructuresListVersion = _lister.listsByGroup[(int)ThingRequestGroup.BuildingArtificial]._version;
|
||||
}
|
||||
|
||||
public bool ShouldRefreshNow
|
||||
public bool Dirty
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
var ticks = Current.Game.tickManager.TicksGame;
|
||||
return !currentlyRefreshing && (_nextLateRefreshTick < ticks
|
||||
|| (_nextEarlyRefreshTick < ticks && _allStructuresListVersion != _lister.listsByGroup[(int)ThingRequestGroup.BuildingArtificial].Version()));
|
||||
}
|
||||
set
|
||||
{
|
||||
var ticks = Current.Game.tickManager.TicksGame;
|
||||
_nextLateRefreshTick = value ? 0 : ticks + 3072 + Math.Abs(Rand.Int % 2048);
|
||||
_nextEarlyRefreshTick = value ? 0 : ticks + 128 + Math.Abs(Rand.Int % 128);
|
||||
var ticks = TickHelper.TicksGame;
|
||||
return _nextLateRefreshTick < ticks
|
||||
|| (_nextEarlyRefreshTick < ticks
|
||||
&& _allStructuresListVersion
|
||||
!= _lister.listsByGroup[(int)ThingRequestGroup.BuildingArtificial]._version);
|
||||
}
|
||||
}
|
||||
|
||||
public SharedAltarCacheValue SetNewValue(Ideo key) => throw new NotImplementedException();
|
||||
public bool Equals(SharedAltarCacheValue other) => (thing == other.thing) & (_lister == other._lister);
|
||||
|
||||
public override int GetHashCode() => HashCode.Combine(thing, _lister);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user