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> /// </summary>
public class Art 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) public Art(Texture2d tex)
{ {
BaseTexture = tex; BaseTexture = tex;

View File

@ -1,6 +1,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace BizHawk.Bizware.BizwareGL namespace BizHawk.Bizware.BizwareGL
{ {
@ -18,7 +19,7 @@ namespace BizHawk.Bizware.BizwareGL
public void Dispose() public void Dispose()
{ {
//todo // todo
} }
/// <summary> /// <summary>
@ -27,7 +28,11 @@ namespace BizHawk.Bizware.BizwareGL
public void Open() public void Open()
{ {
AssertIsOpen(false); 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; IsOpened = true;
} }
@ -52,7 +57,7 @@ namespace BizHawk.Bizware.BizwareGL
{ {
AssertIsOpen(true); AssertIsOpen(true);
Art a = new Art(this); var a = new Art(this);
ArtLooseTextureAssociation.Add((a, tex)); ArtLooseTextureAssociation.Add((a, tex));
ManagedArts.Add(a); ManagedArts.Add(a);
@ -72,16 +77,16 @@ namespace BizHawk.Bizware.BizwareGL
// first, cleanup old stuff // first, cleanup old stuff
foreach (var tex in ManagedTextures) foreach (var tex in ManagedTextures)
{
tex.Dispose(); tex.Dispose();
}
ManagedTextures.Clear(); ManagedTextures.Clear();
// prepare input for atlas process and perform atlas // prepare input for atlas process and perform atlas
// add 2 extra pixels for padding on all sides // add 2 extra pixels for padding on all sides
var atlasItems = new List<TexAtlas.RectItem>(); var atlasItems = ArtLooseTextureAssociation
foreach (var kvp in ArtLooseTextureAssociation) .Select(kvp => new TexAtlas.RectItem(kvp.Bitmap.Width + 2, kvp.Bitmap.Height + 2, kvp))
{ .ToList();
atlasItems.Add(new TexAtlas.RectItem(kvp.Bitmap.Width + 2, kvp.Bitmap.Height + 2, kvp));
}
var results = TexAtlas.PackAtlas(atlasItems); var results = TexAtlas.PackAtlas(atlasItems);
// this isn't supported yet: // this isn't supported yet:
@ -89,27 +94,28 @@ namespace BizHawk.Bizware.BizwareGL
throw new InvalidOperationException("Art files too big for atlas"); throw new InvalidOperationException("Art files too big for atlas");
// prepare the output buffer // 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 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 item = results[0].Items[i];
var (art, bitmap) = ((Art, BitmapBuffer)) item.Item; var (art, bitmap) = ((Art, BitmapBuffer)) item.Item;
int w = bitmap.Width; var w = bitmap.Width;
int h = bitmap.Height; var h = bitmap.Height;
int dx = item.X + 1; var dx = item.X + 1;
int dy = item.Y + 1; var dy = item.Y + 1;
for (int y = 0; y < h; y++) for (var y = 0; y < h; y++)
for (int x = 0; x < w; x++) {
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); bmpResult.SetPixel(x+dx,y+dy,pixel);
} }
}
var myDestBitmap = bmpResult; var myDestWidth = (float)bmpResult.Width;
float myDestWidth = (float)myDestBitmap.Width; var myDestHeight = (float)bmpResult.Height;
float myDestHeight = (float)myDestBitmap.Height;
art.u0 = dx / myDestWidth; art.u0 = dx / myDestWidth;
art.v0 = dy / myDestHeight; art.v0 = dy / myDestHeight;
@ -138,7 +144,13 @@ namespace BizHawk.Bizware.BizwareGL
/// <summary> /// <summary>
/// Throws an exception if the instance is not open /// Throws an exception if the instance is not open
/// </summary> /// </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; } public IGL Owner { get; }
@ -153,11 +165,11 @@ namespace BizHawk.Bizware.BizwareGL
/// <summary> /// <summary>
/// Physical texture resources, which exist after this ArtManager has been closed /// Physical texture resources, which exist after this ArtManager has been closed
/// </summary> /// </summary>
private readonly List<Texture2d> ManagedTextures = new List<Texture2d>(); private readonly List<Texture2d> ManagedTextures = new();
/// <summary> /// <summary>
/// All the Arts managed by this instance /// All the Arts managed by this instance
/// </summary> /// </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 - 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 - 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;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.IO; using System.IO;
using sd = System.Drawing;
namespace BizHawk.Bizware.BizwareGL namespace BizHawk.Bizware.BizwareGL
{ {
/// <summary> /// <summary>
@ -28,7 +27,7 @@ namespace BizHawk.Bizware.BizwareGL
/// </summary> /// </summary>
public bool HasAlpha = true; public bool HasAlpha = true;
public Size Size => new Size(Width, Height); public Size Size => new(Width, Height);
private readonly Bitmap WrappedBitmap; private readonly Bitmap WrappedBitmap;
private GCHandle CurrLockHandle; private GCHandle CurrLockHandle;
@ -48,7 +47,7 @@ namespace BizHawk.Bizware.BizwareGL
} }
CurrLockHandle = GCHandle.Alloc(Pixels, GCHandleType.Pinned); CurrLockHandle = GCHandle.Alloc(Pixels, GCHandleType.Pinned);
CurrLock = new BitmapData CurrLock = new()
{ {
Height = Height, Height = Height,
Width = Width, Width = Width,
@ -82,16 +81,15 @@ namespace BizHawk.Bizware.BizwareGL
public void YFlip() public void YFlip()
{ {
//TODO - could be faster // TODO - could be faster
var bmpdata = LockBits(); var bmpdata = LockBits();
int[] newPixels = new int[Width * Height]; var newPixels = new int[Width * Height];
int todo = Width * Height; var s = (int*)bmpdata.Scan0.ToPointer();
int* s = (int*)bmpdata.Scan0.ToPointer();
fixed (int* d = newPixels) fixed (int* d = newPixels)
{ {
for (int y = 0, si = 0, di = (Height - 1) * Width; y < Height; y++) 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]; d[di] = s[si];
} }
@ -110,16 +108,15 @@ namespace BizHawk.Bizware.BizwareGL
public void Normalize(bool yflip) public void Normalize(bool yflip)
{ {
var bmpdata = LockBits(); var bmpdata = LockBits();
int[] newPixels = new int[Width * Height]; var newPixels = new int[Width * Height];
int todo = Width*Height; var s = (int*)bmpdata.Scan0.ToPointer();
int* s = (int*)bmpdata.Scan0.ToPointer();
fixed (int* d = newPixels) fixed (int* d = newPixels)
{ {
if (yflip) if (yflip)
{ {
for (int y = 0, si = 0, di = (Height - 1) * Width; y < Height; y++) 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); 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 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); d[i] = s[i] | unchecked((int)0xFF000000);
} }
@ -143,11 +140,19 @@ namespace BizHawk.Bizware.BizwareGL
Pixels = newPixels; Pixels = newPixels;
} }
public int GetPixel(int x, int y) { return Pixels[Width * y + x]; } public int GetPixel(int x, int y)
public void SetPixel(int x, int y, int value) { Pixels[Width * y + x] = value; } {
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) public Color GetPixelAsColor(int x, int y)
{ {
int c = Pixels[Width * y + x]; var c = Pixels[Width * y + x];
return Color.FromArgb(c); return Color.FromArgb(c);
} }
@ -157,11 +162,15 @@ namespace BizHawk.Bizware.BizwareGL
public void Alphafy(int tcol) public void Alphafy(int tcol)
{ {
for (int y = 0, idx = 0; y < Height; y++) 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) if (Pixels[idx] == tcol)
{
Pixels[idx] = 0; Pixels[idx] = 0;
}
} }
}
} }
/// <summary> /// <summary>
@ -177,15 +186,16 @@ namespace BizHawk.Bizware.BizwareGL
/// </summary> /// </summary>
public BitmapBuffer Trim(out int xofs, out int yofs) public BitmapBuffer Trim(out int xofs, out int yofs)
{ {
int minx = int.MaxValue; var minx = int.MaxValue;
int maxx = int.MinValue; var maxx = int.MinValue;
int miny = int.MaxValue; var miny = int.MaxValue;
int maxy = int.MinValue; var maxy = int.MinValue;
for (int y = 0; y < Height; y++) for (var y = 0; y < Height; y++)
for (int x = 0; x < Width; x++) {
for (var x = 0; x < Width; x++)
{ {
int pixel = GetPixel(x, y); var pixel = GetPixel(x, y);
int a = (pixel >> 24) & 0xFF; var a = (pixel >> 24) & 0xFF;
if (a != 0) if (a != 0)
{ {
minx = Math.Min(minx, x); minx = Math.Min(minx, x);
@ -194,21 +204,24 @@ namespace BizHawk.Bizware.BizwareGL
maxy = Math.Max(maxy, y); maxy = Math.Max(maxy, y);
} }
} }
}
if (minx == int.MaxValue || maxx == int.MinValue || miny == int.MaxValue || minx == int.MinValue) if (minx == int.MaxValue || maxx == int.MinValue || miny == int.MaxValue || minx == int.MinValue)
{ {
xofs = yofs = 0; xofs = yofs = 0;
return new BitmapBuffer(0, 0); return new(0, 0);
} }
int w = maxx - minx + 1; var w = maxx - minx + 1;
int h = maxy - miny + 1; var h = maxy - miny + 1;
BitmapBuffer bbRet = new BitmapBuffer(w, h); var bbRet = new BitmapBuffer(w, h);
for (int y = 0; y < h; y++) for (var y = 0; y < h; y++)
for (int x = 0; x < w; x++) {
for (var x = 0; x < w; x++)
{ {
bbRet.SetPixel(x, y, GetPixel(x + minx, y + miny)); bbRet.SetPixel(x, y, GetPixel(x + minx, y + miny));
} }
}
xofs = minx; xofs = minx;
yofs = miny; yofs = miny;
@ -220,15 +233,18 @@ namespace BizHawk.Bizware.BizwareGL
/// </summary> /// </summary>
public void Pad() public void Pad()
{ {
int widthRound = NextHigher(Width); var widthRound = NextHigher(Width);
int heightRound = NextHigher(Height); var heightRound = NextHigher(Height);
if (widthRound == Width && heightRound == Height) return; 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 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++]; NewPixels[dptr++] = Pixels[sptr++];
}
dptr += (widthRound - Width); dptr += (widthRound - Width);
} }
@ -265,10 +281,12 @@ namespace BizHawk.Bizware.BizwareGL
Height = bitmap.Height; Height = bitmap.Height;
WrappedBitmap = bitmap; WrappedBitmap = bitmap;
} }
else LoadInternal(null, bitmap, options); else
{
LoadInternal(null, bitmap, options);
}
} }
/// <summary> /// <summary>
/// Initializes a BitmapBuffer --WRAPPED-- from the supplied parameters, which should definitely have a stride==width and be in the standard color format /// Initializes a BitmapBuffer --WRAPPED-- from the supplied parameters, which should definitely have a stride==width and be in the standard color format
/// </summary> /// </summary>
@ -290,82 +308,89 @@ namespace BizHawk.Bizware.BizwareGL
private void LoadInternal(Stream stream, Bitmap bitmap, BitmapLoadOptions options) private void LoadInternal(Stream stream, Bitmap bitmap, BitmapLoadOptions options)
{ {
bool cleanup = options.CleanupAlpha0; var cleanup = options.CleanupAlpha0;
bool needsPad = true; var needsPad = true;
var colorKey24bpp = options.ColorKey24bpp; 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; var bmp = loadedBmp ?? bitmap;
if (bmp == null)
bmp = 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) if (bmp.PixelFormat == PixelFormat.Format24bppRgb && colorKey24bpp != null)
{ {
int colorKey = colorKey24bpp(bmp); var colorKey = colorKey24bpp(bmp);
int w = bmp.Width; var w = bmp.Width;
int h = bmp.Height; var h = bmp.Height;
InitSize(w, h); InitSize(w, h);
BitmapData bmpdata = bmp.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); var bmpdata = bmp.LockBits(new(0, 0, w, h), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
Color[] palette = bmp.Palette.Entries; var ptr = (int*)bmpdata.Scan0.ToPointer();
int* ptr = (int*)bmpdata.Scan0.ToPointer();
int stride = bmpdata.Stride;
fixed (int* pPtr = &Pixels[0]) fixed (int* pPtr = &Pixels[0])
{ {
for (int idx = 0, y = 0; y < h; y++) 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) if (srcPixel == colorKey)
{
srcPixel = 0; srcPixel = 0;
}
pPtr[idx++] = srcPixel; pPtr[idx++] = srcPixel;
} }
}
} }
bmp.UnlockBits(bmpdata); 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; var w = bmp.Width;
int h = bmp.Height; var h = bmp.Height;
InitSize(w, h); InitSize(w, h);
BitmapData bmpdata = bmp.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed); var bmpdata = bmp.LockBits(new(0, 0, w, h), ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
Color[] palette = bmp.Palette.Entries; var palette = bmp.Palette.Entries;
byte* ptr = (byte*)bmpdata.Scan0.ToPointer(); var ptr = (byte*)bmpdata.Scan0.ToPointer();
fixed (int* pPtr = &Pixels[0]) fixed (int* pPtr = &Pixels[0])
{ {
for (int idx = 0, y = 0; y < h; y++) 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]; int srcPixel = ptr[idx];
if (srcPixel != 0) 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. // 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)) // (yes, we can have palette entries with transparency in them (PNGs support this, annoyingly))
if (cleanup) if (cleanup)
{ {
if ((color & 0xFF000000) == 0) color = 0; if ((color & 0xFF000000) == 0)
{
color = 0;
}
pPtr[idx] = color; pPtr[idx] = color;
} }
} }
idx++; idx++;
} }
}
} }
bmp.UnlockBits(bmpdata); bmp.UnlockBits(bmpdata);
} }
else else
{ {
//dump the supplied bitmap into our pixels array // dump the supplied bitmap into our pixels array
int width = bmp.Width; var width = bmp.Width;
int height = bmp.Height; var height = bmp.Height;
InitSize(width, height); InitSize(width, height);
BitmapData bmpdata = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); var bmpdata = bmp.LockBits(new(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
int* ptr = (int*)bmpdata.Scan0; var ptr = (int*)bmpdata.Scan0;
int stride = bmpdata.Stride / 4; var stride = bmpdata.Stride / 4;
LoadFrom(width, stride, height, (byte*)ptr, options); LoadFrom(width, stride, height, (byte*)ptr, options);
bmp.UnlockBits(bmpdata); bmp.UnlockBits(bmpdata);
needsPad = false; needsPad = false;
@ -373,7 +398,9 @@ namespace BizHawk.Bizware.BizwareGL
} }
if (needsPad && options.Pad) if (needsPad && options.Pad)
{
Pad(); Pad();
}
} }
@ -382,29 +409,37 @@ namespace BizHawk.Bizware.BizwareGL
/// </summary> /// </summary>
public void LoadFrom(int width, int stride, int height, byte* data, BitmapLoadOptions options) public void LoadFrom(int width, int stride, int height, byte* data, BitmapLoadOptions options)
{ {
bool cleanup = options.CleanupAlpha0; var cleanup = options.CleanupAlpha0;
Width = width; Width = width;
Height = height; Height = height;
Pixels = new int[width * height]; Pixels = new int[width * height];
fixed (int* pPtr = &Pixels[0]) fixed (int* pPtr = &Pixels[0])
{ {
for (int idx = 0, y = 0; y < Height; y++) 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; var src = y * stride + x;
int srcVal = ((int*)data)[src]; 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 // make transparent pixels turn into black to avoid filtering issues and other annoying issues with stray junk in transparent pixels
if (cleanup) if (cleanup)
{ {
if ((srcVal & 0xFF000000) == 0) srcVal = 0; if ((srcVal & 0xFF000000) == 0)
{
srcVal = 0;
}
pPtr[idx++] = srcVal; pPtr[idx++] = srcVal;
} }
} }
}
} }
if (options.Pad) if (options.Pad)
{
Pad(); Pad();
}
} }
/// <summary> /// <summary>
@ -412,10 +447,10 @@ namespace BizHawk.Bizware.BizwareGL
/// </summary> /// </summary>
public static int PremultiplyColor(int srcVal) public static int PremultiplyColor(int srcVal)
{ {
int b = (srcVal >> 0) & 0xFF; var b = (srcVal >> 0) & 0xFF;
int g = (srcVal >> 8) & 0xFF; var g = (srcVal >> 8) & 0xFF;
int r = (srcVal >> 16) & 0xFF; var r = (srcVal >> 16) & 0xFF;
int a = (srcVal >> 24) & 0xFF; var a = (srcVal >> 24) & 0xFF;
r = (r * a) >> 8; r = (r * a) >> 8;
g = (g * a) >> 8; g = (g * a) >> 8;
b = (b * a) >> 8; b = (b * a) >> 8;
@ -434,7 +469,9 @@ namespace BizHawk.Bizware.BizwareGL
/// <summary> /// <summary>
/// Makes a new bitmap buffer, in ??? state /// Makes a new bitmap buffer, in ??? state
/// </summary> /// </summary>
public BitmapBuffer() { } public BitmapBuffer()
{
}
/// <summary> /// <summary>
/// initializes an empty BitmapBuffer, cleared to all 0 /// initializes an empty BitmapBuffer, cleared to all 0
@ -449,24 +486,24 @@ namespace BizHawk.Bizware.BizwareGL
/// </summary> /// </summary>
public void ClearWithoutAlloc() public void ClearWithoutAlloc()
{ {
//http://techmikael.blogspot.com/2009/12/filling-array-with-default-value.html // http://techmikael.blogspot.com/2009/12/filling-array-with-default-value.html
//this guy says its faster // this guy says its faster
int size = Width * Height; var size = Width * Height;
byte fillValue = 0; const byte fillValue = 0;
ulong fillValueLong = 0; const ulong fillValueLong = 0;
fixed (int* ptr = &Pixels[0]) fixed (int* ptr = &Pixels[0])
{ {
ulong* dest = (ulong*)ptr; var dest = (ulong*)ptr;
int length = size; var length = size;
while (length >= 8) while (length >= 8)
{ {
*dest = fillValueLong; *dest = fillValueLong;
dest++; dest++;
length -= 8; length -= 8;
} }
byte* bDest = (byte*)dest; var bDest = (byte*)dest;
for (byte i = 0; i < length; i++) for (byte i = 0; i < length; i++)
{ {
*bDest = fillValue; *bDest = fillValue;
@ -488,9 +525,12 @@ namespace BizHawk.Bizware.BizwareGL
private static int NextHigher(int k) private static int NextHigher(int k)
{ {
k--; k--;
for (int i = 1; i < 32; i <<= 1) for (var i = 1; i < 32; i <<= 1)
{
k = k | k >> i; k = k | k >> i;
int candidate = k + 1; }
var candidate = k + 1;
return candidate; return candidate;
} }
@ -501,11 +541,12 @@ namespace BizHawk.Bizware.BizwareGL
public Bitmap ToSysdrawingBitmap() public Bitmap ToSysdrawingBitmap()
{ {
if (WrappedBitmap != null) if (WrappedBitmap != null)
{
return (Bitmap)WrappedBitmap.Clone(); return (Bitmap)WrappedBitmap.Clone();
var pf = PixelFormat.Format32bppArgb; }
if (!HasAlpha)
pf = PixelFormat.Format24bppRgb; var pf = HasAlpha ? PixelFormat.Format32bppArgb : PixelFormat.Format24bppRgb;
Bitmap bmp = new Bitmap(Width, Height, pf); var bmp = new Bitmap(Width, Height, pf);
ToSysdrawingBitmap(bmp); ToSysdrawingBitmap(bmp);
return bmp; return bmp;
} }
@ -519,30 +560,33 @@ namespace BizHawk.Bizware.BizwareGL
if (WrappedBitmap != null) if (WrappedBitmap != null)
{ {
using var g = Graphics.FromImage(bmp); using var g = Graphics.FromImage(bmp);
g.CompositingMode = sd.Drawing2D.CompositingMode.SourceCopy; g.CompositingMode = CompositingMode.SourceCopy;
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed; g.CompositingQuality = CompositingQuality.HighSpeed;
g.DrawImageUnscaled(WrappedBitmap, 0, 0); g.DrawImageUnscaled(WrappedBitmap, 0, 0);
return; return;
} }
//note: we lock it as 32bpp even if the bitmap is 24bpp so we can write to it more conveniently. //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); Marshal.Copy(Pixels, 0, bmpdata.Scan0, Width * Height);
}
else if (bmp.Width != 0 && bmp.Height != 0) else if (bmp.Width != 0 && bmp.Height != 0)
{ {
int* ptr = (int*)bmpdata.Scan0.ToPointer(); var ptr = (int*)bmpdata.Scan0.ToPointer();
int stride = bmpdata.Stride;
fixed (int* pPtr = &Pixels[0]) fixed (int* pPtr = &Pixels[0])
{ {
for (int idx = 0, y = 0; y < Height; y++) 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; ptr[idx] = srcPixel;
idx++; idx++;
} }
}
} }
} }

View File

@ -1,12 +1,14 @@
using System; using System;
using System.Drawing;
namespace BizHawk.Bizware.BizwareGL namespace BizHawk.Bizware.BizwareGL
{ {
public class GDIPTextureWrapper : IDisposable public class GDIPTextureWrapper : IDisposable
{ {
public System.Drawing.Bitmap SDBitmap; public Bitmap SDBitmap;
public TextureMinFilter MinFilter = TextureMinFilter.Nearest; public TextureMinFilter MinFilter = TextureMinFilter.Nearest;
public TextureMagFilter MagFilter = TextureMagFilter.Nearest; public TextureMagFilter MagFilter = TextureMagFilter.Nearest;
public void Dispose() public void Dispose()
{ {
if (SDBitmap != null) 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;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.Numerics;
using sd = System.Drawing;
namespace BizHawk.Bizware.BizwareGL namespace BizHawk.Bizware.BizwareGL
{ {
@ -14,7 +15,13 @@ namespace BizHawk.Bizware.BizwareGL
public GDIPlusGuiRenderer(IGL_GdiPlus gl) public GDIPlusGuiRenderer(IGL_GdiPlus gl)
=> Gdi = 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) public void SetCornerColor(int which, Vector4 color)
{ {
@ -25,9 +32,16 @@ namespace BizHawk.Bizware.BizwareGL
public void SetCornerColors(Vector4[] colors) 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. 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]; CornerColors[i] = colors[i];
}
} }
public void Dispose() public void Dispose()
@ -45,30 +59,31 @@ namespace BizHawk.Bizware.BizwareGL
public void SetModulateColorWhite() public void SetModulateColorWhite()
{ {
SetModulateColor(sd.Color.White); SetModulateColor(Color.White);
} }
private ImageAttributes CurrentImageAttributes; private ImageAttributes CurrentImageAttributes;
public void SetModulateColor(sd.Color color)
public void SetModulateColor(Color color)
{ {
//white is really no color at all // white is really no color at all
if (color.ToArgb() == sd.Color.White.ToArgb()) if (color.ToArgb() == Color.White.ToArgb())
{ {
CurrentImageAttributes.ClearColorMatrix(ColorAdjustType.Bitmap); CurrentImageAttributes.ClearColorMatrix(ColorAdjustType.Bitmap);
return; return;
} }
float r = color.R / 255.0f; var r = color.R / 255.0f;
float g = color.G / 255.0f; var g = color.G / 255.0f;
float b = color.B / 255.0f; var b = color.B / 255.0f;
float a = color.A / 255.0f; var a = color.A / 255.0f;
float[][] colorMatrixElements = float[][] colorMatrixElements =
{ {
new float[] { r, 0, 0, 0, 0 }, new[] { r, 0, 0, 0, 0 },
new float[] { 0, g, 0, 0, 0 }, new[] { 0, g, 0, 0, 0 },
new float[] { 0, 0, b, 0, 0 }, new[] { 0, 0, b, 0, 0 },
new float[] { 0, 0, 0, a, 0 }, new[] { 0, 0, 0, a, 0 },
new float[] { 0, 0, 0, 0, 1 }, new float[] { 0, 0, 0, 0, 1 },
}; };
@ -76,15 +91,15 @@ namespace BizHawk.Bizware.BizwareGL
CurrentImageAttributes.SetColorMatrix(colorMatrix,ColorMatrixFlag.Default, ColorAdjustType.Bitmap); CurrentImageAttributes.SetColorMatrix(colorMatrix,ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
} }
private sd.Color CurrentModulateColor = sd.Color.White;
private IBlendState CurrentBlendState; private IBlendState CurrentBlendState;
public void SetBlendState(IBlendState rsBlend) public void SetBlendState(IBlendState rsBlend)
{ {
CurrentBlendState = rsBlend; CurrentBlendState = rsBlend;
} }
private MatrixStack _Projection, _Modelview; private MatrixStack _Projection, _Modelview;
public MatrixStack Projection public MatrixStack Projection
{ {
get => _Projection; get => _Projection;
@ -94,6 +109,7 @@ namespace BizHawk.Bizware.BizwareGL
_Projection.IsDirty = true; _Projection.IsDirty = true;
} }
} }
public MatrixStack Modelview public MatrixStack Modelview
{ {
get => _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) public void Begin(int width, int height)
{ {
@ -117,31 +135,29 @@ namespace BizHawk.Bizware.BizwareGL
Modelview = Owner.CreateGuiViewMatrix(width, height); Modelview = Owner.CreateGuiViewMatrix(width, height);
} }
public void Begin() 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; IsActive = true;
CurrentImageAttributes = new ImageAttributes(); CurrentImageAttributes = new();
} }
public void Flush() 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> /// <exception cref="InvalidOperationException"><see cref="IsActive"/> is <see langword="false"/></exception>
public void End() public void End()
{ {
if (!IsActive) if (!IsActive)
throw new InvalidOperationException($"{nameof(GDIPlusGuiRenderer)} is not active!");
IsActive = false;
if (CurrentImageAttributes != null)
{ {
CurrentImageAttributes.Dispose(); throw new InvalidOperationException($"{nameof(GDIPlusGuiRenderer)} is not active!");
CurrentImageAttributes = null;
} }
IsActive = false;
CurrentImageAttributes?.Dispose();
CurrentImageAttributes = null;
} }
public void RectFill(float x, float y, float w, float h) 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) 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(); var g = Gdi.GetCurrentGraphics();
PrepDraw(g, tex); PrepDraw(g, tex);
SetupMatrix(g); SetupMatrix(g);
float x0 = u0 * tex.Width; var x0 = u0 * tex.Width;
float y0 = v0 * tex.Height; var y0 = v0 * tex.Height;
float x1 = u1 * tex.Width; var x1 = u1 * tex.Width;
float y1 = v1 * tex.Height; var y1 = v1 * tex.Height;
sd.PointF[] destPoints = { PointF[] destPoints =
new sd.PointF(x,y), {
new sd.PointF(x+w,y), new(x, y),
new sd.PointF(x,y+h), 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.DrawImage(tw.SDBitmap, destPoints, new(x0, y0, x1 - x0, y1 - y0), GraphicsUnit.Pixel, CurrentImageAttributes);
g.Transform = new sd.Drawing2D.Matrix(); //.Reset() doesnt work ? ? g.Transform = new(); // .Reset() doesnt work?
} }
@ -184,47 +202,49 @@ namespace BizHawk.Bizware.BizwareGL
DrawInternal(art, x, y, width, height); 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; var tw = (GDIPTextureWrapper)tex.Opaque;
//TODO - we can support bicubic for the final presentation..
// TODO - we can support bicubic for the final presentation...
if ((int)tw.MagFilter != (int)tw.MinFilter) if ((int)tw.MagFilter != (int)tw.MinFilter)
{
throw new InvalidOperationException($"{nameof(tw)}.{nameof(tw.MagFilter)} != {nameof(tw)}.{nameof(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) if (CurrentBlendState == Gdi.BlendNormal)
{ {
g.CompositingMode = sd.Drawing2D.CompositingMode.SourceOver; g.CompositingMode = CompositingMode.SourceOver;
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.Default; //? g.CompositingQuality = CompositingQuality.Default; // ?
//CurrentImageAttributes.ClearColorMatrix(ColorAdjustType.Bitmap); // CurrentImageAttributes.ClearColorMatrix(ColorAdjustType.Bitmap);
} }
else else
//if(CurrentBlendState == Gdi.BlendNoneCopy) // if (CurrentBlendState == Gdi.BlendNoneCopy)
//if(CurrentBlendState == Gdi.BlendNoneOpaque) // if (CurrentBlendState == Gdi.BlendNoneOpaque)
{ {
g.CompositingMode = sd.Drawing2D.CompositingMode.SourceCopy; g.CompositingMode = CompositingMode.SourceCopy;
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed; g.CompositingQuality = CompositingQuality.HighSpeed;
//WARNING : DO NOT USE COLOR MATRIX TO WIPE THE ALPHA // WARNING : DO NOT USE COLOR MATRIX TO WIPE THE ALPHA
//ITS SOOOOOOOOOOOOOOOOOOOOOOOOOOOO SLOW // ITS SOOOOOOOOOOOOOOOOOOOOOOOOOOOO SLOW
//instead, we added kind of hacky support for 24bpp images // 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 // projection is always identity, so who cares i guess
//Matrix4 mat = Projection.Top * Modelview.Top; // var mat = Projection.Top * Modelview.Top;
Matrix4 mat = Modelview.Top; var 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); 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) private void DrawInternal(Art art, float x, float y, float w, float h)
@ -244,26 +264,27 @@ namespace BizHawk.Bizware.BizwareGL
SetupMatrix(g); SetupMatrix(g);
sd.PointF[] destPoints = { PointF[] destPoints =
new sd.PointF(x,y), {
new sd.PointF(x+w,y), new(x, y),
new sd.PointF(x,y+h), new(x+w, y),
new(x, y+h),
}; };
float sx = tex.Width * u0; var sx = tex.Width * u0;
float sy = tex.Height * v0; var sy = tex.Height * v0;
float sx2 = tex.Width * u1; var sx2 = tex.Width * u1;
float sy2 = tex.Height * v1; var sy2 = tex.Height * v1;
float sw = sx2 - sx; var sw = sx2 - sx;
float sh = sy2 - sy; var sh = sy2 - sy;
var tw = tex.Opaque as GDIPTextureWrapper; var tw = (GDIPTextureWrapper)tex.Opaque;
g.PixelOffsetMode = sd.Drawing2D.PixelOffsetMode.Half; g.PixelOffsetMode = PixelOffsetMode.Half;
g.DrawImage(tw.SDBitmap, destPoints, new sd.RectangleF(sx, sy, sw, sh), sd.GraphicsUnit.Pixel, CurrentImageAttributes); g.DrawImage(tw.SDBitmap, destPoints, new(sx, sy, sw, sh), GraphicsUnit.Pixel, CurrentImageAttributes);
g.Transform = new sd.Drawing2D.Matrix(); //.Reset() doesn't work ? ? 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 namespace BizHawk.Bizware.BizwareGL
{ {
public interface IBlendState {} public interface IBlendState
{
}
} }

View File

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

View File

@ -1,9 +1,8 @@
using System; using System;
using System.IO; using System.IO;
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging;
using sd = System.Drawing; using System.Numerics;
using sdi = System.Drawing.Imaging;
//TODO - maybe a layer to cache Graphics parameters (notably, filtering) ? //TODO - maybe a layer to cache Graphics parameters (notably, filtering) ?
namespace BizHawk.Bizware.BizwareGL namespace BizHawk.Bizware.BizwareGL
@ -12,22 +11,25 @@ namespace BizHawk.Bizware.BizwareGL
{ {
public EDispMethod DispMethodEnum => EDispMethod.GdiPlus; public EDispMethod DispMethodEnum => EDispMethod.GdiPlus;
#if false
// rendering state // rendering state
private RenderTarget _currRenderTarget; private RenderTarget _currRenderTarget;
#endif
private readonly Func<IGL_GdiPlus, IGraphicsControl> _createGLControlWrapper; private readonly Func<IGL_GdiPlus, IGraphicsControl> _createGLControlWrapper;
public IGL_GdiPlus(Func<IGL_GdiPlus, IGraphicsControl> createGLControlWrapper) public IGL_GdiPlus(Func<IGL_GdiPlus, IGraphicsControl> createGLControlWrapper)
=> _createGLControlWrapper = createGLControlWrapper; => _createGLControlWrapper = createGLControlWrapper;
void IDisposable.Dispose() public void Dispose()
{ {
MyBufferedGraphicsContext.Dispose();
} }
public void Clear(ClearBufferMask mask) public void Clear(ClearBufferMask mask)
{ {
var g = GetCurrentGraphics(); var g = GetCurrentGraphics();
if((mask & ClearBufferMask.ColorBufferBit) != 0) if ((mask & ClearBufferMask.ColorBufferBit) != 0)
{ {
g.Clear(_currentClearColor); g.Clear(_currentClearColor);
} }
@ -41,31 +43,39 @@ namespace BizHawk.Bizware.BizwareGL
return null; return null;
} }
private sd.Color _currentClearColor = Color.Transparent; private Color _currentClearColor = Color.Transparent;
public void SetClearColor(sd.Color color)
public void SetClearColor(Color color)
{ {
_currentClearColor = color; _currentClearColor = color;
} }
public void BindArrayData(IntPtr pData) {} public void BindArrayData(IntPtr pData)
{
}
public void FreeTexture(Texture2d tex) public void FreeTexture(Texture2d tex)
{ {
var tw = tex.Opaque as GDIPTextureWrapper; var tw = (GDIPTextureWrapper)tex.Opaque;
tw.Dispose(); tw.Dispose();
} }
public Shader CreateFragmentShader(string source, string entry, bool required) => null; public Shader CreateFragmentShader(string source, string entry, bool required)
public Shader CreateVertexShader(string source, string entry, bool required) => null; => null;
public Shader CreateVertexShader(string source, string entry, bool required)
=> null;
public void SetBlendState(IBlendState rsBlend) 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 BlendNoneCopy => _rsBlendNoneVerbatim;
public IBlendState BlendNoneOpaque => _rsBlendNoneOpaque; public IBlendState BlendNoneOpaque => _rsBlendNoneOpaque;
@ -76,9 +86,12 @@ namespace BizHawk.Bizware.BizwareGL
return null; 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) 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; => ((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; => ((GDIPTextureWrapper) texture.Opaque).MagFilter = magFilter;
public Texture2d LoadTexture(Bitmap bitmap) public Texture2d LoadTexture(Bitmap bitmap)
{ {
var sdBitmap = (Bitmap)bitmap.Clone(); var sdBitmap = (Bitmap)bitmap.Clone();
GDIPTextureWrapper tw = new GDIPTextureWrapper { SDBitmap = sdBitmap }; var tw = new GDIPTextureWrapper { SDBitmap = sdBitmap };
return new Texture2d(this, tw, bitmap.Width, bitmap.Height); return new(this, tw, bitmap.Width, bitmap.Height);
} }
public Texture2d LoadTexture(Stream stream) public Texture2d LoadTexture(Stream stream)
{ {
using var bmp = new BitmapBuffer(stream, new BitmapLoadOptions()); using var bmp = new BitmapBuffer(stream, new());
return (this as IGL).LoadTexture(bmp); 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) 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; return null;
} }
public void LoadTextureData(Texture2d tex, BitmapBuffer bmp) public void LoadTextureData(Texture2d tex, BitmapBuffer bmp)
{ {
var tw = tex.Opaque as GDIPTextureWrapper; var tw = (GDIPTextureWrapper)tex.Opaque;
bmp.ToSysdrawingBitmap(tw.SDBitmap); bmp.ToSysdrawingBitmap(tw.SDBitmap);
} }
public Texture2d LoadTexture(BitmapBuffer bmp) public Texture2d LoadTexture(BitmapBuffer bmp)
{ {
// definitely needed (by TextureFrugalizer at least) // definitely needed (by TextureFrugalizer at least)
var sdBitmap = bmp.ToSysdrawingBitmap(); var sdBitmap = bmp.ToSysdrawingBitmap();
var tw = new GDIPTextureWrapper { SDBitmap = sdBitmap }; 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) public BitmapBuffer ResolveTexture2d(Texture2d tex)
{ {
var tw = tex.Opaque as GDIPTextureWrapper; var tw = (GDIPTextureWrapper)tex.Opaque;
var blow = new BitmapLoadOptions var blow = new BitmapLoadOptions
{ {
AllowWrap = false // must be an independent resource AllowWrap = false // must be an independent resource
}; };
var bb = new BitmapBuffer(tw.SDBitmap, blow); var bb = new BitmapBuffer(tw.SDBitmap, blow);
return bb; return bb;
} }
public Texture2d LoadTexture(string path) public Texture2d LoadTexture(string path)
{ {
//todo using var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
//using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) return LoadTexture(fs);
// return (this as IGL).LoadTexture(fs);
return null;
} }
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 // see CreateGuiViewMatrix for more
return Matrix4.Identity; 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 // 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 // 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 // this might be feasible, but its kind of slow and annoying and worse, seemingly numerically unstable
//if (autoFlip && _CurrRenderTarget != null) #if false
//{ if (autoFlip && _currRenderTarget != null)
// Matrix4 ret = Matrix4.Identity; {
// ret.M22 = -1; Matrix4 ret = Matrix4.Identity;
// ret.M42 = dims.Height; ret.M22 = -1;
// return ret; ret.M42 = dims.Height;
//} return ret;
//else }
return Matrix4.Identity; #endif
return Matrix4x4.Identity;
} }
public void SetViewport(int x, int y, int width, int height) public void SetViewport(int x, int y, int width, int height)
@ -242,15 +256,11 @@ namespace BizHawk.Bizware.BizwareGL
CurrentControl = control; CurrentControl = control;
} }
public void EndControl(IGraphicsControl control) public void EndControl()
{ {
CurrentControl = null; CurrentControl = null;
} }
public void SwapControl(IGraphicsControl control)
{
}
public void BeginScene() public void BeginScene()
{ {
} }
@ -265,17 +275,15 @@ namespace BizHawk.Bizware.BizwareGL
public IGraphicsControl Internal_CreateGraphicsControl() public IGraphicsControl Internal_CreateGraphicsControl()
{ {
var ret = _createGLControlWrapper(this); var ret = _createGLControlWrapper(this);
// create a render target for this control // create a render target for this control
var rtw = new RenderTargetWrapper(() => MyBufferedGraphicsContext, ret); var rtw = new RenderTargetWrapper(() => MyBufferedGraphicsContext, ret);
ret.RenderTargetWrapper = rtw; ret.RenderTargetWrapper = rtw;
return ret; return ret;
} }
public void FreeRenderTarget(RenderTarget rt) public void FreeRenderTarget(RenderTarget rt)
{ {
var rtw = rt.Opaque as RenderTargetWrapper; var rtw = (RenderTargetWrapper)rt.Opaque;
rtw.Dispose(); rtw.Dispose();
} }
@ -283,7 +291,7 @@ namespace BizHawk.Bizware.BizwareGL
{ {
var tw = new GDIPTextureWrapper 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); var tex = new Texture2d(this, tw, w, h);
@ -295,46 +303,55 @@ namespace BizHawk.Bizware.BizwareGL
public void BindRenderTarget(RenderTarget rt) public void BindRenderTarget(RenderTarget rt)
{ {
if (_CurrentOffscreenGraphics != null) if (_currOffscreenGraphics != null)
{ {
_CurrentOffscreenGraphics.Dispose(); _currOffscreenGraphics.Dispose();
_CurrentOffscreenGraphics = null; _currOffscreenGraphics = null;
} }
#if false
_currRenderTarget = rt; _currRenderTarget = rt;
if (CurrentRenderTargetWrapper != null) if (CurrentRenderTargetWrapper != null)
{ {
if (CurrentRenderTargetWrapper == CurrentControl.RenderTargetWrapper) if (CurrentRenderTargetWrapper == CurrentControl.RenderTargetWrapper)
{ {
//don't do anything til swapbuffers // don't do anything til swapbuffers
} }
else else
{ {
//CurrentRenderTargetWrapper.MyBufferedGraphics.Render(); // CurrentRenderTargetWrapper.MyBufferedGraphics.Render();
} }
} }
#endif
if (rt == null) 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; CurrentRenderTargetWrapper = CurrentControl.RenderTargetWrapper;
} }
else else
{ {
var tw = rt.Texture2d.Opaque as GDIPTextureWrapper; var tw = (GDIPTextureWrapper)rt.Texture2d.Opaque;
CurrentRenderTargetWrapper = rt.Opaque as RenderTargetWrapper; CurrentRenderTargetWrapper = (RenderTargetWrapper)rt.Opaque;
_CurrentOffscreenGraphics = Graphics.FromImage(tw.SDBitmap); _currOffscreenGraphics = Graphics.FromImage(tw.SDBitmap);
//if (CurrentRenderTargetWrapper.MyBufferedGraphics == null) #if false
// CurrentRenderTargetWrapper.CreateGraphics(); if (CurrentRenderTargetWrapper.MyBufferedGraphics == null)
{
CurrentRenderTargetWrapper.CreateGraphics();
}
#endif
} }
} }
private Graphics _CurrentOffscreenGraphics; private Graphics _currOffscreenGraphics;
public Graphics GetCurrentGraphics() public Graphics GetCurrentGraphics()
{ {
if (_CurrentOffscreenGraphics != null) if (_currOffscreenGraphics != null)
return _CurrentOffscreenGraphics; {
return _currOffscreenGraphics;
}
var rtw = CurrentRenderTargetWrapper; var rtw = CurrentRenderTargetWrapper;
return rtw.MyBufferedGraphics.Graphics; return rtw.MyBufferedGraphics.Graphics;
} }
@ -343,7 +360,5 @@ namespace BizHawk.Bizware.BizwareGL
public RenderTargetWrapper CurrentRenderTargetWrapper; public RenderTargetWrapper CurrentRenderTargetWrapper;
public readonly BufferedGraphicsContext MyBufferedGraphicsContext = new(); public readonly BufferedGraphicsContext MyBufferedGraphicsContext = new();
}
} //class IGL_GdiPlus
} }

View File

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

View File

@ -1,17 +1,18 @@
using System.Drawing; using System.Drawing;
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics;
namespace BizHawk.Bizware.BizwareGL namespace BizHawk.Bizware.BizwareGL
{ {
//note: the sense of these matrices, as well as pre- and post- multiplying may be all mixed up. // 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 // 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 // 1. sprite starts with top left at origin
//2. translate half size, to center sprite at origin // 2. translate half size, to center sprite at origin
//3. rotate around Z // 3. rotate around Z
//4. translate to position in world // 4. translate to position in world
//this class is designed to make that work, that way. the takeaways: // this class is designed to make that work, that way. the takeaways:
//* Use the scale, translate, rotate methods in the order given above // * 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 // * 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 public class MatrixStack
{ {
private const float DEG_TO_RAD_FACTOR = (float) System.Math.PI / 180.0f; private const float DEG_TO_RAD_FACTOR = (float) System.Math.PI / 180.0f;
@ -24,19 +25,22 @@ namespace BizHawk.Bizware.BizwareGL
IsDirty = false; IsDirty = false;
} }
public static implicit operator Matrix4(MatrixStack ms) { return ms.Top; } public static implicit operator Matrix4x4(MatrixStack ms) { return ms.Top; }
public static implicit operator MatrixStack(Matrix4 m) { return new MatrixStack(m); } 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; public bool IsDirty;
private readonly Stack<Matrix4> stack = new Stack<Matrix4>(); private readonly Stack<Matrix4x4> stack = new();
/// <summary> /// <summary>
/// This is made public for performance reasons, to avoid lame copies of the matrix when necessary. Don't mess it up! /// This is made public for performance reasons, to avoid lame copies of the matrix when necessary. Don't mess it up!
/// </summary> /// </summary>
public Matrix4 Top; public Matrix4x4 Top;
/// <summary> /// <summary>
/// Resets the matrix stack to an empty identity matrix stack /// Resets the matrix stack to an empty identity matrix stack
@ -51,41 +55,143 @@ namespace BizHawk.Bizware.BizwareGL
/// <summary> /// <summary>
/// Clears the matrix stack and loads the specified value /// Clears the matrix stack and loads the specified value
/// </summary> /// </summary>
public void Clear(Matrix4 value) public void Clear(Matrix4x4 value)
{ {
stack.Clear(); stack.Clear();
Top = value; Top = value;
IsDirty = true; 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 Pop()
public void Push() { stack.Push(Top); IsDirty = true; } {
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 RotateAxis(Vector3 axisRotation, float angle)
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; } PostMultiplyMatrix(Matrix4x4.CreateFromAxisAngle(axisRotation, angle));
public void Scale(float ratio) { Scale(ratio, ratio, ratio); IsDirty = true; } 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(Matrix4.CreateFromAxisAngle(new Vector3(x, y, z), DegreesToRadians(degrees))); IsDirty = true; } public void Scale(Vector3 scale)
public void RotateY(float degrees) { PostMultiplyMatrix(Matrix4.CreateRotationY(DegreesToRadians(degrees))); IsDirty = true; } {
public void RotateX(float degrees) { PostMultiplyMatrix(Matrix4.CreateRotationX(DegreesToRadians(degrees))); IsDirty = true; } PostMultiplyMatrix(Matrix4x4.CreateScale(scale));
public void RotateZ(float degrees) { PostMultiplyMatrix(Matrix4.CreateRotationZ(DegreesToRadians(degrees))); IsDirty = true; } IsDirty = true;
}
public void Translate(Vector2 v) { Translate(v.X, v.Y, 0); IsDirty = true; } public void Scale(Vector2 scale)
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; } PostMultiplyMatrix(Matrix4x4.CreateScale(scale.X, scale.Y, 1));
public void Translate(float x, float y) { Translate(x, y, 0); IsDirty = true; } 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 Scale(float x, float y, float z)
public void PostMultiplyMatrix(Matrix4 value) { Top = Top * value; IsDirty = true; } {
public void PreMultiplyMatrix(Matrix4 value) { Top = value * Top; IsDirty = true; } 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> /// </summary>
public class Pipeline : IDisposable 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; Memo = memo;
Owner = owner; Owner = owner;
@ -24,10 +24,10 @@ namespace BizHawk.Bizware.BizwareGL
if(!Available) if(!Available)
return; return;
UniformsDictionary = new SpecialWorkingDictionary(this); UniformsDictionary = new(this);
foreach (var ui in uniforms) foreach (var ui in uniforms)
{ {
UniformsDictionary[ui.Name] = new PipelineUniform(this); UniformsDictionary[ui.Name] = new(this);
} }
foreach (var ui in uniforms) 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. /// 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 /// Shader compilers will optimize out unused uniforms, and we wont have a record of it in the uniforms population loop
/// </summary> /// </summary>
private class SpecialWorkingDictionary : Dictionary<string, PipelineUniform> private class UniformWorkingDictionary : Dictionary<string, PipelineUniform>
{ {
public SpecialWorkingDictionary(Pipeline owner) public UniformWorkingDictionary(Pipeline owner)
{ {
Owner = 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; private IDictionary<string, PipelineUniform> Uniforms => UniformsDictionary;
public IEnumerable<PipelineUniform> GetUniforms() => Uniforms.Values; public IEnumerable<PipelineUniform> GetUniforms() => Uniforms.Values;

View File

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

View File

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

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Drawing; using System.Drawing;
using System.Numerics;
namespace BizHawk.Bizware.BizwareGL 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.DefineVertexAttribute("tex", 2, 2, VertexAttribPointerType.Float, AttribUsage.Texcoord0, false, 40, 32);
VertexLayout.Close(); VertexLayout.Close();
string defines = ""; var vsSource = $"#define VERTEX\r\n{source}";
string vsSource = $"#define VERTEX\r\n{defines}{source}"; var psSource = $"#define FRAGMENT\r\n{source}";
string psSource = $"#define FRAGMENT\r\n{defines}{source}";
var vs = owner.CreateVertexShader(vsSource, "main_vertex", debug); var vs = owner.CreateVertexShader(vsSource, "main_vertex", debug);
var ps = owner.CreateFragmentShader(psSource, "main_fragment", debug); var ps = owner.CreateFragmentShader(psSource, "main_fragment", debug);
Pipeline = Owner.CreatePipeline(VertexLayout, vs, ps, debug, "retro"); Pipeline = Owner.CreatePipeline(VertexLayout, vs, ps, debug, "retro");
@ -39,7 +39,7 @@ namespace BizHawk.Bizware.BizwareGL
sampler0 = Pipeline.TryGetUniform("s_p"); sampler0 = Pipeline.TryGetUniform("s_p");
if (sampler0 == null) 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()) foreach (var u in Pipeline.GetUniforms())
{ {
if (u.Sole.IsSampler && u.Sole.SamplerIndex == 0) 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 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) Available = sampler0 != null;
return;
Available = true;
} }
public bool Available { get; } public bool Available { get; }
@ -69,16 +66,13 @@ namespace BizHawk.Bizware.BizwareGL
public void Bind() public void Bind()
{ {
//lame... // lame...
Owner.BindPipeline(Pipeline); Owner.BindPipeline(Pipeline);
} }
public unsafe void Run(Texture2d tex, Size InputSize, Size OutputSize, bool flip) public unsafe void Run(Texture2d tex, Size InputSize, Size OutputSize, bool flip)
{ {
flip = false; // ack! make sure to set the pipeline before setting uniforms
//test
//ack! make sure to set the pipeline before setting uniforms
Bind(); Bind();
Pipeline["IN.video_size"].Set(new Vector2(InputSize.Width, InputSize.Height)); 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_count"].Set(1); //todo
Pipeline["IN.frame_direction"].Set(1); //todo Pipeline["IN.frame_direction"].Set(1); //todo
var Projection = Owner.CreateGuiProjectionMatrix(OutputSize); var Projection = Owner.CreateGuiProjectionMatrix(OutputSize);
var Modelview = Owner.CreateGuiViewMatrix(OutputSize); var Modelview = Owner.CreateGuiViewMatrix(OutputSize);
var mat = Matrix4.Transpose(Modelview * Projection); var mat = Matrix4x4.Transpose(Modelview * Projection);
Pipeline["modelViewProj"].Set(mat, true); Pipeline["modelViewProj"].Set(mat, true);
Owner.SetTextureWrapMode(tex, true); Owner.SetTextureWrapMode(tex, true);
@ -98,16 +91,26 @@ namespace BizHawk.Bizware.BizwareGL
sampler0.Set(tex); sampler0.Set(tex);
Owner.SetViewport(OutputSize); 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); Pipeline["Time"].Set(time);
int w = OutputSize.Width; var w = OutputSize.Width;
int h = OutputSize.Height; var h = OutputSize.Height;
float v0,v1;
if (flip) { v0 = 1; v1 = 0; } float v0, v1;
else { v0 = 0; v1 = 1; } if (flip)
float* pData = stackalloc float[10*4]; {
int i=0; 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++] = 1; //topleft vert
pData[i++] = 0; pData[i++] = 0; pData[i++] = 0; pData[i++] = 0; //useless color pData[i++] = 0; pData[i++] = 0; pData[i++] = 0; pData[i++] = 0; //useless color
pData[i++] = 0; pData[i++] = v0; pData[i++] = 0; pData[i++] = v0;
@ -119,7 +122,7 @@ namespace BizHawk.Bizware.BizwareGL
pData[i++] = 0; pData[i++] = v1; pData[i++] = 0; pData[i++] = v1;
pData[i++] = w; pData[i++] = h; pData[i++] = 0; pData[i++] = 1; //bottomright vert 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++] = 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.SetBlendState(Owner.BlendNoneCopy);
Owner.BindArrayData(new(pData)); Owner.BindArrayData(new(pData));
@ -130,6 +133,6 @@ namespace BizHawk.Bizware.BizwareGL
public IGL Owner { get; } public IGL Owner { get; }
private readonly VertexLayout VertexLayout; private readonly VertexLayout VertexLayout;
public Pipeline Pipeline; public readonly Pipeline Pipeline;
} }
} }

View File

@ -3,9 +3,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing;
using System.IO; using System.IO;
using Cyotek.Drawing.BitmapFont; using Cyotek.Drawing.BitmapFont;
using sd=System.Drawing;
namespace BizHawk.Bizware.BizwareGL namespace BizHawk.Bizware.BizwareGL
{ {
@ -18,7 +19,7 @@ namespace BizHawk.Bizware.BizwareGL
FontInfo.LoadXml(xml); FontInfo.LoadXml(xml);
// load textures // 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])); TexturePages.Add(owner.LoadTexture(textures[i]));
} }
@ -27,25 +28,30 @@ namespace BizHawk.Bizware.BizwareGL
public void Dispose() public void Dispose()
{ {
foreach (var tex in TexturePages) foreach (var tex in TexturePages)
{
tex.Dispose(); tex.Dispose();
}
TexturePages = null; TexturePages = null;
} }
public sd.SizeF Measure(string str) public SizeF Measure(string str)
{ {
float x = 0; float x = 0;
float y = FontInfo.LineHeight; float y = FontInfo.LineHeight;
float ox = x; var ox = x;
int len = str.Length; var len = str.Length;
for (int i = 0; i < len; i++) for (var i = 0; i < len; i++)
{ {
var c = str[i]; var c = str[i];
if (c == '\r') if (c == '\r')
{ {
if (i != len - 1 && str[i + 1] == '\n') if (i != len - 1 && str[i + 1] == '\n')
{
i++; i++;
}
} }
if (c == '\r') if (c == '\r')
@ -56,33 +62,42 @@ namespace BizHawk.Bizware.BizwareGL
if (c == '\n') if (c == '\n')
{ {
if (x > ox) if (x > ox)
{
ox = x; ox = x;
}
x = 0; x = 0;
y += FontInfo.LineHeight; y += FontInfo.LineHeight;
continue; continue;
} }
var bfc = FontInfo[c]; var bfc = FontInfo[c];
x += bfc.XAdvance; 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) public void RenderString(IGuiRenderer renderer, float x, float y, string str)
{ {
float ox = x; if (Owner != renderer.Owner)
int len = str.Length; {
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]; var c = str[i];
if (c == '\r') if (c == '\r')
{ {
if (i != len - 1 && str[i + 1] == '\n') if (i != len - 1 && str[i + 1] == '\n')
{
i++; i++;
}
} }
if (c == '\r') if (c == '\r')
@ -90,7 +105,7 @@ namespace BizHawk.Bizware.BizwareGL
c = '\n'; c = '\n';
} }
if(c == '\n') if (c == '\n')
{ {
x = ox; x = ox;
y += FontInfo.LineHeight; y += FontInfo.LineHeight;
@ -98,19 +113,19 @@ namespace BizHawk.Bizware.BizwareGL
} }
var bfc = FontInfo[c]; 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; // calculate texcoords (we shouldve already had this cached, but im speedcoding now)
float gy = y + bfc.YOffset; 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); renderer.DrawSubrect(tex, gx, gy, bfc.Width, bfc.Height, u0, v0, u1, v1);
x += bfc.XAdvance; x += bfc.XAdvance;
@ -120,6 +135,6 @@ namespace BizHawk.Bizware.BizwareGL
public IGL Owner { get; } public IGL Owner { get; }
private readonly BitmapFont FontInfo; 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; Height = height;
Item = item; Item = item;
} }
public int X, Y; public int X, Y;
public int Width, Height; public int Width, Height;
public int TexIndex; public int TexIndex;
@ -31,14 +32,14 @@ namespace BizHawk.Bizware.BizwareGL
public readonly List<RectangleBinPack.Node> nodes = new List<RectangleBinPack.Node>(); public readonly List<RectangleBinPack.Node> nodes = new List<RectangleBinPack.Node>();
} }
public static int MaxSizeBits = 16; public const int MaxSizeBits = 16;
/// <summary> /// <summary>
/// packs the supplied RectItems into an atlas. Modifies the RectItems with x/y values of location in new atlas. /// packs the supplied RectItems into an atlas. Modifies the RectItems with x/y values of location in new atlas.
/// </summary> /// </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> currentItems = new(initItems);
List<RectItem> remainItems = new(); 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. // 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. // ALSO - we accumulate data in there, so we need to refresh it each time. ... lame.
var todoSizes = new List<TryFitParam>(); 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; var w = 1 << i;
int h = 1 << j; var h = 1 << j;
TryFitParam tfp = new TryFitParam(w, h); var tfp = new TryFitParam(w, h);
todoSizes.Add(tfp); todoSizes.Add(tfp);
} }
} }
@ -68,9 +69,10 @@ namespace BizHawk.Bizware.BizwareGL
rbp.Init(16384, 16384); rbp.Init(16384, 16384);
param.rbp.Init(param.w, param.h); param.rbp.Init(param.w, param.h);
// ReSharper disable once AccessToModifiedClosure
foreach (var ri in currentItems) 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) if (node == null)
{ {
param.ok = false; 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; var best = long.MaxValue;
tfpFinal = todoSizes[0]; tfpFinal = todoSizes[0];
foreach (var tfp in todoSizes) foreach (var tfp in todoSizes)
{ {
if (!tfp.ok) continue; if (!tfp.ok)
{
continue;
}
var area = tfp.w * (long) tfp.h; 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 (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; best = area;
tfpFinal = tfp; tfpFinal = tfp;
} }
//did we find any fit? //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 //nope - move an item to the remaining list and try again
remainItems.Add(currentItems[currentItems.Count - 1]); remainItems.Add(currentItems[currentItems.Count - 1]);
currentItems.RemoveAt(currentItems.Count - 1); currentItems.RemoveAt(currentItems.Count - 1);
} }
//we found a fit. setup this atlas in the result and drop the items into it //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) 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.X = node.x;
item.Y = node.y; item.Y = node.y;
item.TexIndex = atlases.Count - 1; item.TexIndex = atlases.Count - 1;
@ -157,7 +174,7 @@ namespace BizHawk.Bizware.BizwareGL
{ {
binWidth = width; binWidth = width;
binHeight = height; binHeight = height;
root = new Node(); root = new();
root.left = root.right = null; root.left = root.right = null;
root.x = root.y = 0; root.x = root.y = 0;
root.width = width; root.width = width;
@ -176,8 +193,8 @@ namespace BizHawk.Bizware.BizwareGL
/// <summary>Computes the ratio of used surface area.</summary> /// <summary>Computes the ratio of used surface area.</summary>
private float Occupancy() private float Occupancy()
{ {
int totalSurfaceArea = binWidth * binHeight; var totalSurfaceArea = binWidth * binHeight;
int usedSurfaceArea = UsedSurfaceArea(root); var usedSurfaceArea = UsedSurfaceArea(root);
return (float)usedSurfaceArea / totalSurfaceArea; return (float)usedSurfaceArea / totalSurfaceArea;
} }
@ -189,11 +206,11 @@ namespace BizHawk.Bizware.BizwareGL
private int binHeight; private int binHeight;
/// <returns>The surface area used by the subtree rooted at node.</returns> /// <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) if (node.left != null || node.right != null)
{ {
int usedSurfaceArea = node.width * node.height; var usedSurfaceArea = node.width * node.height;
if (node.left != null) if (node.left != null)
usedSurfaceArea += UsedSurfaceArea(node.left); usedSurfaceArea += UsedSurfaceArea(node.left);
if (node.right != null) 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> /// <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. // 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) if (node.left != null)
{ {
Node newNode = Insert(node.left, width, height); var newNode = Insert(node.left, width, height);
if (newNode != null) if (newNode != null)
return newNode; return newNode;
} }
if (node.right != null) if (node.right != null)
{ {
Node newNode = Insert(node.right, width, height); var newNode = Insert(node.right, width, height);
if (newNode != null) if (newNode != null)
return newNode; return newNode;
} }
@ -236,10 +253,10 @@ namespace BizHawk.Bizware.BizwareGL
// The new cell will fit, split the remaining space along the shorter axis, // The new cell will fit, split the remaining space along the shorter axis,
// that is probably more optimal. // that is probably more optimal.
int w = node.width - width; var w = node.width - width;
int h = node.height - height; var h = node.height - height;
node.left = new Node(); node.left = new();
node.right = new Node(); node.right = new();
if (w <= h) // Split the remaining space in horizontal direction. if (w <= h) // Split the remaining space in horizontal direction.
{ {
node.left.x = node.x + width; node.left.x = node.x + width;

View File

@ -16,7 +16,7 @@ namespace BizHawk.Bizware.BizwareGL
{ {
Owner = owner; Owner = owner;
Opaque = opaque; Opaque = opaque;
Items = new MyDictionary(); Items = new();
} }
public object Opaque { get; } public object Opaque { get; }
@ -29,8 +29,10 @@ namespace BizHawk.Bizware.BizwareGL
RefCount--; RefCount--;
if (RefCount <= 0) if (RefCount <= 0)
{ {
//nothing like this yet // nothing like this yet
//Available = false; #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) public void DefineVertexAttribute(string name, int index, int components, VertexAttribPointerType attribType, AttribUsage usage, bool normalized, int stride, int offset = 0)
{ {
if (Closed) if (Closed)
{
throw new InvalidOperationException("Type is Closed and is now immutable."); 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> /// <summary>
@ -66,7 +71,7 @@ namespace BizHawk.Bizware.BizwareGL
public AttribUsage Usage { get; internal set; } public AttribUsage Usage { get; internal set; }
} }
public class MyDictionary : WorkingDictionary<int, LayoutItem> public class LayoutItemWorkingDictionary : WorkingDictionary<int, LayoutItem>
{ {
public new LayoutItem this[int key] public new LayoutItem this[int key]
{ {
@ -75,8 +80,8 @@ namespace BizHawk.Bizware.BizwareGL
} }
} }
public MyDictionary Items { get; } public LayoutItemWorkingDictionary Items { get; }
private bool Closed = false; 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.Drawing;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Numerics;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; 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); => 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) foreach (var ui in uniform.UniformInfos)
{ {
@ -755,32 +756,32 @@ namespace BizHawk.Bizware.Graphics
return LoadTexture(fs); return LoadTexture(fs);
} }
public Matrix4 CreateGuiProjectionMatrix(int w, int h) public Matrix4x4 CreateGuiProjectionMatrix(int w, int h)
{ {
return CreateGuiProjectionMatrix(new(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(w, h), autoFlip); return CreateGuiViewMatrix(new(w, h), autoFlip);
} }
public Matrix4 CreateGuiProjectionMatrix(Size dims) public Matrix4x4 CreateGuiProjectionMatrix(Size dims)
{ {
var ret = Matrix4.Identity; var ret = Matrix4x4.Identity;
ret.Row0.X = 2.0f / dims.Width; ret.M11 = 2.0f / dims.Width;
ret.Row1.Y = 2.0f / dims.Height; ret.M22 = 2.0f / dims.Height;
return ret; return ret;
} }
public Matrix4 CreateGuiViewMatrix(Size dims, bool autoFlip) public Matrix4x4 CreateGuiViewMatrix(Size dims, bool autoFlip)
{ {
var ret = Matrix4.Identity; var ret = Matrix4x4.Identity;
ret.Row1.Y = -1.0f; ret.M22 = -1.0f;
ret.Row3.X = -dims.Width * 0.5f - 0.5f; ret.M41 = -dims.Width * 0.5f - 0.5f;
ret.Row3.Y = dims.Height * 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; return ret;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Drawing; using System.Drawing;
using System.Numerics;
using BizHawk.Bizware.BizwareGL; using BizHawk.Bizware.BizwareGL;
using BizHawk.Client.Common.FilterManager; using BizHawk.Client.Common.FilterManager;
@ -28,19 +29,21 @@ namespace BizHawk.Client.Common.Filters
/// <summary> /// <summary>
/// In case you want to do it yourself /// In case you want to do it yourself
/// </summary> /// </summary>
public LetterboxingLogic() { } public LetterboxingLogic()
{
}
// do maths on the viewport and the native resolution and the user settings to get a display rectangle // do maths on the viewport and the native resolution and the user settings to get a display rectangle
public LetterboxingLogic(bool maintainAspect, bool maintainInteger, int targetWidth, int targetHeight, int sourceWidth, int sourceHeight, Size textureSize, Size virtualSize) public LetterboxingLogic(bool maintainAspect, bool maintainInteger, int targetWidth, int targetHeight, int sourceWidth, int sourceHeight, Size textureSize, Size virtualSize)
{ {
int textureWidth = textureSize.Width; var textureWidth = textureSize.Width;
int textureHeight = textureSize.Height; var textureHeight = textureSize.Height;
int virtualWidth = virtualSize.Width; var virtualWidth = virtualSize.Width;
int virtualHeight = virtualSize.Height; var virtualHeight = virtualSize.Height;
//zero 02-jun-2014 - we passed these in, but ignored them. kind of weird.. // zero 02-jun-2014 - we passed these in, but ignored them. kind of weird..
int oldSourceWidth = sourceWidth; var oldSourceWidth = sourceWidth;
int oldSourceHeight = sourceHeight; var oldSourceHeight = sourceHeight;
sourceWidth = virtualWidth; sourceWidth = virtualWidth;
sourceHeight = virtualHeight; sourceHeight = virtualHeight;
@ -50,8 +53,8 @@ namespace BizHawk.Client.Common.Filters
maintainInteger = false; maintainInteger = false;
} }
float widthScale = (float)targetWidth / sourceWidth; var widthScale = (float)targetWidth / sourceWidth;
float heightScale = (float)targetHeight / sourceHeight; var heightScale = (float)targetHeight / sourceHeight;
if (maintainAspect if (maintainAspect
// zero 20-jul-2014 - hacks upon hacks, this function needs rewriting // zero 20-jul-2014 - hacks upon hacks, this function needs rewriting
@ -68,61 +71,60 @@ namespace BizHawk.Client.Common.Filters
// apply the zooming algorithm (pasted and reworked, for now) // apply the zooming algorithm (pasted and reworked, for now)
// ALERT COPYPASTE LAUNDROMAT // ALERT COPYPASTE LAUNDROMAT
Vector2 AR = new(virtualWidth / (float) textureWidth, virtualHeight / (float) textureHeight); var AR = new Vector2(virtualWidth / (float) textureWidth, virtualHeight / (float) textureHeight);
float targetPar = (AR.X / AR.Y); var targetPar = AR.X / AR.Y;
Vector2 PS = new Vector2(1, 1); //this would malfunction for AR <= 0.5 or AR >= 2.0 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.... // TODO - would be good not to run this per frame....
Vector2[] trials = trials[0] = PS + Vector2.UnitX;
{ trials[1] = PS + Vector2.UnitY;
PS + new Vector2(1, 0), trials[2] = PS + Vector2.One;
PS + new Vector2(0, 1),
PS + new Vector2(1, 1)
};
bool[] trialsLimited = { false, false, false }; var bestIndex = -1;
int bestIndex = -1; var bestValue = 1000.0f;
float bestValue = 1000.0f; for (var t = 0; t < trials.Length; t++)
for (int t = 0; t < trials.Length; t++)
{ {
Vector2 vTrial = trials[t]; var vTrial = trials[t];
trialsLimited[t] = false; trialsLimited[t] = false;
//check whether this is going to exceed our allotted area //check whether this is going to exceed our allotted area
int testVw = (int)(vTrial.X * textureWidth); var testVw = (int)(vTrial.X * textureWidth);
int testVh = (int)(vTrial.Y * textureHeight); var testVh = (int)(vTrial.Y * textureHeight);
if (testVw > targetWidth) trialsLimited[t] = true; if (testVw > targetWidth) trialsLimited[t] = true;
if (testVh > targetHeight) trialsLimited[t] = true; if (testVh > targetHeight) trialsLimited[t] = true;
// I. // I.
float testAr = vTrial.X / vTrial.Y; var testAr = vTrial.X / vTrial.Y;
// II. // II.
// Vector2 calc = Vector2.Multiply(trials[t], VS); // var calc = Vector2.Multiply(trials[t], VS);
// float test_ar = calc.X / calc.Y; // var test_ar = calc.X / calc.Y;
// not clear which approach is superior // 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; var deviationLinear = Math.Abs(testAr - targetPar);
if (value < bestValue) if (deviationLinear < bestValue)
{ {
bestIndex = t; bestIndex = t;
bestValue = value; bestValue = deviationLinear;
} }
} }
// last result was best, so bail out // last result was best, so bail out
if (bestIndex == -1) if (bestIndex == -1)
{
break; break;
}
// if the winner ran off the edge, bail out // if the winner ran off the edge, bail out
if (trialsLimited[bestIndex]) if (trialsLimited[bestIndex])
{
break; break;
}
PS = trials[bestIndex]; PS = trials[bestIndex];
} }
@ -132,8 +134,10 @@ namespace BizHawk.Client.Common.Filters
// vh = (int)(PS.Y * oldSourceHeight); // vh = (int)(PS.Y * oldSourceHeight);
vw = (int)(PS.X * sourceWidth); vw = (int)(PS.X * sourceWidth);
vh = (int)(PS.Y * sourceHeight); vh = (int)(PS.Y * sourceHeight);
#if false
widthScale = PS.X; widthScale = PS.X;
heightScale = PS.Y; heightScale = PS.Y;
#endif
} }
else else
{ {
@ -154,17 +158,20 @@ namespace BizHawk.Client.Common.Filters
vh = targetHeight; vh = targetHeight;
} }
//determine letterboxing parameters // determine letterboxing parameters
vx = (targetWidth - vw) / 2; vx = (targetWidth - vw) / 2;
vy = (targetHeight - vh) / 2; vy = (targetHeight - vh) / 2;
// zero 09-oct-2014 - changed this for TransformPoint. scenario: basic 1x (but system-specified AR) NES window. // zero 09-oct-2014 - changed this for TransformPoint. scenario: basic 1x (but system-specified AR) NES window.
// vw would be 293 but WidthScale would be 1.0. I think it should be something different. // vw would be 293 but WidthScale would be 1.0. I think it should be something different.
// FinalPresentation doesn't use the LL.WidthScale, so this is unlikely to be breaking anything old that depends on it // FinalPresentation doesn't use the LL.WidthScale, so this is unlikely to be breaking anything old that depends on it
// WidthScale = widthScale; #if false
// HeightScale = heightScale; WidthScale = widthScale;
HeightScale = heightScale;
#else
WidthScale = (float)vw / oldSourceWidth; WidthScale = (float)vw / oldSourceWidth;
HeightScale = (float)vh / oldSourceHeight; HeightScale = (float)vh / oldSourceHeight;
#endif
} }
} }
@ -173,34 +180,26 @@ namespace BizHawk.Client.Common.Filters
/// </summary> /// </summary>
public class ScreenControlNDS : BaseFilter public class ScreenControlNDS : BaseFilter
{ {
public IGL GL; private readonly NDS _nds;
public IGuiRenderer GuiRenderer;
private readonly NDS nds; // TODO: actually use this
#if false
private bool Nop = false;
#endif
//TODO: actually use this // matrices used for transforming screens
private bool nop = false; private Matrix4x4 matTop, matBot;
private Matrix4x4 matTopInvert, matBotInvert;
//matrices used for transforming screens // final output area size
private Matrix4 matTop, matBot;
private Matrix4 matTopInvert, matBotInvert;
//final output area size
private Size outputSize; private Size outputSize;
private static float Round(float f) { return (float)Math.Round(f); } private static float Round(float f)
=> (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);
}
public ScreenControlNDS(NDS nds) public ScreenControlNDS(NDS nds)
{ {
//not sure if we actually need this nds instance yet _nds = nds;
this.nds = nds;
} }
public override void Initialize() public override void Initialize()
@ -211,63 +210,65 @@ namespace BizHawk.Client.Common.Filters
private void CrunchNumbers() 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
//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
//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 switch (settings.ScreenLayout)
if (settings.ScreenLayout == NDS.ScreenLayoutKind.Vertical)
{ {
bot.Translate(0, 192); //gap only applies to vertical, I guess
bot.Translate(0, settings.ScreenGap); case NDS.ScreenLayoutKind.Vertical:
} bot.Translate(0, 192);
else if (settings.ScreenLayout == NDS.ScreenLayoutKind.Horizontal) bot.Translate(0, settings.ScreenGap);
{ break;
bot.Translate(256, 0); case NDS.ScreenLayoutKind.Horizontal:
} bot.Translate(256, 0);
else if (settings.ScreenLayout == NDS.ScreenLayoutKind.Top) break;
{ case NDS.ScreenLayoutKind.Top:
//do nothing here, we'll discard bottom screen 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) if (settings.ScreenLayout != NDS.ScreenLayoutKind.Horizontal)
{ {
int rot = 0; var rot = settings.ScreenRotation switch
if (settings.ScreenRotation == NDS.ScreenRotationKind.Rotate90) rot = 90; {
if (settings.ScreenRotation == NDS.ScreenRotationKind.Rotate180) rot = 180; NDS.ScreenRotationKind.Rotate90 => 90,
if (settings.ScreenRotation == NDS.ScreenRotationKind.Rotate270) rot = 270; NDS.ScreenRotationKind.Rotate180 => 180,
NDS.ScreenRotationKind.Rotate270 => 270,
_ => 0
};
top.RotateZ(rot); top.RotateZ(rot);
bot.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; matTop = top.Top;
matBot = bot.Top; matBot = bot.Top;
matTopInvert = matTop.Inverted();
matBotInvert = matBot.Inverted();
//apply transforms from standard input screen positions to output screen positions // apply transforms from standard input screen positions to output screen positions
Vector2 top_TL = Transform(matTop, new Vector2(0, 0)); var top_TL = Vector2.Transform(new(0, 0), matTop);
Vector2 top_TR = Transform(matTop, new Vector2(256, 0)); var top_TR = Vector2.Transform(new(256, 0), matTop);
Vector2 top_BL = Transform(matTop, new Vector2(0, 192)); var top_BL = Vector2.Transform(new(0, 192), matTop);
Vector2 top_BR = Transform(matTop, new Vector2(256, 192)); var top_BR = Vector2.Transform(new(256, 192), matTop);
Vector2 bot_TL = Transform(matBot, new Vector2(0, 0)); var bot_TL = Vector2.Transform(new(0, 0), matBot);
Vector2 bot_TR = Transform(matBot, new Vector2(256, 0)); var bot_TR = Vector2.Transform(new(256, 0), matBot);
Vector2 bot_BL = Transform(matBot, new Vector2(0, 192)); var bot_BL = Vector2.Transform(new(0, 192), matBot);
Vector2 bot_BR = Transform(matBot, new Vector2(256, 192)); var bot_BR = Vector2.Transform(new(256, 192), matBot);
//in case of math errors in the transforms, we'll round this stuff.. although... // 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 // 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_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_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); 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_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); bot_BR.X = Round(bot_BR.X); bot_BR.Y = Round(bot_BR.Y);
////precalculate some useful metrics #if false
//top_width = (int)(top_TR.X - top_TL.X); // precalculate some useful metrics
//top_height = (int)(top_BR.Y - top_TR.Y); top_width = (int)(top_TR.X - top_TL.X);
//bot_width = (int)(bot_TR.X - bot_TL.X); top_height = (int)(top_BR.Y - top_TR.Y);
//bot_height = (int)(bot_BR.Y - bot_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; float fxmin = 100000, fymin = 100000, fxmax = -100000, fymax = -100000;
if (settings.ScreenLayout != NDS.ScreenLayoutKind.Bottom) 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); 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); top.Translate(-fxmin, -fymin);
bot.Translate(-fxmin, -fymin); bot.Translate(-fxmin, -fymin);
matTop = top.Top; matTop = top.Top;
matBot = bot.Top; matBot = bot.Top;
//do some more rounding // do some more rounding
for (int r = 0; r < 4; r++) unsafe
for (int c = 0; c < 4; c++) {
fixed (Matrix4x4* matTopP = &matTop, matBotP = &matBot)
{ {
if (Math.Abs(matTop[r, c]) < 0.0000001f) matTop[r, c] = 0; float* matTopF = (float*)matTopP, matBotF = (float*)matBotP;
if (Math.Abs(matBot[r, c]) < 0.0000001f) matBot[r, c] = 0; 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(); Matrix4x4.Invert(matTop, out matTopInvert);
matBotInvert = matBot.Inverted(); 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) 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) public override void SetInputFormat(string channel, SurfaceState state)
{ {
CrunchNumbers(); CrunchNumbers();
var ss = new SurfaceState(new SurfaceFormat(outputSize), SurfaceDisposition.RenderTarget); var ss = new SurfaceState(new(outputSize), SurfaceDisposition.RenderTarget);
DeclareOutput(ss, channel); DeclareOutput(ss, channel);
} }
public override Vector2 UntransformPoint(string channel, Vector2 point) public override Vector2 UntransformPoint(string channel, Vector2 point)
{ {
var settings = nds.GetSettings(); var settings = _nds.GetSettings();
bool invert = settings.ScreenInvert var invert = settings.ScreenInvert
&& settings.ScreenLayout != NDS.ScreenLayoutKind.Top && settings.ScreenLayout != NDS.ScreenLayoutKind.Top
&& settings.ScreenLayout != NDS.ScreenLayoutKind.Bottom; && 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) // 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. // 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. // 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() // (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; 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); if (settings.ScreenLayout == NDS.ScreenLayoutKind.Top) point = new(0.0f, 0.0f);
//TODO: we probably need more subtle logic here. // TODO: we probably need more subtle logic here.
//some capability to return -1,-1 perhaps in case the cursor is nowhere. // some capability to return -1,-1 perhaps in case the cursor is nowhere.
//not sure about that // not sure about that
return point; return point;
} }
public override Vector2 TransformPoint(string channel, Vector2 point) public override Vector2 TransformPoint(string channel, Vector2 point)
{ {
point = Transform(matBot, point); return Vector2.Transform(point, matTop);
return point;
} }
public override void Run() public override void Run()
{ {
if (nop) #if false
if (Nop)
{
return; return;
}
#endif
//TODO: this could be more efficient (draw only in gap) // TODO: this could be more efficient (draw only in gap)
GL.SetClearColor(Color.Black); FilterProgram.GL.SetClearColor(Color.Black);
GL.Clear(ClearBufferMask.ColorBufferBit); FilterProgram.GL.Clear(ClearBufferMask.ColorBufferBit);
FilterProgram.GuiRenderer.Begin(outputSize); 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 // 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. // 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. // so we will have to break them into render targets first.
InputTexture.SetFilterNearest(); InputTexture.SetFilterNearest();
//draw screens //draw screens
bool renderTop = false; var renderTop = false;
bool renderBottom = false; var renderBottom = false;
var settings = nds.GetSettings(); var settings = _nds.GetSettings();
if (settings.ScreenLayout == NDS.ScreenLayoutKind.Bottom) renderBottom = true; switch (settings.ScreenLayout)
if (settings.ScreenLayout == NDS.ScreenLayoutKind.Top) renderTop = true; {
if (settings.ScreenLayout == NDS.ScreenLayoutKind.Vertical) renderTop = renderBottom = true; case NDS.ScreenLayoutKind.Bottom:
if (settings.ScreenLayout == NDS.ScreenLayoutKind.Horizontal) renderTop = renderBottom = true; 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) if (renderTop)
{ {
GuiRenderer.Modelview.Push(); FilterProgram.GuiRenderer.Modelview.Push();
GuiRenderer.Modelview.PreMultiplyMatrix(invert ? matBot : matTop); FilterProgram.GuiRenderer.Modelview.PreMultiplyMatrix(invert ? matBot : matTop);
GuiRenderer.DrawSubrect(InputTexture, 0, 0, 256, 192, 0.0f, 0.0f, 1.0f, 0.5f); FilterProgram.GuiRenderer.DrawSubrect(InputTexture, 0, 0, 256, 192, 0.0f, 0.0f, 1.0f, 0.5f);
GuiRenderer.Modelview.Pop(); FilterProgram.GuiRenderer.Modelview.Pop();
} }
if (renderBottom) if (renderBottom)
{ {
GuiRenderer.Modelview.Push(); FilterProgram.GuiRenderer.Modelview.Push();
GuiRenderer.Modelview.PreMultiplyMatrix(invert ? matTop : matBot); FilterProgram.GuiRenderer.Modelview.PreMultiplyMatrix(invert ? matTop : matBot);
GuiRenderer.DrawSubrect(InputTexture, 0, 0, 256, 192, 0.0f, 0.5f, 1.0f, 1.0f); FilterProgram.GuiRenderer.DrawSubrect(InputTexture, 0, 0, 256, 192, 0.0f, 0.5f, 1.0f, 1.0f);
GuiRenderer.Modelview.Pop(); FilterProgram.GuiRenderer.Modelview.Pop();
} }
GuiRenderer.End(); FilterProgram.GuiRenderer.End();
} }
} }
@ -505,12 +535,8 @@ namespace BizHawk.Client.Common.Filters
private Size OutputSize, InputSize; private Size OutputSize, InputSize;
public Size TextureSize, VirtualTextureSize; public Size TextureSize, VirtualTextureSize;
public int BackgroundColor; public int BackgroundColor;
public bool AutoPrescale; private bool Nop;
public IGuiRenderer GuiRenderer;
public IGL GL;
private bool nop;
private LetterboxingLogic LL; private LetterboxingLogic LL;
private Size ContentSize;
public bool Config_FixAspectRatio, Config_FixScaleInteger, Config_PadOnly; public bool Config_FixAspectRatio, Config_FixScaleInteger, Config_PadOnly;
@ -522,7 +548,7 @@ namespace BizHawk.Client.Common.Filters
public override void Initialize() public override void Initialize()
{ {
DeclareInput(); DeclareInput();
nop = false; Nop = false;
} }
public override Size PresizeOutput(string channel, Size size) public override Size PresizeOutput(string channel, Size size)
@ -533,6 +559,7 @@ namespace BizHawk.Client.Common.Filters
size.Height = LL.vh; size.Height = LL.vh;
return size; return size;
} }
return base.PresizeOutput(channel, size); return base.PresizeOutput(channel, size);
} }
@ -543,8 +570,8 @@ namespace BizHawk.Client.Common.Filters
if (Config_PadOnly) if (Config_PadOnly)
{ {
//TODO - redundant fix // TODO - redundant fix
LL = new LetterboxingLogic(); LL = new();
LL.vx += Padding.Left; LL.vx += Padding.Left;
LL.vy += Padding.Top; LL.vy += Padding.Top;
LL.vw = size.Width; LL.vw = size.Width;
@ -552,7 +579,7 @@ namespace BizHawk.Client.Common.Filters
} }
else 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.vx += Padding.Left;
LL.vy += Padding.Top; LL.vy += Padding.Top;
} }
@ -562,22 +589,20 @@ namespace BizHawk.Client.Common.Filters
public override void SetInputFormat(string channel, SurfaceState state) 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) if (!need)
{ {
nop = true; Nop = true;
ContentSize = state.SurfaceFormat.Size;
return; return;
} }
FindInput().SurfaceDisposition = SurfaceDisposition.Texture; FindInput().SurfaceDisposition = SurfaceDisposition.Texture;
DeclareOutput(new SurfaceState(new SurfaceFormat(OutputSize), SurfaceDisposition.RenderTarget)); DeclareOutput(new SurfaceState(new(OutputSize), SurfaceDisposition.RenderTarget));
InputSize = state.SurfaceFormat.Size; InputSize = state.SurfaceFormat.Size;
if (Config_PadOnly) if (Config_PadOnly)
{ {
//TODO - redundant fix // TODO - redundant fix
LL = new LetterboxingLogic(); LL = new();
LL.vx += Padding.Left; LL.vx += Padding.Left;
LL.vy += Padding.Top; LL.vy += Padding.Top;
LL.vw = InputSize.Width; LL.vw = InputSize.Width;
@ -587,26 +612,28 @@ namespace BizHawk.Client.Common.Filters
} }
else else
{ {
int ow = OutputSize.Width; var ow = OutputSize.Width;
int oh = OutputSize.Height; var oh = OutputSize.Height;
ow -= Padding.Left + Padding.Right; ow -= Padding.Left + Padding.Right;
oh -= Padding.Top + Padding.Bottom; 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.vx += Padding.Left;
LL.vy += Padding.Top; LL.vy += Padding.Top;
} }
ContentSize = new Size(LL.vw,LL.vh);
if (InputSize == OutputSize) //any reason we need to check vx and vy? if (InputSize == OutputSize) // any reason we need to check vx and vy?
IsNop = true; {
Nop = true;
}
} }
public Size GetContentSize() => ContentSize;
public override Vector2 UntransformPoint(string channel, Vector2 point) public override Vector2 UntransformPoint(string channel, Vector2 point)
{ {
if (nop) if (Nop)
{
return point; return point;
}
point.X -= LL.vx; point.X -= LL.vx;
point.Y -= LL.vy; point.Y -= LL.vy;
point.X /= LL.WidthScale; point.X /= LL.WidthScale;
@ -616,8 +643,11 @@ namespace BizHawk.Client.Common.Filters
public override Vector2 TransformPoint(string channel, Vector2 point) public override Vector2 TransformPoint(string channel, Vector2 point)
{ {
if (nop) if (Nop)
{
return point; return point;
}
point.X *= LL.WidthScale; point.X *= LL.WidthScale;
point.Y *= LL.HeightScale; point.Y *= LL.HeightScale;
point.X += LL.vx; point.X += LL.vx;
@ -627,31 +657,37 @@ namespace BizHawk.Client.Common.Filters
public override void Run() public override void Run()
{ {
if (nop) if (Nop)
{
return; return;
}
GL.SetClearColor(Color.FromArgb(BackgroundColor)); FilterProgram.GL.SetClearColor(Color.FromArgb(BackgroundColor));
GL.Clear(ClearBufferMask.ColorBufferBit); FilterProgram.GL.Clear(ClearBufferMask.ColorBufferBit);
GuiRenderer.Begin(OutputSize.Width, OutputSize.Height); FilterProgram.GuiRenderer.Begin(OutputSize.Width, OutputSize.Height);
GuiRenderer.SetBlendState(GL.BlendNoneCopy); FilterProgram.GuiRenderer.SetBlendState(FilterProgram.GL.BlendNoneCopy);
if(FilterOption != eFilterOption.None) if (FilterOption != eFilterOption.None)
{
InputTexture.SetFilterLinear(); InputTexture.SetFilterLinear();
}
else else
{
InputTexture.SetFilterNearest(); InputTexture.SetFilterNearest();
}
if (FilterOption == eFilterOption.Bicubic) 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); FilterProgram.GuiRenderer.Draw(InputTexture, LL.vx, LL.vy, LL.vw, LL.vh);
GuiRenderer.End(); 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 class PrescaleFilter : BaseFilter
{ {
public int Scale; public int Scale;
@ -666,7 +702,7 @@ namespace BizHawk.Client.Common.Filters
var outputSize = state.SurfaceFormat.Size; var outputSize = state.SurfaceFormat.Size;
outputSize.Width *= Scale; outputSize.Width *= Scale;
outputSize.Height *= Scale; outputSize.Height *= Scale;
var ss = new SurfaceState(new SurfaceFormat(outputSize), SurfaceDisposition.RenderTarget); var ss = new SurfaceState(new(outputSize), SurfaceDisposition.RenderTarget);
DeclareOutput(ss, channel); DeclareOutput(ss, channel);
} }
@ -683,7 +719,7 @@ namespace BizHawk.Client.Common.Filters
public class AutoPrescaleFilter : BaseFilter public class AutoPrescaleFilter : BaseFilter
{ {
private Size OutputSize, InputSize; private Size OutputSize;
private int XIS, YIS; private int XIS, YIS;
public override void Initialize() public override void Initialize()
@ -693,7 +729,7 @@ namespace BizHawk.Client.Common.Filters
public override void SetInputFormat(string channel, SurfaceState state) public override void SetInputFormat(string channel, SurfaceState state)
{ {
//calculate integer scaling factors // calculate integer scaling factors
XIS = OutputSize.Width / state.SurfaceFormat.Size.Width; XIS = OutputSize.Width / state.SurfaceFormat.Size.Width;
YIS = OutputSize.Height / state.SurfaceFormat.Size.Height; YIS = OutputSize.Height / state.SurfaceFormat.Size.Height;
@ -712,11 +748,7 @@ namespace BizHawk.Client.Common.Filters
OutputSize.Height *= YIS; OutputSize.Height *= YIS;
} }
DeclareOutput(new SurfaceState DeclareOutput(new SurfaceState(new(OutputSize), SurfaceDisposition.RenderTarget));
{
SurfaceFormat = new SurfaceFormat(OutputSize),
SurfaceDisposition = SurfaceDisposition.RenderTarget
});
} }
public override Size PresizeOutput(string channel, Size size) 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) public override Size PresizeInput(string channel, Size inSize)
{ {
InputSize = inSize;
return inSize; return inSize;
} }
@ -748,6 +779,7 @@ namespace BizHawk.Client.Common.Filters
{ {
DeclareInput(SurfaceDisposition.RenderTarget); DeclareInput(SurfaceDisposition.RenderTarget);
} }
public override void SetInputFormat(string channel, SurfaceState state) public override void SetInputFormat(string channel, SurfaceState state)
{ {
DeclareOutput(state); DeclareOutput(state);
@ -772,19 +804,24 @@ namespace BizHawk.Client.Common.Filters
public class OSD : BaseFilter public class OSD : BaseFilter
{ {
//this class has the ability to disable its operations for higher performance when the callback is removed, // 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 // 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. // so, we should probably rebuild the chain.
public override void Initialize() public override void Initialize()
{ {
if (RenderCallback == null) return; if (RenderCallback != null)
DeclareInput(SurfaceDisposition.RenderTarget); {
DeclareInput(SurfaceDisposition.RenderTarget);
}
} }
public override void SetInputFormat(string channel, SurfaceState state) public override void SetInputFormat(string channel, SurfaceState state)
{ {
if (RenderCallback == null) return; if (RenderCallback != null)
DeclareOutput(state); {
DeclareOutput(state);
}
} }
public Action RenderCallback; public Action RenderCallback;

View File

@ -4,9 +4,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Drawing; using System.Drawing;
using System.IO;
using System.Numerics;
using System.Text.RegularExpressions;
using BizHawk.Client.Common.FilterManager; using BizHawk.Client.Common.FilterManager;
using BizHawk.Bizware.BizwareGL; using BizHawk.Bizware.BizwareGL;
@ -17,48 +19,56 @@ namespace BizHawk.Client.Common.Filters
{ {
public class RetroShaderChain : IDisposable 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) private static string ResolveIncludes(string content, string baseDirectory)
{ {
for (; ; ) while (true)
{ {
var match = RxInclude.Match(content); var match = RxInclude.Match(content);
if(match.Value == "") break; if (match.Value == string.Empty)
string fname = match.Groups[4].Value; {
return content;
}
var fname = match.Groups[4].Value;
fname = Path.Combine(baseDirectory,fname); 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); 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) public RetroShaderChain(IGL owner, RetroShaderPreset preset, string baseDirectory, bool debug = false)
{ {
Owner = owner; Owner = owner;
Preset = preset;
Passes = preset.Passes.ToArray(); Passes = preset.Passes.ToArray();
Errors = ""; Errors = string.Empty;
//load up the shaders // load up the shaders
var shaders = new RetroShader[preset.Passes.Count]; var shaders = new RetroShader[preset.Passes.Count];
for (var i = 0; i < preset.Passes.Count; i++) 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; string content;
var path = Path.Combine(baseDirectory, preset.Passes[i].ShaderPath); var path = Path.Combine(baseDirectory, preset.Passes[i].ShaderPath);
if (!File.Exists(path)) if (!File.Exists(path))
{ {
if (!Path.HasExtension(path)) if (!Path.HasExtension(path))
{
path += ".cg"; path += ".cg";
}
if (!File.Exists(path)) if (!File.Exists(path))
{ {
if (owner.API == "OPENGL") path = owner.API switch
path = Path.ChangeExtension(path, ".glsl"); {
else "OPENGL" => Path.ChangeExtension(path, ".glsl"),
path = Path.ChangeExtension(path, ".hlsl"); "D3D9" => Path.ChangeExtension(path, ".hlsl"),
_ => throw new InvalidOperationException(),
};
} }
} }
try try
{ {
content = ResolveIncludes(File.ReadAllText(path), Path.GetDirectoryName(path)); content = ResolveIncludes(File.ReadAllText(path), Path.GetDirectoryName(path));
@ -68,16 +78,17 @@ namespace BizHawk.Client.Common.Filters
Errors += $"caught {nameof(DirectoryNotFoundException)}: {e.Message}\n"; Errors += $"caught {nameof(DirectoryNotFoundException)}: {e.Message}\n";
return; return;
} }
catch (FileNotFoundException e) catch (FileNotFoundException e)
{ {
Errors += $"could not read file {e.FileName}\n"; Errors += $"could not read file {e.FileName}\n";
return; return;
} }
var shader = shaders[i] = new RetroShader(Owner, content, debug); var shader = shaders[i] = new(Owner, content, debug);
if (!shader.Available) 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; return;
} }
} }
@ -105,7 +116,6 @@ namespace BizHawk.Client.Common.Filters
public readonly bool Available; public readonly bool Available;
public readonly string Errors; public readonly string Errors;
public readonly IGL Owner; public readonly IGL Owner;
public readonly RetroShaderPreset Preset;
public readonly IReadOnlyList<RetroShader> Shaders = Array.Empty<RetroShader>(); public readonly IReadOnlyList<RetroShader> Shaders = Array.Empty<RetroShader>();
public readonly RetroShaderPreset.ShaderPass[] Passes; public readonly RetroShaderPreset.ShaderPass[] Passes;
@ -122,55 +132,70 @@ namespace BizHawk.Client.Common.Filters
var content = new StreamReader(stream).ReadToEnd(); var content = new StreamReader(stream).ReadToEnd();
var dict = new Dictionary<string, string>(); var dict = new Dictionary<string, string>();
//parse the key-value-pair format of the file // parse the key-value-pair format of the file
content = content.Replace("\r", ""); content = content.Replace("\r", "");
foreach (var splitLine in content.Split('\n')) foreach (var splitLine in content.Split('\n'))
{ {
var line = splitLine.Trim(); var line = splitLine.Trim();
if (line.Length is 0) continue; if (line.Length is 0)
if (line.StartsWith('#')) continue; // comments {
int eq = line.IndexOf('='); continue;
}
if (line.StartsWith('#'))
{
continue; // comments
}
var eq = line.IndexOf('=');
var key = line.Substring(0, eq).Trim(); var key = line.Substring(0, eq).Trim();
var value = line.Substring(eq + 1).Trim(); var value = line.Substring(eq + 1).Trim();
int quote = value.IndexOf('\"'); var quote = value.IndexOf('\"');
if (quote != -1) if (quote != -1)
{
value = value.Substring(quote + 1, value.IndexOf('\"', quote + 1) - (quote + 1)); value = value.Substring(quote + 1, value.IndexOf('\"', quote + 1) - (quote + 1));
}
else else
{ {
//remove comments from end of value. exclusive from above condition, since comments after quoted strings would be snipped by the quoted string extraction // 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('#'); var hash = value.IndexOf('#');
if (hash != -1) if (hash != -1)
{
value = value.Substring(0, hash); value = value.Substring(0, hash);
}
value = value.Trim(); value = value.Trim();
} }
dict[key.ToLower()] = value; dict[key.ToLower()] = value;
} }
// process the keys // process the keys
int nShaders = FetchInt(dict, "shaders", 0); var nShaders = FetchInt(dict, "shaders", 0);
for (int i = 0; i < nShaders; i++) for (var i = 0; i < nShaders; i++)
{ {
var sp = new ShaderPass { Index = i }; var sp = new ShaderPass { Index = i };
Passes.Add(sp); Passes.Add(sp);
sp.InputFilterLinear = FetchBool(dict, $"filter_linear{i}", false); //Should this value not be defined, the filtering option is implementation defined. sp.InputFilterLinear = FetchBool(dict, $"filter_linear{i}", false); // Should this value not be defined, the filtering option is implementation defined.
sp.OutputFloat = FetchBool(dict, $"float_framebuffer{i}", false); sp.OutputFloat = FetchBool(dict, $"float_framebuffer{i}", false);
sp.FrameCountMod = FetchInt(dict, $"frame_count_mod{i}", 1); sp.FrameCountMod = FetchInt(dict, $"frame_count_mod{i}", 1);
sp.ShaderPath = FetchString(dict, $"shader{i}", "?"); //todo - change extension to .cg for better compatibility? just change .cg to .glsl transparently at last second? sp.ShaderPath = FetchString(dict, $"shader{i}", "?"); // todo - change extension to .cg for better compatibility? just change .cg to .glsl transparently at last second?
// If no scale type is assumed, it is assumed that it is set to "source" with scaleN set to 1.0. // If no scale type is assumed, it is assumed that it is set to "source" with scaleN set to 1.0.
// It is possible to set scale_type_xN and scale_type_yN to specialize the scaling type in either direction. scale_typeN however overrides both of these. // It is possible to set scale_type_xN and scale_type_yN to specialize the scaling type in either direction. scale_typeN however overrides both of these.
sp.ScaleTypeX = (ScaleType)Enum.Parse(typeof(ScaleType), FetchString(dict, $"scale_type_x{i}", "Source"), true); sp.ScaleTypeX = (ScaleType)Enum.Parse(typeof(ScaleType), FetchString(dict, $"scale_type_x{i}", "Source"), true);
sp.ScaleTypeY = (ScaleType)Enum.Parse(typeof(ScaleType), FetchString(dict, $"scale_type_y{i}", "Source"), true); sp.ScaleTypeY = (ScaleType)Enum.Parse(typeof(ScaleType), FetchString(dict, $"scale_type_y{i}", "Source"), true);
ScaleType st = (ScaleType)Enum.Parse(typeof(ScaleType), FetchString(dict, $"scale_type{i}", "NotSet"), true); var st = (ScaleType)Enum.Parse(typeof(ScaleType), FetchString(dict, $"scale_type{i}", "NotSet"), true);
if (st != ScaleType.NotSet) if (st != ScaleType.NotSet)
{
sp.ScaleTypeX = sp.ScaleTypeY = st; sp.ScaleTypeX = sp.ScaleTypeY = st;
}
// scaleN controls both scaling type in horizontal and vertical directions. If scaleN is defined, scale_xN and scale_yN have no effect. // scaleN controls both scaling type in horizontal and vertical directions. If scaleN is defined, scale_xN and scale_yN have no effect.
sp.Scale.X = FetchFloat(dict, $"scale_x{i}", 1); sp.Scale.X = FetchFloat(dict, $"scale_x{i}", 1);
sp.Scale.Y = FetchFloat(dict, $"scale_y{i}", 1); sp.Scale.Y = FetchFloat(dict, $"scale_y{i}", 1);
float scale = FetchFloat(dict, $"scale{i}", -999); if (dict.ContainsValue($"scale{i}"))
if (scale != -999)
{ {
sp.Scale.X = sp.Scale.Y = FetchFloat(dict, $"scale{i}", 1); sp.Scale.X = sp.Scale.Y = FetchFloat(dict, $"scale{i}", 1);
} }
@ -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 public enum ScaleType
{ {
@ -214,19 +238,28 @@ namespace BizHawk.Client.Common.Filters
return dict.TryGetValue(key, out var str) ? float.Parse(str) : @default; 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; 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; switch (value)
if (value == "0") return false; {
value = value.ToLower(); case "1":
if (value == "true") return true; return true;
if (value == "false") return false; case "0":
throw new InvalidOperationException("Unparsable bool in CGP file content"); 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) public override void SetInputFormat(string channel, SurfaceState state)
{ {
Size inSize = state.SurfaceFormat.Size; var 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);
DeclareOutput(new SurfaceState _outputSize.Width = _sp.ScaleTypeX switch
{ {
SurfaceFormat = new SurfaceFormat(_outputSize), RetroShaderPreset.ScaleType.Absolute => (int)_sp.Scale.X,
SurfaceDisposition = SurfaceDisposition.RenderTarget 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) 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) public override Size PresizeInput(string channel, Size inSize)
{ {
Size outsize = inSize; var 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; outsize.Width = _sp.ScaleTypeX switch
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); 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; return outsize;
} }
@ -288,7 +338,7 @@ namespace BizHawk.Client.Common.Filters
shader.Bind(); shader.Bind();
// apply all parameters to this shader.. even if it was meant for other shaders. kind of lame. // apply all parameters to this shader.. even if it was meant for other shaders. kind of lame.
if(Parameters != null) if (Parameters != null)
{ {
foreach (var (k, v) in Parameters) foreach (var (k, v) in Parameters)
{ {

View File

@ -1,7 +1,7 @@
using System.Drawing; using System.Drawing;
using BizHawk.Client.Common.FilterManager;
using BizHawk.Bizware.BizwareGL; using BizHawk.Bizware.BizwareGL;
using BizHawk.Client.Common.FilterManager;
namespace BizHawk.Client.Common.Filters namespace BizHawk.Client.Common.Filters
{ {
@ -23,7 +23,7 @@ namespace BizHawk.Client.Common.Filters
public override void Initialize() 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) public override void SetInputFormat(string channel, SurfaceState format)
@ -71,7 +71,7 @@ namespace BizHawk.Client.Common.Filters
public override void Run() public override void Run()
{ {
YieldOutput(FilterProgram.GetRenderTarget().Texture2d); YieldOutput(FilterProgram.CurrRenderTarget.Texture2d);
} }
} }
} }

View File

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