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.
|
||||
/// When the dimensions dont match, a new one will be allocated
|
||||
/// </summary>
|
||||
class TextureFrugalizer : IDisposable
|
||||
public class TextureFrugalizer : IDisposable
|
||||
{
|
||||
public TextureFrugalizer(IGL gl)
|
||||
{
|
||||
|
|
|
@ -72,15 +72,7 @@
|
|||
<DependentUpon>RecordMovie.cs</DependentUpon>
|
||||
</Compile>
|
||||
<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\SwappableDisplaySurfaceSet.cs" />
|
||||
<Compile Include="DisplayManager\TextureFrugalizer.cs" />
|
||||
<Compile Include="EmulatorWindow.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
|
|
|
@ -9,7 +9,7 @@ using System.Drawing;
|
|||
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Client.Common;
|
||||
using BizHawk.Client.MultiHawk.FilterManager;
|
||||
using BizHawk.Client.EmuHawk.FilterManager;
|
||||
using BizHawk.Bizware.BizwareGL;
|
||||
|
||||
using OpenTK;
|
||||
|
@ -52,7 +52,7 @@ namespace BizHawk.Client.MultiHawk
|
|||
else
|
||||
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
|
||||
for (int i = 0; i < 16; i++)
|
||||
|
@ -96,11 +96,11 @@ namespace BizHawk.Client.MultiHawk
|
|||
/// </summary>
|
||||
int currEmuWidth, currEmuHeight;
|
||||
|
||||
TextureFrugalizer VideoTextureFrugalizer;
|
||||
Dictionary<string, TextureFrugalizer> LuaSurfaceFrugalizers = new Dictionary<string, TextureFrugalizer>();
|
||||
BizHawk.Client.EmuHawk.TextureFrugalizer VideoTextureFrugalizer;
|
||||
Dictionary<string, BizHawk.Client.EmuHawk.TextureFrugalizer> LuaSurfaceFrugalizers = new Dictionary<string, BizHawk.Client.EmuHawk.TextureFrugalizer>();
|
||||
RenderTargetFrugalizer[] ShaderChainFrugalizers;
|
||||
|
||||
Filters.RetroShaderChain ShaderChain_user;
|
||||
BizHawk.Client.EmuHawk.Filters.RetroShaderChain ShaderChain_user;
|
||||
|
||||
public void RefreshUserShader()
|
||||
{
|
||||
|
@ -110,7 +110,7 @@ namespace BizHawk.Client.MultiHawk
|
|||
{
|
||||
var fi = new FileInfo(Global.Config.DispUserFilterPath);
|
||||
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
|
||||
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)
|
||||
selectedChain = ShaderChain_user;
|
||||
|
||||
Filters.FinalPresentation fPresent = new Filters.FinalPresentation(chain_outsize);
|
||||
Filters.SourceImage fInput = new Filters.SourceImage(chain_insize);
|
||||
Filters.OSD fOSD = new Filters.OSD();
|
||||
BizHawk.Client.EmuHawk.Filters.FinalPresentation fPresent = new BizHawk.Client.EmuHawk.Filters.FinalPresentation(chain_outsize);
|
||||
BizHawk.Client.EmuHawk.Filters.SourceImage fInput = new BizHawk.Client.EmuHawk.Filters.SourceImage(chain_insize);
|
||||
BizHawk.Client.EmuHawk.Filters.OSD fOSD = new BizHawk.Client.EmuHawk.Filters.OSD();
|
||||
fOSD.RenderCallback = () =>
|
||||
{
|
||||
if (!includeOSD)
|
||||
|
@ -142,11 +142,11 @@ namespace BizHawk.Client.MultiHawk
|
|||
chain.AddFilter(fInput, "input");
|
||||
|
||||
//choose final filter
|
||||
Filters.FinalPresentation.eFilterOption finalFilter = Filters.FinalPresentation.eFilterOption.None;
|
||||
if (Global.Config.DispFinalFilter == 1) finalFilter = Filters.FinalPresentation.eFilterOption.Bilinear;
|
||||
if (Global.Config.DispFinalFilter == 2) finalFilter = Filters.FinalPresentation.eFilterOption.Bicubic;
|
||||
|
||||
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 = BizHawk.Client.EmuHawk.Filters.FinalPresentation.eFilterOption.Bilinear;
|
||||
if (Global.Config.DispFinalFilter == 2) finalFilter = BizHawk.Client.EmuHawk.Filters.FinalPresentation.eFilterOption.Bicubic;
|
||||
|
||||
finalFilter = BizHawk.Client.EmuHawk.Filters.FinalPresentation.eFilterOption.None;
|
||||
|
||||
fPresent.FilterOption = finalFilter;
|
||||
|
||||
|
@ -156,12 +156,12 @@ namespace BizHawk.Client.MultiHawk
|
|||
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++)
|
||||
{
|
||||
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);
|
||||
program.AddFilter(rsp, fname);
|
||||
rsp.Parameters = properties;
|
||||
|
@ -420,11 +420,11 @@ TESTEROO:
|
|||
filterProgram.GL = GL;
|
||||
|
||||
//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;
|
||||
|
||||
//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.TextureSize = new Size(bufferWidth, bufferHeight);
|
||||
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