diff --git a/src/Avalonia.OpenGL/EglConsts.cs b/src/Avalonia.OpenGL/EglConsts.cs index bcb90d7382..62fb3faef6 100644 --- a/src/Avalonia.OpenGL/EglConsts.cs +++ b/src/Avalonia.OpenGL/EglConsts.cs @@ -174,5 +174,23 @@ namespace Avalonia.OpenGL public const int EGL_NO_IMAGE = 0; + public const int EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE = 0x3207; + public const int EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE = 0x3208; + + //EGL_ANGLE_platform_angle + public const int EGL_PLATFORM_ANGLE_ANGLE = 0x3202; + public const int EGL_PLATFORM_ANGLE_TYPE_ANGLE = 0x3203; + public const int EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE = 0x3204; + public const int EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE = 0x3205; + public const int EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED = 0x3451; + public const int EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE = 0x3206; + public const int EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE = 0x320A; + public const int EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE = 0x345E; + + //EGL_ANGLE_platform_angle_d3d + public const int EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE = 0x3209; + public const int EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE = 0x320F; + public const int EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_WARP_ANGLE = 0x320B; + public const int EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_REFERENCE_ANGLE = 0x320C; } } diff --git a/src/Avalonia.OpenGL/EglDisplay.cs b/src/Avalonia.OpenGL/EglDisplay.cs index 852e2cae3c..aef7dc494e 100644 --- a/src/Avalonia.OpenGL/EglDisplay.cs +++ b/src/Avalonia.OpenGL/EglDisplay.cs @@ -14,10 +14,27 @@ namespace Avalonia.OpenGL public EglDisplay(EglInterface egl) { - _egl = egl; - _display = _egl.GetDisplay(IntPtr.Zero); + _egl = egl; + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && _egl.GetPlatformDisplayEXT != null) + { + foreach (var dapi in new[] {EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE}) + { + _display = _egl.GetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, IntPtr.Zero, new[] + { + EGL_PLATFORM_ANGLE_TYPE_ANGLE, dapi, EGL_NONE + }); + if(_display != IntPtr.Zero) + break; + } + } + + if (_display == IntPtr.Zero) + _display = _egl.GetDisplay(IntPtr.Zero); + if(_display == IntPtr.Zero) throw new OpenGlException("eglGetDisplay failed"); + if (!_egl.Initialize(_display, out var major, out var minor)) throw new OpenGlException("eglInitialize failed"); @@ -67,10 +84,16 @@ namespace Avalonia.OpenGL if (_contextAttributes == null) throw new OpenGlException("No suitable EGL config was found"); - GlInterface = new GlInterface(proc => + GlInterface = new GlInterface((proc, optional) => { + using (var u = new Utf8Buffer(proc)) - return _egl.GetProcAddress(u); + { + var rv = _egl.GetProcAddress(u); + if (rv == IntPtr.Zero && !optional) + throw new OpenGlException("Missing function " + proc); + return rv; + } }); } diff --git a/src/Avalonia.OpenGL/EglGlPlatformFeature.cs b/src/Avalonia.OpenGL/EglGlPlatformFeature.cs index f974a3d656..13304cae14 100644 --- a/src/Avalonia.OpenGL/EglGlPlatformFeature.cs +++ b/src/Avalonia.OpenGL/EglGlPlatformFeature.cs @@ -11,21 +11,29 @@ namespace Avalonia.OpenGL public IGlContext DeferredContext { get; set; } public static void TryInitialize() + { + var feature = TryCreate(); + if (feature != null) + AvaloniaLocator.CurrentMutable.Bind().ToConstant(feature); + } + + public static EglGlPlatformFeature TryCreate() { try { var disp = new EglDisplay(); var ctx = disp.CreateContext(null); - AvaloniaLocator.CurrentMutable.Bind().ToConstant(new EglGlPlatformFeature + return new EglGlPlatformFeature { Display = disp, ImmediateContext = ctx, DeferredContext = disp.CreateContext(ctx) - }); + }; } catch(Exception e) { Logger.Error("OpenGL", null, "Unable to initialize EGL-based rendering: {0}", e); + return null; } } } diff --git a/src/Avalonia.OpenGL/EglInterface.cs b/src/Avalonia.OpenGL/EglInterface.cs index f80d3a7b44..025281ff74 100644 --- a/src/Avalonia.OpenGL/EglInterface.cs +++ b/src/Avalonia.OpenGL/EglInterface.cs @@ -16,21 +16,21 @@ namespace Avalonia.OpenGL { } - static Func Load() + static Func Load() { var os = AvaloniaLocator.Current.GetService().GetRuntimeInfo().OperatingSystem; if(os == OperatingSystemType.Linux || os == OperatingSystemType.Android) return Load("libEGL.so.1"); if (os == OperatingSystemType.WinNT) - return Load("libEGL.dll"); + return Load(@"libegl.dll"); throw new PlatformNotSupportedException(); } - static Func Load(string library) + static Func Load(string library) { var dyn = AvaloniaLocator.Current.GetService(); var lib = dyn.LoadLibrary(library); - return s => dyn.GetProcAddress(lib, s, false); + return (s, o) => dyn.GetProcAddress(lib, s, o); } @@ -38,6 +38,10 @@ namespace Avalonia.OpenGL public delegate IntPtr EglGetDisplay(IntPtr nativeDisplay); [EntryPoint("eglGetDisplay")] public EglGetDisplay GetDisplay { get; } + + public delegate IntPtr EglGetPlatformDisplayEXT(int platform, IntPtr nativeDisplay, int[] attrs); + [EntryPoint("eglGetPlatformDisplayEXT", true)] + public EglGetPlatformDisplayEXT GetPlatformDisplayEXT { get; } public delegate bool EglInitialize(IntPtr display, out int major, out int minor); [EntryPoint("eglInitialize")] diff --git a/src/Avalonia.OpenGL/EntryPointAttribute.cs b/src/Avalonia.OpenGL/EntryPointAttribute.cs index cf8e3d4b09..241f517df9 100644 --- a/src/Avalonia.OpenGL/EntryPointAttribute.cs +++ b/src/Avalonia.OpenGL/EntryPointAttribute.cs @@ -5,10 +5,12 @@ namespace Avalonia.OpenGL class EntryPointAttribute : Attribute { public string EntryPoint { get; } + public bool Optional { get; } - public EntryPointAttribute(string entryPoint) + public EntryPointAttribute(string entryPoint, bool optional = false) { EntryPoint = entryPoint; + Optional = optional; } } } diff --git a/src/Avalonia.OpenGL/GlInterface.cs b/src/Avalonia.OpenGL/GlInterface.cs index a29c6b9fed..95479d027f 100644 --- a/src/Avalonia.OpenGL/GlInterface.cs +++ b/src/Avalonia.OpenGL/GlInterface.cs @@ -7,15 +7,15 @@ namespace Avalonia.OpenGL public class GlInterface : GlInterfaceBase { - private readonly Func _getProcAddress; + private readonly Func _getProcAddress; - public GlInterface(Func getProcAddress) : base(getProcAddress) + public GlInterface(Func getProcAddress) : base(getProcAddress) { _getProcAddress = getProcAddress; } - public IntPtr GetProcAddress(string proc) => _getProcAddress(proc); + public IntPtr GetProcAddress(string proc) => _getProcAddress(proc, true); public T GetProcAddress(string proc) => Marshal.GetDelegateForFunctionPointer(GetProcAddress(proc)); diff --git a/src/Avalonia.OpenGL/GlInterfaceBase.cs b/src/Avalonia.OpenGL/GlInterfaceBase.cs index 7c350767f6..33191a6567 100644 --- a/src/Avalonia.OpenGL/GlInterfaceBase.cs +++ b/src/Avalonia.OpenGL/GlInterfaceBase.cs @@ -6,7 +6,7 @@ namespace Avalonia.OpenGL { public class GlInterfaceBase { - public GlInterfaceBase(Func getProcAddress) + public GlInterfaceBase(Func getProcAddress) { foreach (var prop in this.GetType().GetProperties()) { @@ -18,8 +18,9 @@ namespace Avalonia.OpenGL BindingFlags.Instance | BindingFlags.NonPublic); if (field == null) throw new InvalidProgramException($"Expected property {prop.Name} to have {fieldName}"); - field.SetValue(this, - Marshal.GetDelegateForFunctionPointer(getProcAddress(a.EntryPoint), prop.PropertyType)); + var proc = getProcAddress(a.EntryPoint, a.Optional); + if (proc != IntPtr.Zero) + field.SetValue(this, Marshal.GetDelegateForFunctionPointer(proc, prop.PropertyType)); } } } diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index 5039ef7454..80e2608dbe 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -108,7 +108,7 @@ namespace Avalonia.Skia { foreach (var surface in surfaces) { - if (surface is IGlPlatformSurface glSurface) + if (surface is IGlPlatformSurface glSurface && GrContext != null) { return new GlRenderTarget(GrContext, glSurface); } diff --git a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj index 5f26e4ad3e..1fb566dad0 100644 --- a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj +++ b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj @@ -10,6 +10,7 @@ + diff --git a/src/Windows/Avalonia.Win32/Win32GlManager.cs b/src/Windows/Avalonia.Win32/Win32GlManager.cs new file mode 100644 index 0000000000..e0a96108ff --- /dev/null +++ b/src/Windows/Avalonia.Win32/Win32GlManager.cs @@ -0,0 +1,25 @@ +using Avalonia.OpenGL; + +namespace Avalonia.Win32 +{ + static class Win32GlManager + { + /// This property is initialized if drawing platform requests OpenGL support + public static EglGlPlatformFeature EglFeature { get; private set; } + + private static bool s_attemptedToInitialize; + public static void Initialize() + { + AvaloniaLocator.CurrentMutable.Bind().ToFunc(() => + { + if (!s_attemptedToInitialize) + { + EglFeature = EglGlPlatformFeature.TryCreate(); + s_attemptedToInitialize = true; + } + + return EglFeature; + }); + } + } +} diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index ef2dfd3c1a..89943b5b0a 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -88,7 +88,7 @@ namespace Avalonia.Win32 .Bind().ToSingleton() .Bind().ToConstant(s_instance) .Bind().ToConstant(s_instance); - + Win32GlManager.Initialize(); UseDeferredRendering = deferredRendering; _uiThread = UnmanagedMethods.GetCurrentThreadId(); diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 292d8a8138..d81f96a964 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -10,6 +10,7 @@ using System.Runtime.InteropServices; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Input.Raw; +using Avalonia.OpenGL; using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Win32.Input; @@ -18,7 +19,7 @@ using static Avalonia.Win32.Interop.UnmanagedMethods; namespace Avalonia.Win32 { - public class WindowImpl : IWindowImpl + public class WindowImpl : IWindowImpl, IEglWindowGlPlatformSurfaceInfo { private static readonly List s_instances = new List(); @@ -37,6 +38,7 @@ namespace Avalonia.Win32 private WindowState _showWindowState; private WindowState _lastWindowState; private FramebufferManager _framebuffer; + private IGlPlatformSurface _gl; private OleDropTarget _dropTarget; private Size _minSize; private Size _maxSize; @@ -58,6 +60,10 @@ namespace Avalonia.Win32 #endif CreateWindow(); _framebuffer = new FramebufferManager(_hwnd); + if (Win32GlManager.EglFeature != null) + _gl = new EglGlPlatformSurface((EglDisplay)Win32GlManager.EglFeature.Display, + Win32GlManager.EglFeature.DeferredContext, this); + s_instances.Add(this); } @@ -211,7 +217,7 @@ namespace Avalonia.Win32 public IEnumerable Surfaces => new object[] { - Handle, _framebuffer + Handle, _gl, _framebuffer }; public void Activate() @@ -921,5 +927,18 @@ namespace Avalonia.Win32 _topmost = value; } + + System.Drawing.Size IEglWindowGlPlatformSurfaceInfo.PixelSize + { + get + { + RECT rect; + GetClientRect(_hwnd, out rect); + return new System.Drawing.Size( + Math.Max(1, rect.right - rect.left), + Math.Max(1, rect.bottom - rect.top)); + } + } + IntPtr IEglWindowGlPlatformSurfaceInfo.Handle => Handle.Handle; } }