diff --git a/BizHawk.Client.EmuHawk/AVOut/BitmapBufferVideoProvder.cs b/BizHawk.Client.EmuHawk/AVOut/BitmapBufferVideoProvder.cs new file mode 100644 index 0000000000..f727cc1398 --- /dev/null +++ b/BizHawk.Client.EmuHawk/AVOut/BitmapBufferVideoProvder.cs @@ -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; } + } + } +} diff --git a/BizHawk.Client.EmuHawk/AVOut/BmpVideoProvder.cs b/BizHawk.Client.EmuHawk/AVOut/BmpVideoProvder.cs index d8bf7225a2..5773cb461b 100644 --- a/BizHawk.Client.EmuHawk/AVOut/BmpVideoProvder.cs +++ b/BizHawk.Client.EmuHawk/AVOut/BmpVideoProvder.cs @@ -9,12 +9,12 @@ using BizHawk.Emulation.Common; namespace BizHawk.Client.EmuHawk { /// - /// an IVideoProivder wrapping a Bitmap + /// an IVideoProvder wrapping a Bitmap /// - public class BmpVideoProvder : IVideoProvider, IDisposable + public class BmpVideoProvider : IVideoProvider, IDisposable { Bitmap bmp; - public BmpVideoProvder(Bitmap bmp) + public BmpVideoProvider(Bitmap bmp) { this.bmp = bmp; } diff --git a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj index 81c3f004f6..6dbb6e855f 100644 --- a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj +++ b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj @@ -123,6 +123,7 @@ ArchiveChooser.cs + diff --git a/BizHawk.Client.EmuHawk/DisplayManager/DisplayManager.cs b/BizHawk.Client.EmuHawk/DisplayManager/DisplayManager.cs index a7ac0ef255..c940a4867e 100644 --- a/BizHawk.Client.EmuHawk/DisplayManager/DisplayManager.cs +++ b/BizHawk.Client.EmuHawk/DisplayManager/DisplayManager.cs @@ -261,9 +261,28 @@ namespace BizHawk.Client.EmuHawk /// 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 { public int[] GetVideoBuffer() { return new int[] {}; } @@ -293,7 +312,13 @@ namespace BizHawk.Client.EmuHawk if (Global.Config.DispObeyAR && Global.Config.DispFixAspectRatio) 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; @@ -313,8 +338,21 @@ namespace BizHawk.Client.EmuHawk 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 vh = videoProvider.BufferHeight; @@ -381,7 +419,7 @@ TESTEROO: fPresent.GuiRenderer = Renderer; fPresent.GL = GL; - filterProgram.Compile("default", chain_insize, chain_outsize); + filterProgram.Compile("default", chain_insize, chain_outsize, !job.offscreen); if (simulate) { @@ -389,7 +427,7 @@ TESTEROO: else { CurrentFilterProgram = filterProgram; - UpdateSourceDrawingWork(); + UpdateSourceDrawingWork(job); } //cleanup: @@ -398,7 +436,7 @@ TESTEROO: return filterProgram; } - void UpdateSourceDrawingWork() + void UpdateSourceDrawingWork(JobInfo job) { //begin rendering on this context //should this have been done earlier? @@ -440,31 +478,41 @@ TESTEROO: break; } case FilterManager.FilterProgram.ProgramStepType.FinalTarget: - inFinalTarget = true; - rtCurr = null; - CurrentFilterProgram.CurrRenderTarget = null; - GL.BindRenderTarget(null); - break; + { + var size = (Size)step.Args; + inFinalTarget = true; + rtCurr = null; + CurrentFilterProgram.CurrRenderTarget = null; + GL.BindRenderTarget(null); + break; + } } } - 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) + if (job.offscreen) { - presentationPanel.GraphicsControl.SetVsync(vsync); - LastVsyncSettingGraphicsControl = presentationPanel.GraphicsControl; - LastVsyncSetting = vsync; + job.offscreenBB = rtCurr.Texture2d.Resolve(); } + 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 - presentationPanel.GraphicsControl.SwapBuffers(); + //present and conclude drawing + 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 - //presentationPanel.GraphicsControl.End(); + //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(); - NeedsToPaint = false; //?? + NeedsToPaint = false; //?? + } } bool? LastVsyncSetting; diff --git a/BizHawk.Client.EmuHawk/DisplayManager/FilterManager.cs b/BizHawk.Client.EmuHawk/DisplayManager/FilterManager.cs index 5470110035..43d9c13404 100644 --- a/BizHawk.Client.EmuHawk/DisplayManager/FilterManager.cs +++ b/BizHawk.Client.EmuHawk/DisplayManager/FilterManager.cs @@ -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: @@ -231,16 +231,19 @@ namespace BizHawk.Client.EmuHawk.FilterManager } //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]; - if (ps.Type == ProgramStepType.NewTarget) + for (int i = Program.Count - 1; i >= 0; i--) { - var size = (Size)ps.Args; - Debug.Assert(size == outsize); - ps.Type = ProgramStepType.FinalTarget; - ps.Args = null; - break; + var ps = Program[i]; + if (ps.Type == ProgramStepType.NewTarget) + { + var size = (Size)ps.Args; + Debug.Assert(size == outsize); + ps.Type = ProgramStepType.FinalTarget; + ps.Args = size; + break; + } } } } diff --git a/BizHawk.Client.EmuHawk/MainForm.cs b/BizHawk.Client.EmuHawk/MainForm.cs index 994f361f75..c0d4b40c2e 100644 --- a/BizHawk.Client.EmuHawk/MainForm.cs +++ b/BizHawk.Client.EmuHawk/MainForm.cs @@ -10,6 +10,7 @@ using System.Windows.Forms; using BizHawk.Client.Common; using BizHawk.Common; +using BizHawk.Bizware.BizwareGL; using BizHawk.Emulation.Common; using BizHawk.Emulation.Common.IEmulatorExtensions; using BizHawk.Emulation.Cores.Atari.Atari2600; @@ -758,9 +759,10 @@ namespace BizHawk.Client.EmuHawk 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."); @@ -781,9 +783,10 @@ namespace BizHawk.Client.EmuHawk 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."); @@ -1510,43 +1513,46 @@ namespace BizHawk.Client.EmuHawk } } - private static unsafe Image MakeScreenshotImage() + private static unsafe BitmapBuffer MakeScreenshotImage() { - var video = Global.Emulator.VideoProvider; - var image = new Bitmap(video.BufferWidth, video.BufferHeight, PixelFormat.Format32bppArgb); + //var video = Global.Emulator.VideoProvider; + //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 - var framebuf = video.GetVideoBuffer(); - var bmpdata = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); - int* ptr = (int*)bmpdata.Scan0.ToPointer(); - int stride = bmpdata.Stride / 4; - for (int y = 0; y < video.BufferHeight; y++) - { - for (int x = 0; x < video.BufferWidth; x++) - { - int col = framebuf[(y * video.BufferWidth) + x]; + //var framebuf = video.GetVideoBuffer(); + //var bmpdata = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); + //int* ptr = (int*)bmpdata.Scan0.ToPointer(); + //int stride = bmpdata.Stride / 4; + //for (int y = 0; y < video.BufferHeight; y++) + //{ + // for (int x = 0; x < video.BufferWidth; x++) + // { + // int col = framebuf[(y * video.BufferWidth) + x]; - if (Global.Emulator is TI83) - { - if (col == 0) - { - col = Color.Black.ToArgb(); - } - else - { - col = Color.White.ToArgb(); - } - } + // if (Global.Emulator is TI83) + // { + // if (col == 0) + // { + // col = Color.Black.ToArgb(); + // } + // else + // { + // col = Color.White.ToArgb(); + // } + // } - // make opaque - col |= unchecked((int)0xff000000); + // // make opaque + // col |= unchecked((int)0xff000000); - ptr[(y * stride) + x] = col; - } - } + // ptr[(y * stride) + x] = col; + // } + //} - image.UnlockBits(bmpdata); - return image; + //image.UnlockBits(bmpdata); + //return image; } private void SaveStateAs() @@ -1944,26 +1950,11 @@ namespace BizHawk.Client.EmuHawk Slot9StatusButton.BackColor = Global.Config.SaveSlot == 9 ? SystemColors.Highlight : SystemColors.Control; } - //TODO GL - this whole feature will have to be re-added - private Bitmap CaptureOSD() // sort of like MakeScreenShot(), but with OSD and LUA captured as well. slow and bad. + private BitmapBuffer CaptureOSD() { - // // this code captures the emu display with OSD and lua composited onto it. - // // it's slow and a bit hackish; a better solution is to create a new - // // "dummy render" class that implements IRenderer, IBlitter, and possibly - // // 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; + var bb = GlobalWin.DisplayManager.RenderOffscreen(Global.Emulator.VideoProvider); + bb.Normalize(true); + return bb; } private void IncreaseWindowSize() @@ -2713,7 +2704,7 @@ namespace BizHawk.Client.EmuHawk var sfd = new SaveFileDialog(); 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); } else @@ -2816,57 +2807,68 @@ namespace BizHawk.Client.EmuHawk _aviSoundInput.GetSamples(temp); _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 { IVideoProvider output; + IDisposable disposableOutput = null; if (_avwriterResizew > 0 && _avwriterResizeh > 0) { - Bitmap bmpin; - if (Global.Config.AVI_CaptureOSD) + BizHawk.Bizware.BizwareGL.BitmapBuffer bbin = null; + Bitmap bmpin = null; + Bitmap bmpout = null; + try { - bmpin = 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) + if (Global.Config.AVI_CaptureOSD) { - g.Clear(Color.FromArgb(Global.Emulator.VideoProvider.BackgroundColor)); - g.DrawImageUnscaled(bmpin, (bmpout.Width - bmpin.Width) / 2, (bmpout.Height - bmpin.Height) / 2); + bbin = CaptureOSD(); } 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 { - output = Global.Config.AVI_CaptureOSD - ? new BmpVideoProvder(CaptureOSD()) - : Global.Emulator.VideoProvider; + if (Global.Config.AVI_CaptureOSD) + { + output = new BitmapBufferVideoProvider(CaptureOSD()); + disposableOutput = (IDisposable)output; + } + else + output = Global.Emulator.VideoProvider; } _currAviWriter.AddFrame(output); - if (output is BmpVideoProvder) + + if (disposableOutput != null) { - (output as BmpVideoProvder).Dispose(); + disposableOutput.Dispose(); } _currAviWriter.AddSamples(temp); diff --git a/Bizware/BizHawk.Bizware.BizwareGL.OpenTK/IGL_TK.cs b/Bizware/BizHawk.Bizware.BizwareGL.OpenTK/IGL_TK.cs index 0e4b279e2d..2fdb0c4f22 100644 --- a/Bizware/BizHawk.Bizware.BizwareGL.OpenTK/IGL_TK.cs +++ b/Bizware/BizHawk.Bizware.BizwareGL.OpenTK/IGL_TK.cs @@ -465,7 +465,7 @@ namespace BizHawk.Bizware.BizwareGL.Drivers.OpenTK BindTexture2d(tex); var bb = new BitmapBuffer(tex.IntWidth, tex.IntHeight); 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); return bb; } diff --git a/Bizware/BizHawk.Bizware.BizwareGL/BitmapBuffer.cs b/Bizware/BizHawk.Bizware.BizwareGL/BitmapBuffer.cs index 218eef9e57..ae84c7fc10 100644 --- a/Bizware/BizHawk.Bizware.BizwareGL/BitmapBuffer.cs +++ b/Bizware/BizHawk.Bizware.BizwareGL/BitmapBuffer.cs @@ -76,6 +76,34 @@ namespace BizHawk.Bizware.BizwareGL 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 void SetPixel(int x, int y, int value) { Pixels[Width * y + x] = value; } public Color GetPixelAsColor(int x, int y)