2012-04-16 08:18:41 +00:00
|
|
|
|
using System;
|
2013-10-27 22:13:08 +00:00
|
|
|
|
using System.Linq;
|
2012-04-16 08:18:41 +00:00
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Windows.Forms;
|
|
|
|
|
using System.Drawing;
|
|
|
|
|
using System.Drawing.Drawing2D;
|
|
|
|
|
using System.Drawing.Imaging;
|
|
|
|
|
|
2013-11-04 00:36:15 +00:00
|
|
|
|
using BizHawk.Common;
|
2013-11-04 01:39:19 +00:00
|
|
|
|
using BizHawk.Emulation.Common;
|
2013-10-25 00:57:23 +00:00
|
|
|
|
using BizHawk.Client.Common;
|
|
|
|
|
|
2014-01-27 00:02:21 +00:00
|
|
|
|
using BizHawk.Bizware.BizwareGL;
|
|
|
|
|
|
2013-11-03 03:54:37 +00:00
|
|
|
|
namespace BizHawk.Client.EmuHawk
|
2012-04-16 08:18:41 +00:00
|
|
|
|
{
|
2014-01-28 04:39:27 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// A DisplayManager is destined forevermore to drive the PresentationPanel it gets initialized with.
|
|
|
|
|
/// Its job is to receive OSD and emulator outputs, and produce one single buffer (BitampBuffer? Texture2d?) for display by the PresentationPanel.
|
|
|
|
|
/// Details TBD
|
|
|
|
|
/// </summary>
|
2012-04-16 08:18:41 +00:00
|
|
|
|
public class DisplayManager : IDisposable
|
|
|
|
|
{
|
2014-01-28 04:39:27 +00:00
|
|
|
|
|
|
|
|
|
public DisplayManager(PresentationPanel presentationPanel)
|
2012-04-16 08:18:41 +00:00
|
|
|
|
{
|
2014-01-28 04:39:27 +00:00
|
|
|
|
GL = GlobalWin.GL;
|
|
|
|
|
this.presentationPanel = presentationPanel;
|
|
|
|
|
GraphicsControl = this.presentationPanel.GraphicsControl;
|
2012-04-16 08:18:41 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
//it's sort of important for these to be initialized to something nonzero
|
|
|
|
|
currEmuWidth = currEmuHeight = 1;
|
2012-09-02 14:54:30 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
Renderer = new GuiRenderer(GL);
|
|
|
|
|
VideoTextureFrugalizer = new TextureFrugalizer(GL);
|
|
|
|
|
LuaEmuTextureFrugalizer = new TextureFrugalizer(GL);
|
2014-02-03 05:27:59 +00:00
|
|
|
|
Video2xFrugalizer = new RenderTargetFrugalizer(GL);
|
2012-09-02 14:54:30 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
using (var xml = typeof(Program).Assembly.GetManifestResourceStream("BizHawk.Client.EmuHawk.Resources.courier16px.fnt"))
|
|
|
|
|
using (var tex = typeof(Program).Assembly.GetManifestResourceStream("BizHawk.Client.EmuHawk.Resources.courier16px_0.png"))
|
|
|
|
|
TheOneFont = new StringRenderer(GL, xml, tex);
|
2014-02-03 05:27:59 +00:00
|
|
|
|
|
|
|
|
|
using (var stream = typeof(Program).Assembly.GetManifestResourceStream("BizHawk.Client.EmuHawk.DisplayManager.Filters.hq2x.glsl"))
|
|
|
|
|
{
|
|
|
|
|
var str = new System.IO.StreamReader(stream).ReadToEnd();
|
|
|
|
|
RetroShader_Hq2x = new Bizware.BizwareGL.Drivers.OpenTK.RetroShader(GL, str);
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-03 07:01:31 +00:00
|
|
|
|
using (var stream = typeof(Program).Assembly.GetManifestResourceStream("BizHawk.Client.EmuHawk.DisplayManager.Filters.BizScanlines.glsl"))
|
|
|
|
|
{
|
|
|
|
|
var str = new System.IO.StreamReader(stream).ReadToEnd();
|
|
|
|
|
RetroShader_BizScanlines = new Bizware.BizwareGL.Drivers.OpenTK.RetroShader(GL, str);
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-02 14:54:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-02-03 07:01:31 +00:00
|
|
|
|
Bizware.BizwareGL.Drivers.OpenTK.RetroShader RetroShader_Hq2x, RetroShader_BizScanlines;
|
2014-02-03 05:27:59 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
public bool Disposed { get; private set; }
|
2012-09-21 18:01:24 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
public void Dispose()
|
2012-04-16 08:18:41 +00:00
|
|
|
|
{
|
2014-01-28 04:39:27 +00:00
|
|
|
|
if (Disposed) return;
|
|
|
|
|
Disposed = true;
|
|
|
|
|
VideoTextureFrugalizer.Dispose();
|
|
|
|
|
LuaEmuTextureFrugalizer.Dispose();
|
|
|
|
|
}
|
2012-06-10 23:27:30 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
//rendering resources:
|
|
|
|
|
IGL GL;
|
|
|
|
|
StringRenderer TheOneFont;
|
|
|
|
|
GuiRenderer Renderer;
|
2012-07-15 07:38:36 +00:00
|
|
|
|
|
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
//layer resources
|
|
|
|
|
DisplaySurface luaEmuSurface = null;
|
|
|
|
|
PresentationPanel presentationPanel; //well, its the final layer's target, at least
|
|
|
|
|
GraphicsControl GraphicsControl; //well, its the final layer's target, at least
|
2012-06-10 23:27:30 +00:00
|
|
|
|
|
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
public bool NeedsToPaint { get; set; }
|
2012-06-10 23:27:30 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
public void PreFrameUpdateLuaSource()
|
|
|
|
|
{
|
|
|
|
|
luaEmuSurface = luaEmuSurfaceSet.GetCurrent();
|
|
|
|
|
}
|
2012-06-10 23:27:30 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// these variables will track the dimensions of the last frame's (or the next frame? this is confusing) emulator native output size
|
|
|
|
|
/// </summary>
|
|
|
|
|
int currEmuWidth, currEmuHeight;
|
2012-07-15 09:13:46 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
TextureFrugalizer VideoTextureFrugalizer, LuaEmuTextureFrugalizer;
|
2014-02-03 05:27:59 +00:00
|
|
|
|
RenderTargetFrugalizer Video2xFrugalizer;
|
2012-06-10 23:27:30 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// This will receive an emulated output frame from an IVideoProvider and run it through the complete frame processing pipeline
|
|
|
|
|
/// Then it will stuff it into the bound PresentationPanel
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void UpdateSource(IVideoProvider videoProvider)
|
|
|
|
|
{
|
|
|
|
|
//wrap the videoprovider data in a BitmapBuffer (no point to refactoring that many IVidepProviders)
|
|
|
|
|
BitmapBuffer bb = new BitmapBuffer(videoProvider.BufferWidth, videoProvider.BufferHeight, videoProvider.GetVideoBuffer());
|
2012-06-10 23:27:30 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
//record the size of what we received, since lua and stuff is gonna want to draw onto it
|
|
|
|
|
currEmuWidth = bb.Width;
|
|
|
|
|
currEmuHeight = bb.Height;
|
2012-06-10 20:48:04 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
//now, acquire the data sent from the videoProvider into a texture
|
|
|
|
|
var videoTexture = VideoTextureFrugalizer.Get(bb);
|
2013-08-23 02:40:14 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
//acquire the lua emu surface as a texture
|
|
|
|
|
Texture2d luaEmuTexture = null;
|
|
|
|
|
if (luaEmuSurface != null)
|
|
|
|
|
luaEmuTexture = LuaEmuTextureFrugalizer.Get(luaEmuSurface);
|
2012-04-16 08:18:41 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
|
2014-02-03 07:01:31 +00:00
|
|
|
|
//TargetScanlineFilterIntensity
|
2014-02-03 05:27:59 +00:00
|
|
|
|
//apply filter chain (currently, over-simplified)
|
|
|
|
|
Texture2d currentTexture = videoTexture;
|
2014-02-03 07:43:47 +00:00
|
|
|
|
if (Global.Config.TargetDisplayFilter == 1 && RetroShader_Hq2x.Pipeline.Available)
|
2014-02-03 05:27:59 +00:00
|
|
|
|
{
|
|
|
|
|
var rt = Video2xFrugalizer.Get(videoTexture.IntWidth*2,videoTexture.IntHeight*2);
|
|
|
|
|
rt.Bind();
|
|
|
|
|
Size outSize = new Size(videoTexture.IntWidth * 2, videoTexture.IntHeight * 2);
|
|
|
|
|
RetroShader_Hq2x.Run(videoTexture, videoTexture.Size, outSize, true);
|
|
|
|
|
currentTexture = rt.Texture2d;
|
|
|
|
|
}
|
2014-02-03 07:43:47 +00:00
|
|
|
|
if (Global.Config.TargetDisplayFilter == 2 && RetroShader_BizScanlines.Pipeline.Available)
|
2014-02-03 07:01:31 +00:00
|
|
|
|
{
|
|
|
|
|
var rt = Video2xFrugalizer.Get(videoTexture.IntWidth*2,videoTexture.IntHeight*2);
|
|
|
|
|
rt.Bind();
|
|
|
|
|
Size outSize = new Size(videoTexture.IntWidth * 2, videoTexture.IntHeight * 2);
|
|
|
|
|
RetroShader_BizScanlines.Bind();
|
|
|
|
|
RetroShader_BizScanlines.Pipeline["uIntensity"].Set(1.0f - Global.Config.TargetScanlineFilterIntensity / 256.0f);
|
|
|
|
|
RetroShader_BizScanlines.Run(videoTexture, videoTexture.Size, outSize, true);
|
|
|
|
|
currentTexture = rt.Texture2d;
|
|
|
|
|
}
|
2014-02-03 05:27:59 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
//begin drawing to the PresentationPanel:
|
|
|
|
|
GraphicsControl.Begin();
|
2012-04-16 08:18:41 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
//1. clear it with the background color that the emulator specified
|
|
|
|
|
GL.SetClearColor(Color.FromArgb(videoProvider.BackgroundColor));
|
|
|
|
|
GL.Clear(OpenTK.Graphics.OpenGL.ClearBufferMask.ColorBufferBit);
|
2014-02-03 05:27:59 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
|
|
|
|
|
//2. begin 2d rendering
|
|
|
|
|
Renderer.Begin(GraphicsControl.Width, GraphicsControl.Height);
|
|
|
|
|
|
|
|
|
|
//3. figure out how to draw the emulator output content
|
2014-02-03 05:27:59 +00:00
|
|
|
|
var LL = new LetterboxingLogic(GraphicsControl.Width, GraphicsControl.Height, currentTexture.IntWidth, currentTexture.IntHeight);
|
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
|
|
|
|
|
//4. draw the emulator content
|
|
|
|
|
Renderer.SetBlendState(GL.BlendNone);
|
|
|
|
|
Renderer.Modelview.Push();
|
|
|
|
|
Renderer.Modelview.Translate(LL.dx, LL.dy);
|
|
|
|
|
Renderer.Modelview.Scale(LL.finalScale);
|
|
|
|
|
if (Global.Config.DispBlurry)
|
|
|
|
|
videoTexture.SetFilterLinear();
|
|
|
|
|
else
|
|
|
|
|
videoTexture.SetFilterNearest();
|
2014-02-03 05:27:59 +00:00
|
|
|
|
Renderer.Draw(currentTexture);
|
2014-01-28 04:39:27 +00:00
|
|
|
|
//4.b draw the "lua emu surface" which is designed for art matching up exactly with the emulator output
|
|
|
|
|
Renderer.SetBlendState(GL.BlendNormal);
|
|
|
|
|
if(luaEmuTexture != null) Renderer.Draw(luaEmuTexture);
|
|
|
|
|
Renderer.Modelview.Pop();
|
|
|
|
|
|
|
|
|
|
//(should we draw native layer lua here? thats broken right now)
|
|
|
|
|
|
|
|
|
|
//5. draw the native layer OSD
|
|
|
|
|
MyBlitter myBlitter = new MyBlitter(this);
|
|
|
|
|
myBlitter.ClipBounds = new Rectangle(0, 0, GraphicsControl.Width, GraphicsControl.Height);
|
|
|
|
|
GlobalWin.OSD.Begin(myBlitter);
|
|
|
|
|
GlobalWin.OSD.DrawScreenInfo(myBlitter);
|
|
|
|
|
GlobalWin.OSD.DrawMessages(myBlitter);
|
|
|
|
|
|
|
|
|
|
//6. finished drawing
|
|
|
|
|
Renderer.End();
|
|
|
|
|
|
|
|
|
|
//7. apply the vsync setting (should probably try to avoid repeating this)
|
|
|
|
|
bool vsync = Global.Config.VSyncThrottle || Global.Config.VSync;
|
|
|
|
|
presentationPanel.GraphicsControl.SetVsync(vsync);
|
|
|
|
|
|
|
|
|
|
//7. present and conclude drawing
|
|
|
|
|
presentationPanel.GraphicsControl.SwapBuffers();
|
|
|
|
|
presentationPanel.GraphicsControl.End();
|
|
|
|
|
|
|
|
|
|
//cleanup:
|
|
|
|
|
bb.Dispose();
|
|
|
|
|
NeedsToPaint = false; //??
|
2012-04-16 08:18:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SwappableDisplaySurfaceSet luaNativeSurfaceSet = new SwappableDisplaySurfaceSet();
|
|
|
|
|
public void SetLuaSurfaceNativePreOSD(DisplaySurface surface) { luaNativeSurfaceSet.SetPending(surface); }
|
|
|
|
|
public DisplaySurface GetLuaSurfaceNative()
|
|
|
|
|
{
|
2014-01-28 04:39:27 +00:00
|
|
|
|
int currNativeWidth = presentationPanel.NativeSize.Width;
|
|
|
|
|
int currNativeHeight = presentationPanel.NativeSize.Height;
|
2012-04-16 08:18:41 +00:00
|
|
|
|
return luaNativeSurfaceSet.AllocateSurface(currNativeWidth, currNativeHeight);
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-06 07:09:04 +00:00
|
|
|
|
SwappableDisplaySurfaceSet luaEmuSurfaceSet = new SwappableDisplaySurfaceSet();
|
|
|
|
|
public void SetLuaSurfaceEmu(DisplaySurface surface) { luaEmuSurfaceSet.SetPending(surface); }
|
|
|
|
|
public DisplaySurface GetLuaEmuSurfaceEmu()
|
|
|
|
|
{
|
2014-01-28 04:39:27 +00:00
|
|
|
|
return luaEmuSurfaceSet.AllocateSurface(currEmuWidth, currEmuHeight);
|
2012-05-06 07:09:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
//helper classes:
|
2012-04-16 08:18:41 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
class MyBlitter : IBlitter
|
2012-04-16 08:18:41 +00:00
|
|
|
|
{
|
2014-01-28 04:39:27 +00:00
|
|
|
|
DisplayManager Owner;
|
|
|
|
|
public MyBlitter(DisplayManager dispManager)
|
|
|
|
|
{
|
|
|
|
|
Owner = dispManager;
|
|
|
|
|
}
|
2012-04-16 08:18:41 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
class FontWrapper : IBlitterFont
|
|
|
|
|
{
|
|
|
|
|
public FontWrapper(StringRenderer font)
|
|
|
|
|
{
|
|
|
|
|
this.font = font;
|
|
|
|
|
}
|
2012-04-16 08:18:41 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
public readonly StringRenderer font;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
IBlitterFont IBlitter.GetFontType(string fontType) { return new FontWrapper(Owner.TheOneFont); }
|
|
|
|
|
void IBlitter.DrawString(string s, IBlitterFont font, Color color, float x, float y)
|
|
|
|
|
{
|
|
|
|
|
var stringRenderer = ((FontWrapper)font).font;
|
|
|
|
|
Owner.Renderer.SetModulateColor(color);
|
|
|
|
|
stringRenderer.RenderString(Owner.Renderer, x, y, s);
|
|
|
|
|
Owner.Renderer.SetModulateColorWhite();
|
|
|
|
|
}
|
|
|
|
|
SizeF IBlitter.MeasureString(string s, IBlitterFont font)
|
|
|
|
|
{
|
|
|
|
|
var stringRenderer = ((FontWrapper)font).font;
|
|
|
|
|
return stringRenderer.Measure(s);
|
|
|
|
|
}
|
|
|
|
|
public Rectangle ClipBounds { get; set; }
|
2012-06-10 23:27:30 +00:00
|
|
|
|
}
|
2012-06-10 20:48:04 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// applies letterboxing logic to figure out how to fit the source dimensions into the target dimensions.
|
|
|
|
|
/// In the future this could also apply rules like integer-only scaling, etc.
|
|
|
|
|
/// TODO - make this work with a output rect instead of float and dx/dy
|
|
|
|
|
/// </summary>
|
|
|
|
|
class LetterboxingLogic
|
2012-07-15 08:50:24 +00:00
|
|
|
|
{
|
2014-01-28 04:39:27 +00:00
|
|
|
|
public LetterboxingLogic(int targetWidth, int targetHeight, int sourceWidth, int sourceHeight)
|
2012-07-15 08:50:24 +00:00
|
|
|
|
{
|
2014-01-28 04:39:27 +00:00
|
|
|
|
float vw = (float)targetWidth;
|
|
|
|
|
float vh = (float)targetHeight;
|
|
|
|
|
float widthScale = vw / sourceWidth;
|
|
|
|
|
float heightScale = vh / sourceHeight;
|
|
|
|
|
finalScale = Math.Min(widthScale, heightScale);
|
|
|
|
|
dx = (int)((vw - finalScale * sourceWidth) / 2);
|
|
|
|
|
dy = (int)((vh - finalScale * sourceHeight) / 2);
|
2012-07-15 08:50:24 +00:00
|
|
|
|
}
|
2012-06-10 20:48:04 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// scale to be applied to both x and y
|
|
|
|
|
/// </summary>
|
|
|
|
|
public float finalScale;
|
2012-04-16 08:18:41 +00:00
|
|
|
|
|
2014-01-28 04:39:27 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// offset
|
|
|
|
|
/// </summary>
|
|
|
|
|
public float dx, dy;
|
|
|
|
|
}
|
2012-04-16 08:18:41 +00:00
|
|
|
|
}
|
2014-01-28 04:39:27 +00:00
|
|
|
|
|
2012-04-16 08:18:41 +00:00
|
|
|
|
}
|