Skip to content

Commit

Permalink
Avoid zealously copying the entire video buffer
Browse files Browse the repository at this point in the history
this might be larger than bufferwidth*bufferheight, and thus possibly dramantically decrease screenshot/dumping performance
  • Loading branch information
CasualPokePlayer committed Jul 8, 2024
1 parent 57e3c47 commit d8431d6
Show file tree
Hide file tree
Showing 9 changed files with 44 additions and 38 deletions.
2 changes: 1 addition & 1 deletion src/BizHawk.Client.Common/QuickBmpFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ public static unsafe void Copy(IVideoProvider src, IVideoProvider dst)
{
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
{
Expand Down
40 changes: 14 additions & 26 deletions src/BizHawk.Client.Common/savestates/SavestateFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,31 +70,19 @@ public void Create(string filename, SaveStateConfig config)

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
{
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));
}
using (new SimpleTime("Save Framebuffer"))
{
bs.PutLump(BinaryStateLump.Framebuffer, s => QuickBmpFile.Save(_videoProvider, s, outWidth, outHeight));
}
}

Expand Down Expand Up @@ -222,13 +210,13 @@ private static void PopulateFramebuffer(BinaryReader br, IVideoProvider videoPro
}
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)
Expand Down
2 changes: 1 addition & 1 deletion src/BizHawk.Client.EmuHawk/AVOut/AviWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ private class VideoCopy : IVideoProvider

public VideoCopy(IVideoProvider c)
{
_vb = (int[])c.GetVideoBuffer().Clone();
_vb = c.GetVideoBufferCopy();
BufferWidth = c.BufferWidth;
BufferHeight = c.BufferHeight;
BackgroundColor = c.BackgroundColor;
Expand Down
2 changes: 1 addition & 1 deletion src/BizHawk.Client.EmuHawk/AVOut/FFmpegWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ public void AddFrame(IVideoProvider source)
var video = source.GetVideoBuffer();
try
{
_muxer.WriteVideoFrame(video);
_muxer.WriteVideoFrame(video.AsSpan(0, _width * _height));
}
catch
{
Expand Down
13 changes: 7 additions & 6 deletions src/BizHawk.Client.EmuHawk/AVOut/NutMuxer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Text;
using System.IO;
using System.Numerics;
using System.Runtime.InteropServices;

namespace BizHawk.Client.EmuHawk
{
Expand Down Expand Up @@ -538,15 +539,15 @@ public void WriteData(Stream dest)
/// <param name="video">raw video data; if length 0, write EOR</param>
/// <exception cref="Exception">internal error, possible A/V desync</exception>
/// <exception cref="InvalidOperationException">already written EOR</exception>
public void WriteVideoFrame(int[] video)
public void WriteVideoFrame(ReadOnlySpan<int> 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;
Expand Down Expand Up @@ -643,12 +644,12 @@ public void Finish()
{
if (!_videoDone)
{
WriteVideoFrame(Array.Empty<int>());
WriteVideoFrame([ ]);
}

if (!_audioDone)
{
WriteAudioFrame(Array.Empty<short>());
WriteAudioFrame([ ]);
}

// flush any remaining queued packets
Expand Down
2 changes: 1 addition & 1 deletion src/BizHawk.Client.EmuHawk/AVOut/NutWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public void AddFrame(IVideoProvider source)
SetVideoParameters(source.BufferWidth, source.BufferHeight);
}

_current.WriteVideoFrame(source.GetVideoBuffer());
_current.WriteVideoFrame(source.GetVideoBuffer().AsSpan(0, _width * _height));
}

public void AddSamples(short[] samples)
Expand Down
2 changes: 1 addition & 1 deletion src/BizHawk.Client.EmuHawk/MainForm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2458,7 +2458,7 @@ private void AutohideCursor(bool hide)

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;
}
Expand Down
2 changes: 1 addition & 1 deletion src/BizHawk.Client.EmuHawk/movie/RecordMovie.cs
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ private void Ok_Click(object sender, EventArgs e)
movieToRecord.SavestateFramebuffer = Array.Empty<int>();
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())
Expand Down
17 changes: 17 additions & 0 deletions src/BizHawk.Emulation.Common/Interfaces/Services/IVideoProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ public interface IVideoProvider : IEmulatorService
{
/// <summary>
/// Returns a frame buffer of the current video content
/// This might be a reference to a stored frame buffer
/// Only <see cref="BufferWidth"/> * <see cref="BufferHeight"/> pixels valid
/// (The buffer might be larger than such, so don't rely on <see cref="Array.Length"/>
/// </summary>
int[] GetVideoBuffer();

Expand Down Expand Up @@ -81,5 +84,19 @@ public static void PopulateFromBuffer(this IVideoProvider videoProvider, int[] f
b2[i] = b1[i];
}
}

/// <summary>
/// Obtains a copy of the video buffer
/// <see cref="IVideoProvider.GetVideoBuffer" /> may return a reference
/// and might be much larger than the reported <see cref="IVideoProvider.BufferWidth"/> * <see cref="IVideoProvider.BufferHeight"/> (to account for differing frame sizes)
/// so this should be used to get an explicit copy
/// </summary>
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;
}
}
}

0 comments on commit d8431d6

Please sign in to comment.