fix av dumping with osd enabled (screenshots as well);

default av dumping extension to the dumper plugin's desired default
This commit is contained in:
zeromus 2014-06-02 20:16:59 +00:00
parent 239eb5b268
commit bb1b1ff5b5
8 changed files with 260 additions and 122 deletions

View File

@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using BizHawk.Emulation.Common;
using BizHawk.Bizware.BizwareGL;
namespace BizHawk.Client.EmuHawk
{
public class BitmapBufferVideoProvider : IVideoProvider, IDisposable
{
BitmapBuffer bb;
public BitmapBufferVideoProvider(BitmapBuffer bb)
{
this.bb = bb;
}
public void Dispose()
{
if (bb != null) bb.Dispose();
bb = null;
}
public int[] GetVideoBuffer()
{
return bb.Pixels;
}
public int VirtualWidth
{
get { return bb.Width; }
}
public int VirtualHeight
{
get { return bb.Height; }
}
public int BufferWidth
{
get { return bb.Width; }
}
public int BufferHeight
{
get { return bb.Height; }
}
public int BackgroundColor
{
get { return 0; }
}
}
}

View File

@ -9,12 +9,12 @@ using BizHawk.Emulation.Common;
namespace BizHawk.Client.EmuHawk namespace BizHawk.Client.EmuHawk
{ {
/// <summary> /// <summary>
/// an IVideoProivder wrapping a Bitmap /// an IVideoProvder wrapping a Bitmap
/// </summary> /// </summary>
public class BmpVideoProvder : IVideoProvider, IDisposable public class BmpVideoProvider : IVideoProvider, IDisposable
{ {
Bitmap bmp; Bitmap bmp;
public BmpVideoProvder(Bitmap bmp) public BmpVideoProvider(Bitmap bmp)
{ {
this.bmp = bmp; this.bmp = bmp;
} }

View File

@ -123,6 +123,7 @@
<DependentUpon>ArchiveChooser.cs</DependentUpon> <DependentUpon>ArchiveChooser.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="AVOut\AviWriter.cs" /> <Compile Include="AVOut\AviWriter.cs" />
<Compile Include="AVOut\BitmapBufferVideoProvder.cs" />
<Compile Include="AVOut\BmpVideoProvder.cs" /> <Compile Include="AVOut\BmpVideoProvder.cs" />
<Compile Include="AVOut\FFmpegWriter.cs" /> <Compile Include="AVOut\FFmpegWriter.cs" />
<Compile Include="AVOut\FFmpegWriterForm.cs"> <Compile Include="AVOut\FFmpegWriterForm.cs">

View File

@ -261,9 +261,28 @@ namespace BizHawk.Client.EmuHawk
/// </summary> /// </summary>
public void UpdateSource(IVideoProvider videoProvider) public void UpdateSource(IVideoProvider videoProvider)
{ {
UpdateSourceInternal(videoProvider,false, GraphicsControl.Size); var job = new JobInfo
{
videoProvider = videoProvider,
simulate = false,
chain_outsize = GraphicsControl.Size
};
UpdateSourceInternal(job);
} }
public BitmapBuffer RenderOffscreen(IVideoProvider videoProvider)
{
var job = new JobInfo
{
videoProvider = videoProvider,
simulate = false,
chain_outsize = GraphicsControl.Size,
offscreen = true
};
UpdateSourceInternal(job);
return job.offscreenBB;
}
class FakeVideoProvider : IVideoProvider class FakeVideoProvider : IVideoProvider
{ {
public int[] GetVideoBuffer() { return new int[] {}; } public int[] GetVideoBuffer() { return new int[] {}; }
@ -293,7 +312,13 @@ namespace BizHawk.Client.EmuHawk
if (Global.Config.DispObeyAR && Global.Config.DispFixAspectRatio) if (Global.Config.DispObeyAR && Global.Config.DispFixAspectRatio)
chain_outsize = new Size(fvp.VirtualWidth * zoom, fvp.VirtualHeight * zoom); chain_outsize = new Size(fvp.VirtualWidth * zoom, fvp.VirtualHeight * zoom);
var filterProgram = UpdateSourceInternal(fvp, true, chain_outsize); var job = new JobInfo
{
videoProvider = fvp,
simulate = true,
chain_outsize = chain_outsize
};
var filterProgram = UpdateSourceInternal(job);
var size = filterProgram.Filters[filterProgram.Filters.Count - 1].FindOutput().SurfaceFormat.Size; var size = filterProgram.Filters[filterProgram.Filters.Count - 1].FindOutput().SurfaceFormat.Size;
@ -313,8 +338,21 @@ namespace BizHawk.Client.EmuHawk
return size; return size;
} }
FilterManager.FilterProgram UpdateSourceInternal(IVideoProvider videoProvider, bool simulate, Size chain_outsize) class JobInfo
{ {
public IVideoProvider videoProvider;
public bool simulate;
public Size chain_outsize;
public bool offscreen;
public BitmapBuffer offscreenBB;
}
FilterManager.FilterProgram UpdateSourceInternal(JobInfo job)
{
IVideoProvider videoProvider = job.videoProvider;
bool simulate = job.simulate;
Size chain_outsize = job.chain_outsize;
int vw = videoProvider.BufferWidth; int vw = videoProvider.BufferWidth;
int vh = videoProvider.BufferHeight; int vh = videoProvider.BufferHeight;
@ -381,7 +419,7 @@ TESTEROO:
fPresent.GuiRenderer = Renderer; fPresent.GuiRenderer = Renderer;
fPresent.GL = GL; fPresent.GL = GL;
filterProgram.Compile("default", chain_insize, chain_outsize); filterProgram.Compile("default", chain_insize, chain_outsize, !job.offscreen);
if (simulate) if (simulate)
{ {
@ -389,7 +427,7 @@ TESTEROO:
else else
{ {
CurrentFilterProgram = filterProgram; CurrentFilterProgram = filterProgram;
UpdateSourceDrawingWork(); UpdateSourceDrawingWork(job);
} }
//cleanup: //cleanup:
@ -398,7 +436,7 @@ TESTEROO:
return filterProgram; return filterProgram;
} }
void UpdateSourceDrawingWork() void UpdateSourceDrawingWork(JobInfo job)
{ {
//begin rendering on this context //begin rendering on this context
//should this have been done earlier? //should this have been done earlier?
@ -440,31 +478,41 @@ TESTEROO:
break; break;
} }
case FilterManager.FilterProgram.ProgramStepType.FinalTarget: case FilterManager.FilterProgram.ProgramStepType.FinalTarget:
inFinalTarget = true; {
rtCurr = null; var size = (Size)step.Args;
CurrentFilterProgram.CurrRenderTarget = null; inFinalTarget = true;
GL.BindRenderTarget(null); rtCurr = null;
break; CurrentFilterProgram.CurrRenderTarget = null;
GL.BindRenderTarget(null);
break;
}
} }
} }
Debug.Assert(inFinalTarget);
//apply the vsync setting (should probably try to avoid repeating this) if (job.offscreen)
bool vsync = Global.Config.VSyncThrottle || Global.Config.VSync;
if (LastVsyncSetting != vsync || LastVsyncSettingGraphicsControl != presentationPanel.GraphicsControl)
{ {
presentationPanel.GraphicsControl.SetVsync(vsync); job.offscreenBB = rtCurr.Texture2d.Resolve();
LastVsyncSettingGraphicsControl = presentationPanel.GraphicsControl;
LastVsyncSetting = vsync;
} }
else
{
Debug.Assert(inFinalTarget);
//apply the vsync setting (should probably try to avoid repeating this)
bool vsync = Global.Config.VSyncThrottle || Global.Config.VSync;
if (LastVsyncSetting != vsync || LastVsyncSettingGraphicsControl != presentationPanel.GraphicsControl)
{
presentationPanel.GraphicsControl.SetVsync(vsync);
LastVsyncSettingGraphicsControl = presentationPanel.GraphicsControl;
LastVsyncSetting = vsync;
}
//present and conclude drawing //present and conclude drawing
presentationPanel.GraphicsControl.SwapBuffers(); presentationPanel.GraphicsControl.SwapBuffers();
//nope. dont do this. workaround for slow context switching on intel GPUs. just switch to another context when necessary before doing anything //nope. dont do this. workaround for slow context switching on intel GPUs. just switch to another context when necessary before doing anything
//presentationPanel.GraphicsControl.End(); //presentationPanel.GraphicsControl.End();
NeedsToPaint = false; //?? NeedsToPaint = false; //??
}
} }
bool? LastVsyncSetting; bool? LastVsyncSetting;

View File

@ -117,7 +117,7 @@ namespace BizHawk.Client.EmuHawk.FilterManager
} }
} }
public void Compile(string channel, Size insize, Size outsize) public void Compile(string channel, Size insize, Size outsize, bool finalTarget)
{ {
RETRY: RETRY:
@ -231,16 +231,19 @@ namespace BizHawk.Client.EmuHawk.FilterManager
} }
//patch the program so that the final rendertarget set operation is the framebuffer instead //patch the program so that the final rendertarget set operation is the framebuffer instead
for (int i = Program.Count - 1; i >= 0; i--) if (finalTarget)
{ {
var ps = Program[i]; for (int i = Program.Count - 1; i >= 0; i--)
if (ps.Type == ProgramStepType.NewTarget)
{ {
var size = (Size)ps.Args; var ps = Program[i];
Debug.Assert(size == outsize); if (ps.Type == ProgramStepType.NewTarget)
ps.Type = ProgramStepType.FinalTarget; {
ps.Args = null; var size = (Size)ps.Args;
break; Debug.Assert(size == outsize);
ps.Type = ProgramStepType.FinalTarget;
ps.Args = size;
break;
}
} }
} }
} }

View File

@ -10,6 +10,7 @@ using System.Windows.Forms;
using BizHawk.Client.Common; using BizHawk.Client.Common;
using BizHawk.Common; using BizHawk.Common;
using BizHawk.Bizware.BizwareGL;
using BizHawk.Emulation.Common; using BizHawk.Emulation.Common;
using BizHawk.Emulation.Common.IEmulatorExtensions; using BizHawk.Emulation.Common.IEmulatorExtensions;
using BizHawk.Emulation.Cores.Atari.Atari2600; using BizHawk.Emulation.Cores.Atari.Atari2600;
@ -758,9 +759,10 @@ namespace BizHawk.Client.EmuHawk
public void TakeScreenshotToClipboard() public void TakeScreenshotToClipboard()
{ {
using (var img = Global.Config.Screenshot_CaptureOSD ? CaptureOSD() : MakeScreenshotImage()) using (var bb = Global.Config.Screenshot_CaptureOSD ? CaptureOSD() : MakeScreenshotImage())
{ {
Clipboard.SetImage(img); using(var img = bb.ToSysdrawingBitmap())
Clipboard.SetImage(img);
} }
GlobalWin.OSD.AddMessage("Screenshot saved to clipboard."); GlobalWin.OSD.AddMessage("Screenshot saved to clipboard.");
@ -781,9 +783,10 @@ namespace BizHawk.Client.EmuHawk
fi.Directory.Create(); fi.Directory.Create();
} }
using (var img = Global.Config.Screenshot_CaptureOSD ? CaptureOSD() : MakeScreenshotImage()) using (var bb = Global.Config.Screenshot_CaptureOSD ? CaptureOSD() : MakeScreenshotImage())
{ {
img.Save(fi.FullName, ImageFormat.Png); using(var img = bb.ToSysdrawingBitmap())
img.Save(fi.FullName, ImageFormat.Png);
} }
GlobalWin.OSD.AddMessage(fi.Name + " saved."); GlobalWin.OSD.AddMessage(fi.Name + " saved.");
@ -1510,43 +1513,46 @@ namespace BizHawk.Client.EmuHawk
} }
} }
private static unsafe Image MakeScreenshotImage() private static unsafe BitmapBuffer MakeScreenshotImage()
{ {
var video = Global.Emulator.VideoProvider; //var video = Global.Emulator.VideoProvider;
var image = new Bitmap(video.BufferWidth, video.BufferHeight, PixelFormat.Format32bppArgb); //var image = new Bitmap(video.BufferWidth, video.BufferHeight, PixelFormat.Format32bppArgb);
return new BitmapBuffer(Global.Emulator.VideoProvider.BufferWidth, Global.Emulator.VideoProvider.BufferHeight, Global.Emulator.VideoProvider.GetVideoBuffer());
//this is all rotten.
//among other things, cores are required to set 0xFF000000 themselves
// TODO - replace with BitmapBuffer // TODO - replace with BitmapBuffer
var framebuf = video.GetVideoBuffer(); //var framebuf = video.GetVideoBuffer();
var bmpdata = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); //var bmpdata = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
int* ptr = (int*)bmpdata.Scan0.ToPointer(); //int* ptr = (int*)bmpdata.Scan0.ToPointer();
int stride = bmpdata.Stride / 4; //int stride = bmpdata.Stride / 4;
for (int y = 0; y < video.BufferHeight; y++) //for (int y = 0; y < video.BufferHeight; y++)
{ //{
for (int x = 0; x < video.BufferWidth; x++) // for (int x = 0; x < video.BufferWidth; x++)
{ // {
int col = framebuf[(y * video.BufferWidth) + x]; // int col = framebuf[(y * video.BufferWidth) + x];
if (Global.Emulator is TI83) // if (Global.Emulator is TI83)
{ // {
if (col == 0) // if (col == 0)
{ // {
col = Color.Black.ToArgb(); // col = Color.Black.ToArgb();
} // }
else // else
{ // {
col = Color.White.ToArgb(); // col = Color.White.ToArgb();
} // }
} // }
// make opaque // // make opaque
col |= unchecked((int)0xff000000); // col |= unchecked((int)0xff000000);
ptr[(y * stride) + x] = col; // ptr[(y * stride) + x] = col;
} // }
} //}
image.UnlockBits(bmpdata); //image.UnlockBits(bmpdata);
return image; //return image;
} }
private void SaveStateAs() private void SaveStateAs()
@ -1944,26 +1950,11 @@ namespace BizHawk.Client.EmuHawk
Slot9StatusButton.BackColor = Global.Config.SaveSlot == 9 ? SystemColors.Highlight : SystemColors.Control; Slot9StatusButton.BackColor = Global.Config.SaveSlot == 9 ? SystemColors.Highlight : SystemColors.Control;
} }
//TODO GL - this whole feature will have to be re-added private BitmapBuffer CaptureOSD()
private Bitmap CaptureOSD() // sort of like MakeScreenShot(), but with OSD and LUA captured as well. slow and bad.
{ {
// // this code captures the emu display with OSD and lua composited onto it. var bb = GlobalWin.DisplayManager.RenderOffscreen(Global.Emulator.VideoProvider);
// // it's slow and a bit hackish; a better solution is to create a new bb.Normalize(true);
// // "dummy render" class that implements IRenderer, IBlitter, and possibly return bb;
// // IVideoProvider, and pass that to DisplayManager.UpdateSourceEx()
// if (_captureOsdRvp == null)
// {
// _captureOsdRvp = new RetainedViewportPanel();
// _captureOsdSrp = new SysdrawingRenderPanel(_captureOsdRvp);
// }
// // this size can be different for showing off stretching or filters
// _captureOsdRvp.Width = Global.Emulator.VideoProvider.BufferWidth;
// _captureOsdRvp.Height = Global.Emulator.VideoProvider.BufferHeight;
// GlobalWin.DisplayManager.UpdateSourceEx(Global.Emulator.VideoProvider, _captureOsdSrp);
// return (Bitmap)_captureOsdRvp.GetBitmap().Clone();
return null;
} }
private void IncreaseWindowSize() private void IncreaseWindowSize()
@ -2713,7 +2704,7 @@ namespace BizHawk.Client.EmuHawk
var sfd = new SaveFileDialog(); var sfd = new SaveFileDialog();
if (!(Global.Emulator is NullEmulator)) if (!(Global.Emulator is NullEmulator))
{ {
sfd.FileName = PathManager.FilesystemSafeName(Global.Game); sfd.FileName = PathManager.FilesystemSafeName(Global.Game) + "." + aw.DesiredExtension(); //dont use Path.ChangeExtension, it might wreck game names with dots in them
sfd.InitialDirectory = PathManager.MakeAbsolutePath(Global.Config.PathEntries.AvPathFragment, null); sfd.InitialDirectory = PathManager.MakeAbsolutePath(Global.Config.PathEntries.AvPathFragment, null);
} }
else else
@ -2816,57 +2807,68 @@ namespace BizHawk.Client.EmuHawk
_aviSoundInput.GetSamples(temp); _aviSoundInput.GetSamples(temp);
_dumpProxy.buffer.enqueue_samples(temp, (int)nsamp); _dumpProxy.buffer.enqueue_samples(temp, (int)nsamp);
//TODO ZERO - this code is pretty jacked. we'll want to frugalize buffers better for speedier dumping, and we might want to rely on the GL layer for padding
try try
{ {
IVideoProvider output; IVideoProvider output;
IDisposable disposableOutput = null;
if (_avwriterResizew > 0 && _avwriterResizeh > 0) if (_avwriterResizew > 0 && _avwriterResizeh > 0)
{ {
Bitmap bmpin; BizHawk.Bizware.BizwareGL.BitmapBuffer bbin = null;
if (Global.Config.AVI_CaptureOSD) Bitmap bmpin = null;
Bitmap bmpout = null;
try
{ {
bmpin = CaptureOSD(); if (Global.Config.AVI_CaptureOSD)
}
else
{
bmpin = new Bitmap(
Global.Emulator.VideoProvider.BufferWidth,
Global.Emulator.VideoProvider.BufferHeight,
PixelFormat.Format32bppArgb);
var lockdata = bmpin.LockBits(
new Rectangle(0, 0, bmpin.Width, bmpin.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
System.Runtime.InteropServices.Marshal.Copy(
Global.Emulator.VideoProvider.GetVideoBuffer(), 0, lockdata.Scan0, bmpin.Width * bmpin.Height);
bmpin.UnlockBits(lockdata);
}
var bmpout = new Bitmap(_avwriterResizew, _avwriterResizeh, PixelFormat.Format32bppArgb);
using (var g = Graphics.FromImage(bmpout))
{
if (_avwriterpad)
{ {
g.Clear(Color.FromArgb(Global.Emulator.VideoProvider.BackgroundColor)); bbin = CaptureOSD();
g.DrawImageUnscaled(bmpin, (bmpout.Width - bmpin.Width) / 2, (bmpout.Height - bmpin.Height) / 2);
} }
else else
{ {
g.DrawImage(bmpin, new Rectangle(0, 0, bmpout.Width, bmpout.Height)); bbin = new Bizware.BizwareGL.BitmapBuffer(Global.Emulator.VideoProvider.BufferWidth, Global.Emulator.VideoProvider.BufferHeight, Global.Emulator.VideoProvider.GetVideoBuffer());
} }
}
bmpin.Dispose();
output = new BmpVideoProvder(bmpout); bmpout = new Bitmap(_avwriterResizew, _avwriterResizeh, PixelFormat.Format32bppArgb);
bmpin = bbin.ToSysdrawingBitmap();
using (var g = Graphics.FromImage(bmpout))
{
if (_avwriterpad)
{
g.Clear(Color.FromArgb(Global.Emulator.VideoProvider.BackgroundColor));
g.DrawImageUnscaled(bmpin, (bmpout.Width - bmpin.Width) / 2, (bmpout.Height - bmpin.Height) / 2);
}
else
{
g.DrawImage(bmpin, new Rectangle(0, 0, bmpout.Width, bmpout.Height));
}
}
output = new BmpVideoProvider(bmpout);
disposableOutput = (IDisposable)output;
}
finally
{
if (bbin != null) bbin.Dispose();
if (bmpin != null) bmpin.Dispose();
}
} }
else else
{ {
output = Global.Config.AVI_CaptureOSD if (Global.Config.AVI_CaptureOSD)
? new BmpVideoProvder(CaptureOSD()) {
: Global.Emulator.VideoProvider; output = new BitmapBufferVideoProvider(CaptureOSD());
disposableOutput = (IDisposable)output;
}
else
output = Global.Emulator.VideoProvider;
} }
_currAviWriter.AddFrame(output); _currAviWriter.AddFrame(output);
if (output is BmpVideoProvder)
if (disposableOutput != null)
{ {
(output as BmpVideoProvder).Dispose(); disposableOutput.Dispose();
} }
_currAviWriter.AddSamples(temp); _currAviWriter.AddSamples(temp);

View File

@ -465,7 +465,7 @@ namespace BizHawk.Bizware.BizwareGL.Drivers.OpenTK
BindTexture2d(tex); BindTexture2d(tex);
var bb = new BitmapBuffer(tex.IntWidth, tex.IntHeight); var bb = new BitmapBuffer(tex.IntWidth, tex.IntHeight);
var bmpdata = bb.LockBits(); var bmpdata = bb.LockBits();
GL.GetTexImage(TextureTarget.Texture2D, 0, PixelFormat.Bgra, PixelType.Byte, bmpdata.Scan0); GL.GetTexImage(TextureTarget.Texture2D, 0, PixelFormat.Bgra, PixelType.UnsignedByte, bmpdata.Scan0);
bb.UnlockBits(bmpdata); bb.UnlockBits(bmpdata);
return bb; return bb;
} }

View File

@ -76,6 +76,34 @@ namespace BizHawk.Bizware.BizwareGL
UnlockBits(CurrLock); UnlockBits(CurrLock);
} }
public unsafe void Normalize(bool yflip)
{
var bmpdata = LockBits();
int[] newPixels = new int[Width * Height];
int todo = Width*Height;
int* s = (int*)bmpdata.Scan0.ToPointer();
fixed (int* d = newPixels)
{
if (yflip)
for (int y = 0, si = 0, di = (Height - 1) * Width; y < Height; y++)
{
for (int x = 0; x < Width; x++, si++, di++)
{
d[di] = s[si] | unchecked((int)0xFF000000);
}
di -= Width * 2;
}
else
{
//TODO
}
}
UnlockBits(bmpdata);
Pixels = newPixels;
}
public int GetPixel(int x, int y) { return Pixels[Width * y + x]; } public int GetPixel(int x, int y) { return Pixels[Width * y + x]; }
public void SetPixel(int x, int y, int value) { Pixels[Width * y + x] = value; } public void SetPixel(int x, int y, int value) { Pixels[Width * y + x] = value; }
public Color GetPixelAsColor(int x, int y) public Color GetPixelAsColor(int x, int y)