From e465112c02c9c5b1f70d5b91e4e70b82d1acd07b Mon Sep 17 00:00:00 2001 From: thrust26 Date: Tue, 12 Dec 2017 17:07:05 +0100 Subject: [PATCH] rewind/unwind basically working changed values, UI and settings for rewind interval and horizon --- src/common/LinkedObjectPool.hxx | 4 +- src/common/RewindManager.cxx | 136 +++++++++++++++------ src/common/RewindManager.hxx | 66 ++++++++++- src/common/StateManager.cxx | 4 +- src/debugger/Debugger.cxx | 4 +- src/emucore/Settings.cxx | 18 +-- src/gui/DeveloperDialog.cxx | 202 ++++++++++++++++++-------------- src/gui/DeveloperDialog.hxx | 25 ++-- 8 files changed, 302 insertions(+), 157 deletions(-) diff --git a/src/common/LinkedObjectPool.hxx b/src/common/LinkedObjectPool.hxx index eeae815f1..b609395f4 100644 --- a/src/common/LinkedObjectPool.hxx +++ b/src/common/LinkedObjectPool.hxx @@ -172,7 +172,7 @@ class LinkedObjectPool myPool.splice(myPool.end(), myList, offset >= 0 ? std::next(i, offset) : std::prev(i, -offset)); } - +#endif /** Convenience method to remove a single element from the active list by index, offset from the beginning of the list. (ie, '0' means first @@ -181,7 +181,7 @@ class LinkedObjectPool void remove(uInt32 index) { myPool.splice(myPool.end(), myList, std::next(myList.begin(), index)); } -#endif + /** Remove range of elements from the beginning of the active list to diff --git a/src/common/RewindManager.cxx b/src/common/RewindManager.cxx index 0275f52e8..aac8609f9 100644 --- a/src/common/RewindManager.cxx +++ b/src/common/RewindManager.cxx @@ -30,11 +30,80 @@ RewindManager::RewindManager(OSystem& system, StateManager& statemgr) : myOSystem(system), 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 myStateList.removeToLast(); @@ -63,8 +132,12 @@ cerr << "add " << state.count << endl; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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(); Serializer& s = state.data; string message = getMessage(state); @@ -76,72 +149,65 @@ cerr << "rewind " << state.count << endl; // Show message indicating the rewind state 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; } - else - return false; + myOSystem.frameBuffer().showMessage("Rewind not possible"); + return false; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool RewindManager::unwindState() { -#if 0 - if(!atFirst()) // or last??? + if(!atLast()) { - // TODO: get state next to the current state - /*RewindState& state = myStateList.???() + // Set internal current iterator to next state (forward in time), + // since we've now processed this state + myStateList.moveToNext(); + + RewindState& state = myStateList.current(); Serializer& s = state.data; string message = getMessage(state); +cerr << "unwind " << state.count << endl; - s.reset(); // rewind Serializer internal buffers + s.rewind(); // rewind Serializer internal buffers myStateManager.loadState(s); myOSystem.console().tia().loadDisplay(s); // Show message indicating the rewind state - myOSystem.frameBuffer().showMessage(message);*/ + myOSystem.frameBuffer().showMessage(message); return true; } -#endif + myOSystem.frameBuffer().showMessage("Unwind not possible"); return false; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void RewindManager::compressStates() { -#if 0 - myStateList.removeFirst(); // remove the oldest state file -#else //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 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; uInt32 removeIdx = 0; uInt32 idx = myStateList.size() - 1; -cerr << "idx: " << idx << endl; +//cerr << "idx: " << idx << endl; for(auto it = myStateList.last(); it != myStateList.first(); --it) { - if(idx >= STEP_STATES) + if(idx < mySize - myUncompressed) { -cerr << *it << endl << endl; // debug code - expectedCycles *= DENSITY; +//cerr << *it << endl << endl; // debug code + expectedCycles *= myFactor; - double expected = expectedCycles * (1 + DENSITY); + double expected = expectedCycles * (1 + myFactor); uInt64 prev = myStateList.previous(it)->cycle; uInt64 next = myStateList.next(it)->cycle; - double delta = expected / (prev - next); -cerr << "prev: " << prev << ", next: " << next << ", delta: " << delta << endl; + if(next != lastCycle) + lastCycle++; + double delta = expected / (next - prev); +//cerr << "prev: " << prev << ", next: " << next << ", delta: " << delta << endl; if(delta > maxDelta) { @@ -152,17 +218,17 @@ cerr << "prev: " << prev << ", next: " << next << ", delta: " << delta << endl; lastCycle = it->cycle; --idx; } -cerr << "END\n"; if (maxDelta < 1) { // 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 { - //myStateList.remove(removeIdx); // remove + myStateList.remove(removeIdx); // remove +cerr << "remove " << removeIdx << endl; } -#endif } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/common/RewindManager.hxx b/src/common/RewindManager.hxx index edec7ce84..3fa7faf47 100644 --- a/src/common/RewindManager.hxx +++ b/src/common/RewindManager.hxx @@ -46,13 +46,69 @@ class RewindManager RewindManager(OSystem& system, StateManager& statemgr); 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 displayed when the state is replayed. @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 @@ -77,11 +133,17 @@ class RewindManager private: // 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; StateManager& myStateManager; + uInt32 mySize; + uInt32 myUncompressed; + uInt32 myInterval; + uInt64 myHorizon; + double myFactor; + struct RewindState { Serializer data; string message; diff --git a/src/common/StateManager.cxx b/src/common/StateManager.cxx index 8896a2fe1..a279e23f6 100644 --- a/src/common/StateManager.cxx +++ b/src/common/StateManager.cxx @@ -145,7 +145,6 @@ void StateManager::toggleRewindMode() bool StateManager::rewindState() { RewindManager& r = myOSystem.state().rewindManager(); - // TODO: add parameter to indicate rewinding from within emulation return r.rewindState(); } @@ -153,7 +152,6 @@ bool StateManager::rewindState() bool StateManager::unwindState() { RewindManager& r = myOSystem.state().rewindManager(); - // TODO: add parameter to indicate unwinding from within emulation return r.unwindState(); } @@ -163,7 +161,7 @@ void StateManager::update() switch(myActiveMode) { case Mode::Rewind: - myRewindManager->addState("add 1 frame"); + myRewindManager->addState("1 frame", true); break; #if 0 diff --git a/src/debugger/Debugger.cxx b/src/debugger/Debugger.cxx index c8e39d48a..0eb321f9a 100644 --- a/src/debugger/Debugger.cxx +++ b/src/debugger/Debugger.cxx @@ -524,8 +524,8 @@ void Debugger::nextFrame(int frames) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Debugger::updateRewindbuttons(const RewindManager& r) { - myDialog->rewindButton().setEnabled(!r.atLast()); - myDialog->unwindButton().setEnabled(!r.atFirst()); + myDialog->rewindButton().setEnabled(!r.atFirst()); + myDialog->unwindButton().setEnabled(!r.atLast()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/emucore/Settings.cxx b/src/emucore/Settings.cxx index ccbd12463..0bd0e126a 100644 --- a/src/emucore/Settings.cxx +++ b/src/emucore/Settings.cxx @@ -158,8 +158,8 @@ Settings::Settings(OSystem& osystem) setInternal("plr.rewind", false); setInternal("plr.rewind.size", 100); setInternal("plr.rewind.uncompressed", 30); - setInternal("plr.rewind.interval", 4); // = 1 second - setInternal("plr.rewind.horizon", 5); // = ~10 minutes + setInternal("plr.rewind.interval", "30f"); // = 0.5 seconds + setInternal("plr.rewind.horizon", "10m"); // = ~10 minutes // Thumb ARM emulation options setInternal("plr.thumb.trapfatal", "false"); @@ -178,8 +178,8 @@ Settings::Settings(OSystem& osystem) setInternal("dev.rewind", true); setInternal("dev.rewind.size", 100); setInternal("dev.rewind.uncompressed", 60); - setInternal("dev.rewind.interval", 2); // = 1 frame - setInternal("dev.rewind.horizon", 3); // = ~10 seconds + setInternal("dev.rewind.interval", "1f"); // = 1 frame + setInternal("dev.rewind.horizon", "10s"); // = ~10 seconds // Thumb ARM emulation options setInternal("dev.thumb.trapfatal", "true"); } @@ -330,10 +330,10 @@ void Settings::validate() if(i < 0 || i > size) setInternal("dev.rewind.uncompressed", size); 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"); - 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"); if(i < 1 || i > 20) setInternal("plr.tv.jitter_recovery", "10"); @@ -348,11 +348,11 @@ void Settings::validate() i = getInt("plr.rewind.uncompressed"); if(i < 0 || i > size) setInternal("plr.rewind.uncompressed", size); - i = getInt("plr.rewind.interval"); - if(i < 0 || i > 5) setInternal("plr.rewind.interval", 4); + /*i = getInt("plr.rewind.interval"); + if(i < 0 || i > 5) setInternal("plr.rewind.interval", 3); 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 i = getInt("volume"); diff --git a/src/gui/DeveloperDialog.cxx b/src/gui/DeveloperDialog.cxx index e9d1d595c..7ecd501e7 100644 --- a/src/gui/DeveloperDialog.cxx +++ b/src/gui/DeveloperDialog.cxx @@ -38,6 +38,7 @@ #include "TIA.hxx" #include "OSystem.hxx" #include "StateManager.hxx" +#include "RewindManager.hxx" #include "DeveloperDialog.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -85,10 +86,12 @@ void DeveloperDialog::addEmulationTab(const GUI::Font& font) // settings set 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); 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); ypos += lineHeight + VGAP * 1; @@ -177,10 +180,12 @@ void DeveloperDialog::addVideoTab(const GUI::Font& font) // settings set 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); 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); ypos += lineHeight + VGAP * 1; @@ -247,7 +252,6 @@ void DeveloperDialog::addVideoTab(const GUI::Font& font) // Add message concerning usage const GUI::Font& infofont = instance().frameBuffer().infoFont(); 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"); // Add items for tab 2 @@ -257,6 +261,44 @@ void DeveloperDialog::addVideoTab(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 INDENT = 16+4; const int VBORDER = 8; @@ -265,24 +307,28 @@ void DeveloperDialog::addStatesTab(const GUI::Font& font) int lineHeight = font.getLineHeight(); int fontHeight = font.getFontHeight(); WidgetArray wid; + VariantList items; int tabID = myTab->addTab("States"); // settings set 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); 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); 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); ypos += lineHeight + VGAP; int sWidth = font.getMaxCharWidth() * 8; myStateSizeWidget = new SliderWidget(myTab, font, HBORDER + INDENT * 2, ypos - 1, sWidth, lineHeight, - "Buffer size (*) ", 0, kSizeChanged); + "Buffer size (*) ", 0, kSizeChanged); myStateSizeWidget->setMinValue(100); myStateSizeWidget->setMaxValue(1000); myStateSizeWidget->setStepValue(20); @@ -292,32 +338,30 @@ void DeveloperDialog::addStatesTab(const GUI::Font& font) ypos += lineHeight + VGAP; myUncompressedWidget = new SliderWidget(myTab, font, HBORDER + INDENT * 2, ypos - 1, sWidth, lineHeight, - "Uncompressed size (*) ", 0, kUncompressedChanged); + "Uncompressed size ", 0, kUncompressedChanged); myUncompressedWidget->setMinValue(0); myUncompressedWidget->setMaxValue(1000); myUncompressedWidget->setStepValue(20); wid.push_back(myUncompressedWidget); myUncompressedLabelWidget = new StaticTextWidget(myTab, font, myUncompressedWidget->getRight() + 4, - myUncompressedWidget->getTop() + 2, "50 "); + myUncompressedWidget->getTop() + 2, "50 "); ypos += lineHeight + VGAP; - myStateIntervalWidget = new SliderWidget(myTab, font, HBORDER + INDENT * 2, ypos - 1, sWidth, lineHeight, - "Interval ", 0, kIntervalChanged); - - myStateIntervalWidget->setMinValue(0); - myStateIntervalWidget->setMaxValue(NUM_INTERVALS - 1); + items.clear(); + for(int i = 0; i < NUM_INTERVALS; ++i) + VarList::push_back(items, INTERVALS[i], INT_SETTINGS[i]); + int pwidth = font.getStringWidth("~10 seconds"); + myStateIntervalWidget = new PopUpWidget(myTab, font, HBORDER + INDENT * 2, ypos, pwidth, + lineHeight, items, "Interval ", 0, kIntervalChanged); wid.push_back(myStateIntervalWidget); - myStateIntervalLabelWidget = new StaticTextWidget(myTab, font, myStateIntervalWidget->getRight() + 4, - myStateIntervalWidget->getTop() + 2, "50 scanlines"); ypos += lineHeight + VGAP; - myStateHorizonWidget = new SliderWidget(myTab, font, HBORDER + INDENT * 2, ypos - 1, sWidth, lineHeight, - "Horizon ", 0, kHorizonChanged); - myStateHorizonWidget->setMinValue(0); - myStateHorizonWidget->setMaxValue(NUM_HORIZONS - 1); + items.clear(); + for(int i = 0; i < NUM_HORIZONS; ++i) + VarList::push_back(items, HORIZONS[i], HOR_SETTINGS[i]); + myStateHorizonWidget = new PopUpWidget(myTab, font, HBORDER + INDENT * 2, ypos, pwidth, + lineHeight, items, "Horizon ", 0, kHorizonChanged); wid.push_back(myStateHorizonWidget); - myStateHorizonLabelWidget = new StaticTextWidget(myTab, font, myStateHorizonWidget->getRight() + 4, - myStateHorizonWidget->getTop() + 2, "~60 minutes"); // Add message concerning usage const GUI::Font& infofont = instance().frameBuffer().infoFont(); @@ -477,8 +521,11 @@ void DeveloperDialog::loadSettings(SettingsSet set) myContinuousRewind[set] = instance().settings().getBool(prefix + "rewind"); myStateSize[set] = instance().settings().getInt(prefix + "rewind.size"); myUncompressed[set] = instance().settings().getInt(prefix + "rewind.uncompressed"); - myStateInterval[set] = instance().settings().getInt(prefix + "rewind.interval"); - myStateHorizon[set] = instance().settings().getInt(prefix + "rewind.horizon"); + /*myStateInterval[set] = instance().settings().getInt(prefix + "rewind.interval"); + 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(); myStateSize[set] = myStateSizeWidget->getValue(); myUncompressed[set] = myUncompressedWidget->getValue(); - myStateInterval[set] = myStateIntervalWidget->getValue(); - myStateHorizon[set] = myStateHorizonWidget->getValue(); + myStateInterval[set] = myStateIntervalWidget->getSelected(); + myStateInterval[set] = myStateIntervalWidget->getSelectedTag().toString(); + myStateHorizon[set] = myStateHorizonWidget->getSelectedTag().toString(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -583,8 +631,10 @@ void DeveloperDialog::setWidgetStates(SettingsSet set) myContinuousRewindWidget->setState(myContinuousRewind[set]); myStateSizeWidget->setValue(myStateSize[set]); myUncompressedWidget->setValue(myUncompressed[set]); - myStateIntervalWidget->setValue(myStateInterval[set]); - myStateHorizonWidget->setValue(myStateHorizon[set]); + //myStateIntervalWidget->setSelectedIndex(myStateInterval[set]); + myStateIntervalWidget->setSelected(myStateInterval[set]); + //myStateHorizonWidget->setSelectedIndex(myStateHorizon[set]); + myStateHorizonWidget->setSelected(myStateHorizon[set]); handleRewind(); handleSize(); @@ -671,47 +721,11 @@ void DeveloperDialog::saveConfig() // Finally, issue a complete framebuffer re-initialization //instance().createFrameBuffer(); - // TODO: update RewindManager + // update RewindManager + instance().state().rewindManager().setup(); instance().state().setRewindMode(myContinuousRewindWidget->getState() ? 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 instance().settings().setValue("dbg.fontstyle", myDebuggerFontStyle->getSelectedTag().toString()); @@ -767,8 +781,8 @@ void DeveloperDialog::setDefaults() myContinuousRewind[set] = devSettings ? true : false; myStateSize[set] = 100; myUncompressed[set] = devSettings ? 60 : 30; - myStateInterval[set] = devSettings ? 2 : 4; - myStateHorizon[set] = devSettings ? 3 : 5; + myStateInterval[set] = devSettings ? "1f" : "30f"; + myStateHorizon[set] = devSettings ? "10s" : "10m"; setWidgetStates(set); break; @@ -954,10 +968,8 @@ void DeveloperDialog::handleRewind() myUncompressedLabelWidget->setEnabled(enable); myStateIntervalWidget->setEnabled(enable); - myStateIntervalLabelWidget->setEnabled(enable); myStateHorizonWidget->setEnabled(enable); - myStateHorizonLabelWidget->setEnabled(enable); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -965,18 +977,24 @@ void DeveloperDialog::handleSize() { uInt32 size = myStateSizeWidget->getValue(); uInt32 uncompressed = myUncompressedWidget->getValue(); - uInt32 interval = myStateIntervalWidget->getValue(); - uInt32 horizon = myStateHorizonWidget->getValue(); + uInt32 interval = myStateIntervalWidget->getSelected(); + uInt32 horizon = myStateHorizonWidget->getSelected(); bool found = false; Int32 i; + if(interval == -1) + interval = 0; + if(horizon == -1) + horizon = 0; + myStateSizeLabelWidget->setValue(size); // adapt horizon and interval do { 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; break; @@ -988,8 +1006,8 @@ void DeveloperDialog::handleSize() if(size < uncompressed) myUncompressedWidget->setValue(size); - myStateIntervalWidget->setValue(interval); - myStateHorizonWidget->setValue(i); + myStateIntervalWidget->setSelectedIndex(interval); + myStateHorizonWidget->setSelectedIndex(i); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1009,18 +1027,23 @@ void DeveloperDialog::handleInterval() { uInt32 size = myStateSizeWidget->getValue(); uInt32 uncompressed = myUncompressedWidget->getValue(); - uInt32 interval = myStateIntervalWidget->getValue(); - uInt32 horizon = myStateHorizonWidget->getValue(); + uInt32 interval = myStateIntervalWidget->getSelected(); + uInt32 horizon = myStateHorizonWidget->getSelected(); bool found = false; Int32 i; - myStateIntervalLabelWidget->setLabel(INTERVALS[interval]); + if(interval == -1) + interval = 0; + if(horizon == -1) + horizon = 0; + // adapt horizon and size do { 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; break; @@ -1030,10 +1053,10 @@ void DeveloperDialog::handleInterval() size -= myStateSizeWidget->getStepValue(); } while(!found); + myStateHorizonWidget->setSelectedIndex(i); myStateSizeWidget->setValue(size); if(size < uncompressed) myUncompressedWidget->setValue(size); - myStateHorizonWidget->setValue(i); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1041,18 +1064,23 @@ void DeveloperDialog::handleHorizon() { uInt32 size = myStateSizeWidget->getValue(); uInt32 uncompressed = myUncompressedWidget->getValue(); - uInt32 interval = myStateIntervalWidget->getValue(); - uInt32 horizon = myStateHorizonWidget->getValue(); + uInt32 interval = myStateIntervalWidget->getSelected(); + uInt32 horizon = myStateHorizonWidget->getSelected(); bool found = false; Int32 i; - myStateHorizonLabelWidget->setLabel(HORIZONS[horizon]); + if(interval == -1) + interval = 0; + if(horizon == -1) + horizon = 0; + // adapt interval and size do { 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; break; @@ -1062,10 +1090,10 @@ void DeveloperDialog::handleHorizon() size -= myStateSizeWidget->getStepValue(); } while(!found); + myStateIntervalWidget->setSelectedIndex(i); myStateSizeWidget->setValue(size); if(size < uncompressed) myUncompressedWidget->setValue(size); - myStateIntervalWidget->setValue(i); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/gui/DeveloperDialog.hxx b/src/gui/DeveloperDialog.hxx index c34ed3d85..bbc28f002 100644 --- a/src/gui/DeveloperDialog.hxx +++ b/src/gui/DeveloperDialog.hxx @@ -86,17 +86,9 @@ class DeveloperDialog : public Dialog developer }; - static const int NUM_INTERVALS = 6; - // TODO: check for intervals shorter than 1 frame (adjust horizon too!) - 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 }; - 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 }; + // MUST be aligned with RewindManager! + static const int NUM_INTERVALS = 7; + static const int NUM_HORIZONS = 8; static const int DEBUG_COLORS = 6; @@ -130,10 +122,8 @@ class DeveloperDialog : public Dialog StaticTextWidget* myStateSizeLabelWidget; SliderWidget* myUncompressedWidget; StaticTextWidget* myUncompressedLabelWidget; - SliderWidget* myStateIntervalWidget; - StaticTextWidget* myStateIntervalLabelWidget; - SliderWidget* myStateHorizonWidget; - StaticTextWidget* myStateHorizonLabelWidget; + PopUpWidget* myStateIntervalWidget; + PopUpWidget* myStateHorizonWidget; #ifdef DEBUGGER_SUPPORT // Debugger UI widgets @@ -162,8 +152,9 @@ class DeveloperDialog : public Dialog bool myContinuousRewind[2]; int myStateSize[2]; int myUncompressed[2]; - int myStateInterval[2]; - int myStateHorizon[2]; + //int myStateInterval[2]; + string myStateInterval[2]; + string myStateHorizon[2]; private: void addEmulationTab(const GUI::Font& font);