From d8431d615e87020d3ab23b0f145a31adca77e3b3 Mon Sep 17 00:00:00 2001 From: CasualPokePlayer <50538166+CasualPokePlayer@users.noreply.github.com> Date: Mon, 8 Jul 2024 16:28:40 -0700 Subject: [PATCH] Avoid zealously copying the entire video buffer this might be larger than bufferwidth*bufferheight, and thus possibly dramantically decrease screenshot/dumping performance --- src/BizHawk.Client.Common/QuickBmpFile.cs | 2 +- .../savestates/SavestateFile.cs | 40 +++++++------------ src/BizHawk.Client.EmuHawk/AVOut/AviWriter.cs | 2 +- .../AVOut/FFmpegWriter.cs | 2 +- src/BizHawk.Client.EmuHawk/AVOut/NutMuxer.cs | 13 +++--- src/BizHawk.Client.EmuHawk/AVOut/NutWriter.cs | 2 +- src/BizHawk.Client.EmuHawk/MainForm.cs | 2 +- .../movie/RecordMovie.cs | 2 +- .../Interfaces/Services/IVideoProvider.cs | 17 ++++++++ 9 files changed, 44 insertions(+), 38 deletions(-) diff --git a/src/BizHawk.Client.Common/QuickBmpFile.cs b/src/BizHawk.Client.Common/QuickBmpFile.cs index 752e6b1e5d..76af7364f5 100644 --- a/src/BizHawk.Client.Common/QuickBmpFile.cs +++ b/src/BizHawk.Client.Common/QuickBmpFile.cs @@ -187,7 +187,7 @@ namespace BizHawk.Client.Common { if (src.BufferWidth == dst.BufferWidth && src.BufferHeight == dst.BufferHeight) { - Array.Copy(src.GetVideoBuffer(), dst.GetVideoBuffer(), src.GetVideoBuffer().Length); + Array.Copy(src.GetVideoBuffer(), dst.GetVideoBuffer(), src.BufferWidth * src.BufferHeight); } else { diff --git a/src/BizHawk.Client.Common/savestates/SavestateFile.cs b/src/BizHawk.Client.Common/savestates/SavestateFile.cs index 10a58a01ee..ef0e163613 100644 --- a/src/BizHawk.Client.Common/savestates/SavestateFile.cs +++ b/src/BizHawk.Client.Common/savestates/SavestateFile.cs @@ -70,31 +70,19 @@ namespace BizHawk.Client.Common if (config.SaveScreenshot && _videoProvider != null) { - var buff = _videoProvider.GetVideoBuffer(); - if (buff.Length == 1) + var outWidth = _videoProvider.BufferWidth; + var outHeight = _videoProvider.BufferHeight; + + // if buffer is too big, scale down screenshot + if (!config.NoLowResLargeScreenshots && outWidth * outHeight >= config.BigScreenshotSize) { - // is a hacky opengl texture ID. can't handle this now! - // need to discuss options - // 1. cores must be able to provide a pixels VideoProvider in addition to a texture ID, on command (not very hard overall but interface changing and work per core) - // 2. SavestateManager must be setup with a mechanism for resolving texture IDs (even less work, but sloppy) - // There are additional problems with AVWriting. They depend on VideoProvider providing pixels. + outWidth /= 2; + outHeight /= 2; } - else + + using (new SimpleTime("Save Framebuffer")) { - int outWidth = _videoProvider.BufferWidth; - int outHeight = _videoProvider.BufferHeight; - - // if buffer is too big, scale down screenshot - if (!config.NoLowResLargeScreenshots && buff.Length >= config.BigScreenshotSize) - { - outWidth /= 2; - outHeight /= 2; - } - - using (new SimpleTime("Save Framebuffer")) - { - bs.PutLump(BinaryStateLump.Framebuffer, s => QuickBmpFile.Save(_videoProvider, s, outWidth, outHeight)); - } + bs.PutLump(BinaryStateLump.Framebuffer, s => QuickBmpFile.Save(_videoProvider, s, outWidth, outHeight)); } } @@ -222,13 +210,13 @@ namespace BizHawk.Client.Common } catch { - var buff = videoProvider.GetVideoBuffer(); + var vb = videoProvider.GetVideoBuffer(); + var vbLen = videoProvider.BufferWidth * videoProvider.BufferHeight; try { - for (int i = 0; i < buff.Length; i++) + for (var i = 0; i < vbLen; i++) { - int j = br.ReadInt32(); - buff[i] = j; + vb[i] = br.ReadInt32(); } } catch (EndOfStreamException) diff --git a/src/BizHawk.Client.EmuHawk/AVOut/AviWriter.cs b/src/BizHawk.Client.EmuHawk/AVOut/AviWriter.cs index b77a2d9d56..24a84bf05f 100644 --- a/src/BizHawk.Client.EmuHawk/AVOut/AviWriter.cs +++ b/src/BizHawk.Client.EmuHawk/AVOut/AviWriter.cs @@ -124,7 +124,7 @@ namespace BizHawk.Client.EmuHawk public VideoCopy(IVideoProvider c) { - _vb = (int[])c.GetVideoBuffer().Clone(); + _vb = c.GetVideoBufferCopy(); BufferWidth = c.BufferWidth; BufferHeight = c.BufferHeight; BackgroundColor = c.BackgroundColor; diff --git a/src/BizHawk.Client.EmuHawk/AVOut/FFmpegWriter.cs b/src/BizHawk.Client.EmuHawk/AVOut/FFmpegWriter.cs index d21ae01af0..5704493bb3 100644 --- a/src/BizHawk.Client.EmuHawk/AVOut/FFmpegWriter.cs +++ b/src/BizHawk.Client.EmuHawk/AVOut/FFmpegWriter.cs @@ -195,7 +195,7 @@ namespace BizHawk.Client.EmuHawk var video = source.GetVideoBuffer(); try { - _muxer.WriteVideoFrame(video); + _muxer.WriteVideoFrame(video.AsSpan(0, _width * _height)); } catch { diff --git a/src/BizHawk.Client.EmuHawk/AVOut/NutMuxer.cs b/src/BizHawk.Client.EmuHawk/AVOut/NutMuxer.cs index 2c9e574ad2..159fd9cd35 100644 --- a/src/BizHawk.Client.EmuHawk/AVOut/NutMuxer.cs +++ b/src/BizHawk.Client.EmuHawk/AVOut/NutMuxer.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Text; using System.IO; using System.Numerics; +using System.Runtime.InteropServices; namespace BizHawk.Client.EmuHawk { @@ -538,15 +539,15 @@ namespace BizHawk.Client.EmuHawk /// raw video data; if length 0, write EOR /// internal error, possible A/V desync /// already written EOR - public void WriteVideoFrame(int[] video) + public void WriteVideoFrame(ReadOnlySpan video) { if (_videoDone) throw new InvalidOperationException("Can't write data after end of relevance!"); if (_audioQueue.Count > 5) throw new Exception("A/V Desync?"); - int dataLen = video.Length * sizeof(int); - byte[] data = _bufferPool.GetBufferAtLeast(dataLen); - Buffer.BlockCopy(video, 0, data, 0, dataLen); + var dataLen = video.Length * sizeof(int); + var data = _bufferPool.GetBufferAtLeast(dataLen); + MemoryMarshal.AsBytes(video).CopyTo(data.AsSpan(0, dataLen)); if (dataLen == 0) { _videoDone = true; @@ -643,12 +644,12 @@ namespace BizHawk.Client.EmuHawk { if (!_videoDone) { - WriteVideoFrame(Array.Empty()); + WriteVideoFrame([ ]); } if (!_audioDone) { - WriteAudioFrame(Array.Empty()); + WriteAudioFrame([ ]); } // flush any remaining queued packets diff --git a/src/BizHawk.Client.EmuHawk/AVOut/NutWriter.cs b/src/BizHawk.Client.EmuHawk/AVOut/NutWriter.cs index 84c2d7055f..08734b59b1 100644 --- a/src/BizHawk.Client.EmuHawk/AVOut/NutWriter.cs +++ b/src/BizHawk.Client.EmuHawk/AVOut/NutWriter.cs @@ -75,7 +75,7 @@ namespace BizHawk.Client.EmuHawk SetVideoParameters(source.BufferWidth, source.BufferHeight); } - _current.WriteVideoFrame(source.GetVideoBuffer()); + _current.WriteVideoFrame(source.GetVideoBuffer().AsSpan(0, _width * _height)); } public void AddSamples(short[] samples) diff --git a/src/BizHawk.Client.EmuHawk/MainForm.cs b/src/BizHawk.Client.EmuHawk/MainForm.cs index c16c53171e..86429d4bbe 100644 --- a/src/BizHawk.Client.EmuHawk/MainForm.cs +++ b/src/BizHawk.Client.EmuHawk/MainForm.cs @@ -2458,7 +2458,7 @@ namespace BizHawk.Client.EmuHawk public BitmapBuffer MakeScreenshotImage() { - var ret = new BitmapBuffer(_currentVideoProvider.BufferWidth, _currentVideoProvider.BufferHeight, _currentVideoProvider.GetVideoBuffer().ToArray()); + var ret = new BitmapBuffer(_currentVideoProvider.BufferWidth, _currentVideoProvider.BufferHeight, _currentVideoProvider.GetVideoBufferCopy()); ret.DiscardAlpha(); return ret; } diff --git a/src/BizHawk.Client.EmuHawk/movie/RecordMovie.cs b/src/BizHawk.Client.EmuHawk/movie/RecordMovie.cs index 289a05d34d..439772bb13 100644 --- a/src/BizHawk.Client.EmuHawk/movie/RecordMovie.cs +++ b/src/BizHawk.Client.EmuHawk/movie/RecordMovie.cs @@ -250,7 +250,7 @@ namespace BizHawk.Client.EmuHawk movieToRecord.SavestateFramebuffer = Array.Empty(); if (_emulator.HasVideoProvider()) { - movieToRecord.SavestateFramebuffer = (int[])_emulator.AsVideoProvider().GetVideoBuffer().Clone(); + movieToRecord.SavestateFramebuffer = _emulator.AsVideoProvider().GetVideoBufferCopy(); } } else if (StartFromCombo.SelectedItem.ToString() is START_FROM_SAVERAM && _emulator.HasSaveRam()) diff --git a/src/BizHawk.Emulation.Common/Interfaces/Services/IVideoProvider.cs b/src/BizHawk.Emulation.Common/Interfaces/Services/IVideoProvider.cs index 0443040e0c..4e0561cfba 100644 --- a/src/BizHawk.Emulation.Common/Interfaces/Services/IVideoProvider.cs +++ b/src/BizHawk.Emulation.Common/Interfaces/Services/IVideoProvider.cs @@ -10,6 +10,9 @@ namespace BizHawk.Emulation.Common { /// /// Returns a frame buffer of the current video content + /// This might be a reference to a stored frame buffer + /// Only * pixels valid + /// (The buffer might be larger than such, so don't rely on /// int[] GetVideoBuffer(); @@ -81,5 +84,19 @@ namespace BizHawk.Emulation.Common b2[i] = b1[i]; } } + + /// + /// Obtains a copy of the video buffer + /// may return a reference + /// and might be much larger than the reported * (to account for differing frame sizes) + /// so this should be used to get an explicit copy + /// + public static int[] GetVideoBufferCopy(this IVideoProvider videoProvider) + { + var vb = videoProvider.GetVideoBuffer(); + var ret = new int[videoProvider.BufferWidth * videoProvider.BufferHeight]; + vb.AsSpan(0, ret.Length).CopyTo(ret); + return ret; + } } }