emergency measures to return display thread stuff to the way it was before (i think? i hope?) as a reasonable baseline when no video pipeline fanciness is called for (no lua overlay, for instance) so that this is useful for gaming again. also add a toggle for the entire HUD system which bypasses some extra work for people stuck in sysdrawing mode who need more speed.

This commit is contained in:
zeromus 2012-06-10 02:19:36 +00:00
parent 966509858d
commit 5b86a9c375
7 changed files with 462 additions and 297 deletions

View File

@ -196,6 +196,7 @@ namespace BizHawk.MultiClient
public int DispMultiy = 0;
public int DispMultianchor = 0;
public bool DisplayGDI = false;
public bool SuppressGui = false;
public bool DisplayStatusBar = true;
public int DispRamWatchx = 0;
public int DispRamWatchy = 70;

View File

@ -8,8 +8,79 @@ using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
//using dx=SlimDX;
//using d3d=SlimDX.Direct3D9;
namespace BizHawk.MultiClient
{
/// <summary>
/// encapsulates thread-safe concept of pending/current display surfaces, reusing buffers where matching
/// sizes are available and keeping them cleaned up when they dont seem like theyll need to be used anymore
/// </summary>
class SwappableDisplaySurfaceSet
{
DisplaySurface Pending, Current;
Queue<DisplaySurface> ReleasedSurfaces = new Queue<DisplaySurface>();
/// <summary>
/// retrieves a surface with the specified size, reusing an old buffer if available and clearing if requested
/// </summary>
public DisplaySurface AllocateSurface(int width, int height, bool needsClear = true)
{
for (; ; )
{
DisplaySurface trial;
lock (this)
{
if (ReleasedSurfaces.Count == 0) break;
trial = ReleasedSurfaces.Dequeue();
}
if (trial.Width == width && trial.Height == height)
{
if (needsClear) trial.Clear();
return trial;
}
trial.Dispose();
}
return new DisplaySurface(width, height);
}
/// <summary>
/// sets the provided buffer as pending. takes control of the supplied buffer
/// </summary>
public void SetPending(DisplaySurface newPending)
{
lock (this)
{
if (Pending != null) ReleasedSurfaces.Enqueue(Pending);
Pending = newPending;
}
}
public void ReleaseSurface(DisplaySurface surface)
{
lock (this) ReleasedSurfaces.Enqueue(surface);
}
/// <summary>
/// returns the current buffer, making the most recent pending buffer (if there is such) as the new current first.
/// </summary>
public DisplaySurface GetCurrent()
{
lock (this)
{
if (Pending != null)
{
if (Current != null) ReleasedSurfaces.Enqueue(Current);
Current = Pending;
Pending = null;
}
}
return Current;
}
}
public interface IDisplayFilter
{
/// <summary>
@ -29,6 +100,15 @@ namespace BizHawk.MultiClient
public Size OutputSize;
}
interface IDisplayDriver
{
}
class Direct3DDisplayDriver : IDisplayDriver
{
}
public class DisplaySurface : IDisposable
{
/// <summary>
@ -64,6 +144,19 @@ namespace BizHawk.MultiClient
return bmp;
}
public static DisplaySurface DisplaySurfaceWrappingBitmap(Bitmap bmp)
{
DisplaySurface ret = new DisplaySurface();
ret.Width = bmp.Width;
ret.Height = bmp.Height;
ret.bmp = bmp;
return ret;
}
private DisplaySurface()
{
}
public DisplaySurface(int width, int height)
{
//can't create a bitmap with zero dimensions, so for now, just bump it up to one
@ -517,72 +610,6 @@ namespace BizHawk.MultiClient
//the surface to use to render a lua layer at native resolution (under the OSD)
DisplaySurface luaNativeSurfacePreOSD;
/// <summary>
/// encapsulates thread-safe concept of pending/current display surfaces, reusing buffers where matching
/// sizes are available and keeping them cleaned up when they dont seem like theyll need to be used anymore
/// </summary>
class SwappableDisplaySurfaceSet
{
DisplaySurface Pending, Current;
Queue<DisplaySurface> ReleasedSurfaces = new Queue<DisplaySurface>();
/// <summary>
/// retrieves a surface with the specified size, reusing an old buffer if available and clearing if requested
/// </summary>
public DisplaySurface AllocateSurface(int width, int height, bool needsClear=true)
{
for(;;)
{
DisplaySurface trial;
lock (this)
{
if (ReleasedSurfaces.Count == 0) break;
trial = ReleasedSurfaces.Dequeue();
}
if (trial.Width == width && trial.Height == height)
{
if(needsClear) trial.Clear();
return trial;
}
trial.Dispose();
}
return new DisplaySurface(width, height);
}
/// <summary>
/// sets the provided buffer as pending. takes control of the supplied buffer
/// </summary>
public void SetPending(DisplaySurface newPending)
{
lock(this)
{
if (Pending != null) ReleasedSurfaces.Enqueue(Pending);
Pending = newPending;
}
}
public void ReleaseSurface(DisplaySurface surface)
{
lock (this) ReleasedSurfaces.Enqueue(surface);
}
/// <summary>
/// returns the current buffer, making the most recent pending buffer (if there is such) as the new current first.
/// </summary>
public DisplaySurface GetCurrent()
{
lock(this)
{
if(Pending != null)
{
if (Current != null) ReleasedSurfaces.Enqueue(Current);
Current = Pending;
Pending = null;
}
}
return Current;
}
}
SwappableDisplaySurfaceSet luaNativeSurfaceSet = new SwappableDisplaySurfaceSet();
public void SetLuaSurfaceNativePreOSD(DisplaySurface surface) { luaNativeSurfaceSet.SetPending(surface); }
@ -680,40 +707,76 @@ namespace BizHawk.MultiClient
int w = currNativeWidth;
int h = currNativeHeight;
var nativeBmp = nativeDisplaySurfaceSet.AllocateSurface(w, h, true);
using (var g = Graphics.FromImage(nativeBmp.PeekBitmap()))
DisplaySurface luaEmuSurface = luaEmuSurfaceSet.GetCurrent();
DisplaySurface luaSurface = luaNativeSurfaceSet.GetCurrent();
//do we have anything to do?
bool complexComposite = false;
if (luaEmuSurface != null) complexComposite = true;
if (luaSurface != null) complexComposite = true;
if (!complexComposite)
{
//scale the source bitmap to the desired size of the render panel
g.PixelOffsetMode = PixelOffsetMode.HighSpeed;
g.InterpolationMode = InterpolationMode.NearestNeighbor;
g.CompositingMode = CompositingMode.SourceCopy;
g.CompositingQuality = CompositingQuality.HighSpeed;
g.DrawImage(currentSourceSurface.PeekBitmap(), 0, 0, w, h);
if (Global.Config.SuppressGui)
{
Global.RenderPanel.FastRenderAndPresent(currentSourceSurface);
}
else
{
Global.RenderPanel.Render(currentSourceSurface);
var nativeBmp = nativeDisplaySurfaceSet.AllocateSurface(w, h, true);
using (var g = Graphics.FromImage(nativeBmp.PeekBitmap()))
{
Global.OSD.DrawScreenInfo(g);
Global.OSD.DrawMessages(g);
}
Global.RenderPanel.RenderOverlay(nativeBmp);
//release the native resolution image
nativeDisplaySurfaceSet.ReleaseSurface(nativeBmp);
Global.RenderPanel.Present();
}
}
else
{
var nativeBmp = nativeDisplaySurfaceSet.AllocateSurface(w, h, true);
using (var g = Graphics.FromImage(nativeBmp.PeekBitmap()))
{
//scale the source bitmap to the desired size of the render panel
g.PixelOffsetMode = PixelOffsetMode.HighSpeed;
g.InterpolationMode = InterpolationMode.NearestNeighbor;
g.CompositingMode = CompositingMode.SourceCopy;
g.CompositingQuality = CompositingQuality.HighSpeed;
g.DrawImage(currentSourceSurface.PeekBitmap(), 0, 0, w, h);
//switch to fancier composition for OSD overlays and such
g.CompositingMode = CompositingMode.SourceOver;
//switch to fancier composition for OSD overlays and such
g.CompositingMode = CompositingMode.SourceOver;
//this could have been done onto the source surface earlier and then scaled only once but the whole composition system needs revising, soo..
DisplaySurface luaEmuSurface = luaEmuSurfaceSet.GetCurrent();
if (luaEmuSurface != null) g.DrawImage(luaEmuSurface.PeekBitmap(), 0, 0, w, h);
g.Clip = new Region(new Rectangle(0, 0, nativeBmp.Width, nativeBmp.Height));
//this could have been done onto the source surface earlier and then scaled only once but the whole composition system needs revising, soo..
if (luaEmuSurface != null) g.DrawImage(luaEmuSurface.PeekBitmap(), 0, 0, w, h);
g.Clip = new Region(new Rectangle(0, 0, nativeBmp.Width, nativeBmp.Height));
//apply a lua layer
var luaSurface = luaNativeSurfaceSet.GetCurrent();
if (luaSurface != null) g.DrawImageUnscaled(luaSurface.PeekBitmap(), 0, 0);
//although we may want to change this if we want to fade out messages or have some other fancy alpha faded gui stuff
//apply a lua layer
if (luaSurface != null) g.DrawImageUnscaled(luaSurface.PeekBitmap(), 0, 0);
//although we may want to change this if we want to fade out messages or have some other fancy alpha faded gui stuff
//draw the OSD at native resolution
Global.OSD.DrawScreenInfo(g);
Global.OSD.DrawMessages(g);
g.Clip.Dispose();
//draw the OSD at native resolution
if (!Global.Config.SuppressGui)
{
Global.OSD.DrawScreenInfo(g);
Global.OSD.DrawMessages(g);
}
g.Clip.Dispose();
}
//send the native resolution image to the render panel
Global.RenderPanel.Render(nativeBmp);
Global.RenderPanel.Present();
//release the native resolution image
nativeDisplaySurfaceSet.ReleaseSurface(nativeBmp);
}
//send the native resolution image to the render panel
Global.RenderPanel.Render(nativeBmp);
//release the native resolution image
nativeDisplaySurfaceSet.ReleaseSurface(nativeBmp);
}
Thread displayThread;

File diff suppressed because it is too large Load Diff

View File

@ -458,10 +458,14 @@ namespace BizHawk.MultiClient
private void forceGDIPPresentationToolStripMenuItem_Click(object sender, EventArgs e)
{
Global.Config.DisplayGDI ^= true;
Global.DisplayManager.Suspend();
SyncPresentationMode();
}
private void miSuppressGuiLayer_Click(object sender, EventArgs e)
{
Global.Config.SuppressGui ^= true;
}
private void debuggerToolStripMenuItem_Click(object sender, EventArgs e)
{
LoadNESDebugger();
@ -1168,6 +1172,7 @@ namespace BizHawk.MultiClient
startPausedToolStripMenuItem.Checked = Global.Config.StartPaused;
saveWindowPositionToolStripMenuItem.Checked = Global.Config.SaveWindowPosition;
forceGDIPPresentationToolStripMenuItem.Checked = Global.Config.DisplayGDI;
miSuppressGuiLayer.Checked = Global.Config.SuppressGui;
showMenuInFullScreenToolStripMenuItem.Checked = Global.Config.ShowMenuInFullscreen;
runInBackgroundToolStripMenuItem.Checked = Global.Config.RunInBackground;
acceptBackgroundInputToolStripMenuItem.Checked = Global.Config.AcceptBackgroundInput;

View File

@ -3149,5 +3149,7 @@ namespace BizHawk.MultiClient
}
FrameBufferResized();
}
}
}

View File

@ -2,6 +2,7 @@
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Drawing;
using sysdrawing2d=System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Threading;
@ -73,7 +74,7 @@ namespace BizHawk.MultiClient
while (textureWidth < imageWidth) textureWidth <<= 1;
while (textureHeight < imageHeight) textureHeight <<= 1;
// Create a new texture instance.
Texture = new Texture(GraphicsDevice, textureWidth, textureHeight, 1, Usage.Dynamic, Format.X8R8G8B8, Pool.Default);
Texture = new Texture(GraphicsDevice, textureWidth, textureHeight, 1, Usage.Dynamic, Format.A8R8G8B8, Pool.Default);
}
// Copy the image data to the texture.
@ -116,9 +117,13 @@ namespace BizHawk.MultiClient
}
#endif
public interface IRenderer : IDisposable
{
void FastRenderAndPresent(DisplaySurface surface);
void Render(DisplaySurface surface);
void RenderOverlay(DisplaySurface surface);
void Present();
bool Resized { get; set; }
Size NativeSize { get; }
}
@ -129,6 +134,7 @@ namespace BizHawk.MultiClient
public void Dispose() { }
public void Render(DisplaySurface surface)
{
backingControl.ReleaseCallback = RetainedViewportPanelDisposeCallback;
//Color BackgroundColor = Color.FromArgb(video.BackgroundColor);
//int[] data = video.GetVideoBuffer();
@ -140,10 +146,57 @@ namespace BizHawk.MultiClient
//bmp.UnlockBits(bmpdata);
Bitmap bmp = (Bitmap)surface.PeekBitmap().Clone();
lock(this)
tempBuffer = surfaceSet.AllocateSurface(backingControl.Width, backingControl.Height, false);
backingControl.SetBitmap(bmp);
RenderInternal(surface, false);
}
SwappableDisplaySurfaceSet surfaceSet = new SwappableDisplaySurfaceSet();
DisplaySurface tempBuffer;
bool RetainedViewportPanelDisposeCallback(Bitmap bmp)
{
lock (this)
{
DisplaySurface tempSurface = DisplaySurface.DisplaySurfaceWrappingBitmap(bmp);
surfaceSet.ReleaseSurface(tempSurface);
}
return false;
}
void RenderInternal(DisplaySurface surface, bool transparent = false)
{
using (var g = Graphics.FromImage(tempBuffer.PeekBitmap()))
{
g.PixelOffsetMode = sysdrawing2d.PixelOffsetMode.HighSpeed;
g.InterpolationMode = sysdrawing2d.InterpolationMode.NearestNeighbor;
if (transparent) g.CompositingMode = sysdrawing2d.CompositingMode.SourceOver;
else g.CompositingMode = sysdrawing2d.CompositingMode.SourceCopy;
g.CompositingQuality = sysdrawing2d.CompositingQuality.HighSpeed;
if (backingControl.Width == surface.Width && backingControl.Height == surface.Height)
g.DrawImageUnscaled(surface.PeekBitmap(), 0, 0);
else
g.DrawImage(surface.PeekBitmap(), 0, 0, backingControl.Width, backingControl.Height);
}
}
public void FastRenderAndPresent(DisplaySurface surface)
{
backingControl.SetBitmap((Bitmap)surface.PeekBitmap().Clone());
}
public void RenderOverlay(DisplaySurface surface)
{
RenderInternal(surface, true);
}
public void Present()
{
backingControl.SetBitmap(tempBuffer.PeekBitmap());
tempBuffer = null;
}
public SysdrawingRenderPanel(RetainedViewportPanel control)
{
backingControl = control;
@ -242,14 +295,30 @@ namespace BizHawk.MultiClient
Resized = false;
Device.Clear(ClearFlags.Target, BackgroundColor, 1.0f, 0);
Device.Present(Present.DoNotWait);
Device.Present(SlimDX.Direct3D9.Present.DoNotWait);
}
public void FastRenderAndPresent(DisplaySurface surface)
{
Render(surface);
Present();
}
public void RenderOverlay(DisplaySurface surface)
{
RenderWrapper(() => RenderExec(surface, true));
}
public void Render(DisplaySurface surface)
{
RenderWrapper(() => RenderExec(surface, false));
}
public void RenderWrapper(Action todo)
{
try
{
RenderExec(surface);
todo();
}
catch (Direct3D9Exception)
{
@ -264,11 +333,11 @@ namespace BizHawk.MultiClient
// lets try recovery!
DestroyDevice();
backingControl.Invoke(() => CreateDevice());
RenderExec(surface);
todo();
}
}
private void RenderExec(DisplaySurface surface)
private void RenderExec(DisplaySurface surface, bool overlay)
{
if (surface == null)
{
@ -285,7 +354,7 @@ namespace BizHawk.MultiClient
Texture.SetImage(surface, surface.Width, surface.Height);
Device.Clear(ClearFlags.Target, BackgroundColor, 1.0f, 0);
if(!overlay) Device.Clear(ClearFlags.Target, BackgroundColor, 0.0f, 0);
// figure out scaling factor
float widthScale = (float)backingControl.Size.Width / surface.Width;
@ -294,15 +363,22 @@ namespace BizHawk.MultiClient
Device.BeginScene();
Sprite.Begin(SpriteFlags.None);
SpriteFlags flags = SpriteFlags.None;
if (overlay) flags |= SpriteFlags.AlphaBlend;
Sprite.Begin(flags);
Device.SetSamplerState(0, SamplerState.MagFilter, TextureFilter.Point);
Device.SetSamplerState(1, SamplerState.MagFilter, TextureFilter.Point);
Sprite.Transform = Matrix.Scaling(finalScale, finalScale, 0f);
Sprite.Draw(Texture.Texture, new Rectangle(0, 0, surface.Width, surface.Height), new Vector3(surface.Width / 2f, surface.Height / 2f, 0), new Vector3(backingControl.Size.Width / 2f / finalScale, backingControl.Size.Height / 2f / finalScale, 0), Color.White);
if (overlay) Device.SetRenderState(RenderState.AlphaBlendEnable, false);
Sprite.End();
Device.EndScene();
Device.Present(Present.DoNotWait);
}
public void Present()
{
Device.Present(SlimDX.Direct3D9.Present.DoNotWait);
}

View File

@ -17,6 +17,8 @@ namespace BizHawk.Core
EventWaitHandle ewh;
volatile bool killSignal;
public Func<Bitmap,bool> ReleaseCallback;
/// <summary>
/// Turns this panel into multi-threaded mode.
/// This will sort of glitch out other gdi things on the system, but at least its fast...
@ -89,7 +91,13 @@ namespace BizHawk.Core
lock (this)
{
while (DisposeQueue.Count > 0)
DisposeQueue.Dequeue().Dispose();
{
var bmp = DisposeQueue.Dequeue();
bool dispose = true;
if(ReleaseCallback != null)
dispose = ReleaseCallback(bmp);
if(dispose) bmp.Dispose();
}
}
}