Cache no-op component tick lists in RimWorld 1.6
This commit is contained in:
@@ -11,6 +11,15 @@ namespace PerformanceFish;
|
||||
|
||||
public sealed class CompTickOptimization : ClassWithFishPrepatches
|
||||
{
|
||||
#if V1_6
|
||||
public struct ThingTickCache
|
||||
{
|
||||
public List<ThingComp>? Source;
|
||||
public int Version;
|
||||
public bool AllTrivial;
|
||||
}
|
||||
#endif
|
||||
|
||||
private static readonly MethodInfo _thingCompTickMethod
|
||||
= AccessTools.DeclaredMethod(typeof(ThingComp), nameof(ThingComp.CompTick));
|
||||
|
||||
@@ -39,8 +48,12 @@ public sealed class CompTickOptimization : ClassWithFishPrepatches
|
||||
private static readonly bool _hediffCompPostTickIntervalBaseIsTrivial
|
||||
= IsTrivialVoidMethod(_hediffCompPostTickIntervalMethod);
|
||||
|
||||
#if V1_6
|
||||
private static int _canSkipInheritedBaseThingCompTickState;
|
||||
#else
|
||||
private static readonly Lazy<bool> _canSkipInheritedBaseThingCompTick
|
||||
= new(static () => _thingCompTickBaseIsTrivial && !HasHarmonyPatches(_thingCompTickMethod));
|
||||
#endif
|
||||
|
||||
private static readonly Lazy<bool> _canSkipInheritedBaseThingCompTickInterval
|
||||
= new(static () => _thingCompTickIntervalBaseIsTrivial && !HasHarmonyPatches(_thingCompTickIntervalMethod));
|
||||
@@ -89,10 +102,19 @@ public sealed class CompTickOptimization : ClassWithFishPrepatches
|
||||
|
||||
public sealed class ThingTickPatch : FishPrepatch
|
||||
{
|
||||
public override bool DefaultState => false;
|
||||
public override bool DefaultState =>
|
||||
#if V1_6
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
|
||||
public override string? Description { get; }
|
||||
#if V1_6
|
||||
= "Skips the component tick loop when every component inherits the unpatched, empty base CompTick method.";
|
||||
#else
|
||||
= "Skips inherited empty ThingComp.CompTick calls when the base method is still unpatched and trivial.";
|
||||
#endif
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= AccessTools.DeclaredMethod(typeof(ThingWithComps), "Tick");
|
||||
@@ -102,16 +124,61 @@ public sealed class CompTickOptimization : ClassWithFishPrepatches
|
||||
|
||||
public static void ReplacementBody(ThingWithComps instance)
|
||||
{
|
||||
#if V1_6
|
||||
var comps = instance.comps;
|
||||
if (comps is null)
|
||||
return;
|
||||
#else
|
||||
var comps = instance.AllComps;
|
||||
#endif
|
||||
|
||||
var count = comps.Count;
|
||||
#if V1_6
|
||||
// Small lists cost less to dispatch normally than to validate a cache.
|
||||
if (count >= 4 && CanSkipInheritedBaseThingCompTick())
|
||||
{
|
||||
ref var cache = ref instance.ThingTickCache();
|
||||
if (!ReferenceEquals(cache.Source, comps) || cache.Version != comps._version)
|
||||
UpdateThingTickCache(comps, ref cache);
|
||||
|
||||
if (cache.AllTrivial)
|
||||
return;
|
||||
}
|
||||
#else
|
||||
var canSkipInheritedBase = CanSkipInheritedBaseThingCompTick();
|
||||
#endif
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
#if V1_6
|
||||
instance.comps![i].CompTick();
|
||||
#else
|
||||
var comp = comps[i];
|
||||
if (!canSkipInheritedBase || !_thingCompTickTypes.GetOrAdd(comp.GetType()))
|
||||
comp.CompTick();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if V1_6
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void UpdateThingTickCache(List<ThingComp> comps, ref ThingTickCache cache)
|
||||
{
|
||||
var allTrivial = true;
|
||||
for (var i = 0; i < comps.Count; i++)
|
||||
{
|
||||
var comp = comps[i];
|
||||
if (comp is null || !_thingCompTickTypes.GetOrAdd(comp.GetType()))
|
||||
{
|
||||
allTrivial = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cache.Source = comps;
|
||||
cache.Version = comps._version;
|
||||
cache.AllTrivial = allTrivial;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public sealed class ThingTickIntervalPatch : FishPrepatch
|
||||
@@ -267,7 +334,19 @@ public sealed class CompTickOptimization : ClassWithFishPrepatches
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool CanSkipInheritedBaseThingCompTick()
|
||||
=> _canSkipInheritedBaseThingCompTick.Value;
|
||||
{
|
||||
#if V1_6
|
||||
var state = _canSkipInheritedBaseThingCompTickState;
|
||||
if (state == 0)
|
||||
{
|
||||
state = _thingCompTickBaseIsTrivial && !HasHarmonyPatches(_thingCompTickMethod) ? 2 : 1;
|
||||
_canSkipInheritedBaseThingCompTickState = state;
|
||||
}
|
||||
return state == 2;
|
||||
#else
|
||||
return _canSkipInheritedBaseThingCompTick.Value;
|
||||
#endif
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool CanSkipInheritedBaseThingCompTickInterval()
|
||||
|
||||
@@ -39,6 +39,10 @@ public static class PrepatcherFields
|
||||
[PrepatcherField]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static extern ref GeneTrackerOptimization.GeneListVersionState GeneListVersions(this Pawn_GeneTracker geneTracker);
|
||||
|
||||
[PrepatcherField]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static extern ref CompTickOptimization.ThingTickCache ThingTickCache(this ThingWithComps thing);
|
||||
#endif
|
||||
|
||||
[PrepatcherField]
|
||||
|
||||
Reference in New Issue
Block a user