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>
Defines the horizon of the rewind buffer. A large horizon allows
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
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>-plr.rewind.horizon<br>-dev.rewind.horizon</td>
</tr>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -144,6 +144,11 @@ class FrameBuffer
*/
void enableMessages(bool enable);
/**
Reset 'Paused' display delay counter
*/
void resetPauseDelay();
/**
Allocate a new surface. The FrameBuffer class takes all responsibility
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");
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);
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");
if(i < 1 || i > 20) setInternal("plr.tv.jitter_recovery", "10");

View File

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