refactor StateManagerDecay to not use goto, and remove the unused align

(Align didn't seem to make any sense, it was just weighting priority less towards the current frame. Perhaps helpful in some situations, but I don't think enough so to justify complicating the decay algorithm. Either way it was a misnomer and I don't know what there is to align to.)
This commit is contained in:
SuuperW 2020-07-27 14:50:27 -05:00
parent 60cc8d944f
commit 10dd5b746c
1 changed files with 29 additions and 126 deletions

View File

@ -48,183 +48,86 @@ namespace BizHawk.Client.Common
private readonly ITasMovie _movie; private readonly ITasMovie _movie;
private readonly TasStateManager _tsm; private readonly TasStateManager _tsm;
private List<int> _zeros; // amount of least significant zeros in bitwise view (also max pattern step) private List<int> _zeros; // number of ending zeros in binary representation of the index
private int _bits; // size of _zeros is 2 raised to the power of _bits private int _bits; // max number of bits for which to calculate _zeros
private int _mask; // for remainder calculation using bitwise instead of division private int _mask; // to mask index into _zeros, to prevent accessing out of range
private int _base; // repeat count (like fceux's capacity). only used by aligned formula
private int _capacity; // total amount of savestates private int _step; // initial gap between states
private int _step; // initial memory state gap
private bool _align; // extra care about fine alignment. TODO: do we want it?
public StateManagerDecay(ITasMovie movie, TasStateManager tsm) public StateManagerDecay(ITasMovie movie, TasStateManager tsm)
{ {
_movie = movie; _movie = movie;
_tsm = tsm; _tsm = tsm;
_align = false;
} }
// todo: go through all states once, remove as many as we need. refactor to not need goto // todo: go through all states once, remove as many as we need.
public void Trigger(int frame, int decayStates) public void Trigger(int currentEmulatedFrame, int statesToDecay)
{ {
for (; decayStates > 0 && _tsm.Count > 1;) for (; statesToDecay > 0 && _tsm.Count > 1; statesToDecay--)
{ {
int baseStateIndex = _tsm.GetStateIndexByFrame(frame); int baseStateIndex = _tsm.GetStateIndexByFrame(currentEmulatedFrame);
int baseStateFrame = _tsm.GetStateFrameByIndex(baseStateIndex) / _step; // reduce right away int baseStateFrame = _tsm.GetStateFrameByIndex(baseStateIndex) / _step; // reduce to step integral
int forwardPriority = -1000000; int highestPriority = -1000000;
int backwardPriority = -1000000; int frameToDecay = -1;
int forwardFrame = -1; bool decayed = false;
int backwardFrame = -1;
for (int currentStateIndex = 1; currentStateIndex < baseStateIndex; currentStateIndex++) for (int currentStateIndex = 1; currentStateIndex < _tsm.Count; currentStateIndex++)
{ {
int currentFrame = _tsm.GetStateFrameByIndex(currentStateIndex); int currentFrame = _tsm.GetStateFrameByIndex(currentStateIndex);
if (_movie.Markers.IsMarker(currentFrame + 1)) if (_movie.Markers.IsMarker(currentFrame + 1))
{
continue; continue;
}
if (currentFrame + 1 == _movie.LastEditedFrame) if (currentFrame + 1 == _movie.LastEditedFrame)
{
continue; continue;
}
if (currentFrame % _step > 0) if (currentFrame % _step > 0)
{ {
// ignore the pattern if the state doesn't belong already, drop it blindly and skip everything // ignore the pattern if the state doesn't belong already, drop it blindly and skip everything
if (_tsm.Remove(currentFrame)) if (_tsm.Remove(currentFrame))
{ {
// decrementing this if no state was removed is BAD decayed = true;
decayStates--; break;
// this is the kind of highly complex loops that might justify goto
goto next_state;
} }
} }
else else // reduce to step integral for all the decay logic
{
// reduce to imaginary integral greenzone for all the decay logic
currentFrame /= _step; currentFrame /= _step;
}
int zeroCount = _zeros[currentFrame & _mask]; int zeroCount = _zeros[currentFrame & _mask];
int priority = (baseStateFrame - currentFrame) >> zeroCount; int priority = (baseStateFrame - currentFrame) >> zeroCount;
if (_align) if (priority > highestPriority)
{ {
priority -= (_base * ((1 << zeroCount) * 2 - 1)) >> zeroCount; highestPriority = priority;
} frameToDecay = currentFrame;
if (priority > forwardPriority)
{
forwardPriority = priority;
forwardFrame = currentFrame;
} }
} }
if (decayed)
continue;
for (int currentStateIndex = _tsm.Count - 1; currentStateIndex > baseStateIndex; currentStateIndex--) if (frameToDecay > -1)
{ {
int currentFrame = _tsm.GetStateFrameByIndex(currentStateIndex); if (_tsm.Remove(frameToDecay * _step))
decayed = true;
if (_movie.Markers.IsMarker(currentFrame + 1))
{
continue;
}
if (currentFrame % _step > 0 && currentFrame + 1 != _movie.LastEditedFrame)
{
// ignore the pattern if the state doesn't belong already, drop it blindly and skip everything
if (_tsm.Remove(currentFrame))
{
// decrementing this if no state was removed is BAD
decayStates--;
// this is the kind of highly complex loops that might justify goto
goto next_state;
}
}
else
{
// reduce to imaginary integral greenzone for all the decay logic
currentFrame /= _step;
}
int zeroCount = _zeros[currentFrame & _mask];
int priority = (currentFrame - baseStateFrame) >> zeroCount;
if (_align)
{
priority -= (_base * ((1 << zeroCount) * 2 - 1)) >> zeroCount;
}
if (priority > backwardPriority)
{
backwardPriority = priority;
backwardFrame = currentFrame;
}
}
int decayStatesLast = decayStates;
if (forwardFrame > -1 && backwardFrame > -1)
{
if (baseStateFrame - forwardFrame > backwardFrame - baseStateFrame)
{
if (_tsm.Remove(forwardFrame * _step))
{
// decrementing this if no state was removed is BAD
decayStates--;
}
}
else
{
if (_tsm.Remove(backwardFrame * _step))
{
// decrementing this if no state was removed is BAD
decayStates--;
}
}
}
else if (forwardFrame > -1)
{
if (_tsm.Remove(forwardFrame * _step))
{
// decrementing this if no state was removed is BAD
decayStates--;
}
}
else if (backwardFrame > -1)
{
if (_tsm.Remove(backwardFrame * _step))
{
// decrementing this if no state was removed is BAD
decayStates--;
}
} }
// we're very sorry about failing to find states to remove, but we can't go beyond capacity, so remove at least something // we're very sorry about failing to find states to remove, but we can't go beyond capacity, so remove at least something
// this shouldn't happen, but if we don't do it here, nothing good will happen either if (!decayed)
if (decayStatesLast == decayStates)
{ {
if (_tsm.Remove(_tsm.GetStateFrameByIndex(1))) if (!_tsm.Remove(_tsm.GetStateFrameByIndex(1)))
{ {
// decrementing this if no state was removed is BAD // This should never happen, but just in case, we don't want to let memory usage continue to climb.
decayStates--; throw new System.Exception("Failed to remove states.");
} }
} }
// this is the kind of highly complex loops that might justify goto
next_state: ;
} }
} }
public void UpdateSettings(int capacity, int step, int bits) public void UpdateSettings(int capacity, int step, int bits)
{ {
_capacity = capacity;
_step = step; _step = step;
_bits = bits; _bits = bits;
_mask = (1 << _bits) - 1; _mask = (1 << _bits) - 1;
_base = (_capacity + _bits / 2) / (_bits + 1);
_zeros = new List<int> { _bits }; _zeros = new List<int> { _bits };
for (int i = 1; i < (1 << _bits); i++) for (int i = 1; i < (1 << _bits); i++)