diff --git a/Assets/dll/libgcc_s_seh-1.dll b/Assets/dll/libgcc_s_seh-1.dll deleted file mode 100644 index df7e7815d0a..00000000000 Binary files a/Assets/dll/libgcc_s_seh-1.dll and /dev/null differ diff --git a/Assets/dll/librashader.dll b/Assets/dll/librashader.dll new file mode 100644 index 00000000000..d39f5dae0c8 Binary files /dev/null and b/Assets/dll/librashader.dll differ diff --git a/Assets/dll/libstdc++-6.dll b/Assets/dll/libstdc++-6.dll deleted file mode 100644 index 53193c267f6..00000000000 Binary files a/Assets/dll/libstdc++-6.dll and /dev/null differ diff --git a/src/BizHawk.Bizware.Graphics/BizHawk.Bizware.Graphics.csproj b/src/BizHawk.Bizware.Graphics/BizHawk.Bizware.Graphics.csproj index 267244effbe..11228a022a9 100644 --- a/src/BizHawk.Bizware.Graphics/BizHawk.Bizware.Graphics.csproj +++ b/src/BizHawk.Bizware.Graphics/BizHawk.Bizware.Graphics.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 @@ -8,6 +8,7 @@ disable + diff --git a/src/BizHawk.Bizware.Graphics/D3D11/D3D11Resources.cs b/src/BizHawk.Bizware.Graphics/D3D11/D3D11Resources.cs index 5927f95f55f..f7e5a10840f 100644 --- a/src/BizHawk.Bizware.Graphics/D3D11/D3D11Resources.cs +++ b/src/BizHawk.Bizware.Graphics/D3D11/D3D11Resources.cs @@ -47,12 +47,6 @@ public void CreateResources() var creationFlags = DeviceCreationFlags.Singlethreaded | DeviceCreationFlags.BgraSupport; #endif - // GL interop doesn't support the single threaded flag - if (D3D11GLInterop.IsAvailable) - { - creationFlags &= ~DeviceCreationFlags.Singlethreaded; - } - D3D11.D3D11CreateDevice( adapter: null, DriverType.Hardware, diff --git a/src/BizHawk.Bizware.Graphics/D3D11/IGL_D3D11.cs b/src/BizHawk.Bizware.Graphics/D3D11/IGL_D3D11.cs index a0ae2797d14..4a7ab7945c1 100644 --- a/src/BizHawk.Bizware.Graphics/D3D11/IGL_D3D11.cs +++ b/src/BizHawk.Bizware.Graphics/D3D11/IGL_D3D11.cs @@ -308,5 +308,9 @@ public void DrawIndexed(int indexCount, int indexStart, int vertexStart) Context.IASetPrimitiveTopology(PrimitiveTopology.TriangleList); Context.DrawIndexed(indexCount, indexStart, vertexStart); } + + internal ID3D11Device LibrashaderDevice => Device; + internal ID3D11DeviceContext LibrashaderContext => Context; + internal ID3D11RenderTargetView LibrashaderBackBufferRTV => _controlSwapChain?.RTV; } } diff --git a/src/BizHawk.Bizware.Graphics/D3D11/LibrashaderProcessorD3D11.cs b/src/BizHawk.Bizware.Graphics/D3D11/LibrashaderProcessorD3D11.cs new file mode 100644 index 00000000000..44293fdf9d3 --- /dev/null +++ b/src/BizHawk.Bizware.Graphics/D3D11/LibrashaderProcessorD3D11.cs @@ -0,0 +1,111 @@ +namespace BizHawk.Bizware.Graphics +{ + public unsafe class LibrashaderProcessorD3D11 : IDisposable + { + private readonly IGL_D3D11 _gl; + private IntPtr _preset = IntPtr.Zero; + private IntPtr _chain = IntPtr.Zero; + private uint _frameCount = 0; + private bool _initialized = false; + private int _filteredWidth; + private int _filteredHeight; + private readonly string _shaderPresetPath; + + public bool IsAvailable => _initialized; + + public LibrashaderProcessorD3D11(IGL_D3D11 gl, string shaderPresetPath) + { + _gl = gl; + _shaderPresetPath = shaderPresetPath; + } + + public bool Initialize(int width, int height) + { + if (_initialized && _filteredWidth == width && _filteredHeight == height) + return true; + + _filteredWidth = width; + _filteredHeight = height; + + if (_filteredWidth <= 0 || _filteredHeight <= 0) return false; + if (!Librashader.Load()) return false; + if (!System.IO.File.Exists(_shaderPresetPath)) return false; + + if (_preset == IntPtr.Zero) + { + IntPtr error = Librashader.PresetCreate(_shaderPresetPath, out _preset); + if (error != IntPtr.Zero) + { + _ = Librashader.libra_error_print(error); + return false; + } + } + + if (_chain == IntPtr.Zero) + { + var device = _gl.LibrashaderDevice; + if (device == null) return false; + + var options = Librashader.CreateDefaultD3D11Options(); + IntPtr error = Librashader.libra_d3d11_filter_chain_create(ref _preset, device.NativePointer, ref options, out _chain); + if (error != IntPtr.Zero) + { + _ = Librashader.libra_error_print(error); + return false; + } + } + + return _initialized = true; + } + + public void Render(IntPtr inputSRVPointer, IntPtr outputRTVPointer) + { + if (!_initialized || _chain == IntPtr.Zero) return; + if (inputSRVPointer == IntPtr.Zero || outputRTVPointer == IntPtr.Zero) return; + + var context = _gl.LibrashaderContext; + if (context == null) return; + + var viewport = new Librashader.libra_viewport_t + { + x = 0, + y = 0, + width = (uint)_filteredWidth, + height = (uint)_filteredHeight, + }; + + _ = Librashader.libra_d3d11_filter_chain_frame( + ref _chain, + context.NativePointer, + new UIntPtr(_frameCount++), + inputSRVPointer, + outputRTVPointer, + ref viewport, + IntPtr.Zero, + IntPtr.Zero); + } + + public IntPtr GetBackBufferRTVPointer() + { + var rtv = _gl.LibrashaderBackBufferRTV; + return rtv?.NativePointer ?? IntPtr.Zero; + } + + public void Dispose() + { + if (_chain != IntPtr.Zero) + { + _ = Librashader.libra_d3d11_filter_chain_free(ref _chain); + _chain = IntPtr.Zero; + } + + if (_preset != IntPtr.Zero) + { + _ = Librashader.libra_preset_free(ref _preset); + _preset = IntPtr.Zero; + } + + _initialized = false; + } + } +} diff --git a/src/BizHawk.Bizware.Graphics/Librashader/Librashader.cs b/src/BizHawk.Bizware.Graphics/Librashader/Librashader.cs new file mode 100644 index 00000000000..90c1ad3ce2e --- /dev/null +++ b/src/BizHawk.Bizware.Graphics/Librashader/Librashader.cs @@ -0,0 +1,168 @@ +using System.Runtime.InteropServices; +using System.Text; + +using BizHawk.Common; + +namespace BizHawk.Bizware.Graphics +{ + public static unsafe class Librashader + { + private const CallingConvention CC = CallingConvention.Cdecl; + private static bool _loaded = false; + + public static bool IsLoaded => _loaded; + + public static bool Load() + { + if (_loaded) return true; + + Util.DebugWriteLine("[librashader] Attempting to load librashader.dll..."); + try + { + libra_instance_abi_version(); + Util.DebugWriteLine("[librashader] Successfully loaded librashader.dll"); + _loaded = true; + return true; + } + catch (DllNotFoundException) + { + Util.DebugWriteLine("[librashader] Failed to load librashader.dll"); + return false; + } + } + + [DllImport("librashader", CallingConvention = CC)] + public static extern IntPtr libra_instance_abi_version(); + + [DllImport("librashader", CallingConvention = CC)] + public static extern IntPtr libra_instance_api_version(); + + [DllImport("librashader", CallingConvention = CC)] + public static extern IntPtr libra_preset_create(byte* filename, out IntPtr preset); + + [DllImport("librashader", CallingConvention = CC)] + public static extern int libra_preset_free(ref IntPtr preset); + + [DllImport("librashader", CallingConvention = CC)] + public static extern int libra_error_free(ref IntPtr error); + + [DllImport("librashader", CallingConvention = CC)] + public static extern int libra_error_print(IntPtr error); + + [DllImport("librashader", CallingConvention = CC)] + public static extern int libra_error_errno(IntPtr error); + + public static IntPtr PresetCreate(string filename, out IntPtr preset) + { + byte[] bytes = Encoding.UTF8.GetBytes(filename + "\0"); + fixed (byte* ptr = bytes) + { + return libra_preset_create(ptr, out preset); + } + } + + [DllImport("librashader", CallingConvention = CC)] + public static extern IntPtr libra_gl_filter_chain_create( + ref IntPtr preset, + IntPtr loader, + [In] ref filter_chain_gl_opt_t options, + out IntPtr chain); + + [DllImport("librashader", CallingConvention = CC)] + public static extern int libra_gl_filter_chain_frame( + ref IntPtr chain, + UIntPtr frame_count, + libra_image_gl_t image, + libra_image_gl_t output, + IntPtr viewport, + IntPtr mvp, + IntPtr options); + + [DllImport("librashader", CallingConvention = CC)] + public static extern int libra_gl_filter_chain_free(ref IntPtr chain); + + [StructLayout(LayoutKind.Sequential)] + public struct filter_chain_gl_opt_t + { + public UIntPtr version; + public ushort glsl_version; + [MarshalAs(UnmanagedType.U1)] + public bool use_dsa; + [MarshalAs(UnmanagedType.U1)] + public bool force_no_mipmaps; + [MarshalAs(UnmanagedType.U1)] + public bool disable_cache; + } + + [StructLayout(LayoutKind.Sequential)] + public struct libra_image_gl_t + { + public uint handle; + public uint format; + public uint width; + public uint height; + } + + public static filter_chain_gl_opt_t CreateDefaultGLOptions() + { + return new filter_chain_gl_opt_t + { + version = new UIntPtr(2), + glsl_version = 330, + use_dsa = false, + force_no_mipmaps = false, + disable_cache = false, + }; + } + + [DllImport("librashader", CallingConvention = CC)] + public static extern IntPtr libra_d3d11_filter_chain_create( + ref IntPtr preset, + IntPtr device, + [In] ref filter_chain_d3d11_opt_t options, + out IntPtr chain); + + [DllImport("librashader", CallingConvention = CC)] + public static extern int libra_d3d11_filter_chain_frame( + ref IntPtr chain, + IntPtr device_context, + UIntPtr frame_count, + IntPtr image, + IntPtr output, + ref libra_viewport_t viewport, + IntPtr mvp, + IntPtr options); + + [DllImport("librashader", CallingConvention = CC)] + public static extern int libra_d3d11_filter_chain_free(ref IntPtr chain); + + [StructLayout(LayoutKind.Sequential)] + public struct filter_chain_d3d11_opt_t + { + public UIntPtr version; + [MarshalAs(UnmanagedType.U1)] + public bool force_no_mipmaps; + [MarshalAs(UnmanagedType.U1)] + public bool disable_cache; + } + + [StructLayout(LayoutKind.Sequential)] + public struct libra_viewport_t + { + public float x; + public float y; + public uint width; + public uint height; + } + + public static filter_chain_d3d11_opt_t CreateDefaultD3D11Options() + { + return new filter_chain_d3d11_opt_t + { + version = new UIntPtr(2), + force_no_mipmaps = false, + disable_cache = false, + }; + } + } +} diff --git a/src/BizHawk.Bizware.Graphics/OpenGL/IGL_OpenGL.cs b/src/BizHawk.Bizware.Graphics/OpenGL/IGL_OpenGL.cs index 73eec75845d..2b1fc4f1878 100644 --- a/src/BizHawk.Bizware.Graphics/OpenGL/IGL_OpenGL.cs +++ b/src/BizHawk.Bizware.Graphics/OpenGL/IGL_OpenGL.cs @@ -14,6 +14,8 @@ public class IGL_OpenGL : IGL private readonly GL GL; + internal GL LibrashaderGL => GL; + // rendering state private OpenGLPipeline _curPipeline; internal bool DefaultRenderTargetBound; diff --git a/src/BizHawk.Bizware.Graphics/OpenGL/LibrashaderProcessorGL.cs b/src/BizHawk.Bizware.Graphics/OpenGL/LibrashaderProcessorGL.cs new file mode 100644 index 00000000000..db76dc024b1 --- /dev/null +++ b/src/BizHawk.Bizware.Graphics/OpenGL/LibrashaderProcessorGL.cs @@ -0,0 +1,196 @@ +using System.Runtime.InteropServices; + +using BizHawk.Common; + +using Silk.NET.OpenGL; + +namespace BizHawk.Bizware.Graphics +{ + public unsafe class LibrashaderProcessorGL : IDisposable + { + private readonly IGL_OpenGL _gl; + private readonly GL _api; + private IntPtr _preset = IntPtr.Zero; + private IntPtr _chain = IntPtr.Zero; + private uint _frameCount = 0; + private bool _initialized = false; + private int _filteredWidth; + private int _filteredHeight; + private readonly string _shaderPresetPath; + + private uint _framebuffer; + private uint _framebufferTexture; + + private static IntPtr _getProcAddressPtr = IntPtr.Zero; + private static GetProcAddressDelegate _getProcAddressDelegate; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate IntPtr GetProcAddressDelegate(byte* name); + + public bool IsAvailable => _initialized; + + public LibrashaderProcessorGL(IGL_OpenGL gl, string shaderPresetPath) + { + _gl = gl; + _api = gl.LibrashaderGL; + _shaderPresetPath = shaderPresetPath; + } + + public bool Initialize(int width, int height) + { + if (_initialized && _filteredWidth == width && _filteredHeight == height) + return true; + + _filteredWidth = width; + _filteredHeight = height; + + if (_filteredWidth <= 0 || _filteredHeight <= 0) return false; + if (!Librashader.Load()) return false; + if (!System.IO.File.Exists(_shaderPresetPath)) return false; + + CleanupFramebuffer(); + + if (_preset == IntPtr.Zero) + { + IntPtr error = Librashader.PresetCreate(_shaderPresetPath, out _preset); + if (error != IntPtr.Zero) + { + _ = Librashader.libra_error_print(error); + return false; + } + } + + if (_chain == IntPtr.Zero) + { + var options = Librashader.CreateDefaultGLOptions(); + + if (_getProcAddressPtr == IntPtr.Zero) + { + _getProcAddressDelegate = GetProcAddressCallback; + _getProcAddressPtr = Marshal.GetFunctionPointerForDelegate(_getProcAddressDelegate); + } + + IntPtr error = Librashader.libra_gl_filter_chain_create(ref _preset, _getProcAddressPtr, ref options, out _chain); + if (error != IntPtr.Zero) + { + _ = Librashader.libra_error_print(error); + return false; + } + } + + CreateFramebuffer(); + + return _initialized = true; + } + + private void CleanupFramebuffer() + { + if (_framebuffer != 0) + { + _api.DeleteFramebuffer(_framebuffer); + _framebuffer = 0; + } + + if (_framebufferTexture != 0) + { + _api.DeleteTexture(_framebufferTexture); + _framebufferTexture = 0; + } + } + + private void CreateFramebuffer() + { + _framebufferTexture = _api.GenTexture(); + _api.BindTexture(TextureTarget.Texture2D, _framebufferTexture); + _api.TexImage2D(TextureTarget.Texture2D, 0, InternalFormat.Rgb, (uint)_filteredWidth, (uint)_filteredHeight, 0, PixelFormat.Rgb, PixelType.UnsignedByte, null); + _api.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear); + _api.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); + _api.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge); + _api.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge); + + _framebuffer = _api.GenFramebuffer(); + _api.BindFramebuffer(FramebufferTarget.Framebuffer, _framebuffer); + _api.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget.Texture2D, _framebufferTexture, 0); + + var status = _api.CheckFramebufferStatus(FramebufferTarget.Framebuffer); + if (status != GLEnum.FramebufferComplete) + { + _initialized = false; + return; + } + + _api.BindFramebuffer(FramebufferTarget.Framebuffer, 0); + } + + private static IntPtr GetProcAddressCallback(byte* name) + { + string procName = Mershul.PtrToStringUtf8((IntPtr)name); + if (procName == null) return IntPtr.Zero; + + try + { + return SDL2OpenGLContext.GetGLProcAddress(procName); + } + catch + { + return IntPtr.Zero; + } + } + + public void Render(uint inputTexId, uint outputFbo, int inputWidth, int inputHeight) + { + if (!_initialized || _chain == IntPtr.Zero) return; + if (inputTexId == 0) return; + + _api.ActiveTexture(TextureUnit.Texture0); + _api.BindTexture(TextureTarget.Texture2D, inputTexId); + _api.GenerateMipmap(TextureTarget.Texture2D); + _api.BindFramebuffer(FramebufferTarget.Framebuffer, 0); + + var input = new Librashader.libra_image_gl_t + { + handle = inputTexId, + format = 0x8058, + width = (uint)inputWidth, + height = (uint)inputHeight, + }; + + var output = new Librashader.libra_image_gl_t + { + handle = _framebufferTexture, + format = 0x1907, + width = (uint)_filteredWidth, + height = (uint)_filteredHeight, + }; + + _ = Librashader.libra_gl_filter_chain_frame(ref _chain, new UIntPtr(_frameCount++), input, output, + IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + + _api.BindFramebuffer(FramebufferTarget.ReadFramebuffer, _framebuffer); + _api.BindFramebuffer(FramebufferTarget.DrawFramebuffer, outputFbo); + _api.BlitFramebuffer(0, _filteredHeight, _filteredWidth, 0, + 0, 0, _filteredWidth, _filteredHeight, + ClearBufferMask.ColorBufferBit, BlitFramebufferFilter.Linear); + _api.BindFramebuffer(FramebufferTarget.ReadFramebuffer, 0); + } + + public void Dispose() + { + if (_chain != IntPtr.Zero) + { + _ = Librashader.libra_gl_filter_chain_free(ref _chain); + _chain = IntPtr.Zero; + } + + if (_preset != IntPtr.Zero) + { + _ = Librashader.libra_preset_free(ref _preset); + _preset = IntPtr.Zero; + } + + CleanupFramebuffer(); + + _initialized = false; + } + } +} diff --git a/src/BizHawk.Client.Common/Api/Classes/UserDataApi.cs b/src/BizHawk.Client.Common/Api/Classes/UserDataApi.cs index 44c1299ad0d..0fc959b340c 100644 --- a/src/BizHawk.Client.Common/Api/Classes/UserDataApi.cs +++ b/src/BizHawk.Client.Common/Api/Classes/UserDataApi.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; #if NET5_0_OR_GREATER using KeyCollectionType = System.Collections.Generic.IReadOnlySet; diff --git a/src/BizHawk.Client.Common/Api/DisplaySurfaceID.cs b/src/BizHawk.Client.Common/Api/DisplaySurfaceID.cs index 24a1e29e243..ee7edf0e10d 100644 --- a/src/BizHawk.Client.Common/Api/DisplaySurfaceID.cs +++ b/src/BizHawk.Client.Common/Api/DisplaySurfaceID.cs @@ -1,4 +1,5 @@ #nullable enable +#pragma warning disable CS0436 // Type conflicts with imported type using System.Diagnostics.CodeAnalysis; diff --git a/src/BizHawk.Client.Common/DisplayManager/DisplayManagerBase.cs b/src/BizHawk.Client.Common/DisplayManager/DisplayManagerBase.cs index 5a30295c011..5513c219bcf 100644 --- a/src/BizHawk.Client.Common/DisplayManager/DisplayManagerBase.cs +++ b/src/BizHawk.Client.Common/DisplayManager/DisplayManagerBase.cs @@ -1,4 +1,4 @@ -// TODO +// TODO // we could flag textures as 'actually' render targets (keep a reference to the render target?) which could allow us to convert between them more quickly in some cases using System.Collections.Generic; @@ -112,6 +112,7 @@ protected DisplayManagerBase( // `_apiHawkIDTo2DRenderer` is a new empty dict, members are lazily-initialised RefreshUserShader(); + RefreshLibrashader(); } public void UpdateGlobals(Config config, IEmulator emulator) @@ -158,6 +159,9 @@ public void Dispose() s?.Dispose(); } + _librashaderFilterGL?.Dispose(); + _librashaderFilterD3D11?.Dispose(); + _theOneFont?.Dispose(); _renderer.Dispose(); } @@ -222,6 +226,9 @@ private StringRenderer Font private RetroShaderChain _shaderChainUser; + private LibrashaderFilterBase _librashaderFilterGL; + private LibrashaderFilterBase _librashaderFilterD3D11; + public abstract void ActivateOpenGLContext(); protected abstract void ActivateGraphicsControlContext(); @@ -231,6 +238,7 @@ private StringRenderer Font public void RefreshUserShader() { _shaderChainUser?.Dispose(); + _shaderChainUser = null; if (File.Exists(GlobalConfig.DispUserFilterPath)) { var fi = new FileInfo(GlobalConfig.DispUserFilterPath); @@ -239,6 +247,25 @@ public void RefreshUserShader() } } + public void RefreshLibrashader() + { + _librashaderFilterGL?.Dispose(); + _librashaderFilterGL = null; + _librashaderFilterD3D11?.Dispose(); + _librashaderFilterD3D11 = null; + + if (!File.Exists(GlobalConfig.DispUserFilterPath)) return; + + if (_gl is IGL_OpenGL) + { + _librashaderFilterGL = new LibrashaderFilterGL(GlobalConfig.DispUserFilterPath); + } + else if (_gl is IGL_D3D11) + { + _librashaderFilterD3D11 = new LibrashaderFilterD3D11(GlobalConfig.DispUserFilterPath); + } + } + private (int Left, int Top, int Right, int Bottom) CalculateCompleteContentPadding(bool user, bool source) { var padding = (Left: 0, Top: 0, Right: 0, Bottom: 0); @@ -282,6 +309,7 @@ private FilterProgram BuildDefaultChain(Size chainInSize, Size chainOutSize, boo // select user special FX shader chain KeyValuePair[] selectedChainProperties = null; RetroShaderChain selectedChain = null; + bool useLibrashader = false; switch (GlobalConfig.TargetDisplayFilter) { case 1 when _shaderChainHq2X is { Available: true }: @@ -294,11 +322,15 @@ private FilterProgram BuildDefaultChain(Size chainInSize, Size chainOutSize, boo case 3 when _shaderChainUser is { Available: true }: selectedChain = _shaderChainUser; break; + case 4 when _librashaderFilterGL != null || _librashaderFilterD3D11 != null: + useLibrashader = true; + break; } if (!includeUserFilters) { selectedChain = null; + useLibrashader = false; } var fCoreScreenControl = CreateCoreScreenControl(); @@ -362,6 +394,19 @@ private FilterProgram BuildDefaultChain(Size chainInSize, Size chainOutSize, boo AppendRetroShaderChain(chain, "retroShader", selectedChain, selectedChainProperties); } + // add librashader filter + if (useLibrashader) + { + if (_librashaderFilterGL != null) + { + chain.AddFilter(_librashaderFilterGL, "librashader"); + } + else if (_librashaderFilterD3D11 != null) + { + chain.AddFilter(_librashaderFilterD3D11, "librashader"); + } + } + // AutoPrescale makes no sense for a None final filter if (GlobalConfig.DispAutoPrescale && GlobalConfig.DispFinalFilter != (int)FinalPresentation.eFilterOption.None) { diff --git a/src/BizHawk.Client.Common/DisplayManager/Filters/LibrashaderFilterBase.cs b/src/BizHawk.Client.Common/DisplayManager/Filters/LibrashaderFilterBase.cs new file mode 100644 index 00000000000..2f8c2ccfc15 --- /dev/null +++ b/src/BizHawk.Client.Common/DisplayManager/Filters/LibrashaderFilterBase.cs @@ -0,0 +1,53 @@ +using System.Drawing; + +namespace BizHawk.Client.Common.Filters +{ + public abstract unsafe class LibrashaderFilterBase : BaseFilter, IDisposable + { + protected bool _initialized = false; + protected Size _outputSize; + protected Size _lastOutputSize; + protected string _shaderPresetPath; + protected int _filteredWidth; + protected int _filteredHeight; + + public bool IsAvailable => _initialized; + + protected LibrashaderFilterBase(string shaderPresetPath) + { + _shaderPresetPath = shaderPresetPath; + } + + public override void Initialize() + { + DeclareInput(SurfaceDisposition.Texture); + } + + public override void SetInputFormat(string channel, SurfaceState state) + { + DeclareOutput(new SurfaceState(new(_outputSize), SurfaceDisposition.RenderTarget)); + } + + public override Size PresizeOutput(string channel, Size size) + { + _outputSize = size; + return size; + } + + public override Size PresizeInput(string channel, Size inSize) + { + return inSize; + } + + protected void UpdateOutputSize() + { + _filteredWidth = _outputSize.Width; + _filteredHeight = _outputSize.Height; + _lastOutputSize = _outputSize; + } + + protected bool ShouldReinitialize => !_initialized || _lastOutputSize != _outputSize; + + public abstract void Dispose(); + } +} diff --git a/src/BizHawk.Client.Common/DisplayManager/Filters/LibrashaderFilterD3D11.cs b/src/BizHawk.Client.Common/DisplayManager/Filters/LibrashaderFilterD3D11.cs new file mode 100644 index 00000000000..2933cf7bd39 --- /dev/null +++ b/src/BizHawk.Client.Common/DisplayManager/Filters/LibrashaderFilterD3D11.cs @@ -0,0 +1,49 @@ +using BizHawk.Bizware.Graphics; + +namespace BizHawk.Client.Common.Filters +{ + public unsafe class LibrashaderFilterD3D11 : LibrashaderFilterBase + { + private LibrashaderProcessorD3D11 _processor; + + public LibrashaderFilterD3D11(string shaderPresetPath) : base(shaderPresetPath) + { + } + + private bool InitProcessor() + { + if (!ShouldReinitialize) return true; + + if (FilterProgram.GL is not IGL_D3D11 d3d11) return false; + + UpdateOutputSize(); + + _processor ??= new LibrashaderProcessorD3D11(d3d11, _shaderPresetPath); + return _initialized = _processor.Initialize(_filteredWidth, _filteredHeight); + } + + public override void Run() + { + if (!InitProcessor()) return; + + if (InputTexture is not D3D11Texture2D inputD3D11Texture) return; + + var inputSRV = inputD3D11Texture.SRV; + if (inputSRV == null) return; + + var outputRTV = (FilterProgram.CurrRenderTarget as D3D11RenderTarget)?.RTV; + var outputRTVPointer = outputRTV?.NativePointer ?? _processor.GetBackBufferRTVPointer(); + + if (outputRTVPointer == IntPtr.Zero) return; + + _processor.Render(inputSRV.NativePointer, outputRTVPointer); + } + + public override void Dispose() + { + _processor?.Dispose(); + _processor = null; + _initialized = false; + } + } +} diff --git a/src/BizHawk.Client.Common/DisplayManager/Filters/LibrashaderFilterGL.cs b/src/BizHawk.Client.Common/DisplayManager/Filters/LibrashaderFilterGL.cs new file mode 100644 index 00000000000..76ed826fd5a --- /dev/null +++ b/src/BizHawk.Client.Common/DisplayManager/Filters/LibrashaderFilterGL.cs @@ -0,0 +1,44 @@ +using BizHawk.Bizware.Graphics; + +namespace BizHawk.Client.Common.Filters +{ + public unsafe class LibrashaderFilterGL : LibrashaderFilterBase + { + private LibrashaderProcessorGL _processor; + + public LibrashaderFilterGL(string shaderPresetPath) : base(shaderPresetPath) + { + } + + private bool InitProcessor() + { + if (!ShouldReinitialize) return true; + + if (FilterProgram.GL is not IGL_OpenGL igl) return false; + + UpdateOutputSize(); + + _processor ??= new LibrashaderProcessorGL(igl, _shaderPresetPath); + return _initialized = _processor.Initialize(_filteredWidth, _filteredHeight); + } + + public override void Run() + { + if (!InitProcessor()) return; + + var inputTexId = (InputTexture as OpenGLTexture2D)?.TexID ?? default; + if (inputTexId == 0) return; + + var drawFbo = (FilterProgram.CurrRenderTarget as OpenGLRenderTarget)?.FBO ?? default; + + _processor.Render(inputTexId, drawFbo, InputTexture.Width, InputTexture.Height); + } + + public override void Dispose() + { + _processor?.Dispose(); + _processor = null; + _initialized = false; + } + } +} diff --git a/src/BizHawk.Client.EmuHawk/MainForm.Events.cs b/src/BizHawk.Client.EmuHawk/MainForm.Events.cs index c6960e1f2b1..9c136aca718 100644 --- a/src/BizHawk.Client.EmuHawk/MainForm.Events.cs +++ b/src/BizHawk.Client.EmuHawk/MainForm.Events.cs @@ -1322,6 +1322,7 @@ private void DisplayConfigMenuItem_Click(object sender, EventArgs e) { DisplayManager.UpdateGlobals(Config, Emulator); DisplayManager.RefreshUserShader(); + DisplayManager.RefreshLibrashader(); FrameBufferResized(); SynchChrome(); if (window.NeedReset) diff --git a/src/BizHawk.Client.EmuHawk/config/DisplayConfig.Designer.cs b/src/BizHawk.Client.EmuHawk/config/DisplayConfig.Designer.cs index 1fd45458a55..1ee2805cda6 100644 --- a/src/BizHawk.Client.EmuHawk/config/DisplayConfig.Designer.cs +++ b/src/BizHawk.Client.EmuHawk/config/DisplayConfig.Designer.cs @@ -1,4 +1,4 @@ -namespace BizHawk.Client.EmuHawk +namespace BizHawk.Client.EmuHawk { partial class DisplayConfig { @@ -36,7 +36,8 @@ private void InitializeComponent() this.lblScanlines = new BizHawk.WinForms.Controls.LocLabelEx(); this.lblUserFilterName = new BizHawk.WinForms.Controls.LocLabelEx(); this.btnSelectUserFilter = new System.Windows.Forms.Button(); - this.rbUser = new System.Windows.Forms.RadioButton(); + this.rbLegacy = new System.Windows.Forms.RadioButton(); + this.rbLibrashader = new System.Windows.Forms.RadioButton(); this.tbScanlineIntensity = new BizHawk.Client.EmuHawk.TransparentTrackBar(); this.rbNone = new System.Windows.Forms.RadioButton(); this.rbScanlines = new System.Windows.Forms.RadioButton(); @@ -161,14 +162,15 @@ private void InitializeComponent() this.groupBox1.Controls.Add(this.lblScanlines); this.groupBox1.Controls.Add(this.lblUserFilterName); this.groupBox1.Controls.Add(this.btnSelectUserFilter); - this.groupBox1.Controls.Add(this.rbUser); + this.groupBox1.Controls.Add(this.rbLibrashader); + this.groupBox1.Controls.Add(this.rbLegacy); this.groupBox1.Controls.Add(this.tbScanlineIntensity); this.groupBox1.Controls.Add(this.rbNone); this.groupBox1.Controls.Add(this.rbScanlines); this.groupBox1.Controls.Add(this.rbHq2x); this.groupBox1.Location = new System.Drawing.Point(6, 33); this.groupBox1.Name = "groupBox1"; - this.groupBox1.Size = new System.Drawing.Size(193, 132); + this.groupBox1.Size = new System.Drawing.Size(193, 155); this.groupBox1.TabIndex = 7; this.groupBox1.TabStop = false; this.groupBox1.Text = "Scaling Filter"; @@ -182,7 +184,7 @@ private void InitializeComponent() // // lblUserFilterName // - this.lblUserFilterName.Location = new System.Drawing.Point(6, 114); + this.lblUserFilterName.Location = new System.Drawing.Point(6, 137); this.lblUserFilterName.Name = "lblUserFilterName"; this.lblUserFilterName.Text = "Will contain user filter name"; // @@ -196,16 +198,27 @@ private void InitializeComponent() this.btnSelectUserFilter.UseVisualStyleBackColor = true; this.btnSelectUserFilter.Click += new System.EventHandler(this.BtnSelectUserFilter_Click); // - // rbUser + // rbLegacy // - this.rbUser.AutoSize = true; - this.rbUser.Location = new System.Drawing.Point(6, 88); - this.rbUser.Name = "rbUser"; - this.rbUser.Size = new System.Drawing.Size(47, 17); - this.rbUser.TabIndex = 4; - this.rbUser.TabStop = true; - this.rbUser.Text = "User"; - this.rbUser.UseVisualStyleBackColor = true; + this.rbLegacy.AutoSize = true; + this.rbLegacy.Location = new System.Drawing.Point(6, 88); + this.rbLegacy.Name = "rbLegacy"; + this.rbLegacy.Size = new System.Drawing.Size(47, 17); + this.rbLegacy.TabIndex = 4; + this.rbLegacy.TabStop = true; + this.rbLegacy.Text = "Legacy"; + this.rbLegacy.UseVisualStyleBackColor = true; + // + // rbLibrashader + // + this.rbLibrashader.AutoSize = true; + this.rbLibrashader.Location = new System.Drawing.Point(6, 111); + this.rbLibrashader.Name = "rbLibrashader"; + this.rbLibrashader.Size = new System.Drawing.Size(79, 17); + this.rbLibrashader.TabIndex = 6; + this.rbLibrashader.TabStop = true; + this.rbLibrashader.Text = "Librashader"; + this.rbLibrashader.UseVisualStyleBackColor = true; // // tbScanlineIntensity // @@ -1055,7 +1068,9 @@ private void InitializeComponent() private System.Windows.Forms.RadioButton rbFinalFilterNone; private System.Windows.Forms.RadioButton rbFinalFilterBilinear; private System.Windows.Forms.Button btnSelectUserFilter; - private System.Windows.Forms.RadioButton rbUser; + private System.Windows.Forms.RadioButton rbLegacy; + + private System.Windows.Forms.RadioButton rbLibrashader; private BizHawk.WinForms.Controls.LocLabelEx lblUserFilterName; private System.Windows.Forms.RadioButton rbUseRaw; private System.Windows.Forms.RadioButton rbUseSystem; diff --git a/src/BizHawk.Client.EmuHawk/config/DisplayConfig.cs b/src/BizHawk.Client.EmuHawk/config/DisplayConfig.cs index afde7001db0..73ea3f460a2 100755 --- a/src/BizHawk.Client.EmuHawk/config/DisplayConfig.cs +++ b/src/BizHawk.Client.EmuHawk/config/DisplayConfig.cs @@ -7,6 +7,7 @@ using BizHawk.Client.Common.Filters; using BizHawk.Common; using BizHawk.Common.NumberExtensions; +using BizHawk.Common.StringExtensions; using BizHawk.Emulation.Common; using BizHawk.WinForms.Controls; @@ -16,7 +17,12 @@ public partial class DisplayConfig : Form, IDialogParent { private static readonly FilesystemFilterSet CgShaderPresetsFSFilterSet = new( appendAllFilesEntry: false, - new FilesystemFilter(".CGP Files", extensions: [ "cgp", "glslp" ])); + new FilesystemFilter(".CGP Files", extensions: [ "cgp" ])); + + private static readonly FilesystemFilterSet LibrashaderFSFilterSet = new( + appendAllFilesEntry: false, + new FilesystemFilter("SLANG Presets", extensions: [ "slangp" ]), + new FilesystemFilter("GLSL Presets", extensions: [ "glslp" ])); private readonly Config _config; @@ -127,7 +133,8 @@ public DisplayConfig(Config config, IDialogController dialogController, IGL gl) rbNone.Checked = _config.TargetDisplayFilter == 0; rbHq2x.Checked = _config.TargetDisplayFilter == 1; rbScanlines.Checked = _config.TargetDisplayFilter == 2; - rbUser.Checked = _config.TargetDisplayFilter == 3; + rbLegacy.Checked = _config.TargetDisplayFilter == 3; + rbLibrashader.Checked = _config.TargetDisplayFilter == 4; _pathSelection = _config.DispUserFilterPath ?? ""; RefreshState(); @@ -221,8 +228,10 @@ private void BtnOk_Click(object sender, EventArgs e) _config.TargetDisplayFilter = 1; if (rbScanlines.Checked) _config.TargetDisplayFilter = 2; - if (rbUser.Checked) + if (rbLegacy.Checked) _config.TargetDisplayFilter = 3; + if (rbLibrashader.Checked) + _config.TargetDisplayFilter = 4; if (rbFinalFilterNone.Checked) _config.DispFinalFilter = 0; @@ -374,16 +383,22 @@ private void RefreshState() private void BtnSelectUserFilter_Click(object sender, EventArgs e) { var result = this.ShowFileOpenDialog( - filter: CgShaderPresetsFSFilterSet, + filter: rbLibrashader.Checked ? LibrashaderFSFilterSet : CgShaderPresetsFSFilterSet, initDir: string.IsNullOrWhiteSpace(_pathSelection) ? _config.PathEntries.GlobalBaseAbsolutePath() : Path.GetDirectoryName(_pathSelection)!, initFileName: _pathSelection); if (result is null) return; - rbUser.Checked = true; var choice = Path.GetFullPath(result); + if (!Path.GetExtension(choice).EqualsIgnoreCase(".cgp")) + { + _pathSelection = choice; + RefreshState(); + return; + } + //test the preset using (var stream = File.OpenRead(choice)) {