97 lines
3.1 KiB
C#
97 lines
3.1 KiB
C#
using System;
|
|
using System.Runtime.CompilerServices;
|
|
|
|
namespace T3.Core.Operator.Slots;
|
|
|
|
/// <summary>
|
|
/// Manages a version-chasing synchronization where SourceVersion tracks the latest available data state and
|
|
/// ValueVersion tracks the version of the currently cached value. A slot is considered "dirty" whenever SourceVersion > ValueVersion,
|
|
/// triggering an update that recalculates the logic and synchronizes the two counters. This mechanism allows the engine to skip redundant
|
|
/// calculations by ensuring that work is only performed when a dependency has actually incremented its version.
|
|
/// </summary>
|
|
public sealed class DirtyFlag
|
|
{
|
|
public static void IncrementGlobalTicks()
|
|
{
|
|
_globalTickCount += GlobalTickDiffPerFrame;
|
|
}
|
|
|
|
public bool IsDirty => TriggerIsEnabled || ValueVersion != SourceVersion;
|
|
|
|
public static int GlobalInvalidationTick = 0;
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public int Invalidate()
|
|
{
|
|
// If we already visited this slot during the current Invalidation Pass, stop.
|
|
if (InvalidationTick == GlobalInvalidationTick)
|
|
return SourceVersion;
|
|
|
|
InvalidationTick = GlobalInvalidationTick;
|
|
SourceVersion++;
|
|
|
|
return SourceVersion;
|
|
}
|
|
|
|
public void ForceInvalidate()
|
|
{
|
|
InvalidationTick = GlobalInvalidationTick;
|
|
SourceVersion++;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public void Clear()
|
|
{
|
|
ValueVersion = SourceVersion;
|
|
}
|
|
|
|
// editor-specific function
|
|
internal void SetUpdated()
|
|
{
|
|
if (_lastUpdateTick >= _globalTickCount && _lastUpdateTick < _globalTickCount + GlobalTickDiffPerFrame - 1)
|
|
{
|
|
_lastUpdateTick++;
|
|
}
|
|
else
|
|
{
|
|
_lastUpdateTick = _globalTickCount;
|
|
}
|
|
}
|
|
|
|
public int ValueVersion;
|
|
public int SourceVersion = 1; // initially dirty
|
|
|
|
// editor-specific value
|
|
public int FramesSinceLastUpdate => (_globalTickCount - 1 - _lastUpdateTick) / GlobalTickDiffPerFrame;
|
|
|
|
public DirtyFlagTrigger Trigger
|
|
{
|
|
get => _trigger;
|
|
set
|
|
{
|
|
_trigger = value;
|
|
TriggerIsEnabled = value != DirtyFlagTrigger.None;
|
|
TriggerIsAnimated = value == DirtyFlagTrigger.Animated;
|
|
}
|
|
}
|
|
|
|
private DirtyFlagTrigger _trigger;
|
|
internal bool TriggerIsEnabled;
|
|
internal bool TriggerIsAnimated;
|
|
|
|
// editor-specific value
|
|
public int NumUpdatesWithinFrame
|
|
{
|
|
get
|
|
{
|
|
var updatesSinceLastFrame = _lastUpdateTick - _globalTickCount;
|
|
var shift = (-updatesSinceLastFrame >= GlobalTickDiffPerFrame) ? GlobalTickDiffPerFrame : 0;
|
|
return Math.Max(updatesSinceLastFrame + shift + 1, 0); // shift corrects if update was one frame ago
|
|
}
|
|
}
|
|
|
|
internal int InvalidationTick = -1;
|
|
private const int GlobalTickDiffPerFrame = 100; // each frame differs with 100 ticks to last one
|
|
private static int _globalTickCount;
|
|
private int _lastUpdateTick;
|
|
} |