diff --git a/.gitignore b/.gitignore index 17915f1..44e618d 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ /AxibugEmuOnline.Client/ProjectSettings/Packages/ /AxibugEmuOnline.Web/config.cfg /AxibugEmuOnline.Client/ProjectSettings/ProjectVersion.txt +/AxibugEmuOnline.Client/ProjectSettings/AutoStreamingSettings.asset +/AxibugEmuOnline.Client/Logs diff --git a/AxibugEmuOnline.Client/Assets/MyNes.Core/NesEmu.cs b/AxibugEmuOnline.Client/Assets/MyNes.Core/NesEmu.cs index 133b7aa..f39d630 100644 --- a/AxibugEmuOnline.Client/Assets/MyNes.Core/NesEmu.cs +++ b/AxibugEmuOnline.Client/Assets/MyNes.Core/NesEmu.cs @@ -5169,6 +5169,7 @@ namespace MyNes.Core { Tracer.WriteLine("Running in a thread ... using custom frame limiter."); FrameLimiterEnabled = true; + currentFrame = 0; mainThread = new Thread(EmuClock); mainThread.Start(); } @@ -5263,9 +5264,6 @@ namespace MyNes.Core if (mainThread != null) { Tracer.WriteLine("Aborting thread .."); - while (mainThread.IsAlive) - { - } mainThread.Abort(); mainThread = null; } @@ -5274,43 +5272,29 @@ namespace MyNes.Core NesEmu.EmuShutdown?.Invoke(null, new EventArgs()); } - internal static void EMUClockFrame() - { - emu_frame_done = false; - while (!emu_frame_done && ON) - { - if (!PAUSED) - { - CPUClock(); - } - else - { - Thread.Sleep(100); - } - } - } - - public static void ExecuteOneFrame() - { - while (!ppu_frame_finished) - { - CPUClock(); - } - - FrameFinished(); - } - + private static Stopwatch sw = new Stopwatch(); + private static double fixTime; + public static int currentFrame; private static void EmuClock() { while (ON) { if (!PAUSED) { - CPUClock(); - if (ppu_frame_finished) + var waitTime = GetTime() + fps_time_period + fixTime; + + while (!ppu_frame_finished) + CPUClock(); + + FrameFinished(); + + fixTime = waitTime - GetTime(); + while (fixTime > 0) { - FrameFinished(); - } + fixTime = waitTime - GetTime(); + }; + + continue; } render_audio_get_is_playing(out render_audio_is_playing); @@ -5408,24 +5392,6 @@ namespace MyNes.Core audio_timer = 0.0; } fps_time_token = GetTime() - fps_time_start; - if (FrameLimiterEnabled) - { - if (fps_time_token > 0.0) - { - fps_time_dead = fps_time_period - fps_time_token; - if (fps_time_dead > 0.0) - { - Thread.Sleep((int)Math.Floor(fps_time_dead * 1000.0)); - fps_time_dead = GetTime() - fps_time_start; - while (fps_time_period - fps_time_dead > 0.0) - { - fps_time_dead = GetTime() - fps_time_start; - } - } - } - fps_time_last = GetTime(); - fps_time_frame_time = fps_time_last - fps_time_start; - } fps_time_start = GetTime(); } @@ -5445,6 +5411,11 @@ namespace MyNes.Core fps_time_period = period; } + public static void RevertFramePeriod() + { + fps_time_period = 1 / emu_time_target_fps; + } + public static void ApplyRegionSetting() { switch ((RegionSetting)MyNesMain.EmuSettings.RegionSetting) diff --git a/AxibugEmuOnline.Client/Assets/Resources/Roms/tortoise4.nes.bytes b/AxibugEmuOnline.Client/Assets/Resources/Roms/tortoise4.nes.bytes new file mode 100644 index 0000000..46a85a0 Binary files /dev/null and b/AxibugEmuOnline.Client/Assets/Resources/Roms/tortoise4.nes.bytes differ diff --git a/AxibugEmuOnline.Client/Assets/Resources/Roms/tortoise4.nes.bytes.meta b/AxibugEmuOnline.Client/Assets/Resources/Roms/tortoise4.nes.bytes.meta new file mode 100644 index 0000000..db26e21 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Resources/Roms/tortoise4.nes.bytes.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 4933f61382c34574db545f3d9e72b51d +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Scene/EmuTest.unity b/AxibugEmuOnline.Client/Assets/Scene/EmuTest.unity index 451cb5d..52bcb3a 100644 --- a/AxibugEmuOnline.Client/Assets/Scene/EmuTest.unity +++ b/AxibugEmuOnline.Client/Assets/Scene/EmuTest.unity @@ -815,7 +815,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 1 + m_IsActive: 0 --- !u!224 &2100984176 RectTransform: m_ObjectHideFlags: 0 diff --git a/AxibugEmuOnline.Client/Assets/Script/Emu/AudioProvider.cs b/AxibugEmuOnline.Client/Assets/Script/Emu/AudioProvider.cs index f163ecc..73889b1 100644 --- a/AxibugEmuOnline.Client/Assets/Script/Emu/AudioProvider.cs +++ b/AxibugEmuOnline.Client/Assets/Script/Emu/AudioProvider.cs @@ -23,7 +23,7 @@ namespace AxibugEmuOnline.Client private AudioSource m_as; private Stopwatch sw = Stopwatch.StartNew(); - private Queue _buffer = new Queue(2048); + private RingBuffer _buffer = new RingBuffer(4096); public double FPS { get; private set; } @@ -43,9 +43,27 @@ namespace AxibugEmuOnline.Client { int step = channels; + var bufferCount = _buffer.Available(); + if (bufferCount < 4096) + { + double fps = 1 / 61d; + NesEmu.SetFramePeriod(ref fps); + } + else if (bufferCount > 8124) + { + double fps = 1 / 59d; + NesEmu.SetFramePeriod(ref fps); + } + else + { + NesEmu.RevertFramePeriod(); + } for (int i = 0; i < data.Length; i += step) { - var rawFloat = _buffer.Count <= 0 ? lastData : _buffer.Dequeue() / 124f; + float rawFloat = lastData; + if (_buffer.TryRead(out short rawData)) + rawFloat = rawData / 124f; + data[i] = rawFloat; for (int fill = 1; fill < step; fill++) data[i + fill] = rawFloat; @@ -63,14 +81,9 @@ namespace AxibugEmuOnline.Client FPS = 1d / delta.TotalSeconds; - if (_buffer.Count > 2048) - { - _buffer.Clear(); - } - for (int i = 0; i < samples_a; i++) { - _buffer.Enqueue(buffer[i]); + _buffer.Write(buffer[i]); } } diff --git a/AxibugEmuOnline.Client/Assets/Script/Emu/UguiVideoProvider.cs b/AxibugEmuOnline.Client/Assets/Script/Emu/UguiVideoProvider.cs index 0523599..9e68832 100644 --- a/AxibugEmuOnline.Client/Assets/Script/Emu/UguiVideoProvider.cs +++ b/AxibugEmuOnline.Client/Assets/Script/Emu/UguiVideoProvider.cs @@ -44,14 +44,14 @@ namespace AxibugEmuOnline.Client res.b = b / 255f; } - public void Update() + public void Update() { var colors = m_texRawBuffer; m_rawBufferWarper.SetPixels(colors); m_rawBufferWarper.Apply(); Graphics.Blit(m_rawBufferWarper, m_drawCanvas.texture as RenderTexture); - m_fpsText.text = $"Audio:{NesCoreProxy.Instance.AudioCom.FPS}"; + m_fpsText.text = $"fps:{NesCoreProxy.Instance.AudioCom.FPS}"; } public void WriteErrorNotification(string message, bool instant) diff --git a/AxibugEmuOnline.Client/Assets/Script/Manager/AppEmu.cs b/AxibugEmuOnline.Client/Assets/Script/Manager/AppEmu.cs index 75561b6..cfe41aa 100644 --- a/AxibugEmuOnline.Client/Assets/Script/Manager/AppEmu.cs +++ b/AxibugEmuOnline.Client/Assets/Script/Manager/AppEmu.cs @@ -10,12 +10,8 @@ namespace AxibugEmuOnline.Client.Manager public void Init(IVideoProvider videoCom, IAudioProvider audioCom) { MyNesMain.Initialize(this, videoCom, audioCom); - NesEmu.LoadGame("kirby.nes", out var successed, true); - } - public void ExecuteFrameLogic() - { - //NesEmu.ExecuteOneFrame(); + NesEmu.LoadGame("tortoise4.nes", out var successed, true); } public void Dispose() diff --git a/AxibugEmuOnline.Client/Assets/Script/NesCoreProxy.cs b/AxibugEmuOnline.Client/Assets/Script/NesCoreProxy.cs index 8643eda..2b5348b 100644 --- a/AxibugEmuOnline.Client/Assets/Script/NesCoreProxy.cs +++ b/AxibugEmuOnline.Client/Assets/Script/NesCoreProxy.cs @@ -21,11 +21,6 @@ namespace AxibugEmuOnline.Client m_appEnum.Init(VideoCom, AudioCom); } - private void Update() - { - m_appEnum.ExecuteFrameLogic(); - } - private void OnDestroy() { Instance = null; diff --git a/AxibugEmuOnline.Client/Assets/Script/RingBuffer.cs b/AxibugEmuOnline.Client/Assets/Script/RingBuffer.cs new file mode 100644 index 0000000..20f5613 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/RingBuffer.cs @@ -0,0 +1,73 @@ +using System; +using System.Threading; + +public 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/Script/RingBuffer.cs.meta b/AxibugEmuOnline.Client/Assets/Script/RingBuffer.cs.meta new file mode 100644 index 0000000..902ec9a --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/RingBuffer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6bd0f6c1647ed3f49a59e7f06406f49b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: