This commit is contained in:
adelikat 2015-08-16 19:22:11 -04:00
commit 19a1f6cbef
3 changed files with 133 additions and 105 deletions

View File

@ -9,6 +9,8 @@ using BizHawk.Common;
using BizHawk.Emulation.Common; using BizHawk.Emulation.Common;
using BizHawk.Emulation.Common.IEmulatorExtensions; using BizHawk.Emulation.Common.IEmulatorExtensions;
using stateKVP = System.Collections.Generic.KeyValuePair<int, int>;
namespace BizHawk.Client.Common namespace BizHawk.Client.Common
{ {
class tsmState : IDisposable class tsmState : IDisposable
@ -17,13 +19,14 @@ namespace BizHawk.Client.Common
TasStateManager _manager; TasStateManager _manager;
byte[] _state; byte[] _state;
int frame;
long ID; long ID;
public int Frame;
public tsmState(TasStateManager manager, byte[] state) public tsmState(TasStateManager manager, byte[] state, int frame)
{ {
_manager = manager; _manager = manager;
_state = state; _state = state;
Frame = frame;
//I still think this is a bad idea. IDs may need scavenging somehow //I still think this is a bad idea. IDs may need scavenging somehow
if (state_id > long.MaxValue - 100) if (state_id > long.MaxValue - 100)
@ -109,40 +112,6 @@ namespace BizHawk.Client.Common
internal NDBDatabase ndbdatabase; internal NDBDatabase ndbdatabase;
private Guid guid = Guid.NewGuid(); private Guid guid = Guid.NewGuid();
private SortedList<int, tsmState> States = new SortedList<int, tsmState>(); private SortedList<int, tsmState> States = new SortedList<int, tsmState>();
private SortedList<int, SortedList<int, tsmState>> BranchStates = new SortedList<int, SortedList<int, tsmState>>();
private int branches = 0;
/// <summary>
/// Checks if the state at frame in the given branch (-1 for current) has any duplicates.
/// </summary>
/// <returns>Returns the ID of the branch (-1 for current) of the first match. If no match, returns -2.</returns>
private int stateHasDuplicate(int frame, int branch)
{
tsmState stateToMatch;
if (branch == -1)
stateToMatch = States[frame];
else
{
stateToMatch = BranchStates[frame][branch];
if (States.ContainsKey(frame) && States[frame] == stateToMatch)
return -1;
}
for (int i = 0; i < branches; i++)
{
if (i == branch)
continue;
if (BranchStates.ContainsKey(frame))
{
SortedList<int, tsmState> stateList = BranchStates[frame];
if (stateList != null && stateList.ContainsKey(i) && stateList[i] == stateToMatch)
return i;
}
}
return -2;
}
private string statePath private string statePath
{ {
@ -180,7 +149,7 @@ namespace BizHawk.Client.Common
} }
private int maxStates private int maxStates
{ get { return (int)(Settings.Cap / _expectedStateSize); } } { get { return (int)(Settings.Cap / _expectedStateSize) + (int)((ulong)Settings.DiskCapacitymb * 1024 * 1024 / _expectedStateSize); } }
public TasStateManager(TasMovie movie) public TasStateManager(TasMovie movie)
{ {
@ -188,7 +157,7 @@ namespace BizHawk.Client.Common
Settings = new TasStateManagerSettings(Global.Config.DefaultTasProjSettings); Settings = new TasStateManagerSettings(Global.Config.DefaultTasProjSettings);
accessed = new List<int>(); accessed = new List<tsmState>();
} }
public void Dispose() public void Dispose()
@ -250,7 +219,7 @@ namespace BizHawk.Client.Common
return new KeyValuePair<int, byte[]>(-1, new byte[0]); return new KeyValuePair<int, byte[]>(-1, new byte[0]);
} }
} }
private List<int> accessed; private List<tsmState> accessed;
public byte[] InitialState public byte[] InitialState
{ {
@ -313,12 +282,9 @@ namespace BizHawk.Client.Common
if (Used > Settings.Cap) if (Used > Settings.Cap)
{ {
if (DiskUsed > (ulong)Settings.DiskCapacitymb * 1024uL * 1024uL)
MaybeRemoveState();
int lastMemState = -1; int lastMemState = -1;
do { lastMemState++; } while (States[accessed[lastMemState]] == null); do { lastMemState++; } while (States[accessed[lastMemState].Frame] == null);
MoveStateToDisk(accessed[lastMemState]); MoveStateToDisk(accessed[lastMemState].Frame);
} }
} }
private int StateToRemove() private int StateToRemove()
@ -326,35 +292,30 @@ namespace BizHawk.Client.Common
int markerSkips = maxStates / 3; int markerSkips = maxStates / 3;
int shouldRemove = _movie.StartsFromSavestate ? -1 : 0; int shouldRemove = _movie.StartsFromSavestate ? -1 : 0;
//int minFrames = 999;
do do
{ {
shouldRemove++; shouldRemove++;
// No need to have two savestates with only lag frames between them: //// No need to have two savestates with only lag frames between them:
// zero 05-aug-2015 - changed algorithm to iterate through States (a SortedList) instead of repeatedly call ElementAt (which is slow) //for (int i = shouldRemove + 1; i < States.Count; i++)
// previously : for (int i = shouldRemove; i < States.Count - 1; i++) if (AllLag(States.ElementAt(i).Key, States.ElementAt(i + 1).Key)) { shouldRemove = i; break; } } //{
int ctr = 0; // if (AllLag(States.Keys[i - 1], States.Keys[i]))
KeyValuePair<int, tsmState>? prior = null; // {
foreach (var kvp in States) // shouldRemove = i - 1;
{ // break;
ctr++; // }
if (ctr < shouldRemove) //}
{
prior = kvp;
continue;
}
if (prior.HasValue) //// Find states with the fewest frames between them
{ //for (int i = shouldRemove + 1; i < States.Count; i++)
if (AllLag(prior.Value.Key, kvp.Key)) //{
{ // if (States.Keys[i] - States.Keys[i - 1] < minFrames)
shouldRemove = ctr - 1; // {
break; // minFrames = States.Keys[i] - States.Keys[i - 1];
} // shouldRemove = i;
} // }
//}
prior = kvp;
}
// Keep marker states // Keep marker states
markerSkips--; markerSkips--;
@ -405,39 +366,50 @@ namespace BizHawk.Client.Common
else else
{ {
Used += (ulong)state.Length; Used += (ulong)state.Length;
States.Add(frame, new tsmState(this, state)); States.Add(frame, new tsmState(this, state, frame));
} }
StateAccessed(frame); StateAccessed(frame);
} }
private void RemoveState(int frame) private void RemoveState(int frame, int branch = -1)
{
if (branch == -1)
accessed.Remove(States[frame]);
else
accessed.Remove(BranchStates[frame][branch]);
if (branch == -1)
{ {
if (States[frame].IsOnDisk) if (States[frame].IsOnDisk)
{
States[frame].Dispose(); States[frame].Dispose();
}
else else
Used -= (ulong)States[frame].Length; Used -= (ulong)States[frame].Length;
States.RemoveAt(States.IndexOfKey(frame)); States.RemoveAt(States.IndexOfKey(frame));
accessed.Remove(frame);
} }
private void StateAccessed(int index) else
{ {
if (index == 0 && _movie.StartsFromSavestate) if (BranchStates[frame][branch].IsOnDisk)
BranchStates[frame][branch].Dispose();
BranchStates[frame].RemoveAt(BranchStates[frame].IndexOfKey(branch));
}
}
private void StateAccessed(int frame)
{
if (frame == 0 && _movie.StartsFromSavestate)
return; return;
bool removed = accessed.Remove(index); tsmState state = States[frame];
accessed.Add(index); bool removed = accessed.Remove(state);
accessed.Add(state);
if (States[index].IsOnDisk) if (States[frame].IsOnDisk)
{ {
if (!States[accessed[0]].IsOnDisk) if (!States[accessed[0].Frame].IsOnDisk)
MoveStateToDisk(accessed[0]); MoveStateToDisk(accessed[0].Frame);
MoveStateToMemory(index); MoveStateToMemory(frame);
} }
if (!removed && accessed.Count > (int)(Used / _expectedStateSize)) if (!removed && accessed.Count > maxStates)
accessed.RemoveAt(0); accessed.RemoveAt(0);
} }
@ -465,24 +437,22 @@ namespace BizHawk.Client.Common
frame = 1; frame = 1;
} }
var statesToRemove = States List<KeyValuePair<int, tsmState>> statesToRemove =
.Where(x => x.Key >= frame) States.Where(x => x.Key >= frame).ToList();
.ToList();
anyInvalidated = statesToRemove.Any(); anyInvalidated = statesToRemove.Any();
foreach (var state in statesToRemove) foreach (KeyValuePair<int, tsmState> state in statesToRemove)
{ RemoveState(state.Key);
if (state.Value.IsOnDisk)
{
state.Value.Dispose();
}
else
Used -= (ulong)state.Value.Length;
accessed.Remove(state.Key); // Why did I put this here? The branches aren't being edited/invalidated.
States.Remove(state.Key); //var bStateLists = BranchStates.Where(x => x.Key >= frame).ToList();
} //anyInvalidated = anyInvalidated | bStateLists.Any();
//foreach (KeyValuePair<int, SortedList<int, tsmState>> stateList in bStateLists)
//{
// for (int i = 0; i < stateList.Value.Count; i++)
// RemoveState(stateList.Key, stateList.Value.Keys[i]);
//}
CallInvalidateCallback(frame); CallInvalidateCallback(frame);
} }
@ -537,8 +507,23 @@ namespace BizHawk.Client.Common
/// </summary> /// </summary>
public void LimitStateCount() public void LimitStateCount()
{ {
while (Used + DiskUsed > Settings.CapTotal) //while (Used + DiskUsed > Settings.CapTotal)
MaybeRemoveState(); // RemoveState(States.ElementAt(StateToRemove()).Key);
//int index = -1;
//while (DiskUsed > (ulong)Settings.DiskCapacitymb * 1024uL * 1024uL)
//{
// do { index++; } while (!States[accessed[index]].IsOnDisk);
// States[
//}
//if (Used > Settings.Cap)
//{
// int lastMemState = -1;
// do { lastMemState++; } while (States[accessed[lastMemState]] == null);
// MoveStateToDisk(accessed[lastMemState]);
//}
} }
// TODO: save/load BranchStates // TODO: save/load BranchStates
@ -685,6 +670,42 @@ namespace BizHawk.Client.Common
#region "Branches" #region "Branches"
private SortedList<int, SortedList<int, tsmState>> BranchStates = new SortedList<int, SortedList<int, tsmState>>();
private int branches = 0;
private int currentBranch = -1;
/// <summary>
/// Checks if the state at frame in the given branch (-1 for current) has any duplicates.
/// </summary>
/// <returns>Returns the ID of the branch (-1 for current) of the first match. If no match, returns -2.</returns>
private int stateHasDuplicate(int frame, int branch)
{
tsmState stateToMatch;
if (branch == -1)
stateToMatch = States[frame];
else
{
stateToMatch = BranchStates[frame][branch];
if (States.ContainsKey(frame) && States[frame] == stateToMatch)
return -1;
}
for (int i = 0; i < branches; i++)
{
if (i == branch)
continue;
if (BranchStates.ContainsKey(frame))
{
SortedList<int, tsmState> stateList = BranchStates[frame];
if (stateList != null && stateList.ContainsKey(i) && stateList[i] == stateToMatch)
return i;
}
}
return -2;
}
public void AddBranch() public void AddBranch()
{ {
foreach (KeyValuePair<int, tsmState> kvp in States) foreach (KeyValuePair<int, tsmState> kvp in States)
@ -700,6 +721,7 @@ namespace BizHawk.Client.Common
stateList.Add(branches, kvp.Value); stateList.Add(branches, kvp.Value);
} }
branches++; branches++;
currentBranch = branches;
} }
public void RemoveBranch(int index) public void RemoveBranch(int index)
@ -723,6 +745,8 @@ namespace BizHawk.Client.Common
BranchStates[kvp.Key] = null; BranchStates[kvp.Key] = null;
} }
branches--; branches--;
if (currentBranch <= branches)
currentBranch = -1;
} }
public void UpdateBranch(int index) public void UpdateBranch(int index)
@ -760,6 +784,8 @@ namespace BizHawk.Client.Common
} }
stateList.Add(index, kvp.Value); stateList.Add(index, kvp.Value);
} }
currentBranch = index;
} }
public void LoadBranch(int index) public void LoadBranch(int index)
@ -773,6 +799,8 @@ namespace BizHawk.Client.Common
if (kvp.Value.ContainsKey(index)) if (kvp.Value.ContainsKey(index))
SetState(kvp.Key, kvp.Value[index].State); SetState(kvp.Key, kvp.Value[index].State);
} }
currentBranch = index;
} }
#endregion #endregion

View File

@ -747,7 +747,7 @@ namespace BizHawk.Client.EmuHawk
Owner = GlobalWin.MainForm, Owner = GlobalWin.MainForm,
Location = this.ChildPointToScreen(TasView), Location = this.ChildPointToScreen(TasView),
Statable = this.StatableEmulator Statable = this.StatableEmulator
}.Show(); }.ShowDialog();
CurrentTasMovie.TasStateManager.LimitStateCount(); CurrentTasMovie.TasStateManager.LimitStateCount();
UpdateChangesIndicator(); UpdateChangesIndicator();
} }