rewind/unwind basically working

changed values, UI and settings for rewind interval and horizon
This commit is contained in:
thrust26 2017-12-12 17:07:05 +01:00
parent 517cc82baa
commit e465112c02
8 changed files with 302 additions and 157 deletions

View File

@ -172,7 +172,7 @@ class LinkedObjectPool
myPool.splice(myPool.end(), myList, myPool.splice(myPool.end(), myList,
offset >= 0 ? std::next(i, offset) : std::prev(i, -offset)); offset >= 0 ? std::next(i, offset) : std::prev(i, -offset));
} }
#endif
/** /**
Convenience method to remove a single element from the active list by Convenience method to remove a single element from the active list by
index, offset from the beginning of the list. (ie, '0' means first index, offset from the beginning of the list. (ie, '0' means first
@ -181,7 +181,7 @@ class LinkedObjectPool
void remove(uInt32 index) { void remove(uInt32 index) {
myPool.splice(myPool.end(), myList, std::next(myList.begin(), index)); myPool.splice(myPool.end(), myList, std::next(myList.begin(), index));
} }
#endif
/** /**
Remove range of elements from the beginning of the active list to Remove range of elements from the beginning of the active list to

View File

@ -30,11 +30,80 @@ RewindManager::RewindManager(OSystem& system, StateManager& statemgr)
: myOSystem(system), : myOSystem(system),
myStateManager(statemgr) myStateManager(statemgr)
{ {
setup();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool RewindManager::addState(const string& message) void RewindManager::setup()
{ {
/*static const int NUM_INTERVALS = 6;
// TODO: check for intervals shorter than 1 frame (adjust horizon too!)
const uInt32 INTERVAL_CYCLES[NUM_INTERVALS] = { 76, 76 * 50, 76 * 262, 76 * 262 * 10,
76 * 262 * 60, 76 * 262 * 60 * 10 };
static const int NUM_HORIZONS = 7;
const uInt64 HORIZON_CYCLES[NUM_HORIZONS] = { 76 * 262, 76 * 262 * 10, 76 * 262 * 60, 76 * 262 * 60 * 10,
76 * 262 * 60 * 60, 76 * 262 * 60 * 60 * 10, uInt64(76) * 262 * 60 * 60 * 60 };*/
bool devSettings = myOSystem.settings().getBool("dev.settings");
string prefix = devSettings ? "dev." : "plr.";
mySize = MAX_SIZE; // myOSystem.settings().getInt(prefix + "rewind.size");
myUncompressed = MAX_SIZE / 4; // myOSystem.settings().getInt(prefix + "rewind.uncompressed");
myInterval = INTERVAL_CYCLES[0];
for(int i = 0; i < NUM_INTERVALS; ++i)
if(INT_SETTINGS[i] == myOSystem.settings().getString(prefix + "rewind.interval"))
myInterval = INTERVAL_CYCLES[i];
myHorizon = HORIZON_CYCLES[NUM_HORIZONS-1];
for(int i = 0; i < NUM_HORIZONS; ++i)
if(HOR_SETTINGS[i] == myOSystem.settings().getString(prefix + "rewind.horizon"))
myHorizon = HORIZON_CYCLES[i];
// calc interval growth factor
const double MAX_FACTOR = 1E8;
double minFactor = 1, maxFactor = MAX_FACTOR;
while(true)
{
double interval = myInterval;
double cycleSum = interval * myUncompressed;
// calculate next factor
myFactor = (minFactor + maxFactor) / 2;
// horizon not reachable?
if(myFactor == MAX_FACTOR)
break;
// sum up interval cycles
for(uInt32 i = myUncompressed; i < mySize; ++i)
{
interval *= myFactor;
cycleSum += interval;
}
double diff = cycleSum - myHorizon;
// exit loop if result is close enough
if(std::abs(diff) < myHorizon * 1E-5)
break;
// define new boundary
if(cycleSum < myHorizon)
minFactor = myFactor;
else
maxFactor = myFactor;
}
cerr << "factor " << myFactor << endl;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool RewindManager::addState(const string& message, bool continuous)
{
// only check for continuous rewind states, ignore for debugger
if(continuous)
{
// check if the current state has the right interval from the last state
RewindState& lastState = myStateList.current();
if(myOSystem.console().tia().cycles() - lastState.cycle < myInterval)
return false;
}
// Remove all future states // Remove all future states
myStateList.removeToLast(); myStateList.removeToLast();
@ -63,8 +132,12 @@ cerr << "add " << state.count << endl;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool RewindManager::rewindState() bool RewindManager::rewindState()
{ {
if(myStateList.currentIsValid()) if(!atFirst())
{ {
// Set internal current iterator to previous state (back in time),
// since we will now processed this state
myStateList.moveToPrevious();
RewindState& state = myStateList.current(); RewindState& state = myStateList.current();
Serializer& s = state.data; Serializer& s = state.data;
string message = getMessage(state); string message = getMessage(state);
@ -76,72 +149,65 @@ cerr << "rewind " << state.count << endl;
// Show message indicating the rewind state // Show message indicating the rewind state
myOSystem.frameBuffer().showMessage(message); myOSystem.frameBuffer().showMessage(message);
// Set internal current iterator to previous state (back in time),
// since we've now processed this state
myStateList.moveToPrevious();
return true; return true;
} }
else myOSystem.frameBuffer().showMessage("Rewind not possible");
return false; return false;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool RewindManager::unwindState() bool RewindManager::unwindState()
{ {
#if 0 if(!atLast())
if(!atFirst()) // or last???
{ {
// TODO: get state next to the current state // Set internal current iterator to next state (forward in time),
/*RewindState& state = myStateList.???() // since we've now processed this state
myStateList.moveToNext();
RewindState& state = myStateList.current();
Serializer& s = state.data; Serializer& s = state.data;
string message = getMessage(state); string message = getMessage(state);
cerr << "unwind " << state.count << endl;
s.reset(); // rewind Serializer internal buffers s.rewind(); // rewind Serializer internal buffers
myStateManager.loadState(s); myStateManager.loadState(s);
myOSystem.console().tia().loadDisplay(s); myOSystem.console().tia().loadDisplay(s);
// Show message indicating the rewind state // Show message indicating the rewind state
myOSystem.frameBuffer().showMessage(message);*/ myOSystem.frameBuffer().showMessage(message);
return true; return true;
} }
#endif myOSystem.frameBuffer().showMessage("Unwind not possible");
return false; return false;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RewindManager::compressStates() void RewindManager::compressStates()
{ {
#if 0
myStateList.removeFirst(); // remove the oldest state file
#else
//bool debugMode = myOSystem.eventHandler().state() == EventHandler::S_DEBUGGER; //bool debugMode = myOSystem.eventHandler().state() == EventHandler::S_DEBUGGER;
// TODO: let user control these:
const double DENSITY = 1.15; // exponential growth of cycle intervals
const uInt32 STEP_STATES = 6; // single step rewind length (change back to '60')
//const uInt32 SECONDS_STATES = 10; // TODO: one second rewind length
uInt64 currentCycle = myOSystem.console().tia().cycles(); uInt64 currentCycle = myOSystem.console().tia().cycles();
uInt64 lastCycle = currentCycle; uInt64 lastCycle = currentCycle;
double expectedCycles = 76 * 262.0; // == cycles of 1 frame, TODO: use actual number of scanlines double expectedCycles = myInterval; // == cycles of 1 frame, TODO: use actual number of scanlines
double maxDelta = 0; double maxDelta = 0;
uInt32 removeIdx = 0; uInt32 removeIdx = 0;
uInt32 idx = myStateList.size() - 1; uInt32 idx = myStateList.size() - 1;
cerr << "idx: " << idx << endl; //cerr << "idx: " << idx << endl;
for(auto it = myStateList.last(); it != myStateList.first(); --it) for(auto it = myStateList.last(); it != myStateList.first(); --it)
{ {
if(idx >= STEP_STATES) if(idx < mySize - myUncompressed)
{ {
cerr << *it << endl << endl; // debug code //cerr << *it << endl << endl; // debug code
expectedCycles *= DENSITY; expectedCycles *= myFactor;
double expected = expectedCycles * (1 + DENSITY); double expected = expectedCycles * (1 + myFactor);
uInt64 prev = myStateList.previous(it)->cycle; uInt64 prev = myStateList.previous(it)->cycle;
uInt64 next = myStateList.next(it)->cycle; uInt64 next = myStateList.next(it)->cycle;
double delta = expected / (prev - next); if(next != lastCycle)
cerr << "prev: " << prev << ", next: " << next << ", delta: " << delta << endl; lastCycle++;
double delta = expected / (next - prev);
//cerr << "prev: " << prev << ", next: " << next << ", delta: " << delta << endl;
if(delta > maxDelta) if(delta > maxDelta)
{ {
@ -152,17 +218,17 @@ cerr << "prev: " << prev << ", next: " << next << ", delta: " << delta << endl;
lastCycle = it->cycle; lastCycle = it->cycle;
--idx; --idx;
} }
cerr << "END\n";
if (maxDelta < 1) if (maxDelta < 1)
{ {
// the horizon is getting too big // the horizon is getting too big
//myStateList.remove(idx - 1); // remove oldest but one myStateList.remove(1); // remove oldest but one
cerr << "remove oldest + 1" << endl;
} }
else else
{ {
//myStateList.remove(removeIdx); // remove myStateList.remove(removeIdx); // remove
cerr << "remove " << removeIdx << endl;
} }
#endif
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -46,13 +46,69 @@ class RewindManager
RewindManager(OSystem& system, StateManager& statemgr); RewindManager(OSystem& system, StateManager& statemgr);
public: public:
static const int NUM_INTERVALS = 7;
const uInt32 INTERVAL_CYCLES[NUM_INTERVALS] = {
76 * 262,
76 * 262 * 3,
76 * 262 * 10,
76 * 262 * 30,
76 * 262 * 60,
76 * 262 * 60 * 3,
76 * 262 * 60 * 10
};
/*static const int NUM_INTERVALS = 6;
const string INTERVALS[NUM_INTERVALS] = { "1 scanline", "50 scanlines", "1 frame", "10 frames",
"1 second", "10 seconds" };
const uInt32 INTERVAL_CYCLES[NUM_INTERVALS] = { 76, 76 * 50, 76 * 262, 76 * 262 * 10,
76 * 262 * 60, 76 * 262 * 60 * 10 };*/
const string INT_SETTINGS[NUM_INTERVALS] = {
"1f",
"3f",
"10f",
"30f",
"1s",
"3s",
"10s"
};
static const int NUM_HORIZONS = 8;
const uInt64 HORIZON_CYCLES[NUM_HORIZONS] = {
76 * 262 * 60 * 3,
76 * 262 * 60 * 10,
76 * 262 * 60 * 30,
76 * 262 * 60 * 60,
76 * 262 * 60 * 60 * 3,
76 * 262 * 60 * 60 * 10,
uInt64(76) * 262 * 60 * 60 * 30,
uInt64(76) * 262 * 60 * 60 * 60
};
/*static const int NUM_HORIZONS = 7;
const string HORIZONS[NUM_HORIZONS] = { "~1 frame", "~10 frames", "~1 second", "~10 seconds",
"~1 minute", "~10 minutes", "~60 minutes" };
const uInt64 HORIZON_CYCLES[NUM_HORIZONS] = { 76 * 262, 76 * 262 * 10, 76 * 262 * 60, 76 * 262 * 60 * 10,
76 * 262 * 60 * 60, 76 * 262 * 60 * 60 * 10, uInt64(76) * 262 * 60 * 60 * 60 };*/
const string HOR_SETTINGS[NUM_HORIZONS] = {
"3s",
"10s",
"30s",
"1m",
"3m",
"10m",
"30m",
"60m"
};
/**
*/
void setup();
/** /**
Add a new state file with the given message; this message will be Add a new state file with the given message; this message will be
displayed when the state is replayed. displayed when the state is replayed.
@param message Message to display when replaying this state @param message Message to display when replaying this state
*/ */
bool addState(const string& message); bool addState(const string& message, bool continuous = false);
/** /**
Rewind one level of the state list, and display the message associated Rewind one level of the state list, and display the message associated
@ -77,11 +133,17 @@ class RewindManager
private: private:
// Maximum number of states to save // Maximum number of states to save
static constexpr uInt32 MAX_SIZE = 10; // TODO: use a parameter here and allow user to define size in UI static constexpr uInt32 MAX_SIZE = 20; // TODO: use a parameter here and allow user to define size in UI
OSystem& myOSystem; OSystem& myOSystem;
StateManager& myStateManager; StateManager& myStateManager;
uInt32 mySize;
uInt32 myUncompressed;
uInt32 myInterval;
uInt64 myHorizon;
double myFactor;
struct RewindState { struct RewindState {
Serializer data; Serializer data;
string message; string message;

View File

@ -145,7 +145,6 @@ void StateManager::toggleRewindMode()
bool StateManager::rewindState() bool StateManager::rewindState()
{ {
RewindManager& r = myOSystem.state().rewindManager(); RewindManager& r = myOSystem.state().rewindManager();
// TODO: add parameter to indicate rewinding from within emulation
return r.rewindState(); return r.rewindState();
} }
@ -153,7 +152,6 @@ bool StateManager::rewindState()
bool StateManager::unwindState() bool StateManager::unwindState()
{ {
RewindManager& r = myOSystem.state().rewindManager(); RewindManager& r = myOSystem.state().rewindManager();
// TODO: add parameter to indicate unwinding from within emulation
return r.unwindState(); return r.unwindState();
} }
@ -163,7 +161,7 @@ void StateManager::update()
switch(myActiveMode) switch(myActiveMode)
{ {
case Mode::Rewind: case Mode::Rewind:
myRewindManager->addState("add 1 frame"); myRewindManager->addState("1 frame", true);
break; break;
#if 0 #if 0

View File

@ -524,8 +524,8 @@ void Debugger::nextFrame(int frames)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::updateRewindbuttons(const RewindManager& r) void Debugger::updateRewindbuttons(const RewindManager& r)
{ {
myDialog->rewindButton().setEnabled(!r.atLast()); myDialog->rewindButton().setEnabled(!r.atFirst());
myDialog->unwindButton().setEnabled(!r.atFirst()); myDialog->unwindButton().setEnabled(!r.atLast());
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -158,8 +158,8 @@ Settings::Settings(OSystem& osystem)
setInternal("plr.rewind", false); setInternal("plr.rewind", false);
setInternal("plr.rewind.size", 100); setInternal("plr.rewind.size", 100);
setInternal("plr.rewind.uncompressed", 30); setInternal("plr.rewind.uncompressed", 30);
setInternal("plr.rewind.interval", 4); // = 1 second setInternal("plr.rewind.interval", "30f"); // = 0.5 seconds
setInternal("plr.rewind.horizon", 5); // = ~10 minutes setInternal("plr.rewind.horizon", "10m"); // = ~10 minutes
// Thumb ARM emulation options // Thumb ARM emulation options
setInternal("plr.thumb.trapfatal", "false"); setInternal("plr.thumb.trapfatal", "false");
@ -178,8 +178,8 @@ Settings::Settings(OSystem& osystem)
setInternal("dev.rewind", true); setInternal("dev.rewind", true);
setInternal("dev.rewind.size", 100); setInternal("dev.rewind.size", 100);
setInternal("dev.rewind.uncompressed", 60); setInternal("dev.rewind.uncompressed", 60);
setInternal("dev.rewind.interval", 2); // = 1 frame setInternal("dev.rewind.interval", "1f"); // = 1 frame
setInternal("dev.rewind.horizon", 3); // = ~10 seconds setInternal("dev.rewind.horizon", "10s"); // = ~10 seconds
// Thumb ARM emulation options // Thumb ARM emulation options
setInternal("dev.thumb.trapfatal", "true"); setInternal("dev.thumb.trapfatal", "true");
} }
@ -330,10 +330,10 @@ void Settings::validate()
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", 2); 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", 3); 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");
@ -348,11 +348,11 @@ void Settings::validate()
i = getInt("plr.rewind.uncompressed"); i = getInt("plr.rewind.uncompressed");
if(i < 0 || i > size) setInternal("plr.rewind.uncompressed", size); if(i < 0 || i > size) setInternal("plr.rewind.uncompressed", size);
i = getInt("plr.rewind.interval"); /*i = getInt("plr.rewind.interval");
if(i < 0 || i > 5) setInternal("plr.rewind.interval", 4); if(i < 0 || i > 5) setInternal("plr.rewind.interval", 3);
i = getInt("plr.rewind.horizon"); i = getInt("plr.rewind.horizon");
if(i < 0 || i > 6) setInternal("plr.rewind.horizon", 5); if(i < 0 || i > 6) setInternal("plr.rewind.horizon", 5);*/
#ifdef SOUND_SUPPORT #ifdef SOUND_SUPPORT
i = getInt("volume"); i = getInt("volume");

View File

@ -38,6 +38,7 @@
#include "TIA.hxx" #include "TIA.hxx"
#include "OSystem.hxx" #include "OSystem.hxx"
#include "StateManager.hxx" #include "StateManager.hxx"
#include "RewindManager.hxx"
#include "DeveloperDialog.hxx" #include "DeveloperDialog.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -85,10 +86,12 @@ void DeveloperDialog::addEmulationTab(const GUI::Font& font)
// settings set // settings set
mySettingsGroup0 = new RadioButtonGroup(); mySettingsGroup0 = new RadioButtonGroup();
RadioButtonWidget* r = new RadioButtonWidget(myTab, font, HBORDER, ypos + 1, "Player settings", mySettingsGroup0, kPlrSettings); RadioButtonWidget* r = new RadioButtonWidget(myTab, font, HBORDER, ypos + 1,
"Player settings", mySettingsGroup0, kPlrSettings);
wid.push_back(r); wid.push_back(r);
ypos += lineHeight + VGAP; ypos += lineHeight + VGAP;
r = new RadioButtonWidget(myTab, font, HBORDER, ypos + 1, "Developer settings", mySettingsGroup0, kDevSettings); r = new RadioButtonWidget(myTab, font, HBORDER, ypos + 1,
"Developer settings", mySettingsGroup0, kDevSettings);
wid.push_back(r); wid.push_back(r);
ypos += lineHeight + VGAP * 1; ypos += lineHeight + VGAP * 1;
@ -177,10 +180,12 @@ void DeveloperDialog::addVideoTab(const GUI::Font& font)
// settings set // settings set
mySettingsGroup1 = new RadioButtonGroup(); mySettingsGroup1 = new RadioButtonGroup();
RadioButtonWidget* r = new RadioButtonWidget(myTab, font, HBORDER, ypos + 1, "Player settings", mySettingsGroup1, kPlrSettings); RadioButtonWidget* r = new RadioButtonWidget(myTab, font, HBORDER, ypos + 1,
"Player settings", mySettingsGroup1, kPlrSettings);
wid.push_back(r); wid.push_back(r);
ypos += lineHeight + VGAP; ypos += lineHeight + VGAP;
r = new RadioButtonWidget(myTab, font, HBORDER, ypos + 1, "Developer settings", mySettingsGroup1, kDevSettings); r = new RadioButtonWidget(myTab, font, HBORDER, ypos + 1,
"Developer settings", mySettingsGroup1, kDevSettings);
wid.push_back(r); wid.push_back(r);
ypos += lineHeight + VGAP * 1; ypos += lineHeight + VGAP * 1;
@ -247,7 +252,6 @@ void DeveloperDialog::addVideoTab(const GUI::Font& font)
// Add message concerning usage // Add message concerning usage
const GUI::Font& infofont = instance().frameBuffer().infoFont(); const GUI::Font& infofont = instance().frameBuffer().infoFont();
ypos = myTab->getHeight() - 5 - fontHeight - infofont.getFontHeight() - 10; ypos = myTab->getHeight() - 5 - fontHeight - infofont.getFontHeight() - 10;
//new StaticTextWidget(myTab, infofont, 10, ypos, "(*) Colors must be different for each object");
new StaticTextWidget(myTab, infofont, HBORDER, ypos, "(*) colors identical for player and developer settings"); new StaticTextWidget(myTab, infofont, HBORDER, ypos, "(*) colors identical for player and developer settings");
// Add items for tab 2 // Add items for tab 2
@ -257,6 +261,44 @@ void DeveloperDialog::addVideoTab(const GUI::Font& font)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DeveloperDialog::addStatesTab(const GUI::Font& font) void DeveloperDialog::addStatesTab(const GUI::Font& font)
{ {
const string INTERVALS[NUM_INTERVALS] = {
"1 frame",
"3 frames",
"10 frames",
"30 frames",
"1 second",
"3 seconds",
"10 seconds"
};
const string INT_SETTINGS[NUM_INTERVALS] = {
"1f",
"3f",
"10f",
"30f",
"1s",
"3s",
"10s"
};
const string HORIZONS[NUM_HORIZONS] = {
"~3 seconds",
"~10 seconds",
"~30 seconds",
"~1 minute",
"~3 minutes",
"~10 minutes",
"~30 minutes",
"~60 minutes"
};
const string HOR_SETTINGS[NUM_HORIZONS] = {
"3s",
"10s",
"30s",
"1m",
"3m",
"10m",
"30m",
"60m"
};
const int HBORDER = 10; const int HBORDER = 10;
const int INDENT = 16+4; const int INDENT = 16+4;
const int VBORDER = 8; const int VBORDER = 8;
@ -265,18 +307,22 @@ void DeveloperDialog::addStatesTab(const GUI::Font& font)
int lineHeight = font.getLineHeight(); int lineHeight = font.getLineHeight();
int fontHeight = font.getFontHeight(); int fontHeight = font.getFontHeight();
WidgetArray wid; WidgetArray wid;
VariantList items;
int tabID = myTab->addTab("States"); int tabID = myTab->addTab("States");
// settings set // settings set
mySettingsGroup2 = new RadioButtonGroup(); mySettingsGroup2 = new RadioButtonGroup();
RadioButtonWidget* r = new RadioButtonWidget(myTab, font, HBORDER, ypos + 1, "Player settings", mySettingsGroup2, kPlrSettings); RadioButtonWidget* r = new RadioButtonWidget(myTab, font, HBORDER, ypos + 1,
"Player settings", mySettingsGroup2, kPlrSettings);
wid.push_back(r); wid.push_back(r);
ypos += lineHeight + VGAP; ypos += lineHeight + VGAP;
r = new RadioButtonWidget(myTab, font, HBORDER, ypos + 1, "Developer settings", mySettingsGroup2, kDevSettings); r = new RadioButtonWidget(myTab, font, HBORDER, ypos + 1,
"Developer settings", mySettingsGroup2, kDevSettings);
wid.push_back(r); wid.push_back(r);
ypos += lineHeight + VGAP * 1; ypos += lineHeight + VGAP * 1;
myContinuousRewindWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT, ypos + 1, "Continuous rewind", kRewind); myContinuousRewindWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT, ypos + 1,
"Continuous rewind", kRewind);
wid.push_back(myContinuousRewindWidget); wid.push_back(myContinuousRewindWidget);
ypos += lineHeight + VGAP; ypos += lineHeight + VGAP;
@ -292,7 +338,7 @@ void DeveloperDialog::addStatesTab(const GUI::Font& font)
ypos += lineHeight + VGAP; ypos += lineHeight + VGAP;
myUncompressedWidget = new SliderWidget(myTab, font, HBORDER + INDENT * 2, ypos - 1, sWidth, lineHeight, myUncompressedWidget = new SliderWidget(myTab, font, HBORDER + INDENT * 2, ypos - 1, sWidth, lineHeight,
"Uncompressed size (*) ", 0, kUncompressedChanged); "Uncompressed size ", 0, kUncompressedChanged);
myUncompressedWidget->setMinValue(0); myUncompressedWidget->setMinValue(0);
myUncompressedWidget->setMaxValue(1000); myUncompressedWidget->setMaxValue(1000);
myUncompressedWidget->setStepValue(20); myUncompressedWidget->setStepValue(20);
@ -301,23 +347,21 @@ void DeveloperDialog::addStatesTab(const GUI::Font& font)
myUncompressedWidget->getTop() + 2, "50 "); myUncompressedWidget->getTop() + 2, "50 ");
ypos += lineHeight + VGAP; ypos += lineHeight + VGAP;
myStateIntervalWidget = new SliderWidget(myTab, font, HBORDER + INDENT * 2, ypos - 1, sWidth, lineHeight, items.clear();
"Interval ", 0, kIntervalChanged); for(int i = 0; i < NUM_INTERVALS; ++i)
VarList::push_back(items, INTERVALS[i], INT_SETTINGS[i]);
myStateIntervalWidget->setMinValue(0); int pwidth = font.getStringWidth("~10 seconds");
myStateIntervalWidget->setMaxValue(NUM_INTERVALS - 1); myStateIntervalWidget = new PopUpWidget(myTab, font, HBORDER + INDENT * 2, ypos, pwidth,
lineHeight, items, "Interval ", 0, kIntervalChanged);
wid.push_back(myStateIntervalWidget); wid.push_back(myStateIntervalWidget);
myStateIntervalLabelWidget = new StaticTextWidget(myTab, font, myStateIntervalWidget->getRight() + 4,
myStateIntervalWidget->getTop() + 2, "50 scanlines");
ypos += lineHeight + VGAP; ypos += lineHeight + VGAP;
myStateHorizonWidget = new SliderWidget(myTab, font, HBORDER + INDENT * 2, ypos - 1, sWidth, lineHeight, items.clear();
"Horizon ", 0, kHorizonChanged); for(int i = 0; i < NUM_HORIZONS; ++i)
myStateHorizonWidget->setMinValue(0); VarList::push_back(items, HORIZONS[i], HOR_SETTINGS[i]);
myStateHorizonWidget->setMaxValue(NUM_HORIZONS - 1); myStateHorizonWidget = new PopUpWidget(myTab, font, HBORDER + INDENT * 2, ypos, pwidth,
lineHeight, items, "Horizon ", 0, kHorizonChanged);
wid.push_back(myStateHorizonWidget); wid.push_back(myStateHorizonWidget);
myStateHorizonLabelWidget = new StaticTextWidget(myTab, font, myStateHorizonWidget->getRight() + 4,
myStateHorizonWidget->getTop() + 2, "~60 minutes");
// Add message concerning usage // Add message concerning usage
const GUI::Font& infofont = instance().frameBuffer().infoFont(); const GUI::Font& infofont = instance().frameBuffer().infoFont();
@ -477,8 +521,11 @@ void DeveloperDialog::loadSettings(SettingsSet set)
myContinuousRewind[set] = instance().settings().getBool(prefix + "rewind"); myContinuousRewind[set] = instance().settings().getBool(prefix + "rewind");
myStateSize[set] = instance().settings().getInt(prefix + "rewind.size"); myStateSize[set] = instance().settings().getInt(prefix + "rewind.size");
myUncompressed[set] = instance().settings().getInt(prefix + "rewind.uncompressed"); myUncompressed[set] = instance().settings().getInt(prefix + "rewind.uncompressed");
myStateInterval[set] = instance().settings().getInt(prefix + "rewind.interval"); /*myStateInterval[set] = instance().settings().getInt(prefix + "rewind.interval");
myStateHorizon[set] = instance().settings().getInt(prefix + "rewind.horizon"); myStateHorizon[set] = instance().settings().getInt(prefix + "rewind.horizon");*/
myStateInterval[set] = instance().settings().getString(prefix + "rewind.interval");
myStateHorizon[set] = instance().settings().getString(prefix + "rewind.horizon");
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -544,8 +591,9 @@ void DeveloperDialog::getWidgetStates(SettingsSet set)
myContinuousRewind[set] = myContinuousRewindWidget->getState(); myContinuousRewind[set] = myContinuousRewindWidget->getState();
myStateSize[set] = myStateSizeWidget->getValue(); myStateSize[set] = myStateSizeWidget->getValue();
myUncompressed[set] = myUncompressedWidget->getValue(); myUncompressed[set] = myUncompressedWidget->getValue();
myStateInterval[set] = myStateIntervalWidget->getValue(); myStateInterval[set] = myStateIntervalWidget->getSelected();
myStateHorizon[set] = myStateHorizonWidget->getValue(); myStateInterval[set] = myStateIntervalWidget->getSelectedTag().toString();
myStateHorizon[set] = myStateHorizonWidget->getSelectedTag().toString();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -583,8 +631,10 @@ void DeveloperDialog::setWidgetStates(SettingsSet set)
myContinuousRewindWidget->setState(myContinuousRewind[set]); myContinuousRewindWidget->setState(myContinuousRewind[set]);
myStateSizeWidget->setValue(myStateSize[set]); myStateSizeWidget->setValue(myStateSize[set]);
myUncompressedWidget->setValue(myUncompressed[set]); myUncompressedWidget->setValue(myUncompressed[set]);
myStateIntervalWidget->setValue(myStateInterval[set]); //myStateIntervalWidget->setSelectedIndex(myStateInterval[set]);
myStateHorizonWidget->setValue(myStateHorizon[set]); myStateIntervalWidget->setSelected(myStateInterval[set]);
//myStateHorizonWidget->setSelectedIndex(myStateHorizon[set]);
myStateHorizonWidget->setSelected(myStateHorizon[set]);
handleRewind(); handleRewind();
handleSize(); handleSize();
@ -671,47 +721,11 @@ void DeveloperDialog::saveConfig()
// Finally, issue a complete framebuffer re-initialization // Finally, issue a complete framebuffer re-initialization
//instance().createFrameBuffer(); //instance().createFrameBuffer();
// TODO: update RewindManager // update RewindManager
instance().state().rewindManager().setup();
instance().state().setRewindMode(myContinuousRewindWidget->getState() ? instance().state().setRewindMode(myContinuousRewindWidget->getState() ?
StateManager::Mode::Rewind : StateManager::Mode::Off); StateManager::Mode::Rewind : StateManager::Mode::Off);
// define interval growth factor
uInt32 size = myStateSizeWidget->getValue();
uInt32 uncompressed = myUncompressedWidget->getValue();
const double MAX_FACTOR = 1E8;
uInt64 horizon = HORIZON_CYCLES[myStateHorizonWidget->getValue()];
double factor, minFactor = 1, maxFactor = MAX_FACTOR;
while(true)
{
double interval = INTERVAL_CYCLES[myStateIntervalWidget->getValue()];
double cycleSum = interval * uncompressed;
// calculate next factor
factor = (minFactor + maxFactor) / 2;
// horizon not reachable?
if(factor == MAX_FACTOR)
break;
// sum up interval cycles
for(uInt32 i = uncompressed; i < size; ++i)
{
interval *= factor;
cycleSum += interval;
}
double diff = cycleSum - horizon;
//cerr << "factor " << factor << ", diff " << diff << endl;
// exit loop if result is close enough
if(std::abs(diff) < horizon * 1E-5)
break;
// define new boundary
if(cycleSum < horizon)
minFactor = factor;
else
maxFactor = factor;
}
// TODO factor calculation code above into RewindManager
//instance().settings().setValue("dev.rewind.factor", factor);
// Debugger font style // Debugger font style
instance().settings().setValue("dbg.fontstyle", instance().settings().setValue("dbg.fontstyle",
myDebuggerFontStyle->getSelectedTag().toString()); myDebuggerFontStyle->getSelectedTag().toString());
@ -767,8 +781,8 @@ void DeveloperDialog::setDefaults()
myContinuousRewind[set] = devSettings ? true : false; myContinuousRewind[set] = devSettings ? true : false;
myStateSize[set] = 100; myStateSize[set] = 100;
myUncompressed[set] = devSettings ? 60 : 30; myUncompressed[set] = devSettings ? 60 : 30;
myStateInterval[set] = devSettings ? 2 : 4; myStateInterval[set] = devSettings ? "1f" : "30f";
myStateHorizon[set] = devSettings ? 3 : 5; myStateHorizon[set] = devSettings ? "10s" : "10m";
setWidgetStates(set); setWidgetStates(set);
break; break;
@ -954,10 +968,8 @@ void DeveloperDialog::handleRewind()
myUncompressedLabelWidget->setEnabled(enable); myUncompressedLabelWidget->setEnabled(enable);
myStateIntervalWidget->setEnabled(enable); myStateIntervalWidget->setEnabled(enable);
myStateIntervalLabelWidget->setEnabled(enable);
myStateHorizonWidget->setEnabled(enable); myStateHorizonWidget->setEnabled(enable);
myStateHorizonLabelWidget->setEnabled(enable);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -965,18 +977,24 @@ void DeveloperDialog::handleSize()
{ {
uInt32 size = myStateSizeWidget->getValue(); uInt32 size = myStateSizeWidget->getValue();
uInt32 uncompressed = myUncompressedWidget->getValue(); uInt32 uncompressed = myUncompressedWidget->getValue();
uInt32 interval = myStateIntervalWidget->getValue(); uInt32 interval = myStateIntervalWidget->getSelected();
uInt32 horizon = myStateHorizonWidget->getValue(); uInt32 horizon = myStateHorizonWidget->getSelected();
bool found = false; bool found = false;
Int32 i; Int32 i;
if(interval == -1)
interval = 0;
if(horizon == -1)
horizon = 0;
myStateSizeLabelWidget->setValue(size); myStateSizeLabelWidget->setValue(size);
// adapt horizon and interval // adapt horizon and interval
do do
{ {
for(i = horizon; i < NUM_HORIZONS; ++i) for(i = horizon; i < NUM_HORIZONS; ++i)
{ {
if(size * INTERVAL_CYCLES[interval] <= HORIZON_CYCLES[i]) if((uInt64)size * instance().state().rewindManager().INTERVAL_CYCLES[interval]
<= instance().state().rewindManager().HORIZON_CYCLES[i])
{ {
found = true; found = true;
break; break;
@ -988,8 +1006,8 @@ void DeveloperDialog::handleSize()
if(size < uncompressed) if(size < uncompressed)
myUncompressedWidget->setValue(size); myUncompressedWidget->setValue(size);
myStateIntervalWidget->setValue(interval); myStateIntervalWidget->setSelectedIndex(interval);
myStateHorizonWidget->setValue(i); myStateHorizonWidget->setSelectedIndex(i);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1009,18 +1027,23 @@ void DeveloperDialog::handleInterval()
{ {
uInt32 size = myStateSizeWidget->getValue(); uInt32 size = myStateSizeWidget->getValue();
uInt32 uncompressed = myUncompressedWidget->getValue(); uInt32 uncompressed = myUncompressedWidget->getValue();
uInt32 interval = myStateIntervalWidget->getValue(); uInt32 interval = myStateIntervalWidget->getSelected();
uInt32 horizon = myStateHorizonWidget->getValue(); uInt32 horizon = myStateHorizonWidget->getSelected();
bool found = false; bool found = false;
Int32 i; Int32 i;
myStateIntervalLabelWidget->setLabel(INTERVALS[interval]); if(interval == -1)
interval = 0;
if(horizon == -1)
horizon = 0;
// adapt horizon and size // adapt horizon and size
do do
{ {
for(i = horizon; i < NUM_HORIZONS; ++i) for(i = horizon; i < NUM_HORIZONS; ++i)
{ {
if(size * INTERVAL_CYCLES[interval] <= HORIZON_CYCLES[i]) if((uInt64)size * instance().state().rewindManager().INTERVAL_CYCLES[interval]
<= instance().state().rewindManager().HORIZON_CYCLES[i])
{ {
found = true; found = true;
break; break;
@ -1030,10 +1053,10 @@ void DeveloperDialog::handleInterval()
size -= myStateSizeWidget->getStepValue(); size -= myStateSizeWidget->getStepValue();
} while(!found); } while(!found);
myStateHorizonWidget->setSelectedIndex(i);
myStateSizeWidget->setValue(size); myStateSizeWidget->setValue(size);
if(size < uncompressed) if(size < uncompressed)
myUncompressedWidget->setValue(size); myUncompressedWidget->setValue(size);
myStateHorizonWidget->setValue(i);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1041,18 +1064,23 @@ void DeveloperDialog::handleHorizon()
{ {
uInt32 size = myStateSizeWidget->getValue(); uInt32 size = myStateSizeWidget->getValue();
uInt32 uncompressed = myUncompressedWidget->getValue(); uInt32 uncompressed = myUncompressedWidget->getValue();
uInt32 interval = myStateIntervalWidget->getValue(); uInt32 interval = myStateIntervalWidget->getSelected();
uInt32 horizon = myStateHorizonWidget->getValue(); uInt32 horizon = myStateHorizonWidget->getSelected();
bool found = false; bool found = false;
Int32 i; Int32 i;
myStateHorizonLabelWidget->setLabel(HORIZONS[horizon]); if(interval == -1)
interval = 0;
if(horizon == -1)
horizon = 0;
// adapt interval and size // adapt interval and size
do do
{ {
for(i = interval; i >= 0; --i) for(i = interval; i >= 0; --i)
{ {
if(size * INTERVAL_CYCLES[i] <= HORIZON_CYCLES[horizon]) if(size * instance().state().rewindManager().INTERVAL_CYCLES[i]
<= instance().state().rewindManager().HORIZON_CYCLES[horizon])
{ {
found = true; found = true;
break; break;
@ -1062,10 +1090,10 @@ void DeveloperDialog::handleHorizon()
size -= myStateSizeWidget->getStepValue(); size -= myStateSizeWidget->getStepValue();
} while(!found); } while(!found);
myStateIntervalWidget->setSelectedIndex(i);
myStateSizeWidget->setValue(size); myStateSizeWidget->setValue(size);
if(size < uncompressed) if(size < uncompressed)
myUncompressedWidget->setValue(size); myUncompressedWidget->setValue(size);
myStateIntervalWidget->setValue(i);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -86,17 +86,9 @@ class DeveloperDialog : public Dialog
developer developer
}; };
static const int NUM_INTERVALS = 6; // MUST be aligned with RewindManager!
// TODO: check for intervals shorter than 1 frame (adjust horizon too!) static const int NUM_INTERVALS = 7;
const string INTERVALS[NUM_INTERVALS] = { "1 scanline", "50 scanlines", "1 frame", "10 frames", static const int NUM_HORIZONS = 8;
"1 second", "10 seconds" };
const uInt32 INTERVAL_CYCLES[NUM_INTERVALS] = { 76, 76 * 50, 76 * 262, 76 * 262 * 10,
76 * 262 * 60, 76 * 262 * 60 * 10 };
static const int NUM_HORIZONS = 7;
const string HORIZONS[NUM_HORIZONS] = { "~1 frame", "~10 frames", "~1 second", "~10 seconds",
"~1 minute", "~10 minutes", "~60 minutes" };
const uInt64 HORIZON_CYCLES[NUM_HORIZONS] = { 76 * 262, 76 * 262 * 10, 76 * 262 * 60, 76 * 262 * 60 * 10,
76 * 262 * 60 * 60, 76 * 262 * 60 * 60 * 10, uInt64(76) * 262 * 60 * 60 * 60 };
static const int DEBUG_COLORS = 6; static const int DEBUG_COLORS = 6;
@ -130,10 +122,8 @@ class DeveloperDialog : public Dialog
StaticTextWidget* myStateSizeLabelWidget; StaticTextWidget* myStateSizeLabelWidget;
SliderWidget* myUncompressedWidget; SliderWidget* myUncompressedWidget;
StaticTextWidget* myUncompressedLabelWidget; StaticTextWidget* myUncompressedLabelWidget;
SliderWidget* myStateIntervalWidget; PopUpWidget* myStateIntervalWidget;
StaticTextWidget* myStateIntervalLabelWidget; PopUpWidget* myStateHorizonWidget;
SliderWidget* myStateHorizonWidget;
StaticTextWidget* myStateHorizonLabelWidget;
#ifdef DEBUGGER_SUPPORT #ifdef DEBUGGER_SUPPORT
// Debugger UI widgets // Debugger UI widgets
@ -162,8 +152,9 @@ class DeveloperDialog : public Dialog
bool myContinuousRewind[2]; bool myContinuousRewind[2];
int myStateSize[2]; int myStateSize[2];
int myUncompressed[2]; int myUncompressed[2];
int myStateInterval[2]; //int myStateInterval[2];
int myStateHorizon[2]; string myStateInterval[2];
string myStateHorizon[2];
private: private:
void addEmulationTab(const GUI::Font& font); void addEmulationTab(const GUI::Font& font);