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 //BizHawk says: adapted from https://github.com/inexorabletash/PcxFileType/blob/master/Quantize
using System;
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using BizHawk.Common.NumberExtensions;
namespace BizHawk.Client.EmuHawk namespace BizHawk.Client.EmuHawk
{ {

View File

@ -1,5 +1,5 @@
//TODO // TODO
//we could flag textures as 'actually' render targets (keep a reference to the render target?) which could allow us to convert between them more quickly in some cases // we could flag textures as 'actually' render targets (keep a reference to the render target?) which could allow us to convert between them more quickly in some cases
using System; using System;
using System.IO; using System.IO;
@ -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 /// 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. /// THIS IS OLD JUNK. I should get rid of it, I think. complex results from the last filter ingestion should be saved instead.
/// </summary> /// </summary>
int currEmuWidth, currEmuHeight; private int currEmuWidth, currEmuHeight;
/// <summary> /// <summary>
/// additional pixels added at the unscaled level for the use of lua drawing. essentially increases the input video provider dimensions /// 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: case FilterProgram.ProgramStepType.FinalTarget:
{ {
var size = (Size)step.Args;
inFinalTarget = true; inFinalTarget = true;
rtCurr = null; rtCurr = null;
_currentFilterProgram.CurrRenderTarget = null; _currentFilterProgram.CurrRenderTarget = null;
@ -927,17 +926,17 @@ namespace BizHawk.Client.EmuHawk
{ {
Debug.Assert(inFinalTarget); Debug.Assert(inFinalTarget);
//wait for vsync to begin // wait for vsync to begin
if (alternateVsync) dx9.AlternateVsyncPass(0); if (alternateVsync) dx9.AlternateVsyncPass(0);
//present and conclude drawing // present and conclude drawing
presentationPanel.GraphicsControl.SwapBuffers(); presentationPanel.GraphicsControl.SwapBuffers();
//wait for vsync to end // wait for vsync to end
if (alternateVsync) dx9.AlternateVsyncPass(1); if (alternateVsync) dx9.AlternateVsyncPass(1);
//nope. don't do this. workaround for slow context switching on intel GPUs. just switch to another context when necessary before doing anything // nope. don't 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();
} }
} }
@ -955,9 +954,9 @@ namespace BizHawk.Client.EmuHawk
private bool? LastVsyncSetting; private bool? LastVsyncSetting;
private GraphicsControl LastVsyncSettingGraphicsControl; private GraphicsControl LastVsyncSettingGraphicsControl;
private Dictionary<string, DisplaySurface> MapNameToLuaSurface = new Dictionary<string,DisplaySurface>(); private readonly Dictionary<string, DisplaySurface> MapNameToLuaSurface = new Dictionary<string,DisplaySurface>();
private Dictionary<DisplaySurface, string> MapLuaSurfaceToName = new Dictionary<DisplaySurface, string>(); private readonly Dictionary<DisplaySurface, string> MapLuaSurfaceToName = new Dictionary<DisplaySurface, string>();
private Dictionary<string, SwappableDisplaySurfaceSet> LuaSurfaceSets = new Dictionary<string, SwappableDisplaySurfaceSet>(); private readonly Dictionary<string, SwappableDisplaySurfaceSet> LuaSurfaceSets = new Dictionary<string, SwappableDisplaySurfaceSet>();
/// <summary> /// <summary>
/// Peeks a locked lua surface, or returns null if it isn't locked /// Peeks a locked lua surface, or returns null if it isn't locked
@ -993,13 +992,17 @@ namespace BizHawk.Client.EmuHawk
currNativeHeight += ClientExtraPadding.Vertical; currNativeHeight += ClientExtraPadding.Vertical;
int width,height; int width,height;
if(name == "emu") { if (name == "emu")
{
width = currEmuWidth; width = currEmuWidth;
height = currEmuHeight; height = currEmuHeight;
width += GameExtraPadding.Horizontal; width += GameExtraPadding.Horizontal;
height += GameExtraPadding.Vertical; 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}"); else throw new InvalidOperationException($"Unknown lua surface name: {name}");
DisplaySurface ret = sdss.AllocateSurface(width, height, clear); DisplaySurface ret = sdss.AllocateSurface(width, height, clear);
@ -1018,7 +1021,7 @@ namespace BizHawk.Client.EmuHawk
DisplaySurface surfLocked = null; DisplaySurface surfLocked = null;
if (surf == null) if (surf == null)
{ {
surfLocked = LockLuaSurface(kvp.Key,true); surfLocked = LockLuaSurface(kvp.Key, true);
} }
if (surfLocked != null) if (surfLocked != null)

View File

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

View File

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

View File

@ -1,4 +1,3 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using BizHawk.Client.EmuHawk.FilterManager; using BizHawk.Client.EmuHawk.FilterManager;
@ -6,35 +5,33 @@ using BizHawk.Client.EmuHawk.FilterManager;
using BizHawk.Bizware.BizwareGL; using BizHawk.Bizware.BizwareGL;
using OpenTK; using OpenTK;
//Here's how to make a filter: // Here's how to make a filter:
//1. Reset your state entirely in Initialize(). // 1. Reset your state entirely in Initialize().
// The filter will be re-initialized several times while the chain is getting worked out, but not re-instantiated. // The filter will be re-initialized several times while the chain is getting worked out, but not re-instantiated.
// This is sort of annoying, but there's pretty good reasons for it (some external process has created the filters and set parameters needed to govern their chaining and surface properties) // This is sort of annoying, but there's pretty good reasons for it (some external process has created the filters and set parameters needed to govern their chaining and surface properties)
//2. In Initialize(), be sure to use DeclareInput // 2. In Initialize(), be sure to use DeclareInput
//(something about PresizeInput()) // (something about PresizeInput())
//3. PresizeOutput() will be called next // 3. PresizeOutput() will be called next
//4. In SetInputFormat(), use DeclareOutput to set the output based on your desires, or the provided input format. // 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 // 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 // 6. In Run(), if supplying an output texture, use YieldOutput
namespace BizHawk.Client.EmuHawk.Filters namespace BizHawk.Client.EmuHawk.Filters
{ {
public class BaseFilter public class BaseFilter
{ {
//initialization stuff // 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 void Initialize() { }
public virtual Size PresizeInput(string channel, Size size) { return size; } public virtual Size PresizeInput(string channel, Size size) => size;
public virtual Size PresizeOutput(string channel, Size size) { return 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 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 Dictionary<string, object> Parameters = new Dictionary<string, object>();
public bool IsNOP { get => _IsNop; protected set => _IsNop = value; } public bool IsNop { get; set; }
private Boolean _IsNop = false;
//runtime signals // runtime signals
public virtual Vector2 UntransformPoint(string channel, Vector2 point) public virtual Vector2 UntransformPoint(string channel, Vector2 point)
{ {
//base class behaviour here just uses the input and output sizes, if appropriate. few filters will have to do anything more complex // base class behaviour here just uses the input and output sizes, if appropriate. few filters will have to do anything more complex
var input = FindInput(channel); var input = FindInput(channel);
var output = FindOutput(channel); var output = FindOutput(channel);
if (input != null && output != null) if (input != null && output != null)
@ -47,7 +44,7 @@ namespace BizHawk.Client.EmuHawk.Filters
public virtual Vector2 TransformPoint(string channel, Vector2 point) public virtual Vector2 TransformPoint(string channel, Vector2 point)
{ {
//base class behaviour here just uses the input and output sizes, if appropriate. few filters will have to do anything more complex // base class behaviour here just uses the input and output sizes, if appropriate. few filters will have to do anything more complex
var input = FindInput(channel); var input = FindInput(channel);
var output = FindOutput(channel); var output = FindOutput(channel);
if (input != null && output != null) if (input != null && output != null)
@ -63,24 +60,34 @@ namespace BizHawk.Client.EmuHawk.Filters
InputTexture = tex; InputTexture = tex;
} }
public virtual void Run() { } public virtual void Run() { }
public Texture2d GetOutput() { return OutputTexture; } public Texture2d GetOutput() => _outputTexture;
//filter actions // filter actions
protected void YieldOutput(Texture2d tex) protected void YieldOutput(Texture2d tex)
{ {
OutputTexture = tex; _outputTexture = tex;
} }
protected FilterProgram FilterProgram; protected FilterProgram FilterProgram;
protected Texture2d InputTexture; protected Texture2d InputTexture;
private Texture2d OutputTexture; private Texture2d _outputTexture;
//setup utilities // setup utilities
protected IOSurfaceInfo DeclareInput(SurfaceDisposition disposition = SurfaceDisposition.Unspecified, string channel = "default") { return DeclareIO(SurfaceDirection.Input, channel, disposition); } protected IOSurfaceInfo DeclareInput(SurfaceDisposition disposition = SurfaceDisposition.Unspecified, string channel = "default")
protected IOSurfaceInfo DeclareOutput(SurfaceDisposition disposition = SurfaceDisposition.Unspecified, string channel = "default") { return DeclareIO(SurfaceDirection.Output, channel, disposition); } {
//TODO - why a different param order than DeclareOutput? 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") protected IOSurfaceInfo DeclareOutput(SurfaceState state, string channel = "default")
{ {
@ -89,36 +96,51 @@ namespace BizHawk.Client.EmuHawk.Filters
return iosi; return iosi;
} }
public IOSurfaceInfo FindInput(string channel = "default") { return FindIOSurfaceInfo(channel, SurfaceDirection.Input); } public IOSurfaceInfo FindInput(string channel = "default")
public IOSurfaceInfo FindOutput(string channel = "default") { return FindIOSurfaceInfo(channel, SurfaceDirection.Output); } {
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) private IOSurfaceInfo DeclareIO(SurfaceDirection direction, string channel, SurfaceDisposition disposition)
{ {
var iosi = new IOSurfaceInfo(); var iosi = new IOSurfaceInfo
iosi.SurfaceDirection = direction; {
iosi.Channel = channel; SurfaceDirection = direction,
iosi.SurfaceDisposition = disposition; Channel = channel,
IOSurfaceInfos.Add(iosi); SurfaceDisposition = disposition
};
_ioSurfaceInfos.Add(iosi);
return 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) private IOSurfaceInfo FindIOSurfaceInfo(string channel, SurfaceDirection direction)
{ {
foreach (var iosi in IOSurfaceInfos) foreach (var iosi in _ioSurfaceInfos)
{
if (iosi.Channel == channel && iosi.SurfaceDirection == direction) if (iosi.Channel == channel && iosi.SurfaceDirection == direction)
{
return iosi; return iosi;
}
}
return null; return null;
} }
public class IOSurfaceInfo public class IOSurfaceInfo
{ {
public SurfaceFormat SurfaceFormat; public SurfaceFormat SurfaceFormat { get; set; }
public SurfaceDirection SurfaceDirection; public SurfaceDirection SurfaceDirection { get; set; }
public SurfaceDisposition SurfaceDisposition; public SurfaceDisposition SurfaceDisposition { get; set; }
public string Channel; public string Channel { get; set; }
} }
public enum SurfaceDirection 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. /// 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. /// In the future this could also apply rules like integer-only scaling, etc.
/// </summary> /// </summary>
class LetterboxingLogic public class LetterboxingLogic
{ {
/// <summary> /// <summary>
/// the location within the destination region of the output content (scaled and translated) /// the location within the destination region of the output content (scaled and translated)
@ -28,7 +28,7 @@ namespace BizHawk.Client.EmuHawk.Filters
/// </summary> /// </summary>
public LetterboxingLogic() { } public LetterboxingLogic() { }
//do maths on the viewport and the native resolution and the user settings to get a display rectangle // do maths on the viewport and the native resolution and the user settings to get a display rectangle
public LetterboxingLogic(bool maintainAspect, bool maintainInteger, int targetWidth, int targetHeight, int sourceWidth, int sourceHeight, Size textureSize, Size virtualSize) public LetterboxingLogic(bool maintainAspect, bool maintainInteger, int targetWidth, int targetHeight, int sourceWidth, int sourceHeight, Size textureSize, Size virtualSize)
{ {
int textureWidth = textureSize.Width; int textureWidth = textureSize.Width;
@ -42,15 +42,17 @@ namespace BizHawk.Client.EmuHawk.Filters
sourceWidth = (int)virtualWidth; sourceWidth = (int)virtualWidth;
sourceHeight = (int)virtualHeight; sourceHeight = (int)virtualHeight;
//this doesnt make sense // this doesn't make sense
if (!maintainAspect) if (!maintainAspect)
{
maintainInteger = false; maintainInteger = false;
}
float widthScale = (float)targetWidth / sourceWidth; float widthScale = (float)targetWidth / sourceWidth;
float heightScale = (float)targetHeight / sourceHeight; float heightScale = (float)targetHeight / sourceHeight;
if (maintainAspect if (maintainAspect
//zero 20-jul-2014 - hacks upon hacks, this function needs rewriting // zero 20-jul-2014 - hacks upon hacks, this function needs rewriting
&& !maintainInteger && !maintainInteger
) )
{ {
@ -60,51 +62,53 @@ namespace BizHawk.Client.EmuHawk.Filters
if (maintainInteger) if (maintainInteger)
{ {
//just totally different code // just totally different code
//apply the zooming algorithm (pasted and reworked, for now) // apply the zooming algorithm (pasted and reworked, for now)
//ALERT COPYPASTE LAUNDROMAT // ALERT COPYPASTE LAUNDROMAT
Vector2 VS = new Vector2(virtualWidth, virtualHeight); Vector2 VS = new Vector2(virtualWidth, virtualHeight);
Vector2 BS = new Vector2(textureWidth, textureHeight); Vector2 BS = new Vector2(textureWidth, textureHeight);
Vector2 AR = Vector2.Divide(VS, BS); 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 Vector2 PS = new Vector2(1, 1); //this would malfunction for AR <= 0.5 or AR >= 2.0
for(;;) for(;;)
{ {
//TODO - would be good not to run this per frame.... // 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, 0),
PS + new Vector2(1, 1) 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; int bestIndex = -1;
float bestValue = 1000.0f; float bestValue = 1000.0f;
for (int t = 0; t < trials.Length; t++) for (int t = 0; t < trials.Length; t++)
{ {
Vector2 vTrial = trials[t]; Vector2 vTrial = trials[t];
trials_limited[t] = false; trialsLimited[t] = false;
//check whether this is going to exceed our allotted area //check whether this is going to exceed our allotted area
int test_vw = (int)(vTrial.X * textureWidth); int testVw = (int)(vTrial.X * textureWidth);
int test_vh = (int)(vTrial.Y * textureHeight); int testVh = (int)(vTrial.Y * textureHeight);
if (test_vw > targetWidth) trials_limited[t] = true; if (testVw > targetWidth) trialsLimited[t] = true;
if (test_vh > targetHeight) trials_limited[t] = true; if (testVh > targetHeight) trialsLimited[t] = true;
//I. // I.
float test_ar = vTrial.X / vTrial.Y; float testAr = vTrial.X / vTrial.Y;
//II. // II.
//Vector2 calc = Vector2.Multiply(trials[t], VS); // Vector2 calc = Vector2.Multiply(trials[t], VS);
//float test_ar = calc.X / calc.Y; // float test_ar = calc.X / calc.Y;
//not clear which approach is superior // not clear which approach is superior
float deviation_linear = Math.Abs(test_ar - target_par); float deviationLinear = Math.Abs(testAr - targetPar);
float deviation_geom = test_ar / target_par; float deviationGeom = testAr / targetPar;
if (deviation_geom < 1) deviation_geom = 1.0f / deviation_geom; if (deviationGeom < 1) deviationGeom = 1.0f / deviationGeom;
float value = deviation_linear; float value = deviationLinear;
if (value < bestValue) if (value < bestValue)
{ {
bestIndex = t; bestIndex = t;
@ -112,20 +116,20 @@ namespace BizHawk.Client.EmuHawk.Filters
} }
} }
//last result was best, so bail out // last result was best, so bail out
if (bestIndex == -1) if (bestIndex == -1)
break; break;
//if the winner ran off the edge, bail out // if the winner ran off the edge, bail out
if (trials_limited[bestIndex]) if (trialsLimited[bestIndex])
break; break;
PS = trials[bestIndex]; PS = trials[bestIndex];
} }
//"fix problems with gameextrapadding in >1x window scales" (other edits were made, maybe theyre whats important) // "fix problems with gameextrapadding in >1x window scales" (other edits were made, maybe theyre whats important)
//vw = (int)(PS.X * oldSourceWidth); // vw = (int)(PS.X * oldSourceWidth);
//vh = (int)(PS.Y * oldSourceHeight); // vh = (int)(PS.Y * oldSourceHeight);
vw = (int)(PS.X * sourceWidth); vw = (int)(PS.X * sourceWidth);
vh = (int)(PS.Y * sourceHeight); vh = (int)(PS.Y * sourceHeight);
widthScale = PS.X; widthScale = PS.X;
@ -137,27 +141,31 @@ namespace BizHawk.Client.EmuHawk.Filters
vh = (int)(heightScale * sourceHeight); 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 // 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 // instead, since we're more about biz than gaming, lets shrink the view to fit in the small dimension
if (targetWidth < vw) if (targetWidth < vw)
{
vw = targetWidth; vw = targetWidth;
}
if (targetHeight < vh) if (targetHeight < vh)
{
vh = targetHeight; vh = targetHeight;
}
//determine letterboxing parameters //determine letterboxing parameters
vx = (targetWidth - vw) / 2; vx = (targetWidth - vw) / 2;
vy = (targetHeight - vh) / 2; vy = (targetHeight - vh) / 2;
//zero 09-oct-2014 - changed this for TransformPoint. scenario: basic 1x (but system-specified AR) NES window. // 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. // 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; // WidthScale = widthScale;
//HeightScale = heightScale; // HeightScale = heightScale;
WidthScale = (float)vw / oldSourceWidth; WidthScale = (float)vw / oldSourceWidth;
HeightScale = (float)vh / oldSourceHeight; HeightScale = (float)vh / oldSourceHeight;
} }
} }
public class FinalPresentation : BaseFilter public class FinalPresentation : BaseFilter
@ -168,23 +176,22 @@ namespace BizHawk.Client.EmuHawk.Filters
} }
public eFilterOption FilterOption = eFilterOption.None; public eFilterOption FilterOption = eFilterOption.None;
public RetroShaderChain BicubicFilter;
public FinalPresentation(Size size) public FinalPresentation(Size size)
{ {
this.OutputSize = size; this.OutputSize = size;
} }
Size OutputSize, InputSize; private Size OutputSize, InputSize;
public Size TextureSize, VirtualTextureSize; public Size TextureSize, VirtualTextureSize;
public int BackgroundColor; public int BackgroundColor;
public bool AutoPrescale; public bool AutoPrescale;
public IGuiRenderer GuiRenderer; public IGuiRenderer GuiRenderer;
public bool Flip; public bool Flip;
public IGL GL; public IGL GL;
bool nop; private bool nop;
LetterboxingLogic LL; private LetterboxingLogic LL;
Size ContentSize; private Size ContentSize;
public bool Config_FixAspectRatio, Config_FixScaleInteger, Config_PadOnly; public bool Config_FixAspectRatio, Config_FixScaleInteger, Config_PadOnly;
@ -278,10 +285,10 @@ namespace BizHawk.Client.EmuHawk.Filters
ContentSize = new Size(LL.vw,LL.vh); ContentSize = new Size(LL.vw,LL.vh);
if (InputSize == OutputSize) //any reason we need to check vx and vy? 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) 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) public override void SetInputFormat(string channel, SurfaceState state)
{ {
var OutputSize = state.SurfaceFormat.Size; var outputSize = state.SurfaceFormat.Size;
OutputSize.Width *= Scale; outputSize.Width *= Scale;
OutputSize.Height *= Scale; outputSize.Height *= Scale;
var ss = new SurfaceState(new SurfaceFormat(OutputSize), SurfaceDisposition.RenderTarget); var ss = new SurfaceState(new SurfaceFormat(outputSize), SurfaceDisposition.RenderTarget);
DeclareOutput(ss, channel); DeclareOutput(ss, channel);
} }
@ -370,8 +377,8 @@ namespace BizHawk.Client.EmuHawk.Filters
public class AutoPrescaleFilter : BaseFilter public class AutoPrescaleFilter : BaseFilter
{ {
Size OutputSize, InputSize; private Size OutputSize, InputSize;
int XIS, YIS; private int XIS, YIS;
public override void Initialize() public override void Initialize()
{ {
@ -391,7 +398,7 @@ namespace BizHawk.Client.EmuHawk.Filters
if (XIS <= 1 && YIS <= 1) if (XIS <= 1 && YIS <= 1)
{ {
IsNOP = true; IsNop = true;
} }
else else
{ {
@ -411,19 +418,20 @@ namespace BizHawk.Client.EmuHawk.Filters
return base.PresizeOutput(channel, size); return base.PresizeOutput(channel, size);
} }
public override Size PresizeInput(string channel, Size insize) public override Size PresizeInput(string channel, Size inSize)
{ {
InputSize = insize; InputSize = inSize;
return insize; return inSize;
} }
public override void Run() 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.SetBlendState(FilterProgram.GL.BlendNoneCopy);
FilterProgram.GuiRenderer.Modelview.Scale(XIS,YIS); FilterProgram.GuiRenderer.Modelview.Scale(XIS,YIS);
FilterProgram.GuiRenderer.Draw(InputTexture); FilterProgram.GuiRenderer.Draw(InputTexture);
FilterProgram.GuiRenderer.End(); FilterProgram.GuiRenderer.End();
} }
} }
public class LuaLayer : BaseFilter public class LuaLayer : BaseFilter
@ -437,11 +445,11 @@ namespace BizHawk.Client.EmuHawk.Filters
DeclareOutput(state); DeclareOutput(state);
} }
Texture2d Texture; private Texture2d _texture;
public void SetTexture(Texture2d tex) public void SetTexture(Texture2d tex)
{ {
Texture = tex; _texture = tex;
} }
public override void Run() public override void Run()
@ -449,7 +457,7 @@ namespace BizHawk.Client.EmuHawk.Filters
var outSize = FindOutput().SurfaceFormat.Size; var outSize = FindOutput().SurfaceFormat.Size;
FilterProgram.GuiRenderer.Begin(outSize); FilterProgram.GuiRenderer.Begin(outSize);
FilterProgram.GuiRenderer.SetBlendState(FilterProgram.GL.BlendNormal); FilterProgram.GuiRenderer.SetBlendState(FilterProgram.GL.BlendNormal);
FilterProgram.GuiRenderer.Draw(Texture); FilterProgram.GuiRenderer.Draw(_texture);
FilterProgram.GuiRenderer.End(); FilterProgram.GuiRenderer.End();
} }
} }

View File

@ -1,36 +1,27 @@
//https://github.com/Themaister/RetroArch/wiki/GLSL-shaders // https://github.com/Themaister/RetroArch/wiki/GLSL-shaders
//https://github.com/Themaister/Emulator-Shader-Pack/blob/master/Cg/README // https://github.com/Themaister/Emulator-Shader-Pack/blob/master/Cg/README
//https://github.com/libretro/common-shaders/ // https://github.com/libretro/common-shaders/
using System; using System;
using System.Linq;
using System.Diagnostics;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Drawing; using System.Drawing;
using BizHawk.Common;
using BizHawk.Client.Common;
using BizHawk.Client.EmuHawk;
using BizHawk.Client.EmuHawk.FilterManager; using BizHawk.Client.EmuHawk.FilterManager;
using BizHawk.Bizware.BizwareGL; using BizHawk.Bizware.BizwareGL;
using BizHawk.Bizware.BizwareGL.Drivers.OpenTK;
using OpenTK; using OpenTK;
using OpenTK.Graphics;
namespace BizHawk.Client.EmuHawk.Filters namespace BizHawk.Client.EmuHawk.Filters
{ {
public class RetroShaderChain : IDisposable 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) static string ResolveIncludes(string content, string baseDirectory)
{ {
for (; ; ) for (; ; )
{ {
var match = rxInclude.Match(content); var match = RxInclude.Match(content);
if(match.Value == "") break; if(match.Value == "") break;
string fname = match.Groups[4].Value; string fname = match.Groups[4].Value;
fname = Path.Combine(baseDirectory,fname); 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) public RetroShaderChain(IGL owner, RetroShaderPreset preset, string baseDirectory, bool debug = false)
{ {
Owner = owner; Owner = owner;
this.Preset = preset; Preset = preset;
Passes = preset.Passes.ToArray(); Passes = preset.Passes.ToArray();
Errors = ""; Errors = "";
@ -82,9 +73,16 @@ namespace BizHawk.Client.EmuHawk.Filters
public void Dispose() public void Dispose()
{ {
if (_isDisposed) return; if (_isDisposed)
{
return;
}
foreach (var s in Shaders) foreach (var s in Shaders)
{
s.Dispose(); s.Dispose();
}
_isDisposed = true; _isDisposed = true;
} }
@ -107,13 +105,13 @@ namespace BizHawk.Client.EmuHawk.Filters
public RetroShaderPreset(Stream stream) public RetroShaderPreset(Stream stream)
{ {
var content = new StreamReader(stream).ReadToEnd(); 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 //parse the key-value-pair format of the file
content = content.Replace("\r", ""); 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.StartsWith("#")) continue; //lines that are solely comments
if (line == "") continue; //empty line if (line == "") continue; //empty line
int eq = line.IndexOf('='); int eq = line.IndexOf('=');
@ -133,12 +131,11 @@ namespace BizHawk.Client.EmuHawk.Filters
dict[key.ToLower()] = value; dict[key.ToLower()] = value;
} }
//process the keys // process the keys
int nShaders = FetchInt(dict, "shaders", 0); int nShaders = FetchInt(dict, "shaders", 0);
for (int i = 0; i < nShaders; i++) for (int i = 0; i < nShaders; i++)
{ {
ShaderPass sp = new ShaderPass(); var sp = new ShaderPass { Index = i };
sp.Index = i;
Passes.Add(sp); Passes.Add(sp);
sp.InputFilterLinear = FetchBool(dict, $"filter_linear{i}", false); //Should this value not be defined, the filtering option is implementation defined. sp.InputFilterLinear = FetchBool(dict, $"filter_linear{i}", false); //Should this value not be defined, the filtering option is implementation defined.
@ -146,37 +143,44 @@ namespace BizHawk.Client.EmuHawk.Filters
sp.FrameCountMod = FetchInt(dict, $"frame_count_mod{i}", 1); sp.FrameCountMod = FetchInt(dict, $"frame_count_mod{i}", 1);
sp.ShaderPath = FetchString(dict, $"shader{i}", "?"); //todo - change extension to .cg for better compatibility? just change .cg to .glsl transparently at last second? sp.ShaderPath = FetchString(dict, $"shader{i}", "?"); //todo - change extension to .cg for better compatibility? just change .cg to .glsl transparently at last second?
//If no scale type is assumed, it is assumed that it is set to "source" with scaleN set to 1.0. // If no scale type is assumed, it is assumed that it is set to "source" with scaleN set to 1.0.
//It is possible to set scale_type_xN and scale_type_yN to specialize the scaling type in either direction. scale_typeN however overrides both of these. // It is possible to set scale_type_xN and scale_type_yN to specialize the scaling type in either direction. scale_typeN however overrides both of these.
sp.ScaleTypeX = (ScaleType)Enum.Parse(typeof(ScaleType), FetchString(dict, $"scale_type_x{i}", "Source"), true); sp.ScaleTypeX = (ScaleType)Enum.Parse(typeof(ScaleType), FetchString(dict, $"scale_type_x{i}", "Source"), true);
sp.ScaleTypeY = (ScaleType)Enum.Parse(typeof(ScaleType), FetchString(dict, $"scale_type_y{i}", "Source"), true); sp.ScaleTypeY = (ScaleType)Enum.Parse(typeof(ScaleType), FetchString(dict, $"scale_type_y{i}", "Source"), true);
ScaleType st = (ScaleType)Enum.Parse(typeof(ScaleType), FetchString(dict, $"scale_type{i}", "NotSet"), true); ScaleType st = (ScaleType)Enum.Parse(typeof(ScaleType), FetchString(dict, $"scale_type{i}", "NotSet"), true);
if (st != ScaleType.NotSet) if (st != ScaleType.NotSet)
sp.ScaleTypeX = sp.ScaleTypeY = st; sp.ScaleTypeX = sp.ScaleTypeY = st;
//scaleN controls both scaling type in horizontal and vertical directions. If scaleN is defined, scale_xN and scale_yN have no effect. // scaleN controls both scaling type in horizontal and vertical directions. If scaleN is defined, scale_xN and scale_yN have no effect.
sp.Scale.X = FetchFloat(dict, $"scale_x{i}", 1); sp.Scale.X = FetchFloat(dict, $"scale_x{i}", 1);
sp.Scale.Y = FetchFloat(dict, $"scale_y{i}", 1); sp.Scale.Y = FetchFloat(dict, $"scale_y{i}", 1);
float scale = FetchFloat(dict, $"scale{i}", -999); float scale = FetchFloat(dict, $"scale{i}", -999);
if (scale != -999) if (scale != -999)
{
sp.Scale.X = sp.Scale.Y = FetchFloat(dict, $"scale{i}", 1); sp.Scale.X = sp.Scale.Y = FetchFloat(dict, $"scale{i}", 1);
}
//TODO - LUTs //TODO - LUTs
} }
} }
public List<ShaderPass> Passes = new List<ShaderPass>(); public List<ShaderPass> Passes { get; set; } = new List<ShaderPass>();
/// <summary> /// <summary>
/// Indicates whether any of the passes contain GLSL filenames (these are invalid now) /// Indicates whether any of the passes contain GLSL filenames (these are invalid now)
/// </summary> /// </summary>
public bool ContainsGLSL public bool ContainsGlsl
{ {
get get
{ {
foreach (var pass in Passes) foreach (var pass in Passes)
if (Path.GetExtension(pass.ShaderPath).ToLowerInvariant() == ".glsl") {
if (Path.GetExtension(pass.ShaderPath)?.ToLowerInvariant() == ".glsl")
{
return true; return true;
}
}
return false; return false;
} }
} }
@ -198,64 +202,51 @@ namespace BizHawk.Client.EmuHawk.Filters
public Vector2 Scale; 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; return dict.TryGetValue(key, out var str) ? str : @default;
if (dict.TryGetValue(key, out str))
return str;
else return @default;
} }
int FetchInt(Dictionary<string, string> dict, string key, int @default) private int FetchInt(IDictionary<string, string> dict, string key, int @default)
{ {
string str; return dict.TryGetValue(key, out var str) ? int.Parse(str) : @default;
if (dict.TryGetValue(key, out str))
return int.Parse(str);
else return @default;
} }
float FetchFloat(Dictionary<string, string> dict, string key, float @default) private float FetchFloat(IDictionary<string, string> dict, string key, float @default)
{ {
string str; return dict.TryGetValue(key, out var str) ? float.Parse(str) : @default;
if (dict.TryGetValue(key, out str))
return float.Parse(str);
else return @default;
} }
bool FetchBool(Dictionary<string, string> dict, string key, bool @default) private bool FetchBool(IDictionary<string, string> dict, string key, bool @default)
{ {
string str; return dict.TryGetValue(key, out var str) ? ParseBool(str) : @default;
if (dict.TryGetValue(key, out str))
return ParseBool(str);
else return @default;
} }
private bool ParseBool(string value)
bool ParseBool(string value)
{ {
if (value == "1") return true; if (value == "1") return true;
if (value == "0") return false; if (value == "0") return false;
value = value.ToLower(); value = value.ToLower();
if (value == "true") return true; if (value == "true") return true;
if (value == "false") return false; 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 public class RetroShaderPass : BaseFilter
{ {
RetroShaderChain RSC; private readonly RetroShaderChain _rsc;
RetroShaderPreset.ShaderPass SP; private readonly RetroShaderPreset.ShaderPass _sp;
int RSI; private readonly int _rsi;
Size OutputSize; 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; _rsc = rsc;
this.RSI = index; _rsi = index;
this.SP = RSC.Passes[index]; _sp = _rsc.Passes[index];
} }
public override void Initialize() public override void Initialize()
@ -265,59 +256,66 @@ namespace BizHawk.Client.EmuHawk.Filters
public override void SetInputFormat(string channel, SurfaceState state) public override void SetInputFormat(string channel, SurfaceState state)
{ {
Size insize = state.SurfaceFormat.Size; Size inSize = state.SurfaceFormat.Size;
if (SP.ScaleTypeX == RetroShaderPreset.ScaleType.Absolute) OutputSize.Width = (int)SP.Scale.X; 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.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.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); if (_sp.ScaleTypeY == RetroShaderPreset.ScaleType.Source) _outputSize.Height = (int)(inSize.Height * _sp.Scale.Y);
var outState = new SurfaceState(); DeclareOutput(new SurfaceState
outState.SurfaceFormat = new SurfaceFormat(OutputSize); {
outState.SurfaceDisposition = SurfaceDisposition.RenderTarget; SurfaceFormat = new SurfaceFormat(_outputSize),
DeclareOutput(outState); SurfaceDisposition = SurfaceDisposition.RenderTarget
});
} }
public override Size PresizeOutput(string channel, Size size) public override Size PresizeOutput(string channel, Size size)
{ {
OutputSize = size; _outputSize = size;
return size; return size;
} }
public override Size PresizeInput(string channel, Size insize) public override Size PresizeInput(string channel, Size inSize)
{ {
Size outsize = insize; Size outsize = inSize;
if (SP.ScaleTypeX == RetroShaderPreset.ScaleType.Absolute) outsize.Width = (int)SP.Scale.X; 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.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.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); if (_sp.ScaleTypeY == RetroShaderPreset.ScaleType.Source) outsize.Height = (int)(inSize.Height * _sp.Scale.Y);
return outsize; return outsize;
} }
public override void Run() public override void Run()
{ {
var shader = RSC.Shaders[RSI]; var shader = _rsc.Shaders[_rsi];
shader.Bind(); shader.Bind();
//apply all parameters to this shader.. even if it was meant for other shaders. kind of lame. // apply all parameters to this shader.. even if it was meant for other shaders. kind of lame.
if(Parameters != null) if(Parameters != null)
{
foreach (var kvp in Parameters) foreach (var kvp in Parameters)
{ {
if (kvp.Value is float) if (kvp.Value is float value)
shader.Pipeline[kvp.Key].Set((float)kvp.Value); {
shader.Pipeline[kvp.Key].Set(value);
}
} }
}
var input = InputTexture; var input = InputTexture;
if (SP.InputFilterLinear) if (_sp.InputFilterLinear)
{
InputTexture.SetFilterLinear(); InputTexture.SetFilterLinear();
}
else else
{
InputTexture.SetFilterNearest(); InputTexture.SetFilterNearest();
RSC.Shaders[RSI].Run(input, input.Size, OutputSize, InputTexture.IsUpsideDown); }
//maintain invariant.. i think. _rsc.Shaders[_rsi].Run(input, input.Size, _outputSize, InputTexture.IsUpsideDown);
// maintain invariant.. i think.
InputTexture.SetFilterNearest(); InputTexture.SetFilterNearest();
} }
} }
} }

View File

@ -9,12 +9,12 @@ namespace BizHawk.Client.EmuHawk.Filters
{ {
public SourceImage(Size size) 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() public override void Run()
{ {
@ -23,7 +23,7 @@ namespace BizHawk.Client.EmuHawk.Filters
public override void Initialize() 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) public override void SetInputFormat(string channel, SurfaceState format)
@ -35,7 +35,7 @@ namespace BizHawk.Client.EmuHawk.Filters
/// <summary> /// <summary>
/// transforms an input texture to an output render target (by rendering it) /// transforms an input texture to an output render target (by rendering it)
/// </summary> /// </summary>
class Render : BaseFilter public class Render : BaseFilter
{ {
public override void Initialize() public override void Initialize()
{ {
@ -57,7 +57,7 @@ namespace BizHawk.Client.EmuHawk.Filters
} }
} }
class Resolve : BaseFilter public class Resolve : BaseFilter
{ {
public override void Initialize() public override void Initialize()
{ {

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Drawing;
using System.Collections.Generic; using System.Collections.Generic;
using BizHawk.Bizware.BizwareGL; using BizHawk.Bizware.BizwareGL;
@ -7,59 +8,64 @@ namespace BizHawk.Client.EmuHawk
{ {
/// <summary> /// <summary>
/// Recycles a pair of temporary render targets, as long as the dimensions match. /// 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> /// </summary>
public class RenderTargetFrugalizer : IDisposable public class RenderTargetFrugalizer : IDisposable
{ {
public RenderTargetFrugalizer(IGL gl) public RenderTargetFrugalizer(IGL gl)
{ {
GL = gl; _gl = gl;
ResetList(); ResetList();
} }
public void Dispose() public void Dispose()
{ {
foreach (var ct in CurrentRenderTargets) foreach (var ct in _currentRenderTargets)
if (ct != null) {
ct.Dispose(); ct?.Dispose();
}
ResetList(); ResetList();
} }
void ResetList() private void ResetList()
{ {
CurrentRenderTargets = new List<RenderTarget>(); _currentRenderTargets = new List<RenderTarget> { null, null };
CurrentRenderTargets.Add(null);
CurrentRenderTargets.Add(null);
} }
IGL GL; private readonly IGL _gl;
List<RenderTarget> CurrentRenderTargets; 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) public RenderTarget Get(int width, int height)
{ {
//get the current entry //get the current entry
RenderTarget CurrentRenderTarget = CurrentRenderTargets[0]; RenderTarget currentRenderTarget = _currentRenderTargets[0];
//check if its rotten and needs recreating //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... // needs recreating. be sure to kill the old one...
if (CurrentRenderTarget != null) currentRenderTarget?.Dispose();
CurrentRenderTarget.Dispose(); // and make a new one
//and make a new one currentRenderTarget = _gl.CreateRenderTarget(width, height);
CurrentRenderTarget = GL.CreateRenderTarget(width, height);
} }
else else
{ {
//its good! nothing more to do // its good! nothing more to do
} }
//now shuffle the buffers // now shuffle the buffers
CurrentRenderTargets[0] = CurrentRenderTargets[1]; _currentRenderTargets[0] = _currentRenderTargets[1];
CurrentRenderTargets[1] = CurrentRenderTarget; _currentRenderTargets[1] = currentRenderTarget;
return CurrentRenderTarget; return currentRenderTarget;
} }
} }
} }

View File

@ -4,13 +4,13 @@ namespace BizHawk.Client.EmuHawk
{ {
/// <summary> /// <summary>
/// encapsulates thread-safe concept of pending/current display surfaces, reusing buffers where matching /// 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> /// </summary>
public class SwappableDisplaySurfaceSet public class SwappableDisplaySurfaceSet
{ {
private DisplaySurface Pending, Current; private DisplaySurface _pending, _current;
private bool IsPending; private bool _isPending;
private readonly Queue<DisplaySurface> ReleasedSurfaces = new Queue<DisplaySurface>(); private readonly Queue<DisplaySurface> _releasedSurfaces = new Queue<DisplaySurface>();
/// <summary> /// <summary>
/// retrieves a surface with the specified size, reusing an old buffer if available and clearing if requested /// 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; DisplaySurface trial;
lock (this) lock (this)
{ {
if (ReleasedSurfaces.Count == 0) break; if (_releasedSurfaces.Count == 0) break;
trial = ReleasedSurfaces.Dequeue(); trial = _releasedSurfaces.Dequeue();
} }
if (trial.Width == width && trial.Height == height) if (trial.Width == width && trial.Height == height)
{ {
if (needsClear) trial.Clear(); if (needsClear)
{
trial.Clear();
}
return trial; return trial;
} }
trial.Dispose(); trial.Dispose();
} }
return new DisplaySurface(width, height); return new DisplaySurface(width, height);
} }
@ -42,15 +48,15 @@ namespace BizHawk.Client.EmuHawk
{ {
lock (this) lock (this)
{ {
if (Pending != null) ReleasedSurfaces.Enqueue(Pending); if (_pending != null) _releasedSurfaces.Enqueue(_pending);
Pending = newPending; _pending = newPending;
IsPending = true; _isPending = true;
} }
} }
public void ReleaseSurface(DisplaySurface surface) public void ReleaseSurface(DisplaySurface surface)
{ {
lock (this) ReleasedSurfaces.Enqueue(surface); lock (this) _releasedSurfaces.Enqueue(surface);
} }
/// <summary> /// <summary>
@ -60,15 +66,16 @@ namespace BizHawk.Client.EmuHawk
{ {
lock (this) lock (this)
{ {
if (IsPending) if (_isPending)
{ {
if (Current != null) ReleasedSurfaces.Enqueue(Current); if (_current != null) _releasedSurfaces.Enqueue(_current);
Current = Pending; _current = _pending;
Pending = null; _pending = null;
IsPending = false; _isPending = false;
} }
} }
return Current;
return _current;
} }
} }
} }

View File

@ -12,13 +12,13 @@ namespace BizHawk.Client.EmuHawk
{ {
public TextureFrugalizer(IGL gl) public TextureFrugalizer(IGL gl)
{ {
GL = gl; _gl = gl;
ResetList(); ResetList();
} }
public void Dispose() public void Dispose()
{ {
foreach (var ct in CurrentTextures) foreach (var ct in _currentTextures)
{ {
ct?.Dispose(); ct?.Dispose();
} }
@ -26,13 +26,13 @@ namespace BizHawk.Client.EmuHawk
ResetList(); ResetList();
} }
void ResetList() private void ResetList()
{ {
CurrentTextures = new List<Texture2d> { null, null }; _currentTextures = new List<Texture2d> { null, null };
} }
private readonly IGL GL; private readonly IGL _gl;
private List<Texture2d> CurrentTextures; private List<Texture2d> _currentTextures;
public Texture2d Get(DisplaySurface ds) public Texture2d Get(DisplaySurface ds)
{ {
@ -42,7 +42,7 @@ namespace BizHawk.Client.EmuHawk
public Texture2d Get(BitmapBuffer bb) public Texture2d Get(BitmapBuffer bb)
{ {
//get the current entry //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) // 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. // 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... //needs recreating. be sure to kill the old one...
currentTexture?.Dispose(); currentTexture?.Dispose();
//and make a new one //and make a new one
currentTexture = GL.LoadTexture(bb); currentTexture = _gl.LoadTexture(bb);
} }
else else
{ {
//its good! just load in the data //its good! just load in the data
GL.LoadTextureData(currentTexture, bb); _gl.LoadTextureData(currentTexture, bb);
} }
//now shuffle the buffers //now shuffle the buffers
CurrentTextures[0] = CurrentTextures[1]; _currentTextures[0] = _currentTextures[1];
CurrentTextures[1] = currentTexture; _currentTextures[1] = currentTexture;
//deterministic state, i guess //deterministic state, i guess
currentTexture.SetFilterNearest(); currentTexture.SetFilterNearest();

View File

@ -202,7 +202,7 @@ namespace BizHawk.Client.EmuHawk
using (var stream = File.OpenRead(choice)) using (var stream = File.OpenRead(choice))
{ {
var cgp = new BizHawk.Client.EmuHawk.Filters.RetroShaderPreset(stream); 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"); MessageBox.Show("Specified CGP contains references to .glsl files. This is illegal. Use .cg");
return; 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/=Justifier/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=keepalives/@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/=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/=Libretro/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Lightgun/@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> <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/=preload/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Prereqs/@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/=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/=Quantizer/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=quantizing/@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> <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/=Underruns/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Uninitialize/@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/=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/=unpause/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=unpaused/@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> <s:Boolean x:Key="/Default/UserDictionary/Words/=Unpausing/@EntryIndexedValue">True</s:Boolean>