From 54023702ed6fae5fc99192f41c960ba58d503b5a Mon Sep 17 00:00:00 2001 From: RainbowwPhoenixx Date: Sat, 18 Nov 2023 15:50:08 +0100 Subject: [PATCH] fix: better performance for long-running scripts --- src/Features/Tas/TasPlayer.cpp | 57 ++++++++++++++++++++++++++++------ src/Features/Tas/TasPlayer.hpp | 4 +++ 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/src/Features/Tas/TasPlayer.cpp b/src/Features/Tas/TasPlayer.cpp index 89037eec6..c01d419b7 100644 --- a/src/Features/Tas/TasPlayer.cpp +++ b/src/Features/Tas/TasPlayer.cpp @@ -152,6 +152,9 @@ void TasPlayer::Activate(TasPlaybackInfo info) { for (int slot = 0; slot < 2; ++slot) { playbackInfo.slots[slot].ClearGeneratedContent(); + + currentInputFramebulkIndex[slot] = 0; + currentToolsFramebulkIndex[slot] = 0; } active = true; @@ -303,16 +306,50 @@ void TasPlayer::AdvanceFrame() { // returns raw framebulk that should be used for given tick TasFramebulk TasPlayer::GetRawFramebulkAt(int slot, int tick) { - int closestTime = INT_MAX; - TasFramebulk closest; - for (TasFramebulk framebulk : playbackInfo.slots[slot].framebulks) { - int timeDist = tick - framebulk.tick; - if (timeDist >= 0 && timeDist < closestTime) { - closestTime = timeDist; - closest = framebulk; + // Do binary search for bulks immediately before and after our target tick + uint32_t before = 0; + uint32_t after = playbackInfo.slots[slot].framebulks.size() - 1; + + if (playbackInfo.slots[slot].framebulks[before].tick == tick) { + return playbackInfo.slots[slot].framebulks[before]; + } + if (playbackInfo.slots[slot].framebulks[after].tick == tick) { + return playbackInfo.slots[slot].framebulks[after]; + } + + while (before + 1 != after) { + uint32_t middle = (before + after) / 2; + TasFramebulk middle_bulk = playbackInfo.slots[slot].framebulks[middle]; + + if (middle_bulk.tick < tick) { + before = middle; + } else if (middle_bulk.tick > tick) { + after = middle; + } else { + return middle_bulk; } } - return closest; + + return playbackInfo.slots[slot].framebulks[before]; +} + +// returns raw framebulk using more efficient method with cached incremented index of last used tick +TasFramebulk TasPlayer::GetRawFramebulkAt(int slot, int tick, int& cachedIndex) { + if (cachedIndex < 0) { + cachedIndex = 0; + } + + auto maxIndex = playbackInfo.slots[slot].framebulks.size() - 1; + + if (cachedIndex >= maxIndex) { + return playbackInfo.slots[slot].framebulks[maxIndex]; + } + + while (cachedIndex < maxIndex && playbackInfo.slots[slot].framebulks[cachedIndex + 1].tick <= tick) { + cachedIndex++; + } + + return playbackInfo.slots[slot].framebulks[cachedIndex]; } TasPlayerInfo TasPlayer::GetPlayerInfo(int slot, void *player, CUserCmd *cmd, bool clientside) { @@ -494,7 +531,7 @@ void TasPlayer::FetchInputs(int slot, TasController *controller) { // to actually hook at _Host_RunFrame_Input or CL_Move. int tick = currentTick + 1; - TasFramebulk fb = GetRawFramebulkAt(slot, tick); + TasFramebulk fb = GetRawFramebulkAt(slot, tick, currentInputFramebulkIndex[slot]); int fbTick = fb.tick; @@ -563,7 +600,7 @@ void TasPlayer::PostProcess(int slot, void *player, CUserCmd *cmd) { return; } - TasFramebulk fb = GetRawFramebulkAt(slot, tasTick); + TasFramebulk fb = GetRawFramebulkAt(slot, tasTick, currentToolsFramebulkIndex[slot]); // update all tools that needs to be updated auto fbTick = fb.tick; diff --git a/src/Features/Tas/TasPlayer.hpp b/src/Features/Tas/TasPlayer.hpp index 2860a3275..0de8480e3 100644 --- a/src/Features/Tas/TasPlayer.hpp +++ b/src/Features/Tas/TasPlayer.hpp @@ -66,6 +66,9 @@ class TasPlayer : public Feature { int wasEnginePaused = false; // Used to check if we need to revert incrementing a tick + // used to cache last used framebulk to quickly access it for playback + int currentInputFramebulkIndex[2]; + int currentToolsFramebulkIndex[2]; public: void Update(); void UpdateServer(); @@ -99,6 +102,7 @@ class TasPlayer : public Feature { void AdvanceFrame(); TasFramebulk GetRawFramebulkAt(int slot, int tick); + TasFramebulk GetRawFramebulkAt(int slot, int tick, int &cachedIndex); TasPlayerInfo GetPlayerInfo(int slot, void *player, CUserCmd *cmd, bool clientside = false);