Avoid zealously copying the entire video buffer

this might be larger than bufferwidth*bufferheight, and thus possibly dramantically decrease screenshot/dumping performance
This commit is contained in:
CasualPokePlayer 2024-07-08 16:28:40 -07:00
parent 57e3c47bcc
commit d8431d615e
9 changed files with 44 additions and 38 deletions

View File

@ -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
{

View File

@ -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)

View File

@ -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;

View File

@ -195,7 +195,7 @@ namespace BizHawk.Client.EmuHawk
var video = source.GetVideoBuffer();
try
{
_muxer.WriteVideoFrame(video);
_muxer.WriteVideoFrame(video.AsSpan(0, _width * _height));
}
catch
{

View File

@ -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
/// <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;
@ -643,12 +644,12 @@ namespace BizHawk.Client.EmuHawk
{
if (!_videoDone)
{
WriteVideoFrame(Array.Empty<int>());
WriteVideoFrame([ ]);
}
if (!_audioDone)
{
WriteAudioFrame(Array.Empty<short>());
WriteAudioFrame([ ]);
}
// flush any remaining queued packets

View File

@ -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)

View File

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

View File

@ -250,7 +250,7 @@ namespace BizHawk.Client.EmuHawk
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())

View File

@ -10,6 +10,9 @@ namespace BizHawk.Emulation.Common
{
/// <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();
@ -81,5 +84,19 @@ namespace BizHawk.Emulation.Common
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;
}
}
}