117 lines
2.5 KiB
C#
117 lines
2.5 KiB
C#
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Threading;
|
|
|
|
namespace BizHawk.Client.Common
|
|
{
|
|
public class RewindThreader : IDisposable
|
|
{
|
|
private readonly bool _isThreaded;
|
|
private readonly Action<byte[]> _performCapture;
|
|
private readonly Action<int> _performRewind;
|
|
private readonly BlockingCollection<Job> _jobs = new BlockingCollection<Job>(16);
|
|
private readonly ConcurrentStack<byte[]> _stateBufferPool = new ConcurrentStack<byte[]>();
|
|
private readonly EventWaitHandle _rewindCompletedEvent;
|
|
private readonly Thread _thread;
|
|
|
|
public RewindThreader(Action<byte[]> performCapture, Action<int> performRewind, bool isThreaded)
|
|
{
|
|
_isThreaded = isThreaded;
|
|
_performCapture = performCapture;
|
|
_performRewind = performRewind;
|
|
|
|
if (_isThreaded)
|
|
{
|
|
_rewindCompletedEvent = new EventWaitHandle(false, EventResetMode.AutoReset);
|
|
_thread = new Thread(ThreadProc) { IsBackground = true };
|
|
_thread.Start();
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (!_isThreaded)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_jobs.CompleteAdding();
|
|
_thread.Join();
|
|
_rewindCompletedEvent.Dispose();
|
|
}
|
|
|
|
public void Rewind(int frames)
|
|
{
|
|
if (!_isThreaded)
|
|
{
|
|
_performRewind(frames);
|
|
return;
|
|
}
|
|
|
|
_jobs.Add(new Job
|
|
{
|
|
Type = JobType.Rewind,
|
|
Frames = frames
|
|
});
|
|
_rewindCompletedEvent.WaitOne();
|
|
}
|
|
|
|
public void Capture(byte[] coreSavestate)
|
|
{
|
|
if (!_isThreaded)
|
|
{
|
|
_performCapture(coreSavestate);
|
|
return;
|
|
}
|
|
|
|
byte[] savestateCopy = null;
|
|
while (_stateBufferPool.TryPop(out savestateCopy) && savestateCopy.Length != coreSavestate.Length)
|
|
{
|
|
savestateCopy = null;
|
|
}
|
|
if (savestateCopy == null)
|
|
{
|
|
savestateCopy = new byte[coreSavestate.Length];
|
|
}
|
|
|
|
Buffer.BlockCopy(coreSavestate, 0, savestateCopy, 0, coreSavestate.Length);
|
|
|
|
_jobs.Add(new Job
|
|
{
|
|
Type = JobType.Capture,
|
|
CoreState = savestateCopy
|
|
});
|
|
}
|
|
|
|
private void ThreadProc()
|
|
{
|
|
foreach (Job job in _jobs.GetConsumingEnumerable())
|
|
{
|
|
if (job.Type == JobType.Capture)
|
|
{
|
|
_performCapture(job.CoreState);
|
|
_stateBufferPool.Push(job.CoreState);
|
|
}
|
|
|
|
if (job.Type == JobType.Rewind)
|
|
{
|
|
_performRewind(job.Frames);
|
|
_rewindCompletedEvent.Set();
|
|
}
|
|
}
|
|
}
|
|
|
|
private enum JobType
|
|
{
|
|
Capture, Rewind
|
|
}
|
|
|
|
private sealed class Job
|
|
{
|
|
public JobType Type { get; set; }
|
|
public byte[] CoreState { get; set; }
|
|
public int Frames { get; set; }
|
|
}
|
|
}
|
|
}
|