MultiHawk - removing more stuff
This commit is contained in:
parent
e4c2eddc1f
commit
eccbe1ce2a
|
@ -13,7 +13,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
/// Recycles a pair of temporary textures (in case double-buffering helps any) to contain a BitmapBuffer's or DisplaySurface's contents, as long as the dimensions match.
|
/// Recycles a pair of temporary textures (in case double-buffering helps any) to contain a BitmapBuffer's or DisplaySurface's contents, as long as the dimensions match.
|
||||||
/// When the dimensions dont match, a new one will be allocated
|
/// When the dimensions dont match, a new one will be allocated
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class TextureFrugalizer : IDisposable
|
public class TextureFrugalizer : IDisposable
|
||||||
{
|
{
|
||||||
public TextureFrugalizer(IGL gl)
|
public TextureFrugalizer(IGL gl)
|
||||||
{
|
{
|
||||||
|
|
|
@ -72,15 +72,7 @@
|
||||||
<DependentUpon>RecordMovie.cs</DependentUpon>
|
<DependentUpon>RecordMovie.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="DisplayManager\DisplayManager.cs" />
|
<Compile Include="DisplayManager\DisplayManager.cs" />
|
||||||
<Compile Include="DisplayManager\DisplaySurface.cs" />
|
|
||||||
<Compile Include="DisplayManager\FilterManager.cs" />
|
|
||||||
<Compile Include="DisplayManager\Filters\BaseFilter.cs" />
|
|
||||||
<Compile Include="DisplayManager\Filters\Gui.cs" />
|
|
||||||
<Compile Include="DisplayManager\Filters\Retro.cs" />
|
|
||||||
<Compile Include="DisplayManager\Filters\Utils.cs" />
|
|
||||||
<Compile Include="DisplayManager\RenderTargetFrugalizer.cs" />
|
<Compile Include="DisplayManager\RenderTargetFrugalizer.cs" />
|
||||||
<Compile Include="DisplayManager\SwappableDisplaySurfaceSet.cs" />
|
|
||||||
<Compile Include="DisplayManager\TextureFrugalizer.cs" />
|
|
||||||
<Compile Include="EmulatorWindow.cs">
|
<Compile Include="EmulatorWindow.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|
|
@ -9,7 +9,7 @@ using System.Drawing;
|
||||||
|
|
||||||
using BizHawk.Emulation.Common;
|
using BizHawk.Emulation.Common;
|
||||||
using BizHawk.Client.Common;
|
using BizHawk.Client.Common;
|
||||||
using BizHawk.Client.MultiHawk.FilterManager;
|
using BizHawk.Client.EmuHawk.FilterManager;
|
||||||
using BizHawk.Bizware.BizwareGL;
|
using BizHawk.Bizware.BizwareGL;
|
||||||
|
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
|
@ -52,7 +52,7 @@ namespace BizHawk.Client.MultiHawk
|
||||||
else
|
else
|
||||||
Renderer = new GDIPlusGuiRenderer((BizHawk.Bizware.BizwareGL.Drivers.GdiPlus.IGL_GdiPlus)GL);
|
Renderer = new GDIPlusGuiRenderer((BizHawk.Bizware.BizwareGL.Drivers.GdiPlus.IGL_GdiPlus)GL);
|
||||||
|
|
||||||
VideoTextureFrugalizer = new TextureFrugalizer(GL);
|
VideoTextureFrugalizer = new BizHawk.Client.EmuHawk.TextureFrugalizer(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 (int i = 0; i < 16; i++)
|
||||||
|
@ -96,11 +96,11 @@ namespace BizHawk.Client.MultiHawk
|
||||||
/// </summary>
|
/// </summary>
|
||||||
int currEmuWidth, currEmuHeight;
|
int currEmuWidth, currEmuHeight;
|
||||||
|
|
||||||
TextureFrugalizer VideoTextureFrugalizer;
|
BizHawk.Client.EmuHawk.TextureFrugalizer VideoTextureFrugalizer;
|
||||||
Dictionary<string, TextureFrugalizer> LuaSurfaceFrugalizers = new Dictionary<string, TextureFrugalizer>();
|
Dictionary<string, BizHawk.Client.EmuHawk.TextureFrugalizer> LuaSurfaceFrugalizers = new Dictionary<string, BizHawk.Client.EmuHawk.TextureFrugalizer>();
|
||||||
RenderTargetFrugalizer[] ShaderChainFrugalizers;
|
RenderTargetFrugalizer[] ShaderChainFrugalizers;
|
||||||
|
|
||||||
Filters.RetroShaderChain ShaderChain_user;
|
BizHawk.Client.EmuHawk.Filters.RetroShaderChain ShaderChain_user;
|
||||||
|
|
||||||
public void RefreshUserShader()
|
public void RefreshUserShader()
|
||||||
{
|
{
|
||||||
|
@ -110,7 +110,7 @@ namespace BizHawk.Client.MultiHawk
|
||||||
{
|
{
|
||||||
var fi = new FileInfo(Global.Config.DispUserFilterPath);
|
var fi = new FileInfo(Global.Config.DispUserFilterPath);
|
||||||
using (var stream = fi.OpenRead())
|
using (var stream = fi.OpenRead())
|
||||||
ShaderChain_user = new Filters.RetroShaderChain(GL, new Filters.RetroShaderPreset(stream), Path.GetDirectoryName(Global.Config.DispUserFilterPath));
|
ShaderChain_user = new BizHawk.Client.EmuHawk.Filters.RetroShaderChain(GL, new BizHawk.Client.EmuHawk.Filters.RetroShaderPreset(stream), Path.GetDirectoryName(Global.Config.DispUserFilterPath));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,14 +118,14 @@ namespace BizHawk.Client.MultiHawk
|
||||||
{
|
{
|
||||||
//select user special FX shader chain
|
//select user special FX shader chain
|
||||||
Dictionary<string, object> selectedChainProperties = new Dictionary<string, object>();
|
Dictionary<string, object> selectedChainProperties = new Dictionary<string, object>();
|
||||||
Filters.RetroShaderChain selectedChain = null;
|
BizHawk.Client.EmuHawk.Filters.RetroShaderChain selectedChain = null;
|
||||||
|
|
||||||
if (Global.Config.TargetDisplayFilter == 3 && ShaderChain_user != null && ShaderChain_user.Available)
|
if (Global.Config.TargetDisplayFilter == 3 && ShaderChain_user != null && ShaderChain_user.Available)
|
||||||
selectedChain = ShaderChain_user;
|
selectedChain = ShaderChain_user;
|
||||||
|
|
||||||
Filters.FinalPresentation fPresent = new Filters.FinalPresentation(chain_outsize);
|
BizHawk.Client.EmuHawk.Filters.FinalPresentation fPresent = new BizHawk.Client.EmuHawk.Filters.FinalPresentation(chain_outsize);
|
||||||
Filters.SourceImage fInput = new Filters.SourceImage(chain_insize);
|
BizHawk.Client.EmuHawk.Filters.SourceImage fInput = new BizHawk.Client.EmuHawk.Filters.SourceImage(chain_insize);
|
||||||
Filters.OSD fOSD = new Filters.OSD();
|
BizHawk.Client.EmuHawk.Filters.OSD fOSD = new BizHawk.Client.EmuHawk.Filters.OSD();
|
||||||
fOSD.RenderCallback = () =>
|
fOSD.RenderCallback = () =>
|
||||||
{
|
{
|
||||||
if (!includeOSD)
|
if (!includeOSD)
|
||||||
|
@ -142,11 +142,11 @@ namespace BizHawk.Client.MultiHawk
|
||||||
chain.AddFilter(fInput, "input");
|
chain.AddFilter(fInput, "input");
|
||||||
|
|
||||||
//choose final filter
|
//choose final filter
|
||||||
Filters.FinalPresentation.eFilterOption finalFilter = Filters.FinalPresentation.eFilterOption.None;
|
BizHawk.Client.EmuHawk.Filters.FinalPresentation.eFilterOption finalFilter = BizHawk.Client.EmuHawk.Filters.FinalPresentation.eFilterOption.None;
|
||||||
if (Global.Config.DispFinalFilter == 1) finalFilter = Filters.FinalPresentation.eFilterOption.Bilinear;
|
if (Global.Config.DispFinalFilter == 1) finalFilter = BizHawk.Client.EmuHawk.Filters.FinalPresentation.eFilterOption.Bilinear;
|
||||||
if (Global.Config.DispFinalFilter == 2) finalFilter = Filters.FinalPresentation.eFilterOption.Bicubic;
|
if (Global.Config.DispFinalFilter == 2) finalFilter = BizHawk.Client.EmuHawk.Filters.FinalPresentation.eFilterOption.Bicubic;
|
||||||
|
|
||||||
finalFilter = Filters.FinalPresentation.eFilterOption.None;
|
finalFilter = BizHawk.Client.EmuHawk.Filters.FinalPresentation.eFilterOption.None;
|
||||||
|
|
||||||
fPresent.FilterOption = finalFilter;
|
fPresent.FilterOption = finalFilter;
|
||||||
|
|
||||||
|
@ -156,12 +156,12 @@ namespace BizHawk.Client.MultiHawk
|
||||||
return chain;
|
return chain;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppendRetroShaderChain(FilterProgram program, string name, Filters.RetroShaderChain retroChain, Dictionary<string, object> properties)
|
void AppendRetroShaderChain(FilterProgram program, string name, BizHawk.Client.EmuHawk.Filters.RetroShaderChain retroChain, Dictionary<string, object> properties)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < retroChain.Passes.Length; i++)
|
for (int i = 0; i < retroChain.Passes.Length; i++)
|
||||||
{
|
{
|
||||||
var pass = retroChain.Passes[i];
|
var pass = retroChain.Passes[i];
|
||||||
var rsp = new Filters.RetroShaderPass(retroChain, i);
|
var rsp = new BizHawk.Client.EmuHawk.Filters.RetroShaderPass(retroChain, i);
|
||||||
string fname = string.Format("{0}[{1}]", name, i);
|
string fname = string.Format("{0}[{1}]", name, i);
|
||||||
program.AddFilter(rsp, fname);
|
program.AddFilter(rsp, fname);
|
||||||
rsp.Parameters = properties;
|
rsp.Parameters = properties;
|
||||||
|
@ -420,11 +420,11 @@ TESTEROO:
|
||||||
filterProgram.GL = GL;
|
filterProgram.GL = GL;
|
||||||
|
|
||||||
//setup the source image filter
|
//setup the source image filter
|
||||||
Filters.SourceImage fInput = filterProgram["input"] as Filters.SourceImage;
|
BizHawk.Client.EmuHawk.Filters.SourceImage fInput = filterProgram["input"] as BizHawk.Client.EmuHawk.Filters.SourceImage;
|
||||||
fInput.Texture = videoTexture;
|
fInput.Texture = videoTexture;
|
||||||
|
|
||||||
//setup the final presentation filter
|
//setup the final presentation filter
|
||||||
Filters.FinalPresentation fPresent = filterProgram["presentation"] as Filters.FinalPresentation;
|
BizHawk.Client.EmuHawk.Filters.FinalPresentation fPresent = filterProgram["presentation"] as BizHawk.Client.EmuHawk.Filters.FinalPresentation;
|
||||||
fPresent.VirtualTextureSize = new Size(vw, vh);
|
fPresent.VirtualTextureSize = new Size(vw, vh);
|
||||||
fPresent.TextureSize = new Size(bufferWidth, bufferHeight);
|
fPresent.TextureSize = new Size(bufferWidth, bufferHeight);
|
||||||
fPresent.BackgroundColor = videoProvider.BackgroundColor;
|
fPresent.BackgroundColor = videoProvider.BackgroundColor;
|
||||||
|
|
|
@ -1,185 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.Drawing.Imaging;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
using BizHawk.Common;
|
|
||||||
using BizHawk.Client.Common;
|
|
||||||
|
|
||||||
namespace BizHawk.Client.MultiHawk
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// This is a wrapper for a Bitmap, basically, which can also be a int[].
|
|
||||||
/// It should be phased out, in favor of BitmapBuffer and Texture2d's
|
|
||||||
/// </summary>
|
|
||||||
public unsafe class DisplaySurface : IDisposable
|
|
||||||
{
|
|
||||||
Bitmap bmp;
|
|
||||||
BitmapData bmpdata;
|
|
||||||
int[] pixels;
|
|
||||||
|
|
||||||
public unsafe void Clear()
|
|
||||||
{
|
|
||||||
FromBitmap(false);
|
|
||||||
Util.Memset(PixelPtr, 0, Stride * Height);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Bitmap PeekBitmap()
|
|
||||||
{
|
|
||||||
ToBitmap();
|
|
||||||
return bmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// returns a Graphics object used to render to this surface. be sure to dispose it!
|
|
||||||
/// </summary>
|
|
||||||
public Graphics GetGraphics()
|
|
||||||
{
|
|
||||||
ToBitmap();
|
|
||||||
return Graphics.FromImage(bmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe void ToBitmap(bool copy=true)
|
|
||||||
{
|
|
||||||
if (isBitmap) return;
|
|
||||||
isBitmap = true;
|
|
||||||
|
|
||||||
if (bmp == null)
|
|
||||||
{
|
|
||||||
bmp = new Bitmap(Width, Height, PixelFormat.Format32bppArgb);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (copy)
|
|
||||||
{
|
|
||||||
bmpdata = bmp.LockBits(new Rectangle(0, 0, Width, Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
|
|
||||||
|
|
||||||
int w = Width;
|
|
||||||
int h = Height;
|
|
||||||
int stride = bmpdata.Stride / 4;
|
|
||||||
int* bmpbuf = (int*)bmpdata.Scan0.ToPointer();
|
|
||||||
for (int y = 0, i = 0; y < h; y++)
|
|
||||||
for (int x = 0; x < w; x++)
|
|
||||||
bmpbuf[y * stride + x] = pixels[i++];
|
|
||||||
|
|
||||||
bmp.UnlockBits(bmpdata);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsBitmap { get { return isBitmap; } }
|
|
||||||
bool isBitmap = false;
|
|
||||||
|
|
||||||
public unsafe void FromBitmap(bool copy=true)
|
|
||||||
{
|
|
||||||
if (!isBitmap) return;
|
|
||||||
isBitmap = false;
|
|
||||||
|
|
||||||
if (copy)
|
|
||||||
{
|
|
||||||
bmpdata = bmp.LockBits(new Rectangle(0, 0, Width, Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
|
|
||||||
|
|
||||||
int w = Width;
|
|
||||||
int h = Height;
|
|
||||||
int stride = bmpdata.Stride / 4;
|
|
||||||
int* bmpbuf = (int*)bmpdata.Scan0.ToPointer();
|
|
||||||
for (int y = 0, i = 0; y < h; y++)
|
|
||||||
for (int x = 0; x < w; x++)
|
|
||||||
pixels[i++] = bmpbuf[y * stride + x];
|
|
||||||
|
|
||||||
bmp.UnlockBits(bmpdata);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static DisplaySurface DisplaySurfaceWrappingBitmap(Bitmap bmp)
|
|
||||||
{
|
|
||||||
DisplaySurface ret = new DisplaySurface();
|
|
||||||
ret.Width = bmp.Width;
|
|
||||||
ret.Height = bmp.Height;
|
|
||||||
ret.bmp = bmp;
|
|
||||||
ret.isBitmap = true;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
private DisplaySurface()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public DisplaySurface(int width, int height)
|
|
||||||
{
|
|
||||||
//can't create a bitmap with zero dimensions, so for now, just bump it up to one
|
|
||||||
if (width == 0) width = 1;
|
|
||||||
if (height == 0) height = 1;
|
|
||||||
|
|
||||||
Width = width;
|
|
||||||
Height = height;
|
|
||||||
|
|
||||||
pixels = new int[width * height];
|
|
||||||
LockPixels();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int* PixelPtr { get { return (int*)ptr; } }
|
|
||||||
public IntPtr PixelIntPtr { get { return new IntPtr(ptr); } }
|
|
||||||
public int Stride { get { return Width*4; } }
|
|
||||||
public int OffsetOf(int x, int y) { return y * Stride + x*4; }
|
|
||||||
|
|
||||||
void* ptr;
|
|
||||||
GCHandle handle;
|
|
||||||
void LockPixels()
|
|
||||||
{
|
|
||||||
UnlockPixels();
|
|
||||||
handle = GCHandle.Alloc(pixels, GCHandleType.Pinned);
|
|
||||||
ptr = handle.AddrOfPinnedObject().ToPointer();
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnlockPixels()
|
|
||||||
{
|
|
||||||
if(handle.IsAllocated) handle.Free();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// returns a new surface
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="xpad"></param>
|
|
||||||
/// <param name="ypad"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public DisplaySurface ToPaddedSurface(int xpad0, int ypad0, int xpad1, int ypad1)
|
|
||||||
{
|
|
||||||
int new_width = Width + xpad0 + xpad1;
|
|
||||||
int new_height = Height + ypad0 + ypad1;
|
|
||||||
DisplaySurface ret = new DisplaySurface(new_width, new_height);
|
|
||||||
int* dptr = ret.PixelPtr;
|
|
||||||
int* sptr = PixelPtr;
|
|
||||||
int dstride = ret.Stride / 4;
|
|
||||||
int sstride = Stride / 4;
|
|
||||||
for (int y = 0; y < Height; y++)
|
|
||||||
for (int x = 0; x < Width; x++)
|
|
||||||
{
|
|
||||||
dptr[(y + ypad0) * dstride + x + xpad0] = sptr[y * sstride + x];
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Width { get; private set; }
|
|
||||||
public int Height { get; private set; }
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (bmp != null)
|
|
||||||
bmp.Dispose();
|
|
||||||
bmp = null;
|
|
||||||
UnlockPixels();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AcceptIntArray(int[] newpixels)
|
|
||||||
{
|
|
||||||
FromBitmap(false);
|
|
||||||
UnlockPixels();
|
|
||||||
pixels = newpixels;
|
|
||||||
LockPixels();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,263 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Drawing;
|
|
||||||
|
|
||||||
using BizHawk.Common;
|
|
||||||
using BizHawk.Client.Common;
|
|
||||||
using BizHawk.Client.MultiHawk.Filters;
|
|
||||||
|
|
||||||
using BizHawk.Bizware.BizwareGL;
|
|
||||||
using BizHawk.Bizware.BizwareGL.Drivers.OpenTK;
|
|
||||||
|
|
||||||
using OpenTK;
|
|
||||||
using OpenTK.Graphics;
|
|
||||||
|
|
||||||
namespace BizHawk.Client.MultiHawk.FilterManager
|
|
||||||
{
|
|
||||||
public enum SurfaceDisposition
|
|
||||||
{
|
|
||||||
Unspecified, Texture, RenderTarget
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SurfaceFormat
|
|
||||||
{
|
|
||||||
public SurfaceFormat(Size size) { this.Size = size; }
|
|
||||||
public Size Size { get; private set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SurfaceState
|
|
||||||
{
|
|
||||||
public SurfaceState() { }
|
|
||||||
public SurfaceState(SurfaceFormat surfaceFormat, SurfaceDisposition surfaceDisposition = SurfaceDisposition.Unspecified)
|
|
||||||
{
|
|
||||||
this.SurfaceFormat = surfaceFormat;
|
|
||||||
this.SurfaceDisposition = surfaceDisposition;
|
|
||||||
}
|
|
||||||
public SurfaceFormat SurfaceFormat;
|
|
||||||
public SurfaceDisposition SurfaceDisposition;
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IRenderTargetProvider
|
|
||||||
{
|
|
||||||
RenderTarget Get(Size size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class FilterProgram
|
|
||||||
{
|
|
||||||
public List<BaseFilter> Filters = new List<BaseFilter>();
|
|
||||||
Dictionary<string, BaseFilter> FilterNameIndex = new Dictionary<string, BaseFilter>();
|
|
||||||
public List<ProgramStep> Program = new List<ProgramStep>();
|
|
||||||
|
|
||||||
public BaseFilter this[string name]
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
BaseFilter ret;
|
|
||||||
FilterNameIndex.TryGetValue(name, out ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum ProgramStepType
|
|
||||||
{
|
|
||||||
Run,
|
|
||||||
NewTarget,
|
|
||||||
FinalTarget
|
|
||||||
}
|
|
||||||
|
|
||||||
//services to filters:
|
|
||||||
public IGuiRenderer GuiRenderer;
|
|
||||||
public IGL GL;
|
|
||||||
public IRenderTargetProvider RenderTargetProvider;
|
|
||||||
public RenderTarget GetRenderTarget(string channel = "default") { return CurrRenderTarget; }
|
|
||||||
public RenderTarget CurrRenderTarget;
|
|
||||||
|
|
||||||
public void AddFilter(BaseFilter filter, string name = "")
|
|
||||||
{
|
|
||||||
Filters.Add(filter);
|
|
||||||
FilterNameIndex[name] = filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Receives a point in the coordinate space of the output of the filter program and untransforms it back to input points
|
|
||||||
/// </summary>
|
|
||||||
public Vector2 UntransformPoint(string channel, Vector2 point)
|
|
||||||
{
|
|
||||||
for (int i = Filters.Count - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
var filter = Filters[i];
|
|
||||||
point = filter.UntransformPoint(channel, point);
|
|
||||||
}
|
|
||||||
return point;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Receives a point in the input space of the filter program and transforms it through to output points
|
|
||||||
/// </summary>
|
|
||||||
public Vector2 TransformPoint(string channel, Vector2 point)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < Filters.Count; i++)
|
|
||||||
{
|
|
||||||
var filter = Filters[i];
|
|
||||||
point = filter.TransformPoint(channel, point);
|
|
||||||
}
|
|
||||||
return point;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ProgramStep
|
|
||||||
{
|
|
||||||
public ProgramStep(ProgramStepType type, object args, string comment = null)
|
|
||||||
{
|
|
||||||
this.Type = type;
|
|
||||||
this.Args = args;
|
|
||||||
this.Comment = comment;
|
|
||||||
}
|
|
||||||
public ProgramStepType Type;
|
|
||||||
public object Args;
|
|
||||||
public string Comment;
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
if (Type == ProgramStepType.Run)
|
|
||||||
return string.Format("Run {0} ({1})", (int)Args, Comment);
|
|
||||||
if (Type == ProgramStepType.NewTarget)
|
|
||||||
return string.Format("NewTarget {0}", (Size)Args);
|
|
||||||
if (Type == ProgramStepType.FinalTarget)
|
|
||||||
return string.Format("FinalTarget");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Compile(string channel, Size insize, Size outsize, bool finalTarget)
|
|
||||||
{
|
|
||||||
RETRY:
|
|
||||||
|
|
||||||
Program.Clear();
|
|
||||||
|
|
||||||
//prep filters for initialization
|
|
||||||
foreach (var f in Filters)
|
|
||||||
{
|
|
||||||
f.BeginInitialization(this);
|
|
||||||
f.Initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
//propagate input size forwards through filter chain to allow a 'flex' filter to determine what its input will be
|
|
||||||
Size presize = insize;
|
|
||||||
for (int i = 0; i < Filters.Count; i++)
|
|
||||||
{
|
|
||||||
var filter = Filters[i];
|
|
||||||
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
|
|
||||||
presize = outsize;
|
|
||||||
for (int i = Filters.Count - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
var filter = Filters[i];
|
|
||||||
presize = filter.PresizeOutput(channel, presize);
|
|
||||||
}
|
|
||||||
|
|
||||||
SurfaceState currState = null;
|
|
||||||
|
|
||||||
for (int i = 0; i < Filters.Count; i++)
|
|
||||||
{
|
|
||||||
BaseFilter f = Filters[i];
|
|
||||||
|
|
||||||
//check whether this filter needs input. if so, notify it of the current pipeline state
|
|
||||||
var iosi = f.FindInput(channel);
|
|
||||||
if (iosi != null)
|
|
||||||
{
|
|
||||||
iosi.SurfaceFormat = currState.SurfaceFormat;
|
|
||||||
f.SetInputFormat(channel, currState);
|
|
||||||
|
|
||||||
//check if the desired disposition needs to change from texture to render target
|
|
||||||
//(if so, insert a render filter)
|
|
||||||
if (iosi.SurfaceDisposition == SurfaceDisposition.RenderTarget && currState.SurfaceDisposition == SurfaceDisposition.Texture)
|
|
||||||
{
|
|
||||||
var renderer = new Render();
|
|
||||||
Filters.Insert(i, renderer);
|
|
||||||
goto RETRY;
|
|
||||||
}
|
|
||||||
//check if the desired disposition needs to change from a render target to a texture
|
|
||||||
//(if so, the current render target gets resolved, and made no longer current
|
|
||||||
else if (iosi.SurfaceDisposition == SurfaceDisposition.Texture && currState.SurfaceDisposition == SurfaceDisposition.RenderTarget)
|
|
||||||
{
|
|
||||||
var resolver = new Resolve();
|
|
||||||
Filters.Insert(i, resolver);
|
|
||||||
goto RETRY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//now, the filter will have set its output state depending on its input state. check if it outputs:
|
|
||||||
iosi = f.FindOutput(channel);
|
|
||||||
if (iosi != null)
|
|
||||||
{
|
|
||||||
if (currState == null)
|
|
||||||
{
|
|
||||||
currState = new SurfaceState();
|
|
||||||
currState.SurfaceFormat = iosi.SurfaceFormat;
|
|
||||||
currState.SurfaceDisposition = iosi.SurfaceDisposition;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//if output disposition is unspecified, change it to whatever we've got right now
|
|
||||||
if (iosi.SurfaceDisposition == SurfaceDisposition.Unspecified)
|
|
||||||
{
|
|
||||||
iosi.SurfaceDisposition = currState.SurfaceDisposition;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool newTarget = false;
|
|
||||||
if (iosi.SurfaceFormat.Size != currState.SurfaceFormat.Size)
|
|
||||||
newTarget = true;
|
|
||||||
else if (currState.SurfaceDisposition == SurfaceDisposition.Texture && iosi.SurfaceDisposition == SurfaceDisposition.RenderTarget)
|
|
||||||
newTarget = true;
|
|
||||||
|
|
||||||
if (newTarget)
|
|
||||||
{
|
|
||||||
currState = new SurfaceState();
|
|
||||||
iosi.SurfaceFormat = currState.SurfaceFormat = iosi.SurfaceFormat;
|
|
||||||
iosi.SurfaceDisposition = currState.SurfaceDisposition = iosi.SurfaceDisposition;
|
|
||||||
Program.Add(new ProgramStep(ProgramStepType.NewTarget, currState.SurfaceFormat.Size));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
currState.SurfaceDisposition = iosi.SurfaceDisposition;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Program.Add(new ProgramStep(ProgramStepType.Run, i, f.GetType().Name));
|
|
||||||
|
|
||||||
} //filter loop
|
|
||||||
|
|
||||||
//if the current output disposition is a texture, we need to render it
|
|
||||||
if (currState.SurfaceDisposition == SurfaceDisposition.Texture)
|
|
||||||
{
|
|
||||||
var renderer = new Render();
|
|
||||||
Filters.Insert(Filters.Count, renderer);
|
|
||||||
goto RETRY;
|
|
||||||
}
|
|
||||||
|
|
||||||
//patch the program so that the final rendertarget set operation is the framebuffer instead
|
|
||||||
if (finalTarget)
|
|
||||||
{
|
|
||||||
for (int i = Program.Count - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
var ps = Program[i];
|
|
||||||
if (ps.Type == ProgramStepType.NewTarget)
|
|
||||||
{
|
|
||||||
var size = (Size)ps.Args;
|
|
||||||
Debug.Assert(size == outsize);
|
|
||||||
ps.Type = ProgramStepType.FinalTarget;
|
|
||||||
ps.Args = size;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,134 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Drawing;
|
|
||||||
|
|
||||||
using BizHawk.Common;
|
|
||||||
using BizHawk.Client.Common;
|
|
||||||
using BizHawk.Client.MultiHawk.FilterManager;
|
|
||||||
|
|
||||||
using BizHawk.Bizware.BizwareGL;
|
|
||||||
using BizHawk.Bizware.BizwareGL.Drivers.OpenTK;
|
|
||||||
|
|
||||||
using OpenTK;
|
|
||||||
using OpenTK.Graphics;
|
|
||||||
|
|
||||||
//Here's how to make a filter:
|
|
||||||
//1. Reset your state entirely in Initialize().
|
|
||||||
// The filter will be re-initialized several times while the chain is getting worked out, but not re-instantiated.
|
|
||||||
// This is sort of annoying, but there's pretty good reasons for it (some external process has created the filters and set parameters needed to govern their chaining and surface properties)
|
|
||||||
//2. In Initialize(), be sure to use DeclareInput
|
|
||||||
//(something about PresizeInput())
|
|
||||||
//3. PresizeOutput() will be called next
|
|
||||||
//4. In SetInputFormat(), use DeclareOutput to set the output based on your desires, or the provided input format.
|
|
||||||
//5. In Run(), the render target is already set. If using a texture, use InputTexture
|
|
||||||
//6. In Run(), if supplying an output texture, use YieldOutput
|
|
||||||
|
|
||||||
namespace BizHawk.Client.MultiHawk.Filters
|
|
||||||
{
|
|
||||||
public class BaseFilter
|
|
||||||
{
|
|
||||||
//initialization stuff
|
|
||||||
public void BeginInitialization(FilterProgram program) { IOSurfaceInfos.Clear(); FilterProgram = program; }
|
|
||||||
public virtual void Initialize() { }
|
|
||||||
public virtual Size PresizeInput(string channel, Size size) { return size; }
|
|
||||||
public virtual Size PresizeOutput(string channel, Size size) { return size; }
|
|
||||||
public virtual void SetInputFormat(string channel, SurfaceState state) { }
|
|
||||||
public Dictionary<string, object> Parameters = new Dictionary<string, object>();
|
|
||||||
|
|
||||||
//runtime signals
|
|
||||||
public virtual Vector2 UntransformPoint(string channel, Vector2 point)
|
|
||||||
{
|
|
||||||
//base class behaviour here just uses the input and output sizes, if appropriate. few filters will have to do anything more complex
|
|
||||||
var input = FindInput(channel);
|
|
||||||
var output = FindOutput(channel);
|
|
||||||
if (input != null && output != null)
|
|
||||||
{
|
|
||||||
point.X *= ((float)input.SurfaceFormat.Size.Width) / (float)output.SurfaceFormat.Size.Width;
|
|
||||||
point.Y *= ((float)input.SurfaceFormat.Size.Height) / (float)output.SurfaceFormat.Size.Height;
|
|
||||||
}
|
|
||||||
return point;
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual Vector2 TransformPoint(string channel, Vector2 point)
|
|
||||||
{
|
|
||||||
//base class behaviour here just uses the input and output sizes, if appropriate. few filters will have to do anything more complex
|
|
||||||
var input = FindInput(channel);
|
|
||||||
var output = FindOutput(channel);
|
|
||||||
if (input != null && output != null)
|
|
||||||
{
|
|
||||||
point.X *= ((float)output.SurfaceFormat.Size.Width) / (float)input.SurfaceFormat.Size.Width;
|
|
||||||
point.Y *= ((float)output.SurfaceFormat.Size.Height) / (float)input.SurfaceFormat.Size.Height;
|
|
||||||
}
|
|
||||||
return point;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetInput(Texture2d tex)
|
|
||||||
{
|
|
||||||
InputTexture = tex;
|
|
||||||
}
|
|
||||||
public virtual void Run() { }
|
|
||||||
public Texture2d GetOutput() { return OutputTexture; }
|
|
||||||
|
|
||||||
//filter actions
|
|
||||||
protected void YieldOutput(Texture2d tex)
|
|
||||||
{
|
|
||||||
OutputTexture = tex;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected FilterProgram FilterProgram;
|
|
||||||
protected Texture2d InputTexture;
|
|
||||||
private Texture2d OutputTexture;
|
|
||||||
|
|
||||||
//setup utilities
|
|
||||||
protected IOSurfaceInfo DeclareInput(SurfaceDisposition disposition = SurfaceDisposition.Unspecified, string channel = "default") { return DeclareIO(SurfaceDirection.Input, channel, disposition); }
|
|
||||||
protected IOSurfaceInfo DeclareOutput(SurfaceDisposition disposition = SurfaceDisposition.Unspecified, string channel = "default") { return DeclareIO(SurfaceDirection.Output, channel, disposition); }
|
|
||||||
|
|
||||||
protected IOSurfaceInfo DeclareOutput(SurfaceState state, string channel = "default")
|
|
||||||
{
|
|
||||||
var iosi = DeclareIO(SurfaceDirection.Output, channel, state.SurfaceDisposition);
|
|
||||||
iosi.SurfaceFormat = state.SurfaceFormat;
|
|
||||||
return iosi;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IOSurfaceInfo FindInput(string channel = "default") { return FindIOSurfaceInfo(channel, SurfaceDirection.Input); }
|
|
||||||
public IOSurfaceInfo FindOutput(string channel = "default") { return FindIOSurfaceInfo(channel, SurfaceDirection.Output); }
|
|
||||||
|
|
||||||
private IOSurfaceInfo DeclareIO(SurfaceDirection direction, string channel, SurfaceDisposition disposition)
|
|
||||||
{
|
|
||||||
var iosi = new IOSurfaceInfo();
|
|
||||||
iosi.SurfaceDirection = direction;
|
|
||||||
iosi.Channel = channel;
|
|
||||||
iosi.SurfaceDisposition = disposition;
|
|
||||||
IOSurfaceInfos.Add(iosi);
|
|
||||||
return iosi;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<IOSurfaceInfo> IOSurfaceInfos = new List<IOSurfaceInfo>();
|
|
||||||
|
|
||||||
|
|
||||||
IOSurfaceInfo FindIOSurfaceInfo(string channel, SurfaceDirection direction)
|
|
||||||
{
|
|
||||||
foreach (var iosi in IOSurfaceInfos)
|
|
||||||
if (iosi.Channel == channel && iosi.SurfaceDirection == direction)
|
|
||||||
return iosi;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class IOSurfaceInfo
|
|
||||||
{
|
|
||||||
public SurfaceFormat SurfaceFormat;
|
|
||||||
public SurfaceDirection SurfaceDirection;
|
|
||||||
public SurfaceDisposition SurfaceDisposition;
|
|
||||||
public string Channel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum SurfaceDirection
|
|
||||||
{
|
|
||||||
Input, Output
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,334 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Drawing;
|
|
||||||
|
|
||||||
using BizHawk.Common;
|
|
||||||
using BizHawk.Client.Common;
|
|
||||||
using BizHawk.Client.MultiHawk;
|
|
||||||
using BizHawk.Client.MultiHawk.FilterManager;
|
|
||||||
|
|
||||||
using BizHawk.Bizware.BizwareGL;
|
|
||||||
using BizHawk.Bizware.BizwareGL.Drivers.OpenTK;
|
|
||||||
|
|
||||||
using OpenTK;
|
|
||||||
using OpenTK.Graphics;
|
|
||||||
|
|
||||||
namespace BizHawk.Client.MultiHawk.Filters
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// applies letterboxing logic to figure out how to fit the source dimensions into the target dimensions.
|
|
||||||
/// In the future this could also apply rules like integer-only scaling, etc.
|
|
||||||
/// </summary>
|
|
||||||
class LetterboxingLogic
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// the location within the destination region of the output content (scaled and translated)
|
|
||||||
/// </summary>
|
|
||||||
public int vx, vy, vw, vh;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// the scale factor eventually used
|
|
||||||
/// </summary>
|
|
||||||
public float WidthScale, HeightScale;
|
|
||||||
|
|
||||||
//do maths on the viewport and the native resolution and the user settings to get a display rectangle
|
|
||||||
public LetterboxingLogic(bool maintainAspect, bool maintainInteger, int targetWidth, int targetHeight, int sourceWidth, int sourceHeight, Size textureSize, Size virtualSize)
|
|
||||||
{
|
|
||||||
int textureWidth = textureSize.Width;
|
|
||||||
int textureHeight = textureSize.Height;
|
|
||||||
int virtualWidth = virtualSize.Width;
|
|
||||||
int virtualHeight = virtualSize.Height;
|
|
||||||
|
|
||||||
//zero 02-jun-2014 - we passed these in, but ignored them. kind of weird..
|
|
||||||
int oldSourceWidth = sourceWidth;
|
|
||||||
int oldSourceHeight = sourceHeight;
|
|
||||||
sourceWidth = (int)virtualWidth;
|
|
||||||
sourceHeight = (int)virtualHeight;
|
|
||||||
|
|
||||||
//this doesnt make sense
|
|
||||||
if (!maintainAspect)
|
|
||||||
maintainInteger = false;
|
|
||||||
|
|
||||||
float widthScale = (float)targetWidth / sourceWidth;
|
|
||||||
float heightScale = (float)targetHeight / sourceHeight;
|
|
||||||
|
|
||||||
if (maintainAspect
|
|
||||||
//zero 20-jul-2014 - hacks upon hacks, this function needs rewriting
|
|
||||||
&& !maintainInteger
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (widthScale > heightScale) widthScale = heightScale;
|
|
||||||
if (heightScale > widthScale) heightScale = widthScale;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (maintainInteger)
|
|
||||||
{
|
|
||||||
//just totally different code
|
|
||||||
//apply the zooming algorithm (pasted and reworked, for now)
|
|
||||||
|
|
||||||
Vector2 VS = new Vector2(virtualWidth, virtualHeight);
|
|
||||||
Vector2 BS = new Vector2(textureWidth, textureHeight);
|
|
||||||
Vector2 AR = Vector2.Divide(VS, BS);
|
|
||||||
float target_par = (AR.X / AR.Y);
|
|
||||||
Vector2 PS = new Vector2(1, 1); //this would malfunction for AR <= 0.5 or AR >= 2.0
|
|
||||||
|
|
||||||
for(;;)
|
|
||||||
{
|
|
||||||
//TODO - would be good not to run this per frame....
|
|
||||||
Vector2[] trials = new[] {
|
|
||||||
PS + new Vector2(1, 0),
|
|
||||||
PS + new Vector2(0, 1),
|
|
||||||
PS + new Vector2(1, 1)
|
|
||||||
};
|
|
||||||
bool[] trials_limited = new bool[3] { false,false,false};
|
|
||||||
int bestIndex = -1;
|
|
||||||
float bestValue = 1000.0f;
|
|
||||||
for (int t = 0; t < trials.Length; t++)
|
|
||||||
{
|
|
||||||
Vector2 vTrial = trials[t];
|
|
||||||
trials_limited[t] = false;
|
|
||||||
|
|
||||||
//check whether this is going to exceed our allotted area
|
|
||||||
int test_vw = (int)(vTrial.X * textureWidth);
|
|
||||||
int test_vh = (int)(vTrial.Y * textureHeight);
|
|
||||||
if (test_vw > targetWidth) trials_limited[t] = true;
|
|
||||||
if (test_vh > targetHeight) trials_limited[t] = true;
|
|
||||||
|
|
||||||
//I.
|
|
||||||
float test_ar = vTrial.X / vTrial.Y;
|
|
||||||
|
|
||||||
//II.
|
|
||||||
//Vector2 calc = Vector2.Multiply(trials[t], VS);
|
|
||||||
//float test_ar = calc.X / calc.Y;
|
|
||||||
|
|
||||||
//not clear which approach is superior
|
|
||||||
float deviation_linear = Math.Abs(test_ar - target_par);
|
|
||||||
float deviation_geom = test_ar / target_par;
|
|
||||||
if (deviation_geom < 1) deviation_geom = 1.0f / deviation_geom;
|
|
||||||
|
|
||||||
float value = deviation_linear;
|
|
||||||
if (value < bestValue)
|
|
||||||
{
|
|
||||||
bestIndex = t;
|
|
||||||
bestValue = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//last result was best, so bail out
|
|
||||||
if (bestIndex == -1)
|
|
||||||
break;
|
|
||||||
|
|
||||||
//if the winner ran off the edge, bail out
|
|
||||||
if (trials_limited[bestIndex])
|
|
||||||
break;
|
|
||||||
|
|
||||||
PS = trials[bestIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
vw = (int)(PS.X * textureWidth);
|
|
||||||
vh = (int)(PS.Y * textureHeight);
|
|
||||||
widthScale = PS.X;
|
|
||||||
heightScale = PS.Y;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
vw = (int)(widthScale * sourceWidth);
|
|
||||||
vh = (int)(heightScale * sourceHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
//determine letterboxing parameters
|
|
||||||
vx = (targetWidth - vw) / 2;
|
|
||||||
vy = (targetHeight - vh) / 2;
|
|
||||||
|
|
||||||
//zero 09-oct-2014 - changed this for TransformPoint. scenario: basic 1x (but system-specified AR) NES window.
|
|
||||||
//vw would be 293 but WidthScale would be 1.0. I think it should be something different.
|
|
||||||
//FinalPresentation doesnt use the LL.WidthScale, so this is unlikely to be breaking anything old that depends on it
|
|
||||||
//WidthScale = widthScale;
|
|
||||||
//HeightScale = heightScale;
|
|
||||||
WidthScale = (float)vw / oldSourceWidth;
|
|
||||||
HeightScale = (float)vh / oldSourceHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public class FinalPresentation : BaseFilter
|
|
||||||
{
|
|
||||||
public enum eFilterOption
|
|
||||||
{
|
|
||||||
None, Bilinear, Bicubic
|
|
||||||
}
|
|
||||||
|
|
||||||
public eFilterOption FilterOption = eFilterOption.None;
|
|
||||||
public RetroShaderChain BicubicFilter;
|
|
||||||
|
|
||||||
public FinalPresentation(Size size)
|
|
||||||
{
|
|
||||||
this.OutputSize = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
Size OutputSize, InputSize;
|
|
||||||
public Size TextureSize, VirtualTextureSize;
|
|
||||||
public int BackgroundColor;
|
|
||||||
public IGuiRenderer GuiRenderer;
|
|
||||||
public IGL GL;
|
|
||||||
bool nop;
|
|
||||||
LetterboxingLogic LL;
|
|
||||||
Size ContentSize;
|
|
||||||
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
DeclareInput();
|
|
||||||
nop = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Size PresizeOutput(string channel, Size size)
|
|
||||||
{
|
|
||||||
if (FilterOption == eFilterOption.Bicubic)
|
|
||||||
{
|
|
||||||
size.Width = LL.vw;
|
|
||||||
size.Height = LL.vh;
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
return base.PresizeOutput(channel, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Size PresizeInput(string channel, Size size)
|
|
||||||
{
|
|
||||||
if (FilterOption != eFilterOption.Bicubic)
|
|
||||||
return size;
|
|
||||||
|
|
||||||
LL = new LetterboxingLogic(Global.Config.DispFixAspectRatio, Global.Config.DispFixScaleInteger, OutputSize.Width, OutputSize.Height, size.Width, size.Height, TextureSize, VirtualTextureSize);
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetInputFormat(string channel, SurfaceState state)
|
|
||||||
{
|
|
||||||
bool need = false;
|
|
||||||
if (state.SurfaceFormat.Size != OutputSize)
|
|
||||||
need = true;
|
|
||||||
if (FilterOption != eFilterOption.None)
|
|
||||||
need = true;
|
|
||||||
|
|
||||||
if (!need)
|
|
||||||
{
|
|
||||||
nop = true;
|
|
||||||
ContentSize = state.SurfaceFormat.Size;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
FindInput().SurfaceDisposition = SurfaceDisposition.Texture;
|
|
||||||
DeclareOutput(new SurfaceState(new SurfaceFormat(OutputSize), SurfaceDisposition.RenderTarget));
|
|
||||||
InputSize = state.SurfaceFormat.Size;
|
|
||||||
LL = new LetterboxingLogic(Global.Config.DispFixAspectRatio, Global.Config.DispFixScaleInteger, OutputSize.Width, OutputSize.Height, InputSize.Width, InputSize.Height, TextureSize, VirtualTextureSize);
|
|
||||||
ContentSize = new Size(LL.vw,LL.vh);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Size GetContentSize() { return ContentSize; }
|
|
||||||
|
|
||||||
public override Vector2 UntransformPoint(string channel, Vector2 point)
|
|
||||||
{
|
|
||||||
if (nop)
|
|
||||||
return point;
|
|
||||||
point.X -= LL.vx;
|
|
||||||
point.Y -= LL.vy;
|
|
||||||
point.X /= LL.WidthScale;
|
|
||||||
point.Y /= LL.HeightScale;
|
|
||||||
return point;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Vector2 TransformPoint(string channel, Vector2 point)
|
|
||||||
{
|
|
||||||
if (nop)
|
|
||||||
return point;
|
|
||||||
point.X *= LL.WidthScale;
|
|
||||||
point.Y *= LL.HeightScale;
|
|
||||||
point.X += LL.vx;
|
|
||||||
point.Y += LL.vy;
|
|
||||||
return point;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Run()
|
|
||||||
{
|
|
||||||
if (nop)
|
|
||||||
return;
|
|
||||||
|
|
||||||
GL.SetClearColor(Color.FromArgb(BackgroundColor));
|
|
||||||
GL.Clear(OpenTK.Graphics.OpenGL.ClearBufferMask.ColorBufferBit);
|
|
||||||
|
|
||||||
GuiRenderer.Begin(OutputSize.Width, OutputSize.Height);
|
|
||||||
GuiRenderer.SetBlendState(GL.BlendNoneCopy);
|
|
||||||
|
|
||||||
if(FilterOption != eFilterOption.None)
|
|
||||||
InputTexture.SetFilterLinear();
|
|
||||||
else
|
|
||||||
InputTexture.SetFilterNearest();
|
|
||||||
|
|
||||||
if (FilterOption == eFilterOption.Bicubic)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
GuiRenderer.Draw(InputTexture,LL.vx,LL.vy,LL.vw,LL.vh);
|
|
||||||
|
|
||||||
GuiRenderer.End();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class LuaLayer : BaseFilter
|
|
||||||
{
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
DeclareInput(SurfaceDisposition.RenderTarget);
|
|
||||||
}
|
|
||||||
public override void SetInputFormat(string channel, SurfaceState state)
|
|
||||||
{
|
|
||||||
DeclareOutput(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
Texture2d Texture;
|
|
||||||
|
|
||||||
public void SetTexture(Texture2d tex)
|
|
||||||
{
|
|
||||||
Texture = tex;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Run()
|
|
||||||
{
|
|
||||||
var outSize = FindOutput().SurfaceFormat.Size;
|
|
||||||
FilterProgram.GuiRenderer.Begin(outSize);
|
|
||||||
FilterProgram.GuiRenderer.SetBlendState(FilterProgram.GL.BlendNormal);
|
|
||||||
FilterProgram.GuiRenderer.Draw(Texture);
|
|
||||||
FilterProgram.GuiRenderer.End();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class OSD : BaseFilter
|
|
||||||
{
|
|
||||||
//this class has the ability to disable its operations for higher performance when the callback is removed,
|
|
||||||
//without having to take it out of the chain. although, its presence in the chain may slow down performance due to added resolves/renders
|
|
||||||
//so, we should probably rebuild the chain.
|
|
||||||
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
if (RenderCallback == null) return;
|
|
||||||
DeclareInput(SurfaceDisposition.RenderTarget);
|
|
||||||
}
|
|
||||||
public override void SetInputFormat(string channel, SurfaceState state)
|
|
||||||
{
|
|
||||||
if (RenderCallback == null) return;
|
|
||||||
DeclareOutput(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Action RenderCallback;
|
|
||||||
|
|
||||||
public override void Run()
|
|
||||||
{
|
|
||||||
if (RenderCallback == null) return;
|
|
||||||
RenderCallback();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,285 +0,0 @@
|
||||||
//https://github.com/Themaister/RetroArch/wiki/GLSL-shaders
|
|
||||||
//https://github.com/Themaister/Emulator-Shader-Pack/blob/master/Cg/README
|
|
||||||
//https://github.com/libretro/common-shaders/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Drawing;
|
|
||||||
|
|
||||||
using BizHawk.Common;
|
|
||||||
using BizHawk.Client.Common;
|
|
||||||
using BizHawk.Client.MultiHawk;
|
|
||||||
using BizHawk.Client.MultiHawk.FilterManager;
|
|
||||||
|
|
||||||
using BizHawk.Bizware.BizwareGL;
|
|
||||||
using BizHawk.Bizware.BizwareGL.Drivers.OpenTK;
|
|
||||||
|
|
||||||
using OpenTK;
|
|
||||||
using OpenTK.Graphics;
|
|
||||||
|
|
||||||
namespace BizHawk.Client.MultiHawk.Filters
|
|
||||||
{
|
|
||||||
public class RetroShaderChain : IDisposable
|
|
||||||
{
|
|
||||||
public RetroShaderChain(IGL owner, RetroShaderPreset preset, string baseDirectory, bool debug = false)
|
|
||||||
{
|
|
||||||
Owner = owner;
|
|
||||||
this.Preset = preset;
|
|
||||||
Passes = preset.Passes.ToArray();
|
|
||||||
|
|
||||||
bool ok = true;
|
|
||||||
|
|
||||||
//load up the shaders
|
|
||||||
Shaders = new RetroShader[preset.Passes.Count];
|
|
||||||
for (int i = 0; i < preset.Passes.Count; i++)
|
|
||||||
{
|
|
||||||
RetroShaderPreset.ShaderPass pass = preset.Passes[i];
|
|
||||||
|
|
||||||
//acquire content
|
|
||||||
string path = Path.Combine(baseDirectory, pass.ShaderPath);
|
|
||||||
if (!File.Exists(path))
|
|
||||||
{
|
|
||||||
ok = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
string content = File.ReadAllText(path);
|
|
||||||
|
|
||||||
var shader = new RetroShader(Owner, content, debug);
|
|
||||||
Shaders[i] = shader;
|
|
||||||
if (!shader.Pipeline.Available)
|
|
||||||
ok = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Available = ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
//todo
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether this shader chain is available (it wont be available if some resources failed to load or compile)
|
|
||||||
/// </summary>
|
|
||||||
public bool Available { get; private set; }
|
|
||||||
|
|
||||||
public readonly IGL Owner;
|
|
||||||
public readonly RetroShaderPreset Preset;
|
|
||||||
public readonly RetroShader[] Shaders;
|
|
||||||
public readonly RetroShaderPreset.ShaderPass[] Passes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RetroShaderPreset
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Parses an instance from a stream to a CGP file
|
|
||||||
/// </summary>
|
|
||||||
public RetroShaderPreset(Stream stream)
|
|
||||||
{
|
|
||||||
var content = new StreamReader(stream).ReadToEnd();
|
|
||||||
Dictionary<string, string> dict = new Dictionary<string, string>();
|
|
||||||
|
|
||||||
//parse the key-value-pair format of the file
|
|
||||||
content = content.Replace("\r", "");
|
|
||||||
foreach (var _line in content.Split('\n'))
|
|
||||||
{
|
|
||||||
var line = _line.Trim();
|
|
||||||
if (line.StartsWith("#")) continue; //lines that are solely comments
|
|
||||||
if (line == "") continue; //empty line
|
|
||||||
int eq = line.IndexOf('=');
|
|
||||||
var key = line.Substring(0, eq).Trim();
|
|
||||||
var value = line.Substring(eq + 1).Trim();
|
|
||||||
int quote = value.IndexOf('\"');
|
|
||||||
if (quote != -1)
|
|
||||||
value = value.Substring(quote + 1, value.IndexOf('\"', quote + 1) - (quote + 1));
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//remove comments from end of value. exclusive from above condition, since comments after quoted strings would be snipped by the quoted string extraction
|
|
||||||
int hash = value.IndexOf('#');
|
|
||||||
if (hash != -1)
|
|
||||||
value = value.Substring(0, hash);
|
|
||||||
value = value.Trim();
|
|
||||||
}
|
|
||||||
dict[key.ToLower()] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
//process the keys
|
|
||||||
int nShaders = FetchInt(dict, "shaders", 0);
|
|
||||||
for (int i = 0; i < nShaders; i++)
|
|
||||||
{
|
|
||||||
ShaderPass sp = new ShaderPass();
|
|
||||||
sp.Index = i;
|
|
||||||
Passes.Add(sp);
|
|
||||||
|
|
||||||
sp.InputFilterLinear = FetchBool(dict, "filter_linear" + i, false); //Should this value not be defined, the filtering option is implementation defined.
|
|
||||||
sp.OuputFloat = FetchBool(dict, "float_framebuffer" + i, false);
|
|
||||||
sp.FrameCountMod = FetchInt(dict, "frame_count_mod" + i, 1);
|
|
||||||
sp.ShaderPath = FetchString(dict, "shader" + i, "?"); //todo - change extension to .cg for better compatibility? just change .cg to .glsl transparently at last second?
|
|
||||||
|
|
||||||
//If no scale type is assumed, it is assumed that it is set to "source" with scaleN set to 1.0.
|
|
||||||
//It is possible to set scale_type_xN and scale_type_yN to specialize the scaling type in either direction. scale_typeN however overrides both of these.
|
|
||||||
sp.ScaleTypeX = (ScaleType)Enum.Parse(typeof(ScaleType), FetchString(dict, "scale_type_x" + i, "Source"), true);
|
|
||||||
sp.ScaleTypeY = (ScaleType)Enum.Parse(typeof(ScaleType), FetchString(dict, "scale_type_y" + i, "Source"), true);
|
|
||||||
ScaleType st = (ScaleType)Enum.Parse(typeof(ScaleType), FetchString(dict, "scale_type" + i, "NotSet"), true);
|
|
||||||
if (st != ScaleType.NotSet)
|
|
||||||
sp.ScaleTypeX = sp.ScaleTypeY = st;
|
|
||||||
|
|
||||||
//scaleN controls both scaling type in horizontal and vertical directions. If scaleN is defined, scale_xN and scale_yN have no effect.
|
|
||||||
sp.Scale.X = FetchFloat(dict, "scale_x" + i, 1);
|
|
||||||
sp.Scale.Y = FetchFloat(dict, "scale_y" + i, 1);
|
|
||||||
float scale = FetchFloat(dict, "scale" + i, -999);
|
|
||||||
if (scale != -999)
|
|
||||||
sp.Scale.X = sp.Scale.Y = FetchFloat(dict, "scale" + i, 1);
|
|
||||||
|
|
||||||
//TODO - LUTs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<ShaderPass> Passes = new List<ShaderPass>();
|
|
||||||
|
|
||||||
public enum ScaleType
|
|
||||||
{
|
|
||||||
NotSet, Source, Viewport, Absolute
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ShaderPass
|
|
||||||
{
|
|
||||||
public int Index;
|
|
||||||
public string ShaderPath;
|
|
||||||
public bool InputFilterLinear;
|
|
||||||
public bool OuputFloat;
|
|
||||||
public int FrameCountMod;
|
|
||||||
public ScaleType ScaleTypeX;
|
|
||||||
public ScaleType ScaleTypeY;
|
|
||||||
public Vector2 Scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
string FetchString(Dictionary<string, string> dict, string key, string @default)
|
|
||||||
{
|
|
||||||
string str;
|
|
||||||
if (dict.TryGetValue(key, out str))
|
|
||||||
return str;
|
|
||||||
else return @default;
|
|
||||||
}
|
|
||||||
|
|
||||||
int FetchInt(Dictionary<string, string> dict, string key, int @default)
|
|
||||||
{
|
|
||||||
string str;
|
|
||||||
if (dict.TryGetValue(key, out str))
|
|
||||||
return int.Parse(str);
|
|
||||||
else return @default;
|
|
||||||
}
|
|
||||||
|
|
||||||
float FetchFloat(Dictionary<string, string> dict, string key, float @default)
|
|
||||||
{
|
|
||||||
string str;
|
|
||||||
if (dict.TryGetValue(key, out str))
|
|
||||||
return float.Parse(str);
|
|
||||||
else return @default;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FetchBool(Dictionary<string, string> dict, string key, bool @default)
|
|
||||||
{
|
|
||||||
string str;
|
|
||||||
if (dict.TryGetValue(key, out str))
|
|
||||||
return ParseBool(str);
|
|
||||||
else return @default;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool ParseBool(string value)
|
|
||||||
{
|
|
||||||
if (value == "1") return true;
|
|
||||||
if (value == "0") return false;
|
|
||||||
value = value.ToLower();
|
|
||||||
if (value == "true") return true;
|
|
||||||
if (value == "false") return false;
|
|
||||||
throw new InvalidOperationException("Unparseable bool in CGP file content");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RetroShaderPass : BaseFilter
|
|
||||||
{
|
|
||||||
RetroShaderChain RSC;
|
|
||||||
RetroShaderPreset.ShaderPass SP;
|
|
||||||
int RSI;
|
|
||||||
Size OutputSize;
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return string.Format("RetroShaderPass[#{0}]", RSI);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RetroShaderPass(RetroShaderChain RSC, int index)
|
|
||||||
{
|
|
||||||
this.RSC = RSC;
|
|
||||||
this.RSI = index;
|
|
||||||
this.SP = RSC.Passes[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
DeclareInput(SurfaceDisposition.Texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetInputFormat(string channel, SurfaceState state)
|
|
||||||
{
|
|
||||||
Size insize = state.SurfaceFormat.Size;
|
|
||||||
if (SP.ScaleTypeX == RetroShaderPreset.ScaleType.Absolute) OutputSize.Width = (int)SP.Scale.X;
|
|
||||||
if (SP.ScaleTypeY == RetroShaderPreset.ScaleType.Absolute) OutputSize.Width = (int)SP.Scale.Y;
|
|
||||||
if (SP.ScaleTypeX == RetroShaderPreset.ScaleType.Source) OutputSize.Width = (int)(insize.Width * SP.Scale.X);
|
|
||||||
if (SP.ScaleTypeY == RetroShaderPreset.ScaleType.Source) OutputSize.Height = (int)(insize.Height * SP.Scale.Y);
|
|
||||||
|
|
||||||
var outState = new SurfaceState();
|
|
||||||
outState.SurfaceFormat = new SurfaceFormat(OutputSize);
|
|
||||||
outState.SurfaceDisposition = SurfaceDisposition.RenderTarget;
|
|
||||||
DeclareOutput(outState);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Size PresizeOutput(string channel, Size size)
|
|
||||||
{
|
|
||||||
OutputSize = size;
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Size PresizeInput(string channel, Size insize)
|
|
||||||
{
|
|
||||||
Size outsize = insize;
|
|
||||||
if (SP.ScaleTypeX == RetroShaderPreset.ScaleType.Absolute) outsize.Width = (int)SP.Scale.X;
|
|
||||||
if (SP.ScaleTypeY == RetroShaderPreset.ScaleType.Absolute) outsize.Width = (int)SP.Scale.Y;
|
|
||||||
if (SP.ScaleTypeX == RetroShaderPreset.ScaleType.Source) outsize.Width = (int)(insize.Width * SP.Scale.X);
|
|
||||||
if (SP.ScaleTypeY == RetroShaderPreset.ScaleType.Source) outsize.Height = (int)(insize.Height * SP.Scale.Y);
|
|
||||||
return outsize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Run()
|
|
||||||
{
|
|
||||||
var shader = RSC.Shaders[RSI];
|
|
||||||
|
|
||||||
shader.Bind();
|
|
||||||
|
|
||||||
//apply all parameters to this shader.. even if it was meant for other shaders. kind of lame.
|
|
||||||
if(Parameters != null)
|
|
||||||
foreach (var kvp in Parameters)
|
|
||||||
{
|
|
||||||
if (kvp.Value is float)
|
|
||||||
shader.Pipeline[kvp.Key].Set((float)kvp.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
var input = InputTexture;
|
|
||||||
if (SP.InputFilterLinear)
|
|
||||||
InputTexture.SetFilterLinear();
|
|
||||||
else
|
|
||||||
InputTexture.SetFilterNearest();
|
|
||||||
RSC.Shaders[RSI].Run(input, input.Size, OutputSize, InputTexture.IsUpsideDown);
|
|
||||||
|
|
||||||
//maintain invariant.. i think.
|
|
||||||
InputTexture.SetFilterNearest();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,90 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Drawing;
|
|
||||||
|
|
||||||
using BizHawk.Common;
|
|
||||||
using BizHawk.Client.Common;
|
|
||||||
using BizHawk.Client.MultiHawk;
|
|
||||||
using BizHawk.Client.MultiHawk.FilterManager;
|
|
||||||
|
|
||||||
using BizHawk.Bizware.BizwareGL;
|
|
||||||
using BizHawk.Bizware.BizwareGL.Drivers.OpenTK;
|
|
||||||
|
|
||||||
using OpenTK;
|
|
||||||
using OpenTK.Graphics;
|
|
||||||
|
|
||||||
namespace BizHawk.Client.MultiHawk.Filters
|
|
||||||
{
|
|
||||||
public class SourceImage : BaseFilter
|
|
||||||
{
|
|
||||||
public SourceImage(Size size)
|
|
||||||
{
|
|
||||||
this.Size = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
Size Size;
|
|
||||||
|
|
||||||
public Texture2d Texture;
|
|
||||||
|
|
||||||
public override void Run()
|
|
||||||
{
|
|
||||||
YieldOutput(Texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
DeclareOutput(new SurfaceState(new SurfaceFormat(Size), SurfaceDisposition.Texture));
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetInputFormat(string channel, SurfaceState format)
|
|
||||||
{
|
|
||||||
DeclareOutput(SurfaceDisposition.Texture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// transforms an input texture to an output render target (by rendering it)
|
|
||||||
/// </summary>
|
|
||||||
class Render : BaseFilter
|
|
||||||
{
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
DeclareInput(SurfaceDisposition.Texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetInputFormat(string channel, SurfaceState state)
|
|
||||||
{
|
|
||||||
DeclareOutput(new SurfaceState(state.SurfaceFormat, SurfaceDisposition.RenderTarget));
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Run()
|
|
||||||
{
|
|
||||||
var renderer = FilterProgram.GuiRenderer;
|
|
||||||
renderer.Begin(FindOutput().SurfaceFormat.Size);
|
|
||||||
renderer.SetBlendState(FilterProgram.GL.BlendNoneCopy);
|
|
||||||
renderer.Draw(InputTexture);
|
|
||||||
renderer.End();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Resolve : BaseFilter
|
|
||||||
{
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
DeclareInput(SurfaceDisposition.RenderTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetInputFormat(string channel, SurfaceState state)
|
|
||||||
{
|
|
||||||
DeclareOutput(new SurfaceState(state.SurfaceFormat, SurfaceDisposition.Texture));
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Run()
|
|
||||||
{
|
|
||||||
YieldOutput(FilterProgram.GetRenderTarget().Texture2d);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,82 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
using BizHawk.Common;
|
|
||||||
using BizHawk.Emulation.Common;
|
|
||||||
using BizHawk.Client.Common;
|
|
||||||
|
|
||||||
using BizHawk.Bizware.BizwareGL;
|
|
||||||
|
|
||||||
namespace BizHawk.Client.MultiHawk
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Recycles a pair of temporary textures (in case double-buffering helps any) to contain a BitmapBuffer's or DisplaySurface's contents, as long as the dimensions match.
|
|
||||||
/// When the dimensions dont match, a new one will be allocated
|
|
||||||
/// </summary>
|
|
||||||
class TextureFrugalizer : IDisposable
|
|
||||||
{
|
|
||||||
public TextureFrugalizer(IGL gl)
|
|
||||||
{
|
|
||||||
GL = gl;
|
|
||||||
ResetList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
foreach (var ct in CurrentTextures)
|
|
||||||
if(ct != null)
|
|
||||||
ct.Dispose();
|
|
||||||
ResetList();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ResetList()
|
|
||||||
{
|
|
||||||
CurrentTextures = new List<Texture2d>();
|
|
||||||
CurrentTextures.Add(null);
|
|
||||||
CurrentTextures.Add(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
IGL GL;
|
|
||||||
List<Texture2d> CurrentTextures;
|
|
||||||
|
|
||||||
public Texture2d Get(DisplaySurface ds)
|
|
||||||
{
|
|
||||||
using (var bb = new BitmapBuffer(ds.PeekBitmap(), new BitmapLoadOptions()))
|
|
||||||
{
|
|
||||||
return Get(bb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public Texture2d Get(BitmapBuffer bb)
|
|
||||||
{
|
|
||||||
//get the current entry
|
|
||||||
Texture2d CurrentTexture = CurrentTextures[0];
|
|
||||||
|
|
||||||
//TODO - its a bit cruddy here that we dont respect the current texture HasAlpha condition (in fact, theres no such concept)
|
|
||||||
//we might need to deal with that in the future to fix some bugs.
|
|
||||||
|
|
||||||
//check if its rotten and needs recreating
|
|
||||||
if (CurrentTexture == null || CurrentTexture.IntWidth != bb.Width || CurrentTexture.IntHeight != bb.Height)
|
|
||||||
{
|
|
||||||
//needs recreating. be sure to kill the old one...
|
|
||||||
if (CurrentTexture != null)
|
|
||||||
CurrentTexture.Dispose();
|
|
||||||
//and make a new one
|
|
||||||
CurrentTexture = GL.LoadTexture(bb);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//its good! just load in the data
|
|
||||||
GL.LoadTextureData(CurrentTexture, bb);
|
|
||||||
}
|
|
||||||
|
|
||||||
//now shuffle the buffers
|
|
||||||
CurrentTextures[0] = CurrentTextures[1];
|
|
||||||
CurrentTextures[1] = CurrentTexture;
|
|
||||||
|
|
||||||
//deterministic state, i guess
|
|
||||||
CurrentTexture.SetFilterNearest();
|
|
||||||
|
|
||||||
return CurrentTexture;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue