From f8eb9a541d26380693c2215eb42b01b210d54f67 Mon Sep 17 00:00:00 2001 From: zhupengfei Date: Thu, 27 Aug 2020 22:27:29 +0800 Subject: [PATCH] citra_qt: Fix potential indeterminstism caused by starting record/playback Previously the movie was started *after* core starts running, causing potential indeterminism. Some desyncs are still not fixed; they may be caused by core timing. More investigation is required. --- src/citra_qt/main.cpp | 80 ++++++++++++++++++++++++------------------- src/citra_qt/main.h | 5 ++- src/core/movie.cpp | 7 ++-- src/core/movie.h | 4 ++- 4 files changed, 57 insertions(+), 39 deletions(-) diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index edea64511..ec57d0e5b 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -1018,6 +1018,9 @@ void GMainWindow::BootGame(const QString& filename) { if (movie_record_on_start) { Core::Movie::GetInstance().PrepareForRecording(); } + if (movie_playback_on_start) { + Core::Movie::GetInstance().PrepareForPlayback(movie_playback_path.toStdString()); + } // Save configurations UpdateUISettings(); @@ -1027,6 +1030,42 @@ void GMainWindow::BootGame(const QString& filename) { if (!LoadROM(filename)) return; + // Set everything up + if (movie_record_on_start) { + Core::Movie::GetInstance().StartRecording(movie_record_path.toStdString(), + movie_record_author.toStdString()); + movie_record_on_start = false; + movie_record_path.clear(); + movie_record_author.clear(); + } + if (movie_playback_on_start) { + Core::Movie::GetInstance().StartPlayback(movie_playback_path.toStdString()); + movie_playback_on_start = false; + movie_playback_path.clear(); + } + + if (ui->action_Enable_Frame_Advancing->isChecked()) { + ui->action_Advance_Frame->setEnabled(true); + Core::System::GetInstance().frame_limiter.SetFrameAdvancing(true); + } else { + ui->action_Advance_Frame->setEnabled(false); + } + + if (video_dumping_on_start) { + Layout::FramebufferLayout layout{ + Layout::FrameLayoutFromResolutionScale(VideoCore::GetResolutionScaleFactor())}; + if (!Core::System::GetInstance().VideoDumper().StartDumping( + video_dumping_path.toStdString(), layout)) { + + QMessageBox::critical( + this, tr("Citra"), + tr("Could not start video dumping.
Refer to the log for details.")); + ui->action_Dump_Video->setChecked(false); + } + video_dumping_on_start = false; + video_dumping_path.clear(); + } + // Create and start the emulation thread emu_thread = std::make_unique(*render_window); emit EmulationStarting(emu_thread.get()); @@ -1076,35 +1115,6 @@ void GMainWindow::BootGame(const QString& filename) { ShowFullscreen(); } - if (movie_record_on_start) { - Core::Movie::GetInstance().StartRecording(movie_record_path.toStdString(), - movie_record_author.toStdString()); - movie_record_on_start = false; - movie_record_path.clear(); - movie_record_author.clear(); - } - - if (ui->action_Enable_Frame_Advancing->isChecked()) { - ui->action_Advance_Frame->setEnabled(true); - Core::System::GetInstance().frame_limiter.SetFrameAdvancing(true); - } else { - ui->action_Advance_Frame->setEnabled(false); - } - - if (video_dumping_on_start) { - Layout::FramebufferLayout layout{ - Layout::FrameLayoutFromResolutionScale(VideoCore::GetResolutionScaleFactor())}; - if (!Core::System::GetInstance().VideoDumper().StartDumping( - video_dumping_path.toStdString(), layout)) { - - QMessageBox::critical( - this, tr("Citra"), - tr("Could not start video dumping.
Refer to the log for details.")); - ui->action_Dump_Video->setChecked(false); - } - video_dumping_on_start = false; - video_dumping_path.clear(); - } OnStartGame(); } @@ -1128,7 +1138,6 @@ void GMainWindow::ShutdownGame() { AllowOSSleep(); discord_rpc->Pause(); - OnCloseMovie(true); emu_thread->RequestStop(); // Release emu threads from any breakpoints @@ -1147,6 +1156,8 @@ void GMainWindow::ShutdownGame() { emu_thread->wait(); emu_thread = nullptr; + OnCloseMovie(); + discord_rpc->Update(); Camera::QtMultimediaCameraHandler::ReleaseHandlers(); @@ -1875,22 +1886,21 @@ void GMainWindow::OnPlayMovie() { return; } - const auto movie_path = dialog.GetMoviePath().toStdString(); - Core::Movie::GetInstance().PrepareForPlayback(movie_path); + movie_playback_on_start = true; + movie_playback_path = dialog.GetMoviePath(); BootGame(dialog.GetGamePath()); - Core::Movie::GetInstance().StartPlayback(movie_path); ui->action_Close_Movie->setEnabled(true); } -void GMainWindow::OnCloseMovie(bool shutting_down) { +void GMainWindow::OnCloseMovie() { if (movie_record_on_start) { QMessageBox::information(this, tr("Record Movie"), tr("Movie recording cancelled.")); movie_record_on_start = false; movie_record_path.clear(); movie_record_author.clear(); } else { - const bool was_running = !shutting_down && emu_thread && emu_thread->IsRunning(); + const bool was_running = emu_thread && emu_thread->IsRunning(); if (was_running) { OnPauseGame(); } diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index 99451d308..9b29edd2a 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h @@ -208,7 +208,7 @@ private slots: void OnCreateGraphicsSurfaceViewer(); void OnRecordMovie(); void OnPlayMovie(); - void OnCloseMovie(bool shutting_down = false); + void OnCloseMovie(); void OnCaptureScreenshot(); #ifdef ENABLE_FFMPEG_VIDEO_DUMPER void OnStartVideoDumping(); @@ -269,6 +269,9 @@ private: QString movie_record_path; QString movie_record_author; + bool movie_playback_on_start = false; + QString movie_playback_path; + // Video dumping bool video_dumping_on_start = false; QString video_dumping_path; diff --git a/src/core/movie.cpp b/src/core/movie.cpp index f96842d5b..109cf08dd 100644 --- a/src/core/movie.cpp +++ b/src/core/movie.cpp @@ -491,6 +491,7 @@ void Movie::SaveMovie() { CTMHeader header = {}; header.filetype = header_magic_bytes; + header.program_id = program_id; header.clock_init_time = init_time; header.id = id; @@ -500,8 +501,6 @@ void Movie::SaveMovie() { header.rerecord_count = rerecord_count; header.input_count = GetInputCount(recorded_input); - Core::System::GetInstance().GetAppLoader().ReadProgramId(header.program_id); - std::string rev_bytes; CryptoPP::StringSource(Common::g_scm_rev, true, new CryptoPP::HexDecoder(new CryptoPP::StringSink(rev_bytes))); @@ -562,6 +561,10 @@ void Movie::StartRecording(const std::string& movie_file, const std::string& aut CryptoPP::AutoSeededRandomPool rng; rng.GenerateBlock(reinterpret_cast(&id), sizeof(id)); + // Get program ID + program_id = 0; + Core::System::GetInstance().GetAppLoader().ReadProgramId(program_id); + LOG_INFO(Movie, "Enabling Movie recording, ID: {:016X}", id); } diff --git a/src/core/movie.h b/src/core/movie.h index 15cd631ca..0d1b689df 100644 --- a/src/core/movie.h +++ b/src/core/movie.h @@ -159,6 +159,8 @@ private: std::string record_movie_file; std::string record_movie_author; + u64 init_time; // Clock init time override for RNG consistency + std::vector recorded_input; std::size_t current_byte = 0; u64 current_input = 0; @@ -166,7 +168,7 @@ private: u64 total_input = 0; u64 id = 0; // ID of the current movie loaded - u64 init_time; + u64 program_id = 0; u32 rerecord_count = 1; bool read_only = true;