Remove Bizware Vector2/3/4 and Matrix4 in favor of System.Numerics's implementation

Many many cleanups in BizwareGL and DisplayManager and GDI+ code
This commit is contained in:
CasualPokePlayer 2023-08-06 03:14:27 -07:00
parent bb96825c60
commit 59fcdb04cd
32 changed files with 1468 additions and 1470 deletions

View File

@ -5,7 +5,7 @@ namespace BizHawk.Bizware.BizwareGL
/// </summary>
public class Art
{
//bleh, didnt mean to have this here, but I need it now
// bleh, didnt mean to have this here, but I need it now
public Art(Texture2d tex)
{
BaseTexture = tex;

View File

@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
namespace BizHawk.Bizware.BizwareGL
{
@ -18,7 +19,7 @@ namespace BizHawk.Bizware.BizwareGL
public void Dispose()
{
//todo
// todo
}
/// <summary>
@ -27,7 +28,11 @@ namespace BizHawk.Bizware.BizwareGL
public void Open()
{
AssertIsOpen(false);
if (IsClosedForever) throw new InvalidOperationException($"{nameof(ArtManager)} instance has been closed forever!");
if (IsClosedForever)
{
throw new InvalidOperationException($"{nameof(ArtManager)} instance has been closed forever!");
}
IsOpened = true;
}
@ -52,7 +57,7 @@ namespace BizHawk.Bizware.BizwareGL
{
AssertIsOpen(true);
Art a = new Art(this);
var a = new Art(this);
ArtLooseTextureAssociation.Add((a, tex));
ManagedArts.Add(a);
@ -72,16 +77,16 @@ namespace BizHawk.Bizware.BizwareGL
// first, cleanup old stuff
foreach (var tex in ManagedTextures)
{
tex.Dispose();
}
ManagedTextures.Clear();
// prepare input for atlas process and perform atlas
// add 2 extra pixels for padding on all sides
var atlasItems = new List<TexAtlas.RectItem>();
foreach (var kvp in ArtLooseTextureAssociation)
{
atlasItems.Add(new TexAtlas.RectItem(kvp.Bitmap.Width + 2, kvp.Bitmap.Height + 2, kvp));
}
var atlasItems = ArtLooseTextureAssociation
.Select(kvp => new TexAtlas.RectItem(kvp.Bitmap.Width + 2, kvp.Bitmap.Height + 2, kvp))
.ToList();
var results = TexAtlas.PackAtlas(atlasItems);
// this isn't supported yet:
@ -89,27 +94,28 @@ namespace BizHawk.Bizware.BizwareGL
throw new InvalidOperationException("Art files too big for atlas");
// prepare the output buffer
BitmapBuffer bmpResult = new BitmapBuffer(results[0].Size);
var bmpResult = new BitmapBuffer(results[0].Size);
//for each item, copy it into the output buffer and set the tex parameters on them
for (int i = 0; i < atlasItems.Count; i++)
for (var i = 0; i < atlasItems.Count; i++)
{
var item = results[0].Items[i];
var (art, bitmap) = ((Art, BitmapBuffer)) item.Item;
int w = bitmap.Width;
int h = bitmap.Height;
int dx = item.X + 1;
int dy = item.Y + 1;
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++)
var w = bitmap.Width;
var h = bitmap.Height;
var dx = item.X + 1;
var dy = item.Y + 1;
for (var y = 0; y < h; y++)
{
for (var x = 0; x < w; x++)
{
int pixel = bitmap.GetPixel(x, y);
var pixel = bitmap.GetPixel(x, y);
bmpResult.SetPixel(x+dx,y+dy,pixel);
}
}
var myDestBitmap = bmpResult;
float myDestWidth = (float)myDestBitmap.Width;
float myDestHeight = (float)myDestBitmap.Height;
var myDestWidth = (float)bmpResult.Width;
var myDestHeight = (float)bmpResult.Height;
art.u0 = dx / myDestWidth;
art.v0 = dy / myDestHeight;
@ -138,7 +144,13 @@ namespace BizHawk.Bizware.BizwareGL
/// <summary>
/// Throws an exception if the instance is not open
/// </summary>
private void AssertIsOpen(bool state) { if (IsOpened != state) throw new InvalidOperationException($"{nameof(ArtManager)} instance is not open!"); }
private void AssertIsOpen(bool state)
{
if (IsOpened != state)
{
throw new InvalidOperationException($"{nameof(ArtManager)} instance is not open!");
}
}
public IGL Owner { get; }
@ -153,11 +165,11 @@ namespace BizHawk.Bizware.BizwareGL
/// <summary>
/// Physical texture resources, which exist after this ArtManager has been closed
/// </summary>
private readonly List<Texture2d> ManagedTextures = new List<Texture2d>();
private readonly List<Texture2d> ManagedTextures = new();
/// <summary>
/// All the Arts managed by this instance
/// </summary>
private readonly List<Art> ManagedArts = new List<Art>();
private readonly List<Art> ManagedArts = new();
}
}

View File

@ -1,15 +1,14 @@
//TODO - introduce Trim for ArtManager
//TODO - add a small buffer reuse manager.. small images can be stored in larger buffers which we happen to have held. use a timer to wait to free it until some time has passed
// TODO - introduce Trim for ArtManager
// TODO - add a small buffer reuse manager.. small images can be stored in larger buffers which we happen to have held. use a timer to wait to free it until some time has passed
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.IO;
using sd = System.Drawing;
namespace BizHawk.Bizware.BizwareGL
{
/// <summary>
@ -28,7 +27,7 @@ namespace BizHawk.Bizware.BizwareGL
/// </summary>
public bool HasAlpha = true;
public Size Size => new Size(Width, Height);
public Size Size => new(Width, Height);
private readonly Bitmap WrappedBitmap;
private GCHandle CurrLockHandle;
@ -48,7 +47,7 @@ namespace BizHawk.Bizware.BizwareGL
}
CurrLockHandle = GCHandle.Alloc(Pixels, GCHandleType.Pinned);
CurrLock = new BitmapData
CurrLock = new()
{
Height = Height,
Width = Width,
@ -82,16 +81,15 @@ namespace BizHawk.Bizware.BizwareGL
public void YFlip()
{
//TODO - could be faster
// TODO - could be faster
var bmpdata = LockBits();
int[] newPixels = new int[Width * Height];
int todo = Width * Height;
int* s = (int*)bmpdata.Scan0.ToPointer();
var newPixels = new int[Width * Height];
var s = (int*)bmpdata.Scan0.ToPointer();
fixed (int* d = newPixels)
{
for (int y = 0, si = 0, di = (Height - 1) * Width; y < Height; y++)
{
for (int x = 0; x < Width; x++, si++, di++)
for (var x = 0; x < Width; x++, si++, di++)
{
d[di] = s[si];
}
@ -110,16 +108,15 @@ namespace BizHawk.Bizware.BizwareGL
public void Normalize(bool yflip)
{
var bmpdata = LockBits();
int[] newPixels = new int[Width * Height];
int todo = Width*Height;
int* s = (int*)bmpdata.Scan0.ToPointer();
var newPixels = new int[Width * Height];
var s = (int*)bmpdata.Scan0.ToPointer();
fixed (int* d = newPixels)
{
if (yflip)
{
for (int y = 0, si = 0, di = (Height - 1) * Width; y < Height; y++)
{
for (int x = 0; x < Width; x++, si++, di++)
for (var x = 0; x < Width; x++, si++, di++)
{
d[di] = s[si] | unchecked((int)0xFF000000);
}
@ -130,7 +127,7 @@ namespace BizHawk.Bizware.BizwareGL
{
for (int y = 0, i=0; y < Height; y++)
{
for (int x = 0; x < Width; x++, i++)
for (var x = 0; x < Width; x++, i++)
{
d[i] = s[i] | unchecked((int)0xFF000000);
}
@ -143,11 +140,19 @@ namespace BizHawk.Bizware.BizwareGL
Pixels = newPixels;
}
public int GetPixel(int x, int y) { return Pixels[Width * y + x]; }
public void SetPixel(int x, int y, int value) { Pixels[Width * y + x] = value; }
public int GetPixel(int x, int y)
{
return Pixels[Width * y + x];
}
public void SetPixel(int x, int y, int value)
{
Pixels[Width * y + x] = value;
}
public Color GetPixelAsColor(int x, int y)
{
int c = Pixels[Width * y + x];
var c = Pixels[Width * y + x];
return Color.FromArgb(c);
}
@ -157,11 +162,15 @@ namespace BizHawk.Bizware.BizwareGL
public void Alphafy(int tcol)
{
for (int y = 0, idx = 0; y < Height; y++)
for (int x = 0; x < Width; x++, idx++)
{
for (var x = 0; x < Width; x++, idx++)
{
if (Pixels[idx] == tcol)
{
Pixels[idx] = 0;
}
}
}
}
/// <summary>
@ -177,15 +186,16 @@ namespace BizHawk.Bizware.BizwareGL
/// </summary>
public BitmapBuffer Trim(out int xofs, out int yofs)
{
int minx = int.MaxValue;
int maxx = int.MinValue;
int miny = int.MaxValue;
int maxy = int.MinValue;
for (int y = 0; y < Height; y++)
for (int x = 0; x < Width; x++)
var minx = int.MaxValue;
var maxx = int.MinValue;
var miny = int.MaxValue;
var maxy = int.MinValue;
for (var y = 0; y < Height; y++)
{
for (var x = 0; x < Width; x++)
{
int pixel = GetPixel(x, y);
int a = (pixel >> 24) & 0xFF;
var pixel = GetPixel(x, y);
var a = (pixel >> 24) & 0xFF;
if (a != 0)
{
minx = Math.Min(minx, x);
@ -194,21 +204,24 @@ namespace BizHawk.Bizware.BizwareGL
maxy = Math.Max(maxy, y);
}
}
}
if (minx == int.MaxValue || maxx == int.MinValue || miny == int.MaxValue || minx == int.MinValue)
{
xofs = yofs = 0;
return new BitmapBuffer(0, 0);
return new(0, 0);
}
int w = maxx - minx + 1;
int h = maxy - miny + 1;
BitmapBuffer bbRet = new BitmapBuffer(w, h);
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++)
var w = maxx - minx + 1;
var h = maxy - miny + 1;
var bbRet = new BitmapBuffer(w, h);
for (var y = 0; y < h; y++)
{
for (var x = 0; x < w; x++)
{
bbRet.SetPixel(x, y, GetPixel(x + minx, y + miny));
}
}
xofs = minx;
yofs = miny;
@ -220,15 +233,18 @@ namespace BizHawk.Bizware.BizwareGL
/// </summary>
public void Pad()
{
int widthRound = NextHigher(Width);
int heightRound = NextHigher(Height);
var widthRound = NextHigher(Width);
var heightRound = NextHigher(Height);
if (widthRound == Width && heightRound == Height) return;
int[] NewPixels = new int[heightRound * widthRound];
var NewPixels = new int[heightRound * widthRound];
for (int y = 0, sptr = 0, dptr = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
for (var x = 0; x < Width; x++)
{
NewPixels[dptr++] = Pixels[sptr++];
}
dptr += (widthRound - Width);
}
@ -265,10 +281,12 @@ namespace BizHawk.Bizware.BizwareGL
Height = bitmap.Height;
WrappedBitmap = bitmap;
}
else LoadInternal(null, bitmap, options);
else
{
LoadInternal(null, bitmap, options);
}
}
/// <summary>
/// Initializes a BitmapBuffer --WRAPPED-- from the supplied parameters, which should definitely have a stride==width and be in the standard color format
/// </summary>
@ -290,82 +308,89 @@ namespace BizHawk.Bizware.BizwareGL
private void LoadInternal(Stream stream, Bitmap bitmap, BitmapLoadOptions options)
{
bool cleanup = options.CleanupAlpha0;
bool needsPad = true;
var cleanup = options.CleanupAlpha0;
var needsPad = true;
var colorKey24bpp = options.ColorKey24bpp;
using (Bitmap loadedBmp = bitmap == null ? new Bitmap(stream) : null) //sneaky!
using (var loadedBmp = bitmap == null ? new Bitmap(stream) : null) // sneaky!
{
Bitmap bmp = loadedBmp;
if (bmp == null)
bmp = bitmap;
var bmp = loadedBmp ?? bitmap;
//if we have a 24bpp image and a colorkey callback, the callback can choose a colorkey color and we'll use that
// if we have a 24bpp image and a colorkey callback, the callback can choose a colorkey color and we'll use that
if (bmp.PixelFormat == PixelFormat.Format24bppRgb && colorKey24bpp != null)
{
int colorKey = colorKey24bpp(bmp);
int w = bmp.Width;
int h = bmp.Height;
var colorKey = colorKey24bpp(bmp);
var w = bmp.Width;
var h = bmp.Height;
InitSize(w, h);
BitmapData bmpdata = bmp.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
Color[] palette = bmp.Palette.Entries;
int* ptr = (int*)bmpdata.Scan0.ToPointer();
int stride = bmpdata.Stride;
var bmpdata = bmp.LockBits(new(0, 0, w, h), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
var ptr = (int*)bmpdata.Scan0.ToPointer();
fixed (int* pPtr = &Pixels[0])
{
for (int idx = 0, y = 0; y < h; y++)
for (int x = 0; x < w; x++)
{
for (var x = 0; x < w; x++)
{
int srcPixel = ptr[idx];
var srcPixel = ptr[idx];
if (srcPixel == colorKey)
{
srcPixel = 0;
}
pPtr[idx++] = srcPixel;
}
}
}
bmp.UnlockBits(bmpdata);
}
if (bmp.PixelFormat == PixelFormat.Format8bppIndexed || bmp.PixelFormat == PixelFormat.Format4bppIndexed)
if (bmp.PixelFormat is PixelFormat.Format8bppIndexed or PixelFormat.Format4bppIndexed)
{
int w = bmp.Width;
int h = bmp.Height;
var w = bmp.Width;
var h = bmp.Height;
InitSize(w, h);
BitmapData bmpdata = bmp.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
Color[] palette = bmp.Palette.Entries;
byte* ptr = (byte*)bmpdata.Scan0.ToPointer();
var bmpdata = bmp.LockBits(new(0, 0, w, h), ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
var palette = bmp.Palette.Entries;
var ptr = (byte*)bmpdata.Scan0.ToPointer();
fixed (int* pPtr = &Pixels[0])
{
for (int idx = 0, y = 0; y < h; y++)
for (int x = 0; x < w; x++)
{
for (var x = 0; x < w; x++)
{
int srcPixel = ptr[idx];
if (srcPixel != 0)
{
int color = palette[srcPixel].ToArgb();
var color = palette[srcPixel].ToArgb();
//make transparent pixels turn into black to avoid filtering issues and other annoying issues with stray junk in transparent pixels.
//(yes, we can have palette entries with transparency in them (PNGs support this, annoyingly))
// make transparent pixels turn into black to avoid filtering issues and other annoying issues with stray junk in transparent pixels.
// (yes, we can have palette entries with transparency in them (PNGs support this, annoyingly))
if (cleanup)
{
if ((color & 0xFF000000) == 0) color = 0;
if ((color & 0xFF000000) == 0)
{
color = 0;
}
pPtr[idx] = color;
}
}
idx++;
}
}
}
bmp.UnlockBits(bmpdata);
}
else
{
//dump the supplied bitmap into our pixels array
int width = bmp.Width;
int height = bmp.Height;
// dump the supplied bitmap into our pixels array
var width = bmp.Width;
var height = bmp.Height;
InitSize(width, height);
BitmapData bmpdata = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
int* ptr = (int*)bmpdata.Scan0;
int stride = bmpdata.Stride / 4;
var bmpdata = bmp.LockBits(new(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
var ptr = (int*)bmpdata.Scan0;
var stride = bmpdata.Stride / 4;
LoadFrom(width, stride, height, (byte*)ptr, options);
bmp.UnlockBits(bmpdata);
needsPad = false;
@ -373,7 +398,9 @@ namespace BizHawk.Bizware.BizwareGL
}
if (needsPad && options.Pad)
{
Pad();
}
}
@ -382,29 +409,37 @@ namespace BizHawk.Bizware.BizwareGL
/// </summary>
public void LoadFrom(int width, int stride, int height, byte* data, BitmapLoadOptions options)
{
bool cleanup = options.CleanupAlpha0;
var cleanup = options.CleanupAlpha0;
Width = width;
Height = height;
Pixels = new int[width * height];
fixed (int* pPtr = &Pixels[0])
{
for (int idx = 0, y = 0; y < Height; y++)
for (int x = 0; x < Width; x++)
{
for (var x = 0; x < Width; x++)
{
int src = y * stride + x;
int srcVal = ((int*)data)[src];
//make transparent pixels turn into black to avoid filtering issues and other annoying issues with stray junk in transparent pixels
var src = y * stride + x;
var srcVal = ((int*)data)[src];
// make transparent pixels turn into black to avoid filtering issues and other annoying issues with stray junk in transparent pixels
if (cleanup)
{
if ((srcVal & 0xFF000000) == 0) srcVal = 0;
if ((srcVal & 0xFF000000) == 0)
{
srcVal = 0;
}
pPtr[idx++] = srcVal;
}
}
}
}
if (options.Pad)
{
Pad();
}
}
/// <summary>
@ -412,10 +447,10 @@ namespace BizHawk.Bizware.BizwareGL
/// </summary>
public static int PremultiplyColor(int srcVal)
{
int b = (srcVal >> 0) & 0xFF;
int g = (srcVal >> 8) & 0xFF;
int r = (srcVal >> 16) & 0xFF;
int a = (srcVal >> 24) & 0xFF;
var b = (srcVal >> 0) & 0xFF;
var g = (srcVal >> 8) & 0xFF;
var r = (srcVal >> 16) & 0xFF;
var a = (srcVal >> 24) & 0xFF;
r = (r * a) >> 8;
g = (g * a) >> 8;
b = (b * a) >> 8;
@ -434,7 +469,9 @@ namespace BizHawk.Bizware.BizwareGL
/// <summary>
/// Makes a new bitmap buffer, in ??? state
/// </summary>
public BitmapBuffer() { }
public BitmapBuffer()
{
}
/// <summary>
/// initializes an empty BitmapBuffer, cleared to all 0
@ -449,24 +486,24 @@ namespace BizHawk.Bizware.BizwareGL
/// </summary>
public void ClearWithoutAlloc()
{
//http://techmikael.blogspot.com/2009/12/filling-array-with-default-value.html
//this guy says its faster
// http://techmikael.blogspot.com/2009/12/filling-array-with-default-value.html
// this guy says its faster
int size = Width * Height;
byte fillValue = 0;
ulong fillValueLong = 0;
var size = Width * Height;
const byte fillValue = 0;
const ulong fillValueLong = 0;
fixed (int* ptr = &Pixels[0])
{
ulong* dest = (ulong*)ptr;
int length = size;
var dest = (ulong*)ptr;
var length = size;
while (length >= 8)
{
*dest = fillValueLong;
dest++;
length -= 8;
}
byte* bDest = (byte*)dest;
var bDest = (byte*)dest;
for (byte i = 0; i < length; i++)
{
*bDest = fillValue;
@ -488,9 +525,12 @@ namespace BizHawk.Bizware.BizwareGL
private static int NextHigher(int k)
{
k--;
for (int i = 1; i < 32; i <<= 1)
for (var i = 1; i < 32; i <<= 1)
{
k = k | k >> i;
int candidate = k + 1;
}
var candidate = k + 1;
return candidate;
}
@ -501,11 +541,12 @@ namespace BizHawk.Bizware.BizwareGL
public Bitmap ToSysdrawingBitmap()
{
if (WrappedBitmap != null)
{
return (Bitmap)WrappedBitmap.Clone();
var pf = PixelFormat.Format32bppArgb;
if (!HasAlpha)
pf = PixelFormat.Format24bppRgb;
Bitmap bmp = new Bitmap(Width, Height, pf);
}
var pf = HasAlpha ? PixelFormat.Format32bppArgb : PixelFormat.Format24bppRgb;
var bmp = new Bitmap(Width, Height, pf);
ToSysdrawingBitmap(bmp);
return bmp;
}
@ -519,30 +560,33 @@ namespace BizHawk.Bizware.BizwareGL
if (WrappedBitmap != null)
{
using var g = Graphics.FromImage(bmp);
g.CompositingMode = sd.Drawing2D.CompositingMode.SourceCopy;
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
g.CompositingMode = CompositingMode.SourceCopy;
g.CompositingQuality = CompositingQuality.HighSpeed;
g.DrawImageUnscaled(WrappedBitmap, 0, 0);
return;
}
//note: we lock it as 32bpp even if the bitmap is 24bpp so we can write to it more conveniently.
var bmpdata = bmp.LockBits(new Rectangle(0, 0, Width, Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
var bmpdata = bmp.LockBits(new(0, 0, Width, Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
if(bmpdata.Stride == bmpdata.Width*4)
if (bmpdata.Stride == bmpdata.Width * 4)
{
Marshal.Copy(Pixels, 0, bmpdata.Scan0, Width * Height);
}
else if (bmp.Width != 0 && bmp.Height != 0)
{
int* ptr = (int*)bmpdata.Scan0.ToPointer();
int stride = bmpdata.Stride;
var ptr = (int*)bmpdata.Scan0.ToPointer();
fixed (int* pPtr = &Pixels[0])
{
for (int idx = 0, y = 0; y < Height; y++)
for (int x = 0; x < Width; x++)
{
for (var x = 0; x < Width; x++)
{
int srcPixel = pPtr[idx];
var srcPixel = pPtr[idx];
ptr[idx] = srcPixel;
idx++;
}
}
}
}

View File

@ -1,12 +1,14 @@
using System;
using System.Drawing;
namespace BizHawk.Bizware.BizwareGL
{
public class GDIPTextureWrapper : IDisposable
{
public System.Drawing.Bitmap SDBitmap;
public Bitmap SDBitmap;
public TextureMinFilter MinFilter = TextureMinFilter.Nearest;
public TextureMagFilter MagFilter = TextureMagFilter.Nearest;
public void Dispose()
{
if (SDBitmap != null)

View File

@ -1,9 +1,10 @@
//this is full of bugs probably, related to state from old rendering sessions being all messed up. its only barely good enough to work at all
// this is full of bugs probably, related to state from old rendering sessions being all messed up. its only barely good enough to work at all
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using sd = System.Drawing;
using System.Numerics;
namespace BizHawk.Bizware.BizwareGL
{
@ -14,7 +15,13 @@ namespace BizHawk.Bizware.BizwareGL
public GDIPlusGuiRenderer(IGL_GdiPlus gl)
=> Gdi = gl;
private readonly Vector4[] CornerColors = { new(1.0f, 1.0f, 1.0f, 1.0f), new(1.0f, 1.0f, 1.0f, 1.0f), new(1.0f, 1.0f, 1.0f, 1.0f), new(1.0f, 1.0f, 1.0f, 1.0f) };
private readonly Vector4[] CornerColors =
{
new(1.0f, 1.0f, 1.0f, 1.0f),
new(1.0f, 1.0f, 1.0f, 1.0f),
new(1.0f, 1.0f, 1.0f, 1.0f),
new(1.0f, 1.0f, 1.0f, 1.0f)
};
public void SetCornerColor(int which, Vector4 color)
{
@ -25,9 +32,16 @@ namespace BizHawk.Bizware.BizwareGL
public void SetCornerColors(Vector4[] colors)
{
Flush(); //don't really need to flush with current implementation. we might as well roll modulate color into it too.
if (colors.Length != 4) throw new ArgumentException("array must be size 4", nameof(colors));
for (int i = 0; i < 4; i++)
if (colors.Length != 4)
{
throw new ArgumentException("array must be size 4", nameof(colors));
}
for (var i = 0; i < 4; i++)
{
CornerColors[i] = colors[i];
}
}
public void Dispose()
@ -45,30 +59,31 @@ namespace BizHawk.Bizware.BizwareGL
public void SetModulateColorWhite()
{
SetModulateColor(sd.Color.White);
SetModulateColor(Color.White);
}
private ImageAttributes CurrentImageAttributes;
public void SetModulateColor(sd.Color color)
public void SetModulateColor(Color color)
{
//white is really no color at all
if (color.ToArgb() == sd.Color.White.ToArgb())
// white is really no color at all
if (color.ToArgb() == Color.White.ToArgb())
{
CurrentImageAttributes.ClearColorMatrix(ColorAdjustType.Bitmap);
return;
}
float r = color.R / 255.0f;
float g = color.G / 255.0f;
float b = color.B / 255.0f;
float a = color.A / 255.0f;
var r = color.R / 255.0f;
var g = color.G / 255.0f;
var b = color.B / 255.0f;
var a = color.A / 255.0f;
float[][] colorMatrixElements =
{
new float[] { r, 0, 0, 0, 0 },
new float[] { 0, g, 0, 0, 0 },
new float[] { 0, 0, b, 0, 0 },
new float[] { 0, 0, 0, a, 0 },
new[] { r, 0, 0, 0, 0 },
new[] { 0, g, 0, 0, 0 },
new[] { 0, 0, b, 0, 0 },
new[] { 0, 0, 0, a, 0 },
new float[] { 0, 0, 0, 0, 1 },
};
@ -76,15 +91,15 @@ namespace BizHawk.Bizware.BizwareGL
CurrentImageAttributes.SetColorMatrix(colorMatrix,ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
}
private sd.Color CurrentModulateColor = sd.Color.White;
private IBlendState CurrentBlendState;
public void SetBlendState(IBlendState rsBlend)
{
CurrentBlendState = rsBlend;
}
private MatrixStack _Projection, _Modelview;
public MatrixStack Projection
{
get => _Projection;
@ -94,6 +109,7 @@ namespace BizHawk.Bizware.BizwareGL
_Projection.IsDirty = true;
}
}
public MatrixStack Modelview
{
get => _Modelview;
@ -104,8 +120,10 @@ namespace BizHawk.Bizware.BizwareGL
}
}
public void Begin(sd.Size size) { Begin(size.Width, size.Height); }
public void Begin(Size size)
{
Begin(size.Width, size.Height);
}
public void Begin(int width, int height)
{
@ -117,31 +135,29 @@ namespace BizHawk.Bizware.BizwareGL
Modelview = Owner.CreateGuiViewMatrix(width, height);
}
public void Begin()
{
//uhhmmm I want to throw an exception if its already active, but its annoying.
// uhhmmm I want to throw an exception if its already active, but its annoying.
IsActive = true;
CurrentImageAttributes = new ImageAttributes();
CurrentImageAttributes = new();
}
public void Flush()
{
//no batching, nothing to do here yet
// no batching, nothing to do here yet
}
/// <exception cref="InvalidOperationException"><see cref="IsActive"/> is <see langword="false"/></exception>
public void End()
{
if (!IsActive)
throw new InvalidOperationException($"{nameof(GDIPlusGuiRenderer)} is not active!");
IsActive = false;
if (CurrentImageAttributes != null)
{
CurrentImageAttributes.Dispose();
CurrentImageAttributes = null;
throw new InvalidOperationException($"{nameof(GDIPlusGuiRenderer)} is not active!");
}
IsActive = false;
CurrentImageAttributes?.Dispose();
CurrentImageAttributes = null;
}
public void RectFill(float x, float y, float w, float h)
@ -150,24 +166,26 @@ namespace BizHawk.Bizware.BizwareGL
public void DrawSubrect(Texture2d tex, float x, float y, float w, float h, float u0, float v0, float u1, float v1)
{
var tw = tex.Opaque as GDIPTextureWrapper;
var tw = (GDIPTextureWrapper)tex.Opaque;
var g = Gdi.GetCurrentGraphics();
PrepDraw(g, tex);
SetupMatrix(g);
float x0 = u0 * tex.Width;
float y0 = v0 * tex.Height;
float x1 = u1 * tex.Width;
float y1 = v1 * tex.Height;
var x0 = u0 * tex.Width;
var y0 = v0 * tex.Height;
var x1 = u1 * tex.Width;
var y1 = v1 * tex.Height;
sd.PointF[] destPoints = {
new sd.PointF(x,y),
new sd.PointF(x+w,y),
new sd.PointF(x,y+h),
PointF[] destPoints =
{
new(x, y),
new(x+w, y),
new(x, y+h),
};
g.DrawImage(tw.SDBitmap, destPoints, new sd.RectangleF(x0, y0, x1 - x0, y1 - y0), sd.GraphicsUnit.Pixel, CurrentImageAttributes);
g.Transform = new sd.Drawing2D.Matrix(); //.Reset() doesnt work ? ?
g.DrawImage(tw.SDBitmap, destPoints, new(x0, y0, x1 - x0, y1 - y0), GraphicsUnit.Pixel, CurrentImageAttributes);
g.Transform = new(); // .Reset() doesnt work?
}
@ -184,47 +202,49 @@ namespace BizHawk.Bizware.BizwareGL
DrawInternal(art, x, y, width, height);
}
private void PrepDraw(sd.Graphics g, Texture2d tex)
private void PrepDraw(Graphics g, Texture2d tex)
{
var tw = tex.Opaque as GDIPTextureWrapper;
//TODO - we can support bicubic for the final presentation..
var tw = (GDIPTextureWrapper)tex.Opaque;
// TODO - we can support bicubic for the final presentation...
if ((int)tw.MagFilter != (int)tw.MinFilter)
{
throw new InvalidOperationException($"{nameof(tw)}.{nameof(tw.MagFilter)} != {nameof(tw)}.{nameof(tw.MinFilter)}");
if (tw.MagFilter == TextureMagFilter.Linear)
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Bilinear;
if (tw.MagFilter == TextureMagFilter.Nearest)
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
}
//---------
g.InterpolationMode = tw.MagFilter switch
{
TextureMagFilter.Linear => InterpolationMode.Bilinear,
TextureMagFilter.Nearest => InterpolationMode.NearestNeighbor,
_ => g.InterpolationMode
};
if (CurrentBlendState == Gdi.BlendNormal)
{
g.CompositingMode = sd.Drawing2D.CompositingMode.SourceOver;
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.Default; //?
g.CompositingMode = CompositingMode.SourceOver;
g.CompositingQuality = CompositingQuality.Default; // ?
//CurrentImageAttributes.ClearColorMatrix(ColorAdjustType.Bitmap);
// CurrentImageAttributes.ClearColorMatrix(ColorAdjustType.Bitmap);
}
else
//if(CurrentBlendState == Gdi.BlendNoneCopy)
//if(CurrentBlendState == Gdi.BlendNoneOpaque)
// if (CurrentBlendState == Gdi.BlendNoneCopy)
// if (CurrentBlendState == Gdi.BlendNoneOpaque)
{
g.CompositingMode = sd.Drawing2D.CompositingMode.SourceCopy;
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
g.CompositingMode = CompositingMode.SourceCopy;
g.CompositingQuality = CompositingQuality.HighSpeed;
//WARNING : DO NOT USE COLOR MATRIX TO WIPE THE ALPHA
//ITS SOOOOOOOOOOOOOOOOOOOOOOOOOOOO SLOW
//instead, we added kind of hacky support for 24bpp images
// WARNING : DO NOT USE COLOR MATRIX TO WIPE THE ALPHA
// ITS SOOOOOOOOOOOOOOOOOOOOOOOOOOOO SLOW
// instead, we added kind of hacky support for 24bpp images
}
}
private void SetupMatrix(sd.Graphics g)
private void SetupMatrix(Graphics g)
{
//projection is always identity, so who cares i guess
//Matrix4 mat = Projection.Top * Modelview.Top;
Matrix4 mat = Modelview.Top;
g.Transform = new sd.Drawing2D.Matrix(mat.Row0.X, mat.Row0.Y, mat.Row1.X, mat.Row1.Y, mat.Row3.X, mat.Row3.Y);
// projection is always identity, so who cares i guess
// var mat = Projection.Top * Modelview.Top;
var mat = Modelview.Top;
g.Transform = new(mat.M11, mat.M12, mat.M21, mat.M22, mat.M41, mat.M42);
}
private void DrawInternal(Art art, float x, float y, float w, float h)
@ -244,26 +264,27 @@ namespace BizHawk.Bizware.BizwareGL
SetupMatrix(g);
sd.PointF[] destPoints = {
new sd.PointF(x,y),
new sd.PointF(x+w,y),
new sd.PointF(x,y+h),
PointF[] destPoints =
{
new(x, y),
new(x+w, y),
new(x, y+h),
};
float sx = tex.Width * u0;
float sy = tex.Height * v0;
float sx2 = tex.Width * u1;
float sy2 = tex.Height * v1;
float sw = sx2 - sx;
float sh = sy2 - sy;
var sx = tex.Width * u0;
var sy = tex.Height * v0;
var sx2 = tex.Width * u1;
var sy2 = tex.Height * v1;
var sw = sx2 - sx;
var sh = sy2 - sy;
var tw = tex.Opaque as GDIPTextureWrapper;
g.PixelOffsetMode = sd.Drawing2D.PixelOffsetMode.Half;
g.DrawImage(tw.SDBitmap, destPoints, new sd.RectangleF(sx, sy, sw, sh), sd.GraphicsUnit.Pixel, CurrentImageAttributes);
g.Transform = new sd.Drawing2D.Matrix(); //.Reset() doesn't work ? ?
var tw = (GDIPTextureWrapper)tex.Opaque;
g.PixelOffsetMode = PixelOffsetMode.Half;
g.DrawImage(tw.SDBitmap, destPoints, new(sx, sy, sw, sh), GraphicsUnit.Pixel, CurrentImageAttributes);
g.Transform = new(); // .Reset() doesn't work ? ?
}
private void DrawInternal(Art art, float x, float y, float w, float h, bool fx, bool fy)
private static void DrawInternal(Art art, float x, float y, float w, float h, bool fx, bool fy)
{
}

View File

@ -1,4 +1,6 @@
namespace BizHawk.Bizware.BizwareGL
{
public interface IBlendState {}
public interface IBlendState
{
}
}

View File

@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Drawing;
using System.Numerics;
namespace BizHawk.Bizware.BizwareGL
{
@ -55,12 +56,12 @@ namespace BizHawk.Bizware.BizwareGL
/// <summary>
/// Sets a uniform value
/// </summary>
void SetPipelineUniformMatrix(PipelineUniform uniform, Matrix4 mat, bool transpose);
void SetPipelineUniformMatrix(PipelineUniform uniform, Matrix4x4 mat, bool transpose);
/// <summary>
/// Sets a uniform value
/// </summary>
void SetPipelineUniformMatrix(PipelineUniform uniform, ref Matrix4 mat, bool transpose);
void SetPipelineUniformMatrix(PipelineUniform uniform, ref Matrix4x4 mat, bool transpose);
/// <summary>
/// sets a uniform value
@ -219,24 +220,24 @@ namespace BizHawk.Bizware.BizwareGL
/// <summary>
/// generates a proper 2d othographic projection for the given destination size, suitable for use in a GUI
/// </summary>
Matrix4 CreateGuiProjectionMatrix(int w, int h);
Matrix4x4 CreateGuiProjectionMatrix(int w, int h);
/// <summary>
/// generates a proper 2d othographic projection for the given destination size, suitable for use in a GUI
/// </summary>
Matrix4 CreateGuiProjectionMatrix(Size dims);
Matrix4x4 CreateGuiProjectionMatrix(Size dims);
/// <summary>
/// generates a proper view transform for a standard 2d ortho projection, including half-pixel jitter if necessary and
/// re-establishing of a normal 2d graphics top-left origin. suitable for use in a GUI
/// </summary>
Matrix4 CreateGuiViewMatrix(int w, int h, bool autoflip = true);
Matrix4x4 CreateGuiViewMatrix(int w, int h, bool autoflip = true);
/// <summary>
/// generates a proper view transform for a standard 2d ortho projection, including half-pixel jitter if necessary and
/// re-establishing of a normal 2d graphics top-left origin. suitable for use in a GUI
/// </summary>
Matrix4 CreateGuiViewMatrix(Size dims, bool autoflip = true);
Matrix4x4 CreateGuiViewMatrix(Size dims, bool autoflip = true);
/// <summary>
/// Creates a render target. Only includes a color buffer. Pixel format control TBD

View File

@ -1,9 +1,8 @@
using System;
using System.IO;
using System.Drawing;
using sd = System.Drawing;
using sdi = System.Drawing.Imaging;
using System.Drawing.Imaging;
using System.Numerics;
//TODO - maybe a layer to cache Graphics parameters (notably, filtering) ?
namespace BizHawk.Bizware.BizwareGL
@ -12,22 +11,25 @@ namespace BizHawk.Bizware.BizwareGL
{
public EDispMethod DispMethodEnum => EDispMethod.GdiPlus;
#if false
// rendering state
private RenderTarget _currRenderTarget;
#endif
private readonly Func<IGL_GdiPlus, IGraphicsControl> _createGLControlWrapper;
public IGL_GdiPlus(Func<IGL_GdiPlus, IGraphicsControl> createGLControlWrapper)
=> _createGLControlWrapper = createGLControlWrapper;
void IDisposable.Dispose()
public void Dispose()
{
MyBufferedGraphicsContext.Dispose();
}
public void Clear(ClearBufferMask mask)
{
var g = GetCurrentGraphics();
if((mask & ClearBufferMask.ColorBufferBit) != 0)
if ((mask & ClearBufferMask.ColorBufferBit) != 0)
{
g.Clear(_currentClearColor);
}
@ -41,31 +43,39 @@ namespace BizHawk.Bizware.BizwareGL
return null;
}
private sd.Color _currentClearColor = Color.Transparent;
public void SetClearColor(sd.Color color)
private Color _currentClearColor = Color.Transparent;
public void SetClearColor(Color color)
{
_currentClearColor = color;
}
public void BindArrayData(IntPtr pData) {}
public void BindArrayData(IntPtr pData)
{
}
public void FreeTexture(Texture2d tex)
{
var tw = tex.Opaque as GDIPTextureWrapper;
var tw = (GDIPTextureWrapper)tex.Opaque;
tw.Dispose();
}
public Shader CreateFragmentShader(string source, string entry, bool required) => null;
public Shader CreateVertexShader(string source, string entry, bool required) => null;
public Shader CreateFragmentShader(string source, string entry, bool required)
=> null;
public Shader CreateVertexShader(string source, string entry, bool required)
=> null;
public void SetBlendState(IBlendState rsBlend)
{
//TODO for real
// TODO for real
}
private class MyBlendState : IBlendState { }
private class EmptyBlendState : IBlendState
{
}
private static readonly MyBlendState _rsBlendNoneVerbatim = new MyBlendState(), _rsBlendNoneOpaque = new MyBlendState(), _rsBlendNormal = new MyBlendState();
private static readonly EmptyBlendState _rsBlendNoneVerbatim = new(), _rsBlendNoneOpaque = new(), _rsBlendNormal = new();
public IBlendState BlendNoneCopy => _rsBlendNoneVerbatim;
public IBlendState BlendNoneOpaque => _rsBlendNoneOpaque;
@ -76,9 +86,12 @@ namespace BizHawk.Bizware.BizwareGL
return null;
}
public void FreePipeline(Pipeline pipeline) {}
public void FreePipeline(Pipeline pipeline)
{
}
public VertexLayout CreateVertexLayout() => new VertexLayout(this, new IntPtr(0));
public VertexLayout CreateVertexLayout()
=> new(this, null);
public void SetTextureWrapMode(Texture2d tex, bool clamp)
{
@ -102,11 +115,11 @@ namespace BizHawk.Bizware.BizwareGL
}
public void SetPipelineUniformMatrix(PipelineUniform uniform, Matrix4 mat, bool transpose)
public void SetPipelineUniformMatrix(PipelineUniform uniform, Matrix4x4 mat, bool transpose)
{
}
public void SetPipelineUniformMatrix(PipelineUniform uniform, ref Matrix4 mat, bool transpose)
public void SetPipelineUniformMatrix(PipelineUniform uniform, ref Matrix4x4 mat, bool transpose)
{
}
@ -131,97 +144,98 @@ namespace BizHawk.Bizware.BizwareGL
}
public void SetMinFilter(Texture2d texture, Bizware.BizwareGL.TextureMinFilter minFilter)
public void SetMinFilter(Texture2d texture, TextureMinFilter minFilter)
=> ((GDIPTextureWrapper) texture.Opaque).MinFilter = minFilter;
public void SetMagFilter(Texture2d texture, Bizware.BizwareGL.TextureMagFilter magFilter)
public void SetMagFilter(Texture2d texture, TextureMagFilter magFilter)
=> ((GDIPTextureWrapper) texture.Opaque).MagFilter = magFilter;
public Texture2d LoadTexture(Bitmap bitmap)
{
var sdBitmap = (Bitmap)bitmap.Clone();
GDIPTextureWrapper tw = new GDIPTextureWrapper { SDBitmap = sdBitmap };
return new Texture2d(this, tw, bitmap.Width, bitmap.Height);
var tw = new GDIPTextureWrapper { SDBitmap = sdBitmap };
return new(this, tw, bitmap.Width, bitmap.Height);
}
public Texture2d LoadTexture(Stream stream)
{
using var bmp = new BitmapBuffer(stream, new BitmapLoadOptions());
return (this as IGL).LoadTexture(bmp);
using var bmp = new BitmapBuffer(stream, new());
return LoadTexture(bmp);
}
public Texture2d CreateTexture(int width, int height) => null;
public Texture2d CreateTexture(int width, int height)
=> null;
public Texture2d WrapGLTexture2d(IntPtr glTexId, int width, int height)
{
// TODO - need to rip the texture data. we had code for that somewhere...
// only used for OpenGL
return null;
}
public void LoadTextureData(Texture2d tex, BitmapBuffer bmp)
{
var tw = tex.Opaque as GDIPTextureWrapper;
var tw = (GDIPTextureWrapper)tex.Opaque;
bmp.ToSysdrawingBitmap(tw.SDBitmap);
}
public Texture2d LoadTexture(BitmapBuffer bmp)
{
// definitely needed (by TextureFrugalizer at least)
var sdBitmap = bmp.ToSysdrawingBitmap();
var tw = new GDIPTextureWrapper { SDBitmap = sdBitmap };
return new Texture2d(this, tw, bmp.Width, bmp.Height);
return new(this, tw, bmp.Width, bmp.Height);
}
public BitmapBuffer ResolveTexture2d(Texture2d tex)
{
var tw = tex.Opaque as GDIPTextureWrapper;
var tw = (GDIPTextureWrapper)tex.Opaque;
var blow = new BitmapLoadOptions
{
AllowWrap = false // must be an independent resource
};
var bb = new BitmapBuffer(tw.SDBitmap, blow);
return bb;
}
public Texture2d LoadTexture(string path)
{
//todo
//using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
// return (this as IGL).LoadTexture(fs);
return null;
using var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
return LoadTexture(fs);
}
public Matrix4 CreateGuiProjectionMatrix(int w, int h)
public Matrix4x4 CreateGuiProjectionMatrix(int w, int h)
{
return CreateGuiProjectionMatrix(new Size(w, h));
return CreateGuiProjectionMatrix(new(w, h));
}
public Matrix4 CreateGuiViewMatrix(int w, int h, bool autoFlip)
public Matrix4x4 CreateGuiViewMatrix(int w, int h, bool autoFlip)
{
return CreateGuiViewMatrix(new Size(w, h), autoFlip);
return CreateGuiViewMatrix(new(w, h), autoFlip);
}
public Matrix4 CreateGuiProjectionMatrix(Size dims)
public Matrix4x4 CreateGuiProjectionMatrix(Size dims)
{
//see CreateGuiViewMatrix for more
return Matrix4.Identity;
// see CreateGuiViewMatrix for more
return Matrix4x4.Identity;
}
public Matrix4 CreateGuiViewMatrix(Size dims, bool autoFlip)
public Matrix4x4 CreateGuiViewMatrix(Size dims, bool autoFlip)
{
//on account of gdi+ working internally with a default view exactly like we want, we don't need to setup a new one here
//furthermore, we _cant_, without inverting the GuiView and GuiProjection before drawing, to completely undo it
//this might be feasible, but its kind of slow and annoying and worse, seemingly numerically unstable
//if (autoFlip && _CurrRenderTarget != null)
//{
// Matrix4 ret = Matrix4.Identity;
// ret.M22 = -1;
// ret.M42 = dims.Height;
// return ret;
//}
//else
return Matrix4.Identity;
// on account of gdi+ working internally with a default view exactly like we want, we don't need to setup a new one here
// furthermore, we _cant_, without inverting the GuiView and GuiProjection before drawing, to completely undo it
// this might be feasible, but its kind of slow and annoying and worse, seemingly numerically unstable
#if false
if (autoFlip && _currRenderTarget != null)
{
Matrix4 ret = Matrix4.Identity;
ret.M22 = -1;
ret.M42 = dims.Height;
return ret;
}
#endif
return Matrix4x4.Identity;
}
public void SetViewport(int x, int y, int width, int height)
@ -242,15 +256,11 @@ namespace BizHawk.Bizware.BizwareGL
CurrentControl = control;
}
public void EndControl(IGraphicsControl control)
public void EndControl()
{
CurrentControl = null;
}
public void SwapControl(IGraphicsControl control)
{
}
public void BeginScene()
{
}
@ -265,17 +275,15 @@ namespace BizHawk.Bizware.BizwareGL
public IGraphicsControl Internal_CreateGraphicsControl()
{
var ret = _createGLControlWrapper(this);
// create a render target for this control
var rtw = new RenderTargetWrapper(() => MyBufferedGraphicsContext, ret);
ret.RenderTargetWrapper = rtw;
return ret;
}
public void FreeRenderTarget(RenderTarget rt)
{
var rtw = rt.Opaque as RenderTargetWrapper;
var rtw = (RenderTargetWrapper)rt.Opaque;
rtw.Dispose();
}
@ -283,7 +291,7 @@ namespace BizHawk.Bizware.BizwareGL
{
var tw = new GDIPTextureWrapper
{
SDBitmap = new Bitmap(w, h, sdi.PixelFormat.Format32bppArgb)
SDBitmap = new(w, h, PixelFormat.Format32bppArgb)
};
var tex = new Texture2d(this, tw, w, h);
@ -295,46 +303,55 @@ namespace BizHawk.Bizware.BizwareGL
public void BindRenderTarget(RenderTarget rt)
{
if (_CurrentOffscreenGraphics != null)
if (_currOffscreenGraphics != null)
{
_CurrentOffscreenGraphics.Dispose();
_CurrentOffscreenGraphics = null;
_currOffscreenGraphics.Dispose();
_currOffscreenGraphics = null;
}
#if false
_currRenderTarget = rt;
if (CurrentRenderTargetWrapper != null)
{
if (CurrentRenderTargetWrapper == CurrentControl.RenderTargetWrapper)
{
//don't do anything til swapbuffers
// don't do anything til swapbuffers
}
else
{
//CurrentRenderTargetWrapper.MyBufferedGraphics.Render();
// CurrentRenderTargetWrapper.MyBufferedGraphics.Render();
}
}
#endif
if (rt == null)
{
//null means to use the default RT for the current control
// null means to use the default RT for the current control
CurrentRenderTargetWrapper = CurrentControl.RenderTargetWrapper;
}
else
{
var tw = rt.Texture2d.Opaque as GDIPTextureWrapper;
CurrentRenderTargetWrapper = rt.Opaque as RenderTargetWrapper;
_CurrentOffscreenGraphics = Graphics.FromImage(tw.SDBitmap);
//if (CurrentRenderTargetWrapper.MyBufferedGraphics == null)
// CurrentRenderTargetWrapper.CreateGraphics();
var tw = (GDIPTextureWrapper)rt.Texture2d.Opaque;
CurrentRenderTargetWrapper = (RenderTargetWrapper)rt.Opaque;
_currOffscreenGraphics = Graphics.FromImage(tw.SDBitmap);
#if false
if (CurrentRenderTargetWrapper.MyBufferedGraphics == null)
{
CurrentRenderTargetWrapper.CreateGraphics();
}
#endif
}
}
private Graphics _CurrentOffscreenGraphics;
private Graphics _currOffscreenGraphics;
public Graphics GetCurrentGraphics()
{
if (_CurrentOffscreenGraphics != null)
return _CurrentOffscreenGraphics;
if (_currOffscreenGraphics != null)
{
return _currOffscreenGraphics;
}
var rtw = CurrentRenderTargetWrapper;
return rtw.MyBufferedGraphics.Graphics;
}
@ -343,7 +360,5 @@ namespace BizHawk.Bizware.BizwareGL
public RenderTargetWrapper CurrentRenderTargetWrapper;
public readonly BufferedGraphicsContext MyBufferedGraphicsContext = new();
} //class IGL_GdiPlus
}
}

View File

@ -1,4 +1,6 @@
using System;
using System.Drawing;
using System.Numerics;
namespace BizHawk.Bizware.BizwareGL
{
@ -9,7 +11,7 @@ namespace BizHawk.Bizware.BizwareGL
/// </summary>
void Begin();
void Begin(System.Drawing.Size size);
void Begin(Size size);
/// <summary>
/// begin rendering, initializing viewport and projections to the given dimensions
@ -51,7 +53,6 @@ namespace BizHawk.Bizware.BizwareGL
/// </summary>
void Draw(Texture2d tex, float x, float y);
/// <summary>
/// draws the specified Art resource with the given flip flags
/// </summary>
@ -74,10 +75,15 @@ namespace BizHawk.Bizware.BizwareGL
void Flush();
bool IsActive { get; }
MatrixStack Modelview { get; set; }
IGL Owner { get; }
MatrixStack Projection { get; set; }
void RectFill(float x, float y, float w, float h);
void SetBlendState(IBlendState rsBlend);
/// <summary>
@ -96,7 +102,9 @@ namespace BizHawk.Bizware.BizwareGL
/// Restores the pipeline to the default
/// </summary>
void SetDefaultPipeline();
void SetModulateColor(System.Drawing.Color color);
void SetModulateColor(Color color);
void SetModulateColorWhite();
/// <summary>

View File

@ -1,17 +1,18 @@
using System.Drawing;
using System.Collections.Generic;
using System.Numerics;
namespace BizHawk.Bizware.BizwareGL
{
//note: the sense of these matrices, as well as pre- and post- multiplying may be all mixed up.
//conceptually here is how we should be working, in the example of spinning a sprite in place
//1. sprite starts with top left at origin
//2. translate half size, to center sprite at origin
//3. rotate around Z
//4. translate to position in world
//this class is designed to make that work, that way. the takeaways:
//* Use the scale, translate, rotate methods in the order given above
//* Use PostMultiplyMatrix to apply more work to a prior matrix (in the manner described above) since I am calling this all post-work
// note: the sense of these matrices, as well as pre- and post- multiplying may be all mixed up.
// conceptually here is how we should be working, in the example of spinning a sprite in place
// 1. sprite starts with top left at origin
// 2. translate half size, to center sprite at origin
// 3. rotate around Z
// 4. translate to position in world
// this class is designed to make that work, that way. the takeaways:
// * Use the scale, translate, rotate methods in the order given above
// * Use PostMultiplyMatrix to apply more work to a prior matrix (in the manner described above) since I am calling this all post-work
public class MatrixStack
{
private const float DEG_TO_RAD_FACTOR = (float) System.Math.PI / 180.0f;
@ -24,19 +25,22 @@ namespace BizHawk.Bizware.BizwareGL
IsDirty = false;
}
public static implicit operator Matrix4(MatrixStack ms) { return ms.Top; }
public static implicit operator MatrixStack(Matrix4 m) { return new MatrixStack(m); }
public static implicit operator Matrix4x4(MatrixStack ms) { return ms.Top; }
public static implicit operator MatrixStack(Matrix4x4 m) { return new(m); }
public MatrixStack(Matrix4 matrix) { LoadMatrix(matrix); }
public MatrixStack(Matrix4x4 matrix)
{
LoadMatrix(matrix);
}
public bool IsDirty;
private readonly Stack<Matrix4> stack = new Stack<Matrix4>();
private readonly Stack<Matrix4x4> stack = new();
/// <summary>
/// This is made public for performance reasons, to avoid lame copies of the matrix when necessary. Don't mess it up!
/// </summary>
public Matrix4 Top;
public Matrix4x4 Top;
/// <summary>
/// Resets the matrix stack to an empty identity matrix stack
@ -51,41 +55,143 @@ namespace BizHawk.Bizware.BizwareGL
/// <summary>
/// Clears the matrix stack and loads the specified value
/// </summary>
public void Clear(Matrix4 value)
public void Clear(Matrix4x4 value)
{
stack.Clear();
Top = value;
IsDirty = true;
}
public void LoadMatrix(Matrix4 value) { Top = value; IsDirty = true; }
public void LoadMatrix(Matrix4x4 value)
{
Top = value;
IsDirty = true;
}
public void LoadIdentity() { Top = Matrix4.Identity; IsDirty = true; }
public void LoadIdentity()
{
Top = Matrix4x4.Identity;
IsDirty = true;
}
public void Pop() { Top = stack.Pop(); IsDirty = true; }
public void Push() { stack.Push(Top); IsDirty = true; }
public void Pop()
{
Top = stack.Pop();
IsDirty = true;
}
public void RotateAxis(Vector3 axisRotation, float angle) { PostMultiplyMatrix(Matrix4.CreateFromAxisAngle(in axisRotation, angle)); IsDirty = true; }
public void Push()
{
stack.Push(Top);
IsDirty = true;
}
public void Scale(Vector3 scale) { PostMultiplyMatrix(Matrix4.CreateScale(in scale)); IsDirty = true; }
public void Scale(Vector2 scale) { PostMultiplyMatrix(Matrix4.CreateScale(scale.X, scale.Y, 1)); IsDirty = true; }
public void Scale(float x, float y, float z) { PostMultiplyMatrix(Matrix4.CreateScale(x, y, z)); IsDirty = true; }
public void Scale(float ratio) { Scale(ratio, ratio, ratio); IsDirty = true; }
public void Scale(float x, float y) { Scale(x, y, 1); IsDirty = true; }
public void RotateAxis(Vector3 axisRotation, float angle)
{
PostMultiplyMatrix(Matrix4x4.CreateFromAxisAngle(axisRotation, angle));
IsDirty = true;
}
public void RotateAxis(float x, float y, float z, float degrees) { PostMultiplyMatrix(Matrix4.CreateFromAxisAngle(new Vector3(x, y, z), DegreesToRadians(degrees))); IsDirty = true; }
public void RotateY(float degrees) { PostMultiplyMatrix(Matrix4.CreateRotationY(DegreesToRadians(degrees))); IsDirty = true; }
public void RotateX(float degrees) { PostMultiplyMatrix(Matrix4.CreateRotationX(DegreesToRadians(degrees))); IsDirty = true; }
public void RotateZ(float degrees) { PostMultiplyMatrix(Matrix4.CreateRotationZ(DegreesToRadians(degrees))); IsDirty = true; }
public void Scale(Vector3 scale)
{
PostMultiplyMatrix(Matrix4x4.CreateScale(scale));
IsDirty = true;
}
public void Translate(Vector2 v) { Translate(v.X, v.Y, 0); IsDirty = true; }
public void Translate(Vector3 trans) { PostMultiplyMatrix(Matrix4.CreateTranslation(in trans)); IsDirty = true; }
public void Translate(float x, float y, float z) { PostMultiplyMatrix(Matrix4.CreateTranslation(x, y, z)); IsDirty = true; }
public void Translate(float x, float y) { Translate(x, y, 0); IsDirty = true; }
public void Translate(Point pt) { Translate(pt.X, pt.Y, 0); IsDirty = true; }
public void Scale(Vector2 scale)
{
PostMultiplyMatrix(Matrix4x4.CreateScale(scale.X, scale.Y, 1));
IsDirty = true;
}
public void PostMultiplyMatrix(MatrixStack ms) { PostMultiplyMatrix(ms.Top); IsDirty = true; }
public void PostMultiplyMatrix(Matrix4 value) { Top = Top * value; IsDirty = true; }
public void PreMultiplyMatrix(Matrix4 value) { Top = value * Top; IsDirty = true; }
public void Scale(float x, float y, float z)
{
PostMultiplyMatrix(Matrix4x4.CreateScale(x, y, z));
IsDirty = true;
}
public void Scale(float ratio)
{
Scale(ratio, ratio, ratio);
IsDirty = true;
}
public void Scale(float x, float y)
{
Scale(x, y, 1);
IsDirty = true;
}
public void RotateAxis(float x, float y, float z, float degrees)
{
PostMultiplyMatrix(Matrix4x4.CreateFromAxisAngle(new(x, y, z), DegreesToRadians(degrees)));
IsDirty = true;
}
public void RotateY(float degrees)
{
PostMultiplyMatrix(Matrix4x4.CreateRotationY(DegreesToRadians(degrees)));
IsDirty = true;
}
public void RotateX(float degrees)
{
PostMultiplyMatrix(Matrix4x4.CreateRotationX(DegreesToRadians(degrees)));
IsDirty = true;
}
public void RotateZ(float degrees)
{
PostMultiplyMatrix(Matrix4x4.CreateRotationZ(DegreesToRadians(degrees)));
IsDirty = true;
}
public void Translate(Vector2 v)
{
Translate(v.X, v.Y, 0);
IsDirty = true;
}
public void Translate(Vector3 trans)
{
PostMultiplyMatrix(Matrix4x4.CreateTranslation(trans));
IsDirty = true;
}
public void Translate(float x, float y, float z)
{
PostMultiplyMatrix(Matrix4x4.CreateTranslation(x, y, z));
IsDirty = true;
}
public void Translate(float x, float y)
{
Translate(x, y, 0);
IsDirty = true;
}
public void Translate(Point pt)
{
Translate(pt.X, pt.Y, 0);
IsDirty = true;
}
public void PostMultiplyMatrix(MatrixStack ms)
{
PostMultiplyMatrix(ms.Top);
IsDirty = true;
}
public void PostMultiplyMatrix(Matrix4x4 value)
{
Top *= value;
IsDirty = true;
}
public void PreMultiplyMatrix(Matrix4x4 value)
{
Top = value * Top;
IsDirty = true;
}
}
}

View File

@ -10,9 +10,9 @@ namespace BizHawk.Bizware.BizwareGL
/// </summary>
public class Pipeline : IDisposable
{
public string Memo;
public readonly string Memo;
public Pipeline(IGL owner, object opaque, bool available, VertexLayout vertexLayout, IEnumerable<UniformInfo> uniforms, string memo)
public Pipeline(IGL owner, object opaque, bool available, VertexLayout vertexLayout, IReadOnlyList<UniformInfo> uniforms, string memo)
{
Memo = memo;
Owner = owner;
@ -24,10 +24,10 @@ namespace BizHawk.Bizware.BizwareGL
if(!Available)
return;
UniformsDictionary = new SpecialWorkingDictionary(this);
UniformsDictionary = new(this);
foreach (var ui in uniforms)
{
UniformsDictionary[ui.Name] = new PipelineUniform(this);
UniformsDictionary[ui.Name] = new(this);
}
foreach (var ui in uniforms)
@ -40,9 +40,9 @@ namespace BizHawk.Bizware.BizwareGL
/// Allows us to create PipelineUniforms on the fly, in case a non-existing one has been requested.
/// Shader compilers will optimize out unused uniforms, and we wont have a record of it in the uniforms population loop
/// </summary>
private class SpecialWorkingDictionary : Dictionary<string, PipelineUniform>
private class UniformWorkingDictionary : Dictionary<string, PipelineUniform>
{
public SpecialWorkingDictionary(Pipeline owner)
public UniformWorkingDictionary(Pipeline owner)
{
Owner = owner;
}
@ -59,7 +59,7 @@ namespace BizHawk.Bizware.BizwareGL
}
}
private readonly SpecialWorkingDictionary UniformsDictionary;
private readonly UniformWorkingDictionary UniformsDictionary;
private IDictionary<string, PipelineUniform> Uniforms => UniformsDictionary;
public IEnumerable<PipelineUniform> GetUniforms() => Uniforms.Values;

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Numerics;
namespace BizHawk.Bizware.BizwareGL
{
@ -12,17 +13,16 @@ namespace BizHawk.Bizware.BizwareGL
internal PipelineUniform(Pipeline owner)
{
Owner = owner;
//Opaque = info.Opaque;
//SamplerIndex = info.SamplerIndex;
}
internal void AddUniformInfo(UniformInfo ui)
{
_UniformInfos.Add(ui);
_uniformInfos.Add(ui);
}
public IEnumerable<UniformInfo> UniformInfos => _UniformInfos;
private readonly List<UniformInfo> _UniformInfos = new List<UniformInfo>();
public IEnumerable<UniformInfo> UniformInfos => _uniformInfos;
private readonly List<UniformInfo> _uniformInfos = new();
/// <returns>the first and only <see cref="UniformInfo"/></returns>
/// <exception cref="InvalidOperationException">more than one <see cref="UniformInfo"/> exists</exception>
@ -30,14 +30,18 @@ namespace BizHawk.Bizware.BizwareGL
{
get
{
if (_UniformInfos.Count != 1) throw new InvalidOperationException();
return _UniformInfos[0];
if (_uniformInfos.Count != 1)
{
throw new InvalidOperationException();
}
return _uniformInfos[0];
}
}
public Pipeline Owner { get; }
public void Set(Matrix4 mat, bool transpose = false)
public void Set(Matrix4x4 mat, bool transpose = false)
{
Owner?.Owner.SetPipelineUniformMatrix(this, mat, transpose);
}
@ -62,7 +66,7 @@ namespace BizHawk.Bizware.BizwareGL
Owner?.Owner.SetPipelineUniform(this, vecs);
}
public void Set(ref Matrix4 mat, bool transpose = false)
public void Set(ref Matrix4x4 mat, bool transpose = false)
{
Owner?.Owner.SetPipelineUniformMatrix(this, ref mat, transpose);
}

View File

@ -1,9 +1,12 @@
using System;
using System.Drawing;
#if false
using System.Drawing.Drawing2D;
#endif
namespace BizHawk.Bizware.BizwareGL
{
public class RenderTargetWrapper
public class RenderTargetWrapper : IDisposable
{
public RenderTargetWrapper(
Func<BufferedGraphicsContext> getBufferedGraphicsContext,
@ -15,6 +18,7 @@ namespace BizHawk.Bizware.BizwareGL
public void Dispose()
{
MyBufferedGraphics?.Dispose();
}
private readonly Func<BufferedGraphicsContext> _getBufferedGraphicsContext;
@ -31,7 +35,7 @@ namespace BizHawk.Bizware.BizwareGL
public BufferedGraphics MyBufferedGraphics;
public Graphics refGraphics; //?? hacky?
public Graphics refGraphics; // ?? hacky?
public void CreateGraphics()
{
@ -43,19 +47,21 @@ namespace BizHawk.Bizware.BizwareGL
}
else
{
var tw = Target.Texture2d.Opaque as GDIPTextureWrapper;
var tw = (GDIPTextureWrapper)Target.Texture2d.Opaque;
r = Target.Texture2d.Rectangle;
refGraphics = Graphics.FromImage(tw.SDBitmap);
}
MyBufferedGraphics?.Dispose();
MyBufferedGraphics = _getBufferedGraphicsContext().Allocate(refGraphics, r);
// MyBufferedGraphics.Graphics.PixelOffsetMode = PixelOffsetMode.HighSpeed;
#if false
MyBufferedGraphics.Graphics.PixelOffsetMode = PixelOffsetMode.HighSpeed;
//not sure about this stuff...
//it will wreck alpha blending, for one thing
// MyBufferedGraphics.Graphics.CompositingMode = CompositingMode.SourceCopy;
// MyBufferedGraphics.Graphics.CompositingQuality = CompositingQuality.HighSpeed;
// not sure about this stuff...
// it will wreck alpha blending, for one thing
MyBufferedGraphics.Graphics.CompositingMode = CompositingMode.SourceCopy;
MyBufferedGraphics.Graphics.CompositingQuality = CompositingQuality.HighSpeed;
#endif
}
}
}

View File

@ -1,5 +1,6 @@
using System;
using System.Drawing;
using System.Numerics;
namespace BizHawk.Bizware.BizwareGL
{
@ -20,9 +21,8 @@ namespace BizHawk.Bizware.BizwareGL
VertexLayout.DefineVertexAttribute("tex", 2, 2, VertexAttribPointerType.Float, AttribUsage.Texcoord0, false, 40, 32);
VertexLayout.Close();
string defines = "";
string vsSource = $"#define VERTEX\r\n{defines}{source}";
string psSource = $"#define FRAGMENT\r\n{defines}{source}";
var vsSource = $"#define VERTEX\r\n{source}";
var psSource = $"#define FRAGMENT\r\n{source}";
var vs = owner.CreateVertexShader(vsSource, "main_vertex", debug);
var ps = owner.CreateFragmentShader(psSource, "main_fragment", debug);
Pipeline = Owner.CreatePipeline(VertexLayout, vs, ps, debug, "retro");
@ -39,7 +39,7 @@ namespace BizHawk.Bizware.BizwareGL
sampler0 = Pipeline.TryGetUniform("s_p");
if (sampler0 == null)
{
//sampler wasn't named correctly. this can happen on some retroarch shaders
// sampler wasn't named correctly. this can happen on some retroarch shaders
foreach (var u in Pipeline.GetUniforms())
{
if (u.Sole.IsSampler && u.Sole.SamplerIndex == 0)
@ -50,11 +50,8 @@ namespace BizHawk.Bizware.BizwareGL
}
}
//if a sampler isn't available, we can't do much, although this does interfere with debugging (shaders just returning colors will malfunction)
if (sampler0 == null)
return;
Available = true;
// if a sampler isn't available, we can't do much, although this does interfere with debugging (shaders just returning colors will malfunction)
Available = sampler0 != null;
}
public bool Available { get; }
@ -69,16 +66,13 @@ namespace BizHawk.Bizware.BizwareGL
public void Bind()
{
//lame...
// lame...
Owner.BindPipeline(Pipeline);
}
public unsafe void Run(Texture2d tex, Size InputSize, Size OutputSize, bool flip)
{
flip = false;
//test
//ack! make sure to set the pipeline before setting uniforms
// ack! make sure to set the pipeline before setting uniforms
Bind();
Pipeline["IN.video_size"].Set(new Vector2(InputSize.Width, InputSize.Height));
@ -87,10 +81,9 @@ namespace BizHawk.Bizware.BizwareGL
Pipeline["IN.frame_count"].Set(1); //todo
Pipeline["IN.frame_direction"].Set(1); //todo
var Projection = Owner.CreateGuiProjectionMatrix(OutputSize);
var Modelview = Owner.CreateGuiViewMatrix(OutputSize);
var mat = Matrix4.Transpose(Modelview * Projection);
var mat = Matrix4x4.Transpose(Modelview * Projection);
Pipeline["modelViewProj"].Set(mat, true);
Owner.SetTextureWrapMode(tex, true);
@ -98,16 +91,26 @@ namespace BizHawk.Bizware.BizwareGL
sampler0.Set(tex);
Owner.SetViewport(OutputSize);
float time = DateTime.Now.Second + (float)DateTime.Now.Millisecond / 1000;
var time = DateTime.Now.Second + (float)DateTime.Now.Millisecond / 1000;
Pipeline["Time"].Set(time);
int w = OutputSize.Width;
int h = OutputSize.Height;
float v0,v1;
if (flip) { v0 = 1; v1 = 0; }
else { v0 = 0; v1 = 1; }
float* pData = stackalloc float[10*4];
int i=0;
var w = OutputSize.Width;
var h = OutputSize.Height;
float v0, v1;
if (flip)
{
v0 = 1;
v1 = 0;
}
else
{
v0 = 0;
v1 = 1;
}
var pData = stackalloc float[10 * 4];
var i = 0;
pData[i++] = 0; pData[i++] = 0; pData[i++] = 0; pData[i++] = 1; //topleft vert
pData[i++] = 0; pData[i++] = 0; pData[i++] = 0; pData[i++] = 0; //useless color
pData[i++] = 0; pData[i++] = v0;
@ -119,7 +122,7 @@ namespace BizHawk.Bizware.BizwareGL
pData[i++] = 0; pData[i++] = v1;
pData[i++] = w; pData[i++] = h; pData[i++] = 0; pData[i++] = 1; //bottomright vert
pData[i++] = 0; pData[i++] = 0; pData[i++] = 0; pData[i++] = 0; //useless color
pData[i++] = 1; pData[i++] = v1;
pData[i++] = 1; pData[i] = v1;
Owner.SetBlendState(Owner.BlendNoneCopy);
Owner.BindArrayData(new(pData));
@ -130,6 +133,6 @@ namespace BizHawk.Bizware.BizwareGL
public IGL Owner { get; }
private readonly VertexLayout VertexLayout;
public Pipeline Pipeline;
public readonly Pipeline Pipeline;
}
}

View File

@ -3,9 +3,10 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using Cyotek.Drawing.BitmapFont;
using sd=System.Drawing;
namespace BizHawk.Bizware.BizwareGL
{
@ -18,7 +19,7 @@ namespace BizHawk.Bizware.BizwareGL
FontInfo.LoadXml(xml);
// load textures
for(int i=0;i<FontInfo.Pages.Length;i++)
for (var i=0; i<FontInfo.Pages.Length; i++)
{
TexturePages.Add(owner.LoadTexture(textures[i]));
}
@ -27,25 +28,30 @@ namespace BizHawk.Bizware.BizwareGL
public void Dispose()
{
foreach (var tex in TexturePages)
{
tex.Dispose();
}
TexturePages = null;
}
public sd.SizeF Measure(string str)
public SizeF Measure(string str)
{
float x = 0;
float y = FontInfo.LineHeight;
float ox = x;
int len = str.Length;
var ox = x;
var len = str.Length;
for (int i = 0; i < len; i++)
for (var i = 0; i < len; i++)
{
var c = str[i];
if (c == '\r')
{
if (i != len - 1 && str[i + 1] == '\n')
{
i++;
}
}
if (c == '\r')
@ -56,33 +62,42 @@ namespace BizHawk.Bizware.BizwareGL
if (c == '\n')
{
if (x > ox)
{
ox = x;
}
x = 0;
y += FontInfo.LineHeight;
continue;
}
var bfc = FontInfo[c];
x += bfc.XAdvance;
}
return new sd.SizeF(Math.Max(x, ox), y);
return new(Math.Max(x, ox), y);
}
public void RenderString(IGuiRenderer renderer, float x, float y, string str)
{
float ox = x;
int len = str.Length;
if (Owner != renderer.Owner)
{
throw new InvalidOperationException("Owner mismatch!");
}
for (int i = 0; i < len; i++)
var ox = x;
var len = str.Length;
for (var i = 0; i < len; i++)
{
var c = str[i];
if (c == '\r')
{
if (i != len - 1 && str[i + 1] == '\n')
{
i++;
}
}
if (c == '\r')
@ -90,7 +105,7 @@ namespace BizHawk.Bizware.BizwareGL
c = '\n';
}
if(c == '\n')
if (c == '\n')
{
x = ox;
y += FontInfo.LineHeight;
@ -98,19 +113,19 @@ namespace BizHawk.Bizware.BizwareGL
}
var bfc = FontInfo[c];
// calculate texcoords (we shouldve already had this cached, but im speedcoding now)
Texture2d tex = TexturePages[bfc.TexturePage];
float w = tex.Width;
float h = tex.Height;
sd.Rectangle bounds = new(bfc.X, bfc.Y, bfc.Width, bfc.Height);
float u0 = bounds.Left / w;
float v0 = bounds.Top / h;
float u1 = bounds.Right / w;
float v1 = bounds.Bottom / h;
float gx = x + bfc.XOffset;
float gy = y + bfc.YOffset;
// calculate texcoords (we shouldve already had this cached, but im speedcoding now)
var tex = TexturePages[bfc.TexturePage];
var w = tex.Width;
var h = tex.Height;
var bounds = new Rectangle(bfc.X, bfc.Y, bfc.Width, bfc.Height);
var u0 = bounds.Left / w;
var v0 = bounds.Top / h;
var u1 = bounds.Right / w;
var v1 = bounds.Bottom / h;
var gx = x + bfc.XOffset;
var gy = y + bfc.YOffset;
renderer.DrawSubrect(tex, gx, gy, bfc.Width, bfc.Height, u0, v0, u1, v1);
x += bfc.XAdvance;
@ -120,6 +135,6 @@ namespace BizHawk.Bizware.BizwareGL
public IGL Owner { get; }
private readonly BitmapFont FontInfo;
private List<Texture2d> TexturePages = new List<Texture2d>();
private List<Texture2d> TexturePages = new();
}
}

View File

@ -15,6 +15,7 @@ namespace BizHawk.Bizware.BizwareGL
Height = height;
Item = item;
}
public int X, Y;
public int Width, Height;
public int TexIndex;
@ -31,14 +32,14 @@ namespace BizHawk.Bizware.BizwareGL
public readonly List<RectangleBinPack.Node> nodes = new List<RectangleBinPack.Node>();
}
public static int MaxSizeBits = 16;
public const int MaxSizeBits = 16;
/// <summary>
/// packs the supplied RectItems into an atlas. Modifies the RectItems with x/y values of location in new atlas.
/// </summary>
public static IReadOnlyList<(Size Size, List<RectItem> Items)> PackAtlas(IReadOnlyCollection<RectItem> items)
public static IReadOnlyList<(Size Size, List<RectItem> Items)> PackAtlas(IEnumerable<RectItem> items)
{
static void AddAtlas(ICollection<(Size, List<RectItem>)> atlases, IReadOnlyCollection<RectItem> initItems)
static void AddAtlas(ICollection<(Size, List<RectItem>)> atlases, IEnumerable<RectItem> initItems)
{
List<RectItem> currentItems = new(initItems);
List<RectItem> remainItems = new();
@ -50,13 +51,13 @@ namespace BizHawk.Bizware.BizwareGL
// we run this every time we make an atlas, in case we want to variably control the maximum texture output size.
// ALSO - we accumulate data in there, so we need to refresh it each time. ... lame.
var todoSizes = new List<TryFitParam>();
for (int i = 3; i <= MaxSizeBits; i++)
for (var i = 3; i <= MaxSizeBits; i++)
{
for (int j = 3; j <= MaxSizeBits; j++)
for (var j = 3; j <= MaxSizeBits; j++)
{
int w = 1 << i;
int h = 1 << j;
TryFitParam tfp = new TryFitParam(w, h);
var w = 1 << i;
var h = 1 << j;
var tfp = new TryFitParam(w, h);
todoSizes.Add(tfp);
}
}
@ -68,9 +69,10 @@ namespace BizHawk.Bizware.BizwareGL
rbp.Init(16384, 16384);
param.rbp.Init(param.w, param.h);
// ReSharper disable once AccessToModifiedClosure
foreach (var ri in currentItems)
{
RectangleBinPack.Node node = param.rbp.Insert(ri.Width, ri.Height);
var node = param.rbp.Insert(ri.Width, ri.Height);
if (node == null)
{
param.ok = false;
@ -83,35 +85,50 @@ namespace BizHawk.Bizware.BizwareGL
}
});
//find the best fit among the potential sizes that worked
// find the best fit among the potential sizes that worked
var best = long.MaxValue;
tfpFinal = todoSizes[0];
foreach (var tfp in todoSizes)
{
if (!tfp.ok) continue;
if (!tfp.ok)
{
continue;
}
var area = tfp.w * (long) tfp.h;
if (area > best) continue; // larger than best, not interested
if (area > best)
{
continue; // larger than best, not interested
}
if (area == best) // same area, compare perimeter as tie-breaker (to create squares, which are nicer to look at)
{
if (tfp.w + tfp.h >= tfpFinal.w + tfpFinal.h) continue;
if (tfp.w + tfp.h >= tfpFinal.w + tfpFinal.h)
{
continue;
}
}
best = area;
tfpFinal = tfp;
}
//did we find any fit?
if (best < long.MaxValue) break;
if (best < long.MaxValue)
{
break;
}
//nope - move an item to the remaining list and try again
remainItems.Add(currentItems[currentItems.Count - 1]);
currentItems.RemoveAt(currentItems.Count - 1);
}
//we found a fit. setup this atlas in the result and drop the items into it
atlases.Add((new Size(tfpFinal.w, tfpFinal.h), new List<RectItem>(currentItems)));
atlases.Add((new(tfpFinal.w, tfpFinal.h), new(currentItems)));
foreach (var item in currentItems)
{
object o = item.Item;
var node = tfpFinal.nodes.Find((x) => x.ri == item);
var node = tfpFinal.nodes.Find(x => x.ri == item);
item.X = node.x;
item.Y = node.y;
item.TexIndex = atlases.Count - 1;
@ -157,7 +174,7 @@ namespace BizHawk.Bizware.BizwareGL
{
binWidth = width;
binHeight = height;
root = new Node();
root = new();
root.left = root.right = null;
root.x = root.y = 0;
root.width = width;
@ -176,8 +193,8 @@ namespace BizHawk.Bizware.BizwareGL
/// <summary>Computes the ratio of used surface area.</summary>
private float Occupancy()
{
int totalSurfaceArea = binWidth * binHeight;
int usedSurfaceArea = UsedSurfaceArea(root);
var totalSurfaceArea = binWidth * binHeight;
var usedSurfaceArea = UsedSurfaceArea(root);
return (float)usedSurfaceArea / totalSurfaceArea;
}
@ -189,11 +206,11 @@ namespace BizHawk.Bizware.BizwareGL
private int binHeight;
/// <returns>The surface area used by the subtree rooted at node.</returns>
private int UsedSurfaceArea(Node node)
private static int UsedSurfaceArea(Node node)
{
if (node.left != null || node.right != null)
{
int usedSurfaceArea = node.width * node.height;
var usedSurfaceArea = node.width * node.height;
if (node.left != null)
usedSurfaceArea += UsedSurfaceArea(node.left);
if (node.right != null)
@ -208,7 +225,7 @@ namespace BizHawk.Bizware.BizwareGL
/// <summary>Inserts a new rectangle in the subtree rooted at the given node.</summary>
private Node Insert(Node node, int width, int height)
private static Node Insert(Node node, int width, int height)
{
// If this node is an internal node, try both leaves for possible space.
@ -217,13 +234,13 @@ namespace BizHawk.Bizware.BizwareGL
{
if (node.left != null)
{
Node newNode = Insert(node.left, width, height);
var newNode = Insert(node.left, width, height);
if (newNode != null)
return newNode;
}
if (node.right != null)
{
Node newNode = Insert(node.right, width, height);
var newNode = Insert(node.right, width, height);
if (newNode != null)
return newNode;
}
@ -236,10 +253,10 @@ namespace BizHawk.Bizware.BizwareGL
// The new cell will fit, split the remaining space along the shorter axis,
// that is probably more optimal.
int w = node.width - width;
int h = node.height - height;
node.left = new Node();
node.right = new Node();
var w = node.width - width;
var h = node.height - height;
node.left = new();
node.right = new();
if (w <= h) // Split the remaining space in horizontal direction.
{
node.left.x = node.x + width;

View File

@ -16,7 +16,7 @@ namespace BizHawk.Bizware.BizwareGL
{
Owner = owner;
Opaque = opaque;
Items = new MyDictionary();
Items = new();
}
public object Opaque { get; }
@ -29,8 +29,10 @@ namespace BizHawk.Bizware.BizwareGL
RefCount--;
if (RefCount <= 0)
{
//nothing like this yet
//Available = false;
// nothing like this yet
#if false
Available = false;
#endif
}
}
@ -43,8 +45,11 @@ namespace BizHawk.Bizware.BizwareGL
public void DefineVertexAttribute(string name, int index, int components, VertexAttribPointerType attribType, AttribUsage usage, bool normalized, int stride, int offset = 0)
{
if (Closed)
{
throw new InvalidOperationException("Type is Closed and is now immutable.");
Items[index] = new LayoutItem { Name = name, Components = components, AttribType = attribType, Usage = usage, Normalized = normalized, Stride = stride, Offset = offset };
}
Items[index] = new() { Name = name, Components = components, AttribType = attribType, Usage = usage, Normalized = normalized, Stride = stride, Offset = offset };
}
/// <summary>
@ -66,7 +71,7 @@ namespace BizHawk.Bizware.BizwareGL
public AttribUsage Usage { get; internal set; }
}
public class MyDictionary : WorkingDictionary<int, LayoutItem>
public class LayoutItemWorkingDictionary : WorkingDictionary<int, LayoutItem>
{
public new LayoutItem this[int key]
{
@ -75,8 +80,8 @@ namespace BizHawk.Bizware.BizwareGL
}
}
public MyDictionary Items { get; }
private bool Closed = false;
public LayoutItemWorkingDictionary Items { get; }
private bool Closed;
}
}

View File

@ -1,323 +0,0 @@
using System;
namespace BizHawk.Bizware.BizwareGL
{
public struct Matrix4
{
public static readonly Matrix4 Identity = new(new(1.0f, 0.0f, 0.0f, 0.0f), new(0.0f, 1.0f, 0.0f, 0.0f), new(0.0f, 0.0f, 1.0f, 0.0f), new(0.0f, 0.0f, 0.0f, 1.0f));
public static bool AreEqual(in Matrix4 a, in Matrix4 b) => Vector4.AreEqual(in a.Row0, in b.Row0) && Vector4.AreEqual(in a.Row1, in b.Row1) && Vector4.AreEqual(in a.Row2, in b.Row2) && Vector4.AreEqual(in a.Row3, in b.Row3);
/// <returns>a <see cref="Matrix4"/> representing a rotation of <paramref name="angle"/> radians CCW around the given <paramref name="axis"/></returns>
public static Matrix4 CreateFromAxisAngle(in Vector3 axis, float angle)
{
var num0 = (float) (1.0 / Math.Sqrt(axis.X * (double) axis.X + axis.Y * (double) axis.Y + axis.Z * (double) axis.Z));
var x = axis.X * num0;
var y = axis.Y * num0;
var z = axis.Z * num0;
var num1 = (float) Math.Cos(-(double) angle);
var num2 = (float) Math.Sin(-(double) angle);
var num3 = 1.0f - num1;
var num4 = num3 * x * x;
var num5 = num3 * x * y;
var num6 = num3 * x * z;
var num7 = num3 * y * y;
var num8 = num3 * y * z;
var num9 = num3 * z * z;
var num10 = num2 * x;
var num11 = num2 * y;
var num12 = num2 * z;
return new(
new(num4 + num1, num5 - num12, num6 + num11, 0.0f),
new(num5 + num12, num7 + num1, num8 - num10, 0.0f),
new(num6 - num11, num8 + num10, num9 + num1, 0.0f),
new(0.0f, 0.0f, 0.0f, 1.0f));
}
/// <returns>a <see cref="Matrix4"/> representing a rotation of <paramref name="angle"/> radians CCW around the X-axis</returns>
public static Matrix4 CreateRotationX(float angle)
{
var num1 = (float) Math.Cos(angle);
var num2 = (float) Math.Sin(angle);
var result = Identity; // copy
result.Row1.Y = num1;
result.Row1.Z = num2;
result.Row2.Y = -num2;
result.Row2.Z = num1;
return result;
}
/// <returns>a <see cref="Matrix4"/> representing a rotation of <paramref name="angle"/> radians CCW around the Y-axis</returns>
public static Matrix4 CreateRotationY(float angle)
{
var num1 = (float) Math.Cos(angle);
var num2 = (float) Math.Sin(angle);
var result = Identity; // copy
result.Row0.X = num1;
result.Row0.Z = -num2;
result.Row2.X = num2;
result.Row2.Z = num1;
return result;
}
/// <returns>a <see cref="Matrix4"/> representing a rotation of <paramref name="angle"/> radians CCW around the Z-axis</returns>
public static Matrix4 CreateRotationZ(float angle)
{
var num1 = (float) Math.Cos(angle);
var num2 = (float) Math.Sin(angle);
var result = Identity; // copy
result.Row0.X = num1;
result.Row0.Y = num2;
result.Row1.X = -num2;
result.Row1.Y = num1;
return result;
}
/// <returns>a <see cref="Matrix4"/> representing a scaling</returns>
public static Matrix4 CreateScale(float x, float y, float z)
{
var result = Identity; // copy
result.Row0.X = x;
result.Row1.Y = y;
result.Row2.Z = z;
return result;
}
/// <returns>a <see cref="Matrix4"/> representing a scaling</returns>
public static Matrix4 CreateScale(in Vector3 scale)
{
var result = Identity; // copy
result.Row0.X = scale.X;
result.Row1.Y = scale.Y;
result.Row2.Z = scale.Z;
return result;
}
/// <returns>a <see cref="Matrix4"/> representing a translation</returns>
public static Matrix4 CreateTranslation(float x, float y, float z)
{
var result = Identity; // copy
result.Row3.X = x;
result.Row3.Y = y;
result.Row3.Z = z;
return result;
}
/// <returns>a <see cref="Matrix4"/> representing a translation</returns>
public static Matrix4 CreateTranslation(in Vector3 vector)
{
var result = Identity; // copy
result.Row3.X = vector.X;
result.Row3.Y = vector.Y;
result.Row3.Z = vector.Z;
return result;
}
private static float GetDeterminant(in Matrix4 m)
{
var x1 = (double) m.Row0.X;
var y1 = (double) m.Row0.Y;
var z1 = (double) m.Row0.Z;
var w1 = (double) m.Row0.W;
var x2 = (double) m.Row1.X;
var y2 = (double) m.Row1.Y;
var z2 = (double) m.Row1.Z;
var w2 = (double) m.Row1.W;
var x3 = (double) m.Row2.X;
var y3 = (double) m.Row2.Y;
var z3 = (double) m.Row2.Z;
var w3 = (double) m.Row2.W;
var x4 = (double) m.Row3.X;
var y4 = (double) m.Row3.Y;
var z4 = (double) m.Row3.Z;
var w4 = (double) m.Row3.W;
return (float) (x1 * y2 * z3 * w4
- x1 * y2 * w3 * z4
+ x1 * z2 * w3 * y4
- x1 * z2 * y3 * w4
+ x1 * w2 * y3 * z4
- x1 * w2 * z3 * y4
- y1 * z2 * w3 * x4
+ y1 * z2 * x3 * w4
- y1 * w2 * x3 * z4
+ y1 * w2 * z3 * x4
- y1 * x2 * z3 * w4
+ y1 * x2 * w3 * z4
+ z1 * w2 * x3 * y4
- z1 * w2 * y3 * x4
+ z1 * x2 * y3 * w4
- z1 * x2 * w3 * y4
+ z1 * y2 * w3 * x4
- z1 * y2 * x3 * w4
- w1 * x2 * y3 * z4
+ w1 * x2 * z3 * y4
- w1 * y2 * z3 * x4
+ w1 * y2 * x3 * z4
- w1 * z2 * x3 * y4
+ w1 * z2 * y3 * x4);
}
private static unsafe Matrix4 Invert(in Matrix4 mat)
{
var pDbTemp = stackalloc double[16];
fixed (Matrix4* pMatIn = &mat)
{
var pFlIn = (float*) pMatIn;
for (var i = 0; i < 16; i++) pDbTemp[i] = pFlIn[i];
}
Matrix4 result = default;
ref var refResult = ref result;
fixed (Matrix4* pMatOut = &refResult)
{
var pFlOut = (float*) pMatOut;
pFlOut[0] = (float) (pDbTemp[5] * pDbTemp[10] * pDbTemp[15] - pDbTemp[5] * pDbTemp[11] * pDbTemp[14] - pDbTemp[9] * pDbTemp[6] * pDbTemp[15] + pDbTemp[9] * pDbTemp[7] * pDbTemp[14] + pDbTemp[13] * pDbTemp[6] * pDbTemp[11] - pDbTemp[13] * pDbTemp[7] * pDbTemp[10]);
pFlOut[4] = (float) (-pDbTemp[4] * pDbTemp[10] * pDbTemp[15] + pDbTemp[4] * pDbTemp[11] * pDbTemp[14] + pDbTemp[8] * pDbTemp[6] * pDbTemp[15] - pDbTemp[8] * pDbTemp[7] * pDbTemp[14] - pDbTemp[12] * pDbTemp[6] * pDbTemp[11] + pDbTemp[12] * pDbTemp[7] * pDbTemp[10]);
pFlOut[8] = (float) (pDbTemp[4] * pDbTemp[9] * pDbTemp[15] - pDbTemp[4] * pDbTemp[11] * pDbTemp[13] - pDbTemp[8] * pDbTemp[5] * pDbTemp[15] + pDbTemp[8] * pDbTemp[7] * pDbTemp[13] + pDbTemp[12] * pDbTemp[5] * pDbTemp[11] - pDbTemp[12] * pDbTemp[7] * pDbTemp[9]);
pFlOut[12] = (float) (-pDbTemp[4] * pDbTemp[9] * pDbTemp[14] + pDbTemp[4] * pDbTemp[10] * pDbTemp[13] + pDbTemp[8] * pDbTemp[5] * pDbTemp[14] - pDbTemp[8] * pDbTemp[6] * pDbTemp[13] - pDbTemp[12] * pDbTemp[5] * pDbTemp[10] + pDbTemp[12] * pDbTemp[6] * pDbTemp[9]);
pFlOut[1] = (float) (-pDbTemp[1] * pDbTemp[10] * pDbTemp[15] + pDbTemp[1] * pDbTemp[11] * pDbTemp[14] + pDbTemp[9] * pDbTemp[2] * pDbTemp[15] - pDbTemp[9] * pDbTemp[3] * pDbTemp[14] - pDbTemp[13] * pDbTemp[2] * pDbTemp[11] + pDbTemp[13] * pDbTemp[3] * pDbTemp[10]);
pFlOut[5] = (float) (pDbTemp[0] * pDbTemp[10] * pDbTemp[15] - pDbTemp[0] * pDbTemp[11] * pDbTemp[14] - pDbTemp[8] * pDbTemp[2] * pDbTemp[15] + pDbTemp[8] * pDbTemp[3] * pDbTemp[14] + pDbTemp[12] * pDbTemp[2] * pDbTemp[11] - pDbTemp[12] * pDbTemp[3] * pDbTemp[10]);
pFlOut[9] = (float) (-pDbTemp[0] * pDbTemp[9] * pDbTemp[15] + pDbTemp[0] * pDbTemp[11] * pDbTemp[13] + pDbTemp[8] * pDbTemp[1] * pDbTemp[15] - pDbTemp[8] * pDbTemp[3] * pDbTemp[13] - pDbTemp[12] * pDbTemp[1] * pDbTemp[11] + pDbTemp[12] * pDbTemp[3] * pDbTemp[9]);
pFlOut[13] = (float) (pDbTemp[0] * pDbTemp[9] * pDbTemp[14] - pDbTemp[0] * pDbTemp[10] * pDbTemp[13] - pDbTemp[8] * pDbTemp[1] * pDbTemp[14] + pDbTemp[8] * pDbTemp[2] * pDbTemp[13] + pDbTemp[12] * pDbTemp[1] * pDbTemp[10] - pDbTemp[12] * pDbTemp[2] * pDbTemp[9]);
pFlOut[2] = (float) (pDbTemp[1] * pDbTemp[6] * pDbTemp[15] - pDbTemp[1] * pDbTemp[7] * pDbTemp[14] - pDbTemp[5] * pDbTemp[2] * pDbTemp[15] + pDbTemp[5] * pDbTemp[3] * pDbTemp[14] + pDbTemp[13] * pDbTemp[2] * pDbTemp[7] - pDbTemp[13] * pDbTemp[3] * pDbTemp[6]);
pFlOut[6] = (float) (-pDbTemp[0] * pDbTemp[6] * pDbTemp[15] + pDbTemp[0] * pDbTemp[7] * pDbTemp[14] + pDbTemp[4] * pDbTemp[2] * pDbTemp[15] - pDbTemp[4] * pDbTemp[3] * pDbTemp[14] - pDbTemp[12] * pDbTemp[2] * pDbTemp[7] + pDbTemp[12] * pDbTemp[3] * pDbTemp[6]);
pFlOut[10] = (float) (pDbTemp[0] * pDbTemp[5] * pDbTemp[15] - pDbTemp[0] * pDbTemp[7] * pDbTemp[13] - pDbTemp[4] * pDbTemp[1] * pDbTemp[15] + pDbTemp[4] * pDbTemp[3] * pDbTemp[13] + pDbTemp[12] * pDbTemp[1] * pDbTemp[7] - pDbTemp[12] * pDbTemp[3] * pDbTemp[5]);
pFlOut[14] = (float) (-pDbTemp[0] * pDbTemp[5] * pDbTemp[14] + pDbTemp[0] * pDbTemp[6] * pDbTemp[13] + pDbTemp[4] * pDbTemp[1] * pDbTemp[14] - pDbTemp[4] * pDbTemp[2] * pDbTemp[13] - pDbTemp[12] * pDbTemp[1] * pDbTemp[6] + pDbTemp[12] * pDbTemp[2] * pDbTemp[5]);
pFlOut[3] = (float) (-pDbTemp[1] * pDbTemp[6] * pDbTemp[11] + pDbTemp[1] * pDbTemp[7] * pDbTemp[10] + pDbTemp[5] * pDbTemp[2] * pDbTemp[11] - pDbTemp[5] * pDbTemp[3] * pDbTemp[10] - pDbTemp[9] * pDbTemp[2] * pDbTemp[7] + pDbTemp[9] * pDbTemp[3] * pDbTemp[6]);
pFlOut[7] = (float) (pDbTemp[0] * pDbTemp[6] * pDbTemp[11] - pDbTemp[0] * pDbTemp[7] * pDbTemp[10] - pDbTemp[4] * pDbTemp[2] * pDbTemp[11] + pDbTemp[4] * pDbTemp[3] * pDbTemp[10] + pDbTemp[8] * pDbTemp[2] * pDbTemp[7] - pDbTemp[8] * pDbTemp[3] * pDbTemp[6]);
pFlOut[11] = (float) (-pDbTemp[0] * pDbTemp[5] * pDbTemp[11] + pDbTemp[0] * pDbTemp[7] * pDbTemp[9] + pDbTemp[4] * pDbTemp[1] * pDbTemp[11] - pDbTemp[4] * pDbTemp[3] * pDbTemp[9] - pDbTemp[8] * pDbTemp[1] * pDbTemp[7] + pDbTemp[8] * pDbTemp[3] * pDbTemp[5]);
pFlOut[15] = (float) (pDbTemp[0] * pDbTemp[5] * pDbTemp[10] - pDbTemp[0] * pDbTemp[6] * pDbTemp[9] - pDbTemp[4] * pDbTemp[1] * pDbTemp[10] + pDbTemp[4] * pDbTemp[2] * pDbTemp[9] + pDbTemp[8] * pDbTemp[1] * pDbTemp[6] - pDbTemp[8] * pDbTemp[2] * pDbTemp[5]);
var num1 = (float) (pDbTemp[0] * pFlOut[0] + pDbTemp[1] * pFlOut[4] + pDbTemp[2] * pFlOut[8] + pDbTemp[3] * pFlOut[12]);
if (num1 == 0.0f) throw new InvalidOperationException("Matrix is singular and cannot be inverted.");
var num2 = 1.0f / num1;
for (var i = 0; i < 16; i++) pFlOut[i] *= num2;
}
return result;
}
public static Matrix4 Transpose(in Matrix4 mat) => new(
new(mat.Row0.X, mat.Row1.X, mat.Row2.X, mat.Row3.X),
new(mat.Row0.Y, mat.Row1.Y, mat.Row2.Y, mat.Row3.Y),
new(mat.Row0.Z, mat.Row1.Z, mat.Row2.Z, mat.Row3.Z),
new(mat.Row0.W, mat.Row1.W, mat.Row2.W, mat.Row3.W));
public static bool operator ==(in Matrix4 a, in Matrix4 b) => AreEqual(in a, in b);
public static bool operator !=(in Matrix4 a, in Matrix4 b) => !AreEqual(in a, in b);
/// <summary>Matrix multiplication</summary>
/// <param name="left">left-hand operand</param>
/// <param name="right">right-hand operand</param>
/// <returns>A new Matrix4 which holds the result of the multiplication</returns>
public static Matrix4 operator *(in Matrix4 left, in Matrix4 right)
{
var x1 = (double) left.Row0.X;
var y1 = (double) left.Row0.Y;
var z1 = (double) left.Row0.Z;
var w1 = (double) left.Row0.W;
var x2 = (double) left.Row1.X;
var y2 = (double) left.Row1.Y;
var z2 = (double) left.Row1.Z;
var w2 = (double) left.Row1.W;
var x3 = (double) left.Row2.X;
var y3 = (double) left.Row2.Y;
var z3 = (double) left.Row2.Z;
var w3 = (double) left.Row2.W;
var x4 = (double) left.Row3.X;
var y4 = (double) left.Row3.Y;
var z4 = (double) left.Row3.Z;
var w4 = (double) left.Row3.W;
var x5 = (double) right.Row0.X;
var y5 = (double) right.Row0.Y;
var z5 = (double) right.Row0.Z;
var w5 = (double) right.Row0.W;
var x6 = (double) right.Row1.X;
var y6 = (double) right.Row1.Y;
var z6 = (double) right.Row1.Z;
var w6 = (double) right.Row1.W;
var x7 = (double) right.Row2.X;
var y7 = (double) right.Row2.Y;
var z7 = (double) right.Row2.Z;
var w7 = (double) right.Row2.W;
var x8 = (double) right.Row3.X;
var y8 = (double) right.Row3.Y;
var z8 = (double) right.Row3.Z;
var w8 = (double) right.Row3.W;
Matrix4 result;
result.Row0.X = (float) (x1 * x5 + y1 * x6 + z1 * x7 + w1 * x8);
result.Row0.Y = (float) (x1 * y5 + y1 * y6 + z1 * y7 + w1 * y8);
result.Row0.Z = (float) (x1 * z5 + y1 * z6 + z1 * z7 + w1 * z8);
result.Row0.W = (float) (x1 * w5 + y1 * w6 + z1 * w7 + w1 * w8);
result.Row1.X = (float) (x2 * x5 + y2 * x6 + z2 * x7 + w2 * x8);
result.Row1.Y = (float) (x2 * y5 + y2 * y6 + z2 * y7 + w2 * y8);
result.Row1.Z = (float) (x2 * z5 + y2 * z6 + z2 * z7 + w2 * z8);
result.Row1.W = (float) (x2 * w5 + y2 * w6 + z2 * w7 + w2 * w8);
result.Row2.X = (float) (x3 * x5 + y3 * x6 + z3 * x7 + w3 * x8);
result.Row2.Y = (float) (x3 * y5 + y3 * y6 + z3 * y7 + w3 * y8);
result.Row2.Z = (float) (x3 * z5 + y3 * z6 + z3 * z7 + w3 * z8);
result.Row2.W = (float) (x3 * w5 + y3 * w6 + z3 * w7 + w3 * w8);
result.Row3.X = (float) (x4 * x5 + y4 * x6 + z4 * x7 + w4 * x8);
result.Row3.Y = (float) (x4 * y5 + y4 * y6 + z4 * y7 + w4 * y8);
result.Row3.Z = (float) (x4 * z5 + y4 * z6 + z4 * z7 + w4 * z8);
result.Row3.W = (float) (x4 * w5 + y4 * w6 + z4 * w7 + w4 * w8);
return result;
}
/// <returns><paramref name="vec"/> transformed by <paramref name="mat"/></returns>
public static Vector4 operator *(in Vector4 vec, in Matrix4 mat) => new(
(float) (vec.X * (double) mat.Row0.X + vec.Y * (double) mat.Row1.X + vec.Z * (double) mat.Row2.X + vec.W * (double) mat.Row3.X),
(float) (vec.X * (double) mat.Row0.Y + vec.Y * (double) mat.Row1.Y + vec.Z * (double) mat.Row2.Y + vec.W * (double) mat.Row3.Y),
(float) (vec.X * (double) mat.Row0.Z + vec.Y * (double) mat.Row1.Z + vec.Z * (double) mat.Row2.Z + vec.W * (double) mat.Row3.Z),
(float) (vec.X * (double) mat.Row0.W + vec.Y * (double) mat.Row1.W + vec.Z * (double) mat.Row2.W + vec.W * (double) mat.Row3.W));
/// <summary>Top row of the matrix.</summary>
public Vector4 Row0;
/// <summary>2nd row of the matrix.</summary>
public Vector4 Row1;
/// <summary>3rd row of the matrix.</summary>
public Vector4 Row2;
/// <summary>Bottom row of the matrix.</summary>
public Vector4 Row3;
/// <param name="row0">Top row of the matrix.</param>
/// <param name="row1">Second row of the matrix.</param>
/// <param name="row2">Third row of the matrix.</param>
/// <param name="row3">Bottom row of the matrix.</param>
public Matrix4(Vector4 row0, Vector4 row1, Vector4 row2, Vector4 row3)
{
Row0 = row0;
Row1 = row1;
Row2 = row2;
Row3 = row3;
}
public unsafe float this[int rowIndex, int columnIndex]
{
readonly get
{
if (rowIndex is < 0 or > 3) throw new ArgumentOutOfRangeException(paramName: nameof(rowIndex), rowIndex, message: "index out of range");
if (columnIndex is < 0 or > 3) throw new ArgumentOutOfRangeException(paramName: nameof(columnIndex), columnIndex, message: "index out of range");
var i = rowIndex * 4 + columnIndex;
fixed (Matrix4* p = &this) return ((float*) p)[i];
}
set
{
if (rowIndex is < 0 or > 3) throw new ArgumentOutOfRangeException(paramName: nameof(rowIndex), rowIndex, message: "index out of range");
if (columnIndex is < 0 or > 3) throw new ArgumentOutOfRangeException(paramName: nameof(columnIndex), columnIndex, message: "index out of range");
var i = rowIndex * 4 + columnIndex;
fixed (Matrix4* p = &this) ((float*) p)[i] = value;
}
}
/// <returns>an inverted copy of this instance, or an identical copy if it is singular</returns>
public readonly Matrix4 Inverted() => GetDeterminant(in this) == 0.0f ? this : Invert(in this);
public override readonly bool Equals(object obj) => obj is Matrix4 other && AreEqual(in this, in other);
public override readonly int GetHashCode() => ((Row0.GetHashCode() * 397 ^ Row1.GetHashCode()) * 397 ^ Row2.GetHashCode()) * 397 ^ Row3.GetHashCode();
public override readonly string ToString() => string.Join("\n", Row0, Row1, Row2, Row3);
}
}

View File

@ -1,29 +0,0 @@
namespace BizHawk.Bizware.BizwareGL
{
public struct Vector2
{
public static bool AreEqual(in Vector2 a, in Vector2 b) => a.X == b.X && a.Y == b.Y;
public static bool operator ==(in Vector2 a, in Vector2 b) => AreEqual(in a, in b);
public static bool operator !=(in Vector2 a, in Vector2 b) => !AreEqual(in a, in b);
public static Vector2 operator +(in Vector2 left, in Vector2 right) => new(left.X + right.X, left.Y + right.Y);
public float X;
public float Y;
public Vector2(float x, float y)
{
X = x;
Y = y;
}
public override readonly bool Equals(object obj) => obj is Vector2 other && AreEqual(in this, in other);
public override readonly int GetHashCode() => X.GetHashCode() * 397 ^ Y.GetHashCode();
public override readonly string ToString() => $"({X}, {Y})";
}
}

View File

@ -1,30 +0,0 @@
namespace BizHawk.Bizware.BizwareGL
{
public readonly struct Vector3
{
public static bool AreEqual(in Vector3 a, in Vector3 b) => a.X == b.X && a.Y == b.Y && a.Z == b.Z;
public static bool operator ==(in Vector3 a, in Vector3 b) => AreEqual(in a, in b);
public static bool operator !=(in Vector3 a, in Vector3 b) => !AreEqual(in a, in b);
public readonly float X;
public readonly float Y;
public readonly float Z;
public Vector3(float x, float y, float z)
{
X = x;
Y = y;
Z = z;
}
public override readonly bool Equals(object obj) => obj is Vector3 other && AreEqual(in this, in other);
public override readonly int GetHashCode() => (X.GetHashCode() * 397 ^ Y.GetHashCode()) * 397 ^ Z.GetHashCode();
public override readonly string ToString() => $"({X}, {Y}, {Z})";
}
}

View File

@ -1,33 +0,0 @@
namespace BizHawk.Bizware.BizwareGL
{
public struct Vector4
{
public static bool AreEqual(in Vector4 a, in Vector4 b) => a.X == b.X && a.Y == b.Y && a.Z == b.Z && a.W == b.W;
public static bool operator ==(in Vector4 a, in Vector4 b) => AreEqual(in a, in b);
public static bool operator !=(in Vector4 a, in Vector4 b) => !AreEqual(in a, in b);
public float X;
public float Y;
public float Z;
public float W;
public Vector4(float x, float y, float z, float w)
{
X = x;
Y = y;
Z = z;
W = w;
}
public override readonly bool Equals(object obj) => obj is Vector4 other && AreEqual(in this, in other);
public override readonly int GetHashCode() => ((X.GetHashCode() * 397 ^ Y.GetHashCode()) * 397 ^ Z.GetHashCode()) * 397 ^ W.GetHashCode();
public override readonly string ToString() => $"({X}, {Y}, {Z}, {W})";
}
}

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
@ -573,10 +574,10 @@ namespace BizHawk.Bizware.Graphics
}
}
public void SetPipelineUniformMatrix(PipelineUniform uniform, Matrix4 mat, bool transpose)
public void SetPipelineUniformMatrix(PipelineUniform uniform, Matrix4x4 mat, bool transpose)
=> SetPipelineUniformMatrix(uniform, ref mat, transpose);
public void SetPipelineUniformMatrix(PipelineUniform uniform, ref Matrix4 mat, bool transpose)
public void SetPipelineUniformMatrix(PipelineUniform uniform, ref Matrix4x4 mat, bool transpose)
{
foreach (var ui in uniform.UniformInfos)
{
@ -755,32 +756,32 @@ namespace BizHawk.Bizware.Graphics
return LoadTexture(fs);
}
public Matrix4 CreateGuiProjectionMatrix(int w, int h)
public Matrix4x4 CreateGuiProjectionMatrix(int w, int h)
{
return CreateGuiProjectionMatrix(new(w, h));
}
public Matrix4 CreateGuiViewMatrix(int w, int h, bool autoFlip)
public Matrix4x4 CreateGuiViewMatrix(int w, int h, bool autoFlip)
{
return CreateGuiViewMatrix(new(w, h), autoFlip);
}
public Matrix4 CreateGuiProjectionMatrix(Size dims)
public Matrix4x4 CreateGuiProjectionMatrix(Size dims)
{
var ret = Matrix4.Identity;
ret.Row0.X = 2.0f / dims.Width;
ret.Row1.Y = 2.0f / dims.Height;
var ret = Matrix4x4.Identity;
ret.M11 = 2.0f / dims.Width;
ret.M22 = 2.0f / dims.Height;
return ret;
}
public Matrix4 CreateGuiViewMatrix(Size dims, bool autoFlip)
public Matrix4x4 CreateGuiViewMatrix(Size dims, bool autoFlip)
{
var ret = Matrix4.Identity;
ret.Row1.Y = -1.0f;
ret.Row3.X = -dims.Width * 0.5f - 0.5f;
ret.Row3.Y = dims.Height * 0.5f + 0.5f;
var ret = Matrix4x4.Identity;
ret.M22 = -1.0f;
ret.M41 = -dims.Width * 0.5f - 0.5f;
ret.M42 = dims.Height * 0.5f + 0.5f;
// auto-flipping isn't needed on d3d
// auto-flipping isn't needed on D3D
return ret;
}

View File

@ -1,6 +1,5 @@
using System.Drawing;
using BizHawk.Bizware.BizwareGL;
using System.Numerics;
using SharpDX.Mathematics.Interop;
@ -8,21 +7,21 @@ namespace BizHawk.Bizware.Graphics
{
internal static class SharpDXExtensions
{
// SharpDX RawMatrix and BizwareGL Matrix are identical in structure
public static RawMatrix ToSharpDXMatrix(this Matrix4 m, bool transpose)
// SharpDX RawMatrix and Numerics Matrix4x4 are identical in structure
public static RawMatrix ToSharpDXMatrix(this Matrix4x4 m, bool transpose)
{
// Transpose call could be inlined to reduce 2 sets of copies to 1
if (transpose)
{
m = Matrix4.Transpose(in m);
m = Matrix4x4.Transpose(m);
}
return new()
{
M11 = m.Row0.X, M12 = m.Row0.Y, M13 = m.Row0.Z, M14 = m.Row0.W,
M21 = m.Row1.X, M22 = m.Row1.Y, M23 = m.Row1.Z, M24 = m.Row1.W,
M31 = m.Row2.X, M32 = m.Row2.Y, M33 = m.Row2.Z, M34 = m.Row2.W,
M41 = m.Row3.X, M42 = m.Row3.Y, M43 = m.Row3.Z, M44 = m.Row3.W
M11 = m.M11, M12 = m.M12, M13 = m.M13, M14 = m.M14,
M21 = m.M21, M22 = m.M22, M23 = m.M23, M24 = m.M24,
M31 = m.M31, M32 = m.M32, M33 = m.M33, M34 = m.M34,
M41 = m.M41, M42 = m.M42, M43 = m.M43, M44 = m.M44
};
}

View File

@ -7,6 +7,7 @@ using System;
using System.Diagnostics;
#endif
using System.Drawing;
using System.Numerics;
using BizHawk.Bizware.BizwareGL;
@ -153,7 +154,7 @@ namespace BizHawk.Bizware.Graphics
/// <exception cref="InvalidOperationException">no pipeline set (need to call <see cref="SetPipeline"/>)</exception>
public void Begin()
{
//uhhmmm I want to throw an exception if its already active, but its annoying.
// uhhmmm I want to throw an exception if its already active, but its annoying.
if (CurrPipeline == null)
throw new InvalidOperationException("Pipeline hasn't been set!");
@ -176,14 +177,17 @@ namespace BizHawk.Bizware.Graphics
public void Flush()
{
//no batching, nothing to do here yet
// no batching, nothing to do here yet
}
/// <exception cref="InvalidOperationException"><see cref="IsActive"/> is <see langword="false"/></exception>
public void End()
{
if (!IsActive)
{
throw new InvalidOperationException($"{nameof(GuiRenderer)} is not active!");
}
IsActive = false;
}
@ -199,26 +203,40 @@ namespace BizHawk.Bizware.Graphics
DrawSubrectInternal(tex, x, y, w, h, u0, v0, u1, v1);
}
public void Draw(Art art)
{
DrawInternal(art, 0, 0, art.Width, art.Height, false, false);
}
public void Draw(Art art) { DrawInternal(art, 0, 0, art.Width, art.Height, false, false); }
public void Draw(Art art, float x, float y)
{
DrawInternal(art, x, y, art.Width, art.Height, false, false);
}
public void Draw(Art art, float x, float y, float width, float height)
{
DrawInternal(art, x, y, width, height, false, false);
}
public void Draw(Art art, float x, float y) { DrawInternal(art, x, y, art.Width, art.Height, false, false); }
public void Draw(Art art, Vector2 pos)
{
DrawInternal(art, pos.X, pos.Y, art.Width, art.Height, false, false);
}
public void Draw(Texture2d tex)
{
DrawInternal(tex, 0, 0, tex.Width, tex.Height);
}
public void Draw(Art art, float x, float y, float width, float height) { DrawInternal(art, x, y, width, height, false, false); }
public void Draw(Texture2d tex, float x, float y)
{
DrawInternal(tex, x, y, tex.Width, tex.Height);
}
public void Draw(Art art, Vector2 pos) { DrawInternal(art, pos.X, pos.Y, art.Width, art.Height, false, false); }
public void Draw(Texture2d tex) { DrawInternal(tex, 0, 0, tex.Width, tex.Height); }
public void Draw(Texture2d tex, float x, float y) { DrawInternal(tex, x, y, tex.Width, tex.Height); }
public void DrawFlipped(Art art, bool xflip, bool yflip) { DrawInternal(art, 0, 0, art.Width, art.Height, xflip, yflip); }
public void DrawFlipped(Art art, bool xflip, bool yflip)
{
DrawInternal(art, 0, 0, art.Width, art.Height, xflip, yflip);
}
public void Draw(Texture2d art, float x, float y, float width, float height)
{
@ -441,7 +459,7 @@ void main()
vCornerColor = aColor * uModulateColor;
}";
public readonly string DefaultPixelShader_gl = @"
public const string DefaultPixelShader_gl = @"
//opengl 2.0 ~ 2004
#version 110
uniform bool uSamplerEnable;

View File

@ -7,9 +7,10 @@
// glBindAttribLocation (programID, 0, "vertexPosition_modelspace");
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Collections.Generic;
using System.Numerics;
using BizHawk.Bizware.BizwareGL;
using BizHawk.Common;
@ -445,14 +446,14 @@ namespace BizHawk.Bizware.Graphics
GL.Uniform1((int)uniform.Sole.Opaque, value ? 1 : 0);
}
public unsafe void SetPipelineUniformMatrix(PipelineUniform uniform, Matrix4 mat, bool transpose)
public unsafe void SetPipelineUniformMatrix(PipelineUniform uniform, Matrix4x4 mat, bool transpose)
{
GL.UniformMatrix4((int)uniform.Sole.Opaque, 1, transpose, (float*)&mat);
}
public unsafe void SetPipelineUniformMatrix(PipelineUniform uniform, ref Matrix4 mat, bool transpose)
public unsafe void SetPipelineUniformMatrix(PipelineUniform uniform, ref Matrix4x4 mat, bool transpose)
{
fixed (Matrix4* pMat = &mat)
fixed (Matrix4x4* pMat = &mat)
{
GL.UniformMatrix4((int)uniform.Sole.Opaque, 1, transpose, (float*)pMat);
}
@ -646,35 +647,36 @@ namespace BizHawk.Bizware.Graphics
return LoadTexture(fs);
}
public Matrix4 CreateGuiProjectionMatrix(int w, int h)
public Matrix4x4 CreateGuiProjectionMatrix(int w, int h)
{
return CreateGuiProjectionMatrix(new(w, h));
}
public Matrix4 CreateGuiViewMatrix(int w, int h, bool autoflip)
public Matrix4x4 CreateGuiViewMatrix(int w, int h, bool autoflip)
{
return CreateGuiViewMatrix(new(w, h), autoflip);
}
public Matrix4 CreateGuiProjectionMatrix(Size dims)
public Matrix4x4 CreateGuiProjectionMatrix(Size dims)
{
var ret = Matrix4.Identity;
ret.Row0.X = 2.0f / dims.Width;
ret.Row1.Y = 2.0f / dims.Height;
var ret = Matrix4x4.Identity;
ret.M11 = 2.0f / dims.Width;
ret.M22 = 2.0f / dims.Height;
return ret;
}
public Matrix4 CreateGuiViewMatrix(Size dims, bool autoflip)
public Matrix4x4 CreateGuiViewMatrix(Size dims, bool autoflip)
{
var ret = Matrix4.Identity;
ret.Row1.Y = -1.0f;
ret.Row3.X = dims.Width * -0.5f;
ret.Row3.Y = dims.Height * 0.5f;
var ret = Matrix4x4.Identity;
ret.M22 = -1.0f;
ret.M41 = dims.Width * -0.5f;
ret.M42 = dims.Height * 0.5f;
if (autoflip && _currRenderTarget is not null) // flip as long as we're not a final render target
{
ret.Row1.Y = 1.0f;
ret.Row3.Y *= -1;
ret.M22 = 1.0f;
ret.M42 *= -1;
}
return ret;
}

View File

@ -6,7 +6,7 @@ using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Text;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
using BizHawk.Bizware.BizwareGL;
@ -52,7 +52,7 @@ namespace BizHawk.Client.Common
private IEmulator GlobalEmulator;
public DisplayManagerBase(
protected DisplayManagerBase(
Config config,
IEmulator emulator,
InputManager inputManager,
@ -63,58 +63,58 @@ namespace BizHawk.Client.Common
{
GlobalConfig = config;
GlobalEmulator = emulator;
OSD = new OSDManager(config, emulator, inputManager, movieSession);
OSD = new(config, emulator, inputManager, movieSession);
_gl = gl;
_renderer = renderer;
// it's sort of important for these to be initialized to something nonzero
_currEmuWidth = _currEmuHeight = 1;
_videoTextureFrugalizer = new TextureFrugalizer(_gl);
_videoTextureFrugalizer = new(_gl);
_shaderChainFrugalizers = new RenderTargetFrugalizer[16]; // hacky hardcoded limit.. need some other way to manage these
for (int i = 0; i < 16; i++)
for (var i = 0; i < 16; i++)
{
_shaderChainFrugalizers[i] = new RenderTargetFrugalizer(_gl);
_shaderChainFrugalizers[i] = new(_gl);
}
{
using var xml = ReflectionCache.EmbeddedResourceStream("Resources.courier16px.fnt");
using var tex = ReflectionCache.EmbeddedResourceStream("Resources.courier16px_0.png");
_theOneFont = new StringRenderer(_gl, xml, tex);
_theOneFont = new(_gl, xml, tex);
using var gens = ReflectionCache.EmbeddedResourceStream("Resources.gens.ttf");
LoadCustomFont(gens);
using var fceux = ReflectionCache.EmbeddedResourceStream("Resources.fceux.ttf");
LoadCustomFont(fceux);
}
if (dispMethod == EDispMethod.OpenGL || dispMethod == EDispMethod.D3D9)
if (dispMethod is EDispMethod.OpenGL or EDispMethod.D3D9)
{
var fiHq2x = new FileInfo(Path.Combine(PathUtils.ExeDirectoryPath, "Shaders/BizHawk/hq2x.cgp"));
if (fiHq2x.Exists)
{
using var stream = fiHq2x.OpenRead();
_shaderChainHq2X = new RetroShaderChain(_gl, new RetroShaderPreset(stream), Path.Combine(PathUtils.ExeDirectoryPath, "Shaders/BizHawk"));
_shaderChainHq2X = new(_gl, new(stream), Path.Combine(PathUtils.ExeDirectoryPath, "Shaders/BizHawk"));
}
var fiScanlines = new FileInfo(Path.Combine(PathUtils.ExeDirectoryPath, "Shaders/BizHawk/BizScanlines.cgp"));
if (fiScanlines.Exists)
{
using var stream = fiScanlines.OpenRead();
_shaderChainScanlines = new RetroShaderChain(_gl, new RetroShaderPreset(stream), Path.Combine(PathUtils.ExeDirectoryPath, "Shaders/BizHawk"));
_shaderChainScanlines = new(_gl, new(stream), Path.Combine(PathUtils.ExeDirectoryPath, "Shaders/BizHawk"));
}
var bicubicPath = dispMethod == EDispMethod.D3D9 ? "Shaders/BizHawk/bicubic-normal.cgp" : "Shaders/BizHawk/bicubic-fast.cgp";
var fiBicubic = new FileInfo(Path.Combine(PathUtils.ExeDirectoryPath, bicubicPath));
if (fiBicubic.Exists)
{
using var stream = fiBicubic.Open(FileMode.Open, FileAccess.Read, FileShare.Read);
_shaderChainBicubic = new RetroShaderChain(_gl, new RetroShaderPreset(stream), Path.Combine(PathUtils.ExeDirectoryPath, "Shaders/BizHawk"));
_shaderChainBicubic = new(_gl, new(stream), Path.Combine(PathUtils.ExeDirectoryPath, "Shaders/BizHawk"));
}
}
_apiHawkSurfaceSets[DisplaySurfaceID.EmuCore] = new(CreateDisplaySurface);
_apiHawkSurfaceSets[DisplaySurfaceID.Client] = new(CreateDisplaySurface);
_apiHawkSurfaceFrugalizers[DisplaySurfaceID.EmuCore] = new TextureFrugalizer(_gl);
_apiHawkSurfaceFrugalizers[DisplaySurfaceID.Client] = new TextureFrugalizer(_gl);
_apiHawkSurfaceFrugalizers[DisplaySurfaceID.EmuCore] = new(_gl);
_apiHawkSurfaceFrugalizers[DisplaySurfaceID.Client] = new(_gl);
RefreshUserShader();
}
@ -130,7 +130,11 @@ namespace BizHawk.Client.Common
public void Dispose()
{
if (Disposed) return;
if (Disposed)
{
return;
}
Disposed = true;
// OpenGL context needs to be active when Dispose()'ing
@ -185,7 +189,7 @@ namespace BizHawk.Client.Common
/// <summary>
/// custom fonts that don't need to be installed on the user side
/// </summary>
public PrivateFontCollection CustomFonts { get; } = new PrivateFontCollection();
public PrivateFontCollection CustomFonts { get; } = new();
private readonly TextureFrugalizer _videoTextureFrugalizer;
@ -193,11 +197,11 @@ namespace BizHawk.Client.Common
protected readonly RenderTargetFrugalizer[] _shaderChainFrugalizers;
private RetroShaderChain _shaderChainHq2X;
private readonly RetroShaderChain _shaderChainHq2X;
private RetroShaderChain _shaderChainScanlines;
private readonly RetroShaderChain _shaderChainScanlines;
private RetroShaderChain _shaderChainBicubic;
private readonly RetroShaderChain _shaderChainBicubic;
private RetroShaderChain _shaderChainUser;
@ -214,7 +218,7 @@ namespace BizHawk.Client.Common
{
var fi = new FileInfo(GlobalConfig.DispUserFilterPath);
using var stream = fi.OpenRead();
_shaderChainUser = new RetroShaderChain(_gl, new RetroShaderPreset(stream), Path.GetDirectoryName(GlobalConfig.DispUserFilterPath));
_shaderChainUser = new(_gl, new(stream), Path.GetDirectoryName(GlobalConfig.DispUserFilterPath));
}
}
@ -261,26 +265,24 @@ namespace BizHawk.Client.Common
// select user special FX shader chain
var selectedChainProperties = new Dictionary<string, object>();
RetroShaderChain selectedChain = null;
if (GlobalConfig.TargetDisplayFilter == 1 && _shaderChainHq2X != null && _shaderChainHq2X.Available)
switch (GlobalConfig.TargetDisplayFilter)
{
selectedChain = _shaderChainHq2X;
}
if (GlobalConfig.TargetDisplayFilter == 2 && _shaderChainScanlines != null && _shaderChainScanlines.Available)
{
selectedChain = _shaderChainScanlines;
selectedChainProperties["uIntensity"] = 1.0f - GlobalConfig.TargetScanlineFilterIntensity / 256.0f;
}
if (GlobalConfig.TargetDisplayFilter == 3 && _shaderChainUser != null && _shaderChainUser.Available)
{
selectedChain = _shaderChainUser;
case 1 when _shaderChainHq2X is { Available: true }:
selectedChain = _shaderChainHq2X;
break;
case 2 when _shaderChainScanlines is { Available: true }:
selectedChain = _shaderChainScanlines;
selectedChainProperties["uIntensity"] = 1.0f - GlobalConfig.TargetScanlineFilterIntensity / 256.0f;
break;
case 3 when _shaderChainUser is { Available: true }:
selectedChain = _shaderChainUser;
break;
}
if (!includeUserFilters)
selectedChain = null;
BaseFilter fCoreScreenControl = CreateCoreScreenControl();
var fCoreScreenControl = CreateCoreScreenControl();
var fPresent = new FinalPresentation(chainOutSize);
var fInput = new SourceImage(chainInSize);
@ -311,7 +313,9 @@ namespace BizHawk.Client.Common
chain.AddFilter(fInput, "input");
if (fCoreScreenControl != null)
{
chain.AddFilter(fCoreScreenControl, "CoreScreenControl");
}
// if a non-zero padding is required, add a filter to allow for that
// note, we have two sources of padding right now.. one can come from the VideoProvider and one from the user.
@ -322,35 +326,37 @@ namespace BizHawk.Client.Common
if (padding != (0, 0, 0, 0))
{
// TODO - add another filter just for this, its cumbersome to use final presentation... I think. but maybe there's enough similarities to justify it.
Size size = chainInSize;
var size = chainInSize;
size.Width += padding.Left + padding.Right;
size.Height += padding.Top + padding.Bottom;
//in case the user requested so much padding that the dimensions are now negative, just turn it to something small
// in case the user requested so much padding that the dimensions are now negative, just turn it to something small
if (size.Width < 1) size.Width = 1;
if (size.Height < 1) size.Height = 1;
FinalPresentation fPadding = new FinalPresentation(size);
var fPadding = new FinalPresentation(size);
chain.AddFilter(fPadding, "padding");
fPadding.GuiRenderer = _renderer;
fPadding.GL = _gl;
fPadding.Config_PadOnly = true;
fPadding.Padding = padding;
}
//add lua layer 'emu'
// add lua layer 'emu'
AppendApiHawkLayer(chain, DisplaySurfaceID.EmuCore);
if(includeUserFilters)
if (includeUserFilters)
{
if (GlobalConfig.DispPrescale != 1)
{
var fPrescale = new PrescaleFilter() { Scale = GlobalConfig.DispPrescale };
chain.AddFilter(fPrescale, "user_prescale");
}
}
// add user-selected retro shader
if (selectedChain != null)
{
AppendRetroShaderChain(chain, "retroShader", selectedChain, selectedChainProperties);
}
// AutoPrescale makes no sense for a None final filter
if (GlobalConfig.DispAutoPrescale && GlobalConfig.DispFinalFilter != (int)FinalPresentation.eFilterOption.None)
@ -359,22 +365,18 @@ namespace BizHawk.Client.Common
chain.AddFilter(apf, "auto_prescale");
}
//choose final filter
var finalFilter = FinalPresentation.eFilterOption.None;
if (GlobalConfig.DispFinalFilter == 1)
// choose final filter
var finalFilter = GlobalConfig.DispFinalFilter switch
{
finalFilter = FinalPresentation.eFilterOption.Bilinear;
}
1 => FinalPresentation.eFilterOption.Bilinear,
2 => FinalPresentation.eFilterOption.Bicubic,
_ => FinalPresentation.eFilterOption.None
};
if (GlobalConfig.DispFinalFilter == 2)
{
finalFilter = FinalPresentation.eFilterOption.Bicubic;
}
//if bicubic is selected and unavailable, don't use it. use bilinear instead I guess
// if bicubic is selected and unavailable, don't use it. use bilinear instead I guess
if (finalFilter == FinalPresentation.eFilterOption.Bicubic)
{
if (_shaderChainBicubic == null || !_shaderChainBicubic.Available)
if (_shaderChainBicubic is not { Available: true })
{
finalFilter = FinalPresentation.eFilterOption.Bilinear;
}
@ -398,18 +400,20 @@ namespace BizHawk.Client.Common
// and OSD goes on top of that
// TODO - things break if this isn't present (the final presentation filter gets messed up when used with prescaling)
// so, always include it (we'll handle this flag in the callback to do no rendering)
if (true /*includeOSD*/) chain.AddFilter(fOSD, "osd");
if (true /*includeOSD*/)
{
chain.AddFilter(fOSD, "osd");
}
return chain;
}
private void AppendRetroShaderChain(FilterProgram program, string name, RetroShaderChain retroChain, Dictionary<string, object> properties)
private static void AppendRetroShaderChain(FilterProgram program, string name, RetroShaderChain retroChain, Dictionary<string, object> properties)
{
for (int i = 0; i < retroChain.Passes.Length; i++)
for (var i = 0; i < retroChain.Passes.Length; i++)
{
var pass = retroChain.Passes[i];
var rsp = new RetroShaderPass(retroChain, i);
string fname = $"{name}[{i}]";
var fname = $"{name}[{i}]";
program.AddFilter(rsp, fname);
rsp.Parameters = properties;
}
@ -423,13 +427,13 @@ namespace BizHawk.Client.Common
return;
}
Texture2d luaNativeTexture = _apiHawkSurfaceFrugalizers[surfaceID].Get(luaNativeSurface);
var luaNativeTexture = _apiHawkSurfaceFrugalizers[surfaceID].Get(luaNativeSurface);
var fLuaLayer = new LuaLayer();
fLuaLayer.SetTexture(luaNativeTexture);
chain.AddFilter(fLuaLayer, surfaceID.GetName());
}
protected virtual Point GraphicsControlPointToClient(Point p) => throw new NotImplementedException();
protected abstract Point GraphicsControlPointToClient(Point p);
/// <summary>
/// Using the current filter program, turn a mouse coordinate from window space to the original emulator screen space.
@ -454,7 +458,7 @@ namespace BizHawk.Client.Common
/// </summary>
public Point TransformPoint(Point p)
{
//now, if there's no filter program active, just give up
// now, if there's no filter program active, just give up
if (_currentFilterProgram == null)
{
return p;
@ -466,9 +470,9 @@ namespace BizHawk.Client.Common
return new((int)v.X, (int)v.Y);
}
public virtual Size GetPanelNativeSize() => throw new NotImplementedException();
public abstract Size GetPanelNativeSize();
protected virtual Size GetGraphicsControlSize() => throw new NotImplementedException();
protected abstract Size GetGraphicsControlSize();
/// <summary>
/// This will receive an emulated output frame from an IVideoProvider and run it through the complete frame processing pipeline
@ -476,7 +480,7 @@ namespace BizHawk.Client.Common
/// </summary>
public void UpdateSource(IVideoProvider videoProvider)
{
bool displayNothing = GlobalConfig.DispSpeedupFeatures == 0;
var displayNothing = GlobalConfig.DispSpeedupFeatures == 0;
var job = new JobInfo
{
VideoProvider = videoProvider,
@ -485,6 +489,7 @@ namespace BizHawk.Client.Common
IncludeOSD = true,
IncludeUserFilters = true
};
UpdateSourceInternal(job);
}
@ -512,6 +517,7 @@ namespace BizHawk.Client.Common
IncludeOSD = includeOSD,
IncludeUserFilters = true,
};
UpdateSourceInternal(job);
return job.OffscreenBb;
}
@ -524,37 +530,42 @@ namespace BizHawk.Client.Common
{
VideoProvider = videoProvider,
Simulate = false,
ChainOutsize = new Size(videoProvider.BufferWidth, videoProvider.BufferHeight),
ChainOutsize = new(videoProvider.BufferWidth, videoProvider.BufferHeight),
Offscreen = true,
IncludeOSD = false,
IncludeUserFilters = false,
};
UpdateSourceInternal(job);
return job.OffscreenBb;
}
private class FakeVideoProvider : IVideoProvider
{
public FakeVideoProvider(int bw, int bh, int vw, int vh)
{
BufferWidth = bw;
BufferHeight = bh;
VirtualWidth = vw;
VirtualHeight = vh;
}
public int[] GetVideoBuffer()
=> Array.Empty<int>();
public int VirtualWidth { get; set; }
public int VirtualHeight { get; set; }
public int VirtualWidth { get; }
public int VirtualHeight { get; }
public int BufferWidth { get; }
public int BufferHeight { get; }
public int BackgroundColor => 0;
public int BufferWidth { get; set; }
public int BufferHeight { get; set; }
public int BackgroundColor { get; set; }
/// <exception cref="InvalidOperationException">always</exception>
public int VsyncNumerator => throw new InvalidOperationException();
/// <exception cref="InvalidOperationException">always</exception>
public int VsyncDenominator => throw new InvalidOperationException();
public int VsyncNumerator => throw new NotImplementedException();
public int VsyncDenominator => throw new NotImplementedException();
}
private void FixRatio(float x, float y, int inw, int inh, out int outW, out int outH)
private static void FixRatio(float x, float y, int inw, int inh, out int outW, out int outH)
{
float ratio = x / y;
var ratio = x / y;
if (ratio <= 1)
{
// taller. weird. expand height.
@ -576,17 +587,17 @@ namespace BizHawk.Client.Common
/// </summary>
public Size CalculateClientSize(IVideoProvider videoProvider, int zoom)
{
bool arActive = GlobalConfig.DispFixAspectRatio;
bool arSystem = GlobalConfig.DispManagerAR == EDispManagerAR.System;
bool arCustom = GlobalConfig.DispManagerAR == EDispManagerAR.CustomSize;
bool arCustomRatio = GlobalConfig.DispManagerAR == EDispManagerAR.CustomRatio;
bool arCorrect = arSystem || arCustom || arCustomRatio;
bool arInteger = GlobalConfig.DispFixScaleInteger;
var arActive = GlobalConfig.DispFixAspectRatio;
var arSystem = GlobalConfig.DispManagerAR == EDispManagerAR.System;
var arCustom = GlobalConfig.DispManagerAR == EDispManagerAR.CustomSize;
var arCustomRatio = GlobalConfig.DispManagerAR == EDispManagerAR.CustomRatio;
var arCorrect = arSystem || arCustom || arCustomRatio;
var arInteger = GlobalConfig.DispFixScaleInteger;
int bufferWidth = videoProvider.BufferWidth;
int bufferHeight = videoProvider.BufferHeight;
int virtualWidth = videoProvider.VirtualWidth;
int virtualHeight = videoProvider.VirtualHeight;
var bufferWidth = videoProvider.BufferWidth;
var bufferHeight = videoProvider.BufferHeight;
var virtualWidth = videoProvider.VirtualWidth;
var virtualHeight = videoProvider.VirtualHeight;
if (arCustom)
{
@ -596,17 +607,18 @@ namespace BizHawk.Client.Common
if (arCustomRatio)
{
FixRatio(GlobalConfig.DispCustomUserArx, GlobalConfig.DispCustomUserAry, videoProvider.BufferWidth, videoProvider.BufferHeight, out virtualWidth, out virtualHeight);
FixRatio(GlobalConfig.DispCustomUserArx, GlobalConfig.DispCustomUserAry,
videoProvider.BufferWidth, videoProvider.BufferHeight, out virtualWidth, out virtualHeight);
}
//TODO: it is bad that this is happening outside the filter chain
//the filter chain has the ability to add padding...
//for now, we have to have some hacks. this could be improved by refactoring the filter setup hacks to be in one place only though
//could the PADDING be done as filters too? that would be nice.
// TODO: it is bad that this is happening outside the filter chain
// the filter chain has the ability to add padding...
// for now, we have to have some hacks. this could be improved by refactoring the filter setup hacks to be in one place only though
// could the PADDING be done as filters too? that would be nice.
var fCoreScreenControl = CreateCoreScreenControl();
if(fCoreScreenControl != null)
if (fCoreScreenControl != null)
{
var sz = fCoreScreenControl.PresizeInput("default", new Size(bufferWidth, bufferHeight));
var sz = fCoreScreenControl.PresizeInput("default", new(bufferWidth, bufferHeight));
virtualWidth = bufferWidth = sz.Width;
virtualHeight = bufferHeight = sz.Height;
}
@ -619,21 +631,14 @@ namespace BizHawk.Client.Common
bufferWidth += padding.Horizontal;
bufferHeight += padding.Vertical;
//in case the user requested so much padding that the dimensions are now negative, just turn it to something small.
// in case the user requested so much padding that the dimensions are now negative, just turn it to something small.
if (virtualWidth < 1) virtualWidth = 1;
if (virtualHeight < 1) virtualHeight = 1;
if (bufferWidth < 1) bufferWidth = 1;
if (bufferHeight < 1) bufferHeight = 1;
// old stuff
var fvp = new FakeVideoProvider
{
BufferWidth = bufferWidth,
BufferHeight = bufferHeight,
VirtualWidth = virtualWidth,
VirtualHeight = virtualHeight
};
var fvp = new FakeVideoProvider(bufferWidth, bufferHeight, virtualWidth, virtualHeight);
Size chainOutsize;
if (arActive)
@ -643,51 +648,45 @@ namespace BizHawk.Client.Common
if (arInteger)
{
// ALERT COPYPASTE LAUNDROMAT
Vector2 AR = new(virtualWidth / (float) bufferWidth, virtualHeight / (float) bufferHeight);
float targetPar = AR.X / AR.Y;
var AR = new Vector2(virtualWidth / (float) bufferWidth, virtualHeight / (float) bufferHeight);
var targetPar = AR.X / AR.Y;
// this would malfunction for AR <= 0.5 or AR >= 2.0
// EDIT - in fact, we have AR like that coming from PSX, sometimes, so maybe we should solve this better
Vector2 PS = new Vector2(1, 1);
var PS = Vector2.One; // this would malfunction for AR <= 0.5 or AR >= 2.0
// here's how we define zooming, in this case:
// make sure each step is an increment of zoom for at least one of the dimensions (or maybe both of them)
// look for the increment which helps the AR the best
//TODO - this cant possibly support scale factors like 1.5x
//TODO - also, this might be messing up zooms and stuff, we might need to run this on the output size of the filter chain
for (int i = 1; i < zoom;i++)
// TODO - this cant possibly support scale factors like 1.5x
// TODO - also, this might be messing up zooms and stuff, we might need to run this on the output size of the filter chain
Span<Vector2> trials = stackalloc Vector2[3];
for (var i = 1; i < zoom; i++)
{
//would not be good to run this per frame, but it seems to only run when the resolution changes, etc.
Vector2[] trials =
{
PS + new Vector2(1, 0),
PS + new Vector2(0, 1),
PS + new Vector2(1, 1)
};
int bestIndex = -1;
float bestValue = 1000.0f;
for (int t = 0; t < trials.Length; t++)
// would not be good to run this per frame, but it seems to only run when the resolution changes, etc.
trials[0] = PS + Vector2.UnitX;
trials[1] = PS + Vector2.UnitY;
trials[2] = PS + Vector2.One;
var bestIndex = -1;
var bestValue = 1000.0f;
for (var t = 0; t < trials.Length; t++)
{
//I.
float testAr = trials[t].X / trials[t].Y;
var testAr = trials[t].X / trials[t].Y;
// II.
//Vector2 calc = Vector2.Multiply(trials[t], VS);
//float test_ar = calc.X / calc.Y;
// var calc = Vector2.Multiply(trials[t], VS);
// var test_ar = calc.X / calc.Y;
// not clear which approach is superior
float deviationLinear = Math.Abs(testAr - targetPar);
float deviationGeom = testAr / targetPar;
if (deviationGeom < 1)
{
deviationGeom = 1.0f / deviationGeom;
}
float value = deviationLinear;
if (value < bestValue)
var deviationLinear = Math.Abs(testAr - targetPar);
if (deviationLinear < bestValue)
{
bestIndex = t;
bestValue = value;
bestValue = deviationLinear;
}
}
@ -699,26 +698,26 @@ namespace BizHawk.Client.Common
}
}
chainOutsize = new Size((int)(bufferWidth * PS.X), (int)(bufferHeight * PS.Y));
chainOutsize = new((int)(bufferWidth * PS.X), (int)(bufferHeight * PS.Y));
}
else
{
// obey the AR, but allow free scaling: just zoom the virtual size
chainOutsize = new Size(virtualWidth * zoom, virtualHeight * zoom);
chainOutsize = new(virtualWidth * zoom, virtualHeight * zoom);
}
}
else
{
// ar_unity:
// just choose to zoom the buffer (make no effort to incorporate AR)
chainOutsize = new Size(bufferWidth * zoom, bufferHeight * zoom);
chainOutsize = new(bufferWidth * zoom, bufferHeight * zoom);
}
}
else
{
// !ar_active:
// just choose to zoom the buffer (make no effort to incorporate AR)
chainOutsize = new Size(bufferWidth * zoom, bufferHeight * zoom);
chainOutsize = new(bufferWidth * zoom, bufferHeight * zoom);
}
chainOutsize.Width += ClientExtraPadding.Left + ClientExtraPadding.Right;
@ -738,11 +737,10 @@ namespace BizHawk.Client.Common
// we need some other more sensible client size.
if (filterProgram == null)
{
return new Size(256, 192);
return new(256, 192);
}
var size = filterProgram.Filters.Last().FindOutput().SurfaceFormat.Size;
var size = filterProgram.Filters[filterProgram.Filters.Count - 1].FindOutput().SurfaceFormat.Size;
return size;
}
@ -795,10 +793,10 @@ namespace BizHawk.Client.Common
var vw = videoProvider.VirtualWidth;
var vh = videoProvider.VirtualHeight;
//TODO: it is bad that this is happening outside the filter chain
//the filter chain has the ability to add padding...
//for now, we have to have some hacks. this could be improved by refactoring the filter setup hacks to be in one place only though
//could the PADDING be done as filters too? that would be nice.
// TODO: it is bad that this is happening outside the filter chain
// the filter chain has the ability to add padding...
// for now, we have to have some hacks. this could be improved by refactoring the filter setup hacks to be in one place only though
// could the PADDING be done as filters too? that would be nice.
var fCoreScreenControl = CreateCoreScreenControl();
if(fCoreScreenControl != null)
{
@ -809,25 +807,26 @@ namespace BizHawk.Client.Common
if (GlobalConfig.DispFixAspectRatio)
{
if (GlobalConfig.DispManagerAR == EDispManagerAR.None)
switch (GlobalConfig.DispManagerAR)
{
vw = bufferWidth;
vh = bufferHeight;
}
if (GlobalConfig.DispManagerAR == EDispManagerAR.System)
{
//Already set
}
if (GlobalConfig.DispManagerAR == EDispManagerAR.CustomSize)
{
//not clear what any of these other options mean for "screen controlled" systems
vw = GlobalConfig.DispCustomUserARWidth;
vh = GlobalConfig.DispCustomUserARHeight;
}
if (GlobalConfig.DispManagerAR == EDispManagerAR.CustomRatio)
{
//not clear what any of these other options mean for "screen controlled" systems
FixRatio(GlobalConfig.DispCustomUserArx, GlobalConfig.DispCustomUserAry, videoProvider.BufferWidth, videoProvider.BufferHeight, out vw, out vh);
case EDispManagerAR.None:
vw = bufferWidth;
vh = bufferHeight;
break;
case EDispManagerAR.System:
// Already set
break;
case EDispManagerAR.CustomSize:
// not clear what any of these other options mean for "screen controlled" systems
vw = GlobalConfig.DispCustomUserARWidth;
vh = GlobalConfig.DispCustomUserARHeight;
break;
case EDispManagerAR.CustomRatio:
// not clear what any of these other options mean for "screen controlled" systems
FixRatio(GlobalConfig.DispCustomUserArx, GlobalConfig.DispCustomUserAry, videoProvider.BufferWidth, videoProvider.BufferHeight, out vw, out vh);
break;
default:
throw new InvalidOperationException();
}
}
@ -884,20 +883,9 @@ namespace BizHawk.Client.Common
fPresent.VirtualTextureSize = new(vw, vh);
fPresent.TextureSize = new(presenterTextureWidth, presenterTextureHeight);
fPresent.BackgroundColor = videoProvider.BackgroundColor;
fPresent.GuiRenderer = _renderer;
fPresent.Config_FixAspectRatio = GlobalConfig.DispFixAspectRatio;
fPresent.Config_FixScaleInteger = GlobalConfig.DispFixScaleInteger;
fPresent.Padding = (ClientExtraPadding.Left, ClientExtraPadding.Top, ClientExtraPadding.Right, ClientExtraPadding.Bottom);
fPresent.AutoPrescale = GlobalConfig.DispAutoPrescale;
fPresent.GL = _gl;
}
//POOPY. why are we delivering the GL context this way? such bad
if (filterProgram["CoreScreenControl"] is ScreenControlNDS fNDS)
{
fNDS.GuiRenderer = _renderer;
fNDS.GL = _gl;
}
filterProgram.Compile("default", chainInsize, chainOutsize, !job.Offscreen);
@ -938,12 +926,12 @@ namespace BizHawk.Client.Common
//GraphicsControl.Begin(); // CRITICAL POINT for yabause+GL
//TODO - auto-create and age these (and dispose when old)
int rtCounter = 0;
var rtCounter = 0;
// ReSharper disable once AccessToModifiedClosure
_currentFilterProgram.RenderTargetProvider = new DisplayManagerRenderTargetProvider(size => _shaderChainFrugalizers[rtCounter++].Get(size));
_gl.BeginScene();
RunFilterChainSteps(ref rtCounter, out var rtCurr, out var inFinalTarget);
RunFilterChainSteps(ref rtCounter, out var rtCurr, out _);
_gl.EndScene();
job.OffscreenBb = rtCurr.Texture2d.Resolve();
@ -977,19 +965,25 @@ namespace BizHawk.Client.Common
inFinalTarget = true;
break;
default:
throw new Exception();
throw new InvalidOperationException();
}
}
private void LoadCustomFont(Stream fontStream)
{
IntPtr data = Marshal.AllocCoTaskMem((int)fontStream.Length);
byte[] fontData = new byte[fontStream.Length];
fontStream.Read(fontData, 0, (int)fontStream.Length);
Marshal.Copy(fontData, 0, data, (int)fontStream.Length);
CustomFonts.AddMemoryFont(data, fontData.Length);
fontStream.Close();
Marshal.FreeCoTaskMem(data);
var data = Marshal.AllocCoTaskMem((int)fontStream.Length);
try
{
var fontData = new byte[fontStream.Length];
fontStream.Read(fontData, 0, (int)fontStream.Length);
Marshal.Copy(fontData, 0, data, (int)fontStream.Length);
CustomFonts.AddMemoryFont(data, fontData.Length);
}
finally
{
Marshal.FreeCoTaskMem(data);
fontStream.Close();
}
}
private readonly Dictionary<DisplaySurfaceID, IDisplaySurface> _apiHawkIDToSurface = new();
@ -1041,8 +1035,12 @@ namespace BizHawk.Client.Common
if (PeekApiHawkLockedSurface(kvp.Key) == null)
{
var surfLocked = LockApiHawkSurface(kvp.Key, true);
if (surfLocked != null) UnlockApiHawkSurface(surfLocked);
if (surfLocked != null)
{
UnlockApiHawkSurface(surfLocked);
}
}
_apiHawkSurfaceSets[kvp.Key].SetPending(null);
}
catch (InvalidOperationException)
@ -1056,8 +1054,16 @@ namespace BizHawk.Client.Common
/// <exception cref="InvalidOperationException">already unlocked</exception>
public void UnlockApiHawkSurface(IDisplaySurface surface)
{
if (surface is not DisplaySurface dispSurfaceImpl) throw new ArgumentException("don't mix " + nameof(IDisplaySurface) + " implementations!", nameof(surface));
if (!_apiHawkSurfaceToID.TryGetValue(dispSurfaceImpl, out var surfaceID)) throw new InvalidOperationException("Surface was not locked as a lua surface");
if (surface is not DisplaySurface dispSurfaceImpl)
{
throw new ArgumentException("don't mix " + nameof(IDisplaySurface) + " implementations!", nameof(surface));
}
if (!_apiHawkSurfaceToID.TryGetValue(dispSurfaceImpl, out var surfaceID))
{
throw new InvalidOperationException("Surface was not locked as a lua surface");
}
_apiHawkSurfaceToID.Remove(dispSurfaceImpl);
_apiHawkIDToSurface.Remove(surfaceID);
_apiHawkSurfaceSets[surfaceID].SetPending(dispSurfaceImpl);

View File

@ -1,6 +1,10 @@
using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Numerics;
using BizHawk.Client.Common.Filters;
using BizHawk.Bizware.BizwareGL;
@ -14,21 +18,22 @@ namespace BizHawk.Client.Common.FilterManager
public class SurfaceFormat
{
public SurfaceFormat(Size size) => Size = size;
public SurfaceFormat(Size size)
=> Size = size;
public Size Size { get; }
}
public class SurfaceState
public readonly ref struct SurfaceState
{
public SurfaceState() { }
public SurfaceState(SurfaceFormat surfaceFormat, SurfaceDisposition surfaceDisposition = SurfaceDisposition.Unspecified)
{
SurfaceFormat = surfaceFormat;
SurfaceDisposition = surfaceDisposition;
}
public SurfaceFormat SurfaceFormat { get; set; }
public SurfaceDisposition SurfaceDisposition { get; set; }
public readonly SurfaceFormat SurfaceFormat;
public readonly SurfaceDisposition SurfaceDisposition;
}
public interface IRenderTargetProvider
@ -38,7 +43,7 @@ namespace BizHawk.Client.Common.FilterManager
public class FilterProgram
{
private readonly Dictionary<string, BaseFilter> _filterNameIndex = new Dictionary<string, BaseFilter>();
private readonly Dictionary<string, BaseFilter> _filterNameIndex = new();
public readonly IList<BaseFilter> Filters = new List<BaseFilter>();
public readonly IList<ProgramStep> Program = new List<ProgramStep>();
@ -63,12 +68,12 @@ namespace BizHawk.Client.Common.FilterManager
public IGuiRenderer GuiRenderer;
public IGL GL;
public IRenderTargetProvider RenderTargetProvider;
public RenderTarget GetRenderTarget(string channel = "default") { return CurrRenderTarget; }
public RenderTarget CurrRenderTarget;
public RenderTarget GetTempTarget(int width, int height)
{
return RenderTargetProvider.Get(new Size(width, height));
return RenderTargetProvider.Get(new(width, height));
}
public void AddFilter(BaseFilter filter, string name = "")
@ -98,22 +103,24 @@ namespace BizHawk.Client.Common.FilterManager
/// </summary>
public Vector2 TransformPoint(string channel, Vector2 point)
{
foreach (var filter in Filters)
{
point = filter.TransformPoint(channel, point);
}
point = Filters.Aggregate(point, (current, filter) => filter.TransformPoint(channel, current));
// we COULD handle the case where the output size is 0,0, but it's not mathematically sensible
// it should be considered a bug to call this under those conditions
// in case the output size is zero, transform all points to zero, since the above maths may have malfunctioned
////var size = Filters[Filters.Count - 1].FindOutput().SurfaceFormat.Size;
////if (size.Width == 0) point.X = 0;
////if (size.Height == 0) point.Y = 0;
#if false
var size = Filters[Filters.Count - 1].FindOutput().SurfaceFormat.Size;
if (size.Width == 0) point.X = 0;
if (size.Height == 0) point.Y = 0;
#endif
return point;
}
public class ProgramStep
public readonly struct ProgramStep
{
public readonly ProgramStepType Type;
public readonly object Args;
public readonly string Comment;
public ProgramStep(ProgramStepType type, object args, string comment = null)
{
Type = type;
@ -121,9 +128,6 @@ namespace BizHawk.Client.Common.FilterManager
Comment = comment;
}
public ProgramStepType Type;
public object Args;
public string Comment;
public override string ToString()
{
return Type switch
@ -148,30 +152,36 @@ namespace BizHawk.Client.Common.FilterManager
}
//propagate input size forwards through filter chain to allow a 'flex' filter to determine what its input will be
Size presize = inSize;
var presize = inSize;
foreach (var filter in Filters)
{
presize = filter.PresizeInput(channel, presize);
}
//propagate output size backwards through filter chain to allow a 'flex' filter to determine its output based on the desired output needs
// propagate output size backwards through filter chain to allow a 'flex' filter to determine its output based on the desired output needs
presize = outsize;
for (int i = Filters.Count - 1; i >= 0; i--)
for (var i = Filters.Count - 1; i >= 0; i--)
{
var filter = Filters[i];
presize = filter.PresizeOutput(channel, presize);
}
SurfaceState currState = null;
var obtainedFirstOutput = false;
var currState = new SurfaceState(null);
for (int i = 0; i < Filters.Count; i++)
for (var i = 0; i < Filters.Count; i++)
{
BaseFilter f = Filters[i];
var f = Filters[i];
// check whether this filter needs input. if so, notify it of the current pipeline state
var iosi = f.FindInput(channel);
if (iosi != null)
{
if (!obtainedFirstOutput)
{
throw new InvalidOperationException("Obtained input before first output in filter chain");
}
iosi.SurfaceFormat = currState.SurfaceFormat;
f.SetInputFormat(channel, currState);
@ -180,23 +190,29 @@ namespace BizHawk.Client.Common.FilterManager
continue;
}
// check if the desired disposition needs to change from texture to render target
// (if so, insert a render filter)
if (iosi.SurfaceDisposition == SurfaceDisposition.RenderTarget && currState.SurfaceDisposition == SurfaceDisposition.Texture)
switch (iosi.SurfaceDisposition)
{
var renderer = new Render();
Filters.Insert(i, renderer);
Compile(channel, inSize, outsize, finalTarget);
return;
}
// check if the desired disposition needs to change from a render target to a texture
// (if so, the current render target gets resolved, and made no longer current
else if (iosi.SurfaceDisposition == SurfaceDisposition.Texture && currState.SurfaceDisposition == SurfaceDisposition.RenderTarget)
{
var resolver = new Resolve();
Filters.Insert(i, resolver);
Compile(channel, inSize, outsize, finalTarget);
return;
// check if the desired disposition needs to change from texture to render target
// (if so, insert a render filter)
case SurfaceDisposition.RenderTarget when currState.SurfaceDisposition == SurfaceDisposition.Texture:
{
var renderer = new Render();
Filters.Insert(i, renderer);
Compile(channel, inSize, outsize, finalTarget);
return;
}
// check if the desired disposition needs to change from a render target to a texture
// (if so, the current render target gets resolved, and made no longer current
case SurfaceDisposition.Texture when currState.SurfaceDisposition == SurfaceDisposition.RenderTarget:
{
var resolver = new Resolve();
Filters.Insert(i, resolver);
Compile(channel, inSize, outsize, finalTarget);
return;
}
case SurfaceDisposition.Unspecified:
default:
break;
}
}
@ -204,13 +220,10 @@ namespace BizHawk.Client.Common.FilterManager
iosi = f.FindOutput(channel);
if (iosi != null)
{
if (currState == null)
if (!obtainedFirstOutput)
{
currState = new SurfaceState
{
SurfaceFormat = iosi.SurfaceFormat,
SurfaceDisposition = iosi.SurfaceDisposition
};
currState = new(iosi.SurfaceFormat, iosi.SurfaceDisposition);
obtainedFirstOutput = true;
}
else
{
@ -220,29 +233,36 @@ namespace BizHawk.Client.Common.FilterManager
iosi.SurfaceDisposition = currState.SurfaceDisposition;
}
bool newTarget = false;
var newTarget = false;
if (iosi.SurfaceFormat.Size != currState.SurfaceFormat.Size)
{
newTarget = true;
else if (currState.SurfaceDisposition == SurfaceDisposition.Texture && iosi.SurfaceDisposition == SurfaceDisposition.RenderTarget)
}
else if (currState.SurfaceDisposition == SurfaceDisposition.Texture &&
iosi.SurfaceDisposition == SurfaceDisposition.RenderTarget)
{
newTarget = true;
}
if (newTarget)
{
currState = new SurfaceState();
iosi.SurfaceFormat = currState.SurfaceFormat = iosi.SurfaceFormat;
iosi.SurfaceDisposition = currState.SurfaceDisposition = iosi.SurfaceDisposition;
Program.Add(new ProgramStep(ProgramStepType.NewTarget, currState.SurfaceFormat.Size));
currState = new(iosi.SurfaceFormat, iosi.SurfaceDisposition);
Program.Add(new(ProgramStepType.NewTarget, currState.SurfaceFormat.Size));
}
else
{
currState.SurfaceDisposition = iosi.SurfaceDisposition;
currState = new(currState.SurfaceFormat, iosi.SurfaceDisposition);
}
}
}
Program.Add(new ProgramStep(ProgramStepType.Run, i, f.GetType().Name));
Program.Add(new(ProgramStepType.Run, i, f.GetType().Name));
}
} // filter loop
if (!obtainedFirstOutput)
{
throw new InvalidOperationException("Did not obtain any output in filter chain");
}
// if the current output disposition is a texture, we need to render it
if (currState.SurfaceDisposition == SurfaceDisposition.Texture)
@ -256,15 +276,14 @@ namespace BizHawk.Client.Common.FilterManager
// patch the program so that the final RenderTarget set operation is the framebuffer instead
if (finalTarget)
{
for (int i = Program.Count - 1; i >= 0; i--)
for (var i = Program.Count - 1; i >= 0; i--)
{
var ps = Program[i];
if (ps.Type == ProgramStepType.NewTarget)
{
var size = (Size)ps.Args;
Debug.Assert(size == outsize);
ps.Type = ProgramStepType.FinalTarget;
ps.Args = size;
Program[i] = new(ProgramStepType.FinalTarget, size, ps.Comment);
break;
}
}

View File

@ -1,8 +1,9 @@
using System.Collections.Generic;
using System.Drawing;
using BizHawk.Client.Common.FilterManager;
using System.Numerics;
using BizHawk.Bizware.BizwareGL;
using BizHawk.Client.Common.FilterManager;
// Here's how to make a filter:
// 1. Reset your state entirely in Initialize().
@ -19,13 +20,30 @@ namespace BizHawk.Client.Common.Filters
public class BaseFilter
{
// initialization stuff
public void BeginInitialization(FilterProgram program) { _ioSurfaceInfos.Clear(); FilterProgram = program; }
public virtual void Initialize() { }
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; set; }
public void BeginInitialization(FilterProgram program)
{
_ioSurfaceInfos.Clear();
FilterProgram = program;
}
public virtual void Initialize()
{
}
public virtual Size PresizeInput(string channel, Size size)
=> size;
public virtual Size PresizeOutput(string channel, Size size)
=> size;
// TODO - why a different param order than DeclareOutput?
public virtual void SetInputFormat(string channel, SurfaceState state)
{
}
public Dictionary<string, object> Parameters = new();
public bool IsNop { get; protected set; }
// runtime signals
public virtual Vector2 UntransformPoint(string channel, Vector2 point)
@ -38,6 +56,7 @@ namespace BizHawk.Client.Common.Filters
point.X *= input.SurfaceFormat.Size.Width / (float)output.SurfaceFormat.Size.Width;
point.Y *= input.SurfaceFormat.Size.Height / (float)output.SurfaceFormat.Size.Height;
}
return point;
}
@ -51,6 +70,7 @@ namespace BizHawk.Client.Common.Filters
point.X *= output.SurfaceFormat.Size.Width / (float)input.SurfaceFormat.Size.Width;
point.Y *= output.SurfaceFormat.Size.Height / (float)input.SurfaceFormat.Size.Height;
}
return point;
}
@ -58,8 +78,13 @@ namespace BizHawk.Client.Common.Filters
{
InputTexture = tex;
}
public virtual void Run() { }
public Texture2d GetOutput() => _outputTexture;
public virtual void Run()
{
}
public Texture2d GetOutput()
=> _outputTexture;
// filter actions
protected void YieldOutput(Texture2d tex)
@ -121,20 +146,12 @@ namespace BizHawk.Client.Common.Filters
return iosi;
}
private readonly List<IOSurfaceInfo> _ioSurfaceInfos = new List<IOSurfaceInfo>();
private readonly List<IOSurfaceInfo> _ioSurfaceInfos = new();
private IOSurfaceInfo FindIOSurfaceInfo(string channel, SurfaceDirection direction)
{
foreach (var iosi in _ioSurfaceInfos)
{
if (iosi.Channel == channel && iosi.SurfaceDirection == direction)
{
return iosi;
}
}
return null;
return _ioSurfaceInfos.Find(iosi => iosi.Channel == channel && iosi.SurfaceDirection == direction);
}
public class IOSurfaceInfo

View File

@ -1,5 +1,6 @@
using System;
using System.Drawing;
using System.Numerics;
using BizHawk.Bizware.BizwareGL;
using BizHawk.Client.Common.FilterManager;
@ -28,19 +29,21 @@ namespace BizHawk.Client.Common.Filters
/// <summary>
/// In case you want to do it yourself
/// </summary>
public LetterboxingLogic() { }
public LetterboxingLogic()
{
}
// 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)
{
int textureWidth = textureSize.Width;
int textureHeight = textureSize.Height;
int virtualWidth = virtualSize.Width;
int virtualHeight = virtualSize.Height;
var textureWidth = textureSize.Width;
var textureHeight = textureSize.Height;
var virtualWidth = virtualSize.Width;
var virtualHeight = virtualSize.Height;
//zero 02-jun-2014 - we passed these in, but ignored them. kind of weird..
int oldSourceWidth = sourceWidth;
int oldSourceHeight = sourceHeight;
// zero 02-jun-2014 - we passed these in, but ignored them. kind of weird..
var oldSourceWidth = sourceWidth;
var oldSourceHeight = sourceHeight;
sourceWidth = virtualWidth;
sourceHeight = virtualHeight;
@ -50,8 +53,8 @@ namespace BizHawk.Client.Common.Filters
maintainInteger = false;
}
float widthScale = (float)targetWidth / sourceWidth;
float heightScale = (float)targetHeight / sourceHeight;
var widthScale = (float)targetWidth / sourceWidth;
var heightScale = (float)targetHeight / sourceHeight;
if (maintainAspect
// zero 20-jul-2014 - hacks upon hacks, this function needs rewriting
@ -68,61 +71,60 @@ namespace BizHawk.Client.Common.Filters
// apply the zooming algorithm (pasted and reworked, for now)
// ALERT COPYPASTE LAUNDROMAT
Vector2 AR = new(virtualWidth / (float) textureWidth, virtualHeight / (float) textureHeight);
float targetPar = (AR.X / AR.Y);
Vector2 PS = new Vector2(1, 1); //this would malfunction for AR <= 0.5 or AR >= 2.0
var AR = new Vector2(virtualWidth / (float) textureWidth, virtualHeight / (float) textureHeight);
var targetPar = AR.X / AR.Y;
var PS = Vector2.One; // this would malfunction for AR <= 0.5 or AR >= 2.0
for(;;)
Span<Vector2> trials = stackalloc Vector2[3];
Span<bool> trialsLimited = stackalloc bool[3];
while (true)
{
// TODO - would be good not to run this per frame....
Vector2[] trials =
{
PS + new Vector2(1, 0),
PS + new Vector2(0, 1),
PS + new Vector2(1, 1)
};
trials[0] = PS + Vector2.UnitX;
trials[1] = PS + Vector2.UnitY;
trials[2] = PS + Vector2.One;
bool[] trialsLimited = { false, false, false };
int bestIndex = -1;
float bestValue = 1000.0f;
for (int t = 0; t < trials.Length; t++)
var bestIndex = -1;
var bestValue = 1000.0f;
for (var t = 0; t < trials.Length; t++)
{
Vector2 vTrial = trials[t];
var vTrial = trials[t];
trialsLimited[t] = false;
//check whether this is going to exceed our allotted area
int testVw = (int)(vTrial.X * textureWidth);
int testVh = (int)(vTrial.Y * textureHeight);
var testVw = (int)(vTrial.X * textureWidth);
var testVh = (int)(vTrial.Y * textureHeight);
if (testVw > targetWidth) trialsLimited[t] = true;
if (testVh > targetHeight) trialsLimited[t] = true;
// I.
float testAr = vTrial.X / vTrial.Y;
var testAr = vTrial.X / vTrial.Y;
// II.
// Vector2 calc = Vector2.Multiply(trials[t], VS);
// float test_ar = calc.X / calc.Y;
// var calc = Vector2.Multiply(trials[t], VS);
// var test_ar = calc.X / calc.Y;
// not clear which approach is superior
float deviationLinear = Math.Abs(testAr - targetPar);
float deviationGeom = testAr / targetPar;
if (deviationGeom < 1) deviationGeom = 1.0f / deviationGeom;
float value = deviationLinear;
if (value < bestValue)
var deviationLinear = Math.Abs(testAr - targetPar);
if (deviationLinear < bestValue)
{
bestIndex = t;
bestValue = value;
bestValue = deviationLinear;
}
}
// last result was best, so bail out
if (bestIndex == -1)
{
break;
}
// if the winner ran off the edge, bail out
if (trialsLimited[bestIndex])
{
break;
}
PS = trials[bestIndex];
}
@ -132,8 +134,10 @@ namespace BizHawk.Client.Common.Filters
// vh = (int)(PS.Y * oldSourceHeight);
vw = (int)(PS.X * sourceWidth);
vh = (int)(PS.Y * sourceHeight);
#if false
widthScale = PS.X;
heightScale = PS.Y;
#endif
}
else
{
@ -154,17 +158,20 @@ namespace BizHawk.Client.Common.Filters
vh = targetHeight;
}
//determine letterboxing parameters
// determine letterboxing parameters
vx = (targetWidth - vw) / 2;
vy = (targetHeight - vh) / 2;
// 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 doesn't use the LL.WidthScale, so this is unlikely to be breaking anything old that depends on it
// WidthScale = widthScale;
// HeightScale = heightScale;
#if false
WidthScale = widthScale;
HeightScale = heightScale;
#else
WidthScale = (float)vw / oldSourceWidth;
HeightScale = (float)vh / oldSourceHeight;
#endif
}
}
@ -173,34 +180,26 @@ namespace BizHawk.Client.Common.Filters
/// </summary>
public class ScreenControlNDS : BaseFilter
{
public IGL GL;
public IGuiRenderer GuiRenderer;
private readonly NDS _nds;
private readonly NDS nds;
// TODO: actually use this
#if false
private bool Nop = false;
#endif
//TODO: actually use this
private bool nop = false;
// matrices used for transforming screens
private Matrix4x4 matTop, matBot;
private Matrix4x4 matTopInvert, matBotInvert;
//matrices used for transforming screens
private Matrix4 matTop, matBot;
private Matrix4 matTopInvert, matBotInvert;
//final output area size
// final output area size
private Size outputSize;
private static float Round(float f) { return (float)Math.Round(f); }
//TODO: put somewhere in extension methods useful for fixing deficiencies in opentk matrix types
private static Vector2 Transform(Matrix4 m, Vector2 v)
{
var r = new Vector4(v.X,v.Y,0,1) * m;
return new Vector2(r.X, r.Y);
}
private static float Round(float f)
=> (float)Math.Round(f);
public ScreenControlNDS(NDS nds)
{
//not sure if we actually need this nds instance yet
this.nds = nds;
_nds = nds;
}
public override void Initialize()
@ -211,63 +210,65 @@ namespace BizHawk.Client.Common.Filters
private void CrunchNumbers()
{
MatrixStack top = new MatrixStack(), bot = new MatrixStack();
MatrixStack top = new(), bot = new();
//-----------------------------------
//set up transforms for each screen based on screen control values
//this will be TRICKY depending on how many features we have, but once it's done, everything should be easy
// set up transforms for each screen based on screen control values
// this will be TRICKY depending on how many features we have, but once it's done, everything should be easy
var settings = nds.GetSettings();
var settings = _nds.GetSettings();
//gap only applies to vertical, I guess
if (settings.ScreenLayout == NDS.ScreenLayoutKind.Vertical)
switch (settings.ScreenLayout)
{
bot.Translate(0, 192);
bot.Translate(0, settings.ScreenGap);
}
else if (settings.ScreenLayout == NDS.ScreenLayoutKind.Horizontal)
{
bot.Translate(256, 0);
}
else if (settings.ScreenLayout == NDS.ScreenLayoutKind.Top)
{
//do nothing here, we'll discard bottom screen
//gap only applies to vertical, I guess
case NDS.ScreenLayoutKind.Vertical:
bot.Translate(0, 192);
bot.Translate(0, settings.ScreenGap);
break;
case NDS.ScreenLayoutKind.Horizontal:
bot.Translate(256, 0);
break;
case NDS.ScreenLayoutKind.Top:
case NDS.ScreenLayoutKind.Bottom:
// do nothing here, we'll discard the other screen
break;
default:
throw new InvalidOperationException();
}
//this doesn't make any sense, it's likely to be too much for a monitor to gracefully handle too
// this doesn't make any sense, it's likely to be too much for a monitor to gracefully handle too
if (settings.ScreenLayout != NDS.ScreenLayoutKind.Horizontal)
{
int rot = 0;
if (settings.ScreenRotation == NDS.ScreenRotationKind.Rotate90) rot = 90;
if (settings.ScreenRotation == NDS.ScreenRotationKind.Rotate180) rot = 180;
if (settings.ScreenRotation == NDS.ScreenRotationKind.Rotate270) rot = 270;
var rot = settings.ScreenRotation switch
{
NDS.ScreenRotationKind.Rotate90 => 90,
NDS.ScreenRotationKind.Rotate180 => 180,
NDS.ScreenRotationKind.Rotate270 => 270,
_ => 0
};
top.RotateZ(rot);
bot.RotateZ(rot);
}
//-----------------------------------
//TODO: refactor some of the below into a class that doesn't require having top and bottom replica code
// TODO: refactor some of the below into a class that doesn't require having top and bottom replica code
matTop = top.Top;
matBot = bot.Top;
matTopInvert = matTop.Inverted();
matBotInvert = matBot.Inverted();
//apply transforms from standard input screen positions to output screen positions
Vector2 top_TL = Transform(matTop, new Vector2(0, 0));
Vector2 top_TR = Transform(matTop, new Vector2(256, 0));
Vector2 top_BL = Transform(matTop, new Vector2(0, 192));
Vector2 top_BR = Transform(matTop, new Vector2(256, 192));
Vector2 bot_TL = Transform(matBot, new Vector2(0, 0));
Vector2 bot_TR = Transform(matBot, new Vector2(256, 0));
Vector2 bot_BL = Transform(matBot, new Vector2(0, 192));
Vector2 bot_BR = Transform(matBot, new Vector2(256, 192));
// apply transforms from standard input screen positions to output screen positions
var top_TL = Vector2.Transform(new(0, 0), matTop);
var top_TR = Vector2.Transform(new(256, 0), matTop);
var top_BL = Vector2.Transform(new(0, 192), matTop);
var top_BR = Vector2.Transform(new(256, 192), matTop);
var bot_TL = Vector2.Transform(new(0, 0), matBot);
var bot_TR = Vector2.Transform(new(256, 0), matBot);
var bot_BL = Vector2.Transform(new(0, 192), matBot);
var bot_BR = Vector2.Transform(new(256, 192), matBot);
//in case of math errors in the transforms, we'll round this stuff.. although...
//we're gonna use matrix transforms for drawing later, so it isn't extremely helpful
// in case of math errors in the transforms, we'll round this stuff.. although...
// we're gonna use matrix transforms for drawing later, so it isn't extremely helpful
//TODO - need more consideration of numerical precision here, because the typical case should be rock solid
// TODO - need more consideration of numerical precision here, because the typical case should be rock solid
top_TL.X = Round(top_TL.X); top_TL.Y = Round(top_TL.Y);
top_TR.X = Round(top_TR.X); top_TR.Y = Round(top_TR.Y);
top_BL.X = Round(top_BL.X); top_BL.Y = Round(top_BL.Y);
@ -277,13 +278,15 @@ namespace BizHawk.Client.Common.Filters
bot_BL.X = Round(bot_BL.X); bot_BL.Y = Round(bot_BL.Y);
bot_BR.X = Round(bot_BR.X); bot_BR.Y = Round(bot_BR.Y);
////precalculate some useful metrics
//top_width = (int)(top_TR.X - top_TL.X);
//top_height = (int)(top_BR.Y - top_TR.Y);
//bot_width = (int)(bot_TR.X - bot_TL.X);
//bot_height = (int)(bot_BR.Y - bot_TR.Y);
#if false
// precalculate some useful metrics
top_width = (int)(top_TR.X - top_TL.X);
top_height = (int)(top_BR.Y - top_TR.Y);
bot_width = (int)(bot_TR.X - bot_TL.X);
bot_height = (int)(bot_BR.Y - bot_TR.Y);
#endif
//the size can now be determined in a kind of fluffily magical way by transforming edges and checking the bounds
// the size can now be determined in a kind of fluffily magical way by transforming edges and checking the bounds
float fxmin = 100000, fymin = 100000, fxmax = -100000, fymax = -100000;
if (settings.ScreenLayout != NDS.ScreenLayoutKind.Bottom)
{
@ -300,24 +303,37 @@ namespace BizHawk.Client.Common.Filters
fymax = Math.Max(Math.Max(Math.Max(Math.Max(bot_TL.Y, bot_TR.Y), bot_BL.Y), bot_BR.Y), fymax);
}
//relocate whatever we got back into the viewable area
// relocate whatever we got back into the viewable area
top.Translate(-fxmin, -fymin);
bot.Translate(-fxmin, -fymin);
matTop = top.Top;
matBot = bot.Top;
//do some more rounding
for (int r = 0; r < 4; r++)
for (int c = 0; c < 4; c++)
// do some more rounding
unsafe
{
fixed (Matrix4x4* matTopP = &matTop, matBotP = &matBot)
{
if (Math.Abs(matTop[r, c]) < 0.0000001f) matTop[r, c] = 0;
if (Math.Abs(matBot[r, c]) < 0.0000001f) matBot[r, c] = 0;
float* matTopF = (float*)matTopP, matBotF = (float*)matBotP;
for (var i = 0; i < 4 * 4; i++)
{
if (Math.Abs(matTopF[i]) < 0.0000001f)
{
matTopF[i] = 0;
}
if (Math.Abs(matBotF[i]) < 0.0000001f)
{
matBotF[i] = 0;
}
}
}
}
matTopInvert = matTop.Inverted();
matBotInvert = matBot.Inverted();
Matrix4x4.Invert(matTop, out matTopInvert);
Matrix4x4.Invert(matBot, out matBotInvert);
outputSize = new Size((int)(fxmax-fxmin), (int)(fymax-fymin));
outputSize = new((int)(fxmax - fxmin), (int)(fymax - fymin));
}
public override Size PresizeInput(string channel, Size size)
@ -335,86 +351,100 @@ namespace BizHawk.Client.Common.Filters
public override void SetInputFormat(string channel, SurfaceState state)
{
CrunchNumbers();
var ss = new SurfaceState(new SurfaceFormat(outputSize), SurfaceDisposition.RenderTarget);
var ss = new SurfaceState(new(outputSize), SurfaceDisposition.RenderTarget);
DeclareOutput(ss, channel);
}
public override Vector2 UntransformPoint(string channel, Vector2 point)
{
var settings = nds.GetSettings();
bool invert = settings.ScreenInvert
var settings = _nds.GetSettings();
var invert = settings.ScreenInvert
&& settings.ScreenLayout != NDS.ScreenLayoutKind.Top
&& settings.ScreenLayout != NDS.ScreenLayoutKind.Bottom;
point = Transform(invert ? matTopInvert : matBotInvert, point);
point = Vector2.Transform(point, invert ? matTopInvert : matBotInvert);
//hack to accomodate input tracking system's float-point sense (based on the core's VideoBuffer height)
//actually, this is needed for a reason similar to the "TouchScreenStart" that I removed.
//So, something like that needs readding if we're to get rid of this hack.
//(should redo it as a mouse coordinate offset or something.. but the key is to pipe it to the point where this is needed.. that is where MainForm does DisplayManager.UntransformPoint()
// hack to accomodate input tracking system's float-point sense (based on the core's VideoBuffer height)
// actually, this is needed for a reason similar to the "TouchScreenStart" that I removed.
// So, something like that needs readding if we're to get rid of this hack.
// (should redo it as a mouse coordinate offset or something.. but the key is to pipe it to the point where this is needed.. that is where MainForm does DisplayManager.UntransformPoint()
point.Y *= 2;
//in case we're in this layout, we get confused, so fix it
// in case we're in this layout, we get confused, so fix it
if (settings.ScreenLayout == NDS.ScreenLayoutKind.Top) point = new(0.0f, 0.0f);
//TODO: we probably need more subtle logic here.
//some capability to return -1,-1 perhaps in case the cursor is nowhere.
//not sure about that
// TODO: we probably need more subtle logic here.
// some capability to return -1,-1 perhaps in case the cursor is nowhere.
// not sure about that
return point;
}
public override Vector2 TransformPoint(string channel, Vector2 point)
{
point = Transform(matBot, point);
return point;
return Vector2.Transform(point, matTop);
}
public override void Run()
{
if (nop)
#if false
if (Nop)
{
return;
}
#endif
//TODO: this could be more efficient (draw only in gap)
GL.SetClearColor(Color.Black);
GL.Clear(ClearBufferMask.ColorBufferBit);
// TODO: this could be more efficient (draw only in gap)
FilterProgram.GL.SetClearColor(Color.Black);
FilterProgram.GL.Clear(ClearBufferMask.ColorBufferBit);
FilterProgram.GuiRenderer.Begin(outputSize);
GuiRenderer.SetBlendState(GL.BlendNoneCopy);
FilterProgram.GuiRenderer.SetBlendState(FilterProgram.GL.BlendNoneCopy);
//TODO: may depend on input, or other factors, not sure yet
//watch out though... if we filter linear, then screens will bleed into each other.
//so we will have to break them into render targets first.
// TODO: may depend on input, or other factors, not sure yet
// watch out though... if we filter linear, then screens will bleed into each other.
// so we will have to break them into render targets first.
InputTexture.SetFilterNearest();
//draw screens
bool renderTop = false;
bool renderBottom = false;
var settings = nds.GetSettings();
if (settings.ScreenLayout == NDS.ScreenLayoutKind.Bottom) renderBottom = true;
if (settings.ScreenLayout == NDS.ScreenLayoutKind.Top) renderTop = true;
if (settings.ScreenLayout == NDS.ScreenLayoutKind.Vertical) renderTop = renderBottom = true;
if (settings.ScreenLayout == NDS.ScreenLayoutKind.Horizontal) renderTop = renderBottom = true;
var renderTop = false;
var renderBottom = false;
var settings = _nds.GetSettings();
switch (settings.ScreenLayout)
{
case NDS.ScreenLayoutKind.Bottom:
renderBottom = true;
break;
case NDS.ScreenLayoutKind.Top:
renderTop = true;
break;
case NDS.ScreenLayoutKind.Vertical:
case NDS.ScreenLayoutKind.Horizontal:
renderTop = renderBottom = true;
break;
default:
throw new InvalidOperationException();
}
bool invert = settings.ScreenInvert && renderTop && renderBottom;
var invert = settings.ScreenInvert && renderTop && renderBottom;
if (renderTop)
{
GuiRenderer.Modelview.Push();
GuiRenderer.Modelview.PreMultiplyMatrix(invert ? matBot : matTop);
GuiRenderer.DrawSubrect(InputTexture, 0, 0, 256, 192, 0.0f, 0.0f, 1.0f, 0.5f);
GuiRenderer.Modelview.Pop();
FilterProgram.GuiRenderer.Modelview.Push();
FilterProgram.GuiRenderer.Modelview.PreMultiplyMatrix(invert ? matBot : matTop);
FilterProgram.GuiRenderer.DrawSubrect(InputTexture, 0, 0, 256, 192, 0.0f, 0.0f, 1.0f, 0.5f);
FilterProgram.GuiRenderer.Modelview.Pop();
}
if (renderBottom)
{
GuiRenderer.Modelview.Push();
GuiRenderer.Modelview.PreMultiplyMatrix(invert ? matTop : matBot);
GuiRenderer.DrawSubrect(InputTexture, 0, 0, 256, 192, 0.0f, 0.5f, 1.0f, 1.0f);
GuiRenderer.Modelview.Pop();
FilterProgram.GuiRenderer.Modelview.Push();
FilterProgram.GuiRenderer.Modelview.PreMultiplyMatrix(invert ? matTop : matBot);
FilterProgram.GuiRenderer.DrawSubrect(InputTexture, 0, 0, 256, 192, 0.0f, 0.5f, 1.0f, 1.0f);
FilterProgram.GuiRenderer.Modelview.Pop();
}
GuiRenderer.End();
FilterProgram.GuiRenderer.End();
}
}
@ -505,12 +535,8 @@ namespace BizHawk.Client.Common.Filters
private Size OutputSize, InputSize;
public Size TextureSize, VirtualTextureSize;
public int BackgroundColor;
public bool AutoPrescale;
public IGuiRenderer GuiRenderer;
public IGL GL;
private bool nop;
private bool Nop;
private LetterboxingLogic LL;
private Size ContentSize;
public bool Config_FixAspectRatio, Config_FixScaleInteger, Config_PadOnly;
@ -522,7 +548,7 @@ namespace BizHawk.Client.Common.Filters
public override void Initialize()
{
DeclareInput();
nop = false;
Nop = false;
}
public override Size PresizeOutput(string channel, Size size)
@ -533,6 +559,7 @@ namespace BizHawk.Client.Common.Filters
size.Height = LL.vh;
return size;
}
return base.PresizeOutput(channel, size);
}
@ -543,8 +570,8 @@ namespace BizHawk.Client.Common.Filters
if (Config_PadOnly)
{
//TODO - redundant fix
LL = new LetterboxingLogic();
// TODO - redundant fix
LL = new();
LL.vx += Padding.Left;
LL.vy += Padding.Top;
LL.vw = size.Width;
@ -552,7 +579,7 @@ namespace BizHawk.Client.Common.Filters
}
else
{
LL = new LetterboxingLogic(Config_FixAspectRatio, Config_FixScaleInteger, OutputSize.Width, OutputSize.Height, size.Width, size.Height, TextureSize, VirtualTextureSize);
LL = new(Config_FixAspectRatio, Config_FixScaleInteger, OutputSize.Width, OutputSize.Height, size.Width, size.Height, TextureSize, VirtualTextureSize);
LL.vx += Padding.Left;
LL.vy += Padding.Top;
}
@ -562,22 +589,20 @@ namespace BizHawk.Client.Common.Filters
public override void SetInputFormat(string channel, SurfaceState state)
{
bool need = state.SurfaceFormat.Size != OutputSize || FilterOption != eFilterOption.None;
var need = state.SurfaceFormat.Size != OutputSize || FilterOption != eFilterOption.None;
if (!need)
{
nop = true;
ContentSize = state.SurfaceFormat.Size;
Nop = true;
return;
}
FindInput().SurfaceDisposition = SurfaceDisposition.Texture;
DeclareOutput(new SurfaceState(new SurfaceFormat(OutputSize), SurfaceDisposition.RenderTarget));
DeclareOutput(new SurfaceState(new(OutputSize), SurfaceDisposition.RenderTarget));
InputSize = state.SurfaceFormat.Size;
if (Config_PadOnly)
{
//TODO - redundant fix
LL = new LetterboxingLogic();
// TODO - redundant fix
LL = new();
LL.vx += Padding.Left;
LL.vy += Padding.Top;
LL.vw = InputSize.Width;
@ -587,26 +612,28 @@ namespace BizHawk.Client.Common.Filters
}
else
{
int ow = OutputSize.Width;
int oh = OutputSize.Height;
var ow = OutputSize.Width;
var oh = OutputSize.Height;
ow -= Padding.Left + Padding.Right;
oh -= Padding.Top + Padding.Bottom;
LL = new LetterboxingLogic(Config_FixAspectRatio, Config_FixScaleInteger, ow, oh, InputSize.Width, InputSize.Height, TextureSize, VirtualTextureSize);
LL = new(Config_FixAspectRatio, Config_FixScaleInteger, ow, oh, InputSize.Width, InputSize.Height, TextureSize, VirtualTextureSize);
LL.vx += Padding.Left;
LL.vy += Padding.Top;
}
ContentSize = new Size(LL.vw,LL.vh);
if (InputSize == OutputSize) //any reason we need to check vx and vy?
IsNop = true;
if (InputSize == OutputSize) // any reason we need to check vx and vy?
{
Nop = true;
}
}
public Size GetContentSize() => ContentSize;
public override Vector2 UntransformPoint(string channel, Vector2 point)
{
if (nop)
if (Nop)
{
return point;
}
point.X -= LL.vx;
point.Y -= LL.vy;
point.X /= LL.WidthScale;
@ -616,8 +643,11 @@ namespace BizHawk.Client.Common.Filters
public override Vector2 TransformPoint(string channel, Vector2 point)
{
if (nop)
if (Nop)
{
return point;
}
point.X *= LL.WidthScale;
point.Y *= LL.HeightScale;
point.X += LL.vx;
@ -627,31 +657,37 @@ namespace BizHawk.Client.Common.Filters
public override void Run()
{
if (nop)
if (Nop)
{
return;
}
GL.SetClearColor(Color.FromArgb(BackgroundColor));
GL.Clear(ClearBufferMask.ColorBufferBit);
FilterProgram.GL.SetClearColor(Color.FromArgb(BackgroundColor));
FilterProgram.GL.Clear(ClearBufferMask.ColorBufferBit);
GuiRenderer.Begin(OutputSize.Width, OutputSize.Height);
GuiRenderer.SetBlendState(GL.BlendNoneCopy);
FilterProgram.GuiRenderer.Begin(OutputSize.Width, OutputSize.Height);
FilterProgram.GuiRenderer.SetBlendState(FilterProgram.GL.BlendNoneCopy);
if(FilterOption != eFilterOption.None)
if (FilterOption != eFilterOption.None)
{
InputTexture.SetFilterLinear();
}
else
{
InputTexture.SetFilterNearest();
}
if (FilterOption == eFilterOption.Bicubic)
{
//this was handled earlier by another filter
// this was handled earlier by another filter
}
GuiRenderer.Draw(InputTexture, LL.vx, LL.vy, LL.vw, LL.vh);
GuiRenderer.End();
FilterProgram.GuiRenderer.Draw(InputTexture, LL.vx, LL.vy, LL.vw, LL.vh);
FilterProgram.GuiRenderer.End();
}
}
//TODO - turn this into a NOP at 1x, just in case something accidentally activates it with 1x
// TODO - turn this into a NOP at 1x, just in case something accidentally activates it with 1x
public class PrescaleFilter : BaseFilter
{
public int Scale;
@ -666,7 +702,7 @@ namespace BizHawk.Client.Common.Filters
var outputSize = state.SurfaceFormat.Size;
outputSize.Width *= Scale;
outputSize.Height *= Scale;
var ss = new SurfaceState(new SurfaceFormat(outputSize), SurfaceDisposition.RenderTarget);
var ss = new SurfaceState(new(outputSize), SurfaceDisposition.RenderTarget);
DeclareOutput(ss, channel);
}
@ -683,7 +719,7 @@ namespace BizHawk.Client.Common.Filters
public class AutoPrescaleFilter : BaseFilter
{
private Size OutputSize, InputSize;
private Size OutputSize;
private int XIS, YIS;
public override void Initialize()
@ -693,7 +729,7 @@ namespace BizHawk.Client.Common.Filters
public override void SetInputFormat(string channel, SurfaceState state)
{
//calculate integer scaling factors
// calculate integer scaling factors
XIS = OutputSize.Width / state.SurfaceFormat.Size.Width;
YIS = OutputSize.Height / state.SurfaceFormat.Size.Height;
@ -712,11 +748,7 @@ namespace BizHawk.Client.Common.Filters
OutputSize.Height *= YIS;
}
DeclareOutput(new SurfaceState
{
SurfaceFormat = new SurfaceFormat(OutputSize),
SurfaceDisposition = SurfaceDisposition.RenderTarget
});
DeclareOutput(new SurfaceState(new(OutputSize), SurfaceDisposition.RenderTarget));
}
public override Size PresizeOutput(string channel, Size size)
@ -727,7 +759,6 @@ namespace BizHawk.Client.Common.Filters
public override Size PresizeInput(string channel, Size inSize)
{
InputSize = inSize;
return inSize;
}
@ -748,6 +779,7 @@ namespace BizHawk.Client.Common.Filters
{
DeclareInput(SurfaceDisposition.RenderTarget);
}
public override void SetInputFormat(string channel, SurfaceState state)
{
DeclareOutput(state);
@ -772,19 +804,24 @@ namespace BizHawk.Client.Common.Filters
public class OSD : BaseFilter
{
//this class has the ability to disable its operations for higher performance when the callback is removed,
//without having to take it out of the chain. although, its presence in the chain may slow down performance due to added resolves/renders
//so, we should probably rebuild the chain.
// this class has the ability to disable its operations for higher performance when the callback is removed,
// without having to take it out of the chain. although, its presence in the chain may slow down performance due to added resolves/renders
// so, we should probably rebuild the chain.
public override void Initialize()
{
if (RenderCallback == null) return;
DeclareInput(SurfaceDisposition.RenderTarget);
if (RenderCallback != null)
{
DeclareInput(SurfaceDisposition.RenderTarget);
}
}
public override void SetInputFormat(string channel, SurfaceState state)
{
if (RenderCallback == null) return;
DeclareOutput(state);
if (RenderCallback != null)
{
DeclareOutput(state);
}
}
public Action RenderCallback;

View File

@ -4,9 +4,11 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Drawing;
using System.IO;
using System.Numerics;
using System.Text.RegularExpressions;
using BizHawk.Client.Common.FilterManager;
using BizHawk.Bizware.BizwareGL;
@ -17,48 +19,56 @@ namespace BizHawk.Client.Common.Filters
{
public class RetroShaderChain : IDisposable
{
private static readonly Regex RxInclude = new Regex(@"^(\s)?\#include(\s)+(""|<)(.*)?(""|>)", RegexOptions.Multiline | RegexOptions.IgnoreCase);
private static readonly Regex RxInclude = new(@"^(\s)?\#include(\s)+(""|<)(.*)?(""|>)", RegexOptions.Multiline | RegexOptions.IgnoreCase);
private static string ResolveIncludes(string content, string baseDirectory)
{
for (; ; )
while (true)
{
var match = RxInclude.Match(content);
if(match.Value == "") break;
string fname = match.Groups[4].Value;
if (match.Value == string.Empty)
{
return content;
}
var fname = match.Groups[4].Value;
fname = Path.Combine(baseDirectory,fname);
string includedContent = ResolveIncludes(File.ReadAllText(fname),Path.GetDirectoryName(fname));
var includedContent = ResolveIncludes(File.ReadAllText(fname),Path.GetDirectoryName(fname));
content = content.Substring(0, match.Index) + includedContent + content.Substring(match.Index + match.Length);
}
return content;
}
public RetroShaderChain(IGL owner, RetroShaderPreset preset, string baseDirectory, bool debug = false)
{
Owner = owner;
Preset = preset;
Passes = preset.Passes.ToArray();
Errors = "";
Errors = string.Empty;
//load up the shaders
// load up the shaders
var shaders = new RetroShader[preset.Passes.Count];
for (var i = 0; i < preset.Passes.Count; i++)
{
//acquire content. we look for it in any reasonable filename so that one preset can bind to multiple shaders
// acquire content. we look for it in any reasonable filename so that one preset can bind to multiple shaders
string content;
var path = Path.Combine(baseDirectory, preset.Passes[i].ShaderPath);
if (!File.Exists(path))
{
if (!Path.HasExtension(path))
{
path += ".cg";
}
if (!File.Exists(path))
{
if (owner.API == "OPENGL")
path = Path.ChangeExtension(path, ".glsl");
else
path = Path.ChangeExtension(path, ".hlsl");
path = owner.API switch
{
"OPENGL" => Path.ChangeExtension(path, ".glsl"),
"D3D9" => Path.ChangeExtension(path, ".hlsl"),
_ => throw new InvalidOperationException(),
};
}
}
try
{
content = ResolveIncludes(File.ReadAllText(path), Path.GetDirectoryName(path));
@ -68,16 +78,17 @@ namespace BizHawk.Client.Common.Filters
Errors += $"caught {nameof(DirectoryNotFoundException)}: {e.Message}\n";
return;
}
catch (FileNotFoundException e)
{
Errors += $"could not read file {e.FileName}\n";
return;
}
var shader = shaders[i] = new RetroShader(Owner, content, debug);
var shader = shaders[i] = new(Owner, content, debug);
if (!shader.Available)
{
Errors += $"-------------------\r\nPass {i}:\r\n{(shader.Errors??"").Replace("\n","\r\n")}\n";
Errors += $"-------------------\r\nPass {i}:\r\n{(shader.Errors ?? string.Empty).Replace("\n","\r\n")}\n";
return;
}
}
@ -105,7 +116,6 @@ namespace BizHawk.Client.Common.Filters
public readonly bool Available;
public readonly string Errors;
public readonly IGL Owner;
public readonly RetroShaderPreset Preset;
public readonly IReadOnlyList<RetroShader> Shaders = Array.Empty<RetroShader>();
public readonly RetroShaderPreset.ShaderPass[] Passes;
@ -122,55 +132,70 @@ namespace BizHawk.Client.Common.Filters
var content = new StreamReader(stream).ReadToEnd();
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", "");
foreach (var splitLine in content.Split('\n'))
{
var line = splitLine.Trim();
if (line.Length is 0) continue;
if (line.StartsWith('#')) continue; // comments
int eq = line.IndexOf('=');
if (line.Length is 0)
{
continue;
}
if (line.StartsWith('#'))
{
continue; // comments
}
var eq = line.IndexOf('=');
var key = line.Substring(0, eq).Trim();
var value = line.Substring(eq + 1).Trim();
int quote = value.IndexOf('\"');
var quote = value.IndexOf('\"');
if (quote != -1)
{
value = value.Substring(quote + 1, value.IndexOf('\"', quote + 1) - (quote + 1));
}
else
{
//remove comments from end of value. exclusive from above condition, since comments after quoted strings would be snipped by the quoted string extraction
int hash = value.IndexOf('#');
// remove comments from end of value. exclusive from above condition, since comments after quoted strings would be snipped by the quoted string extraction
var hash = value.IndexOf('#');
if (hash != -1)
{
value = value.Substring(0, hash);
}
value = value.Trim();
}
dict[key.ToLower()] = value;
}
// process the keys
int nShaders = FetchInt(dict, "shaders", 0);
for (int i = 0; i < nShaders; i++)
var nShaders = FetchInt(dict, "shaders", 0);
for (var i = 0; i < nShaders; 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.
sp.InputFilterLinear = FetchBool(dict, $"filter_linear{i}", false); // Should this value not be defined, the filtering option is implementation defined.
sp.OutputFloat = FetchBool(dict, $"float_framebuffer{i}", false);
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.
// 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.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);
var st = (ScaleType)Enum.Parse(typeof(ScaleType), FetchString(dict, $"scale_type{i}", "NotSet"), true);
if (st != ScaleType.NotSet)
{
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.
sp.Scale.X = FetchFloat(dict, $"scale_x{i}", 1);
sp.Scale.Y = FetchFloat(dict, $"scale_y{i}", 1);
float scale = FetchFloat(dict, $"scale{i}", -999);
if (scale != -999)
if (dict.ContainsValue($"scale{i}"))
{
sp.Scale.X = sp.Scale.Y = FetchFloat(dict, $"scale{i}", 1);
}
@ -179,8 +204,7 @@ namespace BizHawk.Client.Common.Filters
}
}
public List<ShaderPass> Passes { get; set; } = new List<ShaderPass>();
public List<ShaderPass> Passes { get; set; } = new();
public enum ScaleType
{
@ -214,19 +238,28 @@ namespace BizHawk.Client.Common.Filters
return dict.TryGetValue(key, out var str) ? float.Parse(str) : @default;
}
private bool FetchBool(IDictionary<string, string> dict, string key, bool @default)
private static bool FetchBool(IDictionary<string, string> dict, string key, bool @default)
{
return dict.TryGetValue(key, out var str) ? ParseBool(str) : @default;
}
private bool ParseBool(string value)
private static 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("Unparsable bool in CGP file content");
switch (value)
{
case "1":
return true;
case "0":
return false;
}
value = value.ToLowerInvariant();
return value switch
{
"true" => true,
"false" => false,
_ => throw new InvalidOperationException("Unparsable bool in CGP file content")
};
}
}
@ -253,17 +286,23 @@ namespace BizHawk.Client.Common.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.Height = (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 inSize = state.SurfaceFormat.Size;
DeclareOutput(new SurfaceState
_outputSize.Width = _sp.ScaleTypeX switch
{
SurfaceFormat = new SurfaceFormat(_outputSize),
SurfaceDisposition = SurfaceDisposition.RenderTarget
});
RetroShaderPreset.ScaleType.Absolute => (int)_sp.Scale.X,
RetroShaderPreset.ScaleType.Source => (int)(inSize.Width * _sp.Scale.X),
_ => _outputSize.Width
};
_outputSize.Height = _sp.ScaleTypeY switch
{
RetroShaderPreset.ScaleType.Absolute => (int)_sp.Scale.Y,
RetroShaderPreset.ScaleType.Source => (int)(inSize.Height * _sp.Scale.Y),
_ => _outputSize.Height
};
DeclareOutput(new SurfaceState(new(_outputSize), SurfaceDisposition.RenderTarget));
}
public override Size PresizeOutput(string channel, Size size)
@ -274,11 +313,22 @@ namespace BizHawk.Client.Common.Filters
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.Height = (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);
var outsize = inSize;
outsize.Width = _sp.ScaleTypeX switch
{
RetroShaderPreset.ScaleType.Absolute => (int)_sp.Scale.X,
RetroShaderPreset.ScaleType.Source => (int)(inSize.Width * _sp.Scale.X),
_ => outsize.Width
};
outsize.Height = _sp.ScaleTypeY switch
{
RetroShaderPreset.ScaleType.Absolute => (int)_sp.Scale.Y,
RetroShaderPreset.ScaleType.Source => (int)(inSize.Height * _sp.Scale.Y),
_ => outsize.Height
};
return outsize;
}
@ -288,7 +338,7 @@ namespace BizHawk.Client.Common.Filters
shader.Bind();
// 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 (k, v) in Parameters)
{

View File

@ -1,7 +1,7 @@
using System.Drawing;
using BizHawk.Client.Common.FilterManager;
using BizHawk.Bizware.BizwareGL;
using BizHawk.Client.Common.FilterManager;
namespace BizHawk.Client.Common.Filters
{
@ -23,7 +23,7 @@ namespace BizHawk.Client.Common.Filters
public override void Initialize()
{
DeclareOutput(new SurfaceState(new SurfaceFormat(_size), SurfaceDisposition.Texture));
DeclareOutput(new SurfaceState(new(_size), SurfaceDisposition.Texture));
}
public override void SetInputFormat(string channel, SurfaceState format)
@ -71,7 +71,7 @@ namespace BizHawk.Client.Common.Filters
public override void Run()
{
YieldOutput(FilterProgram.GetRenderTarget().Texture2d);
YieldOutput(FilterProgram.CurrRenderTarget.Texture2d);
}
}
}

View File

@ -1,5 +1,6 @@
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using BizHawk.Bizware.BizwareGL;
namespace BizHawk.Client.EmuHawk
@ -17,9 +18,6 @@ namespace BizHawk.Client.EmuHawk
private readonly IGL_GdiPlus _gdi;
public Control Control => this;
/// <summary>
/// the render target for rendering to this control
/// </summary>
@ -34,24 +32,29 @@ namespace BizHawk.Client.EmuHawk
{
_gdi.BeginControl(this);
RenderTargetWrapper.CreateGraphics();
//using (var g = CreateGraphics())
// MyBufferedGraphics = Gdi.MyBufferedGraphicsContext.Allocate(g, ClientRectangle);
//MyBufferedGraphics.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed;
////not sure about this stuff...
////it will wreck alpha blending, for one thing
//MyBufferedGraphics.Graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
//MyBufferedGraphics.Graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
#if false
using (var g = CreateGraphics())
{
MyBufferedGraphics = _gdi.MyBufferedGraphicsContext.Allocate(g, ClientRectangle);
}
MyBufferedGraphics.Graphics.PixelOffsetMode = PixelOffsetMode.HighSpeed;
// not sure about this stuff...
// it will wreck alpha blending, for one thing
MyBufferedGraphics.Graphics.CompositingMode = CompositingMode.SourceCopy;
MyBufferedGraphics.Graphics.CompositingQuality = CompositingQuality.HighSpeed;
#endif
}
public void End()
{
_gdi.EndControl(this);
_gdi.EndControl();
}
public void SwapBuffers()
{
_gdi.SwapControl(this);
if (RenderTargetWrapper.MyBufferedGraphics == null)
{
return;