From 70fb6154eccf8e95fc8f0e963fdab4563ba43449 Mon Sep 17 00:00:00 2001 From: "ALIENJACK\\alien" Date: Sun, 26 Jan 2025 15:04:26 +0800 Subject: [PATCH] =?UTF-8?q?NetReplay=E5=A2=9E=E5=8A=A0FrameProfiler(?= =?UTF-8?q?=E6=9C=AA=E5=AE=9E=E8=A3=85)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Assets/Plugins/AxiReplay/FrameProfiler.cs | 55 +++++++++++++ .../Plugins/AxiReplay/FrameProfiler.cs.meta | 2 + .../Plugins/AxiReplay/InternalRingbuffer.cs | 78 +++++++++++++++++++ .../AxiReplay/InternalRingbuffer.cs.meta | 2 + .../Assets/Plugins/AxiReplay/NetReplay.cs | 13 +++- 5 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 AxibugEmuOnline.Client/Assets/Plugins/AxiReplay/FrameProfiler.cs create mode 100644 AxibugEmuOnline.Client/Assets/Plugins/AxiReplay/FrameProfiler.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/Plugins/AxiReplay/InternalRingbuffer.cs create mode 100644 AxibugEmuOnline.Client/Assets/Plugins/AxiReplay/InternalRingbuffer.cs.meta diff --git a/AxibugEmuOnline.Client/Assets/Plugins/AxiReplay/FrameProfiler.cs b/AxibugEmuOnline.Client/Assets/Plugins/AxiReplay/FrameProfiler.cs new file mode 100644 index 00000000..df242e06 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Plugins/AxiReplay/FrameProfiler.cs @@ -0,0 +1,55 @@ +using System; +using System.Diagnostics; + +namespace AxiReplay +{ + public partial class FrameProfiler + { + private int m_headFrame; + private int m_cacheCount; + private int m_targetFrameRate; + private RingBuffer m_timePoints; + private double m_lastTime; + + private Stopwatch sw; + + public void InputHead(int headFrame) + { + m_headFrame = headFrame; + var currentTimeMs = GetCurrTime(); + + if (m_timePoints.Available() == 60) + CalcCacheCount(); + m_timePoints.Write(currentTimeMs - m_lastTime); + + m_lastTime = currentTimeMs; + } + public void Reset(int targetFrameRate = 60) + { + if (sw != null) sw.Stop(); + + sw = Stopwatch.StartNew(); + m_timePoints = new RingBuffer(targetFrameRate); + m_lastTime = 0; + m_targetFrameRate = targetFrameRate; + } + + void CalcCacheCount() + { + double deltaMax = 0; + while (m_timePoints.TryRead(out double delta)) + { + deltaMax = Math.Max(deltaMax, delta); + } + + int minCacheCount = (int)Math.Ceiling(deltaMax * m_targetFrameRate); + m_cacheCount = minCacheCount; + } + + double GetCurrTime() + { + if (sw == null) return 0; + return sw.Elapsed.TotalMilliseconds; + } + } +} \ No newline at end of file diff --git a/AxibugEmuOnline.Client/Assets/Plugins/AxiReplay/FrameProfiler.cs.meta b/AxibugEmuOnline.Client/Assets/Plugins/AxiReplay/FrameProfiler.cs.meta new file mode 100644 index 00000000..9fc08cbb --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Plugins/AxiReplay/FrameProfiler.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: cfd4511a83ff0bf4ea7615b87e7d09aa \ No newline at end of file diff --git a/AxibugEmuOnline.Client/Assets/Plugins/AxiReplay/InternalRingbuffer.cs b/AxibugEmuOnline.Client/Assets/Plugins/AxiReplay/InternalRingbuffer.cs new file mode 100644 index 00000000..3e00b092 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Plugins/AxiReplay/InternalRingbuffer.cs @@ -0,0 +1,78 @@ +using System.Threading; + +namespace AxiReplay +{ + public partial class FrameProfiler + { + internal class RingBuffer + { + private readonly T[] buffer; + private readonly int capacity; + private int writePos; + private int readPos; + private int count; + + public RingBuffer(int capacity) + { + this.capacity = capacity; + this.buffer = new T[capacity]; + this.writePos = 0; + this.readPos = 0; + this.count = 0; + } + + public void Write(T item) + { + int localWritePos; + int localReadPos; + + do + { + localWritePos = Volatile.Read(ref writePos); + localReadPos = Volatile.Read(ref readPos); + + int nextWritePos = (localWritePos + 1) % capacity; + + if (nextWritePos == localReadPos) + { + // 缓冲区已满,覆盖最旧的未读数据 + Interlocked.CompareExchange(ref readPos, (localReadPos + 1) % capacity, localReadPos); + } + } + while (Interlocked.CompareExchange(ref writePos, (localWritePos + 1) % capacity, localWritePos) != localWritePos); + + buffer[localWritePos] = item; + Interlocked.Increment(ref count); + } + + public bool TryRead(out T item) + { + item = default(T); + + int localReadPos; + int localWritePos; + + do + { + localReadPos = Volatile.Read(ref readPos); + localWritePos = Volatile.Read(ref writePos); + + if (localReadPos == localWritePos) + { + return false; // 缓冲区为空 + } + } + while (Interlocked.CompareExchange(ref readPos, (localReadPos + 1) % capacity, localReadPos) != localReadPos); + + item = buffer[localReadPos]; + Interlocked.Decrement(ref count); + return true; + } + + public int Available() + { + return Volatile.Read(ref count); + } + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Plugins/AxiReplay/InternalRingbuffer.cs.meta b/AxibugEmuOnline.Client/Assets/Plugins/AxiReplay/InternalRingbuffer.cs.meta new file mode 100644 index 00000000..a4c26ff5 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Plugins/AxiReplay/InternalRingbuffer.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: b17d83b69bd47094594c32fcff9715f4 \ No newline at end of file diff --git a/AxibugEmuOnline.Client/Assets/Plugins/AxiReplay/NetReplay.cs b/AxibugEmuOnline.Client/Assets/Plugins/AxiReplay/NetReplay.cs index 6076318f..cdba7015 100644 --- a/AxibugEmuOnline.Client/Assets/Plugins/AxiReplay/NetReplay.cs +++ b/AxibugEmuOnline.Client/Assets/Plugins/AxiReplay/NetReplay.cs @@ -34,6 +34,8 @@ namespace AxiReplay /// ReplayStep mNextReplay; + FrameProfiler frameProfiler = new FrameProfiler(); + bool bNetInit = false; public NetReplay() { @@ -45,6 +47,8 @@ namespace AxiReplay mCurrReplay = default(ReplayStep); mCurrReplay.FrameStartID = int.MinValue; bNetInit = false; + + frameProfiler.Reset(); } public void InData(ReplayStep inputData, int ServerFrameIdx, uint ServerForwardCount) { @@ -57,7 +61,10 @@ namespace AxiReplay bNetInit = true; mNextReplay = mNetReplayQueue.Dequeue(); } + + frameProfiler.InputHead(inputData.FrameStartID); } + public bool TryGetNextFrame(out ReplayStep data, out int frameDiff, out bool inputDiff) { if (!bNetInit) @@ -93,13 +100,15 @@ namespace AxiReplay { bool result; inputDiff = false; + + //if (targetFrame == mNextReplay.FrameStartID && targetFrame <= mRemoteFrameIdx && mNetReplayQueue.Count > 0) if (targetFrame == mNextReplay.FrameStartID && targetFrame <= mRemoteFrameIdx && mNetReplayQueue.Count >= mRemoteForwardCount) - { + { //当前帧追加 mCurrClientFrameIdx = targetFrame; ulong oldInput = mCurrReplay.InPut; - mCurrReplay = mNextReplay; + mCurrReplay = mNextReplay; if (oldInput != mCurrReplay.InPut) inputDiff = true; mNextReplay = mNetReplayQueue.Dequeue();