input-rec: fix running input recording functions from UI thread

This commit is contained in:
Tyler Wilding 2022-06-23 17:29:45 -04:00 committed by refractionpcsx2
parent 6ae9e7edb5
commit 844ff5bb04
6 changed files with 90 additions and 87 deletions

View File

@ -1638,7 +1638,7 @@ void MainWindow::onInputRecNewActionTriggered()
const bool wasRunning = s_vm_valid; const bool wasRunning = s_vm_valid;
if (wasRunning && !wasPaused) if (wasRunning && !wasPaused)
{ {
VMManager::SetPaused(true); g_emu_thread->setVMPaused(true);
} }
NewInputRecordingDlg dlg(this); NewInputRecordingDlg dlg(this);
@ -1646,20 +1646,26 @@ void MainWindow::onInputRecNewActionTriggered()
if (result == QDialog::Accepted) if (result == QDialog::Accepted)
{ {
if (g_InputRecording.create( Host::RunOnCPUThread([&, filePath = dlg.getFilePath(),
dlg.getFilePath(), fromSavestate = dlg.getInputRecType() == InputRecording::Type::FROM_SAVESTATE,
dlg.getInputRecType() == InputRecording::Type::FROM_SAVESTATE, authorName = dlg.getAuthorName()]() {
dlg.getAuthorName())) if (g_InputRecording.create(
{ filePath,
m_ui.actionInputRecNew->setEnabled(false); fromSavestate,
m_ui.actionInputRecStop->setEnabled(true); authorName))
return; {
} QtHost::RunOnUIThread([&]() {
m_ui.actionInputRecNew->setEnabled(false);
m_ui.actionInputRecStop->setEnabled(true);
return;
});
}
});
} }
if (wasRunning && !wasPaused) if (wasRunning && !wasPaused)
{ {
VMManager::SetPaused(false); g_emu_thread->setVMPaused(false);
} }
} }
@ -1669,7 +1675,7 @@ void MainWindow::onInputRecPlayActionTriggered()
if (!wasPaused) if (!wasPaused)
{ {
VMManager::SetPaused(true); g_emu_thread->setVMPaused(true);
} }
QFileDialog dialog(this); QFileDialog dialog(this);
@ -1685,7 +1691,7 @@ void MainWindow::onInputRecPlayActionTriggered()
{ {
if (!wasPaused) if (!wasPaused)
{ {
VMManager::SetPaused(false); g_emu_thread->setVMPaused(false);
return; return;
} }
} }
@ -1694,14 +1700,22 @@ void MainWindow::onInputRecPlayActionTriggered()
{ {
if (g_InputRecording.isActive()) if (g_InputRecording.isActive())
{ {
g_InputRecording.stop(); Host::RunOnCPUThread([]() {
g_InputRecording.stop();
});
m_ui.actionInputRecStop->setEnabled(false); m_ui.actionInputRecStop->setEnabled(false);
} }
if (g_InputRecording.play(fileNames.first().toStdString())) Host::RunOnCPUThread([&, filename = fileNames.first().toStdString()]() {
{ if (g_InputRecording.play(filename))
m_ui.actionInputRecStop->setEnabled(true); {
return; QtHost::RunOnUIThread([&]() {
} m_ui.actionInputRecStop->setEnabled(true);
return;
});
m_ui.actionInputRecStop->setEnabled(true);
return;
}
});
} }
} }
@ -1709,9 +1723,13 @@ void MainWindow::onInputRecStopActionTriggered()
{ {
if (g_InputRecording.isActive()) if (g_InputRecording.isActive())
{ {
g_InputRecording.stop(); Host::RunOnCPUThread([&]() {
m_ui.actionInputRecNew->setEnabled(true); g_InputRecording.stop();
m_ui.actionInputRecStop->setEnabled(false); QtHost::RunOnUIThread([&]() {
m_ui.actionInputRecNew->setEnabled(true);
m_ui.actionInputRecStop->setEnabled(false);
});
});
} }
} }

View File

@ -842,21 +842,11 @@ void cdvdReset()
// If we are recording, always use the same RTC setting // If we are recording, always use the same RTC setting
// for games that use the RTC to seed their RNG -- this is very important to be the same everytime! // for games that use the RTC to seed their RNG -- this is very important to be the same everytime!
#ifndef PCSX2_CORE #ifndef PCSX2_CORE
if (g_InputRecording.IsActive()) const bool input_recording_active = g_InputRecording.IsActive();
{
Console.WriteLn("Input Recording Active - Using Constant RTC of 04-03-2020 (DD-MM-YYYY)");
// Why not just 0 everything? Some games apparently require the date to be valid in terms of when
// the PS2 / Game actually came out. (MGS3). So set it to a value well beyond any PS2 game's release date.
cdvd.RTC.second = 0;
cdvd.RTC.minute = 0;
cdvd.RTC.hour = 0;
cdvd.RTC.day = 4;
cdvd.RTC.month = 3;
cdvd.RTC.year = 20;
}
else
#else #else
if (g_InputRecording.isActive()) const bool input_recording_active = g_InputRecording.isActive();
#endif
if (input_recording_active)
{ {
Console.WriteLn("Input Recording Active - Using Constant RTC of 04-03-2020 (DD-MM-YYYY)"); Console.WriteLn("Input Recording Active - Using Constant RTC of 04-03-2020 (DD-MM-YYYY)");
// Why not just 0 everything? Some games apparently require the date to be valid in terms of when // Why not just 0 everything? Some games apparently require the date to be valid in terms of when
@ -869,7 +859,6 @@ void cdvdReset()
cdvd.RTC.year = 20; cdvd.RTC.year = 20;
} }
else else
#endif
{ {
// CDVD internally uses GMT+9. If you think the time's wrong, you're wrong. // CDVD internally uses GMT+9. If you think the time's wrong, you're wrong.
// Set up your time zone and winter/summer in the BIOS. No PS2 BIOS I know of features automatic DST. // Set up your time zone and winter/summer in the BIOS. No PS2 BIOS I know of features automatic DST.

View File

@ -482,7 +482,6 @@ void ImGuiManager::DrawInputRecordingOverlay()
dl->AddText(font, font->FontSize, ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x, position_y), color, (text)); \ dl->AddText(font, font->FontSize, ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x, position_y), color, (text)); \
position_y += text_size.y + spacing; \ position_y += text_size.y + spacing; \
} while (0) } while (0)
// TODO - is there a way to forcibly update the imgui frame without requiring to wait until the next vsync?
// TODO - icon list that would be nice to add // TODO - icon list that would be nice to add
// - 'video' when screen capturing // - 'video' when screen capturing
if (g_InputRecording.isActive()) if (g_InputRecording.isActive())
@ -490,7 +489,7 @@ void ImGuiManager::DrawInputRecordingOverlay()
// Status Indicators // Status Indicators
if (g_InputRecording.getControls().isRecording()) if (g_InputRecording.getControls().isRecording())
{ {
DRAW_LINE(standard_font, fmt::format("{} Recording", ICON_FA_CIRCLE).c_str(), IM_COL32(255, 0, 0, 255)); DRAW_LINE(standard_font, fmt::format("{} Recording", ICON_FA_RECORD_VINYL).c_str(), IM_COL32(255, 0, 0, 255));
} }
else else
{ {

View File

@ -517,17 +517,17 @@ bool InputRecording::create(const std::string_view& fileName, const bool fromSav
{ {
FileSystem::CopyFilePath(savestatePath.c_str(), fmt::format("{}.bak", savestatePath).c_str(), true); FileSystem::CopyFilePath(savestatePath.c_str(), fmt::format("{}.bak", savestatePath).c_str(), true);
} }
m_initialSavestateLoadComplete = false; m_initial_savestate_load_complete = false;
m_type = Type::FROM_SAVESTATE; m_type = Type::FROM_SAVESTATE;
m_isActive = true; m_is_active = true;
// TODO - error handling // TODO - error handling
VMManager::SaveState(savestatePath.c_str()); VMManager::SaveState(savestatePath.c_str());
} }
else else
{ {
m_startingFrame = 0; m_starting_frame = 0;
m_type = Type::POWER_ON; m_type = Type::POWER_ON;
m_isActive = true; m_is_active = true;
// TODO - should this be an explicit [full] boot instead of a reset? // TODO - should this be an explicit [full] boot instead of a reset?
VMManager::Reset(); VMManager::Reset();
} }
@ -561,22 +561,22 @@ bool InputRecording::play(const std::string_view& filename)
return false; return false;
} }
m_type = Type::FROM_SAVESTATE; m_type = Type::FROM_SAVESTATE;
m_initialSavestateLoadComplete = false; m_initial_savestate_load_complete = false;
m_isActive = true; m_is_active = true;
const auto loaded = VMManager::LoadState(savestatePath.c_str()); const auto loaded = VMManager::LoadState(savestatePath.c_str());
if (!loaded) if (!loaded)
{ {
InputRec::log("Savestate load failed, unsupported version?"); InputRec::log("Savestate load failed, unsupported version?");
m_file.Close(); m_file.Close();
m_isActive = false; m_is_active = false;
return false; return false;
} }
} }
else else
{ {
m_startingFrame = 0; m_starting_frame = 0;
m_type = Type::POWER_ON; m_type = Type::POWER_ON;
m_isActive = true; m_is_active = true;
// TODO - should this be an explicit [full] boot instead of a reset? // TODO - should this be an explicit [full] boot instead of a reset?
VMManager::Reset(); VMManager::Reset();
} }
@ -594,7 +594,7 @@ bool InputRecording::play(const std::string_view& filename)
void InputRecording::stop() void InputRecording::stop()
{ {
m_isActive = false; m_is_active = false;
if (m_file.Close()) if (m_file.Close())
{ {
InputRec::log("Input recording stopped"); InputRec::log("Input recording stopped");
@ -614,15 +614,15 @@ void InputRecording::ControllerInterrupt(u8 port, size_t fifoSize, u8 dataIn, u8
} }
// If there is data to read (previous two bytes looked correct) // If there is data to read (previous two bytes looked correct)
if (bufCount >= 3 && m_padDataAvailable) if (bufCount >= 3 && m_pad_data_available)
{ {
u8& bufVal = dataOut; u8& bufVal = dataOut;
const u16 bufIndex = fifoSize - 3; const u16 bufIndex = fifoSize - 3;
if (state == InputRecordingMode::Replaying) if (state == InputRecordingMode::Replaying)
{ {
if (!m_file.ReadKeyBuffer(bufVal, m_frameCounter, port, bufIndex)) if (!m_file.ReadKeyBuffer(bufVal, m_frame_counter, port, bufIndex))
{ {
InputRec::consoleLog(fmt::format("Failed to read input data at frame {}", m_frameCounter)); InputRec::consoleLog(fmt::format("Failed to read input data at frame {}", m_frame_counter));
} }
// Update controller data state for future VirtualPad / logging usage. // Update controller data state for future VirtualPad / logging usage.
//pads[port].padData->UpdateControllerData(bufIndex, bufVal); //pads[port].padData->UpdateControllerData(bufIndex, bufVal);
@ -635,16 +635,16 @@ void InputRecording::ControllerInterrupt(u8 port, size_t fifoSize, u8 dataIn, u8
// Commit the byte to the movie file if we are recording // Commit the byte to the movie file if we are recording
if (m_controls.isRecording()) if (m_controls.isRecording())
{ {
if (!m_file.WriteKeyBuffer(m_frameCounter, port, bufIndex, bufVal)) if (!m_file.WriteKeyBuffer(m_frame_counter, port, bufIndex, bufVal))
{ {
InputRec::consoleLog(fmt::format("Failed to write input data at frame {}", m_frameCounter)); InputRec::consoleLog(fmt::format("Failed to write input data at frame {}", m_frame_counter));
} }
} }
} }
} }
if (bufCount > 20) if (bufCount > 20)
{ {
m_padDataAvailable = false; m_pad_data_available = false;
} }
} }
@ -665,51 +665,51 @@ std::string InputRecording::resolveGameName()
void InputRecording::incFrameCounter() void InputRecording::incFrameCounter()
{ {
if (m_frameCounter >= std::numeric_limits<u64>::max()) if (m_frame_counter >= std::numeric_limits<u64>::max())
{ {
// TODO - log the incredible achievment of playing for longer than 4 billion years, and end the recording // TODO - log the incredible achievment of playing for longer than 4 billion years, and end the recording
stop(); stop();
return; return;
} }
m_frameCounter++; m_frame_counter++;
if (m_controls.isReplaying()) if (m_controls.isReplaying())
{ {
// If we've reached the end of the recording while replaying, pause // If we've reached the end of the recording while replaying, pause
if (m_frameCounter == m_file.getTotalFrames() - 1) if (m_frame_counter == m_file.getTotalFrames() - 1)
{ {
VMManager::SetPaused(true); VMManager::SetPaused(true);
// Can also stop watching for re-records, they've watched to the end of the recording // Can also stop watching for re-records, they've watched to the end of the recording
m_watchingForRerecords = false; m_watching_for_rerecords = false;
} }
} }
if (m_controls.isRecording()) if (m_controls.isRecording())
{ {
m_file.SetTotalFrames(m_frameCounter); m_file.SetTotalFrames(m_frame_counter);
// If we've been in record mode and moved to the next frame, we've overrote something // If we've been in record mode and moved to the next frame, we've overrote something
// if this was following a save-state loading, this is considered a re-record, a.k.a an undo // if this was following a save-state loading, this is considered a re-record, a.k.a an undo
if (m_watchingForRerecords) if (m_watching_for_rerecords)
{ {
m_file.IncrementUndoCount(); m_file.IncrementUndoCount();
m_watchingForRerecords = false; m_watching_for_rerecords = false;
} }
} }
} }
u64 InputRecording::getFrameCounter() const u64 InputRecording::getFrameCounter() const
{ {
return m_frameCounter; return m_frame_counter;
} }
bool InputRecording::isActive() const bool InputRecording::isActive() const
{ {
return m_isActive; return m_is_active;
} }
void InputRecording::handleExceededFrameCounter() void InputRecording::handleExceededFrameCounter()
{ {
// if we go past the end, switch to recording mode so nothing is lost // if we go past the end, switch to recording mode so nothing is lost
if (m_frameCounter >= m_file.getTotalFrames() && m_controls.isReplaying()) if (m_frame_counter >= m_file.getTotalFrames() && m_controls.isReplaying())
{ {
m_controls.setRecordMode(false); m_controls.setRecordMode(false);
} }
@ -724,15 +724,15 @@ void InputRecording::handleLoadingSavestate()
// Why? // Why?
// - When you re-record you load another save-state which has it's own frame counter // - When you re-record you load another save-state which has it's own frame counter
// stored within, we use this to adjust the frame we are replaying/recording to // stored within, we use this to adjust the frame we are replaying/recording to
if (isTypeSavestate() && !m_initialSavestateLoadComplete) if (isTypeSavestate() && !m_initial_savestate_load_complete)
{ {
setStartingFrame(g_FrameCount); setStartingFrame(g_FrameCount);
m_initialSavestateLoadComplete = true; m_initial_savestate_load_complete = true;
} }
else else
{ {
adjustFrameCounterOnReRecord(g_FrameCount); adjustFrameCounterOnReRecord(g_FrameCount);
m_watchingForRerecords = true; m_watching_for_rerecords = true;
} }
} }
@ -748,27 +748,27 @@ void InputRecording::setStartingFrame(u64 startingFrame)
return; return;
} }
InputRec::consoleLog(fmt::format("Internal Starting Frame: {}", startingFrame)); InputRec::consoleLog(fmt::format("Internal Starting Frame: {}", startingFrame));
m_startingFrame = startingFrame; m_starting_frame = startingFrame;
} }
void InputRecording::adjustFrameCounterOnReRecord(u64 newFrameCounter) void InputRecording::adjustFrameCounterOnReRecord(u64 newFrameCounter)
{ {
if (newFrameCounter > m_startingFrame + (u64)m_file.getTotalFrames()) if (newFrameCounter > m_starting_frame + (u64)m_file.getTotalFrames())
{ {
InputRec::consoleLog("Warning, you've loaded PCSX2 emulation to a point after the end of the original recording. This should be avoided."); InputRec::consoleLog("Warning, you've loaded PCSX2 emulation to a point after the end of the original recording. This should be avoided.");
InputRec::consoleLog("Savestate's framecount has been ignored, using the max length of the recording instead."); InputRec::consoleLog("Savestate's framecount has been ignored, using the max length of the recording instead.");
m_frameCounter = m_file.getTotalFrames(); m_frame_counter = m_file.getTotalFrames();
if (getControls().isReplaying()) if (getControls().isReplaying())
{ {
getControls().setRecordMode(); getControls().setRecordMode();
} }
return; return;
} }
if (newFrameCounter < m_startingFrame) if (newFrameCounter < m_starting_frame)
{ {
InputRec::consoleLog("Warning, you've loaded PCSX2 emulation to a point before the start of the original recording. This should be avoided."); InputRec::consoleLog("Warning, you've loaded PCSX2 emulation to a point before the start of the original recording. This should be avoided.");
InputRec::consoleLog("Savestate's framecount has been ignored, starting from the beginning in replay mode."); InputRec::consoleLog("Savestate's framecount has been ignored, starting from the beginning in replay mode.");
m_frameCounter = 0; m_frame_counter = 0;
if (getControls().isRecording()) if (getControls().isRecording())
{ {
getControls().setReplayMode(); getControls().setReplayMode();
@ -779,7 +779,7 @@ void InputRecording::adjustFrameCounterOnReRecord(u64 newFrameCounter)
{ {
getControls().setReplayMode(); getControls().setReplayMode();
} }
m_frameCounter = newFrameCounter - m_startingFrame; m_frame_counter = newFrameCounter - m_starting_frame;
} }
InputRecordingControls& InputRecording::getControls() InputRecordingControls& InputRecording::getControls()
@ -794,8 +794,8 @@ const InputRecordingFile& InputRecording::getData() const
void InputRecording::initializeState() void InputRecording::initializeState()
{ {
m_frameCounter = 0; m_frame_counter = 0;
m_watchingForRerecords = false; m_watching_for_rerecords = false;
} }
#endif #endif

View File

@ -194,14 +194,14 @@ private:
Type m_type; Type m_type;
bool m_initialSavestateLoadComplete = false; bool m_initial_savestate_load_complete = false;
bool m_isActive = false; bool m_is_active = false;
bool m_padDataAvailable = false; bool m_pad_data_available = false;
bool m_watchingForRerecords = false; bool m_watching_for_rerecords = false;
u64 m_frameCounter = 0; u64 m_frame_counter = 0;
// Either 0 for a power-on movie, or the g_FrameCount that is stored on the starting frame // Either 0 for a power-on movie, or the g_FrameCount that is stored on the starting frame
u64 m_startingFrame = 0; u64 m_starting_frame = 0;
void initializeState(); void initializeState();
void setStartingFrame(u64 startingFrame); void setStartingFrame(u64 startingFrame);

View File

@ -227,13 +227,10 @@ void InputRecordingControls::StopCapture() const
#include "VMManager.h" #include "VMManager.h"
// TODO - update ImGUI when controls are changed - `GetMTGS().PresentCurrentFrame()`, becareful about only running it on the emu/cpu thread
void InputRecordingControls::toggleRecordMode() void InputRecordingControls::toggleRecordMode()
{ {
// TODO - this needs to be fixed
// NOTE - delete logic here that prevented switching to replay immediately until frame was complete
// Has to be a better new way to do such a thing
//
// Set a lambda (or list of lambdas) to be executed on the next vsync perhaps?
if (isReplaying()) if (isReplaying())
{ {
setRecordMode(); setRecordMode();