fine tuning on interval factor and state compression

rewind now displayed immediately (instead of "Paused")
two initial rewinds in continuous rewind mode fixed
Horizon widget disabled when Uncompressed size == Buffer size
fixed minimum Interval
This commit is contained in:
thrust26 2017-12-13 19:15:09 +01:00
parent 20eb97ce14
commit e2301dfd5e
9 changed files with 53 additions and 26 deletions

View File

@ -3107,9 +3107,12 @@
<td> <td>
Defines the horizon of the rewind buffer. A large horizon allows Defines the horizon of the rewind buffer. A large horizon allows
going back further in time. To reach the horizon, save states going back further in time. To reach the horizon, save states
will be compressed. This means that more and more intermediate will be compressed (*). This means that more and more intermediate
states will be removed and the interval between save states states will be removed and the interval between save states
becomes larger the further they are back in time. becomes larger the further they are back in time. The very first
save state will not be removed.<br>
(*) Compresion only works if 'Uncompressed size' is smaller than
'Buffer size'.
</td> </td>
<td>-plr.rewind.horizon<br>-dev.rewind.horizon</td> <td>-plr.rewind.horizon<br>-dev.rewind.horizon</td>
</tr> </tr>

View File

@ -36,6 +36,8 @@ RewindManager::RewindManager(OSystem& system, StateManager& statemgr)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RewindManager::setup() void RewindManager::setup()
{ {
myLastContinuousAdd = false;
string prefix = myOSystem.settings().getBool("dev.settings") ? "dev." : "plr."; string prefix = myOSystem.settings().getBool("dev.settings") ? "dev." : "plr.";
mySize = myOSystem.settings().getInt(prefix + "rewind.size"); mySize = myOSystem.settings().getInt(prefix + "rewind.size");
@ -55,20 +57,22 @@ void RewindManager::setup()
myHorizon = HORIZON_CYCLES[i]; myHorizon = HORIZON_CYCLES[i];
// calc interval growth factor // calc interval growth factor
// this factor defines the backward horizon
const double MAX_FACTOR = 1E8; const double MAX_FACTOR = 1E8;
double minFactor = 0, maxFactor = MAX_FACTOR; double minFactor = 0, maxFactor = MAX_FACTOR;
myFactor = 1;
while(true) while(myUncompressed < mySize)
{ {
double interval = myInterval; double interval = myInterval;
double cycleSum = interval * myUncompressed; double cycleSum = interval * (myUncompressed + 1);
// calculate nextCycles factor // calculate nextCycles factor
myFactor = (minFactor + maxFactor) / 2; myFactor = (minFactor + maxFactor) / 2;
// horizon not reachable? // horizon not reachable?
if(myFactor == MAX_FACTOR) if(myFactor == MAX_FACTOR)
break; break;
// sum up interval cycles // sum up interval cycles (first and last state are not compressed)
for(uInt32 i = myUncompressed; i < mySize; ++i) for(uInt32 i = myUncompressed + 1; i < mySize - 1; ++i)
{ {
interval *= myFactor; interval *= myFactor;
cycleSum += interval; cycleSum += interval;
@ -119,6 +123,7 @@ bool RewindManager::addState(const string& message, bool continuous)
state.cycles = myOSystem.console().tia().cycles(); state.cycles = myOSystem.console().tia().cycles();
//state.count = count++; //state.count = count++;
//cerr << "add " << state.count << endl; //cerr << "add " << state.count << endl;
myLastContinuousAdd = continuous;
return true; return true;
} }
return false; return false;
@ -129,15 +134,17 @@ bool RewindManager::rewindState()
{ {
if(!atFirst()) if(!atFirst())
{ {
RewindState& lastState = myStateList.current(); if (!myLastContinuousAdd)
// Set internal current iterator to previous state (back in time), // Set internal current iterator to previous state (back in time),
// since we will now processed this state // since we will now processed this state
myStateList.moveToPrevious(); myStateList.moveToPrevious();
myLastContinuousAdd = false;
//RewindState& lastState = myStateList.current();
RewindState& state = myStateList.current(); RewindState& state = myStateList.current();
Serializer& s = state.data; Serializer& s = state.data;
string message = getMessage(state, lastState); string message = getMessage(state);
//cerr << "rewind " << state.count << endl; //cerr << "rewind " << state.count << endl;
s.rewind(); // rewind Serializer internal buffers s.rewind(); // rewind Serializer internal buffers
@ -163,7 +170,7 @@ bool RewindManager::unwindState()
RewindState& state = myStateList.current(); RewindState& state = myStateList.current();
Serializer& s = state.data; Serializer& s = state.data;
string message = getMessage(state, state); string message = getMessage(state);
//cerr << "unwind " << state.count << endl; //cerr << "unwind " << state.count << endl;
s.rewind(); // rewind Serializer internal buffers s.rewind(); // rewind Serializer internal buffers
@ -182,8 +189,8 @@ bool RewindManager::unwindState()
void RewindManager::compressStates() void RewindManager::compressStates()
{ {
uInt64 currentCycles = myOSystem.console().tia().cycles(); uInt64 currentCycles = myOSystem.console().tia().cycles();
double expectedCycles = myInterval * (1*0 + myFactor); double expectedCycles = myInterval * myFactor * (1 + myFactor);
double maxError = 0; double maxError = 1;
uInt32 removeIdx = 0; uInt32 removeIdx = 0;
uInt32 idx = myStateList.size() - 2; uInt32 idx = myStateList.size() - 2;
@ -223,17 +230,18 @@ void RewindManager::compressStates()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string RewindManager::getMessage(RewindState& state, RewindState& lastState) string RewindManager::getMessage(RewindState& state)
{ {
Int64 diff = myOSystem.console().tia().cycles() - state.cycles; Int64 diff = myOSystem.console().tia().cycles() - state.cycles;
stringstream message; stringstream message;
message << (diff >= 0 ? "Rewind" : "Unwind") << " " << getUnitString(diff); message << (diff >= 0 ? "Rewind" : "Unwind") << " " << getUnitString(diff);
// add optional message (TODO: when smart removal works, we have to do something smart with this part too)
if(!lastState.message.empty())
message << " (" << lastState.message << ")";
message << " [" << myStateList.currentIdx() << "/" << myStateList.size() << "]"; message << " [" << myStateList.currentIdx() << "/" << myStateList.size() << "]";
// add optional message
if(!state.message.empty())
message << " (" << state.message << ")";
return message.str(); return message.str();
} }

View File

@ -144,6 +144,7 @@ class RewindManager
uInt32 myInterval; uInt32 myInterval;
uInt64 myHorizon; uInt64 myHorizon;
double myFactor; double myFactor;
bool myLastContinuousAdd;
struct RewindState { struct RewindState {
Serializer data; Serializer data;
@ -168,7 +169,7 @@ class RewindManager
void compressStates(); void compressStates();
string getMessage(RewindState& state, RewindState& lastState); string getMessage(RewindState& state);
private: private:
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported

View File

@ -161,7 +161,7 @@ void StateManager::update()
switch(myActiveMode) switch(myActiveMode)
{ {
case Mode::Rewind: case Mode::Rewind:
myRewindManager->addState("1 frame", true); myRewindManager->addState("continuous rewind", true);
break; break;
#if 0 #if 0

View File

@ -299,13 +299,15 @@ void EventHandler::handleKeyEvent(StellaKey key, StellaMod mod, bool state)
switch(key) switch(key)
{ {
case KBDK_LEFT: // Alt-left rewinds states case KBDK_LEFT: // Alt-left rewinds states
if(myOSystem.state().rewindState() && myState != S_PAUSE) myOSystem.frameBuffer().resetPauseDelay();
setEventState(S_PAUSE); setEventState(S_PAUSE);
myOSystem.state().rewindState();
break; break;
case KBDK_RIGHT: // Alt-right unwinds states case KBDK_RIGHT: // Alt-right unwinds states
if(myOSystem.state().unwindState() && myState != S_PAUSE) myOSystem.frameBuffer().resetPauseDelay();
setEventState(S_PAUSE); setEventState(S_PAUSE);
myOSystem.state().unwindState();
break; break;
default: default:

View File

@ -301,7 +301,7 @@ void FrameBuffer::update()
// Show a pause message immediately and then every 7 seconds // Show a pause message immediately and then every 7 seconds
if (myPausedCount-- <= 0) if (myPausedCount-- <= 0)
{ {
myPausedCount = uInt32(7 * myOSystem.frameRate()); resetPauseDelay();
showMessage("Paused", kMiddleCenter); showMessage("Paused", kMiddleCenter);
} }
break; // S_PAUSE break; // S_PAUSE
@ -471,6 +471,12 @@ inline void FrameBuffer::drawMessage()
myMsg.enabled = false; myMsg.enabled = false;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::resetPauseDelay()
{
myPausedCount = uInt32(7 * myOSystem.frameRate());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
shared_ptr<FBSurface> FrameBuffer::allocateSurface(int w, int h, const uInt32* data) shared_ptr<FBSurface> FrameBuffer::allocateSurface(int w, int h, const uInt32* data)
{ {

View File

@ -144,6 +144,11 @@ class FrameBuffer
*/ */
void enableMessages(bool enable); void enableMessages(bool enable);
/**
Reset 'Paused' display delay counter
*/
void resetPauseDelay();
/** /**
Allocate a new surface. The FrameBuffer class takes all responsibility Allocate a new surface. The FrameBuffer class takes all responsibility
for freeing this surface (ie, other classes must not delete it directly). for freeing this surface (ie, other classes must not delete it directly).

View File

@ -329,11 +329,11 @@ void Settings::validate()
i = getInt("dev.rewind.uncompressed"); i = getInt("dev.rewind.uncompressed");
if(i < 0 || i > size) setInternal("dev.rewind.uncompressed", size); if(i < 0 || i > size) setInternal("dev.rewind.uncompressed", size);
i = getInt("dev.rewind.interval"); /*i = getInt("dev.rewind.interval");
if(i < 0 || i > 5) setInternal("dev.rewind.interval", 0); if(i < 0 || i > 5) setInternal("dev.rewind.interval", 0);
i = getInt("dev.rewind.horizon"); i = getInt("dev.rewind.horizon");
if(i < 0 || i > 6) setInternal("dev.rewind.horizon", 1); if(i < 0 || i > 6) setInternal("dev.rewind.horizon", 1);*/
i = getInt("plr.tv.jitter_recovery"); i = getInt("plr.tv.jitter_recovery");
if(i < 1 || i > 20) setInternal("plr.tv.jitter_recovery", "10"); if(i < 1 || i > 20) setInternal("plr.tv.jitter_recovery", "10");

View File

@ -999,6 +999,7 @@ void DeveloperDialog::handleSize()
myUncompressedWidget->setValue(size); myUncompressedWidget->setValue(size);
myStateIntervalWidget->setSelectedIndex(interval); myStateIntervalWidget->setSelectedIndex(interval);
myStateHorizonWidget->setSelectedIndex(i); myStateHorizonWidget->setSelectedIndex(i);
myStateHorizonWidget->setEnabled(size > uncompressed);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1011,6 +1012,7 @@ void DeveloperDialog::handleUncompressed()
if(size < uncompressed) if(size < uncompressed)
myStateSizeWidget->setValue(uncompressed); myStateSizeWidget->setValue(uncompressed);
myStateHorizonWidget->setEnabled(size > uncompressed);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -