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;
+ }
}
}