From 76adf9000e78515d151bf685c0867cf58fc9b150 Mon Sep 17 00:00:00 2001 From: R2DLiu Date: Tue, 14 Jul 2020 23:35:47 -0400 Subject: [PATCH] sort of working save states --- Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp | 2 +- Source/Core/Core/Host.h | 1 + Source/Core/Core/Slippi/SlippiPlayback.cpp | 16 ++--- Source/Core/Core/Slippi/SlippiPlayback.h | 2 +- Source/Core/DolphinNoGUI/MainNoGUI.cpp | 1 + Source/Core/DolphinQt/Host.cpp | 5 ++ Source/Core/DolphinQt/Host.h | 1 + Source/Core/DolphinQt/RenderWidget.cpp | 9 +++ Source/Core/DolphinQt/RenderWidget.h | 1 + Source/Core/VideoCommon/OnScreenDisplay.cpp | 63 +++++++++++++------- Source/Core/VideoCommon/RenderBase.cpp | 4 +- 11 files changed, 74 insertions(+), 31 deletions(-) diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp index f02cae680e..2d42187359 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp @@ -2008,7 +2008,7 @@ void CEXISlippi::handleUpdateAppRequest() #ifdef __APPLE__ CriticalAlertT("Automatic updates are not available for macOS, please update manually."); #else - // main_frame->LowerRenderWindow(); SLIPPITODO: figure out replacement // mainwindow hide render widget + Host_LowerWindow(); user->UpdateApp(); Host_Exit(); #endif diff --git a/Source/Core/Core/Host.h b/Source/Core/Core/Host.h index 6eb4b64342..ea733afb8e 100644 --- a/Source/Core/Core/Host.h +++ b/Source/Core/Core/Host.h @@ -46,3 +46,4 @@ void Host_YieldToUI(); void Host_TitleChanged(); void Host_LowerWindow(); void Host_Exit(); +void Host_PlaybackSeek(); diff --git a/Source/Core/Core/Slippi/SlippiPlayback.cpp b/Source/Core/Core/Slippi/SlippiPlayback.cpp index b7a2bd392a..7d08a5dfbf 100644 --- a/Source/Core/Core/Slippi/SlippiPlayback.cpp +++ b/Source/Core/Core/Slippi/SlippiPlayback.cpp @@ -175,11 +175,11 @@ void SlippiPlaybackStatus::SavestateThread() INFO_LOG(SLIPPI, "Exiting savestate thread"); } -void SlippiPlaybackStatus::SeekToFrame(s32 frameNum) +void SlippiPlaybackStatus::SeekToFrame() { if (seekMtx.try_lock()) { - if (frameNum < Slippi::PLAYBACK_FIRST_SAVE || frameNum > lastFrame) { - INFO_LOG(SLIPPI, "Error: Invalid seek to frame: %d", frameNum); + if (targetFrameNum < Slippi::PLAYBACK_FIRST_SAVE || targetFrameNum > lastFrame) { + INFO_LOG(SLIPPI, "Error: Invalid seek to frame: %d", targetFrameNum); seekMtx.unlock(); return; } @@ -193,13 +193,14 @@ void SlippiPlaybackStatus::SeekToFrame(s32 frameNum) if (prevState != Core::State::Paused) Core::SetState(Core::State::Paused); - s32 closestStateFrame = frameNum - emod(frameNum - Slippi::PLAYBACK_FIRST_SAVE, FRAME_INTERVAL); - bool isLoadingStateOptimal = frameNum < currentPlaybackFrame || closestStateFrame > currentPlaybackFrame; + s32 closestStateFrame = targetFrameNum - emod(targetFrameNum - Slippi::PLAYBACK_FIRST_SAVE, FRAME_INTERVAL); + bool isLoadingStateOptimal = targetFrameNum < currentPlaybackFrame || closestStateFrame > currentPlaybackFrame; if (isLoadingStateOptimal) { if (closestStateFrame <= Slippi::PLAYBACK_FIRST_SAVE) { State::LoadFromBuffer(iState); + INFO_LOG(SLIPPI, "loaded a state"); } else { @@ -216,18 +217,17 @@ void SlippiPlaybackStatus::SeekToFrame(s32 frameNum) } // Fastforward until we get to the frame we want - if (frameNum != closestStateFrame && frameNum != lastFrame) + if (targetFrameNum != closestStateFrame && targetFrameNum != lastFrame) { isHardFFW = true; SConfig::GetInstance().m_OCEnable = true; SConfig::GetInstance().m_OCFactor = 4.0f; - Core::SetState(Core::State::Running); cv_waitingForTargetFrame.wait(ffwLock); Core::SetState(Core::State::Paused); - SConfig::GetInstance().m_OCFactor = 1.0f; SConfig::GetInstance().m_OCEnable = false; + targetFrameNum = INT_MAX; isHardFFW = false; } diff --git a/Source/Core/Core/Slippi/SlippiPlayback.h b/Source/Core/Core/Slippi/SlippiPlayback.h index 52d781a06d..6da63a6854 100644 --- a/Source/Core/Core/Slippi/SlippiPlayback.h +++ b/Source/Core/Core/Slippi/SlippiPlayback.h @@ -32,10 +32,10 @@ public: void startThreads(void); void resetPlayback(void); void prepareSlippiPlayback(s32& frameIndex); + void SeekToFrame(); private: void SavestateThread(void); - void SeekToFrame(s32 targetFrameNum); void processInitialState(); void clearWatchSettingsStartEnd(); diff --git a/Source/Core/DolphinNoGUI/MainNoGUI.cpp b/Source/Core/DolphinNoGUI/MainNoGUI.cpp index db0e00c29b..1d13e915b7 100644 --- a/Source/Core/DolphinNoGUI/MainNoGUI.cpp +++ b/Source/Core/DolphinNoGUI/MainNoGUI.cpp @@ -108,6 +108,7 @@ void Host_TitleChanged() void Host_LowerWindow() {} void Host_Exit() {} +void Host_PlaybackSeek() {} static std::unique_ptr GetPlatform(const optparse::Values& options) { diff --git a/Source/Core/DolphinQt/Host.cpp b/Source/Core/DolphinQt/Host.cpp index d8e301d3e4..bdfc1ba328 100644 --- a/Source/Core/DolphinQt/Host.cpp +++ b/Source/Core/DolphinQt/Host.cpp @@ -183,3 +183,8 @@ void Host_Exit() { Host::GetInstance()->RequestExit(); } + +void Host_PlaybackSeek() +{ + Host::GetInstance()->RequestSeek(); +} diff --git a/Source/Core/DolphinQt/Host.h b/Source/Core/DolphinQt/Host.h index 7b9d39273d..6b1ade8a53 100644 --- a/Source/Core/DolphinQt/Host.h +++ b/Source/Core/DolphinQt/Host.h @@ -39,6 +39,7 @@ signals: void NotifyMapLoaded(); void RequestLowerWindow(); void RequestExit(); + void RequestSeek(); private: Host(); diff --git a/Source/Core/DolphinQt/RenderWidget.cpp b/Source/Core/DolphinQt/RenderWidget.cpp index ba572fbe00..1b8123fa6d 100644 --- a/Source/Core/DolphinQt/RenderWidget.cpp +++ b/Source/Core/DolphinQt/RenderWidget.cpp @@ -30,6 +30,7 @@ #include "Core/ConfigManager.h" #include "Core/Core.h" #include "Core/State.h" +#include "Core/Slippi/SlippiPlayback.h" #include "DolphinQt/Host.h" #include "DolphinQt/QtUtils/ModalMessageBox.h" @@ -41,6 +42,8 @@ #include "VideoCommon/VertexShaderManager.h" #include "VideoCommon/VideoConfig.h" +extern std::unique_ptr g_playbackStatus; + RenderWidget::RenderWidget(QWidget* parent) : QWidget(parent) { setWindowTitle(QStringLiteral("Dolphin")); @@ -63,6 +66,7 @@ RenderWidget::RenderWidget(QWidget* parent) : QWidget(parent) }); connect(Host::GetInstance(), &Host::RequestLowerWindow, this, &RenderWidget::LowerWindow); connect(Host::GetInstance(), &Host::RequestExit, this, &RenderWidget::Exit); + connect(Host::GetInstance(), &Host::RequestSeek, this, &RenderWidget::PlaybackSeek); connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [this](Core::State state) { if (state == Core::State::Running) SetImGuiKeyMap(); @@ -353,4 +357,9 @@ void RenderWidget::Exit() close(); } +void RenderWidget::PlaybackSeek() +{ + g_playbackStatus->SeekToFrame(); +} + diff --git a/Source/Core/DolphinQt/RenderWidget.h b/Source/Core/DolphinQt/RenderWidget.h index 1c420dc96a..270036cc27 100644 --- a/Source/Core/DolphinQt/RenderWidget.h +++ b/Source/Core/DolphinQt/RenderWidget.h @@ -41,6 +41,7 @@ private: void dropEvent(QDropEvent* event) override; void LowerWindow(); void Exit(); + void PlaybackSeek(); static constexpr int MOUSE_HIDE_DELAY = 3000; QTimer* m_mouse_timer; diff --git a/Source/Core/VideoCommon/OnScreenDisplay.cpp b/Source/Core/VideoCommon/OnScreenDisplay.cpp index 0178c15a3c..22250c4f0b 100644 --- a/Source/Core/VideoCommon/OnScreenDisplay.cpp +++ b/Source/Core/VideoCommon/OnScreenDisplay.cpp @@ -20,6 +20,7 @@ #include "Common/Timer.h" #include "Core/Core.h" #include "Core/ConfigManager.h" +#include "Core/Host.h" #include "Core/Slippi/SlippiPlayback.h" #include "VideoCommon/OnScreenDisplay.h" #include "VideoCommon/IconsFontAwesome4.h" @@ -45,7 +46,17 @@ struct Message }; static std::multimap s_messages; static std::mutex s_messages_mutex; -static int frame = 0; +static s32 frame = 0; + +static std::string GetTimeForFrame(s32 currFrame) { + int currSeconds = int((currFrame - Slippi::GAME_FIRST_FRAME) / 60); + int currMinutes = (int)(currSeconds / 60); + int currRemainder = (int)(currSeconds % 60); + // Position string (i.e. MM:SS) + char currTime[6]; + sprintf(currTime, "%02d:%02d", currMinutes, currRemainder); + return std::string(currTime); +} u32 idle_tick = Common::Timer::GetTimeMs(); ImVec2 prev_mouse(0,0); @@ -217,16 +228,6 @@ bool SliderCustomBehavior(const ImRect& bb, ImGuiID id, int* v, int v_min, int v bool isActive = g.ActiveId == id; static bool isHeld = false; const bool hovered = ImGui::ItemHoverable(bb, id); - if (isHeld) { - isHeld = isHeld && isDown; - // If no longer held, slider was let go. Trigger mark edited - if (!isHeld) { - INFO_LOG(SLIPPI, "Do seek here!"); - ImGui::MarkItemEdited(id); // TODO only mark on mouse up - } - } - else - isHeld = hovered && isDown; if (!isDown && isActive) ImGui::ClearActiveID(); @@ -239,7 +240,6 @@ bool SliderCustomBehavior(const ImRect& bb, ImGuiID id, int* v, int v_min, int v if (axis == ImGuiAxis_Y) clicked_t = 1.0f - clicked_t; - if (is_power) { if (clicked_t < linear_zero_pos) @@ -268,10 +268,21 @@ bool SliderCustomBehavior(const ImRect& bb, ImGuiID id, int* v, int v_min, int v if (*v != new_value && isDown) { *v = new_value; - value_changed = true; } } + if (isHeld) { + isHeld = isHeld && isDown; + // If no longer held, slider was let go. Trigger mark edited + if (!isHeld) { + INFO_LOG(SLIPPI, "Seeking to frame %d!", *v); + value_changed = true; + g_playbackStatus->targetFrameNum = *v; + } + } + else + isHeld = hovered && isDown; + float new_grab_t = ImGui::SliderCalcRatioFromValueT(ImGuiDataType_S32, new_value, v_min, v_max, power, linear_zero_pos); float curr_grab_t = ImGui::SliderCalcRatioFromValueT(ImGuiDataType_S32, *v, v_min, v_max, power, linear_zero_pos); @@ -297,7 +308,7 @@ bool SliderCustomBehavior(const ImRect& bb, ImGuiID id, int* v, int v_min, int v // Darken screen when seeking if (isHeld) - window->DrawList->AddRectFilled(ImVec2(0, 0), ImGui::GetIO().DisplaySize, ImGui::ColorConvertFloat4ToU32(ImVec4(0.0f, 0.0f, 0.0f, 0.5f))); + window->DrawList->AddRectFilled(ImVec2(0, 0), ImGui::GetIO().DisplaySize, ImGui::ColorConvertFloat4ToU32(ImVec4(0.0f, 0.0f, 0.0f, 0.6f))); window->DrawList->AddRectFilled(ImVec2(0, ImGui::GetWindowHeight() - 36), ImVec2(ImGui::GetWindowWidth(), ImGui::GetWindowHeight()), ImGui::ColorConvertFloat4ToU32(ImVec4(0.0f, 0.0f, 0.0f, 0.75f * style.Alpha))); @@ -305,19 +316,21 @@ bool SliderCustomBehavior(const ImRect& bb, ImGuiID id, int* v, int v_min, int v window->DrawList->AddLine(ImVec2(bb.Min.x, bb.Max.y - 6), ImVec2(bb.Max.x, bb.Max.y - 6), ImGui::ColorConvertFloat4ToU32(ImVec4(1.0f, 1.0f, 1.0f, 0.5f * style.Alpha)), 4); // Whiter, more opaque line up to mouse position - if (hovered && !isDown) + if (hovered && !isHeld) window->DrawList->AddLine(ImVec2(bb.Min.x, bb.Max.y - 6), ImVec2(new_grab_bb.Min.x, bb.Max.y - 6), ImGui::ColorConvertFloat4ToU32(ImVec4(1.0f, 1.0f, 1.0f, style.Alpha)), 4); + if (hovered || isHeld) + window->DrawList->AddText(ImVec2(new_grab_bb.GetCenter().x - valuesize.x / 2, bb.Max.y - 30), ImColor(255, 255, 255), GetTimeForFrame(new_value).c_str()); + // Colored line, circle indicator, and text if (isHeld) { - window->DrawList->AddLine(ImVec2(bb.Min.x, bb.Max.y - 6), ImVec2(new_grab_bb.Min.x, bb.Max.y - 6), ImGui::ColorConvertFloat4ToU32(ImVec4(0.0f, 1.0f, 0.0f, style.Alpha)), 4); - window->DrawList->AddCircleFilled(ImVec2(new_grab_bb.Min.x, new_grab_bb.Max.y - 6), 6, ImGui::ColorConvertFloat4ToU32(ImVec4(0.0f, 1.0f, 0.0f, style.Alpha))); - window->DrawList->AddText(ImVec2(new_grab_bb.GetCenter().x - valuesize.x / 2, bb.Max.y - 30), ImColor(255, 255, 255), value, label); + window->DrawList->AddLine(ImVec2(bb.Min.x, bb.Max.y - 6), ImVec2(new_grab_bb.Min.x, bb.Max.y - 6), ImGui::ColorConvertFloat4ToU32(ImVec4(0.0f, 1.0f, 0.0f, 1.0)), 4); + window->DrawList->AddCircleFilled(ImVec2(new_grab_bb.Min.x, new_grab_bb.Max.y - 6), 6, ImGui::ColorConvertFloat4ToU32(ImVec4(0.0f, 1.0f, 0.0f, 1.0))); } // Progress bar if (!isHeld) - frame = g_playbackStatus->currentPlaybackFrame; + frame = (g_playbackStatus->targetFrameNum == INT_MAX) ? g_playbackStatus->currentPlaybackFrame : g_playbackStatus->targetFrameNum; window->DrawList->AddLine(ImVec2(bb.Min.x, bb.Max.y - 6), ImVec2(curr_grab_bb.Min.x, bb.Max.y - 6), ImGui::ColorConvertFloat4ToU32(ImVec4(0.0f, 1.0f, 0.0f, style.Alpha)), 4); return value_changed; @@ -403,7 +416,10 @@ void DrawSlippiPlaybackControls() { ImGui::PushItemWidth(ImGui::GetWindowWidth()); ImGui::SetCursorPos(ImVec2(0.0f, ImGui::GetWindowHeight() - 44)); - SliderCustom("", ImVec4(1.0f, 0.0f, 0.0f, 1.0f), &frame, 0, 8000, 1.0, "%d"); + if (SliderCustom("", ImVec4(1.0f, 0.0f, 0.0f, 1.0f), &frame, Slippi::PLAYBACK_FIRST_SAVE, g_playbackStatus->lastFrame, 1.0, "%d")) { + INFO_LOG(SLIPPI, "seek"); + Host_PlaybackSeek(); + } ImGui::SetCursorPos(ImVec2(0.0f, ImGui::GetWindowHeight() - 30)); ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(1.0f, 0.5f)); if (ButtonCustom(ICON_FA_PLAY, ImVec2(32.0f, 32.0f))) { @@ -426,6 +442,13 @@ void DrawSlippiPlaybackControls() if (ButtonCustom(ICON_FA_FAST_FORWARD, ImVec2(32.0f, 32.0f))) { INFO_LOG(SLIPPI, "fast_foward"); } + ImGuiWindow* window = ImGui::GetCurrentWindow(); + ImGui::SetCursorPos(ImVec2(180.0f, window->DC.CursorPosPrevLine.y + 6.0f)); + + auto playbackTime = GetTimeForFrame(g_playbackStatus->currentPlaybackFrame); + auto endTime = GetTimeForFrame(g_playbackStatus->lastFrame); + auto timeString = playbackTime + " / " + endTime; + ImGui::Text(timeString.c_str()); } ImGui::End(); } diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 06f2ae31bd..e10c1c8298 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -919,7 +919,9 @@ bool Renderer::InitializeImGui() return false; } - ImGui::GetIO().Fonts->AddFontDefault(); + ImFontConfig config; + config.MergeMode = true; + ImGui::GetIO().Fonts->AddFontFromFileTTF("Roboto-Medium.ttf", 14.0f, 0, ImGui::GetIO().Fonts->GetGlyphRangesDefault()); static const ImWchar icons_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 }; ImFontConfig icons_config; icons_config.MergeMode = true; icons_config.PixelSnapH = true; ImGui::GetIO().Fonts->AddFontFromFileTTF(FONT_ICON_FILE_NAME_FA, 20.0f, &icons_config, icons_ranges);