Skip to content

Commit

Permalink
Implement most of the variable framerate feature (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
clementgallet committed May 8, 2020
1 parent 485aa7e commit 337fc9c
Show file tree
Hide file tree
Showing 16 changed files with 164 additions and 7 deletions.
14 changes: 14 additions & 0 deletions src/library/DeterministicTimer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,20 @@ void DeterministicTimer::exitFrameBoundary()
}
}

/* Update baseTimeIncrement if variable framerate */
if (shared_config.variable_framerate) {
TimeHolder newTimeIncrement;
newTimeIncrement.tv_sec = shared_config.framerate_den / shared_config.framerate_num;
newTimeIncrement.tv_nsec = 1000000000 * (uint64_t)(shared_config.framerate_den % shared_config.framerate_num) / shared_config.framerate_num;

/* Check if we changed framerate, and reset fractional part if so. */
if (newTimeIncrement != baseTimeIncrement) {
baseTimeIncrement = newTimeIncrement;
fractional_increment = 1000000000 * (uint64_t)(shared_config.framerate_den % shared_config.framerate_num) % shared_config.framerate_num;
fractional_part = 0;
}
}

insideFrameBoundary = false;
frame_mutex.unlock();
}
Expand Down
5 changes: 5 additions & 0 deletions src/library/TimeHolder.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ class TimeHolder : public timespec
this->tv_nsec = th.tv_nsec;
}

bool operator!=(const timespec& th)
{
return ((this->tv_sec != th.tv_sec) || (this->tv_nsec != th.tv_nsec));
}

TimeHolder &operator=(const timespec& th)
{
this->tv_sec = th.tv_sec;
Expand Down
5 changes: 5 additions & 0 deletions src/library/frame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,11 @@ static void receive_messages(std::function<void()> draw)

case MSGN_ALL_INPUTS:
receiveData(&ai, sizeof(AllInputs));
/* Update framerate if necessary (do we actually need to?) */
if (shared_config.variable_framerate) {
shared_config.framerate_num = ai.framerate_num;
shared_config.framerate_den = ai.framerate_den;
}
break;

case MSGN_EXPOSE:
Expand Down
2 changes: 2 additions & 0 deletions src/program/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ void Config::save(const std::string& gamepath) {
settings.setValue("wait_timeout", sc.wait_timeout);
settings.setValue("game_specific_timing", sc.game_specific_timing);
settings.setValue("game_specific_sync", sc.game_specific_sync);
settings.setValue("variable_framerate", sc.variable_framerate);

settings.beginWriteArray("main_gettimes_threshold");
for (int t=0; t<SharedConfig::TIMETYPE_NUMTRACKEDTYPES; t++) {
Expand Down Expand Up @@ -267,6 +268,7 @@ void Config::load(const std::string& gamepath) {
sc.wait_timeout = settings.value("wait_timeout", sc.wait_timeout).toInt();
sc.game_specific_timing = settings.value("game_specific_timing", sc.game_specific_timing).toInt();
sc.game_specific_sync = settings.value("game_specific_sync", sc.game_specific_sync).toInt();
sc.variable_framerate = settings.value("variable_framerate", sc.variable_framerate).toBool();

sc.video_codec = settings.value("video_codec", sc.video_codec).toInt();
sc.video_bitrate = settings.value("video_bitrate", sc.video_bitrate).toInt();
Expand Down
25 changes: 24 additions & 1 deletion src/program/GameLoop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1412,6 +1412,13 @@ void GameLoop::processInputs(AllInputs &ai)
context->config.km.buildAllInputs(ai, context->game_window, keysyms.get(), context->config.sc, context->config.mouse_warp);
ai.pointer_x += pointer_offset_x;
ai.pointer_y += pointer_offset_y;

/* Add framerate if necessary */
if (context->config.sc.variable_framerate) {
ai.framerate_num = context->config.sc.framerate_num;
ai.framerate_den = context->config.sc.framerate_den;
}

emit inputsToBeSent(ai);
}

Expand Down Expand Up @@ -1464,7 +1471,9 @@ void GameLoop::processInputs(AllInputs &ai)

case SharedConfig::RECORDING_READ:
/* Read inputs from file */
if (movie.getInputs(ai) == 1) {
int ret = movie.getInputs(ai);

if (ret == 1) {
/* We are reading the last frame of the movie */
switch(context->config.on_movie_end) {
case Config::MOVIEEND_READ:
Expand All @@ -1482,6 +1491,20 @@ void GameLoop::processInputs(AllInputs &ai)
}
}

if (ret != -1) { // read succeeded
/* Update framerate */
if (context->config.sc.variable_framerate) {
context->config.sc.framerate_num = ai.framerate_num;
context->config.sc.framerate_den = ai.framerate_den;
emit updateFramerate();
}
}
else {
/* ai is empty, fill the framerate values */
ai.framerate_num = context->config.sc.framerate_num;
ai.framerate_den = context->config.sc.framerate_den;
}

/* Update controller inputs if controller window is shown */
emit showControllerInputs(ai);

Expand Down
1 change: 1 addition & 0 deletions src/program/GameLoop.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ class GameLoop : public QObject {
void sharedConfigChanged();
void fpsChanged(float fps, float lfps);
void askToShow(QString str, void* promise);
void updateFramerate();

void controllerButtonToggled(int controller_id, int button, bool pressed);
void showControllerInputs(const AllInputs &allinputs);
Expand Down
4 changes: 4 additions & 0 deletions src/program/KeyMapping.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,10 @@ void KeyMapping::init(xcb_connection_t* c)
input_list.push_back({SingleInput::IT_FLAG, SingleInput::FLAG_CONTROLLER3_REMOVED, "Joy3 Removed"});
input_list.push_back({SingleInput::IT_FLAG, SingleInput::FLAG_CONTROLLER4_REMOVED, "Joy4 Removed"});

/* Add framerate mapping */
input_list.push_back({SingleInput::IT_FRAMERATE_NUM, 1, "Framerate num"});
input_list.push_back({SingleInput::IT_FRAMERATE_DEN, 1, "Framerate den"});

/* Add controller mapping */
input_list.push_back({SingleInput::IT_CONTROLLER1_BUTTON_A, 1, "Joy1 A"});
input_list.push_back({SingleInput::IT_CONTROLLER1_BUTTON_B, 1, "Joy1 B"});
Expand Down
35 changes: 33 additions & 2 deletions src/program/MovieFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ MovieFile::MovieFile(Context* c) : modifiedSinceLastSave(false), modifiedSinceLa
rem.assign(R"(\|M([\-0-9]+:[\-0-9]+:(?:[AR]:)?[\.1-5]{5})\|)", std::regex::ECMAScript|std::regex::optimize);
rec.assign(R"(\|C((?:[\-0-9]+:){6}.{15})\|)", std::regex::ECMAScript|std::regex::optimize);
ref.assign(R"(\|F(.{1,9})\|)", std::regex::ECMAScript|std::regex::optimize);
ret.assign(R"(\|T([0-9]+:[0-9]+)\|)", std::regex::ECMAScript|std::regex::optimize);
}

const char* MovieFile::errorString(int error_code) {
Expand Down Expand Up @@ -135,6 +136,7 @@ int MovieFile::loadMovie(const std::string& moviefile)
context->authors = config.value("authors").toString().toStdString();
context->md5_movie = config.value("md5").toString().toStdString();
context->config.auto_restart = config.value("auto_restart").toBool();
context->config.sc.variable_framerate = config.value("variable_framerate").toBool();

config.beginGroup("mainthread_timetrack");
context->config.sc.main_gettimes_threshold[SharedConfig::TIMETYPE_TIME] = config.value("time").toInt();
Expand Down Expand Up @@ -261,6 +263,7 @@ int MovieFile::saveMovie(const std::string& moviefile, uint64_t nb_frames)
config.setValue("libtas_patch_version", PATCHVERSION);
config.setValue("savestate_frame_count", static_cast<unsigned long long>(nb_frames));
config.setValue("auto_restart", context->config.auto_restart);
config.setValue("variable_framerate", context->config.sc.variable_framerate);

/* Store the md5 that was extracted from the movie, or store the game
* binary movie if not. */
Expand Down Expand Up @@ -381,7 +384,7 @@ int MovieFile::writeFrame(std::ostream& input_stream, const AllInputs& inputs)
input_stream.put((inputs.controller_buttons[joy]&(1<<SingleInput::BUTTON_DPAD_RIGHT))?'r':'.');
}

/* Write set flag inputs */
/* Write flag inputs */
if (inputs.flags) {
input_stream << '|';
input_stream << 'F';
Expand All @@ -395,6 +398,14 @@ int MovieFile::writeFrame(std::ostream& input_stream, const AllInputs& inputs)
if (inputs.flags & (1 << SingleInput::FLAG_CONTROLLER3_REMOVED)) input_stream.put('U');
if (inputs.flags & (1 << SingleInput::FLAG_CONTROLLER4_REMOVED)) input_stream.put('O');
}

/* Write mouse inputs */
if (context->config.sc.variable_framerate) {
input_stream.put('|');
input_stream.put('T');
input_stream << std::dec;
input_stream << inputs.framerate_num << ':' << inputs.framerate_den;
}
input_stream << '|' << std::endl;

return 1;
Expand Down Expand Up @@ -446,9 +457,17 @@ int MovieFile::readFrame(const std::string& line, AllInputs& inputs)
readFlagFrame(flag_string, inputs);
}

/* Read framerate inputs */
if (std::regex_search(line, match, ret) && match.size() > 1) {
std::istringstream framerate_string(match.str(1));
readFramerateFrame(framerate_string, inputs);
}

return 1;
}

/* Following code is for old input format (1.3.5 and earlier) */

/* Read keyboard inputs */
if (context->config.sc.keyboard_support) {
readKeyboardFrame(input_string, inputs);
Expand All @@ -467,6 +486,11 @@ int MovieFile::readFrame(const std::string& line, AllInputs& inputs)
/* Read flag inputs */
readFlagFrame(input_string, inputs);

/* Read framerate inputs */
if (context->config.sc.variable_framerate) {
readFramerateFrame(input_string, inputs);
}

return 1;
}

Expand Down Expand Up @@ -548,6 +572,13 @@ void MovieFile::readFlagFrame(std::istringstream& input_string, AllInputs& input
}
}

void MovieFile::readFramerateFrame(std::istringstream& input_string, AllInputs& inputs)
{
char d;
input_string >> std::dec;
input_string >> inputs.framerate_num >> d >> inputs.framerate_den >> d;
}

uint64_t MovieFile::nbFrames()
{
return input_list.size();
Expand Down Expand Up @@ -608,7 +639,7 @@ int MovieFile::getInputs(AllInputs& inputs, uint64_t pos) const
{
if (pos >= input_list.size()) {
inputs.emptyInputs();
return 0;
return -1;
}

inputs = input_list[pos];
Expand Down
7 changes: 7 additions & 0 deletions src/program/MovieFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ class MovieFile {
/* Regex for the flag input string */
std::regex ref;

/* Regex for the framerate input string */
std::regex ret;

/* Read the keyboard input string */
void readKeyboardFrame(std::istringstream& input_string, AllInputs& inputs);

Expand All @@ -172,6 +175,10 @@ class MovieFile {

/* Read the flag input string */
void readFlagFrame(std::istringstream& input_string, AllInputs& inputs);

/* Read the framerate input string */
void readFramerateFrame(std::istringstream& input_string, AllInputs& inputs);

};

#endif
25 changes: 23 additions & 2 deletions src/program/ui/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ MainWindow::MainWindow(Context* c) : QMainWindow(), context(c)
connect(gameLoop, &GameLoop::sharedConfigChanged, this, &MainWindow::updateSharedConfigChanged);
connect(gameLoop, &GameLoop::fpsChanged, this, &MainWindow::updateFps);
connect(gameLoop, &GameLoop::askToShow, this, &MainWindow::alertOffer);
connect(gameLoop, &GameLoop::updateFramerate, this, &MainWindow::updateFramerate);

/* Create other windows */
encodeWindow = new EncodeWindow(c, this);
Expand Down Expand Up @@ -146,12 +147,12 @@ MainWindow::MainWindow(Context* c) : QMainWindow(), context(c)
/* Frames per second */
fpsNumField = new QSpinBox();
fpsNumField->setMaximum(std::numeric_limits<int>::max());
disabledWidgetsOnStart.append(fpsNumField);
connect(fpsNumField, QOverload<int>::of(&QSpinBox::valueChanged),[=](int i){context->config.sc.framerate_num = i;});

fpsDenField = new QSpinBox();
fpsDenField->setMaximum(std::numeric_limits<int>::max());
fpsDenField->setMinimum(1);
disabledWidgetsOnStart.append(fpsDenField);
connect(fpsDenField, QOverload<int>::of(&QSpinBox::valueChanged),[=](int i){context->config.sc.framerate_den = i;});

fpsValues = new QLabel("Current FPS: - / -");

Expand Down Expand Up @@ -618,6 +619,11 @@ void MainWindow::createMenus()
autoRestartAction->setToolTip("When checked, the game will automatically restart if closed, except when using the Stop button");
disabledActionsOnStart.append(autoRestartAction);

variableFramerateAction = movieMenu->addAction(tr("Variable framerate"), this, &MainWindow::slotVariableFramerate);
variableFramerateAction->setCheckable(true);
variableFramerateAction->setToolTip("When checked, you will be able to modify the framerate values during the game execution");
disabledActionsOnStart.append(variableFramerateAction);

QMenu *movieEndMenu = movieMenu->addMenu(tr("On Movie End"));
movieEndMenu->addActions(movieEndGroup->actions());
movieMenu->addAction(tr("Input Editor..."), inputEditorWindow, &InputEditorWindow::show);
Expand Down Expand Up @@ -837,6 +843,8 @@ void MainWindow::updateStatus()
movieBox->setCheckable(true);
movieBox->setChecked(context->config.sc.recording != SharedConfig::NO_RECORDING);

fpsNumField->setEnabled(true);
fpsDenField->setEnabled(true);
initialTimeSec->setValue(context->config.sc.initial_time_sec);
initialTimeNsec->setValue(context->config.sc.initial_time_nsec);

Expand Down Expand Up @@ -869,6 +877,11 @@ void MainWindow::updateStatus()
for (QAction* a : disabledActionsOnStart)
a->setEnabled(false);

if (!context->config.sc.variable_framerate) {
fpsNumField->setEnabled(false);
fpsDenField->setEnabled(false);
}

movieBox->setCheckable(false);
if (context->config.sc.recording == SharedConfig::NO_RECORDING) {
movieBox->setEnabled(false);
Expand Down Expand Up @@ -1097,6 +1110,7 @@ void MainWindow::updateMovieParams()
initialTimeSec->setValue(context->config.sc.initial_time_sec);
initialTimeNsec->setValue(context->config.sc.initial_time_nsec);
autoRestartAction->setChecked(context->config.auto_restart);
variableFramerateAction->setChecked(context->config.sc.variable_framerate);
for (auto& action : timeMainGroup->actions()) {
action->setChecked(context->config.sc.main_gettimes_threshold[action->data().toInt()] != -1);
}
Expand Down Expand Up @@ -1544,6 +1558,7 @@ BOOLSLOT(slotIncrementalState, context->config.sc.incremental_savestates)
BOOLSLOT(slotRamState, context->config.sc.savestates_in_ram)
BOOLSLOT(slotBacktrackState, context->config.sc.backtrack_savestate)
BOOLSLOT(slotAutoRestart, context->config.auto_restart)
BOOLSLOT(slotVariableFramerate, context->config.sc.variable_framerate)
BOOLSLOT(slotMouseMode, context->config.sc.mouse_mode_relative)
BOOLSLOT(slotMouseWarp, context->config.mouse_warp)

Expand All @@ -1563,3 +1578,9 @@ void MainWindow::alertDialog(QString alert_msg)
/* Show alert window */
QMessageBox::warning(this, "Warning", alert_msg);
}

void MainWindow::updateFramerate()
{
fpsNumField->setValue(context->config.sc.framerate_num);
fpsDenField->setValue(context->config.sc.framerate_den);
}
5 changes: 4 additions & 1 deletion src/program/ui/MainWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ class MainWindow : public QMainWindow
QAction *annotateMovieAction;

QAction *autoRestartAction;
QAction *variableFramerateAction;
QActionGroup *movieEndGroup;
QActionGroup *screenResGroup;

Expand Down Expand Up @@ -231,7 +232,8 @@ private slots:
/* Show an alert dialog asking the user a question, and get the answer */
void alertOffer(QString alert_msg, void* promise);


/* Update framerate values */
void updateFramerate();

void slotLaunch();
void slotStop();
Expand Down Expand Up @@ -273,6 +275,7 @@ private slots:
void slotAsyncEvents(bool checked);
void slotCalibrateMouse();
void slotAutoRestart(bool checked);
void slotVariableFramerate(bool checked);
void slotMouseMode(bool checked);
void slotMouseWarp(bool checked);
};
Expand Down
Loading

0 comments on commit 337fc9c

Please sign in to comment.