cleanup display manager code

This commit is contained in:
adelikat 2019-12-31 11:14:10 -06:00
parent 7a3606d684
commit 0f33e2b1e7
13 changed files with 374 additions and 307 deletions

View File

@ -16,10 +16,8 @@
//BizHawk says: adapted from https://github.com/inexorabletash/PcxFileType/blob/master/Quantize
using System;
using System.Drawing;
using System.Drawing.Imaging;
using BizHawk.Common.NumberExtensions;
namespace BizHawk.Client.EmuHawk
{

View File

@ -159,7 +159,7 @@ namespace BizHawk.Client.EmuHawk
/// these variables will track the dimensions of the last frame's (or the next frame? this is confusing) emulator native output size
/// THIS IS OLD JUNK. I should get rid of it, I think. complex results from the last filter ingestion should be saved instead.
/// </summary>
int currEmuWidth, currEmuHeight;
private int currEmuWidth, currEmuHeight;
/// <summary>
/// additional pixels added at the unscaled level for the use of lua drawing. essentially increases the input video provider dimensions
@ -906,7 +906,6 @@ namespace BizHawk.Client.EmuHawk
}
case FilterProgram.ProgramStepType.FinalTarget:
{
var size = (Size)step.Args;
inFinalTarget = true;
rtCurr = null;
_currentFilterProgram.CurrRenderTarget = null;
@ -955,9 +954,9 @@ namespace BizHawk.Client.EmuHawk
private bool? LastVsyncSetting;
private GraphicsControl LastVsyncSettingGraphicsControl;
private Dictionary<string, DisplaySurface> MapNameToLuaSurface = new Dictionary<string,DisplaySurface>();
private Dictionary<DisplaySurface, string> MapLuaSurfaceToName = new Dictionary<DisplaySurface, string>();
private Dictionary<string, SwappableDisplaySurfaceSet> LuaSurfaceSets = new Dictionary<string, SwappableDisplaySurfaceSet>();
private readonly Dictionary<string, DisplaySurface> MapNameToLuaSurface = new Dictionary<string,DisplaySurface>();
private readonly Dictionary<DisplaySurface, string> MapLuaSurfaceToName = new Dictionary<DisplaySurface, string>();
private readonly Dictionary<string, SwappableDisplaySurfaceSet> LuaSurfaceSets = new Dictionary<string, SwappableDisplaySurfaceSet>();
/// <summary>
/// Peeks a locked lua surface, or returns null if it isn't locked
@ -993,13 +992,17 @@ namespace BizHawk.Client.EmuHawk
currNativeHeight += ClientExtraPadding.Vertical;
int width,height;
if(name == "emu") {
if (name == "emu")
{
width = currEmuWidth;
height = currEmuHeight;
width += GameExtraPadding.Horizontal;
height += GameExtraPadding.Vertical;
}
else if(name == "native") { width = currNativeWidth; height = currNativeHeight; }
else if (name == "native")
{
width = currNativeWidth; height = currNativeHeight;
}
else throw new InvalidOperationException($"Unknown lua surface name: {name}");
DisplaySurface ret = sdss.AllocateSurface(width, height, clear);

View File

@ -12,9 +12,9 @@ namespace BizHawk.Client.EmuHawk
/// </summary>
public unsafe class DisplaySurface : IDisposable
{
private Bitmap bmp;
private BitmapData bmpdata;
private int[] pixels;
private Bitmap _bmp;
private BitmapData _bmpData;
private readonly int[] _pixels;
public unsafe void Clear()
{
@ -25,7 +25,7 @@ namespace BizHawk.Client.EmuHawk
public Bitmap PeekBitmap()
{
ToBitmap();
return bmp;
return _bmp;
}
/// <summary>
@ -34,7 +34,7 @@ namespace BizHawk.Client.EmuHawk
public Graphics GetGraphics()
{
ToBitmap();
return Graphics.FromImage(bmp);
return Graphics.FromImage(_bmp);
}
public unsafe void ToBitmap(bool copy=true)
@ -42,79 +42,101 @@ namespace BizHawk.Client.EmuHawk
if (_isBitmap) return;
_isBitmap = true;
if (bmp == null)
if (_bmp == null)
{
bmp = new Bitmap(Width, Height, PixelFormat.Format32bppArgb);
_bmp = new Bitmap(Width, Height, PixelFormat.Format32bppArgb);
}
if (copy)
{
bmpdata = bmp.LockBits(new Rectangle(0, 0, Width, Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
_bmpData = _bmp.LockBits(new Rectangle(0, 0, Width, Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
int w = Width;
int h = Height;
int stride = bmpdata.Stride / 4;
int* bmpbuf = (int*)bmpdata.Scan0.ToPointer();
int stride = _bmpData.Stride / 4;
int* bmpBuf = (int*)_bmpData.Scan0.ToPointer();
for (int y = 0, i = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
bmpbuf[y * stride + x] = pixels[i++];
{
bmpBuf[y * stride + x] = _pixels[i++];
}
}
bmp.UnlockBits(bmpdata);
_bmp.UnlockBits(_bmpData);
}
}
bool _isBitmap;
private bool _isBitmap;
public unsafe void FromBitmap(bool copy = true)
{
if (!_isBitmap) return;
if (!_isBitmap)
{
return;
}
_isBitmap = false;
if (copy)
{
bmpdata = bmp.LockBits(new Rectangle(0, 0, Width, Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
_bmpData = _bmp.LockBits(new Rectangle(0, 0, Width, Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
int w = Width;
int h = Height;
int stride = bmpdata.Stride / 4;
int* bmpbuf = (int*)bmpdata.Scan0.ToPointer();
int stride = _bmpData.Stride / 4;
int* bmpBuf = (int*)_bmpData.Scan0.ToPointer();
for (int y = 0, i = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
pixels[i++] = bmpbuf[y * stride + x];
{
_pixels[i++] = bmpBuf[y * stride + x];
}
}
bmp.UnlockBits(bmpdata);
_bmp.UnlockBits(_bmpData);
}
}
public DisplaySurface(int width, int height)
{
// can't create a bitmap with zero dimensions, so for now, just bump it up to one
if (width == 0) width = 1;
if (height == 0) height = 1;
if (width == 0)
{
width = 1;
}
if (height == 0)
{
height = 1;
}
Width = width;
Height = height;
pixels = new int[width * height];
_pixels = new int[width * height];
LockPixels();
}
public int* PixelPtr => (int*)ptr;
public int* PixelPtr => (int*)_ptr;
public int Stride => Width * 4;
void* ptr;
GCHandle handle;
void LockPixels()
private void* _ptr;
private GCHandle _handle;
private void LockPixels()
{
UnlockPixels();
handle = GCHandle.Alloc(pixels, GCHandleType.Pinned);
ptr = handle.AddrOfPinnedObject().ToPointer();
_handle = GCHandle.Alloc(_pixels, GCHandleType.Pinned);
_ptr = _handle.AddrOfPinnedObject().ToPointer();
}
private void UnlockPixels()
{
if(handle.IsAllocated) handle.Free();
if (_handle.IsAllocated)
{
_handle.Free();
}
}
public int Width { get; }
@ -122,8 +144,8 @@ namespace BizHawk.Client.EmuHawk
public void Dispose()
{
bmp?.Dispose();
bmp = null;
_bmp?.Dispose();
_bmp = null;
UnlockPixels();
}
}

View File

@ -182,7 +182,7 @@ namespace BizHawk.Client.EmuHawk.FilterManager
iosi.SurfaceFormat = currState.SurfaceFormat;
f.SetInputFormat(channel, currState);
if (f.IsNOP)
if (f.IsNop)
{
continue;
}

View File

@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using BizHawk.Client.EmuHawk.FilterManager;
@ -16,20 +15,18 @@ using OpenTK;
// 4. In SetInputFormat(), use DeclareOutput to set the output based on your desires, or the provided input format.
// 5. In Run(), the render target is already set. If using a texture, use InputTexture
// 6. In Run(), if supplying an output texture, use YieldOutput
namespace BizHawk.Client.EmuHawk.Filters
{
public class BaseFilter
{
// initialization stuff
public void BeginInitialization(FilterProgram program) { IOSurfaceInfos.Clear(); FilterProgram = program; }
public void BeginInitialization(FilterProgram program) { _ioSurfaceInfos.Clear(); FilterProgram = program; }
public virtual void Initialize() { }
public virtual Size PresizeInput(string channel, Size size) { return size; }
public virtual Size PresizeOutput(string channel, Size size) { return size; }
public virtual Size PresizeInput(string channel, Size size) => size;
public virtual Size PresizeOutput(string channel, Size size) => size;
public virtual void SetInputFormat(string channel, SurfaceState state) { } //TODO - why a different param order than DeclareOutput?
public Dictionary<string, object> Parameters = new Dictionary<string, object>();
public bool IsNOP { get => _IsNop; protected set => _IsNop = value; }
private Boolean _IsNop = false;
public bool IsNop { get; set; }
// runtime signals
public virtual Vector2 UntransformPoint(string channel, Vector2 point)
@ -63,24 +60,34 @@ namespace BizHawk.Client.EmuHawk.Filters
InputTexture = tex;
}
public virtual void Run() { }
public Texture2d GetOutput() { return OutputTexture; }
public Texture2d GetOutput() => _outputTexture;
// filter actions
protected void YieldOutput(Texture2d tex)
{
OutputTexture = tex;
_outputTexture = tex;
}
protected FilterProgram FilterProgram;
protected Texture2d InputTexture;
private Texture2d OutputTexture;
private Texture2d _outputTexture;
// setup utilities
protected IOSurfaceInfo DeclareInput(SurfaceDisposition disposition = SurfaceDisposition.Unspecified, string channel = "default") { return DeclareIO(SurfaceDirection.Input, channel, disposition); }
protected IOSurfaceInfo DeclareOutput(SurfaceDisposition disposition = SurfaceDisposition.Unspecified, string channel = "default") { return DeclareIO(SurfaceDirection.Output, channel, disposition); }
//TODO - why a different param order than DeclareOutput?
protected IOSurfaceInfo DeclareInput(SurfaceDisposition disposition = SurfaceDisposition.Unspecified, string channel = "default")
{
return DeclareIO(SurfaceDirection.Input, channel, disposition);
}
protected RenderTarget GetTempTarget(int width, int height) { return FilterProgram.GetTempTarget(width, height); }
protected IOSurfaceInfo DeclareOutput(SurfaceDisposition disposition = SurfaceDisposition.Unspecified, string channel = "default")
{
return DeclareIO(SurfaceDirection.Output, channel, disposition);
}
// TODO - why a different param order than DeclareOutput?
protected RenderTarget GetTempTarget(int width, int height)
{
return FilterProgram.GetTempTarget(width, height);
}
protected IOSurfaceInfo DeclareOutput(SurfaceState state, string channel = "default")
{
@ -89,36 +96,51 @@ namespace BizHawk.Client.EmuHawk.Filters
return iosi;
}
public IOSurfaceInfo FindInput(string channel = "default") { return FindIOSurfaceInfo(channel, SurfaceDirection.Input); }
public IOSurfaceInfo FindOutput(string channel = "default") { return FindIOSurfaceInfo(channel, SurfaceDirection.Output); }
public IOSurfaceInfo FindInput(string channel = "default")
{
return FindIOSurfaceInfo(channel, SurfaceDirection.Input);
}
public IOSurfaceInfo FindOutput(string channel = "default")
{
return FindIOSurfaceInfo(channel, SurfaceDirection.Output);
}
private IOSurfaceInfo DeclareIO(SurfaceDirection direction, string channel, SurfaceDisposition disposition)
{
var iosi = new IOSurfaceInfo();
iosi.SurfaceDirection = direction;
iosi.Channel = channel;
iosi.SurfaceDisposition = disposition;
IOSurfaceInfos.Add(iosi);
var iosi = new IOSurfaceInfo
{
SurfaceDirection = direction,
Channel = channel,
SurfaceDisposition = disposition
};
_ioSurfaceInfos.Add(iosi);
return iosi;
}
private readonly List<IOSurfaceInfo> IOSurfaceInfos = new List<IOSurfaceInfo>();
private readonly List<IOSurfaceInfo> _ioSurfaceInfos = new List<IOSurfaceInfo>();
private IOSurfaceInfo FindIOSurfaceInfo(string channel, SurfaceDirection direction)
{
foreach (var iosi in IOSurfaceInfos)
foreach (var iosi in _ioSurfaceInfos)
{
if (iosi.Channel == channel && iosi.SurfaceDirection == direction)
{
return iosi;
}
}
return null;
}
public class IOSurfaceInfo
{
public SurfaceFormat SurfaceFormat;
public SurfaceDirection SurfaceDirection;
public SurfaceDisposition SurfaceDisposition;
public string Channel;
public SurfaceFormat SurfaceFormat { get; set; }
public SurfaceDirection SurfaceDirection { get; set; }
public SurfaceDisposition SurfaceDisposition { get; set; }
public string Channel { get; set; }
}
public enum SurfaceDirection

View File

@ -11,7 +11,7 @@ namespace BizHawk.Client.EmuHawk.Filters
/// 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.
/// </summary>
class LetterboxingLogic
public class LetterboxingLogic
{
/// <summary>
/// the location within the destination region of the output content (scaled and translated)
@ -42,9 +42,11 @@ namespace BizHawk.Client.EmuHawk.Filters
sourceWidth = (int)virtualWidth;
sourceHeight = (int)virtualHeight;
//this doesnt make sense
// this doesn't make sense
if (!maintainAspect)
{
maintainInteger = false;
}
float widthScale = (float)targetWidth / sourceWidth;
float heightScale = (float)targetHeight / sourceHeight;
@ -67,44 +69,46 @@ namespace BizHawk.Client.EmuHawk.Filters
Vector2 VS = new Vector2(virtualWidth, virtualHeight);
Vector2 BS = new Vector2(textureWidth, textureHeight);
Vector2 AR = Vector2.Divide(VS, BS);
float target_par = (AR.X / AR.Y);
float targetPar = (AR.X / AR.Y);
Vector2 PS = new Vector2(1, 1); //this would malfunction for AR <= 0.5 or AR >= 2.0
for(;;)
{
// TODO - would be good not to run this per frame....
Vector2[] trials = new[] {
Vector2[] trials =
{
PS + new Vector2(1, 0),
PS + new Vector2(0, 1),
PS + new Vector2(1, 1)
};
bool[] trials_limited = new bool[3] { false,false,false};
bool[] trialsLimited = new bool[3] { false,false,false};
int bestIndex = -1;
float bestValue = 1000.0f;
for (int t = 0; t < trials.Length; t++)
{
Vector2 vTrial = trials[t];
trials_limited[t] = false;
trialsLimited[t] = false;
//check whether this is going to exceed our allotted area
int test_vw = (int)(vTrial.X * textureWidth);
int test_vh = (int)(vTrial.Y * textureHeight);
if (test_vw > targetWidth) trials_limited[t] = true;
if (test_vh > targetHeight) trials_limited[t] = true;
int testVw = (int)(vTrial.X * textureWidth);
int testVh = (int)(vTrial.Y * textureHeight);
if (testVw > targetWidth) trialsLimited[t] = true;
if (testVh > targetHeight) trialsLimited[t] = true;
// I.
float test_ar = vTrial.X / vTrial.Y;
float testAr = vTrial.X / vTrial.Y;
// II.
// Vector2 calc = Vector2.Multiply(trials[t], VS);
// float test_ar = calc.X / calc.Y;
// not clear which approach is superior
float deviation_linear = Math.Abs(test_ar - target_par);
float deviation_geom = test_ar / target_par;
if (deviation_geom < 1) deviation_geom = 1.0f / deviation_geom;
float deviationLinear = Math.Abs(testAr - targetPar);
float deviationGeom = testAr / targetPar;
if (deviationGeom < 1) deviationGeom = 1.0f / deviationGeom;
float value = deviation_linear;
float value = deviationLinear;
if (value < bestValue)
{
bestIndex = t;
@ -117,7 +121,7 @@ namespace BizHawk.Client.EmuHawk.Filters
break;
// if the winner ran off the edge, bail out
if (trials_limited[bestIndex])
if (trialsLimited[bestIndex])
break;
PS = trials[bestIndex];
@ -137,13 +141,18 @@ namespace BizHawk.Client.EmuHawk.Filters
vh = (int)(heightScale * sourceHeight);
}
//theres only one sensible way to letterbox in case we're shrinking a dimension: "pan & scan" to the center
// there's only one sensible way to letterbox in case we're shrinking a dimension: "pan & scan" to the center
// this is unlikely to be what the user wants except in the one case of maybe shrinking off some overscan area
// instead, since we're more about biz than gaming, lets shrink the view to fit in the small dimension
if (targetWidth < vw)
{
vw = targetWidth;
}
if (targetHeight < vh)
{
vh = targetHeight;
}
//determine letterboxing parameters
vx = (targetWidth - vw) / 2;
@ -151,13 +160,12 @@ namespace BizHawk.Client.EmuHawk.Filters
// zero 09-oct-2014 - changed this for TransformPoint. scenario: basic 1x (but system-specified AR) NES window.
// vw would be 293 but WidthScale would be 1.0. I think it should be something different.
//FinalPresentation doesnt use the LL.WidthScale, so this is unlikely to be breaking anything old that depends on it
// FinalPresentation doesn't use the LL.WidthScale, so this is unlikely to be breaking anything old that depends on it
// WidthScale = widthScale;
// HeightScale = heightScale;
WidthScale = (float)vw / oldSourceWidth;
HeightScale = (float)vh / oldSourceHeight;
}
}
public class FinalPresentation : BaseFilter
@ -168,23 +176,22 @@ namespace BizHawk.Client.EmuHawk.Filters
}
public eFilterOption FilterOption = eFilterOption.None;
public RetroShaderChain BicubicFilter;
public FinalPresentation(Size size)
{
this.OutputSize = size;
}
Size OutputSize, InputSize;
private Size OutputSize, InputSize;
public Size TextureSize, VirtualTextureSize;
public int BackgroundColor;
public bool AutoPrescale;
public IGuiRenderer GuiRenderer;
public bool Flip;
public IGL GL;
bool nop;
LetterboxingLogic LL;
Size ContentSize;
private bool nop;
private LetterboxingLogic LL;
private Size ContentSize;
public bool Config_FixAspectRatio, Config_FixScaleInteger, Config_PadOnly;
@ -278,10 +285,10 @@ namespace BizHawk.Client.EmuHawk.Filters
ContentSize = new Size(LL.vw,LL.vh);
if (InputSize == OutputSize) //any reason we need to check vx and vy?
IsNOP = true;
IsNop = true;
}
public Size GetContentSize() { return ContentSize; }
public Size GetContentSize() => ContentSize;
public override Vector2 UntransformPoint(string channel, Vector2 point)
{
@ -350,10 +357,10 @@ namespace BizHawk.Client.EmuHawk.Filters
public override void SetInputFormat(string channel, SurfaceState state)
{
var OutputSize = state.SurfaceFormat.Size;
OutputSize.Width *= Scale;
OutputSize.Height *= Scale;
var ss = new SurfaceState(new SurfaceFormat(OutputSize), SurfaceDisposition.RenderTarget);
var outputSize = state.SurfaceFormat.Size;
outputSize.Width *= Scale;
outputSize.Height *= Scale;
var ss = new SurfaceState(new SurfaceFormat(outputSize), SurfaceDisposition.RenderTarget);
DeclareOutput(ss, channel);
}
@ -370,8 +377,8 @@ namespace BizHawk.Client.EmuHawk.Filters
public class AutoPrescaleFilter : BaseFilter
{
Size OutputSize, InputSize;
int XIS, YIS;
private Size OutputSize, InputSize;
private int XIS, YIS;
public override void Initialize()
{
@ -391,7 +398,7 @@ namespace BizHawk.Client.EmuHawk.Filters
if (XIS <= 1 && YIS <= 1)
{
IsNOP = true;
IsNop = true;
}
else
{
@ -411,14 +418,15 @@ namespace BizHawk.Client.EmuHawk.Filters
return base.PresizeOutput(channel, size);
}
public override Size PresizeInput(string channel, Size insize)
public override Size PresizeInput(string channel, Size inSize)
{
InputSize = insize;
return insize;
InputSize = inSize;
return inSize;
}
public override void Run()
{
FilterProgram.GuiRenderer.Begin(OutputSize); //hope this didnt change
FilterProgram.GuiRenderer.Begin(OutputSize); // hope this didn't change
FilterProgram.GuiRenderer.SetBlendState(FilterProgram.GL.BlendNoneCopy);
FilterProgram.GuiRenderer.Modelview.Scale(XIS,YIS);
FilterProgram.GuiRenderer.Draw(InputTexture);
@ -437,11 +445,11 @@ namespace BizHawk.Client.EmuHawk.Filters
DeclareOutput(state);
}
Texture2d Texture;
private Texture2d _texture;
public void SetTexture(Texture2d tex)
{
Texture = tex;
_texture = tex;
}
public override void Run()
@ -449,7 +457,7 @@ namespace BizHawk.Client.EmuHawk.Filters
var outSize = FindOutput().SurfaceFormat.Size;
FilterProgram.GuiRenderer.Begin(outSize);
FilterProgram.GuiRenderer.SetBlendState(FilterProgram.GL.BlendNormal);
FilterProgram.GuiRenderer.Draw(Texture);
FilterProgram.GuiRenderer.Draw(_texture);
FilterProgram.GuiRenderer.End();
}
}

View File

@ -3,34 +3,25 @@
// https://github.com/libretro/common-shaders/
using System;
using System.Linq;
using System.Diagnostics;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Drawing;
using BizHawk.Common;
using BizHawk.Client.Common;
using BizHawk.Client.EmuHawk;
using BizHawk.Client.EmuHawk.FilterManager;
using BizHawk.Bizware.BizwareGL;
using BizHawk.Bizware.BizwareGL.Drivers.OpenTK;
using OpenTK;
using OpenTK.Graphics;
namespace BizHawk.Client.EmuHawk.Filters
{
public class RetroShaderChain : IDisposable
{
static System.Text.RegularExpressions.Regex rxInclude = new Regex(@"^(\s)?\#include(\s)+(""|<)(.*)?(""|>)", RegexOptions.Multiline | RegexOptions.IgnoreCase);
private static readonly Regex RxInclude = new Regex(@"^(\s)?\#include(\s)+(""|<)(.*)?(""|>)", RegexOptions.Multiline | RegexOptions.IgnoreCase);
static string ResolveIncludes(string content, string baseDirectory)
{
for (; ; )
{
var match = rxInclude.Match(content);
var match = RxInclude.Match(content);
if(match.Value == "") break;
string fname = match.Groups[4].Value;
fname = Path.Combine(baseDirectory,fname);
@ -43,7 +34,7 @@ namespace BizHawk.Client.EmuHawk.Filters
public RetroShaderChain(IGL owner, RetroShaderPreset preset, string baseDirectory, bool debug = false)
{
Owner = owner;
this.Preset = preset;
Preset = preset;
Passes = preset.Passes.ToArray();
Errors = "";
@ -82,9 +73,16 @@ namespace BizHawk.Client.EmuHawk.Filters
public void Dispose()
{
if (_isDisposed) return;
if (_isDisposed)
{
return;
}
foreach (var s in Shaders)
{
s.Dispose();
}
_isDisposed = true;
}
@ -107,13 +105,13 @@ namespace BizHawk.Client.EmuHawk.Filters
public RetroShaderPreset(Stream stream)
{
var content = new StreamReader(stream).ReadToEnd();
Dictionary<string, string> dict = new Dictionary<string, string>();
var dict = new Dictionary<string, string>();
//parse the key-value-pair format of the file
content = content.Replace("\r", "");
foreach (var _line in content.Split('\n'))
foreach (var splitLine in content.Split('\n'))
{
var line = _line.Trim();
var line = splitLine.Trim();
if (line.StartsWith("#")) continue; //lines that are solely comments
if (line == "") continue; //empty line
int eq = line.IndexOf('=');
@ -137,8 +135,7 @@ namespace BizHawk.Client.EmuHawk.Filters
int nShaders = FetchInt(dict, "shaders", 0);
for (int i = 0; i < nShaders; i++)
{
ShaderPass sp = new ShaderPass();
sp.Index = i;
var sp = new ShaderPass { Index = i };
Passes.Add(sp);
sp.InputFilterLinear = FetchBool(dict, $"filter_linear{i}", false); //Should this value not be defined, the filtering option is implementation defined.
@ -159,24 +156,31 @@ namespace BizHawk.Client.EmuHawk.Filters
sp.Scale.Y = FetchFloat(dict, $"scale_y{i}", 1);
float scale = FetchFloat(dict, $"scale{i}", -999);
if (scale != -999)
{
sp.Scale.X = sp.Scale.Y = FetchFloat(dict, $"scale{i}", 1);
}
//TODO - LUTs
}
}
public List<ShaderPass> Passes = new List<ShaderPass>();
public List<ShaderPass> Passes { get; set; } = new List<ShaderPass>();
/// <summary>
/// Indicates whether any of the passes contain GLSL filenames (these are invalid now)
/// </summary>
public bool ContainsGLSL
public bool ContainsGlsl
{
get
{
foreach (var pass in Passes)
if (Path.GetExtension(pass.ShaderPath).ToLowerInvariant() == ".glsl")
{
if (Path.GetExtension(pass.ShaderPath)?.ToLowerInvariant() == ".glsl")
{
return true;
}
}
return false;
}
}
@ -198,64 +202,51 @@ namespace BizHawk.Client.EmuHawk.Filters
public Vector2 Scale;
}
string FetchString(Dictionary<string, string> dict, string key, string @default)
private string FetchString(IDictionary<string, string> dict, string key, string @default)
{
string str;
if (dict.TryGetValue(key, out str))
return str;
else return @default;
return dict.TryGetValue(key, out var str) ? str : @default;
}
int FetchInt(Dictionary<string, string> dict, string key, int @default)
private int FetchInt(IDictionary<string, string> dict, string key, int @default)
{
string str;
if (dict.TryGetValue(key, out str))
return int.Parse(str);
else return @default;
return dict.TryGetValue(key, out var str) ? int.Parse(str) : @default;
}
float FetchFloat(Dictionary<string, string> dict, string key, float @default)
private float FetchFloat(IDictionary<string, string> dict, string key, float @default)
{
string str;
if (dict.TryGetValue(key, out str))
return float.Parse(str);
else return @default;
return dict.TryGetValue(key, out var str) ? float.Parse(str) : @default;
}
bool FetchBool(Dictionary<string, string> dict, string key, bool @default)
private bool FetchBool(IDictionary<string, string> dict, string key, bool @default)
{
string str;
if (dict.TryGetValue(key, out str))
return ParseBool(str);
else return @default;
return dict.TryGetValue(key, out var str) ? ParseBool(str) : @default;
}
bool ParseBool(string value)
private bool ParseBool(string value)
{
if (value == "1") return true;
if (value == "0") return false;
value = value.ToLower();
if (value == "true") return true;
if (value == "false") return false;
throw new InvalidOperationException("Unparseable bool in CGP file content");
throw new InvalidOperationException("Unparsable bool in CGP file content");
}
}
public class RetroShaderPass : BaseFilter
{
RetroShaderChain RSC;
RetroShaderPreset.ShaderPass SP;
int RSI;
Size OutputSize;
private readonly RetroShaderChain _rsc;
private readonly RetroShaderPreset.ShaderPass _sp;
private readonly int _rsi;
private Size _outputSize;
public override string ToString() => $"{nameof(RetroShaderPass)}[#{RSI}]";
public override string ToString() => $"{nameof(RetroShaderPass)}[#{_rsi}]";
public RetroShaderPass(RetroShaderChain RSC, int index)
public RetroShaderPass(RetroShaderChain rsc, int index)
{
this.RSC = RSC;
this.RSI = index;
this.SP = RSC.Passes[index];
_rsc = rsc;
_rsi = index;
_sp = _rsc.Passes[index];
}
public override void Initialize()
@ -265,59 +256,66 @@ namespace BizHawk.Client.EmuHawk.Filters
public override void SetInputFormat(string channel, SurfaceState state)
{
Size insize = state.SurfaceFormat.Size;
if (SP.ScaleTypeX == RetroShaderPreset.ScaleType.Absolute) OutputSize.Width = (int)SP.Scale.X;
if (SP.ScaleTypeY == RetroShaderPreset.ScaleType.Absolute) OutputSize.Width = (int)SP.Scale.Y;
if (SP.ScaleTypeX == RetroShaderPreset.ScaleType.Source) OutputSize.Width = (int)(insize.Width * SP.Scale.X);
if (SP.ScaleTypeY == RetroShaderPreset.ScaleType.Source) OutputSize.Height = (int)(insize.Height * SP.Scale.Y);
Size inSize = state.SurfaceFormat.Size;
if (_sp.ScaleTypeX == RetroShaderPreset.ScaleType.Absolute) _outputSize.Width = (int)_sp.Scale.X;
if (_sp.ScaleTypeY == RetroShaderPreset.ScaleType.Absolute) _outputSize.Width = (int)_sp.Scale.Y;
if (_sp.ScaleTypeX == RetroShaderPreset.ScaleType.Source) _outputSize.Width = (int)(inSize.Width * _sp.Scale.X);
if (_sp.ScaleTypeY == RetroShaderPreset.ScaleType.Source) _outputSize.Height = (int)(inSize.Height * _sp.Scale.Y);
var outState = new SurfaceState();
outState.SurfaceFormat = new SurfaceFormat(OutputSize);
outState.SurfaceDisposition = SurfaceDisposition.RenderTarget;
DeclareOutput(outState);
DeclareOutput(new SurfaceState
{
SurfaceFormat = new SurfaceFormat(_outputSize),
SurfaceDisposition = SurfaceDisposition.RenderTarget
});
}
public override Size PresizeOutput(string channel, Size size)
{
OutputSize = size;
_outputSize = size;
return size;
}
public override Size PresizeInput(string channel, Size insize)
public override Size PresizeInput(string channel, Size inSize)
{
Size outsize = insize;
if (SP.ScaleTypeX == RetroShaderPreset.ScaleType.Absolute) outsize.Width = (int)SP.Scale.X;
if (SP.ScaleTypeY == RetroShaderPreset.ScaleType.Absolute) outsize.Width = (int)SP.Scale.Y;
if (SP.ScaleTypeX == RetroShaderPreset.ScaleType.Source) outsize.Width = (int)(insize.Width * SP.Scale.X);
if (SP.ScaleTypeY == RetroShaderPreset.ScaleType.Source) outsize.Height = (int)(insize.Height * SP.Scale.Y);
Size outsize = inSize;
if (_sp.ScaleTypeX == RetroShaderPreset.ScaleType.Absolute) outsize.Width = (int)_sp.Scale.X;
if (_sp.ScaleTypeY == RetroShaderPreset.ScaleType.Absolute) outsize.Width = (int)_sp.Scale.Y;
if (_sp.ScaleTypeX == RetroShaderPreset.ScaleType.Source) outsize.Width = (int)(inSize.Width * _sp.Scale.X);
if (_sp.ScaleTypeY == RetroShaderPreset.ScaleType.Source) outsize.Height = (int)(inSize.Height * _sp.Scale.Y);
return outsize;
}
public override void Run()
{
var shader = RSC.Shaders[RSI];
var shader = _rsc.Shaders[_rsi];
shader.Bind();
// apply all parameters to this shader.. even if it was meant for other shaders. kind of lame.
if(Parameters != null)
{
foreach (var kvp in Parameters)
{
if (kvp.Value is float)
shader.Pipeline[kvp.Key].Set((float)kvp.Value);
if (kvp.Value is float value)
{
shader.Pipeline[kvp.Key].Set(value);
}
}
}
var input = InputTexture;
if (SP.InputFilterLinear)
if (_sp.InputFilterLinear)
{
InputTexture.SetFilterLinear();
}
else
{
InputTexture.SetFilterNearest();
RSC.Shaders[RSI].Run(input, input.Size, OutputSize, InputTexture.IsUpsideDown);
}
_rsc.Shaders[_rsi].Run(input, input.Size, _outputSize, InputTexture.IsUpsideDown);
// maintain invariant.. i think.
InputTexture.SetFilterNearest();
}
}
}

View File

@ -9,12 +9,12 @@ namespace BizHawk.Client.EmuHawk.Filters
{
public SourceImage(Size size)
{
Size = size;
_size = size;
}
private readonly Size Size;
private readonly Size _size;
public Texture2d Texture;
public Texture2d Texture { get; set; }
public override void Run()
{
@ -23,7 +23,7 @@ namespace BizHawk.Client.EmuHawk.Filters
public override void Initialize()
{
DeclareOutput(new SurfaceState(new SurfaceFormat(Size), SurfaceDisposition.Texture));
DeclareOutput(new SurfaceState(new SurfaceFormat(_size), SurfaceDisposition.Texture));
}
public override void SetInputFormat(string channel, SurfaceState format)
@ -35,7 +35,7 @@ namespace BizHawk.Client.EmuHawk.Filters
/// <summary>
/// transforms an input texture to an output render target (by rendering it)
/// </summary>
class Render : BaseFilter
public class Render : BaseFilter
{
public override void Initialize()
{
@ -57,7 +57,7 @@ namespace BizHawk.Client.EmuHawk.Filters
}
}
class Resolve : BaseFilter
public class Resolve : BaseFilter
{
public override void Initialize()
{

View File

@ -1,4 +1,5 @@
using System;
using System.Drawing;
using System.Collections.Generic;
using BizHawk.Bizware.BizwareGL;
@ -7,48 +8,53 @@ namespace BizHawk.Client.EmuHawk
{
/// <summary>
/// Recycles a pair of temporary render targets, as long as the dimensions match.
/// When the dimensions dont match, a new one will be allocated
/// When the dimensions don't match, a new one will be allocated
/// </summary>
public class RenderTargetFrugalizer : IDisposable
{
public RenderTargetFrugalizer(IGL gl)
{
GL = gl;
_gl = gl;
ResetList();
}
public void Dispose()
{
foreach (var ct in CurrentRenderTargets)
if (ct != null)
ct.Dispose();
foreach (var ct in _currentRenderTargets)
{
ct?.Dispose();
}
ResetList();
}
void ResetList()
private void ResetList()
{
CurrentRenderTargets = new List<RenderTarget>();
CurrentRenderTargets.Add(null);
CurrentRenderTargets.Add(null);
_currentRenderTargets = new List<RenderTarget> { null, null };
}
IGL GL;
List<RenderTarget> CurrentRenderTargets;
private readonly IGL _gl;
private List<RenderTarget> _currentRenderTargets;
public RenderTarget Get(Size dimensions)
{
return Get(dimensions.Width, dimensions.Height);
}
public RenderTarget Get(System.Drawing.Size dimensions) { return Get(dimensions.Width, dimensions.Height); }
public RenderTarget Get(int width, int height)
{
//get the current entry
RenderTarget CurrentRenderTarget = CurrentRenderTargets[0];
RenderTarget currentRenderTarget = _currentRenderTargets[0];
//check if its rotten and needs recreating
if (CurrentRenderTarget == null || CurrentRenderTarget.Texture2d.IntWidth != width || CurrentRenderTarget.Texture2d.IntHeight != height)
if (currentRenderTarget == null
|| currentRenderTarget.Texture2d.IntWidth != width
|| currentRenderTarget.Texture2d.IntHeight != height)
{
// needs recreating. be sure to kill the old one...
if (CurrentRenderTarget != null)
CurrentRenderTarget.Dispose();
currentRenderTarget?.Dispose();
// and make a new one
CurrentRenderTarget = GL.CreateRenderTarget(width, height);
currentRenderTarget = _gl.CreateRenderTarget(width, height);
}
else
{
@ -56,10 +62,10 @@ namespace BizHawk.Client.EmuHawk
}
// now shuffle the buffers
CurrentRenderTargets[0] = CurrentRenderTargets[1];
CurrentRenderTargets[1] = CurrentRenderTarget;
_currentRenderTargets[0] = _currentRenderTargets[1];
_currentRenderTargets[1] = currentRenderTarget;
return CurrentRenderTarget;
return currentRenderTarget;
}
}
}

View File

@ -4,13 +4,13 @@ namespace BizHawk.Client.EmuHawk
{
/// <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
/// sizes are available and keeping them cleaned up when they don't seem like they'll need to be used anymore
/// </summary>
public class SwappableDisplaySurfaceSet
{
private DisplaySurface Pending, Current;
private bool IsPending;
private readonly Queue<DisplaySurface> ReleasedSurfaces = new Queue<DisplaySurface>();
private DisplaySurface _pending, _current;
private bool _isPending;
private readonly Queue<DisplaySurface> _releasedSurfaces = new Queue<DisplaySurface>();
/// <summary>
/// retrieves a surface with the specified size, reusing an old buffer if available and clearing if requested
@ -22,16 +22,22 @@ namespace BizHawk.Client.EmuHawk
DisplaySurface trial;
lock (this)
{
if (ReleasedSurfaces.Count == 0) break;
trial = ReleasedSurfaces.Dequeue();
if (_releasedSurfaces.Count == 0) break;
trial = _releasedSurfaces.Dequeue();
}
if (trial.Width == width && trial.Height == height)
{
if (needsClear) trial.Clear();
if (needsClear)
{
trial.Clear();
}
return trial;
}
trial.Dispose();
}
return new DisplaySurface(width, height);
}
@ -42,15 +48,15 @@ namespace BizHawk.Client.EmuHawk
{
lock (this)
{
if (Pending != null) ReleasedSurfaces.Enqueue(Pending);
Pending = newPending;
IsPending = true;
if (_pending != null) _releasedSurfaces.Enqueue(_pending);
_pending = newPending;
_isPending = true;
}
}
public void ReleaseSurface(DisplaySurface surface)
{
lock (this) ReleasedSurfaces.Enqueue(surface);
lock (this) _releasedSurfaces.Enqueue(surface);
}
/// <summary>
@ -60,15 +66,16 @@ namespace BizHawk.Client.EmuHawk
{
lock (this)
{
if (IsPending)
if (_isPending)
{
if (Current != null) ReleasedSurfaces.Enqueue(Current);
Current = Pending;
Pending = null;
IsPending = false;
if (_current != null) _releasedSurfaces.Enqueue(_current);
_current = _pending;
_pending = null;
_isPending = false;
}
}
return Current;
return _current;
}
}
}

View File

@ -12,13 +12,13 @@ namespace BizHawk.Client.EmuHawk
{
public TextureFrugalizer(IGL gl)
{
GL = gl;
_gl = gl;
ResetList();
}
public void Dispose()
{
foreach (var ct in CurrentTextures)
foreach (var ct in _currentTextures)
{
ct?.Dispose();
}
@ -26,13 +26,13 @@ namespace BizHawk.Client.EmuHawk
ResetList();
}
void ResetList()
private void ResetList()
{
CurrentTextures = new List<Texture2d> { null, null };
_currentTextures = new List<Texture2d> { null, null };
}
private readonly IGL GL;
private List<Texture2d> CurrentTextures;
private readonly IGL _gl;
private List<Texture2d> _currentTextures;
public Texture2d Get(DisplaySurface ds)
{
@ -42,7 +42,7 @@ namespace BizHawk.Client.EmuHawk
public Texture2d Get(BitmapBuffer bb)
{
//get the current entry
Texture2d currentTexture = CurrentTextures[0];
Texture2d currentTexture = _currentTextures[0];
// TODO - its a bit cruddy here that we dont respect the current texture HasAlpha condition (in fact, there's no such concept)
// we might need to deal with that in the future to fix some bugs.
@ -53,17 +53,17 @@ namespace BizHawk.Client.EmuHawk
//needs recreating. be sure to kill the old one...
currentTexture?.Dispose();
//and make a new one
currentTexture = GL.LoadTexture(bb);
currentTexture = _gl.LoadTexture(bb);
}
else
{
//its good! just load in the data
GL.LoadTextureData(currentTexture, bb);
_gl.LoadTextureData(currentTexture, bb);
}
//now shuffle the buffers
CurrentTextures[0] = CurrentTextures[1];
CurrentTextures[1] = currentTexture;
_currentTextures[0] = _currentTextures[1];
_currentTextures[1] = currentTexture;
//deterministic state, i guess
currentTexture.SetFilterNearest();

View File

@ -202,7 +202,7 @@ namespace BizHawk.Client.EmuHawk
using (var stream = File.OpenRead(choice))
{
var cgp = new BizHawk.Client.EmuHawk.Filters.RetroShaderPreset(stream);
if (cgp.ContainsGLSL)
if (cgp.ContainsGlsl)
{
MessageBox.Show("Specified CGP contains references to .glsl files. This is illegal. Use .cg");
return;

View File

@ -271,6 +271,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=Justifier/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=keepalives/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=KEYMENU/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Letterboxing/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Libretro/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Lightgun/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Lmsv/@EntryIndexedValue">True</s:Boolean>
@ -319,6 +320,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=preload/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Prereqs/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=prescale/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Presize/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Quantizer/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=quantizing/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=quickload/@EntryIndexedValue">True</s:Boolean>
@ -368,6 +370,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=Underruns/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Uninitialize/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=unmerge/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Unparsable/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=unpause/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=unpaused/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Unpausing/@EntryIndexedValue">True</s:Boolean>