mirror of https://github.com/stella-emu/stella.git
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:
parent
20eb97ce14
commit
e2301dfd5e
|
@ -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>
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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).
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
Loading…
Reference in New Issue