From cb2b94b82cfa4fb5845d07a8f7babd9286eed0de Mon Sep 17 00:00:00 2001 From: TheTechnician27 Date: Wed, 8 Jan 2025 16:08:56 -0600 Subject: [PATCH] ImGui: Avoid frame count display race condition for input recording and display correct value --- pcsx2/ImGui/ImGuiOverlays.cpp | 10 ++++--- pcsx2/ImGui/ImGuiOverlays.h | 13 +++++++++ pcsx2/Recording/InputRecording.cpp | 39 ++++++++++++++++++++++++-- pcsx2/Recording/InputRecording.h | 9 ++++-- pcsx2/Recording/InputRecordingFile.cpp | 7 ++++- 5 files changed, 69 insertions(+), 9 deletions(-) diff --git a/pcsx2/ImGui/ImGuiOverlays.cpp b/pcsx2/ImGui/ImGuiOverlays.cpp index e67c3184c2..c2bfd722c0 100644 --- a/pcsx2/ImGui/ImGuiOverlays.cpp +++ b/pcsx2/ImGui/ImGuiOverlays.cpp @@ -47,6 +47,8 @@ #include #include +InputRecordingUI::InputRecordingData g_InputRecordingData; + namespace ImGuiManager { static void FormatProcessorStat(SmallStringBase& text, double usage, double time); @@ -680,7 +682,7 @@ __ri void ImGuiManager::DrawInputRecordingOverlay(float& position_y, float scale } while (0) // Status Indicators - if (g_InputRecording.getControls().isRecording()) + if (g_InputRecordingData.is_recording) { DRAW_LINE(standard_font, TinyString::from_format(TRANSLATE_FS("ImGuiOverlays", "{} Recording Input"), ICON_PF_CIRCLE).c_str(), IM_COL32(255, 0, 0, 255)); } @@ -690,9 +692,9 @@ __ri void ImGuiManager::DrawInputRecordingOverlay(float& position_y, float scale } // Input Recording Metadata - DRAW_LINE(fixed_font, TinyString::from_format(TRANSLATE_FS("ImGuiOverlays", "Input Recording Active: {}"), g_InputRecording.getData().getFilename()).c_str(), IM_COL32(117, 255, 241, 255)); - DRAW_LINE(fixed_font, TinyString::from_format(TRANSLATE_FS("ImGuiOverlays", "Frame: {}/{} ({})"), g_InputRecording.getFrameCounter() + 1, g_InputRecording.getData().getTotalFrames(), g_FrameCount).c_str(), IM_COL32(117, 255, 241, 255)); - DRAW_LINE(fixed_font, TinyString::from_format(TRANSLATE_FS("ImGuiOverlays", "Undo Count: {}"), g_InputRecording.getData().getUndoCount()).c_str(), IM_COL32(117, 255, 241, 255)); + DRAW_LINE(fixed_font, g_InputRecordingData.recording_active_message.c_str(), IM_COL32(117, 255, 241, 255)); + DRAW_LINE(fixed_font, g_InputRecordingData.frame_data_message.c_str(), IM_COL32(117, 255, 241, 255)); + DRAW_LINE(fixed_font, g_InputRecordingData.undo_count_message.c_str(), IM_COL32(117, 255, 241, 255)); #undef DRAW_LINE } diff --git a/pcsx2/ImGui/ImGuiOverlays.h b/pcsx2/ImGui/ImGuiOverlays.h index 05f856066c..95951e895d 100644 --- a/pcsx2/ImGui/ImGuiOverlays.h +++ b/pcsx2/ImGui/ImGuiOverlays.h @@ -28,3 +28,16 @@ namespace SaveStateSelectorUI void LoadCurrentSlot(); void SaveCurrentSlot(); } // namespace SaveStateSelectorUI + +namespace InputRecordingUI +{ + struct InputRecordingData + { + bool is_recording = false; + TinyString recording_active_message = ""; + TinyString frame_data_message = ""; + TinyString undo_count_message = ""; + }; +} + +extern InputRecordingUI::InputRecordingData g_InputRecordingData; diff --git a/pcsx2/Recording/InputRecording.cpp b/pcsx2/Recording/InputRecording.cpp index ceb57857a2..41f0d31124 100644 --- a/pcsx2/Recording/InputRecording.cpp +++ b/pcsx2/Recording/InputRecording.cpp @@ -26,6 +26,8 @@ bool SaveStateBase::InputRecordingFreeze() #include "Counters.h" #include "SaveState.h" #include "VMManager.h" +#include "Host.h" +#include "ImGui/ImGuiOverlays.h" #include "DebugTools/Debug.h" #include "GameDatabase.h" #include "fmt/format.h" @@ -237,8 +239,9 @@ void InputRecording::incFrameCounter() if (m_controls.isReplaying()) { + InformGSThread(); // If we've reached the end of the recording while replaying, pause - if (m_frame_counter == m_file.getTotalFrames() - 1) + if (m_frame_counter == m_file.getTotalFrames()) { VMManager::SetPaused(true); // Can also stop watching for re-records, they've watched to the end of the recording @@ -247,6 +250,7 @@ void InputRecording::incFrameCounter() } if (m_controls.isRecording()) { + m_frame_counter_stateless++; m_file.setTotalFrames(m_frame_counter); // 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 @@ -255,14 +259,20 @@ void InputRecording::incFrameCounter() m_file.incrementUndoCount(); m_watching_for_rerecords = false; } + InformGSThread(); } } -u64 InputRecording::getFrameCounter() const +u32 InputRecording::getFrameCounter() const { return m_frame_counter; } +u32 InputRecording::getFrameCounterStateless() const +{ + return m_frame_counter_stateless; +} + bool InputRecording::isActive() const { return m_is_active; @@ -320,6 +330,12 @@ void InputRecording::setStartingFrame(u32 startingFrame) } InputRec::consoleLog(fmt::format("Internal Starting Frame: {}", startingFrame)); m_starting_frame = startingFrame; + InformGSThread(); +} + +u32 InputRecording::getStartingFrame() +{ + return m_starting_frame; } void InputRecording::adjustFrameCounterOnReRecord(u32 newFrameCounter) @@ -351,6 +367,9 @@ void InputRecording::adjustFrameCounterOnReRecord(u32 newFrameCounter) getControls().setReplayMode(); } m_frame_counter = newFrameCounter - m_starting_frame; + m_frame_counter_stateless--; + m_file.setTotalFrames(m_frame_counter); + InformGSThread(); } InputRecordingControls& InputRecording::getControls() @@ -367,4 +386,20 @@ void InputRecording::initializeState() { m_frame_counter = 0; m_watching_for_rerecords = false; + InformGSThread(); +} + +void InputRecording::InformGSThread() +{ + TinyString recording_active_message = TinyString::from_format(TRANSLATE_FS("InputRecording", "Input Recording Active: {}"), g_InputRecording.getData().getFilename()); + TinyString frame_data_message = TinyString::from_format(TRANSLATE_FS("InputRecording", "Frame: {}/{} ({})"), g_InputRecording.getFrameCounter(), g_InputRecording.getData().getTotalFrames(), g_InputRecording.getFrameCounterStateless()); + TinyString undo_count_message = TinyString::from_format(TRANSLATE_FS("InputRecording", "Undo Count: {}"), g_InputRecording.getData().getUndoCount()); + + MTGS::RunOnGSThread([recording_active_message, frame_data_message, undo_count_message](bool is_recording = g_InputRecording.getControls().isRecording()) + { + g_InputRecordingData.is_recording = is_recording; + g_InputRecordingData.recording_active_message = recording_active_message; + g_InputRecordingData.frame_data_message = frame_data_message; + g_InputRecordingData.undo_count_message = undo_count_message; + }); } diff --git a/pcsx2/Recording/InputRecording.h b/pcsx2/Recording/InputRecording.h index 36d1a7e8b9..78360a0d0f 100644 --- a/pcsx2/Recording/InputRecording.h +++ b/pcsx2/Recording/InputRecording.h @@ -22,14 +22,19 @@ public: bool play(const std::string& path); void stop(); + static void InformGSThread(); void handleControllerDataUpdate(); void saveControllerData(const PadData& data, const int port, const int slot); std::optional updateControllerData(const int port, const int slot); void incFrameCounter(); - u64 getFrameCounter() const; + u32 getFrameCounter() const; + u32 getFrameCounterStateless() const; bool isActive() const; void processRecordQueue(); + void setStartingFrame(u32 startingFrame); + u32 getStartingFrame(); + void handleExceededFrameCounter(); void handleReset(); void handleLoadingSavestate(); @@ -53,11 +58,11 @@ private: std::queue> m_recordingQueue; u32 m_frame_counter = 0; + u32 m_frame_counter_stateless = 0; // Either 0 for a power-on movie, or the g_FrameCount that is stored on the starting frame u32 m_starting_frame = 0; void initializeState(); - void setStartingFrame(u32 startingFrame); void closeActiveFile(); }; diff --git a/pcsx2/Recording/InputRecordingFile.cpp b/pcsx2/Recording/InputRecordingFile.cpp index b3c5fbd1c5..b3fc77e933 100644 --- a/pcsx2/Recording/InputRecordingFile.cpp +++ b/pcsx2/Recording/InputRecordingFile.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-3.0+ #include "InputRecordingFile.h" +#include "InputRecording.h" #include "BuildVersion.h" #include "Utilities/InputRecordingLogger.h" @@ -89,6 +90,7 @@ void InputRecordingFile::incrementUndoCount() } fseek(m_recordingFile, s_seekpointUndoCount, SEEK_SET); fwrite(&m_undoCount, 4, 1, m_recordingFile); + InputRecording::InformGSThread(); } bool InputRecordingFile::openNew(const std::string& path, bool fromSavestate) @@ -104,6 +106,7 @@ bool InputRecordingFile::openNew(const std::string& path, bool fromSavestate) m_undoCount = 0; m_header.init(); m_savestate = fromSavestate; + InputRecording::InformGSThread(); return true; } @@ -123,6 +126,7 @@ bool InputRecordingFile::openExisting(const std::string& path) } m_filename = path; + InputRecording::InformGSThread(); return true; } @@ -147,13 +151,14 @@ std::optional InputRecordingFile::readPadData(const uint frame, const u void InputRecordingFile::setTotalFrames(u32 frame) { - if (m_recordingFile == nullptr || m_totalFrames >= frame) + if (m_recordingFile == nullptr) { return; } m_totalFrames = frame; fseek(m_recordingFile, s_seekpointTotalFrames, SEEK_SET); fwrite(&m_totalFrames, 4, 1, m_recordingFile); + InputRecording::InformGSThread(); } bool InputRecordingFile::writeHeader() const