Notify watchers only on output changes and pass previous values to callbacks

This commit is contained in:
Stone_Red
2026-03-26 01:19:25 +01:00
parent aa6d13a46c
commit c40195d0a3
3 changed files with 68 additions and 15 deletions
@@ -6,6 +6,7 @@ public sealed class CycleCircuitSimulator : SimulatorBase
{
private int[] nextInputMasks = [];
private Action<int[], int[], int[]> computeOutputs = (_, _, _) => { };
private int[] previousOutputMasks = [];
protected override void EnsureStorage()
{
@@ -13,6 +14,7 @@ public sealed class CycleCircuitSimulator : SimulatorBase
if (nextInputMasks.Length != gateKinds.Count)
{
nextInputMasks = new int[gateKinds.Count];
previousOutputMasks = new int[gateKinds.Count];
}
}
@@ -20,10 +22,9 @@ public sealed class CycleCircuitSimulator : SimulatorBase
{
base.Reset();
Array.Clear(nextInputMasks);
Array.Clear(previousOutputMasks);
}
protected override void OnSourceChanged(int gateId) { }
public override void Step()
{
EnsureCompiled();
@@ -32,9 +33,13 @@ public sealed class CycleCircuitSimulator : SimulatorBase
Reset();
}
(outputMasks, previousOutputMasks) = (previousOutputMasks, outputMasks);
computeOutputs(inputMasks, outputMasks, sourceStates);
PropagateAndSwap();
NotifyAllWatchers();
if (hasAnyWatchers)
{
NotifyAllWatchers(previousOutputMasks);
}
}
private void PropagateAndSwap()
@@ -103,9 +108,13 @@ public sealed class CycleCircuitSimulator : SimulatorBase
while (changed && steps < maxSteps)
{
steps++;
(outputMasks, previousOutputMasks) = (previousOutputMasks, outputMasks);
computeOutputs(inputMasks, outputMasks, sourceStates);
changed = PropagateAndSwapDetectChange();
NotifyAllWatchers();
if (hasAnyWatchers)
{
NotifyAllWatchers(previousOutputMasks);
}
}
return !changed;
}
@@ -28,6 +28,7 @@ public abstract class SimulatorBase : ICircuitSimulator
private readonly List<GateWatcherEntry> allWatchers = [];
private Action<int, int>[][] watcherCache = [];
private int[] gatesWithWatchers = [];
protected bool hasAnyWatchers;
protected int nextWatcherId;
protected sealed record GateWatcherEntry(int Id, int GateId, Action<int, int> Callback);
@@ -169,6 +170,7 @@ public abstract class SimulatorBase : ICircuitSimulator
GateWatcherEntry entry = new GateWatcherEntry(id, gateId, callback);
allWatchers.Add(entry);
RebuildWatcherCache();
compiled = false;
return new GateWatcherSubscription(this, id);
}
@@ -176,6 +178,7 @@ public abstract class SimulatorBase : ICircuitSimulator
{
_ = allWatchers.RemoveAll(w => w.Id == id);
RebuildWatcherCache();
compiled = false;
}
private void RebuildWatcherCache()
@@ -193,18 +196,22 @@ public abstract class SimulatorBase : ICircuitSimulator
}
gatesWithWatchers = [.. activeGates];
hasAnyWatchers = allWatchers.Count > 0;
}
protected void NotifyAllWatchers()
protected void NotifyAllWatchers(int[] previousOutputMasks)
{
for (int i = 0; i < gatesWithWatchers.Length; i++)
{
int gateId = gatesWithWatchers[i];
Action<int, int>[] callbacks = watcherCache[gateId];
int val = outputMasks[gateId];
for (int j = 0; j < callbacks.Length; j++)
if (outputMasks[gateId] != previousOutputMasks[gateId])
{
callbacks[j](gateId, val);
Action<int, int>[] callbacks = watcherCache[gateId];
int val = outputMasks[gateId];
for (int j = 0; j < callbacks.Length; j++)
{
callbacks[j](gateId, val);
}
}
}
}
@@ -275,7 +282,7 @@ public abstract class SimulatorBase : ICircuitSimulator
GateKind.Buffer => Expression.Condition(Expression.NotEqual(Expression.And(inMask, Expression.Constant(1)), Expression.Constant(0)), Expression.Constant(1), Expression.Constant(0)),
GateKind.Sink => Expression.Condition(Expression.NotEqual(Expression.And(inMask, Expression.Constant(1)), Expression.Constant(0)), Expression.Constant(1), Expression.Constant(0)),
GateKind.Lut => Expression.ArrayIndex(lutDataConst, Expression.Add(Expression.Constant(lutOffsets[gateId]), Expression.And(inMask, Expression.Constant(lutMasks[gateId])))),
_ => throw new ArgumentOutOfRangeException(),
_ => throw new InvalidOperationException($"Unknown gate kind: {gateKinds[gateId]}")
};
}