1 #include "stdafx.h"
2 #include "RewindManager.h"
3 #include "MessageManager.h"
4 #include "Console.h"
5 #include "VideoRenderer.h"
6 #include "SoundMixer.h"
7 #include "BaseControlDevice.h"
8 #include "HistoryViewer.h"
9
RewindManager(shared_ptr<Console> console)10 RewindManager::RewindManager(shared_ptr<Console> console)
11 {
12 _console = console;
13 _settings = console->GetSettings();
14 _rewindState = RewindState::Stopped;
15 _framesToFastForward = 0;
16 _hasHistory = false;
17 AddHistoryBlock();
18
19 _console->GetControlManager()->RegisterInputProvider(this);
20 _console->GetControlManager()->RegisterInputRecorder(this);
21 }
22
~RewindManager()23 RewindManager::~RewindManager()
24 {
25 _console->GetControlManager()->UnregisterInputProvider(this);
26 _console->GetControlManager()->UnregisterInputRecorder(this);
27 }
28
ClearBuffer()29 void RewindManager::ClearBuffer()
30 {
31 _hasHistory = false;
32 _history.clear();
33 _historyBackup.clear();
34 _currentHistory = RewindData();
35 _framesToFastForward = 0;
36 _videoHistory.clear();
37 _videoHistoryBuilder.clear();
38 _audioHistory.clear();
39 _audioHistoryBuilder.clear();
40 _rewindState = RewindState::Stopped;
41 _currentHistory = RewindData();
42 }
43
ProcessNotification(ConsoleNotificationType type,void * parameter)44 void RewindManager::ProcessNotification(ConsoleNotificationType type, void * parameter)
45 {
46 if(type == ConsoleNotificationType::PpuFrameDone) {
47 _hasHistory = _history.size() >= 2;
48 if(_settings->GetRewindBufferSize() > 0) {
49 switch(_rewindState) {
50 case RewindState::Starting:
51 case RewindState::Started:
52 case RewindState::Debugging:
53 _currentHistory.FrameCount--;
54 break;
55
56 case RewindState::Stopping:
57 _framesToFastForward--;
58 _currentHistory.FrameCount++;
59 if(_framesToFastForward == 0) {
60 for(int i = 0; i < 4; i++) {
61 size_t numberToRemove = _currentHistory.InputLogs[i].size();
62 _currentHistory.InputLogs[i] = _historyBackup.front().InputLogs[i];
63 for(size_t j = 0; j < numberToRemove; j++) {
64 _currentHistory.InputLogs[i].pop_back();
65 }
66 }
67 _historyBackup.clear();
68 _rewindState = RewindState::Stopped;
69 _settings->ClearFlags(EmulationFlags::Rewind);
70 _settings->ClearFlags(EmulationFlags::ForceMaxSpeed);
71 }
72 break;
73
74 case RewindState::Stopped:
75 _currentHistory.FrameCount++;
76 break;
77 }
78 } else {
79 ClearBuffer();
80 }
81 } else if(type == ConsoleNotificationType::StateLoaded) {
82 if(_rewindState == RewindState::Stopped) {
83 //A save state was loaded by the user, mark as the end of the current "segment" (for history viewer)
84 _currentHistory.EndOfSegment = true;
85 }
86 }
87 }
88
AddHistoryBlock()89 void RewindManager::AddHistoryBlock()
90 {
91 uint32_t maxHistorySize = _settings->GetRewindBufferSize() * 120;
92 if(maxHistorySize > 0) {
93 while(_history.size() > maxHistorySize) {
94 _history.pop_front();
95 }
96
97 if(_currentHistory.FrameCount > 0) {
98 _history.push_back(_currentHistory);
99 }
100 _currentHistory = RewindData();
101 _currentHistory.SaveState(_console);
102 }
103 }
104
PopHistory()105 void RewindManager::PopHistory()
106 {
107 if(_history.empty() && _currentHistory.FrameCount <= 0) {
108 StopRewinding();
109 } else {
110 if(_currentHistory.FrameCount <= 0) {
111 _currentHistory = _history.back();
112 _history.pop_back();
113 }
114
115 _historyBackup.push_front(_currentHistory);
116 _currentHistory.LoadState(_console);
117 if(!_audioHistoryBuilder.empty()) {
118 _audioHistory.insert(_audioHistory.begin(), _audioHistoryBuilder.begin(), _audioHistoryBuilder.end());
119 _audioHistoryBuilder.clear();
120 }
121 }
122 }
123
Start(bool forDebugger)124 void RewindManager::Start(bool forDebugger)
125 {
126 if(_rewindState == RewindState::Stopped && _settings->GetRewindBufferSize() > 0) {
127 _console->Pause();
128
129 _rewindState = forDebugger ? RewindState::Debugging : RewindState::Starting;
130 _videoHistoryBuilder.clear();
131 _videoHistory.clear();
132 _audioHistoryBuilder.clear();
133 _audioHistory.clear();
134 _historyBackup.clear();
135
136 PopHistory();
137 _console->GetSoundMixer()->StopAudio(true);
138 _settings->SetFlags(EmulationFlags::ForceMaxSpeed);
139 _settings->SetFlags(EmulationFlags::Rewind);
140
141 _console->Resume();
142 }
143 }
144
ForceStop()145 void RewindManager::ForceStop()
146 {
147 if(_rewindState != RewindState::Stopped) {
148 while(_historyBackup.size() > 1) {
149 _history.push_back(_historyBackup.front());
150 _historyBackup.pop_front();
151 }
152 _currentHistory = _historyBackup.front();
153 _historyBackup.clear();
154 _rewindState = RewindState::Stopped;
155 _settings->ClearFlags(EmulationFlags::ForceMaxSpeed);
156 _settings->ClearFlags(EmulationFlags::Rewind);
157 }
158 }
159
Stop()160 void RewindManager::Stop()
161 {
162 if(_rewindState >= RewindState::Starting) {
163 _console->Pause();
164 if(_rewindState == RewindState::Started) {
165 //Move back to the save state containing the frame currently shown on the screen
166 if(_historyBackup.size() > 1) {
167 _framesToFastForward = (uint32_t)_videoHistory.size() + _historyBackup.front().FrameCount;
168 do {
169 _history.push_back(_historyBackup.front());
170 _framesToFastForward -= _historyBackup.front().FrameCount;
171 _historyBackup.pop_front();
172
173 _currentHistory = _historyBackup.front();
174 }
175 while(_framesToFastForward > RewindManager::BufferSize && _historyBackup.size() > 1);
176 }
177 } else {
178 //We started rewinding, but didn't actually visually rewind anything yet
179 //Move back to the save state containing the frame currently shown on the screen
180 while(_historyBackup.size() > 1) {
181 _history.push_back(_historyBackup.front());
182 _historyBackup.pop_front();
183 }
184 _currentHistory = _historyBackup.front();
185 _framesToFastForward = _historyBackup.front().FrameCount;
186 }
187
188 _currentHistory.LoadState(_console);
189 if(_framesToFastForward > 0) {
190 _rewindState = RewindState::Stopping;
191 _currentHistory.FrameCount = 0;
192 _settings->SetFlags(EmulationFlags::ForceMaxSpeed);
193 } else {
194 _rewindState = RewindState::Stopped;
195 _historyBackup.clear();
196 _settings->ClearFlags(EmulationFlags::ForceMaxSpeed);
197 _settings->ClearFlags(EmulationFlags::Rewind);
198 }
199
200 _videoHistoryBuilder.clear();
201 _videoHistory.clear();
202 _audioHistoryBuilder.clear();
203 _audioHistory.clear();
204
205 _console->Resume();
206 }
207 }
208
ProcessEndOfFrame()209 void RewindManager::ProcessEndOfFrame()
210 {
211 if(_rewindState >= RewindState::Starting) {
212 if(_currentHistory.FrameCount <= 0 && _rewindState != RewindState::Debugging) {
213 //If we're debugging, we want to keep running the emulation to the end of the next frame (even if it's incomplete)
214 //Otherwise the emulation might diverge due to missing inputs.
215 PopHistory();
216 }
217 } else if(_currentHistory.FrameCount >= RewindManager::BufferSize) {
218 AddHistoryBlock();
219 }
220 }
221
ProcessFrame(void * frameBuffer,uint32_t width,uint32_t height,bool forRewind)222 void RewindManager::ProcessFrame(void * frameBuffer, uint32_t width, uint32_t height, bool forRewind)
223 {
224 if(_rewindState == RewindState::Starting || _rewindState == RewindState::Started) {
225 if(!forRewind) {
226 //Ignore any frames that occur between start of rewind process & first rewinded frame completed
227 //These are caused by the fact that VideoDecoder is asynchronous - a previous (extra) frame can end up
228 //in the rewind queue, which causes display glitches
229 return;
230 }
231
232 _videoHistoryBuilder.push_back(vector<uint32_t>((uint32_t*)frameBuffer, (uint32_t*)frameBuffer + width*height));
233
234 if(_videoHistoryBuilder.size() == (size_t)_historyBackup.front().FrameCount) {
235 for(int i = (int)_videoHistoryBuilder.size() - 1; i >= 0; i--) {
236 _videoHistory.push_front(_videoHistoryBuilder[i]);
237 }
238 _videoHistoryBuilder.clear();
239 }
240
241 if(_rewindState == RewindState::Started || _videoHistory.size() >= RewindManager::BufferSize) {
242 _rewindState = RewindState::Started;
243 _settings->ClearFlags(EmulationFlags::ForceMaxSpeed);
244 if(!_videoHistory.empty()) {
245 _console->GetVideoRenderer()->UpdateFrame(_videoHistory.back().data(), width, height);
246 _videoHistory.pop_back();
247 }
248 }
249 } else if(_rewindState == RewindState::Stopping || _rewindState == RewindState::Debugging) {
250 //Display nothing while resyncing
251 } else {
252 _console->GetVideoRenderer()->UpdateFrame(frameBuffer, width, height);
253 }
254 }
255
ProcessAudio(int16_t * soundBuffer,uint32_t sampleCount,uint32_t sampleRate)256 bool RewindManager::ProcessAudio(int16_t * soundBuffer, uint32_t sampleCount, uint32_t sampleRate)
257 {
258 if(_rewindState == RewindState::Starting || _rewindState == RewindState::Started) {
259 _audioHistoryBuilder.insert(_audioHistoryBuilder.end(), soundBuffer, soundBuffer + sampleCount * 2);
260
261 if(_rewindState == RewindState::Started && _audioHistory.size() > sampleCount * 2) {
262 for(uint32_t i = 0; i < sampleCount * 2; i++) {
263 soundBuffer[i] = _audioHistory.back();
264 _audioHistory.pop_back();
265 }
266
267 return true;
268 } else {
269 //Mute while we prepare to rewind
270 return false;
271 }
272 } else if(_rewindState == RewindState::Stopping || _rewindState == RewindState::Debugging) {
273 //Mute while we resync
274 return false;
275 } else {
276 return true;
277 }
278 }
279
RecordInput(vector<shared_ptr<BaseControlDevice>> devices)280 void RewindManager::RecordInput(vector<shared_ptr<BaseControlDevice>> devices)
281 {
282 if(_settings->GetRewindBufferSize() > 0 && _rewindState == RewindState::Stopped) {
283 for(shared_ptr<BaseControlDevice> &device : devices) {
284 _currentHistory.InputLogs[device->GetPort()].push_back(device->GetRawState());
285 }
286 }
287 }
288
SetInput(BaseControlDevice * device)289 bool RewindManager::SetInput(BaseControlDevice *device)
290 {
291 uint8_t port = device->GetPort();
292 if(!_currentHistory.InputLogs[port].empty() && IsRewinding()) {
293 ControlDeviceState state = _currentHistory.InputLogs[port].front();
294 _currentHistory.InputLogs[port].pop_front();
295 device->SetRawState(state);
296 return true;
297 } else {
298 return false;
299 }
300 }
301
StartRewinding(bool forDebugger)302 void RewindManager::StartRewinding(bool forDebugger)
303 {
304 Start(forDebugger);
305 }
306
StopRewinding(bool forDebugger)307 void RewindManager::StopRewinding(bool forDebugger)
308 {
309 if(forDebugger) {
310 ForceStop();
311 } else {
312 Stop();
313 }
314 }
315
IsRewinding()316 bool RewindManager::IsRewinding()
317 {
318 return _rewindState != RewindState::Stopped;
319 }
320
IsStepBack()321 bool RewindManager::IsStepBack()
322 {
323 return _rewindState == RewindState::Debugging;
324 }
325
RewindSeconds(uint32_t seconds)326 void RewindManager::RewindSeconds(uint32_t seconds)
327 {
328 if(_rewindState == RewindState::Stopped) {
329 uint32_t removeCount = (seconds * 60 / RewindManager::BufferSize) + 1;
330 _console->Pause();
331 for(uint32_t i = 0; i < removeCount; i++) {
332 if(!_history.empty()) {
333 _currentHistory = _history.back();
334 _history.pop_back();
335 } else {
336 break;
337 }
338 }
339 _currentHistory.LoadState(_console);
340 _console->Resume();
341 }
342 }
343
HasHistory()344 bool RewindManager::HasHistory()
345 {
346 return _hasHistory;
347 }
348
CopyHistory(shared_ptr<HistoryViewer> destHistoryViewer)349 void RewindManager::CopyHistory(shared_ptr<HistoryViewer> destHistoryViewer)
350 {
351 destHistoryViewer->SetHistoryData(_history);
352 }
353
SendFrame(void * frameBuffer,uint32_t width,uint32_t height,bool forRewind)354 void RewindManager::SendFrame(void * frameBuffer, uint32_t width, uint32_t height, bool forRewind)
355 {
356 ProcessFrame(frameBuffer, width, height, forRewind);
357 }
358
SendAudio(int16_t * soundBuffer,uint32_t sampleCount,uint32_t sampleRate)359 bool RewindManager::SendAudio(int16_t * soundBuffer, uint32_t sampleCount, uint32_t sampleRate)
360 {
361 return ProcessAudio(soundBuffer, sampleCount, sampleRate);
362 }
363