mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-09-03 16:16:05 +00:00
working disappearing buttons. TODO opacity over time, disappearing bar
This commit is contained in:
parent
788df02026
commit
f565855b85
4 changed files with 92 additions and 130 deletions
|
@ -1165,8 +1165,8 @@ void CEXISlippi::prepareFrameData(u8* payload)
|
|||
// data from this frame. Don't wait until next frame is processing is complete
|
||||
// (this is the last frame, in that case)
|
||||
auto isFrameFound = m_current_game->DoesFrameExist(frameIndex);
|
||||
g_playbackStatus->latestFrame = m_current_game->GetLatestIndex();
|
||||
auto isNextFrameFound = g_playbackStatus->latestFrame > frameIndex;
|
||||
g_playbackStatus->lastFrame = m_current_game->GetLatestIndex();
|
||||
auto isNextFrameFound = g_playbackStatus->lastFrame > frameIndex;
|
||||
auto isFrameComplete = checkFrameFullyFetched(frameIndex);
|
||||
auto isFrameReady = isFrameFound && (isProcessingComplete || isNextFrameFound || isFrameComplete);
|
||||
|
||||
|
@ -1199,8 +1199,8 @@ void CEXISlippi::prepareFrameData(u8* payload)
|
|||
}
|
||||
|
||||
// If RealTimeMode is enabled, let's trigger fast forwarding under certain conditions
|
||||
auto isFarBehind = g_playbackStatus->latestFrame - frameIndex > 2;
|
||||
auto isVeryFarBehind = g_playbackStatus->latestFrame - frameIndex > 25;
|
||||
auto isFarBehind = g_playbackStatus->lastFrame - frameIndex > 2;
|
||||
auto isVeryFarBehind = g_playbackStatus->lastFrame - frameIndex > 25;
|
||||
if (isFarBehind && commSettings.mode == "mirror" && commSettings.isRealTimeMode)
|
||||
{
|
||||
g_playbackStatus->isSoftFFW = true;
|
||||
|
@ -1211,7 +1211,7 @@ void CEXISlippi::prepareFrameData(u8* payload)
|
|||
g_playbackStatus->isHardFFW = isVeryFarBehind;
|
||||
}
|
||||
|
||||
if (g_playbackStatus->latestFrame == frameIndex)
|
||||
if (g_playbackStatus->lastFrame == frameIndex)
|
||||
{
|
||||
// The reason to disable fast forwarding here is in hopes
|
||||
// of disabling it on the last frame that we have actually received.
|
||||
|
@ -1257,14 +1257,11 @@ void CEXISlippi::prepareFrameData(u8* payload)
|
|||
rollbackCode = 1;
|
||||
}
|
||||
|
||||
// WARN_LOG(EXPANSIONINTERFACE, "[Frame %d] Playback current behind by: %d frames.", frameIndex,
|
||||
// g_playbackStatus->latestFrame - frameIndex);
|
||||
|
||||
// Keep track of last FFW frame, used for soft FFW's
|
||||
if (shouldFFW)
|
||||
{
|
||||
WARN_LOG(EXPANSIONINTERFACE, "[Frame %d] FFW frame, behind by: %d frames.", frameIndex,
|
||||
g_playbackStatus->latestFrame - frameIndex);
|
||||
WARN_LOG(SLIPPI, "[Frame %d] FFW frame, behind by: %d frames.", frameIndex,
|
||||
g_playbackStatus->lastFrame - frameIndex);
|
||||
g_playbackStatus->lastFFWFrame = frameIndex;
|
||||
}
|
||||
|
||||
|
@ -1291,23 +1288,6 @@ void CEXISlippi::prepareFrameData(u8* payload)
|
|||
|
||||
frameSeqIdx += 1;
|
||||
}
|
||||
// else
|
||||
//{
|
||||
// std::vector<u8> fakePayload(8, 0);
|
||||
// *(s32 *)(&fakePayload[0]) = Common::swap32(frame->frame);
|
||||
|
||||
// if (frame->frame == 400)
|
||||
// {
|
||||
// handleCaptureSavestate(&fakePayload[0]);
|
||||
// }
|
||||
|
||||
// if (frame->frame == 950)
|
||||
// {
|
||||
// *(s32 *)(&fakePayload[0]) = Common::swap32(400);
|
||||
// handleLoadSavestate(&fakePayload[0]);
|
||||
// handleCaptureSavestate(&fakePayload[0]);
|
||||
// }
|
||||
//}
|
||||
|
||||
// For normal replays, modify slippi seek/playback data as needed
|
||||
// TODO: maybe handle other modes too?
|
||||
|
|
|
@ -21,6 +21,7 @@ extern std::unique_ptr<SlippiReplayComm> g_replayComm;
|
|||
|
||||
static std::mutex mtx;
|
||||
static std::mutex seekMtx;
|
||||
static std::mutex ffwMtx;
|
||||
static std::mutex diffMtx;
|
||||
static std::unique_lock<std::mutex> processingLock(diffMtx);
|
||||
static std::condition_variable condVar;
|
||||
|
@ -61,14 +62,13 @@ SlippiPlaybackStatus::SlippiPlaybackStatus()
|
|||
lastFFWFrame = INT_MIN;
|
||||
currentPlaybackFrame = INT_MIN;
|
||||
targetFrameNum = INT_MAX;
|
||||
latestFrame = Slippi::GAME_FIRST_FRAME;
|
||||
lastFrame = Slippi::PLAYBACK_FIRST_SAVE;
|
||||
}
|
||||
|
||||
void SlippiPlaybackStatus::startThreads()
|
||||
{
|
||||
shouldRunThreads = true;
|
||||
m_savestateThread = std::thread(&SlippiPlaybackStatus::SavestateThread, this);
|
||||
m_seekThread = std::thread(&SlippiPlaybackStatus::SeekThread, this);
|
||||
}
|
||||
|
||||
void SlippiPlaybackStatus::prepareSlippiPlayback(s32& frameIndex)
|
||||
|
@ -113,9 +113,6 @@ void SlippiPlaybackStatus::resetPlayback()
|
|||
if (m_savestateThread.joinable())
|
||||
m_savestateThread.detach();
|
||||
|
||||
if (m_seekThread.joinable())
|
||||
m_seekThread.detach();
|
||||
|
||||
condVar.notify_one(); // Will allow thread to kill itself
|
||||
futureDiffs.clear();
|
||||
futureDiffs.rehash(0);
|
||||
|
@ -178,96 +175,66 @@ void SlippiPlaybackStatus::SavestateThread()
|
|||
INFO_LOG(SLIPPI, "Exiting savestate thread");
|
||||
}
|
||||
|
||||
void SlippiPlaybackStatus::SeekThread()
|
||||
void SlippiPlaybackStatus::SeekToFrame(s32 frameNum)
|
||||
{
|
||||
Common::SetCurrentThreadName("Seek thread");
|
||||
std::unique_lock<std::mutex> seekLock(seekMtx);
|
||||
|
||||
INFO_LOG(SLIPPI, "Entering seek thread");
|
||||
|
||||
while (shouldRunThreads)
|
||||
{
|
||||
bool shouldSeek = inSlippiPlayback && (shouldJumpBack || shouldJumpForward || targetFrameNum != INT_MAX);
|
||||
|
||||
if (shouldSeek)
|
||||
{
|
||||
auto replayCommSettings = g_replayComm->getSettings();
|
||||
if (replayCommSettings.mode == "queue")
|
||||
clearWatchSettingsStartEnd();
|
||||
|
||||
bool paused = (Core::GetState() == Core::State::Paused);
|
||||
Core::SetState(Core::State::Paused);
|
||||
|
||||
u32 jumpInterval = 300; // 5 seconds;
|
||||
|
||||
if (shouldJumpForward)
|
||||
targetFrameNum = currentPlaybackFrame + jumpInterval;
|
||||
|
||||
if (shouldJumpBack)
|
||||
targetFrameNum = currentPlaybackFrame - jumpInterval;
|
||||
|
||||
// Handle edgecases for trying to seek before start or past end of game
|
||||
if (targetFrameNum < Slippi::PLAYBACK_FIRST_SAVE)
|
||||
targetFrameNum = Slippi::PLAYBACK_FIRST_SAVE;
|
||||
|
||||
if (targetFrameNum > latestFrame)
|
||||
{
|
||||
targetFrameNum = latestFrame;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If this diff has been processed, load it
|
||||
if (futureDiffs.count(closestStateFrame) > 0)
|
||||
{
|
||||
std::string stateString;
|
||||
decoder.Decode((char*)iState.data(), iState.size(), futureDiffs[closestStateFrame].get(),
|
||||
&stateString);
|
||||
std::vector<u8> stateToLoad(stateString.begin(), stateString.end());
|
||||
State::LoadFromBuffer(stateToLoad);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Fastforward until we get to the frame we want
|
||||
if (targetFrameNum != closestStateFrame && targetFrameNum != latestFrame)
|
||||
{
|
||||
isHardFFW = true;
|
||||
SConfig::GetInstance().m_OCEnable = true;
|
||||
SConfig::GetInstance().m_OCFactor = 4.0f;
|
||||
|
||||
Core::SetState(Core::State::Running);
|
||||
cv_waitingForTargetFrame.wait(seekLock);
|
||||
Core::SetState(Core::State::Paused);
|
||||
|
||||
SConfig::GetInstance().m_OCFactor = 1.0f;
|
||||
SConfig::GetInstance().m_OCEnable = false;
|
||||
isHardFFW = false;
|
||||
}
|
||||
|
||||
if (!paused)
|
||||
Core::SetState(Core::State::Running);
|
||||
|
||||
shouldJumpBack = false;
|
||||
shouldJumpForward = false;
|
||||
targetFrameNum = INT_MAX;
|
||||
if (seekMtx.try_lock()) {
|
||||
if (frameNum < Slippi::PLAYBACK_FIRST_SAVE || frameNum > lastFrame) {
|
||||
INFO_LOG(SLIPPI, "Error: Invalid seek to frame: %d", frameNum);
|
||||
seekMtx.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
Common::SleepCurrentThread(SLEEP_TIME_MS);
|
||||
}
|
||||
std::unique_lock<std::mutex> ffwLock(ffwMtx);
|
||||
auto replayCommSettings = g_replayComm->getSettings();
|
||||
if (replayCommSettings.mode == "queue")
|
||||
clearWatchSettingsStartEnd();
|
||||
|
||||
INFO_LOG(SLIPPI, "Exit seek thread");
|
||||
auto prevState = Core::GetState();
|
||||
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;
|
||||
if (isLoadingStateOptimal)
|
||||
{
|
||||
if (closestStateFrame <= Slippi::PLAYBACK_FIRST_SAVE)
|
||||
{
|
||||
State::LoadFromBuffer(iState);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If this diff has been processed, load it
|
||||
if (futureDiffs.count(closestStateFrame) > 0)
|
||||
{
|
||||
std::string stateString;
|
||||
decoder.Decode((char*)iState.data(), iState.size(), futureDiffs[closestStateFrame].get(),
|
||||
&stateString);
|
||||
std::vector<u8> stateToLoad(stateString.begin(), stateString.end());
|
||||
State::LoadFromBuffer(stateToLoad);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Fastforward until we get to the frame we want
|
||||
if (frameNum != closestStateFrame && frameNum != 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;
|
||||
isHardFFW = false;
|
||||
}
|
||||
|
||||
Core::SetState(prevState);
|
||||
} else {
|
||||
INFO_LOG(SLIPPI, "Already seeking. Ignoring this call");
|
||||
}
|
||||
}
|
||||
|
||||
void SlippiPlaybackStatus::clearWatchSettingsStartEnd()
|
||||
|
|
|
@ -25,10 +25,9 @@ public:
|
|||
s32 lastFFWFrame = INT_MIN;
|
||||
s32 currentPlaybackFrame = INT_MIN;
|
||||
s32 targetFrameNum = INT_MAX;
|
||||
s32 latestFrame = Slippi::GAME_FIRST_FRAME;
|
||||
s32 lastFrame = Slippi::PLAYBACK_FIRST_SAVE;
|
||||
|
||||
std::thread m_savestateThread;
|
||||
std::thread m_seekThread;
|
||||
|
||||
void startThreads(void);
|
||||
void resetPlayback(void);
|
||||
|
@ -36,7 +35,7 @@ public:
|
|||
|
||||
private:
|
||||
void SavestateThread(void);
|
||||
void SeekThread(void);
|
||||
void SeekToFrame(s32 targetFrameNum);
|
||||
void processInitialState();
|
||||
void clearWatchSettingsStartEnd();
|
||||
|
||||
|
|
|
@ -169,9 +169,9 @@ bool ButtonCustom(const char* label, const ImVec2& size_arg, ImGuiButtonFlags fl
|
|||
ImGui::RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
|
||||
|
||||
if (hovered || held)
|
||||
ImGui::RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb, ImVec4(0.9f, 0.9f, 0.9f, 1.0f));
|
||||
ImGui::RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb, ImVec4(0.9f, 0.9f, 0.9f, style.Alpha));
|
||||
else
|
||||
ImGui::RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb, ImVec4(0.9f, 0.9f, 0.9f, 0.6f));
|
||||
ImGui::RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb, ImVec4(0.9f, 0.9f, 0.9f, 0.6f * style.Alpha));
|
||||
|
||||
// Automatically close popups
|
||||
//if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))
|
||||
|
@ -210,8 +210,14 @@ 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)
|
||||
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;
|
||||
|
||||
|
@ -257,7 +263,6 @@ bool SliderCustomBehavior(const ImRect& bb, ImGuiID id, int* v, int v_min, int v
|
|||
{
|
||||
*v = new_value;
|
||||
value_changed = true;
|
||||
ImGui::MarkItemEdited(id); // TODO only mark on mouse up
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -365,14 +370,25 @@ void DrawSlippiPlaybackControls()
|
|||
// We have to provide a window name, and these shouldn't be duplicated.
|
||||
// So instead, we generate a name based on the number of messages drawn.
|
||||
const std::string window_name = fmt::format("Slippi Playback Controls");
|
||||
|
||||
const float alpha = 1.0f;
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, alpha);
|
||||
|
||||
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0));
|
||||
ImGui::SetNextWindowSize(ImGui::GetIO().DisplaySize);
|
||||
|
||||
bool isActive = false;
|
||||
auto mousePos = ImGui::GetMousePos();
|
||||
auto mouseDown = GImGui->IO.MouseDown[0];
|
||||
if (mousePos[1] > ImGui::GetIO().DisplaySize.y - 44 && mousePos[1] < ImGui::GetIO().DisplaySize.y || mouseDown) {
|
||||
isActive = true;
|
||||
last_active_tick = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
if (isActive) {
|
||||
INFO_LOG(SLIPPI, "hovering!");
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 1.0f);
|
||||
}
|
||||
else {
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.00001f);
|
||||
}
|
||||
|
||||
if (ImGui::Begin(window_name.c_str(), nullptr,
|
||||
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings |
|
||||
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoBackground |
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue