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:
parent
bb96825c60
commit
59fcdb04cd
|
@ -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;
|
||||||
|
|
|
@ -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++)
|
|
||||||
{
|
{
|
||||||
int pixel = bitmap.GetPixel(x, y);
|
for (var x = 0; x < w; x++)
|
||||||
|
{
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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,12 +162,16 @@ 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>
|
||||||
/// copies this bitmap and trims out transparent pixels, returning the offset to the topleft pixel
|
/// copies this bitmap and trims out transparent pixels, returning the offset to the topleft pixel
|
||||||
|
@ -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++)
|
|
||||||
{
|
{
|
||||||
int pixel = GetPixel(x, y);
|
for (var x = 0; x < Width; x++)
|
||||||
int a = (pixel >> 24) & 0xFF;
|
{
|
||||||
|
var pixel = GetPixel(x, y);
|
||||||
|
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,9 +281,11 @@ 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
|
||||||
|
@ -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++)
|
|
||||||
{
|
{
|
||||||
int srcPixel = ptr[idx];
|
for (var x = 0; x < w; x++)
|
||||||
|
{
|
||||||
|
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,8 +398,10 @@ namespace BizHawk.Bizware.BizwareGL
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsPad && options.Pad)
|
if (needsPad && options.Pad)
|
||||||
|
{
|
||||||
Pad();
|
Pad();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -382,40 +409,48 @@ 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++)
|
|
||||||
{
|
{
|
||||||
int src = y * stride + x;
|
for (var x = 0; x < Width; x++)
|
||||||
int srcVal = ((int*)data)[src];
|
{
|
||||||
|
var src = y * stride + x;
|
||||||
|
var srcVal = ((int*)data)[src];
|
||||||
|
|
||||||
//make transparent pixels turn into black to avoid filtering issues and other annoying issues with stray junk in transparent pixels
|
// 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>
|
||||||
/// premultiplies a color
|
/// premultiplies a color
|
||||||
/// </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,32 +560,35 @@ 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++)
|
|
||||||
{
|
{
|
||||||
int srcPixel = pPtr[idx];
|
for (var x = 0; x < Width; x++)
|
||||||
|
{
|
||||||
|
var srcPixel = pPtr[idx];
|
||||||
ptr[idx] = srcPixel;
|
ptr[idx] = srcPixel;
|
||||||
idx++;
|
idx++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bmp.UnlockBits(bmpdata);
|
bmp.UnlockBits(bmpdata);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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,10 +32,17 @@ 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(Graphics g)
|
||||||
|
|
||||||
private void SetupMatrix(sd.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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
namespace BizHawk.Bizware.BizwareGL
|
namespace BizHawk.Bizware.BizwareGL
|
||||||
{
|
{
|
||||||
public interface IBlendState {}
|
public interface IBlendState
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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,26 +28,31 @@ 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,41 +62,50 @@ 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')
|
||||||
{
|
{
|
||||||
c = '\n';
|
c = '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
if(c == '\n')
|
if (c == '\n')
|
||||||
{
|
{
|
||||||
x = ox;
|
x = ox;
|
||||||
y += FontInfo.LineHeight;
|
y += FontInfo.LineHeight;
|
||||||
|
@ -100,17 +115,17 @@ namespace BizHawk.Bizware.BizwareGL
|
||||||
var bfc = FontInfo[c];
|
var bfc = FontInfo[c];
|
||||||
|
|
||||||
// calculate texcoords (we shouldve already had this cached, but im speedcoding now)
|
// calculate texcoords (we shouldve already had this cached, but im speedcoding now)
|
||||||
Texture2d tex = TexturePages[bfc.TexturePage];
|
var tex = TexturePages[bfc.TexturePage];
|
||||||
float w = tex.Width;
|
var w = tex.Width;
|
||||||
float h = tex.Height;
|
var h = tex.Height;
|
||||||
sd.Rectangle bounds = new(bfc.X, bfc.Y, bfc.Width, bfc.Height);
|
var bounds = new Rectangle(bfc.X, bfc.Y, bfc.Width, bfc.Height);
|
||||||
float u0 = bounds.Left / w;
|
var u0 = bounds.Left / w;
|
||||||
float v0 = bounds.Top / h;
|
var v0 = bounds.Top / h;
|
||||||
float u1 = bounds.Right / w;
|
var u1 = bounds.Right / w;
|
||||||
float v1 = bounds.Bottom / h;
|
var v1 = bounds.Bottom / h;
|
||||||
|
|
||||||
float gx = x + bfc.XOffset;
|
var gx = x + bfc.XOffset;
|
||||||
float gy = y + bfc.YOffset;
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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})";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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})";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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})";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
case 1 when _shaderChainHq2X is { Available: true }:
|
||||||
selectedChain = _shaderChainHq2X;
|
selectedChain = _shaderChainHq2X;
|
||||||
}
|
break;
|
||||||
|
case 2 when _shaderChainScanlines is { Available: true }:
|
||||||
if (GlobalConfig.TargetDisplayFilter == 2 && _shaderChainScanlines != null && _shaderChainScanlines.Available)
|
|
||||||
{
|
|
||||||
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 }:
|
||||||
if (GlobalConfig.TargetDisplayFilter == 3 && _shaderChainUser != null && _shaderChainUser.Available)
|
|
||||||
{
|
|
||||||
selectedChain = _shaderChainUser;
|
selectedChain = _shaderChainUser;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
case EDispManagerAR.None:
|
||||||
vw = bufferWidth;
|
vw = bufferWidth;
|
||||||
vh = bufferHeight;
|
vh = bufferHeight;
|
||||||
}
|
break;
|
||||||
if (GlobalConfig.DispManagerAR == EDispManagerAR.System)
|
case EDispManagerAR.System:
|
||||||
{
|
// Already set
|
||||||
//Already set
|
break;
|
||||||
}
|
case EDispManagerAR.CustomSize:
|
||||||
if (GlobalConfig.DispManagerAR == 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;
|
||||||
if (GlobalConfig.DispManagerAR == EDispManagerAR.CustomRatio)
|
case EDispManagerAR.CustomRatio:
|
||||||
{
|
// 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
|
|
||||||
FixRatio(GlobalConfig.DispCustomUserArx, GlobalConfig.DispCustomUserAry, videoProvider.BufferWidth, videoProvider.BufferHeight, out vw, out vh);
|
FixRatio(GlobalConfig.DispCustomUserArx, GlobalConfig.DispCustomUserAry, videoProvider.BufferWidth, videoProvider.BufferHeight, out vw, out vh);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new InvalidOperationException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -884,20 +883,9 @@ namespace BizHawk.Client.Common
|
||||||
fPresent.VirtualTextureSize = new(vw, vh);
|
fPresent.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
|
||||||
|
{
|
||||||
|
var fontData = new byte[fontStream.Length];
|
||||||
fontStream.Read(fontData, 0, (int)fontStream.Length);
|
fontStream.Read(fontData, 0, (int)fontStream.Length);
|
||||||
Marshal.Copy(fontData, 0, data, (int)fontStream.Length);
|
Marshal.Copy(fontData, 0, data, (int)fontStream.Length);
|
||||||
CustomFonts.AddMemoryFont(data, fontData.Length);
|
CustomFonts.AddMemoryFont(data, fontData.Length);
|
||||||
fontStream.Close();
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
Marshal.FreeCoTaskMem(data);
|
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);
|
||||||
|
|
|
@ -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,9 +190,11 @@ namespace BizHawk.Client.Common.FilterManager
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (iosi.SurfaceDisposition)
|
||||||
|
{
|
||||||
// check if the desired disposition needs to change from texture to render target
|
// check if the desired disposition needs to change from texture to render target
|
||||||
// (if so, insert a render filter)
|
// (if so, insert a render filter)
|
||||||
if (iosi.SurfaceDisposition == SurfaceDisposition.RenderTarget && currState.SurfaceDisposition == SurfaceDisposition.Texture)
|
case SurfaceDisposition.RenderTarget when currState.SurfaceDisposition == SurfaceDisposition.Texture:
|
||||||
{
|
{
|
||||||
var renderer = new Render();
|
var renderer = new Render();
|
||||||
Filters.Insert(i, renderer);
|
Filters.Insert(i, renderer);
|
||||||
|
@ -191,26 +203,27 @@ namespace BizHawk.Client.Common.FilterManager
|
||||||
}
|
}
|
||||||
// check if the desired disposition needs to change from a render target to a texture
|
// check if the desired disposition needs to change from a render target to a texture
|
||||||
// (if so, the current render target gets resolved, and made no longer current
|
// (if so, the current render target gets resolved, and made no longer current
|
||||||
else if (iosi.SurfaceDisposition == SurfaceDisposition.Texture && currState.SurfaceDisposition == SurfaceDisposition.RenderTarget)
|
case SurfaceDisposition.Texture when currState.SurfaceDisposition == SurfaceDisposition.RenderTarget:
|
||||||
{
|
{
|
||||||
var resolver = new Resolve();
|
var resolver = new Resolve();
|
||||||
Filters.Insert(i, resolver);
|
Filters.Insert(i, resolver);
|
||||||
Compile(channel, inSize, outsize, finalTarget);
|
Compile(channel, inSize, outsize, finalTarget);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
case SurfaceDisposition.Unspecified:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// now, the filter will have set its output state depending on its input state. check if it outputs:
|
// now, the filter will have set its output state depending on its input state. check if it outputs:
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
|
||||||
{
|
{
|
||||||
|
//gap only applies to vertical, I guess
|
||||||
|
case NDS.ScreenLayoutKind.Vertical:
|
||||||
bot.Translate(0, 192);
|
bot.Translate(0, 192);
|
||||||
bot.Translate(0, settings.ScreenGap);
|
bot.Translate(0, settings.ScreenGap);
|
||||||
}
|
break;
|
||||||
else if (settings.ScreenLayout == NDS.ScreenLayoutKind.Horizontal)
|
case NDS.ScreenLayoutKind.Horizontal:
|
||||||
{
|
|
||||||
bot.Translate(256, 0);
|
bot.Translate(256, 0);
|
||||||
}
|
break;
|
||||||
else if (settings.ScreenLayout == NDS.ScreenLayoutKind.Top)
|
case NDS.ScreenLayoutKind.Top:
|
||||||
{
|
case NDS.ScreenLayoutKind.Bottom:
|
||||||
//do nothing here, we'll discard bottom screen
|
// 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++)
|
|
||||||
{
|
{
|
||||||
if (Math.Abs(matTop[r, c]) < 0.0000001f) matTop[r, c] = 0;
|
fixed (Matrix4x4* matTopP = &matTop, matBotP = &matBot)
|
||||||
if (Math.Abs(matBot[r, c]) < 0.0000001f) matBot[r, c] = 0;
|
{
|
||||||
|
float* matTopF = (float*)matTopP, matBotF = (float*)matBotP;
|
||||||
|
for (var i = 0; i < 4 * 4; i++)
|
||||||
|
{
|
||||||
|
if (Math.Abs(matTopF[i]) < 0.0000001f)
|
||||||
|
{
|
||||||
|
matTopF[i] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
matTopInvert = matTop.Inverted();
|
if (Math.Abs(matBotF[i]) < 0.0000001f)
|
||||||
matBotInvert = matBot.Inverted();
|
{
|
||||||
|
matBotF[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
outputSize = new Size((int)(fxmax-fxmin), (int)(fymax-fymin));
|
Matrix4x4.Invert(matTop, out matTopInvert);
|
||||||
|
Matrix4x4.Invert(matBot, out matBotInvert);
|
||||||
|
|
||||||
|
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,20 +804,25 @@ 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;
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue